Made Unspecified docs editable and start general Menu class
diff --git a/public/js/src/menu.js b/public/js/src/menu.js
new file mode 100644
index 0000000..3b79bdd
--- /dev/null
+++ b/public/js/src/menu.js
@@ -0,0 +1,202 @@
+var KorAP = KorAP || {};
+
+(function (KorAP) {
+  "use strict";
+
+  /**
+   * Item in the Dropdown menu
+   */
+  KorAP.MenuItem = {
+
+    /**
+     * Create a new MenuItem object.
+     *
+     * @constructor
+     * @this {MenuItem}
+     * @param {Array.<string>} An array object of name, action and
+     *   optionally a description
+     */
+    create : function (params) {
+      return Object.create(KorAP.MenuItem)._init(params);
+    },
+
+    /**
+     * Upgrade this object to another object,
+     * while private data stays intact.
+     *
+     * @param {Object] An object with properties.
+     */
+    upgradeTo : function (props) {
+      for (var prop in props) {
+	this[prop] = props[prop];
+      };
+      return this;
+    },
+
+    content : function (content) {
+      if (arguments.length === 1)
+	this._content = document.createTextNode(content);
+      return this._content;
+    },
+
+    lcField : function () {
+      return this._lcField;
+    },
+
+    action : function (action) {
+      if (arguments.length === 1)
+	this._action = action;
+      return this._action;
+    },
+
+    /**
+     * Check or set if the item is active
+     *
+     * @param {boolean|null} State of activity
+     */
+    active : function (bool) {
+      var cl = this.element().classList;
+      if (bool === undefined)
+	return cl.contains("active");
+      else if (bool)
+	cl.add("active");
+      else
+	cl.remove("active");
+    },
+
+    /**
+     * Check or set if the item is
+     * at the boundary of the menu
+     * list
+     *
+     * @param {boolean|null} State of activity
+     */
+    noMore : function (bool) {
+      var cl = this.element().classList;
+      if (bool === undefined)
+	return cl.contains("no-more");
+      else if (bool)
+	cl.add("no-more");
+      else
+	cl.remove("no-more");
+    },
+
+    /**
+     * Get the document element of the menu item
+     */
+    element : function () {
+      // already defined
+      if (this._element !== undefined)
+	return this._element;
+
+      // Create list item
+      var li = document.createElement("li");
+
+      // Connect action
+      li["action"] = this._action;
+
+      // Append template
+      li.appendChild(this.content());
+
+      return this._element = li;
+    },
+
+    /**
+     * Highlight parts of the item
+     *
+     * @param {string} Prefix string for highlights
+     */
+    highlight : function (prefix) {
+      this._highlight(this.element().firstChild, prefix);
+    },
+
+    // Highlight a certain substring of the menu item
+    _highlight : function (elem, prefix) {
+
+      if (elem.nodeType === 3) {
+
+	var text   = elem.nodeValue;
+	var textlc = text.toLowerCase();
+	var pos    = textlc.indexOf(prefix);
+	if (pos >= 0) {
+
+	  // First element
+	  if (pos > 0) {
+	    elem.parentNode.insertBefore(
+	      document.createTextNode(text.substr(0, pos)),
+	      elem
+	    );
+	  };
+
+	  // Second element
+	  var hl = document.createElement("mark");
+	  hl.appendChild(
+	    document.createTextNode(text.substr(pos, prefix.length))
+	  );
+	  elem.parentNode.insertBefore(hl, elem);
+
+	  // Third element
+	  var third = text.substr(pos + prefix.length);
+	  if (third.length > 0) {
+	    var thirdE = document.createTextNode(third);
+	    elem.parentNode.insertBefore(
+	      thirdE,
+	      elem
+	    );
+	    this._highlight(thirdE, prefix);
+	  };
+
+	  var p = elem.parentNode;
+	  p.removeChild(elem);
+	};
+      }
+      else {
+	var children = elem.childNodes;
+	for (var i = children.length -1; i >= 0; i--) {
+	  this._highlight(children[i], prefix);
+	};
+      };
+    },
+
+
+    /**
+     * 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) {
+      if (params[0] === undefined)
+	throw new Error("Missing parameters");
+
+      this.content(params[0]);
+
+      if (params.length === 2)
+	this._action = params[1];
+
+      this._lcField = ' ' + this.content().textContent.toLowerCase();
+
+      return this;
+    },
+  };
+
+}(this.KorAP));
diff --git a/public/js/src/vc.js b/public/js/src/vc.js
index 42e51c3..b68f0aa 100644
--- a/public/js/src/vc.js
+++ b/public/js/src/vc.js
@@ -60,6 +60,7 @@
   // Add new unspecified document
   KorAP._add = function (obj, type) {
     var ref = obj.parentNode.refTo;
+    console.log("DEBUG: " + type + " on " + ref.toQuery());
     var parent = ref.parent();
 
     if (ref.ldType() === 'docGroup') {
@@ -106,6 +107,7 @@
   // Remove doc or docGroup
   KorAP._delete = function () {
     var ref = this.parentNode.refTo;
+console.log("DEBUG: delete " + ref.toQuery());
     if (ref.parent().ldType() !== null) {
       return ref.parent().delOperand(ref).update();
     }
@@ -326,6 +328,34 @@
       return obj;
     },
 
+    // Set key - replace
+    key : function (v) {
+
+      // Not replaceable
+      if (this._parent === undefined)
+	return null;
+
+      // Set JSON-LD type
+      var newDoc = KorAP.Doc.create(this._parent, {
+	"@type" : "korap:doc",
+	"value" : "",
+	"key"   : v
+      });
+
+      // Unspecified document on root
+      if (this._parent.ldType() === null) {
+	this._parent.root(newDoc);
+	this.destroy();
+      }
+
+      // Unspecified document in group
+      else {
+	this._parent.replaceOperand(this, newDoc);
+      };
+      this._parent.update();
+      return newDoc;
+    },
+
     update : function () {
 
       if (this._element === undefined)
@@ -361,7 +391,9 @@
       this._element.setAttribute('class', 'doc unspecified');
       this.update();
       return this._element;
-    }
+    },
+
+    
   };
 
 
@@ -370,7 +402,7 @@
    */
   KorAP.Doc = {
     _ldType : "doc",
-    _obj : function () { return KorAP.Doc; },
+    _obj : function () { return KorAP.Doc },
 
     create : function (parent, json) {
       var obj = Object(KorAP.JsonLD).
@@ -572,6 +604,7 @@
       if (arguments.length === 1) {
 	this._key = value;
 	this._changed = true;
+	return this;
       };
       return this._key;
     },
@@ -580,6 +613,7 @@
       if (arguments.length === 1) {
 	this._matchop = match.replace(/^match:/, '');
 	this._changed = true;
+	return this;
       };
       return this._matchop || "eq";
     },
@@ -588,6 +622,7 @@
       if (arguments.length === 1) {
 	this._type = type;
 	this._changed = true;
+	return this;
       };
       return this._type || "string";
     },
@@ -596,6 +631,7 @@
       if (arguments.length === 1) {
 	this._value = value;
 	this._changed = true;
+	return this;
       };
       return this._value;
     },
@@ -1042,7 +1078,7 @@
     },
 
     toQuery : function () {
-      return loc.EMPTY;
+      return '';
     }
   };