@joint/core 4.1.3 → 4.2.0-alpha.1

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 (58) hide show
  1. package/README.md +4 -2
  2. package/dist/geometry.js +129 -124
  3. package/dist/geometry.min.js +4 -3
  4. package/dist/joint.d.ts +352 -160
  5. package/dist/joint.js +3654 -2191
  6. package/dist/joint.min.js +4 -3
  7. package/dist/joint.nowrap.js +3653 -2188
  8. package/dist/joint.nowrap.min.js +4 -3
  9. package/dist/vectorizer.js +489 -279
  10. package/dist/vectorizer.min.js +4 -3
  11. package/dist/version.mjs +1 -1
  12. package/package.json +33 -27
  13. package/src/V/create.mjs +51 -0
  14. package/src/V/index.mjs +89 -159
  15. package/src/V/namespace.mjs +9 -0
  16. package/src/V/transform.mjs +183 -0
  17. package/src/V/traverse.mjs +16 -0
  18. package/src/alg/Deque.mjs +126 -0
  19. package/src/anchors/index.mjs +140 -33
  20. package/src/cellTools/Boundary.mjs +15 -13
  21. package/src/cellTools/Button.mjs +7 -5
  22. package/src/cellTools/Control.mjs +38 -15
  23. package/src/cellTools/HoverConnect.mjs +5 -1
  24. package/src/cellTools/helpers.mjs +44 -3
  25. package/src/config/index.mjs +8 -0
  26. package/src/connectionPoints/index.mjs +24 -9
  27. package/src/connectionStrategies/index.mjs +1 -1
  28. package/src/connectors/jumpover.mjs +1 -1
  29. package/src/dia/Cell.mjs +32 -12
  30. package/src/dia/CellView.mjs +53 -38
  31. package/src/dia/Element.mjs +81 -35
  32. package/src/dia/ElementView.mjs +2 -1
  33. package/src/dia/HighlighterView.mjs +54 -11
  34. package/src/dia/LinkView.mjs +118 -98
  35. package/src/dia/Paper.mjs +831 -231
  36. package/src/dia/PaperLayer.mjs +9 -2
  37. package/src/dia/ToolView.mjs +4 -0
  38. package/src/dia/ToolsView.mjs +12 -3
  39. package/src/dia/attributes/text.mjs +16 -5
  40. package/src/dia/layers/GridLayer.mjs +5 -0
  41. package/src/dia/ports.mjs +344 -111
  42. package/src/elementTools/HoverConnect.mjs +14 -8
  43. package/src/env/index.mjs +7 -4
  44. package/src/g/rect.mjs +7 -0
  45. package/src/highlighters/stroke.mjs +1 -1
  46. package/src/layout/ports/port.mjs +30 -15
  47. package/src/layout/ports/portLabel.mjs +1 -1
  48. package/src/linkAnchors/index.mjs +2 -2
  49. package/src/linkTools/Anchor.mjs +2 -2
  50. package/src/linkTools/Vertices.mjs +4 -6
  51. package/src/mvc/View.mjs +4 -0
  52. package/src/mvc/ViewBase.mjs +1 -1
  53. package/src/util/util.mjs +1 -1
  54. package/src/util/utilHelpers.mjs +2 -0
  55. package/types/geometry.d.ts +65 -59
  56. package/types/joint.d.ts +278 -102
  57. package/types/vectorizer.d.ts +11 -1
  58. package/src/V/annotation.mjs +0 -0
