Added tutorial and session mechanism
diff --git a/public/js/src/api.js b/public/js/src/api.js
new file mode 100644
index 0000000..ed33cc2
--- /dev/null
+++ b/public/js/src/api.js
@@ -0,0 +1,80 @@
+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';
+
+ // 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
+ // r.addEventListener("progress", updateProgress, false);
+ // http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml
+ // http://stackoverflow.com/questions/6112744/load-javascript-on-demand
+
+ KorAP.API = {
+ getMatchInfo : function (match, param, cb) {
+ // match is a KorAP.Match object
+
+ var url = KorAP.URL;
+ url += '/corpus';
+ url += '/' + match.corpusID;
+ url += '/' + match.docID + '.' + match.textID; // TODO
+ url += '/' + match.matchID;
+
+ // { spans: true, layer:x, foundry : y}
+ if (param['spans'] == true) {
+ url += '?spans=true';
+ if (param['foundry'] !== undefined)
+ url += '&foundry=' + param['foundry'];
+ if (param['layer'] !== undefined)
+ url += '&layer=' + param['layer'];
+ }
+
+ // { spans : false, layer: [Array of KorAP.InfoLayer] }
+ else {
+ // TODO
+ url += '?spans=false';
+ }
+
+ 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 () {
+ /*
+ States:
+ 0 - unsent (prior to open)
+ 1 - opened (prior to send)
+ 2 - headers received
+ 3 - loading (responseText has partial data)
+ 4 - done
+ */
+ if (this.readyState == 4) {
+ if (this.status === 200)
+ onload(JSON.parse(this.responseText));
+ else
+ KorAP.log(this.status, this.statusText);
+ }
+ };
+ req.ontimeout = function () {
+ KorAP.log(0, 'Request Timeout');
+ };
+ req.send();
+ }
+ };
+
+}(this.KorAP));
diff --git a/public/js/src/hint.js b/public/js/src/hint.js
index 8011bee..80631ec 100644
--- a/public/js/src/hint.js
+++ b/public/js/src/hint.js
@@ -52,7 +52,7 @@
// Update position of the mirror
var that = this;
- window.resize = function () {
+ window.onresize = function () {
that.reposition();
};
@@ -311,6 +311,7 @@
var that = this;
+
// Add event listener for key pressed down
inputFieldElement.addEventListener(
"keypress", function (e) {
diff --git a/public/js/src/match.js b/public/js/src/match.js
index b974031..4e3786b 100644
--- a/public/js/src/match.js
+++ b/public/js/src/match.js
@@ -35,8 +35,10 @@
KorAP.API = KorAP.API || {};
// TODO: Make this async
- KorAP.API.getMatchInfo = KorAP.API.getMatchInfo || function () { return {} };
-
+ KorAP.API.getMatchInfo = KorAP.API.getMatchInfo || function () {
+ KorAP.log(0, 'KorAP.API.getMatchInfo() not implemented')
+ return {};
+ };
/**
* Match object
@@ -302,7 +304,7 @@
/**
* Retrieve and parse snippet for table representation
*/
- getTable : function (tokens) {
+ getTable : function (tokens, cb) {
var focus = [];
// Get all tokens
@@ -330,51 +332,56 @@
// No tokens chosen
if (focus.length == 0)
- return;
+ cb(null);
// Get info (may be cached)
// TODO: Async
- var matchResponse = KorAP.API.getMatchInfo(
+ KorAP.API.getMatchInfo(
this._match,
- { 'spans' : false, 'layer' : focus }
+ { '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)
);
- // Get snippet from match info
- if (matchResponse["snippet"] !== undefined) {
- this._table = KorAP.MatchTable.create(matchResponse["snippet"]);
- return this._table;
- };
-
+/*
// Todo: Store the table as a hash of the focus
-
return null;
+*/
},
/**
* Retrieve and parse snippet for tree representation
*/
- getTree : function (foundry, layer) {
+ getTree : function (foundry, layer, cb) {
var focus = [];
- // TODO: Async
- var matchResponse = KorAP.API.getMatchInfo(
+ // 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)
);
-
- // TODO: Support and cache multiple trees
-
- // Get snippet from match info
- if (matchResponse["snippet"] !== undefined) {
- // Todo: This should be cached somehow
- return KorAP.MatchTree.create(matchResponse["snippet"]);
- };
-
- return null;
},
/**
@@ -397,13 +404,7 @@
/**
* Add a new tree view to the list
*/
- addTree : function (foundry, layer) {
- var treeObj = this.getTree(foundry, layer);
-
- // Something went wrong - probably log!!!
- if (treeObj === null)
- return;
-
+ addTree : function (foundry, layer, cb) {
var matchtree = document.createElement('div');
matchtree.classList.add('matchtree');
@@ -416,7 +417,7 @@
var tree = matchtree.appendChild(
document.createElement('div')
);
- tree.appendChild(treeObj.element());
+
this._element.insertBefore(matchtree, this._element.lastChild);
var close = tree.appendChild(document.createElement('em'));
@@ -427,11 +428,24 @@
}
);
- // 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();
+ // 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);
+ });
},
/**
@@ -449,9 +463,15 @@
// Append default table
var matchtable = document.createElement('div');
matchtable.classList.add('matchtable');
- matchtable.appendChild(this.getTable().element());
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) {
@@ -917,23 +937,33 @@
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, 'width', v.width);
- rect.setAttributeNS(null, 'height', v.height);
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
- 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);
+ 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);
}
);
diff --git a/public/js/src/menu.js b/public/js/src/menu.js
index 3267b69..1cf3455 100644
--- a/public/js/src/menu.js
+++ b/public/js/src/menu.js
@@ -154,6 +154,7 @@
e.style.outline = 0;
e.setAttribute('tabindex', 0);
e.classList.add('menu');
+ e.classList.add('roll');
e.appendChild(this._prefix.element());
// This has to be cleaned up later on
diff --git a/public/js/src/session.js b/public/js/src/session.js
new file mode 100644
index 0000000..a10ce81
--- /dev/null
+++ b/public/js/src/session.js
@@ -0,0 +1,80 @@
+/**
+ * Simple cookie based session library that stores
+ * values in JSON encoded cookies.
+ *
+ * @author Nils Diewald
+ */
+var KorAP = KorAP || {};
+
+(function (KorAP) {
+ "use strict";
+
+
+ KorAP.Session = {
+
+ /**
+ * 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;
+ },
+
+ /**
+ * 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()];
+ },
+
+ /**
+ * 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]));
+ return;
+ };
+ };
+ }
+ }
+
+}(this.KorAP));
diff --git a/public/js/src/tutorial.js b/public/js/src/tutorial.js
new file mode 100644
index 0000000..03cf903
--- /dev/null
+++ b/public/js/src/tutorial.js
@@ -0,0 +1,133 @@
+/**
+ * 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) {
+ "use strict";
+
+ // Localization values
+ var loc = (KorAP.Locale = KorAP.Locale || {} );
+ loc.CLOSE = loc.CLOSE || 'Close';
+
+ KorAP.Tutorial = {
+
+ /**
+ * Create new tutorial object.
+ * Accepts an element to bind the tutorial window to.
+ */
+ create : function (obj) {
+ return Object.create(KorAP.Tutorial)._init(obj);
+ },
+
+ // Initialize Tutorial object
+ _init : function (obj) {
+ this._session = KorAP.Session.create();
+ this._show = obj;
+ this.start = obj.getAttribute('href');
+ obj.removeAttribute('href');
+ var that = this;
+ obj.onclick = function () {
+ that.show();
+ };
+
+ // Injects a tutorial div to the body
+ var div = document.createElement('div');
+ div.setAttribute('id', 'tutorial');
+ div.style.display = 'none';
+ document.getElementsByTagName('body')[0].appendChild(div);
+ this._iframe = null;
+
+ this._element = div;
+ return this;
+ },
+
+ show : function () {
+ var element = this._element;
+ if (element.style.display === 'block')
+ return;
+
+ if (this._iframe === null) {
+ this._iframe = document.createElement('iframe');
+ this._iframe.setAttribute('src', this.getPage() || this.start);
+
+ 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'))
+ .appendChild(document.createTextNode(loc.CLOSE));
+ close.classList.add('close');
+ close.setAttribute('title', loc.CLOSE);
+ close.onclick = function () {
+ element.style.display = 'none';
+ };
+
+ // Add open in new window button
+ // Add scroll to top 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);
+ */
+
+ ul.appendChild(close);
+
+ element.appendChild(ul);
+ element.appendChild(this._iframe);
+ };
+
+ element.style.display = 'block';
+ },
+
+ /**
+ * Close tutorial window.
+ */
+ hide : function () {
+ this._element.display.style = 'none';
+ },
+
+ /**
+ * Set a page to be the current tutorial page.
+ * Expects either a string or an element.
+ */
+ setPage : function (obj) {
+ var page = obj;
+ if (typeof page != 'string') {
+ page = window.location.pathname + window.location.search;
+ for (i = 1; i < 5; i++) {
+ if (obj.nodeName === 'SECTION') {
+ if (obj.hasAttribute('id'))
+ page += '#' + obj.getAttribute('id');
+ break;
+ }
+ else if (obj.nodeName === 'PRE' && obj.hasAttribute('id')) {
+ page += '#' + obj.getAttribute('id');
+ break;
+ }
+ else {
+ obj = obj.parentNode;
+ };
+ };
+ };
+ this._session.set('tutpage', page);
+ },
+
+ /**
+ * Get the current tutorial URL
+ */
+ getPage : function () {
+ this._session.get('tutpage');
+ },
+ }
+}(this.KorAP));
diff --git a/public/js/src/util.js b/public/js/src/util.js
index 3adf5a0..ecaad6f 100644
--- a/public/js/src/util.js
+++ b/public/js/src/util.js
@@ -36,6 +36,7 @@
* Initialize user interface elements
*/
KorAP.init = function () {
+ var obj = Object.create(KorAP.init);
/**
* Add actions to match entries
@@ -81,9 +82,43 @@
};
/**
- * Init hint helper
+ * Init vc
*/
- KorAP.Hint.create();
+ var input = document.getElementById('vc');
+ if (input) {
+ input.style.display = 'none';
+ var vcname = document.createElement('span');
+ vcname.setAttribute('id', 'vc-choose');
+ vcname.appendChild(
+ document.createTextNode(
+ document.getElementById('vc-name').value
+ )
+ );
+ input.parentNode.insertBefore(vcname, input);
+
+ vcname.onclick = function () {
+ var vc = KorAP.VirtualCollection.render(vcExample);
+ var view = document.getElementById('vc-view');
+ view.appendChild(vc.element());
+ };
+ };
+
+ /**
+ * Init Tutorial view
+ */
+ obj.tutorial = KorAP.Tutorial.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();
+ return obj;
};
}(this.KorAP));