blob: 527931dd854375245cb41ca181d18c75e953196d [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 = [
18 ["I-LOC", "I-LOC", "Location"],
19 ["I-MISC", "I-MISC", "Miscellaneous"],
20 ["I-ORG", "I-ORG", "Organization"],
21 ["I-PER", "I-PER", "Person"]
22];
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 // "$.", "$(", "$,"
28 ["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"]
80];
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=" : [
113 ["np", "np", "Nominal Phrase"]
114 ],
115 // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/morph.html
116 "cnx/m=" : [
117 ["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"]
132 ],
133 // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/morph.html
134 "cnx/p=" : [
135 ["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"]
146 ],
147 // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/syntax.html
148 "cnx/syn=" : [
149 ["@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"]
157 ],
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:"],
192 ["No type", "<no-type>"]
193 ],
194 "mate/m=case:" : [
195 ["acc", "acc", "Accusative"],
196 ["dat","dat", "Dative"],
197 ["gen", "gen","Genitive"],
198 ["nom","nom", "Nominative"],
199 ["*","*", "Undefined"]
200 ],
201 "mate/m=degree:" : [
202 ["comp","comp", "Comparative"],
203 ["pos","pos", "Positive"],
204 ["sup","sup", "Superative"]
205 ],
206 "mate/m=gender:" : [
207 ["fem", "fem", "Feminium"],
208 ["masc", "masc", "Masculinum"],
209 ["neut","neut", "Neuter"],
210 ["*","*","Undefined"]
211 ],
212 "mate/m=mood:" : [
213 ["imp","imp", "Imperative"],
214 ["ind","ind", "Indicative"],
215 ["subj","subj", "Subjunctive"]
216 ],
217 "mate/m=number:" : [
218 ["pl","pl","Plural"],
219 ["sg","sg","Singular"],
220 ["*","*","Undefined"]
221 ],
222 "mate/m=person:" : [
223 ["1","1", "First Person"],
224 ["2","2", "Second Person"],
225 ["3","3", "Third Person"]
226 ],
227 "mate/m=tense:" : [
228 ["past","past", "Past"],
229 ["pres","pres", "Present"]
230 ]
231};
232
233
234/**
235 * Analyze strings for prefixes
236 */
237var PrefixAnalyzer = {
238 _regex : new RegExp(
239 "(?:^|[^a-zA-Z0-9])" + // Anchor
240 "((?:[-a-zA-Z0-9]+?)\/" + // Foundry
241 "(?:" +
242 "(?:[^=:]+?)=" + // Layer
243 "(?:(?:[^:]+?):)?" + // Key
244 ")?" +
245 ")$"),
246 analyze : function (text) {
247 if (!this._regex.exec(text))
248 return undefined;
249 return RegExp.$1
250 }
251};
252
253/**
254 * Event handling after a key pressed
255 */
256function updateKey (that, e) {
257 var menu = that.menu();
258 switch (e.key) {
259 case 'Esc':
260 // Hide menu
261 menu.hide();
262 break;
263 case 'Down':
264 e.halt(); // No event propagation
265
266 // Menu is not active
267 if (!menu.active) {
268 that.popUp()
269 }
270 // Menu is active
271 else {
272 that.removePrefix();
273 menu.next();
Nils Diewald44a72782014-06-20 16:03:21 +0000274 };
Nils Diewald465c4252014-06-20 21:51:58 +0000275
Nils Diewaldeca30442014-11-18 20:33:54 +0000276 break;
277 case "Up":
278 if (!menu.active)
279 break;
280 e.halt(); // No event propagation
281 that.removePrefix();
282 menu.prev();
283 break;
284 case "Enter":
285 if (!menu.active)
286 break;
287 e.halt(); // No event propagation
288 that.insertText(menu.getActiveItem().getAction());
289 that.removePrefix();
Nils Diewald44a72782014-06-20 16:03:21 +0000290
Nils Diewaldeca30442014-11-18 20:33:54 +0000291 // Remove menu
292 menu.hide();
Nils Diewald44a72782014-06-20 16:03:21 +0000293
Nils Diewaldeca30442014-11-18 20:33:54 +0000294 // Fill this with the correct value
295 // Todo: This is redundant with click function
296 var show;
Nils Diewaldc1a69852014-11-19 03:06:45 +0000297 if ((show = that.analyzeContext()) != "-") {
Nils Diewaldeca30442014-11-18 20:33:54 +0000298 menu.show(show);
299 menu.update(
300 e.target.getBoundingClientRect().right
301 );
Nils Diewald44a72782014-06-20 16:03:21 +0000302 };
Nils Diewald44a72782014-06-20 16:03:21 +0000303
Nils Diewaldeca30442014-11-18 20:33:54 +0000304 break;
305 default:
306 if (!menu.active)
307 break;
Nils Diewald44a72782014-06-20 16:03:21 +0000308
Nils Diewaldeca30442014-11-18 20:33:54 +0000309 // key stroke is not a character
310 if (e.key.length != 1) {
311
312 // Key stroke is not a text modifying key
Nils Diewaldc1a69852014-11-19 03:06:45 +0000313 if (e.key != 'Shift' &&
314 e.key != 'Up' &&
315 e.key != 'Down' &&
316 e.key != 'Enter' &&
317 e.key != 'Alt' &&
318 e.key != 'AltGraph' &&
319 e.key != 'CapsLock') {
Nils Diewaldeca30442014-11-18 20:33:54 +0000320 that.insertPrefix();
321 menu.hide();
Nils Diewald44a72782014-06-20 16:03:21 +0000322 };
Nils Diewaldeca30442014-11-18 20:33:54 +0000323 break;
Nils Diewald44a72782014-06-20 16:03:21 +0000324 };
Nils Diewald7cad8402014-07-08 17:06:56 +0000325
Nils Diewaldeca30442014-11-18 20:33:54 +0000326 e.halt(); // No event propagation
327
328 // Try to identify prefix
329 if (menu.skipToPrefix(e.key))
330 break;
Nils Diewald44a72782014-06-20 16:03:21 +0000331
Nils Diewaldeca30442014-11-18 20:33:54 +0000332 // Prefix not found
333 that.insertPrefix();
334 menu.hide();
335 };
336};
337
338// new hint object
339var Hint = {
340 _search : undefined, // Return the search element
341 _mirror : undefined, // Return the mirror element
342 _menu : undefined,
343 _analyzer : undefined,
344 firstTry : true,
345 menu : function () {
346 // In case this wasn't defined yet
347 if (this._menu === undefined) {
348 this._menu = Object.create(Menu).init();
349 this._mirror.appendChild(this._menu.getElement());
350 };
351 return this._menu;
352 },
353
354 // Initialize the object
355 init : function () {
356 this._search = document.getElementById("q-field");
357 this._mirror = document.createElement("div");
358 this._mirror.setAttribute("id", "searchMirror");
359 this._mirror.appendChild(document.createElement("span"));
360 document.getElementsByTagName("body")[0].appendChild(this._mirror);
361
362 this._analyzer = Object.create(PrefixAnalyzer);
363
364 // Update positional information, in case the windows size changes
365 var that = this;
366 window.onresize = function () { that.reposition() };
367
368 // Add event listener for key pressed down
369 this._search.addEventListener(
370 "keydown",
371 function (e) {
372 updateKey(that, e)
373 },
374 false
375 );
376
377 // Reposition the mirror
378 this.reposition();
379
380 // Return object for chaining
381 return this;
382 },
383
384 // Popup method
385 popUp : function () {
386 if (this.active)
Nils Diewald44a72782014-06-20 16:03:21 +0000387 return;
388
Nils Diewaldeca30442014-11-18 20:33:54 +0000389 // Reposition hint list on first try
390 if (this.firstTry)
391 this.reposition().firstTry = false;
Nils Diewald44a72782014-06-20 16:03:21 +0000392
Nils Diewaldeca30442014-11-18 20:33:54 +0000393 // Update view
394 this.update();
Nils Diewald44a72782014-06-20 16:03:21 +0000395
Nils Diewaldeca30442014-11-18 20:33:54 +0000396 // Fill this with the correct value
397 if (this.menu().show(this.analyzeContext()))
398 this.update(
399 this._search.getBoundingClientRect().right
400 );
401 else
Nils Diewaldc1a69852014-11-19 03:06:45 +0000402 this.menu().hide();
Nils Diewald44a72782014-06-20 16:03:21 +0000403
Nils Diewaldeca30442014-11-18 20:33:54 +0000404 this._search.focus();
405 },
406
407 // Reposition the mirror object
408 reposition : function () {
409
410 // Update style properties
411 var searchRect = this._search.getBoundingClientRect();
412 var searchStyle = window.getComputedStyle(this._search, null);
413 var mStyle = this._mirror.style;
414 mStyle.left = searchRect.left + "px";
415 mStyle.top = searchRect.bottom + "px";
416 mStyle.borderLeftColor = "transparent";
417 mStyle.height = "1px";
418 mStyle.paddingLeft = searchStyle.getPropertyValue("padding-left");
419 mStyle.marginLeft = searchStyle.getPropertyValue("margin-left");
420 mStyle.borderLeftWidth = searchStyle.getPropertyValue("border-left-width");
421 mStyle.borderLeftStyle = searchStyle.getPropertyValue("border-left-style");
422 mStyle.fontSize = searchStyle.getPropertyValue("font-size");
423 mStyle.fontFamily = searchStyle.getPropertyValue("font-family");
424 return this;
425 },
426
427 // Reposition the menu object
428 update : function () {
429 var s = this._search;
430 var start = s.selectionStart;
431 this._mirror.firstChild.textContent = s.value.substring(0, start);
432 },
433
434 analyzeContext : function () {
435 var context = this._splitInputText()[0];
436 if (context === undefined || context.length === 0)
437 return "-";
438 context = this._analyzer.analyze(context);
439 if (context === undefined || context.length === 0)
440 return "-";
441
442 return context;
443 },
444
445 _splitInputText : function () {
446 var s = this._search;
447 var value = s.value;
448 var start = s.selectionStart;
Nils Diewald44a72782014-06-20 16:03:21 +0000449 var begin = value.substring(0, start);
Nils Diewaldeca30442014-11-18 20:33:54 +0000450 var end = value.substring(start, value.length);
451 return new Array(begin, end);
452 },
Nils Diewald44a72782014-06-20 16:03:21 +0000453
Nils Diewaldeca30442014-11-18 20:33:54 +0000454 // Insert text at the current cursor position
455 insertText : function (text) {
456 var s = this._search;
457 var splitText = this._splitInputText();
458 s.value = splitText[0] + text + splitText[1];
459 s.selectionStart = (splitText[0] + text).length
460 s.selectionEnd = s.selectionStart;
461 this._mirror.firstChild.textContent = splitText[0] + text;
462 },
Nils Diewald44a72782014-06-20 16:03:21 +0000463
Nils Diewaldeca30442014-11-18 20:33:54 +0000464 // Remove stored prefix
465 removePrefix : function () {
466 this.menu()._prefix = undefined;
467 },
Nils Diewald465c4252014-06-20 21:51:58 +0000468
Nils Diewaldeca30442014-11-18 20:33:54 +0000469 // Insert stored prefix at current cursor position
470 insertPrefix : function () {
471 if (this.menu()._prefix === undefined)
Nils Diewald44a72782014-06-20 16:03:21 +0000472 return;
Nils Diewaldeca30442014-11-18 20:33:54 +0000473 this.insertText(this.menu()._prefix);
474 }
475};
Nils Diewald44a72782014-06-20 16:03:21 +0000476
Nils Diewaldeca30442014-11-18 20:33:54 +0000477
478/**
479* Menu list
480*/
481var Menu = {
482 active : false,
483 _element : undefined,
484 _position : 0, // Position of menu item
485 _offset : 0, // Visual offset for chosen highlight
486 _size : 8, // Number of items to be shown
487 _items : [], // Items for menu
488 _name : undefined,
489 _prefix : undefined,
490 getElement : function () {
491 return this._element;
492 },
493 init : function () {
494 this._element = document.createElement("ul");
495
496 // Add onclick event
497 this._element.addEventListener("click", chooseHint, false);
498
499 this._element.style.opacity = 0;
500 this.active = false;
501 this._setDefaults();
502 return this;
503 },
504 update : function (searchRightPosition) {
505 var infoRightPosition = this._element.getBoundingClientRect().right;
506 if (infoRightPosition > searchRightPosition) {
507 this._element.style.marginLeft = '-' + (infoRightPosition - searchRightPosition) + 'px';
508 };
509 return this;
510 },
511 next : function () {
512 if (!this.active)
513 return;
514 this._clearView();
515 this._position++;
516
517 // In case the list is bigger than the view
518 if (this._items.length > this._size) {
519 if (this._position >= this._items.length) {
520 // Roll to top
521 this._offset = 0;
522 this._position = 0;
523 this._showItems(0);
524 }
525 else if (this._position >= (this._size + this._offset)) {
526 // Roll down
527 this._element.removeChild(this._element.firstChild);
528 this._offset++;
529 this._element.appendChild(this.getItem(this._position).getElement());
530 };
531 }
532 else if (this._position >= this._items.length) {
533 this._position = 0;
534 };
535 this._updateView();
536 },
537 prev : function () {
538 if (!this.active)
539 return;
540 this._clearView();
541 this._position--;
542
543 // In case the list is bigger than the view
544 if (this._items.length > this._size) {
545 if (this._position < 0) {
546 // roll to bottom
547 this._setToLast();
548 this._offset = (this._position - this._size) + 1;
549 this._showLastItems();
550 }
551 else if (this._position < this._offset) {
552 // roll up
553 this._element.removeChild(this._element.lastChild);
554 this._offset--;
555 this._element.insertBefore(
556 this.getItem(this._position).getElement(),
557 this._element.firstChild
558 );
559 };
560 }
561 else if (this._position < 0) {
562 this._setToLast();
563 };
564 this._updateView();
565 },
566 skipToPrefix : function (prefix) {
567 if (this._prefix === undefined)
568 this._prefix = prefix.toLocaleLowerCase();
569 else
570 this._prefix += prefix.toLocaleLowerCase();
571
572 var pos = 0;
573 var found = false;
574 var good = -1;
575 var test;
576 for (; pos < this._items.length; pos++) {
Nils Diewaldc1a69852014-11-19 03:06:45 +0000577 if ((test = this.getItem(pos).getLCName().indexOf(this._prefix)) != -1) {
578 if (test == 0) {
Nils Diewaldeca30442014-11-18 20:33:54 +0000579 found = true;
580 break;
581 };
582 good = pos;
Nils Diewald465c4252014-06-20 21:51:58 +0000583 };
584 };
Nils Diewaldeca30442014-11-18 20:33:54 +0000585
586 // Perfect prefix
587 if (found)
588 return this.skipToPos(pos);
589 // At least infix
Nils Diewaldc1a69852014-11-19 03:06:45 +0000590 else if (good != -1)
Nils Diewaldeca30442014-11-18 20:33:54 +0000591 return this.skipToPos(good);
592 // No
593 return false;
594 },
595 skipToPos : function (index) {
596 if (!this.active)
597 return false;
598 if (index < 0 || index >= this._items.length)
599 return false;
600
601 this._clearView();
602 this._position = index;
603
604 if (index < this._offset || index >= (this._offset + this._size)) {
605
606 // Index is in the final frame
607 if (index >= (this._items.length - this._size)) {
608 this._offset = this._items.length - this._size;
609 this._showLastItems();
610 }
611
612 // Index is in the final frame
613 else {
614 this._offset = index;
615 this._showItems(index);
616 };
617 };
618
619 // Activate new position
620 this._updateView();
621 return true;
622 },
623 show : function (name) {
624 // The element is already given
Nils Diewaldc1a69852014-11-19 03:06:45 +0000625 if (this._name != name) {
Nils Diewaldeca30442014-11-18 20:33:54 +0000626
627 // Todo: store hints in hash
628
629 // Delete items
630 this._items.length = 0;
631
632 var items = hintArray[name];
633
634 // Hints not found
635 if (items === undefined)
636 return false;
637
638 var i;
639 for (i in items) {
640 var item = Object.create(MenuItem).init(items[i]);
641 this._items.push(item);
642 };
643
644 // Add classes for rolling menus
645 this.getItem(0).getElement().classList.add("no-more");
646 this.getItem(i).getElement().classList.add("no-more");
647
648 this._name = name;
649 };
650 this._showItems(0);
651 this._element.style.opacity = 1;
652 this._setDefaults();
653 this.active = true;
654 this._updateView();
655 return true;
656 },
657 hide : function () {
658 this._element.style.opacity = 0;
659 if (this.active)
660 this.getActiveItem().deactivate();
661 this._setDefaults();
662 this.active = false;
663 },
664 getActiveItem : function () {
665 return this._items[this._position];
666 },
667 getItem : function (index) {
668 return this._items[index];
669 },
670 getPrefix : function () {
671 return this._prefix;
672 },
673 _setDefaults : function () {
674 this._offset = 0;
675 this._position = 0;
676 this._prefix = undefined;
677 },
678 // Remove all visible list items
679 _deleteMenu : function () {
680 var child;
681 while (child = this._element.firstChild)
682 this._element.removeChild(child);
683 },
684 _clearView : function () {
685 var active = this.getActiveItem();
686 if (active !== undefined)
687 active.deactivate();
688 },
689 _updateView : function () {
690 var active = this.getActiveItem();
691 if (active !== undefined)
692 active.activate();
693 },
694
695 // Make all list items visible starting at a certain offset
696 _showItems : function (offset) {
697 this._deleteMenu();
698 for (var i = offset; i < this._size + offset; i++) {
699 if (i >= this._items.length)
700 break;
701 this._element.appendChild(
702 this._items[i].getElement()
703 )
704 };
705 },
706
707 // Make all final list items visible
708 _showLastItems : function () {
709 this._deleteMenu();
710 for (var i = (this._items.length - 1); i >= (this._items.length - this._size); i--) {
711 if (i < 0)
712 break;
713 if (!this._element.firstChild)
714 this._element.appendChild(this._items[i].getElement());
715 else
716 this._element.insertBefore(
717 this._items[i].getElement(),
718 this._element.firstChild
719 );
720 };
721 },
722 _setToLast : function () {
723 this._position = this._items.length - 1;
724 }
725};
726
727function chooseHint (e) {
728 var element = e.target;
729 while (element.nodeName == "STRONG" || element.nodeName == "SPAN") {
730 element = element.parentNode;
Nils Diewald465c4252014-06-20 21:51:58 +0000731 };
732
Nils Diewaldeca30442014-11-18 20:33:54 +0000733 if (element === undefined || element.nodeName != "LI")
734 return;
735
736 var action = element.getAttribute('data-action');
737 hint.insertText(action);
738 var menu = hint.menu();
739 menu.hide();
740
741 // Fill this with the correct value
742 var show;
Nils Diewaldc1a69852014-11-19 03:06:45 +0000743 if ((show = hint.analyzeContext()) != "-") {
Nils Diewaldeca30442014-11-18 20:33:54 +0000744 menu.show(show);
745 menu.update(
746 hint._search.getBoundingClientRect().right
747 );
748 };
749
750 hint._search.focus();
751};
752
753var MenuItem = {
754 _name : undefined,
755 _lcname : undefined,
756 _desc : undefined,
757 _element : undefined,
758 _action : "",
759 activate : function () {
760 this._element.classList.add("active");
761 },
762 deactivate : function () {
763 this._element.classList.remove("active");
764 },
765 // Initialize this item
766 init : function (param) {
767 this._name = param[0];
768 this._action = param[1];
769 this._lcname = this._name.toLocaleLowerCase();
770
771 if (param.length > 2) {
772 this._desc = param[2];
773 this._lcname += " " + this._desc.toLocaleLowerCase();
774 };
775
776 return this;
777 },
778
779 // Created element of this item
780 getElement : function () {
781 if (this._element !== undefined)
782 return this._element;
783
784 var li = document.createElement("li");
785
786 li.setAttribute("data-action", this._action);
787
788 var name = document.createElement("strong");
789
790 name.appendChild(document.createTextNode(this._name));
791 li.appendChild(name);
792 if (this._desc !== undefined) {
793 var desc = document.createElement("span");
794 desc.appendChild(document.createTextNode(this._desc));
795 li.appendChild(desc);
796 };
797 this._element = li;
798 return this._element;
799 },
800
801 // Name of this item
802 getName : function () {
803 return this._name;
804 },
805
806 getLCName : function () {
807 return this._lcname;
808 },
809
810 // Description of this item
811 getDesc : function () {
812 return this._desc;
813 },
814
815
816 getAction : function () {
817 return this._action;
818 }
Nils Diewald44a72782014-06-20 16:03:21 +0000819};