@joint/core 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +376 -0
- package/README.md +49 -0
- package/dist/geometry.js +6486 -0
- package/dist/geometry.min.js +8 -0
- package/dist/joint.d.ts +5536 -0
- package/dist/joint.js +39629 -0
- package/dist/joint.min.js +8 -0
- package/dist/joint.nowrap.js +39626 -0
- package/dist/joint.nowrap.min.js +8 -0
- package/dist/vectorizer.js +9135 -0
- package/dist/vectorizer.min.js +8 -0
- package/dist/version.mjs +3 -0
- package/index.js +3 -0
- package/joint.mjs +27 -0
- package/package.json +192 -0
- package/src/V/annotation.mjs +0 -0
- package/src/V/index.mjs +2642 -0
- package/src/anchors/index.mjs +123 -0
- package/src/config/index.mjs +12 -0
- package/src/connectionPoints/index.mjs +202 -0
- package/src/connectionStrategies/index.mjs +73 -0
- package/src/connectors/curve.mjs +553 -0
- package/src/connectors/index.mjs +6 -0
- package/src/connectors/jumpover.mjs +452 -0
- package/src/connectors/normal.mjs +12 -0
- package/src/connectors/rounded.mjs +17 -0
- package/src/connectors/smooth.mjs +44 -0
- package/src/connectors/straight.mjs +110 -0
- package/src/dia/Cell.mjs +945 -0
- package/src/dia/CellView.mjs +1316 -0
- package/src/dia/Element.mjs +519 -0
- package/src/dia/ElementView.mjs +859 -0
- package/src/dia/Graph.mjs +1112 -0
- package/src/dia/HighlighterView.mjs +319 -0
- package/src/dia/Link.mjs +565 -0
- package/src/dia/LinkView.mjs +2207 -0
- package/src/dia/Paper.mjs +3171 -0
- package/src/dia/PaperLayer.mjs +75 -0
- package/src/dia/ToolView.mjs +69 -0
- package/src/dia/ToolsView.mjs +128 -0
- package/src/dia/attributes/calc.mjs +128 -0
- package/src/dia/attributes/connection.mjs +75 -0
- package/src/dia/attributes/defs.mjs +76 -0
- package/src/dia/attributes/eval.mjs +64 -0
- package/src/dia/attributes/index.mjs +69 -0
- package/src/dia/attributes/legacy.mjs +148 -0
- package/src/dia/attributes/offset.mjs +53 -0
- package/src/dia/attributes/props.mjs +30 -0
- package/src/dia/attributes/shape.mjs +92 -0
- package/src/dia/attributes/text.mjs +180 -0
- package/src/dia/index.mjs +13 -0
- package/src/dia/layers/GridLayer.mjs +176 -0
- package/src/dia/ports.mjs +874 -0
- package/src/elementTools/Control.mjs +153 -0
- package/src/elementTools/HoverConnect.mjs +37 -0
- package/src/elementTools/index.mjs +5 -0
- package/src/env/index.mjs +43 -0
- package/src/g/bezier.mjs +175 -0
- package/src/g/curve.mjs +956 -0
- package/src/g/ellipse.mjs +245 -0
- package/src/g/extend.mjs +64 -0
- package/src/g/geometry.helpers.mjs +58 -0
- package/src/g/index.mjs +17 -0
- package/src/g/intersection.mjs +511 -0
- package/src/g/line.bearing.mjs +30 -0
- package/src/g/line.length.mjs +5 -0
- package/src/g/line.mjs +356 -0
- package/src/g/line.squaredLength.mjs +10 -0
- package/src/g/path.mjs +2260 -0
- package/src/g/point.mjs +375 -0
- package/src/g/points.mjs +247 -0
- package/src/g/polygon.mjs +51 -0
- package/src/g/polyline.mjs +523 -0
- package/src/g/rect.mjs +556 -0
- package/src/g/types.mjs +10 -0
- package/src/highlighters/addClass.mjs +27 -0
- package/src/highlighters/index.mjs +5 -0
- package/src/highlighters/list.mjs +111 -0
- package/src/highlighters/mask.mjs +220 -0
- package/src/highlighters/opacity.mjs +17 -0
- package/src/highlighters/stroke.mjs +100 -0
- package/src/layout/index.mjs +4 -0
- package/src/layout/ports/port.mjs +188 -0
- package/src/layout/ports/portLabel.mjs +224 -0
- package/src/linkAnchors/index.mjs +76 -0
- package/src/linkTools/Anchor.mjs +235 -0
- package/src/linkTools/Arrowhead.mjs +103 -0
- package/src/linkTools/Boundary.mjs +48 -0
- package/src/linkTools/Button.mjs +121 -0
- package/src/linkTools/Connect.mjs +85 -0
- package/src/linkTools/HoverConnect.mjs +161 -0
- package/src/linkTools/Segments.mjs +393 -0
- package/src/linkTools/Vertices.mjs +253 -0
- package/src/linkTools/helpers.mjs +33 -0
- package/src/linkTools/index.mjs +8 -0
- package/src/mvc/Collection.mjs +560 -0
- package/src/mvc/Data.mjs +46 -0
- package/src/mvc/Dom/Dom.mjs +587 -0
- package/src/mvc/Dom/Event.mjs +130 -0
- package/src/mvc/Dom/animations.mjs +122 -0
- package/src/mvc/Dom/events.mjs +69 -0
- package/src/mvc/Dom/index.mjs +13 -0
- package/src/mvc/Dom/methods.mjs +392 -0
- package/src/mvc/Dom/props.mjs +77 -0
- package/src/mvc/Dom/vars.mjs +5 -0
- package/src/mvc/Events.mjs +337 -0
- package/src/mvc/Listener.mjs +33 -0
- package/src/mvc/Model.mjs +239 -0
- package/src/mvc/View.mjs +323 -0
- package/src/mvc/ViewBase.mjs +182 -0
- package/src/mvc/index.mjs +9 -0
- package/src/mvc/mvcUtils.mjs +90 -0
- package/src/polyfills/array.js +4 -0
- package/src/polyfills/base64.js +68 -0
- package/src/polyfills/index.mjs +5 -0
- package/src/polyfills/number.js +3 -0
- package/src/polyfills/string.js +3 -0
- package/src/polyfills/typedArray.js +47 -0
- package/src/routers/index.mjs +6 -0
- package/src/routers/manhattan.mjs +856 -0
- package/src/routers/metro.mjs +91 -0
- package/src/routers/normal.mjs +6 -0
- package/src/routers/oneSide.mjs +60 -0
- package/src/routers/orthogonal.mjs +323 -0
- package/src/routers/rightAngle.mjs +1056 -0
- package/src/shapes/index.mjs +3 -0
- package/src/shapes/standard.mjs +755 -0
- package/src/util/cloneCells.mjs +67 -0
- package/src/util/getRectPoint.mjs +65 -0
- package/src/util/index.mjs +5 -0
- package/src/util/svgTagTemplate.mjs +110 -0
- package/src/util/util.mjs +1754 -0
- package/src/util/utilHelpers.mjs +2402 -0
- package/src/util/wrappers.mjs +56 -0
- package/types/geometry.d.ts +815 -0
- package/types/index.d.ts +53 -0
- package/types/joint.d.ts +4391 -0
- package/types/joint.head.d.ts +12 -0
- package/types/vectorizer.d.ts +327 -0
|
@@ -0,0 +1,874 @@
|
|
|
1
|
+
import * as util from '../util/index.mjs';
|
|
2
|
+
import V from '../V/index.mjs';
|
|
3
|
+
import { Rect, Point } from '../g/index.mjs';
|
|
4
|
+
import * as Port from '../layout/ports/port.mjs';
|
|
5
|
+
import * as PortLabel from '../layout/ports/portLabel.mjs';
|
|
6
|
+
|
|
7
|
+
var PortData = function(data) {
|
|
8
|
+
|
|
9
|
+
var clonedData = util.cloneDeep(data) || {};
|
|
10
|
+
this.ports = [];
|
|
11
|
+
this.groups = {};
|
|
12
|
+
this.portLayoutNamespace = Port;
|
|
13
|
+
this.portLabelLayoutNamespace = PortLabel;
|
|
14
|
+
|
|
15
|
+
this._init(clonedData);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
PortData.prototype = {
|
|
19
|
+
|
|
20
|
+
getPorts: function() {
|
|
21
|
+
return this.ports;
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
getGroup: function(name) {
|
|
25
|
+
return this.groups[name] || {};
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
getPortsByGroup: function(groupName) {
|
|
29
|
+
|
|
30
|
+
return this.ports.filter(function(port) {
|
|
31
|
+
return port.group === groupName;
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
getGroupPortsMetrics: function(groupName, elBBox) {
|
|
36
|
+
|
|
37
|
+
var group = this.getGroup(groupName);
|
|
38
|
+
var ports = this.getPortsByGroup(groupName);
|
|
39
|
+
|
|
40
|
+
var groupPosition = group.position || {};
|
|
41
|
+
var groupPositionName = groupPosition.name;
|
|
42
|
+
var namespace = this.portLayoutNamespace;
|
|
43
|
+
if (!namespace[groupPositionName]) {
|
|
44
|
+
groupPositionName = 'left';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
var groupArgs = groupPosition.args || {};
|
|
48
|
+
var portsArgs = ports.map(function(port) {
|
|
49
|
+
return port && port.position && port.position.args;
|
|
50
|
+
});
|
|
51
|
+
var groupPortTransformations = namespace[groupPositionName](portsArgs, elBBox, groupArgs);
|
|
52
|
+
|
|
53
|
+
var accumulator = {
|
|
54
|
+
ports: ports,
|
|
55
|
+
result: []
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
util.toArray(groupPortTransformations).reduce(function(res, portTransformation, index) {
|
|
59
|
+
var port = res.ports[index];
|
|
60
|
+
res.result.push({
|
|
61
|
+
portId: port.id,
|
|
62
|
+
portTransformation: portTransformation,
|
|
63
|
+
labelTransformation: this._getPortLabelLayout(port, Point(portTransformation), elBBox),
|
|
64
|
+
portAttrs: port.attrs,
|
|
65
|
+
portSize: port.size,
|
|
66
|
+
labelSize: port.label.size
|
|
67
|
+
});
|
|
68
|
+
return res;
|
|
69
|
+
}.bind(this), accumulator);
|
|
70
|
+
|
|
71
|
+
return accumulator.result;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
_getPortLabelLayout: function(port, portPosition, elBBox) {
|
|
75
|
+
|
|
76
|
+
var namespace = this.portLabelLayoutNamespace;
|
|
77
|
+
var labelPosition = port.label.position.name || 'left';
|
|
78
|
+
|
|
79
|
+
if (namespace[labelPosition]) {
|
|
80
|
+
return namespace[labelPosition](portPosition, elBBox, port.label.position.args);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return null;
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
_init: function(data) {
|
|
87
|
+
|
|
88
|
+
// prepare groups
|
|
89
|
+
if (util.isObject(data.groups)) {
|
|
90
|
+
var groups = Object.keys(data.groups);
|
|
91
|
+
for (var i = 0, n = groups.length; i < n; i++) {
|
|
92
|
+
var key = groups[i];
|
|
93
|
+
this.groups[key] = this._evaluateGroup(data.groups[key]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// prepare ports
|
|
98
|
+
var ports = util.toArray(data.items);
|
|
99
|
+
for (var j = 0, m = ports.length; j < m; j++) {
|
|
100
|
+
this.ports.push(this._evaluatePort(ports[j]));
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
_evaluateGroup: function(group) {
|
|
105
|
+
|
|
106
|
+
return util.merge(group, {
|
|
107
|
+
position: this._getPosition(group.position, true),
|
|
108
|
+
label: this._getLabel(group, true)
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
_evaluatePort: function(port) {
|
|
113
|
+
|
|
114
|
+
var evaluated = util.assign({}, port);
|
|
115
|
+
|
|
116
|
+
var group = this.getGroup(port.group);
|
|
117
|
+
|
|
118
|
+
evaluated.markup = evaluated.markup || group.markup;
|
|
119
|
+
evaluated.attrs = util.merge({}, group.attrs, evaluated.attrs);
|
|
120
|
+
evaluated.position = this._createPositionNode(group, evaluated);
|
|
121
|
+
evaluated.label = util.merge({}, group.label, this._getLabel(evaluated));
|
|
122
|
+
evaluated.z = this._getZIndex(group, evaluated);
|
|
123
|
+
evaluated.size = util.assign({}, group.size, evaluated.size);
|
|
124
|
+
|
|
125
|
+
return evaluated;
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
_getZIndex: function(group, port) {
|
|
129
|
+
|
|
130
|
+
if (util.isNumber(port.z)) {
|
|
131
|
+
return port.z;
|
|
132
|
+
}
|
|
133
|
+
if (util.isNumber(group.z) || group.z === 'auto') {
|
|
134
|
+
return group.z;
|
|
135
|
+
}
|
|
136
|
+
return 'auto';
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
_createPositionNode: function(group, port) {
|
|
140
|
+
|
|
141
|
+
return util.merge({
|
|
142
|
+
name: 'left',
|
|
143
|
+
args: {}
|
|
144
|
+
}, group.position, { args: port.args });
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
_getPosition: function(position, setDefault) {
|
|
148
|
+
|
|
149
|
+
var args = {};
|
|
150
|
+
var positionName;
|
|
151
|
+
|
|
152
|
+
if (util.isFunction(position)) {
|
|
153
|
+
positionName = 'fn';
|
|
154
|
+
args.fn = position;
|
|
155
|
+
} else if (util.isString(position)) {
|
|
156
|
+
positionName = position;
|
|
157
|
+
} else if (position === undefined) {
|
|
158
|
+
positionName = setDefault ? 'left' : null;
|
|
159
|
+
} else if (Array.isArray(position)) {
|
|
160
|
+
positionName = 'absolute';
|
|
161
|
+
args.x = position[0];
|
|
162
|
+
args.y = position[1];
|
|
163
|
+
} else if (util.isObject(position)) {
|
|
164
|
+
positionName = position.name;
|
|
165
|
+
util.assign(args, position.args);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
var result = { args: args };
|
|
169
|
+
|
|
170
|
+
if (positionName) {
|
|
171
|
+
result.name = positionName;
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
_getLabel: function(item, setDefaults) {
|
|
177
|
+
|
|
178
|
+
var label = item.label || {};
|
|
179
|
+
|
|
180
|
+
var ret = label;
|
|
181
|
+
ret.position = this._getPosition(label.position, setDefaults);
|
|
182
|
+
|
|
183
|
+
return ret;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export const elementPortPrototype = {
|
|
188
|
+
|
|
189
|
+
_initializePorts: function() {
|
|
190
|
+
|
|
191
|
+
this._createPortData();
|
|
192
|
+
this.on('change:ports', function() {
|
|
193
|
+
|
|
194
|
+
this._processRemovedPort();
|
|
195
|
+
this._createPortData();
|
|
196
|
+
}, this);
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* remove links tied wiht just removed element
|
|
201
|
+
* @private
|
|
202
|
+
*/
|
|
203
|
+
_processRemovedPort: function() {
|
|
204
|
+
|
|
205
|
+
var current = this.get('ports') || {};
|
|
206
|
+
var currentItemsMap = {};
|
|
207
|
+
|
|
208
|
+
util.toArray(current.items).forEach(function(item) {
|
|
209
|
+
currentItemsMap[item.id] = true;
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
var previous = this.previous('ports') || {};
|
|
213
|
+
var removed = {};
|
|
214
|
+
|
|
215
|
+
util.toArray(previous.items).forEach(function(item) {
|
|
216
|
+
if (!currentItemsMap[item.id]) {
|
|
217
|
+
removed[item.id] = true;
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
var graph = this.graph;
|
|
222
|
+
if (graph && !util.isEmpty(removed)) {
|
|
223
|
+
|
|
224
|
+
var inboundLinks = graph.getConnectedLinks(this, { inbound: true });
|
|
225
|
+
inboundLinks.forEach(function(link) {
|
|
226
|
+
|
|
227
|
+
if (removed[link.get('target').port]) link.remove();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
var outboundLinks = graph.getConnectedLinks(this, { outbound: true });
|
|
231
|
+
outboundLinks.forEach(function(link) {
|
|
232
|
+
|
|
233
|
+
if (removed[link.get('source').port]) link.remove();
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* @returns {boolean}
|
|
240
|
+
*/
|
|
241
|
+
hasPorts: function() {
|
|
242
|
+
|
|
243
|
+
var ports = this.prop('ports/items');
|
|
244
|
+
return Array.isArray(ports) && ports.length > 0;
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* @param {string} id
|
|
249
|
+
* @returns {boolean}
|
|
250
|
+
*/
|
|
251
|
+
hasPort: function(id) {
|
|
252
|
+
|
|
253
|
+
return this.getPortIndex(id) !== -1;
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* @returns {Array<object>}
|
|
258
|
+
*/
|
|
259
|
+
getPorts: function() {
|
|
260
|
+
|
|
261
|
+
return util.cloneDeep(this.prop('ports/items')) || [];
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* @returns {Array<object>}
|
|
266
|
+
*/
|
|
267
|
+
getGroupPorts: function(groupName) {
|
|
268
|
+
const groupPorts = util.toArray(this.prop(['ports','items'])).filter(port => port.group === groupName);
|
|
269
|
+
return util.cloneDeep(groupPorts);
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @param {string} id
|
|
274
|
+
* @returns {object}
|
|
275
|
+
*/
|
|
276
|
+
getPort: function(id) {
|
|
277
|
+
|
|
278
|
+
return util.cloneDeep(util.toArray(this.prop('ports/items')).find(function(port) {
|
|
279
|
+
return port.id && port.id === id;
|
|
280
|
+
}));
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @param {string} groupName
|
|
285
|
+
* @returns {Object<portId, {x: number, y: number, angle: number}>}
|
|
286
|
+
*/
|
|
287
|
+
getPortsPositions: function(groupName) {
|
|
288
|
+
|
|
289
|
+
var portsMetrics = this._portSettingsData.getGroupPortsMetrics(groupName, Rect(this.size()));
|
|
290
|
+
|
|
291
|
+
return portsMetrics.reduce(function(positions, metrics) {
|
|
292
|
+
var transformation = metrics.portTransformation;
|
|
293
|
+
positions[metrics.portId] = {
|
|
294
|
+
x: transformation.x,
|
|
295
|
+
y: transformation.y,
|
|
296
|
+
angle: transformation.angle
|
|
297
|
+
};
|
|
298
|
+
return positions;
|
|
299
|
+
}, {});
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* @param {string|Port} port port id or port
|
|
304
|
+
* @returns {number} port index
|
|
305
|
+
*/
|
|
306
|
+
getPortIndex: function(port) {
|
|
307
|
+
|
|
308
|
+
var id = util.isObject(port) ? port.id : port;
|
|
309
|
+
|
|
310
|
+
if (!this._isValidPortId(id)) {
|
|
311
|
+
return -1;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return util.toArray(this.prop('ports/items')).findIndex(function(item) {
|
|
315
|
+
return item.id === id;
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* @param {object} port
|
|
321
|
+
* @param {object} [opt]
|
|
322
|
+
* @returns {joint.dia.Element}
|
|
323
|
+
*/
|
|
324
|
+
addPort: function(port, opt) {
|
|
325
|
+
|
|
326
|
+
if (!util.isObject(port) || Array.isArray(port)) {
|
|
327
|
+
throw new Error('Element: addPort requires an object.');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
var ports = util.assign([], this.prop('ports/items'));
|
|
331
|
+
ports.push(port);
|
|
332
|
+
this.prop('ports/items', ports, opt);
|
|
333
|
+
|
|
334
|
+
return this;
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* @param {string|Port|number} before
|
|
339
|
+
* @param {object} port
|
|
340
|
+
* @param {object} [opt]
|
|
341
|
+
* @returns {joint.dia.Element}
|
|
342
|
+
*/
|
|
343
|
+
insertPort: function(before, port, opt) {
|
|
344
|
+
const index = (typeof before === 'number') ? before : this.getPortIndex(before);
|
|
345
|
+
|
|
346
|
+
if (!util.isObject(port) || Array.isArray(port)) {
|
|
347
|
+
throw new Error('dia.Element: insertPort requires an object.');
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const ports = util.assign([], this.prop('ports/items'));
|
|
351
|
+
ports.splice(index, 0, port);
|
|
352
|
+
this.prop('ports/items', ports, opt);
|
|
353
|
+
|
|
354
|
+
return this;
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* @param {string} portId
|
|
359
|
+
* @param {string|object=} path
|
|
360
|
+
* @param {*=} value
|
|
361
|
+
* @param {object=} opt
|
|
362
|
+
* @returns {joint.dia.Element}
|
|
363
|
+
*/
|
|
364
|
+
portProp: function(portId, path, value, opt) {
|
|
365
|
+
|
|
366
|
+
var index = this.getPortIndex(portId);
|
|
367
|
+
|
|
368
|
+
if (index === -1) {
|
|
369
|
+
throw new Error('Element: unable to find port with id ' + portId);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
var args = Array.prototype.slice.call(arguments, 1);
|
|
373
|
+
if (Array.isArray(path)) {
|
|
374
|
+
args[0] = ['ports', 'items', index].concat(path);
|
|
375
|
+
} else if (util.isString(path)) {
|
|
376
|
+
|
|
377
|
+
// Get/set an attribute by a special path syntax that delimits
|
|
378
|
+
// nested objects by the colon character.
|
|
379
|
+
args[0] = ['ports/items/', index, '/', path].join('');
|
|
380
|
+
|
|
381
|
+
} else {
|
|
382
|
+
|
|
383
|
+
args = ['ports/items/' + index];
|
|
384
|
+
if (util.isPlainObject(path)) {
|
|
385
|
+
args.push(path);
|
|
386
|
+
args.push(value);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return this.prop.apply(this, args);
|
|
391
|
+
},
|
|
392
|
+
|
|
393
|
+
_validatePorts: function() {
|
|
394
|
+
|
|
395
|
+
var portsAttr = this.get('ports') || {};
|
|
396
|
+
|
|
397
|
+
var errorMessages = [];
|
|
398
|
+
portsAttr = portsAttr || {};
|
|
399
|
+
var ports = util.toArray(portsAttr.items);
|
|
400
|
+
|
|
401
|
+
ports.forEach(function(p) {
|
|
402
|
+
|
|
403
|
+
if (typeof p !== 'object') {
|
|
404
|
+
errorMessages.push('Element: invalid port ', p);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (!this._isValidPortId(p.id)) {
|
|
408
|
+
p.id = this.generatePortId();
|
|
409
|
+
}
|
|
410
|
+
}, this);
|
|
411
|
+
|
|
412
|
+
if (util.uniq(ports, 'id').length !== ports.length) {
|
|
413
|
+
errorMessages.push('Element: found id duplicities in ports.');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return errorMessages;
|
|
417
|
+
},
|
|
418
|
+
|
|
419
|
+
generatePortId: function() {
|
|
420
|
+
return this.generateId();
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* @param {string} id port id
|
|
425
|
+
* @returns {boolean}
|
|
426
|
+
* @private
|
|
427
|
+
*/
|
|
428
|
+
_isValidPortId: function(id) {
|
|
429
|
+
|
|
430
|
+
return id !== null && id !== undefined && !util.isObject(id);
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
addPorts: function(ports, opt) {
|
|
434
|
+
|
|
435
|
+
if (ports.length) {
|
|
436
|
+
this.prop('ports/items', util.assign([], this.prop('ports/items')).concat(ports), opt);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return this;
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
removePort: function(port, opt) {
|
|
443
|
+
const options = opt || {};
|
|
444
|
+
const index = this.getPortIndex(port);
|
|
445
|
+
if (index !== -1) {
|
|
446
|
+
const ports = util.assign([], this.prop(['ports', 'items']));
|
|
447
|
+
ports.splice(index, 1);
|
|
448
|
+
options.rewrite = true;
|
|
449
|
+
this.startBatch('port-remove');
|
|
450
|
+
this.prop(['ports', 'items'], ports, options);
|
|
451
|
+
this.stopBatch('port-remove');
|
|
452
|
+
}
|
|
453
|
+
return this;
|
|
454
|
+
},
|
|
455
|
+
|
|
456
|
+
removePorts: function(portsForRemoval, opt) {
|
|
457
|
+
let options, newPorts;
|
|
458
|
+
if (Array.isArray(portsForRemoval)) {
|
|
459
|
+
options = opt || {};
|
|
460
|
+
if (portsForRemoval.length === 0) return this.this;
|
|
461
|
+
const currentPorts = util.assign([], this.prop(['ports', 'items']));
|
|
462
|
+
newPorts = currentPorts.filter(function(cp) {
|
|
463
|
+
return !portsForRemoval.some(function(rp) {
|
|
464
|
+
const rpId = util.isObject(rp) ? rp.id : rp;
|
|
465
|
+
return cp.id === rpId;
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
} else {
|
|
469
|
+
options = portsForRemoval || {};
|
|
470
|
+
newPorts = [];
|
|
471
|
+
}
|
|
472
|
+
this.startBatch('port-remove');
|
|
473
|
+
options.rewrite = true;
|
|
474
|
+
this.prop(['ports', 'items'], newPorts, options);
|
|
475
|
+
this.stopBatch('port-remove');
|
|
476
|
+
return this;
|
|
477
|
+
},
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* @private
|
|
481
|
+
*/
|
|
482
|
+
_createPortData: function() {
|
|
483
|
+
|
|
484
|
+
var err = this._validatePorts();
|
|
485
|
+
|
|
486
|
+
if (err.length > 0) {
|
|
487
|
+
this.set('ports', this.previous('ports'));
|
|
488
|
+
throw new Error(err.join(' '));
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
var prevPortData;
|
|
492
|
+
|
|
493
|
+
if (this._portSettingsData) {
|
|
494
|
+
|
|
495
|
+
prevPortData = this._portSettingsData.getPorts();
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
this._portSettingsData = new PortData(this.get('ports'));
|
|
499
|
+
|
|
500
|
+
var curPortData = this._portSettingsData.getPorts();
|
|
501
|
+
|
|
502
|
+
if (prevPortData) {
|
|
503
|
+
|
|
504
|
+
var added = curPortData.filter(function(item) {
|
|
505
|
+
if (!prevPortData.find(function(prevPort) {
|
|
506
|
+
return prevPort.id === item.id;
|
|
507
|
+
})) {
|
|
508
|
+
return item;
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
var removed = prevPortData.filter(function(item) {
|
|
513
|
+
if (!curPortData.find(function(curPort) {
|
|
514
|
+
return curPort.id === item.id;
|
|
515
|
+
})) {
|
|
516
|
+
return item;
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
if (removed.length > 0) {
|
|
521
|
+
this.trigger('ports:remove', this, removed);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (added.length > 0) {
|
|
525
|
+
this.trigger('ports:add', this, added);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
export const elementViewPortPrototype = {
|
|
532
|
+
|
|
533
|
+
portContainerMarkup: 'g',
|
|
534
|
+
portMarkup: [{
|
|
535
|
+
tagName: 'circle',
|
|
536
|
+
selector: 'circle',
|
|
537
|
+
attributes: {
|
|
538
|
+
'r': 10,
|
|
539
|
+
'fill': '#FFFFFF',
|
|
540
|
+
'stroke': '#000000'
|
|
541
|
+
}
|
|
542
|
+
}],
|
|
543
|
+
portLabelMarkup: [{
|
|
544
|
+
tagName: 'text',
|
|
545
|
+
selector: 'text',
|
|
546
|
+
attributes: {
|
|
547
|
+
'fill': '#000000'
|
|
548
|
+
}
|
|
549
|
+
}],
|
|
550
|
+
/** @type {Object<string, {portElement: Vectorizer, portLabelElement: Vectorizer}>} */
|
|
551
|
+
_portElementsCache: null,
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* @private
|
|
555
|
+
*/
|
|
556
|
+
_initializePorts: function() {
|
|
557
|
+
this._cleanPortsCache();
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* @typedef {Object} Port
|
|
562
|
+
*
|
|
563
|
+
* @property {string} id
|
|
564
|
+
* @property {Object} position
|
|
565
|
+
* @property {Object} label
|
|
566
|
+
* @property {Object} attrs
|
|
567
|
+
* @property {string} markup
|
|
568
|
+
* @property {string} group
|
|
569
|
+
*/
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* @private
|
|
573
|
+
*/
|
|
574
|
+
_refreshPorts: function() {
|
|
575
|
+
|
|
576
|
+
this._removePorts();
|
|
577
|
+
this._cleanPortsCache();
|
|
578
|
+
this._renderPorts();
|
|
579
|
+
},
|
|
580
|
+
|
|
581
|
+
_cleanPortsCache: function() {
|
|
582
|
+
this._portElementsCache = {};
|
|
583
|
+
},
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* @private
|
|
587
|
+
*/
|
|
588
|
+
_renderPorts: function() {
|
|
589
|
+
|
|
590
|
+
// references to rendered elements without z-index
|
|
591
|
+
var elementReferences = [];
|
|
592
|
+
var elem = this._getContainerElement();
|
|
593
|
+
|
|
594
|
+
for (var i = 0, count = elem.node.childNodes.length; i < count; i++) {
|
|
595
|
+
elementReferences.push(elem.node.childNodes[i]);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
var portsGropsByZ = util.groupBy(this.model._portSettingsData.getPorts(), 'z');
|
|
599
|
+
var withoutZKey = 'auto';
|
|
600
|
+
|
|
601
|
+
// render non-z first
|
|
602
|
+
util.toArray(portsGropsByZ[withoutZKey]).forEach(function(port) {
|
|
603
|
+
var portElement = this._getPortElement(port);
|
|
604
|
+
elem.append(portElement);
|
|
605
|
+
elementReferences.push(portElement);
|
|
606
|
+
}, this);
|
|
607
|
+
|
|
608
|
+
var groupNames = Object.keys(portsGropsByZ);
|
|
609
|
+
for (var k = 0; k < groupNames.length; k++) {
|
|
610
|
+
var groupName = groupNames[k];
|
|
611
|
+
if (groupName !== withoutZKey) {
|
|
612
|
+
var z = parseInt(groupName, 10);
|
|
613
|
+
this._appendPorts(portsGropsByZ[groupName], z, elementReferences);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
this._updatePorts();
|
|
618
|
+
},
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* @returns {V}
|
|
622
|
+
* @private
|
|
623
|
+
*/
|
|
624
|
+
_getContainerElement: function() {
|
|
625
|
+
|
|
626
|
+
return this.rotatableNode || this.vel;
|
|
627
|
+
},
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* @param {Array<Port>}ports
|
|
631
|
+
* @param {number} z
|
|
632
|
+
* @param refs
|
|
633
|
+
* @private
|
|
634
|
+
*/
|
|
635
|
+
_appendPorts: function(ports, z, refs) {
|
|
636
|
+
|
|
637
|
+
var containerElement = this._getContainerElement();
|
|
638
|
+
var portElements = util.toArray(ports).map(this._getPortElement, this);
|
|
639
|
+
|
|
640
|
+
if (refs[z] || z < 0) {
|
|
641
|
+
V(refs[Math.max(z, 0)]).before(portElements);
|
|
642
|
+
} else {
|
|
643
|
+
containerElement.append(portElements);
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Try to get element from cache,
|
|
649
|
+
* @param port
|
|
650
|
+
* @returns {*}
|
|
651
|
+
* @private
|
|
652
|
+
*/
|
|
653
|
+
_getPortElement: function(port) {
|
|
654
|
+
|
|
655
|
+
if (this._portElementsCache[port.id]) {
|
|
656
|
+
return this._portElementsCache[port.id].portElement;
|
|
657
|
+
}
|
|
658
|
+
return this._createPortElement(port);
|
|
659
|
+
},
|
|
660
|
+
|
|
661
|
+
findPortNodes: function(portId, selector) {
|
|
662
|
+
const portCache = this._portElementsCache[portId];
|
|
663
|
+
if (!portCache) return [];
|
|
664
|
+
if (!selector) return [portCache.portContentElement.node];
|
|
665
|
+
const portRoot = portCache.portElement.node;
|
|
666
|
+
const portSelectors = portCache.portSelectors;
|
|
667
|
+
return this.findBySelector(selector, portRoot, portSelectors);
|
|
668
|
+
},
|
|
669
|
+
|
|
670
|
+
findPortNode: function(portId, selector) {
|
|
671
|
+
const [node = null] = this.findPortNodes(portId, selector);
|
|
672
|
+
return node;
|
|
673
|
+
},
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* @private
|
|
677
|
+
*/
|
|
678
|
+
_updatePorts: function() {
|
|
679
|
+
|
|
680
|
+
// layout ports without group
|
|
681
|
+
this._updatePortGroup(undefined);
|
|
682
|
+
// layout ports with explicit group
|
|
683
|
+
var groupsNames = Object.keys(this.model._portSettingsData.groups);
|
|
684
|
+
groupsNames.forEach(this._updatePortGroup, this);
|
|
685
|
+
},
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* @private
|
|
689
|
+
*/
|
|
690
|
+
_removePorts: function() {
|
|
691
|
+
util.invoke(this._portElementsCache, 'portElement.remove');
|
|
692
|
+
},
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* @param {Port} port
|
|
696
|
+
* @returns {V}
|
|
697
|
+
* @private
|
|
698
|
+
*/
|
|
699
|
+
_createPortElement: function(port) {
|
|
700
|
+
|
|
701
|
+
let portElement;
|
|
702
|
+
let labelElement;
|
|
703
|
+
let labelSelectors;
|
|
704
|
+
let portSelectors;
|
|
705
|
+
|
|
706
|
+
var portContainerElement = V(this.portContainerMarkup).addClass('joint-port');
|
|
707
|
+
|
|
708
|
+
var portMarkup = this._getPortMarkup(port);
|
|
709
|
+
if (Array.isArray(portMarkup)) {
|
|
710
|
+
var portDoc = this.parseDOMJSON(portMarkup, portContainerElement.node);
|
|
711
|
+
var portFragment = portDoc.fragment;
|
|
712
|
+
if (portFragment.childNodes.length > 1) {
|
|
713
|
+
portElement = V('g').append(portFragment);
|
|
714
|
+
} else {
|
|
715
|
+
portElement = V(portFragment.firstChild);
|
|
716
|
+
}
|
|
717
|
+
portSelectors = portDoc.selectors;
|
|
718
|
+
} else {
|
|
719
|
+
portElement = V(portMarkup);
|
|
720
|
+
if (Array.isArray(portElement)) {
|
|
721
|
+
portElement = V('g').append(portElement);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
if (!portElement) {
|
|
726
|
+
throw new Error('ElementView: Invalid port markup.');
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
portElement.attr({
|
|
730
|
+
'port': port.id,
|
|
731
|
+
'port-group': port.group
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
const labelMarkupDef = this._getPortLabelMarkup(port.label);
|
|
735
|
+
if (Array.isArray(labelMarkupDef)) {
|
|
736
|
+
// JSON Markup
|
|
737
|
+
const { fragment, selectors } = this.parseDOMJSON(labelMarkupDef, portContainerElement.node);
|
|
738
|
+
const childCount = fragment.childNodes.length;
|
|
739
|
+
if (childCount > 0) {
|
|
740
|
+
labelSelectors = selectors;
|
|
741
|
+
labelElement = (childCount === 1) ? V(fragment.firstChild) : V('g').append(fragment);
|
|
742
|
+
}
|
|
743
|
+
} else {
|
|
744
|
+
// String Markup
|
|
745
|
+
labelElement = V(labelMarkupDef);
|
|
746
|
+
if (Array.isArray(labelElement)) {
|
|
747
|
+
labelElement = V('g').append(labelElement);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
var portContainerSelectors;
|
|
752
|
+
if (portSelectors && labelSelectors) {
|
|
753
|
+
for (var key in labelSelectors) {
|
|
754
|
+
if (portSelectors[key] && key !== this.selector) throw new Error('ElementView: selectors within port must be unique.');
|
|
755
|
+
}
|
|
756
|
+
portContainerSelectors = util.assign({}, portSelectors, labelSelectors);
|
|
757
|
+
} else {
|
|
758
|
+
portContainerSelectors = portSelectors || labelSelectors || {};
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// The `portRootSelector` points to the root SVGNode of the port.
|
|
762
|
+
// Either the implicit wrapping group <g/> in case the port consist of multiple SVGNodes.
|
|
763
|
+
// Or the single SVGNode of the port.
|
|
764
|
+
const portRootSelector = 'portRoot';
|
|
765
|
+
// The `labelRootSelector` points to the root SVGNode of the label.
|
|
766
|
+
const labelRootSelector = 'labelRoot';
|
|
767
|
+
// The `labelTextSelector` points to all text SVGNodes of the label.
|
|
768
|
+
const labelTextSelector = 'labelText';
|
|
769
|
+
|
|
770
|
+
if (!(portRootSelector in portContainerSelectors)) {
|
|
771
|
+
portContainerSelectors[portRootSelector] = portElement.node;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (labelElement) {
|
|
775
|
+
const labelNode = labelElement.node;
|
|
776
|
+
if (!(labelRootSelector in portContainerSelectors)) {
|
|
777
|
+
portContainerSelectors[labelRootSelector] = labelNode;
|
|
778
|
+
}
|
|
779
|
+
if (!(labelTextSelector in portContainerSelectors)) {
|
|
780
|
+
// If the label is a <text> element, we can use it directly.
|
|
781
|
+
// Otherwise, we need to find the <text> element within the label.
|
|
782
|
+
const labelTextNode = (labelElement.tagName() === 'TEXT')
|
|
783
|
+
? labelNode
|
|
784
|
+
: Array.from(labelNode.querySelectorAll('text'));
|
|
785
|
+
portContainerSelectors[labelTextSelector] = labelTextNode;
|
|
786
|
+
if (!labelSelectors) labelSelectors = {};
|
|
787
|
+
labelSelectors[labelTextSelector] = labelTextNode;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
portContainerElement.append(portElement.addClass('joint-port-body'));
|
|
792
|
+
if (labelElement) {
|
|
793
|
+
portContainerElement.append(labelElement.addClass('joint-port-label'));
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
this._portElementsCache[port.id] = {
|
|
797
|
+
portElement: portContainerElement,
|
|
798
|
+
portLabelElement: labelElement,
|
|
799
|
+
portSelectors: portContainerSelectors,
|
|
800
|
+
portLabelSelectors: labelSelectors,
|
|
801
|
+
portContentElement: portElement,
|
|
802
|
+
portContentSelectors: portSelectors
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
return portContainerElement;
|
|
806
|
+
},
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* @param {string=} groupName
|
|
810
|
+
* @private
|
|
811
|
+
*/
|
|
812
|
+
_updatePortGroup: function(groupName) {
|
|
813
|
+
|
|
814
|
+
var elementBBox = Rect(this.model.size());
|
|
815
|
+
var portsMetrics = this.model._portSettingsData.getGroupPortsMetrics(groupName, elementBBox);
|
|
816
|
+
|
|
817
|
+
for (var i = 0, n = portsMetrics.length; i < n; i++) {
|
|
818
|
+
var metrics = portsMetrics[i];
|
|
819
|
+
var portId = metrics.portId;
|
|
820
|
+
var cached = this._portElementsCache[portId] || {};
|
|
821
|
+
var portTransformation = metrics.portTransformation;
|
|
822
|
+
var labelTransformation = metrics.labelTransformation;
|
|
823
|
+
if (labelTransformation && cached.portLabelElement) {
|
|
824
|
+
this.updateDOMSubtreeAttributes(cached.portLabelElement.node, labelTransformation.attrs, {
|
|
825
|
+
rootBBox: new Rect(metrics.labelSize),
|
|
826
|
+
selectors: cached.portLabelSelectors
|
|
827
|
+
});
|
|
828
|
+
this.applyPortTransform(cached.portLabelElement, labelTransformation, (-portTransformation.angle || 0));
|
|
829
|
+
}
|
|
830
|
+
this.updateDOMSubtreeAttributes(cached.portElement.node, metrics.portAttrs, {
|
|
831
|
+
rootBBox: new Rect(metrics.portSize),
|
|
832
|
+
selectors: cached.portSelectors
|
|
833
|
+
});
|
|
834
|
+
this.applyPortTransform(cached.portElement, portTransformation);
|
|
835
|
+
}
|
|
836
|
+
},
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* @param {Vectorizer} element
|
|
840
|
+
* @param {{dx:number, dy:number, angle: number, attrs: Object, x:number: y:number}} transformData
|
|
841
|
+
* @param {number=} initialAngle
|
|
842
|
+
* @constructor
|
|
843
|
+
*/
|
|
844
|
+
applyPortTransform: function(element, transformData, initialAngle) {
|
|
845
|
+
|
|
846
|
+
var matrix = V.createSVGMatrix()
|
|
847
|
+
.rotate(initialAngle || 0)
|
|
848
|
+
.translate(transformData.x || 0, transformData.y || 0)
|
|
849
|
+
.rotate(transformData.angle || 0);
|
|
850
|
+
|
|
851
|
+
element.transform(matrix, { absolute: true });
|
|
852
|
+
},
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* @param {Port} port
|
|
856
|
+
* @returns {string}
|
|
857
|
+
* @private
|
|
858
|
+
*/
|
|
859
|
+
_getPortMarkup: function(port) {
|
|
860
|
+
|
|
861
|
+
return port.markup || this.model.get('portMarkup') || this.model.portMarkup || this.portMarkup;
|
|
862
|
+
},
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* @param {Object} label
|
|
866
|
+
* @returns {string}
|
|
867
|
+
* @private
|
|
868
|
+
*/
|
|
869
|
+
_getPortLabelMarkup: function(label) {
|
|
870
|
+
|
|
871
|
+
return label.markup || this.model.get('portLabelMarkup') || this.model.portLabelMarkup || this.portLabelMarkup;
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
|