Test events (deletion and extension with 'and' and 'or')
diff --git a/public/js/demo/vc.html b/public/js/demo/vc.html
index 372571c..79f7d0e 100644
--- a/public/js/demo/vc.html
+++ b/public/js/demo/vc.html
@@ -66,6 +66,9 @@
         }
       ]
     };
+    KorAP.Locale.AND = 'und';
+    KorAP.Locale.OR  = 'oder';
+
     var vc = KorAP.VirtualCollection.render(json);
     document.getElementById('vc').appendChild(vc.element());
 
diff --git a/public/js/spec/vcSpec.js b/public/js/spec/vcSpec.js
index 767f010..32a018f 100644
--- a/public/js/spec/vcSpec.js
+++ b/public/js/spec/vcSpec.js
@@ -465,7 +465,7 @@
     var docElement = doc.element();
     expect(docElement.getAttribute('class')).toEqual('doc unspecified');
     expect(docElement.firstChild.firstChild.data).toEqual('⋯');
-    expect(docElement.lastChild.getAttribute('class')).toEqual('operators');
+    expect(docElement.lastChild.lastChild.data).toEqual('⋯');
     expect(doc.toQuery()).toEqual('⋯');
 
     // Only removable
@@ -1060,3 +1060,372 @@
     expect(e.children[1].firstChild.data).toEqual('or');
   });
 });
