@mapcreator/sdk 0.0.9 → 0.0.10

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.
Files changed (120) hide show
  1. package/dist/{esm/HighlightManager.d.ts → HighlightManager.d.ts} +2 -2
  2. package/dist/{esm/MCMap.d.ts → MCMap.d.ts} +1 -1
  3. package/dist/{esm/PopupManager.d.ts → PopupManager.d.ts} +4 -4
  4. package/dist/{esm/Registry.d.ts → Registry.d.ts} +3 -3
  5. package/dist/{esm/adornments → adornments}/categoricalLegend.d.ts +1 -1
  6. package/dist/{esm/adornments → adornments}/connectedLegend.d.ts +2 -2
  7. package/dist/{esm/adornments → adornments}/customAdornment.d.ts +1 -1
  8. package/dist/{esm/adornments → adornments}/heading.d.ts +1 -1
  9. package/dist/adornments/insetMap.d.ts +3 -0
  10. package/dist/{esm/adornments → adornments}/manualLegend.d.ts +1 -1
  11. package/dist/adornments/northArrow.d.ts +3 -0
  12. package/dist/adornments/scalebar.d.ts +3 -0
  13. package/dist/{esm/constants → constants}/index.d.ts +2 -2
  14. package/dist/{esm/controls → controls}/fullscreenControls.d.ts +1 -1
  15. package/dist/{esm/controls → controls}/geocoderControl.d.ts +1 -1
  16. package/dist/{esm/controls → controls}/geolocationControls.d.ts +1 -1
  17. package/dist/controls/refreshMapControls.d.ts +3 -0
  18. package/dist/controls/webControls.d.ts +4 -0
  19. package/dist/{esm/controls → controls}/zoomControls.d.ts +1 -1
  20. package/dist/index.d.ts +3 -0
  21. package/dist/locales/da_DK/strings.json.d.ts +10 -0
  22. package/dist/locales/de_DE/strings.json.d.ts +10 -0
  23. package/dist/locales/en_GB/strings.json.d.ts +10 -0
  24. package/dist/locales/es_ES/strings.json.d.ts +10 -0
  25. package/dist/locales/fr_FR/strings.json.d.ts +10 -0
  26. package/dist/locales/it_IT/strings.json.d.ts +10 -0
  27. package/dist/locales/nl_NL/strings.json.d.ts +10 -0
  28. package/dist/mapcreator-sdk.js +39590 -0
  29. package/dist/models/area.d.ts +5 -0
  30. package/dist/models/circle.d.ts +5 -0
  31. package/dist/models/dot.d.ts +3 -0
  32. package/dist/models/line.d.ts +4 -0
  33. package/dist/models/marker.d.ts +5 -0
  34. package/dist/models/polygon.d.ts +5 -0
  35. package/dist/{esm/renderAdornments.d.ts → renderAdornments.d.ts} +3 -3
  36. package/dist/{esm/types → types}/index.d.ts +1 -1
  37. package/dist/{esm/types → types}/mapstyle.d.ts +2 -6
  38. package/dist/{esm/utils → utils}/choropleth.d.ts +3 -3
  39. package/dist/{esm/utils → utils}/geolocation.d.ts +1 -1
  40. package/dist/{esm/utils → utils}/graphhopper.d.ts +1 -1
  41. package/dist/{esm/utils → utils}/helpers.d.ts +2 -2
  42. package/dist/{esm/utils → utils}/language.d.ts +1 -1
  43. package/dist/{esm/utils → utils}/models.d.ts +4 -4
  44. package/dist/{esm/utils → utils}/overlays.d.ts +1 -1
  45. package/dist/{esm/utils → utils}/svgHelpers.d.ts +3 -4
  46. package/dist/{esm/utils → utils}/template.d.ts +2 -2
  47. package/dist/{esm/utils → utils}/youtube.d.ts +1 -1
  48. package/package.json +7 -6
  49. package/dist/esm/HighlightManager.js +0 -203
  50. package/dist/esm/MCMap.js +0 -254
  51. package/dist/esm/PopupManager.js +0 -297
  52. package/dist/esm/Registry.js +0 -74
  53. package/dist/esm/adornments/categoricalLegend.js +0 -141
  54. package/dist/esm/adornments/connectedLegend.js +0 -393
  55. package/dist/esm/adornments/customAdornment.js +0 -29
  56. package/dist/esm/adornments/heading.js +0 -71
  57. package/dist/esm/adornments/insetMap.d.ts +0 -3
  58. package/dist/esm/adornments/insetMap.js +0 -351
  59. package/dist/esm/adornments/manualLegend.js +0 -15
  60. package/dist/esm/adornments/northArrow.d.ts +0 -3
  61. package/dist/esm/adornments/northArrow.js +0 -24
  62. package/dist/esm/adornments/scalebar.d.ts +0 -3
  63. package/dist/esm/adornments/scalebar.js +0 -176
  64. package/dist/esm/constants/index.js +0 -53
  65. package/dist/esm/controls/controls.js +0 -7
  66. package/dist/esm/controls/fullscreenControls.js +0 -29
  67. package/dist/esm/controls/geocoderControl.js +0 -202
  68. package/dist/esm/controls/geolocationControls.js +0 -65
  69. package/dist/esm/controls/refreshMapControls.d.ts +0 -3
  70. package/dist/esm/controls/refreshMapControls.js +0 -26
  71. package/dist/esm/controls/webControls.d.ts +0 -4
  72. package/dist/esm/controls/webControls.js +0 -40
  73. package/dist/esm/controls/zoomControls.js +0 -23
  74. package/dist/esm/i18n.js +0 -21
  75. package/dist/esm/index.d.ts +0 -5
  76. package/dist/esm/index.js +0 -5
  77. package/dist/esm/locales/da_DK/strings.json +0 -7
  78. package/dist/esm/locales/de_DE/strings.json +0 -7
  79. package/dist/esm/locales/en_GB/strings.json +0 -7
  80. package/dist/esm/locales/es_ES/strings.json +0 -7
  81. package/dist/esm/locales/fr_FR/strings.json +0 -7
  82. package/dist/esm/locales/it_IT/strings.json +0 -7
  83. package/dist/esm/locales/nl_NL/strings.json +0 -7
  84. package/dist/esm/models/area.d.ts +0 -5
  85. package/dist/esm/models/area.js +0 -165
  86. package/dist/esm/models/circle.d.ts +0 -5
  87. package/dist/esm/models/circle.js +0 -110
  88. package/dist/esm/models/dot.d.ts +0 -3
  89. package/dist/esm/models/dot.js +0 -42
  90. package/dist/esm/models/line.d.ts +0 -4
  91. package/dist/esm/models/line.js +0 -117
  92. package/dist/esm/models/marker.d.ts +0 -5
  93. package/dist/esm/models/marker.js +0 -179
  94. package/dist/esm/models/polygon.d.ts +0 -5
  95. package/dist/esm/models/polygon.js +0 -80
  96. package/dist/esm/renderAdornments.js +0 -129
  97. package/dist/esm/types/geometry.js +0 -1
  98. package/dist/esm/types/index.js +0 -1
  99. package/dist/esm/types/jobObject.js +0 -1
  100. package/dist/esm/types/mapstyle.js +0 -1
  101. package/dist/esm/utils/browser.js +0 -6
  102. package/dist/esm/utils/choropleth.js +0 -110
  103. package/dist/esm/utils/fullscreen.js +0 -40
  104. package/dist/esm/utils/geolocation.js +0 -93
  105. package/dist/esm/utils/graphhopper.js +0 -41
  106. package/dist/esm/utils/helpers.js +0 -116
  107. package/dist/esm/utils/language.js +0 -170
  108. package/dist/esm/utils/models.js +0 -103
  109. package/dist/esm/utils/overlays.js +0 -87
  110. package/dist/esm/utils/scalebar.js +0 -52
  111. package/dist/esm/utils/svgHelpers.js +0 -1512
  112. package/dist/esm/utils/template.js +0 -120
  113. package/dist/esm/utils/youtube.js +0 -64
  114. /package/dist/{esm/controls → controls}/controls.d.ts +0 -0
  115. /package/dist/{esm/i18n.d.ts → i18n.d.ts} +0 -0
  116. /package/dist/{esm/types → types}/geometry.d.ts +0 -0
  117. /package/dist/{esm/types → types}/jobObject.d.ts +0 -0
  118. /package/dist/{esm/utils → utils}/browser.d.ts +0 -0
  119. /package/dist/{esm/utils → utils}/fullscreen.d.ts +0 -0
  120. /package/dist/{esm/utils → utils}/scalebar.d.ts +0 -0
