Add arbitrary keys to VC

Change-Id: I3a26665a861c428ffb40b2387437dcc4296b461c
diff --git a/dev/js/spec/matchSpec.js b/dev/js/spec/matchSpec.js
index 09c8da7..bee5f86 100644
--- a/dev/js/spec/matchSpec.js
+++ b/dev/js/spec/matchSpec.js
@@ -217,7 +217,7 @@
 
     it('should be initializable by Object', function () {
       expect(function() {
-	matchClass.create()
+	      matchClass.create()
       }).toThrow(new Error('Missing parameters'));
 
       expect(matchClass.create(match)).toBeTruthy();
diff --git a/dev/js/spec/vcSpec.js b/dev/js/spec/vcSpec.js
index 16ddf82..6f2c391 100644
--- a/dev/js/spec/vcSpec.js
+++ b/dev/js/spec/vcSpec.js
@@ -5,6 +5,8 @@
 
   var vcClass =          require('vc');
   var docClass =         require('vc/doc');
+  var menuClass =        require('vc/menu');
+  var prefixClass =      require('vc/prefix');
   var docGroupClass =    require('vc/docgroup');
   var unspecifiedClass = require('vc/unspecified');
   var operatorsClass =   require('vc/operators');
@@ -15,14 +17,14 @@
   buildFactory = function (objClass, defaults) {
     return {
       create : function (overwrites) {
-	var newObj = {};
-	for (var prop in defaults) {
-	  newObj[prop] = defaults[prop];
-	};
-	for (var prop in overwrites) {
-	  newObj[prop] = overwrites[prop];
-	};
-	return objClass.create().fromJson(newObj);
+	      var newObj = {};
+	      for (var prop in defaults) {
+	        newObj[prop] = defaults[prop];
+	      };
+	      for (var prop in overwrites) {
+	        newObj[prop] = overwrites[prop];
+	      };
+	      return objClass.create().fromJson(newObj);
       }
     }
   };
@@ -148,7 +150,7 @@
 
       // No valid string
       doc = stringFactory.create({
-	value : undefined
+	      value : undefined
       });
       expect(doc).toBeUndefined();
 
@@ -787,7 +789,7 @@
     });
   });
 
-  describe('KorAP.VirtualCollection', function () {
+  describe('KorAP.VirtualCorpus', function () {
     var simpleGroupFactory = buildFactory(docGroupClass, {
       "@type" : "koral:docGroup",
       "operation" : "operation:and",
@@ -2022,12 +2024,49 @@
       var sv = stringValClass.create();
       var count = 1;
       sv.store = function (value, regex) {
-	expect(regex).toBe(true);
-	expect(value).toBe('tree');
+	      expect(regex).toBe(true);
+	      expect(value).toBe('tree');
       };
       sv.regex(true);
       sv.value('tree');
       sv.element().lastChild.click();
     });
   });
+
+  // Check prefix
+  describe('KorAP.VC.Prefix', function () {
+
+    it('should be initializable', function () {
+      var p = prefixClass.create();
+      expect(p.element().classList.contains('pref')).toBeTruthy();
+      expect(p.isSet()).not.toBeTruthy();
+    });
+
+
+    it('should be clickable', function () {
+      var vc = vcClass.create([
+	      ['a', null],
+	      ['b', null],
+	      ['c', null]
+	    ]).fromJson();
+      expect(vc.element().firstChild.classList.contains('unspecified')).toBeTruthy();
+
+      // This should open up the menu
+      vc.element().firstChild.firstChild.click();
+      expect(vc.element().firstChild.firstChild.tagName).toEqual('UL');
+
+      KorAP._vcKeyMenu._prefix.clear();
+      KorAP._vcKeyMenu._prefix.add('x');
+
+      var prefElement = vc.element().querySelector('span.pref');
+      expect(prefElement.innerText).toEqual('x');
+
+      // This should add key 'x' to VC
+      prefElement.click();
+
+      expect(vc.element().firstChild.classList.contains('doc')).toBeTruthy();
+      expect(vc.element().firstChild.firstChild.className).toEqual('key');
+      expect(vc.element().firstChild.firstChild.innerText).toEqual('x');
+    });
+  });
 });