+
+describe('KorAP._delete (event)', function () {
+  var complexVCFactory = buildFactory(KorAP.VirtualCollection,{
+    "@type": 'korap:docGroup',
+    'operation' : 'operation:and',
+    'operands' : [
+      {
+	"@type": 'korap:doc',
+	"key": 'pubDate',
+	"match": 'match:eq',
+	"value": '2014-12-05',
+	"type": 'type:date'
+      },
+      {
+	"@type" : 'korap:docGroup',
+	'operation' : 'operation:or',
+	'operands' : [
+	  {
+	    '@type' : 'korap:doc',
+	    'key' : 'title',
+	    'value' : 'Hello World!'
+	  },
+	  {
+	    '@type' : 'korap:doc',
+	    'key' : 'foo',
+	    'value' : 'bar'
+	  }
+	]
+      }
+    ]
+  });
+
+  it('should clean on root docs', function () {
+    var vc = KorAP.VirtualCollection.render({
+      "@type": 'korap:doc',
+      "key": 'pubDate',
+      "match": 'match:eq',
+      "value": '2014-12-05',
+      "type": 'type:date'
+    });
+    expect(vc.root().toQuery()).toEqual('pubDate in 2014-12-05');
+    expect(vc.root().element().lastChild.getAttribute('class')).toEqual('operators');
+
+    // Clean with delete from root
+    expect(vc.root().element().lastChild.lastChild.getAttribute('class')).toEqual('delete');
+    KorAP._delete.bind(vc.root().element().lastChild.lastChild).apply();
+    expect(vc.root().toQuery()).toEqual('⋯');
+    expect(vc.root().element().lastChild.lastChild.data).toEqual('⋯');
+  });
+
+  it ('should remove on nested docs', function () {
+    var vc = KorAP.VirtualCollection.render(
+      {
+	"@type": 'korap:docGroup',
+	'operation' : 'operation:and',
+	'operands' : [
+	  {
+	    "@type": 'korap:doc',
+	    "key": 'pubDate',
+	    "match": 'match:eq',
+	    "value": '2014-12-05',
+	    "type": 'type:date'
+	  },
+	  {
+	    "@type" : 'korap:doc',
+	    'key' : 'foo',
+	    'value' : 'bar'
+	  }
+	]
+      }
+    );
+
+    // Delete with direct element access
+    expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar"');
+    KorAP._delete.bind(vc.root().getOperand(0).element().lastChild.lastChild).apply();
+    expect(vc.toQuery()).toEqual('foo = "bar"');
+    expect(vc.root().ldType()).toEqual('doc');
+  });
+
+  it ('should clean on doc groups', function () {
+    var vc = KorAP.VirtualCollection.render(
+      {
+	"@type": 'korap:docGroup',
+	'operation' : 'operation:and',
+	'operands' : [
+	  {
+	    "@type": 'korap:doc',
+	    "key": 'pubDate',
+	    "match": 'match:eq',
+	    "value": '2014-12-05',
+	    "type": 'type:date'
+	  },
+	  {
+	    "@type" : 'korap:doc',
+	    'key' : 'foo',
+	    'value' : 'bar'
+	  }
+	]
+      }
+    );
+
+    // Cleanwith direct element access
+    expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar"');
+    KorAP._delete.bind(vc.root().element().lastChild.lastChild).apply();
+    expect(vc.toQuery()).toEqual('⋯');
+    expect(vc.root().ldType()).toEqual('non');
+  });
+
+  it ('should remove on nested doc groups (case of ungrouping 1)', function () {
+    var vc = complexVCFactory.create();
+
+    // Delete with direct element access
+    expect(vc.toQuery()).toEqual(
+      'pubDate in 2014-12-05 & (title = "Hello World!" | foo = "bar")'
+    );
+
+    // Remove hello world:
+    KorAP._delete.bind(vc.root().getOperand(1).getOperand(0).element().lastChild.lastChild).apply();
+    expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar"');
+    expect(vc.root().ldType()).toEqual('docGroup');
+  });
+
+  it ('should remove on nested doc groups (case of ungrouping 2)', function () {
+    var vc = complexVCFactory.create();
+
+    // Delete with direct element access
+    expect(vc.toQuery()).toEqual(
+      'pubDate in 2014-12-05 & (title = "Hello World!" | foo = "bar")'
+    );
+
+    // Remove bar
+    KorAP._delete.bind(vc.root().getOperand(1).getOperand(1).element().lastChild.lastChild).apply();
+    expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & title = "Hello World!"');
+    expect(vc.root().ldType()).toEqual('docGroup');
+    expect(vc.root().operation()).toEqual('and');
+  });
+
+  it ('should remove on nested doc groups (case of root changing)', function () {
+    var vc = complexVCFactory.create();
+
+    // Delete with direct element access
+    expect(vc.toQuery()).toEqual(
+      'pubDate in 2014-12-05 & (title = "Hello World!" | foo = "bar")'
+    );
+
+    // Remove bar
+    KorAP._delete.bind(vc.root().getOperand(0).element().lastChild.lastChild).apply();
+    expect(vc.toQuery()).toEqual('title = "Hello World!" | foo = "bar"');
+    expect(vc.root().ldType()).toEqual('docGroup');
+    expect(vc.root().operation()).toEqual('or');
+  });
+
+  it ('should remove on nested doc groups (list flattening)', function () {
+    var vc = KorAP.VirtualCollection.render(
+      {
+	"@type": 'korap:docGroup',
+	'operation' : 'operation:or',
+	'operands' : [
+	  {
+	    "@type": 'korap:doc',
+	    "key": 'pubDate',
+	    "match": 'match:eq',
+	    "value": '2014-12-05',
+	    "type": 'type:date'
+	  },
+	  {
+	    "@type" : 'korap:doc',
+	    'key' : 'foo',
+	    'value' : 'bar'
+	  },
+	  {
+	    "@type": 'korap:docGroup',
+	    'operation' : 'operation:and',
+	    'operands' : [
+	      {
+		"@type": 'korap:doc',
+		"key": 'pubDate',
+		"match": 'match:eq',
+		"value": '2014-12-05',
+		"type": 'type:date'
+	      },
+	      {
+		"@type" : 'korap:docGroup',
+		'operation' : 'operation:or',
+		'operands' : [
+		  {
+		    '@type' : 'korap:doc',
+		    'key' : 'title',
+		    'value' : 'Hello World!'
+		  },
+		  {
+		    '@type' : 'korap:doc',
+		    'key' : 'yeah',
+		    'value' : 'juhu'
+		  }
+		]
+	      }
+	    ]
+	  }
+	]
+      }
+    );
+
+    // Delete with direct element access
+    expect(vc.toQuery()).toEqual(
+      'pubDate in 2014-12-05 | foo = "bar" | ' +
+	'(pubDate in 2014-12-05 & ' +
+	'(title = "Hello World!" | yeah = "juhu"))'
+    );
+
+    expect(vc.root().ldType()).toEqual('docGroup');
+    expect(vc.root().operation()).toEqual('or');
+
+    // Operands and operators
+    expect(vc.element().firstChild.children.length).toEqual(4);
+    expect(vc.element().firstChild.lastChild.getAttribute('class')).toEqual('operators');
+
+    // Remove inner group and flatten
+    KorAP._delete.bind(
+      vc.root().getOperand(2).getOperand(0).element().lastChild.lastChild
+    ).apply();
+
+    expect(vc.toQuery()).toEqual(
+      'pubDate in 2014-12-05 | foo = "bar" | title = "Hello World!" | yeah = "juhu"'
+    );
+    expect(vc.root().ldType()).toEqual('docGroup');
+    expect(vc.root().operation()).toEqual('or');
+
+    // Operands and operators
+    expect(vc.element().firstChild.children.length).toEqual(5);
+    expect(vc.element().firstChild.lastChild.getAttribute('class')).toEqual('operators');
+  });
+});
+
+describe('KorAP._add (event)', function () {
+  var complexVCFactory = buildFactory(KorAP.VirtualCollection,{
+    "@type": 'korap:docGroup',
+    'operation' : 'operation:and',
+    'operands' : [
+      {
+	"@type": 'korap:doc',
+	"key": 'pubDate',
+	"match": 'match:eq',
+	"value": '2014-12-05',
+	"type": 'type:date'
+      },
+      {
+	"@type" : 'korap:docGroup',
+	'operation' : 'operation:or',
+	'operands' : [
+	  {
+	    '@type' : 'korap:doc',
+	    'key' : 'title',
+	    'value' : 'Hello World!'
+	  },
+	  {
+	    '@type' : 'korap:doc',
+	    'key' : 'foo',
+	    'value' : 'bar'
+	  }
+	]
+      }
+    ]
+  });
+
+  it ('should add new unspecified doc with "and"', function () {
+    var vc = KorAP.VirtualCollection.render(
+      {
+	"@type": 'korap:docGroup',
+	'operation' : 'operation:and',
+	'operands' : [
+	  {
+	    "@type": 'korap:doc',
+	    "key": 'pubDate',
+	    "match": 'match:eq',
+	    "value": '2014-12-05',
+	    "type": 'type:date'
+	  },
+	  {
+	    "@type" : 'korap:doc',
+	    'key' : 'foo',
+	    'value' : 'bar'
+	  }
+	]
+      }
+    );
+
+    expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar"');
+
+    var fc = vc.element().firstChild;
+    expect(fc.getAttribute('data-operation')).toEqual('and');
+    expect(fc.children.length).toEqual(3);
+    expect(fc.lastChild.getAttribute('class')).toEqual('operators');
+    expect(fc.children[0].getAttribute('class')).toEqual('doc');
+    expect(fc.children[1].getAttribute('class')).toEqual('doc');
+
+    // add with 'and' in the middle
+    KorAP._and.bind(vc.root().getOperand(0).element().lastChild.lastChild).apply();
+    expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar"');
+
+    fc = vc.element().firstChild;
+    expect(fc.getAttribute('data-operation')).toEqual('and');
+    expect(fc.children.length).toEqual(4);
+    expect(fc.lastChild.getAttribute('class')).toEqual('operators');
+
+    expect(fc.children[0].getAttribute('class')).toEqual('doc');
+    expect(fc.children[1].getAttribute('class')).toEqual('doc unspecified');
+    expect(fc.children[2].getAttribute('class')).toEqual('doc');
+  });
+
+  it ('should add new unspecified doc with "or"', function () {
+    var vc = KorAP.VirtualCollection.render(
+      {
+	"@type": 'korap:docGroup',
+	'operation' : 'operation:and',
+	'operands' : [
+	  {
+	    "@type": 'korap:doc',
+	    "key": 'pubDate',
+	    "match": 'match:eq',
+	    "value": '2014-12-05',
+	    "type": 'type:date'
+	  },
+	  {
+	    "@type" : 'korap:doc',
+	    'key' : 'foo',
+	    'value' : 'bar'
+	  }
+	]
+      }
+    );
+
+    expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar"');
+
+    var fc = vc.element().firstChild;
+    expect(fc.children.length).toEqual(3);
+    expect(fc.lastChild.getAttribute('class')).toEqual('operators');
+    expect(fc.children[0].getAttribute('class')).toEqual('doc');
+    expect(fc.children[1].getAttribute('class')).toEqual('doc');
+
+    // add with 'or' in the middle
+    KorAP._or.bind(vc.root().getOperand(0).element().lastChild.lastChild).apply();
+    expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar"');
+    fc = vc.element().firstChild;
+
+    expect(fc.getAttribute('data-operation')).toEqual('and');
+    expect(fc.children.length).toEqual(3);
+    expect(fc.children[0].getAttribute('class')).toEqual('docGroup');
+    expect(fc.children[0].getAttribute('data-operation')).toEqual('or');
+    expect(fc.children[1].getAttribute('class')).toEqual('doc');
+    expect(fc.children[2].getAttribute('class')).toEqual('operators');
+    expect(fc.lastChild.getAttribute('class')).toEqual('operators');
+
+    fc = vc.element().firstChild.firstChild;
+    expect(fc.children.length).toEqual(3);
+    expect(fc.children[0].getAttribute('class')).toEqual('doc');
+    expect(fc.children[1].getAttribute('class')).toEqual('doc unspecified');
+    expect(fc.children[2].getAttribute('class')).toEqual('operators');
+    expect(fc.lastChild.getAttribute('class')).toEqual('operators');
+  });
+
+  // Todo: wrap on root!!
+});
+
+/*
+ Todo: test event sequences:
+ - In a nested group with a 'doc' and a 'non', remove the 'doc',
+   so the 'non' needs to be flattened!
+*/
diff --git a/public/js/src/vc.js b/public/js/src/vc.js
index 06fcb18..09d20ea 100644
--- a/public/js/src/vc.js
+++ b/public/js/src/vc.js
@@ -34,6 +34,7 @@
   KorAP._validGroupOpRE     = new RegExp("^(?:and|or)$");
   KorAP._quote              = new RegExp("([\"\\\\])", 'g');
 
