blob: 59ebf750b7dfb7b6bc4c2c23cbe29a59c378a5ae [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;
Akron9fe23dc2025-06-11 11:43:50 +020020 const notinindexSuffix = "_NOTININDEX";
21
Nils Diewald0e6992a2015-04-14 20:13:52 +000022 return {
Nils Diewald7148c6f2015-05-04 15:07:53 +000023
24 /**
25 * Create new table view for a match
26 * based on a snippet string.
27 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000028 create : function (snippet) {
29 return Object.create(this)._init(snippet);
30 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000031
32 // Initialize table based on snippet
Nils Diewald0e6992a2015-04-14 20:13:52 +000033 _init : function (snippet) {
Akronff1b1f32020-10-18 11:41:29 +020034
Nils Diewald0e6992a2015-04-14 20:13:52 +000035 // Create html for traversal
Akronff1b1f32020-10-18 11:41:29 +020036 const html = d.createElement("div");
37 const t = this;
Nils Diewald0e6992a2015-04-14 20:13:52 +000038 html.innerHTML = snippet;
39
Akronff1b1f32020-10-18 11:41:29 +020040 t._pos = 0;
41 t._token = [];
Akronb7749052022-07-22 18:03:26 +020042 t._anno = [];
Akronff1b1f32020-10-18 11:41:29 +020043 t._mark = [];
44 t._markE = undefined;
45 t._cutted = [];
46 t._info = [];
47 t._foundry = {};
48 t._layer = {};
Nils Diewald0e6992a2015-04-14 20:13:52 +000049
50 // Parse the snippet
Akronff1b1f32020-10-18 11:41:29 +020051 t._parse(html.childNodes, false);
Akronb6685bb2018-02-04 00:44:47 +010052
Nils Diewald0e6992a2015-04-14 20:13:52 +000053 html.innerHTML = '';
Akronff1b1f32020-10-18 11:41:29 +020054 return t;
Nils Diewald0e6992a2015-04-14 20:13:52 +000055 },
Akronb6685bb2018-02-04 00:44:47 +010056
Akronff1b1f32020-10-18 11:41:29 +020057
Akronb6685bb2018-02-04 00:44:47 +010058 // TODO: Destroy match!
59 destroy : function () {
60 this._matchCreator = undefined;
61 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000062
Akronff1b1f32020-10-18 11:41:29 +020063
Nils Diewald7148c6f2015-05-04 15:07:53 +000064 /**
65 * Length of the table (columns),
66 * aka the number of tokens
67 * in the snippet.
68 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000069 length : function () {
70 return this._pos;
71 },
72
Akronad1e46a2018-09-19 15:55:40 +020073
74 /**
75 * Move the viewport to the match
76 */
77 toMark : function () {
78 if (this._markE === undefined)
79 return;
80 this._markE.scrollIntoView({
81 inline: "start",
82 block: "nearest"
83 });
84 },
85
Akronff1b1f32020-10-18 11:41:29 +020086
Nils Diewald7148c6f2015-05-04 15:07:53 +000087 /**
88 * Get the token in the snippet
89 * At a given position.
90 *
91 * @param pos
92 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000093 getToken : function (pos) {
94 if (pos === undefined)
Akron916ec252016-11-10 17:06:32 +010095 return this._token;
Nils Diewald0e6992a2015-04-14 20:13:52 +000096 return this._token[pos];
97 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000098
Akronff1b1f32020-10-18 11:41:29 +020099
Nils Diewald7148c6f2015-05-04 15:07:53 +0000100 /**
101 * Get the annotation of a token
102 * in the snippet based on the position,
103 * the foundry, and the layer.
104 *
105 * @param pos
106 * @param foundry
107 * @param layer
108 */
Nils Diewald0e6992a2015-04-14 20:13:52 +0000109 getValue : function (pos, foundry, layer) {
110 return this._info[pos][foundry + '/' + layer]
111 },
Nils Diewald0e6992a2015-04-14 20:13:52 +0000112
Akronaeceda72018-02-02 20:44:06 +0100113
Nils Diewald0e6992a2015-04-14 20:13:52 +0000114 // Parse the snippet
Akronad1e46a2018-09-19 15:55:40 +0200115 _parse : function (children, mark) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000116
117 // Get all children
Akron678c26f2020-10-09 08:52:50 +0200118 children.forEach(function(c) {
Akronff1b1f32020-10-18 11:41:29 +0200119 const t = this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000120
Akron916ec252016-11-10 17:06:32 +0100121 // Create object on position unless it exists
Akronff1b1f32020-10-18 11:41:29 +0200122 if (t._info[t._pos] === undefined) {
123 t._info[t._pos] = {};
Akronb7749052022-07-22 18:03:26 +0200124 t._anno[t._pos] = false;
Akron916ec252016-11-10 17:06:32 +0100125 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000126
Akron916ec252016-11-10 17:06:32 +0100127 // Store at position in foundry/layer as array
Akronff1b1f32020-10-18 11:41:29 +0200128 const found = t._info[t._pos];
Nils Diewald0e6992a2015-04-14 20:13:52 +0000129
Akron916ec252016-11-10 17:06:32 +0100130 // Element with title
131 if (c.nodeType === 1) {
Akronff1b1f32020-10-18 11:41:29 +0200132 let newMark = mark;
Akronad1e46a2018-09-19 15:55:40 +0200133
134 if (c.tagName === 'MARK') {
135 newMark = true;
136 }
137
Akron083ec572019-05-16 18:30:40 +0200138 else if (c.hasAttribute("title") &&
Akron916ec252016-11-10 17:06:32 +0100139 _TermRE.exec(c.getAttribute("title"))) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000140
Akron916ec252016-11-10 17:06:32 +0100141 // Fill position with info
Akronff1b1f32020-10-18 11:41:29 +0200142 let foundry, layer, value;
Akron916ec252016-11-10 17:06:32 +0100143 if (RegExp.$2) {
144 foundry = RegExp.$1;
145 layer = RegExp.$2;
146 }
147 else {
148 foundry = "base";
149 layer = RegExp.$1
150 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000151
Akron916ec252016-11-10 17:06:32 +0100152 value = RegExp.$3;
Akron9fe23dc2025-06-11 11:43:50 +0200153
154 if (c.classList.contains("notinindex")) {
155 value += notinindexSuffix;
156 };
157
Akron916ec252016-11-10 17:06:32 +0100158 if (found[foundry + "/" + layer] === undefined) {
159 found[foundry + "/" + layer] = [value];
160 }
161 else {
Akron3b253d32018-07-15 10:16:06 +0200162 // if (found[foundry + "/" + layer].indexOf(value) === -1) {
163 if (!found[foundry + "/" + layer].includes(value)) {
Akron916ec252016-11-10 17:06:32 +0100164 // Push value to foundry/layer at correct position
165 found[foundry + "/" + layer].push(value);
166 };
167 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000168
Akron916ec252016-11-10 17:06:32 +0100169 // Set foundry
Akronff1b1f32020-10-18 11:41:29 +0200170 if (t._foundry[foundry] === undefined)
171 t._foundry[foundry] = {};
172 t._foundry[foundry][layer] = 1;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000173
Akron916ec252016-11-10 17:06:32 +0100174 // Set layer
Akronff1b1f32020-10-18 11:41:29 +0200175 if (t._layer[layer] === undefined)
176 t._layer[layer] = {};
177 t._layer[layer][foundry] = 1;
Akronb7749052022-07-22 18:03:26 +0200178
179 t._anno[t._pos] = true;
Akron083ec572019-05-16 18:30:40 +0200180 }
181
182 // The current position marks a cut
183 else if (c.hasAttribute("class") && c.getAttribute("class") == "cutted") {
Akronff1b1f32020-10-18 11:41:29 +0200184 t._cutted.push(t._pos);
185 t._token[t._pos++] = "";
Akron083ec572019-05-16 18:30:40 +0200186 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000187
Akron916ec252016-11-10 17:06:32 +0100188 // depth search
189 if (c.hasChildNodes())
Akronff1b1f32020-10-18 11:41:29 +0200190 t._parse(c.childNodes, newMark);
Akron916ec252016-11-10 17:06:32 +0100191 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000192
Akron916ec252016-11-10 17:06:32 +0100193 // Leaf node
194 // store string on position and go to next string
195 else if (c.nodeType === 3) {
Akronb7749052022-07-22 18:03:26 +0200196 if (!c.nodeValue.match(/^\s+$/)) {
Akronff1b1f32020-10-18 11:41:29 +0200197 t._mark[t._pos] = mark ? true : false;
198 t._token[t._pos++] = c.nodeValue;
Akronad1e46a2018-09-19 15:55:40 +0200199 };
Akron916ec252016-11-10 17:06:32 +0100200 };
Akron678c26f2020-10-09 08:52:50 +0200201 }, this);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000202
203 delete this._info[this._pos];
204 },
205
206
207 /**
208 * Get HTML table view of annotations.
209 */
210 element : function () {
Akron24aa0052020-11-10 11:00:34 +0100211 if (this._el !== undefined)
212 return this._el;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000213
214 // First the legend table
Akronff1b1f32020-10-18 11:41:29 +0200215 const wrap = d.createElement('div');
Akronaeceda72018-02-02 20:44:06 +0100216
Akronff1b1f32020-10-18 11:41:29 +0200217 const table = wrap.addE('table');
Akronaeceda72018-02-02 20:44:06 +0100218
Akron24aa0052020-11-10 11:00:34 +0100219 this._el = wrap;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000220
221 // Single row in head
Akronff1b1f32020-10-18 11:41:29 +0200222 let tr = table.addE('thead').addE('tr');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000223
Akronff1b1f32020-10-18 11:41:29 +0200224 const ah = KorAP.annotationHelper || { "getDesc" : function () {}};
225
Nils Diewald0e6992a2015-04-14 20:13:52 +0000226 // Add cell to row
Akronff1b1f32020-10-18 11:41:29 +0200227 const addCell = function (type, key, value) {
228 const c = this.addE(type);
Akron0b489ad2018-02-02 16:49:32 +0100229
Akronf2279c42017-12-21 13:48:46 +0100230 if (value === undefined)
Akron916ec252016-11-10 17:06:32 +0100231 return c;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000232
Akronf2279c42017-12-21 13:48:46 +0100233 if (key && value instanceof Array && value[1] !== undefined) {
Akron4e47d0b2017-07-03 17:58:37 +0200234
235 // There are multiple values to add
Akron856af1e2017-07-03 19:57:46 +0200236 c.classList.add('matchkeyvalues');
Akronf2279c42017-12-21 13:48:46 +0100237
Akronb50964a2020-10-12 11:44:37 +0200238 let e, anno;
239 value.forEach(function(v) {
Akron9fe23dc2025-06-11 11:43:50 +0200240
Akronb50964a2020-10-12 11:44:37 +0200241 e = c.addE('div');
Akron9fe23dc2025-06-11 11:43:50 +0200242
243 if (v.endsWith(notinindexSuffix)) {
244 v = v.slice(0, -11);
245 e.classList.add("notinindex");
246 };
247
Akronb50964a2020-10-12 11:44:37 +0200248 e.addT(v);
249
250 anno = ah.getDesc(key, v);
Akronf2279c42017-12-21 13:48:46 +0100251
252 if (anno)
253 e.setAttribute("title", anno);
Akronb50964a2020-10-12 11:44:37 +0200254 });
Akron916ec252016-11-10 17:06:32 +0100255 }
Akronf2279c42017-12-21 13:48:46 +0100256
Akron916ec252016-11-10 17:06:32 +0100257 else {
Akronf2279c42017-12-21 13:48:46 +0100258
259 if (value instanceof Array)
260 value = value[0];
261
Akron9fe23dc2025-06-11 11:43:50 +0200262 if (value.endsWith(notinindexSuffix)) {
263 value = value.slice(0, -11);
264 c.classList.add("notinindex");
265 };
266
Akron0b489ad2018-02-02 16:49:32 +0100267 c.addT(value);
Akronf2279c42017-12-21 13:48:46 +0100268
269 // Add tooltip
Akronff1b1f32020-10-18 11:41:29 +0200270 const anno = ah.getDesc(key, value);
Akronf2279c42017-12-21 13:48:46 +0100271 if (anno)
272 c.setAttribute("title", anno);
Akron916ec252016-11-10 17:06:32 +0100273 };
Akron80055992017-12-20 16:30:52 +0100274
275 return c;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000276 };
277
278 tr.addCell = addCell;
279
280 // Add header information
Akronf2279c42017-12-21 13:48:46 +0100281 tr.addCell('th', undefined, 'Foundry');
282 tr.addCell('th', undefined, 'Layer');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000283
284 // Add tokens
Akron678c26f2020-10-09 08:52:50 +0200285 Object.keys(this._token).forEach(function(i) {
Akronff1b1f32020-10-18 11:41:29 +0200286 const surface = this.getToken(i);
287 const c = tr.addCell('th', undefined, surface);
Akronad1e46a2018-09-19 15:55:40 +0200288 if (this._mark[i]) {
289 c.classList.add('mark');
290 if (this._markE === undefined) {
291 this._markE = c;
292 };
Akron083ec572019-05-16 18:30:40 +0200293 }
Akron1a780fe2019-05-21 15:59:00 +0200294 else if (this._cutted[0] == i || this._cutted[1] == i) {
Akron083ec572019-05-16 18:30:40 +0200295 c.classList.add('cutted');
Akronad1e46a2018-09-19 15:55:40 +0200296 };
Akron5dc31172019-05-15 18:43:48 +0200297
Akronb7749052022-07-22 18:03:26 +0200298 if (!this._anno[i]) {
299 c.classList.add('no-anno');
300 };
301
Akron5dc31172019-05-15 18:43:48 +0200302 // In case the title is very long - add a title attribute
303 if (surface.length > 20) {
304 c.setAttribute("title", surface)
305 }
Akron678c26f2020-10-09 08:52:50 +0200306 }, this);
Akron916ec252016-11-10 17:06:32 +0100307
Akronff1b1f32020-10-18 11:41:29 +0200308 const tbody = table.addE('tbody');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000309
Akronb50964a2020-10-12 11:44:37 +0200310 let layerList, key, v, value, cell;
311
Akronff1b1f32020-10-18 11:41:29 +0200312 Object.keys(this._foundry).sort().forEach(function(foundry) {
Akronb50964a2020-10-12 11:44:37 +0200313 let layerList =
Akron99713ef2017-06-28 18:19:28 +0200314 Object.keys(this._foundry[foundry]).sort();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000315
Akronb50964a2020-10-12 11:44:37 +0200316 layerList.forEach(function(layer) {
317
Akron0b489ad2018-02-02 16:49:32 +0100318 tr = tbody.addE('tr');
Akron99713ef2017-06-28 18:19:28 +0200319 tr.setAttribute('tabindex', 0);
320 tr.addCell = addCell;
Akronf2279c42017-12-21 13:48:46 +0100321 tr.addCell('th', undefined, foundry);
322 tr.addCell('th', undefined, layer);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000323
Akronb50964a2020-10-12 11:44:37 +0200324 key = foundry + '/' + layer + '=';
Akron80055992017-12-20 16:30:52 +0100325
Akronb50964a2020-10-12 11:44:37 +0200326 for (v = 0; v < this.length(); v++) {
Akron80055992017-12-20 16:30:52 +0100327
328 // Get the cell value
Akronb50964a2020-10-12 11:44:37 +0200329 value = this.getValue(v, foundry, layer);
Akron80055992017-12-20 16:30:52 +0100330
331 // Add cell to row
Akronb50964a2020-10-12 11:44:37 +0200332 cell = tr.addCell(
Akron99713ef2017-06-28 18:19:28 +0200333 'td',
Akronf2279c42017-12-21 13:48:46 +0100334 key,
Akron80055992017-12-20 16:30:52 +0100335 value
Akron99713ef2017-06-28 18:19:28 +0200336 );
Akronad1e46a2018-09-19 15:55:40 +0200337
338 if (this._mark[v]) {
339 cell.classList.add('mark');
340 };
Akronb7749052022-07-22 18:03:26 +0200341
342 if (value === undefined && this._anno[v]) {
343 cell.classList.add("not-empty");
344 };
Akron99713ef2017-06-28 18:19:28 +0200345 };
Akronb50964a2020-10-12 11:44:37 +0200346 }, this);
347 }, this);
Akronb6685bb2018-02-04 00:44:47 +0100348
349 // Add query creator
Akron24aa0052020-11-10 11:00:34 +0100350 this._matchCreator = matchQueryCreator.create(this._el);
Akronb6685bb2018-02-04 00:44:47 +0100351
Akron24aa0052020-11-10 11:00:34 +0100352 return this._el;
Akronaeceda72018-02-02 20:44:06 +0100353 },
Nils Diewald0e6992a2015-04-14 20:13:52 +0000354 };
355});