Merge morphTable and morphTree to matchInfo module
diff --git a/public/js/src/matchInfo.js b/public/js/src/matchInfo.js
new file mode 100644
index 0000000..515a78f
--- /dev/null
+++ b/public/js/src/matchInfo.js
@@ -0,0 +1,319 @@
+/**
+ * Make annotations visible.
+ *
+ * @author Nils Diewald
+ */
+/*
+ - Scroll with a static left legend.
+ - Highlight (at least mark as bold) the match
+ - Scroll to match vertically per default
+ */
+var KorAP = KorAP || {};
+
+(function (KorAP) {
+ "use strict";
+
+ KorAP._AvailableRE = new RegExp("^([^\/]+?)\/([^=]+?)(?:=(spans|rels|tokens))?$");
+ KorAP._TermRE = new RegExp("^([^\/]+?)(?:\/([^:]+?))?:(.+?)$");
+ KorAP._matchTerms = ["corpusID", "docID", "textID"];
+
+ // API requests
+ KorAP.API = KorAP.API || {};
+ KorAP.API.getMatchInfo = KorAP.API.getMatchInfo || function () { return {} };
+
+ KorAP.MatchInfo = {
+
+ /**
+ * Create a new annotation object.
+ * Expects an array of available foundry/layer=type terms.
+ * Supported types are 'spans', 'tokens' and 'rels'.
+ */
+ create : function (match, available) {
+ if (arguments.length < 2)
+ throw new Error("Missing parameters");
+
+ return Object.create(KorAP.MatchInfo)._init(match, available);
+ },
+
+ _init : function (match, available) {
+ this._match = KorAP.Match.create(match);
+ this._available = {
+ tokens : [],
+ spans : [],
+ rels : []
+ };
+ for (var i = 0; i < available.length; i++) {
+ var term = available[i];
+ // Create info layer objects
+ try {
+ var layer = KorAP.InfoLayer.create(term);
+ this._available[layer.type].push(layer);
+ }
+ catch (e) {
+ continue;
+ };
+ };
+ return this;
+ },
+
+
+ /**
+ * Return a list of parseable tree annotations.
+ */
+ getSpans : function () {
+ return this._available.spans;
+ },
+
+
+ /**
+ * Return a list of parseable token annotations.
+ */
+ getTokens : function () {
+ return this._available.tokens;
+ },
+
+
+ /**
+ * Return a list of parseable relation annotations.
+ */
+ getRels : function () {
+ return this._available.rels;
+ },
+
+
+ getTable : function (tokens) {
+ var focus = [];
+
+ // Get all tokens
+ if (tokens === undefined) {
+ focus = this.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)
+ return;
+
+ // Get info (may be cached)
+ var matchResponse = KorAP.API.getMatchInfo(
+ this._match,
+ { 'spans' : true, 'layer' : focus }
+ );
+
+ // Get snippet from match info
+ if (matchResponse["snippet"] !== undefined) {
+ this._table = KorAP.InfoTable.create(matchResponse["snippet"]);
+ return this._table;
+ };
+
+ return null;
+ }
+
+ /*
+ // Parse snippet for table visualization
+ getTree : function (foundry, layer) {
+ },
+ */
+ };
+
+ KorAP.Match = {
+ create : function (match) {
+ return Object.create(KorAP.Match)._init(match);
+ },
+ _init : function (match) {
+ for (var i in KorAP._matchTerms) {
+ var term = KorAP._matchTerms[i];
+ if (match[term] !== undefined) {
+ this[term] = match[term];
+ }
+ else {
+ this[term] = undefined;
+ }
+ };
+ return this;
+ },
+ };
+
+ /**
+ *
+ * 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.InfoTable = {
+ create : function (snippet) {
+ return Object.create(KorAP.InfoTable)._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);
+
+ this._layer = undefined;
+ this._foundry = undefined;
+
+ 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])
+ this._foundry[foundry] = {};
+ this._foundry[foundry][layer] = 1;
+
+ // Set layer
+ if (!this._layer[layer])
+ 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];
+ },
+ element : function () {
+ var ce = document.createElement;
+ // First the legend table
+ /*
+ var table = ce('table');
+ var row = ce('tr');
+ table.appendChild(tr);
+ */
+ }
+ };
+
+
+ /*
+ KorAP.InfoFoundryLayer = {};
+ KorAP.InfoTree = {};
+ KorAP.InfoTable = {};
+ */
+}(this.KorAP));
diff --git a/public/js/src/menu.js b/public/js/src/menu.js
index 3d0017d..8ccf740 100644
--- a/public/js/src/menu.js
+++ b/public/js/src/menu.js
@@ -83,6 +83,9 @@
this.next();
break;
case 39: // 'Right'
+ if (this._prefix.active())
+ break;
+
var item = this.liveItem(this._position);
if (item["further"] !== undefined) {
item["further"].bind(item).apply();
@@ -223,6 +226,7 @@
* @param {string} Prefix for filtering the list
*/
show : function () {
+
// Initialize the list
if (!this._initList())
return false;
@@ -233,6 +237,7 @@
// Set the first element to active
// Todo: Or the last element chosen
this.liveItem(0).active(true);
+ this._prefix.active(false);
this._active = this._list[0];
this._position = 0;
@@ -466,8 +471,13 @@
var newItem;
// Set new live item
- var oldItem = this.liveItem(this._position++);
- oldItem.active(false);
+ if (!this._prefix.active()) {
+ var oldItem = this.liveItem(this._position);
+ oldItem.active(false);
+ };
+
+ this._position++;
+
newItem = this.liveItem(this._position);
// The next element is undefined - roll to top or to prefix
@@ -501,6 +511,42 @@
newItem.active(true);
},
+ /*
+ * Page down to the first item on the next page
+ */
+ /*
+ nextPage : function () {
+
+ // Prefix is active
+ if (this._prefix.active()) {
+ this._prefix.active(false);
+ }
+
+ // Last item is chosen
+ else if (this._position >= this.limit() + this._offset) {
+
+ this._position = this.limit() + this._offset - 1;
+ newItem = this.liveItem(this._position);
+ var oldItem = this.liveItem(this._position--);
+ oldItem.active(false);
+ }
+
+ // Last item of page is chosen
+ else if (0) {
+
+ // Jump to last item
+ else {
+ var oldItem = this.liveItem(this._position);
+ oldItem.active(false);
+
+ this._position = this.limit() + this._offset - 1;
+ newItem = this.liveItem(this._position);
+ };
+
+ newItem.active(true);
+ },
+ */
+
/*
* Make the previous item in the menu active
@@ -508,8 +554,10 @@
prev : function () {
// No active element set
- if (this._position == -1)
+ if (this._position === -1) {
return;
+ // TODO: Choose last item
+ };
var newItem;
@@ -534,7 +582,6 @@
this._position = this.liveLength() - 1;
if (prefix.isSet() && !prefix.active()) {
-
this._position++;
prefix.active(true);
return;
@@ -792,6 +839,10 @@
// 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 () {