blob: 80c7d2599195f7e6e5bdba430a33c979b10b5afd [file] [log] [blame]
Nils Diewald44a72782014-06-20 16:03:21 +00001/*
2TODO:
3 - Limit the size to a certain number of elements
4 - addEventListener("click", ... , false);
5 - addEventListener("paste", ... , false);
Nils Diewald4e9fbcb2014-07-15 11:45:09 +00006 - Make this a general purpose hint-System with left-context-suport
7 - Die Funktion, wann was angezeigt werden soll, sollte extern
8 definiert sein (der Kontext / changed)
9 - Die Werteliste sollte weitere Attribute enthalten, wie title und class
Nils Diewald44a72782014-06-20 16:03:21 +000010*/
11
12var Hint = function (param) {
13 var foundryRegex = new RegExp("(?:^|[^a-zA-Z0-9])([-a-zA-Z0-9]+?)\/(?:([^=]+?)=)?$");
14
Nils Diewald465c4252014-06-20 21:51:58 +000015 var search = document.getElementById(param["ref"]);
16 var qlField = document.getElementById(param["qlRef"]);
17 var mirror = document.createElement("div");
18 var hint = document.createElement("ul");
Nils Diewald44a72782014-06-20 16:03:21 +000019 var hintSize = param["hintSize"] ? param["hintSize"] : 10;
Nils Diewald465c4252014-06-20 21:51:58 +000020 var hints = param["hints"];
Nils Diewald44a72782014-06-20 16:03:21 +000021 var that = this;
Nils Diewald465c4252014-06-20 21:51:58 +000022 var ql;
Nils Diewald44a72782014-06-20 16:03:21 +000023
24 // Build the mirror element
25 // <div id="searchMirror"><span></span><ul></ul></div>
26 mirror.setAttribute("id", "searchMirror");
27 mirror.appendChild(document.createElement("span"));
28 mirror.appendChild(hint);
Nils Diewald7cad8402014-07-08 17:06:56 +000029 document.getElementsByTagName("body")[0].appendChild(mirror);
Nils Diewald44a72782014-06-20 16:03:21 +000030 // Default active state
31 this.active = -2;
32
33 // Show hint table
34 this.show = function (topic) {
35 if (!hints[topic])
36 return;
Nils Diewald3b5c4632014-06-20 22:06:02 +000037 this.hide();
Nils Diewald44a72782014-06-20 16:03:21 +000038 this.active = -1;
39 this.list(hints[topic]);
40 var searchRight = search.getBoundingClientRect().right;
41 var infoRight = hint.getBoundingClientRect().right;
42 if (infoRight > searchRight) {
43 hint.style.marginLeft = '-' + (infoRight - searchRight) + 'px';
44 };
45 hint.style.opacity = 1;
46 };
47
48 // Initialize the mirror element
49 function init () {
50 // Copy input style
51 var searchRect = search.getBoundingClientRect();
52 var searchStyle = window.getComputedStyle(search, null);
53
54 with (mirror.style) {
55 left = searchRect.left + "px";
56 top = searchRect.bottom + "px";
57 borderLeftColor = "transparent";
58 paddingLeft = searchStyle.getPropertyValue("padding-left");
59 marginLeft = searchStyle.getPropertyValue("margin-left");
60 borderLeftWidth = searchStyle.getPropertyValue("border-left-width");
61 borderLeftStyle = searchStyle.getPropertyValue("border-left-style");
62 fontSize = searchStyle.getPropertyValue("font-size");
63 fontFamily = searchStyle.getPropertyValue("font-family");
64 };
Nils Diewald465c4252014-06-20 21:51:58 +000065
66 qlSelect();
Nils Diewald44a72782014-06-20 16:03:21 +000067 };
68
69 // Hide hint table
70 this.hide = function () {
Nils Diewald3b5c4632014-06-20 22:06:02 +000071 if (this.active === -2)
72 return;
Nils Diewald44a72782014-06-20 16:03:21 +000073 this.active = -2;
74 hint.style.opacity = 0;
75 hint.style.marginLeft = 0;
76
77 // Remove all children
78 var lis = hint.childNodes;
79 for (var li = lis.length - 1; li >= 0; li--) {
80 hint.removeChild(lis[li])
81 };
82 };
83
84 // List elements in the hint table
85 this.list = function (hintList) {
86 var li, title;
87 var arrayType = hintList instanceof Array;
88 for (var i in hintList) {
89 // Create list items
90 li = document.createElement("li");
91 li.setAttribute("data-action", arrayType ? hintList[i] : hintList[i][0]);
92 title = document.createElement("strong");
93 title.appendChild(document.createTextNode(arrayType ? hintList[i] : i));
94 li.appendChild(title);
95 hint.appendChild(li);
96
97 // Include descriptions
98 if (!arrayType && hintList[i][1]) {
99 var desc = document.createElement("span");
100 desc.appendChild(document.createTextNode(hintList[i][1]));
101 li.appendChild(desc);
102 };
103 };
104 };
105
106 // Choose next item in list
107 this.next = function () {
108 if (this.active === -2)
Nils Diewald7cad8402014-07-08 17:06:56 +0000109 return false;
110
Nils Diewald44a72782014-06-20 16:03:21 +0000111 var lis = hint.getElementsByTagName("li");
112 if (this.active === -1) {
113 lis[0].setAttribute("class", "active");
114 this.active = 0;
115 }
116 else if (this.active === lis.length - 1) {
117 lis[this.active].removeAttribute("class");
118 lis[0].setAttribute("class", "active");
119 this.active = 0;
120 }
121 else {
122 lis[this.active].removeAttribute("class");
123 lis[++this.active].setAttribute("class", "active");
124 };
Nils Diewald7cad8402014-07-08 17:06:56 +0000125
126 return true;
Nils Diewald44a72782014-06-20 16:03:21 +0000127 };
128
129 // Choose previous item in list
130 this.previous = function () {
131 if (this.active === -2)
132 return;
133
134 var lis = hint.getElementsByTagName("li");
135 if (this.active === -1) {
136 this.active = lis.length - 1;
137 lis[this.active].setAttribute("class", "active");
138 }
139 else if (this.active === 0) {
140 lis[0].removeAttribute("class");
141 this.active = lis.length - 1;
142 lis[this.active].setAttribute("class", "active");
143 }
144 else {
145 lis[this.active].removeAttribute("class");
146 lis[--this.active].setAttribute("class", "active");
147 };
148 };
149
150 // Choose item from list
151 this.choose = function () {
152 if (this.active < 0)
153 return;
154
155 var action = hint.getElementsByTagName("li")[this.active].getAttribute("data-action");
156
157 var value = search.value;
158 var start = search.selectionStart;
159 var begin = value.substring(0, start);
160 var end = value.substring(start, value.length);
161 search.value = begin + action + end;
Nils Diewald465c4252014-06-20 21:51:58 +0000162 search.selectionStart = (begin + action).length
163 search.selectionEnd = search.selectionStart;
Nils Diewald44a72782014-06-20 16:03:21 +0000164
165 this.hide();
166
167 // Check for new changes
Nils Diewald465c4252014-06-20 21:51:58 +0000168 mirror.firstChild.textContent = begin + action;
169
Nils Diewald44a72782014-06-20 16:03:21 +0000170 if (foundryRegex.exec(begin + action))
171 this.show(RegExp.$1 + (RegExp.$2 ? '/' + RegExp.$2 : ''));
Nils Diewald7cad8402014-07-08 17:06:56 +0000172
173 return true;
Nils Diewald44a72782014-06-20 16:03:21 +0000174 };
175
176 function changed (e) {
177 var el = e.target;
Nils Diewald44a72782014-06-20 16:03:21 +0000178 if (e.key === '/' || e.key === '=') {
179 var start = el.selectionStart;
Nils Diewald465c4252014-06-20 21:51:58 +0000180 mirror.firstChild.textContent = el.value.substring(0, start);
Nils Diewald44a72782014-06-20 16:03:21 +0000181 var sub = el.value.substring(start - 128 >= 0 ? start - 128 : 0, start);
182
183 if (foundryRegex.exec(sub))
184 that.show(RegExp.$1 + (RegExp.$2 ? '/' + RegExp.$2 : ''));
185 }
Nils Diewald3b5c4632014-06-20 22:06:02 +0000186 else if (e.key === '>') {
187 that.hide();
188 }
189 else if (ql === 'poliqarp' && (e.key === '[' || e.key === '<')) {
Nils Diewald465c4252014-06-20 21:51:58 +0000190 mirror.firstChild.textContent = el.value.substring(0, el.selectionStart);
191 that.show("-foundries");
192 }
Nils Diewald44a72782014-06-20 16:03:21 +0000193 else if (e.key !== 'Shift' &&
194 e.key !== 'Up' &&
195 e.key !== 'Down' &&
Nils Diewald465c4252014-06-20 21:51:58 +0000196 e.key !== 'Enter' &&
197 e.key !== 'Alt' &&
198 e.key !== 'AltGraph' &&
199 e.key !== 'CapsLock') {
Nils Diewald44a72782014-06-20 16:03:21 +0000200 that.hide();
201 };
202 };
203
204 // Select item from suggestion list
205 function select (e) {
206 if (that.active === -2)
207 return;
208 if (e.key === 'Down') {
Nils Diewald89ebc692014-11-17 21:34:02 +0000209 e.stopPropagation();
210 e.preventDefault();
Nils Diewald7cad8402014-07-08 17:06:56 +0000211 return that.next();
Nils Diewald44a72782014-06-20 16:03:21 +0000212 }
213 else if (e.key === 'Up') {
Nils Diewald89ebc692014-11-17 21:34:02 +0000214 e.stopPropagation();
215 e.preventDefault();
Nils Diewald7cad8402014-07-08 17:06:56 +0000216 return that.previous();
Nils Diewald44a72782014-06-20 16:03:21 +0000217 }
Nils Diewald7cad8402014-07-08 17:06:56 +0000218 else if (e.key === 'Enter' && that.choose()) {
Nils Diewald44a72782014-06-20 16:03:21 +0000219 e.stopPropagation();
220 e.preventDefault();
221 return false;
Nils Diewaldbfcf0902014-07-15 13:36:47 +0000222 }
223 else if (e.key !== 'Shift' &&
224 e.key !== 'Alt' &&
225 e.key !== 'AltGraph' &&
226 e.key !== 'CapsLock') {
227 that.hide();
Nils Diewald7cad8402014-07-08 17:06:56 +0000228 };
Nils Diewaldbfcf0902014-07-15 13:36:47 +0000229 return true;
Nils Diewald44a72782014-06-20 16:03:21 +0000230 };
231
Nils Diewald465c4252014-06-20 21:51:58 +0000232 function qlSelect () {
233 var nodes = qlField.childNodes;
234 for (var i = 0; i < nodes.length; i++) {
235 if (nodes[i].selected) {
236 ql = nodes[i].value;
237 break;
238 };
239 };
240 };
241
Nils Diewald44a72782014-06-20 16:03:21 +0000242 // Initialize style
243 init();
Nils Diewalddb03fa92014-06-23 13:36:55 +0000244 window.onresize = init;
Nils Diewald002e8fb2014-06-22 14:27:01 +0000245 search.addEventListener("keyup", changed, false);
Nils Diewald3b5c4632014-06-20 22:06:02 +0000246 search.addEventListener("keydown", select, false);
Nils Diewaldbfcf0902014-07-15 13:36:47 +0000247 qlField.addEventListener("change", qlSelect, false);
Nils Diewald44a72782014-06-20 16:03:21 +0000248};