Major redesign of JS and Sass assets
diff --git a/public/js/src/matchInfo.js b/public/js/src/match.js
similarity index 77%
rename from public/js/src/matchInfo.js
rename to public/js/src/match.js
index 30bd7b5..f8fdd91 100644
--- a/public/js/src/matchInfo.js
+++ b/public/js/src/match.js
@@ -1,11 +1,11 @@
/**
- * Visualize annotations.
+ * Get information on matches,
+ * generate annotation tables and trees.
*
* @author Nils Diewald
*/
-// require menu.js
+// require menu.js, dagre
/*
- * - Scroll with a static left legend.
* - Highlight (at least mark as bold) the match
* - Scroll to match vertically per default
*/
@@ -23,11 +23,13 @@
// Localization values
var loc = (KorAP.Locale = KorAP.Locale || {} );
- loc.ADDTREE = loc.ADDTREE || 'Add tree view';
+ 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"];
+ KorAP._TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
+ KorAP._matchTerms = ['corpusID', 'docID', 'textID', 'matchID', 'available'];
// API requests
KorAP.API = KorAP.API || {};
@@ -35,42 +37,75 @@
// TODO: Make this async
KorAP.API.getMatchInfo = KorAP.API.getMatchInfo || function () { return {} };
- KorAP.MatchInfo = {
+
+ /**
+ * Match object
+ */
+ KorAP.Match = {
/**
* 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);
+ create : function (match) {
+ return Object.create(KorAP.Match)._init(match);
},
/**
- * Destroy this match information view.
+ * Initialize match.
*/
- destroy : function () {
+ _init : function (match) {
+ this._element = null;
- // Remove circular reference
- if (this._treeMenu !== undefined)
- delete this._treeMenu["info"];
+ // No match defined
+ if (arguments.length < 1 ||
+ match === null ||
+ match === undefined) {
+ throw new Error('Missing parameters');
+ }
- this._treeMenu.destroy();
- this._treeMenu = undefined;
- },
+ // Match defined as a node
+ else if (match instanceof Node) {
+ this._element = match;
- _init : function (match, available) {
- this._match = KorAP.Match.create(match);
+ // Circular reference !!
+ match["_match"] = this;
+
+ this.corpusID = match.getAttribute('data-corpus-id'),
+ this.docID = match.getAttribute('data-doc-id'),
+ this.textID = match.getAttribute('data-text-id'),
+ this.matchID = match.getAttribute('data-match-id')
+
+ // List of available annotations
+ this.available = match.getAttribute('data-available-info').split(' ');
+ }
+
+ // Match as an object
+ else {
+
+ // Iterate over allowed match terms
+ for (var i in KorAP._matchTerms) {
+ var term = KorAP._matchTerms[i];
+ if (match[term] !== undefined) {
+ this[term] = match[term];
+ }
+ else {
+ this[term] = undefined;
+ }
+ };
+ };
+
this._available = {
tokens : [],
- spans : [],
- rels : []
+ spans : [],
+ rels : []
};
- for (var i = 0; i < available.length; i++) {
- var term = available[i];
+
+ // Iterate over info layers
+ for (var i = 0; i < this.available.length; i++) {
+ var term = this.available[i];
+
// Create info layer objects
try {
var layer = KorAP.InfoLayer.create(term);
@@ -80,6 +115,7 @@
continue;
};
};
+
return this;
},
@@ -107,16 +143,160 @@
return this._available.rels;
},
+ /**
+ * Open match
+ */
+ open : function () {
+
+ // Add actions unless it's already activated
+ var element = this._element;
+
+ // There is an element to open
+ if (this._element === undefined || this._element === null)
+ return false;
+
+ // The element is already opened
+ if (element.classList.contains('active'))
+ return false;
+
+ // Add active class to element
+ element.classList.add('active');
+
+ // Create action buttons
+ var ul = document.createElement('ul');
+ ul.classList.add('action', 'right');
+ element.appendChild(ul);
+
+ // Use localization
+ var loc = KorAP.Locale;
+
+ // Add close button
+ var close = document.createElement('li');
+ close.appendChild(document.createElement('span'))
+ .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'))
+ .appendChild(document.createTextNode(loc.SHOWINFO));
+ info.classList.add('info');
+ info.setAttribute('title', loc.SHOWINFO);
+
+ var that = this;
+
+ // Close match
+ close.addEventListener('click', function (e) {
+ e.halt();
+ that.close()
+ });
+
+ // Add information, unless it already exists
+ info.addEventListener('click', function (e) {
+ e.halt();
+ that.info();
+ });
+
+ ul.appendChild(close);
+ ul.appendChild(info);
+
+ return true;
+ },
+
/**
- * Get table object.
+ * Close info view
+ */
+ close : function () {
+ this._element.classList.remove('active');
+
+/*
+ if (this._info !== undefined) {
+ this._info.destroy();
+ };
+*/
+ },
+
+
+
+ /**
+ * Get and open associated match info.
+ */
+ info : function () {
+
+ // Create match info
+ if (this._info === undefined)
+ this._info = KorAP.MatchInfo.create(this);
+
+ // There is an element to append
+ if (this._element === undefined ||
+ this._element === null)
+ return this._info;
+
+ // Info is already activated
+ if (this._info._elemet !== undefined)
+ return this._info;
+
+ // Append element to match
+ this._element.children[0].appendChild(
+ this._info.element()
+ );
+
+ return this._info;
+ },
+
+
+ /**
+ * Get match element.
+ */
+ element : function () {
+
+ // May be null
+ 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;
+ return this;
+ },
+
+
+ /**
+ * Get match object
+ */
+ match : function () {
+ return this._match;
+ },
+
+
+ /**
+ * Retrieve and parse snippet for table representation
*/
getTable : function (tokens) {
var focus = [];
// Get all tokens
if (tokens === undefined) {
- focus = this.getTokens();
+ focus = this._match.getTokens();
}
// Get only some tokens
@@ -154,10 +334,15 @@
return this._table;
};
+ // Todo: Store the table as a hash of the focus
+
return null;
},
- // Parse snippet for table visualization
+
+ /**
+ * Retrieve and parse snippet for tree representation
+ */
getTree : function (foundry, layer) {
var focus = [];
@@ -182,6 +367,23 @@
},
/**
+ * 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) {
@@ -225,6 +427,7 @@
* Create match information view.
*/
element : function () {
+
if (this._element !== undefined)
return this._element;
@@ -238,7 +441,8 @@
matchtable.appendChild(this.getTable().element());
info.appendChild(matchtable);
- var spanLayers = this.getSpans().sort(
+ // Get spans
+ var spanLayers = this._match.getSpans().sort(
function (a, b) {
if (a.foundry < b.foundry) {
return -1;
@@ -272,6 +476,7 @@
// 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();
@@ -285,8 +490,15 @@
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;
@@ -295,23 +507,7 @@
}
};
- 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;
- },
- };
+
/**
*
@@ -765,7 +961,8 @@
var menu = this.menu();
menu.hide();
e.halt();
- menu.info().addTree(this._foundry, this._layer);
+ if (menu.info() !== undefined)
+ menu.info().addTree(this._foundry, this._layer);
},
_init : function (params) {
@@ -791,6 +988,7 @@
.upgradeTo(KorAP.MatchTreeMenu)
._init(KorAP.MatchTreeItem, undefined, params);
obj.limit(6);
+
obj._info = info;
// This is only domspecific
diff --git a/public/js/src/util.js b/public/js/src/util.js
new file mode 100644
index 0000000..0f0f114
--- /dev/null
+++ b/public/js/src/util.js
@@ -0,0 +1,134 @@
+/**
+ * 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";
+
+ KorAP.init = function () {
+
+ /**
+ * Add actions to match entries
+ */
+ var inactiveLi = document.querySelectorAll('#search > ol > li:not(.active)');
+ var i = 0;
+ for (i = 0; i < inactiveLi.length; i++) {
+ inactiveLi[i].addEventListener('click', function () {
+
+ if (this._match !== undefined) {
+ this._match.open();
+ console.log('already open');
+ }
+ else {
+ KorAP.Match.create(this).open();
+ console.log('newly open');
+ }
+
+
+ });
+ };
+
+ /**
+ * Toggle the alignment (left <=> right)
+ */
+ if (i > 0) {
+ var br = document.getElementById('button-right');
+ if (br !== undefined) {
+ var toggle = document.createElement('a');
+ toggle.setAttribute('title', 'toggle Alignment');
+ // Todo: Reuse old alignment from cookie!
+ var cl = toggle.classList;
+ cl.add('align');
+ cl.add('right');
+ toggle.addEventListener(
+ 'click',
+ function (e) {
+ var ol = document.querySelector('#search > ol');
+ ol.toggleClass("align-left", "align-right");
+ this.toggleClass("left", "right");
+ });
+ toggle.appendChild(document.createElement('span'))
+ .appendChild(document.createTextNode('Alignment'));
+ br.appendChild(toggle);
+ };
+ };
+ };
+
+ /*
+ function _openMatch (e) {
+ e.halt();
+ this.classList.add("active");
+ var matchElement = this;
+
+ // Todo: Add object to element
+ var ul = document.createElement('ul');
+ ul.classList.add('action', 'right');
+ matchElement.appendChild(ul);
+
+ // Todo:: Localize!
+ var close = document.createElement('li');
+ close.appendChild(document.createElement('span'))
+ .appendChild(document.createTextNode('Close'));
+ close.classList.add('close');
+ close.setAttribute('title', 'Close');
+
+ close.addEventListener('click', function (ie) {
+ ie.halt();
+ var match = matchElement['_match'];
+ match.destroy();
+ matchElement.classList.remove('active');
+ matchElement.removeChild(ul);
+ });
+
+ // Todo:: Localize!
+ var info = document.createElement('li');
+ info.appendChild(document.createElement('span'))
+ .appendChild(document.createTextNode('Info'));
+ info.classList.add('info');
+ info.setAttribute('title', 'Information');
+
+ // Add information, unless it already exists
+ info.addEventListener('click', function (ie) {
+ ie.halt();
+ KorAP.Match.create(matchElement).addInfo();
+ });
+
+ ul.appendChild(close);
+ ul.appendChild(info);
+ };
+*/
+
+ /**
+ function _closeMatch (e) {
+ e.halt();
+ this.parentNode.parentNode.classList.remove("active");
+ };
+ */
+
+}(this.KorAP));