blob: 285e615032ee9194b93d889a55c920e7cb7a64e7 [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) {
Akronff1b1f32020-10-18 11:41:29 +02009 "use strict";
10
Akronb6685bb2018-02-04 00:44:47 +010011 /*
12 * TODO:
13 * Create base object for all matchinfo classes!
14 * TODO:
15 * Rename to match/annotationtable
16 */
Akron0b489ad2018-02-02 16:49:32 +010017 const _TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
18 const d = document;
19
Nils Diewald0e6992a2015-04-14 20:13:52 +000020 return {
Nils Diewald7148c6f2015-05-04 15:07:53 +000021
22 /**
23 * Create new table view for a match
24 * based on a snippet string.
25 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000026 create : function (snippet) {
27 return Object.create(this)._init(snippet);
28 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000029
30 // Initialize table based on snippet
Nils Diewald0e6992a2015-04-14 20:13:52 +000031 _init : function (snippet) {
Akronff1b1f32020-10-18 11:41:29 +020032
Nils Diewald0e6992a2015-04-14 20:13:52 +000033 // Create html for traversal
Akronff1b1f32020-10-18 11:41:29 +020034 const html = d.createElement("div");
35 const t = this;
Nils Diewald0e6992a2015-04-14 20:13:52 +000036 html.innerHTML = snippet;
37
Akronff1b1f32020-10-18 11:41:29 +020038 t._pos = 0;
39 t._token = [];
40 t._mark = [];
41 t._markE = undefined;
42 t._cutted = [];
43 t._info = [];
44 t._foundry = {};
45 t._layer = {};
Nils Diewald0e6992a2015-04-14 20:13:52 +000046
47 // Parse the snippet
Akronff1b1f32020-10-18 11:41:29 +020048 t._parse(html.childNodes, false);
Akronb6685bb2018-02-04 00:44:47 +010049
Nils Diewald0e6992a2015-04-14 20:13:52 +000050 html.innerHTML = '';
Akronff1b1f32020-10-18 11:41:29 +020051 return t;
Nils Diewald0e6992a2015-04-14 20:13:52 +000052 },
Akronb6685bb2018-02-04 00:44:47 +010053
Akronff1b1f32020-10-18 11:41:29 +020054
Akronb6685bb2018-02-04 00:44:47 +010055 // TODO: Destroy match!
56 destroy : function () {
57 this._matchCreator = undefined;
58 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000059
Akronff1b1f32020-10-18 11:41:29 +020060
Nils Diewald7148c6f2015-05-04 15:07:53 +000061 /**
62 * Length of the table (columns),
63 * aka the number of tokens
64 * in the snippet.
65 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000066 length : function () {
67 return this._pos;
68 },
69
Akronad1e46a2018-09-19 15:55:40 +020070
71 /**
72 * Move the viewport to the match
73 */
74 toMark : function () {
75 if (this._markE === undefined)
76 return;
77 this._markE.scrollIntoView({
78 inline: "start",
79 block: "nearest"
80 });
81 },
82
Akronff1b1f32020-10-18 11:41:29 +020083
Nils Diewald7148c6f2015-05-04 15:07:53 +000084 /**
85 * Get the token in the snippet
86 * At a given position.
87 *
88 * @param pos
89 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000090 getToken : function (pos) {
91 if (pos === undefined)
Akron916ec252016-11-10 17:06:32 +010092 return this._token;
Nils Diewald0e6992a2015-04-14 20:13:52 +000093 return this._token[pos];
94 },
Nils Diewald7148c6f2015-05-04 15:07:53 +000095
Akronff1b1f32020-10-18 11:41:29 +020096
Nils Diewald7148c6f2015-05-04 15:07:53 +000097 /**
98 * Get the annotation of a token
99 * in the snippet based on the position,
100 * the foundry, and the layer.
101 *
102 * @param pos
103 * @param foundry
104 * @param layer
105 */
Nils Diewald0e6992a2015-04-14 20:13:52 +0000106 getValue : function (pos, foundry, layer) {
107 return this._info[pos][foundry + '/' + layer]
108 },
Nils Diewald0e6992a2015-04-14 20:13:52 +0000109
Akronaeceda72018-02-02 20:44:06 +0100110
Nils Diewald0e6992a2015-04-14 20:13:52 +0000111 // Parse the snippet
Akronad1e46a2018-09-19 15:55:40 +0200112 _parse : function (children, mark) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000113
114 // Get all children
Akron678c26f2020-10-09 08:52:50 +0200115 children.forEach(function(c) {
Akronff1b1f32020-10-18 11:41:29 +0200116 const t = this;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000117
Akron916ec252016-11-10 17:06:32 +0100118 // Create object on position unless it exists
Akronff1b1f32020-10-18 11:41:29 +0200119 if (t._info[t._pos] === undefined) {
120 t._info[t._pos] = {};
Akron916ec252016-11-10 17:06:32 +0100121 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000122
Akron916ec252016-11-10 17:06:32 +0100123 // Store at position in foundry/layer as array
Akronff1b1f32020-10-18 11:41:29 +0200124 const found = t._info[t._pos];
Nils Diewald0e6992a2015-04-14 20:13:52 +0000125
Akron916ec252016-11-10 17:06:32 +0100126 // Element with title
127 if (c.nodeType === 1) {
Akronff1b1f32020-10-18 11:41:29 +0200128 let newMark = mark;
Akronad1e46a2018-09-19 15:55:40 +0200129
130 if (c.tagName === 'MARK') {
131 newMark = true;
132 }
133
Akron083ec572019-05-16 18:30:40 +0200134 else if (c.hasAttribute("title") &&
Akron916ec252016-11-10 17:06:32 +0100135 _TermRE.exec(c.getAttribute("title"))) {
Nils Diewald0e6992a2015-04-14 20:13:52 +0000136
Akron916ec252016-11-10 17:06:32 +0100137 // Fill position with info
Akronff1b1f32020-10-18 11:41:29 +0200138 let foundry, layer, value;
Akron916ec252016-11-10 17:06:32 +0100139 if (RegExp.$2) {
140 foundry = RegExp.$1;
141 layer = RegExp.$2;
142 }
143 else {
144 foundry = "base";
145 layer = RegExp.$1
146 };
Nils Diewald0e6992a2015-04-14 20:13:52 +0000147
Akron916ec252016-11-10 17:06:32 +0100148 value = RegExp.$3;
149
150 if (found[foundry + "/" + layer] === undefined) {
151 found[foundry + "/" + layer] = [value];
152 }
153 else {
Akron3b253d32018-07-15 10:16:06 +0200154 // if (found[foundry + "/" + layer].indexOf(value) === -1) {
155 if (!found[foundry + "/" + layer].includes(value)) {
Akron916ec252016-11-10 17:06:32 +0100156 // Push value to foundry/layer at correct position
157 found[foundry + "/" + layer].push(value);
158 };
159 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000160
Akron916ec252016-11-10 17:06:32 +0100161 // Set foundry
Akronff1b1f32020-10-18 11:41:29 +0200162 if (t._foundry[foundry] === undefined)
163 t._foundry[foundry] = {};
164 t._foundry[foundry][layer] = 1;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000165
Akron916ec252016-11-10 17:06:32 +0100166 // Set layer
Akronff1b1f32020-10-18 11:41:29 +0200167 if (t._layer[layer] === undefined)
168 t._layer[layer] = {};
169 t._layer[layer][foundry] = 1;
Akron083ec572019-05-16 18:30:40 +0200170 }
171
172 // The current position marks a cut
173 else if (c.hasAttribute("class") && c.getAttribute("class") == "cutted") {
Akronff1b1f32020-10-18 11:41:29 +0200174 t._cutted.push(t._pos);
175 t._token[t._pos++] = "";
Akron083ec572019-05-16 18:30:40 +0200176 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000177
Akron916ec252016-11-10 17:06:32 +0100178 // depth search
179 if (c.hasChildNodes())
Akronff1b1f32020-10-18 11:41:29 +0200180 t._parse(c.childNodes, newMark);
Akron916ec252016-11-10 17:06:32 +0100181 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000182
Akron916ec252016-11-10 17:06:32 +0100183 // Leaf node
184 // store string on position and go to next string
185 else if (c.nodeType === 3) {
Akron158fce12019-12-17 14:43:29 +0100186 if (c.nodeValue.match(/[a-z0-9\u25ae]/iu)) {
Akronff1b1f32020-10-18 11:41:29 +0200187 t._mark[t._pos] = mark ? true : false;
188 t._token[t._pos++] = c.nodeValue;
Akronad1e46a2018-09-19 15:55:40 +0200189 };
Akron916ec252016-11-10 17:06:32 +0100190 };
Akron678c26f2020-10-09 08:52:50 +0200191 }, this);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000192
193 delete this._info[this._pos];
194 },
195
196
197 /**
198 * Get HTML table view of annotations.
199 */
200 element : function () {
201 if (this._element !== undefined)
Akron916ec252016-11-10 17:06:32 +0100202 return this._element;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000203
204 // First the legend table
Akronff1b1f32020-10-18 11:41:29 +0200205 const wrap = d.createElement('div');
Akronaeceda72018-02-02 20:44:06 +0100206
Akronff1b1f32020-10-18 11:41:29 +0200207 const table = wrap.addE('table');
Akronaeceda72018-02-02 20:44:06 +0100208
209 this._element = wrap;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000210
211 // Single row in head
Akronff1b1f32020-10-18 11:41:29 +0200212 let tr = table.addE('thead').addE('tr');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000213
Akronff1b1f32020-10-18 11:41:29 +0200214 const ah = KorAP.annotationHelper || { "getDesc" : function () {}};
215
Nils Diewald0e6992a2015-04-14 20:13:52 +0000216 // Add cell to row
Akronff1b1f32020-10-18 11:41:29 +0200217 const addCell = function (type, key, value) {
218 const c = this.addE(type);
Akron0b489ad2018-02-02 16:49:32 +0100219
Akronf2279c42017-12-21 13:48:46 +0100220 if (value === undefined)
Akron916ec252016-11-10 17:06:32 +0100221 return c;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000222
Akronf2279c42017-12-21 13:48:46 +0100223 if (key && value instanceof Array && value[1] !== undefined) {
Akron4e47d0b2017-07-03 17:58:37 +0200224
225 // There are multiple values to add
Akron856af1e2017-07-03 19:57:46 +0200226 c.classList.add('matchkeyvalues');
Akronf2279c42017-12-21 13:48:46 +0100227
Akronb50964a2020-10-12 11:44:37 +0200228 let e, anno;
229 value.forEach(function(v) {
230 e = c.addE('div');
231 e.addT(v);
232
233 anno = ah.getDesc(key, v);
Akronf2279c42017-12-21 13:48:46 +0100234
235 if (anno)
236 e.setAttribute("title", anno);
Akronb50964a2020-10-12 11:44:37 +0200237 });
Akron916ec252016-11-10 17:06:32 +0100238 }
Akronf2279c42017-12-21 13:48:46 +0100239
Akron916ec252016-11-10 17:06:32 +0100240 else {
Akronf2279c42017-12-21 13:48:46 +0100241
242 if (value instanceof Array)
243 value = value[0];
244
Akron0b489ad2018-02-02 16:49:32 +0100245 c.addT(value);
Akronf2279c42017-12-21 13:48:46 +0100246
247 // Add tooltip
Akronff1b1f32020-10-18 11:41:29 +0200248 const anno = ah.getDesc(key, value);
Akronf2279c42017-12-21 13:48:46 +0100249 if (anno)
250 c.setAttribute("title", anno);
Akron916ec252016-11-10 17:06:32 +0100251 };
Akron80055992017-12-20 16:30:52 +0100252
253 return c;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000254 };
255
256 tr.addCell = addCell;
257
258 // Add header information
Akronf2279c42017-12-21 13:48:46 +0100259 tr.addCell('th', undefined, 'Foundry');
260 tr.addCell('th', undefined, 'Layer');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000261
262 // Add tokens
Akron678c26f2020-10-09 08:52:50 +0200263 Object.keys(this._token).forEach(function(i) {
Akronff1b1f32020-10-18 11:41:29 +0200264 const surface = this.getToken(i);
265 const c = tr.addCell('th', undefined, surface);
Akronad1e46a2018-09-19 15:55:40 +0200266 if (this._mark[i]) {
267 c.classList.add('mark');
268 if (this._markE === undefined) {
269 this._markE = c;
270 };
Akron083ec572019-05-16 18:30:40 +0200271 }
Akron1a780fe2019-05-21 15:59:00 +0200272 else if (this._cutted[0] == i || this._cutted[1] == i) {
Akron083ec572019-05-16 18:30:40 +0200273 c.classList.add('cutted');
Akronad1e46a2018-09-19 15:55:40 +0200274 };
Akron5dc31172019-05-15 18:43:48 +0200275
276 // In case the title is very long - add a title attribute
277 if (surface.length > 20) {
278 c.setAttribute("title", surface)
279 }
Akron678c26f2020-10-09 08:52:50 +0200280 }, this);
Akron916ec252016-11-10 17:06:32 +0100281
Akronff1b1f32020-10-18 11:41:29 +0200282 const tbody = table.addE('tbody');
Nils Diewald0e6992a2015-04-14 20:13:52 +0000283
Akronb50964a2020-10-12 11:44:37 +0200284 let layerList, key, v, value, cell;
285
Akronff1b1f32020-10-18 11:41:29 +0200286 Object.keys(this._foundry).sort().forEach(function(foundry) {
Akronb50964a2020-10-12 11:44:37 +0200287 let layerList =
Akron99713ef2017-06-28 18:19:28 +0200288 Object.keys(this._foundry[foundry]).sort();
Nils Diewald0e6992a2015-04-14 20:13:52 +0000289
Akronb50964a2020-10-12 11:44:37 +0200290 layerList.forEach(function(layer) {
291
Akron0b489ad2018-02-02 16:49:32 +0100292 tr = tbody.addE('tr');
Akron99713ef2017-06-28 18:19:28 +0200293 tr.setAttribute('tabindex', 0);
294 tr.addCell = addCell;
Akronf2279c42017-12-21 13:48:46 +0100295 tr.addCell('th', undefined, foundry);
296 tr.addCell('th', undefined, layer);
Nils Diewald0e6992a2015-04-14 20:13:52 +0000297
Akronb50964a2020-10-12 11:44:37 +0200298 key = foundry + '/' + layer + '=';
Akron80055992017-12-20 16:30:52 +0100299
Akronb50964a2020-10-12 11:44:37 +0200300 for (v = 0; v < this.length(); v++) {
Akron80055992017-12-20 16:30:52 +0100301
302 // Get the cell value
Akronb50964a2020-10-12 11:44:37 +0200303 value = this.getValue(v, foundry, layer);
Akron80055992017-12-20 16:30:52 +0100304
305 // Add cell to row
Akronb50964a2020-10-12 11:44:37 +0200306 cell = tr.addCell(
Akron99713ef2017-06-28 18:19:28 +0200307 'td',
Akronf2279c42017-12-21 13:48:46 +0100308 key,
Akron80055992017-12-20 16:30:52 +0100309 value
Akron99713ef2017-06-28 18:19:28 +0200310 );
Akronad1e46a2018-09-19 15:55:40 +0200311
312 if (this._mark[v]) {
313 cell.classList.add('mark');
314 };
Akron99713ef2017-06-28 18:19:28 +0200315 };
Akronb50964a2020-10-12 11:44:37 +0200316 }, this);
317 }, this);
Akronb6685bb2018-02-04 00:44:47 +0100318
319 // Add query creator
320 this._matchCreator = matchQueryCreator.create(this._element);
321
Akronaeceda72018-02-02 20:44:06 +0100322 return this._element;
323 },
Nils Diewald0e6992a2015-04-14 20:13:52 +0000324 };
325});