blob: 9481728267e58115173c24ba6f496bbea6f5b850 [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 = [];
41 t._mark = [];
42 t._markE = undefined;
43 t._cutted = [];
44 t._info = [];
45 t._foundry = {};
46 t._layer = {};
Nils Diewald0e6992a2015-04-14 20:13:52 +000047
48 // Parse the snippet
Akronff1b1f32020-10-18 11:41:29 +020049 t._parse(html.childNodes, false);
Akronb6685bb2018-02-04 00:44:47 +010050
Nils Diewald0e6992a2015-04-14 20:13:52 +000051 html.innerHTML = '';
Akronff1b1f32020-10-18 11:41:29 +020052 return t;
Nils Diewald0e6992a2015-04-14 20:13:52 +000053 },
Akronb6685bb2018-02-04 00:44:47 +010054
Akronff1b1f32020-10-18 11:41:29 +020055
Akronb6685bb2018-02-04 00:44:47 +010056 // TODO: Destroy match!
57 destroy : function () {
58 this._matchCreator = undefined;
59 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000060
Akronff1b1f32020-10-18 11:41:29 +020061
Nils Diewald7148c6f2015-05-04 15:07:53 +000062 /**
63 * Length of the table (columns),
64 * aka the number of tokens
65 * in the snippet.
66 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000067 length : function () {
68 return this._pos;
69 },
70
Akronad1e46a2018-09-19 15:55:40 +020071
72 /**
73 * Move the viewport to the match
74 */
75 toMark : function () {
76 if (this._markE === undefined)
77 return;
78 this._markE.scrollIntoView({
79 inline: "start",
80 block: "nearest"
81 });
82 },
83
Akronff1b1f32020-10-18 11:41:29 +020084
Nils Diewald7148c6f2015-05-04 15:07:53 +000085 /**
86 * Get the token in the snippet
87 * At a given position.
88 *
89 * @param pos
90 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000091 getToken : function (pos) {
92 if (pos === undefined)
Akron916ec252016-11-10 17:06:32 +010093 return this._token;
Nils Diewald0e6992a2015-04-14 20:13:52 +000094 return this._token[pos];
95 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000096
Akronff1b1f32020-10-18 11:41:29 +020097
Nils Diewald7148c6f2015-05-04 15:07:53 +000098 /**
99 * Get the annotation of a token
100 * in the snippet based on the position,
101 * the foundry, and the layer.
102 *
103 * @param pos
104 * @param foundry
105 * @param layer
106 */
Nils Diewald0e6992a2015-04-14 20:13:52 +0000107 getValue : function (pos, foundry, layer) {
108 return this._info[pos][foundry + '/' + layer]
109 },
Nils Diewald0e6992a2015-04-14 20:13:52 +0000110
Akronaeceda72018-02-02 20:44:06 +0100111
Nils Diewald0e6992a2015-04-14 20:13:52 +0000112 // Parse the snippet
Akronad1e46a2018-09-19 15:55:40 +0200113 _parse : function (children, mark) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000114
115 // Get all children
Akron678c26f2020-10-09 08:52:50 +0200116 children.forEach(function(c) {
Akronff1b1f32020-10-18 11:41:29 +0200117 const t = this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000118
Akron916ec252016-11-10 17:06:32 +0100119 // Create object on position unless it exists
Akronff1b1f32020-10-18 11:41:29 +0200120 if (t._info[t._pos] === undefined) {
121 t._info[t._pos] = {};
Akron916ec252016-11-10 17:06:32 +0100122 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000123
Akron916ec252016-11-10 17:06:32 +0100124 // Store at position in foundry/layer as array
Akronff1b1f32020-10-18 11:41:29 +0200125 const found = t._info[t._pos];
Nils Diewald0e6992a2015-04-14 20:13:52 +0000126
Akron916ec252016-11-10 17:06:32 +0100127 // Element with title
128 if (c.nodeType === 1) {
Akronff1b1f32020-10-18 11:41:29 +0200129 let newMark = mark;
Akronad1e46a2018-09-19 15:55:40 +0200130
131 if (c.tagName === 'MARK') {
132 newMark = true;
133 }
134
Akron083ec572019-05-16 18:30:40 +0200135 else if (c.hasAttribute("title") &&
Akron916ec252016-11-10 17:06:32 +0100136 _TermRE.exec(c.getAttribute("title"))) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000137
Akron916ec252016-11-10 17:06:32 +0100138 // Fill position with info
Akronff1b1f32020-10-18 11:41:29 +0200139 let foundry, layer, value;
Akron916ec252016-11-10 17:06:32 +0100140 if (RegExp.$2) {
141 foundry = RegExp.$1;
142 layer = RegExp.$2;
143 }
144 else {
145 foundry = "base";
146 layer = RegExp.$1
147 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000148
Akron916ec252016-11-10 17:06:32 +0100149 value = RegExp.$3;
150
151 if (found[foundry + "/" + layer] === undefined) {
152 found[foundry + "/" + layer] = [value];
153 }
154 else {
Akron3b253d32018-07-15 10:16:06 +0200155 // if (found[foundry + "/" + layer].indexOf(value) === -1) {
156 if (!found[foundry + "/" + layer].includes(value)) {
Akron916ec252016-11-10 17:06:32 +0100157 // Push value to foundry/layer at correct position
158 found[foundry + "/" + layer].push(value);
159 };
160 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000161
Akron916ec252016-11-10 17:06:32 +0100162 // Set foundry
Akronff1b1f32020-10-18 11:41:29 +0200163 if (t._foundry[foundry] === undefined)
164 t._foundry[foundry] = {};
165 t._foundry[foundry][layer] = 1;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000166
Akron916ec252016-11-10 17:06:32 +0100167 // Set layer
Akronff1b1f32020-10-18 11:41:29 +0200168 if (t._layer[layer] === undefined)
169 t._layer[layer] = {};
170 t._layer[layer][foundry] = 1;
Akron083ec572019-05-16 18:30:40 +0200171 }
172
173 // The current position marks a cut
174 else if (c.hasAttribute("class") && c.getAttribute("class") == "cutted") {
Akronff1b1f32020-10-18 11:41:29 +0200175 t._cutted.push(t._pos);
176 t._token[t._pos++] = "";
Akron083ec572019-05-16 18:30:40 +0200177 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000178
Akron916ec252016-11-10 17:06:32 +0100179 // depth search
180 if (c.hasChildNodes())
Akronff1b1f32020-10-18 11:41:29 +0200181 t._parse(c.childNodes, newMark);
Akron916ec252016-11-10 17:06:32 +0100182 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000183
Akron916ec252016-11-10 17:06:32 +0100184 // Leaf node
185 // store string on position and go to next string
186 else if (c.nodeType === 3) {
Akron158fce12019-12-17 14:43:29 +0100187 if (c.nodeValue.match(/[a-z0-9\u25ae]/iu)) {
Akronff1b1f32020-10-18 11:41:29 +0200188 t._mark[t._pos] = mark ? true : false;
189 t._token[t._pos++] = c.nodeValue;
Akronad1e46a2018-09-19 15:55:40 +0200190 };
Akron916ec252016-11-10 17:06:32 +0100191 };
Akron678c26f2020-10-09 08:52:50 +0200192 }, this);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000193
194 delete this._info[this._pos];
195 },
196
197
198 /**
199 * Get HTML table view of annotations.
200 */
201 element : function () {
202 if (this._element !== undefined)
Akron916ec252016-11-10 17:06:32 +0100203 return this._element;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000204
205 // First the legend table
Akronff1b1f32020-10-18 11:41:29 +0200206 const wrap = d.createElement('div');
Akronaeceda72018-02-02 20:44:06 +0100207
Akronff1b1f32020-10-18 11:41:29 +0200208 const table = wrap.addE('table');
Akronaeceda72018-02-02 20:44:06 +0100209
210 this._element = wrap;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000211
212 // Single row in head
Akronff1b1f32020-10-18 11:41:29 +0200213 let tr = table.addE('thead').addE('tr');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000214
Akronff1b1f32020-10-18 11:41:29 +0200215 const ah = KorAP.annotationHelper || { "getDesc" : function () {}};
216
Nils Diewald0e6992a2015-04-14 20:13:52 +0000217 // Add cell to row
Akronff1b1f32020-10-18 11:41:29 +0200218 const addCell = function (type, key, value) {
219 const c = this.addE(type);
Akron0b489ad2018-02-02 16:49:32 +0100220
Akronf2279c42017-12-21 13:48:46 +0100221 if (value === undefined)
Akron916ec252016-11-10 17:06:32 +0100222 return c;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000223
Akronf2279c42017-12-21 13:48:46 +0100224 if (key && value instanceof Array && value[1] !== undefined) {
Akron4e47d0b2017-07-03 17:58:37 +0200225
226 // There are multiple values to add
Akron856af1e2017-07-03 19:57:46 +0200227 c.classList.add('matchkeyvalues');
Akronf2279c42017-12-21 13:48:46 +0100228
Akronb50964a2020-10-12 11:44:37 +0200229 let e, anno;
230 value.forEach(function(v) {
231 e = c.addE('div');
232 e.addT(v);
233
234 anno = ah.getDesc(key, v);
Akronf2279c42017-12-21 13:48:46 +0100235
236 if (anno)
237 e.setAttribute("title", anno);
Akronb50964a2020-10-12 11:44:37 +0200238 });
Akron916ec252016-11-10 17:06:32 +0100239 }
Akronf2279c42017-12-21 13:48:46 +0100240
Akron916ec252016-11-10 17:06:32 +0100241 else {
Akronf2279c42017-12-21 13:48:46 +0100242
243 if (value instanceof Array)
244 value = value[0];
245
Akron0b489ad2018-02-02 16:49:32 +0100246 c.addT(value);
Akronf2279c42017-12-21 13:48:46 +0100247
248 // Add tooltip
Akronff1b1f32020-10-18 11:41:29 +0200249 const anno = ah.getDesc(key, value);
Akronf2279c42017-12-21 13:48:46 +0100250 if (anno)
251 c.setAttribute("title", anno);
Akron916ec252016-11-10 17:06:32 +0100252 };
Akron80055992017-12-20 16:30:52 +0100253
254 return c;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000255 };
256
257 tr.addCell = addCell;
258
259 // Add header information
Akronf2279c42017-12-21 13:48:46 +0100260 tr.addCell('th', undefined, 'Foundry');
261 tr.addCell('th', undefined, 'Layer');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000262
263 // Add tokens
Akron678c26f2020-10-09 08:52:50 +0200264 Object.keys(this._token).forEach(function(i) {
Akronff1b1f32020-10-18 11:41:29 +0200265 const surface = this.getToken(i);
266 const c = tr.addCell('th', undefined, surface);
Akronad1e46a2018-09-19 15:55:40 +0200267 if (this._mark[i]) {
268 c.classList.add('mark');
269 if (this._markE === undefined) {
270 this._markE = c;
271 };
Akron083ec572019-05-16 18:30:40 +0200272 }
Akron1a780fe2019-05-21 15:59:00 +0200273 else if (this._cutted[0] == i || this._cutted[1] == i) {
Akron083ec572019-05-16 18:30:40 +0200274 c.classList.add('cutted');
Akronad1e46a2018-09-19 15:55:40 +0200275 };
Akron5dc31172019-05-15 18:43:48 +0200276
277 // In case the title is very long - add a title attribute
278 if (surface.length > 20) {
279 c.setAttribute("title", surface)
280 }
Akron678c26f2020-10-09 08:52:50 +0200281 }, this);
Akron916ec252016-11-10 17:06:32 +0100282
Akronff1b1f32020-10-18 11:41:29 +0200283 const tbody = table.addE('tbody');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000284
Akronb50964a2020-10-12 11:44:37 +0200285 let layerList, key, v, value, cell;
286
Akronff1b1f32020-10-18 11:41:29 +0200287 Object.keys(this._foundry).sort().forEach(function(foundry) {
Akronb50964a2020-10-12 11:44:37 +0200288 let layerList =
Akron99713ef2017-06-28 18:19:28 +0200289 Object.keys(this._foundry[foundry]).sort();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000290
Akronb50964a2020-10-12 11:44:37 +0200291 layerList.forEach(function(layer) {
292
Akron0b489ad2018-02-02 16:49:32 +0100293 tr = tbody.addE('tr');
Akron99713ef2017-06-28 18:19:28 +0200294 tr.setAttribute('tabindex', 0);
295 tr.addCell = addCell;
Akronf2279c42017-12-21 13:48:46 +0100296 tr.addCell('th', undefined, foundry);
297 tr.addCell('th', undefined, layer);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000298
Akronb50964a2020-10-12 11:44:37 +0200299 key = foundry + '/' + layer + '=';
Akron80055992017-12-20 16:30:52 +0100300
Akronb50964a2020-10-12 11:44:37 +0200301 for (v = 0; v < this.length(); v++) {
Akron80055992017-12-20 16:30:52 +0100302
303 // Get the cell value
Akronb50964a2020-10-12 11:44:37 +0200304 value = this.getValue(v, foundry, layer);
Akron80055992017-12-20 16:30:52 +0100305
306 // Add cell to row
Akronb50964a2020-10-12 11:44:37 +0200307 cell = tr.addCell(
Akron99713ef2017-06-28 18:19:28 +0200308 'td',
Akronf2279c42017-12-21 13:48:46 +0100309 key,
Akron80055992017-12-20 16:30:52 +0100310 value
Akron99713ef2017-06-28 18:19:28 +0200311 );
Akronad1e46a2018-09-19 15:55:40 +0200312
313 if (this._mark[v]) {
314 cell.classList.add('mark');
315 };
Akron99713ef2017-06-28 18:19:28 +0200316 };
Akronb50964a2020-10-12 11:44:37 +0200317 }, this);
318 }, this);
Akronb6685bb2018-02-04 00:44:47 +0100319
320 // Add query creator
321 this._matchCreator = matchQueryCreator.create(this._element);
322
Akronaeceda72018-02-02 20:44:06 +0100323 return this._element;
324 },
Nils Diewald0e6992a2015-04-14 20:13:52 +0000325 };
326});