Support VC references in VC builder to fix #62

Change-Id: Iec84c12ade2f64e8bbbd3d42b9e52788a0fba3fe
diff --git a/dev/demo/vcdemo.js b/dev/demo/vcdemo.js
index 9bb67e2..a3957f3 100644
--- a/dev/demo/vcdemo.js
+++ b/dev/demo/vcdemo.js
@@ -48,6 +48,10 @@
                   "operation" : "operation:injection",
                 }
               ]
+            },
+            {
+              "@type":"koral:docGroupRef",
+              "ref":"@kalamar/myCorpus"
             }
           ]
         }
@@ -122,7 +126,8 @@
       ['title', 'string'],
       ['subTitle', 'string'],
       ['pubDate', 'date'],
-      ['author', 'text']
+      ['author', 'text'],
+      ['@referTo', 'ref']
     ]).fromJson(json);
 
     document.getElementById('vc-view').appendChild(vc.element());
diff --git a/dev/js/runner/all.html b/dev/js/runner/all.html
index 78e2f19..3f0ed33 100644
--- a/dev/js/runner/all.html
+++ b/dev/js/runner/all.html
@@ -44,7 +44,7 @@
         'spec/pluginSpec',
         'spec/queryCreatorSpec',
         'spec/statSpec',
-        //'spec/vcSpec'
+        // 'spec/vcSpec'
       ],
       function () {
         window.onload();
diff --git a/dev/js/spec/vcSpec.js b/dev/js/spec/vcSpec.js
index 7cfdf3a..e9ec76b 100644
--- a/dev/js/spec/vcSpec.js
+++ b/dev/js/spec/vcSpec.js
@@ -8,6 +8,7 @@
   var menuClass =        require('vc/menu');
   var prefixClass =      require('vc/prefix');
   var docGroupClass =    require('vc/docgroup');
+  var docGroupRefClass = require('vc/docgroupref');
   var unspecifiedClass = require('vc/unspecified');
   var operatorsClass =   require('vc/operators');
   var rewriteClass =     require('vc/rewrite');
@@ -553,6 +554,62 @@
     });
   });
 
+  describe('KorAP.DocGroupRef', function () {
+    // Create example factories
+    var docRefFactory = buildFactory(
+      docGroupRefClass,
+      {
+        "@type" : "koral:docGroupRef",
+        "ref" : "@max/myCorpus"
+      }
+    );
+
+    it('should be initializable', function () {
+      var vcRef = docGroupRefClass.create();
+      expect(vcRef.ref()).toBeUndefined();
+    });
+
+    it('should be definable', function () {
+      var vcRef = docGroupRefClass.create();
+      vcRef.ref("@peter/mycorpus");
+      expect(vcRef.ref()).toEqual("@peter/mycorpus");
+      vcRef.ref("@peter/mycorpus2");
+      expect(vcRef.ref()).toEqual("@peter/mycorpus2");
+    });
+
+    it('should deserialize JSON-LD string', function () {
+      var vcRef = docRefFactory.create();
+      expect(vcRef.ref()).toEqual("@max/myCorpus");
+    });
+
+    it('should serialize to JSON-LD', function () {
+      var vcRef = docRefFactory.create();
+      expect(vcRef.toJson()).toEqual(jasmine.objectContaining({
+        "@type" : "koral:docGroupRef",
+        "ref":"@max/myCorpus"
+      }));
+
+      vcRef.ref("@peter/myCorpus2");
+      expect(vcRef.toJson()).toEqual(jasmine.objectContaining({
+        "@type" : "koral:docGroupRef",
+        "ref":"@peter/myCorpus2"
+      }));
+    });
+
+    it('should serialize to a query', function () {
+      var vcRef = docRefFactory.create();
+      expect(vcRef.toQuery()).toEqual(
+        "referTo \"@max/myCorpus\""
+      );
+
+      vcRef.ref("@peter/myCorpus2");
+      expect(vcRef.toQuery()).toEqual(
+        "referTo \"@peter/myCorpus2\""
+      );
+    });
+  });
+
+  
   describe('KorAP.UnspecifiedDoc', function () {
     it('should be initializable', function () {
       var doc = unspecifiedClass.create();
@@ -591,6 +648,9 @@
       var unspec = docGroup.element().children[1];
       expect(unspec.getAttribute('class')).toEqual('doc unspecified');
 
+      // Only unspec and delete
+      expect(unspec.children.length).toEqual(2);
+
       // Removable
       expect(unspec.lastChild.children.length).toEqual(1);
       expect(unspec.lastChild.children[0].getAttribute('class')).toEqual('delete');
@@ -632,11 +692,14 @@
       expect(docGroup.getOperand(1).key()).toEqual("name");
       expect(docGroup.getOperand(1).value()).toBeUndefined();
 
+      expect(docGroup.getOperand(1).element().children.length).toEqual(4);
+
       op = docGroup.getOperand(1).element().lastChild;
       expect(op.getAttribute('class')).toEqual('operators button-group');
       expect(op.children[0].getAttribute('class')).toEqual('and');
       expect(op.children[1].getAttribute('class')).toEqual('or');
       expect(op.children[2].getAttribute('class')).toEqual('delete');
+
       expect(op.children.length).toEqual(3);
 
       docGroup.getOperand(1).value("Pachelbel");
@@ -647,7 +710,7 @@
       // Specified!
       expect(docGroup.toQuery()).toEqual('pubDate in 2014-12-05 | name = "Pachelbel"');
     });
-
+    
     it('should be replaceable on root', function () {
       var vc = vcClass.create();
       expect(vc.toQuery()).toEqual("");
@@ -712,6 +775,70 @@
         "match":"match:eq"
       }));
     });
