Improved slider behaviour and fixed prefix handling
diff --git a/Changes b/Changes
index 985c69e..c861526 100755
--- a/Changes
+++ b/Changes
@@ -1,3 +1,9 @@
+0.20 2016-05-23
+	- Improved menu using sliders.
+	- Improved menu to make prefixes chooseable,
+	  even if list can't be filtered.
+	- Fixed choosable prefixes in hint menu.
+
 0.19 2016-04-28
         - Improved datepicker to enter date strings.
 	- Improved menus to have fixed length depending
diff --git a/dev/demo/menudemo.js b/dev/demo/menudemo.js
index 1e7fd7e..244aff4 100644
--- a/dev/demo/menudemo.js
+++ b/dev/demo/menudemo.js
@@ -74,8 +74,7 @@
 
   document.getElementById('menu').appendChild(menu.element());
 
-  menu._active = 3;
   menu.limit(3);
-  menu.show('');
+  menu.show(0); // Make this a 3
   menu.focus();
 });
diff --git a/dev/js/spec/hintSpec.js b/dev/js/spec/hintSpec.js
index acfa6f9..6cfd218 100644
--- a/dev/js/spec/hintSpec.js
+++ b/dev/js/spec/hintSpec.js
@@ -376,6 +376,7 @@
 
       menuItem.lowlight();
       expect(menuItem.element().innerHTML).toEqual("<span>CoreNLP</span><span class=\"desc\">This is my Example</span>");
+
     });
   });
 
@@ -390,7 +391,6 @@
     ];
 
     it('should be initializable', function () {
-
       var menu = menuClass.create(null, "cnx/", list);
       expect(menu.element().nodeName).toEqual('UL');
       expect(menu.element().style.opacity).toEqual("0");
@@ -399,6 +399,7 @@
 
       // view
       menu.show();
+      expect(menu.prefix()).toBe('');
 
       // First element in list
       expect(menu.item(0).active()).toBe(true);
@@ -411,6 +412,18 @@
       // Last element in list
       expect(menu.item(menu.length() - 1).active()).toBe(false);
       expect(menu.item(menu.length() - 1).noMore()).toBe(true);
+
+      expect(menu.shownItem(0).active()).toBeTruthy();
+      expect(menu.shownItem(0).lcField()).toEqual(' constituency example 1');
+      expect(menu.shownItem(1).lcField()).toEqual(' lemma');
+      expect(menu.shownItem(2).lcField()).toEqual(' morphology example 2');
+
+      menu.next();
+      expect(menu.shownItem(1).active()).toBeTruthy();
+
+      menu.next();
+      expect(menu.shownItem(2).active()).toBeTruthy();
+
     });
   });
 });
diff --git a/dev/js/spec/menuSpec.js b/dev/js/spec/menuSpec.js
index b8c7d44..e6d5049 100644
--- a/dev/js/spec/menuSpec.js
+++ b/dev/js/spec/menuSpec.js
@@ -767,10 +767,12 @@
     });
 
 
-    it('shouldn\'t be viewable with failing prefix', function () {
+    it('should choose prefix with failing prefix', function () {
       var menu = KorAP.HintMenu.create("cnx/", list);
       menu.limit(2);
-      expect(menu.prefix("exit").show()).toBe(false);    
+      expect(menu.prefix("exit").show()).toBe(true);
+      expect(menu.shownItem(0)).toBeUndefined();
+      expect(menu._prefix.active()).toBe(true);
     });
 
 
@@ -1121,17 +1123,50 @@
       expect(menu.shownItem(3)).toBe(undefined);
     });
 
+    it('should show screens by offset when prefixed', function () {
+      var menu = KorAP.HintMenu.create('cnx/', demolist);
+      menu.limit(3);
+      expect(menu.prefix("e").show()).toBe(true);
+      expect(menu.shownItem(0).active()).toBe(false);
+      expect(menu.shownItem(1).active()).toBe(false);
+      expect(menu.shownItem(2).active()).toBe(false);
+
+      expect(menu.shownItem(0).element().innerHTML).toEqual('<strong>Tit<mark>e</mark>l</strong>');
+      menu.screen(1);
+      expect(menu.shownItem(0).element().innerHTML).toEqual('<strong>Unt<mark>e</mark>rtit<mark>e</mark>l</strong>');
+    });
+
 
     xit('should be page downable');
     xit('should be page upable');
 
