Merge "Show tooltips for annotations in table view"
diff --git a/dev/js/spec/menuSpec.js b/dev/js/spec/menuSpec.js
index aba3f1e..a60d7ab 100644
--- a/dev/js/spec/menuSpec.js
+++ b/dev/js/spec/menuSpec.js
@@ -295,6 +295,7 @@
// Highlight in the middle - both
menuItem = KorAP.ComplexMenuItem.create(['CoreNLP', 'corenlp/', 'This is my Example']);
menuItem.highlight("e");
+
expect(menuItem.element().innerHTML).toEqual("<div><h1>Cor<mark>e</mark>NLP</h1><h2>cor<mark>e</mark>nlp/</h2><h3>This is my <mark>E</mark>xampl<mark>e</mark></h3></div>");
menuItem.lowlight();
@@ -328,6 +329,21 @@
["Syntax", "syn="]
];
+ var listMultiPrefix = [
+ ["PP","PP ","Personal Pronoun"],
+ ["PPP","PPP ","Personal Pronoun, Plural"],
+ ["PPPA","PPPA ","Personal Pronoun, Plural, Acc."],
+ ["PPPD","PPPD ","Personal Pronoun, Plural, Dative"],
+ ["PPPR","PPPR ","Personal Pronoun, Plural, Direct"],
+ ["PPPO","PPPO ","Personal Pronoun, Plural, Oblique"],
+ ["PPS","PPS ","Personal Pronoun, Singular"],
+ ["PPSA","PPSA ","Personal Pronoun, Singular, Accusative"],
+ ["PPSD","PPSD ","Personal Pronoun, Singular, Dative"],
+ ["PPSR","PPSR ","Personal Pronoun, Singular, Direct"],
+ ["PPSN","PPSN ","Personal Pronoun, Singular, Nominative"],
+ ["PPSO","PPSO ","Personal Pronoun, Singular, Oblique"]
+ ];
+
var demolist = [
['Titel', 'title'],
['Untertitel', 'subTitle'],
@@ -784,7 +800,43 @@
expect(menu.shownItem(2)).toBe(undefined);
});
+ it('should be filterable (multiple prefix = "pro sin")', function () {
+ var menu = KorAP.HintMenu.create("drukola/p=", listMultiPrefix);
+ menu._firstActive = true;
+ menu.limit(2);
+ expect(menu.prefix("pro sin").show()).toBe(true);
+ expect(menu.shownItem(0).name()).toEqual("PPS");
+ expect(menu.element().childNodes[3].innerHTML).toEqual(
+ "<strong>PPS</strong><span>Personal <mark>Pro</mark>noun, <mark>Sin</mark>gular</span>"
+ );
+
+ expect(menu.shownItem(0).active()).toBe(true);
+ expect(menu.shownItem(1).name()).toEqual("PPSA");
+ expect(menu.element().childNodes[4].innerHTML).toEqual("<strong>PPSA</strong><span>Personal <mark>Pro</mark>noun, <mark>Sin</mark>gular, Accusative</span>");
+ expect(menu.shownItem(1).active()).toBe(false);
+
+ expect(menu.shownItem(2)).toBe(undefined);
+ });
+
+ it('should be filterable (trimming = " p")', function () {
+ var menu = KorAP.HintMenu.create("/p=", listMultiPrefix);
+ // menu._firstActive = true;
+
+ menu.limit(2);
+ expect(menu.show()).toBe(true);
+ menu._prefix.add(" ");
+ expect(menu.show()).toBe(true);
+ menu._prefix.add("p")
+ expect(menu.show()).toBe(true);
+ expect(menu.shownItem(0).name()).toEqual("PP");
+ expect(menu.element().childNodes[3].innerHTML).toEqual(
+ "<strong><mark>P</mark><mark>P</mark></strong>"+
+ "<span><mark>P</mark>ersonal <mark>P</mark>ronoun</span>"
+ );
+ });
+
+
it('should choose prefix with failing prefix (1)', function () {
var menu = KorAP.HintMenu.create("cnx/", list);
menu.limit(2);
diff --git a/dev/js/src/menu.js b/dev/js/src/menu.js
index e3a8c40..d7ec89f 100644
--- a/dev/js/src/menu.js
+++ b/dev/js/src/menu.js
@@ -4,7 +4,6 @@
* @author Nils Diewald
*/
/*
- * TODO: space is not a valid prefix!
* TODO: Show the slider briefly on move (whenever screen is called).
* TODO: Ignore alt+ and strg+ key strokes.
* TODO: Should scroll to a chosen value after prefixing, if the chosen value is live
@@ -182,18 +181,44 @@
* There is a prefix set, so filter the list!
*/
var pos;
- var prefix = " " + this.prefix().toLowerCase();
+ var prefixList = this.prefix().toLowerCase().split(" ");
+
+ var items = [];
+ var maxPoints = 1; // minimum 1
// Iterate over all items and choose preferred matching items
// i.e. the matching happens at the word start
for (pos = 0; pos < this._items.length; pos++) {
- if ((this.item(pos).lcField().indexOf(prefix)) >= 0)
+
+ var points = 0;
+
+ for (pref = 0; pref < prefixList.length; pref++) {
+ var prefix = " " + prefixList[pref];
+
+ // Check if it matches at the beginning
+ if ((this.item(pos).lcField().indexOf(prefix)) >= 0) {
+ points += 5;
+ }
+
+ // Check if it matches anywhere
+ else if ((this.item(pos).lcField().indexOf(prefix.substring(1))) >= 0) {
+ points += 1;
+ };
+ };
+
+ if (points > maxPoints) {
+ this._list = [pos];
+ maxPoints = points;
+ }
+ else if (points == maxPoints) {
this._list.push(pos);
+ }
};
// The list is empty - so lower your expectations
// Iterate over all items and choose matching items
// i.e. the matching happens anywhere in the word
+ /*
prefix = prefix.substring(1);
if (this._list.length == 0) {
for (pos = 0; pos < this._items.length; pos++) {
@@ -201,6 +226,7 @@
this._list.push(pos);
};
};
+ */
this._slider.length(this._list.length).reInit();
diff --git a/dev/js/src/menu/item.js b/dev/js/src/menu/item.js
index 3f955be..812a398 100644
--- a/dev/js/src/menu/item.js
+++ b/dev/js/src/menu/item.js
@@ -5,6 +5,8 @@
* further: action happen on right arrow
*/
+"use strict";
+
/**
* Item in the Dropdown menu
*/
@@ -153,13 +155,13 @@
for (var i = marks.length - 1; i >= 0; i--) {
// Create text node clone
var x = document.createTextNode(
- marks[i].firstChild.nodeValue
+ marks[i].firstChild.nodeValue
);
// Replace with content
marks[i].parentNode.replaceChild(
- x,
- marks[i]
+ x,
+ marks[i]
);
};
@@ -169,48 +171,82 @@
},
// Highlight a certain substring of the menu item
- _highlight : function (elem, prefix) {
+ _highlight : function (elem, prefixString) {
if (elem.nodeType === 3) {
var text = elem.nodeValue;
var textlc = text.toLowerCase();
- var pos = textlc.indexOf(prefix);
- if (pos >= 0) {
+
+ // Split prefixes
+ if (prefixString) {
+
+ // ND:
+ // Doing this in a single line can trigger
+ // a deep-recursion in Firefox 57.01, though I don't know why.
+ prefixString = prefixString.trim();
+ var prefixes = prefixString.split(" ");
+
+ var prefix;
+ var testPos;
+ var pos = -1;
+ var len = 0;
+
+ // Iterate over all prefixes and get the best one
+ for (var i = 0; i < prefixes.length; i++) {
+
+ // Get first pos of a matching prefix
+ testPos = textlc.indexOf(prefixes[i]);
+ if (testPos < 0)
+ continue;
+
+ if (pos === -1 || testPos < pos) {
+ pos = testPos;
+ len = prefixes[i].length;
+ }
+ else if (testPos === pos && prefixes[i].length > len) {
+ len = prefixes[i].length;
+ };
+ };
+
+ // Matches!
+ if (pos >= 0) {
- // First element
- if (pos > 0) {
- elem.parentNode.insertBefore(
- document.createTextNode(text.substr(0, pos)),
- elem
- );
- };
+ // First element
+ if (pos > 0) {
+ elem.parentNode.insertBefore(
+ document.createTextNode(text.substr(0, pos)),
+ elem
+ );
+ };
- // Second element
- var hl = document.createElement("mark");
- hl.appendChild(
- document.createTextNode(text.substr(pos, prefix.length))
- );
- elem.parentNode.insertBefore(hl, elem);
+ // Second element
+ var hl = document.createElement("mark");
+ hl.appendChild(
+ document.createTextNode(text.substr(pos, len))
+ );
+ elem.parentNode.insertBefore(hl, elem);
- // Third element
- var third = text.substr(pos + prefix.length);
- if (third.length > 0) {
- var thirdE = document.createTextNode(third);
- elem.parentNode.insertBefore(
- thirdE,
- elem
- );
- this._highlight(thirdE, prefix);
- };
+ // Third element
+ var third = text.substr(pos + len);
+
+ if (third.length > 0) {
+ var thirdE = document.createTextNode(third);
+ elem.parentNode.insertBefore(
+ thirdE,
+ elem
+ );
+ this._highlight(thirdE, prefixString);
+ };
- var p = elem.parentNode;
- p.removeChild(elem);
+ var p = elem.parentNode;
+ p.removeChild(elem);
+ };
};
}
else {
var children = elem.childNodes;
for (var i = children.length -1; i >= 0; i--) {
- this._highlight(children[i], prefix);
+ this._highlight(children[i], prefixString);
};
};
},
diff --git a/dev/js/src/menu/prefix.js b/dev/js/src/menu/prefix.js
index abcf66c..90924c9 100644
--- a/dev/js/src/menu/prefix.js
+++ b/dev/js/src/menu/prefix.js
@@ -107,7 +107,7 @@
// Prefix is long enough for backspace
if (this._string.length > 1) {
this._string = this._string.substring(
- 0, this._string.length - 1
+ 0, this._string.length - 1
);
}
else {
diff --git a/templates/doc/korap/kustvakt.html.ep b/templates/doc/korap/kustvakt.html.ep
index a6be167..e3873c0 100644
--- a/templates/doc/korap/kustvakt.html.ep
+++ b/templates/doc/korap/kustvakt.html.ep
@@ -4,6 +4,9 @@
%= korap_overview 'kustvakt'
-<p><strong>Main developer:</strong> Michael Hanl</p>
+<p><strong>Main developer:</strong> Michael Hanl, Eliza Margaretha, Franck Bodmer</p>
<p>Kustvakt is a user and policy management software, that is capable of rewriting queries for policy based document restrictions.</p>
+
+<p>Kustvakt is open source and available on <%= doc_ext_link_to 'GitHub', "https://github.com/KorAP/Kustvakt" %>.</p>
+
diff --git a/templates/doc/navigation.json b/templates/doc/navigation.json
index 12e4683..eff474e 100644
--- a/templates/doc/navigation.json
+++ b/templates/doc/navigation.json
@@ -73,6 +73,10 @@
"id" : "cql"
},
{
+ "title" : "FCSQL",
+ "id" : "fcsql"
+ },
+ {
"title" : "Regular Expressions",
"id" : "regexp"
},
diff --git a/templates/doc/ql.html.ep b/templates/doc/ql.html.ep
index ad1b176..5094811 100644
--- a/templates/doc/ql.html.ep
+++ b/templates/doc/ql.html.ep
@@ -22,32 +22,32 @@
%# [isnt => 'json_pointer', 'result']
%# [not_ok => 'json_pointer']
- <p><strong>Poliqarp</strong>: Find all occurrences of the lemma "baum" as annotated by the <%= doc_link_to 'default foundry', 'data', 'annotation' %>.</p>
+ <p><strong><%= doc_link_to 'Poliqarp', 'ql', 'poliqarp-plus' %></strong>: Find all occurrences of the lemma "baum" as annotated by the <%= doc_link_to 'default foundry', 'data', 'annotation' %>.</p>
%= doc_query poliqarp => '[base=Baum]', 'tests' => [[is => '/query', 'tokens:tt/l:Baum'],[is => '/request/query/wrap/layer', 'lemma'],[is => '/request/query/wrap/foundry', 'tt'], [ok => '/matches/10']]
- <p><strong>Poliqarp</strong>: Find all sequences of adjectives as annotated by Treetagger, that are repeated 3 to 5 times in a row.</p>
+ <p><strong><%= doc_link_to 'Poliqarp', 'ql', 'poliqarp-plus' %></strong>: Find all sequences of adjectives as annotated by Treetagger, that are repeated 3 to 5 times in a row.</p>
%= doc_query poliqarp => '[tt/p=ADJA]{3,5}', 'tests' => [[is => '/query', 'spanRepetition(tokens:tt/p:ADJA{3,5})'], [is => '/request/query/operation', 'operation:repetition'],[is => '/request/query/operands/0/wrap/foundry', 'tt'], [ok => '/matches/5']]
- <p><strong>Cosmas-II</strong>: Find all occurrences of the words "der" and "Baum", in case they are in a maximum distance of 5 tokens. The order is not relevant.</p>
+ <p><strong><%= doc_link_to 'Cosmas-II', 'ql', 'cosmas-2' %></strong>: Find all occurrences of the words "der" and "Baum", in case they are in a maximum distance of 5 tokens. The order is not relevant.</p>
%= doc_query cosmas2 => 'der /w5 Baum', 'tests' => [[is => '/query', 'shrink(129: spanDistance({129: tokens:s:der}, {129: tokens:s:Baum}, [(w[0:5], notOrdered, notExcluded)]))'], [is => '/request/query/operation', 'operation:focus'], [is => '/request/query/@type', 'korap:reference'],[is => '/request/query/operands/0/operands/1/operation', 'operation:class'], [is => '/itemsPerPage', 25], [ok => '/matches/20'], [is => '/matches/4/corpusID', 'WPD'], [is => '/matches/12/corpusID', 'WPD']]
- <p><strong>Cosmas-II</strong>: Find all sequences of a word starting with a "d" (using a wildcard) followed by an adjective as annotated in the mate foundry, followed by the word "Baum" (ignore the case), that is in a sentence element annotated by the <%= doc_link_to 'default foundry', 'data', 'annotation' %>.</p>
+ <p><strong><%= doc_link_to 'Cosmas-II', 'ql', 'cosmas-2' %></strong>: Find all sequences of a word starting with a "d" (using a wildcard) followed by an adjective as annotated in the mate foundry, followed by the word "Baum" (ignore the case), that is in a sentence element annotated by the <%= doc_link_to 'default foundry', 'data', 'annotation' %>.</p>
<p><em>Be aware</em>: Minor incompatibilities with implemented languages may be announced with warnings.</p>
%= doc_query cosmas2 => 'd* MORPH(mate/p=ADJA) $Baum #IN #ELEM(s)', 'tests' => [[ok => '/matches/3'], [is => '/query', 'shrink(130: {131: spanContain({129: <tokens:s />}, {130: spanNext(spanNext(SpanMultiTermQueryWrapper(tokens:s:d*), tokens:mate/p:ADJA), tokens:i:baum)})})'], [is => '/request/query/@type', 'korap:reference'], [is => '/request/query/operation', 'operation:focus'], [is => '/request/query/operands/0/operands/0/operation', 'operation:position'], [is => '/request/query/operands/0/operands/0/operands/1/operands/0/operation', 'operation:sequence'], [is => '/request/query/operands/0/operands/0/operands/1/operands/0/operands/0/wrap/type', 'type:wildcard'], [is => '/request/query/operands/0/operands/0/operands/1/operands/0/operands/1/wrap/key', 'ADJA'], [is => '/request/query/operands/0/operands/0/operands/1/operands/0/operands/1/wrap/foundry', 'mate'], [ok => '/request/query/operands/0/operands/0/operands/1/operands/0/operands/2/wrap/caseInsensitive'], [ok => '/matches/5']]
- <p><strong>Poliqarp+</strong>: Find all nominal phrases as annotated using Connexor, that contain an adverb as annotated by OpenNLP, that is annotated as something starting with an "A" using regular expressions in Treetagger.</p>
+ <p><strong><%= doc_link_to 'Poliqarp+', 'ql', 'poliqarp-plus' %></strong>: Find all nominal phrases as annotated using Connexor, that contain an adverb as annotated by OpenNLP, that is annotated as something starting with an "A" using regular expressions in Treetagger.</p>
%= doc_query poliqarp => 'contains(<cnx/c=np>,{[opennlp/p=ADV & tt/p="A.*"]})', cutoff => 1, 'tests' => [[is => '/query', 'spanContain(<tokens:cnx/c:np />, {1: spanSegment(tokens:opennlp/p:ADV, SpanMultiTermQueryWrapper(tokens:/tt/p:A.*/))})'], [is => '/request/query/operation', 'operation:position'], [is => '/request/query/frames/0', 'frames:contains'], [is => '/request/query/operands/0/foundry', 'cnx'], [is => '/request/query/operands/0/layer', 'c'], [is => '/request/query/operands/0/foundry', 'cnx'], [is => '/request/query/operands/0/key', 'np'], [is => '/request/query/operands/1/operands/0/wrap/operands/0/foundry', 'opennlp'], [is => '/request/query/operands/1/operands/0/wrap/operands/0/layer', 'p'], [is => '/request/query/operands/1/operands/0/wrap/operands/1/foundry', 'tt'], [is => '/request/query/operands/1/operands/0/wrap/operands/1/type', 'type:regex'], [is => '/request/query/operands/1/operands/0/wrap/operands/1/key', 'A.*'], [ok => '/matches/5']]
- <p><strong>Poliqarp+</strong>: Find all sentences as annotated by the base foundry that start with a sequence of one token in present tense as annotated by Connexor and the lemma "die" annotated by the <%= doc_link_to 'default foundry', 'data', 'annotation' %>. Highlight both terms of the sequence.</p>
+ <p><strong><%= doc_link_to 'Poliqarp+', 'ql', 'poliqarp-plus' %></strong>: Find all sentences as annotated by the base foundry that start with a sequence of one token in present tense as annotated by Connexor and the lemma "die" annotated by the <%= doc_link_to 'default foundry', 'data', 'annotation' %>. Highlight both terms of the sequence.</p>
%= doc_query poliqarp => 'startswith(<base/s=s>, {1:[cnx/m=PRES]}{2:[base=die]})', cutoff => 1, tests => [[is => '/query', 'spanStartsWith(<tokens:s />, spanNext({1: tokens:cnx/m:PRES}, {2: tokens:tt/l:die}))'], [is => '/request/meta/startPage', 1], [is => '/request/query/operation', 'operation:position'], [is => '/request/query/operands/0/@type','korap:span'], [is => '/request/query/operands/1/operands/0/operation', 'operation:class'], [is => '/request/query/operands/1/operands/1/operation', 'operation:class'], [is => '/request/query/operands/1/operands/1/operands/0/wrap/foundry', 'tt'], [ok => '/matches/4']]
- <p><strong>Poliqarp+</strong>: Find all sequences of an article, followed by three to four adjectives and a noun as annotated by the Treetagger foundry, that finish a sentence. Highlight all parts of the sequence.</p>
+ <p><strong><%= doc_link_to 'Poliqarp+', 'ql', 'poliqarp-plus' %></strong>: Find all sequences of an article, followed by three to four adjectives and a noun as annotated by the Treetagger foundry, that finish a sentence. Highlight all parts of the sequence.</p>
%= doc_query poliqarp => 'focus(3:endswith(<base/s=s>,{3:[tt/p=ART]{1:{2:[tt/p=ADJA]{3,4}}[tt/p=NN]}}))', cutoff => 1, 'tests' => [[is => '/query', 'shrink(3: spanEndsWith(<tokens:s />, {3: spanNext(tokens:tt/p:ART, {1: spanNext({2: spanRepetition(tokens:tt/p:ADJA{3,4})}, tokens:tt/p:NN)})}))'], [is => '/request/query/operation', 'operation:focus'], [is => '/request/query/operands/0/frames/0', 'frames:endswith'], [ok => '/matches/3'], [is => '/matches/4/corpusID', 'WPD']]
- <p><strong>Annis</strong>: Find all occurrences of the sequence of two tokens annotated as adverbs by the <%= doc_link_to 'default foundry', 'data', 'annotation' %>.</p>
+ <p><strong><%= doc_link_to 'Annis', 'ql', 'annis' %></strong>: Find all occurrences of the sequence of two tokens annotated as adverbs by the <%= doc_link_to 'default foundry', 'data', 'annotation' %>.</p>
%= doc_query annis => 'pos="ADV" & pos="ADV" & #1 . #2', 'tests' => [[is => '/query', 'spanNext(tokens:tt/p:ADV, tokens:tt/p:ADV)'], [is => '/request/query/operands/0/wrap/foundry', 'tt'], [is => '/request/query/operands/1/wrap/foundry', 'tt'], [ok => '/matches/5'], [ok => '/matches/15'], [is => '/matches/15/corpusID', 'WPD']]
- <p><strong>CQL</strong>: Find all occurrences of the sequence "der alte Mann".</p>
+ <p><strong><%= doc_link_to 'CQL', 'ql', 'cql' %></strong>: Find all occurrences of the sequence "der alte Mann".</p>
%= doc_query cql => '"der alte Mann"', 'tests' => [[is => '/query', 'spanNext(spanNext(tokens:s:der, tokens:s:alte), tokens:s:Mann)'], [is => '/request/query/operation', 'operation:sequence'],[is => '/request/query/operands/0/wrap/key', 'der'],[is => '/request/query/operands/1/wrap/key', 'alte'],[is => '/request/query/operands/2/wrap/key', 'Mann'], [ok => '/matches/5'], [ok => '/matches/5']]
</section>
diff --git a/templates/doc/ql/fcsql.html.ep b/templates/doc/ql/fcsql.html.ep
new file mode 100644
index 0000000..b3c6a03
--- /dev/null
+++ b/templates/doc/ql/fcsql.html.ep
@@ -0,0 +1,5 @@
+% layout 'main', title => 'KorAP: FCSQL';
+
+<h2 id="tutorial-top">FCSQL</h2>
+
+%= doc_uc