+
+    
+    it('should be replacable by unspecified', function () {
+      var vc = vcClass.create([
+        ["pubDate", "date"]
+      ]).fromJson({
+        "@type" : "koral:doc",
+        "key":"Titel",
+        "value":"Baum",
+        "match":"match:eq"
+      });
+      expect(vc.toQuery()).toEqual("Titel = \"Baum\"");
+
+      var vcE = vc.element();
+      expect(vcE.firstChild.children.length).toEqual(4);
+
+      // Click to delete
+      vcE.firstChild.lastChild.lastChild.click();
+
+      expect(vcE.firstChild.children.length).toEqual(1);
+
+      expect(vcE.firstChild.textContent).toEqual("⋯");
+      vcE.firstChild.firstChild.click();
+      
+      // Click on pubDate
+      vcE.firstChild.getElementsByTagName("LI")[0].click();
+
+      expect(vcE.firstChild.firstChild.textContent).toEqual("pubDate");
+      expect(vcE.firstChild.children[1].getAttribute("data-type")).toEqual("date");
+
+      expect(vcE.firstChild.children.length).toEqual(4);
+    });
+
+    
+    it('should be replaceable by a docGroupRef', function () {
+      var vc = vcClass.create([
+        ["@referTo", "ref"]
+      ]).fromJson({
+        "@type" : "koral:doc",
+        "key":"Titel",
+        "value":"Baum",
+        "match":"match:eq"
+      });
+
+      expect(vc.toQuery()).toEqual("Titel = \"Baum\"");
+
+      var vcE = vc.element();
+      expect(vcE.firstChild.children.length).toEqual(4);
+
+      // Click to delete
+      vcE.firstChild.lastChild.lastChild.click();
+
+      expect(vcE.firstChild.children.length).toEqual(1);
+
+      expect(vcE.firstChild.textContent).toEqual("⋯");
+      vcE.firstChild.firstChild.click();
+      
+      // Click on @referTo
+      vcE.firstChild.getElementsByTagName("LI")[0].click();
+
+      expect(vcE.firstChild.firstChild.textContent).toEqual("@referTo");
+      expect(vcE.firstChild.children[1].getAttribute("data-type")).toEqual("string");
+      expect(vcE.firstChild.children.length).toEqual(3);
+    });
   });
 
   describe('KorAP.DocGroup element', function () {
@@ -828,6 +955,49 @@
     });
   });
 
+  describe('KorAP.DocGroupRef element', function () {
+    it('should be initializable', function () {
+      var docGroupRef = docGroupRefClass.create(undefined, {
+        "@type" : "koral:docGroupRef",
+        "ref" : "@franz/myVC1"
+      });
+      expect(docGroupRef.ref()).toEqual("@franz/myVC1");
+      var dgrE = docGroupRef.element();
+
+      expect(dgrE.children[0].firstChild.data).toEqual("@referTo");
+      expect(dgrE.children[0].tagName).toEqual("SPAN");
+      expect(dgrE.children[0].classList.contains("key")).toBeTruthy();
+      expect(dgrE.children[0].classList.contains("fixed")).toBeTruthy();
+      expect(dgrE.children[1].firstChild.data).toEqual("@franz/myVC1");
+      expect(dgrE.children[1].tagName).toEqual("SPAN");
+      expect(dgrE.children[1].classList.contains("value")).toBeTruthy();
+      expect(dgrE.children[1].getAttribute("data-type")).toEqual("string");
+    });
+
+    it('should be modifiable on reference', function () {
+      var docGroupRef = docGroupRefClass.create(undefined, {
+        "@type" : "koral:docGroupRef",
+        "ref" : "@franz/myVC1"
+      });
+      var dgrE = docGroupRef.element();
+      expect(docGroupRef.toQuery()).toEqual("referTo \"@franz/myVC1\"");
+      dgrE.children[1].click();
+
+      var input = dgrE.children[1].firstChild;
+      expect(input.tagName).toEqual("INPUT");
+      input.value = "Versuch";
+
+      var event = new KeyboardEvent("keypress", {
+	      "key" : "[Enter]",
+	      "keyCode"  : 13
+      });
+
+      input.dispatchEvent(event);
+      expect(docGroupRef.toQuery()).toEqual("referTo \"Versuch\"");     
+    });    
+  });
+
+  
   describe('KorAP.VirtualCorpus', function () {
     var simpleGroupFactory = buildFactory(docGroupClass, {
       "@type" : "koral:docGroup",
@@ -963,6 +1133,29 @@
       expect(second.matchop()).toEqual('eq');
     });
 
+    it('should be based on a docGroupRef', function () {
+      var vc = vcClass.create().fromJson({
+        "@type" : "koral:docGroupRef",
+        "ref":"myCorpus"
+      });
+
+      // iv class="doc groupref"><span class="key fixed">@referTo</span><span data-type="string" class="value">myCorpus</span>
+      var vcE = vc.element();
+      expect(vcE.getAttribute('class')).toEqual('vc');
+      expect(vcE.firstChild.tagName).toEqual('DIV');
+      expect(vcE.firstChild.classList.contains('groupref')).toBeTruthy();
+
+      expect(vcE.firstChild.firstChild.tagName).toEqual('SPAN');
+      expect(vcE.firstChild.firstChild.classList.contains('key')).toBeTruthy();
+      expect(vcE.firstChild.firstChild.classList.contains('fixed')).toBeTruthy();
+
+      expect(vcE.firstChild.firstChild.textContent).toEqual("@referTo");
+
+      expect(vcE.firstChild.children[1].tagName).toEqual('SPAN');
+      expect(vcE.firstChild.children[1].classList.contains('value')).toBeTruthy();
+      expect(vcE.firstChild.children[1].getAttribute('data-type')).toEqual('string');
+      expect(vcE.firstChild.children[1].textContent).toEqual("myCorpus");
+    });
 
     it('should be based on a nested docGroup', function () {
       var vc = nestedGroupFactory.create();
@@ -978,6 +1171,27 @@
       expect(vc.element().firstChild.children[2].getAttribute('class')).toEqual('operators button-group');
     });    
 
+    it('should be based on a nested docGroupRef', function () {
+      var vc = vcClass.create().fromJson({
+        "@type" : "koral:docGroup",
+        "operation" : "operation:and",
+        "operands" : [{
+          "@type" : "koral:docGroupRef",
+          "ref":"myCorpus"
+        },{
+          "@type" : "koral:doc",
+          "key":"Titel",
+          "value":"Baum",
+          "match":"match:eq"
+        }]
+      });
+
+      expect(vc._root.ldType()).toEqual("docGroup");
+
+      expect(vc.toQuery()).toEqual('referTo "myCorpus" & Titel = "Baum"');
+    });
+
+    
     it('should be modifiable by deletion in flat docGroups', function () {
       var vc = flatGroupFactory.create();
       var docGroup = vc.root();
@@ -1680,34 +1894,53 @@
               "@type" : 'koral:doc',
               'key' : 'foo',
               'value' : 'bar'
+            },
+            {
+              "@type" : "koral:docGroupRef",
+              "ref" : "myCorpus"
             }
           ]
         }
       );
 
-      expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar"');
+      expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar" & referTo "myCorpus"');
 
       var fc = vc.element().firstChild;
       expect(fc.getAttribute('data-operation')).toEqual('and');
-      expect(fc.children.length).toEqual(3);
+      expect(fc.children.length).toEqual(4);
       expect(fc.lastChild.getAttribute('class')).toEqual('operators button-group');
       expect(fc.children[0].getAttribute('class')).toEqual('doc');
       expect(fc.children[1].getAttribute('class')).toEqual('doc');
+      expect(fc.children[2].getAttribute('class')).toEqual('doc groupref');
 
       // add with 'and' in the middle
       _andOn(vc.root().getOperand(0));
