Refactored to remove element objects
diff --git a/public/js/spec/vcSpec.js b/public/js/spec/vcSpec.js
index 3d1d077..18d59b6 100644
--- a/public/js/spec/vcSpec.js
+++ b/public/js/spec/vcSpec.js
@@ -258,14 +258,15 @@
       ]
     });
 
-  it('should be initializable', function () {
 
+  it('should be initializable', function () {
     // Create empty group
     var docGroup = KorAP.DocGroup.create();
     expect(docGroup.operation()).toEqual('and');
 
     // Create empty group
-    docGroup = KorAP.DocGroup.create('or');
+    docGroup = KorAP.DocGroup.create();
+    docGroup.operation('or');
     expect(docGroup.operation()).toEqual('or');
   });
 
@@ -302,7 +303,8 @@
     expect(op2.matchop()).toEqual("eq");
 
     // Create empty group
-    var newGroup = docGroup.appendOperand(KorAP.DocGroup.create("or"));
+    var newGroup = docGroup.appendOperand(KorAP.DocGroup.create());
+    newGroup.operation('or');
     newGroup.appendOperand(docFactory.create());
     newGroup.appendOperand(docFactory.create({
       "type" : "type:regex",
@@ -373,9 +375,9 @@
   });
 });
 
-describe('KorAP.DocElement', function () {
+describe('KorAP.Doc element', function () {
   it('should be initializable', function () {
-    var docElement = KorAP.DocElement.create(undefined, {
+    var docElement = KorAP.Doc.create(undefined, {
       "@type" : "korap:doc",
       "key":"Titel",
       "value":"Baum",
@@ -401,10 +403,10 @@
   });
 });
 
-describe('KorAP.DocGroupElement', function () {
+describe('KorAP.DocGroup element', function () {
   it('should be initializable', function () {
 
-    var docGroupElement = KorAP.DocGroupElement.create(undefined, {
+    var docGroup = KorAP.DocGroup.create(undefined, {
       "@type" : "korap:docGroup",
       "operation" : "operation:and",
       "operands" : [
@@ -425,8 +427,8 @@
       ]
     });
 
-    expect(docGroupElement.operation()).toEqual('and');
-    var e = docGroupElement.element();
+    expect(docGroup.operation()).toEqual('and');
+    var e = docGroup.element();
     expect(e.getAttribute('class')).toEqual('docGroup');
     expect(e.getAttribute('data-operation')).toEqual('and');
 
@@ -453,7 +455,7 @@
   });
 
   it('should be deserializable with nested groups', function () {
-    var docGroupElement = KorAP.DocGroupElement.create(undefined, {
+    var docGroup = KorAP.DocGroup.create(undefined, {
       "@type" : "korap:docGroup",
       "operation" : "operation:or",
       "operands" : [
@@ -487,8 +489,8 @@
       ]
     });
 
-    expect(docGroupElement.operation()).toEqual('or');
-    var e = docGroupElement.element();
+    expect(docGroup.operation()).toEqual('or');
+    var e = docGroup.element();
     expect(e.getAttribute('class')).toEqual('docGroup');
     expect(e.getAttribute('data-operation')).toEqual('or');
 
diff --git a/public/js/src/vc.js b/public/js/src/vc.js
index 9ff6325..771152d 100644
--- a/public/js/src/vc.js
+++ b/public/js/src/vc.js
@@ -1,8 +1,7 @@
 var KorAP = KorAP || {};
 
-// Todo: Implement a working localization solution!
-// Todo: Refactor out the distinction between DocElement and Doc,
-//       DocGroupElement and DocGroup
+// TODO: Implement a working localization solution!
+// TODO: Support 'update' method to update elements on change
 
 /*
  * Error codes:
@@ -20,10 +19,6 @@
 */
 
 
-/*
-  - TODO: Support 'update' method to update elements on change
-*/
-
 (function (KorAP) {
   "use strict";
 
@@ -54,10 +49,10 @@
       if (json !== undefined) {
 	// Root object
 	if (json['@type'] == 'korap:doc') {
-	  obj._root = KorAP.DocElement.create(undefined, json);
+	  obj._root = KorAP.Doc.create(undefined, json);
 	}
 	else if (json['@type'] == 'korap:docGroup') {
-	  obj._root = KorAP.DocGroupElement.create(undefined, json);
+	  obj._root = KorAP.DocGroup.create(undefined, json);
 	}
 	else {
 	  KorAP.log(813, "Collection type is not supported");
@@ -67,7 +62,7 @@
 
       else {
 	// Add unspecified object
-	obj._root = KorAP.UnspecifiedDocElement.create();
+	obj._root = KorAP.UnspecifiedDoc.create();
       };
 
       // Add root element to root node
@@ -90,6 +85,9 @@
     }
   };
 
+  /**
+   * Operators for criteria
+   */
   KorAP.Operators = {
     create : function (and, or, del) {
       var op = Object.create(KorAP.Operators);
@@ -170,11 +168,9 @@
   /**
    * Unspecified criterion
    */
-  KorAP.UnspecifiedDocElement = {
-    _obj : function () { return KorAP.UnspecifiedDocElement; },
-    _objType : 'UnspecifiedDocElement',
+  KorAP.UnspecifiedDoc = {
     create : function (parent) {
-      var obj = Object.create(KorAP.JsonLD).extend(KorAP.UnspecifiedDocElement);
+      var obj = Object.create(KorAP.JsonLD).upgradeTo(KorAP.UnspecifiedDoc);
       if (parent !== undefined)
 	obj._parent = parent;
       return obj;
@@ -189,12 +185,15 @@
   };
 
 
-  KorAP.DocElement = {
-    _obj : function () { return KorAP.DocElement; },
-    _objType : 'DocElement',
+  /**
+   * Virtual collection doc criterion.
+   */
+  KorAP.Doc = {
+    _ldType : "doc",
+    _obj : function () { return KorAP.Doc; },
 
     create : function (parent, json) {
-      var obj = KorAP.Doc.create().extend(KorAP.DocElement).fromJson(json);
+      var obj = Object(KorAP.JsonLD).create().upgradeTo(KorAP.Doc).fromJson(json);
       if (parent !== undefined)
 	obj._parent = parent;
       return obj;
@@ -233,10 +232,9 @@
       return e;
     },
 
-    // parent, element
     // Wrap a new operation around the doc element
     wrap : function (op) {
-      var group = KorAP.DocGroupElement.create(undefined);
+      var group = KorAP.DocGroup.create(undefined);
       group.appendOperand(this);
       group.appendOperand(null);
       this.parent(group);
@@ -249,247 +247,12 @@
       div.appendChild(this.element);
       return div;
 */
-    }
-  };
-
-  KorAP.DocGroupElement = {
-    _obj : function () { return KorAP.DocGroupElement; },
-    _objType : 'DocGroupElement',
-
-    create : function (parent, json) {
-      var obj = KorAP.DocGroup.create().extend(KorAP.DocGroupElement).fromJson(json);
-      if (parent !== undefined)
-	obj._parent = parent;
-      return obj;
     },
-    appendOperand : function (operand) {
-      switch (operand["@type"]) {
-      case undefined:
-	if (operand["objType"]) {
-	  if (operand.objType() === 'Doc') {
-	    operand = operand.upgrade(KorAP.DocElement);
-	  }
-	  else if (operand.objType() === 'DocGroup') {
-	    operand = operand.upgrade(KorAP.DocElementGroup);
-	  }
-	  else if (operand.objType() !== 'DocElement' &&
-		   operand.objType() !== 'DocElementGroup') {
-	    KorAP.log(812, "Operand not supported in document group");
-	    return;
-	  };
-	  this._operands.push(operand);
-	  return operand;
-	};
-
-	KorAP.log(701, "JSON-LD group has no @type attribute");
-	return;
-
-      case "korap:doc":
-	var doc = KorAP.DocElement.create().fromJson(operand);
-	if (doc === undefined)
-	  return;
-	this._operands.push(doc);
-	return doc;
-
-      case "korap:docGroup":
-	var docGroup = KorAP.DocGroupElement.create().fromJson(operand);
-	if (docGroup === undefined)
-	  return;
-	this._operands.push(docGroup);
-	return docGroup;
-
-      default:
-	KorAP.log(812, "Operand not supported in document group");
-	return;
-      };
-    },
-    element : function () {
-      if (this._element !== undefined)
-	return this._element;
-
-      this._element = document.createElement('div');
-      this._element.setAttribute('class', 'docGroup');
-      this._element.setAttribute('data-operation', this.operation());
-
-      for (var i in this.operands()) {
-	this._element.appendChild(
-	  this.getOperand(i).element()
-	);
-      };
-
-      return this._element;
-    },
-  };
-
-
-  // Abstract JsonLD object
-  KorAP.JsonLD = {
-    _obj : function () { return KorAP.JsonLD; },
-    _objType : 'JsonLD',
-    create : function () {
-      return Object.create(KorAP.JsonLD);
-    },
-
-    // Extend this object with another object
-    // Private data will be lost
-    extend : function (props) {
-      var jld = this._obj().create();
-      for (var prop in props) {
-	jld[prop] = props[prop];
-      };
-      return jld;
-    },
-
-    // Upgrade this object to another object
-    // Private data stays intact
-    upgrade : function (props) {
-      for (var prop in props) {
-	this[prop] = props[prop];
-      };
-      return this;
-    },
-    ldType : function (type) {
-      if (arguments.length === 1)
-	this._ldType = type;
-      return this._ldType;
-    },
-    objType : function () {
-      return this._objType;
-    },
-    parent : function (obj) {
-      if (arguments.length === 1)
-	this._parent = obj;
-      return this._parent;
-    }
-  };
-
-
-  KorAP.DocGroup = {
-    _ldType : "docGroup",
-    _obj : function () { return KorAP.DocGroup; },
-    _objType : 'DocGroup',
-    create : function (type) {
-      var docGroup = Object.create(KorAP.JsonLD).extend(KorAP.DocGroup);
-      if (type !== undefined)
-	docGroup.operation(type);
-      docGroup._operands = [];
-      return docGroup;
-    },
-
     // Deserialize from json
     fromJson : function (json) {
       if (json === undefined)
 	return this;
 
-      if (json["@type"] !== "korap:docGroup") {
-	KorAP.log(701, "JSON-LD group has no @type attribute");
-	return;
-      };
-
-      if (json["operation"] === undefined ||
-	  typeof json["operation"] !== 'string') {
-	KorAP.log(811, "Document group expects operation");
-	return;
-      };
-
-      var operation = json["operation"];
-
-      this.operation(operation.replace(/^operation:/,''));
-
-      if (json["operands"] === undefined ||
-	  !(json["operands"] instanceof Array)) {
-	KorAP.log(704, "Operation needs operand list")
-	return;
-      };
-
-      // Add all documents
-      for (var i in json["operands"]) {
-	var operand = json["operands"][i];
-	this.appendOperand(operand);
-      };
-    
-      return this;
-    },
-    operation : function (op) {
-      if (arguments.length === 1) {
-	if (KorAP._validGroupOpRE.test(op)) {
-	  this._op = op;
-	}
-	else {
-	  KorAP.log(810, "Unknown operation type");
-	  return;
-	};
-      };
-      return this._op || 'and';
-    },
-    operands : function () {
-      return this._operands;
-    },
-    appendOperand : function (operand) {
-      switch (operand["@type"]) {
-      case undefined:
-	if (operand["objType"] && (
-	  operand.objType() === 'Doc' ||
-	    operand.objType() === 'DocGroup')) {
-	  this._operands.push(operand);
-	  return operand;
-	};
-	KorAP.log(701, "JSON-LD group has no @type attribute");
-	return;
-
-      case "korap:doc":
-	var doc = KorAP.Doc.create().fromJson(operand);
-	if (doc === undefined)
-	  return;
-	this._operands.push(doc);
-	return doc;
-
-      case "korap:docGroup":
-	var docGroup = KorAP.DocGroup.create().fromJson(operand);
-	if (docGroup === undefined)
-	  return;
-	this._operands.push(docGroup);
-	return docGroup;
-
-      default:
-	KorAP.log(812, "Operand not supported in document group");
-	return;
-      };
-    },
-    getOperand : function (index) {
-      return this._operands[index];
-    },
-    toJson : function () {
-      var opArray = new Array();
-      for (var i in this._operands) {
-	opArray.push(this._operands[i].toJson());
-      };
-      return {
-	"@type"     : "korap:" + this.ldType(),
-	"operation" : "operation:" + this.operation(),
-	"operands"  : opArray
-      };
-    }
-  };
-
-  /**
-   * Virtual collection doc criterion.
-   */
-  KorAP.Doc = {
-    _ldType : "doc",
-    _obj : function () { return KorAP.Doc; },
-    _objType : 'Doc',
-    // Create new
-    create : function () {
-      return Object.create(KorAP.JsonLD).extend(KorAP.Doc);
-    },
-
-    // Deserialize from json
-    fromJson : function (json) {
-      if (json === undefined)
-	return this;
-      // return this.create();
-
       if (json["@type"] !== "korap:doc") {
 	KorAP.log(701, "JSON-LD group has no @type attribute");
 	return;
@@ -620,4 +383,165 @@
       };
     }
   };
+
+  /**
+   * Virtual collection group criterion.
+   */
+  KorAP.DocGroup = {
+    _ldType : "docGroup",
+
+    create : function (parent, json) {
+      var obj = Object.create(KorAP.JsonLD).upgradeTo(KorAP.DocGroup);
+      obj._operands = [];
+      obj.fromJson(json);
+      if (parent !== undefined)
+	obj._parent = parent;
+      return obj;
+    },
+    appendOperand : function (operand) {
+      switch (operand["@type"]) {
+      case undefined:
+	if (operand["ldType"] !== undefined) {
+	  if (operand.ldType() !== 'doc' &&
+		   operand.ldType() !== 'docGroup') {
+	    KorAP.log(812, "Operand not supported in document group");
+	    return;
+	  };
+	  this._operands.push(operand);
+	  return operand;
+	};
+
+	KorAP.log(701, "JSON-LD group has no @type attribute");
+	return;
+
+      case "korap:doc":
+	var doc = KorAP.Doc.create().fromJson(operand);
+	if (doc === undefined)
+	  return;
+	this._operands.push(doc);
+	return doc;
+
+      case "korap:docGroup":
+	var docGroup = KorAP.DocGroup.create().fromJson(operand);
+	if (docGroup === undefined)
+	  return;
+	this._operands.push(docGroup);
+	return docGroup;
+
+      default:
+	KorAP.log(812, "Operand not supported in document group");
+	return;
+      };
+    },
+    element : function () {
+      if (this._element !== undefined)
+	return this._element;
+
+      this._element = document.createElement('div');
+      this._element.setAttribute('class', 'docGroup');
+      this._element.setAttribute('data-operation', this.operation());
+
+      for (var i in this.operands()) {
+	this._element.appendChild(
+	  this.getOperand(i).element()
+	);
+      };
+
+      return this._element;
+    },
+    operation : function (op) {
+      if (arguments.length === 1) {
+	if (KorAP._validGroupOpRE.test(op)) {
+	  this._op = op;
+	}
+	else {
+	  KorAP.log(810, "Unknown operation type");
+	  return;
+	};
+      };
+      return this._op || 'and';
+    },
+    operands : function () {
+      return this._operands;
+    },
+    getOperand : function (index) {
+      return this._operands[index];
+    },
+
+    // Deserialize from json
+    fromJson : function (json) {
+      if (json === undefined)
+	return this;
+
+      if (json["@type"] !== "korap:docGroup") {
+	KorAP.log(701, "JSON-LD group has no @type attribute");
+	return;
+      };
+
+      if (json["operation"] === undefined ||
+	  typeof json["operation"] !== 'string') {
+	KorAP.log(811, "Document group expects operation");
+	return;
+      };
+
+      var operation = json["operation"];
+
+      this.operation(operation.replace(/^operation:/,''));
+
+      if (json["operands"] === undefined ||
+	  !(json["operands"] instanceof Array)) {
+	KorAP.log(704, "Operation needs operand list")
+	return;
+      };
+
+      // Add all documents
+      for (var i in json["operands"]) {
+	var operand = json["operands"][i];
+	this.appendOperand(operand);
+      };
+    
+      return this;
+    },
+    toJson : function () {
+      var opArray = new Array();
+      for (var i in this._operands) {
+	opArray.push(this._operands[i].toJson());
+      };
+      return {
+	"@type"     : "korap:" + this.ldType(),
+	"operation" : "operation:" + this.operation(),
+	"operands"  : opArray
+      };
+    }
+  };
+
+
+  // Abstract JsonLD object
+  KorAP.JsonLD = {
+    create : function () {
+      return Object.create(KorAP.JsonLD);
+    },
+
+    /**
+     * Upgrade this object to another object
+     * while private data stays intact
+     */
+    upgradeTo : function (props) {
+      for (var prop in props) {
+	this[prop] = props[prop];
+      };
+      return this;
+    },
+    ldType : function (type) {
+      if (arguments.length === 1)
+	this._ldType = type;
+      return this._ldType;
+    },
+    parent : function (obj) {
+      if (arguments.length === 1)
+	this._parent = obj;
+      return this._parent;
+    }
+  };
+ 
 }(this.KorAP));