blob: 37b0e0b812cadf536575114fcb2917fc4aa70657 [file] [log] [blame]
/**
* A new document criterion
*/
"use strict";
define([
'vc/jsonld',
'vc/rewritelist',
'vc/stringval',
'vc/docgroupref',
'util'
], function (jsonldClass, rewriteListClass, stringValClass, docGroupRefClass) {
/*
* TODO:
* Improve error handling by using window.onerror() to
* capture thrown errors and log them.
*/
const errstr802 = "Match type is not supported by value type";
const errstr804 = "Unknown value type";
const loc = KorAP.Locale;
loc.EMPTY = loc.EMPTY || '⋯';
return {
// The JSON-LD type
_ldType : "doc",
// The object ... maybe not important
_obj : function () { return '???'; /*KorAP.Doc*/ },
/**
* Create a new document criterion
* by passing the parent object and a json construct.
*/
create : function (parent, json) {
// Create the object, inheriting from Json-LD class
const obj = Object(jsonldClass).
create().
upgradeTo(this).
fromJson(json);
if (obj === undefined) {
console.log(json);
return;
};
// Bind the parent
if (parent !== undefined)
obj._parent = parent;
obj.__changed = true;
return obj;
},
/**
* Update the elements content.
*/
update : function () {
const t = this;
if (t._el === undefined)
return t.element();
// Get element
const e = t._el;
// Check if there is a change in the underlying data
if (!t.__changed)
return e;
// Set ref - TODO: Cleanup!
e.refTo = t;
// Was rewritten
if (t.rewrites() !== undefined) {
e.classList.add("rewritten");
};
// Added key
const keyE = t._keyE = document.createElement('span');
keyE.setAttribute('class', 'key');
// Change key
keyE.addEventListener('click', t._changeKey.bind(t));
if (t.key()) {
const k = t.key();
if (loc['VC_' + k] !== undefined)
k = loc['VC_' + k];
keyE.addT(k);
};
// Added match operator
const matchopE = t._matchopE = document.createElement('span');
matchopE.setAttribute('data-type', t.type());
matchopE.setAttribute('class', 'match');
matchopE.addT(t.matchop());
// Change matchop
t._matchopE.addEventListener(
'click',
t._changeMatchop.bind(t)
);
// Added value operator
const valueE = t._valueE = document.createElement('span');
valueE.setAttribute('data-type', t.type());
valueE.setAttribute('class', 'value');
if (t.value()) {
valueE.addT(t.value());
}
else {
valueE.addT(loc.EMPTY);
valueE.classList.add('unspecified');
};
// Change value
valueE.addEventListener(
'click',
t._changeValue.bind(t)
);
// Remove all element children
_removeChildren(e);
// Add spans
e.appendChild(keyE);
e.appendChild(matchopE);
e.appendChild(valueE);
t.__changed = false;
if (t._rewrites !== undefined) {
e.appendChild(t._rewrites.element());
};
if (t._parent !== undefined) {
// Set operators
// Append new operators
e.appendChild(t.operators(
true,
true,
true
).element());
};
if (KorAP.vc){
KorAP.vc.element().dispatchEvent(
new CustomEvent('vcChange', {'detail' : t})
);
}
return e;
},
/**
* Get the associated element
*/
element : function () {
const t = this;
if (t._el !== undefined)
return t._el;
t._el = document.createElement('div');
t._el.setAttribute('class', 'doc');
t.update();
return t._el;
},
/**
* Wrap a new operation around the doc element
*/
wrap : function (op) {
const parent = this.parent();
const group = require('vc/docgroup').create(parent);
group.operation(op);
group.append(this);
group.append();
return parent.replaceOperand(this, group).update();
},
replaceWith : function (op) {
const p = this.parent();
if (p.ldType() === 'docGroup') {
p.replaceOperand(this, op);
}
else if (p.ldType() == null) {
p.root(op);
};
p.update();
this.destroy();
},
/**
* Deserialize from json
*/
fromJson : function (json) {
const t = this;
if (json === undefined)
return t;
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;
};
let rewrite;
// There is a defined key
if (json["key"] !== undefined &&
typeof json["key"] === 'string') {
// Set key
t.key(json["key"]);
// Set match operation
if (json["match"] !== undefined) {
if (typeof json["match"] === 'string') {
t.matchop(json["match"]);
}
else {
KorAP.log(802, errstr802);
return;
};
};
// Type is unspecified - but may be known by the menu
if (json["type"] === undefined && KorAP._vcKeyMenu) {
// Check the VC list if the field is known
const type = KorAP._vcKeyMenu.typeOf(t.key());
if (type != undefined) {
json["type"] = "type:" + type;
};
};
// Type is still undefined
if (json["type"] === undefined) {
// Check match type
if (!KorAP._validUnspecMatchRE.test(t.matchop())) {
KorAP.log(802, errstr802);
// Rewrite method
t.matchop('eq');
rewrite = 'modification';
};
// Set string value
t.value(json["value"]);
}
// Field is string type
else if (json["type"] == "type:string") {
t.type("string");
// Check match type
if (!KorAP._validStringMatchRE.test(t.matchop())) {
KorAP.log(802, errstr802);
// Rewrite method
t.matchop('eq');
rewrite = 'modification';
};
// Set string value
t.value(json["value"]);
}
// Field is specified
else if (json["type"] == "type:text") {
t.type("text");
// Check match type
if (!KorAP._validTextMatchRE.test(t.matchop())) {
KorAP.log(802, errstr802);
// Rewrite method
t.matchop('eq');
rewrite = 'modification';
};
// Set string value
t.value(json["value"]);
}
// Key is a date
else if (json["type"] === "type:date") {
t.type("date");
if (json["value"] !== undefined &&
KorAP._validDateRE.test(json["value"])) {
if (!KorAP._validDateMatchRE.test(t.matchop())) {
KorAP.log(802, errstr802);
// Rewrite method
t.matchop('eq');
rewrite = 'modification';
};
// Set value
t.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") {
t.type("regex");
try {
// Try to create a regular expression
let check = new RegExp(json["value"]);
if (!KorAP._validStringMatchRE.test(t.matchop())) {
KorAP.log(802, errstr802);
// Rewrite method
t.matchop('eq');
rewrite = 'modification';
};
t.value(json["value"]);
}
catch (e) {
KorAP.log(807, "Value is not a valid regular expression");
return;
};
t.type("regex");
}
else {
KorAP.log(804, errstr804 + ": " + t.type());
throw new Error(errstr804 + ": " + t.type());
};
};
// Rewrite coming from the server
if (json["rewrites"] !== undefined) {
t.rewrite(json["rewrites"]);
}
// Rewrite coming from Kalamar
else if (rewrite !== undefined) {
t.rewrite(rewrite);
};
return t;
},
/**
* Get or set the key
*/
key : function (value) {
if (arguments.length === 1) {
this._key = value;
this._changed();
return this;
};
return this._key;
},
// Click on the key, show me the menu
_changeKey : function (e) {
const menu = KorAP._vcKeyMenu;
// Insert menu
this._el.insertBefore(
menu.element(),
this._keyE
);
// Release event
const that = this;
menu.released(function (key, type) {
if (type === 'ref') {
// KorAP._delete.bind(that);
that.replaceWith(
docGroupRefClass.create(that.parent())
);
}
else {
const doc = that.key(key).type(type);
// This may not be compatible - then switch to default
doc.matchop(doc.matchop());
doc.value(doc.value());
// Update the doc
doc.update();
};
// hide!
this.hide();
});
// TODO: Highlight the old value!
menu.show();
menu.focus();
},
/**
* Get or set the match operator
*/
matchop : function (match) {
const t = this;
if (arguments.length === 1) {
const m = match.replace(/^match:/, '');
if (
(t._type == undefined) // && KorAP._validUnspecMatchRE.test(m))
||
(
(t._type === 'string' || t._type === 'regex') &&
KorAP._validStringMatchRE.test(m)
)
||
(t._type === 'text' && KorAP._validTextMatchRE.test(m))
||
(t._type === 'date' && KorAP._validDateMatchRE.test(m))
) {
t._matchop = m;
}
else {
t._matchop = "eq";
};
t._changed();
return t;
};
return t._matchop || "eq";
},
// Click on the match operator, show me the menu
_changeMatchop : function (e) {
const menu = KorAP._vcMatchopMenu[this.type()];
if (menu === undefined) {
KorAP.log(0, "Unable to init menu for " + this.type());
return;
};
// Insert menu
this._el.insertBefore(
menu.element(),
this._matchopE
);
// Release event
const that = this;
menu.released(function (mo) {
that.matchop(mo).update();
this.hide();
});
menu.show();
menu.focus();
},
/**
* Get or set the type
*/
type : function (type) {
if (arguments.length === 1) {
this._type = type;
this._changed();
return this;
};
return this._type || "string";
},
/**
* Get or set the value
*/
value : function (value) {
const t = this;
if (arguments.length === 1) {
if (t._type === 'date' && !KorAP._validDateRE.test(value)) {
delete t._value;
}
else {
t._value = value;
};
t._changed();
return t;
};
return t._value;
},
// Click on the match operator, show me the menu
_changeValue : function (e) {
const that = this;
// Show datepicker
if (this.type() === 'date') {
const dp = KorAP._vcDatePicker;
dp.fromString(this.value());
// Todo: change this
dp.onclick(function (selected) {
// There are values selected
if (selected['year']) {
that.value(this.toString());
that.update();
return;
};
// Remove datepicker
that._el.removeChild(
dp.element()
);
});
this._el.insertBefore(
dp.show(), // Get element of the date picker
this._valueE
);
dp.input().focus();
}
else {
const regex = this.type() === 'regex' ? true : false;
const str = stringValClass.create(this.value(), regex);
const strElem = str.element();
str.store = function (value, regex) {
that.value(value);
if (regex === true)
that.type('regex');
else
that.type('string');
that._el.removeChild(
this._el
);
that.update();
};
// Insert element
this._el.insertBefore(
strElem,
this._valueE
);
str.focus();
};
},
rewrites : function () {
return this._rewrites;
},
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._el === undefined)
return;
this._el.classList.remove("rewritten");
},
toJson : function () {
const t = this;
if (!t.matchop() || !t.key())
return {};
return {
"@type" : "koral:" + t.ldType(),
"key" : t.key(),
"match" : "match:" + t.matchop(),
"value" : t.value() || '',
"type" : "type:" + t.type()
};
},
incomplete : function () {
return !(this.matchop() && this.key() && this.value());
},
toQuery : function () {
if (this.incomplete())
return "";
// Build doc string based on key
let string = this.key() + ' ';
// Add match operator
switch (this.matchop()) {
case "ne":
string += '!=';
break;
case "contains":
string += '~';
break;
case "excludes":
string += '!~';
break;
case "containsnot":
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();
case "regex":
return string + '/' + this.value().escapeRegex() + '/';
case "string":
case "text":
return string + this.value().quote();
};
return "";
}
};
});