blob: 3ad2731ddce4f3889665e6085e471d75c40c0e47 [file] [log] [blame]
Nils Diewald7148c6f2015-05-04 15:07:53 +00001/**
Akronb6685bb2018-02-04 00:44:47 +01002 * Table representation of token-based
Nils Diewald7148c6f2015-05-04 15:07:53 +00003 * annotations of a match.
4 */
Akronb6685bb2018-02-04 00:44:47 +01005define([
6 'match/querycreator',
7 "util"
8], function (matchQueryCreator) {
9 /*
10 * TODO:
11 * Create base object for all matchinfo classes!
12 * TODO:
13 * Rename to match/annotationtable
14 */
Akron0b489ad2018-02-02 16:49:32 +010015 const _TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
16 const d = document;
17
Nils Diewald0e6992a2015-04-14 20:13:52 +000018 return {
Nils Diewald7148c6f2015-05-04 15:07:53 +000019
20 /**
21 * Create new table view for a match
22 * based on a snippet string.
23 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000024 create : function (snippet) {
25 return Object.create(this)._init(snippet);
26 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000027
28 // Initialize table based on snippet
Nils Diewald0e6992a2015-04-14 20:13:52 +000029 _init : function (snippet) {
30 // Create html for traversal
Akron0b489ad2018-02-02 16:49:32 +010031 var html = d.createElement("div");
Nils Diewald0e6992a2015-04-14 20:13:52 +000032 html.innerHTML = snippet;
33
34 this._pos = 0;
35 this._token = [];
Akronad1e46a2018-09-19 15:55:40 +020036 this._mark = [];
37 this._markE = undefined;
Akron1a780fe2019-05-21 15:59:00 +020038 this._cutted = [];
Nils Diewald0e6992a2015-04-14 20:13:52 +000039 this._info = [];
40 this._foundry = {};
41 this._layer = {};
42
43 // Parse the snippet
Akronad1e46a2018-09-19 15:55:40 +020044 this._parse(html.childNodes, false);
Akronb6685bb2018-02-04 00:44:47 +010045
Nils Diewald0e6992a2015-04-14 20:13:52 +000046 html.innerHTML = '';
47 return this;
48 },
Akronb6685bb2018-02-04 00:44:47 +010049
50 // TODO: Destroy match!
51 destroy : function () {
52 this._matchCreator = undefined;
53 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000054
55 /**
56 * Length of the table (columns),
57 * aka the number of tokens
58 * in the snippet.
59 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000060 length : function () {
61 return this._pos;
62 },
63
Akronad1e46a2018-09-19 15:55:40 +020064
65 /**
66 * Move the viewport to the match
67 */
68 toMark : function () {
69 if (this._markE === undefined)
70 return;
71 this._markE.scrollIntoView({
72 inline: "start",
73 block: "nearest"
74 });
75 },
76
Nils Diewald7148c6f2015-05-04 15:07:53 +000077 /**
78 * Get the token in the snippet
79 * At a given position.
80 *
81 * @param pos
82 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000083 getToken : function (pos) {
84 if (pos === undefined)
Akron916ec252016-11-10 17:06:32 +010085 return this._token;
Nils Diewald0e6992a2015-04-14 20:13:52 +000086 return this._token[pos];
87 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000088
89 /**
90 * Get the annotation of a token
91 * in the snippet based on the position,
92 * the foundry, and the layer.
93 *
94 * @param pos
95 * @param foundry
96 * @param layer
97 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000098 getValue : function (pos, foundry, layer) {
99 return this._info[pos][foundry + '/' + layer]
100 },
Nils Diewald0e6992a2015-04-14 20:13:52 +0000101
Akronaeceda72018-02-02 20:44:06 +0100102
Nils Diewald0e6992a2015-04-14 20:13:52 +0000103 // Parse the snippet
Akronad1e46a2018-09-19 15:55:40 +0200104 _parse : function (children, mark) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000105
106 // Get all children
107 for (var i in children) {
Akron916ec252016-11-10 17:06:32 +0100108 var c = children[i];
Nils Diewald0e6992a2015-04-14 20:13:52 +0000109
Akron916ec252016-11-10 17:06:32 +0100110 // Create object on position unless it exists
111 if (this._info[this._pos] === undefined) {
112 this._info[this._pos] = {};
113 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000114
Akron916ec252016-11-10 17:06:32 +0100115 // Store at position in foundry/layer as array
116 var found = this._info[this._pos];
Nils Diewald0e6992a2015-04-14 20:13:52 +0000117
Akron916ec252016-11-10 17:06:32 +0100118 // Element with title
119 if (c.nodeType === 1) {
Akronad1e46a2018-09-19 15:55:40 +0200120 var newMark = mark;
121
122 if (c.tagName === 'MARK') {
123 newMark = true;
124 }
125
Akron083ec572019-05-16 18:30:40 +0200126 else if (c.hasAttribute("title") &&
Akron916ec252016-11-10 17:06:32 +0100127 _TermRE.exec(c.getAttribute("title"))) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000128
Akron916ec252016-11-10 17:06:32 +0100129 // Fill position with info
130 var foundry, layer, value;
131 if (RegExp.$2) {
132 foundry = RegExp.$1;
133 layer = RegExp.$2;
134 }
135 else {
136 foundry = "base";
137 layer = RegExp.$1
138 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000139
Akron916ec252016-11-10 17:06:32 +0100140 value = RegExp.$3;
141
142 if (found[foundry + "/" + layer] === undefined) {
143 found[foundry + "/" + layer] = [value];
144 }
145 else {
Akron3b253d32018-07-15 10:16:06 +0200146 // if (found[foundry + "/" + layer].indexOf(value) === -1) {
147 if (!found[foundry + "/" + layer].includes(value)) {
Akron916ec252016-11-10 17:06:32 +0100148 // Push value to foundry/layer at correct position
149 found[foundry + "/" + layer].push(value);
150 };
151 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000152
Akron916ec252016-11-10 17:06:32 +0100153 // Set foundry
154 if (this._foundry[foundry] === undefined)
155 this._foundry[foundry] = {};
156 this._foundry[foundry][layer] = 1;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000157
Akron916ec252016-11-10 17:06:32 +0100158 // Set layer
159 if (this._layer[layer] === undefined)
160 this._layer[layer] = {};
161 this._layer[layer][foundry] = 1;
Akron083ec572019-05-16 18:30:40 +0200162 }
163
164 // The current position marks a cut
165 else if (c.hasAttribute("class") && c.getAttribute("class") == "cutted") {
Akron1a780fe2019-05-21 15:59:00 +0200166 this._cutted.push(this._pos);
Akron083ec572019-05-16 18:30:40 +0200167 this._token[this._pos++] = "";
168 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000169
Akron916ec252016-11-10 17:06:32 +0100170 // depth search
171 if (c.hasChildNodes())
Akronad1e46a2018-09-19 15:55:40 +0200172 this._parse(c.childNodes, newMark);
Akron916ec252016-11-10 17:06:32 +0100173 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000174
Akron916ec252016-11-10 17:06:32 +0100175 // Leaf node
176 // store string on position and go to next string
177 else if (c.nodeType === 3) {
Akron158fce12019-12-17 14:43:29 +0100178 if (c.nodeValue.match(/[a-z0-9\u25ae]/iu)) {
Akronad1e46a2018-09-19 15:55:40 +0200179 this._mark[this._pos] = mark ? true : false;
Akron916ec252016-11-10 17:06:32 +0100180 this._token[this._pos++] = c.nodeValue;
Akronad1e46a2018-09-19 15:55:40 +0200181 };
Akron916ec252016-11-10 17:06:32 +0100182 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000183 };
184
185 delete this._info[this._pos];
186 },
187
188
189 /**
190 * Get HTML table view of annotations.
191 */
192 element : function () {
193 if (this._element !== undefined)
Akron916ec252016-11-10 17:06:32 +0100194 return this._element;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000195
196 // First the legend table
Akronaeceda72018-02-02 20:44:06 +0100197 var wrap = d.createElement('div');
198
199 var table = wrap.addE('table');
200
201 this._element = wrap;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000202
203 // Single row in head
Akron0b489ad2018-02-02 16:49:32 +0100204 var tr = table.addE('thead').addE('tr');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000205
Akronf2279c42017-12-21 13:48:46 +0100206 var ah = KorAP.annotationHelper || { "getDesc" : function () {}};
207
Nils Diewald0e6992a2015-04-14 20:13:52 +0000208 // Add cell to row
Akronf2279c42017-12-21 13:48:46 +0100209 var addCell = function (type, key, value) {
Akron0b489ad2018-02-02 16:49:32 +0100210 var c = this.addE(type);
211
Akronf2279c42017-12-21 13:48:46 +0100212 if (value === undefined)
Akron916ec252016-11-10 17:06:32 +0100213 return c;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000214
Akronf2279c42017-12-21 13:48:46 +0100215 if (key && value instanceof Array && value[1] !== undefined) {
Akron4e47d0b2017-07-03 17:58:37 +0200216
217 // There are multiple values to add
Akron856af1e2017-07-03 19:57:46 +0200218 c.classList.add('matchkeyvalues');
Akronf2279c42017-12-21 13:48:46 +0100219 for (var n = 0; n < value.length; n++) {
Akron5b1a6af2018-02-05 15:41:16 +0100220 var e = c.addE('div');
221 e.addT(value[n]);
Akronf2279c42017-12-21 13:48:46 +0100222
223 var anno = ah.getDesc(key, value[n]);
224
225 if (anno)
226 e.setAttribute("title", anno);
Akron856af1e2017-07-03 19:57:46 +0200227 };
Akron916ec252016-11-10 17:06:32 +0100228 }
Akronf2279c42017-12-21 13:48:46 +0100229
Akron916ec252016-11-10 17:06:32 +0100230 else {
Akronf2279c42017-12-21 13:48:46 +0100231
232 if (value instanceof Array)
233 value = value[0];
234
Akron0b489ad2018-02-02 16:49:32 +0100235 c.addT(value);
Akronf2279c42017-12-21 13:48:46 +0100236
237 // Add tooltip
238 var anno = ah.getDesc(key, value);
239 if (anno)
240 c.setAttribute("title", anno);
Akron916ec252016-11-10 17:06:32 +0100241 };
Akron80055992017-12-20 16:30:52 +0100242
243 return c;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000244 };
245
246 tr.addCell = addCell;
247
248 // Add header information
Akronf2279c42017-12-21 13:48:46 +0100249 tr.addCell('th', undefined, 'Foundry');
250 tr.addCell('th', undefined, 'Layer');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000251
252 // Add tokens
253 for (var i in this._token) {
Akron5dc31172019-05-15 18:43:48 +0200254 let surface = this.getToken(i);
255 var c = tr.addCell('th', undefined, surface);
Akronad1e46a2018-09-19 15:55:40 +0200256 if (this._mark[i]) {
257 c.classList.add('mark');
258 if (this._markE === undefined) {
259 this._markE = c;
260 };
Akron083ec572019-05-16 18:30:40 +0200261 }
Akron1a780fe2019-05-21 15:59:00 +0200262 else if (this._cutted[0] == i || this._cutted[1] == i) {
Akron083ec572019-05-16 18:30:40 +0200263 c.classList.add('cutted');
Akronad1e46a2018-09-19 15:55:40 +0200264 };
Akron5dc31172019-05-15 18:43:48 +0200265
266 // In case the title is very long - add a title attribute
267 if (surface.length > 20) {
268 c.setAttribute("title", surface)
269 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000270 };
Akron916ec252016-11-10 17:06:32 +0100271
Akron0b489ad2018-02-02 16:49:32 +0100272 var tbody = table.addE('tbody');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000273
274 var foundryList = Object.keys(this._foundry).sort();
275
276 for (var f = 0; f < foundryList.length; f++) {
Akron99713ef2017-06-28 18:19:28 +0200277 var foundry = foundryList[f];
278 var layerList =
279 Object.keys(this._foundry[foundry]).sort();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000280
Akron99713ef2017-06-28 18:19:28 +0200281 for (var l = 0; l < layerList.length; l++) {
282 var layer = layerList[l];
Akron0b489ad2018-02-02 16:49:32 +0100283 tr = tbody.addE('tr');
Akron99713ef2017-06-28 18:19:28 +0200284 tr.setAttribute('tabindex', 0);
285 tr.addCell = addCell;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000286
Akronf2279c42017-12-21 13:48:46 +0100287 tr.addCell('th', undefined, foundry);
288 tr.addCell('th', undefined, layer);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000289
Akron80055992017-12-20 16:30:52 +0100290 var key = foundry + '/' + layer + '=';
Akron80055992017-12-20 16:30:52 +0100291
Akron99713ef2017-06-28 18:19:28 +0200292 for (var v = 0; v < this.length(); v++) {
Akron80055992017-12-20 16:30:52 +0100293
294 // Get the cell value
295 var value = this.getValue(v, foundry, layer);
296
297 // Add cell to row
298 var cell = tr.addCell(
Akron99713ef2017-06-28 18:19:28 +0200299 'td',
Akronf2279c42017-12-21 13:48:46 +0100300 key,
Akron80055992017-12-20 16:30:52 +0100301 value
Akron99713ef2017-06-28 18:19:28 +0200302 );
Akronad1e46a2018-09-19 15:55:40 +0200303
304 if (this._mark[v]) {
305 cell.classList.add('mark');
306 };
Akron99713ef2017-06-28 18:19:28 +0200307 };
308 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000309 };
Akronb6685bb2018-02-04 00:44:47 +0100310
311 // Add query creator
312 this._matchCreator = matchQueryCreator.create(this._element);
313
Akronaeceda72018-02-02 20:44:06 +0100314 return this._element;
315 },
Nils Diewald0e6992a2015-04-14 20:13:52 +0000316 };
317});