Integrated hint alerts in the main frame
diff --git a/dev/demo/all.html b/dev/demo/all.html
index a01fcc3..67c50fd 100644
--- a/dev/demo/all.html
+++ b/dev/demo/all.html
@@ -27,6 +27,7 @@
 		 placeholder="Find ..."
 		 name="q"
 		 id="q-field"
+		 value="abcdefghijklmnopqrstuvwxyz"
 		 autofocus="autofocus" />
 	  <button type="submit"><span>Go</span></button>
 	  <!-- <i class="fa fa-arrow-circle-down show-hint" onclick="hint.popUp()"></i> -->
@@ -85,7 +86,7 @@
       </div>
 
       <script>
-	KorAP.koralQuery = {"matches":["..."],"query":{"wrap":{"layer":"orth","match":"match:eq","foundry":"opennlp","key":"Goethe","@type":"koral:term"},"@type":"koral:token"},"meta":{"serialQuery":"tokens:s:Goethe","startIndex":0,"version":"unknown","itemsPerPage":25,"context":"p","benchmark":"0.103553092 s","totalResults":-1}};
+	KorAP.koralQuery = {"matches":["..."],"errors":[[302,"Parantheses\/brackets unbalanced.","20"]],"query":{"wrap":{"layer":"orth","match":"match:eq","foundry":"opennlp","key":"Goethe","@type":"koral:term"},"@type":"koral:token"},"meta":{"serialQuery":"tokens:s:Goethe","startIndex":0,"version":"unknown","itemsPerPage":25,"context":"p","benchmark":"0.103553092 s","totalResults":-1}};
       </script>
 
       <div id="search">
diff --git a/dev/demo/hintdemo.js b/dev/demo/hintdemo.js
index fd80b43..3eb38cc 100644
--- a/dev/demo/hintdemo.js
+++ b/dev/demo/hintdemo.js
@@ -16,5 +16,5 @@
 
 function demoAlert (pos, msg) {
   if (hint !== undefined)
-    hint.charAlert(pos, msg);
+    hint.alert(pos, msg);
 }
diff --git a/dev/js/spec/hintSpec.js b/dev/js/spec/hintSpec.js
index 554b61d..c944410 100644
--- a/dev/js/spec/hintSpec.js
+++ b/dev/js/spec/hintSpec.js
@@ -53,9 +53,10 @@
 
     afterAll(function () {
       try {
-	document.getElementsByTagName("body")[0].removeChild(
-	  document.getElementById("searchMirror")
-	);
+	var mirrors = document.querySelectorAll(".hint.mirror");
+	for (var i in mirrors) {
+	  mirrors[i].parentNode.removeChild(mirrors[i])
+	};
       }
       catch (e) {};
     });
@@ -168,6 +169,34 @@
 
       expect(hint).toBeTruthy();
     });
