zotica 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ //
2
+
3
+
4
+ class AccentModifier extends Modifier {
5
+
6
+ modify(element) {
7
+ let baseWrapperElement = this.findChild(element, "math-basewrap");
8
+ let overElement = this.findChild(element, "math-over");
9
+ let contentElement = baseWrapperElement.children[0];
10
+ let parentElements = [baseWrapperElement.children[1], overElement];
11
+ for (let position of [0, 1]) {
12
+ let parentElement = parentElements[position];
13
+ if (parentElement) {
14
+ this.modifyPosition(contentElement, parentElement, position);
15
+ }
16
+ }
17
+ }
18
+
19
+ modifyPosition(contentElement, parentElement, position) {
20
+ let charElement = contentElement.children[0];
21
+ let string = charElement && charElement.textContent;
22
+ if (string && string.length == 1 && contentElement.children.length == 1) {
23
+ let char = string.substring(0, 1);
24
+ let shift = DATA["shift"][position][char];
25
+ if (shift) {
26
+ let marginString;
27
+ if (position == 0) {
28
+ marginString = window.getComputedStyle(parentElement).marginTop;
29
+ } else {
30
+ marginString = window.getComputedStyle(parentElement).marginBottom;
31
+ }
32
+ let margin = parseFloat(marginString) / this.getFontSize(parentElement);
33
+ if (position == 0) {
34
+ parentElement.style.marginTop = "" + (margin + shift) + "em";
35
+ } else {
36
+ parentElement.style.marginBottom = "" + (margin - shift) + "em";
37
+ }
38
+ }
39
+ }
40
+ }
41
+
42
+ }
@@ -0,0 +1,481 @@
1
+ //
2
+
3
+
4
+ const ANGLE_EPSILON = Math.PI / 90;
5
+
6
+ const UNIT = 1 / 18;
7
+ const ARROW_MARGIN = 8 * UNIT;
8
+ const LABEL_DISTANCE = 5 * UNIT;
9
+
10
+
11
+ class DiagramModifier extends Modifier {
12
+
13
+ modify(element) {
14
+ let arrowElements = this.findChildren(element, "math-arrow");
15
+ let cellElements = this.findChildren(element, "math-cellwrap").map((child) => child.children[0]);
16
+ let backgroundColor = this.getBackgroundColor(element);
17
+ let graphic = this.createGraphic(element);
18
+ element.appendChild(graphic);
19
+ for (let arrowElement of arrowElements) {
20
+ let arrowSpec = this.determineArrowSpec(graphic, arrowElement, cellElements, arrowElements);
21
+ let arrows = this.createArrows(arrowSpec, backgroundColor);
22
+ graphic.append(...arrows);
23
+ let labelPoint = this.determineLabelPoint(graphic, arrowElement, arrowSpec);
24
+ let fontRatio = this.getFontSize(graphic) / this.getFontSize(arrowElement);
25
+ arrowElement.style.left = "" + (labelPoint[0] * fontRatio) + "em";
26
+ arrowElement.style.top = "" + (labelPoint[1] * fontRatio) + "em";
27
+ }
28
+ let pathElements = Array.from(graphic.children).filter((child) => child.localName == "path");
29
+ let extrusion = this.calcExtrusion(graphic, arrowElements.concat(pathElements));
30
+ element.style.marginTop = "" + extrusion.top + "em";
31
+ element.style.marginBottom = "" + extrusion.bottom + "em";
32
+ element.style.marginLeft = "" + extrusion.left + "em";
33
+ element.style.marginRight = "" + extrusion.right + "em";
34
+ }
35
+
36
+ determineArrowSpec(graphic, arrowElement, cellElements, arrowElements) {
37
+ let spec = {};
38
+ let startConfigString = arrowElement.getAttribute("data-start");
39
+ let endConfigString = arrowElement.getAttribute("data-end");
40
+ let startConfig = this.parseEdgeConfig(startConfigString, graphic, cellElements, arrowElements);
41
+ let endConfig = this.parseEdgeConfig(endConfigString, graphic, cellElements, arrowElements);
42
+ if (startConfig && endConfig) {
43
+ let bendAngleString = arrowElement.getAttribute("data-bend");
44
+ if (bendAngleString) {
45
+ spec.bendAngle = parseFloat(bendAngleString) * Math.PI / 180;
46
+ }
47
+ let shiftString = arrowElement.getAttribute("data-shift");
48
+ if (shiftString) {
49
+ spec.shift = parseFloat(shiftString) * UNIT;
50
+ }
51
+ let startElement = startConfig.element;
52
+ let endElement = endConfig.element;
53
+ let startDimension = startConfig.dimension;
54
+ let endDimension = endConfig.dimension;
55
+ if (startConfig.point) {
56
+ spec.startPoint = startConfig.point;
57
+ } else {
58
+ spec.startPoint = this.calcEdgePoint(startDimension, endDimension, spec.bendAngle, spec.shift);
59
+ }
60
+ if (endConfig.point) {
61
+ spec.endPoint = endConfig.point;
62
+ } else {
63
+ spec.endPoint = this.calcEdgePoint(endDimension, startDimension, -spec.bendAngle, -spec.shift);
64
+ }
65
+ } else {
66
+ spec.startPoint = [0, 0];
67
+ spec.endPoint = [0, 0];
68
+ }
69
+ let labelPositionString = arrowElement.getAttribute("data-pos");
70
+ if (labelPositionString) {
71
+ spec.labelPosition = parseFloat(labelPositionString) / 100;
72
+ }
73
+ let lineCountString = arrowElement.getAttribute("data-line");
74
+ if (lineCountString) {
75
+ spec.lineCount = parseInt(lineCountString);
76
+ }
77
+ let dashed = !!arrowElement.getAttribute("data-dash");
78
+ if (dashed) {
79
+ spec.dashed = true;
80
+ }
81
+ let inverted = !!arrowElement.getAttribute("data-inv");
82
+ if (inverted) {
83
+ spec.inverted = true;
84
+ }
85
+ let mark = !!arrowElement.getAttribute("data-mark");
86
+ if (mark) {
87
+ spec.mark = true;
88
+ }
89
+ let tipKindsString = arrowElement.getAttribute("data-tip");
90
+ spec.tipKinds = this.parseTipKinds(tipKindsString, spec.lineCount);
91
+ spec.intrudedStartPoint = this.calcIntrudedPoint(spec.startPoint, spec.endPoint, spec.bendAngle, spec.tipKinds.start);
92
+ spec.intrudedEndPoint = this.calcIntrudedPoint(spec.endPoint, spec.startPoint, -spec.bendAngle, spec.tipKinds.end);
93
+ return spec;
94
+ }
95
+
96
+ determineLabelPoint(graphic, labelElement, arrowSpec) {
97
+ let labelDimension = this.calcDimension(graphic, labelElement);
98
+ let startPoint = arrowSpec.startPoint;
99
+ let endPoint = arrowSpec.endPoint;
100
+ let bendAngle = arrowSpec.bendAngle;
101
+ let position = (arrowSpec.labelPosition == undefined) ? 0.5 : arrowSpec.labelPosition;
102
+ let basePoint = [0, 0];
103
+ let angle = 0;
104
+ if (bendAngle != undefined) {
105
+ let controlPoint = this.calcControlPoint(startPoint, endPoint, bendAngle);
106
+ let basePointX = (1 - position) * (1 - position) * startPoint[0] + 2 * (1 - position) * position * controlPoint[0] + position * position * endPoint[0];
107
+ let basePointY = (1 - position) * (1 - position) * startPoint[1] + 2 * (1 - position) * position * controlPoint[1] + position * position * endPoint[1];
108
+ let speedX = -2 * (1 - position) * startPoint[0] + 2 * (1 - 2 * position) * controlPoint[0] + 2 * position * endPoint[0];
109
+ let speedY = -2 * (1 - position) * startPoint[1] + 2 * (1 - 2 * position) * controlPoint[1] + 2 * position * endPoint[1];
110
+ basePoint = [basePointX, basePointY];
111
+ angle = this.calcAngle([0, 0], [speedX, speedY]) + Math.PI / 2;
112
+ } else {
113
+ let basePointX = (1 - position) * startPoint[0] + position * endPoint[0];
114
+ let basePointY = (1 - position) * startPoint[1] + position * endPoint[1];
115
+ basePoint = [basePointX, basePointY];
116
+ angle = this.calcAngle(startPoint, endPoint) + Math.PI / 2;
117
+ }
118
+ if (arrowSpec.inverted) {
119
+ angle += Math.PI;
120
+ }
121
+ angle = this.normalizeAngle(angle);
122
+ let point;
123
+ if (arrowSpec.mark) {
124
+ let pointX = basePoint[0] + labelDimension.northWest[0] - labelDimension.center[0];
125
+ let pointY = basePoint[1] + labelDimension.northWest[0] - labelDimension.center[1];
126
+ point = [pointX, pointY];
127
+ } else {
128
+ point = this.calcLabelPoint(basePoint, labelDimension, angle, arrowSpec.lineCount);
129
+ }
130
+ return point;
131
+ }
132
+
133
+ calcEdgePoint(baseDimension, destinationDimension, bendAngle, shift) {
134
+ let margin = ARROW_MARGIN;
135
+ let angle = this.calcAngle(baseDimension.center, destinationDimension.center) + (bendAngle || 0);
136
+ let shiftAngle = angle + Math.PI / 2;
137
+ let southWestAngle = this.calcAngle(baseDimension.center, baseDimension.southWestMargined);
138
+ let southEastAngle = this.calcAngle(baseDimension.center, baseDimension.southEastMargined);
139
+ let northEastAngle = this.calcAngle(baseDimension.center, baseDimension.northEastMargined);
140
+ let northWestAngle = this.calcAngle(baseDimension.center, baseDimension.northWestMargined);
141
+ let x = 0;
142
+ let y = 0;
143
+ angle = this.normalizeAngle(angle);
144
+ shiftAngle = this.normalizeAngle(shiftAngle);
145
+ if (angle >= southWestAngle && angle <= southEastAngle) {
146
+ x = baseDimension.center[0] + (baseDimension.center[1] - baseDimension.southMargined[1]) / Math.tan(angle);
147
+ y = baseDimension.southMargined[1];
148
+ } else if (angle >= southEastAngle && angle <= northEastAngle) {
149
+ x = baseDimension.eastMargined[0];
150
+ y = baseDimension.center[1] + (baseDimension.center[0] - baseDimension.eastMargined[0]) * Math.tan(angle);
151
+ } else if (angle >= northEastAngle && angle <= northWestAngle) {
152
+ x = baseDimension.center[0] + (baseDimension.center[1] - baseDimension.northMargined[1]) / Math.tan(angle);
153
+ y = baseDimension.northMargined[1];
154
+ } else if (angle >= northWestAngle || angle <= southWestAngle) {
155
+ x = baseDimension.westMargined[0];
156
+ y = baseDimension.center[1] + (baseDimension.center[0] - baseDimension.westMargined[0]) * Math.tan(angle);
157
+ }
158
+ if (shift) {
159
+ x += Math.cos(shiftAngle) * shift;
160
+ y -= Math.sin(shiftAngle) * shift;
161
+ }
162
+ return [x, y];
163
+ }
164
+
165
+ calcIntrudedPoint(basePoint, destinationPoint, bendAngle, tipKind) {
166
+ if (tipKind != "none") {
167
+ let angle = this.calcAngle(basePoint, destinationPoint) + (bendAngle || 0);
168
+ let distance = DATA["arrow"][tipKind]["extrusion"];
169
+ angle = this.normalizeAngle(angle);
170
+ let intrudedPointX = basePoint[0] + distance * Math.cos(angle);
171
+ let intrudedPointY = basePoint[1] - distance * Math.sin(angle);
172
+ let intrudedPoint = [intrudedPointX, intrudedPointY];
173
+ return intrudedPoint;
174
+ } else {
175
+ return basePoint;
176
+ }
177
+ }
178
+
179
+ calcLabelPoint(basePoint, labelDimension, angle, lineCount) {
180
+ let distance = LABEL_DISTANCE + ((lineCount || 1) - 1) * 0.09;
181
+ let direction = "east";
182
+ if (angle <= -Math.PI + ANGLE_EPSILON) {
183
+ direction = "east";
184
+ } else if (angle <= -Math.PI / 2 - ANGLE_EPSILON) {
185
+ direction = "northEast";
186
+ } else if (angle <= -Math.PI / 2 + ANGLE_EPSILON) {
187
+ direction = "north";
188
+ } else if (angle <= -ANGLE_EPSILON) {
189
+ direction = "northWest";
190
+ } else if (angle <= ANGLE_EPSILON) {
191
+ direction = "west";
192
+ } else if (angle <= Math.PI / 2 - ANGLE_EPSILON) {
193
+ direction = "southWest";
194
+ } else if (angle <= Math.PI / 2 + ANGLE_EPSILON) {
195
+ direction = "south";
196
+ } else if (angle <= Math.PI - ANGLE_EPSILON) {
197
+ direction = "southEast";
198
+ } else {
199
+ direction = "east";
200
+ }
201
+ let x = basePoint[0] + Math.cos(angle) * distance + labelDimension.northWest[0] - labelDimension[direction][0];
202
+ let y = basePoint[1] - Math.sin(angle) * distance + labelDimension.northWest[1] - labelDimension[direction][1];
203
+ return [x, y];
204
+ }
205
+
206
+ parseEdgeConfig(string, graphic, cellElements, arrowElements) {
207
+ let config = null;
208
+ let match = string.match(/(?:(\d+)|([A-Za-z]\w*))(?:\:(\w+))?/);
209
+ if (match) {
210
+ let element = null;
211
+ if (match[1]) {
212
+ let number = parseInt(match[1]) - 1;
213
+ element = cellElements[number];
214
+ } else if (match[2]) {
215
+ let candidates = cellElements.map((candidate) => candidate.parentNode).concat(arrowElements);
216
+ let name = match[2];
217
+ element = candidates.find((candidate) => candidate.getAttribute("data-name") == name);
218
+ }
219
+ if (element) {
220
+ let dimension = this.calcDimension(graphic, element);
221
+ let point = null;
222
+ if (match[3]) {
223
+ point = this.parsePoint(match[3], dimension);
224
+ }
225
+ config = {element, dimension, point};
226
+ }
227
+ }
228
+ return config;
229
+ }
230
+
231
+ parsePoint(string, dimension) {
232
+ let point = null;
233
+ let match;
234
+ if (match = string.match(/^n(|w|e)|s(|w|e)|w|e|c$/)) {
235
+ if (string == "nw") {
236
+ point = dimension.northWestMargined;
237
+ } else if (string == "n") {
238
+ point = dimension.northMargined;
239
+ } else if (string == "ne") {
240
+ point = dimension.northEastMargined;
241
+ } else if (string == "e") {
242
+ point = dimension.eastMargined;
243
+ } else if (string == "se") {
244
+ point = dimension.southEastMargined;
245
+ } else if (string == "s") {
246
+ point = dimension.southMargined;
247
+ } else if (string == "sw") {
248
+ point = dimension.southWestMargined;
249
+ } else if (string == "w") {
250
+ point = dimension.westMargined;
251
+ } else if (string == "c") {
252
+ point = dimension.center;
253
+ }
254
+ } else if (match = string.match(/^(t|r|b|l)([\d.]+)$/)) {
255
+ let direction = match[1];
256
+ let position = parseFloat(match[2]) / 100;
257
+ let pointX = null;
258
+ let pointY = null;
259
+ if (direction == "t") {
260
+ pointX = (1 - position) * dimension.northWestMargined[0] + position * dimension.northEastMargined[0];
261
+ pointY = dimension.northMargined[1];
262
+ } else if (direction == "r") {
263
+ pointX = dimension.eastMargined[0];
264
+ pointY = (1 - position) * dimension.northEastMargined[1] + position * dimension.southEastMargined[1];
265
+ } else if (direction == "b") {
266
+ pointX = (1 - position) * dimension.southWestMargined[0] + position * dimension.southEastMargined[0];
267
+ pointY = dimension.southMargined[1];
268
+ } else if (direction == "l") {
269
+ pointX = dimension.westMargined[0];
270
+ pointY = (1 - position) * dimension.northWestMargined[1] + position * dimension.southWestMargined[1];
271
+ }
272
+ if (pointX != null && pointY != null) {
273
+ point = [pointX, pointY];
274
+ }
275
+ }
276
+ return point;
277
+ }
278
+
279
+ parseTipKinds(string, lineCount) {
280
+ let tipKinds = {start: "none", end: "normal"};
281
+ if (string != null) {
282
+ let specifiedTipKinds = string.split(/\s*,\s*/);
283
+ for (let specifiedTipKind of specifiedTipKinds) {
284
+ let spec = DATA["arrow"][specifiedTipKind];
285
+ if (spec) {
286
+ tipKinds[spec.edge] = specifiedTipKind;
287
+ }
288
+ if (specifiedTipKind == "none") {
289
+ tipKinds.end = "none";
290
+ }
291
+ }
292
+ }
293
+ if (lineCount == 2) {
294
+ if (tipKinds.start != "none") {
295
+ tipKinds.start = "d" + tipKinds.start;
296
+ }
297
+ if (tipKinds.end != "none") {
298
+ tipKinds.end = "d" + tipKinds.end;
299
+ }
300
+ } else if (lineCount == 3) {
301
+ if (tipKinds.start != "none") {
302
+ tipKinds.start = "t" + tipKinds.start;
303
+ }
304
+ if (tipKinds.end != "none") {
305
+ tipKinds.end = "t" + tipKinds.end;
306
+ }
307
+ }
308
+ return tipKinds;
309
+ }
310
+
311
+ calcAngle(basePoint, destinationPoint) {
312
+ let x = destinationPoint[0] - basePoint[0];
313
+ let y = destinationPoint[1] - basePoint[1];
314
+ let angle = -Math.atan2(y, x);
315
+ return angle;
316
+ }
317
+
318
+ normalizeAngle(angle) {
319
+ let normalizedAngle = (angle + Math.PI) % (Math.PI * 2) - Math.PI;
320
+ return normalizedAngle;
321
+ }
322
+
323
+ createArrows(arrowSpec, backgroundColor) {
324
+ let startPoint = arrowSpec.intrudedStartPoint;
325
+ let endPoint = arrowSpec.intrudedEndPoint;
326
+ let bendAngle = arrowSpec.bendAngle;
327
+ let lineCount = (arrowSpec.lineCount == undefined) ? 1 : arrowSpec.lineCount;
328
+ let command = "M " + startPoint[0] + " " + startPoint[1];
329
+ if (bendAngle != undefined) {
330
+ let controlPoint = this.calcControlPoint(startPoint, endPoint, bendAngle)
331
+ command += " Q " + controlPoint[0] + " " + controlPoint[1] + ", " + endPoint[0] + " " + endPoint[1];
332
+ } else {
333
+ command += " L " + endPoint[0] + " " + endPoint[1];
334
+ }
335
+ let arrows = [];
336
+ for (let i = 0 ; i < lineCount ; i ++) {
337
+ let arrow = this.createSvgElement("path");
338
+ arrow.setAttribute("d", command);
339
+ if (arrowSpec.tipKinds.start != "none" && i == lineCount - 1) {
340
+ arrow.setAttribute("marker-start", "url(#tip-" + arrowSpec.tipKinds.start +")");
341
+ }
342
+ if (arrowSpec.tipKinds.end != "none" && i == lineCount - 1) {
343
+ arrow.setAttribute("marker-end", "url(#tip-" + arrowSpec.tipKinds.end + ")");
344
+ }
345
+ if (arrowSpec.dashed && i % 2 == 0) {
346
+ arrow.classList.add("dashed");
347
+ }
348
+ if (i == 0) {
349
+ arrow.classList.add("base");
350
+ } else if (i == 1) {
351
+ arrow.classList.add("cover");
352
+ arrow.style.stroke = backgroundColor;
353
+ } else if (i == 2) {
354
+ arrow.classList.add("front");
355
+ }
356
+ if (lineCount == 2) {
357
+ arrow.classList.add("double");
358
+ } else if (lineCount == 3) {
359
+ arrow.classList.add("triple");
360
+ }
361
+ arrows.push(arrow);
362
+ }
363
+ return arrows;
364
+ }
365
+
366
+ calcControlPoint(startPoint, endPoint, bendAngle) {
367
+ let x = (endPoint[0] + startPoint[0] + (endPoint[1] - startPoint[1]) * Math.tan(bendAngle)) / 2;
368
+ let y = (endPoint[1] + startPoint[1] - (endPoint[0] - startPoint[0]) * Math.tan(bendAngle)) / 2;
369
+ return [x, y];
370
+ }
371
+
372
+ calcExtrusion(graphic, elements) {
373
+ let fontSize = this.getFontSize(graphic);
374
+ let xOffset = window.pageXOffset;
375
+ let yOffset = window.pageYOffset;
376
+ let graphicRect = graphic.getBoundingClientRect();
377
+ let graphicTop = graphicRect.top + yOffset
378
+ let graphicBottom = graphicRect.bottom + yOffset;
379
+ let graphicLeft = graphicRect.left + xOffset;
380
+ let graphicRight = graphicRect.right + xOffset;
381
+ let extrusion = {top: 0, bottom: 0, left: 0, right: 0};
382
+ for (let element of elements) {
383
+ let rect = element.getBoundingClientRect();
384
+ let topExtrusion = -(rect.top + yOffset - graphicTop) / fontSize;
385
+ let bottomExtrusion = (rect.bottom + yOffset - graphicBottom) / fontSize;
386
+ let leftExtrusion = -(rect.left + xOffset - graphicLeft) / fontSize;
387
+ let rightExtrusion = (rect.right + xOffset - graphicRight) / fontSize;
388
+ if (topExtrusion > extrusion.top) {
389
+ extrusion.top = topExtrusion;
390
+ }
391
+ if (bottomExtrusion > extrusion.bottom) {
392
+ extrusion.bottom = bottomExtrusion;
393
+ }
394
+ if (leftExtrusion > extrusion.left) {
395
+ extrusion.left = leftExtrusion;
396
+ }
397
+ if (rightExtrusion > extrusion.right) {
398
+ extrusion.right = rightExtrusion;
399
+ }
400
+ }
401
+ return extrusion;
402
+ }
403
+
404
+ createGraphic(element) {
405
+ let width = this.getWidth(element);
406
+ let height = this.getHeight(element);
407
+ let graphic = this.createSvgElement("svg");
408
+ graphic.setAttribute("viewBox", "0 0 " + width + " " + height);
409
+ let definitionElement = this.createSvgElement("defs");
410
+ let tipSpecKeys = Object.keys(DATA["arrow"]);
411
+ for (let tipSpecKey of tipSpecKeys) {
412
+ let tipSpec = DATA["arrow"][tipSpecKey];
413
+ let markerElement = this.createSvgElement("marker");
414
+ let markerPathElement = this.createSvgElement("path");
415
+ markerElement.setAttribute("id", "tip-" + tipSpecKey);
416
+ markerElement.setAttribute("refX", tipSpec["x"]);
417
+ markerElement.setAttribute("refY", tipSpec["y"]);
418
+ markerElement.setAttribute("markerWidth", tipSpec["width"]);
419
+ markerElement.setAttribute("markerHeight", tipSpec["height"]);
420
+ markerElement.setAttribute("markerUnits", "userSpaceOnUse");
421
+ markerElement.setAttribute("orient", "auto");
422
+ markerPathElement.setAttribute("d", tipSpec["command"]);
423
+ markerElement.appendChild(markerPathElement);
424
+ definitionElement.appendChild(markerElement);
425
+ }
426
+ graphic.appendChild(definitionElement);
427
+ return graphic;
428
+ }
429
+
430
+ createSvgElement(name) {
431
+ let element = document.createElementNS("http://www.w3.org/2000/svg", name);
432
+ return element;
433
+ }
434
+
435
+ calcDimension(graphic, element) {
436
+ let dimension = {};
437
+ let margin = ARROW_MARGIN;
438
+ let fontSize = this.getFontSize(graphic)
439
+ let graphicTop = graphic.getBoundingClientRect().top + window.pageYOffset;
440
+ let graphicLeft = graphic.getBoundingClientRect().left + window.pageXOffset;
441
+ let top = (element.getBoundingClientRect().top + window.pageYOffset - graphicTop) / fontSize;
442
+ let left = (element.getBoundingClientRect().left + window.pageXOffset - graphicLeft) / fontSize;
443
+ let width = this.getWidth(element, graphic);
444
+ let height = this.getHeight(element, graphic);
445
+ let lowerHeight = this.getLowerHeight(element, graphic);
446
+ dimension.northWest = [left, top];
447
+ dimension.north = [left + width / 2, top];
448
+ dimension.northEast = [left + width, top];
449
+ dimension.west = [left, top + height - lowerHeight];
450
+ dimension.center = [left + width / 2, top + height - lowerHeight];
451
+ dimension.east = [left + width, top + height - lowerHeight];
452
+ dimension.southWest = [left, top + height];
453
+ dimension.south = [left + width / 2, top + height];
454
+ dimension.southEast = [left + width, top + height];
455
+ dimension.northWestMargined = [left - margin, top - margin];
456
+ dimension.northMargined = [left + width / 2, top - margin];
457
+ dimension.northEastMargined = [left + width + margin, top - margin];
458
+ dimension.westMargined = [left - margin, top + height - lowerHeight];
459
+ dimension.centerMargined = [left + width / 2, top + height - lowerHeight];
460
+ dimension.eastMargined = [left + width + margin, top + height - lowerHeight];
461
+ dimension.southWestMargined = [left - margin, top + height + margin];
462
+ dimension.southMargined = [left + width / 2, top + height + margin];
463
+ dimension.southEastMargined = [left + width + margin, top + height + margin];
464
+ return dimension;
465
+ }
466
+
467
+ getBackgroundColor(element) {
468
+ let currentElement = element;
469
+ let color = "white";
470
+ while (currentElement && currentElement instanceof Element) {
471
+ let currentColor = window.getComputedStyle(currentElement).backgroundColor;
472
+ if (currentColor != "rgba(0, 0, 0, 0)" && currentColor != "transparent") {
473
+ color = currentColor;
474
+ break;
475
+ }
476
+ currentElement = currentElement.parentNode;
477
+ }
478
+ return color;
479
+ }
480
+
481
+ }