Release preparation, documentation, fixing vc bugs
diff --git a/Gruntfile.js b/Gruntfile.js
index d949a90..7901237 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -24,11 +24,13 @@
paths : {
'lib': '../lib'
},
+
+ // TODO: add language versions
wrap:true,
- include : ['app'],
- insertRequire: ['app'],
+ include : ['app/en'],
+ insertRequire: ['app/en'],
name: 'lib/almond',
- out: 'public/js/kalamar-<%= pkg.version %>.js'
+ out: 'public/js/kalamar-<%= pkg.version %>-en.js'
}
}
},
diff --git a/LICENSE b/LICENSE
index 98a0bdd..f1083af 100755
--- a/LICENSE
+++ b/LICENSE
@@ -1,202 +1,24 @@
- The Artistic License 2.0
+Copyright (c) 2015, IDS Mannheim
+All rights reserved.
- Copyright (c) 2000-2006, The Perl Foundation.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
-Preamble
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
-This license establishes the terms under which a given free software
-Package may be copied, modified, distributed, and/or redistributed.
-The intent is that the Copyright Holder maintains some artistic
-control over the development of that Package while still keeping the
-Package available as open source and free software.
-
-You are always permitted to make arrangements wholly outside of this
-license directly with the Copyright Holder of a given Package. If the
-terms of this license do not permit the full use that you propose to
-make of the Package, you should contact the Copyright Holder and seek
-a different licensing arrangement.
-
-Definitions
-
- "Copyright Holder" means the individual(s) or organization(s)
- named in the copyright notice for the entire Package.
-
- "Contributor" means any party that has contributed code or other
- material to the Package, in accordance with the Copyright Holder's
- procedures.
-
- "You" and "your" means any person who would like to copy,
- distribute, or modify the Package.
-
- "Package" means the collection of files distributed by the
- Copyright Holder, and derivatives of that collection and/or of
- those files. A given Package may consist of either the Standard
- Version, or a Modified Version.
-
- "Distribute" means providing a copy of the Package or making it
- accessible to anyone else, or in the case of a company or
- organization, to others outside of your company or organization.
-
- "Distributor Fee" means any fee that you charge for Distributing
- this Package or providing support for this Package to another
- party. It does not mean licensing fees.
-
- "Standard Version" refers to the Package if it has not been
- modified, or has been modified only in ways explicitly requested
- by the Copyright Holder.
-
- "Modified Version" means the Package, if it has been changed, and
- such changes were not explicitly requested by the Copyright
- Holder.
-
- "Original License" means this Artistic License as Distributed with
- the Standard Version of the Package, in its current version or as
- it may be modified by The Perl Foundation in the future.
-
- "Source" form means the source code, documentation source, and
- configuration files for the Package.
-
- "Compiled" form means the compiled bytecode, object code, binary,
- or any other form resulting from mechanical transformation or
- translation of the Source form.
-
-
-Permission for Use and Modification Without Distribution
-
-(1) You are permitted to use the Standard Version and create and use
-Modified Versions for any purpose without restriction, provided that
-you do not Distribute the Modified Version.
-
-
-Permissions for Redistribution of the Standard Version
-
-(2) You may Distribute verbatim copies of the Source form of the
-Standard Version of this Package in any medium without restriction,
-either gratis or for a Distributor Fee, provided that you duplicate
-all of the original copyright notices and associated disclaimers. At
-your discretion, such verbatim copies may or may not include a
-Compiled form of the Package.
-
-(3) You may apply any bug fixes, portability changes, and other
-modifications made available from the Copyright Holder. The resulting
-Package will still be considered the Standard Version, and as such
-will be subject to the Original License.
-
-
-Distribution of Modified Versions of the Package as Source
-
-(4) You may Distribute your Modified Version as Source (either gratis
-or for a Distributor Fee, and with or without a Compiled form of the
-Modified Version) provided that you clearly document how it differs
-from the Standard Version, including, but not limited to, documenting
-any non-standard features, executables, or modules, and provided that
-you do at least ONE of the following:
-
- (a) make the Modified Version available to the Copyright Holder
- of the Standard Version, under the Original License, so that the
- Copyright Holder may include your modifications in the Standard
- Version.
-
- (b) ensure that installation of your Modified Version does not
- prevent the user installing or running the Standard Version. In
- addition, the Modified Version must bear a name that is different
- from the name of the Standard Version.
-
- (c) allow anyone who receives a copy of the Modified Version to
- make the Source form of the Modified Version available to others
- under
-
- (i) the Original License or
-
- (ii) a license that permits the licensee to freely copy,
- modify and redistribute the Modified Version using the same
- licensing terms that apply to the copy that the licensee
- received, and requires that the Source form of the Modified
- Version, and of any works derived from it, be made freely
- available in that license fees are prohibited but Distributor
- Fees are allowed.
-
-
-Distribution of Compiled Forms of the Standard Version
-or Modified Versions without the Source
-
-(5) You may Distribute Compiled forms of the Standard Version without
-the Source, provided that you include complete instructions on how to
-get the Source of the Standard Version. Such instructions must be
-valid at the time of your distribution. If these instructions, at any
-time while you are carrying out such distribution, become invalid, you
-must provide new instructions on demand or cease further distribution.
-If you provide valid instructions or cease distribution within thirty
-days after you become aware that the instructions are invalid, then
-you do not forfeit any of your rights under this license.
-
-(6) You may Distribute a Modified Version in Compiled form without
-the Source, provided that you comply with Section 4 with respect to
-the Source of the Modified Version.
-
-
-Aggregating or Linking the Package
-
-(7) You may aggregate the Package (either the Standard Version or
-Modified Version) with other packages and Distribute the resulting
-aggregation provided that you do not charge a licensing fee for the
-Package. Distributor Fees are permitted, and licensing fees for other
-components in the aggregation are permitted. The terms of this license
-apply to the use and Distribution of the Standard or Modified Versions
-as included in the aggregation.
-
-(8) You are permitted to link Modified and Standard Versions with
-other works, to embed the Package in a larger work of your own, or to
-build stand-alone binary or bytecode versions of applications that
-include the Package, and Distribute the result without restriction,
-provided the result does not expose a direct interface to the Package.
-
-
-Items That are Not Considered Part of a Modified Version
-
-(9) Works (including, but not limited to, modules and scripts) that
-merely extend or make use of the Package, do not, by themselves, cause
-the Package to be a Modified Version. In addition, such works are not
-considered parts of the Package itself, and are not subject to the
-terms of this license.
-
-
-General Provisions
-
-(10) Any use, modification, and distribution of the Standard or
-Modified Versions is governed by this Artistic License. By using,
-modifying or distributing the Package, you accept this license. Do not
-use, modify, or distribute the Package, if you do not accept this
-license.
-
-(11) If your Modified Version has been derived from a Modified
-Version made by someone other than you, you are nevertheless required
-to ensure that your Modified Version complies with the requirements of
-this license.
-
-(12) This license does not grant you the right to use any trademark,
-service mark, tradename, or logo of the Copyright Holder.
-
-(13) This license includes the non-exclusive, worldwide,
-free-of-charge patent license to make, have made, use, offer to sell,
-sell, import and otherwise transfer the Package with respect to any
-patent claims licensable by the Copyright Holder that are necessarily
-infringed by the Package. If you institute patent litigation
-(including a cross-claim or counterclaim) against any party alleging
-that the Package constitutes direct or contributory patent
-infringement, then this Artistic License to you shall terminate on the
-date that such litigation is filed.
-
-(14) Disclaimer of Warranty:
-THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
-IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
-WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
-NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL
-LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
\ No newline at end of file
diff --git a/dev/demo/menudemo.js b/dev/demo/menudemo.js
index e089e89..055963b 100644
--- a/dev/demo/menudemo.js
+++ b/dev/demo/menudemo.js
@@ -3,6 +3,7 @@
});
require(['menu','menu/item', 'menu/prefix'], function (menuClass, itemClass, prefixClass) {
+
var OwnMenuItemClass = {
create : function (params) {
return Object.create(itemClass).upgradeTo(this)._init(params);
diff --git a/dev/js/spec/hintSpec.js b/dev/js/spec/hintSpec.js
index 20935e1..ac50f0f 100644
--- a/dev/js/spec/hintSpec.js
+++ b/dev/js/spec/hintSpec.js
@@ -74,13 +74,13 @@
expect(input.selectionStart).toEqual(5);
expect(inputField.element().selectionStart).toEqual(5);
- expect(inputField.split()[0]).toEqual("abcde");
- expect(inputField.split()[1]).toEqual("fghijklmno");
+ expect(inputField._split()[0]).toEqual("abcde");
+ expect(inputField._split()[1]).toEqual("fghijklmno");
inputField.insert("xyz");
expect(inputField.value()).toEqual('abcdexyzfghijklmno');
- expect(inputField.split()[0]).toEqual("abcdexyz");
- expect(inputField.split()[1]).toEqual("fghijklmno");
+ expect(inputField._split()[0]).toEqual("abcdexyz");
+ expect(inputField._split()[1]).toEqual("fghijklmno");
});
it('should be correctly positioned', function () {
@@ -98,7 +98,7 @@
var inputField = inputClass.create(input);
expect(inputField.value()).toEqual("abcdefghijklmno");
expect(inputField.element().selectionStart).toEqual(5);
- expect(inputField.split()[0]).toEqual("abcde");
+ expect(inputField._split()[0]).toEqual("abcde");
expect(inputField.context()).toEqual("abcde");
});
@@ -385,7 +385,6 @@
it('should be initializable', function () {
var menu = menuClass.create(null, "cnx/", list);
- expect(menu.context()).toEqual('cnx/');
expect(menu.element().nodeName).toEqual('UL');
expect(menu.element().style.opacity).toEqual("0");
diff --git a/dev/js/spec/menuSpec.js b/dev/js/spec/menuSpec.js
index 7436e4d..999ec2b 100644
--- a/dev/js/spec/menuSpec.js
+++ b/dev/js/spec/menuSpec.js
@@ -1,7 +1,8 @@
define(['menu'], function () {
var menuItemClass = require('menu/item');
- var menuClass = require('menu');
+ var prefixClass = require('menu/prefix');
+ var menuClass = require('menu');
// The OwnMenu item
KorAP.OwnMenuItem = {
@@ -800,13 +801,13 @@
expect(menu.shownItem(0).name()).toEqual("Autor");
expect(menu.element().childNodes[1].innerHTML).toEqual("<strong><mark>Au</mark>tor</strong>");
- menu._prefix.backspace();
+ menu._prefix.chop();
expect(menu.show()).toBe(true);
expect(menu.prefix()).toEqual("a");
expect(menu.shownItem(0).name()).toEqual("Autor");
expect(menu.element().childNodes[1].innerHTML).toEqual("<strong><mark>A</mark>utor</strong>");
- menu._prefix.backspace();
+ menu._prefix.chop();
expect(menu.show()).toBe(true);
expect(menu.prefix()).toEqual("");
expect(menu.shownItem(0).name()).toEqual("Titel");
@@ -821,7 +822,7 @@
expect(menu.shownItem(3)).toBe(undefined);
// Forward
- menu._prefix.backspace();
+ menu._prefix.chop();
expect(menu.show()).toBe(true);
expect(menu.prefix()).toEqual("");
expect(menu.shownItem(0).name()).toEqual("Titel");
@@ -1054,7 +1055,78 @@
xit('should be page downable');
xit('should be page upable');
- xit('should scroll to a chosen value')
- xit('should highlight a chosen value')
+ xit('should scroll to a chosen value');
+ xit('should highlight a chosen value');
+ });
+
+ describe('KorAP.Prefix', function () {
+ it('should be initializable', function () {
+ var p = prefixClass.create();
+ expect(p.element().classList.contains('pref')).toBeTruthy();
+ expect(p.isSet()).not.toBeTruthy();
+
+/*
+ expect(mi.lcField()).toEqual(' baum');
+*/
+
+ });
+
+ it('should be modifiable', function () {
+ var p = prefixClass.create();
+ expect(p.value()).toEqual('');
+ expect(p.element().firstChild).toBeNull();
+
+ // Set string
+ expect(p.value('Test')).toEqual('Test');
+ expect(p.value()).toEqual('Test');
+ expect(p.element().firstChild.nodeValue).toEqual('Test');
+
+ // Add string
+ expect(p.add('ified')).toEqual('Testified');
+ expect(p.value()).toEqual('Testified');
+ expect(p.element().firstChild.nodeValue).toEqual('Testified');
+
+ // Clear string
+ p.clear();
+ expect(p.value()).toEqual('');
+ expect(p.element().firstChild).toBeNull();
+
+ // Set string
+ expect(p.value('Test')).toEqual('Test');
+ expect(p.value()).toEqual('Test');
+ expect(p.element().firstChild.nodeValue).toEqual('Test');
+
+ expect(p.chop()).toEqual('Tes');
+ expect(p.value()).toEqual('Tes');
+ expect(p.element().firstChild.nodeValue).toEqual('Tes');
+
+ expect(p.chop()).toEqual('Te');
+ expect(p.value()).toEqual('Te');
+ expect(p.element().firstChild.nodeValue).toEqual('Te');
+
+ expect(p.chop()).toEqual('T');
+ expect(p.value()).toEqual('T');
+ expect(p.element().firstChild.nodeValue).toEqual('T');
+
+ expect(p.chop()).toEqual('');
+ expect(p.value()).toEqual('');
+ expect(p.element().firstChild).toBeNull();
+ });
+
+ it('should be activatable', function () {
+ var p = prefixClass.create();
+ expect(p.value()).toEqual('');
+ expect(p.element().firstChild).toBeNull();
+
+ expect(p.value('Test')).toEqual('Test');
+ expect(p.element().firstChild.nodeValue).toEqual('Test');
+
+ expect(p.active()).not.toBeTruthy();
+ expect(p.element().classList.contains('active')).not.toBeTruthy();
+
+ p.active(true);
+ expect(p.active()).toBeTruthy();
+ expect(p.element().classList.contains('active')).toBeTruthy();
+ });
});
});
diff --git a/dev/js/src/app/de.js b/dev/js/src/app/de.js
new file mode 100644
index 0000000..fc576f2
--- /dev/null
+++ b/dev/js/src/app/de.js
@@ -0,0 +1,26 @@
+define(['init'], function () {
+ var loc = KorAP.Locale;
+ loc.OR = 'oder';
+ loc.AND = 'und';
+ // EMPTY, DELETE
+
+ // Virtual collection:
+ loc.VC_subTitle = 'Untertitel';
+ loc.VC_title = 'Titel';
+ loc.VC_pubDate = 'Veröffentlichungsdatum';
+ loc.VC_pubPlace = 'Veröffentlichungsort';
+
+ // Date picker:
+ loc.WDAY = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'];
+ loc.MONTH = [
+ 'Januar', 'Februar', 'März', 'April',
+ 'Mai', 'Juni', 'Juli', 'August',
+ 'September', 'Oktober', 'November',
+ 'Dezember'
+ ];
+
+ // Match view
+ loc.ADDTREE = 'Baumansicht hinzufügen';
+ loc.SHOWINFO = 'Informationen';
+ loc.CLOSE = 'Schließen';
+});
diff --git a/dev/js/src/app/en.js b/dev/js/src/app/en.js
new file mode 100644
index 0000000..c7a4e5c
--- /dev/null
+++ b/dev/js/src/app/en.js
@@ -0,0 +1 @@
+require(['init']);
diff --git a/dev/js/src/datepicker.js b/dev/js/src/datepicker.js
index 90b0572..6d3086d 100644
--- a/dev/js/src/datepicker.js
+++ b/dev/js/src/datepicker.js
@@ -1,16 +1,19 @@
/**
- * Date picker for the
+ * Simple Date picker for the
* Virtual Collection builder.
+ *
+ * @author Nils Diewald
*/
define(['util'], function () {
"use strict";
+ /*
+ * Localizations
+ */
var loc = KorAP.Locale;
-
loc.WDAY = loc.WDAY || [
'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'
];
-
loc.MONTH = loc.MONTH || [
'January', 'February', 'March', 'April',
'May', 'June', 'July', 'August',
@@ -20,27 +23,24 @@
var d = document;
+ // The datepicker class
return {
+
+ /**
+ * Create a new datepicker view.
+ */
create : function () {
return Object.create(this)._init();
},
+ // Init datepicker
_init : function () {
return this;
},
- set : function (year, month, day) {
- this.select(year, month, day);
- if (this._click !== undefined)
- this._click(this._selected);
- else
- console.dir(this._selected);
- },
-
- onclick : function (cb) {
- this._click = cb;
- },
-
+ /**
+ * Get or select a specific date.
+ */
select : function (year, month, day) {
if (arguments.length >= 1) {
this._selected = {'year' : year};
@@ -54,21 +54,73 @@
return this._selected;
},
+
+ /**
+ * Select a specific date and
+ * init the accompanied action.
+ */
+ set : function (year, month, day) {
+ this.select(year, month, day);
+ if (this._click !== undefined)
+ this._click(this._selected);
+ else
+ console.dir(this._selected);
+ },
+
+
+ /**
+ * Set the action for clicking as a callback.
+ * The callback will retrieve a an object with
+ * an optional year attribute,
+ * an optional month attribute,
+ * and an optional day attribute
+ */
+ onclick : function (cb) {
+ this._click = cb;
+ },
+
+ /**
+ * Show the datepicker.
+ * Will either show the selected year/month
+ * or the current date.
+ * Will return the element for appending to the dom.
+ */
show : function (year, month) {
- this._element = d.createElement('div');
- this._element.classList.add('datepicker');
- this._showYear = year ? year : (this._selected['year'] ? this._selected['year'] : 2012);
- this._showMonth = month ? month : (this._selected['month'] ? this._selected['month'] : 2);
+ var e = this._element = d.createElement('div');
+ e.setAttribute('tabindex', 0);
+ e.style.outline = 0;
+ e.classList.add('datepicker');
+
+ var today = new Date();
+
+ // Show year
+ this._showYear = year ? year :
+ (this._selected['year'] ? this._selected['year'] :
+ today.getYear());
+
+ // Show month
+ this._showMonth = month ? month :
+ (this._selected['month'] ? this._selected['month'] :
+ (today.getMonth() + 1));
+
+ // Append all helpers
this._element.appendChild(this._monthHelper());
this._element.appendChild(this._yearHelper());
this._element.appendChild(this._dayHelper());
return this._element;
},
+ /**
+ * Get the HTML element associated with the datepicker.
+ */
element : function () {
return this._element;
},
+
+ /**
+ * Increment the year.
+ */
incrYear : function () {
this._showYear++;
this._updateYear();
@@ -77,6 +129,9 @@
return;
},
+ /**
+ * Decrement the year.
+ */
decrYear : function () {
this._showYear--;
this._updateYear();
@@ -85,6 +140,9 @@
return;
},
+ /**
+ * Increment the month.
+ */
incrMonth : function () {
this._showMonth++;
if (this._showMonth > 12) {
@@ -97,6 +155,9 @@
};
},
+ /**
+ * Decrement the month.
+ */
decrMonth : function () {
this._showMonth--;
if (this._showMonth < 1) {
@@ -109,6 +170,8 @@
};
},
+
+ // Create the year helper element.
_yearHelper : function () {
var year = d.createElement('div');
year.classList.add('year');
@@ -123,6 +186,7 @@
this._yElement.onclick = function () {
this.set(this._showYear);
}.bind(this);
+ this._selectYear();
// Increment year
year.appendChild(d.createElement('span'))
@@ -131,10 +195,23 @@
return year;
},
+ // Update the year helper view.
_updateYear : function () {
this._yElement.firstChild.data = this._showYear;
+ this._selectYear();
},
+
+ // Check if the viewed year is current
+ _selectYear : function () {
+ if (this._showYear === this.select()['year'])
+ this._yElement.classList.add('selected');
+ else
+ this._yElement.classList.remove('selected');
+ },
+
+
+ // Create the month helper element.
_monthHelper : function () {
var month = d.createElement('div');
month.classList.add('month');
@@ -150,6 +227,8 @@
this._mElement.onclick = function () {
this.set(this._showYear, this._showMonth);
}.bind(this);
+
+ this._selectMonth();
// Increment month
month.appendChild(d.createElement('span'))
@@ -158,13 +237,28 @@
return month;
},
+ // Update the month helper view.
_updateMonth : function () {
this._mElement.firstChild.data = loc.MONTH[this._showMonth-1];
+ this._selectMonth();
},
+
+ // Check if the viewed month is current
+ _selectMonth : function () {
+ if (this._showYear === this.select()['year'] &&
+ this._showMonth === this.select()['month'])
+ this._mElement.classList.add('selected');
+ else
+ this._mElement.classList.remove('selected');
+ },
+
+
+ // Create the day (calendar) helper element.
_dayHelper : function () {
var table = d.createElement('table');
+ // Localized day view
var tr = table.appendChild(d.createElement('thead'))
.appendChild(d.createElement('tr'));
for (var i = 0; i < 7; i++) {
@@ -179,10 +273,29 @@
},
_dayBody : function () {
- var showDate = new Date(this._showYear, this._showMonth - 1, 1, 0, 0, 0, 0);
- var date = new Date(this._showYear, this._showMonth - 1, 1, 0, 0, 0, 0);
- var today = new Date();
+ var showDate = new Date(
+ this._showYear,
+ this._showMonth - 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0
+ );
+ var date = new Date(
+ this._showYear,
+ this._showMonth - 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0
+ );
+ var today = new Date();
var that = this;
+
+ // What happens, in case someone clicks
+ // on a date
var click = function () {
that.set(
that._showYear,
@@ -245,6 +358,7 @@
return tb;
},
+ // Update the calendar view
_updateDay : function () {
var newBody = this._dayBody();
this._dBElement.parentNode.replaceChild(
diff --git a/dev/js/src/hint/contextanalyzer.js b/dev/js/src/hint/contextanalyzer.js
index bdbf885..fc01662 100644
--- a/dev/js/src/hint/contextanalyzer.js
+++ b/dev/js/src/hint/contextanalyzer.js
@@ -2,9 +2,15 @@
* Regex object for checking the context of the hint
*/
define({
+
+ /**
+ * Create analyzer based on regular expression.
+ */
create : function (regex) {
return Object.create(this)._init(regex);
},
+
+ // Initialize analyzer
_init : function (regex) {
try {
this._regex = new RegExp(regex);
@@ -15,6 +21,11 @@
};
return this;
},
+
+ /**
+ * Check a context based on the analyzer
+ * and return a valid context string.
+ */
test : function (text) {
if (!this._regex.exec(text))
return;
diff --git a/dev/js/src/hint/input.js b/dev/js/src/hint/input.js
index b1c6632..c12d26a 100644
--- a/dev/js/src/hint/input.js
+++ b/dev/js/src/hint/input.js
@@ -1,9 +1,14 @@
// Input field for queries
define({
+
+ /**
+ * Create a new input field.
+ */
create : function (element) {
return Object.create(this)._init(element);
},
-
+
+ // Initialize new input field
_init : function (element) {
this._element = element;
@@ -29,34 +34,62 @@
return this;
},
- rightPos : function () {
+ // Get the right position
+ _rightPos : function () {
var box = this._mirror.firstChild.getBoundingClientRect();
return box.right - box.left;
},
+ /**
+ * Get the mirrored input field.
+ */
mirror : function () {
return this._mirror;
},
+
+ /**
+ * Get the container element.
+ * This contains the mirror and
+ * the hint helper.
+ */
container : function () {
return this._container;
},
+
+ /**
+ * Get the input element the
+ * hint helper is attached to.
+ */
element : function () {
return this._element;
},
+ /**
+ * Get the value of the input field
+ * the hint helper is attached to.
+ */
value : function () {
return this._element.value;
},
+
+ /**
+ * Update the mirror content.
+ */
update : function () {
- this._mirror.firstChild.textContent = this.split()[0];
- this._container.style.left = this.rightPos() + 'px';
+ this._mirror.firstChild.textContent = this._split()[0];
+ this._container.style.left = this._rightPos() + 'px';
},
+ /**
+ * Insert text into the mirror.
+ * This is a prefix of the input field's
+ * value.
+ */
insert : function (text) {
- var splittedText = this.split();
+ var splittedText = this._split();
var s = this._element;
s.value = splittedText[0] + text + splittedText[1];
s.selectionStart = (splittedText[0] + text).length;
@@ -64,18 +97,11 @@
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 the input mirror directly
+ * below the input box.
+ */
reposition : function () {
var inputClientRect = this._element.getBoundingClientRect();
var inputStyle = window.getComputedStyle(this._element, null);
@@ -97,7 +123,27 @@
mirrorStyle.fontSize = inputStyle.getPropertyValue("font-size");
mirrorStyle.fontFamily = inputStyle.getPropertyValue("font-family");
},
+
+ /**
+ * Get the context, which is the input
+ * field's value bounded to the
+ * cursor position.
+ */
context : function () {
- return this.split()[0];
+ return this._split()[0];
+ },
+
+ /*
+ * Return two substrings,
+ * splitted at the 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)
+ );
}
});
diff --git a/dev/js/src/hint/item.js b/dev/js/src/hint/item.js
index 4b879f8..b9a1bc8 100644
--- a/dev/js/src/hint/item.js
+++ b/dev/js/src/hint/item.js
@@ -3,12 +3,17 @@
*/
define(['menu/item'], function (itemClass) {
return {
+
+ /**
+ * Create new menu item object.
+ */
create : function (params) {
return Object.create(itemClass)
.upgradeTo(this)
._init(params);
},
+ // Initialize menu item object
_init : function (params) {
if (params[0] === undefined ||
params[1] === undefined)
@@ -26,13 +31,20 @@
return this;
},
+ /**
+ * Get or set the content of the item.
+ */
content : function (content) {
if (arguments.length === 1) {
this._content = content;
};
return this._content;
},
-
+
+ /**
+ * Override the click action
+ * of the menu item.
+ */
onclick : function (e) {
var m = this.menu();
var h = m.hint();
@@ -50,15 +62,32 @@
h.show(true);
},
+ /**
+ * The name of the menu entry.
+ */
name : function () {
return this._name;
},
+
+ /**
+ * The action (the string inserted on click)
+ * of the menu item.
+ */
action : function () {
return this._action;
},
+
+ /**
+ * The description of the menu item.
+ */
desc : function () {
return this._desc;
},
+
+ /**
+ * The associated dom element of the
+ * menu item.
+ */
element : function () {
// already defined
if (this._element !== undefined)
diff --git a/dev/js/src/hint/menu.js b/dev/js/src/hint/menu.js
index c579945..bf965d7 100644
--- a/dev/js/src/hint/menu.js
+++ b/dev/js/src/hint/menu.js
@@ -3,6 +3,10 @@
*/
define(['menu', 'hint/item', 'hint/prefix'], function (menuClass, itemClass, prefixClass) {
return {
+
+ /**
+ * Create new hint helper menu.
+ */
create : function (hint, context, params) {
var obj = Object.create(menuClass)
.upgradeTo(this)
@@ -25,10 +29,11 @@
return obj;
},
- // Todo: Is this necessary?
- context : function () {
- return this._context;
- },
+
+ /**
+ * The hint helper object,
+ * the menu is attached to.
+ */
hint : function () {
return this._hint;
}
diff --git a/dev/js/src/hint/prefix.js b/dev/js/src/hint/prefix.js
index 84f785a..7586617 100644
--- a/dev/js/src/hint/prefix.js
+++ b/dev/js/src/hint/prefix.js
@@ -1,8 +1,17 @@
define(['menu/prefix'], function (prefixClass) {
return {
+
+ /**
+ * Create prefix object for the hint helper menu.
+ */
create : function (params) {
- return Object.create(prefixClass).upgradeTo(this)._init(params);
+ return Object.create(prefixClass).
+ upgradeTo(this)._init(params);
},
+
+ /**
+ * Override the prefix action.
+ */
onclick : function () {
var m = this.menu();
var h = m.hint();
diff --git a/dev/js/src/init.js b/dev/js/src/init.js
index cd99df1..5ef8dee 100644
--- a/dev/js/src/init.js
+++ b/dev/js/src/init.js
@@ -4,16 +4,38 @@
'vc',
'tutorial',
'lib/domReady',
+ 'hint/array',
+ 'lib/alertify',
+ 'api',
'util'
], function (matchClass,
hintClass,
vcClass,
tutClass,
- domReady) {
+ domReady,
+ hintArray) {
domReady(function (event) {
var obj = {};
/**
+ * Replace Virtual Collection field
+ */
+ var vcname;
+ var input = document.getElementById('vc');
+ if (input) {
+ input.style.display = 'none';
+ vcname = document.createElement('span');
+ vcname.setAttribute('id', 'vc-choose');
+ vcname.appendChild(
+ document.createTextNode(
+ document.getElementById('vc-name').value
+ )
+ );
+ input.parentNode.insertBefore(vcname, input);
+ };
+
+
+ /**
* Add actions to match entries
*/
var inactiveLi = document.querySelectorAll(
@@ -31,6 +53,7 @@
});
};
+
/**
* Toggle the alignment (left <=> right)
*/
@@ -41,8 +64,7 @@
toggle.setAttribute('title', 'toggle Alignment');
// Todo: Reuse old alignment from cookie!
var cl = toggle.classList;
- cl.add('align');
- cl.add('right');
+ cl.add('align', 'right');
toggle.addEventListener(
'click',
function (e) {
@@ -56,24 +78,11 @@
};
};
- /**
- * Init vc
- */
- 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);
- /**
- * Toggle the Virtual Collection builder
- */
+ /**
+ * Toggle the Virtual Collection builder
+ */
+ if (vcname) {
var vc;
vcname.onclick = function () {
var view = document.getElementById('vc-view');
@@ -131,6 +140,22 @@
// Todo: Pass an element, so this works with
// tutorial pages as well!
obj.hint = hintClass.create();
+
+ // Set hint array for hint helper
+ KorAP.hintArray = hintArray;
+
+ // Override KorAP.log
+ KorAP.log = function (type, msg) {
+
+ // Use alertify to log errors
+ alertify.log(
+ (type === 0 ? '' : type + ': ') +
+ msg,
+ 'warn',
+ 5000
+ );
+ };
+
return obj;
});
});
diff --git a/dev/js/src/match/info.js b/dev/js/src/match/info.js
index c47f5ae..b75e713 100644
--- a/dev/js/src/match/info.js
+++ b/dev/js/src/match/info.js
@@ -12,7 +12,7 @@
matchTreeClass,
matchTreeMenuClass) {
- // TODO: Make this async
+ // Override
KorAP.API.getMatchInfo = KorAP.API.getMatchInfo || function () {
KorAP.log(0, 'KorAP.API.getMatchInfo() not implemented')
return {};
@@ -20,10 +20,11 @@
var loc = KorAP.Locale;
- /**
- * Create new object
- */
return {
+
+ /**
+ * Create new match object
+ */
create : function (match) {
return Object.create(this)._init(match);
},
@@ -44,6 +45,11 @@
return this._match;
},
+
+ /**
+ * Open the information view,
+ * if closed, otherwise close.
+ */
toggle : function () {
if (this.opened == true) {
this._match.element().children[0].removeChild(
@@ -64,7 +70,8 @@
/**
- * Retrieve and parse snippet for table representation
+ * Retrieve and parse snippet for table
+ * representation
*/
getTable : function (tokens, cb) {
var focus = [];
diff --git a/dev/js/src/match/infolayer.js b/dev/js/src/match/infolayer.js
index dbd93f9..0812f71 100644
--- a/dev/js/src/match/infolayer.js
+++ b/dev/js/src/match/infolayer.js
@@ -1,16 +1,26 @@
/**
- *
- * Alternatively pass a string as <tt>base/s=span</tt>
- *
- * @param foundry
+ * Object representing information
+ * about a match's layer annotation.
*/
define(function () {
- var _AvailableRE = new RegExp("^([^\/]+?)\/([^=]+?)(?:=(spans|rels|tokens))?$");
+ var _AvailableRE =
+ new RegExp("^([^\/]+?)\/([^=]+?)(?:=(spans|rels|tokens))?$");
return {
+ /**
+ * Create new match information
+ * object for one layer.
+ *
+ * Alternatively pass a string as
+ * <tt>base/s=span</tt>
+ *
+ * @param foundry
+ */
create : function (foundry, layer, type) {
return Object.create(this)._init(foundry, layer, type);
},
+
+ // Initialize Layer
_init : function (foundry, layer, type) {
if (foundry === undefined)
throw new Error("Missing parameters");
diff --git a/dev/js/src/match/table.js b/dev/js/src/match/table.js
index 7eba8a0..1a167d2 100644
--- a/dev/js/src/match/table.js
+++ b/dev/js/src/match/table.js
@@ -1,10 +1,21 @@
+/**
+ * Table representation of morphological
+ * annotations of a match.
+ */
define(function () {
var _TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
return {
+
+ /**
+ * Create new table view for a match
+ * based on a snippet string.
+ */
create : function (snippet) {
return Object.create(this)._init(snippet);
},
+
+ // Initialize table based on snippet
_init : function (snippet) {
// Create html for traversal
var html = document.createElement("div");
@@ -23,27 +34,49 @@
return this;
},
+
+ /**
+ * Length of the table (columns),
+ * aka the number of tokens
+ * in the snippet.
+ */
length : function () {
return this._pos;
},
+ /**
+ * Get the token in the snippet
+ * At a given position.
+ *
+ * @param pos
+ */
getToken : function (pos) {
if (pos === undefined)
return this._token;
return this._token[pos];
},
-
+
+ /**
+ * Get the annotation of a token
+ * in the snippet based on the position,
+ * the foundry, and the layer.
+ *
+ * @param pos
+ * @param foundry
+ * @param layer
+ */
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];
- },
+ /*
+ getLayerPerFoundry : function (foundry) {
+ return this._foundry[foundry]
+ },
+ getFoundryPerLayer : function (layer) {
+ return this._layer[layer];
+ },
+ */
// Parse the snippet
_parse : function (children) {
diff --git a/dev/js/src/match/tree.js b/dev/js/src/match/tree.js
index b379b37..9722f94 100644
--- a/dev/js/src/match/tree.js
+++ b/dev/js/src/match/tree.js
@@ -1,5 +1,6 @@
/**
- * Visualize span annotations as a tree using Dagre.
+ * Visualize span annotations as a tree
+ * using Dagre.
*/
define(['lib/dagre'], function (dagre) {
"use strict";
@@ -7,6 +8,9 @@
var svgXmlns = "http://www.w3.org/2000/svg";
var _TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
+ // Node size
+ var WIDTH = 55, HEIGHT = 20;
+
// Create path for node connections
function _line (src, target) {
var x1 = src.x,
@@ -22,24 +26,18 @@
};
return {
+
+ /**
+ * Create new tree visualization based
+ * on a match snippet.
+ */
create : function (snippet) {
- return Object.create(this)._init(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);
- },
-
+ // Initialize the tree based on a snippet.
_init : function (snippet) {
this._next = new Number(0);
@@ -76,6 +74,25 @@
return this;
},
+ /**
+ * The number of nodes in the tree.
+ */
+ nodes : function () {
+ return this._next;
+ },
+
+ // Add new node to graph
+ _addNode : function (id, obj) {
+ obj["width"] = WIDTH;
+ obj["height"] = HEIGHT;
+ this._graph.setNode(id, obj)
+ },
+
+ // Add new edge to graph
+ _addEdge : function (src, target) {
+ this._graph.setEdge(src, target);
+ },
+
// Remove foundry and layer for labels
_clean : function (title) {
return title.replace(_TermRE, "$3");
@@ -148,7 +165,9 @@
};
},
- // Get element
+ /**
+ * Get the dom element of the tree view.
+ */
element : function () {
if (this._element !== undefined)
return this._element;
@@ -183,26 +202,25 @@
// 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);
+ rect.setAttribute('x', v.x - v.width / 2);
+ rect.setAttribute('y', v.y - v.height / 2);
+ rect.setAttribute('rx', 5);
+ rect.setAttribute('ry', 5);
+ rect.setAttribute('width', v.width);
+ rect.setAttribute('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');
+ rect.setAttribute('width', v.height);
+ rect.setAttribute('x', v.x - v.height / 2);
+ rect.setAttribute('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,
+ text.setAttribute('x', v.x - v.width / 2);
+ text.setAttribute('y', v.y - v.height / 2);
+ text.setAttribute(
'transform',
'translate(' + v.width/2 + ',' + ((v.height / 2) + 5) + ')'
);
diff --git a/dev/js/src/match/treeitem.js b/dev/js/src/match/treeitem.js
index f096861..9a4b952 100644
--- a/dev/js/src/match/treeitem.js
+++ b/dev/js/src/match/treeitem.js
@@ -4,10 +4,20 @@
*/
return {
+
+ /**
+ * Create new menu item
+ * for tree views.
+ */
create : function (params) {
return Object.create(itemClass)
.upgradeTo(this)._init(params);
},
+
+ /**
+ * Get or set the content of the
+ * menu item.
+ */
content : function (content) {
if (arguments.length === 1) {
this._content = content;
@@ -15,17 +25,23 @@
return this._content;
},
- // The foundry attribute
+ /**
+ * The foundry attribute of the menu item.
+ */
foundry : function () {
return this._foundry;
},
- // The layer attribute
+ /**
+ * The layer attribute of the menu item.
+ */
layer : function () {
return this._layer;
},
- // enter or click
+ /**
+ * Override click action of the menu item.
+ */
onclick : function (e) {
var menu = this.menu();
menu.hide();
@@ -33,7 +49,8 @@
if (menu.info() !== undefined)
menu.info().addTree(this._foundry, this._layer);
},
-
+
+ // Initialize tree menu item.
_init : function (params) {
if (params[0] === undefined)
throw new Error("Missing parameters");
diff --git a/dev/js/src/match/treemenu.js b/dev/js/src/match/treemenu.js
index 23341a4..16f6306 100644
--- a/dev/js/src/match/treemenu.js
+++ b/dev/js/src/match/treemenu.js
@@ -5,6 +5,16 @@
"use strict";
return {
+
+ /**
+ * Create new menu object.
+ * Pass the match information object
+ * and the item parameters.
+ *
+ * @param info The match info object
+ * @param params The match menu items
+ * as an array of arrays.
+ */
create : function (info, params) {
var obj = Object.create(menuClass)
.upgradeTo(this)
@@ -19,6 +29,10 @@
return obj;
},
+
+ /**
+ * The match information object of the menu.
+ */
info :function () {
return this._info;
}
diff --git a/dev/js/src/menu.js b/dev/js/src/menu.js
index 2bc356b..cc42855 100644
--- a/dev/js/src/menu.js
+++ b/dev/js/src/menu.js
@@ -1,10 +1,11 @@
/**
- * Create scrollable drop-down menus.
+ * Scrollable drop-down menus with view filter.
*
* @author Nils Diewald
*/
/*
* TODO: space is not a valid prefix!
+ * TODO: Prefix should be case sensitive!
*/
define([
'menu/item',
@@ -13,7 +14,6 @@
], function (defaultItemClass,
defaultPrefixClass) {
- // Todo: This may not be necessary
// Default maximum number of menu items
var menuLimit = 8;
@@ -58,10 +58,15 @@
delete this._prefix['_menu'];
},
+
+ /**
+ * Focus on this menu.
+ */
focus : function () {
this._element.focus();
},
+
// mouse wheel treatment
_mousewheel : function (e) {
var delta = 0;
@@ -74,6 +79,7 @@
e.halt();
},
+
// Arrow key and prefix treatment
_keydown : function (e) {
var code = _codeFromEvent(e);
@@ -122,7 +128,7 @@
e.halt();
break;
case 8: // 'Backspace'
- this._prefix.backspace();
+ this._prefix.chop();
this.show();
e.halt();
break;
@@ -214,22 +220,23 @@
},
/**
- * Get the instantiated HTML element
+ * Get the associated dom element.
*/
element : function () {
return this._element;
},
+
/**
- * Get the creator object for items
+ * Get the creator class for items
*/
itemClass : function () {
return this._itemClass;
},
/**
- * Get and set numerical value for limit,
- * i.e. the number of items visible.
+ * Get and set the numerical value
+ * for the maximum number of items visible.
*/
limit : function (limit) {
if (arguments.length === 1) {
@@ -239,6 +246,7 @@
return this._limit;
},
+
/**
* Upgrade this object to another object,
* while private data stays intact.
@@ -252,6 +260,7 @@
return this;
},
+
// Reset chosen item and prefix
_reset : function () {
this._offset = 0;
@@ -259,6 +268,7 @@
this._prefix.value('');
},
+
/**
* Filter the list and make it visible
*
@@ -287,15 +297,23 @@
return true;
},
+
+ /**
+ * Hide the menu and call the onHide callback.
+ */
hide : function () {
this.active = false;
this.delete();
this._element.style.opacity = 0;
+ this._prefix.clear();
this.onHide();
/* this._element.blur(); */
},
- // To be override
+ /**
+ * Function released when the menu hides.
+ * This method is expected to be overridden.
+ */
onHide : function () {},
// Initialize the list
@@ -356,6 +374,7 @@
this.item(this._list[this._list.length - 1]).noMore(bool);
},
+
/**
* Get the prefix for filtering,
* e.g. "ve" for "verb"
@@ -368,6 +387,7 @@
return this._prefix.value();
},
+
// Append Items that should be shown
_showItems : function (offset) {
this.delete();
@@ -644,7 +664,9 @@
},
- // Length of the filtered list
+ /**
+ * Length of the filtered item list.
+ */
liveLength : function () {
if (this._list === undefined)
this._initList();
diff --git a/dev/js/src/menu/item.js b/dev/js/src/menu/item.js
index b80aa2a..b8e8808 100644
--- a/dev/js/src/menu/item.js
+++ b/dev/js/src/menu/item.js
@@ -34,16 +34,28 @@
return this;
},
+
+ /**
+ * Get or set the content of the meun item.
+ */
content : function (content) {
if (arguments.length === 1)
this._content = document.createTextNode(content);
return this._content;
},
+ /**
+ * Get the lower cased field of the item
+ * (used for analyses).
+ */
lcField : function () {
return this._lcField;
},
+
+ /**
+ * Get or set the information for action of this item.
+ */
action : function (action) {
if (arguments.length === 1)
this._action = action;
diff --git a/dev/js/src/menu/prefix.js b/dev/js/src/menu/prefix.js
index a4e428f..abcf66c 100644
--- a/dev/js/src/menu/prefix.js
+++ b/dev/js/src/menu/prefix.js
@@ -1,7 +1,13 @@
define({
- create : function (params) {
+
+ /**
+ * Create new prefix object.
+ */
+ create : function () {
return Object.create(this)._init();
},
+
+ // Initialize prefix object
_init : function () {
this._string = '';
@@ -15,8 +21,9 @@
return this;
},
+
_update : function () {
- this._element.innerHTML
+ return this._element.innerHTML
= this._string;
},
@@ -33,6 +40,10 @@
return this;
},
+
+ /**
+ * Get or set the activity status of the prefix.
+ */
active : function (bool) {
var cl = this.element().classList;
if (bool === undefined)
@@ -43,31 +54,57 @@
cl.remove("active");
},
- element : function () {
- return this._element;
- },
-
+ /**
+ * Check, if a prefix is given or not.
+ */
isSet : function () {
return this._string.length > 0 ?
true : false;
},
+ /**
+ * Get or set the prefix string.
+ */
value : function (string) {
if (arguments.length === 1) {
this._string = string;
- this._update();
+ return this._update();
};
return this._string;
},
-
+
+
+ /**
+ * Add string to prefix.
+ */
add : function (string) {
this._string += string;
- this._update();
+ return this._update();
},
+
+ /**
+ * Clear prefix
+ */
+ clear : function () {
+ this._string = '';
+ return this._update();
+ },
+
+
+ /**
+ * Action method.
+ * Expected to be overridden.
+ */
onclick : function () {},
- backspace : function () {
+
+ /**
+ * Remove the last character of the string
+ */
+ chop : function () {
+
+ // Prefix is long enough for backspace
if (this._string.length > 1) {
this._string = this._string.substring(
0, this._string.length - 1
@@ -77,9 +114,18 @@
this._string = '';
};
- this._update();
+ return this._update();
},
+
+ /**
+ * Get the associated dom element.
+ */
+ element : function () {
+ return this._element;
+ },
+
+
/**
* Return menu list.
*/
diff --git a/dev/js/src/tutorial.js b/dev/js/src/tutorial.js
index cacbe21..a028b8e 100644
--- a/dev/js/src/tutorial.js
+++ b/dev/js/src/tutorial.js
@@ -54,6 +54,10 @@
return this;
},
+
+ /**
+ * Initialize a search with a defined query.
+ */
useQuery : function (e) {
var q = e.getAttribute("data-query");
var ql = e.getAttribute("data-query-language");
@@ -74,6 +78,10 @@
this.hide();
},
+
+ /**
+ * Decorate a page with query event handler.
+ */
initQueries : function (d) {
var qs = d.querySelectorAll('pre.query.tutorial');
var that = this;
@@ -85,6 +93,9 @@
},
+ /**
+ * Show the tutorial page embedded.
+ */
show : function () {
var element = this._element;
if (element.style.display === 'block')
diff --git a/dev/js/src/vc.js b/dev/js/src/vc.js
index 1bb3b15..1aed3cf 100644
--- a/dev/js/src/vc.js
+++ b/dev/js/src/vc.js
@@ -138,6 +138,7 @@
return obj;
},
+
/**
* Create and render a new virtual collection
* based on a KoralQuery collection document
@@ -171,6 +172,10 @@
return this;
},
+
+ /**
+ * Clean the virtual document to uspecified doc.
+ */
clean : function () {
if (this._root.ldType() !== "non") {
this._root.destroy();
@@ -179,6 +184,7 @@
return this;
},
+
/**
* Get or set the root object of the
* virtual collection.
@@ -206,6 +212,7 @@
return this._root;
},
+
/**
* Get the element associated with the virtual collection
*/
diff --git a/dev/js/src/vc/doc.js b/dev/js/src/vc/doc.js
index 2635b2f..f04b445 100644
--- a/dev/js/src/vc/doc.js
+++ b/dev/js/src/vc/doc.js
@@ -10,10 +10,10 @@
/*
var fieldMenu = menuClass.create([
- ['Titel', 'title', 'string'],
- ['Untertitel', 'subTitle', 'string'],
- ['Veröffentlichungsdatum', 'pubDate', 'date'],
- ['Autor', 'author', 'string']
+ ['Titel', 'title', 'string'],
+ ['Untertitel', 'subTitle', 'string'],
+ ['Veröffentlichungsdatum', 'pubDate', 'date'],
+ ['Autor', 'author', 'string']
]);
fieldMenu.limit(5);
@@ -417,29 +417,37 @@
// Click on the match operator, show me the menu
_changeValue : function (e) {
- // TODO: Just kidding - this is temporary!
+ // Show datepicker
if (this.type() === 'date') {
var dp = KorAP._vcDatePicker;
var v = this.value();
if (v !== undefined) {
- var d = v.split('-');
- dp.select(parseInt(d[0]), parseInt(d[1]), parseInt(d[2]));
+
+ var d = v.split('-', 3);
+ d[0] = parseInt(d[0]);
+ if (d[1]) d[1] = parseInt(d[1]);
+ if (d[2]) d[2] = parseInt(d[2]);
+
+ // Select values
+ dp.select(d[0], d[1], d[2]);
};
var that = this;
+
dp.onclick(function (selected) {
// There are values selected
if (selected['year']) {
- var v = selected['year'];
+ var v = '' + selected['year'];
if (selected['month']) {
v += '-';
v += selected['month'] < 10 ? '0' + selected['month'] : selected['month'];
- if (selected['day'])
+ if (selected['day']) {
v += '-';
v += selected['day'] < 10 ? '0' + selected['day'] : selected['day'];
+ };
};
that.value(v);
that.update();
@@ -452,12 +460,22 @@
);
});
+ // Get element of the date picker
+ var dpElem = dp.show();
+
this._element.insertBefore(
- dp.show(),
+ dpElem,
this._valueE
);
+
+ dpElem.focus();
+ dpElem.addEventListener('blur', function (e) {
+ // Remove datepicker
+ that._element.removeChild(this);
+ });
}
else {
+ // TODO: Just kidding - this is temporary!
this.value(window.prompt('Enter new value'));
this.update();
};
diff --git a/dev/js/src/vc/menu.js b/dev/js/src/vc/menu.js
index d305960..17ad073 100644
--- a/dev/js/src/vc/menu.js
+++ b/dev/js/src/vc/menu.js
@@ -4,9 +4,17 @@
define(['menu', 'vc/item'], function (menuClass, itemClass) {
return {
create : function (params) {
- return Object.create(menuClass)
+ var obj = Object.create(menuClass)
.upgradeTo(this)
- ._init(itemClass, undefined, params)
+ ._init(itemClass, undefined, params);
+ obj.limit(6);
+
+ // This is only domspecific
+ obj.element().addEventListener('blur', function (e) {
+ this.menu.hide();
+ });
+
+ return obj;
},
/**
diff --git a/dev/scss/header/datepicker.scss b/dev/scss/header/datepicker.scss
index 8a3fbcd..806ca1e 100644
--- a/dev/scss/header/datepicker.scss
+++ b/dev/scss/header/datepicker.scss
@@ -6,6 +6,7 @@
div.datepicker {
display: inline-block;
position: absolute;
+ z-index: 8000;
font-size: 80%;
padding: 4pt;
box-shadow: $choose-box-shadow;
@@ -21,7 +22,7 @@
> div.month {
float: right;
}
-
+
@include choose-item;
> div > span {
display: inline-block;
@@ -59,6 +60,9 @@
&:hover {
@include choose-hover;
}
+ &.selected {
+ @include choose-active;
+ }
}
}
table {
diff --git a/dev/scss/header/vc.scss b/dev/scss/header/vc.scss
index 12071db..d13ba6e 100644
--- a/dev/scss/header/vc.scss
+++ b/dev/scss/header/vc.scss
@@ -250,6 +250,10 @@
}
}
+#vc {
+ line-height: 1em;
+ border-radius: $standard-border-radius;
+}
#vc-choose {
&::after {
diff --git a/dev/scss/media.scss b/dev/scss/media.scss
index 45e3bee..43122bc 100644
--- a/dev/scss/media.scss
+++ b/dev/scss/media.scss
@@ -93,9 +93,19 @@
width: 100%;
}
- aside ul {
- font-size: 9pt;
- line-height: 1em;
+ aside {
+ &:not(:focus):not(.active) {
+ margin-left: -1 * $logo-left-distance;
+ &::after {
+ font-size: 12pt;
+ width: 10pt;
+ height: 11pt;
+ }
+ }
+ ul {
+ font-size: 9pt;
+ line-height: 1em;
+ }
}
#tutorial {
@@ -116,15 +126,6 @@
}
/*
-
- #sidebar {
- padding-top: 22px;
- }
- #sidebar:not(.active) > i.fa-bars {
- font-size: 12pt;
- width: 10pt;
- height: 11pt;
- }
pre.query {
font-size: 9.5pt;
}
diff --git a/dev/scss/util.scss b/dev/scss/util.scss
index 8b91f22..7f5f723 100644
--- a/dev/scss/util.scss
+++ b/dev/scss/util.scss
@@ -26,9 +26,10 @@
/**
* Green Colors
*/
-$dark-green: $ids-green-1; // #496000;
-$middle-green: lighten($ids-green-1, 5%); // $ids-green-1; // #688704;
-$light-green: lighten($ids-green-1, 13%); // #7ba400;
+$dark-green: $ids-green-1; // #496000;
+$middle-green: lighten($ids-green-1, 5%); // $ids-green-1; // #688704;
+$light-green: lighten($ids-green-1, 13%); // #7ba400;
+$lightest-green: lighten($ids-green-1, 26%);
/**
* Blue Colors
diff --git a/kalamar.conf b/kalamar.conf
index 22ce0dd..e924532 100644
--- a/kalamar.conf
+++ b/kalamar.conf
@@ -55,13 +55,14 @@
tutorial => 'Einführung',
pubOn => 'veröffentlicht am',
matchCount => 'Treffer',
+ jsFile => 'kalamar-<%= $Kalamar::VERSION %>-en.js',
korap => {
-short => 'KorAP',
long => 'KorAP - Korpusanalyseplattform der nächsten Generation',
- overview => 'KorAP - Übersicht'
- }
+ overview => 'KorAP - Übersicht',
+ },
},
- en => {
+ -en => {
about => 'About KorAP',
login => 'Login',
go => 'Go!',
@@ -74,6 +75,7 @@
glimpse => 'Sample',
faq => 'F.A.Q.',
tutorial => 'Tutorial',
+ jsFile => 'kalamar-<%= $Kalamar::VERSION %>-en.js',
korap => {
-short => 'KorAP',
long => 'KorAP - Corpus Analysis Platform',
diff --git a/lib/Kalamar.pm b/lib/Kalamar.pm
index d05257e..d1d0049 100644
--- a/lib/Kalamar.pm
+++ b/lib/Kalamar.pm
@@ -5,6 +5,8 @@
our $VERSION;
+# TODO: The FAQ-Page has a contact form for new questions
+
# Start the application and register all routes and plugins
sub startup {
my $self = shift;
@@ -31,9 +33,9 @@
'Search', # Abstract Search framework
'CHI', # Global caching mechanism
'TagHelpers::Pagination', # Pagination widget
- 'DocNavi', # Navigation for documentation
- 'KalamarHelpers', # Specific Helpers for Kalamar
- 'KalamarTagHelpers' # Specific Taghelpers for Kalamar
+ 'Number::Commify', # Localize numbers
+ 'KalamarHelpers' # Specific Helpers for Kalamar
+
) {
$self->plugin($_);
};
@@ -42,8 +44,8 @@
$self->plugin('MailException' => $self->config('MailException'));
# Configure documentation navigation
- my $navi = b($self->home . '/templates/doc/_nav.json')->slurp;
- $self->config(navi => decode_json($navi));
+ my $navi = b($self->home . '/templates/doc/navigation.json')->slurp;
+ $self->config(navi => decode_json($navi)) if $navi;
# Establish routes
my $r = $self->routes;
@@ -52,109 +54,16 @@
$r->get('/')->to('search#query')->name('index');
# Documentation
- $r->get('/doc')->to('documentation#page', page => 'korap');
+ $r->get('/doc')->to('documentation#page', page => 'korap')->name('doc_start');
$r->get('/doc/:page')->to('documentation#page', scope => undef);
$r->get('/doc/*scope/:page')->to('documentation#page')->name('doc');
-};
-
-1;
-
-
-__END__
-
-
- # Set default totle
-# $self->defaults(
-# layout => 'main',
-# title => 'KorAP - Corpus Analysis Platform'
-# );
-
-
- $self->hook(
- before_dispatch => sub {
- my $c = shift;
- my $host = $c->req->headers->header('X-Forwarded-Host');
- if ($host && $host eq 'korap.ids-mannheim.de') {
- $c->req->url->base->path('/kalamar/');
- };
- }) if $self->mode eq 'production';
-
-
-
- # Load plugins
- foreach (qw/
- Number::Commify
- /) {
- $self->plugin($_);
- };
-
- # $self->plugin(AssetPack => { minify => 1 });
- $self->plugin('AssetPack');
- $self->plugin('AssetPack::LibSass');
-
- # Add assets for AssetPack
- $self->asset(
- 'kalamar.css' => (
-
- # Sass files
- '/sass/style.scss',
- '/sass/sidebar.scss',
- '/sass/tutorial.scss',
- '/sass/hint.scss',
- '/sass/query.scss',
- '/sass/matchinfo.scss',
- '/sass/pagination.scss',
- '/sass/kwic-4.0.scss',
- '/sass/alertify.scss',
-
- # CSS files
- '/css/font-awesome.min.css',
- '/css/media.css',
- '/css/highlight.css',
- $self->notifications->styles
- )
- );
-
- $self->asset(
- 'kalamar.js' => (
-# '/js/d3.v3.min.js',
-# '/js/dagre-d3.min.js',
-# '/js/dagre-d3.js',
-# '/js/translateTree.js',
- '/js/tutorialCookie.js',
- '/js/translateTable.js',
- '/js/hint.js',
- '/js/highlight.pack.js',
- '/js/ajax.js',
- $self->notifications->scripts
- )
- );
-
- $self->helper(
- date_format => sub {
- my ($c, $date) = @_;
- return $date;
- }
- );
-
-
- # Base search route
- $r->get('/')->to('search#query')->name('index');
-
- # Get match information
+ # Match route
my $corpus = $r->route('/corpus/:corpus_id');
- my $doc = $corpus->route('/#doc_id');
- my $match = $doc->route('/:match_id')
- ->to('search#match_info')
- ->name('match');
-
- # Tutorial data
- $r->get('/tutorial')->to('tutorial#page', tutorial => 'index');
- $r->get('/tutorial/(*tutorial)')->to('tutorial#page')->name('tutorial');
-
-
- # Todo: The FAQ-Page has a contact form for new questions
+ my $doc = $corpus->get('/:doc_id');
+ my $text = $doc->get('/:text_id');
+ my $match = $text->get('/:match_id');
+ $match->to('search#match_info')->name('match');
};
diff --git a/lib/Kalamar/API.pm b/lib/Kalamar/API.pm
index 63362ea..9b8a529 100644
--- a/lib/Kalamar/API.pm
+++ b/lib/Kalamar/API.pm
@@ -1,4 +1,4 @@
-#package Kalamar::API;
+package Kalamar::API;
use Mojo::Base 'Mojolicious::Plugin';
use Scalar::Util qw/blessed weaken/;
use strict;
@@ -6,10 +6,10 @@
# KorAP Search engine for Mojolicious::Plugin::Search
-# Todo: Add fixtures
-# Todo: Support search in corpus and virtualcollection
-# Todo: Support caching everywhere!
-# Todo: Correct use of stash info everywhere!
+# TODO: Add fixtures
+# TODO: Support search in corpus and virtualcollection
+# TODO: Support caching everywhere!
+# TODO: Correct use of stash info everywhere!
# Register the plugin
sub register {
diff --git a/lib/Kalamar/Controller/Documentation.pm b/lib/Kalamar/Controller/Documentation.pm
index ef3b751..3588560 100644
--- a/lib/Kalamar/Controller/Documentation.pm
+++ b/lib/Kalamar/Controller/Documentation.pm
@@ -20,6 +20,7 @@
my $page = $c->stash('page');
push(@path, $page);
+ # Set navigation to sidebar
$c->content_for(
sidebar => '<nav>' . $c->doc_navi($c->config('navi')) . '</nav>'
);
@@ -37,3 +38,54 @@
__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+Kalamar::Controller::Documentation
+
+
+=head1 DESCRIPTION
+
+L<Kalamar::Controller::Documentation> is the controller class for
+documentation related endpoints in Kalamar.
+
+
+=head1 METHODS
+
+L<Kalamar::Controller::Documentation> inherits all methods from
+L<Mojolicious::Controller> and implements the following new ones.
+
+=head2 page
+
+Action for all documentation pages. The following stash parameters are supported:
+
+=head3 doc_base
+
+=head3 scope
+
+=head3 page
+
+=head3 embedded
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2015, L<IDS Mannheim|http://www.ids-mannheim.de/>
+Author: L<Nils Diewald|http://nils-diewald.de/>
+
+Kalamar is developed as part of the L<KorAP|http://korap.ids-mannheim.de/>
+Corpus Analysis Platform at the Institute for German Language
+(L<IDS|http://ids-mannheim.de/>),
+funded by the
+L<Leibniz-Gemeinschaft|http://www.leibniz-gemeinschaft.de/en/about-us/leibniz-competition/projekte-2011/2011-funding-line-2/>
+and supported by the L<KobRA|http://www.kobra.tu-dortmund.de> project,
+funded by the Federal Ministry of Education and Research
+(L<BMBF|http://www.bmbf.de/en/>).
+
+Kalamar is free software published under the
+L<BSD-2 License|https://raw.githubusercontent.com/KorAP/Kalamar/master/LICENSE).
+
+=cut
diff --git a/lib/Kalamar/Controller/Search.pm b/lib/Kalamar/Controller/Search.pm
index 494634f..09ab580 100644
--- a/lib/Kalamar/Controller/Search.pm
+++ b/lib/Kalamar/Controller/Search.pm
@@ -66,17 +66,8 @@
};
-1;
-
-__END__
-
-
-
-
-
-
-# Get informations about a match
+# Get information about a match
sub match_info {
my $c = shift;
@@ -97,8 +88,8 @@
# Use the API for fetching matching information non-blocking
$c->search->match(
corpus_id => $c->stash('corpus_id'),
- text_id => $c->stash('text_id'),
doc_id => $c->stash('doc_id'),
+ text_id => $c->stash('text_id'),
match_id => $c->stash('match_id'),
%query,
@@ -127,18 +118,98 @@
);
};
+
1;
__END__
+=pod
-# Todo: Add X-Forwarded-For to user agent call everywhere
- $self->hook(before_dispatch => sub {
- my $c = shift;
- my $h = $c->res->headers;
- $h->header( 'Access-Control-Allow-Origin' => '*' );
- $h->header( 'Access-Control-Allow-Methods' => 'GET, PUT, POST, DELETE, OPTIONS' );
- $h->header( 'Access-Control-Max-Age' => 3600 );
- $h->header( 'Access-Control-Allow-Headers' => 'Content-Type, Authorization, X-Requested-With' );
- });
+=encoding utf8
+
+=head1 NAME
+
+Kalamar::Controller::Search
+
+
+=head1 DESCRIPTION
+
+L<Kalamar::Controller::Search> is the controller class for
+search related endpoints in Kalamar.
+
+
+=head1 METHODS
+
+L<Kalamar::Controller::Search> inherits all methods from
+L<Mojolicious::Controller> and implements the following new ones.
+
+=head2 search
+
+Action for all documentation pages.
+
+=head3 q
+
+Query parameter
+
+=head3 ql
+
+Query language
+
+=head3 action
+
+May be C<inspect>.
+
+=head3 snippet
+
+=head3 cutoff
+
+=head3 count
+
+=head3 p
+
+Number of page
+
+
+=head2 match
+
+/:corpus_id/:doc_id/:text_id/:match_id
+
+=head3 foundry
+
+=head3 layer
+
+=head3 spans
+
+true or false
+
+=head3 corpus_id
+
+
+stash value
+
+=head3 doc_id
+
+=head3 text_id
+
+=head3 match_id
+
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2015, L<IDS Mannheim|http://www.ids-mannheim.de/>
+Author: L<Nils Diewald|http://nils-diewald.de/>
+
+Kalamar is developed as part of the L<KorAP|http://korap.ids-mannheim.de/>
+Corpus Analysis Platform at the Institute for German Language
+(L<IDS|http://ids-mannheim.de/>),
+funded by the
+L<Leibniz-Gemeinschaft|http://www.leibniz-gemeinschaft.de/en/about-us/leibniz-competition/projekte-2011/2011-funding-line-2/>
+and supported by the L<KobRA|http://www.kobra.tu-dortmund.de> project,
+funded by the Federal Ministry of Education and Research
+(L<BMBF|http://www.bmbf.de/en/>).
+
+Kalamar is free software published under the
+L<BSD-2 License|https://raw.githubusercontent.com/KorAP/Kalamar/master/LICENSE).
+
+=cut
diff --git a/lib/Kalamar/Plugin/KalamarHelpers.pm b/lib/Kalamar/Plugin/KalamarHelpers.pm
index d514ceb..5434cd1 100644
--- a/lib/Kalamar/Plugin/KalamarHelpers.pm
+++ b/lib/Kalamar/Plugin/KalamarHelpers.pm
@@ -1,11 +1,164 @@
package Kalamar::Plugin::KalamarHelpers;
use Mojo::Base 'Mojolicious::Plugin';
+use Mojo::JSON 'decode_json';
+use Mojo::JSON::Pointer;
+use Mojo::ByteStream 'b';
+use Mojo::Util qw/xml_escape/;
-
-# Helpers for Kalamar
sub register {
my ($plugin, $mojo) = @_;
+ # Embed the korap architecture image
+ $mojo->helper(
+ korap_overview => sub {
+ my $c = shift;
+ my $scope = shift;
+
+ my $url = $c->url_with('/img/korap-overview.svg');
+
+ # If there is a different base - append this as a base
+ # $url->query([base => $c->stash('doc_base') // '/']);
+ $url->query([base => $c->url_for('doc_start')->to_abs // '/']);
+
+ $url->fragment($scope);
+
+ return $c->tag('object',
+ data => $url,
+ type => 'image/svg+xml',
+ alt => $c->loc('korap_overview'),
+ id => 'overview'
+ );
+ }
+ );
+
+ # Documentation link
+ $mojo->helper(
+ doc_link_to => sub {
+ my $c = shift;
+ my $title = shift;
+ my $page = pop;
+ my $scope = shift;
+ return $c->link_to(
+ $title,
+ $c->url_with('doc', scope => $scope, page => $page)
+ );
+ }
+ );
+
+
+ # Documentation alert - Under Construction!
+ $mojo->helper(
+ doc_uc => sub {
+ return shift->tag('p', 'Under Construction!')
+ }
+ );
+
+
+ # Documentation navigation helper
+ $mojo->helper(
+ doc_navi => sub {
+ my $c = shift;
+ my $items = pop;
+ my $scope = shift;
+
+ # Create unordered list
+ my $html = "<ul>\n";
+
+ # Embed all link tags
+ foreach (@$items) {
+
+ my ($active, $url) = 0;
+
+ # There is a fragment!
+ if (index($_->{id}, '#') == 0) {
+
+ my $part_scope = scalar($scope);
+ $part_scope =~ s!\/([^\/]+)$!!;
+ my $page = $1;
+ my $id = $_->{id};
+ $id =~ s/^#//;
+
+ $url = $c->url_with(
+ 'doc',
+ 'scope' => $part_scope,
+ 'page' => $page
+ );
+
+ $url->fragment($id);
+ }
+
+ # There is no fragment
+ else {
+
+ # The item is active
+ if ($c->stash('page') && $c->stash('page') eq $_->{id}) {
+ $active = 1;
+ };
+
+ # Generate url with query parameter inheritance
+ $url = $c->url_with(
+ 'doc',
+ 'scope' => $scope,
+ 'page' => $_->{id}
+ );
+
+ # Canonicalize (for empty scopes)
+ $url->path->canonicalize;
+ };
+
+ my @classes;
+ push(@classes, $_->{'class'}) if $_->{'class'};
+ push(@classes, 'active') if $active;
+
+
+ # New list item
+ $html .= '<li';
+ if (@classes) {
+ $html .= ' class="' . join(' ', @classes) . '"';
+ };
+ $html .= '>';
+
+
+ # Generate link
+ $html .= $c->link_to($_->{title}, $url);
+
+ # Set sub entries
+ if ($_->{items} && ref($_->{items}) eq 'ARRAY') {
+ $html .= "\n";
+ my $subscope = $scope ? scalar($scope) . '/' . $_->{id} : $_->{id};
+ $html .= $c->doc_navi($subscope, $_->{items});
+ $html .= "</li>\n";
+ }
+ else {
+ $html .= "</li>\n";
+ };
+ };
+ return $html . "</ul>\n";
+ }
+ );
+
+
+ # Create helper for queries in the tutorial
+ $mojo->helper(
+ kalamar_tut_query => sub {
+ my ($c, $ql, $q, %param) = @_;
+
+ # Escape query for html embedding
+ $q = xml_escape $q;
+
+ # Return tag
+ b('<pre class="query tutorial" ' .
+ qq!data-query="$q" data-query-cutoff="! .
+ ($param{cutoff} ? 1 : 0) .
+ '"' .
+ qq! data-query-language="$ql">! .
+ '<code>' . $q . '</code>' .
+ '</pre>'
+ );
+ }
+ );
+
+
# Check for test port
$mojo->helper(
kalamar_test_port => sub {
@@ -29,4 +182,169 @@
});
};
+
1;
+
+
+__END__
+
+
+
+
+
+ # Create links in the tutorial that make sure the current position is preserved,
+ # in case the tutorial was opened embedded
+ $mojo->helper(
+ kalamar_tut_link_to => sub {
+ my $c = shift;
+ my $title = shift;
+ my $link = shift;
+ my $host = $c->req->headers->header('X-Forwarded-Host');
+ my $url = $c->url_for($link);
+
+ # Link is part of the embedded tutorial
+ if ($c->param('embedded')) {
+ $url->query({ embedded => 1 });
+ return $c->link_to(
+ $title,
+ $url,
+ onclick => qq!setTutorialPage("$url")!
+ );
+ };
+
+ # Build link
+ return $c->link_to($title, $url);
+ }
+ );
+
+
+
+ # Create helper for queries in the tutorial
+ $mojo->helper(
+ kalamar_tut_query => sub {
+ my ($c, $ql, $q, %param) = @_;
+ my $onclick = 'top.useQuery(this)';
+
+ my ($fail, $pass, @report) = (0,0);
+
+ if ($c->param('testing')) {
+ $c->res->headers->cache_control('max-age=1, no-cache');
+ };
+
+ # Store current tutorial position
+ if ($c->param('embedded')) {
+ $onclick = 'setTutorialPage(this);' . $onclick;
+ }
+
+ # Tutorial wasn't embedded - but opened for testing
+ # elsif ($c->param('testing') &&
+# $c->kalamar_test_port &&
+# $param{tests}) {
+#
+# Currently disabled
+
+# my $tests = $param{tests} // [];
+# my $index = $c->search(
+# query => $q,
+# ql => $ql,
+# cutoff => 'true',
+# no_cache => 1
+# );
+#
+# # Get the raw results
+# my $json = decode_json($index->api_response);
+#
+# # There is a response
+# if ($json) {
+# my $json_pointer = Mojo::JSON::Pointer->new($json);
+# foreach my $test (@$tests) {
+# my ($type, $path, @rest) = @$test;
+#
+# # Check for equality
+# if ($type eq 'is') {
+# my $found = $json_pointer->get($path);
+# if ($found && $found eq $rest[0]) {
+# $pass++;
+# }
+# else {
+# my $result = $path . q! isn't ! . shift @rest;
+# $result .= ' but was ' . $found if $found;
+# $result .= '; ' . join('; ', @rest);
+# push(@report, $result);
+# $fail++;
+# };
+# }
+#
+# # Check for inequality
+# elsif ($type eq 'isnt') {
+# if ($json_pointer->get($path) ne $rest[0]) {
+# $pass++;
+# }
+# else {
+# push(@report, $path . q! is ! . join('; ', @rest));
+# $fail++;
+# };
+# }
+#
+# # Check for existence
+# elsif ($type eq 'ok') {
+# if ($json_pointer->contains($path)) {
+# $pass++;
+# }
+# else {
+# push(@report, $path . q! doesn't exist; ! . join('; ', @rest));
+# $fail++;
+# };
+# }
+#
+# # Check for inexistence
+# elsif ($type eq 'not_ok') {
+# unless ($json_pointer->contains($path)) {
+# $pass++;
+# }
+# else {
+# push(@report, $path . q! doesn't exist; ! . join('; ', @rest));
+# $fail++;
+# };
+# };
+# };
+# }
+# else {
+# # There may be notifications here!
+# $fail++ foreach @$tests;
+# };
+#
+# # Emit hook to possible subscribers
+# # This is used for self-testing
+# # $plugin->emit_hook(kalamar_tut_query => (
+# # query_language => $ql,
+# # query => $q,
+# # %param
+# # ));
+ # };
+
+ # Escape query for html embedding
+ $q = xml_escape $q;
+
+ # There is something to talk about
+ my $msg = '';
+ if (($pass + $fail) > 0) {
+ $msg .= '<div class="test">';
+ $msg .= qq!<p class="pass">Pass: $pass</p>!;
+ $msg .= qq!<p class="fail">Fail: $fail</p>! if $fail;
+ foreach (@report) {
+ $msg .= qq!<p class="fail">${_}</p>!;
+ };
+ $msg .= '</div>';
+ };
+
+ # Return tag
+ b('<pre class="query tutorial" onclick="' . $onclick . '" ' .
+ qq!data-query="$q" data-query-cutoff="! .
+ ($param{cutoff} ? 1 : 0) .
+ '"' .
+ qq! data-query-language="$ql"><code>! .
+ $q .
+ '</code></pre>' . $msg);
+ }
+ );
diff --git a/lib/Kalamar/Plugin/KalamarTagHelpers.pm b/lib/Kalamar/Plugin/KalamarTagHelpers.pm
deleted file mode 100644
index d039604..0000000
--- a/lib/Kalamar/Plugin/KalamarTagHelpers.pm
+++ /dev/null
@@ -1,235 +0,0 @@
-package Kalamar::Plugin::KalamarTagHelpers;
-use Mojo::Base 'Mojolicious::Plugin';
-use Mojo::JSON 'decode_json';
-use Mojo::JSON::Pointer;
-use Mojo::ByteStream 'b';
-use Mojo::Util qw/xml_escape/;
-
-sub register {
- my ($plugin, $mojo) = @_;
-
- # Embed the korap architecture image
- $mojo->helper(
- korap_overview => sub {
- my $c = shift;
- my $scope = shift;
-
- my $url = $c->url_with('/img/korap-overview.svg');
-
- # If there is a different base - append this as a base
- $url->query([base => $c->stash('doc_base') // '/']);
-
- $url->fragment($scope);
-
- return $c->tag('object',
- data => $url,
- type => 'image/svg+xml',
- alt => $c->loc('korap_overview'),
- id => 'overview'
- );
- }
- );
-
-
-
- $mojo->helper(
- doc_link_to => sub {
- my $c = shift;
- my $title = shift;
- my $page = pop;
- my $scope = shift;
- return $c->link_to($title, $c->url_with('doc', scope => $scope, page => $page));
- }
- );
-
- $mojo->helper(
- doc_uc => sub {
- return shift->tag('p', 'Under Construction!')
- }
- );
-
-
- # Create helper for queries in the tutorial
- $mojo->helper(
- kalamar_tut_query => sub {
- my ($c, $ql, $q, %param) = @_;
-
- # Escape query for html embedding
- $q = xml_escape $q;
-
- # Return tag
- b('<pre class="query tutorial" ' .
- qq!data-query="$q" data-query-cutoff="! .
- ($param{cutoff} ? 1 : 0) .
- '"' .
- qq! data-query-language="$ql">! .
- '<code>' . $q . '</code>' .
- '</pre>'
- );
- }
- );
-
-
- # Create links in the tutorial that make sure the current position is preserved,
- # in case the tutorial was opened embedded
- $mojo->helper(
- kalamar_tut_link_to => sub {
- my $c = shift;
- my $title = shift;
- my $link = shift;
- my $host = $c->req->headers->header('X-Forwarded-Host');
- my $url = $c->url_for($link);
-
- # Link is part of the embedded tutorial
- if ($c->param('embedded')) {
- $url->query({ embedded => 1 });
- return $c->link_to(
- $title,
- $url,
- onclick => qq!setTutorialPage("$url")!
- );
- };
-
- # Build link
- return $c->link_to($title, $url);
- }
- );
-};
-
-1;
-
-
-__END__
-
-
-
-
- # Create helper for queries in the tutorial
- $mojo->helper(
- kalamar_tut_query => sub {
- my ($c, $ql, $q, %param) = @_;
- my $onclick = 'top.useQuery(this)';
-
- my ($fail, $pass, @report) = (0,0);
-
- if ($c->param('testing')) {
- $c->res->headers->cache_control('max-age=1, no-cache');
- };
-
- # Store current tutorial position
- if ($c->param('embedded')) {
- $onclick = 'setTutorialPage(this);' . $onclick;
- }
-
- # Tutorial wasn't embedded - but opened for testing
- # elsif ($c->param('testing') &&
-# $c->kalamar_test_port &&
-# $param{tests}) {
-#
-# Currently disabled
-
-# my $tests = $param{tests} // [];
-# my $index = $c->search(
-# query => $q,
-# ql => $ql,
-# cutoff => 'true',
-# no_cache => 1
-# );
-#
-# # Get the raw results
-# my $json = decode_json($index->api_response);
-#
-# # There is a response
-# if ($json) {
-# my $json_pointer = Mojo::JSON::Pointer->new($json);
-# foreach my $test (@$tests) {
-# my ($type, $path, @rest) = @$test;
-#
-# # Check for equality
-# if ($type eq 'is') {
-# my $found = $json_pointer->get($path);
-# if ($found && $found eq $rest[0]) {
-# $pass++;
-# }
-# else {
-# my $result = $path . q! isn't ! . shift @rest;
-# $result .= ' but was ' . $found if $found;
-# $result .= '; ' . join('; ', @rest);
-# push(@report, $result);
-# $fail++;
-# };
-# }
-#
-# # Check for inequality
-# elsif ($type eq 'isnt') {
-# if ($json_pointer->get($path) ne $rest[0]) {
-# $pass++;
-# }
-# else {
-# push(@report, $path . q! is ! . join('; ', @rest));
-# $fail++;
-# };
-# }
-#
-# # Check for existence
-# elsif ($type eq 'ok') {
-# if ($json_pointer->contains($path)) {
-# $pass++;
-# }
-# else {
-# push(@report, $path . q! doesn't exist; ! . join('; ', @rest));
-# $fail++;
-# };
-# }
-#
-# # Check for inexistence
-# elsif ($type eq 'not_ok') {
-# unless ($json_pointer->contains($path)) {
-# $pass++;
-# }
-# else {
-# push(@report, $path . q! doesn't exist; ! . join('; ', @rest));
-# $fail++;
-# };
-# };
-# };
-# }
-# else {
-# # There may be notifications here!
-# $fail++ foreach @$tests;
-# };
-#
-# # Emit hook to possible subscribers
-# # This is used for self-testing
-# # $plugin->emit_hook(kalamar_tut_query => (
-# # query_language => $ql,
-# # query => $q,
-# # %param
-# # ));
- # };
-
- # Escape query for html embedding
- $q = xml_escape $q;
-
- # There is something to talk about
- my $msg = '';
- if (($pass + $fail) > 0) {
- $msg .= '<div class="test">';
- $msg .= qq!<p class="pass">Pass: $pass</p>!;
- $msg .= qq!<p class="fail">Fail: $fail</p>! if $fail;
- foreach (@report) {
- $msg .= qq!<p class="fail">${_}</p>!;
- };
- $msg .= '</div>';
- };
-
- # Return tag
- b('<pre class="query tutorial" onclick="' . $onclick . '" ' .
- qq!data-query="$q" data-query-cutoff="! .
- ($param{cutoff} ? 1 : 0) .
- '"' .
- qq! data-query-language="$ql"><code>! .
- $q .
- '</code></pre>' . $msg);
- }
- );
diff --git a/templates/doc/_nav.html.ep b/templates/doc/_nav.html.ep
deleted file mode 100644
index 8f9526f..0000000
--- a/templates/doc/_nav.html.ep
+++ /dev/null
@@ -1,67 +0,0 @@
-% content_for sidebar => begin
-<nav>
- <ul>
- <li><a href="/doc/korap" class="active">KorAP</a>
- <ul>
-% foreach (qw/kalamar kustvakt koral krill karang/) {
-% my $url = url_with('doc', scope => '', page => $_);
-% $url->path->canonicalize;
- <li><%= link_to ucfirst($_), $url %></li>
-% }
- </ul>
- </li>
- <li><a href="/doc/ql">Query Languages</a>
- <ul>
-% foreach ('Cosmas II'qw//) {
-% my $url = url_with('doc', scope => '', page => $_);
-% $url->path->canonicalize;
- <li><%= link_to ucfirst($_), $url %></li>
-% }
-<!--
- <li><a href="/doc/ql/cosmas-2">Cosmas II</a></li>
- <li><a href="/doc/ql/poliqarp-plus">Poliqarp+</a>
- <ul>
- <li><a href="#tut-segments">Simple Segments</a></li>
- <li><a href="#tut-complex">Complex Segments</a></li>
- <li><a href="#tut-spans">Span Segments</a></li>
- <li><a href="#tut-paradigmatic-operators">Paradigmatic Operators</a></li>
- <li><a href="#tut-syntagmatic-operators">Syntagmatic Operators</a></li>
- <li><a href="#tut-class-operators">Class Operators</a></li>
- </ul>
- </li>
- <li><a href="/doc/ql/annis">Annis QL</a></li>
- <li><a href="/doc/ql/cql">CQL</a></li>
- <li><a href="/doc/ql/regexp">RegExp</a></li>
-->
- </ul>
- </li>
- <li><a href="/doc/data">Data</a>
- <ul>
- <li><a href="/doc/data/corpus">Corpora</a>
- <ul>
- <li><a href="/doc/data/corpus/dereko">DeReKo</a></li>
- </ul>
- </li>
- <li><a href="/doc/data/annotation">Annotations</a>
- <ul>
- <li><a href="/doc/data/annotation/connexor">Connexor</a></li>
- <li><a href="/doc/data/annotation/mate">Mate</a></li>
- <li><a href="/doc/data/annotation/treetagger">TreeTagger</a></li>
- </ul>
- </li>
- </ul>
- </li>
- <li><a href="/doc/api">API</a>
- <!--
- <ul>
- <li><a href="/doc/api/koralquery">KoralQuery</a></li>
- <li><a href="/doc/api/search">Search API</a></li>
- <li><a href="/doc/api/match">Match Information API</a></li>
- <li><a href="/doc/api/user">User API</a></li>
- </ul>
- -->
- </li>
- <li><a href="/doc/faq">FAQ (mit Mail)</a>
- </ul>
-</nav>
-% end
diff --git a/templates/doc/_nav.json b/templates/doc/navigation.json
similarity index 100%
rename from templates/doc/_nav.json
rename to templates/doc/navigation.json