blob: 040a674474811cc04726a348da2fbb768c03ca5a [file] [log] [blame]
Akronf5dc5102017-05-16 20:32:57 +02001define(function () {
2 "use strict";
3
4 var svgNS = "http://www.w3.org/2000/svg";
5
6 return {
7 create : function (snippet) {
8 var obj = Object.create(this)._init(snippet);
Akron63ae00b2017-05-16 22:03:36 +02009 obj._tokens = ["0", "1", "2", "3", "4", "5", "6", "7", "8"];
Akronf5dc5102017-05-16 20:32:57 +020010 obj._tokenElements = [];
11 obj._arcs = [
Akron63ae00b2017-05-16 22:03:36 +020012 { start: 0, end: 1, label: "a" },
13 { start: 0, end: 1, label: "b" },
14 { start: 1, end: 2, label: "c" },
15 { start: 0, end: 2, label: "d" },
16 { start: 1, end: 5, label: "e" },
17 { start: 4, end: 8, label: "f" },
18 { start: 6, end: 7, label: "g" },
Akronf5dc5102017-05-16 20:32:57 +020019 ]
20 obj.maxArc = 200; // maximum height of the bezier control point
21 return obj;
22 },
23
24 _init : function (snippet) {
25 /*
26 var html = document.createElement("div");
27 html.innerHTML = snippet;
28 */
29 return this;
30 },
31
32 // This is a shorthand for SVG element creation
33 _c : function (tag) {
34 return document.createElementNS(svgNS, tag);
35 },
36
37 // Returns the center point of the requesting token
38 _tokenPoint : function (node) {
39 var box = node.getBoundingClientRect();
40 return box.x + (box.width / 2);
41 },
42
43 // Create an arc with a label
44 // Potentially needs a height parameter for stacks
Akron63ae00b2017-05-16 22:03:36 +020045 _drawArc : function (arc) {
Akronf5dc5102017-05-16 20:32:57 +020046
Akron63ae00b2017-05-16 22:03:36 +020047 var startPos = this._tokenPoint(this._tokenElements[arc.first]);
48 var endPos = this._tokenPoint(this._tokenElements[arc.last]);
Akronf5dc5102017-05-16 20:32:57 +020049
50 var y = 0;
51 var g = this._c("g");
52 var p = g.appendChild(this._c("path"));
53
54 // Create arc
55 var middle = Math.abs(endPos - startPos) / 2;
56
Akron63ae00b2017-05-16 22:03:36 +020057 // var cHeight = middle < this.maxArc ? middle : this.maxArc;
58 // TODO: take the number of tokens into account!
59 var cHeight = 10 + arc.overlaps * 12 + (middle / 2);
Akronf5dc5102017-05-16 20:32:57 +020060
61 var x = Math.min(startPos, endPos);
62
Akron63ae00b2017-05-16 22:03:36 +020063 var arcE = "M "+ startPos + " " + y +
Akronf5dc5102017-05-16 20:32:57 +020064 " C " + startPos + " " + (y-cHeight) +
65 " " + endPos + " " + (y-cHeight) +
66 " " + endPos + " " + y;
Akron63ae00b2017-05-16 22:03:36 +020067 p.setAttribute("d", arcE);
Akronf5dc5102017-05-16 20:32:57 +020068
Akron63ae00b2017-05-16 22:03:36 +020069 if (arc.label !== undefined) {
Akronf5dc5102017-05-16 20:32:57 +020070 var labelE = g.appendChild(this._c("text"));
71 labelE.setAttribute("x", x + middle);
Akron63ae00b2017-05-16 22:03:36 +020072 labelE.setAttribute("y", -1 * cHeight);
Akronf5dc5102017-05-16 20:32:57 +020073 labelE.setAttribute("text-anchor", "middle");
Akron63ae00b2017-05-16 22:03:36 +020074 labelE.appendChild(document.createTextNode(arc.label));
Akronf5dc5102017-05-16 20:32:57 +020075 };
76
77 return g;
78 },
79
80 element : function () {
81 if (this._element !== undefined)
82 return this._element;
83
84 // Create svg
85 var svg = this._c("svg");
86 svg.setAttribute("width", 700);
87 svg.setAttribute("height", 300);
88 this._element = svg;
89 return this._element;
90 },
91
92 // Add a relation with a start, an end,
93 // a direction value and a label text
Akron63ae00b2017-05-16 22:03:36 +020094 addArc : function (arc) {
Akronf5dc5102017-05-16 20:32:57 +020095 },
96
97 /*
98 * All arcs need to be sorted before shown,
99 * to avoid nesting.
100 */
101 _sortArcs : function () {
102
103 // 1. Sort by length
104 // 2. Tag all spans with the number of overlaps before
105 // a) Iterate over all spans
106 // b) check the latest preceeding overlapping span (lpos)
107 // -> not found: tag with 0
108 // -> found: Add +1 to the level of the (lpos)
109 // c) If the new tag is smaller than the previous element,
110 // reorder
Akron63ae00b2017-05-16 22:03:36 +0200111
112 // Normalize start and end
113 var sortedArcs = this._arcs.map(function (v) {
114 if (v.start < v.end) {
115 v.first = v.start;
116 v.last = v.end;
117 v.length = v.end - v.start;
118 }
119 else {
120 v.first = v.end;
121 v.last = v.start;
122 v.length = v.start - v.end;
123 };
124 return v;
125 });
126
127 // Sort based on length
128 sortedArcs.sort(function (a, b) {
129 if (a.length < b.length)
130 return -1;
131 else
132 return 1;
133 });
134
135 var arcStack = [];
136
137 // Iterate over all arc definitions
138 for (var i = 0; i < sortedArcs.length; i++) {
139 var currentArc = sortedArcs[i];
140
141 // Check the stack order
142 var overlaps = 0;
143
144 for (var j = (arcStack.length - 1); j >= 0; j--) {
145 var checkArc = arcStack[j];
146
147 // (a..(b..b)..a)
148 if (currentArc.first <= checkArc.first && currentArc.last >= checkArc.last) {
149 overlaps = checkArc.overlaps + 1;
150 break;
151 }
152
153 // (a..(b..a)..b)
154 else if (currentArc.first < checkArc.first && currentArc.last > checkArc.first) {
155 overlaps = checkArc.overlaps + (currentArc.length == checkArc.length ? 0 : 1);
156 }
157
158 // (b..(a..b)..a)
159 else if (currentArc.first < checkArc.last && currentArc.last > checkArc.last) {
160 overlaps = checkArc.overlaps + (currentArc.length == checkArc.length ? 0 : 1);
161 };
162 };
163
164 // Set overlaps
165 currentArc.overlaps = overlaps;
166
167 arcStack.push(currentArc);
168
169 // Although it is already sorted,
170 // the new item has to be put at the correct place
171 // TODO: Use something like splice() instead
172 arcStack.sort(function (a,b) {
173 b.overlaps - a.overlaps
174 });
175 };
176
177 return arcStack;
Akronf5dc5102017-05-16 20:32:57 +0200178 },
179
180 show : function () {
181 var svg = this._element;
182
183 /*
184 * Generate token list
185 */
186 var text = svg.appendChild(this._c("text"));
187 text.setAttribute("y", 135);
188 text.setAttribute("x", 160);
189
190 var lastRight = 0;
191 for (var node_i in this._tokens) {
192 // Append svg
193 var tspan = text.appendChild(this._c("tspan"));
194 tspan.appendChild(document.createTextNode(this._tokens[node_i]));
195
196 this._tokenElements.push(tspan);
197
198 // Add whitespace!
199 text.appendChild(document.createTextNode(" "));
200 };
201
202 this.arcs = svg.appendChild(this._c("g"));
203 this.arcs.classList.add("arcs");
204
205 var textBox = text.getBoundingClientRect();
206
207 this.arcs.setAttribute(
208 "transform",
209 "translate(0," + textBox.y +")"
210 );
Akron63ae00b2017-05-16 22:03:36 +0200211
Akronf5dc5102017-05-16 20:32:57 +0200212 /*
213 * TODO:
214 * Before creating the arcs, the height of the arc
215 * needs to be calculated to make it possible to "stack" arcs.
216 * That means, the arcs need to be presorted, so massively
217 * overlapping arcs are taken first.
218 */
Akron63ae00b2017-05-16 22:03:36 +0200219 var sortedArcs = this._sortArcs();
220 for (var i in sortedArcs) {
221 this.arcs.appendChild(this._drawArc(sortedArcs[i]));
Akronf5dc5102017-05-16 20:32:57 +0200222 };
223 }
224 }
225});