diff --git a/dev/js/src/hint/prefix.js b/dev/js/src/hint/prefix.js
index bdc7bdf..dc14d5c 100644
--- a/dev/js/src/hint/prefix.js
+++ b/dev/js/src/hint/prefix.js
@@ -6,7 +6,7 @@
      */
     create : function (params) {
       return Object.create(prefixClass).
-	upgradeTo(this)._init(params);
+	      upgradeTo(this)._init(params);
     },
 
     /**
diff --git a/dev/js/src/menu.js b/dev/js/src/menu.js
index 8619d89..e575b7e 100644
--- a/dev/js/src/menu.js
+++ b/dev/js/src/menu.js
@@ -21,9 +21,9 @@
   'menu/slider',
   'util'
 ], function (defaultItemClass,
-	     defaultPrefixClass,
-	     defaultLengthFieldClass,
-	     sliderClass) {
+             defaultPrefixClass,
+             defaultLengthFieldClass,
+             sliderClass) {
 
   // Default maximum number of menu items
   var menuLimit = 8;
@@ -62,25 +62,25 @@
     _init : function (list, params) {
 
       if (params === undefined)
-	params = {};
+        params = {};
 
       this._itemClass = params["itemClass"] || defaultItemClass;
 
       // Add prefix object
       if (params["prefixClass"] !== undefined) {
-	this._prefix = params["prefixClass"].create();
+        this._prefix = params["prefixClass"].create();
       }
       else {
-	this._prefix = defaultPrefixClass.create();
+        this._prefix = defaultPrefixClass.create();
       };
       this._prefix._menu = this;
 
       // Add lengthField object
       if (params["lengthFieldClass"] !== undefined) {
-	this._lengthField = params["lengthFieldClass"].create();
+        this._lengthField = params["lengthFieldClass"].create();
       }
       else {
-	this._lengthField = defaultLengthFieldClass.create();
+        this._lengthField = defaultLengthFieldClass.create();
       };
       this._lengthField._menu = this;
 
@@ -90,12 +90,12 @@
       // Create the element
       var el = document.createElement("ul");
       with (el) {
-	style.outline = 0;
-	setAttribute('tabindex', 0);
-	classList.add('menu', 'roll');
-	appendChild(this._prefix.element());
-	appendChild(this._lengthField.element());
-	appendChild(this._slider.element());
+        style.outline = 0;
+        setAttribute('tabindex', 0);
+        classList.add('menu', 'roll');
+        appendChild(this._prefix.element());
+        appendChild(this._lengthField.element());
+        appendChild(this._slider.element());
       };
 
       // This has to be cleaned up later on
@@ -103,44 +103,44 @@
 
       // Arrow keys
       el.addEventListener(
-	'keydown',
-	this._keydown.bind(this),
-	false
+        'keydown',
+        this._keydown.bind(this),
+        false
       );
 
       // Strings
       el.addEventListener(
-	'keypress',
-	this._keypress.bind(this),
-	false
+        'keypress',
+        this._keypress.bind(this),
+        false
       );
 
       // Mousewheel
       el.addEventListener(
-	'wheel',
-	this._mousewheel.bind(this),
-	false
+        'wheel',
+        this._mousewheel.bind(this),
+        false
       );
       this._element = el;
-
+      
       this._items = new Array();
 
       var i = 0;
       // Initialize item list based on parameters
       for (i in list) {
-	var obj = this._itemClass.create(list[i]);
+        var obj = this._itemClass.create(list[i]);
 
-	// This may become circular
-	obj["_menu"] = this;
-	this._lengthField.add(list[i]);
-	this._items.push(obj);
+        // This may become circular
+        obj["_menu"] = this;
+        this._lengthField.add(list[i]);
+        this._items.push(obj);
       };
 
       this._limit = menuLimit;
       this._slider.length(this.liveLength())
-	.limit(this._limit)
-	.reInit();
-
+        .limit(this._limit)
+        .reInit();
+      
       this._firstActive = false; // Show the first item active always?
       this.offset = 0;
       this.position = 0;
@@ -152,11 +152,11 @@
 
       // Create a new list
       if (this._list === undefined) {
-	this._list = [];
+        this._list = [];
       }
       else if (this._list.length !== 0) {
-	this._boundary(false);
-	this._list.length = 0;
+        this._boundary(false);
+        this._list.length = 0;
       };
 
       // Offset is initially zero
@@ -165,16 +165,16 @@
       // There is no prefix set
       if (this.prefix().length <= 0) {
 
-	// add all items to the list and lowlight
-	var i = 0;
-	for (; i < this._items.length; i++) {
-	  this._list.push(i);
-	  this._items[i].lowlight();
-	};
+        // add all items to the list and lowlight
+        var i = 0;
+        for (; i < this._items.length; i++) {
+          this._list.push(i);
+          this._items[i].lowlight();
+        };
 
-	this._slider.length(i).reInit();;
+        this._slider.length(i).reInit();;
 
-	return true;
+        return true;
       };
 
       /*
@@ -186,8 +186,8 @@
       // Iterate over all items and choose preferred matching items
       // i.e. the matching happens at the word start
       for (pos = 0; pos < this._items.length; pos++) {
-	if ((this.item(pos).lcField().indexOf(prefix)) >= 0)
-	  this._list.push(pos);
+        if ((this.item(pos).lcField().indexOf(prefix)) >= 0)
+          this._list.push(pos);
       };
 
       // The list is empty - so lower your expectations
@@ -195,10 +195,10 @@
       // i.e. the matching happens anywhere in the word
       prefix = prefix.substring(1);
       if (this._list.length == 0) {
-	for (pos = 0; pos < this._items.length; pos++) {
-	  if ((this.item(pos).lcField().indexOf(prefix)) >= 0)
-	    this._list.push(pos);
-	};
+        for (pos = 0; pos < this._items.length; pos++) {
+          if ((this.item(pos).lcField().indexOf(prefix)) >= 0)
+            this._list.push(pos);
+        };
       };
 
       this._slider.length(this._list.length).reInit();
@@ -217,11 +217,11 @@
 
       // Remove circular reference to "this" in menu
       if (this._element != undefined)
-	delete this._element["menu"]; 
+        delete this._element["menu"]; 
 
       // Remove circular reference to "this" in items
       for (var i = 0; i < this._items.length; i++) {
-	delete this._items[i]["_menu"];
+        delete this._items[i]["_menu"];
       };
 
       // Remove circular reference to "this" in prefix
@@ -245,9 +245,9 @@
 
       delta = e.deltaY / 120;
       if (delta > 0)
-	this.next();
+        this.next();
       else if (delta < 0)
-	this.prev();
+        this.prev();
       e.halt();
     },
 
@@ -258,66 +258,66 @@
 
       switch (code) {
       case 27: // 'Esc'
-	e.halt();
-	this.hide();
-	break;
+        e.halt();
+        this.hide();
+        break;
 
       case 38: // 'Up'
-	e.halt();
-	this.prev();
-	break;
+        e.halt();
+        this.prev();
+        break;
       case 33: // 'Page up'
-	e.halt();
-	this.pageUp();
-	break;
+        e.halt();
+        this.pageUp();
+        break;
       case 40: // 'Down'
-	e.halt();
-	this.next();
-	break;
+        e.halt();
+        this.next();
+        break;
       case 34: // 'Page down'
-	e.halt();
-	this.pageDown();
-	break;
+        e.halt();
+        this.pageDown();
+        break;
       case 39: // 'Right'
-	if (this._prefix.active())
-	  break;
+        if (this._prefix.active())
+          break;
 
-	var item = this.liveItem(this.position);
-
-	if (item["further"] !== undefined) {
-	  item["further"].bind(item).apply();
-	};
-
-	e.halt();
-	break;
+        var item = this.liveItem(this.position);
+        
+        if (item["further"] !== undefined) {
+          item["further"].bind(item).apply();
+        };
+        
+        e.halt();
+        break;
       case 13: // 'Enter'
 
-	// Click on prefix
-	if (this._prefix.active())
-	  this._prefix.onclick(e);
+        // Click on prefix
+        if (this._prefix.active())
+          this._prefix.onclick(e);
 
-	// Click on item
-	else
-	  this.liveItem(this.position).onclick(e);
-	e.halt();
-	break;
+        // Click on item
+        else
+          this.liveItem(this.position).onclick(e);
+        e.halt();
+        break;
       case 8: // 'Backspace'
-	this._prefix.chop();
-	this.show();
-	e.halt();
-	break;
+        this._prefix.chop();
+        this.show();
+        e.halt();
+        break;
       };
     },
 
     // Add characters to prefix
     _keypress : function (e) {
       if (e.charCode !== 0) {
-	e.halt();
-	var c = String.fromCharCode(_codeFromEvent(e));
-
-	// Add prefix
-	this._prefix.add(c);
-	this.show();
+        e.halt();
+        var c = String.fromCharCode(_codeFromEvent(e));
+        
+        // Add prefix
+        this._prefix.add(c);
+        this.show();
       };
     },
 
@@ -327,14 +327,14 @@
      */
     screen : function (nr) {
       if (nr < 0) {
-	nr = 0
+        nr = 0
       }
       else if (nr > (this.liveLength() - this.limit())) {
-	nr = (this.liveLength() - this.limit());
+        nr = (this.liveLength() - this.limit());
       };
 
       if (this.offset === nr)
-	return;
+        return;
 
       this._showItems(nr);
     },
