blob: 6ea5e8bb28abd0cc2b3a8a846481956c2dc655ea [file] [log] [blame]
/**
* Document group criterion
*/
/*
* TODO: Let the UPDATE event bubble up through parents!
*/
"use strict";
define([
'vc/jsonld',
'vc/unspecified',
'vc/doc',
'vc/docgroupref',
'util'
], function (jsonldClass,
unspecClass,
docClass,
docGroupRefClass) {
const _validGroupOpRE = new RegExp("^(?:and|or)$");
const docGroupClass = {
_ldType : "docGroup",
create : function (parent, json) {
const obj = Object.create(jsonldClass).upgradeTo(this);
obj._operands = [];
obj.fromJson(json);
if (parent !== undefined)
obj._parent = parent;
return obj;
},
newAfter : function (obj) {
this._operands.forEach(function (op, i) {
if (op === obj) {
const operand = unspecClass.create(this);
this._operands.splice(i + 1, 0, operand);
return this.update();
};
}, this);
},
// The doc is already set in the group
_duplicate : function (operand) {
// TODO:
// Also check for duplicate docGroupRefs!
if (operand.ldType() !== 'doc')
return null;
const f = this._operands.find(
op =>
op.ldType() === 'doc'
&& operand.key() === op.key()
&& operand.matchop() === op.matchop()
&& operand.value() === op.value()
);
if (f)
return f;
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' &&
operand.ldType() !== 'docGroupRef') {
KorAP.log(812, "Operand not supported in document group");
return;
};
// Be aware of cyclic structures!
operand.parent(this);
const 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!
const doc = docClass.create(this, operand);
if (doc === undefined)
return;
const dupl = this._duplicate(doc);
if (dupl === null) {
this._operands.push(doc);
return doc;
};
return dupl;
case "koral:docGroup":
// Be aware of cyclic structures!
const docGroup = docGroupClass.create(this, operand);
if (docGroup === undefined)
return;
// Flatten group
if (docGroup.operation() === this.operation()) {
docGroup.operands().forEach(function(op) {
const dupl = this._duplicate(op);
if (dupl === null) {
this._operands.push(op);
op.parent(this);
};
}, this);
docGroup._operands = [];
docGroup.destroy();
return this;
};
this._operands.push(docGroup);
return docGroup;
case "koral:docGroupRef":
const 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;
};
},
update : function () {
const t = this;
// There is only one operand in group
if (t._operands.length === 1) {
const parent = t.parent();
const op = t.getOperand(0);
// This will prevent destruction of
// the operand
t._operands = [];
// Parent is a group
if (parent.ldType() !== null)
return parent.replaceOperand(t, op).update();
// Parent is vc
else {
t.destroy();
// Cyclic madness
parent.root(op);
op.parent(parent);
return parent.root();
};
};
if (t._el === undefined)
return t;
const group = t._el;
group.setAttribute('data-operation', t.operation());
_removeChildren(group);
// Append operands
t._operands.forEach(
op => group.appendChild(op.element())
);
// Set operators
var op = t.operators(
t.operation() == 'and' ? false : true,
t.operation() == 'or' ? false : true,
true
);
group.appendChild(op.element());
if (KorAP.vc) {
KorAP.vc.element().dispatchEvent(
new CustomEvent('vcChange', {'detail' : t})
);
};
return t;
},
element : function () {
const t = this;
if (t._el !== undefined)
return t._el;
const e = t._el = document.createElement('div');
e.setAttribute('class', 'docGroup');
// Update the object - including optimization
t.update();
return e;
},
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 (let i = 0; i < this._operands.length; i++) {
if (this._operands[i] === oldOp) {
// 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()) {
this._operands[i] = newOp;
newOp.parent(this);
}
// Flatten group
else {
// Remove old group
this._operands.splice(i, 1);
// Inject new operands
newOp.operands().reverse().forEach(
function(op) {
this._operands.splice(i, 0, op);
op.parent(this);
},
this
);
// Prevent destruction of operands
newOp._operands = [];
newOp.destroy();
};
oldOp.destroy();
return this;
}
};
return false;
},
// Delete operand from group
delOperand : function (obj) {
for (let 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;
};
const 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
json["operands"].forEach(i => this.append(i));
return this;
},
toJson : function () {
const opArray = new Array();
this._operands.forEach(function(op) {
if (op.ldType() !== 'non')
opArray.push(op.toJson());
});
return {
"@type" : "koral:" + this.ldType(),
"operation" : "operation:" + this.operation(),
"operands" : opArray
};
},
toQuery : function (brackets) {
var list = this._operands
.filter(function (op) {
return !op.incomplete();
})
.map(function (op) {
return (op.ldType() === 'docGroup') ?
op.toQuery(true) :
op.toQuery();
});
if (list.length === 1)
return list.join('');
else {
const str = list.join(this.operation() === 'or' ? ' | ' : ' & ');
return brackets ? '(' + str + ')' : str;
};
}
};
return docGroupClass;
});