Turn vc operators into buttongroup

Change-Id: I61df007ac73cdb009f26a0fedb5e6f99012d185a
diff --git a/dev/js/spec/buttongroupSpec.js b/dev/js/spec/buttongroupSpec.js
index fa5a888..1e27d96 100644
--- a/dev/js/spec/buttongroupSpec.js
+++ b/dev/js/spec/buttongroupSpec.js
@@ -35,6 +35,40 @@
       expect(btn.innerText).toEqual('Meta');
     });
 
+    it('should be clearable', function () {
+      var group = buttonGroupClass.create();
+      expect(group.element().classList.contains('button-group')).toBeTruthy();
+
+      group.add('Meta', ['meta', 'top'], function (e) {});
+      group.add('Mate', ['mate'], function (e) {});
+
+      var btn = group.element().children[0];
+      expect(btn.tagName).toEqual('SPAN');
+      expect(btn.classList.contains('meta')).toBeTruthy();
+      expect(btn.classList.contains('top')).toBeTruthy();
+      expect(btn.innerText).toEqual('Meta');
+
+      btn = group.element().children[1];
+      expect(btn.tagName).toEqual('SPAN');
+      expect(btn.classList.contains('mate')).toBeTruthy();
+      expect(btn.classList.contains('top')).toBeFalsy();
+      expect(btn.innerText).toEqual('Mate');
+
+      // clear button
+      group.clear();
+
+      expect(group.element().children.length).toEqual(0);
+
+      group.add('New', ['new'], function (e) {});
+
+      btn = group.element().children[0];
+      expect(btn.tagName).toEqual('SPAN');
+      expect(btn.classList.contains('new')).toBeTruthy();
+      expect(btn.classList.contains('top')).toBeFalsy();
+      expect(btn.innerText).toEqual('New');
+    });
+
+    
     it('should listen to button clicks', function () {
       var group = buttonGroupClass.create();
 
diff --git a/dev/js/spec/matchSpec.js b/dev/js/spec/matchSpec.js
index 5a4111b..b09a7cb 100644
--- a/dev/js/spec/matchSpec.js
+++ b/dev/js/spec/matchSpec.js
@@ -350,7 +350,8 @@
       expect(e.classList.contains('active')).toBe(true);
       expect(e["_match"]).not.toBe(undefined);
 
-      actions = e.querySelector("p.ref > div.action.bottom").getElementsByTagName("span");
+      actions = e.querySelector("p.ref > div.action.bottom").children;
+      
       expect(actions[0].getAttribute("class")).toEqual("meta");
       expect(actions[1].getAttribute("class")).toEqual("info");
       expect(actions[2].getAttribute("class")).toEqual("tree");
diff --git a/dev/js/spec/vcSpec.js b/dev/js/spec/vcSpec.js
index 482d384..66183f4 100644
--- a/dev/js/spec/vcSpec.js
+++ b/dev/js/spec/vcSpec.js
@@ -30,15 +30,15 @@
   };
 
   function _andOn (obj) {
-    KorAP._and.bind(obj.element().lastChild.firstChild).apply();
+    KorAP._and.apply(obj);
   };
 
   function _orOn (obj) {
-    KorAP._or.bind(obj.element().lastChild.firstChild).apply();
+    KorAP._or.apply(obj);
   };
 
   function _delOn (obj) {
-    KorAP._delete.bind(obj.element().lastChild.firstChild).apply();
+    KorAP._delete.apply(obj);
   };
 
   var demoFactory = buildFactory(vcClass, {
@@ -566,7 +566,7 @@
       expect(docElement.lastChild.children.length).toEqual(0);
     });
 
-    it('should be removable, when no root', function () {
+    it('should be removable, when no root', function () {    
       var docGroup = docGroupClass.create();
       docGroup.operation('or');
       expect(docGroup.operation()).toEqual('or');
@@ -582,6 +582,9 @@
       // Add unspecified object
       docGroup.append();
 
+      var parent = document.createElement('div');
+      parent.appendChild(docGroup.element());
+
       expect(docGroup.element().getAttribute('class')).toEqual('docGroup');
       expect(docGroup.element().children[0].getAttribute('class')).toEqual('doc');
 
@@ -593,6 +596,7 @@
       expect(unspec.lastChild.children[0].getAttribute('class')).toEqual('delete');
     });
 
+    
     it('should be replaceable by a doc', function () {
       var doc = unspecifiedClass.create();
       expect(doc.ldType()).toEqual("non");
@@ -1141,15 +1145,15 @@
         '(Titel = "Baum" & Veröffentlichungsort = "hihi" & ' +
           '(Titel = "Baum" | Veröffentlichungsort = "hihi")) ' +
           '| Untertitel ~ "huhu"');
-      expect(vc.root().element().lastChild.children[0].firstChild.nodeValue).toEqual('and');
-      expect(vc.root().element().lastChild.children[1].firstChild.nodeValue).toEqual('×');
+      expect(vc.root().element().lastChild.children[0].innerText).toEqual('and');      
+      expect(vc.root().element().lastChild.children[1].innerText).toEqual('×');
       expect(vc.root().delOperand(vc.root().getOperand(0)).update()).not.toBeUndefined();
       expect(vc.toQuery()).toEqual('Untertitel ~ "huhu"');
 
       var lc = vc.root().element().lastChild;
-      expect(lc.children[0].firstChild.nodeValue).toEqual('and');
-      expect(lc.children[1].firstChild.nodeValue).toEqual('or');
-      expect(lc.children[2].firstChild.nodeValue).toEqual('×');
+      expect(lc.children[0].innerText).toEqual('and');
+      expect(lc.children[1].innerText).toEqual('or');
+      expect(lc.children[2].innerText).toEqual('×');
 
       // Clean everything
       vc.clean();
@@ -1230,23 +1234,22 @@
       expect(op.or()).toBeTruthy();
       expect(op.del()).toBeTruthy();
 
-      var e = op.element();
+      var e = op.update();
       expect(e.getAttribute('class')).toEqual('operators button-group');
       expect(e.children[0].getAttribute('class')).toEqual('or');
-      expect(e.children[0].firstChild.data).toEqual('or');
+      expect(e.children[0].innerText).toEqual('or');
       expect(e.children[1].getAttribute('class')).toEqual('delete');
-      expect(e.children[1].firstChild.data).toEqual('×');
+      expect(e.children[1].innerText).toEqual('×');
 
       op.and(true);
       op.del(false);
-      op.update();
+      e = op.update();
 
-      e = op.element();
       expect(e.getAttribute('class')).toEqual('operators button-group');
       expect(e.children[0].getAttribute('class')).toEqual('and');
-      expect(e.children[0].firstChild.data).toEqual('and');
+      expect(e.children[0].innerText).toEqual('and');
       expect(e.children[1].getAttribute('class')).toEqual('or');
-      expect(e.children[1].firstChild.data).toEqual('or');
+      expect(e.children[1].innerText).toEqual('or');
     });
   });
 
