Finished vc builder (although the value change needs finetuning and there are no tests for the menu)
diff --git a/Gruntfile.js b/Gruntfile.js
index 7e9edf8..acf027f 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -11,9 +11,7 @@
  *   for assets.
  *   http://yui.github.io/yuidoc/
  * TODO: Use https://www.npmjs.com/package/grunt-contrib-yuidoc
- * TODO: Use RequireJS
- *   http://addyosmani.com/writing-modular-js/
- *   http://qnundrum.com/question/393866
+ * TODO: Implement a LaTeX generator for a pdf of the dokumentation 
  */
 module.exports = function(grunt) {
   grunt.initConfig({
@@ -101,7 +99,9 @@
 	files: ['dev/scss/{util,base,fonts,kalamar,media}.scss',
 		'dev/scss/footer/footer.scss',
 		'dev/scss/header/{header,hint,menu,searchbar,vc}.scss',
-		'dev/scss/main/{alertify,highlight,kwic,logos,main,matchinfo,pagination,query,resultinfo,sidebar,tutorial}.scss'
+		'dev/scss/main/{alertify,highlight,kwic,logos,' +
+		'main,matchinfo,pagination,query,'+
+		'resultinfo,sidebar,tutorial}.scss'
 	       ],
 	tasks: ['sass'],
 	options: {
diff --git a/dev/demo/vcdemo.js b/dev/demo/vcdemo.js
index d136cef..ff93f27 100644
--- a/dev/demo/vcdemo.js
+++ b/dev/demo/vcdemo.js
@@ -15,13 +15,13 @@
       "operands":[
         {
           "@type":"koral:doc",
-          "key":"Titel",
+          "key":"title",
           "value":"Der Birnbaum",
           "match":"match:eq"
         },
         {
           "@type":"koral:doc",
-          "key":"Veröffentlichungsort",
+          "key":"pubPlace",
           "value":"Mannheim",
           "match":"match:eq"
         },
@@ -31,13 +31,13 @@
           "operands":[
             {
               "@type":"koral:doc",
-              "key":"Untertitel",
+              "key":"subTitle",
               "value":"Aufzucht und Pflege",
               "match":"match:eq"
             },
             {
               "@type":"koral:doc",
-              "key":"Untertitel",
+              "key":"subTitle",
               "value":"Gedichte",
               "match":"match:eq",
               "rewrites" : [
@@ -54,7 +54,7 @@
     },
     {
       "@type":"koral:doc",
-      "key":"Veröffentlichungsdatum",
+      "key":"pubDate",
       "type":"type:date",
       "value":"2015-03-05",
       "match":"match:geq"
@@ -63,22 +63,34 @@
 };
 
 require(['vc','lib/domReady'], function (vcClass, domReady) {
-  KorAP.Locale.AND = 'und';
-  KorAP.Locale.OR  = 'oder';
+
+  var loc = KorAP.Locale;
+
+  loc.AND = 'und';
+  loc.OR  = 'oder';
+  loc.VC_subTitle = 'Untertitel';
+  loc.VC_title = 'Titel';
+  loc.VC_pubDate = 'Veröffentlichungsdatum';
+  loc.VC_pubPlace = 'Veröffentlichungsort';
 
   domReady(function() {
+
+    // Create a new virtual collection by passing a based json object and
+    // field menu information
     var vc = vcClass.render(json, [
-      ['Titel', 'title', 'string'],
-      ['Untertitel', 'subTitle', 'string'],
-      ['Veröffentlichungsdatum', 'pubDate', 'date'],
-      ['Autor', 'author', 'string']
+      ['title', 'string'],
+      ['subTitle', 'string'],
+      ['pubDate', 'date'],
+      ['author', 'string']
     ]);
     document.getElementById('vc').appendChild(vc.element());
 
+    // show the current JSON serialization
     KorAP.showJSON = function () {
       document.getElementById("json").innerHTML = JSON.stringify(vc.root().toJson());
     };
 
+    // show the current query serialization
     KorAP.showQuery = function () {
       document.getElementById("query").innerHTML = vc.root().toQuery();
     };
diff --git a/dev/js/src/vc.js b/dev/js/src/vc.js
index 2587d1c..80f6627 100644
--- a/dev/js/src/vc.js
+++ b/dev/js/src/vc.js
@@ -1,5 +1,9 @@
 /**
  * Create virtual collections with a visual user interface.
+ * This resembles the collection type objects of a KoralQuery
+ * "collection" object.
+ *
+ * KoralQuery v0.3 is expected.
  *
  * @author Nils Diewald
  */
@@ -35,6 +39,7 @@
     OR : 'or',
     DELETE : 'x'
   }
+  and various field names with the prefix 'VC_'
 */
 
 define([
@@ -95,7 +100,28 @@
 
 	// Create key menu
 	KorAP._vcKeyMenu = menuClass.create(keyList);
-	KorAP._vcKeyMenu.limit(5);
+	KorAP._vcKeyMenu.limit(6);
+
+	// Create match menus ....
+	KorAP._vcMatchopMenu = {
+	  'string' : menuClass.create([
+	    ['eq', null],
+	    ['ne', null],
+	    ['contains', null],
+	    ['excludes', null]
+	  ]),
+	  'date' : menuClass.create([
+	    ['eq', null],
+	    ['geq', null],
+	    ['leq', null]
+	  ]),
+	  'regex' : menuClass.create([
+	    ['eq', null],
+	    ['ne', null],
+	    ['contains', null],
+	    ['excludes', null]
+	  ])
+	};
       };
 
       return this;
diff --git a/dev/js/src/vc/doc.js b/dev/js/src/vc/doc.js
index 436f9ef..10a49a8 100644
--- a/dev/js/src/vc/doc.js
+++ b/dev/js/src/vc/doc.js
@@ -1,45 +1,50 @@
 /**
- * Document criterion
+ * A new document criterion
  */
 
 define([
-  'vc/jsonld', 'vc/rewritelist'], function (jsonldClass, rewriteListClass) {
+  'vc/jsonld',
+  'vc/rewritelist',
+  'util'
+], function (jsonldClass, rewriteListClass) {
 
-/*
-  var fieldMenu = menuClass.create([
-    ['Titel', 'title', 'string'],
-    ['Untertitel', 'subTitle', 'string'],
-    ['Veröffentlichungsdatum', 'pubDate', 'date'],
-    ['Autor', 'author', 'string']
-  ]);
-
-  fieldMenu.limit(5);
-*/
+    /*
+      var fieldMenu = menuClass.create([
+      ['Titel', 'title', 'string'],
+      ['Untertitel', 'subTitle', 'string'],
+      ['Veröffentlichungsdatum', 'pubDate', 'date'],
+      ['Autor', 'author', 'string']
+      ]);
+      
+      fieldMenu.limit(5);
+    */
 
   _validRegexMatchRE  = new RegExp("^(?:eq|ne)$");
 
-    /**
-     * Criterion in a KorAP.Doc
-     */
-    function _changeKey () {
-      var doc = this.parentNode.refTo;
-      var key = doc.element().firstChild;
-      key.appendChild(fieldMenu.element());
-      fieldMenu.show();
-      fieldMenu.focus();
-      // key, matchop, type, value
-    };
+  var loc = KorAP.Locale;
+  loc.EMPTY = loc.EMPTY || '⋯';
 
   return {
+
+    // The JSON-LD type
     _ldType : "doc",
+
+    // The object ... maybe not important
     _obj : function () { return '???'; /*KorAP.Doc*/ },
     
+    /**
+     * Create a new document criterion
+     * by passing the parent object and a json construct.
+     */
     create : function (parent, json) {
+
+      // Create the object, inheriting from Json-LD class
       var obj = Object(jsonldClass).
 	create().
 	upgradeTo(this).
 	fromJson(json);
-      
+
+      // Bind the parent
       if (parent !== undefined)
 	obj._parent = parent;
       
@@ -47,6 +52,9 @@
       return obj;
     },
 
+    /**
+     * Update the elements content.
+     */
     update : function () {
       if (this._element === undefined)
 	return this.element();
@@ -66,39 +74,58 @@
 	};
 
 	// Added key
-	var key = document.createElement('span');
-	key.setAttribute('class', 'key');
+	this._keyE = document.createElement('span');
+	this._keyE.setAttribute('class', 'key');
 
 	// Change key
-	key.addEventListener('click', _changeKey, false);
+	this._keyE.addEventListener('click', this._changeKey.bind(this));
 
-	if (this.key())
-	  key.appendChild(document.createTextNode(this.key()));
-	
+	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
-	var matchop = document.createElement('span');
-	matchop.setAttribute('data-type', this.type());
-	matchop.setAttribute('class', 'match');
-	matchop.appendChild(
+	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
-	var value = document.createElement('span');
-	value.setAttribute('data-type', this.type());
-	value.setAttribute('class', 'value');
-	if (this.value())
-	  value.appendChild(
-	    document.createTextNode(this.value())
-	  );
+	// 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));
+	};
+
+	// Change value
+	this._valueE.addEventListener(
+	  'click',
+	  this._changeValue.bind(this)
+	);
+
 
 	// Remove all element children
 	_removeChildren(e);
 
 	// Add spans
-	e.appendChild(key);
-	e.appendChild(matchop);
-	e.appendChild(value);
+	e.appendChild(this._keyE);
+	e.appendChild(this._matchopE);
+	e.appendChild(this._valueE);
 
 	this.__changed = false;
       };
@@ -122,6 +149,10 @@
       return e;
     },
 
+
+    /**
+     * Get the associated element
+     */
     element : function () {
       if (this._element !== undefined)
 	return this._element;
@@ -133,7 +164,9 @@
       return this._element;
     },
 
-    // Wrap a new operation around the doc element
+    /**
+     * Wrap a new operation around the doc element
+     */
     wrap : function (op) {
       var parent = this.parent();
       var group = require('vc/docgroup').create(parent);
@@ -143,7 +176,9 @@
       return parent.replaceOperand(this, group).update();
     },
 
-    // Deserialize from json
+    /**
+     * Deserialize from json
+     */
     fromJson : function (json) {
       if (json === undefined)
 	return this;
@@ -250,6 +285,9 @@
       return this;
     },
 
+    /**
+     * Get or set the key
+     */
     key : function (value) {
       if (arguments.length === 1) {
 	this._key = value;
@@ -259,15 +297,93 @@
       return this._key;
     },
 
+    // Click on the key, show me the menu
+    _changeKey : function (e) {
+      var menu = KorAP._vcKeyMenu;
+
+      // Insert menu
+      this._element.insertBefore(
+	menu.element(),
+	this._keyE
+      );
+
+      // Release event
+      var that = this;
+      menu.released(function (key, 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());
+
+	// Update the doc
+	doc.update();
+
+	// hide!
+	this.hide();
+      });
+
+      // TODO: Highlight the old value!
+      menu.show();
+      menu.focus();
+    },
+
+    /**
+     * Get or set the match operator
+     */
     matchop : function (match) {
       if (arguments.length === 1) {
-	this._matchop = match.replace(/^match:/, '');
+	var m = match.replace(/^match:/, '');
+	if (
+	  (
+	    (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;
       };
       return this._matchop || "eq";
     },
 
+
+    // Click on the match operator, show me the menu
+    _changeMatchop : function (e) {
+      var menu = KorAP._vcMatchopMenu[this.type()];
+
+      if (menu === undefined) {
+	KorAP.log(0, "Unable to init menu for " + this.type());
+	return;
+      };
+
+      // Insert menu
+      this._element.insertBefore(
+	menu.element(),
+	this._matchopE
+      );
+
+      // Release event
+      var that = this;
+      menu.released(function (mo) {
+	that.matchop(mo).update();
+	this.hide();
+      });
+
+      menu.show();
+      menu.focus();
+    },
+
+
+    /**
+     * Get or set the type
+     */
     type : function (type) {
       if (arguments.length === 1) {
 	this._type = type;
@@ -277,15 +393,33 @@
       return this._type || "string";
     },
 
+
+    /**
+     * Get or set the value
+     */
     value : function (value) {
       if (arguments.length === 1) {
-	this._value = value;
+	if (this._type === 'date' && !KorAP._validDateRE.test(value)) {
+	  delete this._value;
+	}
+	else {
+	  this._value = value;
+	};
 	this._changed();
 	return this;
       };
       return this._value;
     },
 
+
+    // Click on the match operator, show me the menu
+    _changeValue : function (e) {
+      // TODO: Just kidding - this is temporary!
+      this.value(window.prompt('Enter new value'));
+      this.update();
+    },
+
+
     rewrites : function () {
       return this._rewrites;
     },
@@ -303,6 +437,7 @@
       this._element.classList.remove("rewritten");
     },
 
+
     toJson : function () {
       if (!this.matchop() || !this.key())
 	return {};
@@ -316,6 +451,7 @@
       };
     },
 
+
     toQuery : function () {
       if (!this.matchop() || !this.key())
 	return "";
diff --git a/dev/js/src/vc/item.js b/dev/js/src/vc/item.js
index 7922bbd..c05fbad 100644
--- a/dev/js/src/vc/item.js
+++ b/dev/js/src/vc/item.js
@@ -1,43 +1,79 @@
 // Field menu item
-define(['menu/item'], function (itemClass) {
+define(['menu/item', 'util'], function (itemClass) {
+
+  var loc = KorAP.Locale;
+
   return {
+
+    /**
+     * Create new menu item.
+     * Pass two parameters: value and type.
+     * the value may be localized by a name in
+     * KorAP.Locale with the prefix 'VC_',
+     * e.g. 'VC_subTitle'.
+     */
     create : function (params) {
       return Object.create(itemClass)
 	.upgradeTo(this)
 	._init(params);
     },
 
+    // Initialize item object
     _init : function (params) {
       if (params[0] === undefined)
 	throw new Error("Missing parameters");
 
-      this._name  = params[0];
-      this._value = params[1];
-      this._type  = params[2];
+      this._key = params[0];
+      this._type  = params[1];
+
+      var k = this._key;
+      this._name  = loc["VC_" + k] ? loc["VC_" + k] : k;
 
       this._lcField = ' ' + this._name.toLowerCase();
 
       return this;
     },
 
+    /**
+     * Override click event by passing all clicks
+     * to the menu object.
+     */
     onclick : function (e) {
       this.menu().release(
-	this._name,
-	this._value,
+	this._key,
 	this._type
       );
       e.halt();
     },
 
+    /**
+     * Get the name of the item.
+     * This is a potential localized version
+     * of the value.
+     */
     name : function () {
       return this._name;
     },
 
+    /**
+     * Get the type of the item.
+     */
     type : function () {
       return this._type;
     },
 
+    /**
+     * Get the key of the item.
+     */
+    key : function () {
+      return this._key;
+    },
+
+    /**
+     * Get the HTML element associated with the item. 
+     */
     element : function () {
+
       // already defined
       if (this._element !== undefined)
 	return this._element;
@@ -45,7 +81,7 @@
       // Create list item
       var li = document.createElement("li");
       li.setAttribute("data-type", this._type);
-      li.setAttribute("data-value", this._value);
+      li.setAttribute("data-key",  this._key);
 
       // Connect action
       li["onclick"] = this.onclick.bind(this);
diff --git a/dev/js/src/vc/menu.js b/dev/js/src/vc/menu.js
index 2cdc0e8..d305960 100644
--- a/dev/js/src/vc/menu.js
+++ b/dev/js/src/vc/menu.js
@@ -1,4 +1,6 @@
-// Field menu
+/**
+ * Menu showing all key fields.
+ */
 define(['menu', 'vc/item'], function (menuClass, itemClass) {
   return {
     create : function (params) {
@@ -6,12 +8,20 @@
 	.upgradeTo(this)
 	._init(itemClass, undefined, params)
     },
+
+    /**
+     * Register callback for click event.
+     */
     released : function (cb) {
       this._cb = cb;
     },
-    release : function (name, value, type) {
+
+    /**
+     * A click event was released
+     */
+    release : function (key, type) {
       if (this._cb !== undefined)
-	this._cb(name, value, type);
+	this._cb(key, type);
     }
   };
 });
diff --git a/dev/js/src/vc/unspecified.js b/dev/js/src/vc/unspecified.js
index f10dcf7..31101ef 100644
--- a/dev/js/src/vc/unspecified.js
+++ b/dev/js/src/vc/unspecified.js
@@ -117,9 +117,11 @@
      * Click on the unspecified object
      */
     onclick : function () {
+
+      // Get the key menu
       var menu = KorAP._vcKeyMenu;
 
-      // Add keyelement at the correct position
+      // Add key menu element at the correct position
       this._element.insertBefore(
 	menu.element(),	
 	this._element.firstChild
@@ -128,9 +130,9 @@
       var that = this;
 
       // Set released method
-      menu.released(function (name, value, type) {
-	that.key(name);
-	// + ' # ' + type);
+      menu.released(function (key, type) {
+	// Set chosen key and type - will return a doc
+	that.key(key).type(type).update();
 	this.hide();
       });
 
diff --git a/dev/scss/header/vc.scss b/dev/scss/header/vc.scss
index 16f62c3..d6650a4 100644
--- a/dev/scss/header/vc.scss
+++ b/dev/scss/header/vc.scss
@@ -101,7 +101,8 @@
    */
   .doc {
     padding-left: $left-padding;
-    > span + span {
+    > span + span,
+    > span + .menu + span{
       margin-left: 5pt;
     }
     > span.key,
@@ -145,8 +146,11 @@
     }
   }
 
-  .unspecified > span {
+  .doc > span {
     cursor: pointer;
+    &:hover {
+      color: $dark-green;
+    }
   }
 
   /**
@@ -200,6 +204,9 @@
     }
   }
 
+  .menu {
+    display: inline-block;
+  }
 }