Use requirejs for clientside scripting
diff --git a/dev/js/src/vc/doc.js b/dev/js/src/vc/doc.js
new file mode 100644
index 0000000..61c6439
--- /dev/null
+++ b/dev/js/src/vc/doc.js
@@ -0,0 +1,363 @@
+/**
+ * Document criterion
+ */
+
+/**
+ * Criterion in a KorAP.Doc
+ */
+function _changeKey () {
+ var doc = this.parentNode.refTo;
+ var key = doc.element().firstChild;
+ key.appendChild(fieldMenu.element());
+ fieldMenu.show();
+ fieldMenu.focus();
+ // key, matchop, type, value
+};
+
+define([
+ 'vc/jsonld', 'vc/menu', 'vc/rewritelist'], function (jsonldClass, menuClass, rewriteListClass) {
+ var fieldMenu = menuClass.create([
+ ['Titel', 'title', 'string'],
+ ['Untertitel', 'subTitle', 'string'],
+ ['Veröffentlichungsdatum', 'pubDate', 'date'],
+ ['Autor', 'author', 'string']
+ ]);
+
+ fieldMenu.limit(5);
+
+ _validRegexMatchRE = new RegExp("^(?:eq|ne)$");
+
+ return {
+ _ldType : "doc",
+ _obj : function () { return '???'; /*KorAP.Doc*/ },
+
+ create : function (parent, json) {
+ var obj = Object(jsonldClass).
+ create().
+ upgradeTo(this).
+ fromJson(json);
+
+ if (parent !== undefined)
+ obj._parent = parent;
+
+ obj.__changed = true;
+ return obj;
+ },
+
+ update : function () {
+ if (this._element === undefined)
+ return this.element();
+
+ // Get element
+ var e = this._element;
+
+ // Set ref - TODO: Cleanup!
+ e.refTo = this;
+
+ // Check if there is a change
+ if (this.__changed) {
+
+ // Was rewritten
+ if (this.rewrites() !== undefined) {
+ e.classList.add("rewritten");
+ };
+
+ // Added key
+ var key = document.createElement('span');
+ key.setAttribute('class', 'key');
+
+ // Change key
+ key.addEventListener('click', _changeKey, false);
+
+ if (this.key())
+ key.appendChild(document.createTextNode(this.key()));
+
+ // Added match operator
+ var matchop = document.createElement('span');
+ matchop.setAttribute('data-type', this.type());
+ matchop.setAttribute('class', 'match');
+ matchop.appendChild(
+ document.createTextNode(this.matchop())
+ );
+
+ // Added match operator
+ var value = document.createElement('span');
+ value.setAttribute('data-type', this.type());
+ value.setAttribute('class', 'value');
+ if (this.value())
+ value.appendChild(
+ document.createTextNode(this.value())
+ );
+
+ // Remove all element children
+ _removeChildren(e);
+
+ // Add spans
+ e.appendChild(key);
+ e.appendChild(matchop);
+ e.appendChild(value);
+
+ 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 e;
+ },
+
+ element : function () {
+ if (this._element !== undefined)
+ return this._element;
+
+ this._element = document.createElement('div');
+ this._element.setAttribute('class', 'doc');
+
+ this.update();
+ return this._element;
+ },
+
+ // Wrap a new operation around the doc element
+ wrap : function (op) {
+ var parent = this.parent();
+ var group = KorAP.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["value"] === undefined ||
+ typeof json["value"] != 'string') {
+ KorAP.log(805, "Value is invalid");
+ return;
+ };
+
+ // There is a defined key
+ if (json["key"] !== undefined &&
+ typeof json["key"] === 'string') {
+
+ // Set key
+ this.key(json["key"]);
+
+ // Set match operation
+ if (json["match"] !== undefined) {
+ if (typeof json["match"] === 'string') {
+ this.matchop(json["match"]);
+ }
+ else {
+ KorAP.log(802, "Match type is not supported by value type");
+ return;
+ };
+ };
+
+ // Key is a string
+ if (json["type"] === undefined ||
+ json["type"] == "type:string") {
+ this.type("string");
+
+ // Check match type
+ if (!KorAP._validStringMatchRE.test(this.matchop())) {
+ KorAP.log(802, "Match type is not supported by value type");
+ return;
+ };
+
+ // Set string value
+ this.value(json["value"]);
+ }
+
+ // Key is a date
+ else if (json["type"] === "type:date") {
+ this.type("date");
+
+ if (json["value"] !== undefined &&
+ KorAP._validDateRE.test(json["value"])) {
+
+ if (!KorAP._validDateMatchRE.test(this.matchop())) {
+ KorAP.log(802, "Match type is not supported by value type");
+ return;
+ };
+
+ // Set value
+ this.value(json["value"]);
+ }
+ else {
+ KorAP.log(806, "Value is not a valid date string");
+ return;
+ };
+ }
+
+ // Key is a regular expression
+ else if (json["type"] === "type:regex") {
+ this.type("regex");
+
+ try {
+
+ // Try to create a regular expression
+ var check = new RegExp(json["value"]);
+
+ if (!_validRegexMatchRE.test(this.matchop())) {
+ KorAP.log(802, "Match type is not supported by value type");
+ return;
+ };
+
+ this.value(json["value"]);
+ }
+
+ catch (e) {
+ KorAP.log(807, "Value is not a valid regular expression");
+ return;
+ };
+ this.type("regex");
+ }
+
+ else {
+ KorAP.log(804, "Unknown value type");
+ return;
+ };
+ };
+
+ if (json["rewrites"] !== undefined) {
+ this._rewrites = rewriteListClass.create(json["rewrites"]);
+ };
+
+ return this;
+ },
+
+ key : function (value) {
+ if (arguments.length === 1) {
+ this._key = value;
+ this._changed();
+ return this;
+ };
+ return this._key;
+ },
+
+ matchop : function (match) {
+ if (arguments.length === 1) {
+ this._matchop = match.replace(/^match:/, '');
+ this._changed();
+ return this;
+ };
+ return this._matchop || "eq";
+ },
+
+ type : function (type) {
+ if (arguments.length === 1) {
+ this._type = type;
+ this._changed();
+ return this;
+ };
+ return this._type || "string";
+ },
+
+ value : function (value) {
+ if (arguments.length === 1) {
+ this._value = value;
+ this._changed();
+ return this;
+ };
+ return this._value;
+ },
+
+ rewrites : function () {
+ return this._rewrites;
+ },
+
+ _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.matchop() || !this.key())
+ return {};
+
+ return {
+ "@type" : "koral:" + this.ldType(),
+ "key" : this.key(),
+ "match" : "match:" + this.matchop(),
+ "value" : this.value() || '',
+ "type" : "type:" + this.type()
+ };
+ },
+
+ toQuery : function () {
+ if (!this.matchop() || !this.key())
+ return "";
+
+ // Build doc string based on key
+ var string = this.key() + ' ';
+
+ // Add match operator
+ switch (this.matchop()) {
+ case "ne":
+ string += '!=';
+ break;
+ case "contains":
+ string += '~';
+ break;
+ case "excludes":
+ string += '!~';
+ break;
+ case "geq":
+ string += 'since';
+ break;
+ case "leq":
+ string += 'until';
+ break;
+ default:
+ string += (this.type() == 'date') ? 'in' : '=';
+ break;
+ };
+
+ string += ' ';
+
+ // Add value
+ switch (this.type()) {
+ case "date":
+ return string + this.value();
+ break;
+ case "regex":
+ return string + '/' + this.value() + '/';
+ break;
+ case "string":
+ return string + '"' + this.value().replace(KorAP._quote, '\\$1') + '"';
+ break;
+ };
+
+ return "";
+ }
+ };
+});
diff --git a/dev/js/src/vc/docgroup.js b/dev/js/src/vc/docgroup.js
new file mode 100644
index 0000000..5e5f010
--- /dev/null
+++ b/dev/js/src/vc/docgroup.js
@@ -0,0 +1,343 @@
+/**
+ * Document group criterion
+ */
+define([
+ 'vc/jsonld',
+ 'vc/unspecified',
+ 'vc/doc',
+ 'util'
+], function (jsonldClass,
+ unspecClass,
+ docClass) {
+
+ var _validGroupOpRE = new RegExp("^(?:and|or)$");
+
+ var docGroupClass = {
+ _ldType : "docGroup",
+
+ create : function (parent, json) {
+ var obj = Object.create(jsonldClass).upgradeTo(this);
+ obj._operands = [];
+ obj.fromJson(json);
+ if (parent !== undefined)
+ obj._parent = parent;
+ return obj;
+ },
+
+ newAfter : function (obj) {
+ for (var i = 0; i < this._operands.length; i++) {
+ if (this._operands[i] === obj) {
+ var operand = unspecClass.create(this);
+ this._operands.splice(i + 1, 0, operand);
+ return this.update();
+ };
+ };
+ },
+
+ // The doc is already set in the group
+ _duplicate : function (operand) {
+ if (operand.ldType() !== 'doc')
+ return null;
+
+ for (var i = 0; i < this._operands.length; i++) {
+ var op = this.getOperand(i);
+ if (op.ldType() === 'doc'
+ && operand.key() === op.key()
+ && operand.matchop() === op.matchop()
+ && operand.value() === op.value()) {
+ return op;
+ };
+ };
+ return null;
+ },
+
+ append : function (operand) {
+
+ // Append unspecified object
+ if (operand === undefined) {
+
+ // Be aware of cyclic structures!
+ operand = unspecClass.create(this);
+ this._operands.push(operand);
+ return operand;
+ };
+
+ switch (operand["@type"]) {
+
+ case undefined:
+ // No @type defined
+ if (operand["ldType"] !== undefined) {
+ if (operand.ldType() !== 'doc' &&
+ operand.ldType() !== 'docGroup') {
+ KorAP.log(812, "Operand not supported in document group");
+ return;
+ };
+
+ // Be aware of cyclic structures!
+ operand.parent(this);
+
+ var dupl = this._duplicate(operand);
+ if (dupl === null) {
+ this._operands.push(operand);
+ return operand;
+ };
+ return dupl;
+ };
+
+ KorAP.log(701, "JSON-LD group has no @type attribute");
+ return;
+
+ case "koral:doc":
+ // Be aware of cyclic structures!
+ var doc = docClass.create(this, operand);
+ if (doc === undefined)
+ return;
+ var dupl = this._duplicate(doc);
+ if (dupl === null) {
+ this._operands.push(doc);
+ return doc;
+ };
+ return dupl;
+
+ case "koral:docGroup":
+ // Be aware of cyclic structures!
+ var docGroup = docGroupClass.create(this, operand);
+ if (docGroup === undefined)
+ return;
+
+ // Flatten group
+ if (docGroup.operation() === this.operation()) {
+ for (var op in docGroup.operands()) {
+ op = docGroup.getOperand(op);
+ var dupl = this._duplicate(op);
+ if (dupl === null) {
+ this._operands.push(op);
+ op.parent(this);
+ };
+ };
+ docGroup._operands = [];
+ docGroup.destroy();
+ return this;
+ };
+ this._operands.push(docGroup);
+ return docGroup;
+
+ default:
+ KorAP.log(812, "Operand not supported in document group");
+ return;
+ };
+ },
+
+ update : function () {
+ // There is only one operand in group
+
+ if (this._operands.length === 1) {
+
+ var parent = this.parent();
+ var op = this.getOperand(0);
+
+ // This will prevent destruction of
+ // the operand
+ this._operands = [];
+
+ // Parent is a group
+ if (parent.ldType() !== null)
+ return parent.replaceOperand(this, op).update();
+
+ // Parent is vc
+ else {
+ this.destroy();
+ // Cyclic madness
+ parent.root(op);
+ op.parent(parent);
+ return parent.root();
+ };
+ };
+
+ if (this._element === undefined)
+ return this;
+
+ var group = this._element;
+ group.setAttribute('data-operation', this.operation());
+
+ _removeChildren(group);
+
+ // Append operands
+ for (var i = 0; i < this._operands.length; i++) {
+ group.appendChild(
+ this.getOperand(i).element()
+ );
+ };
+
+ // Set operators
+ var op = this.operators(
+ this.operation() == 'and' ? false : true,
+ this.operation() == 'or' ? false : true,
+ true
+ );
+
+ group.appendChild(op.element());
+
+ return this;
+ },
+
+ element : function () {
+ if (this._element !== undefined)
+ return this._element;
+
+ this._element = document.createElement('div');
+ this._element.setAttribute('class', 'docGroup');
+
+ // Update the object - including optimization
+ this.update();
+
+ return this._element;
+ },
+
+ operation : function (op) {
+ if (arguments.length === 1) {
+ if (_validGroupOpRE.test(op)) {
+ this._op = op;
+ }
+ else {
+ KorAP.log(810, "Unknown operation type");
+ return;
+ };
+ };
+ return this._op || 'and';
+ },
+
+ operands : function () {
+ return this._operands;
+ },
+
+ getOperand : function (index) {
+ return this._operands[index];
+ },
+
+ // Replace operand
+ replaceOperand : function (oldOp, newOp) {
+
+ for (var i = 0; i < this._operands.length; i++) {
+ if (this._operands[i] === oldOp) {
+
+ // Just insert a doc or ...
+ if (newOp.ldType() === "doc" ||
+ newOp.ldType() === "non" ||
+ // ... 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);
+ }
+
+ // Flatten group
+ else {
+ // Remove old group
+ this._operands.splice(i, 1);
+
+ // Inject new operands
+ for (var op in newOp.operands().reverse()) {
+ op = newOp.getOperand(op);
+ this._operands.splice(i, 0, op);
+ op.parent(this);
+ };
+ // Prevent destruction of operands
+ newOp._operands = [];
+ newOp.destroy();
+ };
+ oldOp.destroy();
+ return this;
+ }
+ };
+ return false;
+ },
+
+ // Delete operand from group
+ delOperand : function (obj) {
+ for (var i = 0; i < this._operands.length; i++) {
+ if (this._operands[i] === obj) {
+
+ // Delete identified operand
+ this._operands.splice(i,1);
+
+ // Destroy object for cyclic references
+ obj.destroy();
+
+ return this;
+ };
+ };
+
+ // Operand not found
+ return undefined;
+ },
+
+ // 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["operation"] === undefined ||
+ typeof json["operation"] !== 'string') {
+ KorAP.log(811, "Document group expects operation");
+ return;
+ };
+
+ var operation = json["operation"];
+
+ this.operation(operation.replace(/^operation:/,''));
+
+ if (json["operands"] === undefined ||
+ !(json["operands"] instanceof Array)) {
+ KorAP.log(704, "Operation needs operand list")
+ return;
+ };
+
+ // Add all documents
+ for (var i in json["operands"]) {
+ var operand = json["operands"][i];
+ this.append(operand);
+ };
+
+ return this;
+ },
+
+ toJson : function () {
+ var opArray = new Array();
+ for (var i = 0; i < this._operands.length; i++) {
+ if (this._operands[i].ldType() !== 'non')
+ opArray.push(this._operands[i].toJson());
+ };
+ return {
+ "@type" : "koral:" + this.ldType(),
+ "operation" : "operation:" + this.operation(),
+ "operands" : opArray
+ };
+ },
+
+ toQuery : function (brackets) {
+ var list = this._operands
+ .filter(function (op) {
+ return op.ldType() !== 'non';
+ })
+ .map(function (op) {
+ return (op.ldType() === 'docGroup') ?
+ op.toQuery(true) :
+ op.toQuery();
+ });
+
+ if (list.length === 1)
+ return list.join('');
+ else {
+ var str = list.join(this.operation() === 'or' ? ' | ' : ' & ');
+ return brackets ? '(' + str + ')' : str;
+ };
+ }
+ };
+ return docGroupClass;
+});
diff --git a/dev/js/src/vc/item.js b/dev/js/src/vc/item.js
new file mode 100644
index 0000000..bfefe38
--- /dev/null
+++ b/dev/js/src/vc/item.js
@@ -0,0 +1,44 @@
+// Field menu item
+define(['menu/item'], function (itemClass) {
+ return {
+ create : function (params) {
+ return Object.create(itemClass)
+ .upgradeTo(this)
+ ._init(params);
+ },
+
+ _init : function (params) {
+ if (params[0] === undefined)
+ throw new Error("Missing parameters");
+
+ this._name = params[0];
+ this._value = params[1];
+ this._type = params[2];
+
+ this._lcField = ' ' + this._name.toLowerCase();
+
+ return this;
+ },
+
+ name : function () {
+ return this._name;
+ },
+
+ type : function () {
+ return this._type;
+ },
+
+ element : function () {
+ // already defined
+ if (this._element !== undefined)
+ return this._element;
+
+ // Create list item
+ var li = document.createElement("li");
+ li.setAttribute("data-type", this._type);
+ li.setAttribute("data-value", this._value);
+ li.appendChild(document.createTextNode(this._name));
+ return this._element = li;
+ }
+ };
+});
diff --git a/dev/js/src/vc/jsonld.js b/dev/js/src/vc/jsonld.js
new file mode 100644
index 0000000..324e7db
--- /dev/null
+++ b/dev/js/src/vc/jsonld.js
@@ -0,0 +1,99 @@
+/**
+ * Abstract JsonLD criterion object
+ */
+define(['vc/operators'], function (operatorsClass) {
+ return {
+ __changed : false,
+
+ create : function () {
+ return Object.create(this);
+ },
+
+ /**
+ * Upgrade this object to another object
+ * while private data stays intact
+ */
+ upgradeTo : function (props) {
+ for (var prop in props) {
+ this[prop] = props[prop];
+ };
+ return this;
+ },
+
+ ldType : function (type) {
+ if (arguments.length === 1)
+ this._ldType = type;
+ return this._ldType;
+ },
+
+ parent : function (obj) {
+ if (arguments.length === 1) {
+ this._parent = obj;
+ this.__changed = true;
+ };
+ return this._parent;
+ },
+
+ // Destroy object - especially for
+ // acyclic structures!
+ // I'm paranoid!
+ destroy : function () {
+ if (this._ops != undefined) {
+ this._ops._parent = undefined;
+ if (this._ops._element !== undefined)
+ this._ops._element.refTo = undefined;
+ this._ops = undefined;
+ };
+ if (this._element !== undefined)
+ this._element = undefined;
+
+ // In case of a group, destroy all operands
+ if (this._operands !== undefined) {
+ for (var i = 0; i < this._operands.length; i++)
+ this.getOperand(i).destroy();
+ this._operands = [];
+ };
+ },
+
+ // Wrap a new operation around the root group element
+ wrapOnRoot : function (op) {
+ var parent = this.parent();
+
+ var group = require('vc/docgroup').create(parent);
+ if (arguments.length === 1)
+ group.operation(op);
+ else
+ group.operation(
+ this.operation() === 'and' ? 'or' : 'and'
+ );
+ group.append(this);
+ this.parent(group);
+ group.append();
+ group.element(); // Init (seems to be necessary)
+ parent.root(group);
+ return this.parent();
+ },
+
+ // Be aware! This may be cyclic
+ operators : function (and, or, del) {
+ if (arguments === 0)
+ return this._ops;
+ this._ops = operatorsClass.create(
+ and, or, del
+ );
+ this._ops.parent(this);
+ return this._ops;
+ },
+
+ toJson : function () {
+ return {
+ // Unspecified object
+ "@type" : "koral:" + this.ldType()
+ };
+ },
+
+ toQuery : function () {
+ return '';
+ }
+ };
+});
diff --git a/dev/js/src/vc/menu.js b/dev/js/src/vc/menu.js
new file mode 100644
index 0000000..6f6d2bd
--- /dev/null
+++ b/dev/js/src/vc/menu.js
@@ -0,0 +1,10 @@
+// Field menu
+define(['menu', 'menu/item'], function (menuClass, itemClass) {
+ return {
+ create : function (params) {
+ return Object.create(menuClass)
+ .upgradeTo(this)
+ ._init(itemClass, undefined, params)
+ }
+ };
+});
diff --git a/dev/js/src/vc/operators.js b/dev/js/src/vc/operators.js
new file mode 100644
index 0000000..312f75d
--- /dev/null
+++ b/dev/js/src/vc/operators.js
@@ -0,0 +1,168 @@
+/**
+ * Operators for criteria
+ */
+define(['util'], function () {
+
+ var loc = KorAP.Locale;
+ loc.AND = loc.AND || 'and';
+ loc.OR = loc.OR || 'or';
+ loc.DEL = loc.DEL || '×';
+
+
+ // Utility for analysing boolean values
+ function _bool (bool) {
+ return (bool === undefined || bool === null || bool === false) ? false : true;
+ };
+
+
+ // Add new unspecified document
+ function _add (obj, type) {
+ var ref = obj.parentNode.refTo;
+ var parent = ref.parent();
+
+ if (ref.ldType() === 'docGroup') {
+
+ // Check that the action differs from the type
+ if (ref.operation() === type)
+ return;
+
+ if (parent.ldType() !== null) {
+ return parent.newAfter(ref);
+ }
+ else {
+ // The group is on root - wrap
+ return ref.wrapOnRoot();
+ };
+ }
+ else if (ref.ldType() === 'doc') {
+
+ if (parent.ldType() === null) {
+ return ref.wrapOnRoot(type);
+ }
+ else if (parent.operation() === type) {
+ return parent.newAfter(ref);
+ }
+ else {
+ return ref.wrap(type);
+ };
+ };
+ };
+
+
+ // Add doc with 'and' relation
+ var _and = function () {
+ return _add(this, 'and');
+ };
+
+
+ // Add doc with 'or' relation
+ var _or = function () {
+ return _add(this, 'or');
+ };
+
+
+ // Remove doc or docGroup
+ var _delete = function () {
+ var ref = this.parentNode.refTo;
+ if (ref.parent().ldType() !== null) {
+ return ref.parent().delOperand(ref).update();
+ }
+ else {
+ ref.parent().clean();
+ };
+ };
+
+
+ return {
+ create : function (and, or, del) {
+ var op = Object.create(this);
+ op.and(and);
+ op.or(or);
+ op.del(del);
+ return op;
+ },
+
+ update : function () {
+ // Init the element
+ if (this._element === undefined)
+ return this.element();
+
+ var op = this._element;
+
+ op.refTo = this.parent();
+
+ // Remove everything underneath
+ _removeChildren(op);
+
+ // Add and button
+ if (this._and === true) {
+ var andE = document.createElement('span');
+ andE.setAttribute('class', 'and');
+ andE.addEventListener('click', _and, false);
+ andE.appendChild(
+ document.createTextNode(loc.AND)
+ );
+ op.appendChild(andE);
+ };
+
+ // Add or button
+ if (this._or === true) {
+ var orE = document.createElement('span');
+ orE.setAttribute('class', 'or');
+ orE.addEventListener('click', _or, false);
+ orE.appendChild(document.createTextNode(loc.OR));
+ op.appendChild(orE);
+ };
+
+ // Add delete button
+ if (this._del === true) {
+ var delE = document.createElement('span');
+ delE.setAttribute('class', 'delete');
+ delE.appendChild(document.createTextNode(loc.DEL));
+ delE.addEventListener('click', _delete, false);
+ op.appendChild(delE);
+ };
+
+ return op;
+ },
+
+ // Be aware! This may be cyclic
+ parent : function (obj) {
+ if (arguments.length === 1)
+ this._parent = obj;
+ return this._parent;
+ },
+
+ element : function () {
+
+ // Return existing element
+ if (this._element !== undefined)
+ return this._element;
+
+ this._element = document.createElement('div');
+ this._element.setAttribute('class', 'operators');
+
+ // Init elements
+ this.update();
+ return this._element;
+ },
+
+ and : function (bool) {
+ if (arguments.length === 1)
+ this._and = _bool(bool);
+ return this._and;
+ },
+
+ or : function (bool) {
+ if (arguments.length === 1)
+ this._or = _bool(bool);
+ return this._or;
+ },
+
+ del : function (bool) {
+ if (arguments.length === 1)
+ this._del = _bool(bool);
+ return this._del;
+ }
+ };
+});
diff --git a/dev/js/src/vc/rewrite.js b/dev/js/src/vc/rewrite.js
new file mode 100644
index 0000000..79ba4b3
--- /dev/null
+++ b/dev/js/src/vc/rewrite.js
@@ -0,0 +1,100 @@
+/**
+ * Implementation of rewrite objects.
+ */
+define(['vc/jsonld', 'util'], function (jsonldClass) {
+
+ var _validRewriteOpRE = new RegExp("^(?:injec|modifica)tion$");
+ var _quote = new RegExp("([\"\\\\])", 'g');
+
+ return {
+ // Construction method
+ create : function (json) {
+ var obj = Object(jsonldClass).
+ create().
+ upgradeTo(this).
+ fromJson(json);
+ return obj;
+ },
+
+ // Get or set source
+ src : function (string) {
+ if (arguments.length === 1)
+ this._src = string;
+ return this._src;
+ },
+
+ // Get or set operation
+ operation : function (op) {
+ if (arguments.length === 1) {
+ if (_validRewriteOpRE.test(op)) {
+ this._op = op;
+ }
+ else {
+ KorAP.log(814, "Unknown rewrite operation");
+ return;
+ };
+ };
+ return this._op || 'injection';
+ },
+
+ // Get or set scope
+ scope : function (attr) {
+ if (arguments.length === 1)
+ this._scope = attr;
+ return this._scope;
+ },
+
+ // Serialize from Json
+ fromJson : function (json) {
+ if (json === undefined)
+ return this;
+
+ // Missing @type
+ if (json["@type"] === undefined) {
+ KorAP.log(701, "JSON-LD group has no @type attribute");
+ return;
+ };
+
+ // Missing source
+ if (json["src"] === undefined ||
+ typeof json["src"] !== 'string') {
+ KorAP.log(815, "Rewrite expects source");
+ return;
+ };
+
+ // Set source
+ this.src(json["src"]);
+
+ // Set operation
+ if (json["operation"] !== undefined) {
+ var operation = json["operation"];
+ this.operation(operation.replace(/^operation:/,''));
+ };
+
+ // Set scope
+ if (json["scope"] !== undefined &&
+ typeof json["scope"] === 'string')
+ this.scope(json["scope"]);
+
+ return this;
+ },
+
+ toString : function () {
+ var str = '';
+ var op = this.operation();
+ str += op.charAt(0).toUpperCase() + op.slice(1);
+ str += ' of ' + (
+ this._scope === null ?
+ 'object' :
+ '"' +
+ this.scope().replace(_quote, '\\$1') +
+ '"'
+ );
+ str += ' by ' +
+ '"' +
+ this.src().replace(_quote, '\\$1') +
+ '"';
+ return str;
+ }
+ };
+});
diff --git a/dev/js/src/vc/rewritelist.js b/dev/js/src/vc/rewritelist.js
new file mode 100644
index 0000000..af3c05a
--- /dev/null
+++ b/dev/js/src/vc/rewritelist.js
@@ -0,0 +1,49 @@
+define(['vc/jsonld', 'vc/rewrite'], function (jsonldClass, rewriteClass) {
+ return {
+ // Construction method
+ create : function (json) {
+ var obj = Object(jsonldClass).
+ create().
+ upgradeTo(this).
+ fromJson(json);
+ return obj;
+ },
+ fromJson : function (json) {
+ this._list = new Array();
+ for (var i = 0; i < json.length; i++) {
+ this._list.push(
+ rewriteClass.create(json[i])
+ );
+ };
+ return this;
+ },
+ element : function () {
+ if (this._element !== undefined)
+ return this._element;
+
+ this._element = document.createElement('div');
+ this._element.setAttribute('class', 'rewrite');
+ for (var x in this._list) {
+ var rewrite = this._list[x];
+ var span = document.createElement('span');
+
+ // Set class attribute
+ span.setAttribute('class', rewrite.operation());
+
+ // Append source information
+ span.appendChild(document.createTextNode(rewrite.src()));
+
+ // Append scope information
+ if (rewrite.scope() !== undefined) {
+ span.appendChild(
+ document.createTextNode(
+ ': ' + rewrite.scope()
+ )
+ );
+ };
+ this._element.appendChild(span);
+ };
+ return this._element;
+ }
+ };
+});
diff --git a/dev/js/src/vc/unspecified.js b/dev/js/src/vc/unspecified.js
new file mode 100644
index 0000000..9efd007
--- /dev/null
+++ b/dev/js/src/vc/unspecified.js
@@ -0,0 +1,89 @@
+/**
+ * Unspecified criterion
+ */
+define(['vc/jsonld', 'vc/doc', 'util'], function (jsonldClass, docClass) {
+
+ var loc = KorAP.Locale;
+ loc.EMPTY = loc.EMPTY || '⋯';
+
+ return {
+ _ldType : "non",
+ create : function (parent) {
+ var obj = Object.create(jsonldClass).
+ upgradeTo(this);
+
+ if (parent !== undefined)
+ obj._parent = parent;
+
+ return obj;
+ },
+
+ // Set key - replace
+ key : function (v) {
+
+ // Not replaceable
+ if (this._parent === undefined)
+ return null;
+
+ // Set JSON-LD type
+ var newDoc = docClass.create(this._parent, {
+ "@type" : "koral:doc",
+ "value" : "",
+ "key" : v
+ });
+
+ // Unspecified document on root
+ if (this._parent.ldType() === null) {
+ this._parent.root(newDoc);
+ this.destroy();
+ }
+
+ // Unspecified document in group
+ else {
+ this._parent.replaceOperand(this, newDoc);
+ };
+ this._parent.update();
+ return newDoc;
+ },
+
+ update : function () {
+
+ if (this._element === undefined)
+ return this.element();
+
+ // Remove element content
+ _removeChildren(this._element);
+
+ var ellipsis = document.createElement('span');
+ ellipsis.appendChild(document.createTextNode(loc.EMPTY));
+ this._element.appendChild(ellipsis);
+
+ // Set ref - TODO: Cleanup!
+ this._element.refTo = this;
+
+ // Set operators
+ if (this._parent !== undefined && this.parent().ldType() !== null) {
+ var op = this.operators(
+ false,
+ false,
+ true
+ );
+
+ this._element.appendChild(
+ op.element()
+ );
+ };
+
+ return this.element();
+ },
+
+ element : function () {
+ if (this._element !== undefined)
+ return this._element;
+ this._element = document.createElement('div');
+ this._element.setAttribute('class', 'doc unspecified');
+ this.update();
+ return this._element;
+ },
+ };
+});