Added hint and vc to collected kalamar assets
diff --git a/public/js/src/hint.js b/public/js/src/hint.js
index b5c2043..8011bee 100644
--- a/public/js/src/hint.js
+++ b/public/js/src/hint.js
@@ -1,64 +1,21 @@
 /**
- * Context aware drop down menu
- * for annotation related query hints.
+ * Hint menu for Kalamar.
  *
  * @author Nils Diewald
  */
 
-// - http://www.cryer.co.uk/resources/javascript/script20_respond_to_keypress.htm
-// - https://developers.google.com/closure/compiler/docs/js-for-compiler
-// TODO:
-// - Add help option that opens the tutorial, e.g. to the foundry
-// - http://en.wikipedia.org/wiki/JSDoc
+// requires menu.js
 
-// The last entry types (foundry, foundry/layer) is remembered and chosen by default
-
-// Show the context at the top as breadcrumbs
-// Highlight the context in the query (probably)
-// Support backspace for removing the last prefix
-
-/*
-Alternative: Use right arrow for temporary context switch and arrow back
-for temporary context removal
-*/
-
-/**
- * The KorAP namespace for project related scripts
- * @namespace
- */
 var KorAP = KorAP || {};
 
-/*
-    this._search.addEventListener(
-      "keyup",
-      function () {
-	that.update();
-      },
-      false
-    );
-*/
-
 (function (KorAP) {
   "use strict";
 
-  // Don't let events bubble up
-  if (Event.halt === undefined) {
-    Event.prototype.halt = function () {
-      this.stopPropagation();
-      this.preventDefault();
-    };
-  };
-
   // Default log message
   KorAP.log = KorAP.log || function (type, msg) {
     console.log(type + ": " + msg);
   };
 
-  /* TODO: Add event listener on windows resize to change that! */
-
-  /** @define {number} Limited view of menu items */
-  KorAP.limit = 8;
-
   /**
    * @define {regex} Regular expression for context
    */
@@ -74,14 +31,12 @@
   // Initialize hint array
   KorAP.hintArray = KorAP.hintArray || {};
 
-
-  KorAP.updateKeyDown = function (event) {
-  };
-
+  // Input field for queries
   KorAP.InputField = {
     create : function (element) {
       return Object.create(KorAP.InputField)._init(element);
     },
+
     _init : function (element) {
       this._element = element;
 
@@ -90,6 +45,7 @@
 	this._mirror = document.createElement("div");
 	this._mirror.setAttribute("id", "searchMirror");
 	this._mirror.appendChild(document.createElement("span"));
+	this._container = this._mirror.appendChild(document.createElement("div"));
 	this._mirror.style.height = "1px";
 	document.getElementsByTagName("body")[0].appendChild(this._mirror);
       };
@@ -99,39 +55,47 @@
       window.resize = function () {
 	that.reposition();
       };
-/*
-      // Add event listener for key down
-      element.addEventListener(
-	"keydown",
-	function (e) {
-//	  KorAP.updateKeyDown(e).bind(that)
-	},
-	false
-      );
-*/
+
+      that.reposition();
+
       return this;
     },
-    get mirror () {
+
+    rightPos : function () {
+      var box = this._mirror.getBoundingClientRect();
+      return box.right - box.left;
+    },
+
+    mirror : function () {
       return this._mirror;
     },
-    get element () {
+
+    container : function () {
+      return this._container;
+    },
+
+    element : function () {
       return this._element;
     },
-    get value () {
+
+    value : function () {
       return this._element.value;
     },
+
     update : function () {
       this._mirror.firstChild.textContent = this.split()[0];
     },
+
     insert : function (text) {
       var splittedText = this.split();
-      var s = this.element;
+      var s = this._element;
       s.value = splittedText[0] + text + splittedText[1];
       s.selectionStart = (splittedText[0] + text).length;
       s.selectionEnd = s.selectionStart;
       this._mirror.firstChild.textContent = splittedText[0] + text;
     },
-    // Return two substrings, splitted at current position
+
+    // Return two substrings, splitted at current cursor position
     split : function () {
       var s = this._element;
       var value = s.value;
@@ -141,13 +105,16 @@
 	value.substring(start, value.length)
       );
     },
+
     // Position the input mirror directly below the input box
     reposition : function () {
       var inputClientRect = this._element.getBoundingClientRect();
       var inputStyle = window.getComputedStyle(this._element, null);
+
+      // Reset position
       var mirrorStyle = this._mirror.style;
       mirrorStyle.left = inputClientRect.left + "px";
-      mirrorStyle.top = inputClientRect.bottom + "px";
+      mirrorStyle.top  = inputClientRect.bottom + "px";
 
       // These may be relevant in case of media depending css
       mirrorStyle.paddingLeft     = inputStyle.getPropertyValue("padding-left");
@@ -157,182 +124,14 @@
       mirrorStyle.fontSize        = inputStyle.getPropertyValue("font-size");
       mirrorStyle.fontFamily      = inputStyle.getPropertyValue("font-family");
     },
-    get context () {
+    context : function () {
       return this.split()[0];
     }
   };
 
-  KorAP.Hint = {
-    _firstTry : true,
-    create : function (param) {
-      return Object.create(KorAP.Hint)._init(param);
-    },
-    _init : function (param) {
-      param = param || {};
-      this._menu = {};
-
-      // Get input field
-      this._inputField = KorAP.InputField.create(
-	param["inputField"] || document.getElementById("q-field")
-      );
-
-      var that = this;
-      var inputFieldElement = this._inputField.element;
-
-      // Add event listener for key pressed down
-      inputFieldElement.addEventListener(
-	"keypress", function (e) {that.updateKeyPress(e)}, false
-      );
-
-      // Set Analyzer for context
-      this._analyzer = KorAP.ContextAnalyzer.create(
-	param["context"]|| KorAP.context
-      );
-
-      return this;
-    },
-    _codeFromEvent : function (e) {
-      if ((e.charCode) && (e.keyCode==0))
-	return e.charCode
-      return e.keyCode;
-    },
-    updateKeyPress : function (e) {
-      if (!this._active)
-	return;
-
-      var character = String.fromCharCode(
-	this._codeFromEvent(e)
-      );
-
-      e.halt(); // No event propagation
-
-      console.log("TODO: filter view");
-    },
-    updateKeyDown : function (e) {
-      var code = this._codeFromEvent(e)
-      
-      /*
-       * keyCodes:
-       * - Down  = 40
-       * - Esc   = 27
-       * - Up    = 38
-       * - Enter = 13
-       * - shift = 16
-       * for characters use e.key
-       */
-      switch (code) {
-      case 27: // 'Esc'
-	// TODO: menu.hide();
-	break;
-      case 40: // 'Down'
-	e.halt(); // No event propagation
-	
-	// Menu is not active
-	if (!this._active)
-	  this.popUp();
-	// Menu is active
-	else {
-	  // TODO: that.removePrefix();
-	  // TODO: menu.next();
-	};
-
-	break;
-      case 38: // "Up"
-	if (!this._active)
-	  break;
-	e.halt(); // No event propagation
-	// TODO: that.removePrefix();
-	// TODO: menu.prev();
-	break;
-      case 13: // "Enter"
-	if (!this._active)
-	  break;
-	e.halt(); // No event propagation
-	// TODO: that.insertText(menu.getActiveItem().getAction());
-	// TODO: that.removePrefix();
-    
-	// Remove menu
-	// TODO: 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 (!this._active)
-	  return;
-
-	// Surpress propagation in firefox
-	/*
-	if (e.key !== undefined && e.key.length != 1) {
-	  menu.hide();
-	};
-	*/
-      };
-    },
-
-    getMenuByContext : function () {
-      var context = this._inputField.context;
-      if (context === undefined || context.length == 0)
-	return this.menu("-");
-
-      context = this._analyzer.analyze(context);
-      if (context === undefined || context.length == 0)
-	return this.menu("-");
-
-      return this.menu(context);
-    },
-    // Return and probably init a menu based on an action
-    menu : function (action) {
-      if (this._menu[action] === undefined) {
-	if (KorAP.hintArray[action] === undefined)
-	  return;
-	this._menu[action] = KorAP.menu.create(action, KorAP.hintArray[action]);
-      };
-      return this._menu[action];
-    },
-    get inputField () {
-      return this._inputField;
-    },
-    get active () {
-      return this._active;
-    },
-    popUp : function () {
-      if (this.active)
-	return;
-
-      if (this._firstTry) {
-	this._inputField.reposition();
-	this._firstTry = false;
-      };
-
-      // update
-
-      var menu;
-      if (menu = this.getMenuByContext()) {
-	menu.show();
-// Update bounding box
-      }
-      else {
-//	this.hide();
-      };
-
-      // Focus on input field
-      this.inputField.element.focus();
-    }
-  };
 
   /**
-     Regex object for checking the context of the hint
+   * Regex object for checking the context of the hint
    */
   KorAP.ContextAnalyzer = {
     create : function (regex) {
@@ -357,498 +156,295 @@
 
 
   /**
-   * List of items for drop down menu (complete).
-   * Only a sublist of the menu is filtered (live).
-   * Only a sublist of the filtered menu is visible (shown).
+   * Hint menu item based on MenuItem
    */
-  KorAP.Menu = {
-    _position : 0,  // position in the active list
-    _active   : -1, // active item in the item list
-
-    /**
-     * Create new Menu based on the action prefix
-     * and a list of menu items.
-     *
-     * @this {Menu}
-     * @constructor
-     * @param {string} Context prefix
-     * @param {Array.<Array.<string>>} List of menu items
-     */
-    create : function (context, items) {
-      return Object.create(KorAP.Menu)._init(context, items);
+  KorAP.HintMenuItem = {
+    create : function (params) {
+      return Object.create(KorAP.MenuItem)
+	.upgradeTo(KorAP.HintMenuItem)
+	._init(params);
     },
-
-    /*
-     * Make the previous item in the menu active
-     */
-    prev : function () {
-      if (this._position == -1)
-	return;
-
-      // Set new live item
-      var oldItem = this.liveItem(this._position--);
-      oldItem.active(false);
-      var newItem = this.liveItem(this._position);
-
-      // The previous element is undefined - roll to bottom
-      if (newItem === undefined) {
-	this._position = this.liveLength - 1;
-	newItem = this.liveItem(this._position);
-	this._offset = this.liveLength - this.limit;
-	this._showItems(this._offset);
-      }
-
-      // The previous element is outside the view - roll up
-      else if (this._position < this._offset) {
-	this._removeLast();
-	this._offset--;
-	this._prepend(this._list[this._position]);
+    content : function (content) {
+      if (arguments.length === 1) {
+	this._content = content;
       };
-      newItem.active(true);
+      return this._content;
     },
-
-    /*
-     * Make the next item in the menu active
-     */
-    next : function () {
-      // No active element set
-      if (this._position == -1)
-	return;
-
-      // Set new live item
-      var oldItem = this.liveItem(this._position++);
-      oldItem.active(false);
-      var newItem = this.liveItem(this._position);
-
-      // The next element is undefined - roll to top
-      if (newItem === undefined) {
-	this._offset = 0;
-	this._position = 0;
-	newItem = this.liveItem(0);
-	this._showItems(0);
-      }
-
-      // The next element is outside the view - roll down
-      else if (this._position >= (this.limit + this._offset)) {
-	this._removeFirst();
-	this._offset++;
-	this._append(this._list[this._position]);
-      };
-      newItem.active(true);
-    },
-
-    /**
-     * Delete all visible items from the menu element
-     */
-    delete : function () {
-      var child;
-      for (var i = 0; i <= this.limit; i++)
-	if (child = this.shownItem(i))
-	  child.lowlight();
-      while (child = this._element.firstChild)
-	this._element.removeChild(child);
-    },
-
-    /**
-     * Filter the list and make it visible
-     *
-     * @param {string} Prefix for filtering the list
-     */
-    show : function (prefix) {
-      this._prefix = prefix;
-
-      // Initialize the list
-      if (!this._initList())
-	return;
-
-      // show based on offset
-      this._showItems(0);
-
-      // Set the first element to active
-      this.liveItem(0).active(true);
-      this._position = 0;
-      this._active = this._list[0];
-
-      // Add classes for rolling menus
-      this._boundary(true);
-    },
-
-    /**
-     * Get the prefix for filtering,
-     * e.g. &quot;ve"&quot; for &quot;verb&quot;
-     */
-    get prefix () {
-      return this._prefix || '';
-    },
-
-    /**
-     * Get the numerical value for limit
-     */
-    get limit () {
-      return KorAP.limit;
-    },
-
-    /**
-     * Get the context of the menue,
-     * e.g. &quot;tt/&quot; for the tree tagger menu
-     */
-    get context () {
-      return this._context;
-    },
-
-    /**
-     * Get a specific item from the complete list
-     *
-     * @param {number} index of the list item
-     */
-    item : function (index) {
-      return this._items[index]
-    },
-
-    /**
-     * Get a specific item from the filtered list
-     *
-     * @param {number} index of the list item
-     */
-    liveItem : function (index) {
-      if (this._list === undefined)
-	if (!this._initList())
-	  return;
-
-      return this._items[this._list[index]];
-    },
-    /*
-     * Get a specific item from the visible list
-     *
-     * @param {number} index of the list item
-     */
-    shownItem : function (index) {
-      if (index >= this.limit)
-	return;
-      return this.liveItem(this._offset + index);
-    },
-    get element () {
-      return this._element;
-    },
-    get length () {
-      return this._items.length;
-    },
-    get liveLength () {
-      if (this._list === undefined)
-	this._initList();
-      return this._list.length;
-    },
-    chooseHint : function (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
-	);
+    _init : function (params) {
+      if (params[0] === undefined ||
+	  params[1] === undefined)
+	throw new Error("Missing parameters");
+      
+      this._name   = params[0];
+      this._action = params[1];
+      this._lcField = ' ' + this._name.toLowerCase();
+      
+      if (params.length > 2) {
+	this._desc = params[2];
+	this._lcField += " " + this._desc.toLowerCase();
       };
 
-      hint._search.focus();
-*/
-    },
-
-    _reset : function () {
-      this._offset = 0;
-      this._pos = 0;
-      this._prefix = undefined;
-    },
-    _boundary : function (bool) {
-      this.item(this._list[0]).noMore(bool);
-      this.item(this._list[this._list.length - 1]).noMore(bool);
-    },
-    _initList : function () {
-
-console.log("..." + this._items.length);
-
-      if (this._list === undefined)
-	this._list = [];
-      else if (this._list.length != 0) {
-	this._boundary(false);
-	this._list.length = 0;
-      };
-
-      this._offset = 0;
-
-      if (this.prefix().length <= 0) {
-	for (var i = 0; i < this._items.length; i++)
-	  this._list.push(i);
-	return true;
-      };
-
-      var pos;
-      var paddedPrefix = " " + this.prefix;
-      for (pos = 0; pos < this._items.length; pos++) {
-	if ((this.item(pos).lcfield.indexOf(paddedPrefix)) >= 0)
-	  this._list.push(pos);
-      };
-      if (this._list.length == 0) {
-	for (pos = 0; pos < this._items.length; pos++) {
-	  if ((this.item(pos).lcfield.indexOf(this.prefix)) >= 0)
-	    this._list.push(pos);
-	};
-      };
-
-      // Filter was successful
-      return this._list.length > 0 ? true : false;
-    },
-
-    _removeFirst : function () {
-      this.item(this._list[this._offset]).lowlight();
-      this._element.removeChild(this._element.firstChild);
-    },
-
-    _removeLast : function () {
-      this.item(this._list[this._offset + this.limit - 1]).lowlight();
-      this._element.removeChild(this._element.lastChild);
-    },
-
-    // Append item to the shown list based on index
-    _append : function (i) {
-      var item = this.item(i);
-
-      // Highlight based on prefix
-      if (this.prefix.length > 0)
-	item.highlight(this.prefix);
-
-      // Append element
-      this.element.appendChild(item.element);
-    },
-
-    // Prepend item to the shown list based on index
-    _prepend : function (i) {
-      var item = this.item(i);
-
-      // Highlight based on prefix
-      if (this.prefix.length > 0)
-	item.highlight(this.prefix);
-
-      // Append element
-      this.element.insertBefore(
-	item.element,
-	this.element.firstChild
-      );
-    },
-    _init : function (context, items) {
-      this._context = context;
-      this._element = document.createElement("ul");
-      this._element.style.opacity = 0;
-      this.active = false;
-/*
-  Todo:
-      this._element.addEventListener("click", chooseHint, false);
-*/
-      this._items = new Array();
-      var i;
-      for (i in items)
-	this._items.push(KorAP.MenuItem.create(items[i]));
-
-      this._reset();
       return this;
     },
+    onclick : function () {
+      var m = this.menu();
+      var h = m.hint();
+      m.hide();
 
-    _showItems : function (offset) {
-      this.delete();
+      h.inputField().insert(this._action);
+      h.active = false;
 
-      // Use list
-      var shown = 0;
-      var i;
-      for (i in this._list) {
-
-	// Don't show - it's before offset
-	if (shown++ < offset)
-	  continue;
-
-	this._append(this._list[i]);
-
-	if (shown >= (this.limit + this._offset))
-	  break;
-      };
-    }
-  };
-
-
-  /**
-   * Item in the Dropdown menu
-   */
-  KorAP.MenuItem = {
-
-    /**
-     * Create a new MenuItem object.
-     *
-     * @constructor
-     * @this {MenuItem}
-     * @param {Array.<string>} An array object of name, action and
-     *   optionally a description
-     */
-    create : function (params) {
-      return Object.create(KorAP.MenuItem)._init(params);
+      h.show(true);
     },
-
-    /**
-     * Get the name of the item
-     */
-    get name () {
+    name : function () {
       return this._name;
     },
-
-    /**
-     * Get the action string
-     */
-    get action () {
+    action : function () {
       return this._action;
     },
-
-    /**
-     * Get the description of the item
-     */
-    get desc () {
+    desc : function () {
       return this._desc;
     },
-
-    /**
-     * Get the lower case field
-     */
-    get lcfield () {
-      return this._lcfield;
-    },
-
-    /**
-     * Check or set if the item is active
-     *
-     * @param {boolean|null} State of activity
-     */
-    active : function (bool) {
-      var cl = this.element.classList;
-      if (bool === undefined)
-	return cl.contains("active");
-      else if (bool)
-	cl.add("active");
-      else
-	cl.remove("active");
-    },
-
-    /**
-     * Check or set if the item is
-     * at the boundary of the menu
-     * list
-     *
-     * @param {boolean|null} State of activity
-     */
-    noMore : function (bool) {
-      var cl = this.element.classList;
-      if (bool === undefined)
-	return cl.contains("no-more");
-      else if (bool)
-	cl.add("no-more");
-      else
-	cl.remove("no-more");
-    },
-
-    /**
-     * Get the document element of the menu item
-     */
-    get element () {
+    element : function () {
       // already defined
       if (this._element !== undefined)
 	return this._element;
 
       // Create list item
       var li = document.createElement("li");
-      li.setAttribute("data-action", this._action);
+
+      if (this.onclick !== undefined) {
+	li["onclick"] = this.onclick.bind(this);
+      };
 
       // Create title
-      var name =  document.createElement("strong");
+      var name =  document.createElement("span");
       name.appendChild(document.createTextNode(this._name));
-
+      
       li.appendChild(name);
 
       // Create description
       if (this._desc !== undefined) {
 	var desc = document.createElement("span");
+	desc.classList.add('desc');
 	desc.appendChild(document.createTextNode(this._desc));
 	li.appendChild(desc);
       };
       return this._element = li;
+    }
+  };
+
+  KorAP.HintMenuPrefix = {
+    create : function (params) {
+      return Object.create(KorAP.MenuPrefix).upgradeTo(KorAP.HintMenuPrefix)._init(params);
     },
+    onclick : function () {
+      var m = this.menu();
+      var h = m.hint();
+      m.hide();
 
-    /**
-     * Highlight parts of the item
-     *
-     * @param {string} Prefix string for highlights
-     */
-    highlight : function (prefix) {
-      var e = this.element;
-      this._highlight(e.firstChild, prefix);
-      if (this._desc !== undefined)
-	this._highlight(e.lastChild, prefix);
-    },
+      h.inputField().insert(this.value());
+      h.active = false;
+    }
+  };
 
-    /**
-     * Remove highlight of the menu item
-     */
-    lowlight : function () {
-      var e = this.element;
-      e.firstChild.innerHTML = this._name;
-      if (this._desc !== undefined)
-	e.lastChild.innerHTML = this._desc;
-    },
+  KorAP.HintMenu = {
+    create : function (hint, context, params) {
+      var obj = Object.create(KorAP.Menu)
+	.upgradeTo(KorAP.HintMenu)
+	._init(KorAP.HintMenuItem, KorAP.HintMenuPrefix, params);
+      obj._context = context;
+      obj._element.classList.add('hint');
+      obj._hint = hint;
 
-    // Initialize menu item
-    _init : function (params) {
-      if (params[0] === undefined || params[1] === undefined)
-	throw new Error("Missing parameters");
+      // This is only domspecific
+      obj.element().addEventListener('blur', function (e) {
+	this.menu.hide();
+      });
 
-      this._name   = params[0];
-      this._action = params[1];
-      this._lcfield = " " + this._name.toLowerCase();
-
-      if (params.length > 2) {
-	this._desc = params[2];
-	this._lcfield += " " + this._desc.toLowerCase();
+      // Focus on input field on hide
+      obj.onHide = function () {
+	var input = this._hint.inputField();
+	input.element().focus();
       };
+
+      return obj;
+    },
+    // Todo: Is this necessary?
+    context : function () {
+      return this._context;
+    },
+    hint : function () {
+      return this._hint;
+    }
+  };
+
+
+  /**
+   * KorAP.Hint.create({
+   *   inputField : node,
+   *   context : context regex
+   * });
+   */
+  KorAP.Hint = {
+
+    // Some variables
+    // _firstTry : true,
+    active : false,
+
+    create : function (param) {
+      return Object.create(KorAP.Hint)._init(param);
+    },
+
+    _init : function (param) {
+      param = param || {};
+
+      // Holds all menus per prefix context
+      this._menu = {};
+
+      // Get input field
+      this._inputField = KorAP.InputField.create(
+	param["inputField"] || document.getElementById("q-field")
+      );
+
+      var inputFieldElement = this._inputField.element();
+
+      var that = this;
+
+      // Add event listener for key pressed down
+      inputFieldElement.addEventListener(
+	"keypress", function (e) {
+	  var code = _codeFromEvent(e);
+	  if (code === 40) {
+	    that.show(false);
+	    e.halt();
+	  };
+	}, false
+      );
+
+      // Move infobox 
+      inputFieldElement.addEventListener(
+	"keyup", function (e) {
+	  var input = that._inputField;
+	  input.update();
+	  input.container().style.left = input.rightPos() + 'px';
+	}
+      );
+
+      // Set Analyzer for context
+      this._analyzer = KorAP.ContextAnalyzer.create(
+	param["context"] || KorAP.context
+      );
       return this;
     },
 
-    // Highlight a certain element of the menu item
-    _highlight : function (elem, prefix) {
-      var text   = elem.firstChild.nodeValue;
-      var textlc = text.toLowerCase();
-      var pos    = textlc.indexOf(prefix);
-      if (pos >= 0) {
+    inputField : function () {
+      return this._inputField;
+    },
 
-	// First element
-	elem.firstChild.nodeValue = pos > 0 ? text.substr(0, pos) : "";
+    /**
+     * A new update by keypress
+     */
+    /*
+updateKeyPress : function (e) {
+      if (!this._active)
+	return;
 
-	// Second element
-	var hl = document.createElement("em");
-	hl.appendChild(
-	  document.createTextNode(text.substr(pos, prefix.length))
+      var character = String.fromCharCode(_codeFromEvent(e));
+
+      e.halt(); // No event propagation
+
+      // Only relevant for key down
+      console.log("TODO: filter view");
+    },
+    */
+
+    // updateKeyDown : function (e) {},
+
+    /**
+     * Return hint menu and probably init based on an action
+     */
+    menu : function (action) {
+
+      if (this._menu[action] === undefined) {
+
+	// No matching hint menu
+	if (KorAP.hintArray[action] === undefined)
+	  return;
+
+	// Create matching hint menu
+	this._menu[action] = KorAP.HintMenu.create(
+	  this, action, KorAP.hintArray[action]
 	);
-	elem.appendChild(hl);
 
-	// Third element
-	elem.appendChild(
-	  document.createTextNode(text.substr(pos + prefix.length))
-	);
+      };
+
+      // Return matching hint menu
+      return this._menu[action];
+    },
+
+    /**
+     * Get the correct menu based on the context
+     */
+    contextMenu : function (ifContext) {
+      var context = this._inputField.context();
+      if (context === undefined || context.length == 0)
+	return ifContext ? undefined : this.menu("-");
+
+      context = this._analyzer.test(context);
+      if (context === undefined || context.length == 0)
+	return ifContext ? undefined : this.menu("-");
+
+      return this.menu(context);
+    },
+
+
+    /**
+     * Show the menu
+     */
+    show : function (ifContext) {
+
+      // Menu is already active
+      if (this.active)
+	return;
+
+      // Initialize the menus position
+      /*
+      if (this._firstTry) {
+	this._inputField.reposition();
+	this._firstTry = false;
+      };
+      */
+
+      // update
+
+      // Get the menu
+      var menu;
+      if (menu = this.contextMenu(ifContext)) {
+	this._inputField.container().appendChild(menu.element());
+	menu.show('');
+	menu.focus();
+// Update bounding box
+/*
+      }
+      else if (!ifContext) {
+	//	this.hide();
+      };
+*/
+      // Focus on input field
+      // this.inputField.element.focus();
       };
     }
   };
+
+
+  /**
+   * Return keycode based on event
+   */
+  function _codeFromEvent (e) {
+    if ((e.charCode) && (e.keyCode==0))
+      return e.charCode
+    return e.keyCode;
+  };
+
 }(this.KorAP));
diff --git a/public/js/src/match.js b/public/js/src/match.js
index f8fdd91..b974031 100644
--- a/public/js/src/match.js
+++ b/public/js/src/match.js
@@ -195,7 +195,7 @@
       // Add information, unless it already exists
       info.addEventListener('click', function (e) {
 	e.halt();
-	that.info();
+	that.info().toggle();
       });
 
       ul.appendChild(close);
@@ -204,7 +204,6 @@
       return true;
     },
 
-
     /**
      * Close info view
      */
@@ -219,7 +218,6 @@
     },
 
 
-
     /**
      * Get and open associated match info.
      */
@@ -235,14 +233,9 @@
 	return this._info;
 
       // Info is already activated
-      if (this._info._elemet !== undefined)
+      if (this._info._element !== undefined)
 	return this._info;
 
-      // Append element to match
-      this._element.children[0].appendChild(
-	this._info.element()
-      );
-
       return this._info;
     },
 
@@ -276,10 +269,10 @@
      */
     _init : function (match) {
       this._match = match;
+      this.opened = false;
       return this;
     },
 
-
     /**
      * Get match object
      */
@@ -287,6 +280,24 @@
       return this._match;
     },
 
+    toggle : function () {
+      if (this.opened == true) {
+	this._match.element().children[0].removeChild(
+	  this.element()
+	);
+	this.opened = false;
+      }
+      else {
+	// Append element to match
+	this._match.element().children[0].appendChild(
+	  this.element()
+	);
+	this.opened = true;
+      };
+
+      return this.opened;
+    },
+
 
     /**
      * Retrieve and parse snippet for table representation
diff --git a/public/js/src/menu.js b/public/js/src/menu.js
index a7271c5..3267b69 100644
--- a/public/js/src/menu.js
+++ b/public/js/src/menu.js
@@ -52,7 +52,7 @@
       for (var i = 0; i < this._items.length; i++) {
 	delete this._items[i]["_menu"];
       };
-      
+      delete this._prefix['_menu'];
     },
 
     focus : function () {
@@ -147,11 +147,13 @@
       else
 	this._prefix = KorAP.MenuPrefix.create();
 
+      this._prefix._menu = this;
+
       var e = document.createElement("ul");
       e.style.opacity = 0;
       e.style.outline = 0;
       e.setAttribute('tabindex', 0);
-      e.setAttribute('class', 'menu');
+      e.classList.add('menu');
       e.appendChild(this._prefix.element());
 
       // This has to be cleaned up later on
@@ -274,9 +276,13 @@
       this.active = false;
       this.delete();
       this._element.style.opacity = 0;
+      this.onHide();
       /* this._element.blur(); */
     },
 
+    // To be override
+    onHide : function () {},
+
     // Initialize the list
     _initList : function () {
 
@@ -676,7 +682,6 @@
       return this;
     },
 
-
     content : function (content) {
       if (arguments.length === 1)
 	this._content = document.createTextNode(content);
@@ -737,8 +742,9 @@
       var li = document.createElement("li");
 
       // Connect action
-      if (this.onclick !== undefined)
+      if (this["onclick"] !== undefined) {
 	li["onclick"] = this.onclick.bind(this);
+      };
 
       // Append template
       li.appendChild(this.content());
@@ -866,7 +872,8 @@
       this._element = document.createElement('span');
       this._element.classList.add('pref');
       // Connect action
-      if (this.onclick !== undefined)
+
+      if (this["onclick"] !== undefined)
 	this._element["onclick"] = this.onclick.bind(this);
 
       return this;
@@ -898,13 +905,16 @@
       else
 	cl.remove("active");
     },
+
     element : function () {
       return this._element;
     },
+
     isSet : function () {
       return this._string.length > 0 ?
 	true : false;
     },
+
     value : function (string) {
       if (arguments.length === 1) {
 	this._string = string;
@@ -912,11 +922,14 @@
       };
       return this._string;
     },
+
     add : function (string) {
       this._string += string;
       this._update();
     },
-    onclick : function (e) {},
+
+    onclick : function () {},
+
     backspace : function () {
       if (this._string.length > 1) {
 	this._string = this._string.substring(
@@ -928,6 +941,13 @@
       };
 
       this._update();
+    },
+
+    /**
+     * Return menu list.
+     */
+    menu : function () {
+      return this._menu;
     }
   };
 
diff --git a/public/js/src/util.js b/public/js/src/util.js
index 0f0f114..3adf5a0 100644
--- a/public/js/src/util.js
+++ b/public/js/src/util.js
@@ -31,35 +31,36 @@
 (function (KorAP) {
   "use strict";
 
+
+  /**
+   * Initialize user interface elements
+   */
   KorAP.init = function () {
 
     /**
      * Add actions to match entries
      */
-    var inactiveLi = document.querySelectorAll('#search > ol > li:not(.active)');
+    var inactiveLi = document.querySelectorAll(
+      '#search > ol > li:not(.active)'
+    );
     var i = 0;
     for (i = 0; i < inactiveLi.length; i++) {
-      inactiveLi[i].addEventListener('click', function () {
-
-	if (this._match !== undefined) {
+      inactiveLi[i].addEventListener('click', function (e) {
+	if (this._match !== undefined)
 	  this._match.open();
-	  console.log('already open');
-	}
-	else {
+	else
 	  KorAP.Match.create(this).open();
-	  console.log('newly open');
-	}
-
-	
+	e.halt();
       });
     };
 
+
     /**
      * Toggle the alignment (left <=> right)
      */
     if (i > 0) {
       var br = document.getElementById('button-right');
-      if (br !== undefined) {
+      if (br !== null) {
 	var toggle = document.createElement('a');
 	toggle.setAttribute('title', 'toggle Alignment');
 	// Todo: Reuse old alignment from cookie!
@@ -78,57 +79,11 @@
 	br.appendChild(toggle);
       };
     };
+
+    /**
+     * Init hint helper
+     */
+    KorAP.Hint.create();
   };
 
-  /*
-  function _openMatch (e) {
-    e.halt();
-    this.classList.add("active");
-    var matchElement = this;
-
-    // Todo: Add object to element
-    var ul = document.createElement('ul');
-    ul.classList.add('action', 'right');
-    matchElement.appendChild(ul);
-
-    // Todo:: Localize!
-    var close = document.createElement('li');
-    close.appendChild(document.createElement('span'))
-      .appendChild(document.createTextNode('Close'));
-    close.classList.add('close');
-    close.setAttribute('title', 'Close');
-
-    close.addEventListener('click', function (ie) {
-      ie.halt();
-      var match = matchElement['_match'];
-      match.destroy();
-      matchElement.classList.remove('active');
-      matchElement.removeChild(ul);
-    });
-
-    // Todo:: Localize!
-    var info = document.createElement('li');
-    info.appendChild(document.createElement('span'))
-      .appendChild(document.createTextNode('Info'));
-    info.classList.add('info');
-    info.setAttribute('title', 'Information');
-
-    // Add information, unless it already exists
-    info.addEventListener('click', function (ie) {
-      ie.halt();
-      KorAP.Match.create(matchElement).addInfo();
-    });
-
-    ul.appendChild(close);
-    ul.appendChild(info);
-  };
-*/
-
-  /**
-  function _closeMatch (e) {
-    e.halt();
-    this.parentNode.parentNode.classList.remove("active");
-  };
-  */
-
 }(this.KorAP));