Support VC references in VC builder to fix #62
Change-Id: Iec84c12ade2f64e8bbbd3d42b9e52788a0fba3fe
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();
});