blob: 96f08a3cf292b40b00118bfaebec1943d6b21417 [file] [log] [blame]
Nils Diewaldeca30442014-11-18 20:33:54 +00001"use strict";
Nils Diewald44a72782014-06-20 16:03:21 +00002
Nils Diewaldcd571622014-11-19 00:58:59 +00003/*
4Todo:
5- limit the view based on prefix matches
6- highlight matching substrings
7*/
8
Nils Diewaldeca30442014-11-18 20:33:54 +00009// Don't let events bubble up
10Event.prototype.halt = function () {
11 this.stopPropagation();
12 this.preventDefault();
13};
Nils Diewald44a72782014-06-20 16:03:21 +000014
Nils Diewaldeca30442014-11-18 20:33:54 +000015// http://www.nlpado.de/~sebastian/software/ner_german.shtml
16// http://www.cnts.ua.ac.be/conll2003/ner/
17var namedEntities = [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +000018 ["I-LOC", "I-LOC ", "Location"],
19 ["I-MISC", "I-MISC ", "Miscellaneous"],
20 ["I-ORG", "I-ORG ", "Organization"],
21 ["I-PER", "I-PER ", "Person"]
Nils Diewaldeca30442014-11-18 20:33:54 +000022];
Nils Diewald44a72782014-06-20 16:03:21 +000023
Nils Diewaldeca30442014-11-18 20:33:54 +000024// http://www.ids-mannheim.de/cosmas2/projekt/referenz/stts/morph.html
25// http://nachhalt.sfb632.uni-potsdam.de/owl-docu/stts.html
26var sttsArray = [
27 // "$.", "$(", "$,"
Nils Diewaldc5d9bac2014-11-19 18:10:38 +000028 ["ADJA","ADJA ", "Attributive Adjective"],
29 ["ADJD","ADJD ", "Predicative Adjective"],
30 ["ADV","ADV ", "Adverb"],
31 ["APPO","APPO ", "Postposition"],
32 ["APPR","APPR ", "Preposition"],
33 ["APPRART","APPRART ", "Preposition with Determiner"],
34 ["APZR","APZR ","Right Circumposition"],
35 ["ART","ART ", "Determiner"],
36 ["CARD","CARD ", "Cardinal Number"],
37 ["FM","FM ", "Foreign Material"],
38 ["ITJ","ITJ ", "Interjection"],
39 ["KOKOM","KOKOM ", "Comparison Particle"],
40 ["KON","KON ", "Coordinating Conjuncion"],
41 ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
42 ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
43 ["NE","NE ", "Named Entity"],
44 ["NN","NN ", "Normal Nomina"],
45 ["PAV", "PAV ", "Pronominal Adverb"],
46 ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
47 ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
48 ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
49 ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
50 ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
51 ["PPER","PPER ", "Personal Pronoun"],
52 ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
53 ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
54 ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
55 ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
56 ["PRF","PRF ", "Reflexive Pronoun"],
57 ["PROAV","PROAV ", "Pronominal Adverb"],
58 ["PTKA","PTKA ","Particle with Adjective"],
59 ["PTKANT","PTKANT ", "Answering Particle"],
60 ["PTKNEG","PTKNEG ", "Negation Particle"],
61 ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
62 ["PTKZU","PTKZU ", "'zu' Particle"],
63 ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
64 ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
65 ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
66 ["TRUNC","TRUNC ","Truncated"],
67 ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
68 ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
69 ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
70 ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
71 ["VMFIN","VMFIN ", "Modal Finite Verb"],
72 ["VMINF","VMINF ", "Modal Infinite Verb"],
73 ["VMPP","VMPP ", "Modal Perfect Participle"],
74 ["VVFIN","VVFIN ","Finite Verb"],
75 ["VVIMP","VVIMP ", "Finite Imperative Verb"],
76 ["VVINF","VVINF ", "Infinite Verb"],
77 ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
78 ["VVPP","VVPP ", "Perfect Participle"],
79 ["XY", "XY ", "Non-Word"]
Nils Diewaldeca30442014-11-18 20:33:54 +000080];
Nils Diewald44a72782014-06-20 16:03:21 +000081
Nils Diewaldeca30442014-11-18 20:33:54 +000082var mateSttsArray = sttsArray.slice(0);
83mateSttsArray.push(
84 ["<root-POS>","<root-POS>","Root Part of Speech"]
85);
Nils Diewald44a72782014-06-20 16:03:21 +000086
Nils Diewald44a72782014-06-20 16:03:21 +000087
Nils Diewaldeca30442014-11-18 20:33:54 +000088var hintArray = {
89 "-" : [
90 ["Connexor", "cnx/"],
91 ["CoreNLP", "corenlp/"],
92 ["Mate", "mate/"],
93 ["OpenNLP", "opennlp/"],
94 ["TreeTagger", "tt/"],
95 ["Xerox Parser", "xip/"]
96 ],
97 "corenlp/" : [
98 ["Named Entity", "ne=" , "Combined"],
99 ["Named Entity", "ne_dewac_175m_600=" , "ne_dewac_175m_600"],
100 ["Named Entity", "ne_hgc_175m_600=", "ne_hgc_175m_600"]
101 ],
102 "corenlp/ne=" : namedEntities,
103 "corenlp/ne_dewac_175m_600=" : namedEntities,
104 "corenlp/ne_hgc_175m_600=" : namedEntities,
105 "cnx/" : [
106 ["Constituency", "c="],
107 ["Lemma", "l="],
108 ["Morphology", "m="],
109 ["Part-of-Speech", "p="],
110 ["Syntax", "syn="]
111 ],
112 "cnx/c=" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000113 ["np", "np ", "Nominal Phrase"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000114 ],
115 // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/morph.html
116 "cnx/m=" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000117 ["Abbr","Abbr ", "Nouns: Abbreviation"],
118 ["CMP","CMP ", "Adjective: Comparative"],
119 ["IMP", "IMP ", "Mood: Imperative"],
120 ["IND", "IND ", "Mood: Indicative"],
121 ["INF", "INF ", "Infinitive"],
122 ["ORD","ORD ", "Numeral: Ordinal"],
123 ["PAST", "PAST ", "Tense: past"],
124 ["PCP", "PCP ", "Participle"],
125 ["PERF", "PERF ", "Perfective Participle"],
126 ["PL","PL ", "Nouns: Plural"],
127 ["PRES", "PRES ", "Tense: present"],
128 ["PROG", "PROG ", "Progressive Participle"],
129 ["Prop","Prop ", "Nouns: Proper Noun"],
130 ["SUB", "SUB ", "Mood: Subjunctive"],
131 ["SUP","SUP ", "Adjective: Superlative"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000132 ],
133 // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/morph.html
134 "cnx/p=" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000135 ["A", "A ", "Adjective"],
136 ["ADV", "ADV ", "Adverb"],
137 ["CC", "CC ", "Coordination Marker"],
138 ["CS", "CS ", "Clause Marker"],
139 ["DET", "DET ", "Determiner"],
140 ["INTERJ", "INTERJ ", "Interjection"],
141 ["N", "N ", "Noun"],
142 ["NUM", "NUM ", "Numeral"],
143 ["PREP", "PREP ", "Preposition"],
144 ["PRON", "PRON ", "Pro-Nominal"],
145 ["V", "V ", "Verb"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000146 ],
147 // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/syntax.html
148 "cnx/syn=" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000149 ["@ADVL", "@ADVL ", "Adverbial Head"],
150 ["@AUX", "@AUX ", "Auxiliary Verb"],
151 ["@CC", "@CC ", "Coordination"]
152 ["@MAIN", "@MAIN ", "Main Verb"],
153 ["@NH", "@NH ", "Nominal Head"],
154 ["@POSTMOD", "@POSTMOD ", "Postmodifier"],
155 ["@PREMARK", "@PREMARK ", "Preposed Marker"],
156 ["@PREMOD", "@POSTMOD ", "Premodifier"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000157 ],
158 "opennlp/" : [
159 ["Part-of-Speech", "p="]
160 ],
161 "opennlp/p=" : sttsArray,
162 "xip/" : [
163 ["Constituency", "c="],
164 // Inactive: ["Dependency", "d="],
165 ["Lemma", "l="],
166 ["Part-of-Speech", "p="],
167 ],
168 // "xip/c=" : [],
169 // Inactive: "xip/d=" : [],
170 // "xip/p=" : [],
171 "tt/" : [
172 ["Lemma", "l="],
173 ["Part-of-Speech", "p="]
174 ],
175 "tt/p=" : sttsArray,
176 "mate/" : [
177 // Inactive: "d" : ["d=", "Dependency"],
178 ["Lemma", "l="],
179 ["Morphology", "m="],
180 ["Part-of-Speech", "p="]
181 ],
182 // Inactive: mate/d=
183 "mate/p=" : mateSttsArray,
184 "mate/m=" : [
185 ["Case", "case:"],
186 ["Degree", "degree:"],
187 ["Gender", "gender:"],
188 ["Mood", "mood:"],
189 ["Number", "number:"],
190 ["Person", "person:"],
191 ["Tense","tense:"],
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000192 ["No type", "<no-type> "]
Nils Diewaldeca30442014-11-18 20:33:54 +0000193 ],
194 "mate/m=case:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000195 ["acc", "acc ", "Accusative"],
196 ["dat","dat ", "Dative"],
197 ["gen", "gen ","Genitive"],
198 ["nom","nom ", "Nominative"],
199 ["*","* ", "Undefined"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000200 ],
201 "mate/m=degree:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000202 ["comp","comp ", "Comparative"],
203 ["pos","pos ", "Positive"],
204 ["sup","sup ", "Superative"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000205 ],
206 "mate/m=gender:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000207 ["fem", "fem ", "Feminium"],
208 ["masc", "masc ", "Masculinum"],
209 ["neut","neut ", "Neuter"],
210 ["*","* ","Undefined"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000211 ],
212 "mate/m=mood:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000213 ["imp","imp ", "Imperative"],
214 ["ind","ind ", "Indicative"],
215 ["subj","subj ", "Subjunctive"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000216 ],
217 "mate/m=number:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000218 ["pl","pl ","Plural"],
219 ["sg","sg ","Singular"],
220 ["*","* ","Undefined"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000221 ],
222 "mate/m=person:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000223 ["1","1 ", "First Person"],
224 ["2","2 ", "Second Person"],
225 ["3","3 ", "Third Person"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000226 ],
227 "mate/m=tense:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000228 ["past","past ", "Past"],
229 ["pres","pres ", "Present"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000230 ]
231};
232
233
234/**
235 * Analyze strings for prefixes
236 */
237var PrefixAnalyzer = {
238 _regex : new RegExp(
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000239 "(?:^|[^-a-zA-Z0-9])" + // Anchor
Nils Diewaldeca30442014-11-18 20:33:54 +0000240 "((?:[-a-zA-Z0-9]+?)\/" + // Foundry
241 "(?:" +
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000242 "(?:[-a-zA-Z0-9]+?)=" + // Layer
243 "(?:(?:[^:=\/ ]+?):)?" + // Key
Nils Diewaldeca30442014-11-18 20:33:54 +0000244 ")?" +
245 ")$"),
246 analyze : function (text) {
247 if (!this._regex.exec(text))
248 return undefined;
249 return RegExp.$1
250 }
251};
252
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000253
Nils Diewaldeca30442014-11-18 20:33:54 +0000254/**
255 * Event handling after a key pressed
256 */
257function updateKey (that, e) {
258 var menu = that.menu();
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000259
Nils Diewaldeca30442014-11-18 20:33:54 +0000260 switch (e.key) {
261 case 'Esc':
262 // Hide menu
263 menu.hide();
264 break;
265 case 'Down':
266 e.halt(); // No event propagation
267
268 // Menu is not active
269 if (!menu.active) {
270 that.popUp()
271 }
272 // Menu is active
273 else {
274 that.removePrefix();
275 menu.next();
Nils Diewald44a72782014-06-20 16:03:21 +0000276 };
Nils Diewald465c4252014-06-20 21:51:58 +0000277
Nils Diewaldeca30442014-11-18 20:33:54 +0000278 break;
279 case "Up":
280 if (!menu.active)
281 break;
282 e.halt(); // No event propagation
283 that.removePrefix();
284 menu.prev();
285 break;
286 case "Enter":
287 if (!menu.active)
288 break;
289 e.halt(); // No event propagation
290 that.insertText(menu.getActiveItem().getAction());
291 that.removePrefix();
Nils Diewald44a72782014-06-20 16:03:21 +0000292
Nils Diewaldeca30442014-11-18 20:33:54 +0000293 // Remove menu
294 menu.hide();
Nils Diewald44a72782014-06-20 16:03:21 +0000295
Nils Diewaldeca30442014-11-18 20:33:54 +0000296 // Fill this with the correct value
297 // Todo: This is redundant with click function
298 var show;
Nils Diewaldc1a69852014-11-19 03:06:45 +0000299 if ((show = that.analyzeContext()) != "-") {
Nils Diewaldeca30442014-11-18 20:33:54 +0000300 menu.show(show);
301 menu.update(
302 e.target.getBoundingClientRect().right
303 );
Nils Diewald44a72782014-06-20 16:03:21 +0000304 };
Nils Diewald44a72782014-06-20 16:03:21 +0000305
Nils Diewaldeca30442014-11-18 20:33:54 +0000306 break;
307 default:
308 if (!menu.active)
309 break;
Nils Diewald44a72782014-06-20 16:03:21 +0000310
Nils Diewaldeca30442014-11-18 20:33:54 +0000311 // key stroke is not a character
312 if (e.key.length != 1) {
313
314 // Key stroke is not a text modifying key
Nils Diewaldc1a69852014-11-19 03:06:45 +0000315 if (e.key != 'Shift' &&
316 e.key != 'Up' &&
317 e.key != 'Down' &&
318 e.key != 'Enter' &&
319 e.key != 'Alt' &&
320 e.key != 'AltGraph' &&
321 e.key != 'CapsLock') {
Nils Diewaldeca30442014-11-18 20:33:54 +0000322 that.insertPrefix();
323 menu.hide();
Nils Diewald44a72782014-06-20 16:03:21 +0000324 };
Nils Diewaldeca30442014-11-18 20:33:54 +0000325 break;
Nils Diewald44a72782014-06-20 16:03:21 +0000326 };
Nils Diewald7cad8402014-07-08 17:06:56 +0000327
Nils Diewaldeca30442014-11-18 20:33:54 +0000328 e.halt(); // No event propagation
329
330 // Try to identify prefix
331 if (menu.skipToPrefix(e.key))
332 break;
Nils Diewald44a72782014-06-20 16:03:21 +0000333
Nils Diewaldeca30442014-11-18 20:33:54 +0000334 // Prefix not found
335 that.insertPrefix();
336 menu.hide();
337 };
338};
339
340// new hint object
341var Hint = {
342 _search : undefined, // Return the search element
343 _mirror : undefined, // Return the mirror element
344 _menu : undefined,
345 _analyzer : undefined,
346 firstTry : true,
347 menu : function () {
348 // In case this wasn't defined yet
349 if (this._menu === undefined) {
350 this._menu = Object.create(Menu).init();
351 this._mirror.appendChild(this._menu.getElement());
352 };
353 return this._menu;
354 },
355
356 // Initialize the object
357 init : function () {
358 this._search = document.getElementById("q-field");
359 this._mirror = document.createElement("div");
360 this._mirror.setAttribute("id", "searchMirror");
361 this._mirror.appendChild(document.createElement("span"));
362 document.getElementsByTagName("body")[0].appendChild(this._mirror);
363
364 this._analyzer = Object.create(PrefixAnalyzer);
365
366 // Update positional information, in case the windows size changes
367 var that = this;
368 window.onresize = function () { that.reposition() };
369
370 // Add event listener for key pressed down
371 this._search.addEventListener(
372 "keydown",
373 function (e) {
374 updateKey(that, e)
375 },
376 false
377 );
378
379 // Reposition the mirror
380 this.reposition();
381
382 // Return object for chaining
383 return this;
384 },
385
386 // Popup method
387 popUp : function () {
388 if (this.active)
Nils Diewald44a72782014-06-20 16:03:21 +0000389 return;
390
Nils Diewaldeca30442014-11-18 20:33:54 +0000391 // Reposition hint list on first try
392 if (this.firstTry)
393 this.reposition().firstTry = false;
Nils Diewald44a72782014-06-20 16:03:21 +0000394
Nils Diewaldeca30442014-11-18 20:33:54 +0000395 // Update view
396 this.update();
Nils Diewald44a72782014-06-20 16:03:21 +0000397
Nils Diewaldeca30442014-11-18 20:33:54 +0000398 // Fill this with the correct value
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000399 if (this.menu().show(this.analyzeContext())) {
Nils Diewaldeca30442014-11-18 20:33:54 +0000400 this.update(
401 this._search.getBoundingClientRect().right
402 );
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000403 }
404 else {
Nils Diewaldc1a69852014-11-19 03:06:45 +0000405 this.menu().hide();
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000406 };
Nils Diewald44a72782014-06-20 16:03:21 +0000407
Nils Diewaldeca30442014-11-18 20:33:54 +0000408 this._search.focus();
409 },
410
411 // Reposition the mirror object
412 reposition : function () {
413
414 // Update style properties
415 var searchRect = this._search.getBoundingClientRect();
416 var searchStyle = window.getComputedStyle(this._search, null);
417 var mStyle = this._mirror.style;
418 mStyle.left = searchRect.left + "px";
419 mStyle.top = searchRect.bottom + "px";
420 mStyle.borderLeftColor = "transparent";
421 mStyle.height = "1px";
422 mStyle.paddingLeft = searchStyle.getPropertyValue("padding-left");
423 mStyle.marginLeft = searchStyle.getPropertyValue("margin-left");
424 mStyle.borderLeftWidth = searchStyle.getPropertyValue("border-left-width");
425 mStyle.borderLeftStyle = searchStyle.getPropertyValue("border-left-style");
426 mStyle.fontSize = searchStyle.getPropertyValue("font-size");
427 mStyle.fontFamily = searchStyle.getPropertyValue("font-family");
428 return this;
429 },
430
431 // Reposition the menu object
432 update : function () {
433 var s = this._search;
434 var start = s.selectionStart;
435 this._mirror.firstChild.textContent = s.value.substring(0, start);
436 },
437
438 analyzeContext : function () {
439 var context = this._splitInputText()[0];
440 if (context === undefined || context.length === 0)
441 return "-";
442 context = this._analyzer.analyze(context);
443 if (context === undefined || context.length === 0)
444 return "-";
445
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000446 if (!hintArray[context])
447 return "-";
448
Nils Diewaldeca30442014-11-18 20:33:54 +0000449 return context;
450 },
451
452 _splitInputText : function () {
453 var s = this._search;
454 var value = s.value;
455 var start = s.selectionStart;
Nils Diewald44a72782014-06-20 16:03:21 +0000456 var begin = value.substring(0, start);
Nils Diewaldeca30442014-11-18 20:33:54 +0000457 var end = value.substring(start, value.length);
458 return new Array(begin, end);
459 },
Nils Diewald44a72782014-06-20 16:03:21 +0000460
Nils Diewaldeca30442014-11-18 20:33:54 +0000461 // Insert text at the current cursor position
462 insertText : function (text) {
463 var s = this._search;
464 var splitText = this._splitInputText();
465 s.value = splitText[0] + text + splitText[1];
466 s.selectionStart = (splitText[0] + text).length
467 s.selectionEnd = s.selectionStart;
468 this._mirror.firstChild.textContent = splitText[0] + text;
469 },
Nils Diewald44a72782014-06-20 16:03:21 +0000470
Nils Diewaldeca30442014-11-18 20:33:54 +0000471 // Remove stored prefix
472 removePrefix : function () {
473 this.menu()._prefix = undefined;
474 },
Nils Diewald465c4252014-06-20 21:51:58 +0000475
Nils Diewaldeca30442014-11-18 20:33:54 +0000476 // Insert stored prefix at current cursor position
477 insertPrefix : function () {
478 if (this.menu()._prefix === undefined)
Nils Diewald44a72782014-06-20 16:03:21 +0000479 return;
Nils Diewaldeca30442014-11-18 20:33:54 +0000480 this.insertText(this.menu()._prefix);
481 }
482};
Nils Diewald44a72782014-06-20 16:03:21 +0000483
Nils Diewaldeca30442014-11-18 20:33:54 +0000484
485/**
486* Menu list
487*/
488var Menu = {
489 active : false,
490 _element : undefined,
491 _position : 0, // Position of menu item
492 _offset : 0, // Visual offset for chosen highlight
493 _size : 8, // Number of items to be shown
494 _items : [], // Items for menu
495 _name : undefined,
496 _prefix : undefined,
497 getElement : function () {
498 return this._element;
499 },
500 init : function () {
501 this._element = document.createElement("ul");
502
503 // Add onclick event
504 this._element.addEventListener("click", chooseHint, false);
505
506 this._element.style.opacity = 0;
507 this.active = false;
508 this._setDefaults();
509 return this;
510 },
511 update : function (searchRightPosition) {
512 var infoRightPosition = this._element.getBoundingClientRect().right;
513 if (infoRightPosition > searchRightPosition) {
514 this._element.style.marginLeft = '-' + (infoRightPosition - searchRightPosition) + 'px';
515 };
516 return this;
517 },
518 next : function () {
519 if (!this.active)
520 return;
521 this._clearView();
522 this._position++;
523
524 // In case the list is bigger than the view
525 if (this._items.length > this._size) {
526 if (this._position >= this._items.length) {
527 // Roll to top
528 this._offset = 0;
529 this._position = 0;
530 this._showItems(0);
531 }
532 else if (this._position >= (this._size + this._offset)) {
533 // Roll down
534 this._element.removeChild(this._element.firstChild);
535 this._offset++;
536 this._element.appendChild(this.getItem(this._position).getElement());
537 };
538 }
539 else if (this._position >= this._items.length) {
540 this._position = 0;
541 };
542 this._updateView();
543 },
544 prev : function () {
545 if (!this.active)
546 return;
547 this._clearView();
548 this._position--;
549
550 // In case the list is bigger than the view
551 if (this._items.length > this._size) {
552 if (this._position < 0) {
553 // roll to bottom
554 this._setToLast();
555 this._offset = (this._position - this._size) + 1;
556 this._showLastItems();
557 }
558 else if (this._position < this._offset) {
559 // roll up
560 this._element.removeChild(this._element.lastChild);
561 this._offset--;
562 this._element.insertBefore(
563 this.getItem(this._position).getElement(),
564 this._element.firstChild
565 );
566 };
567 }
568 else if (this._position < 0) {
569 this._setToLast();
570 };
571 this._updateView();
572 },
573 skipToPrefix : function (prefix) {
574 if (this._prefix === undefined)
575 this._prefix = prefix.toLocaleLowerCase();
576 else
577 this._prefix += prefix.toLocaleLowerCase();
578
579 var pos = 0;
580 var found = false;
581 var good = -1;
582 var test;
583 for (; pos < this._items.length; pos++) {
Nils Diewaldc1a69852014-11-19 03:06:45 +0000584 if ((test = this.getItem(pos).getLCName().indexOf(this._prefix)) != -1) {
585 if (test == 0) {
Nils Diewaldeca30442014-11-18 20:33:54 +0000586 found = true;
587 break;
588 };
589 good = pos;
Nils Diewald465c4252014-06-20 21:51:58 +0000590 };
591 };
Nils Diewaldeca30442014-11-18 20:33:54 +0000592
593 // Perfect prefix
594 if (found)
595 return this.skipToPos(pos);
596 // At least infix
Nils Diewaldc1a69852014-11-19 03:06:45 +0000597 else if (good != -1)
Nils Diewaldeca30442014-11-18 20:33:54 +0000598 return this.skipToPos(good);
599 // No
600 return false;
601 },
602 skipToPos : function (index) {
603 if (!this.active)
604 return false;
605 if (index < 0 || index >= this._items.length)
606 return false;
607
608 this._clearView();
609 this._position = index;
610
611 if (index < this._offset || index >= (this._offset + this._size)) {
612
613 // Index is in the final frame
614 if (index >= (this._items.length - this._size)) {
615 this._offset = this._items.length - this._size;
616 this._showLastItems();
617 }
618
619 // Index is in the final frame
620 else {
621 this._offset = index;
622 this._showItems(index);
623 };
624 };
625
626 // Activate new position
627 this._updateView();
628 return true;
629 },
630 show : function (name) {
631 // The element is already given
Nils Diewaldc1a69852014-11-19 03:06:45 +0000632 if (this._name != name) {
Nils Diewaldeca30442014-11-18 20:33:54 +0000633
634 // Todo: store hints in hash
635
636 // Delete items
637 this._items.length = 0;
638
639 var items = hintArray[name];
640
641 // Hints not found
642 if (items === undefined)
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000643 return undefined;
Nils Diewaldeca30442014-11-18 20:33:54 +0000644
645 var i;
646 for (i in items) {
647 var item = Object.create(MenuItem).init(items[i]);
648 this._items.push(item);
649 };
650
651 // Add classes for rolling menus
652 this.getItem(0).getElement().classList.add("no-more");
653 this.getItem(i).getElement().classList.add("no-more");
654
655 this._name = name;
656 };
657 this._showItems(0);
658 this._element.style.opacity = 1;
659 this._setDefaults();
660 this.active = true;
661 this._updateView();
662 return true;
663 },
664 hide : function () {
665 this._element.style.opacity = 0;
666 if (this.active)
667 this.getActiveItem().deactivate();
668 this._setDefaults();
669 this.active = false;
670 },
671 getActiveItem : function () {
672 return this._items[this._position];
673 },
674 getItem : function (index) {
675 return this._items[index];
676 },
677 getPrefix : function () {
678 return this._prefix;
679 },
680 _setDefaults : function () {
681 this._offset = 0;
682 this._position = 0;
683 this._prefix = undefined;
684 },
685 // Remove all visible list items
686 _deleteMenu : function () {
687 var child;
688 while (child = this._element.firstChild)
689 this._element.removeChild(child);
690 },
691 _clearView : function () {
692 var active = this.getActiveItem();
693 if (active !== undefined)
694 active.deactivate();
695 },
696 _updateView : function () {
697 var active = this.getActiveItem();
698 if (active !== undefined)
699 active.activate();
700 },
701
702 // Make all list items visible starting at a certain offset
703 _showItems : function (offset) {
704 this._deleteMenu();
705 for (var i = offset; i < this._size + offset; i++) {
706 if (i >= this._items.length)
707 break;
708 this._element.appendChild(
709 this._items[i].getElement()
710 )
711 };
712 },
713
714 // Make all final list items visible
715 _showLastItems : function () {
716 this._deleteMenu();
717 for (var i = (this._items.length - 1); i >= (this._items.length - this._size); i--) {
718 if (i < 0)
719 break;
720 if (!this._element.firstChild)
721 this._element.appendChild(this._items[i].getElement());
722 else
723 this._element.insertBefore(
724 this._items[i].getElement(),
725 this._element.firstChild
726 );
727 };
728 },
729 _setToLast : function () {
730 this._position = this._items.length - 1;
731 }
732};
733
734function chooseHint (e) {
735 var element = e.target;
736 while (element.nodeName == "STRONG" || element.nodeName == "SPAN") {
737 element = element.parentNode;
Nils Diewald465c4252014-06-20 21:51:58 +0000738 };
739
Nils Diewaldeca30442014-11-18 20:33:54 +0000740 if (element === undefined || element.nodeName != "LI")
741 return;
742
743 var action = element.getAttribute('data-action');
744 hint.insertText(action);
745 var menu = hint.menu();
746 menu.hide();
747
748 // Fill this with the correct value
749 var show;
Nils Diewaldc1a69852014-11-19 03:06:45 +0000750 if ((show = hint.analyzeContext()) != "-") {
Nils Diewaldeca30442014-11-18 20:33:54 +0000751 menu.show(show);
752 menu.update(
753 hint._search.getBoundingClientRect().right
754 );
755 };
756
757 hint._search.focus();
758};
759
760var MenuItem = {
761 _name : undefined,
762 _lcname : undefined,
763 _desc : undefined,
764 _element : undefined,
765 _action : "",
766 activate : function () {
767 this._element.classList.add("active");
768 },
769 deactivate : function () {
770 this._element.classList.remove("active");
771 },
772 // Initialize this item
773 init : function (param) {
774 this._name = param[0];
775 this._action = param[1];
776 this._lcname = this._name.toLocaleLowerCase();
777
778 if (param.length > 2) {
779 this._desc = param[2];
780 this._lcname += " " + this._desc.toLocaleLowerCase();
781 };
782
783 return this;
784 },
785
786 // Created element of this item
787 getElement : function () {
788 if (this._element !== undefined)
789 return this._element;
790
791 var li = document.createElement("li");
792
793 li.setAttribute("data-action", this._action);
794
795 var name = document.createElement("strong");
796
797 name.appendChild(document.createTextNode(this._name));
798 li.appendChild(name);
799 if (this._desc !== undefined) {
800 var desc = document.createElement("span");
801 desc.appendChild(document.createTextNode(this._desc));
802 li.appendChild(desc);
803 };
804 this._element = li;
805 return this._element;
806 },
807
808 // Name of this item
809 getName : function () {
810 return this._name;
811 },
812
813 getLCName : function () {
814 return this._lcname;
815 },
816
817 // Description of this item
818 getDesc : function () {
819 return this._desc;
820 },
821
822
823 getAction : function () {
824 return this._action;
825 }
Nils Diewald44a72782014-06-20 16:03:21 +0000826};