-      expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar"');
+      expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar" & referTo "myCorpus"');
 
       fc = vc.element().firstChild;
       expect(fc.getAttribute('data-operation')).toEqual('and');
-      expect(fc.children.length).toEqual(4);
+      expect(fc.children.length).toEqual(5);
       expect(fc.lastChild.getAttribute('class')).toEqual('operators button-group');
 
       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');
+      expect(fc.children[3].getAttribute('class')).toEqual('doc groupref');
+      expect(fc.children[4].classList.contains('button-group')).toBeTruthy();
+      expect(fc.children.length).toEqual(5);
+
+      _andOn(vc.root().getOperand(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('doc');
+      expect(fc.children[3].getAttribute('class')).toEqual('doc groupref');
+      expect(fc.children[4].getAttribute('class')).toEqual('doc unspecified');
+      expect(fc.children[5].classList.contains('button-group')).toBeTruthy();
+      expect(fc.children.length).toEqual(6);
+
     });
 
+
     it('should add new unspecified doc with "or"', function () {
       var vc = vcClass.create().fromJson(
         {
@@ -1725,30 +1958,36 @@
               "@type" : 'koral:doc',
               'key' : 'foo',
               'value' : 'bar'
+            },
+            {
+              "@type" : "koral:docGroupRef",
+              "ref" : "myCorpus"
             }
           ]
         }
       );
 
-      expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar"');
+      expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar" & referTo "myCorpus"');
 
       var fc = vc.element().firstChild;
-      expect(fc.children.length).toEqual(3);
+      expect(fc.children.length).toEqual(4);
       expect(fc.lastChild.getAttribute('class')).toEqual('operators button-group');
       expect(fc.children[0].getAttribute('class')).toEqual('doc');
       expect(fc.children[1].getAttribute('class')).toEqual('doc');
+      expect(fc.children[2].getAttribute('class')).toEqual('doc groupref');
 
       // add with 'or' in the middle
       _orOn(vc.root().getOperand(0));
-      expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar"');
+      expect(vc.toQuery()).toEqual('pubDate in 2014-12-05 & foo = "bar" & referTo "myCorpus"');
       fc = vc.element().firstChild;
 
       expect(fc.getAttribute('data-operation')).toEqual('and');
-      expect(fc.children.length).toEqual(3);
+      expect(fc.children.length).toEqual(4);
       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 button-group');
+      expect(fc.children[2].getAttribute('class')).toEqual('doc groupref');
+      expect(fc.children[3].getAttribute('class')).toEqual('operators button-group');
       expect(fc.lastChild.getAttribute('class')).toEqual('operators button-group');
 
       fc = vc.element().firstChild.firstChild;
@@ -1757,6 +1996,22 @@
       expect(fc.children[1].getAttribute('class')).toEqual('doc unspecified');
       expect(fc.children[2].getAttribute('class')).toEqual('operators button-group');
       expect(fc.lastChild.getAttribute('class')).toEqual('operators button-group');
+
+      _orOn(vc.root().getOperand(2));
+      fc = vc.element().firstChild;
+      expect(fc.children.length).toEqual(4);
+
+      expect(fc.children[0].getAttribute('class')).toEqual('docGroup');
+      expect(fc.children[1].getAttribute('class')).toEqual('doc');
+      expect(fc.children[2].getAttribute('class')).toEqual('docGroup');
+      expect(fc.children[3].getAttribute('class')).toEqual('operators button-group');
+
+      fc = vc.element().firstChild.children[2];
+      expect(fc.children[0].getAttribute('class')).toEqual('doc groupref');
+      expect(fc.children[1].getAttribute('class')).toEqual('doc unspecified');
+      expect(fc.children[2].getAttribute('class')).toEqual('operators button-group');
+      expect(fc.lastChild.getAttribute('class')).toEqual('operators button-group');
+
     });
 
     it('should add new unspecified doc with "and" before group', function () {
@@ -2278,6 +2533,16 @@
       sv.element().lastChild.click();
     });
 