-    xit('should scroll to a chosen value', function () {
+    it('should scroll to a chosen value', function () {
       var menu = KorAP.OwnMenu.create(demolist);
       menu.limit(3);
-      this._active = 5;
+
+      // Choose value 1
+      expect(menu.show(1)).toBe(true);
+
+      expect(menu.shownItem(0).active()).toBe(false);
+      expect(menu.shownItem(0).lcField()).toEqual(' titel');
+      expect(menu.shownItem(1).active()).toBe(true);
+      expect(menu.shownItem(2).active()).toBe(false);
+      expect(menu.shownItem(3)).toBe(undefined);
+
+      // Choose value 2
+      expect(menu.show(2)).toBe(true);
+
+      expect(menu.shownItem(0).active()).toBe(false);
+      expect(menu.shownItem(0).lcField()).toEqual(' titel');
+      expect(menu.shownItem(1).active()).toBe(false);
+      expect(menu.shownItem(2).active()).toBe(true);
+      expect(menu.shownItem(3)).toBe(undefined);
+
     });
 
     xit('should highlight a chosen value');
+
+    xit('should move the viewport to active, if active is not in the viewport');
   });
 
 
@@ -1243,7 +1278,7 @@
   });
 
   describe('KorAP.Slider', function () {
-    it('should be correctly initializable', function () {
+    it('should correctly be initializable', function () {
       var list = [
 	["Constituency"],
 	["Lemma"],
@@ -1264,6 +1299,23 @@
       expect(menu.shownItem(2).active()).toBe(false);
       expect(menu.slider().offset()).toEqual(0);
       expect(menu.position).toEqual(0);
+    });
+
+    it('should correctly move on arrow keys', function () {
+      var list = [
+	["Constituency"],
+	["Lemma"],
+	["Morphology"],
+	["Part-of-Speech"],
+	["Syntax"]
+      ];
+
+      var menu = KorAP.OwnMenu.create(list);
+
+      menu._firstActive = true;
+      menu.limit(3);
+
+      expect(menu.show()).toBe(true);
 
       menu.next();
       expect(menu.shownItem(0).active()).toBe(false);
@@ -1301,7 +1353,57 @@
       expect(menu.position).toEqual(0);
 
       expect(menu.slider()._slider.style.height).toEqual('60%');
+    });
 
+    it('should correctly move the list on mousemove', function () {
+      var list = [
+	["Constituency"],
+	["Lemma"],
+	["Morphology"],
+	["Part-of-Speech"],
+	["Syntax"]
+      ];
+
+      var menu = KorAP.OwnMenu.create(list);
+
+      menu._firstActive = true;
+      menu.limit(3);
+
+      expect(menu.show()).toBe(true);
+
+      expect(menu.shownItem(0).active()).toBe(true);
+      expect(menu.shownItem(1).active()).toBe(false);
+      expect(menu.shownItem(2).active()).toBe(false);
+      expect(menu.slider().offset()).toEqual(0);
+
+      // This will normally be done on 
+      menu.slider()._rulerHeight = 100;
+      menu.slider()._sliderHeight = 40;
+      expect(menu.slider().length()).toEqual(5);
+
+      menu.slider().movetoRel(10);
+      expect(menu.slider().offset()).toEqual(0);
+      expect(menu.shownItem(0).active()).toBe(true);
+      expect(menu.shownItem(0).lcField()).toEqual(' constituency');
+      menu.slider().movetoRel(24);
+      expect(menu.slider().offset()).toEqual(0);
+      menu.slider().movetoRel(25);
+      expect(menu.slider().offset()).toEqual(0);
+
+      menu.slider().movetoRel(30);
+      expect(menu.slider().offset()).toEqual(1);
+      menu.slider().movetoRel(59);
+      expect(menu.slider().offset()).toEqual(1);
+      expect(menu.shownItem(0).active()).toBe(false);
+      expect(menu.shownItem(0).lcField()).toEqual(' lemma');
+
+      // Everything > 60 is offset 2
+      menu.slider().movetoRel(60);
+      expect(menu.slider().offset()).toEqual(2);
+      menu.slider().movetoRel(180);
+      expect(menu.slider().offset()).toEqual(2);
+      expect(menu.shownItem(0).active()).toBe(false);
+      expect(menu.shownItem(0).lcField()).toEqual(' morphology');
     });
   });
 });
diff --git a/dev/js/src/hint.js b/dev/js/src/hint.js
index 45d2169..1db2fd0 100644
--- a/dev/js/src/hint.js
+++ b/dev/js/src/hint.js
@@ -162,7 +162,7 @@
 	var c = this._inputField.container();
 	c.classList.add('active');
 	c.appendChild(menu.element());
-	menu.show('');
+	menu.show();
 	menu.focus();
       // Focus on input field
       // this.inputField.element.focus();
diff --git a/dev/js/src/hint/prefix.js b/dev/js/src/hint/prefix.js
index 7586617..bdc7bdf 100644
--- a/dev/js/src/hint/prefix.js
+++ b/dev/js/src/hint/prefix.js
@@ -14,10 +14,11 @@
      */
     onclick : function () {
       var m = this.menu();
+      var value = this.value();
       var h = m.hint();
       m.hide();
 
-      h.inputField().insert(this.value());
+      h.inputField().insert(value);
       h.active = false;
     }
   };
