blob: 1e5f6ec7f1f908b1c00439781c26f03ab69b2616 [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/*
Nils Diewald0f77cb82014-11-19 20:24:55 +00004 * Todo:
5 * - limit the view based on prefix matches
6 * - highlight matching substrings
7 * - http://www.cryer.co.uk/resources/javascript/script20_respond_to_keypress.htm
8 */
Nils Diewaldcd571622014-11-19 00:58:59 +00009
Nils Diewaldeca30442014-11-18 20:33:54 +000010// Don't let events bubble up
11Event.prototype.halt = function () {
12 this.stopPropagation();
13 this.preventDefault();
14};
Nils Diewald44a72782014-06-20 16:03:21 +000015
Nils Diewaldeca30442014-11-18 20:33:54 +000016// http://www.nlpado.de/~sebastian/software/ner_german.shtml
17// http://www.cnts.ua.ac.be/conll2003/ner/
18var namedEntities = [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +000019 ["I-LOC", "I-LOC ", "Location"],
20 ["I-MISC", "I-MISC ", "Miscellaneous"],
21 ["I-ORG", "I-ORG ", "Organization"],
22 ["I-PER", "I-PER ", "Person"]
Nils Diewaldeca30442014-11-18 20:33:54 +000023];
Nils Diewald44a72782014-06-20 16:03:21 +000024
Nils Diewaldeca30442014-11-18 20:33:54 +000025// http://www.ids-mannheim.de/cosmas2/projekt/referenz/stts/morph.html
26// http://nachhalt.sfb632.uni-potsdam.de/owl-docu/stts.html
27var sttsArray = [
28 // "$.", "$(", "$,"
Nils Diewaldc5d9bac2014-11-19 18:10:38 +000029 ["ADJA","ADJA ", "Attributive Adjective"],
30 ["ADJD","ADJD ", "Predicative Adjective"],
31 ["ADV","ADV ", "Adverb"],
32 ["APPO","APPO ", "Postposition"],
33 ["APPR","APPR ", "Preposition"],
34 ["APPRART","APPRART ", "Preposition with Determiner"],
35 ["APZR","APZR ","Right Circumposition"],
36 ["ART","ART ", "Determiner"],
37 ["CARD","CARD ", "Cardinal Number"],
38 ["FM","FM ", "Foreign Material"],
39 ["ITJ","ITJ ", "Interjection"],
40 ["KOKOM","KOKOM ", "Comparison Particle"],
41 ["KON","KON ", "Coordinating Conjuncion"],
42 ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
43 ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
44 ["NE","NE ", "Named Entity"],
45 ["NN","NN ", "Normal Nomina"],
46 ["PAV", "PAV ", "Pronominal Adverb"],
47 ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
48 ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
49 ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
50 ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
51 ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
52 ["PPER","PPER ", "Personal Pronoun"],
53 ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
54 ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
55 ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
56 ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
57 ["PRF","PRF ", "Reflexive Pronoun"],
58 ["PROAV","PROAV ", "Pronominal Adverb"],
59 ["PTKA","PTKA ","Particle with Adjective"],
60 ["PTKANT","PTKANT ", "Answering Particle"],
61 ["PTKNEG","PTKNEG ", "Negation Particle"],
62 ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
63 ["PTKZU","PTKZU ", "'zu' Particle"],
64 ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
65 ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
66 ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
67 ["TRUNC","TRUNC ","Truncated"],
68 ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
69 ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
70 ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
71 ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
72 ["VMFIN","VMFIN ", "Modal Finite Verb"],
73 ["VMINF","VMINF ", "Modal Infinite Verb"],
74 ["VMPP","VMPP ", "Modal Perfect Participle"],
75 ["VVFIN","VVFIN ","Finite Verb"],
76 ["VVIMP","VVIMP ", "Finite Imperative Verb"],
77 ["VVINF","VVINF ", "Infinite Verb"],
78 ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
79 ["VVPP","VVPP ", "Perfect Participle"],
80 ["XY", "XY ", "Non-Word"]
Nils Diewaldeca30442014-11-18 20:33:54 +000081];
Nils Diewald44a72782014-06-20 16:03:21 +000082
Nils Diewaldeca30442014-11-18 20:33:54 +000083var mateSttsArray = sttsArray.slice(0);
84mateSttsArray.push(
85 ["<root-POS>","<root-POS>","Root Part of Speech"]
86);
Nils Diewald44a72782014-06-20 16:03:21 +000087
Nils Diewald44a72782014-06-20 16:03:21 +000088
Nils Diewaldeca30442014-11-18 20:33:54 +000089var hintArray = {
90 "-" : [
91 ["Connexor", "cnx/"],
92 ["CoreNLP", "corenlp/"],
93 ["Mate", "mate/"],
94 ["OpenNLP", "opennlp/"],
95 ["TreeTagger", "tt/"],
96 ["Xerox Parser", "xip/"]
97 ],
98 "corenlp/" : [
99 ["Named Entity", "ne=" , "Combined"],
100 ["Named Entity", "ne_dewac_175m_600=" , "ne_dewac_175m_600"],
101 ["Named Entity", "ne_hgc_175m_600=", "ne_hgc_175m_600"]
102 ],
103 "corenlp/ne=" : namedEntities,
104 "corenlp/ne_dewac_175m_600=" : namedEntities,
105 "corenlp/ne_hgc_175m_600=" : namedEntities,
106 "cnx/" : [
107 ["Constituency", "c="],
108 ["Lemma", "l="],
109 ["Morphology", "m="],
110 ["Part-of-Speech", "p="],
111 ["Syntax", "syn="]
112 ],
113 "cnx/c=" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000114 ["np", "np ", "Nominal Phrase"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000115 ],
116 // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/morph.html
117 "cnx/m=" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000118 ["Abbr","Abbr ", "Nouns: Abbreviation"],
119 ["CMP","CMP ", "Adjective: Comparative"],
120 ["IMP", "IMP ", "Mood: Imperative"],
121 ["IND", "IND ", "Mood: Indicative"],
122 ["INF", "INF ", "Infinitive"],
123 ["ORD","ORD ", "Numeral: Ordinal"],
124 ["PAST", "PAST ", "Tense: past"],
125 ["PCP", "PCP ", "Participle"],
126 ["PERF", "PERF ", "Perfective Participle"],
127 ["PL","PL ", "Nouns: Plural"],
128 ["PRES", "PRES ", "Tense: present"],
129 ["PROG", "PROG ", "Progressive Participle"],
130 ["Prop","Prop ", "Nouns: Proper Noun"],
131 ["SUB", "SUB ", "Mood: Subjunctive"],
132 ["SUP","SUP ", "Adjective: Superlative"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000133 ],
134 // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/morph.html
135 "cnx/p=" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000136 ["A", "A ", "Adjective"],
137 ["ADV", "ADV ", "Adverb"],
138 ["CC", "CC ", "Coordination Marker"],
139 ["CS", "CS ", "Clause Marker"],
140 ["DET", "DET ", "Determiner"],
141 ["INTERJ", "INTERJ ", "Interjection"],
142 ["N", "N ", "Noun"],
143 ["NUM", "NUM ", "Numeral"],
144 ["PREP", "PREP ", "Preposition"],
145 ["PRON", "PRON ", "Pro-Nominal"],
146 ["V", "V ", "Verb"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000147 ],
148 // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/syntax.html
149 "cnx/syn=" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000150 ["@ADVL", "@ADVL ", "Adverbial Head"],
151 ["@AUX", "@AUX ", "Auxiliary Verb"],
152 ["@CC", "@CC ", "Coordination"]
153 ["@MAIN", "@MAIN ", "Main Verb"],
154 ["@NH", "@NH ", "Nominal Head"],
155 ["@POSTMOD", "@POSTMOD ", "Postmodifier"],
156 ["@PREMARK", "@PREMARK ", "Preposed Marker"],
157 ["@PREMOD", "@POSTMOD ", "Premodifier"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000158 ],
159 "opennlp/" : [
160 ["Part-of-Speech", "p="]
161 ],
162 "opennlp/p=" : sttsArray,
163 "xip/" : [
164 ["Constituency", "c="],
165 // Inactive: ["Dependency", "d="],
166 ["Lemma", "l="],
167 ["Part-of-Speech", "p="],
168 ],
169 // "xip/c=" : [],
170 // Inactive: "xip/d=" : [],
171 // "xip/p=" : [],
172 "tt/" : [
173 ["Lemma", "l="],
174 ["Part-of-Speech", "p="]
175 ],
176 "tt/p=" : sttsArray,
177 "mate/" : [
178 // Inactive: "d" : ["d=", "Dependency"],
179 ["Lemma", "l="],
180 ["Morphology", "m="],
181 ["Part-of-Speech", "p="]
182 ],
183 // Inactive: mate/d=
184 "mate/p=" : mateSttsArray,
185 "mate/m=" : [
186 ["Case", "case:"],
187 ["Degree", "degree:"],
188 ["Gender", "gender:"],
189 ["Mood", "mood:"],
190 ["Number", "number:"],
191 ["Person", "person:"],
192 ["Tense","tense:"],
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000193 ["No type", "<no-type> "]
Nils Diewaldeca30442014-11-18 20:33:54 +0000194 ],
195 "mate/m=case:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000196 ["acc", "acc ", "Accusative"],
197 ["dat","dat ", "Dative"],
198 ["gen", "gen ","Genitive"],
199 ["nom","nom ", "Nominative"],
200 ["*","* ", "Undefined"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000201 ],
202 "mate/m=degree:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000203 ["comp","comp ", "Comparative"],
204 ["pos","pos ", "Positive"],
205 ["sup","sup ", "Superative"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000206 ],
207 "mate/m=gender:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000208 ["fem", "fem ", "Feminium"],
209 ["masc", "masc ", "Masculinum"],
210 ["neut","neut ", "Neuter"],
211 ["*","* ","Undefined"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000212 ],
213 "mate/m=mood:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000214 ["imp","imp ", "Imperative"],
215 ["ind","ind ", "Indicative"],
216 ["subj","subj ", "Subjunctive"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000217 ],
218 "mate/m=number:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000219 ["pl","pl ","Plural"],
220 ["sg","sg ","Singular"],
221 ["*","* ","Undefined"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000222 ],
223 "mate/m=person:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000224 ["1","1 ", "First Person"],
225 ["2","2 ", "Second Person"],
226 ["3","3 ", "Third Person"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000227 ],
228 "mate/m=tense:" : [
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000229 ["past","past ", "Past"],
230 ["pres","pres ", "Present"]
Nils Diewaldeca30442014-11-18 20:33:54 +0000231 ]
232};
233
234
235/**
236 * Analyze strings for prefixes
237 */
238var PrefixAnalyzer = {
239 _regex : new RegExp(
Nils Diewald0f77cb82014-11-19 20:24:55 +0000240 "(?:^|[^-_a-zA-Z0-9])" + // Anchor
241 "((?:[-_a-zA-Z0-9]+?)\/" + // Foundry
Nils Diewaldeca30442014-11-18 20:33:54 +0000242 "(?:" +
Nils Diewald0f77cb82014-11-19 20:24:55 +0000243 "(?:[-_a-zA-Z0-9]+?)=" + // Layer
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000244 "(?:(?:[^:=\/ ]+?):)?" + // Key
Nils Diewaldeca30442014-11-18 20:33:54 +0000245 ")?" +
246 ")$"),
247 analyze : function (text) {
248 if (!this._regex.exec(text))
249 return undefined;
250 return RegExp.$1
251 }
252};
253
Nils Diewald0f77cb82014-11-19 20:24:55 +0000254function codeFromEvent (e) {
255 if ((e.charCode) && (e.keyCode==0))
256 return e.charCode
257 return e.keyCode;
258};
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000259
Nils Diewald0f77cb82014-11-19 20:24:55 +0000260
261/*
262 * Event handling after a key is down
263 * for arrows!
Nils Diewaldeca30442014-11-18 20:33:54 +0000264 */
Nils Diewald0f77cb82014-11-19 20:24:55 +0000265function updateKeyDown (that, e) {
266 var code = codeFromEvent(e);
Nils Diewaldeca30442014-11-18 20:33:54 +0000267 var menu = that.menu();
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000268
Nils Diewald0f77cb82014-11-19 20:24:55 +0000269 /*
270 * keyCodes:
271 * - Down = 40
272 * - Esc = 27
273 * - Up = 38
274 * - Enter = 13
275 * - shift = 16
276 * for characters use e.key
277 */
278 switch (code) {
279 case 27: // 'Esc'
Nils Diewaldeca30442014-11-18 20:33:54 +0000280 // Hide menu
281 menu.hide();
282 break;
Nils Diewald0f77cb82014-11-19 20:24:55 +0000283 case 40: // 'Down'
Nils Diewaldeca30442014-11-18 20:33:54 +0000284 e.halt(); // No event propagation
285
286 // Menu is not active
287 if (!menu.active) {
288 that.popUp()
289 }
290 // Menu is active
291 else {
292 that.removePrefix();
293 menu.next();
Nils Diewald44a72782014-06-20 16:03:21 +0000294 };
Nils Diewald465c4252014-06-20 21:51:58 +0000295
Nils Diewaldeca30442014-11-18 20:33:54 +0000296 break;
Nils Diewald0f77cb82014-11-19 20:24:55 +0000297 case 38: // "Up"
Nils Diewaldeca30442014-11-18 20:33:54 +0000298 if (!menu.active)
299 break;
300 e.halt(); // No event propagation
301 that.removePrefix();
302 menu.prev();
303 break;
Nils Diewald0f77cb82014-11-19 20:24:55 +0000304 case 13: // "Enter"
Nils Diewaldeca30442014-11-18 20:33:54 +0000305 if (!menu.active)
306 break;
307 e.halt(); // No event propagation
308 that.insertText(menu.getActiveItem().getAction());
309 that.removePrefix();
Nils Diewald0f77cb82014-11-19 20:24:55 +0000310
Nils Diewaldeca30442014-11-18 20:33:54 +0000311 // Remove menu
312 menu.hide();
Nils Diewald44a72782014-06-20 16:03:21 +0000313
Nils Diewaldeca30442014-11-18 20:33:54 +0000314 // Fill this with the correct value
315 // Todo: This is redundant with click function
316 var show;
Nils Diewaldc1a69852014-11-19 03:06:45 +0000317 if ((show = that.analyzeContext()) != "-") {
Nils Diewaldeca30442014-11-18 20:33:54 +0000318 menu.show(show);
319 menu.update(
320 e.target.getBoundingClientRect().right
321 );
Nils Diewald44a72782014-06-20 16:03:21 +0000322 };
Nils Diewald0f77cb82014-11-19 20:24:55 +0000323
Nils Diewaldeca30442014-11-18 20:33:54 +0000324 break;
325 default:
326 if (!menu.active)
Nils Diewald0f77cb82014-11-19 20:24:55 +0000327 return;
Nils Diewald44a72782014-06-20 16:03:21 +0000328
Nils Diewald0f77cb82014-11-19 20:24:55 +0000329 // Surpress propagation in firefox
330 if (e.key !== undefined && e.key.length != 1) {
331 menu.hide();
Nils Diewald44a72782014-06-20 16:03:21 +0000332 };
Nils Diewaldeca30442014-11-18 20:33:54 +0000333 };
334};
335
Nils Diewald0f77cb82014-11-19 20:24:55 +0000336/**
337 * Event handling after a key pressed
338 * for characters!
339 */
340function updateKeyPress (that, e) {
341 var character = String.fromCharCode(codeFromEvent(e));
342 var menu = that.menu();
343
344 if (!menu.active)
345 return;
346
347 e.halt(); // No event propagation
348
349 // Try to identify prefix
350 if (menu.skipToPrefix(character))
351 return;
352
353 // Prefix not found
354 that.insertPrefix();
355 menu.hide();
356};
357
358
359
Nils Diewaldeca30442014-11-18 20:33:54 +0000360// new hint object
361var Hint = {
362 _search : undefined, // Return the search element
363 _mirror : undefined, // Return the mirror element
364 _menu : undefined,
365 _analyzer : undefined,
366 firstTry : true,
367 menu : function () {
368 // In case this wasn't defined yet
369 if (this._menu === undefined) {
370 this._menu = Object.create(Menu).init();
371 this._mirror.appendChild(this._menu.getElement());
372 };
373 return this._menu;
374 },
375
376 // Initialize the object
377 init : function () {
378 this._search = document.getElementById("q-field");
379 this._mirror = document.createElement("div");
380 this._mirror.setAttribute("id", "searchMirror");
381 this._mirror.appendChild(document.createElement("span"));
382 document.getElementsByTagName("body")[0].appendChild(this._mirror);
383
384 this._analyzer = Object.create(PrefixAnalyzer);
385
386 // Update positional information, in case the windows size changes
387 var that = this;
388 window.onresize = function () { that.reposition() };
389
390 // Add event listener for key pressed down
391 this._search.addEventListener(
Nils Diewald0f77cb82014-11-19 20:24:55 +0000392 "keypress",
393 function (e) {
394 updateKeyPress(that, e)
395 },
396 false
397 );
398
399 // Add event listener for key pressed down
400 this._search.addEventListener(
Nils Diewaldeca30442014-11-18 20:33:54 +0000401 "keydown",
402 function (e) {
Nils Diewald0f77cb82014-11-19 20:24:55 +0000403 updateKeyDown(that, e)
Nils Diewaldeca30442014-11-18 20:33:54 +0000404 },
405 false
406 );
407
408 // Reposition the mirror
409 this.reposition();
410
411 // Return object for chaining
412 return this;
413 },
414
415 // Popup method
416 popUp : function () {
417 if (this.active)
Nils Diewald44a72782014-06-20 16:03:21 +0000418 return;
419
Nils Diewaldeca30442014-11-18 20:33:54 +0000420 // Reposition hint list on first try
421 if (this.firstTry)
422 this.reposition().firstTry = false;
Nils Diewald44a72782014-06-20 16:03:21 +0000423
Nils Diewaldeca30442014-11-18 20:33:54 +0000424 // Update view
425 this.update();
Nils Diewald44a72782014-06-20 16:03:21 +0000426
Nils Diewaldeca30442014-11-18 20:33:54 +0000427 // Fill this with the correct value
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000428 if (this.menu().show(this.analyzeContext())) {
Nils Diewaldeca30442014-11-18 20:33:54 +0000429 this.update(
430 this._search.getBoundingClientRect().right
431 );
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000432 }
433 else {
Nils Diewaldc1a69852014-11-19 03:06:45 +0000434 this.menu().hide();
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000435 };
Nils Diewald44a72782014-06-20 16:03:21 +0000436
Nils Diewaldeca30442014-11-18 20:33:54 +0000437 this._search.focus();
438 },
439
440 // Reposition the mirror object
441 reposition : function () {
442
443 // Update style properties
444 var searchRect = this._search.getBoundingClientRect();
445 var searchStyle = window.getComputedStyle(this._search, null);
446 var mStyle = this._mirror.style;
447 mStyle.left = searchRect.left + "px";
448 mStyle.top = searchRect.bottom + "px";
449 mStyle.borderLeftColor = "transparent";
450 mStyle.height = "1px";
451 mStyle.paddingLeft = searchStyle.getPropertyValue("padding-left");
452 mStyle.marginLeft = searchStyle.getPropertyValue("margin-left");
453 mStyle.borderLeftWidth = searchStyle.getPropertyValue("border-left-width");
454 mStyle.borderLeftStyle = searchStyle.getPropertyValue("border-left-style");
455 mStyle.fontSize = searchStyle.getPropertyValue("font-size");
456 mStyle.fontFamily = searchStyle.getPropertyValue("font-family");
457 return this;
458 },
459
460 // Reposition the menu object
461 update : function () {
462 var s = this._search;
463 var start = s.selectionStart;
464 this._mirror.firstChild.textContent = s.value.substring(0, start);
465 },
466
467 analyzeContext : function () {
468 var context = this._splitInputText()[0];
469 if (context === undefined || context.length === 0)
470 return "-";
471 context = this._analyzer.analyze(context);
472 if (context === undefined || context.length === 0)
473 return "-";
474
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000475 if (!hintArray[context])
476 return "-";
477
Nils Diewaldeca30442014-11-18 20:33:54 +0000478 return context;
479 },
480
481 _splitInputText : function () {
482 var s = this._search;
483 var value = s.value;
484 var start = s.selectionStart;
Nils Diewald44a72782014-06-20 16:03:21 +0000485 var begin = value.substring(0, start);
Nils Diewaldeca30442014-11-18 20:33:54 +0000486 var end = value.substring(start, value.length);
487 return new Array(begin, end);
488 },
Nils Diewald44a72782014-06-20 16:03:21 +0000489
Nils Diewaldeca30442014-11-18 20:33:54 +0000490 // Insert text at the current cursor position
491 insertText : function (text) {
492 var s = this._search;
493 var splitText = this._splitInputText();
494 s.value = splitText[0] + text + splitText[1];
495 s.selectionStart = (splitText[0] + text).length
496 s.selectionEnd = s.selectionStart;
497 this._mirror.firstChild.textContent = splitText[0] + text;
498 },
Nils Diewald44a72782014-06-20 16:03:21 +0000499
Nils Diewaldeca30442014-11-18 20:33:54 +0000500 // Remove stored prefix
501 removePrefix : function () {
502 this.menu()._prefix = undefined;
503 },
Nils Diewald465c4252014-06-20 21:51:58 +0000504
Nils Diewaldeca30442014-11-18 20:33:54 +0000505 // Insert stored prefix at current cursor position
506 insertPrefix : function () {
507 if (this.menu()._prefix === undefined)
Nils Diewald44a72782014-06-20 16:03:21 +0000508 return;
Nils Diewaldeca30442014-11-18 20:33:54 +0000509 this.insertText(this.menu()._prefix);
510 }
511};
Nils Diewald44a72782014-06-20 16:03:21 +0000512
Nils Diewaldeca30442014-11-18 20:33:54 +0000513
514/**
515* Menu list
516*/
517var Menu = {
518 active : false,
519 _element : undefined,
520 _position : 0, // Position of menu item
521 _offset : 0, // Visual offset for chosen highlight
522 _size : 8, // Number of items to be shown
523 _items : [], // Items for menu
524 _name : undefined,
525 _prefix : undefined,
526 getElement : function () {
527 return this._element;
528 },
529 init : function () {
530 this._element = document.createElement("ul");
531
532 // Add onclick event
533 this._element.addEventListener("click", chooseHint, false);
534
535 this._element.style.opacity = 0;
536 this.active = false;
537 this._setDefaults();
538 return this;
539 },
540 update : function (searchRightPosition) {
541 var infoRightPosition = this._element.getBoundingClientRect().right;
542 if (infoRightPosition > searchRightPosition) {
543 this._element.style.marginLeft = '-' + (infoRightPosition - searchRightPosition) + 'px';
544 };
545 return this;
546 },
547 next : function () {
548 if (!this.active)
549 return;
550 this._clearView();
551 this._position++;
552
553 // In case the list is bigger than the view
554 if (this._items.length > this._size) {
555 if (this._position >= this._items.length) {
556 // Roll to top
557 this._offset = 0;
558 this._position = 0;
559 this._showItems(0);
560 }
561 else if (this._position >= (this._size + this._offset)) {
562 // Roll down
563 this._element.removeChild(this._element.firstChild);
564 this._offset++;
565 this._element.appendChild(this.getItem(this._position).getElement());
566 };
567 }
568 else if (this._position >= this._items.length) {
569 this._position = 0;
570 };
571 this._updateView();
572 },
573 prev : function () {
574 if (!this.active)
575 return;
576 this._clearView();
577 this._position--;
578
579 // In case the list is bigger than the view
580 if (this._items.length > this._size) {
581 if (this._position < 0) {
582 // roll to bottom
583 this._setToLast();
584 this._offset = (this._position - this._size) + 1;
585 this._showLastItems();
586 }
587 else if (this._position < this._offset) {
588 // roll up
589 this._element.removeChild(this._element.lastChild);
590 this._offset--;
591 this._element.insertBefore(
592 this.getItem(this._position).getElement(),
593 this._element.firstChild
594 );
595 };
596 }
597 else if (this._position < 0) {
598 this._setToLast();
599 };
600 this._updateView();
601 },
602 skipToPrefix : function (prefix) {
603 if (this._prefix === undefined)
604 this._prefix = prefix.toLocaleLowerCase();
605 else
606 this._prefix += prefix.toLocaleLowerCase();
607
608 var pos = 0;
609 var found = false;
610 var good = -1;
611 var test;
612 for (; pos < this._items.length; pos++) {
Nils Diewaldc1a69852014-11-19 03:06:45 +0000613 if ((test = this.getItem(pos).getLCName().indexOf(this._prefix)) != -1) {
614 if (test == 0) {
Nils Diewaldeca30442014-11-18 20:33:54 +0000615 found = true;
616 break;
617 };
618 good = pos;
Nils Diewald465c4252014-06-20 21:51:58 +0000619 };
620 };
Nils Diewaldeca30442014-11-18 20:33:54 +0000621
622 // Perfect prefix
623 if (found)
624 return this.skipToPos(pos);
625 // At least infix
Nils Diewaldc1a69852014-11-19 03:06:45 +0000626 else if (good != -1)
Nils Diewaldeca30442014-11-18 20:33:54 +0000627 return this.skipToPos(good);
628 // No
629 return false;
630 },
631 skipToPos : function (index) {
632 if (!this.active)
633 return false;
634 if (index < 0 || index >= this._items.length)
635 return false;
636
637 this._clearView();
638 this._position = index;
639
640 if (index < this._offset || index >= (this._offset + this._size)) {
641
642 // Index is in the final frame
643 if (index >= (this._items.length - this._size)) {
644 this._offset = this._items.length - this._size;
645 this._showLastItems();
646 }
647
648 // Index is in the final frame
649 else {
650 this._offset = index;
651 this._showItems(index);
652 };
653 };
654
655 // Activate new position
656 this._updateView();
657 return true;
658 },
659 show : function (name) {
660 // The element is already given
Nils Diewaldc1a69852014-11-19 03:06:45 +0000661 if (this._name != name) {
Nils Diewaldeca30442014-11-18 20:33:54 +0000662
663 // Todo: store hints in hash
664
665 // Delete items
666 this._items.length = 0;
667
668 var items = hintArray[name];
669
670 // Hints not found
671 if (items === undefined)
Nils Diewaldc5d9bac2014-11-19 18:10:38 +0000672 return undefined;
Nils Diewaldeca30442014-11-18 20:33:54 +0000673
674 var i;
675 for (i in items) {
676 var item = Object.create(MenuItem).init(items[i]);
677 this._items.push(item);
678 };
679
680 // Add classes for rolling menus
681 this.getItem(0).getElement().classList.add("no-more");
682 this.getItem(i).getElement().classList.add("no-more");
683
684 this._name = name;
685 };
686 this._showItems(0);
687 this._element.style.opacity = 1;
688 this._setDefaults();
689 this.active = true;
690 this._updateView();
691 return true;
692 },
693 hide : function () {
694 this._element.style.opacity = 0;
695 if (this.active)
696 this.getActiveItem().deactivate();
697 this._setDefaults();
698 this.active = false;
699 },
700 getActiveItem : function () {
701 return this._items[this._position];
702 },
703 getItem : function (index) {
704 return this._items[index];
705 },
706 getPrefix : function () {
707 return this._prefix;
708 },
709 _setDefaults : function () {
710 this._offset = 0;
711 this._position = 0;
712 this._prefix = undefined;
713 },
714 // Remove all visible list items
715 _deleteMenu : function () {
716 var child;
717 while (child = this._element.firstChild)
718 this._element.removeChild(child);
719 },
720 _clearView : function () {
721 var active = this.getActiveItem();
722 if (active !== undefined)
723 active.deactivate();
724 },
725 _updateView : function () {
726 var active = this.getActiveItem();
727 if (active !== undefined)
728 active.activate();
729 },
730
731 // Make all list items visible starting at a certain offset
732 _showItems : function (offset) {
733 this._deleteMenu();
734 for (var i = offset; i < this._size + offset; i++) {
735 if (i >= this._items.length)
736 break;
737 this._element.appendChild(
738 this._items[i].getElement()
739 )
740 };
741 },
742
743 // Make all final list items visible
744 _showLastItems : function () {
745 this._deleteMenu();
746 for (var i = (this._items.length - 1); i >= (this._items.length - this._size); i--) {
747 if (i < 0)
748 break;
749 if (!this._element.firstChild)
750 this._element.appendChild(this._items[i].getElement());
751 else
752 this._element.insertBefore(
753 this._items[i].getElement(),
754 this._element.firstChild
755 );
756 };
757 },
758 _setToLast : function () {
759 this._position = this._items.length - 1;
760 }
761};
762
763function chooseHint (e) {
764 var element = e.target;
765 while (element.nodeName == "STRONG" || element.nodeName == "SPAN") {
766 element = element.parentNode;
Nils Diewald465c4252014-06-20 21:51:58 +0000767 };
768
Nils Diewaldeca30442014-11-18 20:33:54 +0000769 if (element === undefined || element.nodeName != "LI")
770 return;
771
772 var action = element.getAttribute('data-action');
773 hint.insertText(action);
774 var menu = hint.menu();
775 menu.hide();
776
777 // Fill this with the correct value
778 var show;
Nils Diewaldc1a69852014-11-19 03:06:45 +0000779 if ((show = hint.analyzeContext()) != "-") {
Nils Diewaldeca30442014-11-18 20:33:54 +0000780 menu.show(show);
781 menu.update(
782 hint._search.getBoundingClientRect().right
783 );
784 };
785
786 hint._search.focus();
787};
788
789var MenuItem = {
790 _name : undefined,
791 _lcname : undefined,
792 _desc : undefined,
793 _element : undefined,
794 _action : "",
795 activate : function () {
796 this._element.classList.add("active");
797 },
798 deactivate : function () {
799 this._element.classList.remove("active");
800 },
801 // Initialize this item
802 init : function (param) {
803 this._name = param[0];
804 this._action = param[1];
805 this._lcname = this._name.toLocaleLowerCase();
806
807 if (param.length > 2) {
808 this._desc = param[2];
809 this._lcname += " " + this._desc.toLocaleLowerCase();
810 };
811
812 return this;
813 },
814
815 // Created element of this item
816 getElement : function () {
817 if (this._element !== undefined)
818 return this._element;
819
820 var li = document.createElement("li");
821
822 li.setAttribute("data-action", this._action);
823
824 var name = document.createElement("strong");
825
826 name.appendChild(document.createTextNode(this._name));
827 li.appendChild(name);
828 if (this._desc !== undefined) {
829 var desc = document.createElement("span");
830 desc.appendChild(document.createTextNode(this._desc));
831 li.appendChild(desc);
832 };
833 this._element = li;
834 return this._element;
835 },
836
837 // Name of this item
838 getName : function () {
839 return this._name;
840 },
841
842 getLCName : function () {
843 return this._lcname;
844 },
845
846 // Description of this item
847 getDesc : function () {
848 return this._desc;
849 },
850
851
852 getAction : function () {
853 return this._action;
854 }
Nils Diewald44a72782014-06-20 16:03:21 +0000855};