diff --git a/dev/js/src/buttongroup.js b/dev/js/src/buttongroup.js
index a1dac7b..12870b5 100644
--- a/dev/js/src/buttongroup.js
+++ b/dev/js/src/buttongroup.js
@@ -19,6 +19,7 @@
       return this;
     },
 
+    
     /**
      * Return main element
      */
@@ -26,6 +27,21 @@
       return this._element;
     },
 
+    
+    /**
+     * 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;
+    },
+
+    
     /**
      * Add button in order
      */
@@ -48,6 +64,7 @@
       });
     },
 
+    
     /**
      * Bind an object to all callbacks of the button group
      */
@@ -56,6 +73,15 @@
         this._bind = obj;
       };
       return this._bind || this;
+    },
+
+    
+    /**
+     * Remove all defined buttons
+     */
+    clear : function () {
+      _removeChildren(this._element);
+      return this;
     }
   }
 });
diff --git a/dev/js/src/vc/operators.js b/dev/js/src/vc/operators.js
index 842fbc1..1d4dc94 100644
--- a/dev/js/src/vc/operators.js
+++ b/dev/js/src/vc/operators.js
@@ -1,7 +1,7 @@
 /**
  * Operators for criteria
  */
-define(['util'], function () {
+define(['buttongroup'], function (buttonGroupClass) {
 
   const loc = KorAP.Locale;
   loc.DEL   = loc.DEL   || '×';
@@ -15,33 +15,32 @@
 
   // Add new unspecified document
   function _add (obj, type) {
-    var ref = obj.parentNode.refTo;
-    var parent = ref.parent();
+    var parent = obj.parent();
 
-    if (ref.ldType() === 'docGroup') {
+    if (obj.ldType() === 'docGroup') {
 
       // Check that the action differs from the type
-      if (ref.operation() === type)
+      if (obj.operation() === type)
 	      return;
 
       if (parent.ldType() !== null) {
-	      return parent.newAfter(ref);
+	      return parent.newAfter(obj);
       }
       else {
 	      // The group is on root - wrap
-	      return ref.wrapOnRoot();
+	      return obj.wrapOnRoot();
       };
     }
-    else if (ref.ldType() === 'doc') {
+    else if (obj.ldType() === 'doc') {
 
       if (parent.ldType() === null) {
-	      return ref.wrapOnRoot(type);
+	      return obj.wrapOnRoot(type);
       }
       else if (parent.operation() === type) {
-	      return parent.newAfter(ref);
+	      return parent.newAfter(obj);
       }
       else {
-	      return ref.wrap(type);
+	      return obj.wrap(type);
       };
     };
   };
@@ -61,85 +60,65 @@
 
   // Remove doc or docGroup
   KorAP._delete = function () {
-    var ref = this.parentNode.refTo;
-    if (ref.parent().ldType() !== null) {
-      return ref.parent().delOperand(ref).update();
+    if (this.parent().ldType() !== null) {
+      return this.parent().delOperand(this).update();
     }
     else {
-      ref.parent().clean();
+      this.parent().clean();
     };
   };
 
 
   return {
     create : function (and, or, del) {
-      var op = Object.create(this);
+
+      // Inherit from buttonGroupClass
+      var op = Object(buttonGroupClass).create(['operators']).upgradeTo(this);
       op.and(and);
       op.or(or);
       op.del(del);
+      op.update();
+
       return op;
     },
 
+    /*
+      * Update the element
+     */
     update : function () {
-      // Init the element
-      if (this._element === undefined)
-	      return this.element();
 
-      var op = this._element;
+      // Clear button group
+      this.clear();
 
-      op.refTo = this.parent();
-
-      // Remove everything underneath
-      _removeChildren(op);
-      
-      // Add and button
       if (this._and === true) {
-	      var andE = op.addE('span');
-	      andE.setAttribute('class', 'and');
-	      andE.addEventListener('click', KorAP._and, false);
-	      andE.addT(loc.AND);
+        this.add(loc.AND, ['and'], KorAP._and);
       };
 
       // Add or button
       if (this._or === true) {
-	      var orE = op.addE('span');
-	      orE.setAttribute('class', 'or');
-	      orE.addEventListener('click', KorAP._or, false);
-	      orE.addT(loc.OR);
+        this.add(loc.OR, ['or'], KorAP._or);
       };
 
       // Add delete button
       if (this._del === true) {
-	      var delE = op.addE('span');
-	      delE.setAttribute('class', 'delete');
-	      delE.addT(loc.DEL);
-	      delE.addEventListener('click', KorAP._delete, false);
+        this.add(loc.DEL, ['delete'], KorAP._delete);
       };
 
-      return op;
+      return this.element();
     },
 
     // Be aware! This may be cyclic
+    // This is somehow redundant with bind, but relevant for ldTypes
     parent : function (obj) {
-      if (arguments.length === 1)
+      if (arguments.length === 1) {
 	      this._parent = obj;
+
+        // This is somehow duplicate - but it's not that relevant
+        this.bind(obj);
+      };
       return this._parent;
     },
 
-    element : function () {
-
-      // Return existing element
-      if (this._element !== undefined)
-	      return this._element;
-
-      this._element = document.createElement('div');
-      this._element.classList.add('operators', 'button-group');
-
-      // Init elements
-      this.update();
-      return this._element;
-    },
-
     and : function (bool) {
       if (arguments.length === 1)
 	      this._and = _bool(bool);