@@ -1,1512 +0,0 @@
1
- import { bindingToPrimitive, bindingToString, clamp, fnv32b } from '@/utils/helpers';
2
- import { isFirefox } from '@/utils/browser';
3
- import 'path-data-polyfill';
4
- export function escapeXML(str) {
5
- return str
6
- .replace(/&/g, '&')
7
- .replace(/"/g, '"')
8
- .replace(/</g, '&lt;')
9
- .replace(/>/g, '&gt;');
10
- }
11
- export async function loadFonts(fontNames, vapiUrl, accessToken) {
12
- for (const fontName of fontNames) {
13
- try {
14
- const buffer = await fetch(`${vapiUrl}/fonts/${fontName}.woff2?access_token=${accessToken}`).then(response => response.arrayBuffer());
15
- document.fonts.add(new FontFace(fontName, buffer));
16
- }
17
- catch {
18
- //
19
- }
20
- }
21
- }
22
- function almostEqual(a, b, delta = 0.0001) {
23
- return Math.abs(a - b) < delta;
24
- }
25
- async function createImage(svgString, width, height, token) {
26
- if (isFirefox() || svgString.length < 2000000) {
27
- // If it's smaller than 2M
28
- try {
29
- // await for catch to work
30
- return { token, image: await createImageFast(svgString, width, height) };
31
- }
32
- catch (e) {
33
- // do nothing, fail-over to safe creation
34
- }
35
- }
36
- const blob = new Blob([svgString], { type: 'image/svg+xml' });
37
- return { token, image: await createImageSafe(blob, width, height) };
38
- }
39
- function createImageFast(svgString, width, height) {
40
- const image = new Image();
41
- image.width = width;
42
- image.height = height;
43
- return new Promise((resolve, reject) => {
44
- image.onload = () => resolve(image);
45
- image.onerror = () => reject(new Error('Failed to quickly generate image from svg'));
46
- image.src = `data:image/svg+xml;base64,${btoa(svgString)}`;
47
- });
48
- }
49
- async function createImageSafe(blob, width, height) {
50
- const image = new Image();
51
- image.width = width;
52
- image.height = height;
53
- const url = URL.createObjectURL(blob);
54
- try {
55
- await new Promise((resolve, reject) => {
56
- image.onload = () => resolve();
57
- image.onerror = () => reject(new Error('Failed to safely generate image from svg'));
58
- image.src = url;
59
- });
60
- }
61
- finally {
62
- URL.revokeObjectURL(url);
63
- }
64
- return image;
65
- }
66
- const interpolation = /\$\{([^}]+)}/g;
67
- const parser = new DOMParser();
68
- const serializer = new XMLSerializer();
69
- const svgCache = new Map();
70
- function getElement(svg) {
71
- let element = svgCache.get(svg);
72
- if (!element) {
73
- element = parser.parseFromString(svg, 'image/svg+xml')
74
- .documentElement;
75
- svgCache.set(svg, element);
76
- }
77
- return element;
78
- }
79
- function getSvg(element) {
80
- const svg = serializer.serializeToString(element);
81
- if (!svgCache.has(svg)) {
82
- svgCache.set(svg, element);
83
- }
84
- return svg;
85
- }
86
- export class SvgHelper {
87
- _seen = [];
88
- _svg = null;
89
- _element = null;
90
- constructor(svg) {
91
- this._svg = svg;
92
- }
93
- setElement() {
94
- this._element = getElement(this._svg).cloneNode(true);
95
- this._svg = null;
96
- return this._element;
97
- }
98
- getSvgString() {
99
- return this._svg ?? getSvg(this._element);
100
- }
101
- getSvgSize() {
102
- const element = this._element ?? getElement(this._svg);
103
- return [element.width.baseVal.value, element.height.baseVal.value];
104
- }
105
- getSvgColors() {
106
- const output = [];
107
- const seen = [];
108
- const element = this._element ?? getElement(this._svg);
109
- const nodeList = element.querySelectorAll('[mol_edit_style=true]');
110
- for (let i = 0; i < nodeList.length; ++i) {
111
- const node = nodeList[i];
112
- const name = node.id; // || node.firstElementChild?.id;
113
- if (name && seen.every(seenNode => seenNode.id !== name)) {
114
- const fill = node.getAttribute('fill')?.trim();
115
- if (fill) {
116
- const color = new Color(fill);
117
- if (color.badValue) {
118
- output.push({ name: `fill#${name}`, color: fill });
119
- }
120
- else {
121
- const { rgba } = color;
122
- const a = node.getAttribute('fill-opacity');
123
- rgba[3] *= a ? clamp(Number(a), 0, 1) : 1;
124
- output.push({ name: `fill#${name}`, color: Color.fromRGB(rgba) });
125
- }
126
- seen.push(node);
127
- }
128
- const stroke = node.getAttribute('stroke')?.trim();
129
- if (stroke) {
130
- const color = new Color(stroke);
131
- if (color.badValue) {
132
- output.push({ name: `stroke#${name}`, color: stroke });
133
- }
134
- else {
135
- const { rgba } = color;
136
- const a = node.getAttribute('stroke-opacity');
137
- rgba[3] *= a ? clamp(Number(a), 0, 1) : 1;
138
- output.push({ name: `stroke#${name}`, color: Color.fromRGB(rgba) });
139
- }
140
- seen.push(node);
141
- }
142
- }
143
- }
144
- return output;
145
- }
146
- setSvgColors(values) {
147
- const seen = [];
148
- const element = this._element ?? this.setElement();
149
- const nodeList = element.querySelectorAll('[mol_edit_style=true]');
150
- for (let i = 0; i < nodeList.length; ++i) {
151
- const node = nodeList[i];
152
- const name = node.id;
153
- if (name &&
154
- seen.every(seenNode => seenNode.id !== name) &&
155
- (node.getAttribute('fill')?.trim() || node.getAttribute('stroke')?.trim())) {
156
- const fill = values.find(value => value.name === `fill#${name}`);
157
- if (fill?.color) {
158
- const [r, g, b, a] = fill.color.rgba;
159
- node.setAttribute('fill', `rgb(${r},${g},${b})`);
160
- node.setAttribute(`fill-opacity`, String(a));
161
- }
162
- const stroke = values.find(value => value.name === `stroke#${name}`);
163
- if (stroke?.color) {
164
- const [r, g, b, a] = stroke.color.rgba;
165
- node.setAttribute('stroke', `rgb(${r},${g},${b})`);
166
- node.setAttribute(`stroke-opacity`, String(a));
167
- }
168
- seen.push(node);
169
- }
170
- }
171
- return this;
172
- }
173
- getSvgTexts() {
174
- const output = [];
175
- const element = this._element ?? getElement(this._svg);
176
- const nodeList = element.querySelectorAll('[mol_edit_text=true]');
177
- for (let i = 0; i < nodeList.length; ++i) {
178
- const node = nodeList[i];
179
- const name = node.id;
180
- if (name && output.every(svgText => svgText.name !== name)) {
181
- output.push({
182
- name,
183
- text: node.textContent || undefined,
184
- isMultiline: /^\s*true/i.test(node.getAttribute('mol_wrap') ?? 'false'),
185
- });
186
- }
187
- }
188
- return output;
189
- }
190
- setSvgTexts(values) {
191
- this._seen = [];
192
- const element = this._element ?? this.setElement();
193
- const nodeList = element.querySelectorAll('[mol_edit_text=true]');
194
- document.body.appendChild(element);
195
- try {
196
- const scale = element.width.baseVal.value / element.viewBox.baseVal.width;
197
- for (let i = 0; i < nodeList.length; ++i) {
198
- const node = nodeList[i];
199
- const name = node.id;
200
- if (name && this._seen.every(seenNode => seenNode.id !== name)) {
201
- const svgText = values.find(value => value.name === name);
202
- if (node.querySelector('textPath')) {
203
- if (svgText) {
204
- for (let j = 0; j < node.childNodes.length; ++j) {
205
- if (node.childNodes[j].nodeName === 'textPath') {
206
- node.childNodes[j].childNodes[0].nodeValue = svgText.text ?? '';
207
- }
208
- }
209
- }
210
- }
211
- else {
212
- this.wrapText(element, node, svgText && (svgText.text ?? ''));
213
- }
214
- this.nodeChanged(node); // this._seen.push(node)
215
- }
216
- }
217
- if (this._seen.length) {
218
- this.resetSize(scale);
219
- this.resetAnchor();
220
- }
221
- }
222
- finally {
223
- document.body.removeChild(element);
224
- }
225
- return this;
226
- }
227
- nodeChanged(node) {
228
- if (!node.id || this._seen.includes(node)) {
229
- return;
230
- }
231
- const element = this._element ?? this.setElement();
232
- const bbox = this.getBBox(node);
233
- let i = 0;
234
- let refs = element.querySelectorAll(`[mol_wrap~="${node.id}"]`) ?? [];
235
- for (i = 0; i < refs.length; ++i) {
236
- this.wrapText(element, refs[i]);
237
- }
238
- refs = element.querySelectorAll(`[mol_autoresizeid~="${node.id}"]`) ?? [];
239
- for (i = 0; i < refs.length; ++i) {
240
- this.autoResizeId(refs[i], bbox);
241
- }
242
- refs = element.querySelectorAll(`[mol_samewidth~="${node.id}"]`) ?? [];
243
- for (i = 0; i < refs.length; ++i) {
244
- this.sameWidth(refs[i], bbox);
245
- }
246
- refs = element.querySelectorAll(`[mol_sameheight~="${node.id}"]`) ?? [];
247
- for (i = 0; i < refs.length; ++i) {
248
- this.sameHeight(refs[i], bbox);
249
- }
250
- refs = element.querySelectorAll(`[mol_stack~="${node.id}"]`) ?? [];
251
- for (i = 0; i < refs.length; ++i) {
252
- this.stack(refs[i], bbox);
253
- }
254
- refs = element.querySelectorAll(`[mol_rectposition~="${node.id}"]`) ?? [];
255
- for (i = 0; i < refs.length; ++i) {
256
- this.rectPosition(refs[i], bbox);
257
- }
258
- refs = element.querySelectorAll(`[mol_pathposition~="${node.id}"]`) ?? [];
259
- for (i = 0; i < refs.length; ++i) {
260
- this.pathPosition(refs[i], node);
261
- }
262
- refs = element.querySelectorAll(`[mol_copyposition~="${node.id}"]`) ?? [];
263
- for (i = 0; i < refs.length; ++i) {
264
- this.copyPosition(refs[i], node);
265
- }
266
- this._seen.push(node);
267
- }
268
- autoResizeId(node, bbox) {
269
- const marginAttrib = node.attributes.mol_autoresizemargin?.value;
270
- let margins = [0, 0, 0, 0];
271
- if (marginAttrib) {
272
- const v = marginAttrib.split(' ').map((x) => parseFloat(x));
273
- if (v.length === 1) {
274
- margins = [v[0], v[0], v[0], v[0]];
275
- }
276
- else {
277
- margins = [v[0], v[1], v[2], v[3]];
278
- }
279
- }
280
- const changed = !(almostEqual(node.attributes.x.value, bbox.x - margins[0]) &&
281
- almostEqual(node.attributes.y.value, bbox.y - margins[2]) &&
282
- almostEqual(node.attributes.width.value, bbox.width + margins[0] + margins[1]) &&
283
- almostEqual(node.attributes.height.value, bbox.height + margins[2] + margins[3]));
284
- if (changed) {
285
- node.attributes.x.value = bbox.x - margins[0];
286
- node.attributes.y.value = bbox.y - margins[2];
287
- node.attributes.width.value = bbox.width + margins[0] + margins[1];
288
- node.attributes.height.value = bbox.height + margins[2] + margins[3];
289
- this.nodeChanged(node);
290
- }
291
- }
292
- sameWidth(node, bbox) {
293
- const width = bbox.width + 2 * parseFloat(node.attributes.mol_samewidth.value.split(' ')[1]);
294
- if (!almostEqual(node.attributes.width.value, width)) {
295
- node.attributes.width.value = width;
296
- this.nodeChanged(node);
297
- }
298
- }
299
- sameHeight(node, bbox) {
300
- const height = bbox.height + 2 * parseFloat(node.attributes.mol_sameheight.value.split(' ')[1]);
301
- if (!almostEqual(node.attributes.height.value, height)) {
302
- this._seen.push(node);
303
- node.attributes.height.value = height;
304
- this.nodeChanged(node);
305
- }
306
- }
307
- stack(node, bbox) {
308
- let changed = false;
309
- const side = node.attributes.mol_stack.value.split(' ')[0];
310
- switch (side) {
311
- case 'top':
312
- changed = !(almostEqual(node.attributes.width.value, bbox.width) &&
313
- almostEqual(node.attributes.x.value, bbox.x) &&
314
- almostEqual(node.attributes.y.value, bbox.y - node.attributes.height.value));
315
- if (changed) {
316
- node.attributes.width.value = bbox.width;
317
- node.attributes.x.value = bbox.x;
318
- node.attributes.y.value = bbox.y - node.attributes.height.value;
319
- }
320
- break;
321
- case 'bottom':
322
- changed = !(almostEqual(node.attributes.width.value, bbox.width) &&
323
- almostEqual(node.attributes.x.value, bbox.x) &&
324
- almostEqual(node.attributes.y.value, bbox.y + bbox.height));
325
- if (changed) {
326
- node.attributes.width.value = bbox.width;
327
- node.attributes.x.value = bbox.x;
328
- node.attributes.y.value = bbox.y + bbox.height;
329
- }
330
- break;
331
- case 'left':
332
- changed = !(almostEqual(node.attributes.height.value, bbox.height) &&
333
- almostEqual(node.attributes.x.value, bbox.x - node.attributes.width.value) &&
334
- almostEqual(node.attributes.y.value, bbox.y));
335
- if (changed) {
336
- node.attributes.height.value = bbox.height;
337
- node.attributes.x.value = bbox.x - node.attributes.width.value;
338
- node.attributes.y.value = bbox.y;
339
- }
340
- break;
341
- case 'right':
342
- changed = !(almostEqual(node.attributes.height.value, bbox.height) &&
343
- almostEqual(node.attributes.x.value, bbox.x + bbox.width) &&
344
- almostEqual(node.attributes.y.value, bbox.y));
345
- if (changed) {
346
- node.attributes.height.value = bbox.height;
347
- node.attributes.x.value = bbox.x + bbox.width;
348
- node.attributes.y.value = bbox.y;
349
- }
350
- break;
351
- }
352
- if (changed) {
353
- this.nodeChanged(node);
354
- }
355
- }
356
- rectPosition(node, bbox) {
357
- const side = node.attributes.mol_rectposition.value.split(' ')[1];
358
- const pos = node.attributes.mol_rectposition.value.split(' ')[2];
359
- const offset = parseFloat(node.attributes.mol_rectposition.value.split(' ')[3]) || 0;
360
- let svgCrd = { x: 0, y: 0 };
361
- switch (side) {
362
- case 'center':
363
- svgCrd = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 };
364
- break;
365
- case 'top':
366
- case 'bottom':
367
- svgCrd.x = bbox.x;
368
- switch (pos) {
369
- case 'start':
370
- svgCrd.x += offset;
371
- break;
372
- case 'middle':
373
- svgCrd.x += bbox.width / 2 + offset;
374
- break;
375
- case 'end':
376
- svgCrd.x += bbox.width - offset;
377
- break;
378
- }
379
- svgCrd.y = bbox.y;
380
- if (side === 'bottom') {
381
- svgCrd.y += bbox.height;
382
- }
383
- break;
384
- case 'left':
385
- case 'right':
386
- svgCrd.y = bbox.y;
387
- switch (pos) {
388
- case 'start':
389
- svgCrd.y += offset;
390
- break;
391
- case 'middle':
392
- svgCrd.y += bbox.height / 2 + offset;
393
- break;
394
- case 'end':
395
- svgCrd.y += bbox.height + offset;
396
- break;
397
- }
398
- svgCrd.x = bbox.x;
399
- if (side === 'right') {
400
- svgCrd.x += bbox.width;
401
- }
402
- break;
403
- }
404
- const oriCrd = this.getCoordinate(node, 0, 0);
405
- if (oriCrd && !(almostEqual(oriCrd.x, svgCrd.x) && almostEqual(oriCrd.y, svgCrd.y))) {
406
- this.moveElement(node, svgCrd);
407
- this.nodeChanged(node);
408
- }
409
- }
410
- pathPosition(nodeTo, nodeFrom) {
411
- const vars = nodeTo.attributes.mol_pathposition.value
412
- .split(' ')
413
- .map((x) => parseFloat(x));
414
- const toCrd = this.getCoordinate(nodeTo, 0, 0);
415
- const fromCrd = this.getCoordinate(nodeFrom, vars[1], vars[2]);
416
- if (fromCrd && toCrd && !(almostEqual(fromCrd.x, toCrd.x) && almostEqual(fromCrd.y, toCrd.y))) {
417
- this.moveElement(nodeTo, fromCrd);
418
- this.nodeChanged(nodeTo);
419
- }
420
- }
421
- copyPosition(nodeTo, nodeFrom) {
422
- const vars = nodeTo.attributes.mol_copyposition.value
423
- .split(' ')
424
- .map((x) => parseFloat(x));
425
- const toCrd = this.getCoordinate(nodeTo, vars[0], vars[1]);
426
- const fromCrd = this.getCoordinate(nodeFrom, vars[3], vars[4]);
427
- if (fromCrd && toCrd && !(almostEqual(fromCrd.x, toCrd.x) && almostEqual(fromCrd.y, toCrd.y))) {
428
- this.setCoordinate(nodeTo, fromCrd, vars[0], vars[1]);
429
- this.nodeChanged(nodeTo);
430
- }
431
- }
432
- resetSize(scale) {
433
- const element = this._element ?? this.setElement();
434
- // resize width and height
435
- const newBBox = this.getBBox(element);
436
- // add 10% of width and height for strokes and markers (they are not taken into account by getBBox / the DOM)
437
- newBBox.x -= 0.1 * newBBox.width;
438
- newBBox.y -= 0.1 * newBBox.height;
439
- newBBox.width *= 1.2;
440
- newBBox.height *= 1.2;
441
- element.setAttribute('viewBox', `${newBBox.x} ${newBBox.y} ${newBBox.width} ${newBBox.height}`);
442
- element.setAttribute('width', `${newBBox.width * scale}`);
443
- element.setAttribute('height', `${newBBox.height * scale}`);
444
- }
445
- resetAnchor() {
446
- const editAnchorElement = (this._element ?? getElement(this._svg)).querySelector('[mol_edit_anchor]');
447
- if (editAnchorElement) {
448
- const editAnchor = editAnchorElement.getAttribute('mol_edit_anchor');
449
- const anchorIndex = editAnchor.split(' ').map((x) => parseFloat(x));
450
- const anchorCrd = this.getCoordinate(editAnchorElement, anchorIndex[0], anchorIndex[1]);
451
- const element = this._element ?? this.setElement();
452
- // see if there is a scale group and scale the anchor accordingly (its outside the scaled svg)
453
- const scaleNode = element.getElementById('scale');
454
- if (scaleNode) {
455
- const transform = scaleNode.getAttribute('transform');
456
- const scaleRe = /scale\(([\d.]+)\)/i;
457
- const scale = (scaleRe.exec(transform) || ['', '1']).map(Number.parseFloat)[1];
458
- anchorCrd.x *= scale;
459
- anchorCrd.y *= scale;
460
- }
461
- element.setAttribute('mol_svganchor', `${anchorCrd.x} ${anchorCrd.y}`);
462
- }
463
- }
464
- getBBox(node) {
465
- // our own implementation to getBBox to make sure it returns what we want
466
- // Note that the DOM getBBox used below doesn't work in fireFox when display:none. Use visibility:hidden instead
467
- // see: https://github.com/susielu/d3-annotation/issues/25
468
- function textContentLocal(n) {
469
- // textContent concatenates the texts in the childNodes, and finds the largest string,
470
- // but we do not want leading and trailing empty textNodes to be added
471
- // We create them with spaces when we had leading or trailing \n
472
- // because otherwise they would not be measured in getBBox
473
- // This function create the text content but removes trailing 'empty' textSpans
474
- if (n.nodeName !== 'text') {
475
- return '';
476
- }
477
- let retVal = n.textContent;
478
- // remove leading empty tspans
479
- let i = 0;
480
- while (i < n.childNodes.length &&
481
- n.childNodes[i].nodeName === 'tspan' &&
482
- n.childNodes[i].textContent.match(/\S+/) === null) {
483
- retVal = retVal.slice(n.childNodes[i].textContent.length);
484
- i++;
485
- }
486
- // remove trailing empty tspans
487
- i = n.childNodes.length - 1;
488
- while (i >= 0 &&
489
- n.childNodes[i].nodeName === 'tspan' &&
490
- n.childNodes[i].textContent.match(/\S+/) === null) {
491
- retVal = retVal.slice(0, retVal.length - n.childNodes[i].textContent.length);
492
- i--;
493
- }
494
- // split on \n
495
- const lines = retVal.split('\n');
496
- // find the longest string (taking number of characters instead of actual character size)
497
- retVal = lines[0];
498
- for (i = 1; i < lines.length; ++i) {
499
- if (lines[i].length > retVal.length) {
500
- retVal = lines[i];
501
- }
502
- }
503
- return retVal;
504
- }
505
- let retVal = null;
506
- // measure the reference shape.
507
- const bBox = node.getBBox();
508
- // measures spaces for single line text
509
- if (node.nodeName === 'text') {
510
- const textContent = textContentLocal(node);
511
- let CTL = bBox.width;
512
- let x = bBox.x;
513
- const meat = textContent.match(/\S+.*\S+/); // meat of the string
514
- if (meat && meat.length > 0) {
515
- const lead = textContent.match(/^\s*/);
516
- if (lead.length > 0) {
517
- x -= (CTL / meat[0].length) * lead[0].length; // move x over for leading spaces
518
- }
519
- CTL += (CTL / meat[0].length) * (textContent.length - meat[0].length);
520
- }
521
- retVal = {
522
- width: CTL,
523
- height: bBox.height,
524
- x,
525
- y: bBox.y,
526
- descent: bBox.y + bBox.height - Number(node.getAttribute('y')),
527
- };
528
- }
529
- else {
530
- retVal = {
531
- x: bBox.x,
532
- y: bBox.y,
533
- width: Math.max(1, bBox.width),
534
- height: Math.max(1, bBox.height),
535
- };
536
- }
537
- return retVal;
538
- }
539
- /**
540
- * Convert the points in the path shape into an array of points
541
- * @param {Object} node of the shape to be used for this object
542
- * @return {Object} node Array of coordinates
543
- */
544
- nodeToPoints(node) {
545
- const pnts = [];
546
- let p;
547
- let len;
548
- let i;
549
- if (node.points.length) {
550
- len = node.points.length;
551
- }
552
- else {
553
- len = node.points.numberOfItems;
554
- }
555
- for (i = 0; i < len; ++i) {
556
- p = node.points.getItem(i);
557
- pnts.push(p);
558
- }
559
- return pnts;
560
- }
561
- /**
562
- * Convert the points in the given array into a path string
563
- * @param {Object} pnts Node of the shape to be used for this object
564
- * @return {String} String of the SVG path
565
- */
566
- pointsToString(pnts) {
567
- let val = '';
568
- let i;
569
- for (i = 0; i < pnts.length; ++i) {
570
- val = `${val} ${pnts[i].x.toString()}, ${pnts[i].y.toString()}`;
571
- }
572
- return val;
573
- }
574
- getCoordinate(node, iCrd, iSeg) {
575
- // MULTIPLE RETURN POINTS!
576
- const svgCrd = { x: 0, y: 0 };
577
- let pnts = [];
578
- let pathData = [];
579
- switch (node.nodeName) {
580
- case 'rect':
581
- if (iCrd === 0) {
582
- svgCrd.x = parseFloat(node.getAttribute('x'));
583
- svgCrd.y = parseFloat(node.getAttribute('y'));
584
- return svgCrd;
585
- }
586
- if (iCrd === 1) {
587
- svgCrd.x = parseFloat(node.getAttribute('x')) + parseFloat(node.getAttribute('width'));
588
- svgCrd.y = parseFloat(node.getAttribute('y')) + parseFloat(node.getAttribute('height'));
589
- return svgCrd;
590
- }
591
- break;
592
- case 'circle':
593
- if (iCrd === 0) {
594
- svgCrd.x = parseFloat(node.getAttribute('cx'));
595
- svgCrd.y = parseFloat(node.getAttribute('cy'));
596
- return svgCrd;
597
- }
598
- if (iCrd === 1) {
599
- svgCrd.x = parseFloat(node.getAttribute('cx')) + parseFloat(node.getAttribute('r'));
600
- svgCrd.y = parseFloat(node.getAttribute('cy'));
601
- return svgCrd;
602
- }
603
- break;
604
- case 'ellipse':
605
- if (iCrd === 0) {
606
- svgCrd.x = parseFloat(node.getAttribute('cx'));
607
- svgCrd.y = parseFloat(node.getAttribute('cy'));
608
- return svgCrd;
609
- }
610
- if (iCrd === 1) {
611
- svgCrd.x = parseFloat(node.getAttribute('cx')) + parseFloat(node.getAttribute('rx'));
612
- svgCrd.y = parseFloat(node.getAttribute('cy'));
613
- return svgCrd;
614
- }
615
- if (iCrd === 2) {
616
- svgCrd.x = parseFloat(node.getAttribute('cx'));
617
- svgCrd.y = parseFloat(node.getAttribute('cy')) - parseFloat(node.getAttribute('ry'));
618
- return svgCrd;
619
- }
620
- break;
621
- case 'line':
622
- if (iCrd === 0) {
623
- svgCrd.x = parseFloat(node.getAttribute('x1'));
624
- svgCrd.y = parseFloat(node.getAttribute('y1'));
625
- return svgCrd;
626
- }
627
- if (iCrd === 1) {
628
- svgCrd.x = parseFloat(node.getAttribute('x2'));
629
- svgCrd.y = parseFloat(node.getAttribute('y2'));
630
- return svgCrd;
631
- }
632
- break;
633
- case 'polyline':
634
- pnts = this.nodeToPoints(node);
635
- if (iCrd < pnts.length) {
636
- svgCrd.x = pnts[iCrd].x;
637
- svgCrd.y = pnts[iCrd].y;
638
- return svgCrd;
639
- }
640
- break;
641
- case 'polygon':
642
- pnts = this.nodeToPoints(node);
643
- if (iCrd < pnts.length) {
644
- svgCrd.x = pnts[iCrd].x;
645
- svgCrd.y = pnts[iCrd].y;
646
- return svgCrd;
647
- }
648
- break;
649
- case 'path':
650
- pathData = node.getPathData({ normalize: true });
651
- switch (pathData[iSeg].type) {
652
- case 'M':
653
- switch (iCrd) {
654
- case 0:
655
- svgCrd.x = pathData[iSeg].values[0];
656
- svgCrd.y = pathData[iSeg].values[1];
657
- return svgCrd;
658
- }
659
- break;
660
- case 'L':
661
- switch (iCrd) {
662
- case 0:
663
- svgCrd.x = pathData[iSeg].values[0];
664
- svgCrd.y = pathData[iSeg].values[1];
665
- return svgCrd;
666
- }
667
- break;
668
- }
669
- break;
670
- }
671
- return null;
672
- }
673
- setCoordinate(node, svgCrd, iCrd, iSeg) {
674
- // todo fix this function and its calls
675
- // because sometimes it's called with parameters
676
- // that will throw error or do nothing
677
- let val = '';
678
- let pnts = [];
679
- let pathData = [];
680
- let v = 0;
681
- let a = 0;
682
- let b = 0;
683
- switch (node.nodeName) {
684
- case 'rect':
685
- if (iCrd === 0) {
686
- node.setAttribute('x', svgCrd.x.toString());
687
- node.setAttribute('y', svgCrd.y.toString());
688
- }
689
- if (iCrd === 1) {
690
- v = svgCrd.x - Number(node.getAttribute('x'));
691
- if (v >= 0) {
692
- node.setAttribute('width', v.toString());
693
- }
694
- else {
695
- // move x down to -v
696
- a = Number(node.getAttribute('x')) + v;
697
- node.setAttribute('x', a.toString());
698
- node.setAttribute('width', '0');
699
- }
700
- v = svgCrd.y - Number(node.getAttribute('y'));
701
- if (v >= 0) {
702
- node.setAttribute('height', v.toString());
703
- }
704
- else {
705
- // move y up to -v
706
- a = Number(node.getAttribute('y')) + v;
707
- node.setAttribute('y', a.toString());
708
- node.setAttribute('height', '0');
709
- }
710
- }
711
- break;
712
- case 'circle':
713
- if (iCrd === 0) {
714
- node.setAttribute('cx', svgCrd.x.toString());
715
- node.setAttribute('cy', svgCrd.y.toString());
716
- }
717
- if (iCrd === 1) {
718
- a = parseFloat(node.getAttribute('cx')) - svgCrd.x;
719
- b = parseFloat(node.getAttribute('cy')) - svgCrd.y;
720
- v = Math.sqrt(a * a + b * b);
721
- node.setAttribute('r', v.toString());
722
- }
723
- break;
724
- case 'ellipse':
725
- if (iCrd === 0) {
726
- node.setAttribute('cx', svgCrd.x.toString());
727
- node.setAttribute('cy', svgCrd.y.toString());
728
- }
729
- if (iCrd === 1) {
730
- v = Math.abs(parseFloat(node.getAttribute('cx')) - svgCrd.x);
731
- node.setAttribute('rx', v.toString());
732
- }
733
- if (iCrd === 2) {
734
- v = Math.abs(parseFloat(node.getAttribute('cy')) - svgCrd.y);
735
- node.setAttribute('ry', v.toString());
736
- }
737
- break;
738
- case 'line':
739
- if (iCrd === 0) {
740
- node.setAttribute('x1', svgCrd.x.toString());
741
- node.setAttribute('y2', svgCrd.y.toString());
742
- }
743
- if (iCrd === 1) {
744
- node.setAttribute('x2', svgCrd.x.toString());
745
- node.setAttribute('y2', svgCrd.y.toString());
746
- }
747
- break;
748
- case 'polyline':
749
- pnts = this.nodeToPoints(node);
750
- if (iCrd < pnts.length) {
751
- pnts[iCrd].x = svgCrd.x;
752
- pnts[iCrd].y = svgCrd.y;
753
- val = this.pointsToString(pnts);
754
- node.setAttribute('points', val);
755
- }
756
- break;
757
- case 'polygon':
758
- pnts = this.nodeToPoints(node);
759
- if (iCrd < pnts.length) {
760
- pnts[iCrd].x = svgCrd.x;
761
- pnts[iCrd].y = svgCrd.y;
762
- val = this.pointsToString(pnts);
763
- node.setAttribute('points', val);
764
- }
765
- break;
766
- case 'path':
767
- if (!iSeg) {
768
- break;
769
- }
770
- pathData = node.getPathData({ normalize: true });
771
- switch (pathData[iSeg].type) {
772
- case 'M':
773
- pathData[iSeg].values[0] = svgCrd.x;
774
- pathData[iSeg].values[1] = svgCrd.y;
775
- break;
776
- case 'L':
777
- pathData[iSeg].values[0] = svgCrd.x;
778
- pathData[iSeg].values[1] = svgCrd.y;
779
- break;
780
- }
781
- node.setPathData(pathData);
782
- break;
783
- }
784
- }
785
- moveElement(node, svgCrd) {
786
- let svgOriCrd = { x: 0, y: 0 };
787
- const vector = { x: 0, y: 0 };
788
- let j = 0;
789
- let changed = false;
790
- let pathData = [];
791
- switch (node.nodeName) {
792
- case 'rect':
793
- case 'circle':
794
- case 'ellipse':
795
- svgOriCrd = this.getCoordinate(node, 0, 0);
796
- changed = !almostEqual(svgOriCrd.x, svgCrd.x) || !almostEqual(svgOriCrd.y, svgCrd.y);
797
- if (changed) {
798
- this.setCoordinate(node, svgCrd, 0, 0);
799
- }
800
- break;
801
- case 'polyline':
802
- case 'polygon':
803
- svgOriCrd = this.getCoordinate(node, 0, 0);
804
- vector.x = svgCrd.x - svgOriCrd.x;
805
- vector.y = svgCrd.y - svgOriCrd.y;
806
- changed = !almostEqual(vector.x, 0) || !almostEqual(vector.y, 0);
807
- if (changed) {
808
- this.setCoordinate(node, svgCrd, 0, 0);
809
- // loop through all coords and move them
810
- j = 1;
811
- svgOriCrd = this.getCoordinate(node, j, 0);
812
- while (svgOriCrd !== null) {
813
- svgOriCrd.x += vector.x;
814
- svgOriCrd.y += vector.y;
815
- this.setCoordinate(svgOriCrd, j, 0, undefined);
816
- // next
817
- j++;
818
- svgOriCrd = this.getCoordinate(node, j, 0);
819
- }
820
- }
821
- break;
822
- case 'path':
823
- svgOriCrd = this.getCoordinate(node, 0, 0);
824
- vector.x = svgCrd.x - svgOriCrd.x;
825
- vector.y = svgCrd.y - svgOriCrd.y;
826
- changed = !almostEqual(vector.x, 0) || !almostEqual(vector.y, 0);
827
- if (changed) {
828
- // loop through all coords and move them
829
- pathData = node.getPathData({ normalize: true });
830
- for (let i = 0; i < pathData.length; ++i) {
831
- const path = pathData[i];
832
- if (path?.type === 'M' || path?.type === 'L') {
833
- path.values[0] += vector.x;
834
- path.values[1] += vector.y;
835
- }
836
- }
837
- node.setPathData(pathData);
838
- }
839
- break;
840
- }
841
- return changed;
842
- }
843
- /**
844
- * TODO Needs to be made DRY
845
- */
846
- wrapText(element, textNode, text) {
847
- let words = [];
848
- let wordsInThisLine = 0;
849
- let i;
850
- let cumulY = 0;
851
- let line = '';
852
- let newLine = '';
853
- let myTextNode;
854
- let tspanEl;
855
- let tspanElId;
856
- let id = 0;
857
- let idString = '';
858
- let computedTextLength;
859
- const svgNS = 'http://www.w3.org/2000/svg';
860
- // const gatherText = text === undefined;
861
- text = text ?? textNode.textContent;
862
- while (textNode.hasChildNodes()) {
863
- textNode.removeChild(textNode.firstChild);
864
- }
865
- // find the wrap parameters
866
- // add a dummy textnode for measurement
867
- const temp = document.createTextNode('gh');
868
- textNode.appendChild(temp);
869
- const ytn = textNode.getAttribute('y');
870
- // measure the reference shape
871
- let bBox = null;
872
- let wrapId = '';
873
- let attrs = [];
874
- let wrapMargin = 0;
875
- let lineDist = 0;
876
- let newlineOnly = false;
877
- if (textNode.hasAttribute('mol_wrap')) {
878
- attrs = textNode.getAttribute('mol_wrap').split(' ', 4);
879
- if (attrs[0] === 'true') {
880
- wrapId = attrs[1];
881
- if (wrapId === 'newlineOnly') {
882
- newlineOnly = true;
883
- wrapId = '';
884
- wrapMargin = 0;
885
- }
886
- else {
887
- wrapMargin = parseFloat(attrs[2]);
888
- }
889
- lineDist = attrs.length >= 4 ? parseFloat(attrs[3]) : wrapMargin;
890
- }
891
- }
892
- if (wrapId) {
893
- const node = element.getElementById(wrapId);
894
- bBox = this.getBBox(node || textNode);
895
- }
896
- else {
897
- bBox = this.getBBox(textNode);
898
- }
899
- let x = bBox.x;
900
- let y = bBox.y;
901
- const textWidth = bBox.width;
902
- let maxWidth = 10000;
903
- if (wrapId !== '' && !newlineOnly) {
904
- maxWidth = textWidth - 2 * wrapMargin;
905
- }
906
- // measure the text
907
- bBox = this.getBBox(textNode);
908
- let textHeight = bBox.height;
909
- if (textNode.hasAttribute('font-size')) {
910
- textHeight = parseFloat(textNode.getAttribute('font-size'));
911
- }
912
- const descent = bBox.y + bBox.height - ytn;
913
- // remove the dummy textnode
914
- textNode.removeChild(temp);
915
- // update x, y for margin and text-anchor
916
- // (x, y) is the lower left corner of the first line
917
- // start
918
- x += wrapMargin;
919
- y += wrapMargin + bBox.height;
920
- // y = y + lineDist + bBox.height;
921
- if (textNode.hasAttribute('text-anchor')) {
922
- switch (textNode.getAttribute('text-anchor')) {
923
- case 'end':
924
- x = x - wrapMargin + textWidth - wrapMargin;
925
- break;
926
- case 'middle':
927
- x = x - wrapMargin + textWidth / 2;
928
- break;
929
- }
930
- }
931
- idString = textNode.getAttribute('id');
932
- textNode.setAttribute('x', x.toString());
933
- textNode.setAttribute('y', (y - descent).toString());
934
- // split the text at all spaces and dashes
935
- // also split after a newline, but keep the newline
936
- // also keep the -
937
- if (text) {
938
- words = text
939
- .replace(/\n/g, '\n§')
940
- .replace(/-/g, '-§')
941
- .replace(/ /g, ' §')
942
- .replace(/\t/g, ' §')
943
- .split('§');
944
- }
945
- // create first textSpan
946
- const baseY = parseFloat(textNode.attributes.y.value);
947
- tspanEl = document.createElementNS(svgNS, 'tspan');
948
- tspanEl.setAttributeNS(null, 'x', String(x));
949
- tspanEl.setAttributeNS(null, 'y', String(baseY + cumulY));
950
- tspanElId = idString + id.toString();
951
- tspanEl.setAttribute('id', tspanElId);
952
- id++;
953
- line = '';
954
- wordsInThisLine = 0;
955
- myTextNode = document.createTextNode(line);
956
- tspanEl.appendChild(myTextNode);
957
- textNode.appendChild(tspanEl);
958
- cumulY += textHeight;
959
- i = 0;
960
- while (i < words.length) {
961
- // see if word fits behind current line
962
- newLine = line + words[i];
963
- wordsInThisLine++;
964
- myTextNode.nodeValue = newLine;
965
- computedTextLength = element.querySelector(`#${tspanElId}`).getComputedTextLength();
966
- // so what is the situation?
967
- if (computedTextLength < maxWidth) {
968
- // word fits perfectly
969
- line = newLine;
970
- // but if it ends in a newline-char and is not the last word, create new tspan
971
- if (line.length > 0 && line[line.length - 1] === '\n' && i < words.length - 1) {
972
- // if (!line.match(/\S+/)) {
973
- // line = `§${line}`;
974
- // tspanEl.setAttribute('visibility', 'hidden'); // empty line fix (dummy content and hidden)
975
- // }
976
- myTextNode.nodeValue = line;
977
- // create new tspan
978
- tspanEl = document.createElementNS(svgNS, 'tspan');
979
- tspanEl.setAttributeNS(null, 'x', String(x));
980
- tspanEl.setAttributeNS(null, 'y', String(baseY + cumulY));
981
- tspanElId = idString + id.toString();
982
- tspanEl.setAttribute('id', tspanElId);
983
- cumulY += textHeight + lineDist;
984
- id++;
985
- line = '';
986
- wordsInThisLine = 0;
987
- myTextNode = document.createTextNode(line);
988
- tspanEl.appendChild(myTextNode);
989
- textNode.appendChild(tspanEl);
990
- }
991
- // move on
992
- i++;
993
- }
994
- else {
995
- // so, line too long or newline
996
- if (wordsInThisLine > 1) {
997
- // if word is not the only word, do not add it
998
- myTextNode.nodeValue = line;
999
- }
1000
- else {
1001
- // if word is the only word, leave it and move on
1002
- line = newLine;
1003
- i++;
1004
- }
1005
- if (line.length > 0 && line[line.length - 1] === '\n') {
1006
- // remove the newline-char and space
1007
- line = line.substr(0, line.length - 1);
1008
- myTextNode.nodeValue = line;
1009
- }
1010
- // create new tspan
1011
- tspanEl = document.createElementNS(svgNS, 'tspan');
1012
- tspanEl.setAttributeNS(null, 'x', String(x));
1013
- tspanEl.setAttributeNS(null, 'y', String(baseY + cumulY));
1014
- tspanElId = idString + id.toString();
1015
- tspanEl.setAttribute('id', tspanElId);
1016
- cumulY += textHeight + lineDist;
1017
- id++;
1018
- line = '';
1019
- wordsInThisLine = 0;
1020
- myTextNode = document.createTextNode(line);
1021
- tspanEl.appendChild(myTextNode);
1022
- textNode.appendChild(tspanEl);
1023
- }
1024
- }
1025
- // If we only have one line, we don’t need tspans at all, so instead we just
1026
- // put the whole text directly into the textNode. This also fixes MC-991.
1027
- if (textNode.childElementCount === 1) {
1028
- textNode.firstElementChild.replaceWith(textNode.firstElementChild.textContent);
1029
- }
1030
- // return the cumulY
1031
- return cumulY;
1032
- }
1033
- }
1034
- export class SvgCache {
1035
- map;
1036
- keyCache = new Map();
1037
- constructor(map) {
1038
- this.map = map;
1039
- }
1040
- getMapLibreImageKey(svg, pixelRatio) {
1041
- let subCache = this.keyCache.get(pixelRatio);
1042
- if (!subCache) {
1043
- subCache = new Map();
1044
- this.keyCache.set(pixelRatio, subCache);
1045
- }
1046
- let mapLibreKey = subCache.get(svg);
1047
- if (!mapLibreKey) {
1048
- const key = svg + pixelRatio;
1049
- const size = new SvgHelper(svg).getSvgSize();
1050
- const scaledWidth = Math.max(size[0] * pixelRatio, 1);
1051
- const scaledHeight = Math.max(size[1] * pixelRatio, 1);
1052
- mapLibreKey = `mc-image-${fnv32b(key)}`;
1053
- createImage(svg, scaledWidth, scaledHeight, mapLibreKey)
1054
- .then(({ token, image }) => this.map.addImage(token, image, { pixelRatio }))
1055
- .catch(error => console.log(error));
1056
- subCache.set(svg, mapLibreKey);
1057
- }
1058
- return mapLibreKey;
1059
- }
1060
- }
1061
- let measureFontSize = 0;
1062
- let measureFontFamily = '';
1063
- const mcx = document.createElement('canvas').getContext('2d');
1064
- export function measureTextBlock(lines, fontFamily, fontSize) {
1065
- let width = 0;
1066
- let height = 0;
1067
- let ascent = 0;
1068
- let descent = 0;
1069
- if (mcx) {
1070
- if (measureFontSize !== fontSize || measureFontFamily !== fontFamily) {
1071
- mcx.font = `${fontSize}px ${fontFamily}`;
1072
- measureFontFamily = fontFamily;
1073
- measureFontSize = fontSize;
1074
- }
1075
- ({ actualBoundingBoxAscent: ascent, actualBoundingBoxDescent: descent } = mcx.measureText('█'));
1076
- for (let i = 0; i < lines.length; ++i) {
1077
- const { actualBoundingBoxLeft, actualBoundingBoxRight } = mcx.measureText(lines[i]);
1078
- if (actualBoundingBoxLeft + actualBoundingBoxRight > width) {
1079
- width = actualBoundingBoxLeft + actualBoundingBoxRight;
1080
- }
1081
- }
1082
- height = (ascent + descent) * lines.length;
1083
- }
1084
- return { width, height, ascent, descent };
1085
- }
1086
- export function getColorValue(dataBindings, modelOrigValue, groupOrigValue, colorGenerator, unmatchedColor) {
1087
- const origValue = groupOrigValue ?? modelOrigValue;
1088
- const doRawValue = !colorGenerator;
1089
- const columnName = origValue?.match(/\$\{([^}]+)}/)?.[1];
1090
- const columnValue = columnName
1091
- ? doRawValue
1092
- ? bindingToString(dataBindings[columnName])
1093
- : bindingToPrimitive(dataBindings[columnName])
1094
- : null;
1095
- const value = columnName
1096
- ? doRawValue
1097
- ? columnValue
1098
- : typeof columnValue !== 'boolean' && columnValue !== null && columnValue !== ''
1099
- ? colorGenerator(columnValue)
1100
- : unmatchedColor
1101
- : origValue;
1102
- return value && !new Color(value).badValue ? value : '#000000';
1103
- }
1104
- export function getTextValue(dataBindings, modelOrigValue, groupOrigValue) {
1105
- return ((groupOrigValue ?? modelOrigValue)
1106
- ?.split(interpolation)
1107
- .map((part, index) => (index & 1 ? bindingToString(dataBindings[part]) : part))
1108
- .join('') || undefined);
1109
- }
1110
- export function getSvgValue(dataBindings, modelOrigValue, groupOrigValue) {
1111
- let value = modelOrigValue;
1112
- if (modelOrigValue) {
1113
- const svgHelper = new SvgHelper(modelOrigValue);
1114
- const svgColors = svgHelper.getSvgColors();
1115
- const svgTexts = svgHelper.getSvgTexts();
1116
- const colorsOverride = groupOrigValue
1117
- ? new Map(new SvgHelper(groupOrigValue).getSvgColors().map(({ name, color }) => [name, color]))
1118
- : new Map();
1119
- const textsOverride = groupOrigValue
1120
- ? new Map(new SvgHelper(groupOrigValue).getSvgTexts().map(({ name, text }) => [name, text]))
1121
- : new Map();
1122
- const colorValues = svgColors
1123
- .map(({ name, color }) => ({
1124
- name,
1125
- color: new Color(getColorValue(dataBindings, color?.toString(), colorsOverride.get(name)?.toString())),
1126
- }))
1127
- .filter((value, index) => typeof svgColors[index].color === 'string' ||
1128
- !value.color.sameColor(svgColors[index].color));
1129
- const textValues = svgTexts
1130
- .map(({ name, text }) => ({
1131
- name,
1132
- text: getTextValue(dataBindings, text, textsOverride.get(name)),
1133
- }))
1134
- .filter((value, index) => value.text !== svgTexts[index].text);
1135
- if (colorValues.length) {
1136
- svgHelper.setSvgColors(colorValues);
1137
- }
1138
- if (textValues.length) {
1139
- svgHelper.setSvgTexts(textValues);
1140
- }
1141
- if (colorValues.length || textValues.length) {
1142
- value = svgHelper.getSvgString();
1143
- }
1144
- }
1145
- return value;
1146
- }
1147
- class Color {
1148
- _error = false;
1149
- _rgba;
1150
- static fromRGB(rgb) {
1151
- const color = new Color('transparent');
1152
- color._rgba[0] = rgb[0];
1153
- color._rgba[1] = rgb[1];
1154
- color._rgba[2] = rgb[2];
1155
- color._rgba[3] = rgb.length > 3 ? rgb[3] : 1;
1156
- return color;
1157
- }
1158
- constructor(cstr) {
1159
- cstr = String(cstr).trim().toLowerCase();
1160
- const rgba = (this._rgba = [0, 0, 0, 1]);
1161
- const size = cstr.length;
1162
- let m;
1163
- if (size === 0) {
1164
- this._error = true;
1165
- }
1166
- else if (cstr === 'transparent' || cstr === 'none') {
1167
- rgba[3] = 0;
1168
- }
1169
- else if (cstr[0] === '#') {
1170
- if (size < 6) {
1171
- rgba[0] = parseInt(cstr[1] + cstr[1], 16) || 0;
1172
- rgba[1] = parseInt(cstr[2] + cstr[2], 16) || 0;
1173
- rgba[2] = parseInt(cstr[3] + cstr[3], 16) || 0;
1174
- if (size === 5) {
1175
- rgba[3] = parseInt(cstr[4] + cstr[4], 16) / 255;
1176
- }
1177
- }
1178
- else {
1179
- rgba[0] = parseInt(cstr[1] + cstr[2], 16) || 0;
1180
- rgba[1] = parseInt(cstr[3] + cstr[4], 16) || 0;
1181
- rgba[2] = parseInt(cstr[5] + cstr[6], 16) || 0;
1182
- if (size > 8) {
1183
- rgba[3] = parseInt(cstr[7] + cstr[8], 16) / 255;
1184
- }
1185
- }
1186
- }
1187
- else if (cstr in colorNames) {
1188
- const namedColor = colorNames[cstr];
1189
- rgba[0] = namedColor[0];
1190
- rgba[1] = namedColor[1];
1191
- rgba[2] = namedColor[2];
1192
- }
1193
- else if ((m = /^(rgba?|hs[lv]a?)\s*\(([^)]*)\)/.exec(cstr))) {
1194
- const space = m[1].replace(/a$/, '');
1195
- const parts = m[2].trim().split(/\s*[,/]\s*|\s+/);
1196
- const color = parts.map(parsePart, { space });
1197
- if (space === 'rgb') {
1198
- rgba[0] = color[0];
1199
- rgba[1] = color[1];
1200
- rgba[2] = color[2];
1201
- if (color.length > 3) {
1202
- rgba[3] = color[3];
1203
- }
1204
- }
1205
- else if (space === 'hsl') {
1206
- hslToRgb(color, rgba);
1207
- }
1208
- else if (space === 'hsv') {
1209
- hsvToRgb(color, rgba);
1210
- } // TODO: process other spaces
1211
- }
1212
- else {
1213
- this._error = true;
1214
- }
1215
- }
1216
- get badValue() {
1217
- return this._error;
1218
- }
1219
- get a() {
1220
- return this._rgba[3];
1221
- }
1222
- get hex() {
1223
- // prettier-ignore
1224
- return `#${(Math.round(this._rgba[0]) << 16 |
1225
- Math.round(this._rgba[1]) << 8 |
1226
- Math.round(this._rgba[2])).toString(16).padStart(6, '0')}`;
1227
- }
1228
- get rgba() {
1229
- return this._rgba.slice();
1230
- }
1231
- sameColor(color) {
1232
- return (almostEqual(this._rgba[0], color._rgba[0]) &&
1233
- almostEqual(this._rgba[1], color._rgba[1]) &&
1234
- almostEqual(this._rgba[2], color._rgba[2]) &&
1235
- almostEqual(this._rgba[3], color._rgba[3]));
1236
- }
1237
- toString() {
1238
- const [r, g, b, a] = this._rgba;
1239
- return a === 1 ? this.hex : `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})`;
1240
- }
1241
- }
1242
- const baseHues = {
1243
- red: 0,
1244
- orange: 60,
1245
- yellow: 120,
1246
- green: 180,
1247
- blue: 240,
1248
- purple: 300,
1249
- };
1250
- // prettier-ignore
1251
- function parsePart(s, i) {
1252
- const { space } = this;
1253
- if (s[s.length - 1] === '%') {
1254
- const x = parseFloat(s);
1255
- const mul = i < 3 && space === 'rgb' ? 255 : 1;
1256
- const div = i > 0 && i < 3 && space[0] === 'h' ? 1 : 100;
1257
- return (x < 0 ? 0 : x > 100 ? 100 : x) / div * mul;
1258
- }
1259
- if (space[i] === 'h' || (i === 2 && space[space.length - 1] === 'h')) {
1260
- if (s in baseHues) {
1261
- return baseHues[s];
1262
- }
1263
- let x = parseFloat(s);
1264
- switch (true) {
1265
- // case s.endsWith('deg'): break;
1266
- case s.endsWith('turn'):
1267
- x *= 360;
1268
- break;
1269
- case s.endsWith('grad'):
1270
- x = x * 360 / 400;
1271
- break;
1272
- case s.endsWith('rad'):
1273
- x = x * 180 / Math.PI;
1274
- break;
1275
- }
1276
- return ((x % 360) + 360) % 360;
1277
- }
1278
- if (s === 'none') {
1279
- return 0;
1280
- }
1281
- {
1282
- const x = parseFloat(s);
1283
- const max = i < 3 && space === 'rgb' ? 255 : 1;
1284
- return x < 0 ? 0 : x > max ? max : x;
1285
- }
1286
- }
1287
- // prettier-ignore
1288
- function hue2rgb(p, q, t) {
1289
- if (t < 0) {
1290
- t += 1;
1291
- }
1292
- if (t > 1) {
1293
- t -= 1;
1294
- }
1295
- return (t < 1 / 6 ? p + (q - p) * 6 * t : t < 1 / 2 ? q : t < 2 / 3 ? p + (q - p) * (2 / 3 - t) * 6 : p) * 255;
1296
- }
1297
- function hslToRgb(hsl, rgba = [0, 0, 0, 1]) {
1298
- const h = hsl[0] / 360;
1299
- const s = hsl[1] / 100;
1300
- const l = hsl[2] / 100;
1301
- if (s === 0) {
1302
- rgba[2] = rgba[1] = rgba[0] = l * 255;
1303
- }
1304
- else {
1305
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
1306
- const p = 2 * l - q;
1307
- rgba[0] = hue2rgb(p, q, h + 1 / 3);
1308
- rgba[1] = hue2rgb(p, q, h);
1309
- rgba[2] = hue2rgb(p, q, h - 1 / 3);
1310
- }
1311
- if (hsl.length > 3) {
1312
- rgba[3] = hsl[3];
1313
- }
1314
- return rgba;
1315
- }
1316
- // prettier-ignore
1317
- function hsvToRgb(hsv, rgba = [0, 0, 0, 1]) {
1318
- const h = hsv[0] / 60;
1319
- const s = hsv[1] / 100;
1320
- const v = hsv[2] / 100 * 255;
1321
- const i = Math.floor(h);
1322
- const f = h - i;
1323
- const p = v * (1 - s);
1324
- const q = v * (1 - f * s);
1325
- const t = v * (1 - (1 - f) * s);
1326
- switch (i % 6) {
1327
- case 0:
1328
- rgba[0] = v;
1329
- rgba[1] = t;
1330
- rgba[2] = p;
1331
- break;
1332
- case 1:
1333
- rgba[0] = q;
1334
- rgba[1] = v;
1335
- rgba[2] = p;
1336
- break;
1337
- case 2:
1338
- rgba[0] = p;
1339
- rgba[1] = v;
1340
- rgba[2] = t;
1341
- break;
1342
- case 3:
1343
- rgba[0] = p;
1344
- rgba[1] = q;
1345
- rgba[2] = v;
1346
- break;
1347
- case 4:
1348
- rgba[0] = t;
1349
- rgba[1] = p;
1350
- rgba[2] = v;
1351
- break;
1352
- case 5:
1353
- rgba[0] = v;
1354
- rgba[1] = p;
1355
- rgba[2] = q;
1356
- break;
1357
- }
1358
- if (hsv.length > 3) {
1359
- rgba[3] = hsv[3];
1360
- }
1361
- return rgba;
1362
- }
1363
- const colorNames = {
1364
- aliceblue: [240, 248, 255],
1365
- antiquewhite: [250, 235, 215],
1366
- aqua: [0, 255, 255],
1367
- aquamarine: [127, 255, 212],
1368
- azure: [240, 255, 255],
1369
- beige: [245, 245, 220],
1370
- bisque: [255, 228, 196],
1371
- black: [0, 0, 0],
1372
- blanchedalmond: [255, 235, 205],
1373
- blue: [0, 0, 255],
1374
- blueviolet: [138, 43, 226],
1375
- brown: [165, 42, 42],
1376
- burlywood: [222, 184, 135],
1377
- cadetblue: [95, 158, 160],
1378
- chartreuse: [127, 255, 0],
1379
- chocolate: [210, 105, 30],
1380
- coral: [255, 127, 80],
1381
- cornflowerblue: [100, 149, 237],
1382
- cornsilk: [255, 248, 220],
1383
- crimson: [220, 20, 60],
1384
- cyan: [0, 255, 255],
1385
- darkblue: [0, 0, 139],
1386
- darkcyan: [0, 139, 139],
1387
- darkgoldenrod: [184, 134, 11],
1388
- darkgray: [169, 169, 169],
1389
- darkgreen: [0, 100, 0],
1390
- darkgrey: [169, 169, 169],
1391
- darkkhaki: [189, 183, 107],
1392
- darkmagenta: [139, 0, 139],
1393
- darkolivegreen: [85, 107, 47],
1394
- darkorange: [255, 140, 0],
1395
- darkorchid: [153, 50, 204],
1396
- darkred: [139, 0, 0],
1397
- darksalmon: [233, 150, 122],
1398
- darkseagreen: [143, 188, 143],
1399
- darkslateblue: [72, 61, 139],
1400
- darkslategray: [47, 79, 79],
1401
- darkslategrey: [47, 79, 79],
1402
- darkturquoise: [0, 206, 209],
1403
- darkviolet: [148, 0, 211],
1404
- deeppink: [255, 20, 147],
1405
- deepskyblue: [0, 191, 255],
1406
- dimgray: [105, 105, 105],
1407
- dimgrey: [105, 105, 105],
1408
- dodgerblue: [30, 144, 255],
1409
- firebrick: [178, 34, 34],
1410
- floralwhite: [255, 250, 240],
1411
- forestgreen: [34, 139, 34],
1412
- fuchsia: [255, 0, 255],
1413
- gainsboro: [220, 220, 220],
1414
- ghostwhite: [248, 248, 255],
1415
- gold: [255, 215, 0],
1416
- goldenrod: [218, 165, 32],
1417
- gray: [128, 128, 128],
1418
- green: [0, 128, 0],
1419
- greenyellow: [173, 255, 47],
1420
- grey: [128, 128, 128],
1421
- honeydew: [240, 255, 240],
1422
- hotpink: [255, 105, 180],
1423
- indianred: [205, 92, 92],
1424
- indigo: [75, 0, 130],
1425
- ivory: [255, 255, 240],
1426
- khaki: [240, 230, 140],
1427
- lavender: [230, 230, 250],
1428
- lavenderblush: [255, 240, 245],
1429
- lawngreen: [124, 252, 0],
1430
- lemonchiffon: [255, 250, 205],
1431
- lightblue: [173, 216, 230],
1432
- lightcoral: [240, 128, 128],
1433
- lightcyan: [224, 255, 255],
1434
- lightgoldenrodyellow: [250, 250, 210],
1435
- lightgray: [211, 211, 211],
1436
- lightgreen: [144, 238, 144],
1437
- lightgrey: [211, 211, 211],
1438
- lightpink: [255, 182, 193],
1439
- lightsalmon: [255, 160, 122],
1440
- lightseagreen: [32, 178, 170],
1441
- lightskyblue: [135, 206, 250],
1442
- lightslategray: [119, 136, 153],
1443
- lightslategrey: [119, 136, 153],
1444
- lightsteelblue: [176, 196, 222],
1445
- lightyellow: [255, 255, 224],
1446
- lime: [0, 255, 0],
1447
- limegreen: [50, 205, 50],
1448
- linen: [250, 240, 230],
1449
- magenta: [255, 0, 255],
1450
- maroon: [128, 0, 0],
1451
- mediumaquamarine: [102, 205, 170],
1452
- mediumblue: [0, 0, 205],
1453
- mediumorchid: [186, 85, 211],
1454
- mediumpurple: [147, 112, 219],
1455
- mediumseagreen: [60, 179, 113],
1456
- mediumslateblue: [123, 104, 238],
1457
- mediumspringgreen: [0, 250, 154],
1458
- mediumturquoise: [72, 209, 204],
1459
- mediumvioletred: [199, 21, 133],
1460
- midnightblue: [25, 25, 112],
1461
- mintcream: [245, 255, 250],
1462
- mistyrose: [255, 228, 225],
1463
- moccasin: [255, 228, 181],
1464
- navajowhite: [255, 222, 173],
1465
- navy: [0, 0, 128],
1466
- oldlace: [253, 245, 230],
1467
- olive: [128, 128, 0],
1468
- olivedrab: [107, 142, 35],
1469
- orange: [255, 165, 0],
1470
- orangered: [255, 69, 0],
1471
- orchid: [218, 112, 214],
1472
- palegoldenrod: [238, 232, 170],
1473
- palegreen: [152, 251, 152],
1474
- paleturquoise: [175, 238, 238],
1475
- palevioletred: [219, 112, 147],
1476
- papayawhip: [255, 239, 213],
1477
- peachpuff: [255, 218, 185],
1478
- peru: [205, 133, 63],
1479
- pink: [255, 192, 203],
1480
- plum: [221, 160, 221],
1481
- powderblue: [176, 224, 230],
1482
- purple: [128, 0, 128],
1483
- rebeccapurple: [102, 51, 153],
1484
- red: [255, 0, 0],
1485
- rosybrown: [188, 143, 143],
1486
- royalblue: [65, 105, 225],
1487
- saddlebrown: [139, 69, 19],
1488
- salmon: [250, 128, 114],
1489
- sandybrown: [244, 164, 96],
1490
- seagreen: [46, 139, 87],
1491
- seashell: [255, 245, 238],
1492
- sienna: [160, 82, 45],
1493
- silver: [192, 192, 192],
1494
- skyblue: [135, 206, 235],
1495
- slateblue: [106, 90, 205],
1496
- slategray: [112, 128, 144],
1497
- slategrey: [112, 128, 144],
1498
- snow: [255, 250, 250],
1499
- springgreen: [0, 255, 127],
1500
- steelblue: [70, 130, 180],
1501
- tan: [210, 180, 140],
1502
- teal: [0, 128, 128],
1503
- thistle: [216, 191, 216],
1504
- tomato: [255, 99, 71],
1505
- turquoise: [64, 224, 208],
1506
- violet: [238, 130, 238],
1507
- wheat: [245, 222, 179],
1508
- white: [255, 255, 255],
1509
- whitesmoke: [245, 245, 245],
1510
- yellow: [255, 255, 0],
1511
- yellowgreen: [154, 205, 50],
1512
- };