@@ -359,11 +359,11 @@
      */
     limit : function (limit) {
       if (arguments.length === 1) {
-	if (this._limit !== limit) {
-	  this._limit = limit;
-	  this._slider.limit(limit).reInit();
-	};
-	return this;
+        if (this._limit !== limit) {
+          this._limit = limit;
+          this._slider.limit(limit).reInit();
+        };
+        return this;
       };
       return this._limit;
     },
@@ -377,7 +377,7 @@
      */
     upgradeTo : function (props) {
       for (var prop in props) {
-	this[prop] = props[prop];
+        this[prop] = props[prop];
       };
       return this;
     },
@@ -398,13 +398,13 @@
       // Initialize the list
       if (!this._initList()) {
 
-	// The prefix is not active
-	this._prefix.active(true);
+        // The prefix is not active
+        this._prefix.active(true);
 
-	// finally show the element
-	this._element.classList.add('visible');
-
-	return true;
+        // finally show the element
+        this._element.classList.add('visible');
+        
+        return true;
       };
 
       var offset = 0;
@@ -412,33 +412,33 @@
       // Set a chosen value to active and move the viewport
       if (arguments.length === 1) {
 
-	// Normalize active value
-	if (active < 0) {
-	  active = 0;
-	}
-	else if (active >= this.liveLength()) {
-	  active = this.liveLength() - 1;
-	};
+        // Normalize active value
+        if (active < 0) {
+          active = 0;
+        }
+        else if (active >= this.liveLength()) {
+          active = this.liveLength() - 1;
+        };
 
-	// Item is outside the first viewport
-	if (active >= this._limit) {
-	  offset = active;
-	  if (offset > (this.liveLength() - this._limit)) {
-	      offset = this.liveLength() - this._limit;
-	  };
-	};
-
-	this.position = active;
+        // Item is outside the first viewport
+        if (active >= this._limit) {
+          offset = active;
+          if (offset > (this.liveLength() - this._limit)) {
+            offset = this.liveLength() - this._limit;
+          };
+        };
+        
+        this.position = active;
       }
 
       // Choose the first item
       else if (this._firstActive) {
-	this.position = 0;
+        this.position = 0;
       }
 
       // Choose no item
       else {
-	this.position = -1;
+        this.position = -1;
       };
 
       this.offset = offset;
@@ -446,7 +446,7 @@
 
       // Make chosen value active
       if (this.position !== -1) {
-	this.liveItem(this.position).active(true);
+        this.liveItem(this.position).active(true);
       };
 
       // The prefix is not active
@@ -487,8 +487,8 @@
      */
     prefix : function (pref) {
       if (arguments.length === 1) {
-	this._prefix.value(pref);
-	return this;
+        this._prefix.value(pref);
+        return this;
       };
       return this._prefix.value();
     },
@@ -518,9 +518,9 @@
       var children = this._element.childNodes;
       // Leave the prefix and lengthField
       for (var i = children.length - 1; i >= 3; i--) {
-	this._element.removeChild(
-	  children[i]
-	);
+        this._element.removeChild(
+          children[i]
+        );
       };
     },
 
@@ -542,8 +542,8 @@
      */
     liveItem : function (index) {
       if (this._list === undefined)
-	if (!this._initList())
-	  return;
+        if (!this._initList())
+          return;
 
       return this._items[this._list[index]];
     },
@@ -557,7 +557,7 @@
      */
     shownItem : function (index) {
       if (index >= this.limit())
-	return;
+        return;
       return this.liveItem(this.offset + index);
     },
 
@@ -575,7 +575,7 @@
      */
     liveLength : function () {
       if (this._list === undefined)
-	this._initList();
+        this._initList();
       return this._list.length;
     },
 
@@ -587,11 +587,11 @@
 
       // No list
       if (this.liveLength() === 0)
-	return;
+        return;
 
       // Deactivate old item
       if (this.position !== -1 && !this._prefix.active()) {
-	this.liveItem(this.position).active(false);
+        this.liveItem(this.position).active(false);
       };
 
       // Get new active item
@@ -601,33 +601,33 @@
       // The next element is undefined - roll to top or to prefix
       if (newItem === undefined) {
 
-	// Activate prefix
-	var prefix = this._prefix;
+        // Activate prefix
+        var prefix = this._prefix;
 
-	// Prefix is set and not active - choose!
-	if (prefix.isSet() && !prefix.active()) {
-	  this.position--;
-	  prefix.active(true);
-	  return;
-	}
+        // Prefix is set and not active - choose!
+        if (prefix.isSet() && !prefix.active()) {
+          this.position--;
+          prefix.active(true);
+          return;
+        }
 
-	// Choose first item
-	else {
-	  newItem = this.liveItem(0);
-	  // choose first item
-	  this.position = 0;
-	  this._showItems(0);
-	};
+        // Choose first item
+        else {
+          newItem = this.liveItem(0);
+          // choose first item
+          this.position = 0;
+          this._showItems(0);
+        };
       }
 
       // The next element is after the viewport - roll down
       else if (this.position >= (this.limit() + this.offset)) {
-	this.screen(this.position - this.limit() + 1);
+        this.screen(this.position - this.limit() + 1);
       }
 
       // The next element is before the viewport - roll up
       else if (this.position <= this.offset) {
-	this.screen(this.position);
+        this.screen(this.position);
       };
 
       this._prefix.active(false);
@@ -641,20 +641,20 @@
 
       // No list
       if (this.liveLength() === 0)