+
+    it('should alert at char pos', function () {
+      var hint = hintClass.create({
+	inputField : input
+      });
+
+      expect(hint.active()).toBeFalsy();
+
+      expect(hint.alert(4, 'That does not work!')).toBeTruthy();
+
+      expect(hint.active()).toBeTruthy();
+
+      var container = hint.inputField().container();
+      expect(container.firstChild.classList.contains('hint')).toBe(true);
+      expect(container.firstChild.classList.contains('alert')).toBe(true);
+      expect(container.firstChild.textContent).toEqual('That does not work!');
+      expect(hint.inputField().mirrorValue()).toEqual('abcd');
+
+      expect(hint.alert(4, 'That does not work!')).toBeFalsy();
+
+      // Update - meaning: hide alert
+      hint.update();
+
+      expect(hint.alert().active).toBeFalsy();
+
+      expect(hint.active()).toBeFalsy();
+
+    });
   });
 
   describe('KorAP.HintMenuItem', function () {
diff --git a/dev/js/src/hint.js b/dev/js/src/hint.js
index 4f55d0f..455312d 100644
--- a/dev/js/src/hint.js
+++ b/dev/js/src/hint.js
@@ -14,10 +14,12 @@
   'hint/input',
   'hint/menu',
   'hint/contextanalyzer',
+  'hint/alert',
   'util'
 ], function (inputClass, 
-	     menuClass, 
-	     analyzerClass) {
+	     menuClass,
+	     analyzerClass,
+	     alertClass) {
   "use strict";
 
   /**
@@ -52,7 +54,7 @@
 
     // Some variables
     // _firstTry : true,
-    active : false,
+    // active : false,
 
     /**
      * Create new hint helper.
@@ -66,29 +68,27 @@
       param = param || {};
 
       // Holds all menus per prefix context
-      this._menu = {};
+      this._menu   = {};
+      this._alert  = alertClass.create();
+      this._active = false;
 
       // Get input field
       var 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 that = this;
+      var c = this._inputField.container();
 
-      // Add event listener for key pressed down
-      inputFieldElement.addEventListener(
-	"keydown", function (e) {
-	  var code = _codeFromEvent(e);
-	  if (code === 40) {
-	    that.show(false);
-	    e.halt();
-	  };
-	}, false
-      );
+      // create alert
+      c.appendChild(this._alert.element());
+
+
+      var that = this;
 
       this._inputField.container().addEventListener('click', function (e) {
 	if (!this.classList.contains('active')) {
@@ -96,14 +96,22 @@
 	};
       });
 
-      var _up = function (e) {
-	var input = that._inputField;
-	input.update();
+      var _down = function (e) {
+	var code = _codeFromEvent(e);
+	if (code === 40) {
+	  this.show(false);
+	  e.halt();
+	};
       };
 
       // Move infobox
-      inputFieldElement.addEventListener("keyup", _up);
-      inputFieldElement.addEventListener("click", _up);
+      inputFieldElement.addEventListener("keyup", this.update.bind(this));
+      inputFieldElement.addEventListener("click", this.update.bind(this));
+
+      // Add event listener for key pressed down
+      inputFieldElement.addEventListener(
+	"keydown", _down.bind(this), false
+      );
 
       // Set Analyzer for context
       this._analyzer = analyzerClass.create(
@@ -124,15 +132,37 @@
     /**
      * Altert at a specific character position.
      */
-    charAlert : function (charPos, msg) {
+    alert : function (charPos, msg) {
+
+      if (arguments.length === 0)
+	return this._alert;
+
+      // Do not alert if already alerted!
+      if (this._alert.active)
+	return false;
+
+      // Move to the correct position
       this._inputField.moveto(charPos);
-      var c = this._inputField.container();
-      c.classList.add('active');
-      var error = c.appendChild(document.createElement('div'));
-      error.classList.add('alert', 'hint');
-      error.appendChild(document.createTextNode(msg));
+
+      // Set container to active (aka hide the hint helper button)
+
+      this._alert.show(msg);
+      this.active(true);
+      return true;
     },
 
+    _unshowAlert : function () {
+      this._alert.unshow();
+      this.active(false);
+    },
+
+    update : function () {
+      this._inputField.update();
+      if (this._alert.unshow())
+	this.active(false);
+    },
+
+
     /**
      * Return hint menu and probably init based on an action
      */
@@ -169,6 +199,21 @@
       return this.menu(context);
     },
 
+    active : function (bool) {
+      if (arguments.length === 1) {
+	var c = this._inputField.container();
+	if (bool && !this._active) {
+	  c.classList.add('active');
+	  this._active = true;
+	}
+	else {
+	  c.classList.remove('active');
+	  this._active = false;
+	}
+      };
+      return this._active;
+    },
+
 
     /**
      * Show the menu.
@@ -178,20 +223,30 @@
     show : function (ifContext) {
 
       // Menu is already active
-      if (this.active)
-	return;
+      if (this.active()) {
+
+	// Alert is not active
+	if (!this._alert.unshow())
+	  return;
+      };
 
       // Get the menu
       var menu;
       if (menu = this.contextMenu(ifContext)) {
 	var c = this._inputField.container();
-	c.classList.add('active');
+	this.active(true);
+	// c.classList.add('active');
 	c.appendChild(menu.element());
 	menu.show();
 	menu.focus();
 	// Focus on input field
 	// this.inputField.element.focus();
       };
+    },
+
+    unshow : function () {
+      this.active(false);
+      this.inputField().element().focus();
     }
   };
 });
diff --git a/dev/js/src/hint/alert.js b/dev/js/src/hint/alert.js
new file mode 100644
index 0000000..a5b43de
--- /dev/null
+++ b/dev/js/src/hint/alert.js
@@ -0,0 +1,35 @@
+/**
+ * Hint menu alert
+ */
+define(function () {
+  "use strict";
+  return {
+    create : function (msg) {
+      return Object.create(this)._init(msg);
+    },
+    _init : function (msg) {
+      this.active = false;
+      this._element = document.createElement('div');
+      this._element.style.opacity = 0;
+      this._element.classList.add('alert', 'hint');
+      return this;
+    },
+    show : function (msg) {
+      this._element.textContent = msg;
+      this.active = true;
+      this._element.style.opacity = 1;
+    },
+
+    unshow : function () {
+      if (!this.active)
+	return false;
+      this._element.style.opacity = 0;
+      this.active = false;
+      return true;
+    },
+
+    element : function () {
+      return this._element;
+    }
+  }
+});
diff --git a/dev/js/src/hint/input.js b/dev/js/src/hint/input.js
index 6e02793..7145919 100644
--- a/dev/js/src/hint/input.js
+++ b/dev/js/src/hint/input.js
@@ -49,6 +49,13 @@
     return this._element.value;
   },
 
+  /**
+   * Get the value of the input field mirror.
+   */
+  mirrorValue : function () {
+    return this._mirror.firstChild.textContent;
+  },
+
 
   /**
    * Update the mirror content.
@@ -130,14 +137,14 @@
 
     // Create mirror for searchField
     // This is important for positioning
-    if ((this._mirror = document.getElementById("searchMirror")) === null) {
+    // if ((this._mirror = document.getElementById("searchMirror")) === null) {
       this._mirror = document.createElement("div");
-      this._mirror.setAttribute("id", "searchMirror");
+      this._mirror.classList.add('hint', 'mirror');
       this._mirror.appendChild(document.createElement("span"));
       this._container = this._mirror.appendChild(document.createElement("div"));
       this._mirror.style.height = "0px";
       document.getElementsByTagName("body")[0].appendChild(this._mirror);
-    };
+//    };
 
     // Update position of the mirror
     window.addEventListener('resize', this.reposition.bind(this));
diff --git a/dev/js/src/hint/item.js b/dev/js/src/hint/item.js
index 7d97334..317dae7 100644
--- a/dev/js/src/hint/item.js
+++ b/dev/js/src/hint/item.js
@@ -54,8 +54,6 @@
       var input = h.inputField();
       input.insert(this._action).update();
 
-      h.active = false;
-
       e.halt();
 
       h.show(true);
diff --git a/dev/js/src/hint/menu.js b/dev/js/src/hint/menu.js
index 7d89e51..df27a11 100644
--- a/dev/js/src/hint/menu.js
+++ b/dev/js/src/hint/menu.js
@@ -28,9 +28,7 @@
 
       // Focus on input field on hide
       obj.onHide = function () {
-	var input = this._hint.inputField();
-	input.container().classList.remove('active');
-	input.element().focus();
+	this._hint.unshow();
       };
 
       return obj;
diff --git a/dev/js/src/init.js b/dev/js/src/init.js
index c003a1d..3b911b6 100644
--- a/dev/js/src/init.js
+++ b/dev/js/src/init.js
@@ -178,6 +178,17 @@
 	};
       };
 
+      if (KorAP.koralQuery["errors"]) {
+	var errors = KorAP.koralQuery["errors"];
+	for (var i in errors) {
+	  if (errors[i][0] === 302) {
+	    obj.hint = hintClass.create();
+	    obj.hint.alert(errors[i][2], errors[i][1]);
+	    break;
+	  }
+	}
+      };
+
       // Session has KQ visibility stored
       if (show["kq"])
 	showKQ.apply();
@@ -303,7 +314,8 @@
      */
     // Todo: Pass an element, so this works with
     // tutorial pages as well!
-    obj.hint = hintClass.create();
+    if (obj.hint === undefined)
+      obj.hint = hintClass.create();
 
     return obj;
   });
diff --git a/dev/scss/header/hint.scss b/dev/scss/header/hint.scss
index a50ffbf..5659a2e 100644
--- a/dev/scss/header/hint.scss
+++ b/dev/scss/header/hint.scss
@@ -28,7 +28,7 @@
 }
 
 div.alert.hint {
-  position: relative;
+  position: absolute;
   background-color: rgba(254,26,0,.9); // from alertify
   color: $nearly-white;
   padding: $item-padding;
@@ -38,6 +38,10 @@
     radius: $standard-border-radius;
     top-left-radius: 0;
   }
+  width: auto;
+  min-width: 10em;
+  max-width: 23em !important;
+  transition: opacity 0.2s ease 0s;
 }
 
 div.hint.alert::before {
@@ -55,11 +59,7 @@
   }
 }
 
-
-
-
-
-#searchMirror {
+.hint.mirror {
   position: absolute;
   left: 0;
   top: 0;
@@ -72,7 +72,7 @@
     white-space: pre-wrap;
     overflow: hidden;
   }
-  // Todo: Besser nur, wenn im Focus
+  // TODO: Only in focus
   > div {
     position: absolute;
     display: block;