| "use strict"; |
| |
| /* |
| Todo: |
| - limit the view based on prefix matches |
| - highlight matching substrings |
| */ |
| |
| // Don't let events bubble up |
| Event.prototype.halt = function () { |
| this.stopPropagation(); |
| this.preventDefault(); |
| }; |
| |
| // http://www.nlpado.de/~sebastian/software/ner_german.shtml |
| // http://www.cnts.ua.ac.be/conll2003/ner/ |
| var namedEntities = [ |
| ["I-LOC", "I-LOC ", "Location"], |
| ["I-MISC", "I-MISC ", "Miscellaneous"], |
| ["I-ORG", "I-ORG ", "Organization"], |
| ["I-PER", "I-PER ", "Person"] |
| ]; |
| |
| // http://www.ids-mannheim.de/cosmas2/projekt/referenz/stts/morph.html |
| // http://nachhalt.sfb632.uni-potsdam.de/owl-docu/stts.html |
| var sttsArray = [ |
| // "$.", "$(", "$," |
| ["ADJA","ADJA ", "Attributive Adjective"], |
| ["ADJD","ADJD ", "Predicative Adjective"], |
| ["ADV","ADV ", "Adverb"], |
| ["APPO","APPO ", "Postposition"], |
| ["APPR","APPR ", "Preposition"], |
| ["APPRART","APPRART ", "Preposition with Determiner"], |
| ["APZR","APZR ","Right Circumposition"], |
| ["ART","ART ", "Determiner"], |
| ["CARD","CARD ", "Cardinal Number"], |
| ["FM","FM ", "Foreign Material"], |
| ["ITJ","ITJ ", "Interjection"], |
| ["KOKOM","KOKOM ", "Comparison Particle"], |
| ["KON","KON ", "Coordinating Conjuncion"], |
| ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"], |
| ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"], |
| ["NE","NE ", "Named Entity"], |
| ["NN","NN ", "Normal Nomina"], |
| ["PAV", "PAV ", "Pronominal Adverb"], |
| ["PDAT","PDAT ","Attributive Demonstrative Pronoun"], |
| ["PDS","PDS ", "Substitutive Demonstrative Pronoun"], |
| ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"], |
| ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"], |
| ["PIS","PIS ", "Substitutive Indefinite Pronoun"], |
| ["PPER","PPER ", "Personal Pronoun"], |
| ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"], |
| ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"], |
| ["PRELAT","PRELAT ", "Attributive Relative Pronoun"], |
| ["PRELS","PRELS ", "Substitutive Relative Pronoun"], |
| ["PRF","PRF ", "Reflexive Pronoun"], |
| ["PROAV","PROAV ", "Pronominal Adverb"], |
| ["PTKA","PTKA ","Particle with Adjective"], |
| ["PTKANT","PTKANT ", "Answering Particle"], |
| ["PTKNEG","PTKNEG ", "Negation Particle"], |
| ["PTKVZ","PTKVZ ", "Separated Verbal Particle"], |
| ["PTKZU","PTKZU ", "'zu' Particle"], |
| ["PWAT","PWAT ", "Attributive Interrogative Pronoun"], |
| ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"], |
| ["PWS","PWS ", "Substitutive Interrogative Pronoun"], |
| ["TRUNC","TRUNC ","Truncated"], |
| ["VAFIN","VAFIN ", "Auxiliary Finite Verb"], |
| ["VAINF","VAINF ", "Auxiliary Infinite Verb"], |
| ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"], |
| ["VAPP","VAPP ", "Auxiliary Perfect Participle"], |
| ["VMFIN","VMFIN ", "Modal Finite Verb"], |
| ["VMINF","VMINF ", "Modal Infinite Verb"], |
| ["VMPP","VMPP ", "Modal Perfect Participle"], |
| ["VVFIN","VVFIN ","Finite Verb"], |
| ["VVIMP","VVIMP ", "Finite Imperative Verb"], |
| ["VVINF","VVINF ", "Infinite Verb"], |
| ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"], |
| ["VVPP","VVPP ", "Perfect Participle"], |
| ["XY", "XY ", "Non-Word"] |
| ]; |
| |
| var mateSttsArray = sttsArray.slice(0); |
| mateSttsArray.push( |
| ["<root-POS>","<root-POS>","Root Part of Speech"] |
| ); |
| |
| |
| var hintArray = { |
| "-" : [ |
| ["Connexor", "cnx/"], |
| ["CoreNLP", "corenlp/"], |
| ["Mate", "mate/"], |
| ["OpenNLP", "opennlp/"], |
| ["TreeTagger", "tt/"], |
| ["Xerox Parser", "xip/"] |
| ], |
| "corenlp/" : [ |
| ["Named Entity", "ne=" , "Combined"], |
| ["Named Entity", "ne_dewac_175m_600=" , "ne_dewac_175m_600"], |
| ["Named Entity", "ne_hgc_175m_600=", "ne_hgc_175m_600"] |
| ], |
| "corenlp/ne=" : namedEntities, |
| "corenlp/ne_dewac_175m_600=" : namedEntities, |
| "corenlp/ne_hgc_175m_600=" : namedEntities, |
| "cnx/" : [ |
| ["Constituency", "c="], |
| ["Lemma", "l="], |
| ["Morphology", "m="], |
| ["Part-of-Speech", "p="], |
| ["Syntax", "syn="] |
| ], |
| "cnx/c=" : [ |
| ["np", "np ", "Nominal Phrase"] |
| ], |
| // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/morph.html |
| "cnx/m=" : [ |
| ["Abbr","Abbr ", "Nouns: Abbreviation"], |
| ["CMP","CMP ", "Adjective: Comparative"], |
| ["IMP", "IMP ", "Mood: Imperative"], |
| ["IND", "IND ", "Mood: Indicative"], |
| ["INF", "INF ", "Infinitive"], |
| ["ORD","ORD ", "Numeral: Ordinal"], |
| ["PAST", "PAST ", "Tense: past"], |
| ["PCP", "PCP ", "Participle"], |
| ["PERF", "PERF ", "Perfective Participle"], |
| ["PL","PL ", "Nouns: Plural"], |
| ["PRES", "PRES ", "Tense: present"], |
| ["PROG", "PROG ", "Progressive Participle"], |
| ["Prop","Prop ", "Nouns: Proper Noun"], |
| ["SUB", "SUB ", "Mood: Subjunctive"], |
| ["SUP","SUP ", "Adjective: Superlative"] |
| ], |
| // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/morph.html |
| "cnx/p=" : [ |
| ["A", "A ", "Adjective"], |
| ["ADV", "ADV ", "Adverb"], |
| ["CC", "CC ", "Coordination Marker"], |
| ["CS", "CS ", "Clause Marker"], |
| ["DET", "DET ", "Determiner"], |
| ["INTERJ", "INTERJ ", "Interjection"], |
| ["N", "N ", "Noun"], |
| ["NUM", "NUM ", "Numeral"], |
| ["PREP", "PREP ", "Preposition"], |
| ["PRON", "PRON ", "Pro-Nominal"], |
| ["V", "V ", "Verb"] |
| ], |
| // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/syntax.html |
| "cnx/syn=" : [ |
| ["@ADVL", "@ADVL ", "Adverbial Head"], |
| ["@AUX", "@AUX ", "Auxiliary Verb"], |
| ["@CC", "@CC ", "Coordination"] |
| ["@MAIN", "@MAIN ", "Main Verb"], |
| ["@NH", "@NH ", "Nominal Head"], |
| ["@POSTMOD", "@POSTMOD ", "Postmodifier"], |
| ["@PREMARK", "@PREMARK ", "Preposed Marker"], |
| ["@PREMOD", "@POSTMOD ", "Premodifier"] |
| ], |
| "opennlp/" : [ |
| ["Part-of-Speech", "p="] |
| ], |
| "opennlp/p=" : sttsArray, |
| "xip/" : [ |
| ["Constituency", "c="], |
| // Inactive: ["Dependency", "d="], |
| ["Lemma", "l="], |
| ["Part-of-Speech", "p="], |
| ], |
| // "xip/c=" : [], |
| // Inactive: "xip/d=" : [], |
| // "xip/p=" : [], |
| "tt/" : [ |
| ["Lemma", "l="], |
| ["Part-of-Speech", "p="] |
| ], |
| "tt/p=" : sttsArray, |
| "mate/" : [ |
| // Inactive: "d" : ["d=", "Dependency"], |
| ["Lemma", "l="], |
| ["Morphology", "m="], |
| ["Part-of-Speech", "p="] |
| ], |
| // Inactive: mate/d= |
| "mate/p=" : mateSttsArray, |
| "mate/m=" : [ |
| ["Case", "case:"], |
| ["Degree", "degree:"], |
| ["Gender", "gender:"], |
| ["Mood", "mood:"], |
| ["Number", "number:"], |
| ["Person", "person:"], |
| ["Tense","tense:"], |
| ["No type", "<no-type> "] |
| ], |
| "mate/m=case:" : [ |
| ["acc", "acc ", "Accusative"], |
| ["dat","dat ", "Dative"], |
| ["gen", "gen ","Genitive"], |
| ["nom","nom ", "Nominative"], |
| ["*","* ", "Undefined"] |
| ], |
| "mate/m=degree:" : [ |
| ["comp","comp ", "Comparative"], |
| ["pos","pos ", "Positive"], |
| ["sup","sup ", "Superative"] |
| ], |
| "mate/m=gender:" : [ |
| ["fem", "fem ", "Feminium"], |
| ["masc", "masc ", "Masculinum"], |
| ["neut","neut ", "Neuter"], |
| ["*","* ","Undefined"] |
| ], |
| "mate/m=mood:" : [ |
| ["imp","imp ", "Imperative"], |
| ["ind","ind ", "Indicative"], |
| ["subj","subj ", "Subjunctive"] |
| ], |
| "mate/m=number:" : [ |
| ["pl","pl ","Plural"], |
| ["sg","sg ","Singular"], |
| ["*","* ","Undefined"] |
| ], |
| "mate/m=person:" : [ |
| ["1","1 ", "First Person"], |
| ["2","2 ", "Second Person"], |
| ["3","3 ", "Third Person"] |
| ], |
| "mate/m=tense:" : [ |
| ["past","past ", "Past"], |
| ["pres","pres ", "Present"] |
| ] |
| }; |
| |
| |
| /** |
| * Analyze strings for prefixes |
| */ |
| var PrefixAnalyzer = { |
| _regex : new RegExp( |
| "(?:^|[^-a-zA-Z0-9])" + // Anchor |
| "((?:[-a-zA-Z0-9]+?)\/" + // Foundry |
| "(?:" + |
| "(?:[-a-zA-Z0-9]+?)=" + // Layer |
| "(?:(?:[^:=\/ ]+?):)?" + // Key |
| ")?" + |
| ")$"), |
| analyze : function (text) { |
| if (!this._regex.exec(text)) |
| return undefined; |
| return RegExp.$1 |
| } |
| }; |
| |
| |
| /** |
| * Event handling after a key pressed |
| */ |
| function updateKey (that, e) { |
| var menu = that.menu(); |
| |
| switch (e.key) { |
| case 'Esc': |
| // Hide menu |
| menu.hide(); |
| break; |
| case 'Down': |
| e.halt(); // No event propagation |
| |
| // Menu is not active |
| if (!menu.active) { |
| that.popUp() |
| } |
| // Menu is active |
| else { |
| that.removePrefix(); |
| menu.next(); |
| }; |
| |
| break; |
| case "Up": |
| if (!menu.active) |
| break; |
| e.halt(); // No event propagation |
| that.removePrefix(); |
| menu.prev(); |
| break; |
| case "Enter": |
| if (!menu.active) |
| break; |
| e.halt(); // No event propagation |
| that.insertText(menu.getActiveItem().getAction()); |
| that.removePrefix(); |
| |
| // Remove menu |
| menu.hide(); |
| |
| // Fill this with the correct value |
| // Todo: This is redundant with click function |
| var show; |
| if ((show = that.analyzeContext()) != "-") { |
| menu.show(show); |
| menu.update( |
| e.target.getBoundingClientRect().right |
| ); |
| }; |
| |
| break; |
| default: |
| if (!menu.active) |
| break; |
| |
| // key stroke is not a character |
| if (e.key.length != 1) { |
| |
| // Key stroke is not a text modifying key |
| if (e.key != 'Shift' && |
| e.key != 'Up' && |
| e.key != 'Down' && |
| e.key != 'Enter' && |
| e.key != 'Alt' && |
| e.key != 'AltGraph' && |
| e.key != 'CapsLock') { |
| that.insertPrefix(); |
| menu.hide(); |
| }; |
| break; |
| }; |
| |
| e.halt(); // No event propagation |
| |
| // Try to identify prefix |
| if (menu.skipToPrefix(e.key)) |
| break; |
| |
| // Prefix not found |
| that.insertPrefix(); |
| menu.hide(); |
| }; |
| }; |
| |
| // new hint object |
| var Hint = { |
| _search : undefined, // Return the search element |
| _mirror : undefined, // Return the mirror element |
| _menu : undefined, |
| _analyzer : undefined, |
| firstTry : true, |
| menu : function () { |
| // In case this wasn't defined yet |
| if (this._menu === undefined) { |
| this._menu = Object.create(Menu).init(); |
| this._mirror.appendChild(this._menu.getElement()); |
| }; |
| return this._menu; |
| }, |
| |
| // Initialize the object |
| init : function () { |
| this._search = document.getElementById("q-field"); |
| this._mirror = document.createElement("div"); |
| this._mirror.setAttribute("id", "searchMirror"); |
| this._mirror.appendChild(document.createElement("span")); |
| document.getElementsByTagName("body")[0].appendChild(this._mirror); |
| |
| this._analyzer = Object.create(PrefixAnalyzer); |
| |
| // Update positional information, in case the windows size changes |
| var that = this; |
| window.onresize = function () { that.reposition() }; |
| |
| // Add event listener for key pressed down |
| this._search.addEventListener( |
| "keydown", |
| function (e) { |
| updateKey(that, e) |
| }, |
| false |
| ); |
| |
| // Reposition the mirror |
| this.reposition(); |
| |
| // Return object for chaining |
| return this; |
| }, |
| |
| // Popup method |
| popUp : function () { |
| if (this.active) |
| return; |
| |
| // Reposition hint list on first try |
| if (this.firstTry) |
| this.reposition().firstTry = false; |
| |
| // Update view |
| this.update(); |
| |
| // Fill this with the correct value |
| if (this.menu().show(this.analyzeContext())) { |
| this.update( |
| this._search.getBoundingClientRect().right |
| ); |
| } |
| else { |
| this.menu().hide(); |
| }; |
| |
| this._search.focus(); |
| }, |
| |
| // Reposition the mirror object |
| reposition : function () { |
| |
| // Update style properties |
| var searchRect = this._search.getBoundingClientRect(); |
| var searchStyle = window.getComputedStyle(this._search, null); |
| var mStyle = this._mirror.style; |
| mStyle.left = searchRect.left + "px"; |
| mStyle.top = searchRect.bottom + "px"; |
| mStyle.borderLeftColor = "transparent"; |
| mStyle.height = "1px"; |
| mStyle.paddingLeft = searchStyle.getPropertyValue("padding-left"); |
| mStyle.marginLeft = searchStyle.getPropertyValue("margin-left"); |
| mStyle.borderLeftWidth = searchStyle.getPropertyValue("border-left-width"); |
| mStyle.borderLeftStyle = searchStyle.getPropertyValue("border-left-style"); |
| mStyle.fontSize = searchStyle.getPropertyValue("font-size"); |
| mStyle.fontFamily = searchStyle.getPropertyValue("font-family"); |
| return this; |
| }, |
| |
| // Reposition the menu object |
| update : function () { |
| var s = this._search; |
| var start = s.selectionStart; |
| this._mirror.firstChild.textContent = s.value.substring(0, start); |
| }, |
| |
| analyzeContext : function () { |
| var context = this._splitInputText()[0]; |
| if (context === undefined || context.length === 0) |
| return "-"; |
| context = this._analyzer.analyze(context); |
| if (context === undefined || context.length === 0) |
| return "-"; |
| |
| if (!hintArray[context]) |
| return "-"; |
| |
| return context; |
| }, |
| |
| _splitInputText : function () { |
| var s = this._search; |
| var value = s.value; |
| var start = s.selectionStart; |
| var begin = value.substring(0, start); |
| var end = value.substring(start, value.length); |
| return new Array(begin, end); |
| }, |
| |
| // Insert text at the current cursor position |
| insertText : function (text) { |
| var s = this._search; |
| var splitText = this._splitInputText(); |
| s.value = splitText[0] + text + splitText[1]; |
| s.selectionStart = (splitText[0] + text).length |
| s.selectionEnd = s.selectionStart; |
| this._mirror.firstChild.textContent = splitText[0] + text; |
| }, |
| |
| // Remove stored prefix |
| removePrefix : function () { |
| this.menu()._prefix = undefined; |
| }, |
| |
| // Insert stored prefix at current cursor position |
| insertPrefix : function () { |
| if (this.menu()._prefix === undefined) |
| return; |
| this.insertText(this.menu()._prefix); |
| } |
| }; |
| |
| |
| /** |
| * Menu list |
| */ |
| var Menu = { |
| active : false, |
| _element : undefined, |
| _position : 0, // Position of menu item |
| _offset : 0, // Visual offset for chosen highlight |
| _size : 8, // Number of items to be shown |
| _items : [], // Items for menu |
| _name : undefined, |
| _prefix : undefined, |
| getElement : function () { |
| return this._element; |
| }, |
| init : function () { |
| this._element = document.createElement("ul"); |
| |
| // Add onclick event |
| this._element.addEventListener("click", chooseHint, false); |
| |
| this._element.style.opacity = 0; |
| this.active = false; |
| this._setDefaults(); |
| return this; |
| }, |
| update : function (searchRightPosition) { |
| var infoRightPosition = this._element.getBoundingClientRect().right; |
| if (infoRightPosition > searchRightPosition) { |
| this._element.style.marginLeft = '-' + (infoRightPosition - searchRightPosition) + 'px'; |
| }; |
| return this; |
| }, |
| next : function () { |
| if (!this.active) |
| return; |
| this._clearView(); |
| this._position++; |
| |
| // In case the list is bigger than the view |
| if (this._items.length > this._size) { |
| if (this._position >= this._items.length) { |
| // Roll to top |
| this._offset = 0; |
| this._position = 0; |
| this._showItems(0); |
| } |
| else if (this._position >= (this._size + this._offset)) { |
| // Roll down |
| this._element.removeChild(this._element.firstChild); |
| this._offset++; |
| this._element.appendChild(this.getItem(this._position).getElement()); |
| }; |
| } |
| else if (this._position >= this._items.length) { |
| this._position = 0; |
| }; |
| this._updateView(); |
| }, |
| prev : function () { |
| if (!this.active) |
| return; |
| this._clearView(); |
| this._position--; |
| |
| // In case the list is bigger than the view |
| if (this._items.length > this._size) { |
| if (this._position < 0) { |
| // roll to bottom |
| this._setToLast(); |
| this._offset = (this._position - this._size) + 1; |
| this._showLastItems(); |
| } |
| else if (this._position < this._offset) { |
| // roll up |
| this._element.removeChild(this._element.lastChild); |
| this._offset--; |
| this._element.insertBefore( |
| this.getItem(this._position).getElement(), |
| this._element.firstChild |
| ); |
| }; |
| } |
| else if (this._position < 0) { |
| this._setToLast(); |
| }; |
| this._updateView(); |
| }, |
| skipToPrefix : function (prefix) { |
| if (this._prefix === undefined) |
| this._prefix = prefix.toLocaleLowerCase(); |
| else |
| this._prefix += prefix.toLocaleLowerCase(); |
| |
| var pos = 0; |
| var found = false; |
| var good = -1; |
| var test; |
| for (; pos < this._items.length; pos++) { |
| if ((test = this.getItem(pos).getLCName().indexOf(this._prefix)) != -1) { |
| if (test == 0) { |
| found = true; |
| break; |
| }; |
| good = pos; |
| }; |
| }; |
| |
| // Perfect prefix |
| if (found) |
| return this.skipToPos(pos); |
| // At least infix |
| else if (good != -1) |
| return this.skipToPos(good); |
| // No |
| return false; |
| }, |
| skipToPos : function (index) { |
| if (!this.active) |
| return false; |
| if (index < 0 || index >= this._items.length) |
| return false; |
| |
| this._clearView(); |
| this._position = index; |
| |
| if (index < this._offset || index >= (this._offset + this._size)) { |
| |
| // Index is in the final frame |
| if (index >= (this._items.length - this._size)) { |
| this._offset = this._items.length - this._size; |
| this._showLastItems(); |
| } |
| |
| // Index is in the final frame |
| else { |
| this._offset = index; |
| this._showItems(index); |
| }; |
| }; |
| |
| // Activate new position |
| this._updateView(); |
| return true; |
| }, |
| show : function (name) { |
| // The element is already given |
| if (this._name != name) { |
| |
| // Todo: store hints in hash |
| |
| // Delete items |
| this._items.length = 0; |
| |
| var items = hintArray[name]; |
| |
| // Hints not found |
| if (items === undefined) |
| return undefined; |
| |
| var i; |
| for (i in items) { |
| var item = Object.create(MenuItem).init(items[i]); |
| this._items.push(item); |
| }; |
| |
| // Add classes for rolling menus |
| this.getItem(0).getElement().classList.add("no-more"); |
| this.getItem(i).getElement().classList.add("no-more"); |
| |
| this._name = name; |
| }; |
| this._showItems(0); |
| this._element.style.opacity = 1; |
| this._setDefaults(); |
| this.active = true; |
| this._updateView(); |
| return true; |
| }, |
| hide : function () { |
| this._element.style.opacity = 0; |
| if (this.active) |
| this.getActiveItem().deactivate(); |
| this._setDefaults(); |
| this.active = false; |
| }, |
| getActiveItem : function () { |
| return this._items[this._position]; |
| }, |
| getItem : function (index) { |
| return this._items[index]; |
| }, |
| getPrefix : function () { |
| return this._prefix; |
| }, |
| _setDefaults : function () { |
| this._offset = 0; |
| this._position = 0; |
| this._prefix = undefined; |
| }, |
| // Remove all visible list items |
| _deleteMenu : function () { |
| var child; |
| while (child = this._element.firstChild) |
| this._element.removeChild(child); |
| }, |
| _clearView : function () { |
| var active = this.getActiveItem(); |
| if (active !== undefined) |
| active.deactivate(); |
| }, |
| _updateView : function () { |
| var active = this.getActiveItem(); |
| if (active !== undefined) |
| active.activate(); |
| }, |
| |
| // Make all list items visible starting at a certain offset |
| _showItems : function (offset) { |
| this._deleteMenu(); |
| for (var i = offset; i < this._size + offset; i++) { |
| if (i >= this._items.length) |
| break; |
| this._element.appendChild( |
| this._items[i].getElement() |
| ) |
| }; |
| }, |
| |
| // Make all final list items visible |
| _showLastItems : function () { |
| this._deleteMenu(); |
| for (var i = (this._items.length - 1); i >= (this._items.length - this._size); i--) { |
| if (i < 0) |
| break; |
| if (!this._element.firstChild) |
| this._element.appendChild(this._items[i].getElement()); |
| else |
| this._element.insertBefore( |
| this._items[i].getElement(), |
| this._element.firstChild |
| ); |
| }; |
| }, |
| _setToLast : function () { |
| this._position = this._items.length - 1; |
| } |
| }; |
| |
| function chooseHint (e) { |
| var element = e.target; |
| while (element.nodeName == "STRONG" || element.nodeName == "SPAN") { |
| element = element.parentNode; |
| }; |
| |
| if (element === undefined || element.nodeName != "LI") |
| return; |
| |
| var action = element.getAttribute('data-action'); |
| hint.insertText(action); |
| var menu = hint.menu(); |
| menu.hide(); |
| |
| // Fill this with the correct value |
| var show; |
| if ((show = hint.analyzeContext()) != "-") { |
| menu.show(show); |
| menu.update( |
| hint._search.getBoundingClientRect().right |
| ); |
| }; |
| |
| hint._search.focus(); |
| }; |
| |
| var MenuItem = { |
| _name : undefined, |
| _lcname : undefined, |
| _desc : undefined, |
| _element : undefined, |
| _action : "", |
| activate : function () { |
| this._element.classList.add("active"); |
| }, |
| deactivate : function () { |
| this._element.classList.remove("active"); |
| }, |
| // Initialize this item |
| init : function (param) { |
| this._name = param[0]; |
| this._action = param[1]; |
| this._lcname = this._name.toLocaleLowerCase(); |
| |
| if (param.length > 2) { |
| this._desc = param[2]; |
| this._lcname += " " + this._desc.toLocaleLowerCase(); |
| }; |
| |
| return this; |
| }, |
| |
| // Created element of this item |
| getElement : function () { |
| if (this._element !== undefined) |
| return this._element; |
| |
| var li = document.createElement("li"); |
| |
| li.setAttribute("data-action", this._action); |
| |
| var name = document.createElement("strong"); |
| |
| name.appendChild(document.createTextNode(this._name)); |
| li.appendChild(name); |
| if (this._desc !== undefined) { |
| var desc = document.createElement("span"); |
| desc.appendChild(document.createTextNode(this._desc)); |
| li.appendChild(desc); |
| }; |
| this._element = li; |
| return this._element; |
| }, |
| |
| // Name of this item |
| getName : function () { |
| return this._name; |
| }, |
| |
| getLCName : function () { |
| return this._lcname; |
| }, |
| |
| // Description of this item |
| getDesc : function () { |
| return this._desc; |
| }, |
| |
| |
| getAction : function () { |
| return this._action; |
| } |
| }; |