@joint/core 4.0.3 → 4.1.0-beta.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.
- package/README.md +2 -10
- package/dist/geometry.js +4962 -6132
- package/dist/geometry.min.js +2 -2
- package/dist/joint.d.ts +338 -52
- package/dist/joint.js +34067 -37525
- package/dist/joint.min.js +2 -2
- package/dist/joint.nowrap.js +34067 -37525
- package/dist/joint.nowrap.min.js +2 -2
- package/dist/vectorizer.js +7288 -8907
- package/dist/vectorizer.min.js +2 -2
- package/dist/version.mjs +1 -1
- package/package.json +10 -15
- package/src/{linkTools → cellTools}/Button.mjs +8 -6
- package/src/{elementTools → cellTools}/Control.mjs +3 -3
- package/src/{linkTools → cellTools}/HoverConnect.mjs +1 -1
- package/src/dia/Cell.mjs +60 -33
- package/src/dia/CellView.mjs +75 -8
- package/src/dia/ElementView.mjs +13 -8
- package/src/dia/Graph.mjs +148 -40
- package/src/dia/HighlighterView.mjs +8 -4
- package/src/dia/LinkView.mjs +61 -4
- package/src/dia/Paper.mjs +84 -0
- package/src/dia/ToolView.mjs +29 -4
- package/src/dia/ToolsView.mjs +25 -10
- package/src/dia/attributes/connection.mjs +5 -0
- package/src/dia/attributes/defs.mjs +3 -0
- package/src/dia/attributes/eval.mjs +3 -3
- package/src/dia/attributes/index.mjs +3 -0
- package/src/dia/attributes/shape.mjs +4 -0
- package/src/dia/attributes/text.mjs +41 -15
- package/src/dia/ports.mjs +4 -0
- package/src/elementTools/HoverConnect.mjs +5 -5
- package/src/elementTools/index.mjs +5 -4
- package/src/env/index.mjs +5 -0
- package/src/g/rect.mjs +13 -5
- package/src/layout/ports/port.mjs +4 -5
- package/src/linkTools/Anchor.mjs +1 -1
- package/src/linkTools/Arrowhead.mjs +2 -1
- package/src/linkTools/RotateLabel.mjs +110 -0
- package/src/linkTools/Segments.mjs +1 -1
- package/src/linkTools/Vertices.mjs +41 -4
- package/src/linkTools/index.mjs +7 -4
- package/src/mvc/View.mjs +0 -1
- package/src/mvc/ViewBase.mjs +2 -1
- package/src/routers/rightAngle.mjs +538 -140
- package/src/shapes/standard.mjs +8 -1
- package/src/{dia/attributes → util}/calc.mjs +24 -12
- package/src/util/index.mjs +1 -0
- package/src/util/util.mjs +39 -0
- package/src/util/utilHelpers.mjs +2 -1
- package/types/geometry.d.ts +6 -1
- package/types/joint.d.ts +331 -50
- /package/src/{linkTools → cellTools}/Boundary.mjs +0 -0
- /package/src/{linkTools → cellTools}/Connect.mjs +0 -0
- /package/src/{linkTools → cellTools}/helpers.mjs +0 -0
package/src/dia/LinkView.mjs
CHANGED
|
@@ -2,10 +2,10 @@ import { CellView } from './CellView.mjs';
|
|
|
2
2
|
import { Link } from './Link.mjs';
|
|
3
3
|
import V from '../V/index.mjs';
|
|
4
4
|
import { addClassNamePrefix, merge, assign, isObject, isFunction, clone, isPercentage, result, isEqual } from '../util/index.mjs';
|
|
5
|
-
import { Point, Line, Path, normalizeAngle, Rect, Polyline } from '../g/index.mjs';
|
|
5
|
+
import { Point, Line, Path, normalizeAngle, Rect, Polyline, intersection } from '../g/index.mjs';
|
|
6
6
|
import * as routers from '../routers/index.mjs';
|
|
7
7
|
import * as connectors from '../connectors/index.mjs';
|
|
8
|
-
|
|
8
|
+
import { env } from '../env/index.mjs';
|
|
9
9
|
|
|
10
10
|
const Flags = {
|
|
11
11
|
TOOLS: CellView.Flags.TOOLS,
|
|
@@ -63,7 +63,7 @@ export const LinkView = CellView.extend({
|
|
|
63
63
|
attrs: [Flags.UPDATE],
|
|
64
64
|
router: [Flags.UPDATE],
|
|
65
65
|
connector: [Flags.CONNECTOR],
|
|
66
|
-
labels: [Flags.LABELS],
|
|
66
|
+
labels: [Flags.LABELS, Flags.TOOLS],
|
|
67
67
|
labelMarkup: [Flags.LABELS],
|
|
68
68
|
vertices: [Flags.UPDATE],
|
|
69
69
|
source: [Flags.SOURCE, Flags.UPDATE],
|
|
@@ -73,6 +73,7 @@ export const LinkView = CellView.extend({
|
|
|
73
73
|
initFlag: [Flags.RENDER, Flags.SOURCE, Flags.TARGET, Flags.TOOLS],
|
|
74
74
|
|
|
75
75
|
UPDATE_PRIORITY: 1,
|
|
76
|
+
EPSILON: 1e-6,
|
|
76
77
|
|
|
77
78
|
confirmUpdate: function(flags, opt) {
|
|
78
79
|
|
|
@@ -99,6 +100,11 @@ export const LinkView = CellView.extend({
|
|
|
99
100
|
this.updateHighlighters(true);
|
|
100
101
|
this.updateTools(opt);
|
|
101
102
|
flags = this.removeFlag(flags, [Flags.RENDER, Flags.UPDATE, Flags.LABELS, Flags.TOOLS, Flags.CONNECTOR]);
|
|
103
|
+
|
|
104
|
+
if (env.test('isSafari')) {
|
|
105
|
+
this.__fixSafariBug268376();
|
|
106
|
+
}
|
|
107
|
+
|
|
102
108
|
return flags;
|
|
103
109
|
}
|
|
104
110
|
|
|
@@ -151,6 +157,19 @@ export const LinkView = CellView.extend({
|
|
|
151
157
|
return flags;
|
|
152
158
|
},
|
|
153
159
|
|
|
160
|
+
__fixSafariBug268376: function() {
|
|
161
|
+
// Safari has a bug where any change after the first render is not reflected in the DOM.
|
|
162
|
+
// https://bugs.webkit.org/show_bug.cgi?id=268376
|
|
163
|
+
const { el } = this;
|
|
164
|
+
const childNodes = Array.from(el.childNodes);
|
|
165
|
+
const fragment = document.createDocumentFragment();
|
|
166
|
+
for (let i = 0, n = childNodes.length; i < n; i++) {
|
|
167
|
+
el.removeChild(childNodes[i]);
|
|
168
|
+
fragment.appendChild(childNodes[i]);
|
|
169
|
+
}
|
|
170
|
+
el.appendChild(fragment);
|
|
171
|
+
},
|
|
172
|
+
|
|
154
173
|
requestConnectionUpdate: function(opt) {
|
|
155
174
|
this.requestUpdate(this.getFlag(Flags.UPDATE), opt);
|
|
156
175
|
},
|
|
@@ -443,6 +462,13 @@ export const LinkView = CellView.extend({
|
|
|
443
462
|
|
|
444
463
|
if (!this._V.labels) return this;
|
|
445
464
|
|
|
465
|
+
if (!this.paper.options.labelLayer) {
|
|
466
|
+
// If there is no label layer, the cache needs to be cleared
|
|
467
|
+
// of the root node because the labels are attached
|
|
468
|
+
// to it and could affect the bounding box.
|
|
469
|
+
this.cleanNodeCache(this.el);
|
|
470
|
+
}
|
|
471
|
+
|
|
446
472
|
var model = this.model;
|
|
447
473
|
var labels = model.get('labels') || [];
|
|
448
474
|
var canLabelMove = this.can('labelMove');
|
|
@@ -798,6 +824,34 @@ export const LinkView = CellView.extend({
|
|
|
798
824
|
return connectionPoint.round(this.decimalsRounding);
|
|
799
825
|
},
|
|
800
826
|
|
|
827
|
+
isIntersecting: function(geometryShape, geometryData) {
|
|
828
|
+
const connection = this.getConnection();
|
|
829
|
+
if (!connection) return false;
|
|
830
|
+
return intersection.exists(
|
|
831
|
+
geometryShape,
|
|
832
|
+
connection,
|
|
833
|
+
geometryData,
|
|
834
|
+
{ segmentSubdivisions: this.getConnectionSubdivisions() },
|
|
835
|
+
);
|
|
836
|
+
},
|
|
837
|
+
|
|
838
|
+
isEnclosedIn: function(geometryRect) {
|
|
839
|
+
const connection = this.getConnection();
|
|
840
|
+
if (!connection) return false;
|
|
841
|
+
const bbox = connection.bbox();
|
|
842
|
+
if (!bbox) return false;
|
|
843
|
+
return geometryRect.containsRect(bbox);
|
|
844
|
+
},
|
|
845
|
+
|
|
846
|
+
isAtPoint: function(point /*, options */) {
|
|
847
|
+
// Note: `strict` option is not applicable for links.
|
|
848
|
+
// There is currently no method to determine if a path contains a point.
|
|
849
|
+
const area = new Rect(point);
|
|
850
|
+
// Intersection with a zero-size area is not possible.
|
|
851
|
+
area.inflate(this.EPSILON);
|
|
852
|
+
return this.isIntersecting(area);
|
|
853
|
+
},
|
|
854
|
+
|
|
801
855
|
// combine default label position with built-in default label position
|
|
802
856
|
_getDefaultLabelPositionProperty: function() {
|
|
803
857
|
|
|
@@ -1805,7 +1859,10 @@ export const LinkView = CellView.extend({
|
|
|
1805
1859
|
// checking view in close area of the pointer
|
|
1806
1860
|
|
|
1807
1861
|
var r = snapLinks.radius || 50;
|
|
1808
|
-
var viewsInArea = paper.
|
|
1862
|
+
var viewsInArea = paper.findElementViewsInArea(
|
|
1863
|
+
{ x: x - r, y: y - r, width: 2 * r, height: 2 * r },
|
|
1864
|
+
snapLinks.findInAreaOptions
|
|
1865
|
+
);
|
|
1809
1866
|
|
|
1810
1867
|
var prevClosestView = data.closestView || null;
|
|
1811
1868
|
var prevClosestMagnet = data.closestMagnet || null;
|
package/src/dia/Paper.mjs
CHANGED
|
@@ -382,6 +382,11 @@ export const Paper = View.extend({
|
|
|
382
382
|
],
|
|
383
383
|
MIN_SCALE: 1e-6,
|
|
384
384
|
|
|
385
|
+
// Default find buffer for the findViewsInArea and findViewsAtPoint methods.
|
|
386
|
+
// The find buffer is used to extend the area of the search
|
|
387
|
+
// to mitigate the differences between the model and view geometry.
|
|
388
|
+
DEFAULT_FIND_BUFFER: 200,
|
|
389
|
+
|
|
385
390
|
init: function() {
|
|
386
391
|
|
|
387
392
|
const { options } = this;
|
|
@@ -1858,6 +1863,85 @@ export const Paper = View.extend({
|
|
|
1858
1863
|
}, this);
|
|
1859
1864
|
},
|
|
1860
1865
|
|
|
1866
|
+
findElementViewsInArea(plainArea, opt) {
|
|
1867
|
+
return this._filterViewsInArea(
|
|
1868
|
+
plainArea,
|
|
1869
|
+
(extArea, findOpt) => this.model.findElementsInArea(extArea, findOpt),
|
|
1870
|
+
opt
|
|
1871
|
+
);
|
|
1872
|
+
},
|
|
1873
|
+
|
|
1874
|
+
findLinkViewsInArea: function(plainArea, opt) {
|
|
1875
|
+
return this._filterViewsInArea(
|
|
1876
|
+
plainArea,
|
|
1877
|
+
(extArea, findOpt) => this.model.findLinksInArea(extArea, findOpt),
|
|
1878
|
+
opt
|
|
1879
|
+
);
|
|
1880
|
+
},
|
|
1881
|
+
|
|
1882
|
+
findCellViewsInArea: function(plainArea, opt) {
|
|
1883
|
+
return this._filterViewsInArea(
|
|
1884
|
+
plainArea,
|
|
1885
|
+
(extArea, findOpt) => this.model.findCellsInArea(extArea, findOpt),
|
|
1886
|
+
opt
|
|
1887
|
+
);
|
|
1888
|
+
},
|
|
1889
|
+
|
|
1890
|
+
findElementViewsAtPoint: function(plainPoint, opt) {
|
|
1891
|
+
return this._filterViewsAtPoint(
|
|
1892
|
+
plainPoint,
|
|
1893
|
+
(extArea) => this.model.findElementsInArea(extArea),
|
|
1894
|
+
opt
|
|
1895
|
+
);
|
|
1896
|
+
},
|
|
1897
|
+
|
|
1898
|
+
findLinkViewsAtPoint: function(plainPoint, opt) {
|
|
1899
|
+
return this._filterViewsAtPoint(
|
|
1900
|
+
plainPoint,
|
|
1901
|
+
(extArea) => this.model.findLinksInArea(extArea),
|
|
1902
|
+
opt,
|
|
1903
|
+
);
|
|
1904
|
+
},
|
|
1905
|
+
|
|
1906
|
+
findCellViewsAtPoint: function(plainPoint, opt) {
|
|
1907
|
+
return this._filterViewsAtPoint(
|
|
1908
|
+
plainPoint,
|
|
1909
|
+
// Note: we do not want to pass `opt` to `findCellsInArea`
|
|
1910
|
+
// because the `strict` option works differently for querying at a point
|
|
1911
|
+
(extArea) => this.model.findCellsInArea(extArea),
|
|
1912
|
+
opt
|
|
1913
|
+
);
|
|
1914
|
+
},
|
|
1915
|
+
|
|
1916
|
+
_findInExtendedArea: function(area, findCellsFn, opt = {}) {
|
|
1917
|
+
const {
|
|
1918
|
+
buffer = this.DEFAULT_FIND_BUFFER,
|
|
1919
|
+
} = opt;
|
|
1920
|
+
const extendedArea = (new Rect(area)).inflate(buffer);
|
|
1921
|
+
const cellsInExtendedArea = findCellsFn(extendedArea, opt);
|
|
1922
|
+
return cellsInExtendedArea.map(element => this.findViewByModel(element));
|
|
1923
|
+
},
|
|
1924
|
+
|
|
1925
|
+
_filterViewsInArea: function(plainArea, findCells, opt = {}) {
|
|
1926
|
+
const area = new Rect(plainArea);
|
|
1927
|
+
const viewsInExtendedArea = this._findInExtendedArea(area, findCells, opt);
|
|
1928
|
+
const viewsInArea = viewsInExtendedArea.filter(view => {
|
|
1929
|
+
if (!view) return false;
|
|
1930
|
+
return view.isInArea(area, opt);
|
|
1931
|
+
});
|
|
1932
|
+
return viewsInArea;
|
|
1933
|
+
},
|
|
1934
|
+
|
|
1935
|
+
_filterViewsAtPoint: function(plainPoint, findCells, opt = {}) {
|
|
1936
|
+
const area = new Rect(plainPoint); // zero-size area
|
|
1937
|
+
const viewsInExtendedArea = this._findInExtendedArea(area, findCells, opt);
|
|
1938
|
+
const viewsAtPoint = viewsInExtendedArea.filter(view => {
|
|
1939
|
+
if (!view) return false;
|
|
1940
|
+
return view.isAtPoint(plainPoint, opt);
|
|
1941
|
+
});
|
|
1942
|
+
return viewsAtPoint;
|
|
1943
|
+
},
|
|
1944
|
+
|
|
1861
1945
|
removeTools: function() {
|
|
1862
1946
|
this.dispatchToolsEvent('remove');
|
|
1863
1947
|
return this;
|
package/src/dia/ToolView.mjs
CHANGED
|
@@ -6,6 +6,7 @@ export const ToolView = mvc.View.extend({
|
|
|
6
6
|
className: 'tool',
|
|
7
7
|
svgElement: true,
|
|
8
8
|
_visible: true,
|
|
9
|
+
_visibleExplicit: true,
|
|
9
10
|
|
|
10
11
|
init: function() {
|
|
11
12
|
var name = this.name;
|
|
@@ -30,16 +31,40 @@ export const ToolView = mvc.View.extend({
|
|
|
30
31
|
return this.name;
|
|
31
32
|
},
|
|
32
33
|
|
|
34
|
+
// Evaluate the visibility of the tool and update the `display` CSS property
|
|
35
|
+
updateVisibility: function() {
|
|
36
|
+
const isVisible = this.computeVisibility();
|
|
37
|
+
this.el.style.display = isVisible ? '' : 'none';
|
|
38
|
+
this._visible = isVisible;
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// Evaluate the visibility of the tool. The method returns `true` if the tool
|
|
42
|
+
// should be visible in the DOM.
|
|
43
|
+
computeVisibility() {
|
|
44
|
+
if (!this.isExplicitlyVisible()) return false;
|
|
45
|
+
const { visibility } = this.options;
|
|
46
|
+
if (typeof visibility !== 'function') return true;
|
|
47
|
+
return !!visibility.call(this, this.relatedView, this);
|
|
48
|
+
},
|
|
49
|
+
|
|
33
50
|
show: function() {
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
51
|
+
this._visibleExplicit = true;
|
|
52
|
+
this.updateVisibility();
|
|
36
53
|
},
|
|
37
54
|
|
|
38
55
|
hide: function() {
|
|
39
|
-
this.
|
|
40
|
-
this.
|
|
56
|
+
this._visibleExplicit = false;
|
|
57
|
+
this.updateVisibility();
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// The method returns `false` if the `hide()` method was called on the tool.
|
|
61
|
+
isExplicitlyVisible: function() {
|
|
62
|
+
return !!this._visibleExplicit;
|
|
41
63
|
},
|
|
42
64
|
|
|
65
|
+
// The method returns `false` if the tool is not visible (it has `display: none`).
|
|
66
|
+
// This can happen if the `hide()` method was called or the tool is not visible
|
|
67
|
+
// because of the `visibility` option was evaluated to `false`.
|
|
43
68
|
isVisible: function() {
|
|
44
69
|
return !!this._visible;
|
|
45
70
|
},
|
package/src/dia/ToolsView.mjs
CHANGED
|
@@ -44,25 +44,37 @@ export const ToolsView = mvc.View.extend({
|
|
|
44
44
|
update: function(opt) {
|
|
45
45
|
|
|
46
46
|
opt || (opt = {});
|
|
47
|
-
|
|
47
|
+
const tools = this.tools;
|
|
48
48
|
if (!tools) return this;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
const n = tools.length;
|
|
50
|
+
const wasRendered = this.isRendered;
|
|
51
|
+
for (let i = 0; i < n; i++) {
|
|
52
|
+
const tool = tools[i];
|
|
53
|
+
tool.updateVisibility();
|
|
54
|
+
if (!tool.isVisible()) continue;
|
|
55
|
+
if (!this.isRendered) {
|
|
56
|
+
// There is at least one visible tool
|
|
57
|
+
this.isRendered = Array(n).fill(false);
|
|
58
|
+
}
|
|
59
|
+
if (!this.isRendered[i]) {
|
|
53
60
|
// First update executes render()
|
|
54
61
|
tool.render();
|
|
55
|
-
|
|
62
|
+
this.isRendered[i] = true;
|
|
63
|
+
} else if (opt.tool !== tool.cid) {
|
|
56
64
|
tool.update();
|
|
57
65
|
}
|
|
58
66
|
}
|
|
67
|
+
if (!this.isRendered && n > 0) {
|
|
68
|
+
// None of the tools is visible
|
|
69
|
+
// Note: ToolsView with no tools are always mounted
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
59
72
|
if (!this.isMounted()) {
|
|
60
73
|
this.mount();
|
|
61
74
|
}
|
|
62
|
-
if (!
|
|
75
|
+
if (!wasRendered) {
|
|
63
76
|
// Make sure tools are visible (if they were hidden and the tool removed)
|
|
64
77
|
this.blurTool();
|
|
65
|
-
this.isRendered = true;
|
|
66
78
|
}
|
|
67
79
|
return this;
|
|
68
80
|
},
|
|
@@ -87,9 +99,12 @@ export const ToolsView = mvc.View.extend({
|
|
|
87
99
|
if (!tools) return this;
|
|
88
100
|
for (var i = 0, n = tools.length; i < n; i++) {
|
|
89
101
|
var tool = tools[i];
|
|
90
|
-
if (tool !== blurredTool && !tool.
|
|
102
|
+
if (tool !== blurredTool && !tool.isExplicitlyVisible()) {
|
|
91
103
|
tool.show();
|
|
92
|
-
tool
|
|
104
|
+
// Check if the tool is conditionally visible too
|
|
105
|
+
if (tool.isVisible()) {
|
|
106
|
+
tool.update();
|
|
107
|
+
}
|
|
93
108
|
}
|
|
94
109
|
}
|
|
95
110
|
return this;
|
|
@@ -25,6 +25,7 @@ const connectionAttributesNS = {
|
|
|
25
25
|
|
|
26
26
|
'connection': {
|
|
27
27
|
qualify: isLinkView,
|
|
28
|
+
unset: 'd',
|
|
28
29
|
set: function({ stubs = 0 }) {
|
|
29
30
|
let d;
|
|
30
31
|
if (isFinite(stubs) && stubs !== 0) {
|
|
@@ -49,21 +50,25 @@ const connectionAttributesNS = {
|
|
|
49
50
|
|
|
50
51
|
'at-connection-length-keep-gradient': {
|
|
51
52
|
qualify: isLinkView,
|
|
53
|
+
unset: 'transform',
|
|
52
54
|
set: atConnectionWrapper('getTangentAtLength', { rotate: true })
|
|
53
55
|
},
|
|
54
56
|
|
|
55
57
|
'at-connection-length-ignore-gradient': {
|
|
56
58
|
qualify: isLinkView,
|
|
59
|
+
unset: 'transform',
|
|
57
60
|
set: atConnectionWrapper('getTangentAtLength', { rotate: false })
|
|
58
61
|
},
|
|
59
62
|
|
|
60
63
|
'at-connection-ratio-keep-gradient': {
|
|
61
64
|
qualify: isLinkView,
|
|
65
|
+
unset: 'transform',
|
|
62
66
|
set: atConnectionWrapper('getTangentAtRatio', { rotate: true })
|
|
63
67
|
},
|
|
64
68
|
|
|
65
69
|
'at-connection-ratio-ignore-gradient': {
|
|
66
70
|
qualify: isLinkView,
|
|
71
|
+
unset: 'transform',
|
|
67
72
|
set: atConnectionWrapper('getTangentAtRatio', { rotate: false })
|
|
68
73
|
}
|
|
69
74
|
|
|
@@ -33,6 +33,7 @@ const defsAttributesNS = {
|
|
|
33
33
|
|
|
34
34
|
'source-marker': {
|
|
35
35
|
qualify: isPlainObject,
|
|
36
|
+
unset: 'marker-start',
|
|
36
37
|
set: function(marker, refBBox, node, attrs) {
|
|
37
38
|
marker = assign(contextMarker(attrs), marker);
|
|
38
39
|
return { 'marker-start': 'url(#' + this.paper.defineMarker(marker) + ')' };
|
|
@@ -41,6 +42,7 @@ const defsAttributesNS = {
|
|
|
41
42
|
|
|
42
43
|
'target-marker': {
|
|
43
44
|
qualify: isPlainObject,
|
|
45
|
+
unset: 'marker-end',
|
|
44
46
|
set: function(marker, refBBox, node, attrs) {
|
|
45
47
|
marker = assign(contextMarker(attrs), { 'transform': 'rotate(180)' }, marker);
|
|
46
48
|
return { 'marker-end': 'url(#' + this.paper.defineMarker(marker) + ')' };
|
|
@@ -49,6 +51,7 @@ const defsAttributesNS = {
|
|
|
49
51
|
|
|
50
52
|
'vertex-marker': {
|
|
51
53
|
qualify: isPlainObject,
|
|
54
|
+
unset: 'marker-mid',
|
|
52
55
|
set: function(marker, refBBox, node, attrs) {
|
|
53
56
|
marker = assign(contextMarker(attrs), marker);
|
|
54
57
|
return { 'marker-mid': 'url(#' + this.paper.defineMarker(marker) + ')' };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isCalcExpression, evalCalcExpression } from '../../util/calc.mjs';
|
|
2
2
|
|
|
3
3
|
const calcAttributesList = [
|
|
4
4
|
'transform',
|
|
@@ -53,8 +53,8 @@ export function evalAttributes(attrs, refBBox) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export function evalAttribute(attrName, attrValue, refBBox) {
|
|
56
|
-
if (attrName in calcAttributes &&
|
|
57
|
-
let evalAttrValue =
|
|
56
|
+
if (attrName in calcAttributes && isCalcExpression(attrValue)) {
|
|
57
|
+
let evalAttrValue = evalCalcExpression(attrValue, refBBox);
|
|
58
58
|
if (attrName in positiveValueAttributes) {
|
|
59
59
|
evalAttrValue = Math.max(0, evalAttrValue);
|
|
60
60
|
}
|
|
@@ -69,18 +69,22 @@ function pointsWrapper(opt) {
|
|
|
69
69
|
const shapeAttributesNS = {
|
|
70
70
|
|
|
71
71
|
'ref-d-reset-offset': {
|
|
72
|
+
unset: 'd',
|
|
72
73
|
set: dWrapper({ resetOffset: true })
|
|
73
74
|
},
|
|
74
75
|
|
|
75
76
|
'ref-d-keep-offset': {
|
|
77
|
+
unset: 'd',
|
|
76
78
|
set: dWrapper({ resetOffset: false })
|
|
77
79
|
},
|
|
78
80
|
|
|
79
81
|
'ref-points-reset-offset': {
|
|
82
|
+
unset: 'points',
|
|
80
83
|
set: pointsWrapper({ resetOffset: true })
|
|
81
84
|
},
|
|
82
85
|
|
|
83
86
|
'ref-points-keep-offset': {
|
|
87
|
+
unset: 'points',
|
|
84
88
|
set: pointsWrapper({ resetOffset: false })
|
|
85
89
|
},
|
|
86
90
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assign, isPlainObject, isObject, isPercentage, breakText } from '../../util/util.mjs';
|
|
2
|
-
import {
|
|
2
|
+
import { isCalcExpression, evalCalcExpression } from '../../util/calc.mjs';
|
|
3
3
|
import $ from '../../mvc/Dom/index.mjs';
|
|
4
4
|
import V from '../../V/index.mjs';
|
|
5
5
|
|
|
@@ -7,6 +7,8 @@ function isTextInUse(_value, _node, attrs) {
|
|
|
7
7
|
return (attrs.text !== undefined);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
const FONT_ATTRIBUTES = ['font-weight', 'font-family', 'font-size', 'letter-spacing', 'text-transform'];
|
|
11
|
+
|
|
10
12
|
const textAttributesNS = {
|
|
11
13
|
|
|
12
14
|
'line-height': {
|
|
@@ -38,6 +40,9 @@ const textAttributesNS = {
|
|
|
38
40
|
const textWrap = attrs['text-wrap'];
|
|
39
41
|
return !textWrap || !isPlainObject(textWrap);
|
|
40
42
|
},
|
|
43
|
+
unset: function(node) {
|
|
44
|
+
node.textContent = '';
|
|
45
|
+
},
|
|
41
46
|
set: function(text, refBBox, node, attrs) {
|
|
42
47
|
const cacheName = 'joint-text';
|
|
43
48
|
const cache = $.data.get(node, cacheName);
|
|
@@ -89,8 +94,8 @@ const textAttributesNS = {
|
|
|
89
94
|
var width = value.width || 0;
|
|
90
95
|
if (isPercentage(width)) {
|
|
91
96
|
size.width = refBBox.width * parseFloat(width) / 100;
|
|
92
|
-
} else if (
|
|
93
|
-
size.width = Number(
|
|
97
|
+
} else if (isCalcExpression(width)) {
|
|
98
|
+
size.width = Number(evalCalcExpression(width, refBBox));
|
|
94
99
|
} else {
|
|
95
100
|
if (value.width === null) {
|
|
96
101
|
// breakText() requires width to be specified.
|
|
@@ -105,8 +110,8 @@ const textAttributesNS = {
|
|
|
105
110
|
var height = value.height || 0;
|
|
106
111
|
if (isPercentage(height)) {
|
|
107
112
|
size.height = refBBox.height * parseFloat(height) / 100;
|
|
108
|
-
} else if (
|
|
109
|
-
size.height = Number(
|
|
113
|
+
} else if (isCalcExpression(height)) {
|
|
114
|
+
size.height = Number(evalCalcExpression(height, refBBox));
|
|
110
115
|
} else {
|
|
111
116
|
if (value.height === null) {
|
|
112
117
|
// if height is not specified breakText() does not
|
|
@@ -122,18 +127,28 @@ const textAttributesNS = {
|
|
|
122
127
|
var text = value.text;
|
|
123
128
|
if (text === undefined) text = attrs.text;
|
|
124
129
|
if (text !== undefined) {
|
|
130
|
+
|
|
125
131
|
const breakTextFn = value.breakText || breakText;
|
|
126
132
|
const computedStyles = getComputedStyle(node);
|
|
133
|
+
const wrapFontAttributes = {};
|
|
134
|
+
// The font size attributes must be set on the node
|
|
135
|
+
// to get the correct text wrapping.
|
|
136
|
+
// TODO: set the native SVG attributes before special attributes
|
|
137
|
+
for (let i = 0; i < FONT_ATTRIBUTES.length; i++) {
|
|
138
|
+
const name = FONT_ATTRIBUTES[i];
|
|
139
|
+
if (name in attrs) {
|
|
140
|
+
node.setAttribute(name, attrs[name]);
|
|
141
|
+
}
|
|
142
|
+
// Note: computedStyles is a live object
|
|
143
|
+
// i.e. the properties are evaluated when accessed.
|
|
144
|
+
wrapFontAttributes[name] = computedStyles[name];
|
|
145
|
+
}
|
|
127
146
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
'letter-spacing': computedStyles.letterSpacing,
|
|
134
|
-
// The `line-height` attribute in SVG is JoinJS specific.
|
|
135
|
-
'lineHeight': attrs['line-height'],
|
|
136
|
-
}, {
|
|
147
|
+
// The `line-height` attribute in SVG is JoinJS specific.
|
|
148
|
+
// TODO: change the `lineHeight` to breakText option.
|
|
149
|
+
wrapFontAttributes.lineHeight = attrs['line-height'];
|
|
150
|
+
|
|
151
|
+
wrappedText = breakTextFn('' + text, size, wrapFontAttributes, {
|
|
137
152
|
// Provide an existing SVG Document here
|
|
138
153
|
// instead of creating a temporary one over again.
|
|
139
154
|
svgDocument: this.paper.svg,
|
|
@@ -147,7 +162,11 @@ const textAttributesNS = {
|
|
|
147
162
|
wrappedText = '';
|
|
148
163
|
}
|
|
149
164
|
textAttributesNS.text.set.call(this, wrappedText, refBBox, node, attrs);
|
|
150
|
-
}
|
|
165
|
+
},
|
|
166
|
+
// We expose the font attributes list to allow
|
|
167
|
+
// the user to take other custom font attributes into account
|
|
168
|
+
// when wrapping the text.
|
|
169
|
+
FONT_ATTRIBUTES
|
|
151
170
|
},
|
|
152
171
|
|
|
153
172
|
'title': {
|
|
@@ -155,6 +174,13 @@ const textAttributesNS = {
|
|
|
155
174
|
// HTMLElement title is specified via an attribute (i.e. not an element)
|
|
156
175
|
return node instanceof SVGElement;
|
|
157
176
|
},
|
|
177
|
+
unset: function(node) {
|
|
178
|
+
$.data.remove(node, 'joint-title');
|
|
179
|
+
const titleNode = node.firstElementChild;
|
|
180
|
+
if (titleNode) {
|
|
181
|
+
titleNode.remove();
|
|
182
|
+
}
|
|
183
|
+
},
|
|
158
184
|
set: function(title, refBBox, node) {
|
|
159
185
|
var cacheName = 'joint-title';
|
|
160
186
|
var cache = $.data.get(node, cacheName);
|
package/src/dia/ports.mjs
CHANGED
|
@@ -280,6 +280,10 @@ export const elementPortPrototype = {
|
|
|
280
280
|
}));
|
|
281
281
|
},
|
|
282
282
|
|
|
283
|
+
getPortGroupNames: function() {
|
|
284
|
+
return Object.keys(this._portSettingsData.groups);
|
|
285
|
+
},
|
|
286
|
+
|
|
283
287
|
/**
|
|
284
288
|
* @param {string} groupName
|
|
285
289
|
* @returns {Object<portId, {x: number, y: number, angle: number}>}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { HoverConnect as LinkHoverConnect } from '../
|
|
1
|
+
import { HoverConnect as LinkHoverConnect } from '../cellTools/HoverConnect.mjs';
|
|
2
2
|
import V from '../V/index.mjs';
|
|
3
3
|
import * as g from '../g/index.mjs';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { isCalcExpression, evalCalcExpression } from '../util/calc.mjs';
|
|
5
|
+
import { getViewBBox } from '../cellTools/helpers.mjs';
|
|
6
6
|
|
|
7
7
|
export const HoverConnect = LinkHoverConnect.extend({
|
|
8
8
|
|
|
@@ -15,9 +15,9 @@ export const HoverConnect = LinkHoverConnect.extend({
|
|
|
15
15
|
if (typeof trackPath === 'function') {
|
|
16
16
|
trackPath = trackPath.call(this, view);
|
|
17
17
|
}
|
|
18
|
-
if (
|
|
18
|
+
if (isCalcExpression(trackPath)) {
|
|
19
19
|
const bbox = getViewBBox(view, useModelGeometry);
|
|
20
|
-
trackPath =
|
|
20
|
+
trackPath = evalCalcExpression(trackPath, bbox);
|
|
21
21
|
}
|
|
22
22
|
return new g.Path(V.normalizePathData(trackPath));
|
|
23
23
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export * from './Control.mjs';
|
|
2
|
-
export { Button, Remove } from '../linkTools/Button.mjs';
|
|
3
|
-
export { Connect } from '../linkTools/Connect.mjs';
|
|
4
|
-
export { Boundary } from '../linkTools/Boundary.mjs';
|
|
5
1
|
export { HoverConnect } from './HoverConnect.mjs';
|
|
2
|
+
|
|
3
|
+
export { Button, Remove } from '../cellTools/Button.mjs';
|
|
4
|
+
export { Connect } from '../cellTools/Connect.mjs';
|
|
5
|
+
export { Boundary } from '../cellTools/Boundary.mjs';
|
|
6
|
+
export { Control } from '../cellTools/Control.mjs';
|
package/src/env/index.mjs
CHANGED
|
@@ -7,6 +7,11 @@ export const env = {
|
|
|
7
7
|
svgforeignobject: function() {
|
|
8
8
|
return !!document.createElementNS &&
|
|
9
9
|
/SVGForeignObject/.test(({}).toString.call(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject')));
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
// works for iOS browsers too
|
|
13
|
+
isSafari: function() {
|
|
14
|
+
return /Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor);
|
|
10
15
|
}
|
|
11
16
|
},
|
|
12
17
|
|
package/src/g/rect.mjs
CHANGED
|
@@ -138,12 +138,20 @@ Rect.prototype = {
|
|
|
138
138
|
},
|
|
139
139
|
|
|
140
140
|
// @return {bool} true if point p is inside me.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
// @param {bool} strict If true, the point has to be strictly inside (not on the border).
|
|
142
|
+
containsPoint: function(p, opt) {
|
|
143
|
+
let x, y;
|
|
144
|
+
if (!p || (typeof p === 'string')) {
|
|
145
|
+
// Backwards compatibility: if the point is not provided,
|
|
146
|
+
// the point is considered to be the origin [0, 0].
|
|
147
|
+
({ x, y } = new Point(p));
|
|
148
|
+
} else {
|
|
149
|
+
// Do not create a new Point object if the point is already a Point-like object.
|
|
150
|
+
({ x = 0, y = 0 } = p);
|
|
145
151
|
}
|
|
146
|
-
return
|
|
152
|
+
return opt && opt.strict
|
|
153
|
+
? (x > this.x && x < this.x + this.width && y > this.y && y < this.y + this.height)
|
|
154
|
+
: x >= this.x && x <= this.x + this.width && y >= this.y && y <= this.y + this.height;
|
|
147
155
|
},
|
|
148
156
|
|
|
149
157
|
// @return {bool} true if rectangle `r` is inside me.
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { evalCalcAttribute, isCalcAttribute } from '../../dia/attributes/calc.mjs';
|
|
2
1
|
import * as g from '../../g/index.mjs';
|
|
3
2
|
import * as util from '../../util/index.mjs';
|
|
4
3
|
|
|
@@ -58,13 +57,13 @@ function argTransform(bbox, args) {
|
|
|
58
57
|
let { x, y, angle } = args;
|
|
59
58
|
if (util.isPercentage(x)) {
|
|
60
59
|
x = parseFloat(x) / 100 * bbox.width;
|
|
61
|
-
} else if (
|
|
62
|
-
x = Number(
|
|
60
|
+
} else if (util.isCalcExpression(x)) {
|
|
61
|
+
x = Number(util.evalCalcExpression(x, bbox));
|
|
63
62
|
}
|
|
64
63
|
if (util.isPercentage(y)) {
|
|
65
64
|
y = parseFloat(y) / 100 * bbox.height;
|
|
66
|
-
} else if (
|
|
67
|
-
y = Number(
|
|
65
|
+
} else if (util.isCalcExpression(y)) {
|
|
66
|
+
y = Number(util.evalCalcExpression(y, bbox));
|
|
68
67
|
}
|
|
69
68
|
return { x, y, angle };
|
|
70
69
|
}
|