Attempt to fix Hint compatibility problem (WIP)

Change-Id: I3016afc2f74b90c223dd4f8f2386b71aab1eedcd
diff --git a/dev/js/spec/hintSpec.js b/dev/js/spec/hintSpec.js
index 79726f3..45850a4 100644
--- a/dev/js/spec/hintSpec.js
+++ b/dev/js/spec/hintSpec.js
@@ -241,13 +241,22 @@
 
       // Show menu, hide alert!
       hint.show(false);
-
-      // PROBLEM!
-      expect(hint.active()).toBeTruthy();
-      expect(hint.inputField().mirrorValue()).toEqual('abcde');
-      expect(hint.alert().active).toBeFalsy();
     });
 
+    it('should work both in Chrome and Firefox', function () {
+      var hint = hintClass.create({
+        inputField : input
+      });
+      hint.show(false);
+      expect(hint.alert(5, 'That does not work!')).toBeTruthy();
+
+      // Show menu, hide alert!
+      hint.show(false);
+
+      expect(hint.active()).toBeFalsy();
+    });
+
+    
     it('should view main menu on default', function () {
       var hint = hintClass.create({
         inputField : input
diff --git a/dev/js/src/hint.js b/dev/js/src/hint.js
index 91381f5..409bd6f 100644
--- a/dev/js/src/hint.js
+++ b/dev/js/src/hint.js
@@ -16,6 +16,9 @@
  * TODO: Improve context analyzer from hint!
  * TODO: Marked annotations should be addable to "fragments"
  */
+
+"use strict";
+
 define([
   'hint/input',
   'hint/menu',
@@ -26,11 +29,6 @@
              menuClass,
              analyzerClass,
              alertClass) {
-  "use strict";
-
-  /**
-   * Return keycode based on event
-   */
 
   // Initialize hint array
 
@@ -42,10 +40,6 @@
    */
   return {
 
-    // Some variables
-    // _firstTry : true,
-    // active : false,
-
     /**
      * Create new hint helper.
      */
@@ -53,6 +47,7 @@
       return Object.create(this)._init(param);
     },
 
+
     // Initialize hint helper
     _init : function (param) {
       param = param || {};
@@ -60,6 +55,8 @@
       // Holds all menus per prefix context
       this._menu   = {};
       this._alert  = alertClass.create();
+
+      // Active may either hold an alert or a menu
       this._active = null;
 
       // No annotation helper available
@@ -84,41 +81,37 @@
         ")$";
 
       // Get input field
-      var qfield = param["inputField"] || document.getElementById("q-field");
+      const qfield = param["inputField"] || document.getElementById("q-field");
       if (!qfield)
         return null;
 
       // Create input field
       this._inputField = inputClass.create(qfield);
 
-      var inputFieldElement = this._inputField.element();
-
-      var c = this._inputField.container();
-
       // create alert
+      const that = this;
+
+      const c = this._inputField.container();
       c.appendChild(this._alert.element());
-
-      var that = this;
-
-      this._inputField.container().addEventListener('click', function (e) {
+      c.addEventListener('click', function (e) {
         if (!this.classList.contains('active')) {
           that.show(false);
         };
       });
 
-      var _down = function (e) {
-        var code = _codeFromEvent(e);
-        if (code === 40) {
-          this.show(false);
-          e.halt();
-        };
-      };
-      
       // Move infobox
+      const inputFieldElement = this._inputField.element();
       inputFieldElement.addEventListener("keyup", this.update.bind(this));
       inputFieldElement.addEventListener("click", this.update.bind(this));
 
       // Add event listener for key pressed down
+      let _down = function (e) {
+        if (_codeFromEvent(e) === 40) {
+          this.show(false);
+          e.halt();
+        };
+      };
+
       inputFieldElement.addEventListener(
         "keydown", _down.bind(this), false
       );
@@ -160,29 +153,28 @@
      * Altert at a specific character position.
      */
     alert : function (charPos, msg) {
-
+      const t = this;
       if (arguments.length === 0)
-        return this._alert;
+        return t._alert;
 
       // Do not alert if already alerted!
-      if (this._alert.active)
+      if (t._alert.active)
         return false;
 
       // Move to the correct position
-      this._inputField.moveto(charPos);
+      t._inputField.moveto(charPos);
 
       // Set container to active (aka hide the hint helper button)
 
-      this._alert.show(msg);
-      this.active(this._alert);
+      t._alert.show(msg);
+      t.active(t._alert);
       return true;
     },
     
-    _unshowAlert : function () {
-      this._alert.hide();
-      this.active(null);
-    },
 
+    /**
+     * Update input field.
+     */
     update : function () {
       this._inputField.update();
       if (this._alert.hide())
@@ -195,7 +187,7 @@
      */
     menu : function (action) {
       if (this._menu[action] === undefined) {
-
+        
         // No matching hint menu
         if (KorAP.annotationHelper[action] === undefined)
           return;
@@ -210,28 +202,31 @@
       return this._menu[action];
     },
 
+
     /**
      * Get the correct menu based on the context
      */
     contextMenu : function (ifContext) {
+      const noC = ifContext ? undefined : this.menu("-");
       
       // Get context (aka left text)
-      var context = this._inputField.context();
+      let context = this._inputField.context();
 
       if (context === undefined || context.length === 0) {
-        return ifContext ? undefined : this.menu("-");
+        return noC;
       };
 
       // Get context (aka left text matching regex)
       context = this._analyzer.test(context);
 
       if (context === undefined || context.length == 0) {
-        return ifContext ? undefined : this.menu("-");
+        return noC;
       };
 
-      return this.menu(context) || (ifContext ? undefined : this.menu('-'));
+      return this.menu(context) || noC;
     },
 
+
     /**
      * Activate a certain menu.
      * If a menu is passed, the menu will be activated.
@@ -239,11 +234,11 @@
      * If nothing is passed, returns the active menu.
      */
     active : function (obj) {
-
+      
       // A menu or null was passed
-      if (arguments.length === 1) {
-        var c = this._inputField.container();
-
+      if (arguments.length === 1) {        
+        const c = this._inputField.container();
+       
         // Make the menu active
         if (obj !== null) {
           c.classList.add('active');
@@ -277,33 +272,31 @@
       this._unshow();
       
       // Get the menu
-      var menu;
+      let menu;
       if (menu = this.contextMenu(ifContext)) {
+
         this.active(menu);
 
-        var c = this._inputField.container();        
-        c.appendChild(menu.element());
+        let e = menu.element();
+        // console.log("Chrome may send a blur on the old menu here");
+        this._active.element().blur();
+        this._inputField.container().appendChild(e);
 
         menu.show();
         menu.focus();
         // Focus on input field
         // this.inputField.element.focus();
       }
+
       else {
         this._inputField.element().focus();
       };
     },
     
+
     // This will get the context of the field
     getContext : function () {},
 
-    /**
-     * Deactivate the current menu and focus on the input field.
-     */
-    unshow_old : function () {
-      this.active(null);
-      this.inputField().element().focus();
-    },
 
     /**
      * Deactivate the current menu and focus on the input field.
@@ -316,17 +309,16 @@
 
     // Catch touch events
     _touch : function (e) {
-
       if (e.type === 'touchstart') {
-        var t = e.touches[0];
-        this._lastTouch = t.clientY;
+        this._lastTouch = e.touches[0].clientY;
       }
+
       else if (e.type === 'touchend') {
         this._lastTouch = undefined;
       }
+      
       else if (e.type == 'touchmove') {
-        var t = e.touches[0];
-        if ((this._lastTouch + 10) < t.clientY) {
+        if ((this._lastTouch + 10) < e.touches[0].clientY) {
           this.show();
           this._lastTouch = undefined;
         };
@@ -334,26 +326,32 @@
       }
     },
 
-    
+
+    // Unshow the hint menu
     _unshow : function () {
       if (this.active() !== null) {
-        // var act = this.active();
-
+       
         // This does not work for alert currently!
-        //if (act._type !== 'alert') {
         if (!this._alert.active) {
 
-          // This does not work for webkit!
-          var c = this._inputField.container();
-          c.removeChild(this._active.element());
+          // TODO: This does not work for webkit?
+          this._inputField
+            .container()
+            .removeChild(this._active.element());
         }
+
         else {
           this._unshowAlert();
         };
         
-        // this._active.hide();
         this.active(null);
       };
+    },
+
+    // Unshow alert
+    _unshowAlert : function () {
+      this._alert.hide();
+      this.active(null);
     }
   };
 });
diff --git a/dev/js/src/hint/alert.js b/dev/js/src/hint/alert.js
index 768d719..c8d7696 100644
--- a/dev/js/src/hint/alert.js
+++ b/dev/js/src/hint/alert.js
@@ -1,26 +1,45 @@
 /**
- * Hint menu alert
+ * Hint menu alert, positioned at the exact char.
  */
+"use strict";
+
 define(function () {
-  "use strict";
+
   return {
+
+    /**
+     * Construct a new alert object
+     */
     create : function (msg) {
       return Object.create(this)._init(msg);
     },
+
+    // Init
     _init : function (msg) {
-      this._type = 'alert';
-      this.active = false;
-      this._element = document.createElement('div');
-      this._element.style.display = 'none';
-      this._element.classList.add('alert', 'hint');
-      return this;
-    },
-    show : function (msg) {
-      this._element.textContent = msg;
-      this.active = true;
-      this._element.style.display = 'block';
+      const t = this;
+      t._type = 'alert';
+      t.active = false;
+      t._element = document.createElement('div');
+      t._element.style.display = 'none';
+      t._element.classList.add('alert', 'hint');
+      return t;
     },
 
+
+    /**
+     * Show alert.
+     */
+    show : function (msg) {
+      this.active = true;
+      const e = this._element;
+      e.textContent = msg;
+      e.style.display = 'block';
+    },
+
+
+    /**
+     * Hide alert.
+     */
     hide : function () {
       if (!this.active)
 	      return false;
@@ -29,6 +48,10 @@
       return true;
     },
 
+
+    /**
+     * Get alert object.
+     */
     element : function () {
       return this._element;
     }
diff --git a/dev/js/src/hint/contextanalyzer.js b/dev/js/src/hint/contextanalyzer.js
index fc01662..7030cb9 100644
--- a/dev/js/src/hint/contextanalyzer.js
+++ b/dev/js/src/hint/contextanalyzer.js
@@ -1,6 +1,8 @@
 /**
  * Regex object for checking the context of the hint
  */
+"use strict";
+
 define({
 
   /**
diff --git a/dev/js/src/hint/foundries.js b/dev/js/src/hint/foundries.js
index 256ee76..ce6ef43 100644
--- a/dev/js/src/hint/foundries.js
+++ b/dev/js/src/hint/foundries.js
@@ -1,22 +1,21 @@
-window.KorAP = window.KorAP || {};
 KorAP.annotationHelper = KorAP.annotationHelper || { '-' : [] };
 
-define([], function () {
-  "use strict";
+"use strict";
+
+define(["util"], function () {
 
   const ah = KorAP.annotationHelper;
 
-  ah["getDesc"] = function (foundryLayer, value) {
+  ah.getDesc = function (foundryLayer, value) {
 
     if (!foundryLayer)
       return;
 
-    var anno = this[foundryLayer];
+    let anno = this[foundryLayer];
 
     if (!anno)
       return;
 
-    // if (value.indexOf(':') < 0) {
     if (!value.includes(':')) {
       value += ' ';
 
@@ -35,16 +34,15 @@
 
       return;
     }
-    else {
-      var text = '';
-      var v = value.split(":");
-      var l1 = v[0];
-      var l2 = v[1];
 
-      l1 += ':';
+    else {
+      const v = value.split(":");
+      let l1 = v[0] + ':';
+      let l2 = v[1] + ' ';
+      let text = '';
 
       // Add key description
-      for (var i = 0; i < anno.length; i++) {
+      for (let i = 0; i < anno.length; i++) {
         if (anno[i] &&
             anno[i][1] == l1) {
           if (anno[i][2])
@@ -65,10 +63,8 @@
       if (!anno)
         return;
 
-      l2 += ' ';
-
       // Add value description
-      for (var i = 0; i < anno.length; i++) {
+      for (let i = 0; i < anno.length; i++) {
         if (anno[i] &&
             anno[i][1] == l2) {
           if (anno[i][2])
diff --git a/dev/js/src/hint/input.js b/dev/js/src/hint/input.js
index 120a953..d779776 100644
--- a/dev/js/src/hint/input.js
+++ b/dev/js/src/hint/input.js
@@ -1,194 +1,203 @@
 // Input field for queries
 /*
- * TODO: Support allert for query problems.
+ * TODO: Support alert for query problems.
  */
 
-define({
+"use strict";
 
-  /**
-   * Create a new input field.
-   */
-  create : function (element) {
-    return Object.create(this)._init(element);
-  },
+define(['util'], function () {
+
+  return {
+
+    /**
+     * Create a new input field.
+     */
+    create : function (element) {
+      return Object.create(this)._init(element);
+    },
 
 
-  /**
-   * Get the mirrored input field.
-   */
-  mirror : function () {
-    return this._mirror;
-  },
+    // Initialize new input field
+    _init : function (element) {
+      this._element = element;
+
+      this._container = document.createElement("div");
+      this._container.setAttribute('id', 'hint');
+
+      // Create mirror for searchField
+      // This is important for positioning
+      // if ((this._mirror = document.getElementById("searchMirror")) === null) {
+      this._mirror = document.createElement("div");
+      const m = this._mirror;
+      m.classList.add('hint', 'mirror');
+      m.addE("span");
+      m.appendChild(this._container);
+      m.style.height = "0px";
+      document.getElementsByTagName("body")[0].appendChild(m);
+
+      // Update position of the mirror
+      const re = this.reposition.bind(this);
+      window.addEventListener('resize', re);
+      this._element.addEventListener('onfocus', re);
+      this.reposition();
+      return this;
+    },
 
 
-  /**
-   * Get the container element.
-   * This contains the hint helper / menus
-   * and probably an
-   * error message.
-   */
-  container : function () {
-    return this._container;
-  },
+    /**
+     * Get the mirrored input field.
+     */
+    mirror : function () {
+      return this._mirror;
+    },
 
 
-  /**
-   * Get the input element the
-   * hint helper is attached to.
-   */
-  element : function () {
-    return this._element;
-  },
+    /**
+     * Get the container element.
+     * This contains the hint helper / menus
+     * and probably an
+     * error message.
+     */
+    container : function () {
+      return this._container;
+    },
 
 
-  /**
-   * Get the value of the input field
-   * the hint helper is attached to.
-   */
-  value : function () {
-    return this._element.value;
-  },
+    /**
+     * Get the input element the
+     * hint helper is attached to.
+     */
+    element : function () {
+      return this._element;
+    },
 
 
-  /**
-   * Get the value of the input field mirror.
-   */
-  mirrorValue : function () {
-    return this._mirror.firstChild.textContent;
-  },
+    /**
+     * Get the value of the input field
+     * the hint helper is attached to.
+     */
+    value : function () {
+      return this._element.value;
+    },
 
 
-  /**
-   * Reset the input value
-   */
-  reset : function () {
-    this._element.value = "";
-  },
+    /**
+     * Get the value of the input field mirror.
+     */
+    mirrorValue : function () {
+      return this._mirror.firstChild.textContent;
+    },
 
 
-  /**
-   * Update the mirror content.
-   */
-  update : function () {
-    this._mirror.firstChild.textContent = this._split()[0];
-    this._container.style.left = this._rightPos() + 'px';
-    return this;
-  },
+    /**
+     * Reset the input value
+     */
+    reset : function () {
+      this._element.value = "";
+    },
 
 
-  /**
-   * Insert text into the mirror.
-   * This is a prefix of the input field's
-   * value.
-   */
-  insert : function (text) {
-    var splittedText = this._split();
-    var s = this._element;
-    s.value = splittedText[0] + text + splittedText[1];
-    s.selectionStart = (splittedText[0] + text).length;
-    s.selectionEnd = s.selectionStart;
-
-    // Maybe update?
-    this._mirror.firstChild.textContent = splittedText[0] + text;
-    return this;
-  },
-
-  /**
-   * Move hinthelper to given character position
-   */
-  moveto : function (charpos) {
-    this._element.selectionStart = charpos;
-    this._element.selectionEnd = charpos;
-    this._element.focus();
-    return this.update();
-  },
-
-  /**
-   * Reposition the input mirror directly
-   * below the input box.
-   */
-  reposition : function () {
-    var inputClientRect = this._element.getBoundingClientRect();
-    var inputStyle = window.getComputedStyle(this._element, null);
-
-    var bodyClientRect = 
-      document.getElementsByTagName('body')[0].getBoundingClientRect();
-
-    // Reset position
-    var mirrorStyle = this._mirror.style;
-    mirrorStyle.left = parseInt(inputClientRect.left) + "px";
-    mirrorStyle.top  = parseInt(inputClientRect.bottom - bodyClientRect.top) + "px";
-    mirrorStyle.width = inputStyle.getPropertyValue("width");
-
-    // These may be relevant in case of media depending css
-    mirrorStyle.paddingLeft     = inputStyle.getPropertyValue("padding-left");
-    mirrorStyle.marginLeft      = inputStyle.getPropertyValue("margin-left");
-    mirrorStyle.borderLeftWidth = inputStyle.getPropertyValue("border-left-width");
-    mirrorStyle.borderLeftStyle = inputStyle.getPropertyValue("border-left-style");
-    mirrorStyle.fontSize        = inputStyle.getPropertyValue("font-size");
-    mirrorStyle.fontFamily      = inputStyle.getPropertyValue("font-family");
-  },
+    /**
+     * Update the mirror content.
+     */
+    update : function () {
+      this._mirror.firstChild.textContent = this._split()[0];
+      this._container.style.left = this._rightPos() + 'px';
+      return this;
+    },
 
 
-  /**
-   * Get the context, which is the input
-   * field's value bounded to the
-   * cursor position.
-   */
-  context : function () {
-    return this._split()[0];
-  },
+    /**
+     * Insert text into the mirror.
+     * This is a prefix of the input field's
+     * value.
+     */
+    insert : function (text) {
+      const splittedText = this._split();
+      const s = this._element;
+      s.value = splittedText[0] + text + splittedText[1];
+      s.selectionStart = (splittedText[0] + text).length;
+      s.selectionEnd = s.selectionStart;
+
+      // Maybe update?
+      this._mirror.firstChild.textContent = splittedText[0] + text;
+      return this;
+    },
 
 
-  // Initialize new input field
-  _init : function (element) {
-    this._element = element;
-
-    // Create mirror for searchField
-    // This is important for positioning
-    // if ((this._mirror = document.getElementById("searchMirror")) === null) {
-    this._mirror = document.createElement("div");
-    this._mirror.classList.add('hint', 'mirror');
-    this._mirror.appendChild(document.createElement("span"));
-    this._container = document.createElement("div");
-    this._container.setAttribute('id', 'hint');
-    
-    this._mirror.appendChild(this._container);
-    this._mirror.style.height = "0px";
-    document.getElementsByTagName("body")[0].appendChild(this._mirror);
-    //    };
-
-    // Update position of the mirror
-    window.addEventListener('resize', this.reposition.bind(this));
-    this._element.addEventListener('onfocus', this.reposition.bind(this));
-    this.reposition();
-    return this;
-  },
+    /**
+     * Move hinthelper to given character position
+     */
+    moveto : function (charpos) {
+      const e = this._element;
+      e.selectionStart = charpos;
+      e.selectionEnd = charpos;
+      e.focus();
+      return this.update();
+    },
 
 
-  // Get the right position
-  _rightPos : function () {
-    var box = this._mirror.firstChild.getBoundingClientRect();
-    return box.right - box.left;
-  },
+    /**
+     * Reposition the input mirror directly
+     * below the input box.
+     */
+    reposition : function () {
+      const inputClientRect = this._element.getBoundingClientRect();
+      const inputStyle = window.getComputedStyle(this._element, null);
+
+      const bodyClientRect = 
+            document.getElementsByTagName('body')[0].getBoundingClientRect();
+
+      // Reset position
+      const mirrorStyle = this._mirror.style;
+      mirrorStyle.left  = parseInt(inputClientRect.left) + "px";
+      mirrorStyle.top   = parseInt(inputClientRect.bottom - bodyClientRect.top) + "px";
+      mirrorStyle.width = inputStyle.getPropertyValue("width");
+
+      // These may be relevant in case of media depending css
+      mirrorStyle.paddingLeft     = inputStyle.getPropertyValue("padding-left");
+      mirrorStyle.marginLeft      = inputStyle.getPropertyValue("margin-left");
+      mirrorStyle.borderLeftWidth = inputStyle.getPropertyValue("border-left-width");
+      mirrorStyle.borderLeftStyle = inputStyle.getPropertyValue("border-left-style");
+      mirrorStyle.fontSize        = inputStyle.getPropertyValue("font-size");
+      mirrorStyle.fontFamily      = inputStyle.getPropertyValue("font-family");
+    },
 
 
-  /*
-   * Return two substrings,
-   * splitted at a given position
-   * or at the current cursor position.
-   */
-  _split : function (start) {
-    var s = this._element;
-    var value = s.value;
+    /**
+     * Get the context, which is the input
+     * field's value bounded to the
+     * cursor position.
+     */
+    context : function () {
+      return this._split()[0];
+    },
 
-    // Get start from cursor position
-    if (arguments.length === 0)
-      start = s.selectionStart;
 
-    return new Array(
-      value.substring(0, start),
-      value.substring(start, value.length)
-    );
+    // Get the right position
+    _rightPos : function () {
+      const box = this._mirror.firstChild.getBoundingClientRect();
+      return box.right - box.left;
+    },
+
+
+    /*
+     * Return two substrings,
+     * splitted at a given position
+     * or at the current cursor position.
+     */
+    _split : function (start) {
+      const s = this._element;
+      const value = s.value;
+
+      // Get start from cursor position
+      if (arguments.length === 0)
+        start = s.selectionStart;
+
+      return new Array(
+        value.substring(0, start),
+        value.substring(start, value.length)
+      );
+    }
   }
 });
diff --git a/dev/js/src/hint/lengthField.js b/dev/js/src/hint/lengthField.js
index 541b917..79c7b7d 100644
--- a/dev/js/src/hint/lengthField.js
+++ b/dev/js/src/hint/lengthField.js
@@ -1,3 +1,5 @@
+"use strict";
+
 define(['menu/lengthField', 'util'], function (lengthFieldClass) {
   return {
 
diff --git a/dev/js/src/hint/menu.js b/dev/js/src/hint/menu.js
index a9a3fb2..bf1a04b 100644
--- a/dev/js/src/hint/menu.js
+++ b/dev/js/src/hint/menu.js
@@ -1,6 +1,9 @@
 /**
  * Hint menu
  */
+
+"use strict";
+
 define([
   'menu',
 	'hint/item',
diff --git a/dev/js/src/hint/prefix.js b/dev/js/src/hint/prefix.js
index dc14d5c..197e36e 100644
--- a/dev/js/src/hint/prefix.js
+++ b/dev/js/src/hint/prefix.js
@@ -1,3 +1,5 @@
+"use strict";
+
 define(['menu/prefix'], function (prefixClass) {
   return {