-	return;
+        return;
 
       // Deactivate old item
       if (!this._prefix.active()) {
 
-	// No active element set
-	if (this.position === -1) {
-	  this.position = this.liveLength();
-	}
+        // No active element set
+        if (this.position === -1) {
+          this.position = this.liveLength();
+        }
 
-	// No active element set
-	else {
-	  this.liveItem(this.position--).active(false);
-	};
+        // No active element set
+        else {
+          this.liveItem(this.position--).active(false);
+        };
       };
 
       // Get new active item
@@ -663,39 +663,39 @@
       // The previous element is undefined - roll to bottom
       if (newItem === undefined) {
 
-	// Activate prefix
-	var prefix = this._prefix;
-	var offset =  this.liveLength() - this.limit();
+        // Activate prefix
+        var prefix = this._prefix;
+        var offset =  this.liveLength() - this.limit();
+        
+        // Normalize offset
+        offset = offset < 0 ? 0 : offset;
 
-	// Normalize offset
-	offset = offset < 0 ? 0 : offset;
+        // Choose the last item
+        this.position = this.liveLength() - 1;
+        
+        // Prefix is set and not active - choose!
+        if (prefix.isSet() && !prefix.active()) {
+          this.position++;
+          prefix.active(true);
+          this.offset = offset;
+          return;
+        }
 
-	// Choose the last item
-	this.position = this.liveLength() - 1;
-
-	// Prefix is set and not active - choose!
-	if (prefix.isSet() && !prefix.active()) {
-	  this.position++;
-	  prefix.active(true);
-	  this.offset = offset;
-	  return;
-	}
-
-	// Choose last item
-	else {
-	  newItem = this.liveItem(this.position);
-	  this._showItems(offset);
-	};
+        // Choose last item
+        else {
+          newItem = this.liveItem(this.position);
+          this._showItems(offset);
+        };
       }
 
       // The previous element is before the view - roll up
       else if (this.position < this.offset) {
-	this.screen(this.position);
+        this.screen(this.position);
       }
 
       // The previous element is after the view - roll down
       else if (this.position >= (this.limit() + this.offset)) {
-	this.screen(this.position - this.limit() + 2);
+        this.screen(this.position - this.limit() + 2);
       };
 
       this._prefix.active(false);
@@ -721,9 +721,9 @@
     // Unmark all items
     _unmark : function () {
       for (var i in this._list) {
-	var item = this._items[this._list[i]];
-	item.lowlight();
-	item.active(false);	
+        var item = this._items[this._list[i]];
+        item.lowlight();
+        item.active(false);  
       };
     },
 
@@ -739,48 +739,48 @@
 
       // optimization: scroll down one step
       if (this.offset === (off - 1)) {
-	this.offset = off;
+        this.offset = off;
 
-	// Remove the HTML node from the first item
-	// leave lengthField/prefix/slider
-	this._element.removeChild(this._element.children[3]);
-	var pos = this.offset + this.limit() - 1;
-	this._append(this._list[pos]);
+        // Remove the HTML node from the first item
+        // leave lengthField/prefix/slider
+        this._element.removeChild(this._element.children[3]);
+        var pos = this.offset + this.limit() - 1;
+        this._append(this._list[pos]);
       }
 
       // optimization: scroll up one step
       else if (this.offset === (off + 1)) {
-	this.offset = off;
+        this.offset = off;
 
-	// Remove the HTML node from the last item
-	this._element.removeChild(this._element.lastChild);
+        // Remove the HTML node from the last item
+        this._element.removeChild(this._element.lastChild);
 
-	this._prepend(this._list[this.offset]);
+        this._prepend(this._list[this.offset]);
       }
       else {
-	this.offset = off;
+        this.offset = off;
 
-	// Remove all items
-	this.removeItems();
+        // Remove all items
+        this.removeItems();
 
-	// Use list
-	var shown = 0;
-	var i;
+        // Use list
+        var shown = 0;
+        var i;
 
-	for (i in this._list) {
+        for (i in this._list) {
 
-	  // Don't show - it's before offset
-	  shown++;
-	  if (shown <= off)
-	    continue;
+          // Don't show - it's before offset
+          shown++;
+          if (shown <= off)
+            continue;
 
-	  var itemNr = this._list[i];
-	  var item = this.item(itemNr);
-	  this._append(itemNr);
-
-	  if (shown >= (this.limit() + off))
-	    break;
-	};
+          var itemNr = this._list[i];
+          var item = this.item(itemNr);
+          this._append(itemNr);
+          
+          if (shown >= (this.limit() + off))
+            break;
+        };
       };
 
       // set the slider to the new offset
@@ -794,7 +794,7 @@
 
       // Highlight based on prefix
       if (this.prefix().length > 0) {
-	item.highlight(this.prefix().toLowerCase());
+        item.highlight(this.prefix().toLowerCase());
       };
 
 
@@ -809,15 +809,15 @@
 
       // Highlight based on prefix
       if (this.prefix().length > 0) {
-	item.highlight(this.prefix().toLowerCase());
+        item.highlight(this.prefix().toLowerCase());
       };
 
       var e = this.element();
 
       // Append element after lengthField/prefix/slider
       e.insertBefore(
-	item.element(),
-	e.children[3]
+        item.element(),
+        e.children[3]
       );
     }
   };