@@ -0,0 +1,51 @@
1
+ import * as ns from './namespace.mjs';
2
+
3
+ /**
4
+ * @constant {boolean}
5
+ * @description Indicates the environment supports SVG.
6
+ */
7
+ export const isSVGSupported = typeof window === 'object' && !!window.SVGAngle;
8
+
9
+ /**
10
+ * @constant {string}
11
+ * @description The version of the SVG document.
12
+ */
13
+ export const SVG_VERSION = '1.1';
14
+
15
+ /**
16
+ * @constant {SVGSVGElement}
17
+ * @description The detached SVG document for various internal purposes.
18
+ * e.g. SVGMatrix has no constructor, so the only way to create it is
19
+ * to create an SVG document and then call `createSVGMatrix()`.
20
+ */
21
+ export const internalSVGDocument = isSVGSupported
22
+ ? createSVGDocument()
23
+ : null;
24
+
25
+ /**
26
+ * @constant {SVGGElement}
27
+ * @description The detached SVG group element for various internal purposes.
28
+ */
29
+ export const internalSVGGroup = isSVGSupported
30
+ ? createSVGElement('g')
31
+ : null;
32
+
33
+ /**
34
+ * @returns {SVGSVGElement}
35
+ * @description Creates an SVG document.
36
+ */
37
+ export function createSVGDocument() {
38
+ const svg = createSVGElement('svg');
39
+ svg.setAttributeNS(ns.xmlns, 'xmlns:xlink', ns.xlink);
40
+ svg.setAttribute('version', SVG_VERSION);
41
+ return svg;
42
+ }
43
+
44
+ /**
45
+ * @param {string} name
46
+ * @returns {SVGElement}
47
+ * @description Creates an SVG element with the given name.
48
+ */
49
+ export function createSVGElement(name) {
50
+ return document.createElementNS(ns.svg, name);
51
+ }
package/src/V/index.mjs CHANGED
@@ -5,13 +5,20 @@
5
5
  // The only Vectorizer dependency is the Geometry library.
6
6
 
7
7
  import * as g from '../g/index.mjs';
8
+ import * as ns from './namespace.mjs';
9
+ import { isSVGSupported, internalSVGDocument, SVG_VERSION, createSVGDocument, createSVGElement } from './create.mjs';
10
+ import {
11
+ createIdentityMatrix, createMatrix, getNodeMatrix, isSVGMatrix,
12
+ getRelativeTransformation, getRelativeTransformationSafe,
13
+ matrixToTransformString, createMatrixFromTransformString,
14
+ transformNode, replaceTransformNode,
15
+ } from './transform.mjs';
16
+ import { getCommonAncestor } from './traverse.mjs';
8
17
 