+    it('should have a disableoption for regex', function () {
+      var sv = stringValClass.create(undefined, undefined, true);
+      var svE = sv.element();
+      expect(svE.children.length).toEqual(2);
+
+      sv = stringValClass.create(undefined, undefined, false);
+      svE = sv.element();
+      expect(svE.children.length).toEqual(1);
+    });
+
   });
 
   describe('KorAP.VC.Menu', function () {
diff --git a/dev/js/src/vc.js b/dev/js/src/vc.js
index 082ede0..b0ebc44 100644
--- a/dev/js/src/vc.js
+++ b/dev/js/src/vc.js
@@ -44,299 +44,326 @@
    and various field names with the prefix 'VC_'
  */
 
-define([ 'vc/unspecified', 'vc/doc', 'vc/docgroup', 'vc/menu', 'vc/statistic',
-    'datepicker', 'buttongroup', 'panel', 'view/corpstatv', 'util', ],
-    function(unspecDocClass, docClass, docGroupClass, menuClass, statClass,
-        dpClass, buttonGrClass, panelClass, corpStatVClass) {
-      "use strict";
+define([
+  'vc/unspecified',
+  'vc/doc',
+  'vc/docgroup',
+  'vc/docgroupref',
+  'vc/menu',
+  'vc/statistic',
+  'datepicker',
+  'buttongroup',
+  'panel',
+  'view/corpstatv',
+  'util'
+], function(
+  unspecDocClass,
+  docClass,
+  docGroupClass,
+  docGroupRefClass,
+  menuClass,
+  statClass,
+  dpClass,
+  buttonGrClass,
+  panelClass,
+  corpStatVClass) {
+  "use strict";
 
-      KorAP._validUnspecMatchRE = new RegExp(
-          "^(?:eq|ne|contains(?:not)?|excludes)$");
-      KorAP._validStringMatchRE = new RegExp("^(?:eq|ne)$");
-      KorAP._validTextMatchRE = KorAP._validUnspecMatchRE;
-      KorAP._validTextOnlyMatchRE = new RegExp(
-          "^(?:contains(?:not)?|excludes)$");
-      KorAP._overrideStyles = false;
-      // KorAP._validDateMatchRE is defined in datepicker.js!
+  KorAP._validUnspecMatchRE = new RegExp(
+    "^(?:eq|ne|contains(?:not)?|excludes)$");
+  KorAP._validStringMatchRE = new RegExp("^(?:eq|ne)$");
+  KorAP._validTextMatchRE = KorAP._validUnspecMatchRE;
+  KorAP._validTextOnlyMatchRE = new RegExp(
+    "^(?:contains(?:not)?|excludes)$");
+  KorAP._overrideStyles = false;
+  // KorAP._validDateMatchRE is defined in datepicker.js!
 
-      const loc = KorAP.Locale;
-      loc.SHOW_STAT = loc.SHOW_STAT || 'Statistics';
-      loc.VERB_SHOWSTAT = loc.VERB_SHOWSTAT || 'Corpus Statistics';
+  const loc = KorAP.Locale;
+  loc.SHOW_STAT = loc.SHOW_STAT || 'Statistics';
+  loc.VERB_SHOWSTAT = loc.VERB_SHOWSTAT || 'Corpus Statistics';
 
-      KorAP._vcKeyMenu = undefined;
-      KorAP._vcDatePicker = dpClass.create();
+  KorAP._vcKeyMenu = undefined;
+  KorAP._vcDatePicker = dpClass.create();
 
-      // Create match menus ....
-      KorAP._vcMatchopMenu = {
-        'string' : menuClass.create([ [ 'eq', null ], [ 'ne', null ] ]),
-        'text' : menuClass.create([ [ 'eq', null ], // Requires exact match
-        [ 'ne', null ], [ 'contains', null ], // Requires token sequence match
-        [ 'containsnot', null ] ]),
-        'date' : menuClass.create([ [ 'eq', null ], [ 'ne', null ],
-            [ 'geq', null ], [ 'leq', null ] ]),
-        'regex' : menuClass.create([ [ 'eq', null ], [ 'ne', null ] ])
+  // Create match menus ....
+  KorAP._vcMatchopMenu = {
+    'string' : menuClass.create([
+      [ 'eq', null ],
+      [ 'ne', null ]
+    ]),
+    'text' : menuClass.create([
+      [ 'eq', null ], // Requires exact match
+      [ 'ne', null ],
+      [ 'contains', null ], // Requires token sequence match
+      [ 'containsnot', null ]
+    ]),
+    'date' : menuClass.create([
+      [ 'eq', null ],
+      [ 'ne', null ],
+      [ 'geq', null ],
+      [ 'leq', null ]
+    ]),
+    'regex' : menuClass.create([
+      [ 'eq', null ],
+      [ 'ne', null ]
+    ])
+  };
+
+  /**
+   * Virtual Collection
+   */
+  return {
+
+    /**
+     * The JSON-LD type of the virtual collection
+     */
+    ldType : function() {
+      return null;
+    },
+
+    // Initialize virtual collection
+    _init : function(keyList) {
+
+      // Inject localized css styles
+      if (!KorAP._overrideStyles) {
+        var sheet = KorAP.newStyleSheet();
+
+        // Add css rule for OR operations
+        sheet.insertRule('.vc .docGroup[data-operation=or] > .doc::before,'
+                         + '.vc .docGroup[data-operation=or] > .docGroup::before '
+                         + '{ content: "' + loc.OR + '" }', 0);
+
+        // Add css rule for AND operations
+        sheet.insertRule(
+          '.vc .docGroup[data-operation=and] > .doc::before,'
+            + '.vc .docGroup[data-operation=and] > .docGroup::before '
+            + '{ content: "' + loc.AND + '" }', 1);
+
+        KorAP._overrideStyles = true;
       };
 
-      /**
-       * Virtual Collection
-       */
-      return {
+      // Create key menu
+      KorAP._vcKeyMenu = menuClass.create(keyList);
+      KorAP._vcKeyMenu.limit(6);
 
-        /**
-         * The JSON-LD type of the virtual collection
-         */
-        ldType : function() {
-          return null;
-        },
+      return this;
+    },
 
-        // Initialize virtual collection
-        _init : function(keyList) {
+    /**
+     * Create a new virtual collection.
+     */
+    create : function(keyList) {
+      var obj = Object.create(this)._init(keyList);
+      obj._root = unspecDocClass.create(obj);
+      return obj;
+    },
 
-          // Inject localized css styles
-          if (!KorAP._overrideStyles) {
-            var sheet = KorAP.newStyleSheet();
-
-            // Add css rule for OR operations
-            sheet.insertRule('.vc .docGroup[data-operation=or] > .doc::before,'
-                + '.vc .docGroup[data-operation=or] > .docGroup::before '
-                + '{ content: "' + loc.OR + '" }', 0);
-
-            // Add css rule for AND operations
-            sheet.insertRule(
-                '.vc .docGroup[data-operation=and] > .doc::before,'
-                    + '.vc .docGroup[data-operation=and] > .docGroup::before '
-                    + '{ content: "' + loc.AND + '" }', 1);
-
-            KorAP._overrideStyles = true;
-          }
-          ;
-
-          // Create key menu
-          KorAP._vcKeyMenu = menuClass.create(keyList);
-          KorAP._vcKeyMenu.limit(6);
-
-          return this;
-        },
-
-        /**
-         * Create a new virtual collection.
-         */
-        create : function(keyList) {
-          var obj = Object.create(this)._init(keyList);
-          obj._root = unspecDocClass.create(obj);
-          return obj;
-        },
-
-        /**
-         * Create and render a new virtual collection based on a KoralQuery
-         * collection document
-         */
-        fromJson : function(json) {
-          if (json !== undefined) {
-            // Parse root document
-            if (json['@type'] == 'koral:doc') {
-              this._root = docClass.create(this, json);
-            }
-            // parse root group
-            else if (json['@type'] == 'koral:docGroup') {
-              this._root = docGroupClass.create(this, json);
-            }
-            // Unknown collection type
-            else {
-              KorAP.log(813, "Collection type is not supported");
-              return;
-            }
-            ;
-          }
-
-          else {
-            // Add unspecified object
-            this._root = unspecDocClass.create(this);
-          }
-          ;
-
-          // Init element and update
-          this.update();
-
-          return this;
-        },
-
-        // Check if the virtual corpus contains a rewrite
-        // This is a class method
-        checkRewrite : function(json) {
-
-          // There is a rewrite attribute
-          if (json['rewrites'] !== undefined) {
-            return true;
-          }
-
-          // There is a group to check for rewrites
-          else if (json['@type'] === 'koral:docGroup') {
-            var ops = json['operands'];
-            if (ops === undefined)
-              return false;
-
-            for ( var i in ops) {
-
-              // "this" is the class
-              if (this.checkRewrite(ops[i])) {
-                return true;
-              }
-              ;
-            }
-            ;
-          }
-          ;
-          return false;
-        },
-
-        /**
-         * Clean the virtual document to uspecified doc.
-         */
-        clean : function() {
-          if (this._root.ldType() !== "non") {
-            this._root.destroy();
-            this.root(unspecDocClass.create(this));
-          }
-          ;
-          return this;
-        },
-
-        /**
-         * Get or set the root object of the virtual collection.
-         */
-        root : function(obj) {
-          if (arguments.length === 1) {
-            var e = this.element();
-
-            if (e.firstChild !== null) {
-              if (e.firstChild !== obj.element()) {
-                e.replaceChild(obj.element(), e.firstChild);
-              }
-              ;
-            }
-
-            // Append root element
-            else {
-              e.appendChild(obj.element());
-            }
-            ;
-
-            // Update parent child relations
-            this._root = obj;
-            obj.parent(this);
-
-            this.update();
-          }
-          ;
-          return this._root;
-        },
-
-        /**
-         * Get the element associated with the virtual collection
-         */
-        element : function() {
-
-          
-          if (this._element !== undefined) {
-            return this._element;
-          }
-          ;
-
-          this._element = document.createElement('div');
-          this._element.setAttribute('class', 'vc');
-
-          // Initialize root
-          this._element.appendChild(this._root.element());
-          
-          /*
-           * TODO by Helge Hack! additional div, because statistic button is
-           * removed after choosing and/or/x in vc builder. REMOVE this lines
-           * after solving the problem!!!!
-           */
-          this._element.addE('div');
-          this._element.addE('div');
-          this._element.addE('div');
-          
-          // Add panel to display corpus statistic, ...
-          this.addVcInfPanel();
-      
-          return this._element;
-        },
-
-        /**
-         * Update the whole object based on the underlying data structure
-         */
-        update : function() {
-          this._root.update();
-          return this;
-        },
-
-        /**
-         * Make the vc persistant by injecting the current timestamp as a
-         * creation date limit criterion.
-         */
-        makePersistant : function() {
-          // this.root().wrapOnRoot('and');
-          var todayStr = KorAP._vcDatePicker.today();
-          var doc = docClass.create();
-          var root = this.root();
-
-          if (root.ldType() === 'docGroup' && root.operation === 'and') {
-            root.append(cond);
-          } else {
-            root.wrapOnRoot('and');
-            root.append(doc);
-          }
-          ;
-
-          doc.key("creationDate");
-          doc.type("date");
-          doc.matchop("leq");
-          doc.value(todayStr);
-
-          /*
-           * { "@type" : "koral:doc", "key" : "creationDate", "type" :
-           * "type:date", "match" : "match:leq", "value" : todayStr }
-           * this.root().append(cond);
-           */
-          this.update();
-        },
-
-        /**
-         * Get the generated json string
-         */
-        toJson : function() {
-          return this._root.toJson();
-        },
-
-        /**
-         * Get the generated query string
-         */
-        toQuery : function() {
-          return this._root.toQuery();
-        },
-
-
-       /*
-        * Add panel to display virtual corpus information
-        */
-        addVcInfPanel : function() {
-
-          var dv = this._element.addE('div');
-          var panel = panelClass.create([ 'vcinfo' ]);
-          dv.appendChild(panel.element());
- 
-          var that = this;
-          var actions = panel.actions;
-          var statView;
-                    
-          actions.add(loc.SHOW_STAT, [ 'statistic' ], function() {
-            if (statView === undefined || !statView.shown()) {
-              statView = corpStatVClass.create(that);
-              panel.add(statView);
-            }
-          });
+    /**
+     * Create and render a new virtual collection based on a KoralQuery
+     * collection document
+     */
+    fromJson : function(json) {
+      if (json !== undefined) {
+        // Parse root document
+        if (json['@type'] == 'koral:doc') {
+          this._root = docClass.create(this, json);
         }
+        // parse root group
+        else if (json['@type'] == 'koral:docGroup') {
+          this._root = docGroupClass.create(this, json);
+        }
+
+        // parse root reference
+        else if (json['@type'] == 'koral:docGroupRef') {
+          this._root = docGroupRefClass.create(this, json);
+        }
+        
+        // Unknown collection type
+        else {
+          KorAP.log(813, "Collection type is not supported");
+          return;
+        };
+      }
+
+      else {
+        // Add unspecified object
+        this._root = unspecDocClass.create(this);
       };
-    });
+
+      // Init element and update
+      this.update();
+
+      return this;
+    },
+
+    // Check if the virtual corpus contains a rewrite
+    // This is a class method
+    checkRewrite : function(json) {
+
+      // There is a rewrite attribute
+      if (json['rewrites'] !== undefined) {
+        return true;
+      }
+
+      // There is a group to check for rewrites
+      else if (json['@type'] === 'koral:docGroup') {
+        var ops = json['operands'];
+        if (ops === undefined)
+          return false;
+
+        for ( var i in ops) {
+
+          // "this" is the class
+          if (this.checkRewrite(ops[i])) {
+            return true;
+          };
+        };
+      };
+      return false;
+    },
+
+    /**
+     * Clean the virtual document to uspecified doc.
+     */
+    clean : function() {
+      if (this._root.ldType() !== "non") {
+        this._root.destroy();
+        this.root(unspecDocClass.create(this));
+      };
+      return this;
+    },
+
+    /**
+     * Get or set the root object of the virtual collection.
+     */
+    root : function(obj) {
+      if (arguments.length === 1) {
+        var e = this.element();
+
+        if (e.firstChild !== null) {
+          if (e.firstChild !== obj.element()) {
+            e.replaceChild(obj.element(), e.firstChild);
+          };
+        }
+
+        // Append root element
+        else {
+          e.appendChild(obj.element());
+        };
+
+        // Update parent child relations
+        this._root = obj;
+        obj.parent(this);
+
+        this.update();
+      };
+      return this._root;
+    },
+
+    /**
+     * Get the element associated with the virtual collection
+     */
+    element : function() {
+
+      
+      if (this._element !== undefined) {
+        return this._element;
+      };
+
+      this._element = document.createElement('div');
+      this._element.setAttribute('class', 'vc');
+
+      // Initialize root
+      this._element.appendChild(this._root.element());
+      
+      /*
+       * TODO by Helge Hack! additional div, because statistic button is
+       * removed after choosing and/or/x in vc builder. REMOVE this lines
+       * after solving the problem!!!!
+       */
+      this._element.addE('div');
+      this._element.addE('div');
+      this._element.addE('div');
+      
+      // Add panel to display corpus statistic, ...
+      this.addVcInfPanel();
+      
+      return this._element;
+    },
+
+    /**
+     * Update the whole object based on the underlying data structure
+     */
+    update : function() {
+      this._root.update();
+      return this;
+    },
+
+    /**
+     * Make the vc persistant by injecting the current timestamp as a
+     * creation date limit criterion.
+     * THIS IS CURRENTLY NOT USED
+     */
+    makePersistant : function() {
+      // this.root().wrapOnRoot('and');
+      var todayStr = KorAP._vcDatePicker.today();
+      var doc = docClass.create();
+      var root = this.root();
+
+      if (root.ldType() === 'docGroup' && root.operation === 'and') {
+        root.append(cond);
+      } else {
+        root.wrapOnRoot('and');
+        root.append(doc);
+      };
+
+      doc.key("creationDate");
+      doc.type("date");
+      doc.matchop("leq");
+      doc.value(todayStr);
+
+      /*
+       * { "@type" : "koral:doc", "key" : "creationDate", "type" :
+       * "type:date", "match" : "match:leq", "value" : todayStr }
+       * this.root().append(cond);
+       */
+      this.update();
+    },
+
+    /**
+     * Get the generated json string
+     */
+    toJson : function() {
+      return this._root.toJson();
+    },
+
+    /**
+     * Get the generated query string
+     */
+    toQuery : function() {
+      return this._root.toQuery();
+    },
+
+
+    /*
+     * Add panel to display virtual corpus information
+     */
+    addVcInfPanel : function() {
+
+      var dv = this._element.addE('div');
+      var panel = panelClass.create([ 'vcinfo' ]);
+      dv.appendChild(panel.element());
+      
+      var that = this;
+      var actions = panel.actions;
+      var statView;
+      
+      actions.add(loc.SHOW_STAT, [ 'statistic' ], function() {
+        if (statView === undefined || !statView.shown()) {
+          statView = corpStatVClass.create(that);
+          panel.add(statView);
+        }
+      });
+    }
+  };
+});
diff --git a/dev/js/src/vc/doc.js b/dev/js/src/vc/doc.js
index 15c7287..7c4f98a 100644
--- a/dev/js/src/vc/doc.js
+++ b/dev/js/src/vc/doc.js
@@ -62,77 +62,79 @@
       // Get element
       var e = this._element;
 
+      // Check if there is a change in the underlying data
+      if (!this.__changed)
+        return e;
+
       // Set ref - TODO: Cleanup!
       e.refTo = this;
 
-      // Check if there is a change in the underlying data
-      if (this.__changed) {
 
-        // Was rewritten
-        if (this.rewrites() !== undefined) {
-          e.classList.add("rewritten");
-        };
-
-        // Added key
-        this._keyE = document.createElement('span');
-        this._keyE.setAttribute('class', 'key');
-
-        // Change key
-        this._keyE.addEventListener('click', this._changeKey.bind(this));
-
-        if (this.key()) {
-          var k = this.key();
-          if (loc['VC_' + k] !== undefined)
-            k = loc['VC_' + k];
-          this._keyE.addT(k);
-        };
-
-        // Added match operator
-        this._matchopE = document.createElement('span');
-        this._matchopE.setAttribute('data-type', this.type());
-        this._matchopE.setAttribute('class', 'match');
-        this._matchopE.addT(this.matchop());
-
-        // 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.addT(this.value());
-        }
-        else {
-          this._valueE.addT(loc.EMPTY);
-        };
-
-        // Change value
-        this._valueE.addEventListener(
-          'click',
-          this._changeValue.bind(this)
-        );
-
-
-        // Remove all element children
-        _removeChildren(e);
-
-        // Add spans
-        e.appendChild(this._keyE);
-        e.appendChild(this._matchopE);
-        e.appendChild(this._valueE);
-
-        this.__changed = false;
+      // Was rewritten
+      if (this.rewrites() !== undefined) {
+        e.classList.add("rewritten");
       };
 
+      // Added key
+      this._keyE = document.createElement('span');
+      this._keyE.setAttribute('class', 'key');
+
+      // Change key
+      this._keyE.addEventListener('click', this._changeKey.bind(this));
+
+      if (this.key()) {
+        var k = this.key();
+        if (loc['VC_' + k] !== undefined)
+          k = loc['VC_' + k];
+        this._keyE.addT(k);
+      };
+
+      // Added match operator
+      this._matchopE = document.createElement('span');
+      this._matchopE.setAttribute('data-type', this.type());
+      this._matchopE.setAttribute('class', 'match');
+      this._matchopE.addT(this.matchop());
+
+      // 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.addT(this.value());
+      }
+      else {
+        this._valueE.addT(loc.EMPTY);
+      };
+
+      // Change value
+      this._valueE.addEventListener(
+        'click',
+        this._changeValue.bind(this)
+      );
+
+      // Remove all element children
+      _removeChildren(e);
+
+      // Add spans
+      e.appendChild(this._keyE);
+      e.appendChild(this._matchopE);
+      e.appendChild(this._valueE);
+
+      this.__changed = false;
+
       if (this._rewrites !== undefined) {
         e.appendChild(this._rewrites.element());
       };
 
       if (this._parent !== undefined) {
+        
         // Set operators
         var op = this.operators(
           true,
diff --git a/dev/js/src/vc/docgroup.js b/dev/js/src/vc/docgroup.js
index 58926f2..d3097e9 100644
--- a/dev/js/src/vc/docgroup.js
+++ b/dev/js/src/vc/docgroup.js
@@ -8,10 +8,12 @@
   'vc/jsonld',
   'vc/unspecified',
   'vc/doc',
+  'vc/docgroupref',
   'util'
 ], function (jsonldClass,
-	     unspecClass,
-	     docClass) {
+	           unspecClass,
+	           docClass,
+            docGroupRefClass) {
 
   const _validGroupOpRE = new RegExp("^(?:and|or)$");
 
@@ -41,6 +43,10 @@
 
     // The doc is already set in the group
     _duplicate : function (operand) {
+
+      // TODO:
+      //   Also check for duplicate docGroupRefs!
+      
       if (operand.ldType() !== 'doc')
 	      return null;
 
@@ -73,7 +79,8 @@
 	      // No @type defined
 	      if (operand["ldType"] !== undefined) {
 	        if (operand.ldType() !== 'doc' &&
-	            operand.ldType() !== 'docGroup') {
+	            operand.ldType() !== 'docGroup' &&
+              operand.ldType() !== 'docGroupRef') {
 	          KorAP.log(812, "Operand not supported in document group");
 	          return;
 	        };
@@ -127,6 +134,28 @@
 	      this._operands.push(docGroup);
 	      return docGroup;
 
+      case "koral:docGroupRef":
+      
+        var docGroupRef = docGroupRefClass.create(this, operand);
+      
+        if (docGroupRef === undefined) {
+          return
+        };
+
+        // TODO:
+        //   Currently this doesn't do anything meaningful,
+        //   as duplicate only checks on docs for the moment
+        /*
+	      var dupl = this._duplicate(doc);
+	      if (dupl === null) {
+	        this._operands.push(doc);
+	        return doc;
+	      };
+	      return dupl;
+        */
+	      this._operands.push(docGroupRef);
+        return docGroupRef;
+
       default:
 	      KorAP.log(812, "Operand not supported in document group");
 	      return;
@@ -229,6 +258,7 @@
 	        // Just insert a doc or ...
 	        if (newOp.ldType() === "doc" ||
 	            newOp.ldType() === "non" ||
+              newOp.ldType() === 'docGroupRef' ||
 	            // ... insert a group of a different operation
 	            // (i.e. "and" in "or"/"or" in "and")
 	            newOp.operation() != this.operation()) {
diff --git a/dev/js/src/vc/docgroupref.js b/dev/js/src/vc/docgroupref.js
new file mode 100644
index 0000000..43bb0ca
--- /dev/null
+++ b/dev/js/src/vc/docgroupref.js
@@ -0,0 +1,269 @@
+/**
+ * A reference to another VC.
+ * Inherits everything from jsonld
+ */
+define([
+  'vc/jsonld',
+  'vc/rewritelist',
+  'vc/stringval',
+  'util'
+], function (jsonldClass, rewriteListClass, stringValClass) {
+
+  const loc = KorAP.Locale;
+  loc.EMPTY = loc.EMPTY || '⋯';
+
+  return {
+
+    // The ld-type
+    _ldType : "docGroupRef",
+
+    /**
+     * Create new unspecified criterion
+     * with a link to the parent object
+     */
+    create : function (parent, json) {
+      var obj = Object(jsonldClass)
+          .create().
+	        upgradeTo(this)
+          .fromJson(json);
+
+      if (obj === undefined) {
+        console.log(json);
+      };
+
+      if (parent !== undefined)
+	      obj._parent = parent;
+
+      obj.__changed = true;
+      return obj;
+    },
+
+
+    /**
+     * Update the element
+     */
+    update : function () {
+      if (this._element === undefined)
+        return this.element();
+
+      var e = this._element;
+
+      // Check if there is a change in the underlying data
+      if (!this.__changed)
+        return e;
+
+      // Set ref - TODO: Cleanup!
+      e.refTo = this;
+
+      // Was rewritten
+      if (this.rewrites() !== undefined) {
+        e.classList.add("rewritten");
+      };
+
+      var refTitle = document.createElement('span');
+      refTitle.classList.add('key','fixed');
+      refTitle.addT('@referTo');
+
+      // Added value operator
+      this._refE = document.createElement('span');
+      this._refE.setAttribute('data-type', "string");
+      this._refE.setAttribute('class', 'value');
+      if (this.ref()) {
+        this._refE.addT(this.ref());
+      }
+      else {
+        this._refE.addT(loc.EMPTY);
+      };
+
+      // Change value
+      this._refE.addEventListener(
+        'click',
+        this._changeRef.bind(this)
+      );
+
+      // Remove all element children
+      _removeChildren(e);
+
+      // Add spans
+      e.appendChild(refTitle);
+      e.appendChild(this._refE);
+
+      this.__changed = false;
+
+      if (this._rewrites !== undefined) {
+        e.appendChild(this._rewrites.element());
+      };
+
+      if (this._parent !== undefined) {
+        // Set operators
+        var op = this.operators(
+          true,
+          true,
+          true
+        );
+
+        // Append new operators
+        e.appendChild(op.element());
+      };  
+
+      return this.element();
+    },
+
+
+    /**
+     * Get the associated element
+     */
+    element : function () {
+      if (this._element !== undefined)
+	      return this._element;
+      this._element = document.createElement('div');
+      this._element.setAttribute('class', 'doc groupref');
+      this.update();
+      return this._element;
+    },
+
+
+    /**
+     * Get or set the value
+     */
+    ref : function (ref) {
+      if (arguments.length === 1) {
+        this._ref = ref;
+        this._changed();
+        return this;
+      };
+      return this._ref;
+    },
+
+
+    // Click on the reference operator, show me the option
+    _changeRef : function (e) {
+      var that = this;
+
+      var str = stringValClass.create(this.ref(), false, false);
+      var strElem = str.element();
+
+      str.store = function (ref, regex) {
+        that.ref(ref);
+          
+        that._element.removeChild(
+          this._element
+        );
+        that.update();
+      };
+
+      // Insert element
+      this._element.insertBefore(
+        strElem,
+        this._refE
+      );
+
+      str.focus();
+    },
+    
+
+    /**
+     * Wrap a new operation around the doc element.
+     * This is copypasta from doc.js
+     */
+    wrap : function (op) {
+      var parent = this.parent();
+      var group = require('vc/docgroup').create(parent);
+      group.operation(op);
+      group.append(this);
+      group.append();
+      return parent.replaceOperand(this, group).update();
+    },
+
+    /**
+     * Deserialize from json
+     */
+    fromJson : function (json) {
+      if (json === undefined)
+        return this;
+
+      if (json["@type"] === undefined) {
+        KorAP.log(701, "JSON-LD group has no @type attribute");
+        return;
+      };
+
+      if (json["ref"] === undefined ||
+          typeof json["ref"] != 'string') {
+        KorAP.log(821, "Reference is missing");
+        return;
+      };
+
+      this.ref(json["ref"]);
+
+      // Rewrite coming from the server
+      if (json["rewrites"] !== undefined) {
+        this.rewrite(json["rewrites"]);
+      };
+
+      return this;
+    },
+
+
+    /**
+     * Click on the unspecified object
+     */
+    onclick : function () {
+      console.log("Do not support click on this");
+    },
+
+    // TODO: This is identical to doc.js
+    rewrites : function () {
+      return this._rewrites;
+    },
+
+    // TODO: This is identical to doc.js
+    rewrite : function (value) {
+      if (typeof value === 'string') {
+        value = [{
+          "@type" : "koral:rewrite",
+          "operation" : "operation:" + value,
+          "src" : "Kalamar"
+        }];
+      };
+      this._rewrites = rewriteListClass.create(value);
+    },
+
+
+    // Mark the underlying data as being changed.
+    // This is important for rerendering the dom.
+    // This will also remove rewrite markers, when the data
+    // change happened by the user
+    _changed : function () {
+      this.__changed = true;
+
+      if (this._rewrites === undefined)
+        return;
+
+        delete this["_rewrites"];
+
+      if (this._element === undefined)
+        return;
+
+      this._element.classList.remove("rewritten");
+    },
+
+    toJson : function () {
+      if (!this.ref)
+        return {};
+      
+      return {
+        "@type" : "koral:" + this.ldType(),
+        "ref"  : this.ref()
+      };
+    },
+
+    
+    toQuery : function () {
+      if (!this.ref())
+        return "";
+
+      // Build doc string based on key
+      return 'referTo "' + this.ref().quote() + '"';
+    }
+  };
+});
diff --git a/dev/js/src/vc/jsonld.js b/dev/js/src/vc/jsonld.js
index 846d55c..545652b 100644
--- a/dev/js/src/vc/jsonld.js
+++ b/dev/js/src/vc/jsonld.js
@@ -38,12 +38,15 @@
     // acyclic structures!
     // I'm paranoid!
     destroy : function () {
+      
       if (this._ops != undefined) {
 	      this._ops._parent = undefined;
-	      if (this._ops._element !== undefined)
+	      if (this._ops._element !== undefined) {
 	        this._ops._element.refTo = undefined;
+        };
 	      this._ops = undefined;
       };
+
       if (this._element !== undefined)
 	      this._element = undefined;
       
diff --git a/dev/js/src/vc/operators.js b/dev/js/src/vc/operators.js
index 1d4dc94..50605a3 100644
--- a/dev/js/src/vc/operators.js
+++ b/dev/js/src/vc/operators.js
@@ -31,7 +31,7 @@
 	      return obj.wrapOnRoot();
       };
     }
-    else if (obj.ldType() === 'doc') {
+    else if (obj.ldType() === 'doc' || obj.ldType() === 'docGroupRef') {
 
       if (parent.ldType() === null) {
 	      return obj.wrapOnRoot(type);
@@ -71,7 +71,7 @@
 
   return {
     create : function (and, or, del) {
-
+      
       // Inherit from buttonGroupClass
       var op = Object(buttonGroupClass).create(['operators']).upgradeTo(this);
       op.and(and);
diff --git a/dev/js/src/vc/stringval.js b/dev/js/src/vc/stringval.js
index 10daaee..77c6d90 100644
--- a/dev/js/src/vc/stringval.js
+++ b/dev/js/src/vc/stringval.js
@@ -7,24 +7,38 @@
    * Create new string value helper.
    */
   create : function () {
+    var regexOp = true;
     var regex = false;
     var value = '';
-    if (arguments.length == 2) {
-      regex = arguments[1];
-    };
+
+    // Set value
     if (arguments.length >= 1) {
       if (arguments[0] !== undefined)
         value = arguments[0];
     };
-    return Object.create(this)._init(value, regex);
+
+    // Set regex
+    if (arguments.length >= 2) {
+      if (arguments[1] !== undefined)
+        regex = arguments[1];
+    };
+
+    // Set regexOp
+    if (arguments.length >= 3) {
+      regexOp = arguments[2];
+      if (regexOp === false) {
+        regex = false;
+      }
+    };
+    return Object.create(this)._init(value, regex, regexOp);
   },
   
 
   // Initialize the string value
-  _init : function (value, regex) {
-    this.element();
+  _init : function (value, regex, regexOp) {
     this.value(value);
     this.regex(regex);
+    this._regexOp(regexOp);
     return this;
   },
 
@@ -47,6 +61,18 @@
     return this._regex;
   },
 
+  _regexOp : function (regexOp) {
+    if (arguments.length === 1) {
+      if (regexOp) {
+        this.__regexOp = true;
+      }
+      else {
+        this.__regexOp = false;
+      };
+      this._update();
+    };
+    return this.__regexOp;
+  },
 
   /**
    * Toggle the regex, make it either true,
@@ -65,18 +91,20 @@
   value : function (val) {
     if (arguments.length === 1) {
       this._value = val;
-      this._input.value = val;
+      // this._input.value = val;
       this._update();
     };
     return this._value;
   },
 
-
   // Update dom element
   _update : function () {
+    if (this._element === undefined)
+      return;
+ 
     this._value = this._input.value;
 
-    if (this._regex) {
+    if (this._regexOp() && this._regex) {
       this._element.classList.add('regex');
     }
     else {
@@ -92,7 +120,6 @@
    */
   store : function (v,r) {},
 
-
   /**
    * Put focus on element
    */
@@ -126,16 +153,18 @@
     };
 
     // Add regex button
-    var re = e.addE('div');
-    re.addEventListener(
-      'click',
-      function (ev) {
-	      this.toggleRegex();
-        // ev.halt();
-      }.bind(this),
-      true
-    );
-    re.addT('RE');
+    if (this._regexOp()) {
+      var re = e.addE('div');
+      re.addEventListener(
+        'click',
+        function (ev) {
+	        this.toggleRegex();
+          // ev.halt();
+        }.bind(this),
+        true
+      );
+      re.addT('RE');
+    };
 
     // If the focus is not on the text field anymore,
     // delegate focus to
diff --git a/dev/js/src/vc/unspecified.js b/dev/js/src/vc/unspecified.js
index 56d0869..6ffa0e3 100644
--- a/dev/js/src/vc/unspecified.js
+++ b/dev/js/src/vc/unspecified.js
@@ -5,8 +5,9 @@
 define([
   'vc/jsonld',
   'vc/doc',
+  'vc/docgroupref',
   'util'
-], function (jsonldClass, docClass) {
+], function (jsonldClass, docClass, docGroupRefClass) {
 
   // Localize empty string
   var loc = KorAP.Locale;
@@ -40,9 +41,18 @@
       if (this._parent === undefined)
 	      return null;
 
+      var newDoc;
+      var keyType = KorAP._vcKeyMenu.typeOf(v);
+
       // Set JSON-LD type
-      var newDoc = docClass.create(this._parent);
-      newDoc.key(v);
+      if (keyType && keyType === 'ref') {
+        newDoc = docGroupRefClass.create(this._parent);
+      }
+      else {
+        newDoc = docClass.create(this._parent);
+        newDoc.key(v);
+        newDoc.type(keyType);
+      };
   
       // Unspecified document on root
       if (this._parent.ldType() === null) {
@@ -82,7 +92,8 @@
       this._element.refTo = this;
 
       // Set operators
-      if (this._parent !== undefined && this.parent().ldType() !== null) {
+      if (this._parent !== undefined &&
+          this.parent().ldType() !== null) {
 	      var op = this.operators(
 	        false,
 	        false,
@@ -127,9 +138,9 @@
       var that = this;
 
       // Set released method
-      menu.released(function (key, type) {
+      menu.released(function (key) {
 	      // Set chosen key and type - will return a doc
-	      that.key(key).type(type).update();
+	      that.key(key).update();
 	      this.hide();
       });
 
diff --git a/dev/scss/header/vc.scss b/dev/scss/header/vc.scss
index b7b97cd..3b3fd50 100644
--- a/dev/scss/header/vc.scss
+++ b/dev/scss/header/vc.scss
@@ -148,6 +148,8 @@
     > span.key,
     > span.value {
       font-weight: bold;
+    }
+    > span.value {
       &[data-type=regex] {
         font-style: italic;
         &::after,
@@ -157,6 +159,10 @@
       }
     }
 
+    > span.key.fixed {
+      color: $light-green;
+    }
+
     /**
      * All operators on docs
      */
@@ -193,7 +199,7 @@
     }
   }
 
-  .doc > span {
+  .doc > span:not(.fixed) {
     cursor: pointer;
     &:hover {
       // color: $dark-green;