diff --git a/dev/js/src/vc.js b/dev/js/src/vc.js
index e551d63..3e9f16e 100644
--- a/dev/js/src/vc.js
+++ b/dev/js/src/vc.js
@@ -84,50 +84,50 @@
 
       // Inject localized css styles
       if (!KorAP._overrideStyles) {
-	var sheet = KorAP.newStyleSheet();
+	      var sheet = KorAP.newStyleSheet();
 
-	// Add css rule for OR operations
-	sheet.insertRule(
-	  '.vc .docGroup[data-operation=or] > .doc::before,' +
-	  '.vc .docGroup[data-operation=or] > .docGroup::before ' +
-	    '{ content: "' + loc.OR + '" }',
-	  0
-	);
+	      // Add css rule for OR operations
+	      sheet.insertRule(
+	        '.vc .docGroup[data-operation=or] > .doc::before,' +
+	          '.vc .docGroup[data-operation=or] > .docGroup::before ' +
+	          '{ content: "' + loc.OR + '" }',
+	        0
+	      );
 
-	// Add css rule for AND operations
-	sheet.insertRule(
-	  '.vc .docGroup[data-operation=and] > .doc::before,' +
-	  '.vc .docGroup[data-operation=and] > .docGroup::before ' +
-	    '{ content: "' + loc.AND + '" }',
-	  1
-	);
+	      // Add css rule for AND operations
+	      sheet.insertRule(
+	        '.vc .docGroup[data-operation=and] > .doc::before,' +
+	          '.vc .docGroup[data-operation=and] > .docGroup::before ' +
+	          '{ content: "' + loc.AND + '" }',
+	        1
+	      );
 
-	KorAP._overrideStyles = true;
+	      KorAP._overrideStyles = true;
 
-	// Create key menu
-	KorAP._vcKeyMenu = menuClass.create(keyList);
-	KorAP._vcKeyMenu.limit(6);
+	      // Create key menu
+	      KorAP._vcKeyMenu = menuClass.create(keyList);
+	      KorAP._vcKeyMenu.limit(6);
 
-	// Create match menus ....
-	KorAP._vcMatchopMenu = {
-	  'string' : menuClass.create([
-	    ['eq', null],
-	    ['ne', null],
-	    ['contains', null],
-	    ['containsnot', null]
-	  ]),
-	  'date' : menuClass.create([
-	    ['eq', null],
-	    ['geq', null],
-	    ['leq', null]
-	  ]),
-	  'regex' : menuClass.create([
-	    ['eq', null],
-	    ['ne', null],
-	    ['contains', null],
-	    ['containsnot', null]
-	  ])
-	};
+	      // Create match menus ....
+	      KorAP._vcMatchopMenu = {
+	        'string' : menuClass.create([
+	          ['eq', null],
+	          ['ne', null],
+	          ['contains', null],
+	          ['containsnot', null]
+	        ]),
+	        'date' : menuClass.create([
+	          ['eq', null],
+	          ['geq', null],
+	          ['leq', null]
+	        ]),
+	        'regex' : menuClass.create([
+	          ['eq', null],
+	          ['ne', null],
+	          ['contains', null],
+	          ['containsnot', null]
+	        ])
+	      };
       };
 
       return this;
