blob: 996c3470db56ad48c81d1a8a24a62105bba414db [file] [log] [blame]
/**
* A new document criterion
*/
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
var 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 () {
if (this._element === undefined)
return this.element();
// 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;
// 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);
this._valueE.classList.add('unspecified');
};
// 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,
true,
true
);
// Append new operators
e.appendChild(op.element());
};
return e;
},
/**
* Get the associated element
*/
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 = require('vc/docgroup').create(parent);
group.operation(op);
group.append(this);
group.append();
return parent.replaceOperand(this, group).update();
},
replaceWith : function (op) {
var 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) {
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;
};
var rewrite;
// 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, 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
var type = KorAP._vcKeyMenu.typeOf(this.key());
if (type != undefined) {
json["type"] = "type:" + type;
};
};
// Type is still undefined
if (json["type"] === undefined) {
// Check match type
if (!KorAP._validUnspecMatchRE.test(this.matchop())) {
KorAP.log(802, errstr802);
// Rewrite method
this.matchop('eq');
rewrite = 'modification';
};
// Set string value
this.value(json["value"]);
}
// Field is string type
else if (json["type"] == "type:string") {
this.type("string");
// Check match type
if (!KorAP._validStringMatchRE.test(this.matchop())) {
KorAP.log(802, errstr802);
// Rewrite method
this.matchop('eq');
rewrite = 'modification';
};
// Set string value
this.value(json["value"]);
}
// Field is specified
else if (json["type"] == "type:text") {
this.type("text");
// Check match type
if (!KorAP._validTextMatchRE.test(this.matchop())) {
KorAP.log(802, errstr802);
// Rewrite method
this.matchop('eq');
rewrite = 'modification';
};
// 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, errstr802);
// Rewrite method
this.matchop('eq');
rewrite = 'modification';
};
// 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 (!KorAP._validStringMatchRE.test(this.matchop())) {
KorAP.log(802, errstr802);
// Rewrite method
this.matchop('eq');
rewrite = 'modification';
};
this.value(json["value"]);
}
catch (e) {
KorAP.log(807, "Value is not a valid regular expression");
return;
};
this.type("regex");
}
else {
KorAP.log(804, errstr804 + ": " + this.type());
throw new Error(errstr804 + ": " + this.type());
};
};
// Rewrite coming from the server
if (json["rewrites"] !== undefined) {
this.rewrite(json["rewrites"]);
}
// Rewrite coming from Kalamar
else if (rewrite !== undefined) {
this.rewrite(rewrite);
};
return this;
},
/**
* 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) {
var menu = KorAP._vcKeyMenu;
// Insert menu
this._element.insertBefore(
menu.element(),
this._keyE
);
// Release event
var that = this;
menu.released(function (key, type) {
if (type === 'ref') {
// KorAP._delete.bind(that);
var ref = docGroupRefClass.create(that.parent());
that.replaceWith(ref);
}
else {
var 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) {
if (arguments.length === 1) {
var m = match.replace(/^match:/, '');
if (
(this._type == undefined) // && KorAP._validUnspecMatchRE.test(m))
||
(
(this._type === 'string' || this._type === 'regex') &&
KorAP._validStringMatchRE.test(m)
)
||
(this._type === 'text' && KorAP._validTextMatchRE.test(m))
||
(this._type === 'date' && KorAP._validDateMatchRE.test(m))
) {
this._matchop = m;
}
else {
this._matchop = "eq";
};
this._changed();
return this
};
return this._matchop || "eq";
},
// Click on the match operator, show me the menu
_changeMatchop : function (e) {
var menu = KorAP._vcMatchopMenu[this.type()];
if (menu === undefined) {
KorAP.log(0, "Unable to init menu for " + this.type());
return;
};
// Insert menu
this._element.insertBefore(
menu.element(),
this._matchopE
);
// Release event
var 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) {
if (arguments.length === 1) {
if (this._type === 'date' && !KorAP._validDateRE.test(value)) {
delete this._value;
}
else {
this._value = value;
};
this._changed();
return this;
};
return this._value;
},
// Click on the match operator, show me the menu
_changeValue : function (e) {
var v = this.value();
var that = this;
// Show datepicker
if (this.type() === 'date') {
var dp = KorAP._vcDatePicker;
dp.fromString(v);
// Todo: change this
dp.onclick(function (selected) {
// There are values selected
if (selected['year']) {
that.value(this.toString());
that.update();
return;
};
// Remove datepicker
that._element.removeChild(
dp.element()
);
});
// Get element of the date picker
var dpElem = dp.show();
this._element.insertBefore(
dpElem,
this._valueE
);
dp.input().focus();
}
else {
var regex = this.type() === 'regex' ? true : false;
var str = stringValClass.create(this.value(), regex);
var strElem = str.element();
str.store = function (value, regex) {
that.value(value);
if (regex === true)
that.type('regex');
else
that.type('string');
that._element.removeChild(
this._element
);
that.update();
};
// Insert element
this._element.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._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 "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 "";
}
};
});