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