@@ -148,26 +148,25 @@
      * based on a KoralQuery collection document 
      */
     fromJson : function (json) {
-
       if (json !== undefined) {
-	// Parse root document
-	if (json['@type'] == 'koral:doc') {
-	  this._root = docClass.create(this, json);
-	}
-	// parse root group
-	else if (json['@type'] == 'koral:docGroup') {
-	  this._root = docGroupClass.create(this, json);
-	}
-	// Unknown collection type
-	else {
-	  KorAP.log(813, "Collection type is not supported");
-	  return;
-	};
+	      // Parse root document
+	      if (json['@type'] == 'koral:doc') {
+	        this._root = docClass.create(this, json);
+	      }
+	      // parse root group
+	      else if (json['@type'] == 'koral:docGroup') {
+	        this._root = docGroupClass.create(this, json);
+	      }
+	      // Unknown collection type
+	      else {
+	        KorAP.log(813, "Collection type is not supported");
+	        return;
+	      };
       }
 
       else {
-	// Add unspecified object
-	this._root = unspecDocClass.create(this);
+	      // Add unspecified object
+	      this._root = unspecDocClass.create(this);
       };
 
       // Init element and update
@@ -195,24 +194,24 @@
      */
     root : function (obj) {
       if (arguments.length === 1) {
-	var e = this.element();
+	      var e = this.element();
 
-	if (e.firstChild !== null) {
-	  if (e.firstChild !== obj.element()) {
-	    e.replaceChild(obj.element(), e.firstChild);
-	  };
-	}
+	      if (e.firstChild !== null) {
+	        if (e.firstChild !== obj.element()) {
+	          e.replaceChild(obj.element(), e.firstChild);
+	        };
+	      }
 
-	// Append root element
-	else {
-	  e.appendChild(obj.element());
-	};
+	      // Append root element
+	      else {
+	        e.appendChild(obj.element());
+	      };
 
-	// Update parent child relations
-	this._root = obj;
-	obj.parent(this);
+	      // Update parent child relations
+	      this._root = obj;
+	      obj.parent(this);
 
-	this.update();
+	      this.update();
       };
       return this._root;
     },
diff --git a/dev/js/src/vc/chooseitem.js b/dev/js/src/vc/chooseitem.js
index b2d3c1b..5dc42b3 100644
--- a/dev/js/src/vc/chooseitem.js
+++ b/dev/js/src/vc/chooseitem.js
@@ -13,14 +13,14 @@
      */
     create : function (params) {
       return Object.create(itemClass)
-	.upgradeTo(this)
-	._init(params);
+	      .upgradeTo(this)
+	      ._init(params);
     },
 
     // Initialize item object
     _init : function (params) {
       if (params[0] === undefined)
-	throw new Error("Missing parameters");
+	      throw new Error("Missing parameters");
       
       this._id  = params[0];
       this._name = params[1];
@@ -38,8 +38,8 @@
      */
     onclick : function (e) {
       this.menu().release(
-	this._id,
-	this._name
+	      this._id,
+	      this._name
       );
       e.halt();
     },
@@ -72,7 +72,7 @@
 
       // already defined
       if (this._element !== undefined)
-	return this._element;
+	      return this._element;
 
       // Create list item
       var li = document.createElement("li");
diff --git a/dev/js/src/vc/doc.js b/dev/js/src/vc/doc.js
index f22f3b4..b56ba53 100644
--- a/dev/js/src/vc/doc.js
+++ b/dev/js/src/vc/doc.js
@@ -41,13 +41,13 @@
 
       // Create the object, inheriting from Json-LD class
       var obj = Object(jsonldClass).
-	create().
-	upgradeTo(this).
-	fromJson(json);
+          create().
+          upgradeTo(this).
+          fromJson(json);
 
       // Bind the parent
       if (parent !== undefined)
-	obj._parent = parent;
+        obj._parent = parent;
       
       obj.__changed = true;
       return obj;
@@ -58,7 +58,7 @@
      */
     update : function () {
       if (this._element === undefined)
-	return this.element();
+        return this.element();
       
       // Get element
       var e = this._element;
@@ -69,82 +69,82 @@
       // Check if there is a change
       if (this.__changed) {
 
-	// Was rewritten
-	if (this.rewrites() !== undefined) {
-	  e.classList.add("rewritten");
-	};
+        // Was rewritten
+        if (this.rewrites() !== undefined) {
+          e.classList.add("rewritten");
+        };
 
-	// Added key
-	this._keyE = document.createElement('span');
-	this._keyE.setAttribute('class', 'key');
+        // Added key
+        this._keyE = document.createElement('span');
+        this._keyE.setAttribute('class', 'key');
 
-	// Change key
-	this._keyE.addEventListener('click', this._changeKey.bind(this));
+        // Change key
+        this._keyE.addEventListener('click', this._changeKey.bind(this));
 
-	if (this.key()) {
-	  var k = this.key();
-	  if (loc['VC_' + k] !== undefined)
-	    k = loc['VC_' + k];
-	  this._keyE.appendChild(document.createTextNode(k));
-	};
+        if (this.key()) {
+          var k = this.key();
+          if (loc['VC_' + k] !== undefined)
+            k = loc['VC_' + k];
+          this._keyE.appendChild(document.createTextNode(k));
+        };
 
-	// Added match operator
-	this._matchopE = document.createElement('span');
-	this._matchopE.setAttribute('data-type', this.type());
-	this._matchopE.setAttribute('class', 'match');
-	this._matchopE.appendChild(
-	  document.createTextNode(this.matchop())
-	);
+        // Added match operator
+        this._matchopE = document.createElement('span');
+        this._matchopE.setAttribute('data-type', this.type());
+        this._matchopE.setAttribute('class', 'match');
+        this._matchopE.appendChild(
+          document.createTextNode(this.matchop())
+        );
 
-	// Change matchop
-	this._matchopE.addEventListener(
-	  'click',
-	  this._changeMatchop.bind(this)
-	);
+        // Change matchop
+        this._matchopE.addEventListener(
+          'click',
+          this._changeMatchop.bind(this)
+        );
 
-	// Added value operator
-	this._valueE = document.createElement('span');
-	this._valueE.setAttribute('data-type', this.type());
-	this._valueE.setAttribute('class', 'value');
-	if (this.value()) {
-	  this._valueE.appendChild(document.createTextNode(this.value()));
-	}
-	else {
-	  this._valueE.appendChild(document.createTextNode(loc.EMPTY));
-	};
+        // Added value operator
+        this._valueE = document.createElement('span');
+        this._valueE.setAttribute('data-type', this.type());
+        this._valueE.setAttribute('class', 'value');
+        if (this.value()) {
+          this._valueE.appendChild(document.createTextNode(this.value()));
+        }
+        else {
+          this._valueE.appendChild(document.createTextNode(loc.EMPTY));
+        };
 
-	// Change value
-	this._valueE.addEventListener(
-	  'click',
-	  this._changeValue.bind(this)
-	);
+        // Change value
+        this._valueE.addEventListener(
+          'click',
+          this._changeValue.bind(this)
+        );
 
 
-	// Remove all element children
-	_removeChildren(e);
+        // Remove all element children
+        _removeChildren(e);
 
-	// Add spans
-	e.appendChild(this._keyE);
-	e.appendChild(this._matchopE);
-	e.appendChild(this._valueE);
+        // Add spans
+        e.appendChild(this._keyE);
+        e.appendChild(this._matchopE);
+        e.appendChild(this._valueE);
 
-	this.__changed = false;
+        this.__changed = false;
       };
 
       if (this._rewrites !== undefined) {
-	e.appendChild(this._rewrites.element());
+        e.appendChild(this._rewrites.element());
       };
 
       if (this._parent !== undefined) {
-	// Set operators
-	var op = this.operators(
-	  true,
-	  true,
-	  true
-	);
+        // Set operators
+        var op = this.operators(
+          true,
+          true,
+          true
+        );
 
-	// Append new operators
-	e.appendChild(op.element());
+        // Append new operators
+        e.appendChild(op.element());
       };
       
       return e;
@@ -156,7 +156,7 @@
      */
     element : function () {
       if (this._element !== undefined)
-	return this._element;
+        return this._element;
 
       this._element = document.createElement('div');
       this._element.setAttribute('class', 'doc');
@@ -182,105 +182,105 @@
      */
     fromJson : function (json) {
       if (json === undefined)
-	return this;
+        return this;
 
       if (json["@type"] === undefined) {
-	KorAP.log(701, "JSON-LD group has no @type attribute");
-	return;
+        KorAP.log(701, "JSON-LD group has no @type attribute");
+        return;
       };
 
       if (json["value"] === undefined ||
-	  typeof json["value"] != 'string') {
-	KorAP.log(805, "Value is invalid");
-	return;
+          typeof json["value"] != 'string') {
+        KorAP.log(805, "Value is invalid");
+        return;
       };
 
       // There is a defined key
       if (json["key"] !== undefined &&
-	  typeof json["key"] === 'string') {
+          typeof json["key"] === 'string') {
 
-	// Set key
-	this.key(json["key"]);
+        // Set key
+        this.key(json["key"]);
 
-	// Set match operation
-	if (json["match"] !== undefined) {
-	  if (typeof json["match"] === 'string') {
-	    this.matchop(json["match"]);
-	  }
-	  else {
-	    KorAP.log(802, "Match type is not supported by value type");
-	    return;
-	  };
-	};
+        // Set match operation
+        if (json["match"] !== undefined) {
+          if (typeof json["match"] === 'string') {
+            this.matchop(json["match"]);
+          }
+          else {
+            KorAP.log(802, "Match type is not supported by value type");
+            return;
+          };
+        };
 
-	// Key is a string
-	if (json["type"] === undefined ||
-	    json["type"] == "type:string") {
-	  this.type("string");
+        // Key is a string
+        if (json["type"] === undefined ||
+            json["type"] == "type:string") {
+          this.type("string");
 
-	  // Check match type
-	  if (!KorAP._validStringMatchRE.test(this.matchop())) {
-	    KorAP.log(802, "Match type is not supported by value type");
-	    return;
-	  };
+          // Check match type
+          if (!KorAP._validStringMatchRE.test(this.matchop())) {
+            KorAP.log(802, "Match type is not supported by value type");
+            return;
+          };
 
-	  // Set string value
-	  this.value(json["value"]);
-	}
+          // Set string value
+          this.value(json["value"]);
+        }
 
-	// Key is a date
-	else if (json["type"] === "type:date") {
-	  this.type("date");
+        // Key is a date
+        else if (json["type"] === "type:date") {
+          this.type("date");
 
-	  if (json["value"] !== undefined &&
-	      KorAP._validDateRE.test(json["value"])) {
+          if (json["value"] !== undefined &&
+              KorAP._validDateRE.test(json["value"])) {
 
-	    if (!KorAP._validDateMatchRE.test(this.matchop())) {
-	      KorAP.log(802, "Match type is not supported by value type");
-	      return;
-	    };
+            if (!KorAP._validDateMatchRE.test(this.matchop())) {
+              KorAP.log(802, "Match type is not supported by value type");
+              return;
+            };
 
-	    // Set value
-	    this.value(json["value"]);
-	  }
-	  else {
-	    KorAP.log(806, "Value is not a valid date string");
-	    return;
-	  };
-	}
+            // Set value
+            this.value(json["value"]);
+          }
+          else {
+            KorAP.log(806, "Value is not a valid date string");
+            return;
+          };
+        }
 
-	// Key is a regular expression
-	else if (json["type"] === "type:regex") {
-	  this.type("regex");
+        // Key is a regular expression
+        else if (json["type"] === "type:regex") {
+          this.type("regex");
 
-	  try {
+          try {
 
-	    // Try to create a regular expression
-	    var check = new RegExp(json["value"]);
+            // Try to create a regular expression
+            var check = new RegExp(json["value"]);
 
-	    if (!_validRegexMatchRE.test(this.matchop())) {
-	      KorAP.log(802, "Match type is not supported by value type");
-	      return;
-	    };
+            if (!_validRegexMatchRE.test(this.matchop())) {
+              KorAP.log(802, "Match type is not supported by value type");
+              return;
+            };
 
-	    this.value(json["value"]);
-	  }
+            this.value(json["value"]);
+          }
 
-	  catch (e) {
-	    KorAP.log(807, "Value is not a valid regular expression");
-	    return;
-	  };
-	  this.type("regex");
-	}
+          catch (e) {
+            KorAP.log(807, "Value is not a valid regular expression");
+            return;
+          };
+          this.type("regex");
+        }
 
-	else {
-	  KorAP.log(804, "Unknown value type");
-	  return;
-	};
+        else {
+          KorAP.log(804, "Unknown value type");
+          return;
+        };
       };
 
       if (json["rewrites"] !== undefined) {
-	this._rewrites = rewriteListClass.create(json["rewrites"]);
+        this._rewrites = rewriteListClass.create(json["rewrites"]);
       };
       
       return this;
@@ -291,9 +291,9 @@
      */
     key : function (value) {
       if (arguments.length === 1) {
-	this._key = value;
-	this._changed();
-	return this;
+        this._key = value;
+        this._changed();
+        return this;
       };
       return this._key;
     },
@@ -304,24 +304,24 @@
 
       // Insert menu
       this._element.insertBefore(
-	menu.element(),
-	this._keyE
+        menu.element(),
+        this._keyE
       );
 
       // Release event
       var that = this;
       menu.released(function (key, type) {
-	var doc = that.key(key).type(type);
+        var doc = that.key(key).type(type);
 
-	// This may not be compatible - then switch to default
-	doc.matchop(doc.matchop());
-	doc.value(doc.value());
+        // This may not be compatible - then switch to default
+        doc.matchop(doc.matchop());
+        doc.value(doc.value());
 
-	// Update the doc
-	doc.update();
+        // Update the doc
+        doc.update();
 
-	// hide!
-	this.hide();
+        // hide!
+        this.hide();
       });
 
       // TODO: Highlight the old value!
@@ -334,25 +334,25 @@
      */
     matchop : function (match) {
       if (arguments.length === 1) {
-	var m = match.replace(/^match:/, '');
-	if (
-	  (this._type === undefined)
-	    ||
-	    (
-	      (this._type === 'string' || this._type === 'regex') &&
-		KorAP._validStringMatchRE.test(m)
-	    )
-	    ||
-	    (this._type === 'date' && KorAP._validDateMatchRE.test(m))
-	) {
-	  this._matchop = m;
-	}
-	else {
-	  this._matchop = "eq";
-	};
+        var m = match.replace(/^match:/, '');
+        if (
+          (this._type === undefined)
+            ||
+            (
+              (this._type === 'string' || this._type === 'regex') &&
+                KorAP._validStringMatchRE.test(m)
+            )
+            ||
+            (this._type === 'date' && KorAP._validDateMatchRE.test(m))
+        ) {
+          this._matchop = m;
+        }
+        else {
+          this._matchop = "eq";
+        };
 
-	this._changed();
-	return this;
+        this._changed();
+        return this;
       };
       return this._matchop || "eq";
     },
@@ -363,21 +363,21 @@
       var menu = KorAP._vcMatchopMenu[this.type()];
 
       if (menu === undefined) {
-	KorAP.log(0, "Unable to init menu for " + this.type());
-	return;
+        KorAP.log(0, "Unable to init menu for " + this.type());
+        return;
       };
 
       // Insert menu
       this._element.insertBefore(
-	menu.element(),
-	this._matchopE
+        menu.element(),
+        this._matchopE
       );
 
       // Release event
       var that = this;
       menu.released(function (mo) {
-	that.matchop(mo).update();
-	this.hide();
+        that.matchop(mo).update();
+        this.hide();
       });
 
       menu.show();
@@ -390,9 +390,9 @@
      */
     type : function (type) {
       if (arguments.length === 1) {
-	this._type = type;
-	this._changed();
-	return this;
+        this._type = type;
+        this._changed();
+        return this;
       };
       return this._type || "string";
     },
@@ -403,14 +403,14 @@
      */
     value : function (value) {
       if (arguments.length === 1) {
-	if (this._type === 'date' && !KorAP._validDateRE.test(value)) {
-	  delete this._value;
-	}
-	else {
-	  this._value = value;
-	};
-	this._changed();
-	return this;
+        if (this._type === 'date' && !KorAP._validDateRE.test(value)) {
+          delete this._value;
+        }
+        else {
+          this._value = value;
+        };
+        this._changed();
+        return this;
       };
       return this._value;
     },
@@ -423,70 +423,69 @@
 
       // Show datepicker
       if (this.type() === 'date') {
-	var dp = KorAP._vcDatePicker;
-	dp.fromString(v)
+        var dp = KorAP._vcDatePicker;
+        dp.fromString(v)
 
-	// Todo: change this
-	dp.onclick(function (selected) {
+        // Todo: change this
+        dp.onclick(function (selected) {
 
-	  // There are values selected
-	  if (selected['year']) {
-	    that.value(this.toString());
-	    that.update();
-	    return;
-	  };
+          // There are values selected
+          if (selected['year']) {
+            that.value(this.toString());
+            that.update();
+            return;
+          };
 
-	  // Remove datepicker
-	  that._element.removeChild(
-	    dp.element()
-	  );
-	});
+          // Remove datepicker
+          that._element.removeChild(
+            dp.element()
+          );
+        });
 
-	// Get element of the date picker
-	var dpElem = dp.show();
+        // Get element of the date picker
+        var dpElem = dp.show();
 
-	this._element.insertBefore(
-	  dpElem,
-	  this._valueE
-	);
+        this._element.insertBefore(
+          dpElem,
+          this._valueE
+        );
 
-	dpElem.focus();
-	/*
-	dpElem.addEventListener('blur', function (e) {
-	  e.halt();
-
-	  // Remove datepicker
-	  // TODO: If focus is not set to string input
-	  that._element.removeChild(this);
-	});
-	*/
+        dpElem.focus();
+        /*
+          dpElem.addEventListener('blur', function (e) {
+          e.halt();
+          
+          // Remove datepicker
+          // TODO: If focus is not set to string input
+          that._element.removeChild(this);
+          });
+        */
       }
       else {
-	var regex = this.type() === 'regex' ? true : false;
-	var str = stringValClass.create(this.value(), regex);
-	var strElem = str.element();
+        var regex = this.type() === 'regex' ? true : false;
+        var str = stringValClass.create(this.value(), regex);
+        var strElem = str.element();
 
+        str.store = function (value, regex) {
+          that.value(value);
+          if (regex === true)
+            that.type('regex');
+          else
+            that.type('string');
+          
+          that._element.removeChild(
+            this._element
+          );
+          that.update();
+        };
 
-	str.store = function (value, regex) {
-	  that.value(value);
-	  if (regex === true)
-	    that.type('regex');
-	  else
-	    that.type('string');
+        // Insert element
+        this._element.insertBefore(
+          strElem,
+          this._valueE
+        );
 
-	  that._element.removeChild(
-	    this._element
-	  );
-	  that.update();
-	};
-
-	// Insert element
-	this._element.insertBefore(
-	  strElem,
-	  this._valueE
-	);
-
-	str.focus();
+        str.focus();
       };
     },
 
@@ -498,37 +497,39 @@
     _changed : function () {
       this.__changed = true;
       
-      if (this._parent) {
-      };
+      /*
+        if (this._parent) {
+        };
+      */
 
       if (this._rewrites === undefined)
-	return;
+        return;
 
       delete this["_rewrites"];
 
       if (this._element === undefined)
-	return;
+        return;
       this._element.classList.remove("rewritten");
     },
 
 
     toJson : function () {
       if (!this.matchop() || !this.key())
-	return {};
+        return {};
       
       return {
-	"@type" : "koral:" + this.ldType(),
-	"key"   : this.key(),
-	"match" : "match:" + this.matchop(),
-	"value" : this.value() || '',
-	"type"  : "type:" + this.type()
+        "@type" : "koral:" + this.ldType(),
+        "key"   : this.key(),
+        "match" : "match:" + this.matchop(),
+        "value" : this.value() || '',
+        "type"  : "type:" + this.type()
       };
     },
 
 
     toQuery : function () {
       if (!this.matchop() || !this.key())
-	return "";
+        return "";
 
       // Build doc string based on key
       var string = this.key() + ' ';
@@ -536,26 +537,26 @@
       // Add match operator
       switch (this.matchop()) {
       case "ne":
-	string += '!=';
-	break;
+        string += '!=';
+        break;
       case "contains":
-	string += '~';
-	break;
+        string += '~';
+        break;
       case "excludes":
-	string += '!~';
-	break;
+        string += '!~';
+        break;
       case "containsnot":
-	string += '!~';
-	break;
+        string += '!~';
+        break;
       case "geq":
-	string += 'since';
-	break;
+        string += 'since';
+        break;
       case "leq":
-	string += 'until';
-	break;
+        string += 'until';
+        break;
       default:
-	string += (this.type() == 'date') ? 'in' : '=';
-	break;
+        string += (this.type() == 'date') ? 'in' : '=';
+        break;
       };
 
       string += ' ';
@@ -563,14 +564,14 @@
       // Add value
       switch (this.type()) {
       case "date":
-	return string + this.value();
-	break;
+        return string + this.value();
+        break;
       case "regex":
-	return string + '/' + this.value() + '/';
-	break;
+        return string + '/' + this.value() + '/';
+        break;
       case "string":
-	return string + '"' + this.value().quote() + '"';
-	break;
+        return string + '"' + this.value().quote() + '"';
+        break;
       };
 
       return "";
diff --git a/dev/js/src/vc/menu.js b/dev/js/src/vc/menu.js
index 5840438..af4f8e1 100644
--- a/dev/js/src/vc/menu.js
+++ b/dev/js/src/vc/menu.js
@@ -1,19 +1,22 @@
 /**
  * Menu showing all key fields.
  */
-define(['menu', 'vc/item'], function (menuClass, itemClass) {
+define(['menu', 'vc/item', 'vc/prefix'], function (menuClass, itemClass, prefixClass) {
   return {
     create : function (params) {
       var obj = Object.create(menuClass)
-	.upgradeTo(this)
-	._init(params, {itemClass : itemClass});
+          .upgradeTo(this)
+          ._init(params, {
+            itemClass : itemClass,
+            prefixClass : prefixClass
+          });
       obj.limit(6);
 
       // This is only domspecific
       obj.element().addEventListener('blur', function (e) {
-	this.menu.hide();
+        this.menu.hide();
       });
-
+ 
       return obj;
     },
 
@@ -29,7 +32,7 @@
      */
     release : function (key, type) {
       if (this._cb !== undefined)
-	this._cb(key, type);
+        this._cb(key, type);
     }
   };
 });
diff --git a/dev/js/src/vc/prefix.js b/dev/js/src/vc/prefix.js
new file mode 100644
index 0000000..5deefe4
--- /dev/null
+++ b/dev/js/src/vc/prefix.js
@@ -0,0 +1,21 @@
+define(['menu/prefix'], function (prefixClass) {
+  return {
+
+    /**
+     * Create prefix object for the hint helper menu.
+     */
+    create : function (params) {
+      return Object.create(prefixClass).
+	      upgradeTo(this)._init(params);
+    },
+
+    /**
+     * Override the prefix action.
+     */
+    onclick : function (e) {
+      var m = this.menu();
+      var value = this.value();
+      m.release(value, "string");
+    }
+  };
+});