blob: b379b372dd8fb117ea426df958193f51b1155c59 [file] [log] [blame]
Nils Diewald0e6992a2015-04-14 20:13:52 +00001/**
2 * Visualize span annotations as a tree using Dagre.
3 */
4define(['lib/dagre'], function (dagre) {
5 "use strict";
6
7 var svgXmlns = "http://www.w3.org/2000/svg";
8 var _TermRE = new RegExp("^(?:([^\/]+?)\/)?([^:]+?):(.+?)$");
9
10 // Create path for node connections
11 function _line (src, target) {
12 var x1 = src.x,
13 y1 = src.y,
14 x2 = target.x,
15 y2 = target.y - target.height / 2;
16
17 // c 0,0 -10,0
18 return 'M ' + x1 + ',' + y1 + ' ' +
19 'C ' + x1 + ',' + y1 + ' ' +
20 x2 + ',' + (y2 - (y2 - y1) / 2) + ' ' +
21 x2 + ',' + y2;
22 };
23
24 return {
25 create : function (snippet) {
26 return Object.create(this)._init(snippet);
27 },
28
29 nodes : function () {
30 return this._next;
31 },
32
33 _addNode : function (id, obj) {
34 obj["width"] = 55;
35 obj["height"] = 20;
36 this._graph.setNode(id, obj)
37 },
38
39 _addEdge : function (src, target) {
40 this._graph.setEdge(src, target);
41 },
42
43 _init : function (snippet) {
44 this._next = new Number(0);
45
46 // Create html for traversal
47 var html = document.createElement("div");
48 html.innerHTML = snippet;
49 var g = new dagre.graphlib.Graph({
50 "directed" : true
51 });
52 g.setGraph({
53 "nodesep" : 35,
54 "ranksep" : 15,
55 "marginx" : 40,
56 "marginy" : 10
57 });
58 g.setDefaultEdgeLabel({});
59
60 this._graph = g;
61
62 // This is a new root
63 this._addNode(
64 this._next++,
65 { "class" : "root" }
66 );
67
68 // Parse nodes from root
69 this._parse(0, html.childNodes);
70
71 // Root node has only one child - remove
72 if (g.outEdges(0).length === 1)
73 g.removeNode(0);
74
75 html = undefined;
76 return this;
77 },
78
79 // Remove foundry and layer for labels
80 _clean : function (title) {
81 return title.replace(_TermRE, "$3");
82 },
83
84 // Parse the snippet
85 _parse : function (parent, children) {
86 for (var i in children) {
87 var c = children[i];
88
89 // Element node
90 if (c.nodeType == 1) {
91
92 // Get title from html
93 if (c.getAttribute("title")) {
94 var title = this._clean(c.getAttribute("title"));
95
96 // Add child node
97 var id = this._next++;
98
99 this._addNode(id, {
100 "class" : "middle",
101 "label" : title
102 });
103 this._addEdge(parent, id);
104
105 // Check for next level
106 if (c.hasChildNodes())
107 this._parse(id, c.childNodes);
108 }
109
110 // Step further
111 else if (c.hasChildNodes())
112 this._parse(parent, c.childNodes);
113 }
114
115 // Text node
116 else if (c.nodeType == 3)
117
118 if (c.nodeValue.match(/[-a-z0-9]/i)) {
119
120 // Add child node
121 var id = this._next++;
122 this._addNode(id, {
123 "class" : "leaf",
124 "label" : c.nodeValue
125 });
126
127 this._addEdge(parent, id);
128 };
129 };
130 return this;
131 },
132
133 /**
134 * Center the viewport of the canvas
135 */
136 center : function () {
137 if (this._element === undefined)
138 return;
139
140 var treeDiv = this._element.parentNode;
141
142 var cWidth = parseFloat(window.getComputedStyle(this._element).width);
143 var treeWidth = parseFloat(window.getComputedStyle(treeDiv).width);
144 // Reposition:
145 if (cWidth > treeWidth) {
146 var scrollValue = (cWidth - treeWidth) / 2;
147 treeDiv.scrollLeft = scrollValue;
148 };
149 },
150
151 // Get element
152 element : function () {
153 if (this._element !== undefined)
154 return this._element;
155
156 var g = this._graph;
157
158 dagre.layout(g);
159
160 var canvas = document.createElementNS(svgXmlns, 'svg');
161 this._element = canvas;
162
163 canvas.setAttribute('height', g.graph().height);
164 canvas.setAttribute('width', g.graph().width);
165
166 // Create edges
167 g.edges().forEach(
168 function (e) {
169 var src = g.node(e.v);
170 var target = g.node(e.w);
171 var p = document.createElementNS(svgXmlns, 'path');
172 p.setAttributeNS(null, "d", _line(src, target));
173 p.classList.add('edge');
174 canvas.appendChild(p);
175 });
176
177 // Create nodes
178 g.nodes().forEach(
179 function (v) {
180 v = g.node(v);
181 var group = document.createElementNS(svgXmlns, 'g');
182 group.classList.add(v.class);
183
184 // Add node box
185 var rect = group.appendChild(document.createElementNS(svgXmlns, 'rect'));
186 rect.setAttributeNS(null, 'x', v.x - v.width / 2);
187 rect.setAttributeNS(null, 'y', v.y - v.height / 2);
188 rect.setAttributeNS(null, 'rx', 5);
189 rect.setAttributeNS(null, 'ry', 5);
190 rect.setAttributeNS(null, 'width', v.width);
191 rect.setAttributeNS(null, 'height', v.height);
192
193 if (v.class === 'root' && v.label === undefined) {
194 rect.setAttributeNS(null, 'width', v.height);
195 rect.setAttributeNS(null, 'x', v.x - v.height / 2);
196 rect.setAttributeNS(null, 'class', 'empty');
197 };
198
199 // Add label
200 if (v.label !== undefined) {
201 var text = group.appendChild(document.createElementNS(svgXmlns, 'text'));
202 text.setAttributeNS(null, 'x', v.x - v.width / 2);
203 text.setAttributeNS(null, 'y', v.y - v.height / 2);
204 text.setAttributeNS(
205 null,
206 'transform',
207 'translate(' + v.width/2 + ',' + ((v.height / 2) + 5) + ')'
208 );
209
210 var tspan = document.createElementNS(svgXmlns, 'tspan');
211 tspan.appendChild(document.createTextNode(v.label));
212 text.appendChild(tspan);
213 };
214
215 canvas.appendChild(group);
216 }
217 );
218
219 return this._element;
220 }
221 };
222});