9
18
  const V = (function() {
10
19
 
11
- var hasSvg = typeof window === 'object' && !!window.SVGAngle;
12
-
13
20
  // SVG support is required.
14
- if (!hasSvg) {
21
+ if (!isSVGSupported) {
15
22
 
16
23
  // Return a function that throws an error when it is used.
17
24
  return function() {
@@ -19,17 +26,6 @@ const V = (function() {
19
26
  };
20
27
  }
21
28
 
22
- // XML namespaces.
23
- var ns = {
24
- svg: 'http://www.w3.org/2000/svg',
25
- xmlns: 'http://www.w3.org/2000/xmlns/',
26
- xml: 'http://www.w3.org/XML/1998/namespace',
27
- xlink: 'http://www.w3.org/1999/xlink',
28
- xhtml: 'http://www.w3.org/1999/xhtml'
29
- };
30
-
31
- var SVGVersion = '1.1';
32
-
33
29
  // Declare shorthands to the most used math functions.
34
30
  var math = Math;
35
31
  var PI = math.PI;
@@ -92,7 +88,7 @@ const V = (function() {
92
88
 
93
89
  } else {
94
90
 
95
- el = document.createElementNS(ns.svg, el);
91
+ el = createSVGElement(el);
96
92
  }
97
93
 
98
94
  V.ensureId(el);
@@ -122,40 +118,48 @@ const V = (function() {
122
118
  });
123
119
 
124
120
  /**
125
- * @param {SVGGElement} toElem
126
- * @returns {SVGMatrix}
121
+ * Calculates the transformation matrix from this element to the target element.
122
+ * @param {SVGElement|V} target - The target element.
123
+ * @param {Object} [opt] - Options object for transformation calculation.
124
+ * @param {boolean} [opt.safe] - Use a safe traversal method to compute the matrix.
125
+ * @returns {DOMMatrix} The transformation matrix from this element to the target element.
127
126
  */
128
- VPrototype.getTransformToElement = function(target) {
129
- var node = this.node;
130
- if (V.isSVGGraphicsElement(target) && V.isSVGGraphicsElement(node)) {
131
- var targetCTM = V.toNode(target).getScreenCTM();
132
- var nodeCTM = node.getScreenCTM();
133
- if (targetCTM && nodeCTM) {
134
- return targetCTM.inverse().multiply(nodeCTM);
127
+ VPrototype.getTransformToElement = function(target, opt) {
128
+ const node = this.node;
129
+ const targetNode = V.toNode(target);
130
+ let m;
131
+ if (V.isSVGGraphicsElement(targetNode) && V.isSVGGraphicsElement(node)) {
132
+ if (opt && opt.safe) {
133
+ // Use the traversal method to get the transformation matrix.
134
+ m = getRelativeTransformationSafe(node, targetNode);
135
+ } else {
136
+ m = getRelativeTransformation(node, targetNode);
135
137
  }
136
138
  }
137
- // Could not get actual transformation matrix
138
- return V.createSVGMatrix();
139
+ return m || createIdentityMatrix();
139
140
  };
140
141
 
142
+
141
143
  /**
142
144
  * @param {SVGMatrix} matrix
143
145
  * @param {Object=} opt
144
146
  * @returns {Vectorizer|SVGMatrix} Setter / Getter
145
147
  */
146
148
  VPrototype.transform = function(matrix, opt) {
149
+ const node = this.node;
147
150
 
148
- var node = this.node;
151
+ // Getter
149
152
  if (V.isUndefined(matrix)) {
150
- return V.transformStringToMatrix(this.attr('transform'));
153
+ return getNodeMatrix(node) || createIdentityMatrix();
151
154
  }
152
155
 
156
+ // Setter
153
157
  if (opt && opt.absolute) {
154
- return this.attr('transform', V.matrixToTransformString(matrix));
158
+ replaceTransformNode(node, matrix);
159
+ } else {
160
+ transformNode(node, matrix);
155
161
  }
156
162
 
157
- var svgTransform = V.createSVGTransform(matrix);
158
- node.transform.baseVal.appendItem(svgTransform);
159
163
  return this;
160
164
  };
161
165
 
@@ -250,7 +254,7 @@ const V = (function() {
250
254
 
251
255
  box = node.getBBox();
252
256
 
253
- } catch (e) {
257
+ } catch {
254
258
 
255
259
  // Fallback for IE.
256
260
  box = {
@@ -303,7 +307,7 @@ const V = (function() {
303
307
  if (!options.recursive) {
304
308
  try {
305
309
  outputBBox = node.getBBox();
306
- } catch (e) {
310
+ } catch {
307
311
  // Fallback for IE.
308
312
  outputBBox = {
309
313
  x: node.clientLeft,
@@ -484,10 +488,19 @@ const V = (function() {
484
488
 
485
489
  if (content && typeof content !== 'string') throw new Error('Vectorizer: text() expects the first argument to be a string.');
486
490
 
487
- // Replace all spaces with the Unicode No-break space (http://www.fileformat.info/info/unicode/char/a0/index.htm).
488
- // IE would otherwise collapse all spaces into one.
489
- content = V.sanitizeText(content);
490
491
  opt || (opt = {});
492
+
493
+ // Backwards-compatibility: if no content was provided, treat it as an
494
+ // empty string so that subsequent string operations (e.g. split) do
495
+ // not throw and behaviour matches the previous implementation that
496
+ // always sanitised the input.
497
+ if (content == null) content = '';
498
+
499
+ if (opt.useNoBreakSpace) {
500
+ // Replace all spaces with the Unicode No-break space (http://www.fileformat.info/info/unicode/char/a0/index.htm).
501
+ // IE would otherwise collapse all spaces into one.
502
+ content = V.sanitizeText(content);
503
+ }
491
504
  // Should we allow the text to be selected?
492
505
  var displayEmpty = opt.displayEmpty;
493
506
  // End of Line character
@@ -924,9 +937,9 @@ const V = (function() {
924
937
  var globalPoint = p.matrixTransform(svg.getScreenCTM().inverse());
925
938
  var globalToLocalMatrix = this.getTransformToElement(svg).inverse();
926
939
 
927
- } catch (e) {
940
+ } catch {
928
941
  // IE9 throws an exception in odd cases. (`Unexpected call to method or property access`)
929
- // We have to make do with the original coordianates.
942
+ // We have to make do with the original coordinates.
930
943
  return p;
931
944
  }
932
945
 
@@ -986,7 +999,7 @@ const V = (function() {
986
999
  translateToOrigin.matrix.multiply(
987
1000
  ctm.scale(scale.sx, scale.sy)))));
988
1001
 
989
- this.attr('transform', V.matrixToTransformString(transform.matrix));
1002
+ this.attr('transform', matrixToTransformString(transform.matrix));
990
1003
 
991
1004
  return this;
992
1005
  };
@@ -1004,7 +1017,7 @@ const V = (function() {
1004
1017
  this.append(animateMotion);
1005
1018
  try {
1006
1019
  animateMotion.node.beginElement();
1007
- } catch (e) {
1020
+ } catch {
1008
1021
  // Fallback for IE 9.
1009
1022
  // Run the animation programmatically if FakeSmile (`http://leunen.me/fakesmile/`) present
1010
1023
  if (document.documentElement.getAttribute('smiling') === 'fake') {
@@ -1287,15 +1300,12 @@ const V = (function() {
1287
1300
  V.createSvgDocument = function(content) {
1288
1301
 
1289
1302
  if (content) {
1290
- const XMLString = `<svg xmlns="${ns.svg}" xmlns:xlink="${ns.xlink}" version="${SVGVersion}">${content}</svg>`;
1303
+ const XMLString = `<svg xmlns="${ns.svg}" xmlns:xlink="${ns.xlink}" version="${SVG_VERSION}">${content}</svg>`;
1291
1304
  const { documentElement } = V.parseXML(XMLString, { async: false });
1292
1305
  return documentElement;
1293
1306
  }
1294
1307
 
1295
- const svg = document.createElementNS(ns.svg, 'svg');
1296
- svg.setAttributeNS(ns.xmlns, 'xmlns:xlink', ns.xlink);
1297
- svg.setAttribute('version', SVGVersion);
1298
- return svg;
1308
+ return createSVGDocument();
1299
1309
  };
1300
1310
 
1301
1311
  V.createSVGStyle = function(stylesheet) {
@@ -1334,6 +1344,9 @@ const V = (function() {
1334
1344
  // also exposed so that the programmer can use it in case he needs to. This is useful e.g. in tests
1335
1345
  // when you want to compare the actual DOM text content without having to add the unicode character in
1336
1346
  // the place of all spaces.
1347
+ /**
1348
+ * @deprecated Use regular spaces and rely on xml:space="preserve" instead.
1349
+ */
1337
1350
  V.sanitizeText = function(text) {
1338
1351
 
1339
1352
  return (text || '').replace(/ /g, '\u00A0');
@@ -1370,7 +1383,7 @@ const V = (function() {
1370
1383
  }
1371
1384
 
1372
1385
  xml = parser.parseFromString(data, 'text/xml');
1373
- } catch (error) {
1386
+ } catch {
1374
1387
  xml = undefined;
1375
1388
  }
1376
1389
 
@@ -1389,6 +1402,7 @@ const V = (function() {
1389
1402
  // List of attributes for which not to split camel case words.
1390
1403
  // It contains known SVG attribute names and may be extended with user-defined attribute names.
1391
1404
  [
1405
+ 'attributeName',
1392
1406
  'baseFrequency',
1393
1407
  'baseProfile',
1394
1408
  'clipPathUnits',
@@ -1426,6 +1440,7 @@ const V = (function() {
1426
1440
  'refY',
1427
1441
  'requiredExtensions',
1428
1442
  'requiredFeatures',
1443
+ 'repeatCount',
1429
1444
  'specularConstant',
1430
1445
  'specularExponent',
1431
1446
  'spreadMethod',
@@ -1442,7 +1457,7 @@ const V = (function() {
1442
1457
  'viewTarget', // deprecated
1443
1458
  'xChannelSelector',
1444
1459
  'yChannelSelector',
1445
- 'zoomAndPan' // deprecated
1460
+ 'zoomAndPan', // deprecated
1446
1461
  ].forEach((name) => _attributeNames[name] = name);
1447
1462
 
1448
1463
  _attributeNames['xlinkShow'] = 'xlink:show';
@@ -1511,108 +1526,22 @@ const V = (function() {
1511
1526
  // ReDoS mitigation: Use an anchor at the beginning of the match
1512
1527
  // ReDoS mitigation: Avoid backtracking (uses `[^()]+` instead of `.*?`)
1513
1528
  // ReDoS mitigation: Don't match initial `(` inside repeated part
1514
- // The following regex needs to use /g (= cannot use capturing groups)
1515
- V.transformRegex = /\b\w+\([^()]+\)/g;
1516
1529
  // The following regexes need to use capturing groups (= cannot use /g)
1517
1530
  V.transformFunctionRegex = /\b(\w+)\(([^()]+)\)/;
1518
1531
  V.transformTranslateRegex = /\btranslate\(([^()]+)\)/;
1519
1532
  V.transformRotateRegex = /\brotate\(([^()]+)\)/;
1520
1533
  V.transformScaleRegex = /\bscale\(([^()]+)\)/;
1521
1534
 
1522
- V.transformStringToMatrix = function(transform) {
1523
-
1524
- // Initialize result matrix as identity matrix
1525
- let transformationMatrix = V.createSVGMatrix();
1526
-
1527
- // Note: Multiple transform functions are allowed in `transform` string
1528
- // `match()` returns `null` if none found
1529
- const transformMatches = transform && transform.match(V.transformRegex);
1530
- if (!transformMatches) {
1531
- // Return identity matrix
1532
- return transformationMatrix;
1533
- }
1534
-
1535
- const numMatches = transformMatches.length;
1536
- for (let i = 0; i < numMatches; i++) {
1537
-
1538
- const transformMatch = transformMatches[i];
1539
- // Use same regex as above, but with capturing groups
1540
- // `match()` returns values of capturing groups as `[1]`, `[2]`
1541
- const transformFunctionMatch = transformMatch.match(V.transformFunctionRegex);
1542
- if (transformFunctionMatch) {
1543
-
1544
- let sx, sy, tx, ty, angle;
1545
- let ctm = V.createSVGMatrix();
1546
- const transformFunction = transformFunctionMatch[1].toLowerCase();
1547
- const args = transformFunctionMatch[2].split(V.transformSeparatorRegex);
1548
- switch (transformFunction) {
1549
-
1550
- case 'scale':
1551
- sx = parseFloat(args[0]);
1552
- sy = (args[1] === undefined) ? sx : parseFloat(args[1]);
1553
- ctm = ctm.scaleNonUniform(sx, sy);
1554
- break;
1555
-
1556
- case 'translate':
1557
- tx = parseFloat(args[0]);
1558
- ty = parseFloat(args[1]);
1559
- ctm = ctm.translate(tx, ty);
1560
- break;
1561
-
1562
- case 'rotate':
1563
- angle = parseFloat(args[0]);
1564
- tx = parseFloat(args[1]) || 0;
1565
- ty = parseFloat(args[2]) || 0;
1566
- if (tx !== 0 || ty !== 0) {
1567
- ctm = ctm.translate(tx, ty).rotate(angle).translate(-tx, -ty);
1568
- } else {
1569
- ctm = ctm.rotate(angle);
1570
- }
1571
- break;
1572
-
1573
- case 'skewx':
1574
- angle = parseFloat(args[0]);
1575
- ctm = ctm.skewX(angle);
1576
- break;
1577
-
1578
- case 'skewy':
1579
- angle = parseFloat(args[0]);
1580
- ctm = ctm.skewY(angle);
1581
- break;
1582
-
1583
- case 'matrix':
1584
- ctm.a = parseFloat(args[0]);
1585
- ctm.b = parseFloat(args[1]);
1586
- ctm.c = parseFloat(args[2]);
1587
- ctm.d = parseFloat(args[3]);
1588
- ctm.e = parseFloat(args[4]);
1589
- ctm.f = parseFloat(args[5]);
1590
- break;
1591
-
1592
- default:
1593
- continue;
1594
- }
1595
-
1596
- // Multiply current transformation into result matrix
1597
- transformationMatrix = transformationMatrix.multiply(ctm);
1598
- }
1599
1535
 
1536
+ V.transformStringToMatrix = function(transform) {
1537
+ let matrix;
1538
+ if (V.isString(transform)) {
1539
+ matrix = createMatrixFromTransformString(transform);
1600
1540
  }
1601
- return transformationMatrix;
1541
+ return matrix || createIdentityMatrix();
1602
1542
  };
1603
1543
 
1604
- V.matrixToTransformString = function(matrix) {
1605
- matrix || (matrix = true);
1606
-
1607
- return 'matrix(' +
1608
- (matrix.a !== undefined ? matrix.a : 1) + ',' +
1609
- (matrix.b !== undefined ? matrix.b : 0) + ',' +
1610
- (matrix.c !== undefined ? matrix.c : 0) + ',' +
1611
- (matrix.d !== undefined ? matrix.d : 1) + ',' +
1612
- (matrix.e !== undefined ? matrix.e : 0) + ',' +
1613
- (matrix.f !== undefined ? matrix.f : 0) +
1614
- ')';
1615
- };
1544
+ V.matrixToTransformString = matrixToTransformString;
1616
1545
 
1617
1546
  V.parseTransformString = function(transform) {
1618
1547
 
@@ -1780,35 +1709,25 @@ const V = (function() {
1780
1709
  return node instanceof SVGElement && typeof node.getScreenCTM === 'function';
1781
1710
  };
1782
1711
 
1783
- var svgDocument = V('svg').node;
1784
-
1785
- V.createSVGMatrix = function(matrix) {
1786
-
1787
- var svgMatrix = svgDocument.createSVGMatrix();
1788
- for (var component in matrix) {
1789
- svgMatrix[component] = matrix[component];
1790
- }
1791
-
1792
- return svgMatrix;
1793
- };
1712
+ V.createSVGMatrix = createMatrix;
1794
1713
 
1795
1714
  V.createSVGTransform = function(matrix) {
1796
1715
 
1797
1716
  if (!V.isUndefined(matrix)) {
1798
1717
 
1799
- if (!(matrix instanceof SVGMatrix)) {
1800
- matrix = V.createSVGMatrix(matrix);
1718
+ if (!isSVGMatrix(matrix)) {
1719
+ matrix = createMatrix(matrix);
1801
1720
  }
1802
1721
 
1803
- return svgDocument.createSVGTransformFromMatrix(matrix);
1722
+ return internalSVGDocument.createSVGTransformFromMatrix(matrix);
1804
1723
  }
1805
1724
 
1806
- return svgDocument.createSVGTransform();
1725
+ return internalSVGDocument.createSVGTransform();
1807
1726
  };
1808
1727
 
1809
1728
  V.createSVGPoint = function(x, y) {
1810
1729
 
1811
- var p = svgDocument.createSVGPoint();
1730
+ var p = internalSVGDocument.createSVGPoint();
1812
1731
  p.x = x;
1813
1732
  p.y = y;
1814
1733
  return p;
@@ -1816,7 +1735,7 @@ const V = (function() {
1816
1735
 
1817
1736
  V.transformRect = function(r, matrix) {
1818
1737
 
1819
- var p = svgDocument.createSVGPoint();
1738
+ var p = internalSVGDocument.createSVGPoint();
1820
1739
 
1821
1740
  p.x = r.x;
1822
1741
  p.y = r.y;
@@ -2083,8 +2002,8 @@ const V = (function() {
2083
2002
 
2084
2003
  line = V(line);
2085
2004
  var d = [
2086
- 'M', line.attr('x1'), line.attr('y1'),
2087
- 'L', line.attr('x2'), line.attr('y2')
2005
+ 'M', line.attr('x1') || '0', line.attr('y1') || '0',
2006
+ 'L', line.attr('x2') || '0', line.attr('y2') || '0'
2088
2007
  ].join(' ');
2089
2008
  return d;
2090
2009
  };
@@ -2635,7 +2554,18 @@ const V = (function() {
2635
2554
  };
2636
2555
  })();
2637
2556
 
2638
- V.namespace = ns;
2557
+ /**
2558
+ *
2559
+ * @param {SVGElement|V} node1
2560
+ * @param {SVGElement|V} node2
2561
+ * @returns {SVGElement|null}
2562
+ */
2563
+ V.getCommonAncestor = function(node1, node2) {
2564
+ if (!node1 || !node2) return null;
2565
+ return getCommonAncestor(V.toNode(node1), V.toNode(node2));
2566
+ };
2567
+
2568
+ V.namespace = { ...ns };
2639
2569
 
2640
2570
  V.g = g;
2641
2571
 
@@ -0,0 +1,9 @@
1
+ export const svg = 'http://www.w3.org/2000/svg';
2
+
3
+ export const xmlns = 'http://www.w3.org/2000/xmlns/';
4
+
5
+ export const xml = 'http://www.w3.org/XML/1998/namespace';
6
+
7
+ export const xlink = 'http://www.w3.org/1999/xlink';
8
+
9
+ export const xhtml = 'http://www.w3.org/1999/xhtml';
@@ -0,0 +1,183 @@
1
+ import { internalSVGDocument, internalSVGGroup } from './create.mjs';
2
+ import { getCommonAncestor } from './traverse.mjs';
3
+
4
+ /**
5
+ * @returns {SVGMatrix}
6
+ * @description Creates an identity matrix.
7
+ */
8
+ export function createIdentityMatrix() {
9
+ return internalSVGDocument.createSVGMatrix();
10
+ }
11
+
12
+ /**
13
+ * @param {Partial<SVGMatrix>} matrixInit
14
+ * @returns {SVGMatrix}
15
+ * @description Creates a new SVGMatrix object.
16
+ * If no matrix is provided, it returns the identity matrix.
17
+ * If a matrix like object is provided, it sets the matrix values.
18
+ */
19
+ export function createMatrix(matrixInit = {}) {
20
+ const matrix = internalSVGDocument.createSVGMatrix();
21
+ if (!matrixInit) return matrix;
22
+ if ('a' in matrixInit) matrix.a = matrixInit.a;
23
+ if ('b' in matrixInit) matrix.b = matrixInit.b;
24
+ if ('c' in matrixInit) matrix.c = matrixInit.c;
25
+ if ('d' in matrixInit) matrix.d = matrixInit.d;
26
+ if ('e' in matrixInit) matrix.e = matrixInit.e;
27
+ if ('f' in matrixInit) matrix.f = matrixInit.f;
28
+ return matrix;
29
+ }
30
+
31
+ /**
32
+ * @returns {SVGTransform}
33
+ * @description Creates a new SVGTransform object.
34
+ */
35
+ export function createSVGTransform() {
36
+ return internalSVGDocument.createSVGTransform();
37
+ }
38
+
39
+ /**
40
+ * @param {SVGElement} node
41
+ * @returns {SVGMatrix|null}
42
+ * @description Returns the transformation matrix of the given node.
43
+ * If the node has no transformation, it returns null.
44
+ */
45
+ export function getNodeMatrix(node) {
46
+ const consolidatedTransformation = node.transform.baseVal.consolidate();
47
+ return consolidatedTransformation ? consolidatedTransformation.matrix : null;
48
+ }
49
+
50
+ /**
51
+ * @param {string} transformString
52
+ * @returns {SVGMatrix}
53
+ * @description Creates a matrix from the given transform string.
54
+ */
55
+ export function createMatrixFromTransformString(transformString) {
56
+ internalSVGGroup.setAttribute('transform', transformString);
57
+ return getNodeMatrix(internalSVGGroup);
58
+ }
59
+
60
+ /**
61
+ * @param {SVGElement} node
62
+ * @param {Partial<SVGMatrix>} matrixInit
63
+ * @param {boolean} override
64
+ * @description Sets the transformation matrix of the given node.
65
+ * We don't use `node.transform.baseVal` here (@see `transformNode`)
66
+ * for the following reasons:
67
+ * - Performance: while Chrome performs slightly better, Firefox
68
+ * and Safari are significantly slower
69
+ * https://www.measurethat.net/Benchmarks/Show/34447/1/overriding-svg-transform-attribute
70
+ * - Limited support: JSDOM does not support `node.transform.baseVal`
71
+ */
72
+ export function replaceTransformNode(node, matrixInit) {
73
+ node.setAttribute('transform', matrixToTransformString(matrixInit));
74
+ }
75
+
76
+ /**
77
+ * @param {SVGElement} node
78
+ * @param {Partial<SVGMatrix>} matrixInit
79
+ * @description Applies a transformation matrix to the given node.
80
+ * If the node already has a transformation, it appends the new transformation.
81
+ * If the node has no transformation, it creates a new one.
82
+ */
83
+ export function transformNode(node, matrixInit) {
84
+ const transform = createSVGTransform();
85
+ const matrix = isSVGMatrix(matrixInit) ? matrixInit : createMatrix(matrixInit);
86
+ transform.setMatrix(matrix);
87
+ node.transform.baseVal.appendItem(transform);
88
+ }
89
+
90
+ const MATRIX_TYPE = '[object SVGMatrix]';
91
+
92
+ /**
93
+ * @param {any} obj
94
+ * @returns {boolean}
95
+ * @description Checks if the given object is an SVGMatrix.
96
+ */
97
+ export function isSVGMatrix(obj) {
98
+ return Object.prototype.toString.call(obj) === MATRIX_TYPE;
99
+ }
100
+
101
+ /**
102
+ * @param {Partial<SVGMatrix>} matrixInit
103
+ * @returns {string}
104
+ * @description Converts a matrix to a transform string.
105
+ * If no matrix is provided, it returns the identity matrix string.
106
+ */
107
+ export function matrixToTransformString(matrixInit = {}) {
108
+ const { a = 1, b = 0, c = 0, d = 1, e = 0, f = 0 } = matrixInit;
109
+ return `matrix(${a},${b},${c},${d},${e},${f})`;
110
+ }
111
+
112
+ /**
113
+ *
114
+ * @param {SVGElement} a
115
+ * @param {SVGElement} b
116
+ * @returns {SVGMatrix|null}
117
+ * @description Finds the transformation matrix from `a` to `b`.
118
+ * It requires that both elements to be visible (in the render tree)
119
+ * in order to calculate the correct transformation matrix.
120
+ */
121
+ export function getRelativeTransformation(a, b) {
122
+ // Different SVG elements, no transformation possible
123
+ // Note: SVGSVGElement has no `ownerSVGElement`
124
+ if ((a.ownerSVGElement || a) !== (b.ownerSVGElement || b)) return null;
125
+ // Get the transformation matrix from `a` to `b`.
126
+ const am = b.getScreenCTM();
127
+ if (!am) return null;
128
+ const bm = a.getScreenCTM();
129
+ if (!bm) return null;
130
+ return am.inverse().multiply(bm);
131
+ }
132
+
133
+ /**
134
+ * @param {SVGElement} a
135
+ * @param {SVGElement} b
136
+ * @returns {SVGMatrix|null}
137
+ * @description Finds the transformation matrix from `a` to `b`.
138
+ * A safe way to calculate the transformation matrix between two elements.
139
+ * It does not require the elements to be visible (in the render tree).
140
+ */
141
+ export function getRelativeTransformationSafe(a, b) {
142
+ if (a === b) {
143
+ // No transformation needed
144
+ return createIdentityMatrix();
145
+ }
146
+ const position = a.compareDocumentPosition(b);
147
+ if (position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
148
+ // `b` is a descendant of `a`
149
+ return getLinealTransformation(a, b).inverse();
150
+ } else if (position & Node.DOCUMENT_POSITION_CONTAINS) {
151
+ // `a` is a descendant of `b`
152
+ return getLinealTransformation(b, a);
153
+ }
154
+
155
+ const c = getCommonAncestor(a, b);
156
+ if (!c) {
157
+ // No common ancestor
158
+ return null;
159
+ }
160
+
161
+ const mca = getLinealTransformation(c, a);
162
+ const mcb = getLinealTransformation(c, b);
163
+ return mcb.inverse().multiply(mca);
164
+ }
165
+
166
+ /**
167
+ * @param {SVGElement} descendant
168
+ * @param {SVGElement} ancestor
169
+ * @returns {SVGMatrix}
170
+ * @description Finds the transformation matrix between the `ancestor` and `descendant`.
171
+ */
172
+ function getLinealTransformation(ancestor, descendant) {
173
+ const transformations = [];
174
+ let n = descendant;
175
+ while (n && n.nodeType === Node.ELEMENT_NODE && n !== ancestor) {
176
+ const nm = getNodeMatrix(n);
177
+ if (nm) {
178
+ transformations.unshift(nm);
179
+ }
180
+ n = n.parentNode;
181
+ }
182
+ return transformations.reduce((m, t) => m.multiply(t), createIdentityMatrix());
183
+ }