blob: 30bd7b5f69ff9b1937cb34cb4181e6a1083025ec [file] [log] [blame]
Nils Diewalde8518f82015-03-18 22:41:49 +00001/**
Nils Diewald4f6521a2015-03-20 21:30:13 +00002 * Visualize annotations.
Nils Diewalde8518f82015-03-18 22:41:49 +00003 *
4 * @author Nils Diewald
5 */
Nils Diewald6e43ffd2015-03-25 18:55:39 +00006// require menu.js
Nils Diewalde8518f82015-03-18 22:41:49 +00007/*
Nils Diewald6e43ffd2015-03-25 18:55:39 +00008 * - Scroll with a static left legend.
9 * - Highlight (at least mark as bold) the match
10 * - Scroll to match vertically per default
Nils Diewalde8518f82015-03-18 22:41:49 +000011 */
12var KorAP = KorAP || {};
13
14(function (KorAP) {
15 "use strict";
16
Nils Diewald6e43ffd2015-03-25 18:55:39 +000017 var svgXmlns = "http://www.w3.org/2000/svg";
18
Nils Diewald4f6521a2015-03-20 21:30:13 +000019 // Default log message
20 KorAP.log = KorAP.log || function (type, msg) {
21 console.log(type + ": " + msg);
22 };
23
Nils Diewald6e43ffd2015-03-25 18:55:39 +000024 // Localization values
25 var loc = (KorAP.Locale = KorAP.Locale || {} );
26 loc.ADDTREE = loc.ADDTREE || 'Add tree view';
27
Nils Diewalde8518f82015-03-18 22:41:49 +000028 KorAP._AvailableRE = new RegExp("^([^\/]+?)\/([^=]+?)(?:=(spans|rels|tokens))?$");
Nils Diewald4f6521a2015-03-20 21:30:13 +000029 KorAP._TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
Nils Diewalde8518f82015-03-18 22:41:49 +000030 KorAP._matchTerms = ["corpusID", "docID", "textID"];
31
32 // API requests
33 KorAP.API = KorAP.API || {};
Nils Diewald6e43ffd2015-03-25 18:55:39 +000034
Nils Diewald8bc7e412015-03-19 22:08:27 +000035 // TODO: Make this async
Nils Diewalde8518f82015-03-18 22:41:49 +000036 KorAP.API.getMatchInfo = KorAP.API.getMatchInfo || function () { return {} };
37
38 KorAP.MatchInfo = {
39
40 /**
41 * Create a new annotation object.
42 * Expects an array of available foundry/layer=type terms.
43 * Supported types are 'spans', 'tokens' and 'rels'.
44 */
45 create : function (match, available) {
46 if (arguments.length < 2)
47 throw new Error("Missing parameters");
48
49 return Object.create(KorAP.MatchInfo)._init(match, available);
50 },
51
Nils Diewald6e43ffd2015-03-25 18:55:39 +000052 /**
53 * Destroy this match information view.
54 */
55 destroy : function () {
56
57 // Remove circular reference
58 if (this._treeMenu !== undefined)
59 delete this._treeMenu["info"];
60
61 this._treeMenu.destroy();
62 this._treeMenu = undefined;
63 },
64
Nils Diewalde8518f82015-03-18 22:41:49 +000065 _init : function (match, available) {
66 this._match = KorAP.Match.create(match);
67 this._available = {
68 tokens : [],
69 spans : [],
70 rels : []
71 };
72 for (var i = 0; i < available.length; i++) {
73 var term = available[i];
74 // Create info layer objects
75 try {
76 var layer = KorAP.InfoLayer.create(term);
77 this._available[layer.type].push(layer);
78 }
79 catch (e) {
80 continue;
81 };
82 };
83 return this;
84 },
85
86
87 /**
88 * Return a list of parseable tree annotations.
89 */
90 getSpans : function () {
91 return this._available.spans;
92 },
93
94
95 /**
96 * Return a list of parseable token annotations.
97 */
98 getTokens : function () {
99 return this._available.tokens;
100 },
101
102
103 /**
104 * Return a list of parseable relation annotations.
105 */
106 getRels : function () {
107 return this._available.rels;
108 },
109
110
Nils Diewald8bc7e412015-03-19 22:08:27 +0000111 /**
112 * Get table object.
113 */
Nils Diewalde8518f82015-03-18 22:41:49 +0000114 getTable : function (tokens) {
115 var focus = [];
116
117 // Get all tokens
118 if (tokens === undefined) {
119 focus = this.getTokens();
120 }
121
122 // Get only some tokens
123 else {
124
125 // Push newly to focus array
126 for (var i = 0; i < tokens.length; i++) {
127 var term = tokens[i];
128 try {
129 // Create info layer objects
130 var layer = KorAP.InfoLayer.create(term);
131 layer.type = "tokens";
132 focus.push(layer);
133 }
134 catch (e) {
135 continue;
136 };
137 };
138 };
139
140 // No tokens chosen
141 if (focus.length == 0)
142 return;
143
144 // Get info (may be cached)
Nils Diewald4f6521a2015-03-20 21:30:13 +0000145 // TODO: Async
Nils Diewalde8518f82015-03-18 22:41:49 +0000146 var matchResponse = KorAP.API.getMatchInfo(
147 this._match,
Nils Diewald4f6521a2015-03-20 21:30:13 +0000148 { 'spans' : false, 'layer' : focus }
Nils Diewalde8518f82015-03-18 22:41:49 +0000149 );
150
151 // Get snippet from match info
152 if (matchResponse["snippet"] !== undefined) {
Nils Diewald8bc7e412015-03-19 22:08:27 +0000153 this._table = KorAP.MatchTable.create(matchResponse["snippet"]);
Nils Diewalde8518f82015-03-18 22:41:49 +0000154 return this._table;
155 };
156
157 return null;
Nils Diewald4f6521a2015-03-20 21:30:13 +0000158 },
Nils Diewalde8518f82015-03-18 22:41:49 +0000159
Nils Diewalde8518f82015-03-18 22:41:49 +0000160 // Parse snippet for table visualization
161 getTree : function (foundry, layer) {
Nils Diewald4f6521a2015-03-20 21:30:13 +0000162 var focus = [];
163
164 // TODO: Async
165 var matchResponse = KorAP.API.getMatchInfo(
166 this._match, {
167 'spans' : true,
168 'foundry' : foundry,
169 'layer' : layer
170 }
171 );
172
173 // TODO: Support and cache multiple trees
174
175 // Get snippet from match info
176 if (matchResponse["snippet"] !== undefined) {
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000177 // Todo: This should be cached somehow
178 return KorAP.MatchTree.create(matchResponse["snippet"]);
Nils Diewald4f6521a2015-03-20 21:30:13 +0000179 };
180
181 return null;
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000182 },
183
184 /**
185 * Add a new tree view to the list
186 */
187 addTree : function (foundry, layer) {
188 var treeObj = this.getTree(foundry, layer);
189
190 // Something went wrong - probably log!!!
191 if (treeObj === null)
192 return;
193
194 var matchtree = document.createElement('div');
195 matchtree.classList.add('matchtree');
196
197 var h6 = matchtree.appendChild(document.createElement('h6'));
198 h6.appendChild(document.createElement('span'))
199 .appendChild(document.createTextNode(foundry));
200 h6.appendChild(document.createElement('span'))
201 .appendChild(document.createTextNode(layer));
202
203 var tree = matchtree.appendChild(
204 document.createElement('div')
205 );
206 tree.appendChild(treeObj.element());
207 this._element.insertBefore(matchtree, this._element.lastChild);
208
209 var close = tree.appendChild(document.createElement('em'));
210 close.addEventListener(
211 'click', function (e) {
212 matchtree.parentNode.removeChild(matchtree);
213 e.halt();
214 }
215 );
216
217 // Reposition the view to the center
218 // (This may in a future release be a reposition
219 // to move the root into the center or the actual
220 // match)
221 treeObj.center();
222 },
223
224 /**
225 * Create match information view.
226 */
227 element : function () {
228 if (this._element !== undefined)
229 return this._element;
230
231 // Create info table
232 var info = document.createElement('div');
233 info.classList.add('matchinfo');
234
235 // Append default table
236 var matchtable = document.createElement('div');
237 matchtable.classList.add('matchtable');
238 matchtable.appendChild(this.getTable().element());
239 info.appendChild(matchtable);
240
241 var spanLayers = this.getSpans().sort(
242 function (a, b) {
243 if (a.foundry < b.foundry) {
244 return -1;
245 }
246 else if (a.foundry > b.foundry) {
247 return 1;
248 }
249 else if (a.layer < b.layer) {
250 return -1;
251 }
252 else if (a.layer > b.layer) {
253 return 1;
254 };
255 return 0;
256 });
257
258 var menuList = [];
259
260 // Show tree views
261 for (var i = 0; i < spanLayers.length; i++) {
262 var span = spanLayers[i];
263
264 // Add foundry/layer to menu list
265 menuList.push([
266 span.foundry + '/' + span.layer,
267 span.foundry,
268 span.layer
269 ]);
270 };
271
272 // Create tree menu
273 var treemenu = this.treeMenu(menuList);
274 var span = info.appendChild(document.createElement('p'));
275 span.appendChild(document.createTextNode(loc.ADDTREE));
276
277 var treeElement = treemenu.element();
278 span.appendChild(treeElement);
279
280 span.addEventListener('click', function (e) {
281 treemenu.show('');
282 treemenu.focus();
283 });
284
285 this._element = info;
286
287 return info;
288 },
289
290 treeMenu : function (list) {
291 if (this._treeMenu !== undefined)
292 return this._treeMenu;
293
294 return this._treeMenu = KorAP.MatchTreeMenu.create(this, list);
Nils Diewald8bc7e412015-03-19 22:08:27 +0000295 }
Nils Diewalde8518f82015-03-18 22:41:49 +0000296 };
297
298 KorAP.Match = {
299 create : function (match) {
300 return Object.create(KorAP.Match)._init(match);
301 },
302 _init : function (match) {
303 for (var i in KorAP._matchTerms) {
304 var term = KorAP._matchTerms[i];
305 if (match[term] !== undefined) {
306 this[term] = match[term];
307 }
308 else {
309 this[term] = undefined;
310 }
311 };
312 return this;
313 },
314 };
315
316 /**
317 *
318 * Alternatively pass a string as <tt>base/s=span</tt>
319 *
320 * @param foundry
321 */
322 KorAP.InfoLayer = {
323 create : function (foundry, layer, type) {
324 return Object.create(KorAP.InfoLayer)._init(foundry, layer, type);
325 },
326 _init : function (foundry, layer, type) {
327 if (foundry === undefined)
328 throw new Error("Missing parameters");
329
330 if (layer === undefined) {
331 if (KorAP._AvailableRE.exec(foundry)) {
332 this.foundry = RegExp.$1;
333 this.layer = RegExp.$2;
334 this.type = RegExp.$3;
335 }
336 else {
337 throw new Error("Missing parameters");
338 };
339 }
340 else {
341 this.foundry = foundry;
342 this.layer = layer;
343 this.type = type;
344 };
345
346 if (this.type === undefined)
347 this.type = 'tokens';
348
349 return this;
350 }
351 };
352
353
Nils Diewald8bc7e412015-03-19 22:08:27 +0000354 KorAP.MatchTable = {
Nils Diewalde8518f82015-03-18 22:41:49 +0000355 create : function (snippet) {
Nils Diewald8bc7e412015-03-19 22:08:27 +0000356 return Object.create(KorAP.MatchTable)._init(snippet);
Nils Diewalde8518f82015-03-18 22:41:49 +0000357 },
358 _init : function (snippet) {
359 // Create html for traversal
360 var html = document.createElement("div");
361 html.innerHTML = snippet;
362
363 this._pos = 0;
364 this._token = [];
365 this._info = [];
Nils Diewald8bc7e412015-03-19 22:08:27 +0000366 this._foundry = {};
367 this._layer = {};
Nils Diewalde8518f82015-03-18 22:41:49 +0000368
369 // Parse the snippet
370 this._parse(html.childNodes);
371
Nils Diewalde8518f82015-03-18 22:41:49 +0000372 html.innerHTML = '';
373 return this;
374 },
375
376 length : function () {
377 return this._pos;
378 },
379
380 getToken : function (pos) {
381 if (pos === undefined)
382 return this._token;
383 return this._token[pos];
384 },
385
386 getValue : function (pos, foundry, layer) {
387 return this._info[pos][foundry + '/' + layer]
388 },
389
390 getLayerPerFoundry : function (foundry) {
391 return this._foundry[foundry]
392 },
393
394 getFoundryPerLayer : function (layer) {
395 return this._layer[layer];
396 },
397
398 // Parse the snippet
399 _parse : function (children) {
400
401 // Get all children
402 for (var i in children) {
403 var c = children[i];
404
405 // Create object on position unless it exists
406 if (this._info[this._pos] === undefined)
407 this._info[this._pos] = {};
408
409 // Store at position in foundry/layer as array
410 var found = this._info[this._pos];
411
412 // Element with title
413 if (c.nodeType === 1) {
414 if (c.getAttribute("title") &&
415 KorAP._TermRE.exec(c.getAttribute("title"))) {
416
417 // Fill position with info
418 var foundry, layer, value;
419 if (RegExp.$2) {
420 foundry = RegExp.$1;
421 layer = RegExp.$2;
422 }
423 else {
424 foundry = "base";
425 layer = RegExp.$1
426 };
427
428 value = RegExp.$3;
429
430 if (found[foundry + "/" + layer] === undefined)
431 found[foundry + "/" + layer] = [];
432
433 // Push value to foundry/layer at correct position
434 found[foundry + "/" + layer].push(RegExp.$3);
435
436 // Set foundry
Nils Diewald8bc7e412015-03-19 22:08:27 +0000437 if (this._foundry[foundry] === undefined)
Nils Diewalde8518f82015-03-18 22:41:49 +0000438 this._foundry[foundry] = {};
439 this._foundry[foundry][layer] = 1;
440
441 // Set layer
Nils Diewald8bc7e412015-03-19 22:08:27 +0000442 if (this._layer[layer] === undefined)
Nils Diewalde8518f82015-03-18 22:41:49 +0000443 this._layer[layer] = {};
444 this._layer[layer][foundry] = 1;
445 };
446
447 // depth search
448 if (c.hasChildNodes())
449 this._parse(c.childNodes);
450 }
451
Nils Diewald8bc7e412015-03-19 22:08:27 +0000452 // Leaf node
453 // store string on position and go to next string
Nils Diewalde8518f82015-03-18 22:41:49 +0000454 else if (c.nodeType === 3) {
455 if (c.nodeValue.match(/[a-z0-9]/i))
456 this._token[this._pos++] = c.nodeValue;
457 };
458 };
459
460 delete this._info[this._pos];
461 },
Nils Diewald4f6521a2015-03-20 21:30:13 +0000462
463
464 /**
465 * Get HTML table view of annotations.
466 */
Nils Diewalde8518f82015-03-18 22:41:49 +0000467 element : function () {
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000468 if (this._element !== undefined)
469 return this._element;
470
Nils Diewalde8518f82015-03-18 22:41:49 +0000471 // First the legend table
Nils Diewald8bc7e412015-03-19 22:08:27 +0000472 var d = document;
473 var table = d.createElement('table');
Nils Diewald8bc7e412015-03-19 22:08:27 +0000474
Nils Diewald4f6521a2015-03-20 21:30:13 +0000475 // Single row in head
476 var tr = table.appendChild(d.createElement('thead'))
477 .appendChild(d.createElement('tr'));
478
479 // Add cell to row
Nils Diewald8bc7e412015-03-19 22:08:27 +0000480 var addCell = function (type, name) {
481 var c = this.appendChild(d.createElement(type))
482 if (name === undefined)
483 return c;
484
485 if (name instanceof Array) {
486 for (var n = 0; n < name.length; n++) {
487 c.appendChild(d.createTextNode(name[n]));
488 if (n !== name.length - 1) {
489 c.appendChild(d.createElement('br'));
490 };
491 };
492 }
493 else {
494 c.appendChild(d.createTextNode(name));
495 };
496 };
497
498 tr.addCell = addCell;
499
500 // Add header information
501 tr.addCell('th', 'Foundry');
502 tr.addCell('th', 'Layer');
503
504 // Add tokens
505 for (var i in this._token) {
506 tr.addCell('th', this.getToken(i));
507 };
508
509 var tbody = table.appendChild(
510 d.createElement('tbody')
511 );
512
513 var foundryList = Object.keys(this._foundry).sort();
514
515 for (var f = 0; f < foundryList.length; f++) {
516 var foundry = foundryList[f];
517 var layerList =
518 Object.keys(this._foundry[foundry]).sort();
519
520 for (var l = 0; l < layerList.length; l++) {
521 var layer = layerList[l];
522 tr = tbody.appendChild(
523 d.createElement('tr')
524 );
525 tr.setAttribute('tabindex', 0);
526 tr.addCell = addCell;
527
528 tr.addCell('th', foundry);
529 tr.addCell('th', layer);
530
531 for (var v = 0; v < this.length(); v++) {
532 tr.addCell(
533 'td',
534 this.getValue(v, foundry, layer)
535 );
536 };
537 };
538 };
539
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000540 return this._element = table;
Nils Diewalde8518f82015-03-18 22:41:49 +0000541 }
542 };
543
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000544
Nils Diewald4f6521a2015-03-20 21:30:13 +0000545 /**
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000546 * Visualize span annotations as a tree using Dagre.
Nils Diewald4f6521a2015-03-20 21:30:13 +0000547 */
Nils Diewald4f6521a2015-03-20 21:30:13 +0000548 KorAP.MatchTree = {
549
550 create : function (snippet) {
551 return Object.create(KorAP.MatchTree)._init(snippet);
552 },
553
554 nodes : function () {
555 return this._next;
556 },
557
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000558 _addNode : function (id, obj) {
559 obj["width"] = 55;
560 obj["height"] = 20;
561 this._graph.setNode(id, obj)
562 },
563
564 _addEdge : function (src, target) {
565 this._graph.setEdge(src, target);
566 },
567
Nils Diewald4f6521a2015-03-20 21:30:13 +0000568 _init : function (snippet) {
569 this._next = new Number(0);
570
571 // Create html for traversal
572 var html = document.createElement("div");
573 html.innerHTML = snippet;
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000574 var g = new dagre.graphlib.Graph({
575 "directed" : true
576 });
577 g.setGraph({
578 "nodesep" : 35,
579 "ranksep" : 15,
580 "marginx" : 40,
581 "marginy" : 10
582 });
583 g.setDefaultEdgeLabel({});
584
585 this._graph = g;
Nils Diewald4f6521a2015-03-20 21:30:13 +0000586
587 // This is a new root
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000588 this._addNode(
Nils Diewald4f6521a2015-03-20 21:30:13 +0000589 this._next++,
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000590 { "class" : "root" }
Nils Diewald4f6521a2015-03-20 21:30:13 +0000591 );
592
593 // Parse nodes from root
594 this._parse(0, html.childNodes);
595
596 // Root node has only one child - remove
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000597 if (g.outEdges(0).length === 1)
598 g.removeNode(0);
Nils Diewald4f6521a2015-03-20 21:30:13 +0000599
600 html = undefined;
601 return this;
602 },
603
604 // Remove foundry and layer for labels
605 _clean : function (title) {
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000606 return title.replace(KorAP._TermRE, "$3");
Nils Diewald4f6521a2015-03-20 21:30:13 +0000607 },
608
609 // Parse the snippet
610 _parse : function (parent, children) {
611 for (var i in children) {
612 var c = children[i];
613
614 // Element node
615 if (c.nodeType == 1) {
616
617 // Get title from html
618 if (c.getAttribute("title")) {
619 var title = this._clean(c.getAttribute("title"));
620
621 // Add child node
622 var id = this._next++;
623
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000624 this._addNode(id, {
625 "class" : "middle",
Nils Diewald4f6521a2015-03-20 21:30:13 +0000626 "label" : title
627 });
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000628 this._addEdge(parent, id);
Nils Diewald4f6521a2015-03-20 21:30:13 +0000629
630 // Check for next level
631 if (c.hasChildNodes())
632 this._parse(id, c.childNodes);
633 }
634
635 // Step further
636 else if (c.hasChildNodes())
637 this._parse(parent, c.childNodes);
638 }
639
640 // Text node
641 else if (c.nodeType == 3)
642
643 if (c.nodeValue.match(/[-a-z0-9]/i)) {
644
645 // Add child node
646 var id = this._next++;
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000647 this._addNode(id, {
648 "class" : "leaf",
Nils Diewald4f6521a2015-03-20 21:30:13 +0000649 "label" : c.nodeValue
650 });
651
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000652 this._addEdge(parent, id);
Nils Diewald4f6521a2015-03-20 21:30:13 +0000653 };
654 };
655 return this;
656 },
657
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000658 /**
659 * Center the viewport of the canvas
660 */
661 center : function () {
662 if (this._element === undefined)
663 return;
664
665 var treeDiv = this._element.parentNode;
666
667 var cWidth = parseFloat(window.getComputedStyle(this._element).width);
668 var treeWidth = parseFloat(window.getComputedStyle(treeDiv).width);
669 // Reposition:
670 if (cWidth > treeWidth) {
671 var scrollValue = (cWidth - treeWidth) / 2;
672 treeDiv.scrollLeft = scrollValue;
673 };
674 },
675
676 // Get element
Nils Diewald4f6521a2015-03-20 21:30:13 +0000677 element : function () {
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000678 if (this._element !== undefined)
679 return this._element;
Nils Diewald4f6521a2015-03-20 21:30:13 +0000680
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000681 var g = this._graph;
Nils Diewald4f6521a2015-03-20 21:30:13 +0000682
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000683 dagre.layout(g);
684
685 var canvas = document.createElementNS(svgXmlns, 'svg');
686 this._element = canvas;
687
688 canvas.setAttribute('height', g.graph().height);
689 canvas.setAttribute('width', g.graph().width);
690
691 // Create edges
692 g.edges().forEach(
693 function (e) {
694 var src = g.node(e.v);
695 var target = g.node(e.w);
696 var p = document.createElementNS(svgXmlns, 'path');
697 p.setAttributeNS(null, "d", _line(src, target));
698 p.classList.add('edge');
699 canvas.appendChild(p);
700 });
701
702 // Create nodes
703 g.nodes().forEach(
704 function (v) {
705 v = g.node(v);
706 var group = document.createElementNS(svgXmlns, 'g');
707 group.classList.add(v.class);
708
709 // Add node box
710 var rect = group.appendChild(document.createElementNS(svgXmlns, 'rect'));
711 rect.setAttributeNS(null, 'x', v.x - v.width / 2);
712 rect.setAttributeNS(null, 'y', v.y - v.height / 2);
713 rect.setAttributeNS(null, 'width', v.width);
714 rect.setAttributeNS(null, 'height', v.height);
715 rect.setAttributeNS(null, 'rx', 5);
716 rect.setAttributeNS(null, 'ry', 5);
717
718 // Add label
719 var text = group.appendChild(document.createElementNS(svgXmlns, 'text'));
720 text.setAttributeNS(null, 'x', v.x - v.width / 2);
721 text.setAttributeNS(null, 'y', v.y - v.height / 2);
722 text.setAttributeNS(
723 null,
724 'transform',
725 'translate(' + v.width/2 + ',' + ((v.height / 2) + 5) + ')'
726 );
727 var tspan = document.createElementNS(svgXmlns, 'tspan');
728 tspan.appendChild(document.createTextNode(v.label));
729 text.appendChild(tspan);
730 canvas.appendChild(group);
731 }
732 );
733
Nils Diewald4f6521a2015-03-20 21:30:13 +0000734 return this._element;
735 }
736 };
737
Nils Diewald6e43ffd2015-03-25 18:55:39 +0000738 /**
739 * Menu item for tree view choice.
740 */
741 KorAP.MatchTreeItem = {
742 create : function (params) {
743 return Object.create(KorAP.MenuItem)
744 .upgradeTo(KorAP.MatchTreeItem)._init(params);
745 },
746 content : function (content) {
747 if (arguments.length === 1) {
748 this._content = content;
749 };
750 return this._content;
751 },
752
753 // The foundry attribute
754 foundry : function () {
755 return this._foundry;
756 },
757
758 // The layer attribute
759 layer : function () {
760 return this._layer;
761 },
762
763 // enter or click
764 onclick : function (e) {
765 var menu = this.menu();
766 menu.hide();
767 e.halt();
768 menu.info().addTree(this._foundry, this._layer);
769 },
770
771 _init : function (params) {
772 if (params[0] === undefined)
773 throw new Error("Missing parameters");
774
775 this._name = params[0];
776 this._foundry = params[1];
777 this._layer = params[2];
778 this._content = document.createTextNode(this._name);
779 this._lcField = ' ' + this.content().textContent.toLowerCase();
780 return this;
781 }
782 };
783
784
785 /**
786 * Menu to choose from for tree views.
787 */
788 KorAP.MatchTreeMenu = {
789 create : function (info, params) {
790 var obj = Object.create(KorAP.Menu)
791 .upgradeTo(KorAP.MatchTreeMenu)
792 ._init(KorAP.MatchTreeItem, undefined, params);
793 obj.limit(6);
794 obj._info = info;
795
796 // This is only domspecific
797 obj.element().addEventListener('blur', function (e) {
798 this.menu.hide();
799 });
800
801 return obj;
802 },
803 info :function () {
804 return this._info;
805 }
806 };
807
808
809 // Create path for node connections
810 function _line (src, target) {
811 var x1 = src.x,
812 y1 = src.y,
813 x2 = target.x,
814 y2 = target.y - target.height / 2;
815
816 // c 0,0 -10,0
817 return 'M ' + x1 + ',' + y1 + ' ' +
818 'C ' + x1 + ',' + y1 + ' ' +
819 x2 + ',' + (y2 - (y2 - y1) / 2) + ' ' +
820 x2 + ',' + y2;
821 };
822
Nils Diewalde8518f82015-03-18 22:41:49 +0000823}(this.KorAP));