blob: f21798a8647d68ec1b8f73988cde3bde85982506 [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;
34 this.active = -1;
35 this.list(hints[topic]);
36 var searchRight = search.getBoundingClientRect().right;
37 var infoRight = hint.getBoundingClientRect().right;
38 if (infoRight > searchRight) {
39 hint.style.marginLeft = '-' + (infoRight - searchRight) + 'px';
40 };
41 hint.style.opacity = 1;
42 };
43
44 // Initialize the mirror element
45 function init () {
46 // Copy input style
47 var searchRect = search.getBoundingClientRect();
48 var searchStyle = window.getComputedStyle(search, null);
49
50 with (mirror.style) {
51 left = searchRect.left + "px";
52 top = searchRect.bottom + "px";
53 borderLeftColor = "transparent";
54 paddingLeft = searchStyle.getPropertyValue("padding-left");
55 marginLeft = searchStyle.getPropertyValue("margin-left");
56 borderLeftWidth = searchStyle.getPropertyValue("border-left-width");
57 borderLeftStyle = searchStyle.getPropertyValue("border-left-style");
58 fontSize = searchStyle.getPropertyValue("font-size");
59 fontFamily = searchStyle.getPropertyValue("font-family");
60 };
Nils Diewald465c4252014-06-20 21:51:58 +000061
62 qlSelect();
Nils Diewald44a72782014-06-20 16:03:21 +000063 };
64
65 // Hide hint table
66 this.hide = function () {
67 this.active = -2;
68 hint.style.opacity = 0;
69 hint.style.marginLeft = 0;
70
71 // Remove all children
72 var lis = hint.childNodes;
73 for (var li = lis.length - 1; li >= 0; li--) {
74 hint.removeChild(lis[li])
75 };
76 };
77
78 // List elements in the hint table
79 this.list = function (hintList) {
80 var li, title;
81 var arrayType = hintList instanceof Array;
82 for (var i in hintList) {
83 // Create list items
84 li = document.createElement("li");
85 li.setAttribute("data-action", arrayType ? hintList[i] : hintList[i][0]);
86 title = document.createElement("strong");
87 title.appendChild(document.createTextNode(arrayType ? hintList[i] : i));
88 li.appendChild(title);
89 hint.appendChild(li);
90
91 // Include descriptions
92 if (!arrayType && hintList[i][1]) {
93 var desc = document.createElement("span");
94 desc.appendChild(document.createTextNode(hintList[i][1]));
95 li.appendChild(desc);
96 };
97 };
98 };
99
100 // Choose next item in list
101 this.next = function () {
102 if (this.active === -2)
103 return;
104 var lis = hint.getElementsByTagName("li");
105 if (this.active === -1) {
106 lis[0].setAttribute("class", "active");
107 this.active = 0;
108 }
109 else if (this.active === lis.length - 1) {
110 lis[this.active].removeAttribute("class");
111 lis[0].setAttribute("class", "active");
112 this.active = 0;
113 }
114 else {
115 lis[this.active].removeAttribute("class");
116 lis[++this.active].setAttribute("class", "active");
117 };
118 };
119
120 // Choose previous item in list
121 this.previous = function () {
122 if (this.active === -2)
123 return;
124
125 var lis = hint.getElementsByTagName("li");
126 if (this.active === -1) {
127 this.active = lis.length - 1;
128 lis[this.active].setAttribute("class", "active");
129 }
130 else if (this.active === 0) {
131 lis[0].removeAttribute("class");
132 this.active = lis.length - 1;
133 lis[this.active].setAttribute("class", "active");
134 }
135 else {
136 lis[this.active].removeAttribute("class");
137 lis[--this.active].setAttribute("class", "active");
138 };
139 };
140
141 // Choose item from list
142 this.choose = function () {
143 if (this.active < 0)
144 return;
145
146 var action = hint.getElementsByTagName("li")[this.active].getAttribute("data-action");
147
148 var value = search.value;
149 var start = search.selectionStart;
150 var begin = value.substring(0, start);
151 var end = value.substring(start, value.length);
152 search.value = begin + action + end;
Nils Diewald465c4252014-06-20 21:51:58 +0000153 search.selectionStart = (begin + action).length
154 search.selectionEnd = search.selectionStart;
Nils Diewald44a72782014-06-20 16:03:21 +0000155
156 this.hide();
157
158 // Check for new changes
Nils Diewald465c4252014-06-20 21:51:58 +0000159 mirror.firstChild.textContent = begin + action;
160
Nils Diewald44a72782014-06-20 16:03:21 +0000161 if (foundryRegex.exec(begin + action))
162 this.show(RegExp.$1 + (RegExp.$2 ? '/' + RegExp.$2 : ''));
163 };
164
165 function changed (e) {
166 var el = e.target;
167
168 if (e.key === '/' || e.key === '=') {
169 var start = el.selectionStart;
Nils Diewald465c4252014-06-20 21:51:58 +0000170 mirror.firstChild.textContent = el.value.substring(0, start);
Nils Diewald44a72782014-06-20 16:03:21 +0000171 var sub = el.value.substring(start - 128 >= 0 ? start - 128 : 0, start);
172
173 if (foundryRegex.exec(sub))
174 that.show(RegExp.$1 + (RegExp.$2 ? '/' + RegExp.$2 : ''));
175 }
Nils Diewald465c4252014-06-20 21:51:58 +0000176 else if (ql === 'poliqarp' && (e.key === '[' || (e.key === '<' && !e.shiftKey))) {
177 mirror.firstChild.textContent = el.value.substring(0, el.selectionStart);
178 that.show("-foundries");
179 }
Nils Diewald44a72782014-06-20 16:03:21 +0000180 else if (e.key !== 'Shift' &&
181 e.key !== 'Up' &&
182 e.key !== 'Down' &&
Nils Diewald465c4252014-06-20 21:51:58 +0000183 e.key !== 'Enter' &&
184 e.key !== 'Alt' &&
185 e.key !== 'AltGraph' &&
186 e.key !== 'CapsLock') {
Nils Diewald44a72782014-06-20 16:03:21 +0000187 that.hide();
188 };
189 };
190
191 // Select item from suggestion list
192 function select (e) {
193 if (that.active === -2)
194 return;
195 if (e.key === 'Down') {
196 that.next();
197 }
198 else if (e.key === 'Up') {
199 that.previous();
200 }
201 else if (e.key === 'Enter') {
202 that.choose();
203 e.stopPropagation();
204 e.preventDefault();
205 return false;
206 };
207 };
208
Nils Diewald465c4252014-06-20 21:51:58 +0000209 function qlSelect () {
210 var nodes = qlField.childNodes;
211 for (var i = 0; i < nodes.length; i++) {
212 if (nodes[i].selected) {
213 ql = nodes[i].value;
214 break;
215 };
216 };
217 };
218
Nils Diewald44a72782014-06-20 16:03:21 +0000219 // Initialize style
220 init();
Nils Diewald465c4252014-06-20 21:51:58 +0000221 search.addEventListener("keyup", changed, false);
222 search.addEventListener("keypress", select, false);
223 qlField.addEventListener('change', qlSelect, false);
Nils Diewald44a72782014-06-20 16:03:21 +0000224};