blob: a8ccdc613d63903e8faf6f96a47d9a0e01a04b1c [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 */
Akrone51eaa32020-11-10 09:35:53 +01005"use strict";
6
Akronb6685bb2018-02-04 00:44:47 +01007define([
8 'match/querycreator',
9 "util"
10], function (matchQueryCreator) {
Akronff1b1f32020-10-18 11:41:29 +020011
Akronb6685bb2018-02-04 00:44:47 +010012 /*
13 * TODO:
14 * Create base object for all matchinfo classes!
15 * TODO:
16 * Rename to match/annotationtable
17 */
Akron0b489ad2018-02-02 16:49:32 +010018 const _TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
19 const d = document;
20
Nils Diewald0e6992a2015-04-14 20:13:52 +000021 return {
Nils Diewald7148c6f2015-05-04 15:07:53 +000022
23 /**
24 * Create new table view for a match
25 * based on a snippet string.
26 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000027 create : function (snippet) {
28 return Object.create(this)._init(snippet);
29 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000030
31 // Initialize table based on snippet
Nils Diewald0e6992a2015-04-14 20:13:52 +000032 _init : function (snippet) {
Akronff1b1f32020-10-18 11:41:29 +020033
Nils Diewald0e6992a2015-04-14 20:13:52 +000034 // Create html for traversal
Akronff1b1f32020-10-18 11:41:29 +020035 const html = d.createElement("div");
36 const t = this;
Nils Diewald0e6992a2015-04-14 20:13:52 +000037 html.innerHTML = snippet;
38
Akronff1b1f32020-10-18 11:41:29 +020039 t._pos = 0;
40 t._token = [];
Akronb7749052022-07-22 18:03:26 +020041 t._anno = [];
Akronff1b1f32020-10-18 11:41:29 +020042 t._mark = [];
43 t._markE = undefined;
44 t._cutted = [];
45 t._info = [];
46 t._foundry = {};
47 t._layer = {};
Nils Diewald0e6992a2015-04-14 20:13:52 +000048
49 // Parse the snippet
Akronff1b1f32020-10-18 11:41:29 +020050 t._parse(html.childNodes, false);
Akronb6685bb2018-02-04 00:44:47 +010051
Nils Diewald0e6992a2015-04-14 20:13:52 +000052 html.innerHTML = '';
Akronff1b1f32020-10-18 11:41:29 +020053 return t;
Nils Diewald0e6992a2015-04-14 20:13:52 +000054 },
Akronb6685bb2018-02-04 00:44:47 +010055
Akronff1b1f32020-10-18 11:41:29 +020056
Akronb6685bb2018-02-04 00:44:47 +010057 // TODO: Destroy match!
58 destroy : function () {
59 this._matchCreator = undefined;
60 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000061
Akronff1b1f32020-10-18 11:41:29 +020062
Nils Diewald7148c6f2015-05-04 15:07:53 +000063 /**
64 * Length of the table (columns),
65 * aka the number of tokens
66 * in the snippet.
67 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000068 length : function () {
69 return this._pos;
70 },
71
Akronad1e46a2018-09-19 15:55:40 +020072
73 /**
74 * Move the viewport to the match
75 */
76 toMark : function () {
77 if (this._markE === undefined)
78 return;
79 this._markE.scrollIntoView({
80 inline: "start",
81 block: "nearest"
82 });
83 },
84
Akronff1b1f32020-10-18 11:41:29 +020085
Nils Diewald7148c6f2015-05-04 15:07:53 +000086 /**
87 * Get the token in the snippet
88 * At a given position.
89 *
90 * @param pos
91 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000092 getToken : function (pos) {
93 if (pos === undefined)
Akron916ec252016-11-10 17:06:32 +010094 return this._token;
Nils Diewald0e6992a2015-04-14 20:13:52 +000095 return this._token[pos];
96 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000097
Akronff1b1f32020-10-18 11:41:29 +020098
Nils Diewald7148c6f2015-05-04 15:07:53 +000099 /**
100 * Get the annotation of a token
101 * in the snippet based on the position,
102 * the foundry, and the layer.
103 *
104 * @param pos
105 * @param foundry
106 * @param layer
107 */
Nils Diewald0e6992a2015-04-14 20:13:52 +0000108 getValue : function (pos, foundry, layer) {
109 return this._info[pos][foundry + '/' + layer]
110 },
Nils Diewald0e6992a2015-04-14 20:13:52 +0000111
Akronaeceda72018-02-02 20:44:06 +0100112
Nils Diewald0e6992a2015-04-14 20:13:52 +0000113 // Parse the snippet
Akronad1e46a2018-09-19 15:55:40 +0200114 _parse : function (children, mark) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000115
116 // Get all children
Akron678c26f2020-10-09 08:52:50 +0200117 children.forEach(function(c) {
Akronff1b1f32020-10-18 11:41:29 +0200118 const t = this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000119
Akron916ec252016-11-10 17:06:32 +0100120 // Create object on position unless it exists
Akronff1b1f32020-10-18 11:41:29 +0200121 if (t._info[t._pos] === undefined) {
122 t._info[t._pos] = {};
Akronb7749052022-07-22 18:03:26 +0200123 t._anno[t._pos] = false;
Akron916ec252016-11-10 17:06:32 +0100124 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000125
Akron916ec252016-11-10 17:06:32 +0100126 // Store at position in foundry/layer as array
Akronff1b1f32020-10-18 11:41:29 +0200127 const found = t._info[t._pos];
Nils Diewald0e6992a2015-04-14 20:13:52 +0000128
Akron916ec252016-11-10 17:06:32 +0100129 // Element with title
130 if (c.nodeType === 1) {
Akronff1b1f32020-10-18 11:41:29 +0200131 let newMark = mark;
Akronad1e46a2018-09-19 15:55:40 +0200132
133 if (c.tagName === 'MARK') {
134 newMark = true;
135 }
136
Akron083ec572019-05-16 18:30:40 +0200137 else if (c.hasAttribute("title") &&
Akron916ec252016-11-10 17:06:32 +0100138 _TermRE.exec(c.getAttribute("title"))) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000139
Akron916ec252016-11-10 17:06:32 +0100140 // Fill position with info
Akronff1b1f32020-10-18 11:41:29 +0200141 let foundry, layer, value;
Akron916ec252016-11-10 17:06:32 +0100142 if (RegExp.$2) {
143 foundry = RegExp.$1;
144 layer = RegExp.$2;
145 }
146 else {
147 foundry = "base";
148 layer = RegExp.$1
149 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000150
Akron916ec252016-11-10 17:06:32 +0100151 value = RegExp.$3;
152
153 if (found[foundry + "/" + layer] === undefined) {
154 found[foundry + "/" + layer] = [value];
155 }
156 else {
Akron3b253d32018-07-15 10:16:06 +0200157 // if (found[foundry + "/" + layer].indexOf(value) === -1) {
158 if (!found[foundry + "/" + layer].includes(value)) {
Akron916ec252016-11-10 17:06:32 +0100159 // Push value to foundry/layer at correct position
160 found[foundry + "/" + layer].push(value);
161 };
162 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000163
Akron916ec252016-11-10 17:06:32 +0100164 // Set foundry
Akronff1b1f32020-10-18 11:41:29 +0200165 if (t._foundry[foundry] === undefined)
166 t._foundry[foundry] = {};
167 t._foundry[foundry][layer] = 1;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000168
Akron916ec252016-11-10 17:06:32 +0100169 // Set layer
Akronff1b1f32020-10-18 11:41:29 +0200170 if (t._layer[layer] === undefined)
171 t._layer[layer] = {};
172 t._layer[layer][foundry] = 1;
Akronb7749052022-07-22 18:03:26 +0200173
174 t._anno[t._pos] = true;
Akron083ec572019-05-16 18:30:40 +0200175 }
176
177 // The current position marks a cut
178 else if (c.hasAttribute("class") && c.getAttribute("class") == "cutted") {
Akronff1b1f32020-10-18 11:41:29 +0200179 t._cutted.push(t._pos);
180 t._token[t._pos++] = "";
Akron083ec572019-05-16 18:30:40 +0200181 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000182
Akron916ec252016-11-10 17:06:32 +0100183 // depth search
184 if (c.hasChildNodes())
Akronff1b1f32020-10-18 11:41:29 +0200185 t._parse(c.childNodes, newMark);
Akron916ec252016-11-10 17:06:32 +0100186 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000187
Akron916ec252016-11-10 17:06:32 +0100188 // Leaf node
189 // store string on position and go to next string
190 else if (c.nodeType === 3) {
Akronb7749052022-07-22 18:03:26 +0200191 if (!c.nodeValue.match(/^\s+$/)) {
Akronff1b1f32020-10-18 11:41:29 +0200192 t._mark[t._pos] = mark ? true : false;
193 t._token[t._pos++] = c.nodeValue;
Akronad1e46a2018-09-19 15:55:40 +0200194 };
Akron916ec252016-11-10 17:06:32 +0100195 };
Akron678c26f2020-10-09 08:52:50 +0200196 }, this);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000197
198 delete this._info[this._pos];
199 },
200
201
202 /**
203 * Get HTML table view of annotations.
204 */
205 element : function () {
Akron24aa0052020-11-10 11:00:34 +0100206 if (this._el !== undefined)
207 return this._el;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000208
209 // First the legend table
Akronff1b1f32020-10-18 11:41:29 +0200210 const wrap = d.createElement('div');
Akronaeceda72018-02-02 20:44:06 +0100211
Akronff1b1f32020-10-18 11:41:29 +0200212 const table = wrap.addE('table');
Akronaeceda72018-02-02 20:44:06 +0100213
Akron24aa0052020-11-10 11:00:34 +0100214 this._el = wrap;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000215
216 // Single row in head
Akronff1b1f32020-10-18 11:41:29 +0200217 let tr = table.addE('thead').addE('tr');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000218
Akronff1b1f32020-10-18 11:41:29 +0200219 const ah = KorAP.annotationHelper || { "getDesc" : function () {}};
220
Nils Diewald0e6992a2015-04-14 20:13:52 +0000221 // Add cell to row
Akronff1b1f32020-10-18 11:41:29 +0200222 const addCell = function (type, key, value) {
223 const c = this.addE(type);
Akron0b489ad2018-02-02 16:49:32 +0100224
Akronf2279c42017-12-21 13:48:46 +0100225 if (value === undefined)
Akron916ec252016-11-10 17:06:32 +0100226 return c;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000227
Akronf2279c42017-12-21 13:48:46 +0100228 if (key && value instanceof Array && value[1] !== undefined) {
Akron4e47d0b2017-07-03 17:58:37 +0200229
230 // There are multiple values to add
Akron856af1e2017-07-03 19:57:46 +0200231 c.classList.add('matchkeyvalues');
Akronf2279c42017-12-21 13:48:46 +0100232
Akronb50964a2020-10-12 11:44:37 +0200233 let e, anno;
234 value.forEach(function(v) {
235 e = c.addE('div');
236 e.addT(v);
237
238 anno = ah.getDesc(key, v);
Akronf2279c42017-12-21 13:48:46 +0100239
240 if (anno)
241 e.setAttribute("title", anno);
Akronb50964a2020-10-12 11:44:37 +0200242 });
Akron916ec252016-11-10 17:06:32 +0100243 }
Akronf2279c42017-12-21 13:48:46 +0100244
Akron916ec252016-11-10 17:06:32 +0100245 else {
Akronf2279c42017-12-21 13:48:46 +0100246
247 if (value instanceof Array)
248 value = value[0];
249
Akron0b489ad2018-02-02 16:49:32 +0100250 c.addT(value);
Akronf2279c42017-12-21 13:48:46 +0100251
252 // Add tooltip
Akronff1b1f32020-10-18 11:41:29 +0200253 const anno = ah.getDesc(key, value);
Akronf2279c42017-12-21 13:48:46 +0100254 if (anno)
255 c.setAttribute("title", anno);
Akron916ec252016-11-10 17:06:32 +0100256 };
Akron80055992017-12-20 16:30:52 +0100257
258 return c;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000259 };
260
261 tr.addCell = addCell;
262
263 // Add header information
Akronf2279c42017-12-21 13:48:46 +0100264 tr.addCell('th', undefined, 'Foundry');
265 tr.addCell('th', undefined, 'Layer');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000266
267 // Add tokens
Akron678c26f2020-10-09 08:52:50 +0200268 Object.keys(this._token).forEach(function(i) {
Akronff1b1f32020-10-18 11:41:29 +0200269 const surface = this.getToken(i);
270 const c = tr.addCell('th', undefined, surface);
Akronad1e46a2018-09-19 15:55:40 +0200271 if (this._mark[i]) {
272 c.classList.add('mark');
273 if (this._markE === undefined) {
274 this._markE = c;
275 };
Akron083ec572019-05-16 18:30:40 +0200276 }
Akron1a780fe2019-05-21 15:59:00 +0200277 else if (this._cutted[0] == i || this._cutted[1] == i) {
Akron083ec572019-05-16 18:30:40 +0200278 c.classList.add('cutted');
Akronad1e46a2018-09-19 15:55:40 +0200279 };
Akron5dc31172019-05-15 18:43:48 +0200280
Akronb7749052022-07-22 18:03:26 +0200281 if (!this._anno[i]) {
282 c.classList.add('no-anno');
283 };
284
Akron5dc31172019-05-15 18:43:48 +0200285 // In case the title is very long - add a title attribute
286 if (surface.length > 20) {
287 c.setAttribute("title", surface)
288 }
Akron678c26f2020-10-09 08:52:50 +0200289 }, this);
Akron916ec252016-11-10 17:06:32 +0100290
Akronff1b1f32020-10-18 11:41:29 +0200291 const tbody = table.addE('tbody');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000292
Akronb50964a2020-10-12 11:44:37 +0200293 let layerList, key, v, value, cell;
294
Akronff1b1f32020-10-18 11:41:29 +0200295 Object.keys(this._foundry).sort().forEach(function(foundry) {
Akronb50964a2020-10-12 11:44:37 +0200296 let layerList =
Akron99713ef2017-06-28 18:19:28 +0200297 Object.keys(this._foundry[foundry]).sort();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000298
Akronb50964a2020-10-12 11:44:37 +0200299 layerList.forEach(function(layer) {
300
Akron0b489ad2018-02-02 16:49:32 +0100301 tr = tbody.addE('tr');
Akron99713ef2017-06-28 18:19:28 +0200302 tr.setAttribute('tabindex', 0);
303 tr.addCell = addCell;
Akronf2279c42017-12-21 13:48:46 +0100304 tr.addCell('th', undefined, foundry);
305 tr.addCell('th', undefined, layer);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000306
Akronb50964a2020-10-12 11:44:37 +0200307 key = foundry + '/' + layer + '=';
Akron80055992017-12-20 16:30:52 +0100308
Akronb50964a2020-10-12 11:44:37 +0200309 for (v = 0; v < this.length(); v++) {
Akron80055992017-12-20 16:30:52 +0100310
311 // Get the cell value
Akronb50964a2020-10-12 11:44:37 +0200312 value = this.getValue(v, foundry, layer);
Akron80055992017-12-20 16:30:52 +0100313
314 // Add cell to row
Akronb50964a2020-10-12 11:44:37 +0200315 cell = tr.addCell(
Akron99713ef2017-06-28 18:19:28 +0200316 'td',
Akronf2279c42017-12-21 13:48:46 +0100317 key,
Akron80055992017-12-20 16:30:52 +0100318 value
Akron99713ef2017-06-28 18:19:28 +0200319 );
Akronad1e46a2018-09-19 15:55:40 +0200320
321 if (this._mark[v]) {
322 cell.classList.add('mark');
323 };
Akronb7749052022-07-22 18:03:26 +0200324
325 if (value === undefined && this._anno[v]) {
326 cell.classList.add("not-empty");
327 };
Akron99713ef2017-06-28 18:19:28 +0200328 };
Akronb50964a2020-10-12 11:44:37 +0200329 }, this);
330 }, this);
Akronb6685bb2018-02-04 00:44:47 +0100331
332 // Add query creator
Akron24aa0052020-11-10 11:00:34 +0100333 this._matchCreator = matchQueryCreator.create(this._el);
Akronb6685bb2018-02-04 00:44:47 +0100334
Akron24aa0052020-11-10 11:00:34 +0100335 return this._el;
Akronaeceda72018-02-02 20:44:06 +0100336 },
Nils Diewald0e6992a2015-04-14 20:13:52 +0000337 };
338});