+  // Localization values
   var loc   = (KorAP.Locale = KorAP.Locale || {} );
   loc.AND   = loc.AND   || 'and';
   loc.OR    = loc.OR    || 'or';
@@ -41,11 +42,13 @@
   loc.EMPTY = loc.EMPTY || '⋯'
 
 
+  // Utility for analysing boolean values
   function _bool (bool) {
-    return (bool === undefined || bool === false) ? false : true;
+    return (bool === undefined || bool === null || bool === false) ? false : true;
   };
 
 
+  // Utility for removing all children of a node
   function _removeChildren (node) {
     // Remove everything underneath
     while (node.firstChild)
@@ -72,8 +75,19 @@
   };
 
 
+  // Add doc with 'and' relation
+  KorAP._and = function () {
+    return KorAP._add(this, 'and');
+  };
+
+
+  // Add doc with 'or' relation
+  KorAP._or = function () {
+    return KorAP._add(this, 'or');
+  };
+
   // Remove doc or docGroup
-  KorAP._delete = function (e) {
+  KorAP._delete = function () {
     var ref = this.parentNode.refTo;
     if (ref.parent().ldType() !== null)
       ref.parent().delOperand(ref).update();
@@ -208,7 +222,7 @@
       if (this._and === true) {
 	var andE = document.createElement('span');
 	andE.setAttribute('class', 'and');
-	andE.addEventListener('click', function () { return KorAP._add(this, 'and') }, false);
+	andE.addEventListener('click', KorAP._and, false);
 	andE.appendChild(
 	  document.createTextNode(KorAP.Locale.AND)
 	);
@@ -219,7 +233,7 @@
       if (this._or === true) {
 	var orE = document.createElement('span');
 	orE.setAttribute('class', 'or');
-	orE.addEventListener('click', function () { return KorAP._add(this, 'or') }, false);
+	orE.addEventListener('click', KorAP._or, false);
 	orE.appendChild(document.createTextNode(KorAP.Locale.OR));
 	op.appendChild(orE);
       };
@@ -305,17 +319,17 @@
       this._element.appendChild(ellipsis);
 
       // Set operators
-      var op = this.operators(
-	false,
-	false,
-	// No delete object, if this is the root
-	(this._parent !== undefined &&
-	 this.parent().ldType() !== null) ? true : false
-      );
+      if (this._parent !== undefined && this.parent().ldType() !== null) {
+	var op = this.operators(
+	  false,
+	  false,
+	  true
+	);
 
-      this._element.appendChild(
-	op.element()
-      );
+	this._element.appendChild(
+	  op.element()
+	);
+      };
 
       return this.element();
     },
@@ -793,14 +807,11 @@
       for (var i in this._operands) {
 	if (this._operands[i] === oldOp) {
 
-	  // Just insert a doc
-	  if (newOp.ldType() === "doc") {
-	    this._operands[i] = newOp;
-	    newOp.parent(this);
-	  }
-	  // Insert a group of a different operation
-	  // (i.e. "and" in "or"/"or" in "and")
-	  else if (newOp.operation() != this.operation()) {
+	  // Just insert a doc or ...
+	  if (newOp.ldType() === "doc" ||
+	      // ... insert a group of a different operation
+	      // (i.e. "and" in "or"/"or" in "and")
+	      newOp.operation() != this.operation()) {
 	    this._operands[i] = newOp;
 	    newOp.parent(this);
 	  }
@@ -883,7 +894,8 @@
     toJson : function () {
       var opArray = new Array();
       for (var i in this._operands) {
-	opArray.push(this._operands[i].toJson());
+	if (this._operands[i].ldType() !== 'non')
+	  opArray.push(this._operands[i].toJson());
       };
       return {
 	"@type"     : "korap:" + this.ldType(),
@@ -892,13 +904,23 @@
       };
     },
 
-    toQuery : function () {
-      return this._operands.
-	map(function (op) {
+    toQuery : function (brackets) {
+      var list = this._operands
+	.filter(function (op) {
+	  return op.ldType() !== 'non';
+	})
+	.map(function (op) {
 	  return (op.ldType() === 'docGroup') ?
-	    '(' + op.toQuery() + ')' :
+	    op.toQuery(true) :
 	    op.toQuery();
-	}).join(this.operation() === 'or' ? ' | ' : ' & ')
+	});
+
+      if (list.length === 1)
+	return list.join('');
+      else {
+	var str = list.join(this.operation() === 'or' ? ' | ' : ' & ');
+	return brackets ? '(' + str + ')' : str;
+      };
     }
   };