@elyra/canvas 12.36.0 → 12.37.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/dist/canvas-constants-766c12a9.js +2 -0
- package/dist/{canvas-constants-85883d4c.js.map → canvas-constants-766c12a9.js.map} +1 -1
- package/dist/canvas-constants-f4219d26.js +2 -0
- package/dist/{canvas-constants-d8652829.js.map → canvas-constants-f4219d26.js.map} +1 -1
- package/dist/canvas-controller-62b66fc8.js +2 -0
- package/dist/canvas-controller-62b66fc8.js.map +1 -0
- package/dist/canvas-controller-76f68572.js +2 -0
- package/dist/canvas-controller-76f68572.js.map +1 -0
- package/dist/common-canvas-339584b8.js +2 -0
- package/dist/common-canvas-339584b8.js.map +1 -0
- package/dist/common-canvas-c728f092.js +2 -0
- package/dist/common-canvas-c728f092.js.map +1 -0
- package/dist/common-canvas.es.js +1 -1
- package/dist/common-canvas.es.js.map +1 -1
- package/dist/common-canvas.js +1 -1
- package/dist/common-canvas.js.map +1 -1
- package/dist/common-properties-009d29d6.js +2 -0
- package/dist/{common-properties-a34905c3.js.map → common-properties-009d29d6.js.map} +1 -1
- package/dist/common-properties-99d34523.js +2 -0
- package/dist/{common-properties-d5775a12.js.map → common-properties-99d34523.js.map} +1 -1
- package/dist/context-menu-wrapper-624a1e7c.js +2 -0
- package/dist/context-menu-wrapper-624a1e7c.js.map +1 -0
- package/dist/context-menu-wrapper-ab018d6e.js +2 -0
- package/dist/context-menu-wrapper-ab018d6e.js.map +1 -0
- package/dist/{datarecord-metadata-v3-schema-531c7b07.js → datarecord-metadata-v3-schema-1f21696a.js} +2 -2
- package/dist/{datarecord-metadata-v3-schema-531c7b07.js.map → datarecord-metadata-v3-schema-1f21696a.js.map} +1 -1
- package/dist/{datarecord-metadata-v3-schema-28d4d7bb.js → datarecord-metadata-v3-schema-c2ad8862.js} +2 -2
- package/dist/{datarecord-metadata-v3-schema-28d4d7bb.js.map → datarecord-metadata-v3-schema-c2ad8862.js.map} +1 -1
- package/dist/flexible-table-4cf19e2e.js +2 -0
- package/dist/flexible-table-4cf19e2e.js.map +1 -0
- package/dist/flexible-table-8d10f5c9.js +2 -0
- package/dist/flexible-table-8d10f5c9.js.map +1 -0
- package/dist/{icon-909437d7.js → icon-5e06bfe1.js} +2 -2
- package/dist/{icon-909437d7.js.map → icon-5e06bfe1.js.map} +1 -1
- package/dist/{icon-de9c6b33.js → icon-8433d369.js} +2 -2
- package/dist/{icon-de9c6b33.js.map → icon-8433d369.js.map} +1 -1
- package/dist/{index-9960d3bf.js → index-2a61be58.js} +2 -2
- package/dist/{index-9960d3bf.js.map → index-2a61be58.js.map} +1 -1
- package/dist/{index-61e4a113.js → index-9a355ed6.js} +2 -2
- package/dist/{index-61e4a113.js.map → index-9a355ed6.js.map} +1 -1
- package/dist/lib/canvas-controller.es.js +1 -1
- package/dist/lib/canvas-controller.js +1 -1
- package/dist/lib/canvas.es.js +1 -1
- package/dist/lib/canvas.js +1 -1
- package/dist/lib/context-menu.es.js +1 -1
- package/dist/lib/context-menu.js +1 -1
- package/dist/lib/properties/field-picker.es.js +1 -1
- package/dist/lib/properties/field-picker.js +1 -1
- package/dist/lib/properties/flexible-table.es.js +1 -1
- package/dist/lib/properties/flexible-table.js +1 -1
- package/dist/lib/properties.es.js +1 -1
- package/dist/lib/properties.js +1 -1
- package/dist/styles/common-canvas.min.css +1 -1
- package/dist/styles/common-canvas.min.css.map +1 -1
- package/dist/{toolbar-cdb38f4a.js → toolbar-76733735.js} +2 -2
- package/dist/{toolbar-cdb38f4a.js.map → toolbar-76733735.js.map} +1 -1
- package/dist/{toolbar-3b5a592c.js → toolbar-85e1e463.js} +2 -2
- package/dist/{toolbar-3b5a592c.js.map → toolbar-85e1e463.js.map} +1 -1
- package/locales/common-canvas/locales/en.json +1 -1
- package/locales/common-canvas/locales/eo.json +1 -1
- package/package.json +2 -3
- package/src/common-canvas/canvas-controller.js +7 -0
- package/src/common-canvas/cc-contents.jsx +42 -4
- package/src/common-canvas/common-canvas-utils.js +9 -2
- package/src/common-canvas/common-canvas.scss +30 -3
- package/src/common-canvas/constants/canvas-constants.js +0 -6
- package/src/common-canvas/svg-canvas-d3.js +1 -3
- package/src/common-canvas/svg-canvas-d3.scss +0 -26
- package/src/common-canvas/svg-canvas-pipeline.js +6 -0
- package/src/common-canvas/svg-canvas-renderer.js +488 -2815
- package/src/common-canvas/svg-canvas-utils-drag-det-link.js +491 -0
- package/src/common-canvas/svg-canvas-utils-drag-new-link.js +595 -0
- package/src/common-canvas/svg-canvas-utils-drag-objects.js +832 -0
- package/src/common-canvas/svg-canvas-utils-external.js +82 -16
- package/src/common-canvas/svg-canvas-utils-zoom.js +780 -0
- package/src/context-menu/common-context-menu.jsx +57 -26
- package/src/context-menu/context-menu.scss +33 -53
- package/src/notification-panel/notification-panel.jsx +6 -1
- package/src/notification-panel/notification-panel.scss +4 -2
- package/src/palette/palette-content-list-item.jsx +23 -7
- package/stats.html +1 -1
- package/dist/canvas-constants-85883d4c.js +0 -2
- package/dist/canvas-constants-d8652829.js +0 -2
- package/dist/canvas-controller-7beb80ec.js +0 -2
- package/dist/canvas-controller-7beb80ec.js.map +0 -1
- package/dist/canvas-controller-b70d0f98.js +0 -2
- package/dist/canvas-controller-b70d0f98.js.map +0 -1
- package/dist/common-canvas-86fb7d21.js +0 -2
- package/dist/common-canvas-86fb7d21.js.map +0 -1
- package/dist/common-canvas-a6bfce5e.js +0 -2
- package/dist/common-canvas-a6bfce5e.js.map +0 -1
- package/dist/common-properties-a34905c3.js +0 -2
- package/dist/common-properties-d5775a12.js +0 -2
- package/dist/context-menu-wrapper-19a1cf72.js +0 -2
- package/dist/context-menu-wrapper-19a1cf72.js.map +0 -1
- package/dist/context-menu-wrapper-c3a98c63.js +0 -2
- package/dist/context-menu-wrapper-c3a98c63.js.map +0 -1
- package/dist/extends-093996c9.js +0 -2
- package/dist/extends-093996c9.js.map +0 -1
- package/dist/extends-1b35a664.js +0 -2
- package/dist/extends-1b35a664.js.map +0 -1
- package/dist/flexible-table-b9c08069.js +0 -2
- package/dist/flexible-table-b9c08069.js.map +0 -1
- package/dist/flexible-table-ddd6132b.js +0 -2
- package/dist/flexible-table-ddd6132b.js.map +0 -1
|
@@ -19,12 +19,9 @@
|
|
|
19
19
|
/* eslint no-lonely-if: "off" */
|
|
20
20
|
|
|
21
21
|
// Import just the D3 modules that are needed and combine them as the 'd3' object.
|
|
22
|
-
import * as d3Drag from "d3-drag";
|
|
23
|
-
import * as d3Ease from "d3-ease";
|
|
24
22
|
import * as d3Selection from "d3-selection";
|
|
25
23
|
import * as d3Fetch from "d3-fetch";
|
|
26
|
-
|
|
27
|
-
const d3 = Object.assign({}, d3Drag, d3Ease, d3Selection, d3Fetch, d3Zoom);
|
|
24
|
+
const d3 = Object.assign({}, d3Selection, d3Fetch);
|
|
28
25
|
|
|
29
26
|
const markdownIt = require("markdown-it")({
|
|
30
27
|
html: false, // Don't allow HTML to be executed in comments.
|
|
@@ -32,17 +29,15 @@ const markdownIt = require("markdown-it")({
|
|
|
32
29
|
typographer: true
|
|
33
30
|
});
|
|
34
31
|
|
|
35
|
-
import {
|
|
36
|
-
import { addNodeExternalObject, addDecExternalObject, removeExternalObject } from "./svg-canvas-utils-external.js";
|
|
32
|
+
import { escape as escapeText, forOwn, get } from "lodash";
|
|
37
33
|
import { ASSOC_RIGHT_SIDE_CURVE, ASSOCIATION_LINK, NODE_LINK, COMMENT_LINK,
|
|
38
34
|
ASSOC_VAR_CURVE_LEFT, ASSOC_VAR_CURVE_RIGHT, ASSOC_VAR_DOUBLE_BACK_RIGHT,
|
|
39
35
|
LINK_TYPE_CURVE, LINK_TYPE_ELBOW, LINK_TYPE_STRAIGHT,
|
|
40
36
|
LINK_DIR_LEFT_RIGHT, LINK_DIR_TOP_BOTTOM, LINK_DIR_BOTTOM_TOP,
|
|
41
37
|
LINK_SELECTION_NONE, LINK_SELECTION_HANDLES, LINK_SELECTION_DETACHABLE,
|
|
42
|
-
CONTEXT_MENU_BUTTON, DEC_LINK, DEC_NODE,
|
|
43
|
-
NODE_MENU_ICON, SUPER_NODE_EXPAND_ICON,
|
|
38
|
+
CONTEXT_MENU_BUTTON, DEC_LINK, DEC_NODE, EDIT_ICON,
|
|
39
|
+
NODE_MENU_ICON, SUPER_NODE_EXPAND_ICON, PORT_OBJECT_IMAGE,
|
|
44
40
|
TIP_TYPE_NODE, TIP_TYPE_PORT, TIP_TYPE_DEC, TIP_TYPE_LINK,
|
|
45
|
-
INTERACTION_MOUSE, INTERACTION_TRACKPAD, INTERACTION_CARBON,
|
|
46
41
|
USE_DEFAULT_ICON, USE_DEFAULT_EXT_ICON,
|
|
47
42
|
SUPER_NODE, SNAP_TO_GRID_AFTER, SNAP_TO_GRID_DURING,
|
|
48
43
|
NORTH, SOUTH, EAST, WEST }
|
|
@@ -56,15 +51,16 @@ import SvgCanvasNodes from "./svg-canvas-utils-nodes.js";
|
|
|
56
51
|
import SvgCanvasComments from "./svg-canvas-utils-comments.js";
|
|
57
52
|
import SvgCanvasLinks from "./svg-canvas-utils-links.js";
|
|
58
53
|
import SvgCanvasDecs from "./svg-canvas-utils-decs.js";
|
|
54
|
+
import SvgCanvasExternal from "./svg-canvas-utils-external.js";
|
|
59
55
|
import SvgCanvasTextArea from "./svg-canvas-utils-textarea.js";
|
|
56
|
+
import SvgCanvasDragObject from "./svg-canvas-utils-drag-objects.js";
|
|
57
|
+
import SvgCanvasDragNewLink from "./svg-canvas-utils-drag-new-link.js";
|
|
58
|
+
import SvgCanvasDragDetLink from "./svg-canvas-utils-drag-det-link.js";
|
|
59
|
+
import SvgCanvasZoom from "./svg-canvas-utils-zoom.js";
|
|
60
60
|
import SVGCanvasPipeline from "./svg-canvas-pipeline";
|
|
61
61
|
|
|
62
62
|
const NINETY_DEGREES = 90;
|
|
63
63
|
|
|
64
|
-
const INPUT_TYPE = "input_type";
|
|
65
|
-
const OUTPUT_TYPE = "output_type";
|
|
66
|
-
|
|
67
|
-
|
|
68
64
|
export default class SVGCanvasRenderer {
|
|
69
65
|
constructor(pipelineId, canvasDiv, canvasController, canvasInfo, selectionInfo, breadcrumbs, nodeLayout, canvasLayout, config, supernodeInfo = {}) {
|
|
70
66
|
this.logger = new Logger(["SVGCanvasRenderer", "PipeId", pipelineId]);
|
|
@@ -92,6 +88,11 @@ export default class SVGCanvasRenderer {
|
|
|
92
88
|
this.commentUtils = new SvgCanvasComments();
|
|
93
89
|
this.linkUtils = new SvgCanvasLinks(this.config, this.canvasLayout, this.nodeUtils, this.commentUtils);
|
|
94
90
|
this.decUtils = new SvgCanvasDecs(this.canvasLayout);
|
|
91
|
+
this.dragObjectUtils = new SvgCanvasDragObject(this);
|
|
92
|
+
this.dragNewLinkUtils = new SvgCanvasDragNewLink(this);
|
|
93
|
+
this.dragDetLinkUtils = new SvgCanvasDragDetLink(this);
|
|
94
|
+
this.zoomUtils = new SvgCanvasZoom(this);
|
|
95
|
+
this.externalUtils = new SvgCanvasExternal(this);
|
|
95
96
|
this.svgCanvasTextArea = new SvgCanvasTextArea(
|
|
96
97
|
this.config,
|
|
97
98
|
this.dispUtils,
|
|
@@ -108,44 +109,6 @@ export default class SVGCanvasRenderer {
|
|
|
108
109
|
this.dispUtils.setDisplayState();
|
|
109
110
|
this.logger.log(this.dispUtils.getDisplayStateMsg());
|
|
110
111
|
|
|
111
|
-
// Initialize zoom variables
|
|
112
|
-
this.initializeZoomVariables();
|
|
113
|
-
|
|
114
|
-
// Dimensions for extent of canvas scaling
|
|
115
|
-
this.minScaleExtent = 0.2;
|
|
116
|
-
this.maxScaleExtent = 1.8;
|
|
117
|
-
|
|
118
|
-
// Allows us to track the sizing behavior of comments
|
|
119
|
-
this.commentSizing = false;
|
|
120
|
-
this.commentSizingDirection = "";
|
|
121
|
-
|
|
122
|
-
// Allows us to track the sizing behavior of nodes
|
|
123
|
-
this.nodeSizing = false;
|
|
124
|
-
this.nodeSizingDirection = "";
|
|
125
|
-
this.nodeSizingObjectsInfo = {};
|
|
126
|
-
this.nodeSizingDetLinksInfo = {};
|
|
127
|
-
|
|
128
|
-
// Keeps track of the size and position, at the start of the sizing event,
|
|
129
|
-
// of the object (node or comment) being sized.
|
|
130
|
-
this.resizeObjInitialInfo = null;
|
|
131
|
-
|
|
132
|
-
// Keeps track of the size and position, during a sizing event, of the
|
|
133
|
-
// object (node or comment) being sized, before it is snapped to grid.
|
|
134
|
-
this.notSnappedXPos = 0;
|
|
135
|
-
this.notSnappedYPos = 0;
|
|
136
|
-
this.notSnappedWidth = 0;
|
|
137
|
-
this.notSnappedHeight = 0;
|
|
138
|
-
|
|
139
|
-
// Allows us to record the drag behavior or nodes and comments.
|
|
140
|
-
this.dragging = false;
|
|
141
|
-
this.dragOffsetX = 0;
|
|
142
|
-
this.dragOffsetY = 0;
|
|
143
|
-
this.dragRunningX = 0;
|
|
144
|
-
this.dragRunningY = 0;
|
|
145
|
-
this.dragObjects = [];
|
|
146
|
-
this.dragStartX = 0;
|
|
147
|
-
this.dragStartY = 0;
|
|
148
|
-
|
|
149
112
|
// The data link a node is currently being dragged over. It will be null
|
|
150
113
|
// when the node being dragged is not over a data link.
|
|
151
114
|
this.dragOverLink = null;
|
|
@@ -165,101 +128,32 @@ export default class SVGCanvasRenderer {
|
|
|
165
128
|
// option is switched on.
|
|
166
129
|
this.dragNewLinkOverNode = null;
|
|
167
130
|
|
|
168
|
-
// Flag to indicate if the current drag operation is for a node that can
|
|
169
|
-
// be inserted into a link. Such a node would need input and output ports.
|
|
170
|
-
this.existingNodeInsertableIntoLink = false;
|
|
171
|
-
|
|
172
|
-
// Flag to indicate if the current drag operation is for a node that can
|
|
173
|
-
// be attached to a detached link.
|
|
174
|
-
this.existingNodeAttachableToDetachedLinks = false;
|
|
175
|
-
|
|
176
|
-
// Flag to indicate when the space key is down (used when dragging).
|
|
177
|
-
this.spaceKeyPressed = false;
|
|
178
|
-
|
|
179
|
-
// Flag to indicate when a zoom is invoked programmatically.
|
|
180
|
-
this.zoomingAction = false;
|
|
181
|
-
|
|
182
|
-
// Keep track of when the context menu has been closed, so we don't remove
|
|
183
|
-
// selections when a context menu is closed during a zoom gesture.
|
|
184
|
-
this.contextMenuClosedOnZoom = false;
|
|
185
|
-
|
|
186
|
-
// Keep track of when text editing has been closed, so we don't remove
|
|
187
|
-
// selections when that happens during a zoom gesture.
|
|
188
|
-
this.textEditingClosedOnZoom = false;
|
|
189
|
-
|
|
190
|
-
// Used to monitor the region selection rectangle.
|
|
191
|
-
this.regionSelect = false;
|
|
192
|
-
|
|
193
|
-
// Used to track the start of the zoom.
|
|
194
|
-
this.zoomStartPoint = { x: 0, y: 0, k: 0, startX: 0, startY: 0 };
|
|
195
|
-
|
|
196
|
-
// I was not able to figure out how to use the zoom filter method to
|
|
197
|
-
// allow mousedown and mousemove messages to go through to the canvas to
|
|
198
|
-
// do region selection. Therefore I had to implement region selection in
|
|
199
|
-
// the zoom methods. This has the side effect that, when a region is
|
|
200
|
-
// selected, d3Event.transform.x and d3Event.transform.y are incremented
|
|
201
|
-
// even though the objects in the canvas have not moved. The values below
|
|
202
|
-
// are used to store the current transform x and y amounts at the beginning
|
|
203
|
-
// of the region selection and then restore those amounts at the end of
|
|
204
|
-
// the region selection.
|
|
205
|
-
this.regionStartTransformX = 0;
|
|
206
|
-
this.regionStartTransformY = 0;
|
|
207
|
-
|
|
208
|
-
// Object to store variables for dynamically drawing a new link line. The
|
|
209
|
-
// existence of this object means a new link is being drawn. A null means
|
|
210
|
-
// no link is currently being drawn.
|
|
211
|
-
this.drawingNewLinkData = null;
|
|
212
|
-
|
|
213
|
-
// Create a drag handler for use with nodes and comments.
|
|
214
|
-
this.dragHandler = d3.drag()
|
|
215
|
-
.on("start", this.dragStart.bind(this))
|
|
216
|
-
.on("drag", this.dragMove.bind(this))
|
|
217
|
-
.on("end", this.dragEnd.bind(this));
|
|
218
|
-
|
|
219
|
-
this.draggingLinkData = null;
|
|
220
|
-
|
|
221
|
-
// Create a drag handler that can be used with draggable ends of
|
|
222
|
-
// detached links.
|
|
223
|
-
this.dragLinkHandler = d3.drag()
|
|
224
|
-
.on("start", this.dragStartLinkHandle.bind(this))
|
|
225
|
-
.on("drag", this.dragMoveLinkHandle.bind(this))
|
|
226
|
-
.on("end", this.dragEndLinkHandle.bind(this));
|
|
227
|
-
|
|
228
|
-
// Create a zoom object for use with the canvas.
|
|
229
|
-
this.zoom =
|
|
230
|
-
d3.zoom()
|
|
231
|
-
.trackpad(this.config.enableInteractionType === INTERACTION_TRACKPAD)
|
|
232
|
-
.preventBackGesture(true)
|
|
233
|
-
.wheelDelta((d3Event) => -d3Event.deltaY * (this.config.enableInteractionType === INTERACTION_TRACKPAD ? 0.02 : 0.002))
|
|
234
|
-
.scaleExtent([this.minScaleExtent, this.maxScaleExtent])
|
|
235
|
-
.on("start", this.zoomStart.bind(this))
|
|
236
|
-
.on("zoom", this.zoomAction.bind(this))
|
|
237
|
-
.on("end", this.zoomEnd.bind(this));
|
|
238
|
-
|
|
239
131
|
this.initializeGhostDiv();
|
|
240
132
|
|
|
241
133
|
this.canvasSVG = this.createCanvasSVG();
|
|
242
134
|
this.canvasDefs = this.canvasSVG.selectChildren("defs");
|
|
243
|
-
this.canvasGrp = this.createCanvasGroup(this.canvasSVG, "d3-canvas-group"); // Group to contain all canvas objects
|
|
244
|
-
this.canvasUnderlay = this.createCanvasUnderlay(this.canvasGrp, "d3-canvas-underlay"); // Put underlay rectangle under comments, nodes and links
|
|
245
|
-
this.commentsGrp = this.createCanvasGroup(this.canvasGrp, "d3-comments-group"); // Group to always position comments under nodes and links
|
|
246
|
-
this.nodesLinksGrp = this.createCanvasGroup(this.canvasGrp, "d3-nodes-links-group"); // Group to position nodes and links over comments
|
|
247
|
-
this.boundingRectsGrp = this.createBoundingRectanglesGrp(this.canvasGrp, "d3-bounding-rect-group"); // Group to optionally add bounding rectangles over all objects
|
|
248
135
|
|
|
136
|
+
// Group to contain all canvas objects
|
|
137
|
+
this.canvasGrp = this.createCanvasGroup(this.canvasSVG, "d3-canvas-group");
|
|
138
|
+
|
|
139
|
+
// Put underlay rectangle under comments, nodes and links
|
|
140
|
+
this.canvasUnderlay = this.createCanvasUnderlay(this.canvasGrp, "d3-canvas-underlay");
|
|
141
|
+
|
|
142
|
+
// Group to always position comments under nodes and links
|
|
143
|
+
this.commentsGrp = this.createCanvasGroup(this.canvasGrp, "d3-comments-group");
|
|
144
|
+
|
|
145
|
+
// Group to position nodes and links over comments
|
|
146
|
+
this.nodesLinksGrp = this.createCanvasGroup(this.canvasGrp, "d3-nodes-links-group");
|
|
147
|
+
|
|
148
|
+
// Group to optionally add bounding rectangles over all objects
|
|
149
|
+
this.boundingRectsGrp = this.createBoundingRectanglesGrp(this.canvasGrp, "d3-bounding-rect-group");
|
|
249
150
|
|
|
250
151
|
this.resetCanvasSVGBehaviors();
|
|
251
152
|
|
|
252
153
|
this.displayCanvas();
|
|
253
154
|
|
|
254
155
|
if (this.dispUtils.isDisplayingFullPage()) {
|
|
255
|
-
this.restoreZoom();
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// If we are showing a sub-flow in full screen mode, or the options is
|
|
259
|
-
// switched on to always display it, show the 'back to parent' control.
|
|
260
|
-
if (this.dispUtils.isDisplayingSubFlowFullPage() ||
|
|
261
|
-
this.canvasLayout.alwaysDisplayBackToParentFlow) {
|
|
262
|
-
this.addBackToParentFlowArrow(this.canvasSVG);
|
|
156
|
+
this.zoomUtils.restoreZoom();
|
|
263
157
|
}
|
|
264
158
|
|
|
265
159
|
// If we are showing a sub-flow in full screen mode and there
|
|
@@ -280,30 +174,62 @@ export default class SVGCanvasRenderer {
|
|
|
280
174
|
this.logger.logEndTimer("constructor" + pipelineId.substring(0, 5));
|
|
281
175
|
}
|
|
282
176
|
|
|
177
|
+
// Sets the pressed state of the space bar. This is called
|
|
178
|
+
// from outside canvas via svg-canvas-d3.
|
|
283
179
|
setSpaceKeyPressed(state) {
|
|
284
|
-
this.
|
|
180
|
+
this.zoomUtils.setSpaceKeyPressed(state);
|
|
285
181
|
this.resetCanvasCursor();
|
|
286
182
|
}
|
|
287
183
|
|
|
288
184
|
// Returns true if the space bar is pressed and held down. This is called
|
|
289
|
-
// from outside canvas via svg-canvas-d3
|
|
185
|
+
// from outside canvas via svg-canvas-d3.
|
|
290
186
|
isSpaceKeyPressed() {
|
|
291
|
-
return this.
|
|
187
|
+
return this.zoomUtils.isSpaceKeyPressed();
|
|
292
188
|
}
|
|
293
189
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
190
|
+
zoomTo(zoomObject) {
|
|
191
|
+
this.zoomUtils.zoomTo(zoomObject);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
translateBy(x, y, animateTime) {
|
|
195
|
+
this.zoomUtils.translateBy(x, y, animateTime);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
zoomIn() {
|
|
199
|
+
this.zoomUtils.zoomIn();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
zoomOut() {
|
|
203
|
+
this.zoomUtils.zoomOut();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
zoomToFit() {
|
|
207
|
+
this.zoomUtils.zoomToFit();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
isZoomedToMax() {
|
|
211
|
+
return this.zoomUtils.isZoomedToMax();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
isZoomedToMin() {
|
|
215
|
+
return this.zoomUtils.isZoomedToMin();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
getZoomToReveal(objectIDs, xPos, yPos) {
|
|
219
|
+
return this.zoomUtils.getZoomToReveal(objectIDs, xPos, yPos);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
getZoom() {
|
|
223
|
+
return this.zoomUtils.getZoom();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
getTransformedViewportDimensions() {
|
|
227
|
+
return this.zoomUtils.getTransformedViewportDimensions();
|
|
302
228
|
}
|
|
303
229
|
|
|
304
230
|
// Returns the data object for the parent supernode that references the
|
|
305
231
|
// active pipeline (managed by this renderer). We get the supernode by
|
|
306
|
-
// looking through the overall canvas info
|
|
232
|
+
// looking through the overall canvas info object.
|
|
307
233
|
// Don't be tempted into thinking you can retrieve the supernode datum by
|
|
308
234
|
// calling the parent renderer because there is no parent renderer when we
|
|
309
235
|
// are showing a sub-flow in full page mode.
|
|
@@ -321,15 +247,8 @@ export default class SVGCanvasRenderer {
|
|
|
321
247
|
return node;
|
|
322
248
|
}
|
|
323
249
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
initializeZoomVariables() {
|
|
329
|
-
// Allows us to record the current zoom amounts.
|
|
330
|
-
this.zoomTransform = d3.zoomIdentity.translate(0, 0).scale(1);
|
|
331
|
-
}
|
|
332
|
-
|
|
250
|
+
// Provides new canvas info data to this renderer and other display and layout info
|
|
251
|
+
// so the canvas can be redisplayed.
|
|
333
252
|
setCanvasInfoRenderer(canvasInfo, selectionInfo, breadcrumbs, nodeLayout, canvasLayout, config) {
|
|
334
253
|
this.logger.logStartTimer("setCanvasInfoRenderer" + this.pipelineId.substring(0, 5));
|
|
335
254
|
this.config = config;
|
|
@@ -422,7 +341,7 @@ export default class SVGCanvasRenderer {
|
|
|
422
341
|
}
|
|
423
342
|
|
|
424
343
|
clearCanvas() {
|
|
425
|
-
this.
|
|
344
|
+
this.zoomUtils.resetZoomTransform();
|
|
426
345
|
this.canvasSVG.remove();
|
|
427
346
|
}
|
|
428
347
|
|
|
@@ -468,25 +387,27 @@ export default class SVGCanvasRenderer {
|
|
|
468
387
|
this.displaySVGToFitSupernode();
|
|
469
388
|
}
|
|
470
389
|
|
|
471
|
-
// The supernode will not have any calculated port positions when the
|
|
472
|
-
// subflow is being displayed full screen, so calculate them first.
|
|
473
|
-
if (this.dispUtils.isDisplayingSubFlowFullPage()) {
|
|
474
|
-
this.displayPortsForSubFlowFullPage();
|
|
475
|
-
}
|
|
476
|
-
|
|
477
390
|
this.displayCanvasAccoutrements();
|
|
478
391
|
|
|
479
392
|
this.logger.logEndTimer("displayCanvas");
|
|
480
393
|
}
|
|
481
394
|
|
|
395
|
+
// Displays zoom and size dependent canvas elements.
|
|
482
396
|
displayCanvasAccoutrements() {
|
|
483
397
|
if (this.config.enableBoundingRectangles) {
|
|
484
398
|
this.displayBoundingRectangles();
|
|
485
399
|
}
|
|
486
400
|
|
|
487
|
-
if (this.config.enableCanvasUnderlay !== "None" &&
|
|
401
|
+
if (this.config.enableCanvasUnderlay !== "None" &&
|
|
402
|
+
this.dispUtils.isDisplayingPrimaryFlowFullPage()) {
|
|
488
403
|
this.setCanvasUnderlaySize();
|
|
489
404
|
}
|
|
405
|
+
|
|
406
|
+
// The supernode will not have any calculated port positions when the
|
|
407
|
+
// subflow is being displayed full screen, so calculate them first.
|
|
408
|
+
if (this.dispUtils.isDisplayingSubFlowFullPage()) {
|
|
409
|
+
this.displayPortsForSubFlowFullPage();
|
|
410
|
+
}
|
|
490
411
|
}
|
|
491
412
|
|
|
492
413
|
// Ensures the binding ports for a full-page sub-flow are calculated
|
|
@@ -544,7 +465,7 @@ export default class SVGCanvasRenderer {
|
|
|
544
465
|
// Moves the binding nodes in a sub-flow, which map to nodes in the parent
|
|
545
466
|
// supernode, to the edge of the SVG area.
|
|
546
467
|
moveSuperBindingNodes() {
|
|
547
|
-
const transformedSVGRect = this.getTransformedViewportDimensions();
|
|
468
|
+
const transformedSVGRect = this.zoomUtils.getTransformedViewportDimensions();
|
|
548
469
|
|
|
549
470
|
// this.logger.log("transformedSVGRect" +
|
|
550
471
|
// " x = " + transformedSVGRect.x +
|
|
@@ -611,10 +532,10 @@ export default class SVGCanvasRenderer {
|
|
|
611
532
|
if (!this.activePipeline) {
|
|
612
533
|
return;
|
|
613
534
|
}
|
|
614
|
-
const svgRect = this.getViewportDimensions();
|
|
615
|
-
const transformedSVGRect = this.getTransformedRect(svgRect, 1);
|
|
616
|
-
const canv = this.
|
|
617
|
-
const canvWithPadding = this.
|
|
535
|
+
const svgRect = this.zoomUtils.getViewportDimensions();
|
|
536
|
+
const transformedSVGRect = this.zoomUtils.getTransformedRect(svgRect, 1);
|
|
537
|
+
const canv = this.zoomUtils.getCanvasDimensions();
|
|
538
|
+
const canvWithPadding = this.zoomUtils.getCanvasDimensionsWithPadding();
|
|
618
539
|
|
|
619
540
|
this.boundingRectsGrp.selectChildren(".d3-bounding").remove();
|
|
620
541
|
|
|
@@ -668,6 +589,16 @@ export default class SVGCanvasRenderer {
|
|
|
668
589
|
}
|
|
669
590
|
}
|
|
670
591
|
|
|
592
|
+
// Selects any objects in the region provided where region is { x, y, width, height }
|
|
593
|
+
selectObjsInRegion(region) {
|
|
594
|
+
const selections =
|
|
595
|
+
CanvasUtils.selectInRegion(region, this.activePipeline,
|
|
596
|
+
this.config.enableLinkSelection !== LINK_SELECTION_NONE,
|
|
597
|
+
this.config.enableLinkType,
|
|
598
|
+
this.config.enableAssocLinkType);
|
|
599
|
+
this.canvasController.setSelections(selections, this.activePipeline.id);
|
|
600
|
+
}
|
|
601
|
+
|
|
671
602
|
// Returns true when we are editing text. Called by svg-canvas-d3.
|
|
672
603
|
isEditingText() {
|
|
673
604
|
if (this.svgCanvasTextArea.isEditingText()) {
|
|
@@ -684,20 +615,17 @@ export default class SVGCanvasRenderer {
|
|
|
684
615
|
|
|
685
616
|
// Returns true when we are dragging objects. Called by svg-canvas-d3.
|
|
686
617
|
isDragging() {
|
|
687
|
-
return this.
|
|
618
|
+
return this.dragObjectUtils.isMoving() || this.dragNewLinkUtils.isDragging() || this.dragDetLinkUtils.isDragging();
|
|
688
619
|
}
|
|
689
620
|
|
|
690
|
-
// Returns true
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
return false;
|
|
699
|
-
}
|
|
700
|
-
return true;
|
|
621
|
+
// Returns true whenever a node or comment is being resized.
|
|
622
|
+
isSizing() {
|
|
623
|
+
this.dragObjectUtils.isSizing();
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// Returns true whenever a node or comment is being moved.
|
|
627
|
+
isMoving() {
|
|
628
|
+
this.dragObjectUtils.isMoving();
|
|
701
629
|
}
|
|
702
630
|
|
|
703
631
|
// Returns true if the node should have a resizing area. We should display
|
|
@@ -784,22 +712,11 @@ export default class SVGCanvasRenderer {
|
|
|
784
712
|
return transPos;
|
|
785
713
|
}
|
|
786
714
|
|
|
787
|
-
// Returns the object passed in with its position and size snapped to
|
|
788
|
-
// the current grid dimensions.
|
|
789
|
-
snapToGridObject(inResizeObj) {
|
|
790
|
-
const resizeObj = inResizeObj;
|
|
791
|
-
resizeObj.x_pos = CanvasUtils.snapToGrid(resizeObj.x_pos, this.canvasLayout.snapToGridXPx);
|
|
792
|
-
resizeObj.y_pos = CanvasUtils.snapToGrid(resizeObj.y_pos, this.canvasLayout.snapToGridYPx);
|
|
793
|
-
resizeObj.width = CanvasUtils.snapToGrid(resizeObj.width, this.canvasLayout.snapToGridXPx);
|
|
794
|
-
resizeObj.height = CanvasUtils.snapToGrid(resizeObj.height, this.canvasLayout.snapToGridYPx);
|
|
795
|
-
return resizeObj;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
715
|
// Returns the current mouse position transformed by the current zoom
|
|
799
716
|
// transformation amounts based on the local SVG -- that is, if we're
|
|
800
717
|
// displaying a sub-flow it is based on the SVG in the supernode.
|
|
801
718
|
getTransformedMousePos(d3Event) {
|
|
802
|
-
return this.transformPos(this.getMousePos(d3Event, this.canvasSVG));
|
|
719
|
+
return this.zoomUtils.transformPos(this.getMousePos(d3Event, this.canvasSVG));
|
|
803
720
|
}
|
|
804
721
|
|
|
805
722
|
// Returns the current mouse position based on the D3 SVG selection object
|
|
@@ -818,45 +735,19 @@ export default class SVGCanvasRenderer {
|
|
|
818
735
|
return { x: 0, y: 0 };
|
|
819
736
|
}
|
|
820
737
|
|
|
738
|
+
// Returns the page position passed in snapped to the grid in canvas
|
|
739
|
+
// coordinates. Called externally via svg-canvas-d3.
|
|
740
|
+
convertPageCoordsToSnappedCanvasCoords(pos) {
|
|
741
|
+
let positon = this.convertPageCoordsToCanvasCoords(pos.x, pos.y);
|
|
742
|
+
positon = this.getMousePosSnapToGrid(positon);
|
|
743
|
+
return positon;
|
|
744
|
+
}
|
|
745
|
+
|
|
821
746
|
// Convert coordinates from the page (based on the page top left corner) to
|
|
822
747
|
// canvas coordinates based on the canvas coordinate system.
|
|
823
748
|
convertPageCoordsToCanvasCoords(x, y) {
|
|
824
749
|
const svgRect = this.canvasSVG.node().getBoundingClientRect();
|
|
825
|
-
return this.transformPos({ x: x - Math.round(svgRect.left), y: y - Math.round(svgRect.top) });
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
// Transforms the x and y fields passed in by the current zoom
|
|
829
|
-
// transformation amounts to convert a coordinate position in screen pixels
|
|
830
|
-
// to a canvas coordinate position.
|
|
831
|
-
transformPos(pos) {
|
|
832
|
-
return {
|
|
833
|
-
x: (pos.x - this.zoomTransform.x) / this.zoomTransform.k,
|
|
834
|
-
y: (pos.y - this.zoomTransform.y) / this.zoomTransform.k
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
// Transforms the x and y fields passed in by the current zoom
|
|
839
|
-
// transformation amounts to convert a canvas coordinate position
|
|
840
|
-
// to a coordinate position in screen pixels.
|
|
841
|
-
unTransformPos(pos) {
|
|
842
|
-
return {
|
|
843
|
-
x: (pos.x * this.zoomTransform.k) + this.zoomTransform.x,
|
|
844
|
-
y: (pos.y * this.zoomTransform.k) + this.zoomTransform.y
|
|
845
|
-
};
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
// Transforms the x, y, height and width fields of the object passed in by the
|
|
849
|
-
// current zoom transformation amounts to convert coordinate positions and
|
|
850
|
-
// dimensions in screen pixels to coordinate positions and dimensions in
|
|
851
|
-
// zoomed pixels.
|
|
852
|
-
getTransformedRect(svgRect, pad) {
|
|
853
|
-
const transPad = (pad / this.zoomTransform.k);
|
|
854
|
-
return {
|
|
855
|
-
x: (-this.zoomTransform.x / this.zoomTransform.k) + transPad,
|
|
856
|
-
y: (-this.zoomTransform.y / this.zoomTransform.k) + transPad,
|
|
857
|
-
height: (svgRect.height / this.zoomTransform.k) - (2 * transPad),
|
|
858
|
-
width: (svgRect.width / this.zoomTransform.k) - (2 * transPad)
|
|
859
|
-
};
|
|
750
|
+
return this.zoomUtils.transformPos({ x: x - Math.round(svgRect.left), y: y - Math.round(svgRect.top) });
|
|
860
751
|
}
|
|
861
752
|
|
|
862
753
|
// Creates the div which contains the ghost node for drag and
|
|
@@ -893,6 +784,7 @@ export default class SVGCanvasRenderer {
|
|
|
893
784
|
getGhostNode(node) {
|
|
894
785
|
const that = this;
|
|
895
786
|
const ghostDivSel = this.getGhostDivSel();
|
|
787
|
+
const zoomScale = this.zoomUtils.getZoomScale();
|
|
896
788
|
|
|
897
789
|
// Calculate the ghost area width which is the maximum of either the node
|
|
898
790
|
// label or the default node width.
|
|
@@ -908,8 +800,8 @@ export default class SVGCanvasRenderer {
|
|
|
908
800
|
// Create a new SVG area for the ghost area.
|
|
909
801
|
const ghostAreaSVG = ghostDivSel
|
|
910
802
|
.append("svg")
|
|
911
|
-
.attr("width", ghostAreaWidth *
|
|
912
|
-
.attr("height", (50 + node.height) *
|
|
803
|
+
.attr("width", ghostAreaWidth * zoomScale)
|
|
804
|
+
.attr("height", (50 + node.height) * zoomScale) // Add some extra pixels, in case label is below label bottom
|
|
913
805
|
.attr("x", 0)
|
|
914
806
|
.attr("y", 0)
|
|
915
807
|
.attr("class", "d3-ghost-svg");
|
|
@@ -971,7 +863,7 @@ export default class SVGCanvasRenderer {
|
|
|
971
863
|
|
|
972
864
|
// Next calculate the amount, if any, the label protrudes beyond the edge
|
|
973
865
|
// of the node width and move the ghost group by that amount.
|
|
974
|
-
xOffset = Math.max(0, (labelDisplayLength - node.width) / 2) *
|
|
866
|
+
xOffset = Math.max(0, (labelDisplayLength - node.width) / 2) * zoomScale;
|
|
975
867
|
|
|
976
868
|
// If the label is center justified, restrict the label width to the
|
|
977
869
|
// display amount and adjust the x coordinate to compensate for the change
|
|
@@ -986,7 +878,7 @@ export default class SVGCanvasRenderer {
|
|
|
986
878
|
}
|
|
987
879
|
}
|
|
988
880
|
|
|
989
|
-
ghostGrp.attr("transform", `translate(${xOffset}, 0) scale(${
|
|
881
|
+
ghostGrp.attr("transform", `translate(${xOffset}, 0) scale(${zoomScale})`);
|
|
990
882
|
|
|
991
883
|
// Get the amount the actual browser page is 'zoomed'. This is differet
|
|
992
884
|
// to the zoom amount for the canvas objects.
|
|
@@ -994,8 +886,8 @@ export default class SVGCanvasRenderer {
|
|
|
994
886
|
|
|
995
887
|
// Calculate the center of the node area for positioning the mouse pointer
|
|
996
888
|
// on the image when it is being dragged.
|
|
997
|
-
const centerX = (xOffset + ((node.width / 2) *
|
|
998
|
-
const centerY = ((node.height / 2) *
|
|
889
|
+
const centerX = (xOffset + ((node.width / 2) * zoomScale)) * browserZoom;
|
|
890
|
+
const centerY = ((node.height / 2) * zoomScale) * browserZoom;
|
|
999
891
|
|
|
1000
892
|
return {
|
|
1001
893
|
element: ghostDivSel.node(),
|
|
@@ -1141,62 +1033,33 @@ export default class SVGCanvasRenderer {
|
|
|
1141
1033
|
.attr("data-drag-node-over", state ? true : null); // true will add the attr, null will remove it
|
|
1142
1034
|
}
|
|
1143
1035
|
|
|
1144
|
-
// Switches on or off node
|
|
1036
|
+
// Switches on or off node highlighting depending on the node
|
|
1145
1037
|
// passed in and keeps track of the currently highlighted node. This is
|
|
1146
|
-
// called as a new link
|
|
1147
|
-
// the target node.
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
if (nodeNearMouse && this.isConnectionAllowedToNearbyNode(d3Event, nodeNearMouse)) {
|
|
1038
|
+
// called as a new link or a detached link is being dragged towards or
|
|
1039
|
+
// away from a target node to highlight or unhighlight the target node.
|
|
1040
|
+
setHighlightingOverNode(state, nodeNearMouse) {
|
|
1041
|
+
if (state) {
|
|
1151
1042
|
if (!this.dragNewLinkOverNode) {
|
|
1152
1043
|
this.dragNewLinkOverNode = nodeNearMouse;
|
|
1153
|
-
this.
|
|
1044
|
+
this.setLinkOverNode(this.dragNewLinkOverNode, true);
|
|
1154
1045
|
|
|
1155
1046
|
} else if (nodeNearMouse.id !== this.dragNewLinkOverNode.id) {
|
|
1156
|
-
this.
|
|
1047
|
+
this.setLinkOverNode(this.dragNewLinkOverNode, false);
|
|
1157
1048
|
this.dragNewLinkOverNode = nodeNearMouse;
|
|
1158
|
-
this.
|
|
1049
|
+
this.setLinkOverNode(this.dragNewLinkOverNode, true);
|
|
1159
1050
|
}
|
|
1160
1051
|
|
|
1161
1052
|
} else {
|
|
1162
1053
|
if (this.dragNewLinkOverNode) {
|
|
1163
|
-
this.
|
|
1054
|
+
this.setLinkOverNode(this.dragNewLinkOverNode, false);
|
|
1164
1055
|
this.dragNewLinkOverNode = null;
|
|
1165
1056
|
}
|
|
1166
1057
|
}
|
|
1167
1058
|
}
|
|
1168
1059
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
const srcNode = this.drawingNewLinkData.srcNode;
|
|
1173
|
-
const trgNode = nodeNearMouse;
|
|
1174
|
-
const srcNodePortId = this.drawingNewLinkData.srcNodePortId;
|
|
1175
|
-
const trgNodePortId = CanvasUtils.getDefaultInputPortId(trgNode); // TODO - make specific to nodes.
|
|
1176
|
-
return CanvasUtils.isDataConnectionAllowed(srcNodePortId, trgNodePortId, srcNode, trgNode, this.activePipeline.links);
|
|
1177
|
-
|
|
1178
|
-
} else if (this.drawingNewLinkData.action === ASSOCIATION_LINK) {
|
|
1179
|
-
const srcNode = this.drawingNewLinkData.srcNode;
|
|
1180
|
-
const trgNode = nodeNearMouse;
|
|
1181
|
-
return CanvasUtils.isAssocConnectionAllowed(srcNode, trgNode, this.activePipeline.links);
|
|
1182
|
-
|
|
1183
|
-
} else if (this.drawingNewLinkData.action === COMMENT_LINK) {
|
|
1184
|
-
const srcObjId = this.drawingNewLinkData.srcObjId;
|
|
1185
|
-
const trgNodeId = nodeNearMouse.id;
|
|
1186
|
-
return CanvasUtils.isCommentLinkConnectionAllowed(srcObjId, trgNodeId, this.activePipeline.links);
|
|
1187
|
-
}
|
|
1188
|
-
} else if (this.draggingLinkData) {
|
|
1189
|
-
const newLink = this.getNewLinkOnDrag(d3Event, this.canvasLayout.nodeProximity);
|
|
1190
|
-
if (newLink) {
|
|
1191
|
-
return true;
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
return false;
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
// Switches on or off the input-port highlighting on the node passed in.
|
|
1198
|
-
// This is called when the user drags a new link towards a target node.
|
|
1199
|
-
setNewLinkOverNodeHighlighting(node, state) {
|
|
1060
|
+
// Switches on or off the highlighting on the node passed in.
|
|
1061
|
+
// This is called when the user drags a link towards a target node.
|
|
1062
|
+
setLinkOverNode(node, state) {
|
|
1200
1063
|
if (node) {
|
|
1201
1064
|
this.getNodeGroupSelectionById(node.id)
|
|
1202
1065
|
.attr("data-new-link-over", state ? "yes" : "no");
|
|
@@ -1205,8 +1068,8 @@ export default class SVGCanvasRenderer {
|
|
|
1205
1068
|
|
|
1206
1069
|
// Removes the data-new-link-over attribute used for highlighting a node
|
|
1207
1070
|
// that a new link is being dragged towards or over.
|
|
1208
|
-
|
|
1209
|
-
this.
|
|
1071
|
+
setLinkOverNodeCancel() {
|
|
1072
|
+
this.setLinkOverNode(this.dragNewLinkOverNode, false);
|
|
1210
1073
|
this.dragNewLinkOverNode = null;
|
|
1211
1074
|
}
|
|
1212
1075
|
|
|
@@ -1275,25 +1138,6 @@ export default class SVGCanvasRenderer {
|
|
|
1275
1138
|
!this.isPortMaxCardinalityZero(nodeTemplate.outputs[0]);
|
|
1276
1139
|
}
|
|
1277
1140
|
|
|
1278
|
-
// Returns true if the current drag objects array has a single node which
|
|
1279
|
-
// is 'insertable' into a data link between nodes on the canvas. Returns
|
|
1280
|
-
// false otherwise, including if a single comment is being dragged.
|
|
1281
|
-
isExistingNodeInsertableIntoLink() {
|
|
1282
|
-
return (this.config.enableInsertNodeDroppedOnLink &&
|
|
1283
|
-
this.dragObjects.length === 1 &&
|
|
1284
|
-
CanvasUtils.isNode(this.dragObjects[0]) &&
|
|
1285
|
-
CanvasUtils.hasInputAndOutputPorts(this.dragObjects[0]) &&
|
|
1286
|
-
!CanvasUtils.isNodeDefaultPortsCardinalityAtMax(this.dragObjects[0], this.activePipeline.links));
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
// Returns true if the current drag objects array has a single node which
|
|
1290
|
-
// is 'attachable' to any detached link on the canvas. Returns false otherwise,
|
|
1291
|
-
// including if a single comment is being dragged.
|
|
1292
|
-
isExistingNodeAttachableToDetachedLinks() {
|
|
1293
|
-
return (this.config.enableLinkSelection === LINK_SELECTION_DETACHABLE &&
|
|
1294
|
-
this.dragObjects.length === 1 &&
|
|
1295
|
-
CanvasUtils.isNode(this.dragObjects[0]));
|
|
1296
|
-
}
|
|
1297
1141
|
|
|
1298
1142
|
// Returns true if the current node template being dragged from the palette
|
|
1299
1143
|
// is 'attachable' to any detached link on the canvas. Returns false otherwise.
|
|
@@ -1328,7 +1172,6 @@ export default class SVGCanvasRenderer {
|
|
|
1328
1172
|
});
|
|
1329
1173
|
|
|
1330
1174
|
if (attachableLinks.length > 0) {
|
|
1331
|
-
|
|
1332
1175
|
// Make sure the attachable links can be attached to the node based on
|
|
1333
1176
|
// the availability of ports and whether they are maxed out or not.
|
|
1334
1177
|
const linkArrays =
|
|
@@ -1354,15 +1197,15 @@ export default class SVGCanvasRenderer {
|
|
|
1354
1197
|
// means the factors will multiply as they percolate up to the top flow.
|
|
1355
1198
|
setMaxZoomExtent(factor) {
|
|
1356
1199
|
if (this.dispUtils.isDisplayingFullPage()) {
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
this.zoom.scaleExtent([this.minScaleExtent, newMaxExtent]);
|
|
1200
|
+
this.zoomUtils.setMaxZoomExtent(factor);
|
|
1360
1201
|
} else {
|
|
1361
|
-
const newFactor = Number(factor) * 1 / this.
|
|
1202
|
+
const newFactor = Number(factor) * 1 / this.zoomUtils.getZoomScale();
|
|
1362
1203
|
this.supernodeInfo.renderer.setMaxZoomExtent(newFactor);
|
|
1363
1204
|
}
|
|
1364
1205
|
}
|
|
1365
1206
|
|
|
1207
|
+
// Returns a new canvas SVG object with all the behavior expected of a great
|
|
1208
|
+
// SVG canvas object.
|
|
1366
1209
|
createCanvasSVG() {
|
|
1367
1210
|
this.logger.log("Create Canvas SVG.");
|
|
1368
1211
|
|
|
@@ -1393,14 +1236,14 @@ export default class SVGCanvasRenderer {
|
|
|
1393
1236
|
.attr("x", dims.x)
|
|
1394
1237
|
.attr("y", dims.y)
|
|
1395
1238
|
.on("mouseenter", (d3Event, d) => {
|
|
1396
|
-
// If we are a sub-flow (i.e we have a parent renderer) set the max
|
|
1239
|
+
// If we are a sub-flow (i.e. we have a parent renderer) set the max
|
|
1397
1240
|
// zoom extent with a factor calculated from our zoom amount.
|
|
1398
1241
|
if (this.supernodeInfo.renderer && this.config.enableZoomIntoSubFlows) {
|
|
1399
|
-
this.supernodeInfo.renderer.setMaxZoomExtent(1 / this.
|
|
1242
|
+
this.supernodeInfo.renderer.setMaxZoomExtent(1 / this.zoomUtils.getZoomScale());
|
|
1400
1243
|
}
|
|
1401
1244
|
})
|
|
1402
1245
|
.on("mouseleave", (d3Event, d) => {
|
|
1403
|
-
// If we are a sub-flow (i.e we have a parent renderer) set the max
|
|
1246
|
+
// If we are a sub-flow (i.e. we have a parent renderer) set the max
|
|
1404
1247
|
// zoom extent with a factor of 1.
|
|
1405
1248
|
if (this.supernodeInfo.renderer && this.config.enableZoomIntoSubFlows) {
|
|
1406
1249
|
this.supernodeInfo.renderer.setMaxZoomExtent(1);
|
|
@@ -1458,21 +1301,12 @@ export default class SVGCanvasRenderer {
|
|
|
1458
1301
|
if (!this.activePipeline.isEmptyOrBindingsOnly() &&
|
|
1459
1302
|
this.dispUtils.isDisplayingFullPage()) {
|
|
1460
1303
|
this.canvasSVG
|
|
1461
|
-
.call(this.
|
|
1304
|
+
.call(this.zoomUtils.getZoomHandler());
|
|
1462
1305
|
}
|
|
1463
1306
|
|
|
1464
1307
|
// These behaviors will be applied to SVG areas at the top level and
|
|
1465
1308
|
// SVG areas displaying an in-place subflow
|
|
1466
1309
|
this.canvasSVG
|
|
1467
|
-
.on("mousemove.zoom", (d3Event) => {
|
|
1468
|
-
// this.logger.log("Zoom - mousemove - drawingNewLink = " + this.drawingNewLinkData ? "yes" : "no");
|
|
1469
|
-
if (this.drawingNewLinkData) {
|
|
1470
|
-
this.drawNewLink(d3Event);
|
|
1471
|
-
}
|
|
1472
|
-
if (this.draggingLinkData) {
|
|
1473
|
-
this.dragLinkHandle(d3Event);
|
|
1474
|
-
}
|
|
1475
|
-
})
|
|
1476
1310
|
// Don't use mousedown.zoom here as it will replace the zoom start behavior
|
|
1477
1311
|
// and prevent panning of canvas background.
|
|
1478
1312
|
.on("mousedown", (d3Event) => {
|
|
@@ -1485,16 +1319,8 @@ export default class SVGCanvasRenderer {
|
|
|
1485
1319
|
d3Event.stopPropagation();
|
|
1486
1320
|
}
|
|
1487
1321
|
})
|
|
1488
|
-
.on("mouseup.zoom", (d3Event) => {
|
|
1489
|
-
this.logger.log("Canvas - mouseup-zoom");
|
|
1490
|
-
if (this.drawingNewLinkData) {
|
|
1491
|
-
this.completeNewLink(d3Event);
|
|
1492
|
-
}
|
|
1493
|
-
if (this.draggingLinkData) {
|
|
1494
|
-
this.completeDraggedLink(d3Event);
|
|
1495
|
-
}
|
|
1496
|
-
})
|
|
1497
1322
|
.on("click.zoom", (d3Event) => {
|
|
1323
|
+
// Control comes here after the zoomClick action has been perfoemd in zoomUtils.
|
|
1498
1324
|
this.logger.log("Canvas - click-zoom");
|
|
1499
1325
|
|
|
1500
1326
|
this.canvasController.clickActionHandler({
|
|
@@ -1519,7 +1345,7 @@ export default class SVGCanvasRenderer {
|
|
|
1519
1345
|
// Resets the pointer cursor on the background rectangle in the Canvas SVG area.
|
|
1520
1346
|
resetCanvasCursor() {
|
|
1521
1347
|
const selector = ".d3-svg-background[data-pipeline-id='" + this.activePipeline.id + "']";
|
|
1522
|
-
this.canvasSVG.select(selector).style("cursor", this.isDragActivated() && this.dispUtils.isDisplayingFullPage() ? "grab" : "default");
|
|
1348
|
+
this.canvasSVG.select(selector).style("cursor", this.zoomUtils.isDragActivated() && this.dispUtils.isDisplayingFullPage() ? "grab" : "default");
|
|
1523
1349
|
}
|
|
1524
1350
|
|
|
1525
1351
|
createCanvasGroup(canvasObj, className) {
|
|
@@ -1541,7 +1367,7 @@ export default class SVGCanvasRenderer {
|
|
|
1541
1367
|
}
|
|
1542
1368
|
|
|
1543
1369
|
setCanvasUnderlaySize(x = 0, y = 0) {
|
|
1544
|
-
const canv = this.
|
|
1370
|
+
const canv = this.zoomUtils.getCanvasDimensionsWithPadding();
|
|
1545
1371
|
if (canv) {
|
|
1546
1372
|
this.canvasUnderlay
|
|
1547
1373
|
.attr("x", canv.left - 50)
|
|
@@ -1551,50 +1377,6 @@ export default class SVGCanvasRenderer {
|
|
|
1551
1377
|
}
|
|
1552
1378
|
}
|
|
1553
1379
|
|
|
1554
|
-
addBackToParentFlowArrow(canvasSVG) {
|
|
1555
|
-
const g = canvasSVG
|
|
1556
|
-
.append("g")
|
|
1557
|
-
.attr("transform", "translate(15, 15)")
|
|
1558
|
-
.on("mouseenter", function(d3Event, d) { // Use function keyword so 'this' pointer references the DOM text object
|
|
1559
|
-
d3.select(this).select("rect")
|
|
1560
|
-
.attr("data-pointer-hover", "yes");
|
|
1561
|
-
})
|
|
1562
|
-
.on("mouseleave", function(d3Event, d) { // Use function keyword so 'this' pointer references the DOM text object
|
|
1563
|
-
d3.select(this).select("rect")
|
|
1564
|
-
.attr("data-pointer-hover", "no");
|
|
1565
|
-
})
|
|
1566
|
-
.on("mousedown mouseup", (d3Event) => {
|
|
1567
|
-
// Prevent mouse events going through to the canvas. This prevents
|
|
1568
|
-
// a drag gesture on the button activating the canvas drag action.
|
|
1569
|
-
CanvasUtils.stopPropagationAndPreventDefault(d3Event);
|
|
1570
|
-
})
|
|
1571
|
-
.on("click", (d3Event) => {
|
|
1572
|
-
CanvasUtils.stopPropagationAndPreventDefault(d3Event);
|
|
1573
|
-
this.canvasController.displayPreviousPipeline();
|
|
1574
|
-
});
|
|
1575
|
-
|
|
1576
|
-
g.append("rect")
|
|
1577
|
-
.attr("x", 0)
|
|
1578
|
-
.attr("y", 0)
|
|
1579
|
-
.attr("width", 210)
|
|
1580
|
-
.attr("height", 40)
|
|
1581
|
-
.attr("class", "d3-back-to-previous-flow-box");
|
|
1582
|
-
|
|
1583
|
-
g.append("svg")
|
|
1584
|
-
.attr("x", 16)
|
|
1585
|
-
.attr("y", 11)
|
|
1586
|
-
.attr("width", 16)
|
|
1587
|
-
.attr("height", 16)
|
|
1588
|
-
.html(LEFT_ARROW_ICON)
|
|
1589
|
-
.attr("class", "d3-back-to-previous-flow-text");
|
|
1590
|
-
|
|
1591
|
-
g.append("text")
|
|
1592
|
-
.attr("x", 40)
|
|
1593
|
-
.attr("y", 24)
|
|
1594
|
-
.attr("class", "d3-back-to-previous-flow-text")
|
|
1595
|
-
.text("Return to previous flow");
|
|
1596
|
-
}
|
|
1597
|
-
|
|
1598
1380
|
createDropShadow(defs) {
|
|
1599
1381
|
var dropShadowFilter = defs.append("filter")
|
|
1600
1382
|
.attr("id", this.getId("node_drop_shadow"))
|
|
@@ -1643,6 +1425,7 @@ export default class SVGCanvasRenderer {
|
|
|
1643
1425
|
});
|
|
1644
1426
|
}
|
|
1645
1427
|
}
|
|
1428
|
+
|
|
1646
1429
|
setNodeLabelEditingMode(nodeId, pipelineId) {
|
|
1647
1430
|
if (this.pipelineId === pipelineId) {
|
|
1648
1431
|
const node = this.activePipeline.getNode(nodeId);
|
|
@@ -1694,1066 +1477,133 @@ export default class SVGCanvasRenderer {
|
|
|
1694
1477
|
}
|
|
1695
1478
|
}
|
|
1696
1479
|
|
|
1697
|
-
//
|
|
1698
|
-
//
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
newZoom = { x: -canvWithPadding.left, y: -canvWithPadding.top, k: 1 };
|
|
1480
|
+
// Repositions the comment toolbar so it is always over the top of the
|
|
1481
|
+
// comment being edited.
|
|
1482
|
+
repositionCommentToolbar() {
|
|
1483
|
+
if (this.config.enableMarkdownInComments &&
|
|
1484
|
+
this.dispUtils.isDisplayingFullPage() &&
|
|
1485
|
+
this.svgCanvasTextArea.isEditingText()) {
|
|
1486
|
+
// If a node label or text decoration is being edited com will be undefined.
|
|
1487
|
+
const com = this.activePipeline.getComment(this.svgCanvasTextArea.getEditingTextId());
|
|
1488
|
+
if (com) {
|
|
1489
|
+
const pos = this.getCommentToolbarPos(com);
|
|
1490
|
+
this.canvasController.moveTextToolbar(pos.x, pos.y);
|
|
1709
1491
|
}
|
|
1710
1492
|
}
|
|
1711
|
-
|
|
1712
|
-
// If there's no saved zoom and we have some initial pan amounts provided use them.
|
|
1713
|
-
if (!newZoom && this.canvasLayout.initialPanX && this.canvasLayout.initialPanY) {
|
|
1714
|
-
newZoom = { x: this.canvasLayout.initialPanX, y: this.canvasLayout.initialPanY, k: 1 };
|
|
1715
|
-
}
|
|
1716
|
-
|
|
1717
|
-
// If new zoom is different to the current zoom amount, apply it.
|
|
1718
|
-
if (newZoom &&
|
|
1719
|
-
(newZoom.k !== this.zoomTransform.k ||
|
|
1720
|
-
newZoom.x !== this.zoomTransform.x ||
|
|
1721
|
-
newZoom.y !== this.zoomTransform.y)) {
|
|
1722
|
-
this.zoomCanvasInvokeZoomBehavior(newZoom);
|
|
1723
|
-
}
|
|
1724
1493
|
}
|
|
1725
1494
|
|
|
1726
|
-
//
|
|
1727
|
-
//
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
this.zoomHasChanged(newZoomTransform)) {
|
|
1735
|
-
this.zoomingAction = true;
|
|
1736
|
-
const zoomTransform = d3.zoomIdentity.translate(newZoomTransform.x, newZoomTransform.y).scale(newZoomTransform.k);
|
|
1737
|
-
if (animateTime) {
|
|
1738
|
-
this.canvasSVG.call(this.zoom).transition()
|
|
1739
|
-
.duration(animateTime)
|
|
1740
|
-
.call(this.zoom.transform, zoomTransform);
|
|
1741
|
-
} else {
|
|
1742
|
-
this.canvasSVG.call(this.zoom.transform, zoomTransform);
|
|
1743
|
-
}
|
|
1744
|
-
this.zoomingAction = false;
|
|
1745
|
-
}
|
|
1495
|
+
// Returns a position object that describes the position in page coordinates
|
|
1496
|
+
// of the comment toolbar so that it is positioned above the comment being edited.
|
|
1497
|
+
getCommentToolbarPos(com) {
|
|
1498
|
+
const pos = this.zoomUtils.unTransformPos({ x: com.x_pos, y: com.y_pos });
|
|
1499
|
+
return {
|
|
1500
|
+
x: pos.x + this.canvasLayout.commentToolbarPosX,
|
|
1501
|
+
y: pos.y + this.canvasLayout.commentToolbarPosY
|
|
1502
|
+
};
|
|
1746
1503
|
}
|
|
1747
1504
|
|
|
1748
|
-
//
|
|
1749
|
-
//
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
}
|
|
1505
|
+
// Returns the snap-to-grid position of the object positioned at objPos.x
|
|
1506
|
+
// and objPos.y. The grid that is snapped to is defined by this.snapToGridXPx
|
|
1507
|
+
// and this.snapToGridYPx values which are pixel values.
|
|
1508
|
+
snapToGridPosition(objPos) {
|
|
1509
|
+
const stgPosX = CanvasUtils.snapToGrid(objPos.x, this.canvasLayout.snapToGridXPx);
|
|
1510
|
+
const stgPosY = CanvasUtils.snapToGrid(objPos.y, this.canvasLayout.snapToGridYPx);
|
|
1755
1511
|
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
const canvasDimensions = this.getCanvasDimensionsAdjustedForScale(1, padding);
|
|
1759
|
-
const viewPortDimensions = this.getViewportDimensions();
|
|
1512
|
+
return { x: stgPosX, y: stgPosY };
|
|
1513
|
+
}
|
|
1760
1514
|
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1515
|
+
// Displays all the nodes on the canvas either by creating new nodes,
|
|
1516
|
+
// updating existing nodes or removing unwanted nodes.
|
|
1517
|
+
displayNodes() {
|
|
1518
|
+
this.logger.logStartTimer("displayNodes " + this.getFlags());
|
|
1765
1519
|
|
|
1766
|
-
|
|
1767
|
-
|
|
1520
|
+
// Set the port positions for all ports - these will be needed when displaying
|
|
1521
|
+
// nodes and links. This needs to be done here because a resized supernode
|
|
1522
|
+
// will cause its ports to move and resizing comments causes links to be
|
|
1523
|
+
// redrawn which will need port positions to be set appropriately.
|
|
1524
|
+
this.setPortPositionsAllNodes();
|
|
1768
1525
|
|
|
1769
|
-
|
|
1770
|
-
|
|
1526
|
+
const sel = this.getAllNodeGroupsSelection();
|
|
1527
|
+
this.displayNodesSubset(sel, this.activePipeline.nodes);
|
|
1771
1528
|
|
|
1772
|
-
|
|
1773
|
-
}
|
|
1529
|
+
this.logger.logEndTimer("displayNodes " + this.getFlags());
|
|
1774
1530
|
}
|
|
1775
1531
|
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1532
|
+
displayMovedNodes() {
|
|
1533
|
+
this.logger.logStartTimer("displayMovedNodes");
|
|
1534
|
+
const nodeGroupSel = this.getAllNodeGroupsSelection();
|
|
1535
|
+
|
|
1536
|
+
nodeGroupSel
|
|
1537
|
+
.datum((d) => this.activePipeline.getNode(d.id))
|
|
1538
|
+
.attr("transform", (d) => `translate(${d.x_pos}, ${d.y_pos})`);
|
|
1781
1539
|
|
|
1782
1540
|
if (this.dispUtils.isDisplayingSubFlow()) {
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1541
|
+
nodeGroupSel
|
|
1542
|
+
.each((d, i, nodeGrps) => {
|
|
1543
|
+
if (d.isSupernodeInputBinding) {
|
|
1544
|
+
this.updatePortRadiusAndPos(nodeGrps[i], d, "d3-node-port-output-main");
|
|
1545
|
+
}
|
|
1546
|
+
if (d.isSupernodeOutputBinding) {
|
|
1547
|
+
this.updatePortRadiusAndPos(nodeGrps[i], d, "d3-node-port-input-main");
|
|
1548
|
+
this.updatePortArrowPath(nodeGrps[i], "d3-node-port-input-arrow");
|
|
1549
|
+
}
|
|
1550
|
+
});
|
|
1786
1551
|
}
|
|
1787
|
-
|
|
1552
|
+
this.logger.logEndTimer("displayMovedNodes");
|
|
1788
1553
|
}
|
|
1789
1554
|
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
this.zoomCanvasInvokeZoomBehavior(zoomObject, animateTime);
|
|
1793
|
-
}
|
|
1555
|
+
displayNodesSelectionStatus(selectionInfo) {
|
|
1556
|
+
this.logger.logStartTimer("displayNodesSelectionStatus");
|
|
1794
1557
|
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1558
|
+
this.getAllNodeGroupsSelection()
|
|
1559
|
+
.selectChildren(".d3-node-selection-highlight")
|
|
1560
|
+
.attr("data-selected", (d) => (this.activePipeline.isSelected(d.id) ? "yes" : "no"));
|
|
1798
1561
|
|
|
1799
|
-
|
|
1800
|
-
const z = this.getZoomTransform();
|
|
1801
|
-
const zoomObject = d3.zoomIdentity.translate(z.x + x, z.y + y).scale(z.k);
|
|
1802
|
-
this.zoomCanvasInvokeZoomBehavior(zoomObject, animateTime);
|
|
1562
|
+
this.logger.logEndTimer("displayNodesSelectionStatus");
|
|
1803
1563
|
}
|
|
1804
1564
|
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
const newScale = Math.min(this.zoomTransform.k * 1.1, this.maxScaleExtent);
|
|
1808
|
-
this.canvasSVG.call(this.zoom.scaleTo, newScale);
|
|
1809
|
-
}
|
|
1810
|
-
}
|
|
1565
|
+
displaySingleNode(d) {
|
|
1566
|
+
this.logger.logStartTimer("displaySingleNode " + this.getFlags());
|
|
1811
1567
|
|
|
1812
|
-
|
|
1813
|
-
if (this.zoomTransform.k > this.minScaleExtent) {
|
|
1814
|
-
const newScale = Math.max(this.zoomTransform.k / 1.1, this.minScaleExtent);
|
|
1815
|
-
this.canvasSVG.call(this.zoom.scaleTo, newScale);
|
|
1816
|
-
}
|
|
1817
|
-
}
|
|
1568
|
+
this.setPortPositionsForNode(d);
|
|
1818
1569
|
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
}
|
|
1570
|
+
const selection = this.getNodeGroupSelectionById(d.id);
|
|
1571
|
+
this.displayNodesSubset(selection, [d]);
|
|
1822
1572
|
|
|
1823
|
-
|
|
1824
|
-
return this.zoomTransform ? this.zoomTransform.k === this.minScaleExtent : false;
|
|
1573
|
+
this.logger.logEndTimer("displaySingleNode " + this.getFlags());
|
|
1825
1574
|
}
|
|
1826
1575
|
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
let xOffset;
|
|
1841
|
-
let yOffset;
|
|
1576
|
+
displayNodesSubset(selection, data) {
|
|
1577
|
+
selection
|
|
1578
|
+
.data(data, (d) => d.id)
|
|
1579
|
+
.join(
|
|
1580
|
+
(enter) => this.createNodes(enter),
|
|
1581
|
+
null,
|
|
1582
|
+
(remove) => this.removeNodes(remove)
|
|
1583
|
+
)
|
|
1584
|
+
.attr("transform", (d) => `translate(${d.x_pos}, ${d.y_pos})`)
|
|
1585
|
+
.attr("class", (d) => this.getNodeGroupClass(d))
|
|
1586
|
+
.attr("style", (d) => this.getNodeGrpStyle(d))
|
|
1587
|
+
.call((joinedNodeGrps) => this.updateNodes(joinedNodeGrps, data));
|
|
1588
|
+
}
|
|
1842
1589
|
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
yOffset = transformedSVGRect.y + (transformedSVGRect.height * (yPosInt / 100)) - (canv.top + (canv.height / 2));
|
|
1590
|
+
createNodes(enter) {
|
|
1591
|
+
this.logger.logStartTimer("createNodes");
|
|
1846
1592
|
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
if (canv.left < transformedSVGRect.x) {
|
|
1852
|
-
xOffset = transformedSVGRect.x - canv.left;
|
|
1853
|
-
}
|
|
1854
|
-
if (canv.bottom > transformedSVGRect.y + transformedSVGRect.height) {
|
|
1855
|
-
yOffset = transformedSVGRect.y + transformedSVGRect.height - canv.bottom;
|
|
1856
|
-
}
|
|
1857
|
-
if (canv.top < transformedSVGRect.y) {
|
|
1858
|
-
yOffset = transformedSVGRect.y - canv.top;
|
|
1859
|
-
}
|
|
1860
|
-
}
|
|
1593
|
+
const newNodeGroups = enter
|
|
1594
|
+
.append("g")
|
|
1595
|
+
.attr("data-id", (d) => this.getId("node_grp", d.id))
|
|
1596
|
+
.call(this.attachNodeGroupListeners.bind(this));
|
|
1861
1597
|
|
|
1862
|
-
|
|
1863
|
-
const x = this.zoomTransform.x + ((xOffset || 0)) * this.zoomTransform.k;
|
|
1864
|
-
const y = this.zoomTransform.y + ((yOffset || 0)) * this.zoomTransform.k;
|
|
1865
|
-
return { x: x || 0, y: y || 0, k: this.zoomTransform.k };
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
}
|
|
1598
|
+
this.logger.logEndTimer("createNodes");
|
|
1869
1599
|
|
|
1870
|
-
return
|
|
1600
|
+
return newNodeGroups;
|
|
1871
1601
|
}
|
|
1872
1602
|
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
getTransformedViewportDimensions() {
|
|
1876
|
-
const svgRect = this.getViewportDimensions();
|
|
1877
|
-
return this.getTransformedRect(svgRect, 0);
|
|
1878
|
-
}
|
|
1603
|
+
updateNodes(joinedNodeGrps, data) {
|
|
1604
|
+
this.logger.logStartTimer("updateNodes");
|
|
1879
1605
|
|
|
1880
|
-
|
|
1881
|
-
// we can use the supernode's dimensions. If not we are displaying
|
|
1882
|
-
// full-page so we can use getBoundingClientRect() to get the dimensions
|
|
1883
|
-
// (for some reason that method doesn't return correct values with embedded SVG areas).
|
|
1884
|
-
getViewportDimensions() {
|
|
1885
|
-
let viewportDimensions = {};
|
|
1886
|
-
|
|
1887
|
-
if (this.dispUtils.isDisplayingSubFlowInPlace()) {
|
|
1888
|
-
const dims = this.getParentSupernodeSVGDimensions();
|
|
1889
|
-
viewportDimensions.width = dims.width;
|
|
1890
|
-
viewportDimensions.height = dims.height;
|
|
1891
|
-
|
|
1892
|
-
} else {
|
|
1893
|
-
if (this.canvasSVG && this.canvasSVG.node()) {
|
|
1894
|
-
viewportDimensions = this.canvasSVG.node().getBoundingClientRect();
|
|
1895
|
-
} else {
|
|
1896
|
-
viewportDimensions = { x: 0, y: 0, width: 1100, height: 640 }; // Return a sensible default (for Jest tests)
|
|
1897
|
-
}
|
|
1898
|
-
}
|
|
1899
|
-
return viewportDimensions;
|
|
1900
|
-
}
|
|
1901
|
-
|
|
1902
|
-
zoomStart(d3Event) {
|
|
1903
|
-
this.logger.log("zoomStart - " + JSON.stringify(d3Event.transform));
|
|
1904
|
-
|
|
1905
|
-
// Ensure any open tip is closed before starting a zoom operation.
|
|
1906
|
-
this.canvasController.closeTip();
|
|
1907
|
-
|
|
1908
|
-
// Close the context menu, if it's open, before panning or zooming.
|
|
1909
|
-
// If the context menu is opened inside the expanded supernode (in-place
|
|
1910
|
-
// subflow), when the user zooms the canvas, the full page flow is handling
|
|
1911
|
-
// that zoom, which causes a refresh in the subflow, so the full page flow
|
|
1912
|
-
// will take care of closing the context menu. This means the in-place
|
|
1913
|
-
// subflow doesn’t need to do anything on zoom,
|
|
1914
|
-
// hence: !this.dispUtils.isDisplayingSubFlowInPlace()
|
|
1915
|
-
if (this.canvasController.isContextMenuDisplayed() &&
|
|
1916
|
-
!this.dispUtils.isDisplayingSubFlowInPlace()) {
|
|
1917
|
-
this.canvasController.closeContextMenu();
|
|
1918
|
-
this.contextMenuClosedOnZoom = true;
|
|
1919
|
-
}
|
|
1920
|
-
|
|
1921
|
-
// Any text editing in progress will be closed by the textarea's blur event
|
|
1922
|
-
// if the user clicks on the canvas background. So we set this flag to
|
|
1923
|
-
// prevent the selection being lost in the zoomEnd (mouseup) event.
|
|
1924
|
-
if (this.svgCanvasTextArea.isEditingText()) {
|
|
1925
|
-
this.textEditingClosedOnZoom = true;
|
|
1926
|
-
}
|
|
1927
|
-
|
|
1928
|
-
this.regionSelect = this.shouldDoRegionSelect(d3Event);
|
|
1929
|
-
|
|
1930
|
-
if (this.regionSelect) {
|
|
1931
|
-
// Add a delay so, if the user just clicks, they don't see the crosshair.
|
|
1932
|
-
// This will be cleared in zoomEnd if the user's click takes less than 200 ms.
|
|
1933
|
-
this.addingCursorOverlay = setTimeout(() => this.addTempCursorOverlay("crosshair"), 200);
|
|
1934
|
-
this.regionStartTransformX = d3Event.transform.x;
|
|
1935
|
-
this.regionStartTransformY = d3Event.transform.y;
|
|
1936
|
-
|
|
1937
|
-
} else {
|
|
1938
|
-
if (this.isDragActivated(d3Event)) {
|
|
1939
|
-
this.addingCursorOverlay = setTimeout(() => this.addTempCursorOverlay("grabbing"), 200);
|
|
1940
|
-
} else {
|
|
1941
|
-
this.addingCursorOverlay = setTimeout(() => this.addTempCursorOverlay("default"), 200);
|
|
1942
|
-
}
|
|
1943
|
-
}
|
|
1944
|
-
|
|
1945
|
-
const transPos = this.getTransformedMousePos(d3Event);
|
|
1946
|
-
this.zoomStartPoint = { x: d3Event.transform.x, y: d3Event.transform.y, k: d3Event.transform.k, startX: transPos.x, startY: transPos.y };
|
|
1947
|
-
this.previousD3Event = { x: d3Event.transform.x, y: d3Event.transform.y, k: d3Event.transform.k };
|
|
1948
|
-
// Calculate the canvas dimensions here, so we don't have to recalculate
|
|
1949
|
-
// them for every zoom action event.
|
|
1950
|
-
this.zoomCanvasDimensions = CanvasUtils.getCanvasDimensions(
|
|
1951
|
-
this.activePipeline.nodes, this.activePipeline.comments,
|
|
1952
|
-
this.activePipeline.links, this.canvasLayout.commentHighlightGap);
|
|
1953
|
-
}
|
|
1954
|
-
|
|
1955
|
-
zoomAction(d3Event) {
|
|
1956
|
-
this.logger.log("zoomAction - " + JSON.stringify(d3Event.transform));
|
|
1957
|
-
|
|
1958
|
-
// If the scale amount is the same we are not zooming, so we must be panning.
|
|
1959
|
-
if (d3Event.transform.k === this.zoomStartPoint.k) {
|
|
1960
|
-
if (this.regionSelect) {
|
|
1961
|
-
this.drawRegionSelector(d3Event);
|
|
1962
|
-
|
|
1963
|
-
} else {
|
|
1964
|
-
this.zoomCanvasBackground(d3Event);
|
|
1965
|
-
}
|
|
1966
|
-
} else {
|
|
1967
|
-
this.addTempCursorOverlay("default");
|
|
1968
|
-
this.zoomCanvasBackground(d3Event);
|
|
1969
|
-
this.zoomCommentToolbar();
|
|
1970
|
-
}
|
|
1971
|
-
}
|
|
1972
|
-
|
|
1973
|
-
zoomEnd(d3Event) {
|
|
1974
|
-
this.logger.log("zoomEnd - " + JSON.stringify(d3Event.transform));
|
|
1975
|
-
|
|
1976
|
-
// Clears the display of the cursor overlay if the user clicks within 200 ms
|
|
1977
|
-
clearTimeout(this.addingCursorOverlay);
|
|
1978
|
-
|
|
1979
|
-
const transPos = this.getTransformedMousePos(d3Event);
|
|
1980
|
-
|
|
1981
|
-
if (this.drawingNewLinkData) {
|
|
1982
|
-
this.stopDrawingNewLink();
|
|
1983
|
-
|
|
1984
|
-
} else if (this.draggingLinkData) {
|
|
1985
|
-
this.stopDraggingLink();
|
|
1986
|
-
|
|
1987
|
-
// The user just clicked -- with no drag.
|
|
1988
|
-
} else if (transPos.x === this.zoomStartPoint.startX &&
|
|
1989
|
-
transPos.y === this.zoomStartPoint.startY &&
|
|
1990
|
-
!this.zoomChanged()) {
|
|
1991
|
-
this.zoomClick(d3Event);
|
|
1992
|
-
|
|
1993
|
-
} else if (this.regionSelect) {
|
|
1994
|
-
this.zoomEndRegionSelect(d3Event);
|
|
1995
|
-
|
|
1996
|
-
} else if (this.dispUtils.isDisplayingFullPage() && this.zoomChanged()) {
|
|
1997
|
-
this.zoomSave();
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
// Remove the cursor overlay and reset the SVG background rectangle
|
|
2001
|
-
// cursor style, which was set in the zoom start method.
|
|
2002
|
-
this.resetCanvasCursor(d3Event);
|
|
2003
|
-
this.removeTempCursorOverlay();
|
|
2004
|
-
this.contextMenuClosedOnZoom = false;
|
|
2005
|
-
this.textEditingClosedOnZoom = false;
|
|
2006
|
-
this.regionSelect = false;
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
// Returns true if the region select gesture is requested by the user.
|
|
2010
|
-
shouldDoRegionSelect(d3Event) {
|
|
2011
|
-
// The this.zoomingAction flag indicates zooming is being invoked
|
|
2012
|
-
// programmatically.
|
|
2013
|
-
if (this.zoomingAction) {
|
|
2014
|
-
return false;
|
|
2015
|
-
|
|
2016
|
-
} else if (this.config.enableInteractionType === INTERACTION_MOUSE &&
|
|
2017
|
-
(d3Event && d3Event.sourceEvent && d3Event.sourceEvent.shiftKey)) {
|
|
2018
|
-
return true;
|
|
2019
|
-
|
|
2020
|
-
} else if (this.config.enableInteractionType === INTERACTION_CARBON &&
|
|
2021
|
-
!this.isSpaceKeyPressed(d3Event)) {
|
|
2022
|
-
return true;
|
|
2023
|
-
|
|
2024
|
-
} else if (this.config.enableInteractionType === INTERACTION_TRACKPAD &&
|
|
2025
|
-
(d3Event.sourceEvent && d3Event.sourceEvent.buttons === 1) && // Main button is pressed
|
|
2026
|
-
!this.spaceKeyPressed) {
|
|
2027
|
-
return true;
|
|
2028
|
-
}
|
|
2029
|
-
|
|
2030
|
-
return false;
|
|
2031
|
-
}
|
|
2032
|
-
|
|
2033
|
-
// Returns true if the current zoom transform is different from the
|
|
2034
|
-
// zoom values at the beginning of the zoom action.
|
|
2035
|
-
zoomChanged() {
|
|
2036
|
-
return (this.zoomTransform.k !== this.zoomStartPoint.k ||
|
|
2037
|
-
this.zoomTransform.x !== this.zoomStartPoint.x ||
|
|
2038
|
-
this.zoomTransform.y !== this.zoomStartPoint.y);
|
|
2039
|
-
}
|
|
2040
|
-
|
|
2041
|
-
zoomCanvasBackground(d3Event) {
|
|
2042
|
-
this.regionSelect = false;
|
|
2043
|
-
|
|
2044
|
-
if (this.dispUtils.isDisplayingPrimaryFlowFullPage()) {
|
|
2045
|
-
const incTransform = this.getTransformIncrement(d3Event);
|
|
2046
|
-
this.zoomTransform = this.zoomConstrainRegular(incTransform, this.getViewportDimensions(), this.zoomCanvasDimensions);
|
|
2047
|
-
} else {
|
|
2048
|
-
this.zoomTransform = d3.zoomIdentity.translate(d3Event.transform.x, d3Event.transform.y).scale(d3Event.transform.k);
|
|
2049
|
-
}
|
|
2050
|
-
|
|
2051
|
-
this.canvasGrp.attr("transform", this.zoomTransform);
|
|
2052
|
-
|
|
2053
|
-
if (this.config.enableBoundingRectangles) {
|
|
2054
|
-
this.displayBoundingRectangles();
|
|
2055
|
-
}
|
|
2056
|
-
|
|
2057
|
-
if (this.config.enableCanvasUnderlay !== "None" &&
|
|
2058
|
-
this.dispUtils.isDisplayingPrimaryFlowFullPage()) {
|
|
2059
|
-
this.setCanvasUnderlaySize();
|
|
2060
|
-
}
|
|
2061
|
-
|
|
2062
|
-
// The supernode will not have any calculated port positions when the
|
|
2063
|
-
// subflow is being displayed full screen, so calculate them first.
|
|
2064
|
-
if (this.dispUtils.isDisplayingSubFlowFullPage()) {
|
|
2065
|
-
this.displayPortsForSubFlowFullPage();
|
|
2066
|
-
}
|
|
2067
|
-
}
|
|
2068
|
-
|
|
2069
|
-
// Handles a zoom operation that is just a click on the canvas background.
|
|
2070
|
-
zoomClick(d3Event) {
|
|
2071
|
-
// Only clear selections under these conditions:
|
|
2072
|
-
// * if the click was on the canvas of the current active pipeline. (This
|
|
2073
|
-
// is because clicking on the canvas background of an expanded supernode
|
|
2074
|
-
// should select that node.)
|
|
2075
|
-
// * if we have not just closed a context menu
|
|
2076
|
-
// * if we have not just closed text editing
|
|
2077
|
-
// * if editing actions are enabled OR the target is the canvas background.
|
|
2078
|
-
// (This condition is necessary because when editing actions are disabled,
|
|
2079
|
-
// for a read-only canvas, the mouse-up over a node can cause this method
|
|
2080
|
-
// to be called.)
|
|
2081
|
-
if (this.dispUtils.isDisplayingCurrentPipeline() &&
|
|
2082
|
-
!this.contextMenuClosedOnZoom &&
|
|
2083
|
-
!this.textEditingClosedOnZoom &&
|
|
2084
|
-
(this.config.enableEditingActions ||
|
|
2085
|
-
(d3Event.sourceEvent &&
|
|
2086
|
-
d3Event.sourceEvent.target.classList.contains("d3-svg-background")))) {
|
|
2087
|
-
this.canvasController.clearSelections();
|
|
2088
|
-
}
|
|
2089
|
-
}
|
|
2090
|
-
|
|
2091
|
-
// Handles the behavior when the user stops doing a region select.
|
|
2092
|
-
zoomEndRegionSelect(d3Event) {
|
|
2093
|
-
this.removeRegionSelector();
|
|
2094
|
-
|
|
2095
|
-
// Reset the transform x and y to what they were before the region
|
|
2096
|
-
// selection action was started. This directly sets the x and y values
|
|
2097
|
-
// in the __zoom property of the svgCanvas DOM object.
|
|
2098
|
-
d3Event.transform.x = this.regionStartTransformX;
|
|
2099
|
-
d3Event.transform.y = this.regionStartTransformY;
|
|
2100
|
-
|
|
2101
|
-
const { startX, startY, width, height } = this.getRegionDimensions(d3Event);
|
|
2102
|
-
|
|
2103
|
-
const region = { x1: startX, y1: startY, x2: startX + width, y2: startY + height };
|
|
2104
|
-
const selections =
|
|
2105
|
-
CanvasUtils.selectInRegion(region, this.activePipeline,
|
|
2106
|
-
this.config.enableLinkSelection !== LINK_SELECTION_NONE,
|
|
2107
|
-
this.config.enableLinkType,
|
|
2108
|
-
this.config.enableAssocLinkType);
|
|
2109
|
-
this.canvasController.setSelections(selections, this.activePipeline.id);
|
|
2110
|
-
}
|
|
2111
|
-
|
|
2112
|
-
// Save the zoom amount. The canvas controller/object model will decide
|
|
2113
|
-
// how this info is saved.
|
|
2114
|
-
zoomSave() {
|
|
2115
|
-
// Set the internal zoom value for canvasSVG used by D3. This will be
|
|
2116
|
-
// used by d3Event next time a zoom action is initiated.
|
|
2117
|
-
this.canvasSVG.property("__zoom", this.zoomTransform);
|
|
2118
|
-
|
|
2119
|
-
const data = {
|
|
2120
|
-
editType: "setZoom",
|
|
2121
|
-
editSource: "canvas",
|
|
2122
|
-
zoom: this.zoomTransform,
|
|
2123
|
-
pipelineId: this.activePipeline.id
|
|
2124
|
-
};
|
|
2125
|
-
this.canvasController.editActionHandler(data);
|
|
2126
|
-
|
|
2127
|
-
}
|
|
2128
|
-
|
|
2129
|
-
// Repositions the comment toolbar so it is always over the top of the
|
|
2130
|
-
// comment being edited.
|
|
2131
|
-
zoomCommentToolbar() {
|
|
2132
|
-
if (this.config.enableMarkdownInComments &&
|
|
2133
|
-
this.dispUtils.isDisplayingFullPage() &&
|
|
2134
|
-
this.svgCanvasTextArea.isEditingText()) {
|
|
2135
|
-
// If a node label or text decoration is being edited com will be undefined.
|
|
2136
|
-
const com = this.activePipeline.getComment(this.svgCanvasTextArea.getEditingTextId());
|
|
2137
|
-
if (com) {
|
|
2138
|
-
const pos = this.getCommentToolbarPos(com);
|
|
2139
|
-
this.canvasController.moveTextToolbar(pos.x, pos.y);
|
|
2140
|
-
}
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
|
-
|
|
2144
|
-
// Returns a position object that describes the position in page coordinates
|
|
2145
|
-
// of the comment toolbar so that it is positioned above the comment being edited.
|
|
2146
|
-
getCommentToolbarPos(com) {
|
|
2147
|
-
const pos = this.unTransformPos({ x: com.x_pos, y: com.y_pos });
|
|
2148
|
-
return {
|
|
2149
|
-
x: pos.x + this.canvasLayout.commentToolbarPosX,
|
|
2150
|
-
y: pos.y + this.canvasLayout.commentToolbarPosY
|
|
2151
|
-
};
|
|
2152
|
-
}
|
|
2153
|
-
|
|
2154
|
-
// Returns a new zoom which is the result of incrementing the current zoom
|
|
2155
|
-
// by the amount since the previous d3Event transform amount.
|
|
2156
|
-
// We calculate increments because d3Event.transform is not based on
|
|
2157
|
-
// the constrained zoom position (which is very annoying) so we keep track
|
|
2158
|
-
// of the current constraind zoom amount in this.zoomTransform.
|
|
2159
|
-
getTransformIncrement(d3Event) {
|
|
2160
|
-
const xInc = d3Event.transform.x - this.previousD3Event.x;
|
|
2161
|
-
const yInc = d3Event.transform.y - this.previousD3Event.y;
|
|
2162
|
-
|
|
2163
|
-
const newTransform = { x: this.zoomTransform.x + xInc, y: this.zoomTransform.y + yInc, k: d3Event.transform.k };
|
|
2164
|
-
this.previousD3Event = { x: d3Event.transform.x, y: d3Event.transform.y, k: d3Event.transform.k };
|
|
2165
|
-
return newTransform;
|
|
2166
|
-
}
|
|
2167
|
-
|
|
2168
|
-
// Returns a modifed transform object so that the canvas area (the area
|
|
2169
|
-
// containing nodes and comments) is constrained such that it never totally
|
|
2170
|
-
// disappears from the view port.
|
|
2171
|
-
zoomConstrainRegular(transform, viewPort, canvasDimensions) {
|
|
2172
|
-
if (!canvasDimensions) {
|
|
2173
|
-
return this.zoomTransform;
|
|
2174
|
-
}
|
|
2175
|
-
|
|
2176
|
-
const k = transform.k;
|
|
2177
|
-
let x = transform.x;
|
|
2178
|
-
let y = transform.y;
|
|
2179
|
-
|
|
2180
|
-
const canv =
|
|
2181
|
-
this.convertCanvasDimensionsAdjustedForScaleWithPadding(canvasDimensions, k, this.getZoomToFitPadding());
|
|
2182
|
-
|
|
2183
|
-
const rightOffsetLimit = viewPort.width - Math.min((viewPort.width * 0.25), (canv.width * 0.25));
|
|
2184
|
-
const leftOffsetLimit = -(Math.max((canv.width - (viewPort.width * 0.25)), (canv.width * 0.75)));
|
|
2185
|
-
|
|
2186
|
-
const bottomOffsetLimit = viewPort.height - Math.min((viewPort.height * 0.25), (canv.height * 0.25));
|
|
2187
|
-
const topOffsetLimit = -(Math.max((canv.height - (viewPort.height * 0.25)), (canv.height * 0.75)));
|
|
2188
|
-
|
|
2189
|
-
if (x > -canv.left + rightOffsetLimit) {
|
|
2190
|
-
x = -canv.left + rightOffsetLimit;
|
|
2191
|
-
|
|
2192
|
-
} else if (x < -canv.left + leftOffsetLimit) {
|
|
2193
|
-
x = -canv.left + leftOffsetLimit;
|
|
2194
|
-
}
|
|
2195
|
-
|
|
2196
|
-
if (y > -canv.top + bottomOffsetLimit) {
|
|
2197
|
-
y = -canv.top + bottomOffsetLimit;
|
|
2198
|
-
|
|
2199
|
-
} else if (y < -canv.top + topOffsetLimit) {
|
|
2200
|
-
y = -canv.top + topOffsetLimit;
|
|
2201
|
-
}
|
|
2202
|
-
|
|
2203
|
-
return d3.zoomIdentity.translate(x, y).scale(k);
|
|
2204
|
-
}
|
|
2205
|
-
|
|
2206
|
-
// Returns the dimensions in SVG coordinates of the canvas area. This is
|
|
2207
|
-
// based on the position and width and height of the nodes and comments. It
|
|
2208
|
-
// does not include the 'super binding nodes' which are the binding nodes in
|
|
2209
|
-
// a sub-flow that map to a port in the containing supernode. The dimensions
|
|
2210
|
-
// are scaled by k and padded by pad (if provided).
|
|
2211
|
-
getCanvasDimensionsAdjustedForScale(k, pad) {
|
|
2212
|
-
const gap = this.canvasLayout.commentHighlightGap;
|
|
2213
|
-
const canvasDimensions = this.activePipeline.getCanvasDimensions(gap);
|
|
2214
|
-
return this.convertCanvasDimensionsAdjustedForScaleWithPadding(canvasDimensions, k, pad);
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
|
-
convertCanvasDimensionsAdjustedForScaleWithPadding(canv, k, pad) {
|
|
2218
|
-
const padding = pad || 0;
|
|
2219
|
-
if (canv) {
|
|
2220
|
-
return {
|
|
2221
|
-
left: (canv.left * k) - padding,
|
|
2222
|
-
top: (canv.top * k) - padding,
|
|
2223
|
-
right: (canv.right * k) + padding,
|
|
2224
|
-
bottom: (canv.bottom * k) + padding,
|
|
2225
|
-
width: (canv.width * k) + (2 * padding),
|
|
2226
|
-
height: (canv.height * k) + (2 * padding)
|
|
2227
|
-
};
|
|
2228
|
-
}
|
|
2229
|
-
return null;
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
drawRegionSelector(d3Event) {
|
|
2233
|
-
this.removeRegionSelector();
|
|
2234
|
-
const { startX, startY, width, height } = this.getRegionDimensions(d3Event);
|
|
2235
|
-
|
|
2236
|
-
this.canvasGrp
|
|
2237
|
-
.append("rect")
|
|
2238
|
-
.attr("width", width)
|
|
2239
|
-
.attr("height", height)
|
|
2240
|
-
.attr("x", startX)
|
|
2241
|
-
.attr("y", startY)
|
|
2242
|
-
.attr("class", "d3-region-selector");
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
removeRegionSelector() {
|
|
2246
|
-
this.canvasGrp.selectAll(".d3-region-selector").remove();
|
|
2247
|
-
}
|
|
2248
|
-
|
|
2249
|
-
// Returns the startX, startY, width and height of the selection region
|
|
2250
|
-
// where startX and startY are always the top left corner of the region
|
|
2251
|
-
// and width and height are therefore always positive.
|
|
2252
|
-
getRegionDimensions(d3Event) {
|
|
2253
|
-
const transPos = this.getTransformedMousePos(d3Event);
|
|
2254
|
-
let startX = this.zoomStartPoint.startX;
|
|
2255
|
-
let startY = this.zoomStartPoint.startY;
|
|
2256
|
-
let width = transPos.x - startX;
|
|
2257
|
-
let height = transPos.y - startY;
|
|
2258
|
-
|
|
2259
|
-
if (width < 0) {
|
|
2260
|
-
width = Math.abs(width);
|
|
2261
|
-
startX -= width;
|
|
2262
|
-
}
|
|
2263
|
-
if (height < 0) {
|
|
2264
|
-
height = Math.abs(height);
|
|
2265
|
-
startY -= height;
|
|
2266
|
-
}
|
|
2267
|
-
|
|
2268
|
-
return { startX, startY, width, height };
|
|
2269
|
-
}
|
|
2270
|
-
|
|
2271
|
-
// Records the initial starting position of the object being sized so
|
|
2272
|
-
// that drag increments can be added to the original starting
|
|
2273
|
-
// position to aid calculating the snap-to-grid position.
|
|
2274
|
-
initializeResizeVariables(resizeObj) {
|
|
2275
|
-
this.resizeObjInitialInfo = {
|
|
2276
|
-
x_pos: resizeObj.x_pos, y_pos: resizeObj.y_pos, width: resizeObj.width, height: resizeObj.height };
|
|
2277
|
-
this.notSnappedXPos = resizeObj.x_pos;
|
|
2278
|
-
this.notSnappedYPos = resizeObj.y_pos;
|
|
2279
|
-
this.notSnappedWidth = resizeObj.width;
|
|
2280
|
-
this.notSnappedHeight = resizeObj.height;
|
|
2281
|
-
}
|
|
2282
|
-
|
|
2283
|
-
// Returns an array of objects to drag. If enableDragWithoutSelect is true,
|
|
2284
|
-
// and the object on which this drag start has initiated is not in the
|
|
2285
|
-
// set of selected objects, then just that object is to be dragged. Otherwise,
|
|
2286
|
-
// the selected objects are the objects to be dragged.
|
|
2287
|
-
getDragObjects(d) {
|
|
2288
|
-
const selectedObjects = this.activePipeline.getSelectedNodesAndComments();
|
|
2289
|
-
|
|
2290
|
-
if (this.config.enableDragWithoutSelect &&
|
|
2291
|
-
selectedObjects.findIndex((o) => o.id === d.id) === -1) {
|
|
2292
|
-
return [d];
|
|
2293
|
-
}
|
|
2294
|
-
|
|
2295
|
-
return selectedObjects;
|
|
2296
|
-
}
|
|
2297
|
-
|
|
2298
|
-
dragStart(d3Event, d) {
|
|
2299
|
-
this.logger.logStartTimer("dragStart");
|
|
2300
|
-
|
|
2301
|
-
this.closeContextMenuIfOpen();
|
|
2302
|
-
|
|
2303
|
-
if (this.config.enableContextToolbar) {
|
|
2304
|
-
this.removeContextToolbar();
|
|
2305
|
-
}
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
// Note: Comment and Node resizing is started by the comment/node highlight rectangle.
|
|
2309
|
-
if (this.commentSizing) {
|
|
2310
|
-
this.initializeResizeVariables(d);
|
|
2311
|
-
|
|
2312
|
-
} else if (this.nodeSizing) {
|
|
2313
|
-
this.initializeResizeVariables(d);
|
|
2314
|
-
|
|
2315
|
-
} else {
|
|
2316
|
-
this.dragObjectsStart(d3Event, d);
|
|
2317
|
-
}
|
|
2318
|
-
this.logger.logEndTimer("dragStart", true);
|
|
2319
|
-
}
|
|
2320
|
-
|
|
2321
|
-
dragMove(d3Event, d) {
|
|
2322
|
-
this.logger.logStartTimer("dragMove");
|
|
2323
|
-
if (this.commentSizing) {
|
|
2324
|
-
this.resizeComment(d3Event, d);
|
|
2325
|
-
} else if (this.nodeSizing) {
|
|
2326
|
-
this.resizeNode(d3Event, d);
|
|
2327
|
-
} else {
|
|
2328
|
-
this.dragObjectsAction(d3Event);
|
|
2329
|
-
}
|
|
2330
|
-
|
|
2331
|
-
this.logger.logEndTimer("dragMove", true);
|
|
2332
|
-
}
|
|
2333
|
-
|
|
2334
|
-
dragEnd(d3Event, d) {
|
|
2335
|
-
this.logger.logStartTimer("dragEnd");
|
|
2336
|
-
|
|
2337
|
-
this.removeTempCursorOverlay();
|
|
2338
|
-
|
|
2339
|
-
if (this.commentSizing) {
|
|
2340
|
-
this.endCommentSizing(d);
|
|
2341
|
-
|
|
2342
|
-
} else if (this.nodeSizing) {
|
|
2343
|
-
this.endNodeSizing(d);
|
|
2344
|
-
|
|
2345
|
-
} else if (this.dragging) {
|
|
2346
|
-
this.dragObjectsEnd(d3Event, d);
|
|
2347
|
-
}
|
|
2348
|
-
|
|
2349
|
-
this.logger.logEndTimer("dragEnd", true);
|
|
2350
|
-
}
|
|
2351
|
-
|
|
2352
|
-
// Starts the dragging action for canvas objects (nodes and comments).
|
|
2353
|
-
dragObjectsStart(d3Event, d) {
|
|
2354
|
-
// Ensure flags are false before staring a new drag.
|
|
2355
|
-
this.existingNodeInsertableIntoLink = false;
|
|
2356
|
-
this.existingNodeAttachableToDetachedLinks = false;
|
|
2357
|
-
|
|
2358
|
-
this.dragging = true;
|
|
2359
|
-
this.dragOffsetX = 0;
|
|
2360
|
-
this.dragOffsetY = 0;
|
|
2361
|
-
this.dragRunningX = 0;
|
|
2362
|
-
this.dragRunningY = 0;
|
|
2363
|
-
this.dragObjects = this.getDragObjects(d);
|
|
2364
|
-
if (this.dragObjects && this.dragObjects.length > 0) {
|
|
2365
|
-
this.dragStartX = this.dragObjects[0].x_pos;
|
|
2366
|
-
this.dragStartY = this.dragObjects[0].y_pos;
|
|
2367
|
-
}
|
|
2368
|
-
|
|
2369
|
-
// If we are dragging an 'insertable' node, set it to be translucent so
|
|
2370
|
-
// that, when it is dragged over a link line, the highlightd line can be seen OK.
|
|
2371
|
-
if (this.isExistingNodeInsertableIntoLink()) {
|
|
2372
|
-
// Only style the node to be translucent if this action isn't cancelled
|
|
2373
|
-
// by the user releasing the mouse button within 200 ms of pressing it.
|
|
2374
|
-
// This stops the node flashing when the user is only selecting it.
|
|
2375
|
-
this.startNodeInsertingInLink = setTimeout(() => {
|
|
2376
|
-
this.existingNodeInsertableIntoLink = true;
|
|
2377
|
-
this.setNodeTranslucentState(this.dragObjects[0].id, true);
|
|
2378
|
-
this.setDataLinkSelectionAreaWider(true);
|
|
2379
|
-
}, 200);
|
|
2380
|
-
}
|
|
2381
|
-
|
|
2382
|
-
// If we are dragging an 'attachable' node, set it to be translucent so
|
|
2383
|
-
// that, when it is dragged over link lines, the highlightd lines can be seen OK.
|
|
2384
|
-
if (this.isExistingNodeAttachableToDetachedLinks()) {
|
|
2385
|
-
// Only style the node to be translucent if this action isn't cancelled
|
|
2386
|
-
// by the user releasing the mouse button within 200 ms of pressing it.
|
|
2387
|
-
// This stops the node from when the user is only selecting it.
|
|
2388
|
-
this.startNodeAttachingToDetachedLinks = setTimeout(() => {
|
|
2389
|
-
this.existingNodeAttachableToDetachedLinks = true;
|
|
2390
|
-
const mousePos = this.getTransformedMousePos(d3Event);
|
|
2391
|
-
this.dragPointerOffsetInNode = {
|
|
2392
|
-
x: mousePos.x - this.dragObjects[0].x_pos,
|
|
2393
|
-
y: mousePos.y - this.dragObjects[0].y_pos
|
|
2394
|
-
};
|
|
2395
|
-
this.setNodeTranslucentState(this.dragObjects[0].id, true);
|
|
2396
|
-
}, 200);
|
|
2397
|
-
}
|
|
2398
|
-
}
|
|
2399
|
-
|
|
2400
|
-
// Performs the dragging action for canvas objects (nodes and comments).
|
|
2401
|
-
dragObjectsAction(d3Event) {
|
|
2402
|
-
this.dragOffsetX += d3Event.dx;
|
|
2403
|
-
this.dragOffsetY += d3Event.dy;
|
|
2404
|
-
|
|
2405
|
-
// Limit the size a drag can be so, when the user is dragging objects in
|
|
2406
|
-
// an in-place subflow they do not drag them too far.
|
|
2407
|
-
// this.logger.log("Drag offset X = " + this.dragOffsetX + " y = " + this.dragOffsetY);
|
|
2408
|
-
if (this.dispUtils.isDisplayingSubFlowInPlace() &&
|
|
2409
|
-
(this.dragOffsetX > 1000 || this.dragOffsetX < -1000 ||
|
|
2410
|
-
this.dragOffsetY > 1000 || this.dragOffsetY < -1000)) {
|
|
2411
|
-
this.dragOffsetX -= d3Event.dx;
|
|
2412
|
-
this.dragOffsetY -= d3Event.dy;
|
|
2413
|
-
|
|
2414
|
-
} else {
|
|
2415
|
-
let increment = { x: 0, y: 0 };
|
|
2416
|
-
|
|
2417
|
-
if (this.config.enableSnapToGridType === SNAP_TO_GRID_DURING) {
|
|
2418
|
-
const stgPos = this.snapToGridDraggedNode();
|
|
2419
|
-
|
|
2420
|
-
increment = {
|
|
2421
|
-
x: stgPos.x - this.dragObjects[0].x_pos,
|
|
2422
|
-
y: stgPos.y - this.dragObjects[0].y_pos
|
|
2423
|
-
};
|
|
2424
|
-
|
|
2425
|
-
} else {
|
|
2426
|
-
increment = {
|
|
2427
|
-
x: d3Event.dx,
|
|
2428
|
-
y: d3Event.dy
|
|
2429
|
-
};
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
|
-
this.dragRunningX += increment.x;
|
|
2433
|
-
this.dragRunningY += increment.y;
|
|
2434
|
-
|
|
2435
|
-
this.dragObjects.forEach((d) => {
|
|
2436
|
-
d.x_pos += increment.x;
|
|
2437
|
-
d.y_pos += increment.y;
|
|
2438
|
-
});
|
|
2439
|
-
|
|
2440
|
-
if (this.config.enableLinkSelection === LINK_SELECTION_DETACHABLE) {
|
|
2441
|
-
this.activePipeline.getSelectedLinks().forEach((link) => {
|
|
2442
|
-
if (link.srcPos) {
|
|
2443
|
-
link.srcPos.x_pos += increment.x;
|
|
2444
|
-
link.srcPos.y_pos += increment.y;
|
|
2445
|
-
}
|
|
2446
|
-
if (link.trgPos) {
|
|
2447
|
-
link.trgPos.x_pos += increment.x;
|
|
2448
|
-
link.trgPos.y_pos += increment.y;
|
|
2449
|
-
}
|
|
2450
|
-
});
|
|
2451
|
-
}
|
|
2452
|
-
}
|
|
2453
|
-
|
|
2454
|
-
this.displayMovedComments();
|
|
2455
|
-
this.displayMovedNodes();
|
|
2456
|
-
this.displayMovedLinks();
|
|
2457
|
-
this.displayCanvasAccoutrements();
|
|
2458
|
-
|
|
2459
|
-
if (this.dispUtils.isDisplayingSubFlowInPlace()) {
|
|
2460
|
-
this.displaySVGToFitSupernode();
|
|
2461
|
-
}
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
if (this.existingNodeInsertableIntoLink) {
|
|
2465
|
-
const link = this.getLinkAtMousePos(d3Event.sourceEvent.clientX, d3Event.sourceEvent.clientY);
|
|
2466
|
-
// Set highlighting when there is no link because this will turn
|
|
2467
|
-
// current highlighting off. And only switch on highlighting when we are
|
|
2468
|
-
// over a fully attached link (not a detached link) and provided the
|
|
2469
|
-
// link is not to/from the node being dragged (which is possible in
|
|
2470
|
-
// some odd situations).
|
|
2471
|
-
if (!link ||
|
|
2472
|
-
(this.isLinkFullyAttached(link) &&
|
|
2473
|
-
this.dragObjects[0].id !== link.srcNodeId &&
|
|
2474
|
-
this.dragObjects[0].id !== link.trgNodeId)) {
|
|
2475
|
-
this.setInsertNodeIntoLinkHighlighting(link);
|
|
2476
|
-
}
|
|
2477
|
-
}
|
|
2478
|
-
|
|
2479
|
-
if (this.existingNodeAttachableToDetachedLinks) {
|
|
2480
|
-
const mousePos = this.getTransformedMousePos(d3Event);
|
|
2481
|
-
const node = this.dragObjects[0];
|
|
2482
|
-
const ghostArea = {
|
|
2483
|
-
x1: mousePos.x - this.dragPointerOffsetInNode.x,
|
|
2484
|
-
y1: mousePos.y - this.dragPointerOffsetInNode.y,
|
|
2485
|
-
x2: mousePos.x - this.dragPointerOffsetInNode.x + node.width,
|
|
2486
|
-
y2: mousePos.y - this.dragPointerOffsetInNode.y + node.height
|
|
2487
|
-
};
|
|
2488
|
-
const links = this.getAttachableLinksForNodeAtPos(node, ghostArea);
|
|
2489
|
-
this.setDetachedLinkHighlighting(links);
|
|
2490
|
-
}
|
|
2491
|
-
}
|
|
2492
|
-
|
|
2493
|
-
// Ends the dragging action for canvas objects (nodes and comments).
|
|
2494
|
-
dragObjectsEnd(d3Event, d) {
|
|
2495
|
-
// Set to false before updating object model so main body of displayNodes is run.
|
|
2496
|
-
this.dragging = false;
|
|
2497
|
-
|
|
2498
|
-
// Cancels the styling of insertable/attachable nodes if the user releases
|
|
2499
|
-
// the mouse button with 200 milliseconds of pressing it on the node. This
|
|
2500
|
-
// stops the node flashing when the user just selects the node.
|
|
2501
|
-
clearTimeout(this.startNodeInsertingInLink);
|
|
2502
|
-
clearTimeout(this.startNodeAttachingToDetachedLinks);
|
|
2503
|
-
|
|
2504
|
-
// If the pointer hasn't moved and enableDragWithoutSelect we interpret
|
|
2505
|
-
// that as a select on the object.
|
|
2506
|
-
if (this.dragOffsetX === 0 &&
|
|
2507
|
-
this.dragOffsetY === 0 &&
|
|
2508
|
-
this.config.enableDragWithoutSelect) {
|
|
2509
|
-
this.selectObjectSourceEvent(d3Event, d);
|
|
2510
|
-
|
|
2511
|
-
} else {
|
|
2512
|
-
if (this.dragRunningX !== 0 ||
|
|
2513
|
-
this.dragRunningY !== 0) {
|
|
2514
|
-
let dragFinalOffset = null;
|
|
2515
|
-
if (this.config.enableSnapToGridType === SNAP_TO_GRID_AFTER) {
|
|
2516
|
-
const stgPos = this.snapToGridDraggedNode();
|
|
2517
|
-
dragFinalOffset = {
|
|
2518
|
-
x: stgPos.x - this.dragStartX,
|
|
2519
|
-
y: stgPos.y - this.dragStartY
|
|
2520
|
-
};
|
|
2521
|
-
} else {
|
|
2522
|
-
dragFinalOffset = { x: this.dragRunningX, y: this.dragRunningY };
|
|
2523
|
-
}
|
|
2524
|
-
|
|
2525
|
-
if (this.existingNodeInsertableIntoLink &&
|
|
2526
|
-
this.dragOverLink) {
|
|
2527
|
-
this.canvasController.editActionHandler({
|
|
2528
|
-
editType: "insertNodeIntoLink",
|
|
2529
|
-
editSource: "canvas",
|
|
2530
|
-
node: this.dragObjects[0],
|
|
2531
|
-
link: this.dragOverLink,
|
|
2532
|
-
offsetX: dragFinalOffset.x,
|
|
2533
|
-
offsetY: dragFinalOffset.y,
|
|
2534
|
-
pipelineId: this.activePipeline.id });
|
|
2535
|
-
|
|
2536
|
-
} else if (this.existingNodeAttachableToDetachedLinks &&
|
|
2537
|
-
this.dragOverDetachedLinks.length > 0) {
|
|
2538
|
-
this.canvasController.editActionHandler({
|
|
2539
|
-
editType: "attachNodeToLinks",
|
|
2540
|
-
editSource: "canvas",
|
|
2541
|
-
node: this.dragObjects[0],
|
|
2542
|
-
detachedLinks: this.dragOverDetachedLinks,
|
|
2543
|
-
offsetX: dragFinalOffset.x,
|
|
2544
|
-
offsetY: dragFinalOffset.y,
|
|
2545
|
-
pipelineId: this.activePipeline.id });
|
|
2546
|
-
|
|
2547
|
-
} else {
|
|
2548
|
-
this.canvasController.editActionHandler({
|
|
2549
|
-
editType: "moveObjects",
|
|
2550
|
-
editSource: "canvas",
|
|
2551
|
-
nodes: this.dragObjects.map((o) => o.id),
|
|
2552
|
-
links: this.activePipeline.getSelectedDetachedLinks(),
|
|
2553
|
-
offsetX: dragFinalOffset.x,
|
|
2554
|
-
offsetY: dragFinalOffset.y,
|
|
2555
|
-
pipelineId: this.activePipeline.id });
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2558
|
-
}
|
|
2559
|
-
|
|
2560
|
-
// Switch off any drag highlighting
|
|
2561
|
-
this.setDataLinkSelectionAreaWider(false);
|
|
2562
|
-
this.unsetNodeTranslucentState();
|
|
2563
|
-
this.unsetInsertNodeIntoLinkHighlighting();
|
|
2564
|
-
this.unsetDetachedLinkHighlighting();
|
|
2565
|
-
}
|
|
2566
|
-
|
|
2567
|
-
dragStartLinkHandle(d3Event, d) {
|
|
2568
|
-
this.logger.logStartTimer("dragStartLinkHandle");
|
|
2569
|
-
|
|
2570
|
-
this.closeContextMenuIfOpen();
|
|
2571
|
-
|
|
2572
|
-
this.draggingLinkHandle = true;
|
|
2573
|
-
|
|
2574
|
-
const handleSelection = d3.select(d3Event.sourceEvent.currentTarget);
|
|
2575
|
-
const link = this.activePipeline.getLink(d.id);
|
|
2576
|
-
const oldLink = cloneDeep(link);
|
|
2577
|
-
|
|
2578
|
-
const linkGrpSelector = this.getLinkGroupSelectionById(d.id);
|
|
2579
|
-
|
|
2580
|
-
this.draggingLinkData = {
|
|
2581
|
-
lineInfo: d,
|
|
2582
|
-
link: link,
|
|
2583
|
-
oldLink: oldLink,
|
|
2584
|
-
linkGrpSelection: d3.select(linkGrpSelector)
|
|
2585
|
-
};
|
|
2586
|
-
|
|
2587
|
-
if (handleSelection.attr("class").includes("d3-link-handle-end")) {
|
|
2588
|
-
this.draggingLinkData.endBeingDragged = "end";
|
|
2589
|
-
|
|
2590
|
-
} else if (handleSelection.attr("class").includes("d3-link-handle-start")) {
|
|
2591
|
-
this.draggingLinkData.endBeingDragged = "start";
|
|
2592
|
-
}
|
|
2593
|
-
|
|
2594
|
-
if (this.config.enableHighlightUnavailableNodes) {
|
|
2595
|
-
if (this.draggingLinkData.endBeingDragged === "end") {
|
|
2596
|
-
const links = this.activePipeline.links.filter((lnk) => lnk.id !== link.id);
|
|
2597
|
-
this.setUnavailableTargetNodesHighlighting(
|
|
2598
|
-
this.activePipeline.getNode(this.draggingLinkData.link.srcNodeId),
|
|
2599
|
-
this.draggingLinkData.link.srcNodePortId,
|
|
2600
|
-
links
|
|
2601
|
-
);
|
|
2602
|
-
} else if (this.draggingLinkData.endBeingDragged === "start") {
|
|
2603
|
-
const links = this.activePipeline.links.filter((lnk) => lnk.id !== link.id);
|
|
2604
|
-
this.setUnavailableSourceNodesHighlighting(
|
|
2605
|
-
this.activePipeline.getNode(this.draggingLinkData.oldLink.trgNodeId),
|
|
2606
|
-
this.draggingLinkData.link.trgNodePortId,
|
|
2607
|
-
links
|
|
2608
|
-
);
|
|
2609
|
-
}
|
|
2610
|
-
}
|
|
2611
|
-
|
|
2612
|
-
this.dragLinkHandle(d3Event);
|
|
2613
|
-
this.logger.logEndTimer("dragStartLinkHandle", true);
|
|
2614
|
-
}
|
|
2615
|
-
|
|
2616
|
-
dragMoveLinkHandle(d3Event) {
|
|
2617
|
-
this.logger.logStartTimer("dragMoveLinkHandle");
|
|
2618
|
-
this.dragLinkHandle(d3Event);
|
|
2619
|
-
this.logger.logEndTimer("dragMoveLinkHandle", true);
|
|
2620
|
-
}
|
|
2621
|
-
|
|
2622
|
-
dragEndLinkHandle(d3Event) {
|
|
2623
|
-
this.logger.logStartTimer("dragEndLinkHandle");
|
|
2624
|
-
this.completeDraggedLink(d3Event);
|
|
2625
|
-
this.draggingLinkHandle = false;
|
|
2626
|
-
this.logger.logEndTimer("dragEndLinkHandle", true);
|
|
2627
|
-
}
|
|
2628
|
-
|
|
2629
|
-
// Switches on or off the translucent state of the node identified by the
|
|
2630
|
-
// node ID passed in. This is used when an 'insertable' node is dragged on
|
|
2631
|
-
// the canvas. It makes is easier for the user to see the highlighted link
|
|
2632
|
-
// when the node is dragged over it.
|
|
2633
|
-
setNodeTranslucentState(nodeId, state) {
|
|
2634
|
-
this.getNodeGroupSelectionById(nodeId).classed("d3-node-group-translucent", state);
|
|
2635
|
-
}
|
|
2636
|
-
|
|
2637
|
-
// Switched off the translucent state of the objects being dragged (if
|
|
2638
|
-
// there are any).
|
|
2639
|
-
unsetNodeTranslucentState() {
|
|
2640
|
-
if (this.dragObjects && this.dragObjects.length > 0) {
|
|
2641
|
-
this.setNodeTranslucentState(this.dragObjects[0].id, false);
|
|
2642
|
-
}
|
|
2643
|
-
}
|
|
2644
|
-
|
|
2645
|
-
// Returns the snap-to-grid position of the object positioned at
|
|
2646
|
-
// this.dragStartX and this.dragStartY after applying the current offset of
|
|
2647
|
-
// this.dragOffsetX and this.dragOffsetY.
|
|
2648
|
-
snapToGridDraggedNode() {
|
|
2649
|
-
const objPosX = this.dragStartX + this.dragOffsetX;
|
|
2650
|
-
const objPosY = this.dragStartY + this.dragOffsetY;
|
|
2651
|
-
|
|
2652
|
-
return this.snapToGridPosition({ x: objPosX, y: objPosY });
|
|
2653
|
-
}
|
|
2654
|
-
|
|
2655
|
-
// Returns the snap-to-grid position of the object positioned at objPos.x
|
|
2656
|
-
// and objPos.y. The grid that is snapped to is defined by this.snapToGridXPx
|
|
2657
|
-
// and this.snapToGridYPx values which are pixel values.
|
|
2658
|
-
snapToGridPosition(objPos) {
|
|
2659
|
-
const stgPosX = CanvasUtils.snapToGrid(objPos.x, this.canvasLayout.snapToGridXPx);
|
|
2660
|
-
const stgPosY = CanvasUtils.snapToGrid(objPos.y, this.canvasLayout.snapToGridYPx);
|
|
2661
|
-
|
|
2662
|
-
return { x: stgPosX, y: stgPosY };
|
|
2663
|
-
}
|
|
2664
|
-
|
|
2665
|
-
// Displays all the nodes on the canvas either by creating new nodes,
|
|
2666
|
-
// updating existing nodes or removing unwanted nodes.
|
|
2667
|
-
displayNodes() {
|
|
2668
|
-
this.logger.logStartTimer("displayNodes " + this.getFlags());
|
|
2669
|
-
|
|
2670
|
-
// Set the port positions for all ports - these will be needed when displaying
|
|
2671
|
-
// nodes and links. This needs to be done here because a resized supernode
|
|
2672
|
-
// will cause its ports to move and resizing comments causes links to be
|
|
2673
|
-
// redrawn which will need port positions to be set appropriately.
|
|
2674
|
-
this.setPortPositionsAllNodes();
|
|
2675
|
-
|
|
2676
|
-
const sel = this.getAllNodeGroupsSelection();
|
|
2677
|
-
this.displayNodesSubset(sel, this.activePipeline.nodes);
|
|
2678
|
-
|
|
2679
|
-
this.logger.logEndTimer("displayNodes " + this.getFlags());
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
|
-
displayMovedNodes() {
|
|
2683
|
-
this.logger.logStartTimer("displayMovedNodes");
|
|
2684
|
-
const nodeGroupSel = this.getAllNodeGroupsSelection();
|
|
2685
|
-
|
|
2686
|
-
nodeGroupSel
|
|
2687
|
-
.datum((d) => this.activePipeline.getNode(d.id))
|
|
2688
|
-
.attr("transform", (d) => `translate(${d.x_pos}, ${d.y_pos})`);
|
|
2689
|
-
|
|
2690
|
-
if (this.dispUtils.isDisplayingSubFlow()) {
|
|
2691
|
-
nodeGroupSel
|
|
2692
|
-
.each((d, i, nodeGrps) => {
|
|
2693
|
-
if (d.isSupernodeInputBinding) {
|
|
2694
|
-
this.updatePortRadiusAndPos(nodeGrps[i], d, "d3-node-port-output-main");
|
|
2695
|
-
}
|
|
2696
|
-
if (d.isSupernodeOutputBinding) {
|
|
2697
|
-
this.updatePortRadiusAndPos(nodeGrps[i], d, "d3-node-port-input-main");
|
|
2698
|
-
this.updatePortArrowPath(nodeGrps[i], "d3-node-port-input-arrow");
|
|
2699
|
-
}
|
|
2700
|
-
});
|
|
2701
|
-
}
|
|
2702
|
-
this.logger.logEndTimer("displayMovedNodes");
|
|
2703
|
-
}
|
|
2704
|
-
|
|
2705
|
-
displayNodesSelectionStatus(selectionInfo) {
|
|
2706
|
-
this.logger.logStartTimer("displayNodesSelectionStatus");
|
|
2707
|
-
|
|
2708
|
-
this.getAllNodeGroupsSelection()
|
|
2709
|
-
.selectChildren(".d3-node-selection-highlight")
|
|
2710
|
-
.attr("data-selected", (d) => (this.activePipeline.isSelected(d.id) ? "yes" : "no"));
|
|
2711
|
-
|
|
2712
|
-
this.logger.logEndTimer("displayNodesSelectionStatus");
|
|
2713
|
-
}
|
|
2714
|
-
|
|
2715
|
-
displaySingleNode(d) {
|
|
2716
|
-
this.logger.logStartTimer("displaySingleNode " + this.getFlags());
|
|
2717
|
-
|
|
2718
|
-
this.setPortPositionsForNode(d);
|
|
2719
|
-
|
|
2720
|
-
const selection = this.getNodeGroupSelectionById(d.id);
|
|
2721
|
-
this.displayNodesSubset(selection, [d]);
|
|
2722
|
-
|
|
2723
|
-
this.logger.logEndTimer("displaySingleNode " + this.getFlags());
|
|
2724
|
-
}
|
|
2725
|
-
|
|
2726
|
-
displayNodesSubset(selection, data) {
|
|
2727
|
-
selection
|
|
2728
|
-
.data(data, (d) => d.id)
|
|
2729
|
-
.join(
|
|
2730
|
-
(enter) => this.createNodes(enter),
|
|
2731
|
-
null,
|
|
2732
|
-
(remove) => this.removeNodes(remove)
|
|
2733
|
-
)
|
|
2734
|
-
.attr("transform", (d) => `translate(${d.x_pos}, ${d.y_pos})`)
|
|
2735
|
-
.attr("class", (d) => this.getNodeGroupClass(d))
|
|
2736
|
-
.attr("style", (d) => this.getNodeGrpStyle(d))
|
|
2737
|
-
.call((joinedNodeGrps) => this.updateNodes(joinedNodeGrps, data));
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
createNodes(enter) {
|
|
2741
|
-
this.logger.logStartTimer("createNodes");
|
|
2742
|
-
|
|
2743
|
-
const newNodeGroups = enter
|
|
2744
|
-
.append("g")
|
|
2745
|
-
.attr("data-id", (d) => this.getId("node_grp", d.id))
|
|
2746
|
-
.call(this.attachNodeGroupListeners.bind(this));
|
|
2747
|
-
|
|
2748
|
-
this.logger.logEndTimer("createNodes");
|
|
2749
|
-
|
|
2750
|
-
return newNodeGroups;
|
|
2751
|
-
}
|
|
2752
|
-
|
|
2753
|
-
updateNodes(joinedNodeGrps, data) {
|
|
2754
|
-
this.logger.logStartTimer("updateNodes");
|
|
2755
|
-
|
|
2756
|
-
const nonBindingNodeGrps = joinedNodeGrps.filter((node) => !CanvasUtils.isSuperBindingNode(node));
|
|
1606
|
+
const nonBindingNodeGrps = joinedNodeGrps.filter((node) => !CanvasUtils.isSuperBindingNode(node));
|
|
2757
1607
|
|
|
2758
1608
|
// Node Sizing Area
|
|
2759
1609
|
nonBindingNodeGrps
|
|
@@ -2769,7 +1619,6 @@ export default class SVGCanvasRenderer {
|
|
|
2769
1619
|
.datum((d) => this.activePipeline.getNode(d.id))
|
|
2770
1620
|
.attr("d", (d) => this.getNodeShapePathSizing(d));
|
|
2771
1621
|
|
|
2772
|
-
|
|
2773
1622
|
// Node Selection Highlighting Outline.
|
|
2774
1623
|
nonBindingNodeGrps
|
|
2775
1624
|
.selectChildren(".d3-node-selection-highlight")
|
|
@@ -2810,7 +1659,8 @@ export default class SVGCanvasRenderer {
|
|
|
2810
1659
|
.attr("class", "d3-foreign-object-external-node"),
|
|
2811
1660
|
null,
|
|
2812
1661
|
(exit) => {
|
|
2813
|
-
exit.each(
|
|
1662
|
+
exit.each((d, idx, exts) =>
|
|
1663
|
+
this.externalUtils.removeExternalObject(d, idx, exts));
|
|
2814
1664
|
exit.remove();
|
|
2815
1665
|
}
|
|
2816
1666
|
)
|
|
@@ -2819,7 +1669,8 @@ export default class SVGCanvasRenderer {
|
|
|
2819
1669
|
.attr("height", (d) => d.height)
|
|
2820
1670
|
.attr("x", 0)
|
|
2821
1671
|
.attr("y", 0)
|
|
2822
|
-
.each(
|
|
1672
|
+
.each((d, idx, exts) =>
|
|
1673
|
+
this.externalUtils.addNodeExternalObject(d, idx, exts));
|
|
2823
1674
|
|
|
2824
1675
|
// Node Image
|
|
2825
1676
|
nonBindingNodeGrps
|
|
@@ -2898,8 +1749,9 @@ export default class SVGCanvasRenderer {
|
|
|
2898
1749
|
|
|
2899
1750
|
// Add or remove drag behavior as appropriate
|
|
2900
1751
|
if (this.config.enableEditingActions) {
|
|
1752
|
+
const handler = this.dragObjectUtils.getDragObjectHandler();
|
|
2901
1753
|
nonBindingNodeGrps
|
|
2902
|
-
.call(
|
|
1754
|
+
.call(handler);
|
|
2903
1755
|
} else {
|
|
2904
1756
|
nonBindingNodeGrps
|
|
2905
1757
|
.on(".drag", null);
|
|
@@ -2912,7 +1764,8 @@ export default class SVGCanvasRenderer {
|
|
|
2912
1764
|
// Remove any foreign objects for react nodes, if necessary.
|
|
2913
1765
|
removeSel
|
|
2914
1766
|
.selectChildren(".d3-foreign-object-external-node")
|
|
2915
|
-
.each(
|
|
1767
|
+
.each((d, idx, exts) =>
|
|
1768
|
+
this.externalUtils.removeExternalObject(d, idx, exts));
|
|
2916
1769
|
|
|
2917
1770
|
// Remove all nodes in the selection.
|
|
2918
1771
|
removeSel.remove();
|
|
@@ -3026,6 +1879,13 @@ export default class SVGCanvasRenderer {
|
|
|
3026
1879
|
.attr("transform", this.getPortArrowPathTransform(port));
|
|
3027
1880
|
}
|
|
3028
1881
|
});
|
|
1882
|
+
|
|
1883
|
+
if (this.config.enableEditingActions) {
|
|
1884
|
+
const handler = this.dragNewLinkUtils.getDragNewLinkHandler();
|
|
1885
|
+
joinedInputPortGrps.call(handler);
|
|
1886
|
+
} else {
|
|
1887
|
+
joinedInputPortGrps.on(".drag", null);
|
|
1888
|
+
}
|
|
3029
1889
|
}
|
|
3030
1890
|
|
|
3031
1891
|
displayOutputPorts(nodeGrp, node) {
|
|
@@ -3068,8 +1928,8 @@ export default class SVGCanvasRenderer {
|
|
|
3068
1928
|
return outputPortGroups;
|
|
3069
1929
|
}
|
|
3070
1930
|
|
|
3071
|
-
updateOutputPorts(
|
|
3072
|
-
|
|
1931
|
+
updateOutputPorts(joinedOutputPortGrps, node) {
|
|
1932
|
+
joinedOutputPortGrps.selectChildren(".d3-node-port-output-main")
|
|
3073
1933
|
.datum((port) => node.outputs.find((o) => port.id === o.id))
|
|
3074
1934
|
.each((port, i, outputPorts) => {
|
|
3075
1935
|
const obj = d3.select(outputPorts[i]);
|
|
@@ -3087,12 +1947,22 @@ export default class SVGCanvasRenderer {
|
|
|
3087
1947
|
.attr("cy", port.cy);
|
|
3088
1948
|
}
|
|
3089
1949
|
});
|
|
1950
|
+
|
|
1951
|
+
if (this.config.enableEditingActions) {
|
|
1952
|
+
const handler = this.dragNewLinkUtils.getDragNewLinkHandler();
|
|
1953
|
+
joinedOutputPortGrps.call(handler);
|
|
1954
|
+
} else {
|
|
1955
|
+
joinedOutputPortGrps.on(".drag", null);
|
|
1956
|
+
}
|
|
3090
1957
|
}
|
|
3091
1958
|
|
|
3092
1959
|
// Attaches the appropriate listeners to the node groups.
|
|
3093
1960
|
attachNodeGroupListeners(nodeGrps) {
|
|
3094
1961
|
nodeGrps
|
|
3095
1962
|
.on("mouseenter", (d3Event, d) => {
|
|
1963
|
+
if (this.isDragging()) {
|
|
1964
|
+
return;
|
|
1965
|
+
}
|
|
3096
1966
|
const nodeGrp = d3.select(d3Event.currentTarget);
|
|
3097
1967
|
this.raiseNodeToTop(nodeGrp);
|
|
3098
1968
|
this.setNodeStyles(d, "hover", nodeGrp);
|
|
@@ -3125,7 +1995,7 @@ export default class SVGCanvasRenderer {
|
|
|
3125
1995
|
// Use mouse down instead of click because it gets called before drag start.
|
|
3126
1996
|
.on("mousedown", (d3Event, d) => {
|
|
3127
1997
|
this.logger.logStartTimer("Node Group - mouse down");
|
|
3128
|
-
d3Event.stopPropagation();
|
|
1998
|
+
d3Event.stopPropagation();
|
|
3129
1999
|
if (this.svgCanvasTextArea.isEditingText()) {
|
|
3130
2000
|
this.svgCanvasTextArea.completeEditing();
|
|
3131
2001
|
}
|
|
@@ -3139,16 +2009,6 @@ export default class SVGCanvasRenderer {
|
|
|
3139
2009
|
// Don't stop propogation. Mouse move messages must be allowed to
|
|
3140
2010
|
// propagate to canvas zoom operation.
|
|
3141
2011
|
})
|
|
3142
|
-
.on("mouseup", (d3Event, d) => {
|
|
3143
|
-
this.logger.log("Node Group - mouse up");
|
|
3144
|
-
d3Event.stopPropagation();
|
|
3145
|
-
if (this.drawingNewLinkData) {
|
|
3146
|
-
this.completeNewLink(d3Event);
|
|
3147
|
-
}
|
|
3148
|
-
if (this.draggingLinkData) {
|
|
3149
|
-
this.completeDraggedLink(d3Event, d);
|
|
3150
|
-
}
|
|
3151
|
-
})
|
|
3152
2012
|
.on("click", (d3Event, d) => {
|
|
3153
2013
|
this.logger.log("Node Group - click");
|
|
3154
2014
|
d3Event.stopPropagation();
|
|
@@ -3181,11 +2041,7 @@ export default class SVGCanvasRenderer {
|
|
|
3181
2041
|
attachNodeSizingListeners(nodeGrps) {
|
|
3182
2042
|
nodeGrps
|
|
3183
2043
|
.on("mousedown", (d3Event, d) => {
|
|
3184
|
-
|
|
3185
|
-
this.nodeSizing = true;
|
|
3186
|
-
// Note - node resizing and finalization of size is handled by drag functions.
|
|
3187
|
-
this.addTempCursorOverlay(this.nodeSizingCursor);
|
|
3188
|
-
}
|
|
2044
|
+
this.dragObjectUtils.mouseDownNodeSizingArea(d);
|
|
3189
2045
|
})
|
|
3190
2046
|
// Use mousemove as well as mouseenter so the cursor will change
|
|
3191
2047
|
// if the pointer moves from one area of the node outline to another
|
|
@@ -3194,19 +2050,10 @@ export default class SVGCanvasRenderer {
|
|
|
3194
2050
|
// pointer leaves the temporary overlay (which is removed) and enters
|
|
3195
2051
|
// the node outline.
|
|
3196
2052
|
.on("mousemove mouseenter", (d3Event, d) => {
|
|
3197
|
-
|
|
3198
|
-
!this.isRegionSelectOrSizingInProgress()) { // Don't switch sizing direction if we are already sizing
|
|
3199
|
-
let cursorType = "default";
|
|
3200
|
-
if (!this.isPointerCloseToBodyEdge(d3Event, d)) {
|
|
3201
|
-
this.nodeSizingDirection = this.getSizingDirection(d3Event, d, d.layout.nodeCornerResizeArea);
|
|
3202
|
-
this.nodeSizingCursor = this.getCursorBasedOnDirection(this.nodeSizingDirection);
|
|
3203
|
-
cursorType = this.nodeSizingCursor;
|
|
3204
|
-
}
|
|
3205
|
-
d3.select(d3Event.currentTarget).style("cursor", cursorType);
|
|
3206
|
-
}
|
|
2053
|
+
this.dragObjectUtils.mouseEnterNodeSizingArea(d3Event, d);
|
|
3207
2054
|
})
|
|
3208
2055
|
.on("mouseleave", (d3Event, d) => {
|
|
3209
|
-
|
|
2056
|
+
this.dragObjectUtils.mouseLeaveNodeSizingArea(d3Event, d);
|
|
3210
2057
|
});
|
|
3211
2058
|
}
|
|
3212
2059
|
|
|
@@ -3219,7 +2066,7 @@ export default class SVGCanvasRenderer {
|
|
|
3219
2066
|
labelSel
|
|
3220
2067
|
.attr("x", this.nodeUtils.getNodeLabelHoverPosX(d))
|
|
3221
2068
|
.attr("width", this.nodeUtils.getNodeLabelHoverWidth(d))
|
|
3222
|
-
.attr("height", this.nodeUtils.getNodeLabelHoverHeight(d, spanSel.node(), this.
|
|
2069
|
+
.attr("height", this.nodeUtils.getNodeLabelHoverHeight(d, spanSel.node(), this.zoomUtils.getZoomScale()));
|
|
3223
2070
|
spanSel.classed("d3-label-full", true);
|
|
3224
2071
|
}
|
|
3225
2072
|
})
|
|
@@ -3259,37 +2106,6 @@ export default class SVGCanvasRenderer {
|
|
|
3259
2106
|
|
|
3260
2107
|
attachInputPortListeners(inputPorts, node) {
|
|
3261
2108
|
inputPorts
|
|
3262
|
-
.on("mousedown", (d3Event, port) => {
|
|
3263
|
-
if (!this.config.enableEditingActions) {
|
|
3264
|
-
CanvasUtils.stopPropagationAndPreventDefault(d3Event);
|
|
3265
|
-
return;
|
|
3266
|
-
}
|
|
3267
|
-
if (this.config.enableAssocLinkCreation) {
|
|
3268
|
-
// Make sure this is just a left mouse button click - we don't want context menu click starting a line being drawn
|
|
3269
|
-
if (d3Event.button === 0) {
|
|
3270
|
-
CanvasUtils.stopPropagationAndPreventDefault(d3Event); // Stops the node drag behavior when clicking on the handle/circle
|
|
3271
|
-
const srcNode = this.activePipeline.getNode(node.id);
|
|
3272
|
-
this.drawingNewLinkData = {
|
|
3273
|
-
srcObjId: node.id,
|
|
3274
|
-
srcPortId: port.id,
|
|
3275
|
-
action: this.config.enableAssocLinkCreation ? ASSOCIATION_LINK : NODE_LINK,
|
|
3276
|
-
srcNode: srcNode,
|
|
3277
|
-
startPos: { x: srcNode.x_pos + port.cx, y: srcNode.y_pos + port.cy },
|
|
3278
|
-
portType: "input",
|
|
3279
|
-
portObject: node.layout.inputPortObject,
|
|
3280
|
-
portImage: node.layout.inputPortImage,
|
|
3281
|
-
portWidth: node.layout.inputPortWidth,
|
|
3282
|
-
portHeight: node.layout.inputPortHeight,
|
|
3283
|
-
portRadius: this.getPortRadius(srcNode),
|
|
3284
|
-
minInitialLine: srcNode.layout.minInitialLine,
|
|
3285
|
-
guideObject: node.layout.inputPortGuideObject,
|
|
3286
|
-
guideImage: node.layout.inputPortGuideImage,
|
|
3287
|
-
linkArray: []
|
|
3288
|
-
};
|
|
3289
|
-
this.drawNewLink(d3Event);
|
|
3290
|
-
}
|
|
3291
|
-
}
|
|
3292
|
-
})
|
|
3293
2109
|
.on("mouseenter", (d3Event, port) => {
|
|
3294
2110
|
CanvasUtils.stopPropagationAndPreventDefault(d3Event); // stop event propagation, otherwise node tip is shown
|
|
3295
2111
|
if (this.canOpenTip(TIP_TYPE_PORT)) {
|
|
@@ -3322,40 +2138,6 @@ export default class SVGCanvasRenderer {
|
|
|
3322
2138
|
|
|
3323
2139
|
attachOutputPortListeners(outputPorts, node) {
|
|
3324
2140
|
outputPorts
|
|
3325
|
-
.on("mousedown", (d3Event, port) => {
|
|
3326
|
-
if (!this.config.enableEditingActions) {
|
|
3327
|
-
CanvasUtils.stopPropagationAndPreventDefault(d3Event);
|
|
3328
|
-
return;
|
|
3329
|
-
}
|
|
3330
|
-
// Make sure this is just a left mouse button click - we don't want context menu click starting a line being drawn
|
|
3331
|
-
if (d3Event.button === 0) {
|
|
3332
|
-
CanvasUtils.stopPropagationAndPreventDefault(d3Event); // Stops the node drag behavior when clicking on the handle/circle
|
|
3333
|
-
const srcNode = this.activePipeline.getNode(node.id);
|
|
3334
|
-
if (!CanvasUtils.isSrcCardinalityAtMax(port.id, srcNode, this.activePipeline.links)) {
|
|
3335
|
-
this.drawingNewLinkData = {
|
|
3336
|
-
srcObjId: node.id,
|
|
3337
|
-
srcPortId: port.id,
|
|
3338
|
-
action: this.config.enableAssocLinkCreation ? ASSOCIATION_LINK : NODE_LINK,
|
|
3339
|
-
srcNode: srcNode,
|
|
3340
|
-
startPos: { x: srcNode.x_pos + port.cx, y: srcNode.y_pos + port.cy },
|
|
3341
|
-
portType: "output",
|
|
3342
|
-
portObject: node.layout.outputPortObject,
|
|
3343
|
-
portImage: node.layout.outputPortImage,
|
|
3344
|
-
portWidth: node.layout.outputPortWidth,
|
|
3345
|
-
portHeight: node.layout.outputPortHeight,
|
|
3346
|
-
portRadius: this.getPortRadius(srcNode),
|
|
3347
|
-
minInitialLine: srcNode.layout.minInitialLine,
|
|
3348
|
-
guideObject: node.layout.outputPortGuideObject,
|
|
3349
|
-
guideImage: node.layout.outputPortGuideImage,
|
|
3350
|
-
linkArray: []
|
|
3351
|
-
};
|
|
3352
|
-
if (this.config.enableHighlightUnavailableNodes) {
|
|
3353
|
-
this.setUnavailableTargetNodesHighlighting(srcNode, port.id, this.activePipeline.links);
|
|
3354
|
-
}
|
|
3355
|
-
this.drawNewLink(d3Event);
|
|
3356
|
-
}
|
|
3357
|
-
}
|
|
3358
|
-
})
|
|
3359
2141
|
.on("mouseenter", (d3Event, port) => {
|
|
3360
2142
|
CanvasUtils.stopPropagationAndPreventDefault(d3Event); // stop event propagation, otherwise node tip is shown
|
|
3361
2143
|
if (this.canOpenTip(TIP_TYPE_PORT)) {
|
|
@@ -3416,7 +2198,7 @@ export default class SVGCanvasRenderer {
|
|
|
3416
2198
|
const nodeObj = foreignObj.parentElement;
|
|
3417
2199
|
const nodeGrpSel = d3.select(nodeObj);
|
|
3418
2200
|
const transform = this.nodeUtils.getNodeLabelEditIconTranslate(node, spanObj,
|
|
3419
|
-
this.
|
|
2201
|
+
this.zoomUtils.getZoomScale(), this.config.enableDisplayFullLabelOnHover);
|
|
3420
2202
|
|
|
3421
2203
|
this.displayEditIcon(spanObj, nodeGrpSel, transform,
|
|
3422
2204
|
(d3Event, d) => this.displayNodeLabelTextArea(d, d3Event.currentTarget.parentNode));
|
|
@@ -3430,7 +2212,7 @@ export default class SVGCanvasRenderer {
|
|
|
3430
2212
|
const decObj = foreignObj.parentElement;
|
|
3431
2213
|
const decGrpSel = d3.select(decObj);
|
|
3432
2214
|
const transform = this.decUtils.getDecLabelEditIconTranslate(
|
|
3433
|
-
dec, obj, objType, spanObj, this.
|
|
2215
|
+
dec, obj, objType, spanObj, this.zoomUtils.getZoomScale());
|
|
3434
2216
|
|
|
3435
2217
|
this.displayEditIcon(spanObj, decGrpSel, transform,
|
|
3436
2218
|
(d3Event, d) => this.displayDecLabelTextArea(dec, obj, objType, d3Event.currentTarget.parentNode));
|
|
@@ -3683,9 +2465,11 @@ export default class SVGCanvasRenderer {
|
|
|
3683
2465
|
extSel
|
|
3684
2466
|
.attr("width", this.decUtils.getDecWidth(dec, d, objType))
|
|
3685
2467
|
.attr("height", this.decUtils.getDecHeight(dec, d, objType))
|
|
3686
|
-
.each(
|
|
2468
|
+
.each((decData, idx, exts) =>
|
|
2469
|
+
this.externalUtils.addDecExternalObject(decData, idx, exts));
|
|
3687
2470
|
} else {
|
|
3688
|
-
extSel.each(
|
|
2471
|
+
extSel.each((decData, idx, exts) =>
|
|
2472
|
+
this.externalUtils.removeExternalObject(decData, idx, exts));
|
|
3689
2473
|
extSel.remove();
|
|
3690
2474
|
}
|
|
3691
2475
|
}
|
|
@@ -3940,55 +2724,11 @@ export default class SVGCanvasRenderer {
|
|
|
3940
2724
|
expandedSupernodeHaveStyledNodes = true;
|
|
3941
2725
|
return;
|
|
3942
2726
|
} else if (!expandedSupernodeHaveStyledNodes && CanvasUtils.isExpandedSupernode(node)) {
|
|
3943
|
-
expandedSupernodeHaveStyledNodes = this.doesExpandedSupernodeHaveStyledNodes(node);
|
|
3944
|
-
}
|
|
3945
|
-
});
|
|
3946
|
-
}
|
|
3947
|
-
return expandedSupernodeHaveStyledNodes;
|
|
3948
|
-
}
|
|
3949
|
-
|
|
3950
|
-
// Returns the maximum amount for padding, when zooming to fit the canvas
|
|
3951
|
-
// objects within a subflow, to allow the connection lines to be displayed
|
|
3952
|
-
// without them doubling back on themselves.
|
|
3953
|
-
getMaxZoomToFitPaddingForConnections() {
|
|
3954
|
-
const paddingForInputBinding = this.getMaxPaddingForConnectionsFromInputBindingNodes();
|
|
3955
|
-
const paddingForOutputBinding = this.getMaxPaddingForConnectionsToOutputBindingNodes();
|
|
3956
|
-
const padding = Math.max(paddingForInputBinding, paddingForOutputBinding);
|
|
3957
|
-
return padding;
|
|
3958
|
-
}
|
|
3959
|
-
|
|
3960
|
-
// Returns the maximum amount for padding, when zooming to fit the canvas
|
|
3961
|
-
// objects within a subflow, to allow the connection lines (from input binding
|
|
3962
|
-
// nodes to other sub-flow nodes) to be displayed without them doubling back
|
|
3963
|
-
// on themselves.
|
|
3964
|
-
getMaxPaddingForConnectionsFromInputBindingNodes() {
|
|
3965
|
-
let maxPadding = 0;
|
|
3966
|
-
const inputBindingNodes = this.activePipeline.nodes.filter((n) => n.isSupernodeInputBinding);
|
|
3967
|
-
|
|
3968
|
-
inputBindingNodes.forEach((n) => {
|
|
3969
|
-
const nodePadding = CanvasUtils.getNodePaddingToTargetNodes(n, this.activePipeline.nodes,
|
|
3970
|
-
this.activePipeline.links, this.canvasLayout.linkType);
|
|
3971
|
-
maxPadding = Math.max(maxPadding, nodePadding);
|
|
3972
|
-
});
|
|
3973
|
-
|
|
3974
|
-
return maxPadding;
|
|
3975
|
-
}
|
|
3976
|
-
|
|
3977
|
-
// Returns the maximum amount for padding, when zooming to fit the canvas
|
|
3978
|
-
// objects within a subflow, to allow the connection lines (from sub-flow nodes
|
|
3979
|
-
// to output binding nodes) to be displayed without them doubling back
|
|
3980
|
-
// on themselves.
|
|
3981
|
-
getMaxPaddingForConnectionsToOutputBindingNodes() {
|
|
3982
|
-
let maxPadding = 0;
|
|
3983
|
-
const outputBindingNodes = this.activePipeline.nodes.filter((n) => n.isSupernodeOutputBinding);
|
|
3984
|
-
|
|
3985
|
-
this.activePipeline.nodes.forEach((n) => {
|
|
3986
|
-
const nodePadding = CanvasUtils.getNodePaddingToTargetNodes(n, outputBindingNodes,
|
|
3987
|
-
this.activePipeline.links, this.canvasLayout.linkType);
|
|
3988
|
-
maxPadding = Math.max(maxPadding, nodePadding);
|
|
3989
|
-
});
|
|
3990
|
-
|
|
3991
|
-
return maxPadding;
|
|
2727
|
+
expandedSupernodeHaveStyledNodes = this.doesExpandedSupernodeHaveStyledNodes(node);
|
|
2728
|
+
}
|
|
2729
|
+
});
|
|
2730
|
+
}
|
|
2731
|
+
return expandedSupernodeHaveStyledNodes;
|
|
3992
2732
|
}
|
|
3993
2733
|
|
|
3994
2734
|
getPortRadius(d) {
|
|
@@ -3998,11 +2738,11 @@ export default class SVGCanvasRenderer {
|
|
|
3998
2738
|
// Returns the radius size of the supernode binding ports scaled up by
|
|
3999
2739
|
// the zoom scale amount to give the actual size.
|
|
4000
2740
|
getBindingPortRadius() {
|
|
4001
|
-
return this.canvasLayout.supernodeBindingPortRadius / this.
|
|
2741
|
+
return this.canvasLayout.supernodeBindingPortRadius / this.zoomUtils.getZoomScale();
|
|
4002
2742
|
}
|
|
4003
2743
|
|
|
4004
2744
|
addDynamicNodeIcons(d3Event, d, nodeGrp) {
|
|
4005
|
-
if (!this.
|
|
2745
|
+
if (!this.isSizing() && !CanvasUtils.isSuperBindingNode(d)) {
|
|
4006
2746
|
// Add the ellipsis icon if requested by layout config.
|
|
4007
2747
|
if (d.layout.ellipsisDisplay) {
|
|
4008
2748
|
this.addEllipsisIcon(d, nodeGrp);
|
|
@@ -4027,11 +2767,11 @@ export default class SVGCanvasRenderer {
|
|
|
4027
2767
|
}
|
|
4028
2768
|
|
|
4029
2769
|
addContextToolbar(d3Event, d, objType) {
|
|
4030
|
-
if (!this.
|
|
2770
|
+
if (!this.isSizing() && !this.isDragging() &&
|
|
4031
2771
|
!this.svgCanvasTextArea.isEditingText() && !CanvasUtils.isSuperBindingNode(d)) {
|
|
4032
2772
|
this.canvasController.setMouseInObject(true);
|
|
4033
2773
|
let pos = this.getContextToolbarPos(objType, d);
|
|
4034
|
-
pos = this.unTransformPos(pos);
|
|
2774
|
+
pos = this.zoomUtils.unTransformPos(pos);
|
|
4035
2775
|
this.openContextMenu(d3Event, objType, d, null, pos);
|
|
4036
2776
|
}
|
|
4037
2777
|
}
|
|
@@ -4122,836 +2862,167 @@ export default class SVGCanvasRenderer {
|
|
|
4122
2862
|
.attr("y", this.canvasLayout.supernodeExpansionIconHoverAreaPadding);
|
|
4123
2863
|
}
|
|
4124
2864
|
|
|
4125
|
-
// Returns an array of breadcrumbs for the DOM element passed in. The DOM
|
|
4126
|
-
// element is expected to be an element within a node (like the expansion
|
|
4127
|
-
// icon). The output array will contain one breadcrumb for each nested
|
|
4128
|
-
// supernode down to the supernode of which the DOM element is a part. So if
|
|
4129
|
-
// there are three nested supernodes and the DOM element is part of the third
|
|
4130
|
-
// one, the breadcrumbs array will have three elements.
|
|
4131
|
-
getSupernodeBreadcrumbs(domEl) {
|
|
4132
|
-
const breadcrumbs = [];
|
|
4133
|
-
|
|
4134
|
-
let nodeGroupEl = domEl.closest(".d3-node-group");
|
|
4135
|
-
|
|
4136
|
-
while (nodeGroupEl) {
|
|
4137
|
-
const svgAreaEl = nodeGroupEl.closest(".svg-area");
|
|
4138
|
-
const supernodeDatum = this.getD3DatumFromDomEl(nodeGroupEl);
|
|
4139
|
-
const parentPipelineId = svgAreaEl.getAttribute("data-pipeline-id");
|
|
4140
|
-
|
|
4141
|
-
breadcrumbs.push(
|
|
4142
|
-
this.canvasController.createBreadcrumb(supernodeDatum, parentPipelineId)
|
|
4143
|
-
);
|
|
4144
|
-
|
|
4145
|
-
nodeGroupEl = svgAreaEl.closest(".d3-node-group");
|
|
4146
|
-
}
|
|
4147
|
-
|
|
4148
|
-
// Reverse the order, so they appear in the nesting order of the supernodes.
|
|
4149
|
-
return breadcrumbs.reverse();
|
|
4150
|
-
}
|
|
4151
|
-
|
|
4152
|
-
// Returns the datum object (managd by D3) for the DOM element passed in.
|
|
4153
|
-
getD3DatumFromDomEl(el) {
|
|
4154
|
-
const sel = d3.select(el);
|
|
4155
|
-
if (sel) {
|
|
4156
|
-
return sel.datum();
|
|
4157
|
-
}
|
|
4158
|
-
return null;
|
|
4159
|
-
}
|
|
4160
|
-
|
|
4161
|
-
updatePortRadiusAndPos(nodeObj, node, portObjClassName) {
|
|
4162
|
-
const nodeGrp = d3.select(nodeObj);
|
|
4163
|
-
nodeGrp.selectAll("." + portObjClassName)
|
|
4164
|
-
.attr("r", () => this.getPortRadius(node))
|
|
4165
|
-
.attr("cx", (port) => port.cx)
|
|
4166
|
-
.attr("cy", (port) => port.cy); // Port position may change for binding nodes with multiple-ports.
|
|
4167
|
-
}
|
|
4168
|
-
|
|
4169
|
-
updatePortArrowPath(nodeObj, portArrowClassName) {
|
|
4170
|
-
const nodeGrp = d3.select(nodeObj);
|
|
4171
|
-
nodeGrp.selectAll("." + portArrowClassName)
|
|
4172
|
-
.attr("d", (port) => this.getPortArrowPath(port))
|
|
4173
|
-
.attr("transform", (port) => this.getPortArrowPathTransform(port));
|
|
4174
|
-
}
|
|
4175
|
-
|
|
4176
|
-
// Returns true if the port (from a node template) passed in has a max
|
|
4177
|
-
// cardinaility of zero. If cardinality or cardinality.max is missing the
|
|
4178
|
-
// max is considered to be non-zero.
|
|
4179
|
-
isPortMaxCardinalityZero(port) {
|
|
4180
|
-
return (get(port, "app_data.ui_data.cardinality.max", 1) === 0);
|
|
4181
|
-
}
|
|
4182
|
-
|
|
4183
|
-
isMouseOverContextToolbar(d3Event) {
|
|
4184
|
-
return this.getElementWithClassAtMousePos(d3Event, "context-toolbar");
|
|
4185
|
-
}
|
|
4186
|
-
|
|
4187
|
-
removeDynamicNodeIcons(d3Event, d, nodeGrp) {
|
|
4188
|
-
if (d.layout.ellipsisDisplay) {
|
|
4189
|
-
nodeGrp.selectChildren(".d3-node-ellipsis-group").remove();
|
|
4190
|
-
}
|
|
4191
|
-
nodeGrp.selectChildren(".d3-node-super-expand-icon-group").remove();
|
|
4192
|
-
}
|
|
4193
|
-
|
|
4194
|
-
createSupernodeRenderer(d, supernodeD3Object) {
|
|
4195
|
-
if (d.subflow_ref && d.subflow_ref.pipeline_id_ref) {
|
|
4196
|
-
const superRenderer = new SVGCanvasRenderer(
|
|
4197
|
-
d.subflow_ref.pipeline_id_ref,
|
|
4198
|
-
this.canvasDiv,
|
|
4199
|
-
this.canvasController,
|
|
4200
|
-
this.canvasInfo,
|
|
4201
|
-
this.selectionInfo,
|
|
4202
|
-
this.breadcrumbs,
|
|
4203
|
-
this.nodeLayout,
|
|
4204
|
-
this.canvasLayout,
|
|
4205
|
-
this.config,
|
|
4206
|
-
{ id: d.id,
|
|
4207
|
-
pipelineId: this.activePipeline.id,
|
|
4208
|
-
renderer: this, // Only provided for in-place sub-flow
|
|
4209
|
-
d3Selection: supernodeD3Object // Only provided for in-place sub-flow
|
|
4210
|
-
});
|
|
4211
|
-
return superRenderer;
|
|
4212
|
-
}
|
|
4213
|
-
return null;
|
|
4214
|
-
}
|
|
4215
|
-
|
|
4216
|
-
// Returns the renderer for the supernode passed in. With external
|
|
4217
|
-
// pipeline handling the pipeline referencd by the supernode can change
|
|
4218
|
-
// over time so we have to make sure the renderer is for the supernode AND
|
|
4219
|
-
// for the active pipeline.
|
|
4220
|
-
getRendererForSupernode(d) {
|
|
4221
|
-
return this.superRenderers.find((sr) =>
|
|
4222
|
-
sr.supernodeInfo.id === d.id && sr.activePipeline.id === d.subflow_ref.pipeline_id_ref);
|
|
4223
|
-
}
|
|
4224
|
-
|
|
4225
|
-
// Returns an array containing any renderers that are for the supernode passed
|
|
4226
|
-
// in but where the supernode does NOT reference the renderer's active pipeline.
|
|
4227
|
-
getDiscardedRenderersForSupernode(d) {
|
|
4228
|
-
return this.superRenderers.filter((sr) =>
|
|
4229
|
-
sr.supernodeInfo.id === d.id && sr.activePipeline.id !== d.subflow_ref.pipeline_id_ref);
|
|
4230
|
-
|
|
4231
|
-
}
|
|
4232
|
-
|
|
4233
|
-
openContextMenu(d3Event, type, d, port, pos) {
|
|
4234
|
-
CanvasUtils.stopPropagationAndPreventDefault(d3Event); // Stop the browser context menu appearing
|
|
4235
|
-
this.canvasController.contextMenuHandler({
|
|
4236
|
-
type: type,
|
|
4237
|
-
targetObject: type === "canvas" ? null : d,
|
|
4238
|
-
id: type === "canvas" ? null : d.id, // For historical puposes, we pass d.id as well as d as targetObject.
|
|
4239
|
-
pipelineId: this.activePipeline.id,
|
|
4240
|
-
cmPos: pos
|
|
4241
|
-
? pos
|
|
4242
|
-
: this.getMousePos(d3Event, this.canvasDiv.selectAll("svg")), // Get mouse pos relative to top most SVG area even in a subflow.
|
|
4243
|
-
mousePos: this.getMousePosSnapToGrid(this.getTransformedMousePos(d3Event)),
|
|
4244
|
-
selectedObjectIds: this.canvasController.getSelectedObjectIds(),
|
|
4245
|
-
addBreadcrumbs: (d && d.type === SUPER_NODE) ? this.getSupernodeBreadcrumbs(d3Event.currentTarget) : null,
|
|
4246
|
-
port: port,
|
|
4247
|
-
zoom: this.zoomTransform.k });
|
|
4248
|
-
}
|
|
4249
|
-
|
|
4250
|
-
closeContextMenuIfOpen() {
|
|
4251
|
-
if (this.canvasController.isContextMenuDisplayed()) {
|
|
4252
|
-
this.canvasController.closeContextMenu();
|
|
4253
|
-
}
|
|
4254
|
-
if (this.config.enableContextToolbar) {
|
|
4255
|
-
this.removeContextToolbar();
|
|
4256
|
-
}
|
|
4257
|
-
}
|
|
4258
|
-
|
|
4259
|
-
callDecoratorCallback(d3Event, node, dec) {
|
|
4260
|
-
d3Event.stopPropagation();
|
|
4261
|
-
if (this.canvasController.decorationActionHandler) {
|
|
4262
|
-
this.canvasController.decorationActionHandler(node, dec.id, this.activePipeline.id);
|
|
4263
|
-
}
|
|
4264
|
-
}
|
|
4265
|
-
|
|
4266
|
-
drawNewLink(d3Event) {
|
|
4267
|
-
if (this.config.enableEditingActions === false) {
|
|
4268
|
-
return;
|
|
4269
|
-
}
|
|
4270
|
-
|
|
4271
|
-
this.closeContextMenuIfOpen();
|
|
4272
|
-
|
|
4273
|
-
const transPos = this.getTransformedMousePos(d3Event);
|
|
4274
|
-
|
|
4275
|
-
if (this.drawingNewLinkData.action === COMMENT_LINK) {
|
|
4276
|
-
this.drawNewCommentLinkForPorts(transPos);
|
|
4277
|
-
} else {
|
|
4278
|
-
this.drawNewNodeLinkForPorts(transPos);
|
|
4279
|
-
}
|
|
4280
|
-
// Switch on an attribute to indicate a new link is being dragged
|
|
4281
|
-
// towards and over a target node.
|
|
4282
|
-
if (this.config.enableHighlightNodeOnNewLinkDrag) {
|
|
4283
|
-
this.setNewLinkOverNode(d3Event);
|
|
4284
|
-
}
|
|
4285
|
-
}
|
|
4286
|
-
|
|
4287
|
-
drawNewNodeLinkForPorts(transPos) {
|
|
4288
|
-
var that = this;
|
|
4289
|
-
const linkType = this.config.enableAssocLinkCreation ? ASSOCIATION_LINK : NODE_LINK;
|
|
4290
|
-
|
|
4291
|
-
let startPos;
|
|
4292
|
-
if (this.canvasLayout.linkType === LINK_TYPE_STRAIGHT) {
|
|
4293
|
-
startPos = this.linkUtils.getNewStraightNodeLinkStartPos(this.drawingNewLinkData.srcNode, transPos);
|
|
4294
|
-
} else {
|
|
4295
|
-
startPos = {
|
|
4296
|
-
x: this.drawingNewLinkData.startPos.x,
|
|
4297
|
-
y: this.drawingNewLinkData.startPos.y };
|
|
4298
|
-
}
|
|
4299
|
-
|
|
4300
|
-
this.drawingNewLinkData.linkArray = [{
|
|
4301
|
-
"x1": startPos.x,
|
|
4302
|
-
"y1": startPos.y,
|
|
4303
|
-
"x2": transPos.x,
|
|
4304
|
-
"y2": transPos.y,
|
|
4305
|
-
"originX": startPos.originX,
|
|
4306
|
-
"originY": startPos.originY,
|
|
4307
|
-
"type": linkType }];
|
|
4308
|
-
|
|
4309
|
-
if (this.config.enableAssocLinkCreation) {
|
|
4310
|
-
this.drawingNewLinkData.linkArray[0].assocLinkVariation =
|
|
4311
|
-
this.getNewLinkAssocVariation(
|
|
4312
|
-
this.drawingNewLinkData.linkArray[0].x1,
|
|
4313
|
-
this.drawingNewLinkData.linkArray[0].x2,
|
|
4314
|
-
this.drawingNewLinkData.portType);
|
|
4315
|
-
}
|
|
4316
|
-
|
|
4317
|
-
const pathInfo = this.linkUtils.getConnectorPathInfo(
|
|
4318
|
-
this.drawingNewLinkData.linkArray[0], this.drawingNewLinkData.minInitialLine);
|
|
4319
|
-
|
|
4320
|
-
const connectionLineSel = this.nodesLinksGrp.selectAll(".d3-new-connection-line");
|
|
4321
|
-
const connectionStartSel = this.nodesLinksGrp.selectAll(".d3-new-connection-start");
|
|
4322
|
-
const connectionGuideSel = this.nodesLinksGrp.selectAll(".d3-new-connection-guide");
|
|
4323
|
-
|
|
4324
|
-
// For a straight node line, don't draw the new link line when the guide
|
|
4325
|
-
// icon or object is inside the node boundary.
|
|
4326
|
-
if (linkType === NODE_LINK &&
|
|
4327
|
-
this.canvasLayout.linkType === LINK_TYPE_STRAIGHT &&
|
|
4328
|
-
this.nodeUtils.isPointInNodeBoundary(transPos, this.drawingNewLinkData.srcNode)) {
|
|
4329
|
-
this.removeNewLinkLine();
|
|
4330
|
-
|
|
4331
|
-
} else {
|
|
4332
|
-
connectionLineSel
|
|
4333
|
-
.data(this.drawingNewLinkData.linkArray)
|
|
4334
|
-
.enter()
|
|
4335
|
-
.append("path")
|
|
4336
|
-
.attr("class", "d3-new-connection-line")
|
|
4337
|
-
.attr("linkType", linkType)
|
|
4338
|
-
.merge(connectionLineSel)
|
|
4339
|
-
.attr("d", pathInfo.path)
|
|
4340
|
-
.attr("transform", pathInfo.transform);
|
|
4341
|
-
}
|
|
4342
|
-
|
|
4343
|
-
if (this.canvasLayout.linkType !== LINK_TYPE_STRAIGHT) {
|
|
4344
|
-
connectionStartSel
|
|
4345
|
-
.data(this.drawingNewLinkData.linkArray)
|
|
4346
|
-
.enter()
|
|
4347
|
-
.append(this.drawingNewLinkData.portObject)
|
|
4348
|
-
.attr("class", "d3-new-connection-start")
|
|
4349
|
-
.attr("linkType", linkType)
|
|
4350
|
-
.merge(connectionStartSel)
|
|
4351
|
-
.each(function(d) {
|
|
4352
|
-
// No need to draw the starting object of the new line if it is an image.
|
|
4353
|
-
if (that.drawingNewLinkData.portObject === PORT_OBJECT_CIRCLE) {
|
|
4354
|
-
d3.select(this)
|
|
4355
|
-
.attr("cx", d.x1)
|
|
4356
|
-
.attr("cy", d.y1)
|
|
4357
|
-
.attr("r", that.drawingNewLinkData.portRadius);
|
|
4358
|
-
}
|
|
4359
|
-
});
|
|
4360
|
-
}
|
|
4361
|
-
|
|
4362
|
-
connectionGuideSel
|
|
4363
|
-
.data(this.drawingNewLinkData.linkArray)
|
|
4364
|
-
.enter()
|
|
4365
|
-
.append(this.drawingNewLinkData.guideObject)
|
|
4366
|
-
.attr("class", "d3-new-connection-guide")
|
|
4367
|
-
.attr("linkType", linkType)
|
|
4368
|
-
.on("mouseup", (d3Event) => {
|
|
4369
|
-
CanvasUtils.stopPropagationAndPreventDefault(d3Event);
|
|
4370
|
-
this.completeNewLink(d3Event);
|
|
4371
|
-
})
|
|
4372
|
-
.merge(connectionGuideSel)
|
|
4373
|
-
.each(function(d) {
|
|
4374
|
-
if (that.drawingNewLinkData.guideObject === PORT_OBJECT_IMAGE) {
|
|
4375
|
-
d3.select(this)
|
|
4376
|
-
.attr("xlink:href", that.drawingNewLinkData.guideImage)
|
|
4377
|
-
.attr("x", d.x2 - (that.drawingNewLinkData.portWidth / 2))
|
|
4378
|
-
.attr("y", d.y2 - (that.drawingNewLinkData.portHeight / 2))
|
|
4379
|
-
.attr("width", that.drawingNewLinkData.portWidth)
|
|
4380
|
-
.attr("height", that.drawingNewLinkData.portHeight)
|
|
4381
|
-
.attr("transform", that.getLinkImageTransform(d));
|
|
4382
|
-
} else {
|
|
4383
|
-
d3.select(this)
|
|
4384
|
-
.attr("cx", d.x2)
|
|
4385
|
-
.attr("cy", d.y2)
|
|
4386
|
-
.attr("r", that.drawingNewLinkData.portRadius);
|
|
4387
|
-
}
|
|
4388
|
-
});
|
|
4389
|
-
}
|
|
4390
|
-
|
|
4391
|
-
getLinkImageTransform(d) {
|
|
4392
|
-
let angle = 0;
|
|
4393
|
-
if (this.canvasLayout.linkType === LINK_TYPE_STRAIGHT) {
|
|
4394
|
-
const adjacent = d.x2 - (d.originX || d.x1);
|
|
4395
|
-
const opposite = d.y2 - (d.originY || d.y1);
|
|
4396
|
-
if (adjacent === 0 && opposite === 0) {
|
|
4397
|
-
angle = 0;
|
|
4398
|
-
} else {
|
|
4399
|
-
angle = Math.atan(opposite / adjacent) * (180 / Math.PI);
|
|
4400
|
-
angle = adjacent >= 0 ? angle : angle + 180;
|
|
4401
|
-
if (this.canvasLayout.linkDirection === LINK_DIR_TOP_BOTTOM) {
|
|
4402
|
-
angle -= 90;
|
|
4403
|
-
} else if (this.canvasLayout.linkDirection === LINK_DIR_BOTTOM_TOP) {
|
|
4404
|
-
angle += 90;
|
|
4405
|
-
}
|
|
4406
|
-
}
|
|
4407
|
-
return `rotate(${angle},${d.x2},${d.y2})`;
|
|
4408
|
-
}
|
|
4409
|
-
return null;
|
|
4410
|
-
}
|
|
4411
|
-
|
|
4412
|
-
drawNewCommentLinkForPorts(transPos) {
|
|
4413
|
-
const that = this;
|
|
4414
|
-
const srcComment = this.activePipeline.getComment(this.drawingNewLinkData.srcObjId);
|
|
4415
|
-
const startPos = this.linkUtils.getNewStraightCommentLinkStartPos(srcComment, transPos);
|
|
4416
|
-
const linkType = COMMENT_LINK;
|
|
4417
|
-
|
|
4418
|
-
this.drawingNewLinkData.linkArray = [{
|
|
4419
|
-
"x1": startPos.x,
|
|
4420
|
-
"y1": startPos.y,
|
|
4421
|
-
"x2": transPos.x,
|
|
4422
|
-
"y2": transPos.y,
|
|
4423
|
-
"type": linkType }];
|
|
4424
|
-
|
|
4425
|
-
const connectionLineSel = this.nodesLinksGrp.selectAll(".d3-new-connection-line");
|
|
4426
|
-
const connectionGuideSel = this.nodesLinksGrp.selectAll(".d3-new-connection-guide");
|
|
4427
|
-
|
|
4428
|
-
connectionLineSel
|
|
4429
|
-
.data(this.drawingNewLinkData.linkArray)
|
|
4430
|
-
.enter()
|
|
4431
|
-
.append("path")
|
|
4432
|
-
.attr("class", "d3-new-connection-line")
|
|
4433
|
-
.attr("linkType", linkType)
|
|
4434
|
-
.merge(connectionLineSel)
|
|
4435
|
-
.attr("d", (d) => that.linkUtils.getConnectorPathInfo(d).path);
|
|
4436
|
-
|
|
4437
|
-
connectionGuideSel
|
|
4438
|
-
.data(this.drawingNewLinkData.linkArray)
|
|
4439
|
-
.enter()
|
|
4440
|
-
.append("circle")
|
|
4441
|
-
.attr("class", "d3-new-connection-guide")
|
|
4442
|
-
.attr("linkType", linkType)
|
|
4443
|
-
.on("mouseup", (d3Event) => {
|
|
4444
|
-
CanvasUtils.stopPropagationAndPreventDefault(d3Event);
|
|
4445
|
-
this.completeNewLink(d3Event);
|
|
4446
|
-
})
|
|
4447
|
-
.merge(connectionGuideSel)
|
|
4448
|
-
.attr("cx", (d) => d.x2)
|
|
4449
|
-
.attr("cy", (d) => d.y2)
|
|
4450
|
-
.attr("r", this.canvasLayout.commentPortRadius);
|
|
4451
|
-
|
|
4452
|
-
if (this.canvasLayout.commentLinkArrowHead) {
|
|
4453
|
-
const connectionArrowHeadSel = this.nodesLinksGrp.selectAll(".d3-new-connection-arrow");
|
|
4454
|
-
|
|
4455
|
-
connectionArrowHeadSel
|
|
4456
|
-
.data(this.drawingNewLinkData.linkArray)
|
|
4457
|
-
.enter()
|
|
4458
|
-
.append("path")
|
|
4459
|
-
.attr("class", "d3-new-connection-arrow")
|
|
4460
|
-
.attr("linkType", linkType)
|
|
4461
|
-
.on("mouseup", (d3Event) => {
|
|
4462
|
-
CanvasUtils.stopPropagationAndPreventDefault(d3Event);
|
|
4463
|
-
this.completeNewLink(d3Event);
|
|
4464
|
-
})
|
|
4465
|
-
.merge(connectionArrowHeadSel)
|
|
4466
|
-
.attr("d", (d) => this.getArrowHead(d))
|
|
4467
|
-
.attr("transform", (d) => this.getArrowHeadTransform(d));
|
|
4468
|
-
}
|
|
4469
|
-
}
|
|
4470
|
-
|
|
4471
|
-
// Handles the completion of a new link being drawn from a source node.
|
|
4472
|
-
completeNewLink(d3Event) {
|
|
4473
|
-
if (this.config.enableEditingActions === false) {
|
|
4474
|
-
return;
|
|
4475
|
-
}
|
|
4476
|
-
|
|
4477
|
-
if (this.config.enableHighlightUnavailableNodes) {
|
|
4478
|
-
this.unsetUnavailableNodesHighlighting();
|
|
4479
|
-
}
|
|
4480
|
-
var trgNode = this.getNodeAtMousePos(d3Event);
|
|
4481
|
-
if (trgNode !== null) {
|
|
4482
|
-
this.completeNewLinkOnNode(d3Event, trgNode);
|
|
4483
|
-
} else {
|
|
4484
|
-
if (this.config.enableLinkSelection === LINK_SELECTION_DETACHABLE &&
|
|
4485
|
-
this.drawingNewLinkData.action === NODE_LINK &&
|
|
4486
|
-
!this.config.enableAssocLinkCreation) {
|
|
4487
|
-
this.completeNewDetachedLink(d3Event);
|
|
4488
|
-
} else {
|
|
4489
|
-
this.stopDrawingNewLink();
|
|
4490
|
-
}
|
|
4491
|
-
}
|
|
4492
|
-
}
|
|
4493
|
-
|
|
4494
|
-
// Handles the completion of a new link when the end is dropped on a node.
|
|
4495
|
-
completeNewLinkOnNode(d3Event, trgNode) {
|
|
4496
|
-
// If we completed a connection remove the new line objects.
|
|
4497
|
-
this.removeNewLink();
|
|
4498
|
-
|
|
4499
|
-
// Switch 'new link over node' highlighting off
|
|
4500
|
-
if (this.config.enableHighlightNodeOnNewLinkDrag) {
|
|
4501
|
-
this.setNewLinkOverNodeCancel();
|
|
4502
|
-
}
|
|
4503
|
-
|
|
4504
|
-
if (trgNode !== null) {
|
|
4505
|
-
const type = this.drawingNewLinkData.action;
|
|
4506
|
-
if (type === NODE_LINK) {
|
|
4507
|
-
const srcNode = this.activePipeline.getNode(this.drawingNewLinkData.srcObjId);
|
|
4508
|
-
const srcPortId = this.drawingNewLinkData.srcPortId;
|
|
4509
|
-
const trgPortId = this.getInputNodePortId(d3Event, trgNode);
|
|
4510
|
-
|
|
4511
|
-
if (CanvasUtils.isDataConnectionAllowed(srcPortId, trgPortId, srcNode, trgNode, this.activePipeline.links)) {
|
|
4512
|
-
this.canvasController.editActionHandler({
|
|
4513
|
-
editType: "linkNodes",
|
|
4514
|
-
editSource: "canvas",
|
|
4515
|
-
nodes: [{ "id": this.drawingNewLinkData.srcObjId, "portId": this.drawingNewLinkData.srcPortId }],
|
|
4516
|
-
targetNodes: [{ "id": trgNode.id, "portId": trgPortId }],
|
|
4517
|
-
type: type,
|
|
4518
|
-
linkType: "data", // Added for historical purposes - for WML Canvas support
|
|
4519
|
-
pipelineId: this.pipelineId });
|
|
4520
|
-
|
|
4521
|
-
} else if (this.config.enableLinkReplaceOnNewConnection &&
|
|
4522
|
-
CanvasUtils.isDataLinkReplacementAllowed(srcPortId, trgPortId, srcNode, trgNode, this.activePipeline.links)) {
|
|
4523
|
-
const linksToTrgPort = CanvasUtils.getDataLinksConnectedTo(trgPortId, trgNode, this.activePipeline.links);
|
|
4524
|
-
// We only replace a link to a maxed out cardinality port if there
|
|
4525
|
-
// is only one link. i.e. the input port cardinality is 0:1
|
|
4526
|
-
if (linksToTrgPort.length === 1) {
|
|
4527
|
-
this.canvasController.editActionHandler({
|
|
4528
|
-
editType: "linkNodesAndReplace",
|
|
4529
|
-
editSource: "canvas",
|
|
4530
|
-
nodes: [{ "id": this.drawingNewLinkData.srcObjId, "portId": this.drawingNewLinkData.srcPortId }],
|
|
4531
|
-
targetNodes: [{ "id": trgNode.id, "portId": trgPortId }],
|
|
4532
|
-
type: type,
|
|
4533
|
-
pipelineId: this.pipelineId,
|
|
4534
|
-
replaceLink: linksToTrgPort[0]
|
|
4535
|
-
});
|
|
4536
|
-
}
|
|
4537
|
-
}
|
|
4538
|
-
|
|
4539
|
-
} else if (type === ASSOCIATION_LINK) {
|
|
4540
|
-
const srcNode = this.activePipeline.getNode(this.drawingNewLinkData.srcObjId);
|
|
4541
|
-
|
|
4542
|
-
if (CanvasUtils.isAssocConnectionAllowed(srcNode, trgNode, this.activePipeline.links)) {
|
|
4543
|
-
this.canvasController.editActionHandler({
|
|
4544
|
-
editType: "linkNodes",
|
|
4545
|
-
editSource: "canvas",
|
|
4546
|
-
nodes: [{ "id": this.drawingNewLinkData.srcObjId }],
|
|
4547
|
-
targetNodes: [{ "id": trgNode.id }],
|
|
4548
|
-
type: type,
|
|
4549
|
-
pipelineId: this.pipelineId });
|
|
4550
|
-
}
|
|
4551
|
-
|
|
4552
|
-
} else {
|
|
4553
|
-
if (CanvasUtils.isCommentLinkConnectionAllowed(this.drawingNewLinkData.srcObjId, trgNode.id, this.activePipeline.links)) {
|
|
4554
|
-
this.canvasController.editActionHandler({
|
|
4555
|
-
editType: "linkComment",
|
|
4556
|
-
editSource: "canvas",
|
|
4557
|
-
nodes: [this.drawingNewLinkData.srcObjId],
|
|
4558
|
-
targetNodes: [trgNode.id],
|
|
4559
|
-
type: COMMENT_LINK,
|
|
4560
|
-
linkType: "comment", // Added for historical purposes - for WML Canvas support
|
|
4561
|
-
pipelineId: this.pipelineId });
|
|
4562
|
-
}
|
|
4563
|
-
}
|
|
4564
|
-
}
|
|
4565
|
-
|
|
4566
|
-
this.drawingNewLinkData = null;
|
|
4567
|
-
}
|
|
4568
|
-
|
|
4569
|
-
// Handles the completion of a new link when the end is dropped away from
|
|
4570
|
-
// a node (when enableLinkSelection is set to LINK_SELECTION_DETACHABLE)
|
|
4571
|
-
// which creates a new detached link.
|
|
4572
|
-
completeNewDetachedLink(d3Event) {
|
|
4573
|
-
// If we completed a connection remove the new line objects.
|
|
4574
|
-
this.removeNewLink();
|
|
4575
|
-
|
|
4576
|
-
// Switch 'new link over node' highlighting off
|
|
4577
|
-
if (this.config.enableHighlightNodeOnNewLinkDrag) {
|
|
4578
|
-
this.setNewLinkOverNodeCancel();
|
|
4579
|
-
}
|
|
4580
|
-
|
|
4581
|
-
const endPoint = this.getTransformedMousePos(d3Event);
|
|
4582
|
-
this.canvasController.editActionHandler({
|
|
4583
|
-
editType: "createDetachedLink",
|
|
4584
|
-
editSource: "canvas",
|
|
4585
|
-
srcNodeId: this.drawingNewLinkData.srcObjId,
|
|
4586
|
-
srcNodePortId: this.drawingNewLinkData.srcPortId,
|
|
4587
|
-
trgPos: endPoint,
|
|
4588
|
-
type: NODE_LINK,
|
|
4589
|
-
pipelineId: this.pipelineId });
|
|
4590
|
-
|
|
4591
|
-
this.drawingNewLinkData = null;
|
|
4592
|
-
}
|
|
4593
|
-
|
|
4594
|
-
stopDrawingNewLink() {
|
|
4595
|
-
// Switch 'new link over node' highlighting off
|
|
4596
|
-
if (this.config.enableHighlightNodeOnNewLinkDrag) {
|
|
4597
|
-
this.setNewLinkOverNodeCancel();
|
|
4598
|
-
}
|
|
4599
|
-
|
|
4600
|
-
this.stopDrawingNewLinkForPorts();
|
|
4601
|
-
this.drawingNewLinkData = null;
|
|
4602
|
-
}
|
|
4603
|
-
|
|
4604
|
-
stopDrawingNewLinkForPorts() {
|
|
4605
|
-
const saveX1 = this.drawingNewLinkData.linkArray[0].x1;
|
|
4606
|
-
const saveY1 = this.drawingNewLinkData.linkArray[0].y1;
|
|
4607
|
-
const saveX2 = this.drawingNewLinkData.linkArray[0].x2;
|
|
4608
|
-
const saveY2 = this.drawingNewLinkData.linkArray[0].y2;
|
|
4609
|
-
|
|
4610
|
-
const saveNewLinkData = Object.assign({}, this.drawingNewLinkData);
|
|
4611
|
-
|
|
4612
|
-
this.drawingNewLinkData = null;
|
|
4613
|
-
|
|
4614
|
-
// If we completed a connection successfully just remove the new line
|
|
4615
|
-
// objects.
|
|
4616
|
-
let newPath = "";
|
|
4617
|
-
let duration = 350;
|
|
4618
|
-
|
|
4619
|
-
if (this.canvasLayout.linkType === LINK_TYPE_CURVE) {
|
|
4620
|
-
newPath = "M " + saveX1 + " " + saveY1 +
|
|
4621
|
-
"C " + saveX2 + " " + saveY2 +
|
|
4622
|
-
" " + saveX2 + " " + saveY2 +
|
|
4623
|
-
" " + saveX2 + " " + saveY2;
|
|
4624
|
-
|
|
4625
|
-
} else if (this.canvasLayout.linkType === LINK_TYPE_STRAIGHT) {
|
|
4626
|
-
if (saveX1 < saveX2) {
|
|
4627
|
-
duration = 0;
|
|
4628
|
-
}
|
|
4629
|
-
newPath = "M " + saveX1 + " " + saveY1 +
|
|
4630
|
-
"L " + saveX2 + " " + saveY2 +
|
|
4631
|
-
" " + saveX2 + " " + saveY2 +
|
|
4632
|
-
" " + saveX2 + " " + saveY2;
|
|
4633
|
-
|
|
4634
|
-
} else {
|
|
4635
|
-
newPath = "M " + saveX1 + " " + saveY1 +
|
|
4636
|
-
"L " + saveX2 + " " + saveY2 +
|
|
4637
|
-
"Q " + saveX2 + " " + saveY2 + " " + saveX2 + " " + saveY2 +
|
|
4638
|
-
"L " + saveX2 + " " + saveY2 +
|
|
4639
|
-
"Q " + saveX2 + " " + saveY2 + " " + saveX2 + " " + saveY2 +
|
|
4640
|
-
"L " + saveX2 + " " + saveY2 +
|
|
4641
|
-
"Q " + saveX2 + " " + saveY2 + " " + saveX2 + " " + saveY2 +
|
|
4642
|
-
"L " + saveX2 + " " + saveY2 +
|
|
4643
|
-
"Q " + saveX2 + " " + saveY2 + " " + saveX2 + " " + saveY2 +
|
|
4644
|
-
"L " + saveX2 + " " + saveY2;
|
|
4645
|
-
}
|
|
4646
|
-
|
|
4647
|
-
this.nodesLinksGrp.selectAll(".d3-new-connection-line")
|
|
4648
|
-
.transition()
|
|
4649
|
-
.duration(duration)
|
|
4650
|
-
.attr("d", newPath)
|
|
4651
|
-
.on("end", () => {
|
|
4652
|
-
this.nodesLinksGrp.selectAll(".d3-new-connection-arrow").remove();
|
|
4653
|
-
|
|
4654
|
-
this.nodesLinksGrp.selectAll(".d3-new-connection-guide")
|
|
4655
|
-
.transition()
|
|
4656
|
-
.duration(1000)
|
|
4657
|
-
.ease(d3.easeElastic)
|
|
4658
|
-
// The lines below set all attributes for images AND circles even
|
|
4659
|
-
// though some attributes will not be relevant. This is done
|
|
4660
|
-
// because I could not get the .each() method to work here (which
|
|
4661
|
-
// would be necessary to have an if statement based on guide object)
|
|
4662
|
-
.attr("x", saveX1 - (saveNewLinkData.portWidth / 2))
|
|
4663
|
-
.attr("y", saveY1 - (saveNewLinkData.portHeight / 2))
|
|
4664
|
-
.attr("cx", saveX1)
|
|
4665
|
-
.attr("cy", saveY1)
|
|
4666
|
-
.attr("transform", null);
|
|
4667
|
-
this.nodesLinksGrp.selectAll(".d3-new-connection-line")
|
|
4668
|
-
.transition()
|
|
4669
|
-
.duration(1000)
|
|
4670
|
-
.ease(d3.easeElastic)
|
|
4671
|
-
.attr("d", "M " + saveX1 + " " + saveY1 +
|
|
4672
|
-
"L " + saveX1 + " " + saveY1)
|
|
4673
|
-
.on("end", this.removeNewLink.bind(this));
|
|
4674
|
-
});
|
|
4675
|
-
}
|
|
4676
|
-
|
|
4677
|
-
removeNewLink() {
|
|
4678
|
-
this.nodesLinksGrp.selectAll(".d3-new-connection-line").remove();
|
|
4679
|
-
this.nodesLinksGrp.selectAll(".d3-new-connection-start").remove();
|
|
4680
|
-
this.nodesLinksGrp.selectAll(".d3-new-connection-guide").remove();
|
|
4681
|
-
this.nodesLinksGrp.selectAll(".d3-new-connection-arrow").remove();
|
|
4682
|
-
}
|
|
2865
|
+
// Returns an array of breadcrumbs for the DOM element passed in. The DOM
|
|
2866
|
+
// element is expected to be an element within a node (like the expansion
|
|
2867
|
+
// icon). The output array will contain one breadcrumb for each nested
|
|
2868
|
+
// supernode down to the supernode of which the DOM element is a part. So if
|
|
2869
|
+
// there are three nested supernodes and the DOM element is part of the third
|
|
2870
|
+
// one, the breadcrumbs array will have three elements.
|
|
2871
|
+
getSupernodeBreadcrumbs(domEl) {
|
|
2872
|
+
const breadcrumbs = [];
|
|
4683
2873
|
|
|
4684
|
-
|
|
4685
|
-
this.nodesLinksGrp.selectAll(".d3-new-connection-line").remove();
|
|
4686
|
-
}
|
|
2874
|
+
let nodeGroupEl = domEl.closest(".d3-node-group");
|
|
4687
2875
|
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
2876
|
+
while (nodeGroupEl) {
|
|
2877
|
+
const svgAreaEl = nodeGroupEl.closest(".svg-area");
|
|
2878
|
+
const supernodeDatum = this.getD3DatumFromDomEl(nodeGroupEl);
|
|
2879
|
+
const parentPipelineId = svgAreaEl.getAttribute("data-pipeline-id");
|
|
4691
2880
|
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
delete link.srcNodePortId;
|
|
4696
|
-
delete link.srcObj;
|
|
2881
|
+
breadcrumbs.push(
|
|
2882
|
+
this.canvasController.createBreadcrumb(supernodeDatum, parentPipelineId)
|
|
2883
|
+
);
|
|
4697
2884
|
|
|
4698
|
-
|
|
4699
|
-
link.trgPos = { x_pos: transPos.x, y_pos: transPos.y };
|
|
4700
|
-
delete link.trgNodeId;
|
|
4701
|
-
delete link.trgNodePortId;
|
|
4702
|
-
delete link.trgNode;
|
|
2885
|
+
nodeGroupEl = svgAreaEl.closest(".d3-node-group");
|
|
4703
2886
|
}
|
|
4704
2887
|
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
// towards and over a target node.
|
|
4709
|
-
if (this.config.enableHighlightNodeOnNewLinkDrag) {
|
|
4710
|
-
this.setNewLinkOverNode(d3Event);
|
|
4711
|
-
}
|
|
4712
|
-
}
|
|
4713
|
-
|
|
4714
|
-
completeDraggedLink(d3Event) {
|
|
4715
|
-
const newLink = this.getNewLinkOnDrag(d3Event);
|
|
4716
|
-
|
|
4717
|
-
if (newLink) {
|
|
4718
|
-
const editSubType = this.getLinkEditSubType(newLink);
|
|
4719
|
-
// If editSubType is set the user did a gesture that requires a change
|
|
4720
|
-
// to the object model.
|
|
4721
|
-
if (editSubType) {
|
|
4722
|
-
this.canvasController.editActionHandler({
|
|
4723
|
-
editType: "updateLink",
|
|
4724
|
-
editSubType: editSubType,
|
|
4725
|
-
editSource: "canvas",
|
|
4726
|
-
newLink: newLink,
|
|
4727
|
-
pipelineId: this.pipelineId });
|
|
4728
|
-
// If editSubType is null, the user performed a gesture which should
|
|
4729
|
-
// not be executed as an action so draw the link back in its old position.
|
|
4730
|
-
} else {
|
|
4731
|
-
this.snapBackOldLink();
|
|
4732
|
-
}
|
|
4733
|
-
// newLink might be null when we are dragging a link handle with
|
|
4734
|
-
// enableLinkSelection not set to detachable. If that's the case the
|
|
4735
|
-
// link needs to snap back (redrawn) to its original position.
|
|
4736
|
-
} else {
|
|
4737
|
-
this.snapBackOldLink();
|
|
4738
|
-
}
|
|
2888
|
+
// Reverse the order, so they appear in the nesting order of the supernodes.
|
|
2889
|
+
return breadcrumbs.reverse();
|
|
2890
|
+
}
|
|
4739
2891
|
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
2892
|
+
// Returns the datum object (managd by D3) for the DOM element passed in.
|
|
2893
|
+
getD3DatumFromDomEl(el) {
|
|
2894
|
+
const sel = d3.select(el);
|
|
2895
|
+
if (sel) {
|
|
2896
|
+
return sel.datum();
|
|
4743
2897
|
}
|
|
4744
|
-
|
|
4745
|
-
this.unsetUnavailableNodesHighlighting();
|
|
4746
|
-
this.stopDraggingLink();
|
|
2898
|
+
return null;
|
|
4747
2899
|
}
|
|
4748
2900
|
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
this.displayLinks();
|
|
2901
|
+
updatePortRadiusAndPos(nodeObj, node, portObjClassName) {
|
|
2902
|
+
const nodeGrp = d3.select(nodeObj);
|
|
2903
|
+
nodeGrp.selectAll("." + portObjClassName)
|
|
2904
|
+
.attr("r", () => this.getPortRadius(node))
|
|
2905
|
+
.attr("cx", (port) => port.cx)
|
|
2906
|
+
.attr("cy", (port) => port.cy); // Port position may change for binding nodes with multiple-ports.
|
|
4756
2907
|
}
|
|
4757
2908
|
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
return "detachFromSrcNode";
|
|
4765
|
-
|
|
4766
|
-
} else if (oldLink.trgNodeId && !newLink.trgNodeId) {
|
|
4767
|
-
return "detachFromTrgNode";
|
|
4768
|
-
|
|
4769
|
-
} else if (!oldLink.srcNodeId && newLink.srcNodeId) {
|
|
4770
|
-
return "attachToSrcNode";
|
|
4771
|
-
|
|
4772
|
-
} else if (!oldLink.trgNodeId && newLink.trgNodeId) {
|
|
4773
|
-
return "attachToTrgNode";
|
|
4774
|
-
|
|
4775
|
-
} else if (!oldLink.srcNodeId && !newLink.srcNodeId &&
|
|
4776
|
-
(oldLink.srcPos.x_pos !== newLink.srcPos.x_pos ||
|
|
4777
|
-
oldLink.srcPos.y_pos !== newLink.srcPos.y_pos)) {
|
|
4778
|
-
return "moveSrcPosition";
|
|
4779
|
-
|
|
4780
|
-
} else if (!oldLink.trgNodeId && !newLink.trgNodeId &&
|
|
4781
|
-
(oldLink.trgPos.x_pos !== newLink.trgPos.x_pos ||
|
|
4782
|
-
oldLink.trgPos.y_pos !== newLink.trgPos.y_pos)) {
|
|
4783
|
-
return "moveTrgPosition";
|
|
2909
|
+
updatePortArrowPath(nodeObj, portArrowClassName) {
|
|
2910
|
+
const nodeGrp = d3.select(nodeObj);
|
|
2911
|
+
nodeGrp.selectAll("." + portArrowClassName)
|
|
2912
|
+
.attr("d", (port) => this.getPortArrowPath(port))
|
|
2913
|
+
.attr("transform", (port) => this.getPortArrowPathTransform(port));
|
|
2914
|
+
}
|
|
4784
2915
|
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
2916
|
+
// Returns true if the port (from a node template) passed in has a max
|
|
2917
|
+
// cardinaility of zero. If cardinality or cardinality.max is missing the
|
|
2918
|
+
// max is considered to be non-zero.
|
|
2919
|
+
isPortMaxCardinalityZero(port) {
|
|
2920
|
+
return (get(port, "app_data.ui_data.cardinality.max", 1) === 0);
|
|
2921
|
+
}
|
|
4788
2922
|
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
2923
|
+
isMouseOverContextToolbar(d3Event) {
|
|
2924
|
+
return this.getElementWithClassAtMousePos(d3Event, "context-toolbar");
|
|
2925
|
+
}
|
|
4792
2926
|
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
2927
|
+
removeDynamicNodeIcons(d3Event, d, nodeGrp) {
|
|
2928
|
+
if (d.layout.ellipsisDisplay) {
|
|
2929
|
+
nodeGrp.selectChildren(".d3-node-ellipsis-group").remove();
|
|
2930
|
+
}
|
|
2931
|
+
nodeGrp.selectChildren(".d3-node-super-expand-icon-group").remove();
|
|
2932
|
+
}
|
|
4797
2933
|
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
2934
|
+
createSupernodeRenderer(d, supernodeD3Object) {
|
|
2935
|
+
if (d.subflow_ref && d.subflow_ref.pipeline_id_ref) {
|
|
2936
|
+
const superRenderer = new SVGCanvasRenderer(
|
|
2937
|
+
d.subflow_ref.pipeline_id_ref,
|
|
2938
|
+
this.canvasDiv,
|
|
2939
|
+
this.canvasController,
|
|
2940
|
+
this.canvasInfo,
|
|
2941
|
+
this.selectionInfo,
|
|
2942
|
+
this.breadcrumbs,
|
|
2943
|
+
this.nodeLayout,
|
|
2944
|
+
this.canvasLayout,
|
|
2945
|
+
this.config,
|
|
2946
|
+
{ id: d.id,
|
|
2947
|
+
pipelineId: this.activePipeline.id,
|
|
2948
|
+
renderer: this, // Only provided for in-place sub-flow
|
|
2949
|
+
d3Selection: supernodeD3Object // Only provided for in-place sub-flow
|
|
2950
|
+
});
|
|
2951
|
+
return superRenderer;
|
|
4802
2952
|
}
|
|
4803
|
-
// We arrive here, in two ways:
|
|
4804
|
-
// 1. if the user dragged a link handle from a node/port and dropped it
|
|
4805
|
-
// back on the same node/port.
|
|
4806
|
-
// 2. If the user clicked on the unattached end of a detached link but did
|
|
4807
|
-
// not move it anywhere
|
|
4808
|
-
// In these cases, the updateLink action should NOT be performed and
|
|
4809
|
-
// consequently NO command should be added to the command stack.
|
|
4810
2953
|
return null;
|
|
4811
2954
|
}
|
|
4812
2955
|
|
|
4813
|
-
// Returns
|
|
4814
|
-
//
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
delete newLink.srcNodeId;
|
|
4822
|
-
delete newLink.srcNodePortId;
|
|
4823
|
-
delete newLink.srcPos;
|
|
4824
|
-
|
|
4825
|
-
const srcNode = nodeProximity
|
|
4826
|
-
? this.getNodeNearMousePos(d3Event, nodeProximity)
|
|
4827
|
-
: this.getNodeAtMousePos(d3Event);
|
|
4828
|
-
|
|
4829
|
-
if (srcNode) {
|
|
4830
|
-
newLink.srcNodeId = srcNode.id;
|
|
4831
|
-
newLink.srcObj = this.activePipeline.getNode(srcNode.id);
|
|
4832
|
-
newLink.srcNodePortId = nodeProximity
|
|
4833
|
-
? this.getNodePortIdNearMousePos(d3Event, OUTPUT_TYPE, srcNode)
|
|
4834
|
-
: this.getOutputNodePortId(d3Event, srcNode);
|
|
4835
|
-
} else {
|
|
4836
|
-
newLink.srcPos = this.draggingLinkData.link.srcPos;
|
|
4837
|
-
}
|
|
4838
|
-
|
|
4839
|
-
} else {
|
|
4840
|
-
delete newLink.trgNode;
|
|
4841
|
-
delete newLink.trgNodeId;
|
|
4842
|
-
delete newLink.trgNodePortId;
|
|
4843
|
-
delete newLink.trgPos;
|
|
4844
|
-
|
|
4845
|
-
const trgNode = nodeProximity
|
|
4846
|
-
? this.getNodeNearMousePos(d3Event, nodeProximity)
|
|
4847
|
-
: this.getNodeAtMousePos(d3Event);
|
|
4848
|
-
|
|
4849
|
-
if (trgNode) {
|
|
4850
|
-
newLink.trgNodeId = trgNode.id;
|
|
4851
|
-
newLink.trgNode = this.activePipeline.getNode(trgNode.id);
|
|
4852
|
-
newLink.trgNodePortId = nodeProximity
|
|
4853
|
-
? this.getNodePortIdNearMousePos(d3Event, INPUT_TYPE, trgNode)
|
|
4854
|
-
: this.getInputNodePortId(d3Event, trgNode);
|
|
4855
|
-
} else {
|
|
4856
|
-
newLink.trgPos = this.draggingLinkData.link.trgPos;
|
|
4857
|
-
}
|
|
4858
|
-
}
|
|
2956
|
+
// Returns the renderer for the supernode passed in. With external
|
|
2957
|
+
// pipeline handling the pipeline referencd by the supernode can change
|
|
2958
|
+
// over time so we have to make sure the renderer is for the supernode AND
|
|
2959
|
+
// for the active pipeline.
|
|
2960
|
+
getRendererForSupernode(d) {
|
|
2961
|
+
return this.superRenderers.find((sr) =>
|
|
2962
|
+
sr.supernodeInfo.id === d.id && sr.activePipeline.id === d.subflow_ref.pipeline_id_ref);
|
|
2963
|
+
}
|
|
4859
2964
|
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
return null;
|
|
4866
|
-
}
|
|
2965
|
+
// Returns an array containing any renderers that are for the supernode passed
|
|
2966
|
+
// in but where the supernode does NOT reference the renderer's active pipeline.
|
|
2967
|
+
getDiscardedRenderersForSupernode(d) {
|
|
2968
|
+
return this.superRenderers.filter((sr) =>
|
|
2969
|
+
sr.supernodeInfo.id === d.id && sr.activePipeline.id !== d.subflow_ref.pipeline_id_ref);
|
|
4867
2970
|
|
|
4868
|
-
if (this.canExecuteUpdateLinkCommand(newLink, oldLink)) {
|
|
4869
|
-
return newLink;
|
|
4870
|
-
}
|
|
4871
|
-
return null;
|
|
4872
2971
|
}
|
|
4873
2972
|
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
2973
|
+
openContextMenu(d3Event, type, d, port, pos) {
|
|
2974
|
+
CanvasUtils.stopPropagationAndPreventDefault(d3Event); // Stop the browser context menu appearing
|
|
2975
|
+
this.canvasController.contextMenuHandler({
|
|
2976
|
+
type: type,
|
|
2977
|
+
targetObject: type === "canvas" ? null : d,
|
|
2978
|
+
id: type === "canvas" ? null : d.id, // For historical puposes, we pass d.id as well as d as targetObject.
|
|
2979
|
+
pipelineId: this.activePipeline.id,
|
|
2980
|
+
cmPos: pos
|
|
2981
|
+
? pos
|
|
2982
|
+
: this.getMousePos(d3Event, this.canvasDiv.selectAll("svg")), // Get mouse pos relative to top most SVG area even in a subflow.
|
|
2983
|
+
mousePos: this.getMousePosSnapToGrid(this.getTransformedMousePos(d3Event)),
|
|
2984
|
+
selectedObjectIds: this.canvasController.getSelectedObjectIds(),
|
|
2985
|
+
addBreadcrumbs: (d && d.type === SUPER_NODE) ? this.getSupernodeBreadcrumbs(d3Event.currentTarget) : null,
|
|
2986
|
+
port: port,
|
|
2987
|
+
zoom: this.zoomUtils.getZoomScale() });
|
|
2988
|
+
}
|
|
4883
2989
|
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
}
|
|
4888
|
-
if (linkTrgChanged && trgNode &&
|
|
4889
|
-
!CanvasUtils.isTrgConnectionAllowedWithDetachedLinks(newLink.trgNodePortId, trgNode, links)) {
|
|
4890
|
-
executeCommand = false;
|
|
2990
|
+
closeContextMenuIfOpen() {
|
|
2991
|
+
if (this.canvasController.isContextMenuDisplayed()) {
|
|
2992
|
+
this.canvasController.closeContextMenu();
|
|
4891
2993
|
}
|
|
4892
|
-
if (
|
|
4893
|
-
|
|
4894
|
-
executeCommand = false;
|
|
2994
|
+
if (this.config.enableContextToolbar) {
|
|
2995
|
+
this.removeContextToolbar();
|
|
4895
2996
|
}
|
|
4896
|
-
return executeCommand;
|
|
4897
2997
|
}
|
|
4898
2998
|
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
if (newLink.srcNodeId) {
|
|
4905
|
-
if (newLink.srcNodeId !== oldLink.srcNodeId) {
|
|
4906
|
-
linkUpdated = true;
|
|
4907
|
-
}
|
|
4908
|
-
|
|
4909
|
-
if (newLink.srcNodePortId !== oldLink.srcNodePortId) {
|
|
4910
|
-
linkUpdated = true;
|
|
4911
|
-
}
|
|
4912
|
-
|
|
4913
|
-
} else {
|
|
4914
|
-
if (oldLink.srcPos) {
|
|
4915
|
-
if (newLink.srcPos.x_pos !== oldLink.srcPos.x_pos ||
|
|
4916
|
-
newLink.srcPos.y_pos !== oldLink.srcPos.y_pos) {
|
|
4917
|
-
linkUpdated = true;
|
|
4918
|
-
}
|
|
4919
|
-
} else {
|
|
4920
|
-
linkUpdated = true;
|
|
4921
|
-
}
|
|
2999
|
+
callDecoratorCallback(d3Event, node, dec) {
|
|
3000
|
+
d3Event.stopPropagation();
|
|
3001
|
+
if (this.canvasController.decorationActionHandler) {
|
|
3002
|
+
this.canvasController.decorationActionHandler(node, dec.id, this.activePipeline.id);
|
|
4922
3003
|
}
|
|
4923
|
-
return linkUpdated;
|
|
4924
3004
|
}
|
|
4925
3005
|
|
|
4926
|
-
// Returns true if the target information has changed between
|
|
4927
|
-
// the two links.
|
|
4928
|
-
hasLinkTrgChanged(newLink, oldLink) {
|
|
4929
|
-
let linkUpdated = false;
|
|
4930
|
-
|
|
4931
|
-
if (newLink.trgNodeId) {
|
|
4932
|
-
if (newLink.trgNodeId !== oldLink.trgNodeId) {
|
|
4933
|
-
linkUpdated = true;
|
|
4934
|
-
}
|
|
4935
|
-
|
|
4936
|
-
if (newLink.trgNodePortId !== oldLink.trgNodePortId) {
|
|
4937
|
-
linkUpdated = true;
|
|
4938
|
-
}
|
|
4939
3006
|
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
3007
|
+
getLinkImageTransform(d) {
|
|
3008
|
+
let angle = 0;
|
|
3009
|
+
if (this.canvasLayout.linkType === LINK_TYPE_STRAIGHT) {
|
|
3010
|
+
const adjacent = d.x2 - (d.originX || d.x1);
|
|
3011
|
+
const opposite = d.y2 - (d.originY || d.y1);
|
|
3012
|
+
if (adjacent === 0 && opposite === 0) {
|
|
3013
|
+
angle = 0;
|
|
4946
3014
|
} else {
|
|
4947
|
-
|
|
3015
|
+
angle = Math.atan(opposite / adjacent) * (180 / Math.PI);
|
|
3016
|
+
angle = adjacent >= 0 ? angle : angle + 180;
|
|
3017
|
+
if (this.canvasLayout.linkDirection === LINK_DIR_TOP_BOTTOM) {
|
|
3018
|
+
angle -= 90;
|
|
3019
|
+
} else if (this.canvasLayout.linkDirection === LINK_DIR_BOTTOM_TOP) {
|
|
3020
|
+
angle += 90;
|
|
3021
|
+
}
|
|
4948
3022
|
}
|
|
3023
|
+
return `rotate(${angle},${d.x2},${d.y2})`;
|
|
4949
3024
|
}
|
|
4950
|
-
return
|
|
4951
|
-
}
|
|
4952
|
-
|
|
4953
|
-
stopDraggingLink() {
|
|
4954
|
-
this.draggingLinkData = null;
|
|
3025
|
+
return null;
|
|
4955
3026
|
}
|
|
4956
3027
|
|
|
4957
3028
|
// Returns a link, if one can be found, at the position indicated by x and y
|
|
@@ -5083,7 +3154,7 @@ export default class SVGCanvasRenderer {
|
|
|
5083
3154
|
.each((d) => {
|
|
5084
3155
|
let portRadius = d.layout.portRadius;
|
|
5085
3156
|
if (CanvasUtils.isSuperBindingNode(d)) {
|
|
5086
|
-
portRadius = this.canvasLayout.supernodeBindingPortRadius / this.
|
|
3157
|
+
portRadius = this.canvasLayout.supernodeBindingPortRadius / this.zoomUtils.getZoomScale();
|
|
5087
3158
|
}
|
|
5088
3159
|
|
|
5089
3160
|
if (pos.x >= d.x_pos - portRadius - prox && // Target port sticks out by its radius so need to allow for it.
|
|
@@ -5096,75 +3167,6 @@ export default class SVGCanvasRenderer {
|
|
|
5096
3167
|
return node;
|
|
5097
3168
|
}
|
|
5098
3169
|
|
|
5099
|
-
getNodePortIdNearMousePos(d3Event, portType, node) {
|
|
5100
|
-
const pos = this.getTransformedMousePos(d3Event);
|
|
5101
|
-
let portId = null;
|
|
5102
|
-
let defaultPortId = null;
|
|
5103
|
-
|
|
5104
|
-
if (node) {
|
|
5105
|
-
if (portType === OUTPUT_TYPE) {
|
|
5106
|
-
const portObjs = this.getAllNodeGroupsSelection()
|
|
5107
|
-
.selectChildren("." + this.getNodeOutputPortClassName())
|
|
5108
|
-
.selectChildren(".d3-node-port-output-main");
|
|
5109
|
-
|
|
5110
|
-
portId = this.searchForPortNearMouse(
|
|
5111
|
-
node, pos, portObjs,
|
|
5112
|
-
node.layout.outputPortObject,
|
|
5113
|
-
node.width);
|
|
5114
|
-
defaultPortId = CanvasUtils.getDefaultOutputPortId(node);
|
|
5115
|
-
|
|
5116
|
-
} else {
|
|
5117
|
-
const portObjs = this.getAllNodeGroupsSelection()
|
|
5118
|
-
.selectChildren("." + this.getNodeInputPortClassName())
|
|
5119
|
-
.selectChildren(".d3-node-port-input-main");
|
|
5120
|
-
|
|
5121
|
-
portId = this.searchForPortNearMouse(
|
|
5122
|
-
node, pos, portObjs,
|
|
5123
|
-
node.layout.inputPortObject,
|
|
5124
|
-
0);
|
|
5125
|
-
defaultPortId = CanvasUtils.getDefaultInputPortId(node);
|
|
5126
|
-
}
|
|
5127
|
-
}
|
|
5128
|
-
|
|
5129
|
-
if (!portId) {
|
|
5130
|
-
portId = defaultPortId;
|
|
5131
|
-
}
|
|
5132
|
-
return portId;
|
|
5133
|
-
}
|
|
5134
|
-
|
|
5135
|
-
// Returns a port ID for the port identified by the position (pos) on the
|
|
5136
|
-
// node (node) further specified by the other parameters.
|
|
5137
|
-
searchForPortNearMouse(node, pos, portObjs, portObjectType, nodeWidthOffset) {
|
|
5138
|
-
let portId = null;
|
|
5139
|
-
portObjs
|
|
5140
|
-
.each((p, i, portGrps) => {
|
|
5141
|
-
const portSel = d3.select(portGrps[i]);
|
|
5142
|
-
if (portObjectType === PORT_OBJECT_IMAGE) {
|
|
5143
|
-
const xx = node.x_pos + Number(portSel.attr("x"));
|
|
5144
|
-
const yy = node.y_pos + Number(portSel.attr("y"));
|
|
5145
|
-
const wd = Number(portSel.attr("width"));
|
|
5146
|
-
const ht = Number(portSel.attr("height"));
|
|
5147
|
-
if (pos.x >= xx &&
|
|
5148
|
-
pos.x <= xx + nodeWidthOffset + wd &&
|
|
5149
|
-
pos.y >= yy &&
|
|
5150
|
-
pos.y <= yy + ht) {
|
|
5151
|
-
portId = portGrps[i].getAttribute("data-port-id");
|
|
5152
|
-
}
|
|
5153
|
-
} else { // Port must be a circle
|
|
5154
|
-
const cx = node.x_pos + Number(portSel.attr("cx"));
|
|
5155
|
-
const cy = node.y_pos + Number(portSel.attr("cy"));
|
|
5156
|
-
if (pos.x >= cx - node.layout.portRadius && // Target port sticks out by its radius so need to allow for it.
|
|
5157
|
-
pos.x <= cx + node.layout.portRadius &&
|
|
5158
|
-
pos.y >= cy - node.layout.portRadius &&
|
|
5159
|
-
pos.y <= cy + node.layout.portRadius) {
|
|
5160
|
-
portId = portGrps[i].getAttribute("data-port-id");
|
|
5161
|
-
}
|
|
5162
|
-
}
|
|
5163
|
-
});
|
|
5164
|
-
|
|
5165
|
-
return portId;
|
|
5166
|
-
}
|
|
5167
|
-
|
|
5168
3170
|
// Returns a sizing rectangle for nodes and comments. This extends an
|
|
5169
3171
|
// invisible area out beyond the highlight sizing line to improve usability
|
|
5170
3172
|
// when sizing.
|
|
@@ -5410,7 +3412,7 @@ export default class SVGCanvasRenderer {
|
|
|
5410
3412
|
// necessary with binding nodes with mutiple ports.
|
|
5411
3413
|
let multiplier = 1;
|
|
5412
3414
|
if (CanvasUtils.isSuperBindingNode(data)) {
|
|
5413
|
-
multiplier = 1 / this.
|
|
3415
|
+
multiplier = 1 / this.zoomUtils.getZoomScale();
|
|
5414
3416
|
}
|
|
5415
3417
|
|
|
5416
3418
|
ports.forEach((p) => {
|
|
@@ -5464,7 +3466,7 @@ export default class SVGCanvasRenderer {
|
|
|
5464
3466
|
// necessary with binding nodes with mutiple ports.
|
|
5465
3467
|
let multiplier = 1;
|
|
5466
3468
|
if (CanvasUtils.isSuperBindingNode(data)) {
|
|
5467
|
-
multiplier = 1 / this.
|
|
3469
|
+
multiplier = 1 / this.zoomUtils.getZoomScale();
|
|
5468
3470
|
}
|
|
5469
3471
|
ports.forEach((p) => {
|
|
5470
3472
|
yPosition += (data.layout.portArcRadius * multiplier);
|
|
@@ -5623,10 +3625,11 @@ export default class SVGCanvasRenderer {
|
|
|
5623
3625
|
? markdownIt.render(c.content)
|
|
5624
3626
|
: escapeText(c.content)));
|
|
5625
3627
|
|
|
5626
|
-
// Add or remove drag behavior
|
|
3628
|
+
// Add or remove drag object behavior for the comment groups.
|
|
5627
3629
|
if (this.config.enableEditingActions) {
|
|
3630
|
+
const handler = this.dragObjectUtils.getDragObjectHandler();
|
|
5628
3631
|
joinedCommentGrps
|
|
5629
|
-
.call(
|
|
3632
|
+
.call(handler);
|
|
5630
3633
|
} else {
|
|
5631
3634
|
joinedCommentGrps
|
|
5632
3635
|
.on(".drag", null);
|
|
@@ -5637,6 +3640,9 @@ export default class SVGCanvasRenderer {
|
|
|
5637
3640
|
attachCommentGroupListeners(commentGrps) {
|
|
5638
3641
|
commentGrps
|
|
5639
3642
|
.on("mouseenter", (d3Event, d) => {
|
|
3643
|
+
if (this.isDragging()) {
|
|
3644
|
+
return;
|
|
3645
|
+
}
|
|
5640
3646
|
this.setCommentStyles(d, "hover", d3.select(d3Event.currentTarget));
|
|
5641
3647
|
if (this.config.enableEditingActions && d.id !== this.svgCanvasTextArea.getEditingTextId()) {
|
|
5642
3648
|
this.createCommentPort(d3Event.currentTarget, d);
|
|
@@ -5657,7 +3663,7 @@ export default class SVGCanvasRenderer {
|
|
|
5657
3663
|
// Use mouse down instead of click because it gets called before drag start.
|
|
5658
3664
|
.on("mousedown", (d3Event, d) => {
|
|
5659
3665
|
this.logger.logStartTimer("Comment Group - mouse down");
|
|
5660
|
-
d3Event.stopPropagation();
|
|
3666
|
+
d3Event.stopPropagation();
|
|
5661
3667
|
if (this.svgCanvasTextArea.isEditingText()) {
|
|
5662
3668
|
this.svgCanvasTextArea.completeEditing();
|
|
5663
3669
|
}
|
|
@@ -5700,9 +3706,7 @@ export default class SVGCanvasRenderer {
|
|
|
5700
3706
|
attachCommentSizingListeners(commentGrps) {
|
|
5701
3707
|
commentGrps
|
|
5702
3708
|
.on("mousedown", (d3Event, d) => {
|
|
5703
|
-
this.
|
|
5704
|
-
// Note - comment resizing and finalization of size is handled by drag functions.
|
|
5705
|
-
this.addTempCursorOverlay(this.commentSizingCursor);
|
|
3709
|
+
this.dragObjectUtils.mouseDownCommentSizingArea();
|
|
5706
3710
|
})
|
|
5707
3711
|
// Use mousemove here rather than mouseenter so the cursor will change
|
|
5708
3712
|
// if the pointer moves from one area of the node outline to another
|
|
@@ -5711,17 +3715,10 @@ export default class SVGCanvasRenderer {
|
|
|
5711
3715
|
// pointer leaves the temporary overlay (which is removed) and enters
|
|
5712
3716
|
// the node outline.
|
|
5713
3717
|
.on("mousemove mouseenter", (d3Event, d) => {
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
if (!this.isPointerCloseToBodyEdge(d3Event, d)) {
|
|
5719
|
-
this.commentSizingDirection = this.getSizingDirection(d3Event, d, this.canvasLayout.commentCornerResizeArea);
|
|
5720
|
-
this.commentSizingCursor = this.getCursorBasedOnDirection(this.commentSizingDirection);
|
|
5721
|
-
cursorType = this.commentSizingCursor;
|
|
5722
|
-
}
|
|
5723
|
-
d3.select(d3Event.currentTarget).style("cursor", cursorType);
|
|
5724
|
-
}
|
|
3718
|
+
this.dragObjectUtils.mouseEnterCommentSizingArea(d3Event, d);
|
|
3719
|
+
})
|
|
3720
|
+
.on("mouseleave", (d3Event, d) => {
|
|
3721
|
+
this.dragObjectUtils.mouseLeaveCommentSizingArea(d3Event, d);
|
|
5725
3722
|
});
|
|
5726
3723
|
}
|
|
5727
3724
|
|
|
@@ -5731,26 +3728,19 @@ export default class SVGCanvasRenderer {
|
|
|
5731
3728
|
createCommentPort(commentObj, d) {
|
|
5732
3729
|
const commentGrp = d3.select(commentObj);
|
|
5733
3730
|
|
|
5734
|
-
commentGrp
|
|
3731
|
+
const commentPort = commentGrp
|
|
5735
3732
|
.append("circle")
|
|
5736
3733
|
.attr("cx", (com) => com.width / 2)
|
|
5737
3734
|
.attr("cy", (com) => com.height + this.canvasLayout.commentHighlightGap)
|
|
5738
3735
|
.attr("r", this.canvasLayout.commentPortRadius)
|
|
5739
|
-
.attr("class", "d3-comment-port-circle")
|
|
5740
|
-
.on("mousedown", (d3Event, cd) => {
|
|
5741
|
-
CanvasUtils.stopPropagationAndPreventDefault(d3Event); // Stops the node drag behavior when clicking on the handle/circle
|
|
5742
|
-
this.drawingNewLinkData = {
|
|
5743
|
-
srcObjId: d.id,
|
|
5744
|
-
action: COMMENT_LINK,
|
|
5745
|
-
startPos: {
|
|
5746
|
-
x: d.x_pos - this.canvasLayout.commentHighlightGap,
|
|
5747
|
-
y: d.y_pos - this.canvasLayout.commentHighlightGap
|
|
5748
|
-
},
|
|
5749
|
-
linkArray: []
|
|
5750
|
-
};
|
|
5751
|
-
this.drawNewLink(d3Event);
|
|
5752
|
-
});
|
|
3736
|
+
.attr("class", "d3-comment-port-circle");
|
|
5753
3737
|
|
|
3738
|
+
if (this.config.enableEditingActions) {
|
|
3739
|
+
const handler = this.dragNewLinkUtils.getDragNewLinkHandler();
|
|
3740
|
+
commentPort.call(handler);
|
|
3741
|
+
} else {
|
|
3742
|
+
commentPort.on(".drag", null);
|
|
3743
|
+
}
|
|
5754
3744
|
}
|
|
5755
3745
|
|
|
5756
3746
|
deleteCommentPort(commentObj) {
|
|
@@ -5840,7 +3830,7 @@ export default class SVGCanvasRenderer {
|
|
|
5840
3830
|
// Returns true if this renderer or any of its ancestors are currently in the
|
|
5841
3831
|
// process of selecting a region or sizing a node or comment.
|
|
5842
3832
|
isRegionSelectOrSizingInProgress() {
|
|
5843
|
-
if (this.regionSelect || this.
|
|
3833
|
+
if (this.regionSelect || this.isSizing()) {
|
|
5844
3834
|
return true;
|
|
5845
3835
|
}
|
|
5846
3836
|
if (this.supernodeInfo.renderer) {
|
|
@@ -5851,336 +3841,6 @@ export default class SVGCanvasRenderer {
|
|
|
5851
3841
|
return false;
|
|
5852
3842
|
}
|
|
5853
3843
|
|
|
5854
|
-
// This method allows us to avoid a strange behavior which only appears in the
|
|
5855
|
-
// Chrome browser. That is, when the mouse pointer is inside the
|
|
5856
|
-
// node/comment selection highlight area but is close to either the
|
|
5857
|
-
// right or bottom side of the node/comment body, any mousedown events will go
|
|
5858
|
-
// to the body instead of the highlight area. We use this method to detect
|
|
5859
|
-
// this situation and use the result to decide whether to display the sizing
|
|
5860
|
-
// cursor or not.
|
|
5861
|
-
isPointerCloseToBodyEdge(d3Event, d) {
|
|
5862
|
-
const pos = this.getTransformedMousePos(d3Event);
|
|
5863
|
-
const rightEdge = d.x_pos + d.width;
|
|
5864
|
-
const bottomEdge = d.y_pos + d.height;
|
|
5865
|
-
|
|
5866
|
-
// Is the pointer within 1 pixel of the right edge of the node or comment
|
|
5867
|
-
const rightEdgeState =
|
|
5868
|
-
pos.x >= rightEdge && pos.x <= rightEdge + 1 &&
|
|
5869
|
-
pos.y >= 0 && pos.y <= bottomEdge;
|
|
5870
|
-
|
|
5871
|
-
// Is the pointer within 1 pixel of the bottom edge of the node or comment
|
|
5872
|
-
const bottomEdgeState =
|
|
5873
|
-
pos.y >= bottomEdge && pos.y <= bottomEdge + 1 &&
|
|
5874
|
-
pos.x >= 0 && pos.x <= rightEdge;
|
|
5875
|
-
|
|
5876
|
-
return rightEdgeState || bottomEdgeState;
|
|
5877
|
-
}
|
|
5878
|
-
|
|
5879
|
-
// Returns the comment or supernode sizing direction (i.e. one of n, s, e, w, nw, ne,
|
|
5880
|
-
// sw or se) based on the current mouse position and the position and
|
|
5881
|
-
// dimensions of the comment or node outline.
|
|
5882
|
-
getSizingDirection(d3Event, d, cornerResizeArea) {
|
|
5883
|
-
var xPart = "";
|
|
5884
|
-
var yPart = "";
|
|
5885
|
-
|
|
5886
|
-
const transPos = this.getTransformedMousePos(d3Event);
|
|
5887
|
-
if (transPos.x < d.x_pos + cornerResizeArea) {
|
|
5888
|
-
xPart = "w";
|
|
5889
|
-
} else if (transPos.x > d.x_pos + d.width - cornerResizeArea) {
|
|
5890
|
-
xPart = "e";
|
|
5891
|
-
}
|
|
5892
|
-
if (transPos.y < d.y_pos + cornerResizeArea) {
|
|
5893
|
-
yPart = "n";
|
|
5894
|
-
} else if (transPos.y > d.y_pos + d.height - cornerResizeArea) {
|
|
5895
|
-
yPart = "s";
|
|
5896
|
-
}
|
|
5897
|
-
|
|
5898
|
-
return yPart + xPart;
|
|
5899
|
-
}
|
|
5900
|
-
|
|
5901
|
-
// Returns a cursor type based on the currect comment sizing direction.
|
|
5902
|
-
// Possible values are: ns-resize, ew-resize, nwse-resize or nesw-resize.
|
|
5903
|
-
getCursorBasedOnDirection(direction) {
|
|
5904
|
-
var cursorType;
|
|
5905
|
-
switch (direction) {
|
|
5906
|
-
case "n":
|
|
5907
|
-
case "s":
|
|
5908
|
-
cursorType = "ns-resize";
|
|
5909
|
-
break;
|
|
5910
|
-
case "e":
|
|
5911
|
-
case "w":
|
|
5912
|
-
cursorType = "ew-resize";
|
|
5913
|
-
break;
|
|
5914
|
-
case "nw":
|
|
5915
|
-
case "se":
|
|
5916
|
-
cursorType = "nwse-resize";
|
|
5917
|
-
break;
|
|
5918
|
-
case "ne":
|
|
5919
|
-
case "sw":
|
|
5920
|
-
cursorType = "nesw-resize";
|
|
5921
|
-
break;
|
|
5922
|
-
default:
|
|
5923
|
-
cursorType = "";
|
|
5924
|
-
}
|
|
5925
|
-
|
|
5926
|
-
return cursorType;
|
|
5927
|
-
}
|
|
5928
|
-
|
|
5929
|
-
// Returns the minimum allowed height for the node passed in. For supernodes
|
|
5930
|
-
// this means combining the bigger of the space for the inputs and output ports
|
|
5931
|
-
// with some space for the top of the display frame and the padding at the
|
|
5932
|
-
// bottom of the frame. Then the bigger of that height versus the default
|
|
5933
|
-
// supernode minimum height is retunred.
|
|
5934
|
-
getMinHeight(node) {
|
|
5935
|
-
if (CanvasUtils.isSupernode(node)) {
|
|
5936
|
-
const minHt = Math.max(node.inputPortsHeight, node.outputPortsHeight) +
|
|
5937
|
-
this.canvasLayout.supernodeTopAreaHeight + this.canvasLayout.supernodeSVGAreaPadding;
|
|
5938
|
-
return Math.max(this.canvasLayout.supernodeMinHeight, minHt);
|
|
5939
|
-
}
|
|
5940
|
-
return node.layout.defaultNodeHeight;
|
|
5941
|
-
}
|
|
5942
|
-
|
|
5943
|
-
// Returns the minimum allowed width for the node passed in.
|
|
5944
|
-
getMinWidth(node) {
|
|
5945
|
-
if (CanvasUtils.isSupernode(node)) {
|
|
5946
|
-
return this.canvasLayout.supernodeMinWidth;
|
|
5947
|
-
}
|
|
5948
|
-
return node.layout.defaultNodeWidth;
|
|
5949
|
-
}
|
|
5950
|
-
|
|
5951
|
-
// Sets the size and position of the node in the canvasInfo.nodes
|
|
5952
|
-
// array based on the position of the pointer during the resize action
|
|
5953
|
-
// then redraws the nodes and links (the link positions may move based
|
|
5954
|
-
// on the node size change).
|
|
5955
|
-
resizeNode(d3Event, resizeObj) {
|
|
5956
|
-
const oldSupernode = Object.assign({}, resizeObj);
|
|
5957
|
-
const minHeight = this.getMinHeight(resizeObj);
|
|
5958
|
-
const minWidth = this.getMinWidth(resizeObj);
|
|
5959
|
-
|
|
5960
|
-
const delta = this.resizeObject(d3Event, resizeObj,
|
|
5961
|
-
this.nodeSizingDirection, minWidth, minHeight);
|
|
5962
|
-
|
|
5963
|
-
if (delta && (delta.x_pos !== 0 || delta.y_pos !== 0 || delta.width !== 0 || delta.height !== 0)) {
|
|
5964
|
-
if (CanvasUtils.isSupernode(resizeObj) &&
|
|
5965
|
-
this.config.enableMoveNodesOnSupernodeResize) {
|
|
5966
|
-
const objectsInfo = CanvasUtils.moveSurroundingObjects(
|
|
5967
|
-
oldSupernode,
|
|
5968
|
-
this.activePipeline.getNodesAndComments(),
|
|
5969
|
-
this.nodeSizingDirection,
|
|
5970
|
-
resizeObj.width,
|
|
5971
|
-
resizeObj.height,
|
|
5972
|
-
true // Pass true to indicate that object positions should be updated.
|
|
5973
|
-
);
|
|
5974
|
-
|
|
5975
|
-
const linksInfo = CanvasUtils.moveSurroundingDetachedLinks(
|
|
5976
|
-
oldSupernode,
|
|
5977
|
-
this.activePipeline.links,
|
|
5978
|
-
this.nodeSizingDirection,
|
|
5979
|
-
resizeObj.width,
|
|
5980
|
-
resizeObj.height,
|
|
5981
|
-
true // Pass true to indicate that link positions should be updated.
|
|
5982
|
-
);
|
|
5983
|
-
|
|
5984
|
-
// Overwrite the object and link info with any new info.
|
|
5985
|
-
this.nodeSizingObjectsInfo = Object.assign(this.nodeSizingObjectsInfo, objectsInfo);
|
|
5986
|
-
this.nodeSizingDetLinksInfo = Object.assign(this.nodeSizingDetLinksInfo, linksInfo);
|
|
5987
|
-
}
|
|
5988
|
-
|
|
5989
|
-
this.logger.logStartTimer("displayObjects");
|
|
5990
|
-
|
|
5991
|
-
this.displayMovedComments();
|
|
5992
|
-
this.displayMovedNodes();
|
|
5993
|
-
this.displaySingleNode(resizeObj);
|
|
5994
|
-
this.displayMovedLinks();
|
|
5995
|
-
this.displayCanvasAccoutrements();
|
|
5996
|
-
|
|
5997
|
-
if (CanvasUtils.isSupernode(resizeObj)) {
|
|
5998
|
-
if (this.dispUtils.isDisplayingSubFlow()) {
|
|
5999
|
-
this.displayBindingNodesToFitSVG();
|
|
6000
|
-
}
|
|
6001
|
-
this.superRenderers.forEach((renderer) => renderer.displaySVGToFitSupernode());
|
|
6002
|
-
}
|
|
6003
|
-
this.logger.logEndTimer("displayObjects");
|
|
6004
|
-
}
|
|
6005
|
-
}
|
|
6006
|
-
|
|
6007
|
-
// Sets the size and position of the comment in the canvasInfo.comments
|
|
6008
|
-
// array based on the position of the pointer during the resize action
|
|
6009
|
-
// then redraws the comment and links (the link positions may move based
|
|
6010
|
-
// on the comment size change).
|
|
6011
|
-
resizeComment(d3Event, resizeObj) {
|
|
6012
|
-
this.resizeObject(d3Event, resizeObj, this.commentSizingDirection, 20, 20);
|
|
6013
|
-
this.displaySingleComment(resizeObj);
|
|
6014
|
-
this.displayMovedLinks();
|
|
6015
|
-
this.displayCanvasAccoutrements();
|
|
6016
|
-
}
|
|
6017
|
-
|
|
6018
|
-
// Sets the size and position of the object in the canvasInfo
|
|
6019
|
-
// array based on the position of the pointer during the resize action.
|
|
6020
|
-
resizeObject(d3Event, canvasObj, direction, minWidth, minHeight) {
|
|
6021
|
-
let incrementX = 0;
|
|
6022
|
-
let incrementY = 0;
|
|
6023
|
-
let incrementWidth = 0;
|
|
6024
|
-
let incrementHeight = 0;
|
|
6025
|
-
|
|
6026
|
-
if (direction.indexOf("e") > -1) {
|
|
6027
|
-
incrementWidth += d3Event.dx;
|
|
6028
|
-
}
|
|
6029
|
-
if (direction.indexOf("s") > -1) {
|
|
6030
|
-
incrementHeight += d3Event.dy;
|
|
6031
|
-
}
|
|
6032
|
-
if (direction.indexOf("n") > -1) {
|
|
6033
|
-
incrementY += d3Event.dy;
|
|
6034
|
-
incrementHeight -= d3Event.dy;
|
|
6035
|
-
}
|
|
6036
|
-
if (direction.indexOf("w") > -1) {
|
|
6037
|
-
incrementX += d3Event.dx;
|
|
6038
|
-
incrementWidth -= d3Event.dx;
|
|
6039
|
-
}
|
|
6040
|
-
|
|
6041
|
-
let xPos = 0;
|
|
6042
|
-
let yPos = 0;
|
|
6043
|
-
let width = 0;
|
|
6044
|
-
let height = 0;
|
|
6045
|
-
|
|
6046
|
-
if (this.config.enableSnapToGridType === SNAP_TO_GRID_DURING) {
|
|
6047
|
-
// Calculate where the object being resized would be and its size given
|
|
6048
|
-
// current increments.
|
|
6049
|
-
this.notSnappedXPos += incrementX;
|
|
6050
|
-
this.notSnappedYPos += incrementY;
|
|
6051
|
-
this.notSnappedWidth += incrementWidth;
|
|
6052
|
-
this.notSnappedHeight += incrementHeight;
|
|
6053
|
-
|
|
6054
|
-
xPos = CanvasUtils.snapToGrid(this.notSnappedXPos, this.canvasLayout.snapToGridXPx);
|
|
6055
|
-
yPos = CanvasUtils.snapToGrid(this.notSnappedYPos, this.canvasLayout.snapToGridYPx);
|
|
6056
|
-
width = CanvasUtils.snapToGrid(this.notSnappedWidth, this.canvasLayout.snapToGridXPx);
|
|
6057
|
-
height = CanvasUtils.snapToGrid(this.notSnappedHeight, this.canvasLayout.snapToGridYPx);
|
|
6058
|
-
|
|
6059
|
-
width = Math.max(width, minWidth);
|
|
6060
|
-
height = Math.max(height, minHeight);
|
|
6061
|
-
|
|
6062
|
-
} else {
|
|
6063
|
-
xPos = canvasObj.x_pos + incrementX;
|
|
6064
|
-
yPos = canvasObj.y_pos + incrementY;
|
|
6065
|
-
width = canvasObj.width + incrementWidth;
|
|
6066
|
-
height = canvasObj.height + incrementHeight;
|
|
6067
|
-
}
|
|
6068
|
-
|
|
6069
|
-
// Don't allow the object area to shrink below the min width and height.
|
|
6070
|
-
// For comment sizing, errors may occur especially if the width becomes
|
|
6071
|
-
// less that one character's width. For node sizing we want at least some
|
|
6072
|
-
// area to display the sub-flow.
|
|
6073
|
-
if (width < minWidth || height < minHeight) {
|
|
6074
|
-
return null;
|
|
6075
|
-
}
|
|
6076
|
-
|
|
6077
|
-
const delta = {
|
|
6078
|
-
width: width - canvasObj.width,
|
|
6079
|
-
height: height - canvasObj.height,
|
|
6080
|
-
x_pos: xPos - canvasObj.x_pos,
|
|
6081
|
-
y_pos: yPos - canvasObj.y_pos
|
|
6082
|
-
};
|
|
6083
|
-
|
|
6084
|
-
canvasObj.x_pos = xPos;
|
|
6085
|
-
canvasObj.y_pos = yPos;
|
|
6086
|
-
canvasObj.width = width;
|
|
6087
|
-
canvasObj.height = height;
|
|
6088
|
-
|
|
6089
|
-
return delta;
|
|
6090
|
-
}
|
|
6091
|
-
|
|
6092
|
-
// Finalises the sizing of a node by calling editActionHandler
|
|
6093
|
-
// with an editNode action.
|
|
6094
|
-
endNodeSizing(node) {
|
|
6095
|
-
let resizeObj = node;
|
|
6096
|
-
if (this.config.enableSnapToGridType === SNAP_TO_GRID_AFTER) {
|
|
6097
|
-
resizeObj = this.snapToGridObject(resizeObj);
|
|
6098
|
-
resizeObj = this.restrictNodeSizingToMinimums(resizeObj);
|
|
6099
|
-
}
|
|
6100
|
-
|
|
6101
|
-
// If the dimensions or position has changed, issue the "resizeObjects" command.
|
|
6102
|
-
// Note: x_pos or y_pos might change on resize if the node is sized
|
|
6103
|
-
// upwards or to the left.
|
|
6104
|
-
if (this.resizeObjInitialInfo.x_pos !== resizeObj.x_pos ||
|
|
6105
|
-
this.resizeObjInitialInfo.y_pos !== resizeObj.y_pos ||
|
|
6106
|
-
this.resizeObjInitialInfo.width !== resizeObj.width ||
|
|
6107
|
-
this.resizeObjInitialInfo.height !== resizeObj.height) {
|
|
6108
|
-
// Add the dimensions of the object being resized to the array of object infos.
|
|
6109
|
-
this.nodeSizingObjectsInfo[resizeObj.id] = {
|
|
6110
|
-
width: resizeObj.width,
|
|
6111
|
-
height: resizeObj.height,
|
|
6112
|
-
x_pos: resizeObj.x_pos,
|
|
6113
|
-
y_pos: resizeObj.y_pos
|
|
6114
|
-
};
|
|
6115
|
-
|
|
6116
|
-
// If the node has been resized set the resize properties appropriately.
|
|
6117
|
-
// We use some padding because sometimes, when a node is sized back to its
|
|
6118
|
-
// original dimensions, it isn't retunred to EXACTLY its default width/height.
|
|
6119
|
-
if (resizeObj.height > resizeObj.layout.defaultNodeHeight + 2 ||
|
|
6120
|
-
resizeObj.width > resizeObj.layout.defaultNodeWidth + 2) {
|
|
6121
|
-
this.nodeSizingObjectsInfo[resizeObj.id].isResized = true;
|
|
6122
|
-
this.nodeSizingObjectsInfo[resizeObj.id].resizeWidth = resizeObj.width;
|
|
6123
|
-
this.nodeSizingObjectsInfo[resizeObj.id].resizeHeight = resizeObj.height;
|
|
6124
|
-
}
|
|
6125
|
-
|
|
6126
|
-
this.canvasController.editActionHandler({
|
|
6127
|
-
editType: "resizeObjects",
|
|
6128
|
-
editSource: "canvas",
|
|
6129
|
-
objectsInfo: this.nodeSizingObjectsInfo,
|
|
6130
|
-
detachedLinksInfo: this.nodeSizingDetLinksInfo,
|
|
6131
|
-
pipelineId: this.pipelineId
|
|
6132
|
-
});
|
|
6133
|
-
}
|
|
6134
|
-
this.nodeSizing = false;
|
|
6135
|
-
this.nodeSizingObjectsInfo = {};
|
|
6136
|
-
this.nodeSizingDetLinksInfo = {};
|
|
6137
|
-
}
|
|
6138
|
-
|
|
6139
|
-
// Finalises the sizing of a comment by calling editActionHandler
|
|
6140
|
-
// with an editComment action.
|
|
6141
|
-
endCommentSizing(comment) {
|
|
6142
|
-
let resizeObj = comment;
|
|
6143
|
-
if (this.config.enableSnapToGridType === SNAP_TO_GRID_AFTER) {
|
|
6144
|
-
resizeObj = this.snapToGridObject(resizeObj);
|
|
6145
|
-
}
|
|
6146
|
-
|
|
6147
|
-
// If the dimensions or position has changed, issue the command.
|
|
6148
|
-
// Note: x_pos or y_pos might change on resize if the node is sized
|
|
6149
|
-
// upwards or to the left.
|
|
6150
|
-
if (this.resizeObjInitialInfo.x_pos !== resizeObj.x_pos ||
|
|
6151
|
-
this.resizeObjInitialInfo.y_pos !== resizeObj.y_pos ||
|
|
6152
|
-
this.resizeObjInitialInfo.width !== resizeObj.width ||
|
|
6153
|
-
this.resizeObjInitialInfo.height !== resizeObj.height) {
|
|
6154
|
-
const commentSizingObjectsInfo = [];
|
|
6155
|
-
commentSizingObjectsInfo[resizeObj.id] = {
|
|
6156
|
-
width: resizeObj.width,
|
|
6157
|
-
height: resizeObj.height,
|
|
6158
|
-
x_pos: resizeObj.x_pos,
|
|
6159
|
-
y_pos: resizeObj.y_pos
|
|
6160
|
-
};
|
|
6161
|
-
|
|
6162
|
-
const data = {
|
|
6163
|
-
editType: "resizeObjects",
|
|
6164
|
-
editSource: "canvas",
|
|
6165
|
-
objectsInfo: commentSizingObjectsInfo,
|
|
6166
|
-
detachedLinksInfo: {}, // Comments cannot have detached links
|
|
6167
|
-
pipelineId: this.pipelineId
|
|
6168
|
-
};
|
|
6169
|
-
this.canvasController.editActionHandler(data);
|
|
6170
|
-
}
|
|
6171
|
-
this.commentSizing = false;
|
|
6172
|
-
}
|
|
6173
|
-
|
|
6174
|
-
// Ensure the snap-to-grid does not make the width or height smaller than
|
|
6175
|
-
// the minimums allowed.
|
|
6176
|
-
restrictNodeSizingToMinimums(resizeObj) {
|
|
6177
|
-
const minHeight = this.getMinHeight(resizeObj);
|
|
6178
|
-
const minWidth = this.getMinWidth(resizeObj);
|
|
6179
|
-
resizeObj.width = Math.max(resizeObj.width, minWidth);
|
|
6180
|
-
resizeObj.height = Math.max(resizeObj.height, minHeight);
|
|
6181
|
-
return resizeObj;
|
|
6182
|
-
}
|
|
6183
|
-
|
|
6184
3844
|
// Displays all the links on the canvas either by creating new links,
|
|
6185
3845
|
// updating existing links or removing unwanted links.
|
|
6186
3846
|
displayLinks() {
|
|
@@ -6238,6 +3898,7 @@ export default class SVGCanvasRenderer {
|
|
|
6238
3898
|
|
|
6239
3899
|
// Creates all newly created links specified in the enter selection.
|
|
6240
3900
|
createLinks(enter) {
|
|
3901
|
+
this.logger.logStartTimer("createLinks");
|
|
6241
3902
|
// Add groups for links
|
|
6242
3903
|
const newLinkGrps = enter.append("g")
|
|
6243
3904
|
.attr("data-id", (d) => this.getId("link_grp", d.id))
|
|
@@ -6281,6 +3942,8 @@ export default class SVGCanvasRenderer {
|
|
|
6281
3942
|
});
|
|
6282
3943
|
}
|
|
6283
3944
|
|
|
3945
|
+
this.logger.logEndTimer("createLinks");
|
|
3946
|
+
|
|
6284
3947
|
return newLinkGrps;
|
|
6285
3948
|
}
|
|
6286
3949
|
|
|
@@ -6288,6 +3951,7 @@ export default class SVGCanvasRenderer {
|
|
|
6288
3951
|
// selection object. The selection object will contain newly created links
|
|
6289
3952
|
// as well as existing links.
|
|
6290
3953
|
updateLinks(joinedLinkGrps, lineArray) {
|
|
3954
|
+
this.logger.logStartTimer("updateLinks");
|
|
6291
3955
|
// Update link selection area
|
|
6292
3956
|
joinedLinkGrps
|
|
6293
3957
|
.selectAll(".d3-link-selection-area")
|
|
@@ -6333,9 +3997,11 @@ export default class SVGCanvasRenderer {
|
|
|
6333
3997
|
});
|
|
6334
3998
|
}
|
|
6335
3999
|
|
|
6336
|
-
if (!this.
|
|
4000
|
+
if (!this.isMoving() && !this.isSizing()) {
|
|
6337
4001
|
this.setDisplayOrder(joinedLinkGrps);
|
|
6338
4002
|
}
|
|
4003
|
+
|
|
4004
|
+
this.logger.logEndTimer("updateLinks");
|
|
6339
4005
|
}
|
|
6340
4006
|
|
|
6341
4007
|
attachLinkGroupListeners(linkGrps) {
|
|
@@ -6365,6 +4031,10 @@ export default class SVGCanvasRenderer {
|
|
|
6365
4031
|
this.openContextMenu(d3Event, "link", d);
|
|
6366
4032
|
})
|
|
6367
4033
|
.on("mouseenter", (d3Event, link) => {
|
|
4034
|
+
if (this.isDragging()) {
|
|
4035
|
+
return;
|
|
4036
|
+
}
|
|
4037
|
+
|
|
6368
4038
|
const targetObj = d3Event.currentTarget;
|
|
6369
4039
|
|
|
6370
4040
|
if (this.config.enableLinkSelection === LINK_SELECTION_HANDLES ||
|
|
@@ -6377,8 +4047,7 @@ export default class SVGCanvasRenderer {
|
|
|
6377
4047
|
this.addContextToolbar(d3Event, link, "link");
|
|
6378
4048
|
}
|
|
6379
4049
|
|
|
6380
|
-
if (this.canOpenTip(TIP_TYPE_LINK)
|
|
6381
|
-
!this.draggingLinkData) {
|
|
4050
|
+
if (this.canOpenTip(TIP_TYPE_LINK)) {
|
|
6382
4051
|
this.canvasController.closeTip();
|
|
6383
4052
|
this.canvasController.openTip({
|
|
6384
4053
|
id: this.getId("link_tip", link.id),
|
|
@@ -6444,7 +4113,8 @@ export default class SVGCanvasRenderer {
|
|
|
6444
4113
|
|
|
6445
4114
|
// Add or remove drag behavior as appropriate
|
|
6446
4115
|
if (this.config.enableEditingActions) {
|
|
6447
|
-
|
|
4116
|
+
const handler = this.dragDetLinkUtils.getDragDetachedLinkHandler();
|
|
4117
|
+
startHandle.call(handler);
|
|
6448
4118
|
} else {
|
|
6449
4119
|
startHandle.on(".drag", null);
|
|
6450
4120
|
}
|
|
@@ -6473,7 +4143,8 @@ export default class SVGCanvasRenderer {
|
|
|
6473
4143
|
|
|
6474
4144
|
// Add or remove drag behavior as appropriate
|
|
6475
4145
|
if (this.config.enableEditingActions) {
|
|
6476
|
-
|
|
4146
|
+
const handler = this.dragDetLinkUtils.getDragDetachedLinkHandler();
|
|
4147
|
+
endHandle.call(handler);
|
|
6477
4148
|
} else {
|
|
6478
4149
|
endHandle.on(".drag", null);
|
|
6479
4150
|
}
|
|
@@ -6623,22 +4294,26 @@ export default class SVGCanvasRenderer {
|
|
|
6623
4294
|
}
|
|
6624
4295
|
}
|
|
6625
4296
|
|
|
4297
|
+
// Raises the node, specified by the node ID, above other nodes and objects.
|
|
4298
|
+
// Called by external utils.
|
|
4299
|
+
raiseNodeToTopById(nodeId) {
|
|
4300
|
+
this.getNodeGroupSelectionById(nodeId).raise();
|
|
4301
|
+
}
|
|
4302
|
+
|
|
6626
4303
|
// Raises the node above other nodes and objects (on the mouse entering
|
|
6627
4304
|
// the node). This is necessary for apps that have ports that protrude from
|
|
6628
4305
|
// the side of the node and where those nodes may be positioned close to each
|
|
6629
4306
|
// other so it makes the ports appear on top of any adjacent node. We don't
|
|
6630
4307
|
// raise the nodes for various conditions:
|
|
6631
4308
|
// * The enableRaiseNodesToTopOnHover config option is set to false
|
|
6632
|
-
// * We are currently
|
|
6633
|
-
// * We are dragging some object(s) around
|
|
4309
|
+
// * We are currently dragging to create a new link, or to move objects or detached links
|
|
6634
4310
|
// * There are one or more selected links
|
|
6635
4311
|
// * We are editing text
|
|
6636
4312
|
raiseNodeToTop(nodeGrp) {
|
|
6637
4313
|
if (this.config.enableRaiseNodesToTopOnHover &&
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
!this.isEditingText()) {
|
|
4314
|
+
!this.isDragging() &&
|
|
4315
|
+
this.activePipeline.getSelectedLinksCount() === 0 &&
|
|
4316
|
+
!this.isEditingText()) {
|
|
6642
4317
|
nodeGrp.raise();
|
|
6643
4318
|
}
|
|
6644
4319
|
}
|
|
@@ -6667,11 +4342,9 @@ export default class SVGCanvasRenderer {
|
|
|
6667
4342
|
return link.decorations && link.decorations.length > 0;
|
|
6668
4343
|
}
|
|
6669
4344
|
|
|
6670
|
-
isLinkBeingDragged(link) {
|
|
6671
|
-
return this.draggingLinkData && this.draggingLinkData.link.id === link.id;
|
|
6672
|
-
}
|
|
6673
|
-
|
|
6674
4345
|
buildLinksArray() {
|
|
4346
|
+
this.logger.logStartTimer("buildLinksArray");
|
|
4347
|
+
|
|
6675
4348
|
let linksArray = [];
|
|
6676
4349
|
|
|
6677
4350
|
if (this.canvasLayout.linkType === LINK_TYPE_STRAIGHT) {
|
|
@@ -6681,8 +4354,9 @@ export default class SVGCanvasRenderer {
|
|
|
6681
4354
|
this.activePipeline.links.forEach((link) => {
|
|
6682
4355
|
let linkObj = null;
|
|
6683
4356
|
|
|
6684
|
-
if (((this.config.enableLinkSelection === LINK_SELECTION_HANDLES &&
|
|
6685
|
-
|
|
4357
|
+
if (((this.config.enableLinkSelection === LINK_SELECTION_HANDLES &&
|
|
4358
|
+
this.dragDetLinkUtils.isLinkBeingDragged(link)) ||
|
|
4359
|
+
this.config.enableLinkSelection === LINK_SELECTION_DETACHABLE) &&
|
|
6686
4360
|
(!link.srcObj || !link.trgNode)) {
|
|
6687
4361
|
linkObj = this.getDetachedLineObj(link);
|
|
6688
4362
|
|
|
@@ -6703,6 +4377,8 @@ export default class SVGCanvasRenderer {
|
|
|
6703
4377
|
// Add connection path info to the links.
|
|
6704
4378
|
linksArray = this.linkUtils.addConnectionPaths(linksArray);
|
|
6705
4379
|
|
|
4380
|
+
this.logger.logEndTimer("buildLinksArray");
|
|
4381
|
+
|
|
6706
4382
|
return linksArray;
|
|
6707
4383
|
}
|
|
6708
4384
|
|
|
@@ -7258,24 +4934,24 @@ export default class SVGCanvasRenderer {
|
|
|
7258
4934
|
|
|
7259
4935
|
canOpenTip(tipType) {
|
|
7260
4936
|
return this.canvasController.isTipEnabled(tipType) &&
|
|
7261
|
-
!this.regionSelect && !this.
|
|
7262
|
-
!this.commentSizing && !this.nodeSizing && !this.drawingNewLinkData;
|
|
4937
|
+
!this.regionSelect && !this.isDragging() && !this.isSizing();
|
|
7263
4938
|
}
|
|
7264
4939
|
|
|
7265
|
-
// Return the x,y coordinates
|
|
7266
|
-
//
|
|
7267
|
-
//
|
|
4940
|
+
// Return the x,y coordinates for the default position of a new comment
|
|
4941
|
+
// created from the toolbar. This make sure the new comment always appears
|
|
4942
|
+
// in the top left corner of the view port.
|
|
7268
4943
|
getDefaultCommentOffset() {
|
|
7269
4944
|
let xPos = this.canvasLayout.addCommentOffsetX;
|
|
7270
4945
|
let yPos = this.canvasLayout.addCommentOffsetY;
|
|
4946
|
+
const z = this.zoomUtils.getZoomTransform();
|
|
7271
4947
|
|
|
7272
|
-
if (
|
|
7273
|
-
|
|
7274
|
-
|
|
4948
|
+
if (z) {
|
|
4949
|
+
const xPanByScale = z.x / z.k;
|
|
4950
|
+
const yPanByScale = z.y / z.k;
|
|
7275
4951
|
|
|
7276
|
-
//
|
|
7277
|
-
xPos = -
|
|
7278
|
-
yPos = -
|
|
4952
|
+
// Offset in the negative direction.
|
|
4953
|
+
xPos = -xPanByScale + this.canvasLayout.addCommentOffsetX;
|
|
4954
|
+
yPos = -yPanByScale + this.canvasLayout.addCommentOffsetY;
|
|
7279
4955
|
}
|
|
7280
4956
|
|
|
7281
4957
|
if (this.config.enableSnapToGridType === SNAP_TO_GRID_DURING ||
|
|
@@ -7288,14 +4964,11 @@ export default class SVGCanvasRenderer {
|
|
|
7288
4964
|
// Returns a string that explains which flags are set to true.
|
|
7289
4965
|
getFlags() {
|
|
7290
4966
|
let str = "Flags:";
|
|
7291
|
-
if (this.
|
|
4967
|
+
if (this.isDragging()) {
|
|
7292
4968
|
str += " dragging = true";
|
|
7293
4969
|
}
|
|
7294
|
-
if (this.
|
|
7295
|
-
str += "
|
|
7296
|
-
}
|
|
7297
|
-
if (this.commentSizing) {
|
|
7298
|
-
str += " commentSizing = true";
|
|
4970
|
+
if (this.isSizing()) {
|
|
4971
|
+
str += " sizing = true";
|
|
7299
4972
|
}
|
|
7300
4973
|
if (this.regionSelect) {
|
|
7301
4974
|
str += " regionSelect = true";
|