diff --git a/dev/js/src/init.js b/dev/js/src/init.js
index 1ead697..61e783b 100644
--- a/dev/js/src/init.js
+++ b/dev/js/src/init.js
@@ -123,7 +123,7 @@
     };
 
     var result = document.getElementById('resultinfo');
-    var resultButton;
+    var resultButton = null;
     if (result != null) {
       resultButton = result.appendChild(document.createElement('div'));
       resultButton.classList.add('result', 'button'); 
@@ -248,11 +248,12 @@
     };
 
     // Initialize queries for document
-    if (obj.tutorial)
+    if (obj.tutorial) {
       obj.tutorial.initQueries(document);
 
-    // Initialize documentation links
-    obj.tutorial.initDocLinks(document);
+      // Initialize documentation links
+      obj.tutorial.initDocLinks(document);
+    };
 
 
     /**
diff --git a/dev/js/src/match/info.js b/dev/js/src/match/info.js
index 97624dc..9d1922a 100644
--- a/dev/js/src/match/info.js
+++ b/dev/js/src/match/info.js
@@ -288,7 +288,7 @@
       span.appendChild(treeElement);
 
       span.addEventListener('click', function (e) {
-	treemenu.show('');
+	treemenu.show();
 	treemenu.focus();
       });
       
diff --git a/dev/js/src/menu.js b/dev/js/src/menu.js
index b9672c1..484f47c 100644
--- a/dev/js/src/menu.js
+++ b/dev/js/src/menu.js
@@ -7,6 +7,11 @@
  * TODO: space is not a valid prefix!
  * TODO: Prefix should be case sensitive!
  * TODO: What is _pos and what is position?
+ * TODO: What is the difference between position
+ *       and _active?
+ * TODO: if the prefix is not found, it should be visible and active!
+ * TODO: Bug: The slider vanishes, if the prefix wasn't found in the demo
+ *       (example: type "elt")
  */
 define([
   'menu/item',
@@ -192,6 +197,8 @@
 	};
       };
 
+      this._slider.length(this._list.length);
+
       // Filter was successful - yeah!
       return this._list.length > 0 ? true : false;
     },
@@ -372,38 +379,71 @@
      *
      * @param {string} Prefix for filtering the list
      */
