blob: cd8082a9a0e93dc070affb985a4f97fe86c28eab [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);
6*/
7
8var Hint = function (param) {
9 var foundryRegex = new RegExp("(?:^|[^a-zA-Z0-9])([-a-zA-Z0-9]+?)\/(?:([^=]+?)=)?$");
10
Nils Diewald465c4252014-06-20 21:51:58 +000011 var search = document.getElementById(param["ref"]);
12 var qlField = document.getElementById(param["qlRef"]);
13 var mirror = document.createElement("div");
14 var hint = document.createElement("ul");
Nils Diewald44a72782014-06-20 16:03:21 +000015 var hintSize = param["hintSize"] ? param["hintSize"] : 10;
Nils Diewald465c4252014-06-20 21:51:58 +000016 var hints = param["hints"];
Nils Diewald44a72782014-06-20 16:03:21 +000017 var that = this;
Nils Diewald465c4252014-06-20 21:51:58 +000018 var ql;
Nils Diewald44a72782014-06-20 16:03:21 +000019
20 // Build the mirror element
21 // <div id="searchMirror"><span></span><ul></ul></div>
22 mirror.setAttribute("id", "searchMirror");
23 mirror.appendChild(document.createElement("span"));
24 mirror.appendChild(hint);
25 search.parentNode.insertBefore(mirror, search);
26
27 // Default active state
28 this.active = -2;
29
30 // Show hint table
31 this.show = function (topic) {
32 if (!hints[topic])
33 return;
Nils Diewald3b5c4632014-06-20 22:06:02 +000034 this.hide();
Nils Diewald44a72782014-06-20 16:03:21 +000035 this.active = -1;
36 this.list(hints[topic]);
37 var searchRight = search.getBoundingClientRect().right;
38 var infoRight = hint.getBoundingClientRect().right;
39 if (infoRight > searchRight) {
40 hint.style.marginLeft = '-' + (infoRight - searchRight) + 'px';
41 };
42 hint.style.opacity = 1;
43 };
44
45 // Initialize the mirror element
46 function init () {
47 // Copy input style
48 var searchRect = search.getBoundingClientRect();
49 var searchStyle = window.getComputedStyle(search, null);
50
51 with (mirror.style) {
52 left = searchRect.left + "px";
53 top = searchRect.bottom + "px";
54 borderLeftColor = "transparent";
55 paddingLeft = searchStyle.getPropertyValue("padding-left");
56 marginLeft = searchStyle.getPropertyValue("margin-left");
57 borderLeftWidth = searchStyle.getPropertyValue("border-left-width");
58 borderLeftStyle = searchStyle.getPropertyValue("border-left-style");
59 fontSize = searchStyle.getPropertyValue("font-size");
60 fontFamily = searchStyle.getPropertyValue("font-family");
61 };
Nils Diewald465c4252014-06-20 21:51:58 +000062
63 qlSelect();
Nils Diewald44a72782014-06-20 16:03:21 +000064 };
65
66 // Hide hint table
67 this.hide = function () {
Nils Diewald3b5c4632014-06-20 22:06:02 +000068 if (this.active === -2)
69 return;
Nils Diewald44a72782014-06-20 16:03:21 +000070 this.active = -2;
71 hint.style.opacity = 0;
72 hint.style.marginLeft = 0;
73
74 // Remove all children
75 var lis = hint.childNodes;
76 for (var li = lis.length - 1; li >= 0; li--) {
77 hint.removeChild(lis[li])
78 };
79 };
80
81 // List elements in the hint table
82 this.list = function (hintList) {
83 var li, title;
84 var arrayType = hintList instanceof Array;
85 for (var i in hintList) {
86 // Create list items
87 li = document.createElement("li");
88 li.setAttribute("data-action", arrayType ? hintList[i] : hintList[i][0]);
89 title = document.createElement("strong");
90 title.appendChild(document.createTextNode(arrayType ? hintList[i] : i));
91 li.appendChild(title);
92 hint.appendChild(li);
93
94 // Include descriptions
95 if (!arrayType && hintList[i][1]) {
96 var desc = document.createElement("span");
97 desc.appendChild(document.createTextNode(hintList[i][1]));
98 li.appendChild(desc);
99 };
100 };
101 };
102
103 // Choose next item in list
104 this.next = function () {
105 if (this.active === -2)
106 return;
107 var lis = hint.getElementsByTagName("li");
108 if (this.active === -1) {
109 lis[0].setAttribute("class", "active");
110 this.active = 0;
111 }
112 else if (this.active === lis.length - 1) {
113 lis[this.active].removeAttribute("class");
114 lis[0].setAttribute("class", "active");
115 this.active = 0;
116 }
117 else {
118 lis[this.active].removeAttribute("class");
119 lis[++this.active].setAttribute("class", "active");
120 };
121 };
122
123 // Choose previous item in list
124 this.previous = function () {
125 if (this.active === -2)
126 return;
127
128 var lis = hint.getElementsByTagName("li");
129 if (this.active === -1) {
130 this.active = lis.length - 1;
131 lis[this.active].setAttribute("class", "active");
132 }
133 else if (this.active === 0) {
134 lis[0].removeAttribute("class");
135 this.active = lis.length - 1;
136 lis[this.active].setAttribute("class", "active");
137 }
138 else {
139 lis[this.active].removeAttribute("class");
140 lis[--this.active].setAttribute("class", "active");
141 };
142 };
143
144 // Choose item from list
145 this.choose = function () {
146 if (this.active < 0)
147 return;
148
149 var action = hint.getElementsByTagName("li")[this.active].getAttribute("data-action");
150
151 var value = search.value;
152 var start = search.selectionStart;
153 var begin = value.substring(0, start);
154 var end = value.substring(start, value.length);
155 search.value = begin + action + end;
Nils Diewald465c4252014-06-20 21:51:58 +0000156 search.selectionStart = (begin + action).length
157 search.selectionEnd = search.selectionStart;
Nils Diewald44a72782014-06-20 16:03:21 +0000158
159 this.hide();
160
161 // Check for new changes
Nils Diewald465c4252014-06-20 21:51:58 +0000162 mirror.firstChild.textContent = begin + action;
163
Nils Diewald44a72782014-06-20 16:03:21 +0000164 if (foundryRegex.exec(begin + action))
165 this.show(RegExp.$1 + (RegExp.$2 ? '/' + RegExp.$2 : ''));
166 };
167
168 function changed (e) {
169 var el = e.target;
170
171 if (e.key === '/' || e.key === '=') {
172 var start = el.selectionStart;
Nils Diewald465c4252014-06-20 21:51:58 +0000173 mirror.firstChild.textContent = el.value.substring(0, start);
Nils Diewald44a72782014-06-20 16:03:21 +0000174 var sub = el.value.substring(start - 128 >= 0 ? start - 128 : 0, start);
175
176 if (foundryRegex.exec(sub))
177 that.show(RegExp.$1 + (RegExp.$2 ? '/' + RegExp.$2 : ''));
178 }
Nils Diewald3b5c4632014-06-20 22:06:02 +0000179 else if (e.key === '>') {
180 that.hide();
181 }
182 else if (ql === 'poliqarp' && (e.key === '[' || e.key === '<')) {
Nils Diewald465c4252014-06-20 21:51:58 +0000183 mirror.firstChild.textContent = el.value.substring(0, el.selectionStart);
184 that.show("-foundries");
185 }
Nils Diewald44a72782014-06-20 16:03:21 +0000186 else if (e.key !== 'Shift' &&
187 e.key !== 'Up' &&
188 e.key !== 'Down' &&
Nils Diewald465c4252014-06-20 21:51:58 +0000189 e.key !== 'Enter' &&
190 e.key !== 'Alt' &&
191 e.key !== 'AltGraph' &&
192 e.key !== 'CapsLock') {
Nils Diewald44a72782014-06-20 16:03:21 +0000193 that.hide();
194 };
195 };
196
197 // Select item from suggestion list
198 function select (e) {
199 if (that.active === -2)
200 return;
201 if (e.key === 'Down') {
202 that.next();
203 }
204 else if (e.key === 'Up') {
205 that.previous();
206 }
207 else if (e.key === 'Enter') {
208 that.choose();
209 e.stopPropagation();
210 e.preventDefault();
211 return false;
Nils Diewald3b5c4632014-06-20 22:06:02 +0000212 }
Nils Diewald44a72782014-06-20 16:03:21 +0000213 };
214
Nils Diewald465c4252014-06-20 21:51:58 +0000215 function qlSelect () {
216 var nodes = qlField.childNodes;
217 for (var i = 0; i < nodes.length; i++) {
218 if (nodes[i].selected) {
219 ql = nodes[i].value;
220 break;
221 };
222 };
223 };
224
Nils Diewald44a72782014-06-20 16:03:21 +0000225 // Initialize style
226 init();
Nils Diewald465c4252014-06-20 21:51:58 +0000227 search.addEventListener("keyup", changed, false);
Nils Diewald3b5c4632014-06-20 22:06:02 +0000228 search.addEventListener("keydown", select, false);
Nils Diewald465c4252014-06-20 21:51:58 +0000229 qlField.addEventListener('change', qlSelect, false);
Nils Diewald44a72782014-06-20 16:03:21 +0000230};