Use requirejs for clientside scripting
diff --git a/dev/js/src/api.js b/dev/js/src/api.js
index ed33cc2..f9058bc 100644
--- a/dev/js/src/api.js
+++ b/dev/js/src/api.js
@@ -1,15 +1,4 @@
-var KorAP = KorAP || {};
-
-(function (KorAP) {
- "use strict";
-
- // Default log message
- KorAP.log = KorAP.log || function (type, msg) {
- console.log(type + ": " + msg);
- };
-
- KorAP.URL = KorAP.URL || 'http://korap.ids-mannheim.de/kalamar';
-
+define(['util'], function () {
// TODO: https://github.com/honza/140medley/blob/master/140medley.js
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
@@ -17,6 +6,8 @@
// http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml
// http://stackoverflow.com/questions/6112744/load-javascript-on-demand
+ KorAP.URL = KorAP.URL || 'http://korap.ids-mannheim.de/kalamar';
+
KorAP.API = {
getMatchInfo : function (match, param, cb) {
// match is a KorAP.Match object
@@ -44,14 +35,11 @@
this.getJSON(url, cb);
},
+
getJSON : function (url, onload) {
var req = new XMLHttpRequest();
- console.log('Request url: ' + url);
-
req.open("GET", url, true);
-
-
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
req.onreadystatechange = function () {
@@ -62,7 +50,7 @@
2 - headers received
3 - loading (responseText has partial data)
4 - done
- */
+ */
if (this.readyState == 4) {
if (this.status === 200)
onload(JSON.parse(this.responseText));
@@ -76,5 +64,4 @@
req.send();
}
};
-
-}(this.KorAP));
+});
diff --git a/dev/js/src/hint.js b/dev/js/src/hint.js
index 11c3e1e..6aed544 100644
--- a/dev/js/src/hint.js
+++ b/dev/js/src/hint.js
@@ -3,23 +3,20 @@
*
* @author Nils Diewald
*/
-
-// requires menu.js
-
-var KorAP = KorAP || {};
-
-(function (KorAP) {
+define([
+ 'hint/input',
+ 'hint/menu',
+ 'hint/contextanalyzer',
+ 'util'
+], function (inputClass,
+ menuClass,
+ analyzerClass) {
"use strict";
- // Default log message
- KorAP.log = KorAP.log || function (type, msg) {
- console.log(type + ": " + msg);
- };
-
/**
* @define {regex} Regular expression for context
*/
- KorAP.context =
+ KorAP.context = KorAP.context ||
"(?:^|[^-_a-zA-Z0-9])" + // Anchor
"((?:[-_a-zA-Z0-9]+?)\/" + // Foundry
"(?:" +
@@ -27,10 +24,18 @@
"(?:(?:[^:=\/ ]+?):)?" + // Key
")?" +
")$";
-
- // Initialize hint array
KorAP.hintArray = KorAP.hintArray || {};
+ /**
+ * Return keycode based on event
+ */
+ function _codeFromEvent (e) {
+ if ((e.charCode) && (e.keyCode==0))
+ return e.charCode
+ return e.keyCode;
+ };
+
+ // Initialize hint array
/**
* KorAP.Hint.create({
@@ -38,16 +43,20 @@
* context : context regex
* });
*/
- KorAP.Hint = {
+ return {
// Some variables
// _firstTry : true,
active : false,
+ /**
+ * Create new hint helper.
+ */
create : function (param) {
- return Object.create(KorAP.Hint)._init(param);
+ return Object.create(this)._init(param);
},
+ // Initialize hint helper
_init : function (param) {
param = param || {};
@@ -55,9 +64,11 @@
this._menu = {};
// Get input field
- this._inputField = KorAP.InputField.create(
- param["inputField"] || document.getElementById("q-field")
- );
+ var qfield = param["inputField"] || document.getElementById("q-field");
+ if (!qfield)
+ return null;
+
+ this._inputField = inputClass.create(qfield);
var inputFieldElement = this._inputField.element();
@@ -83,7 +94,7 @@
);
// Set Analyzer for context
- this._analyzer = KorAP.ContextAnalyzer.create(
+ this._analyzer = analyzerClass.create(
param["context"] || KorAP.context
);
return this;
@@ -124,10 +135,9 @@
return;
// Create matching hint menu
- this._menu[action] = KorAP.HintMenu.create(
+ this._menu[action] = menuClass.create(
this, action, KorAP.hintArray[action]
);
-
};
// Return matching hint menu
@@ -189,278 +199,4 @@
};
}
};
-
-
- // Input field for queries
- KorAP.InputField = {
- create : function (element) {
- return Object.create(KorAP.InputField)._init(element);
- },
-
- _init : function (element) {
- this._element = element;
-
- // Create mirror for searchField
- if ((this._mirror = document.getElementById("searchMirror")) === null) {
- this._mirror = document.createElement("div");
- this._mirror.setAttribute("id", "searchMirror");
- this._mirror.appendChild(document.createElement("span"));
- this._container = this._mirror.appendChild(document.createElement("div"));
- this._mirror.style.height = "0px";
- document.getElementsByTagName("body")[0].appendChild(this._mirror);
- };
-
- // Update position of the mirror
- var that = this;
- var repos = function () {
- that.reposition();
- };
- window.addEventListener('resize', repos);
- this._element.addEventListener('onfocus', repos);
- that.reposition();
-
- return this;
- },
-
- rightPos : function () {
- var box = this._mirror.firstChild.getBoundingClientRect();
- return box.right - box.left;
- },
-
- mirror : function () {
- return this._mirror;
- },
-
- container : function () {
- return this._container;
- },
-
- element : function () {
- return this._element;
- },
-
- value : function () {
- return this._element.value;
- },
-
- update : function () {
- this._mirror.firstChild.textContent = this.split()[0];
- this._container.style.left = this.rightPos() + 'px';
- },
-
- insert : function (text) {
- var splittedText = this.split();
- var s = this._element;
- s.value = splittedText[0] + text + splittedText[1];
- s.selectionStart = (splittedText[0] + text).length;
- s.selectionEnd = s.selectionStart;
- this._mirror.firstChild.textContent = splittedText[0] + text;
- },
-
- // Return two substrings, splitted at current cursor position
- split : function () {
- var s = this._element;
- var value = s.value;
- var start = s.selectionStart;
- return new Array(
- value.substring(0, start),
- value.substring(start, value.length)
- );
- },
-
- // Position the input mirror directly below the input box
- reposition : function () {
- var inputClientRect = this._element.getBoundingClientRect();
- var inputStyle = window.getComputedStyle(this._element, null);
-
- var bodyClientRect =
- document.getElementsByTagName('body')[0].getBoundingClientRect();
-
- // Reset position
- var mirrorStyle = this._mirror.style;
- mirrorStyle.left = inputClientRect.left + "px";
- mirrorStyle.top = (inputClientRect.bottom - bodyClientRect.top) + "px";
- mirrorStyle.width = inputStyle.getPropertyValue("width");
-
- // These may be relevant in case of media depending css
- mirrorStyle.paddingLeft = inputStyle.getPropertyValue("padding-left");
- mirrorStyle.marginLeft = inputStyle.getPropertyValue("margin-left");
- mirrorStyle.borderLeftWidth = inputStyle.getPropertyValue("border-left-width");
- mirrorStyle.borderLeftStyle = inputStyle.getPropertyValue("border-left-style");
- mirrorStyle.fontSize = inputStyle.getPropertyValue("font-size");
- mirrorStyle.fontFamily = inputStyle.getPropertyValue("font-family");
- },
- context : function () {
- return this.split()[0];
- }
- };
-
-
- /**
- * Regex object for checking the context of the hint
- */
- KorAP.ContextAnalyzer = {
- create : function (regex) {
- return Object.create(KorAP.ContextAnalyzer)._init(regex);
- },
- _init : function (regex) {
- try {
- this._regex = new RegExp(regex);
- }
- catch (e) {
- KorAP.log(0, e);
- return;
- };
- return this;
- },
- test : function (text) {
- if (!this._regex.exec(text))
- return;
- return RegExp.$1;
- }
- };
-
-
- /**
- * Hint menu
- */
- KorAP.HintMenu = {
- create : function (hint, context, params) {
- var obj = Object.create(KorAP.Menu)
- .upgradeTo(KorAP.HintMenu)
- ._init(KorAP.HintMenuItem, KorAP.HintMenuPrefix, params);
- obj._context = context;
- obj._element.classList.add('hint');
- obj._hint = hint;
-
- // This is only domspecific
- obj.element().addEventListener('blur', function (e) {
- this.menu.hide();
- });
-
- // Focus on input field on hide
- obj.onHide = function () {
- var input = this._hint.inputField();
- input.container().classList.remove('active');
- input.element().focus();
- };
-
- return obj;
- },
- // Todo: Is this necessary?
- context : function () {
- return this._context;
- },
- hint : function () {
- return this._hint;
- }
- };
-
-
- /**
- * Hint menu item based on MenuItem
- */
- KorAP.HintMenuItem = {
- create : function (params) {
- return Object.create(KorAP.MenuItem)
- .upgradeTo(KorAP.HintMenuItem)
- ._init(params);
- },
- _init : function (params) {
- if (params[0] === undefined ||
- params[1] === undefined)
- throw new Error("Missing parameters");
-
- this._name = params[0];
- this._action = params[1];
- this._lcField = ' ' + this._name.toLowerCase();
-
- if (params.length > 2) {
- this._desc = params[2];
- this._lcField += " " + this._desc.toLowerCase();
- };
-
- return this;
- },
- content : function (content) {
- if (arguments.length === 1) {
- this._content = content;
- };
- return this._content;
- },
- onclick : function () {
- var m = this.menu();
- var h = m.hint();
- m.hide();
-
- // Update input field
- var input = h.inputField();
- input.insert(this._action);
- input.update();
-
- h.active = false;
- h.show(true);
- },
- name : function () {
- return this._name;
- },
- action : function () {
- return this._action;
- },
- desc : function () {
- return this._desc;
- },
- element : function () {
- // already defined
- if (this._element !== undefined)
- return this._element;
-
- // Create list item
- var li = document.createElement("li");
-
- if (this.onclick !== undefined) {
- li["onclick"] = this.onclick.bind(this);
- };
-
- // Create title
- var name = document.createElement("span");
- name.appendChild(document.createTextNode(this._name));
-
- li.appendChild(name);
-
- // Create description
- if (this._desc !== undefined) {
- var desc = document.createElement("span");
- desc.classList.add('desc');
- desc.appendChild(document.createTextNode(this._desc));
- li.appendChild(desc);
- };
- return this._element = li;
- }
- };
-
- KorAP.HintMenuPrefix = {
- create : function (params) {
- return Object.create(KorAP.MenuPrefix).upgradeTo(KorAP.HintMenuPrefix)._init(params);
- },
- onclick : function () {
- var m = this.menu();
- var h = m.hint();
- m.hide();
-
- h.inputField().insert(this.value());
- h.active = false;
- }
- };
-
-
- /**
- * Return keycode based on event
- */
- function _codeFromEvent (e) {
- if ((e.charCode) && (e.keyCode==0))
- return e.charCode
- return e.keyCode;
- };
-
-}(this.KorAP));
+});
diff --git a/dev/js/src/hint/contextanalyzer.js b/dev/js/src/hint/contextanalyzer.js
new file mode 100644
index 0000000..bdbf885
--- /dev/null
+++ b/dev/js/src/hint/contextanalyzer.js
@@ -0,0 +1,23 @@
+/**
+ * Regex object for checking the context of the hint
+ */
+define({
+ create : function (regex) {
+ return Object.create(this)._init(regex);
+ },
+ _init : function (regex) {
+ try {
+ this._regex = new RegExp(regex);
+ }
+ catch (e) {
+ KorAP.log(0, e);
+ return;
+ };
+ return this;
+ },
+ test : function (text) {
+ if (!this._regex.exec(text))
+ return;
+ return RegExp.$1;
+ }
+});
diff --git a/dev/js/src/hint/input.js b/dev/js/src/hint/input.js
new file mode 100644
index 0000000..b1c6632
--- /dev/null
+++ b/dev/js/src/hint/input.js
@@ -0,0 +1,103 @@
+// Input field for queries
+define({
+ create : function (element) {
+ return Object.create(this)._init(element);
+ },
+
+ _init : function (element) {
+ this._element = element;
+
+ // Create mirror for searchField
+ if ((this._mirror = document.getElementById("searchMirror")) === null) {
+ this._mirror = document.createElement("div");
+ this._mirror.setAttribute("id", "searchMirror");
+ this._mirror.appendChild(document.createElement("span"));
+ this._container = this._mirror.appendChild(document.createElement("div"));
+ this._mirror.style.height = "0px";
+ document.getElementsByTagName("body")[0].appendChild(this._mirror);
+ };
+
+ // Update position of the mirror
+ var that = this;
+ var repos = function () {
+ that.reposition();
+ };
+ window.addEventListener('resize', repos);
+ this._element.addEventListener('onfocus', repos);
+ that.reposition();
+
+ return this;
+ },
+
+ rightPos : function () {
+ var box = this._mirror.firstChild.getBoundingClientRect();
+ return box.right - box.left;
+ },
+
+ mirror : function () {
+ return this._mirror;
+ },
+
+ container : function () {
+ return this._container;
+ },
+
+ element : function () {
+ return this._element;
+ },
+
+ value : function () {
+ return this._element.value;
+ },
+
+ update : function () {
+ this._mirror.firstChild.textContent = this.split()[0];
+ this._container.style.left = this.rightPos() + 'px';
+ },
+
+ insert : function (text) {
+ var splittedText = this.split();
+ var s = this._element;
+ s.value = splittedText[0] + text + splittedText[1];
+ s.selectionStart = (splittedText[0] + text).length;
+ s.selectionEnd = s.selectionStart;
+ this._mirror.firstChild.textContent = splittedText[0] + text;
+ },
+
+ // Return two substrings, splitted at current cursor position
+ split : function () {
+ var s = this._element;
+ var value = s.value;
+ var start = s.selectionStart;
+ return new Array(
+ value.substring(0, start),
+ value.substring(start, value.length)
+ );
+ },
+
+ // Position the input mirror directly below the input box
+ reposition : function () {
+ var inputClientRect = this._element.getBoundingClientRect();
+ var inputStyle = window.getComputedStyle(this._element, null);
+
+ var bodyClientRect =
+ document.getElementsByTagName('body')[0].getBoundingClientRect();
+
+ // Reset position
+ var mirrorStyle = this._mirror.style;
+ mirrorStyle.left = inputClientRect.left + "px";
+ mirrorStyle.top = (inputClientRect.bottom - bodyClientRect.top) + "px";
+ mirrorStyle.width = inputStyle.getPropertyValue("width");
+
+ // These may be relevant in case of media depending css
+ mirrorStyle.paddingLeft = inputStyle.getPropertyValue("padding-left");
+ mirrorStyle.marginLeft = inputStyle.getPropertyValue("margin-left");
+ mirrorStyle.borderLeftWidth = inputStyle.getPropertyValue("border-left-width");
+ mirrorStyle.borderLeftStyle = inputStyle.getPropertyValue("border-left-style");
+ mirrorStyle.fontSize = inputStyle.getPropertyValue("font-size");
+ mirrorStyle.fontFamily = inputStyle.getPropertyValue("font-family");
+ },
+ context : function () {
+ return this.split()[0];
+ }
+});
diff --git a/dev/js/src/hint/item.js b/dev/js/src/hint/item.js
new file mode 100644
index 0000000..f23cabc
--- /dev/null
+++ b/dev/js/src/hint/item.js
@@ -0,0 +1,85 @@
+/**
+ * Hint menu item based on MenuItem
+ */
+define(['menu/item'], function (itemClass) {
+ return {
+ create : function (params) {
+ return Object.create(itemClass)
+ .upgradeTo(this)
+ ._init(params);
+ },
+ _init : function (params) {
+ if (params[0] === undefined ||
+ params[1] === undefined)
+ throw new Error("Missing parameters");
+
+ this._name = params[0];
+ this._action = params[1];
+ this._lcField = ' ' + this._name.toLowerCase();
+
+ if (params.length > 2) {
+ this._desc = params[2];
+ this._lcField += " " + this._desc.toLowerCase();
+ };
+
+ return this;
+ },
+
+ content : function (content) {
+ if (arguments.length === 1) {
+ this._content = content;
+ };
+ return this._content;
+ },
+
+ onclick : function () {
+ var m = this.menu();
+ var h = m.hint();
+ m.hide();
+
+ // Update input field
+ var input = h.inputField();
+ input.insert(this._action);
+ input.update();
+
+ h.active = false;
+ h.show(true);
+ },
+ name : function () {
+ return this._name;
+ },
+ action : function () {
+ return this._action;
+ },
+ desc : function () {
+ return this._desc;
+ },
+ element : function () {
+ // already defined
+ if (this._element !== undefined)
+ return this._element;
+
+ // Create list item
+ var li = document.createElement("li");
+
+ if (this.onclick !== undefined) {
+ li["onclick"] = this.onclick.bind(this);
+ };
+
+ // Create title
+ var name = document.createElement("span");
+ name.appendChild(document.createTextNode(this._name));
+
+ li.appendChild(name);
+
+ // Create description
+ if (this._desc !== undefined) {
+ var desc = document.createElement("span");
+ desc.classList.add('desc');
+ desc.appendChild(document.createTextNode(this._desc));
+ li.appendChild(desc);
+ };
+ return this._element = li;
+ }
+ };
+});
diff --git a/dev/js/src/hint/menu.js b/dev/js/src/hint/menu.js
new file mode 100644
index 0000000..c579945
--- /dev/null
+++ b/dev/js/src/hint/menu.js
@@ -0,0 +1,36 @@
+/**
+ * Hint menu
+ */
+define(['menu', 'hint/item', 'hint/prefix'], function (menuClass, itemClass, prefixClass) {
+ return {
+ create : function (hint, context, params) {
+ var obj = Object.create(menuClass)
+ .upgradeTo(this)
+ ._init(itemClass, prefixClass, params);
+ obj._context = context;
+ obj._element.classList.add('hint');
+ obj._hint = hint;
+
+ // This is only domspecific
+ obj.element().addEventListener('blur', function (e) {
+ this.menu.hide();
+ });
+
+ // Focus on input field on hide
+ obj.onHide = function () {
+ var input = this._hint.inputField();
+ input.container().classList.remove('active');
+ input.element().focus();
+ };
+
+ return obj;
+ },
+ // Todo: Is this necessary?
+ context : function () {
+ return this._context;
+ },
+ hint : function () {
+ return this._hint;
+ }
+ };
+});
diff --git a/dev/js/src/hint/prefix.js b/dev/js/src/hint/prefix.js
new file mode 100644
index 0000000..84f785a
--- /dev/null
+++ b/dev/js/src/hint/prefix.js
@@ -0,0 +1,15 @@
+define(['menu/prefix'], function (prefixClass) {
+ return {
+ create : function (params) {
+ return Object.create(prefixClass).upgradeTo(this)._init(params);
+ },
+ onclick : function () {
+ var m = this.menu();
+ var h = m.hint();
+ m.hide();
+
+ h.inputField().insert(this.value());
+ h.active = false;
+ }
+ };
+});
diff --git a/dev/js/src/init.js b/dev/js/src/init.js
index ecaad6f..66bc081 100644
--- a/dev/js/src/init.js
+++ b/dev/js/src/init.js
@@ -1,42 +1,17 @@
-/**
- * These are utility functions for the frontend
- */
-
-// Add toggleClass method similar to jquery
-HTMLElement.prototype.toggleClass = function (c1, c2) {
- var cl = this.classList;
- if (cl.contains(c1)) {
- cl.add(c2);
- cl.remove(c1);
- }
- else {
- cl.remove(c2);
- cl.add(c1);
- };
-};
-
-
-// Don't let events bubble up
-if (Event.halt === undefined) {
- // Don't let events bubble up
- Event.prototype.halt = function () {
- this.stopPropagation();
- this.preventDefault();
- };
-};
-
-var KorAP = KorAP || {};
-
-
-(function (KorAP) {
- "use strict";
-
-
- /**
- * Initialize user interface elements
- */
- KorAP.init = function () {
- var obj = Object.create(KorAP.init);
+define([
+ 'match',
+ 'hint',
+ 'vc',
+ 'tutorial',
+ 'lib/domReady',
+ 'util'
+], function (matchClass,
+ hintClass,
+ vcClass,
+ tutClass,
+ domReady) {
+ domReady(function (event) {
+ var obj = {};
/**
* Add actions to match entries
@@ -49,13 +24,13 @@
inactiveLi[i].addEventListener('click', function (e) {
if (this._match !== undefined)
this._match.open();
- else
- KorAP.Match.create(this).open();
+ else {
+ matchClass.create(this).open();
+ };
e.halt();
});
};
-
/**
* Toggle the alignment (left <=> right)
*/
@@ -97,28 +72,29 @@
input.parentNode.insertBefore(vcname, input);
vcname.onclick = function () {
- var vc = KorAP.VirtualCollection.render(vcExample);
+ var vc = vcClass.render(vcExample);
var view = document.getElementById('vc-view');
view.appendChild(vc.element());
};
};
+
/**
* Init Tutorial view
*/
- obj.tutorial = KorAP.Tutorial.create(
+ obj.tutorial = tutClass.create(
document.getElementById('view-tutorial')
);
+
/**
* Init hint helper
* has to be final because of
* reposition
*/
-// Todo: Pass an element, so this works with
-// tutorial pages as well!
- obj.hint = KorAP.Hint.create();
+ // Todo: Pass an element, so this works with
+ // tutorial pages as well!
+ obj.hint = hintClass.create();
return obj;
- };
-
-}(this.KorAP));
+ });
+});
diff --git a/dev/js/src/match.js b/dev/js/src/match.js
index 4e3786b..beb4bca 100644
--- a/dev/js/src/match.js
+++ b/dev/js/src/match.js
@@ -4,46 +4,31 @@
*
* @author Nils Diewald
*/
-// require menu.js, dagre
/*
* - Highlight (at least mark as bold) the match
* - Scroll to match vertically per default
*/
-var KorAP = KorAP || {};
-
-(function (KorAP) {
- "use strict";
-
- var svgXmlns = "http://www.w3.org/2000/svg";
-
- // Default log message
- KorAP.log = KorAP.log || function (type, msg) {
- console.log(type + ": " + msg);
- };
+define([
+ 'match/infolayer',
+ 'match/info',
+ 'util'
+], function (infoLayerClass,
+ infoClass) {
// Localization values
- var loc = (KorAP.Locale = KorAP.Locale || {} );
+ var loc = KorAP.Locale;
loc.ADDTREE = loc.ADDTREE || 'Add tree view';
loc.SHOWINFO = loc.SHOWINFO || 'Show information';
loc.CLOSE = loc.CLOSE || 'Close';
-
- KorAP._AvailableRE = new RegExp("^([^\/]+?)\/([^=]+?)(?:=(spans|rels|tokens))?$");
- KorAP._TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
- KorAP._matchTerms = ['corpusID', 'docID', 'textID', 'matchID', 'available'];
-
- // API requests
- KorAP.API = KorAP.API || {};
-
- // TODO: Make this async
- KorAP.API.getMatchInfo = KorAP.API.getMatchInfo || function () {
- KorAP.log(0, 'KorAP.API.getMatchInfo() not implemented')
- return {};
- };
+
+ // KorAP._AvailableRE = new RegExp("^([^\/]+?)\/([^=]+?)(?:=(spans|rels|tokens))?$");
+ // KorAP._TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
+ var _matchTerms = ['corpusID', 'docID', 'textID', 'matchID', 'available'];
/**
* Match object
*/
- KorAP.Match = {
+ return {
/**
* Create a new annotation object.
@@ -51,7 +36,7 @@
* Supported types are 'spans', 'tokens' and 'rels'.
*/
create : function (match) {
- return Object.create(KorAP.Match)._init(match);
+ return Object.create(this)._init(match);
},
/**
@@ -87,8 +72,8 @@
else {
// Iterate over allowed match terms
- for (var i in KorAP._matchTerms) {
- var term = KorAP._matchTerms[i];
+ for (var i in _matchTerms) {
+ var term = _matchTerms[i];
if (match[term] !== undefined) {
this[term] = match[term];
}
@@ -97,7 +82,7 @@
}
};
};
-
+
this._available = {
tokens : [],
spans : [],
@@ -110,7 +95,7 @@
// Create info layer objects
try {
- var layer = KorAP.InfoLayer.create(term);
+ var layer = infoLayerClass.create(term);
this._available[layer.type].push(layer);
}
catch (e) {
@@ -178,7 +163,7 @@
.appendChild(document.createTextNode(loc.CLOSE));
close.classList.add('close');
close.setAttribute('title', loc.CLOSE);
-
+
// Add info button
var info = document.createElement('li');
info.appendChild(document.createElement('span'))
@@ -227,7 +212,7 @@
// Create match info
if (this._info === undefined)
- this._info = KorAP.MatchInfo.create(this);
+ this._info = infoClass.create(this);
// There is an element to append
if (this._element === undefined ||
@@ -251,812 +236,4 @@
return this._element;
}
};
-
-
-
- /**
- * Information about a match.
- */
- KorAP.MatchInfo = {
-
- /**
- * Create new object
- */
- create : function (match) {
- return Object.create(KorAP.MatchInfo)._init(match);
- },
-
- /**
- * Initialize object
- */
- _init : function (match) {
- this._match = match;
- this.opened = false;
- return this;
- },
-
- /**
- * Get match object
- */
- match : function () {
- return this._match;
- },
-
- toggle : function () {
- if (this.opened == true) {
- this._match.element().children[0].removeChild(
- this.element()
- );
- this.opened = false;
- }
- else {
- // Append element to match
- this._match.element().children[0].appendChild(
- this.element()
- );
- this.opened = true;
- };
-
- return this.opened;
- },
-
-
- /**
- * Retrieve and parse snippet for table representation
- */
- getTable : function (tokens, cb) {
- var focus = [];
-
- // Get all tokens
- if (tokens === undefined) {
- focus = this._match.getTokens();
- }
-
- // Get only some tokens
- else {
-
- // Push newly to focus array
- for (var i = 0; i < tokens.length; i++) {
- var term = tokens[i];
- try {
- // Create info layer objects
- var layer = KorAP.InfoLayer.create(term);
- layer.type = "tokens";
- focus.push(layer);
- }
- catch (e) {
- continue;
- };
- };
- };
-
- // No tokens chosen
- if (focus.length == 0)
- cb(null);
-
- // Get info (may be cached)
- // TODO: Async
- KorAP.API.getMatchInfo(
- this._match,
- { 'spans' : false, 'layer' : focus },
-
- // Callback for retrieval
- function (matchResponse) {
- // Get snippet from match info
- if (matchResponse["snippet"] !== undefined) {
- this._table = KorAP.MatchTable.create(matchResponse["snippet"]);
- cb(this._table);
- };
- }.bind(this)
- );
-
-/*
- // Todo: Store the table as a hash of the focus
- return null;
-*/
- },
-
-
- /**
- * Retrieve and parse snippet for tree representation
- */
- getTree : function (foundry, layer, cb) {
- var focus = [];
-
- // TODO: Support and cache multiple trees
-
- KorAP.API.getMatchInfo(
- this._match, {
- 'spans' : true,
- 'foundry' : foundry,
- 'layer' : layer
- },
- function (matchResponse) {
- // Get snippet from match info
- if (matchResponse["snippet"] !== undefined) {
- // Todo: This should be cached somehow
- cb(KorAP.MatchTree.create(matchResponse["snippet"]));
- }
- else {
- cb(null);
- };
- }.bind(this)
- );
- },
-
- /**
- * Destroy this match information view.
- */
- destroy : function () {
-
- // Remove circular reference
- if (this._treeMenu !== undefined)
- delete this._treeMenu["info"];
-
- this._treeMenu.destroy();
- this._treeMenu = undefined;
- this._match = undefined;
-
- // Element destroy
- },
-
-
- /**
- * Add a new tree view to the list
- */
- addTree : function (foundry, layer, cb) {
- var matchtree = document.createElement('div');
- matchtree.classList.add('matchtree');
-
- var h6 = matchtree.appendChild(document.createElement('h6'));
- h6.appendChild(document.createElement('span'))
- .appendChild(document.createTextNode(foundry));
- h6.appendChild(document.createElement('span'))
- .appendChild(document.createTextNode(layer));
-
- var tree = matchtree.appendChild(
- document.createElement('div')
- );
-
- this._element.insertBefore(matchtree, this._element.lastChild);
-
- var close = tree.appendChild(document.createElement('em'));
- close.addEventListener(
- 'click', function (e) {
- matchtree.parentNode.removeChild(matchtree);
- e.halt();
- }
- );
-
- // Get tree data async
- this.getTree(foundry, layer, function (treeObj) {
- // Something went wrong - probably log!!!
- if (treeObj === null) {
- tree.appendChild(document.createTextNode('No data available.'));
- }
- else {
- tree.appendChild(treeObj.element());
- // Reposition the view to the center
- // (This may in a future release be a reposition
- // to move the root into the center or the actual
- // match)
- treeObj.center();
- }
-
- if (cb !== undefined)
- cb(treeObj);
- });
- },
-
- /**
- * Create match information view.
- */
- element : function () {
-
- if (this._element !== undefined)
- return this._element;
-
- // Create info table
- var info = document.createElement('div');
- info.classList.add('matchinfo');
-
- // Append default table
- var matchtable = document.createElement('div');
- matchtable.classList.add('matchtable');
- info.appendChild(matchtable);
-
- // Create the table asynchronous
- this.getTable(undefined, function (table) {
- if (table !== null) {
- matchtable.appendChild(table.element());
- };
- });
-
- // Get spans
- var spanLayers = this._match.getSpans().sort(
- function (a, b) {
- if (a.foundry < b.foundry) {
- return -1;
- }
- else if (a.foundry > b.foundry) {
- return 1;
- }
- else if (a.layer < b.layer) {
- return -1;
- }
- else if (a.layer > b.layer) {
- return 1;
- };
- return 0;
- });
-
- var menuList = [];
-
- // Show tree views
- for (var i = 0; i < spanLayers.length; i++) {
- var span = spanLayers[i];
-
- // Add foundry/layer to menu list
- menuList.push([
- span.foundry + '/' + span.layer,
- span.foundry,
- span.layer
- ]);
- };
-
- // Create tree menu
- var treemenu = this.treeMenu(menuList);
- var span = info.appendChild(document.createElement('p'));
- span.classList.add('addtree');
- span.appendChild(document.createTextNode(loc.ADDTREE));
-
- var treeElement = treemenu.element();
- span.appendChild(treeElement);
-
- span.addEventListener('click', function (e) {
- treemenu.show('');
- treemenu.focus();
- });
-
- this._element = info;
-
- return info;
-
- },
-
-
- /**
- * Get tree menu.
- * There is only one menu rendered
- * - no matter how many trees exist
- */
- treeMenu : function (list) {
- if (this._treeMenu !== undefined)
- return this._treeMenu;
-
- return this._treeMenu = KorAP.MatchTreeMenu.create(this, list);
- }
- };
-
-
-
- /**
- *
- * Alternatively pass a string as <tt>base/s=span</tt>
- *
- * @param foundry
- */
- KorAP.InfoLayer = {
- create : function (foundry, layer, type) {
- return Object.create(KorAP.InfoLayer)._init(foundry, layer, type);
- },
- _init : function (foundry, layer, type) {
- if (foundry === undefined)
- throw new Error("Missing parameters");
-
- if (layer === undefined) {
- if (KorAP._AvailableRE.exec(foundry)) {
- this.foundry = RegExp.$1;
- this.layer = RegExp.$2;
- this.type = RegExp.$3;
- }
- else {
- throw new Error("Missing parameters");
- };
- }
- else {
- this.foundry = foundry;
- this.layer = layer;
- this.type = type;
- };
-
- if (this.type === undefined)
- this.type = 'tokens';
-
- return this;
- }
- };
-
-
- KorAP.MatchTable = {
- create : function (snippet) {
- return Object.create(KorAP.MatchTable)._init(snippet);
- },
- _init : function (snippet) {
- // Create html for traversal
- var html = document.createElement("div");
- html.innerHTML = snippet;
-
- this._pos = 0;
- this._token = [];
- this._info = [];
- this._foundry = {};
- this._layer = {};
-
- // Parse the snippet
- this._parse(html.childNodes);
-
- html.innerHTML = '';
- return this;
- },
-
- length : function () {
- return this._pos;
- },
-
- getToken : function (pos) {
- if (pos === undefined)
- return this._token;
- return this._token[pos];
- },
-
- getValue : function (pos, foundry, layer) {
- return this._info[pos][foundry + '/' + layer]
- },
-
- getLayerPerFoundry : function (foundry) {
- return this._foundry[foundry]
- },
-
- getFoundryPerLayer : function (layer) {
- return this._layer[layer];
- },
-
- // Parse the snippet
- _parse : function (children) {
-
- // Get all children
- for (var i in children) {
- var c = children[i];
-
- // Create object on position unless it exists
- if (this._info[this._pos] === undefined)
- this._info[this._pos] = {};
-
- // Store at position in foundry/layer as array
- var found = this._info[this._pos];
-
- // Element with title
- if (c.nodeType === 1) {
- if (c.getAttribute("title") &&
- KorAP._TermRE.exec(c.getAttribute("title"))) {
-
- // Fill position with info
- var foundry, layer, value;
- if (RegExp.$2) {
- foundry = RegExp.$1;
- layer = RegExp.$2;
- }
- else {
- foundry = "base";
- layer = RegExp.$1
- };
-
- value = RegExp.$3;
-
- if (found[foundry + "/" + layer] === undefined)
- found[foundry + "/" + layer] = [];
-
- // Push value to foundry/layer at correct position
- found[foundry + "/" + layer].push(RegExp.$3);
-
- // Set foundry
- if (this._foundry[foundry] === undefined)
- this._foundry[foundry] = {};
- this._foundry[foundry][layer] = 1;
-
- // Set layer
- if (this._layer[layer] === undefined)
- this._layer[layer] = {};
- this._layer[layer][foundry] = 1;
- };
-
- // depth search
- if (c.hasChildNodes())
- this._parse(c.childNodes);
- }
-
- // Leaf node
- // store string on position and go to next string
- else if (c.nodeType === 3) {
- if (c.nodeValue.match(/[a-z0-9]/i))
- this._token[this._pos++] = c.nodeValue;
- };
- };
-
- delete this._info[this._pos];
- },
-
-
- /**
- * Get HTML table view of annotations.
- */
- element : function () {
- if (this._element !== undefined)
- return this._element;
-
- // First the legend table
- var d = document;
- var table = d.createElement('table');
-
- // Single row in head
- var tr = table.appendChild(d.createElement('thead'))
- .appendChild(d.createElement('tr'));
-
- // Add cell to row
- var addCell = function (type, name) {
- var c = this.appendChild(d.createElement(type))
- if (name === undefined)
- return c;
-
- if (name instanceof Array) {
- for (var n = 0; n < name.length; n++) {
- c.appendChild(d.createTextNode(name[n]));
- if (n !== name.length - 1) {
- c.appendChild(d.createElement('br'));
- };
- };
- }
- else {
- c.appendChild(d.createTextNode(name));
- };
- };
-
- tr.addCell = addCell;
-
- // Add header information
- tr.addCell('th', 'Foundry');
- tr.addCell('th', 'Layer');
-
- // Add tokens
- for (var i in this._token) {
- tr.addCell('th', this.getToken(i));
- };
-
- var tbody = table.appendChild(
- d.createElement('tbody')
- );
-
- var foundryList = Object.keys(this._foundry).sort();
-
- for (var f = 0; f < foundryList.length; f++) {
- var foundry = foundryList[f];
- var layerList =
- Object.keys(this._foundry[foundry]).sort();
-
- for (var l = 0; l < layerList.length; l++) {
- var layer = layerList[l];
- tr = tbody.appendChild(
- d.createElement('tr')
- );
- tr.setAttribute('tabindex', 0);
- tr.addCell = addCell;
-
- tr.addCell('th', foundry);
- tr.addCell('th', layer);
-
- for (var v = 0; v < this.length(); v++) {
- tr.addCell(
- 'td',
- this.getValue(v, foundry, layer)
- );
- };
- };
- };
-
- return this._element = table;
- }
- };
-
-
- /**
- * Visualize span annotations as a tree using Dagre.
- */
- KorAP.MatchTree = {
-
- create : function (snippet) {
- return Object.create(KorAP.MatchTree)._init(snippet);
- },
-
- nodes : function () {
- return this._next;
- },
-
- _addNode : function (id, obj) {
- obj["width"] = 55;
- obj["height"] = 20;
- this._graph.setNode(id, obj)
- },
-
- _addEdge : function (src, target) {
- this._graph.setEdge(src, target);
- },
-
- _init : function (snippet) {
- this._next = new Number(0);
-
- // Create html for traversal
- var html = document.createElement("div");
- html.innerHTML = snippet;
- var g = new dagre.graphlib.Graph({
- "directed" : true
- });
- g.setGraph({
- "nodesep" : 35,
- "ranksep" : 15,
- "marginx" : 40,
- "marginy" : 10
- });
- g.setDefaultEdgeLabel({});
-
- this._graph = g;
-
- // This is a new root
- this._addNode(
- this._next++,
- { "class" : "root" }
- );
-
- // Parse nodes from root
- this._parse(0, html.childNodes);
-
- // Root node has only one child - remove
- if (g.outEdges(0).length === 1)
- g.removeNode(0);
-
- html = undefined;
- return this;
- },
-
- // Remove foundry and layer for labels
- _clean : function (title) {
- return title.replace(KorAP._TermRE, "$3");
- },
-
- // Parse the snippet
- _parse : function (parent, children) {
- for (var i in children) {
- var c = children[i];
-
- // Element node
- if (c.nodeType == 1) {
-
- // Get title from html
- if (c.getAttribute("title")) {
- var title = this._clean(c.getAttribute("title"));
-
- // Add child node
- var id = this._next++;
-
- this._addNode(id, {
- "class" : "middle",
- "label" : title
- });
- this._addEdge(parent, id);
-
- // Check for next level
- if (c.hasChildNodes())
- this._parse(id, c.childNodes);
- }
-
- // Step further
- else if (c.hasChildNodes())
- this._parse(parent, c.childNodes);
- }
-
- // Text node
- else if (c.nodeType == 3)
-
- if (c.nodeValue.match(/[-a-z0-9]/i)) {
-
- // Add child node
- var id = this._next++;
- this._addNode(id, {
- "class" : "leaf",
- "label" : c.nodeValue
- });
-
- this._addEdge(parent, id);
- };
- };
- return this;
- },
-
- /**
- * Center the viewport of the canvas
- */
- center : function () {
- if (this._element === undefined)
- return;
-
- var treeDiv = this._element.parentNode;
-
- var cWidth = parseFloat(window.getComputedStyle(this._element).width);
- var treeWidth = parseFloat(window.getComputedStyle(treeDiv).width);
- // Reposition:
- if (cWidth > treeWidth) {
- var scrollValue = (cWidth - treeWidth) / 2;
- treeDiv.scrollLeft = scrollValue;
- };
- },
-
- // Get element
- element : function () {
- if (this._element !== undefined)
- return this._element;
-
- var g = this._graph;
-
- dagre.layout(g);
-
- var canvas = document.createElementNS(svgXmlns, 'svg');
- this._element = canvas;
-
- canvas.setAttribute('height', g.graph().height);
- canvas.setAttribute('width', g.graph().width);
-
- // Create edges
- g.edges().forEach(
- function (e) {
- var src = g.node(e.v);
- var target = g.node(e.w);
- var p = document.createElementNS(svgXmlns, 'path');
- p.setAttributeNS(null, "d", _line(src, target));
- p.classList.add('edge');
- canvas.appendChild(p);
- });
-
- // Create nodes
- g.nodes().forEach(
- function (v) {
- v = g.node(v);
- var group = document.createElementNS(svgXmlns, 'g');
- group.classList.add(v.class);
-
- // Add node box
- var rect = group.appendChild(document.createElementNS(svgXmlns, 'rect'));
- rect.setAttributeNS(null, 'x', v.x - v.width / 2);
- rect.setAttributeNS(null, 'y', v.y - v.height / 2);
- rect.setAttributeNS(null, 'rx', 5);
- rect.setAttributeNS(null, 'ry', 5);
- rect.setAttributeNS(null, 'width', v.width);
- rect.setAttributeNS(null, 'height', v.height);
-
- if (v.class === 'root' && v.label === undefined) {
- rect.setAttributeNS(null, 'width', v.height);
- rect.setAttributeNS(null, 'x', v.x - v.height / 2);
- rect.setAttributeNS(null, 'class', 'empty');
- };
-
- // Add label
- if (v.label !== undefined) {
- var text = group.appendChild(document.createElementNS(svgXmlns, 'text'));
- text.setAttributeNS(null, 'x', v.x - v.width / 2);
- text.setAttributeNS(null, 'y', v.y - v.height / 2);
- text.setAttributeNS(
- null,
- 'transform',
- 'translate(' + v.width/2 + ',' + ((v.height / 2) + 5) + ')'
- );
-
- var tspan = document.createElementNS(svgXmlns, 'tspan');
- tspan.appendChild(document.createTextNode(v.label));
- text.appendChild(tspan);
- };
-
- canvas.appendChild(group);
- }
- );
-
- return this._element;
- }
- };
-
- /**
- * Menu item for tree view choice.
- */
- KorAP.MatchTreeItem = {
- create : function (params) {
- return Object.create(KorAP.MenuItem)
- .upgradeTo(KorAP.MatchTreeItem)._init(params);
- },
- content : function (content) {
- if (arguments.length === 1) {
- this._content = content;
- };
- return this._content;
- },
-
- // The foundry attribute
- foundry : function () {
- return this._foundry;
- },
-
- // The layer attribute
- layer : function () {
- return this._layer;
- },
-
- // enter or click
- onclick : function (e) {
- var menu = this.menu();
- menu.hide();
- e.halt();
- if (menu.info() !== undefined)
- menu.info().addTree(this._foundry, this._layer);
- },
-
- _init : function (params) {
- if (params[0] === undefined)
- throw new Error("Missing parameters");
-
- this._name = params[0];
- this._foundry = params[1];
- this._layer = params[2];
- this._content = document.createTextNode(this._name);
- this._lcField = ' ' + this.content().textContent.toLowerCase();
- return this;
- }
- };
-
-
- /**
- * Menu to choose from for tree views.
- */
- KorAP.MatchTreeMenu = {
- create : function (info, params) {
- var obj = Object.create(KorAP.Menu)
- .upgradeTo(KorAP.MatchTreeMenu)
- ._init(KorAP.MatchTreeItem, undefined, params);
- obj.limit(6);
-
- obj._info = info;
-
- // This is only domspecific
- obj.element().addEventListener('blur', function (e) {
- this.menu.hide();
- });
-
- return obj;
- },
- info :function () {
- return this._info;
- }
- };
-
-
- // Create path for node connections
- function _line (src, target) {
- var x1 = src.x,
- y1 = src.y,
- x2 = target.x,
- y2 = target.y - target.height / 2;
-
- // c 0,0 -10,0
- return 'M ' + x1 + ',' + y1 + ' ' +
- 'C ' + x1 + ',' + y1 + ' ' +
- x2 + ',' + (y2 - (y2 - y1) / 2) + ' ' +
- x2 + ',' + y2;
- };
-
-}(this.KorAP));
+});
diff --git a/dev/js/src/match/info.js b/dev/js/src/match/info.js
new file mode 100644
index 0000000..b24bed5
--- /dev/null
+++ b/dev/js/src/match/info.js
@@ -0,0 +1,290 @@
+ /**
+ * Information about a match.
+ */
+define(['match/infolayer','match/table','match/tree', 'match/treemenu', 'util'], function (infoLayerClass, matchTableClass, matchTreeClass, matchTreeMenuClass) {
+
+ // TODO: Make this async
+ KorAP.API.getMatchInfo = KorAP.API.getMatchInfo || function () {
+ KorAP.log(0, 'KorAP.API.getMatchInfo() not implemented')
+ return {};
+ };
+
+ var loc = KorAP.Locale;
+
+ /**
+ * Create new object
+ */
+ return {
+ create : function (match) {
+ return Object.create(this)._init(match);
+ },
+
+ /**
+ * Initialize object
+ */
+ _init : function (match) {
+ this._match = match;
+ this.opened = false;
+ return this;
+ },
+
+ /**
+ * Get match object
+ */
+ match : function () {
+ return this._match;
+ },
+
+ toggle : function () {
+ if (this.opened == true) {
+ this._match.element().children[0].removeChild(
+ this.element()
+ );
+ this.opened = false;
+ }
+ else {
+ // Append element to match
+ this._match.element().children[0].appendChild(
+ this.element()
+ );
+ this.opened = true;
+ };
+
+ return this.opened;
+ },
+
+
+ /**
+ * Retrieve and parse snippet for table representation
+ */
+ getTable : function (tokens, cb) {
+ var focus = [];
+
+ // Get all tokens
+ if (tokens === undefined) {
+ focus = this._match.getTokens();
+ }
+
+ // Get only some tokens
+ else {
+
+ // Push newly to focus array
+ for (var i = 0; i < tokens.length; i++) {
+ var term = tokens[i];
+ try {
+ // Create info layer objects
+ var layer = infoLayerClass.create(term);
+ layer.type = "tokens";
+ focus.push(layer);
+ }
+ catch (e) {
+ continue;
+ };
+ };
+ };
+
+ // No tokens chosen
+ if (focus.length == 0)
+ cb(null);
+
+ // Get info (may be cached)
+ // TODO: Async
+ KorAP.API.getMatchInfo(
+ this._match,
+ { 'spans' : false, 'layer' : focus },
+
+ // Callback for retrieval
+ function (matchResponse) {
+ // Get snippet from match info
+ if (matchResponse["snippet"] !== undefined) {
+ this._table = matchTableClass.create(matchResponse["snippet"]);
+ cb(this._table);
+ };
+ }.bind(this)
+ );
+
+ /*
+ // Todo: Store the table as a hash of the focus
+ return null;
+ */
+ },
+
+
+ /**
+ * Retrieve and parse snippet for tree representation
+ */
+ getTree : function (foundry, layer, cb) {
+ var focus = [];
+
+ // TODO: Support and cache multiple trees
+ KorAP.API.getMatchInfo(
+ this._match, {
+ 'spans' : true,
+ 'foundry' : foundry,
+ 'layer' : layer
+ },
+ function (matchResponse) {
+ // Get snippet from match info
+ if (matchResponse["snippet"] !== undefined) {
+ // Todo: This should be cached somehow
+ cb(matchTreeClass.create(matchResponse["snippet"]));
+ }
+ else {
+ cb(null);
+ };
+ }.bind(this)
+ );
+ },
+
+ /**
+ * Destroy this match information view.
+ */
+ destroy : function () {
+
+ // Remove circular reference
+ if (this._treeMenu !== undefined)
+ delete this._treeMenu["info"];
+
+ this._treeMenu.destroy();
+ this._treeMenu = undefined;
+ this._match = undefined;
+
+ // Element destroy
+ },
+
+ /**
+ * Add a new tree view to the list
+ */
+ addTree : function (foundry, layer, cb) {
+ var matchtree = document.createElement('div');
+ matchtree.classList.add('matchtree');
+
+ var h6 = matchtree.appendChild(document.createElement('h6'));
+ h6.appendChild(document.createElement('span'))
+ .appendChild(document.createTextNode(foundry));
+ h6.appendChild(document.createElement('span'))
+ .appendChild(document.createTextNode(layer));
+
+ var tree = matchtree.appendChild(
+ document.createElement('div')
+ );
+
+ this._element.insertBefore(matchtree, this._element.lastChild);
+
+ var close = tree.appendChild(document.createElement('em'));
+ close.addEventListener(
+ 'click', function (e) {
+ matchtree.parentNode.removeChild(matchtree);
+ e.halt();
+ }
+ );
+
+ // Get tree data async
+ this.getTree(foundry, layer, function (treeObj) {
+ // Something went wrong - probably log!!!
+ if (treeObj === null) {
+ tree.appendChild(document.createTextNode('No data available.'));
+ }
+ else {
+ tree.appendChild(treeObj.element());
+ // Reposition the view to the center
+ // (This may in a future release be a reposition
+ // to move the root into the center or the actual
+ // match)
+ treeObj.center();
+ }
+
+ if (cb !== undefined)
+ cb(treeObj);
+ });
+ },
+
+ /**
+ * Create match information view.
+ */
+ element : function () {
+
+ if (this._element !== undefined)
+ return this._element;
+
+ // Create info table
+ var info = document.createElement('div');
+ info.classList.add('matchinfo');
+
+ // Append default table
+ var matchtable = document.createElement('div');
+ matchtable.classList.add('matchtable');
+ info.appendChild(matchtable);
+
+ // Create the table asynchronous
+ this.getTable(undefined, function (table) {
+ if (table !== null) {
+ matchtable.appendChild(table.element());
+ };
+ });
+
+ // Get spans
+ var spanLayers = this._match.getSpans().sort(
+ function (a, b) {
+ if (a.foundry < b.foundry) {
+ return -1;
+ }
+ else if (a.foundry > b.foundry) {
+ return 1;
+ }
+ else if (a.layer < b.layer) {
+ return -1;
+ }
+ else if (a.layer > b.layer) {
+ return 1;
+ };
+ return 0;
+ });
+
+ var menuList = [];
+
+ // Show tree views
+ for (var i = 0; i < spanLayers.length; i++) {
+ var span = spanLayers[i];
+
+ // Add foundry/layer to menu list
+ menuList.push([
+ span.foundry + '/' + span.layer,
+ span.foundry,
+ span.layer
+ ]);
+ };
+
+ // Create tree menu
+ var treemenu = this.treeMenu(menuList);
+ var span = info.appendChild(document.createElement('p'));
+ span.classList.add('addtree');
+ span.appendChild(document.createTextNode(loc.ADDTREE));
+
+ var treeElement = treemenu.element();
+ span.appendChild(treeElement);
+
+ span.addEventListener('click', function (e) {
+ treemenu.show('');
+ treemenu.focus();
+ });
+
+ this._element = info;
+
+ return info;
+ },
+
+
+ /**
+ * Get tree menu.
+ * There is only one menu rendered
+ * - no matter how many trees exist
+ */
+ treeMenu : function (list) {
+ if (this._treeMenu !== undefined)
+ return this._treeMenu;
+
+ return this._treeMenu = matchTreeMenuClass.create(this, list);
+ }
+ };
+});
diff --git a/dev/js/src/match/infolayer.js b/dev/js/src/match/infolayer.js
new file mode 100644
index 0000000..dbd93f9
--- /dev/null
+++ b/dev/js/src/match/infolayer.js
@@ -0,0 +1,41 @@
+/**
+ *
+ * Alternatively pass a string as <tt>base/s=span</tt>
+ *
+ * @param foundry
+ */
+define(function () {
+ var _AvailableRE = new RegExp("^([^\/]+?)\/([^=]+?)(?:=(spans|rels|tokens))?$");
+
+ return {
+ create : function (foundry, layer, type) {
+ return Object.create(this)._init(foundry, layer, type);
+ },
+ _init : function (foundry, layer, type) {
+ if (foundry === undefined)
+ throw new Error("Missing parameters");
+
+ if (layer === undefined) {
+ if (_AvailableRE.exec(foundry)) {
+ this.foundry = RegExp.$1;
+ this.layer = RegExp.$2;
+ this.type = RegExp.$3;
+ }
+ else {
+ throw new Error("Missing parameters");
+ };
+ }
+ else {
+ this.foundry = foundry;
+ this.layer = layer;
+ this.type = type;
+ };
+
+ if (this.type === undefined)
+ this.type = 'tokens';
+
+ return this;
+ }
+ };
+});
+
diff --git a/dev/js/src/match/table.js b/dev/js/src/match/table.js
new file mode 100644
index 0000000..7eba8a0
--- /dev/null
+++ b/dev/js/src/match/table.js
@@ -0,0 +1,193 @@
+define(function () {
+ var _TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
+
+ return {
+ create : function (snippet) {
+ return Object.create(this)._init(snippet);
+ },
+ _init : function (snippet) {
+ // Create html for traversal
+ var html = document.createElement("div");
+ html.innerHTML = snippet;
+
+ this._pos = 0;
+ this._token = [];
+ this._info = [];
+ this._foundry = {};
+ this._layer = {};
+
+ // Parse the snippet
+ this._parse(html.childNodes);
+
+ html.innerHTML = '';
+ return this;
+ },
+
+ length : function () {
+ return this._pos;
+ },
+
+ getToken : function (pos) {
+ if (pos === undefined)
+ return this._token;
+ return this._token[pos];
+ },
+
+ getValue : function (pos, foundry, layer) {
+ return this._info[pos][foundry + '/' + layer]
+ },
+
+ getLayerPerFoundry : function (foundry) {
+ return this._foundry[foundry]
+ },
+
+ getFoundryPerLayer : function (layer) {
+ return this._layer[layer];
+ },
+
+ // Parse the snippet
+ _parse : function (children) {
+
+ // Get all children
+ for (var i in children) {
+ var c = children[i];
+
+ // Create object on position unless it exists
+ if (this._info[this._pos] === undefined)
+ this._info[this._pos] = {};
+
+ // Store at position in foundry/layer as array
+ var found = this._info[this._pos];
+
+ // Element with title
+ if (c.nodeType === 1) {
+ if (c.getAttribute("title") &&
+ _TermRE.exec(c.getAttribute("title"))) {
+
+ // Fill position with info
+ var foundry, layer, value;
+ if (RegExp.$2) {
+ foundry = RegExp.$1;
+ layer = RegExp.$2;
+ }
+ else {
+ foundry = "base";
+ layer = RegExp.$1
+ };
+
+ value = RegExp.$3;
+
+ if (found[foundry + "/" + layer] === undefined)
+ found[foundry + "/" + layer] = [];
+
+ // Push value to foundry/layer at correct position
+ found[foundry + "/" + layer].push(RegExp.$3);
+
+ // Set foundry
+ if (this._foundry[foundry] === undefined)
+ this._foundry[foundry] = {};
+ this._foundry[foundry][layer] = 1;
+
+ // Set layer
+ if (this._layer[layer] === undefined)
+ this._layer[layer] = {};
+ this._layer[layer][foundry] = 1;
+ };
+
+ // depth search
+ if (c.hasChildNodes())
+ this._parse(c.childNodes);
+ }
+
+ // Leaf node
+ // store string on position and go to next string
+ else if (c.nodeType === 3) {
+ if (c.nodeValue.match(/[a-z0-9]/i))
+ this._token[this._pos++] = c.nodeValue;
+ };
+ };
+
+ delete this._info[this._pos];
+ },
+
+
+ /**
+ * Get HTML table view of annotations.
+ */
+ element : function () {
+ if (this._element !== undefined)
+ return this._element;
+
+ // First the legend table
+ var d = document;
+ var table = d.createElement('table');
+
+ // Single row in head
+ var tr = table.appendChild(d.createElement('thead'))
+ .appendChild(d.createElement('tr'));
+
+ // Add cell to row
+ var addCell = function (type, name) {
+ var c = this.appendChild(d.createElement(type))
+ if (name === undefined)
+ return c;
+
+ if (name instanceof Array) {
+ for (var n = 0; n < name.length; n++) {
+ c.appendChild(d.createTextNode(name[n]));
+ if (n !== name.length - 1) {
+ c.appendChild(d.createElement('br'));
+ };
+ };
+ }
+ else {
+ c.appendChild(d.createTextNode(name));
+ };
+ };
+
+ tr.addCell = addCell;
+
+ // Add header information
+ tr.addCell('th', 'Foundry');
+ tr.addCell('th', 'Layer');
+
+ // Add tokens
+ for (var i in this._token) {
+ tr.addCell('th', this.getToken(i));
+ };
+
+ var tbody = table.appendChild(
+ d.createElement('tbody')
+ );
+
+ var foundryList = Object.keys(this._foundry).sort();
+
+ for (var f = 0; f < foundryList.length; f++) {
+ var foundry = foundryList[f];
+ var layerList =
+ Object.keys(this._foundry[foundry]).sort();
+
+ for (var l = 0; l < layerList.length; l++) {
+ var layer = layerList[l];
+ tr = tbody.appendChild(
+ d.createElement('tr')
+ );
+ tr.setAttribute('tabindex', 0);
+ tr.addCell = addCell;
+
+ tr.addCell('th', foundry);
+ tr.addCell('th', layer);
+
+ for (var v = 0; v < this.length(); v++) {
+ tr.addCell(
+ 'td',
+ this.getValue(v, foundry, layer)
+ );
+ };
+ };
+ };
+
+ return this._element = table;
+ }
+ };
+});
diff --git a/dev/js/src/match/tree.js b/dev/js/src/match/tree.js
new file mode 100644
index 0000000..b379b37
--- /dev/null
+++ b/dev/js/src/match/tree.js
@@ -0,0 +1,222 @@
+/**
+ * Visualize span annotations as a tree using Dagre.
+ */
+define(['lib/dagre'], function (dagre) {
+ "use strict";
+
+ var svgXmlns = "http://www.w3.org/2000/svg";
+ var _TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
+
+ // Create path for node connections
+ function _line (src, target) {
+ var x1 = src.x,
+ y1 = src.y,
+ x2 = target.x,
+ y2 = target.y - target.height / 2;
+
+ // c 0,0 -10,0
+ return 'M ' + x1 + ',' + y1 + ' ' +
+ 'C ' + x1 + ',' + y1 + ' ' +
+ x2 + ',' + (y2 - (y2 - y1) / 2) + ' ' +
+ x2 + ',' + y2;
+ };
+
+ return {
+ create : function (snippet) {
+ return Object.create(this)._init(snippet);
+ },
+
+ nodes : function () {
+ return this._next;
+ },
+
+ _addNode : function (id, obj) {
+ obj["width"] = 55;
+ obj["height"] = 20;
+ this._graph.setNode(id, obj)
+ },
+
+ _addEdge : function (src, target) {
+ this._graph.setEdge(src, target);
+ },
+
+ _init : function (snippet) {
+ this._next = new Number(0);
+
+ // Create html for traversal
+ var html = document.createElement("div");
+ html.innerHTML = snippet;
+ var g = new dagre.graphlib.Graph({
+ "directed" : true
+ });
+ g.setGraph({
+ "nodesep" : 35,
+ "ranksep" : 15,
+ "marginx" : 40,
+ "marginy" : 10
+ });
+ g.setDefaultEdgeLabel({});
+
+ this._graph = g;
+
+ // This is a new root
+ this._addNode(
+ this._next++,
+ { "class" : "root" }
+ );
+
+ // Parse nodes from root
+ this._parse(0, html.childNodes);
+
+ // Root node has only one child - remove
+ if (g.outEdges(0).length === 1)
+ g.removeNode(0);
+
+ html = undefined;
+ return this;
+ },
+
+ // Remove foundry and layer for labels
+ _clean : function (title) {
+ return title.replace(_TermRE, "$3");
+ },
+
+ // Parse the snippet
+ _parse : function (parent, children) {
+ for (var i in children) {
+ var c = children[i];
+
+ // Element node
+ if (c.nodeType == 1) {
+
+ // Get title from html
+ if (c.getAttribute("title")) {
+ var title = this._clean(c.getAttribute("title"));
+
+ // Add child node
+ var id = this._next++;
+
+ this._addNode(id, {
+ "class" : "middle",
+ "label" : title
+ });
+ this._addEdge(parent, id);
+
+ // Check for next level
+ if (c.hasChildNodes())
+ this._parse(id, c.childNodes);
+ }
+
+ // Step further
+ else if (c.hasChildNodes())
+ this._parse(parent, c.childNodes);
+ }
+
+ // Text node
+ else if (c.nodeType == 3)
+
+ if (c.nodeValue.match(/[-a-z0-9]/i)) {
+
+ // Add child node
+ var id = this._next++;
+ this._addNode(id, {
+ "class" : "leaf",
+ "label" : c.nodeValue
+ });
+
+ this._addEdge(parent, id);
+ };
+ };
+ return this;
+ },
+
+ /**
+ * Center the viewport of the canvas
+ */
+ center : function () {
+ if (this._element === undefined)
+ return;
+
+ var treeDiv = this._element.parentNode;
+
+ var cWidth = parseFloat(window.getComputedStyle(this._element).width);
+ var treeWidth = parseFloat(window.getComputedStyle(treeDiv).width);
+ // Reposition:
+ if (cWidth > treeWidth) {
+ var scrollValue = (cWidth - treeWidth) / 2;
+ treeDiv.scrollLeft = scrollValue;
+ };
+ },
+
+ // Get element
+ element : function () {
+ if (this._element !== undefined)
+ return this._element;
+
+ var g = this._graph;
+
+ dagre.layout(g);
+
+ var canvas = document.createElementNS(svgXmlns, 'svg');
+ this._element = canvas;
+
+ canvas.setAttribute('height', g.graph().height);
+ canvas.setAttribute('width', g.graph().width);
+
+ // Create edges
+ g.edges().forEach(
+ function (e) {
+ var src = g.node(e.v);
+ var target = g.node(e.w);
+ var p = document.createElementNS(svgXmlns, 'path');
+ p.setAttributeNS(null, "d", _line(src, target));
+ p.classList.add('edge');
+ canvas.appendChild(p);
+ });
+
+ // Create nodes
+ g.nodes().forEach(
+ function (v) {
+ v = g.node(v);
+ var group = document.createElementNS(svgXmlns, 'g');
+ group.classList.add(v.class);
+
+ // Add node box
+ var rect = group.appendChild(document.createElementNS(svgXmlns, 'rect'));
+ rect.setAttributeNS(null, 'x', v.x - v.width / 2);
+ rect.setAttributeNS(null, 'y', v.y - v.height / 2);
+ rect.setAttributeNS(null, 'rx', 5);
+ rect.setAttributeNS(null, 'ry', 5);
+ rect.setAttributeNS(null, 'width', v.width);
+ rect.setAttributeNS(null, 'height', v.height);
+
+ if (v.class === 'root' && v.label === undefined) {
+ rect.setAttributeNS(null, 'width', v.height);
+ rect.setAttributeNS(null, 'x', v.x - v.height / 2);
+ rect.setAttributeNS(null, 'class', 'empty');
+ };
+
+ // Add label
+ if (v.label !== undefined) {
+ var text = group.appendChild(document.createElementNS(svgXmlns, 'text'));
+ text.setAttributeNS(null, 'x', v.x - v.width / 2);
+ text.setAttributeNS(null, 'y', v.y - v.height / 2);
+ text.setAttributeNS(
+ null,
+ 'transform',
+ 'translate(' + v.width/2 + ',' + ((v.height / 2) + 5) + ')'
+ );
+
+ var tspan = document.createElementNS(svgXmlns, 'tspan');
+ tspan.appendChild(document.createTextNode(v.label));
+ text.appendChild(tspan);
+ };
+
+ canvas.appendChild(group);
+ }
+ );
+
+ return this._element;
+ }
+ };
+});
diff --git a/dev/js/src/match/treeitem.js b/dev/js/src/match/treeitem.js
new file mode 100644
index 0000000..f096861
--- /dev/null
+++ b/dev/js/src/match/treeitem.js
@@ -0,0 +1,49 @@
+define(['menu/item'], function (itemClass) {
+ /**
+ * Menu item for tree view choice.
+ */
+
+ return {
+ create : function (params) {
+ return Object.create(itemClass)
+ .upgradeTo(this)._init(params);
+ },
+ content : function (content) {
+ if (arguments.length === 1) {
+ this._content = content;
+ };
+ return this._content;
+ },
+
+ // The foundry attribute
+ foundry : function () {
+ return this._foundry;
+ },
+
+ // The layer attribute
+ layer : function () {
+ return this._layer;
+ },
+
+ // enter or click
+ onclick : function (e) {
+ var menu = this.menu();
+ menu.hide();
+ e.halt();
+ if (menu.info() !== undefined)
+ menu.info().addTree(this._foundry, this._layer);
+ },
+
+ _init : function (params) {
+ if (params[0] === undefined)
+ throw new Error("Missing parameters");
+
+ this._name = params[0];
+ this._foundry = params[1];
+ this._layer = params[2];
+ this._content = document.createTextNode(this._name);
+ this._lcField = ' ' + this.content().textContent.toLowerCase();
+ return this;
+ }
+ };
+});
diff --git a/dev/js/src/match/treemenu.js b/dev/js/src/match/treemenu.js
new file mode 100644
index 0000000..23341a4
--- /dev/null
+++ b/dev/js/src/match/treemenu.js
@@ -0,0 +1,26 @@
+ /**
+ * Menu to choose from for tree views.
+ */
+define(['menu', 'match/treeitem'], function (menuClass, itemClass) {
+ "use strict";
+
+ return {
+ create : function (info, params) {
+ var obj = Object.create(menuClass)
+ .upgradeTo(this)
+ ._init(itemClass, undefined, params);
+ obj.limit(6);
+ obj._info = info;
+
+ // This is only domspecific
+ obj.element().addEventListener('blur', function (e) {
+ this.menu.hide();
+ });
+
+ return obj;
+ },
+ info :function () {
+ return this._info;
+ }
+ };
+});
diff --git a/dev/js/src/menu.js b/dev/js/src/menu.js
index 78f9ea7..0d5e6ac 100644
--- a/dev/js/src/menu.js
+++ b/dev/js/src/menu.js
@@ -1,35 +1,35 @@
-var KorAP = KorAP || {};
-
/**
* Create scrollable drop-down menus.
*
* @author Nils Diewald
*/
-
/*
- * TODO: space is not a valid prefix!
+ * TODO: space is not a valid prefix!
*/
-(function (KorAP) {
- "use strict";
+define([
+ 'menu/item',
+ 'menu/prefix',
+ 'util'
+], function (defaultItemClass,
+ defaultPrefixClass) {
- // Don't let events bubble up
- if (Event.halt === undefined) {
- // Don't let events bubble up
- Event.prototype.halt = function () {
- this.stopPropagation();
- this.preventDefault();
- };
+ // Todo: This may not be necessary
+ // Default maximum number of menu items
+ var menuLimit = 8;
+
+ function _codeFromEvent (e) {
+ if (e.charCode && (e.keyCode == 0))
+ return e.charCode
+ return e.keyCode;
};
- // Default maximum number of menu items
- KorAP.menuLimit = 8;
/**
* List of items for drop down menu (complete).
* Only a sublist of the menu is filtered (live).
* Only a sublist of the filtered menu is visible (shown).
*/
- KorAP.Menu = {
+ return {
/**
* Create new Menu based on the action prefix
* and a list of menu items.
@@ -40,7 +40,7 @@
* @param {Array.<Array.<string>>} List of menu items
*/
create : function (params) {
- return Object.create(KorAP.Menu)._init(params);
+ return Object.create(this)._init(params);
},
/**
@@ -143,12 +143,12 @@
// Initialize list
_init : function (itemClass, prefixClass, params) {
var that = this;
- this._itemClass = itemClass;
+ this._itemClass = itemClass || defaultItemClass;
if (prefixClass !== undefined)
this._prefix = prefixClass.create();
else
- this._prefix = KorAP.MenuPrefix.create();
+ this._prefix = defaultPrefixClass.create();
this._prefix._menu = this;
@@ -188,14 +188,14 @@
// Initialize item list based on parameters
for (i in params) {
- var obj = itemClass.create(params[i]);
+ var obj = this._itemClass.create(params[i]);
// This may become circular
obj["_menu"] = this;
this._items.push(obj);
};
- this._limit = KorAP.menuLimit;
+ this._limit = menuLimit;
this._position = 0; // position in the active list
this._active = -1; // active item in the item list
this._reset();
@@ -654,318 +654,4 @@
this._element.removeChild(this._element.lastChild);
}
};
-
-
- /**
- * Item in the Dropdown menu
- */
- KorAP.MenuItem = {
-
- /**
- * Create a new MenuItem object.
- *
- * @constructor
- * @this {MenuItem}
- * @param {Array.<string>} An array object of name, action and
- * optionally a description
- */
- create : function (params) {
- return Object.create(KorAP.MenuItem)._init(params);
- },
-
- /**
- * Upgrade this object to another object,
- * while private data stays intact.
- *
- * @param {Object] An object with properties.
- */
- upgradeTo : function (props) {
- for (var prop in props) {
- this[prop] = props[prop];
- };
- return this;
- },
-
- content : function (content) {
- if (arguments.length === 1)
- this._content = document.createTextNode(content);
- return this._content;
- },
-
- lcField : function () {
- return this._lcField;
- },
-
- action : function (action) {
- if (arguments.length === 1)
- this._action = action;
- return this._action;
- },
-
- /**
- * Check or set if the item is active
- *
- * @param {boolean|null} State of activity
- */
- active : function (bool) {
- var cl = this.element().classList;
- if (bool === undefined)
- return cl.contains("active");
- else if (bool)
- cl.add("active");
- else
- cl.remove("active");
- },
-
- /**
- * Check or set if the item is
- * at the boundary of the menu
- * list
- *
- * @param {boolean|null} State of activity
- */
- noMore : function (bool) {
- var cl = this.element().classList;
- if (bool === undefined)
- return cl.contains("no-more");
- else if (bool)
- cl.add("no-more");
- else
- cl.remove("no-more");
- },
-
- /**
- * Get the document element of the menu item
- */
- element : function () {
- // already defined
- if (this._element !== undefined)
- return this._element;
-
- // Create list item
- var li = document.createElement("li");
-
- // Connect action
- if (this["onclick"] !== undefined) {
- li["onclick"] = this.onclick.bind(this);
- };
-
- // Append template
- li.appendChild(this.content());
-
- return this._element = li;
- },
-
- /**
- * Highlight parts of the item
- *
- * @param {string} Prefix string for highlights
- */
- highlight : function (prefix) {
- var children = this.element().childNodes;
- for (var i = children.length -1; i >= 0; i--) {
- this._highlight(children[i], prefix);
- };
- },
-
- // Highlight a certain substring of the menu item
- _highlight : function (elem, prefix) {
-
- if (elem.nodeType === 3) {
-
- var text = elem.nodeValue;
- var textlc = text.toLowerCase();
- var pos = textlc.indexOf(prefix);
- if (pos >= 0) {
-
- // First element
- if (pos > 0) {
- elem.parentNode.insertBefore(
- document.createTextNode(text.substr(0, pos)),
- elem
- );
- };
-
- // Second element
- var hl = document.createElement("mark");
- hl.appendChild(
- document.createTextNode(text.substr(pos, prefix.length))
- );
- elem.parentNode.insertBefore(hl, elem);
-
- // Third element
- var third = text.substr(pos + prefix.length);
- if (third.length > 0) {
- var thirdE = document.createTextNode(third);
- elem.parentNode.insertBefore(
- thirdE,
- elem
- );
- this._highlight(thirdE, prefix);
- };
-
- var p = elem.parentNode;
- p.removeChild(elem);
- };
- }
- else {
- var children = elem.childNodes;
- for (var i = children.length -1; i >= 0; i--) {
- this._highlight(children[i], prefix);
- };
- };
- },
-
-
- /**
- * Remove highlight of the menu item
- */
- lowlight : function () {
- var e = this.element();
-
- var marks = e.getElementsByTagName("mark");
- for (var i = marks.length - 1; i >= 0; i--) {
- // Create text node clone
- var x = document.createTextNode(
- marks[i].firstChild.nodeValue
- );
-
- // Replace with content
- marks[i].parentNode.replaceChild(
- x,
- marks[i]
- );
- };
-
- // Remove consecutive textnodes
- e.normalize();
- },
-
- // Initialize menu item
- _init : function (params) {
-
- if (params[0] === undefined)
- throw new Error("Missing parameters");
-
- this.content(params[0]);
-
- if (params.length === 2)
- this._action = params[1];
-
- this._lcField = ' ' + this.content().textContent.toLowerCase();
-
- return this;
- },
-
- /**
- * Return menu list.
- */
- menu : function () {
- return this._menu;
- }
- };
-
- KorAP.MenuPrefix = {
- create : function (params) {
- return Object.create(KorAP.MenuPrefix)._init();
- },
- _init : function () {
- this._string = '';
-
- // Add prefix span
- this._element = document.createElement('span');
- this._element.classList.add('pref');
- // Connect action
-
- if (this["onclick"] !== undefined)
- this._element["onclick"] = this.onclick.bind(this);
-
- return this;
- },
- _update : function () {
- this._element.innerHTML
- = this._string;
- },
-
- /**
- * Upgrade this object to another object,
- * while private data stays intact.
- *
- * @param {Object} An object with properties.
- */
- upgradeTo : function (props) {
- for (var prop in props) {
- this[prop] = props[prop];
- };
- return this;
- },
-
- active : function (bool) {
- var cl = this.element().classList;
- if (bool === undefined)
- return cl.contains("active");
- else if (bool)
- cl.add("active");
- else
- cl.remove("active");
- },
-
- element : function () {
- return this._element;
- },
-
- isSet : function () {
- return this._string.length > 0 ?
- true : false;
- },
-
- value : function (string) {
- if (arguments.length === 1) {
- this._string = string;
- this._update();
- };
- return this._string;
- },
-
- add : function (string) {
- this._string += string;
- this._update();
- },
-
- onclick : function () {},
-
- backspace : function () {
- if (this._string.length > 1) {
- this._string = this._string.substring(
- 0, this._string.length - 1
- );
- }
- else {
- this._string = '';
- };
-
- this._update();
- },
-
- /**
- * Return menu list.
- */
- menu : function () {
- return this._menu;
- }
- };
-
- function _codeFromEvent (e) {
- if (e.charCode && (e.keyCode == 0))
- return e.charCode
- return e.keyCode;
- };
-
-}(this.KorAP));
-
-/**
- * MenuItems may define:
- *
- * onclick: action happen on click and enter.
- * further: action happen on right arrow
- */
+});
diff --git a/dev/js/src/menu/item.js b/dev/js/src/menu/item.js
new file mode 100644
index 0000000..b80aa2a
--- /dev/null
+++ b/dev/js/src/menu/item.js
@@ -0,0 +1,213 @@
+/*
+ * MenuItems may define:
+ *
+ * onclick: action happen on click and enter.
+ * further: action happen on right arrow
+ */
+
+/**
+ * Item in the Dropdown menu
+ */
+define({
+ /**
+ * Create a new MenuItem object.
+ *
+ * @constructor
+ * @this {MenuItem}
+ * @param {Array.<string>} An array object of name, action and
+ * optionally a description
+ */
+ create : function (params) {
+ return Object.create(this)._init(params);
+ },
+
+ /**
+ * Upgrade this object to another object,
+ * while private data stays intact.
+ *
+ * @param {Object] An object with properties.
+ */
+ upgradeTo : function (props) {
+ for (var prop in props) {
+ this[prop] = props[prop];
+ };
+ return this;
+ },
+
+ content : function (content) {
+ if (arguments.length === 1)
+ this._content = document.createTextNode(content);
+ return this._content;
+ },
+
+ lcField : function () {
+ return this._lcField;
+ },
+
+ action : function (action) {
+ if (arguments.length === 1)
+ this._action = action;
+ return this._action;
+ },
+
+ /**
+ * Check or set if the item is active
+ *
+ * @param {boolean|null} State of activity
+ */
+ active : function (bool) {
+ var cl = this.element().classList;
+ if (bool === undefined)
+ return cl.contains("active");
+ else if (bool)
+ cl.add("active");
+ else
+ cl.remove("active");
+ },
+
+ /**
+ * Check or set if the item is
+ * at the boundary of the menu
+ * list
+ *
+ * @param {boolean|null} State of activity
+ */
+ noMore : function (bool) {
+ var cl = this.element().classList;
+ if (bool === undefined)
+ return cl.contains("no-more");
+ else if (bool)
+ cl.add("no-more");
+ else
+ cl.remove("no-more");
+ },
+
+ /**
+ * Get the document element of the menu item
+ */
+ element : function () {
+ // already defined
+ if (this._element !== undefined)
+ return this._element;
+
+ // Create list item
+ var li = document.createElement("li");
+
+ // Connect action
+ if (this["onclick"] !== undefined) {
+ li["onclick"] = this.onclick.bind(this);
+ };
+
+ // Append template
+ li.appendChild(this.content());
+
+ return this._element = li;
+ },
+
+ /**
+ * Highlight parts of the item
+ *
+ * @param {string} Prefix string for highlights
+ */
+ highlight : function (prefix) {
+ var children = this.element().childNodes;
+ for (var i = children.length -1; i >= 0; i--) {
+ this._highlight(children[i], prefix);
+ };
+ },
+
+ // Highlight a certain substring of the menu item
+ _highlight : function (elem, prefix) {
+
+ if (elem.nodeType === 3) {
+
+ var text = elem.nodeValue;
+ var textlc = text.toLowerCase();
+ var pos = textlc.indexOf(prefix);
+ if (pos >= 0) {
+
+ // First element
+ if (pos > 0) {
+ elem.parentNode.insertBefore(
+ document.createTextNode(text.substr(0, pos)),
+ elem
+ );
+ };
+
+ // Second element
+ var hl = document.createElement("mark");
+ hl.appendChild(
+ document.createTextNode(text.substr(pos, prefix.length))
+ );
+ elem.parentNode.insertBefore(hl, elem);
+
+ // Third element
+ var third = text.substr(pos + prefix.length);
+ if (third.length > 0) {
+ var thirdE = document.createTextNode(third);
+ elem.parentNode.insertBefore(
+ thirdE,
+ elem
+ );
+ this._highlight(thirdE, prefix);
+ };
+
+ var p = elem.parentNode;
+ p.removeChild(elem);
+ };
+ }
+ else {
+ var children = elem.childNodes;
+ for (var i = children.length -1; i >= 0; i--) {
+ this._highlight(children[i], prefix);
+ };
+ };
+ },
+
+ /**
+ * Remove highlight of the menu item
+ */
+ lowlight : function () {
+ var e = this.element();
+
+ var marks = e.getElementsByTagName("mark");
+ for (var i = marks.length - 1; i >= 0; i--) {
+ // Create text node clone
+ var x = document.createTextNode(
+ marks[i].firstChild.nodeValue
+ );
+
+ // Replace with content
+ marks[i].parentNode.replaceChild(
+ x,
+ marks[i]
+ );
+ };
+
+ // Remove consecutive textnodes
+ e.normalize();
+ },
+
+ // Initialize menu item
+ _init : function (params) {
+
+ if (params[0] === undefined)
+ throw new Error("Missing parameters");
+
+ this.content(params[0]);
+
+ if (params.length === 2)
+ this._action = params[1];
+
+ this._lcField = ' ' + this.content().textContent.toLowerCase();
+
+ return this;
+ },
+
+ /**
+ * Return menu list.
+ */
+ menu : function () {
+ return this._menu;
+ }
+});
diff --git a/dev/js/src/menu/prefix.js b/dev/js/src/menu/prefix.js
new file mode 100644
index 0000000..a4e428f
--- /dev/null
+++ b/dev/js/src/menu/prefix.js
@@ -0,0 +1,89 @@
+define({
+ create : function (params) {
+ return Object.create(this)._init();
+ },
+ _init : function () {
+ this._string = '';
+
+ // Add prefix span
+ this._element = document.createElement('span');
+ this._element.classList.add('pref');
+ // Connect action
+
+ if (this["onclick"] !== undefined)
+ this._element["onclick"] = this.onclick.bind(this);
+
+ return this;
+ },
+ _update : function () {
+ this._element.innerHTML
+ = this._string;
+ },
+
+ /**
+ * Upgrade this object to another object,
+ * while private data stays intact.
+ *
+ * @param {Object} An object with properties.
+ */
+ upgradeTo : function (props) {
+ for (var prop in props) {
+ this[prop] = props[prop];
+ };
+ return this;
+ },
+
+ active : function (bool) {
+ var cl = this.element().classList;
+ if (bool === undefined)
+ return cl.contains("active");
+ else if (bool)
+ cl.add("active");
+ else
+ cl.remove("active");
+ },
+
+ element : function () {
+ return this._element;
+ },
+
+ isSet : function () {
+ return this._string.length > 0 ?
+ true : false;
+ },
+
+ value : function (string) {
+ if (arguments.length === 1) {
+ this._string = string;
+ this._update();
+ };
+ return this._string;
+ },
+
+ add : function (string) {
+ this._string += string;
+ this._update();
+ },
+
+ onclick : function () {},
+
+ backspace : function () {
+ if (this._string.length > 1) {
+ this._string = this._string.substring(
+ 0, this._string.length - 1
+ );
+ }
+ else {
+ this._string = '';
+ };
+
+ this._update();
+ },
+
+ /**
+ * Return menu list.
+ */
+ menu : function () {
+ return this._menu;
+ }
+});
diff --git a/dev/js/src/session.js b/dev/js/src/session.js
index a10ce81..f81eb8f 100644
--- a/dev/js/src/session.js
+++ b/dev/js/src/session.js
@@ -4,77 +4,68 @@
*
* @author Nils Diewald
*/
-var KorAP = KorAP || {};
+define({
+ /**
+ * Create a new session.
+ * Expects a name or defaults to 'korap'
+ */
+ create : function (name) {
+ var obj = Object.create(this);
+ if (name === undefined)
+ name = 'korap';
+ obj._name = name.toLowerCase();
+ obj._hash = {};
+ obj._parse();
+ return obj;
+ },
-(function (KorAP) {
- "use strict";
+ /**
+ * Get a value based on a key.
+ * The value can be complex, as the value is stored as JSON.
+ */
+ get : function (key) {
+ return this._hash[key.toLowerCase()];
+ },
-
- KorAP.Session = {
+ /**
+ * Set a value based on a key.
+ * The value can be complex, as the value is stored as JSON.
+ */
+ set : function (key, value) {
+ this._hash[key] = value;
+ this._store();
+ },
- /**
- * Create a new session.
- * Expects a name or defaults to 'korap'
- */
- create : function (name) {
- var obj = Object.create(KorAP.Session);
- if (name === undefined)
- name = 'korap';
- obj._name = name.toLowerCase();
- obj._hash = {};
- obj._parse();
- return obj;
- },
+ /**
+ * Clears the session by removing the cookie
+ */
+ clear : function () {
+ document.cookie = this._name + '=; expires=-1';
+ },
- /**
- * Get a value based on a key.
- * The value can be complex, as the value is stored as JSON.
- */
- get : function (key) {
- return this._hash[key.toLowerCase()];
- },
+ /* Store cookie */
+ _store : function () {
+ /*
+ var date = new Date();
+ date.setYear(date.getFullYear() + 1);
+ */
+ document.cookie =
+ this._name + '=' + encodeURIComponent(JSON.stringify(this._hash)) + ';';
+ },
- /**
- * Set a value based on a key.
- * The value can be complex, as the value is stored as JSON.
- */
- set : function (key, value) {
- this._hash[key] = value;
- this._store();
- },
-
- /**
- * Clears the session by removing the cookie
- */
- clear : function () {
- document.cookie = this._name + '=; expires=-1';
- },
-
- /* Store cookie */
- _store : function () {
- /*
- var date = new Date();
- date.setYear(date.getFullYear() + 1);
- */
- document.cookie =
- this._name + '=' + encodeURIComponent(JSON.stringify(this._hash)) + ';';
- },
-
- /* Parse cookie */
- _parse : function () {
- var c = document.cookie;
- var part = document.cookie.split(';');
- for(var i = 0; i < part.length; i++) {
- var pair = part[i].split('=');
- var name = pair[0].trim().toLowerCase();
- if (name === this._name) {
- if (pair.length === 1 || pair[1].length === 0)
- return;
- this._hash = JSON.parse(decodeURIComponent(pair[1]));
+ /* Parse cookie */
+ _parse : function () {
+ var c = document.cookie;
+ var part = document.cookie.split(';');
+ for(var i = 0; i < part.length; i++) {
+ var pair = part[i].split('=');
+ var name = pair[0].trim().toLowerCase();
+ if (name === this._name) {
+ if (pair.length === 1 || pair[1].length === 0)
return;
- };
+ this._hash = JSON.parse(decodeURIComponent(pair[1]));
+ return;
};
- }
+ };
}
-
-}(this.KorAP));
+});
diff --git a/dev/js/src/tutorial.js b/dev/js/src/tutorial.js
index 03cf903..dae5fbf 100644
--- a/dev/js/src/tutorial.js
+++ b/dev/js/src/tutorial.js
@@ -2,31 +2,30 @@
* Open and close a tutorial page.
* The current page is stored and retrieved in a session cookie.
*/
-// Requires session.js
-var KorAP = KorAP || {};
-
// Todo: add query mechanism!
-(function (KorAP) {
+define(['session', 'util'], function (sessionClass) {
"use strict";
// Localization values
- var loc = (KorAP.Locale = KorAP.Locale || {} );
+ var loc = KorAP.Locale;
loc.CLOSE = loc.CLOSE || 'Close';
- KorAP.Tutorial = {
-
+ return {
/**
* Create new tutorial object.
* Accepts an element to bind the tutorial window to.
*/
create : function (obj) {
- return Object.create(KorAP.Tutorial)._init(obj);
+ if (!obj)
+ return null;
+ return Object.create(this)._init(obj);
},
// Initialize Tutorial object
_init : function (obj) {
- this._session = KorAP.Session.create();
+
+ this._session = sessionClass.create();
this._show = obj;
this.start = obj.getAttribute('href');
obj.removeAttribute('href');
@@ -58,9 +57,6 @@
var ul = document.createElement('ul');
ul.classList.add('action', 'right');
- // Use localization
- var loc = KorAP.Locale;
-
// Add close button
var close = document.createElement('li');
close.appendChild(document.createElement('span'))
@@ -80,7 +76,7 @@
info.classList.add('info');
info.setAttribute('title', loc.SHOWINFO);
*/
-
+
ul.appendChild(close);
element.appendChild(ul);
@@ -129,5 +125,5 @@
getPage : function () {
this._session.get('tutpage');
},
- }
-}(this.KorAP));
+ };
+});
diff --git a/dev/js/src/util.js b/dev/js/src/util.js
new file mode 100644
index 0000000..d183c36
--- /dev/null
+++ b/dev/js/src/util.js
@@ -0,0 +1,45 @@
+var KorAP = KorAP || {};
+
+// TODO: Make this part of util!
+// Don't let events bubble up
+if (Event.halt === undefined) {
+ // Don't let events bubble up
+ Event.prototype.halt = function () {
+ this.stopPropagation();
+ this.preventDefault();
+ };
+};
+
+// Add toggleClass method similar to jquery
+HTMLElement.prototype.toggleClass = function (c1, c2) {
+ var cl = this.classList;
+ if (cl.contains(c1)) {
+ cl.add(c2);
+ cl.remove(c1);
+ }
+ else {
+ cl.remove(c2);
+ cl.add(c1);
+ };
+};
+
+
+// Utility for removing all children of a node
+function _removeChildren (node) {
+ // Remove everything underneath
+ while (node.firstChild)
+ node.removeChild(node.firstChild);
+};
+
+
+define(function () {
+ KorAP.API = KorAP.API || {};
+ KorAP.Locale = KorAP.Locale || {};
+
+ // Default log message
+ KorAP.log = KorAP.log || function (type, msg) {
+ console.log(type + ": " + msg);
+ };
+
+ return KorAP;
+});
diff --git a/dev/js/src/vc.js b/dev/js/src/vc.js
index 2c8ad01..48c5694 100644
--- a/dev/js/src/vc.js
+++ b/dev/js/src/vc.js
@@ -6,9 +6,6 @@
/*
* Replaces a previous version written by Mengfei Zhou
*/
-var KorAP = KorAP || {};
-
-// Requires menu.js
/*
TODO: Implement a working localization solution!
@@ -33,131 +30,60 @@
815: "Rewrite expects source"
*/
-(function (KorAP) {
+define([
+ 'vc/unspecified',
+ 'vc/doc',
+ 'vc/docgroup',
+ 'util'
+], function (unspecDocClass, docClass, docGroupClass) {
"use strict";
- // Default log message
- KorAP.log = KorAP.log || function (type, msg) {
- console.log(type + ": " + msg);
- };
-
KorAP._validStringMatchRE = new RegExp("^(?:eq|ne|contains|excludes)$");
- KorAP._validRegexMatchRE = new RegExp("^(?:eq|ne)$");
+ // KorAP._validRegexMatchRE = new RegExp("^(?:eq|ne)$");
KorAP._validDateMatchRE = new RegExp("^[lg]?eq$");
KorAP._validDateRE = new RegExp("^(?:\\d{4})(?:-\\d\\d(?:-\\d\\d)?)?$");
- KorAP._validGroupOpRE = new RegExp("^(?:and|or)$");
- KorAP._validRewriteOpRE = new RegExp("^(?:injec|modifica)tion$");
- KorAP._quote = new RegExp("([\"\\\\])", 'g');
+ // KorAP._validGroupOpRE = new RegExp("^(?:and|or)$");
+ // KorAP._quote = new RegExp("([\"\\\\])", 'g');
// Localization values
var loc = (KorAP.Locale = KorAP.Locale || {} );
- loc.AND = loc.AND || 'and';
- loc.OR = loc.OR || 'or';
- loc.DEL = loc.DEL || '×';
- loc.EMPTY = loc.EMPTY || '⋯'
-
- // Utility for analysing boolean values
- function _bool (bool) {
- return (bool === undefined || bool === null || bool === false) ? false : true;
- };
-
-
- // Utility for removing all children of a node
- function _removeChildren (node) {
- // Remove everything underneath
- while (node.firstChild)
- node.removeChild(node.firstChild);
- };
-
-
- // Add new unspecified document
- KorAP._add = function (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
- KorAP._and = function () {
- return KorAP._add(this, 'and');
- };
-
-
- // Add doc with 'or' relation
- KorAP._or = function () {
- return KorAP._add(this, 'or');
- };
-
-
- // Remove doc or docGroup
- KorAP._delete = function () {
- var ref = this.parentNode.refTo;
- if (ref.parent().ldType() !== null) {
- return ref.parent().delOperand(ref).update();
- }
- else {
- ref.parent().clean();
- };
- };
-
+ /*
+ loc.AND = loc.AND || 'and';
+ loc.OR = loc.OR || 'or';
+ loc.DEL = loc.DEL || '×';
+ loc.EMPTY = loc.EMPTY || '⋯'
+ */
/**
* Virtual Collection
*/
- KorAP.VirtualCollection = {
+ return {
ldType : function () {
return null;
},
create : function () {
- return Object.create(KorAP.VirtualCollection);
+ return Object.create(this);
},
clean : function () {
if (this._root.ldType() !== "non") {
this._root.destroy();
- this.root(KorAP.UnspecifiedDoc.create(this));
+ this.root(unspecDocClass.create(this));
};
return this;
},
render : function (json) {
- var obj = Object.create(KorAP.VirtualCollection);
+ var obj = Object.create(this);
if (json !== undefined) {
// Root object
if (json['@type'] == 'koral:doc') {
- obj._root = KorAP.Doc.create(obj, json);
+ obj._root = docClass.create(obj, json);
}
else if (json['@type'] == 'koral:docGroup') {
- obj._root = KorAP.DocGroup.create(obj, json);
+ obj._root = docGroupClass.create(obj, json);
}
else {
KorAP.log(813, "Collection type is not supported");
@@ -225,1174 +151,4 @@
return this._root.toQuery();
}
};
-
-
- /**
- * Operators for criteria
- */
- KorAP.Operators = {
- create : function (and, or, del) {
- var op = Object.create(KorAP.Operators);
- 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', KorAP._and, false);
- andE.appendChild(
- document.createTextNode(KorAP.Locale.AND)
- );
- op.appendChild(andE);
- };
-
- // Add or button
- if (this._or === true) {
- var orE = document.createElement('span');
- orE.setAttribute('class', 'or');
- orE.addEventListener('click', KorAP._or, false);
- orE.appendChild(document.createTextNode(KorAP.Locale.OR));
- op.appendChild(orE);
- };
-
- // Add delete button
- if (this._del === true) {
- var delE = document.createElement('span');
- delE.setAttribute('class', 'delete');
- delE.appendChild(document.createTextNode(KorAP.Locale.DEL));
- delE.addEventListener('click', KorAP._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;
- }
- };
-
-
- /**
- * Unspecified criterion
- */
- KorAP.UnspecifiedDoc = {
- _ldType : "non",
- create : function (parent) {
- var obj = Object.create(KorAP.JsonLD).
- upgradeTo(KorAP.UnspecifiedDoc);
-
- 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 = KorAP.Doc.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;
- },
-
-
- };
-
-
- /**
- * Document criterion
- */
- KorAP.Doc = {
- _ldType : "doc",
- _obj : function () { return KorAP.Doc },
-
- create : function (parent, json) {
- var obj = Object(KorAP.JsonLD).
- create().
- upgradeTo(KorAP.Doc).
- 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', KorAP._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 (!KorAP._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 = KorAP.RewriteList.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 "";
- }
- };
-
-
- /**
- * Document group criterion
- */
- KorAP.DocGroup = {
- _ldType : "docGroup",
-
- create : function (parent, json) {
- var obj = Object.create(KorAP.JsonLD).upgradeTo(KorAP.DocGroup);
- 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 = KorAP.UnspecifiedDoc.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 = KorAP.UnspecifiedDoc.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 = KorAP.Doc.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 = KorAP.DocGroup.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 (KorAP._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;
- };
- }
- };
-
-
- KorAP.RewriteList = {
- // Construction method
- create : function (json) {
- var obj = Object(KorAP.JsonLD).
- create().
- upgradeTo(KorAP.RewriteList).
- fromJson(json);
- return obj;
- },
- fromJson : function (json) {
- this._list = new Array();
- for (var i = 0; i < json.length; i++) {
- this._list.push(
- KorAP.Rewrite.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;
- }
- };
-
-
- /**
- * Implementation of rewrite objects.
- */
- KorAP.Rewrite = {
-
- // Construction method
- create : function (json) {
- var obj = Object(KorAP.JsonLD).
- create().
- upgradeTo(KorAP.Rewrite).
- 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 (KorAP._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(KorAP._quote, '\\$1') +
- '"'
- );
- str += ' by ' +
- '"' +
- this.src().replace(KorAP._quote, '\\$1') +
- '"';
- return str;
- }
- };
-
-
- /**
- * Abstract JsonLD criterion object
- */
- KorAP.JsonLD = {
- __changed : false,
-
- create : function () {
- return Object.create(KorAP.JsonLD);
- },
-
- /**
- * 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 = KorAP.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 = KorAP.Operators.create(
- and, or, del
- );
- this._ops.parent(this);
- return this._ops;
- },
-
- toJson : function () {
- return {
- // Unspecified object
- "@type" : "koral:" + this.ldType()
- };
- },
-
- toQuery : function () {
- return '';
- }
- };
-
-
- /**
- * Criterion in a KorAP.Doc
- */
- KorAP._changeKey = function () {
- var doc = this.parentNode.refTo;
- var key = doc.element().firstChild;
- key.appendChild(KorAP.FieldChooser.element());
- KorAP.FieldChooser.show();
- KorAP.FieldChooser.focus();
- // key, matchop, type, value
- };
-
- // Field menu
- KorAP.FieldMenu = {
- create : function (params) {
- return Object.create(KorAP.Menu)
- .upgradeTo(KorAP.FieldMenu)
- ._init(KorAP.FieldMenuItem, undefined, params)
- }
- };
-
-
- // Field menu item
- KorAP.FieldMenuItem = {
- create : function (params) {
- return Object.create(KorAP.MenuItem)
- .upgradeTo(KorAP.FieldMenuItem)
- ._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;
- }
- };
-
- KorAP.FieldChooser = KorAP.FieldMenu.create([
- ['Titel', 'title', 'string'],
- ['Untertitel', 'subTitle', 'string'],
- ['Veröffentlichungsdatum', 'pubDate', 'date'],
- ['Autor', 'author', 'string']
- ]);
- KorAP.FieldChooser.limit(5);
-
-}(this.KorAP));
+});
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;
+ },
+ };
+});