-    show : function () {
-
-      // Initialize the list
-      if (!this._initList())
-	return false;
+    show : function (active) {
 
       // show menu based on initial offset
       this._unmark();      // Unmark everything that was marked before
       this.unshow();      // Delete everything that is shown
-      this._showItems(0); // Show new item list
+
+      // Initialize the list
+      if (!this._initList()) {
+	// The prefix is not active
+	this._prefix.active(true);
+
+	// finally show the element
+	this._element.style.opacity = 1;
+
+	return true;
+      };
+
+      var offset = 0;
 
       // Set the first element to active
       // Todo: Or the last element chosen
-      if (this._firstActive) {
-	this.liveItem(0).active(true);
+      if (arguments.length === 1) {
+
+	// Normalize active value
+	if (active < 0)
+	  active = 0;
+	else if (active > this.length())
+	  active = this.length();
+
+	if (active > this._limit) {
+	  offset = active;
+	  if (offset > (this.length() - this._limit)) {
+	      offset = this.length() - this._limit;
+	  };
+	};
+
+	this.position = active;
+	this._active = active;
+      }
+
+      else if (this._firstActive) {
 	this.position = 0;
 	this._active = 0;
       }
 
       else {
 	this.position = -1;
-      }
+      };
+
+      this._offset = offset;
+      this._showItems(offset); // Show new item list
+
+      // Make chosen value active
+      if (this.position !== -1) {
+	this.shownItem(this.position).active(true);
+      };
 
       // The prefix is not active
       this._prefix.active(false);
 
-
       // finally show the element
       this._element.style.opacity = 1;
 
       // Show the slider
-      this._slider.show();
+      //this._slider.show();
 
       // Iterate to the active item
       if (this._active !== -1 && !this._prefix.isSet()) {
diff --git a/dev/js/src/menu/item.js b/dev/js/src/menu/item.js
index b8e8808..8278d17 100644
--- a/dev/js/src/menu/item.js
+++ b/dev/js/src/menu/item.js
@@ -122,10 +122,50 @@
    * @param {string} Prefix string for highlights
    */
   highlight : function (prefix) {
+
+    // The prefix already matches
+    if (this._prefix === prefix)
+      return;
+
+    // There is a prefix but it doesn't match
+    if (this._prefix !== null) {
+      this.lowlight();
+    }
+
     var children = this.element().childNodes;
     for (var i = children.length -1; i >= 0; i--) {
       this._highlight(children[i], prefix);
     };
+
+    this._prefix = prefix;
+  },
+
+  /**
+   * Remove highlight of the menu item
+   */
+  lowlight : function () {
+    if (this._prefix === null)
+      return;
+
+    var e = this.element();
+    
+    var marks = e.getElementsByTagName("mark");
+    for (var i = marks.length - 1; i >= 0; i--) {
+      // Create text node clone
+      var x = document.createTextNode(
+	marks[i].firstChild.nodeValue
+      );
+      
+      // Replace with content
+      marks[i].parentNode.replaceChild(
+	x,
+	marks[i]
+      );
+    };
+
+    // Remove consecutive textnodes
+    e.normalize();
+    this._prefix = null;
   },
 
   // Highlight a certain substring of the menu item
@@ -176,30 +216,6 @@
     };
   },
 
-  /**
-   * Remove highlight of the menu item
-   */
-  lowlight : function () {
-    var e = this.element();
-    
-    var marks = e.getElementsByTagName("mark");
-    for (var i = marks.length - 1; i >= 0; i--) {
-      // Create text node clone
-      var x = document.createTextNode(
-	marks[i].firstChild.nodeValue
-      );
-      
-      // Replace with content
-      marks[i].parentNode.replaceChild(
-	x,
-	marks[i]
-      );
-    };
-
-    // Remove consecutive textnodes
-    e.normalize();
-  },
-
   // Initialize menu item
   _init : function (params) {
     
@@ -212,6 +228,7 @@
       this._action = params[1];
 
     this._lcField = ' ' + this.content().textContent.toLowerCase();
+    this._highlight = null;
 
     return this;
   },
diff --git a/dev/js/src/menu/slider.js b/dev/js/src/menu/slider.js
index c35f284..d71bae1 100644
--- a/dev/js/src/menu/slider.js
+++ b/dev/js/src/menu/slider.js
@@ -1,14 +1,33 @@
 define({
 
   /**
-   * Create new prefix object.
+   * Create new slider object.
+   * The slider will only be used by mouse - touch support
+   * shouldn't be necessary.
    */
   create : function (menu) {
     return Object.create(this)._init(menu);
   },
 
-  _mousemove : function (e) {
-    var relativePos = e.clientY - this._event.init;
+  length : function (i) {
+    if (arguments.length === 0)
+      return this._length;
+    if (i == this._length)
+      return;
+    this._length = i;
+    this._initSize();
+  },
+
+  limit : function (i) {
+    if (arguments.length === 0)
+      return this._limit;
+    if (i == this._limit)
+      return;
+    this._limit = i;
+    this._initSize();
+  },
+
+  movetoRel : function (relativePos) {
     var diffHeight = (this._rulerHeight - this._sliderHeight);
     var relativeOffset = (relativePos / diffHeight);
 
@@ -16,34 +35,38 @@
     if (off !== undefined) {
       this._menu.screen(off);
     };
-
-    e.halt();
-
-    // Support touch!
   },
 
-  _mouseup : function (e) {
-    this._element.classList.remove('active');
-    window.removeEventListener('mousemove', this._event.mov);
-    window.removeEventListener('mouseup', this._event.up);
+  movetoAbs : function (absPos) {
+    var absOffset = (absPos / this._rulerHeight);
+
+    var off = this.offset(parseInt(absOffset * (this._screens + 1)));
+    if (off !== undefined) {
+      this._menu.screen(off);
+    };
   },
 
-  _mousedown : function (e) {
-    // Bind drag handler
-    var ev = this._event;
-    ev.init = e.clientY - (this._step * this._offset);
-    ev.mov = this._mousemove.bind(this);
-    ev.up = this._mouseup.bind(this);
+  offset : function (off) {
+    if (arguments.length === 0)
+      return this._offset;
 
-    this._rulerHeight  = this._element.clientHeight; // offsetHeight?
-    this._sliderHeight = this._slider.clientHeight;  // offsetHeight?
+    if (off > this._screens) {
+      off = this._screens;
+    }
+    else if (off < 0) {
+      off = 0;
+    };
 
-    this._element.classList.add('active');
+    if (off === this._offset)
+      return undefined;
 
-    window.addEventListener('mousemove', ev.mov);
-    window.addEventListener('mouseup', ev.up);
+    this._offset = off;
+    this._slider.style.top = (this._step * off) + '%';
+    return off;
+  },
 
-    e.halt();
+  element : function () {
+    return this._element;
   },
 
   // Initialize prefix object
@@ -61,51 +84,78 @@
       document.createElement('span')
     );
 
-    this._element.appendChild(document.createElement('div'))
-    // Do not mark the menu on mousedown
-    .addEventListener('mousedown', function (e) {
-      e.halt()
-    });
+    this._ruler = this._element.appendChild(document.createElement('div'));
 
-    // TODO: Support touch!
+    // Do not mark the menu on mousedown
+    this._ruler.addEventListener('mousedown', function (e) {
+      e.halt()
+    }, false);
+
+    // Move the slider to the click position
+    this._ruler.addEventListener('click', this._mouseclick.bind(this), false);
+
     this._slider.addEventListener('mousedown', this._mousedown.bind(this), false);
 
     return this;
   },
 
   _initSize : function () {
+    if (this._length <= this._limit) {
+      this._element.style.display = 'none';
+      return;
+    }
+    else {
+      this._element.style.display = 'block';
+    };
+
     this._height = ((this._limit / this._length) * 100);
     this._screens = this._length - this._limit;
     this._step = (100 - this._height) / this._screens;
-  },
-
-  show : function (i) {
     this._slider.style.height = this._height + '%';
   },
 
-  length : function (i) {
-    this._length = i;
-    this._initSize();
+  _initClientHeight : function () {
+    this._rulerHeight  = this._element.clientHeight; // offsetHeight?
+    this._sliderHeight = this._slider.clientHeight;  // offsetHeight?
   },
 
-  limit : function (i) {
-    this._limit = i;
-    this._initSize();
+  _mousemove : function (e) {
+    this.movetoRel(e.clientY - this._event.init);
+    e.halt();
+    // Support touch!
   },
 
-  offset : function (off) {
-    if (arguments.length === 0)
-      return this._offset;
-
-    if (off === this._offset || off > this._screens || off < 0)
-      return undefined;
-
-    this._offset = off;
-    this._slider.style.top = (this._step * off) + '%';
-    return off;
+  _mouseup : function (e) {
+    this._element.classList.remove('active');
+    window.removeEventListener('mousemove', this._event.mov);
+    window.removeEventListener('mouseup', this._event.up);
+    this._menu.focus();
   },
 
-  element : function () {
-    return this._element;
+  _mousedown : function (e) {
+    // Bind drag handler
+    var ev = this._event;
+    ev.init = e.clientY - (this._step * this._offset);
+    ev.mov = this._mousemove.bind(this);
+    ev.up = this._mouseup.bind(this);
+
+    // TODO: This may not be necessary all the time
+    this._initClientHeight();
+
+    this._element.classList.add('active');
+
+    window.addEventListener('mousemove', ev.mov);
+    window.addEventListener('mouseup', ev.up);
+
+    e.halt();
+  },
+
+  _mouseclick : function (e) {
+    this._initClientHeight();
+
+    this.movetoAbs(
+      e.clientY - this._ruler.getClientRects()[0].top
+    );
+    e.halt();
   }
 });
diff --git a/dev/scss/header/menu.scss b/dev/scss/header/menu.scss
index 5cfbad4..582e543 100644
--- a/dev/scss/header/menu.scss
+++ b/dev/scss/header/menu.scss
@@ -67,9 +67,9 @@
   > div.ruler {
     position: absolute;
     right: 0px;
-    margin-right: -12px;
+    margin-right: -14px;
     background-color: transparent;
-    width: 12px;
+    width: 14px;
     height: 100%;
     opacity: 0;
     cursor: pointer;
@@ -77,25 +77,27 @@
       position: absolute;
       @include choose-active;
       display: block;
-      right: 1px;
-      width: 6px;
+      right: 0;
+      width: 10px;
       z-index: 600;
-      border-radius: 4px;
+      border: {
+	radius: 4px;
+	width: 2px;
+	style: solid;
+      }
     }
-
     &.active > span {
       @include choose-hover;
     }
-
     > div {
       box-shadow: $choose-box-shadow;
       position: absolute;
       right: 0;
       border: {
-	width: 1px;
+	width: 2px;
 	style: solid;
       }
-      width: 8px;
+      width: 10px;
       @include choose-item;
       // background-color: -grey;
       height: 100%;
diff --git a/lib/Kalamar.pm b/lib/Kalamar.pm
index d6e0cb7..ee19cd7 100644
--- a/lib/Kalamar.pm
+++ b/lib/Kalamar.pm
@@ -4,7 +4,7 @@
 use Mojo::JSON 'decode_json';
 
 # Minor version - may be patched from package.json
-our $VERSION = '0.19';
+our $VERSION = '0.20';
 
 # TODO: The FAQ-Page has a contact form for new questions
 # TODO: Embed query serialization
@@ -152,7 +152,7 @@
 
 =head2 COPYRIGHT AND LICENSE
 
-Copyright (C) 2015, L<IDS Mannheim|http://www.ids-mannheim.de/>
+Copyright (C) 2015-2016, L<IDS Mannheim|http://www.ids-mannheim.de/>
 Author: L<Nils Diewald|http://nils-diewald.de/>
 
 Kalamar is developed as part of the L<KorAP|http://korap.ids-mannheim.de/>
diff --git a/package.json b/package.json
index 885590b..d50b1de 100755
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "Kalamar",
   "description": "Mojolicious-based Frontend for KorAP",
-  "version": "0.19.0",
+  "version": "0.20.0",
   "repository" : {
     "type": "git",
     "url": "https://github.com/KorAP/Kalamar.git"