@elyra/canvas 12.11.2 → 12.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/dist/_baseForOwn-7d4e8506.js +2 -0
  2. package/dist/_baseForOwn-7d4e8506.js.map +1 -0
  3. package/dist/_baseForOwn-d38b560e.js +2 -0
  4. package/dist/_baseForOwn-d38b560e.js.map +1 -0
  5. package/dist/canvas-constants-34cdb7df.js +2 -0
  6. package/dist/canvas-constants-34cdb7df.js.map +1 -0
  7. package/dist/canvas-constants-3c09c7f6.js +2 -0
  8. package/dist/canvas-constants-3c09c7f6.js.map +1 -0
  9. package/dist/canvas-controller-de76a796.js +2 -0
  10. package/dist/canvas-controller-de76a796.js.map +1 -0
  11. package/dist/canvas-controller-e91d037b.js +2 -0
  12. package/dist/canvas-controller-e91d037b.js.map +1 -0
  13. package/dist/canvas-logger-815781bb.js.map +1 -1
  14. package/dist/canvas-logger-a0f1beaa.js.map +1 -1
  15. package/dist/common-canvas-522f6263.js +2 -0
  16. package/dist/common-canvas-522f6263.js.map +1 -0
  17. package/dist/common-canvas-90539c97.js +2 -0
  18. package/dist/common-canvas-90539c97.js.map +1 -0
  19. package/dist/common-canvas.es.js +1 -1
  20. package/dist/common-canvas.es.js.map +1 -1
  21. package/dist/common-canvas.js +1 -1
  22. package/dist/common-canvas.js.map +1 -1
  23. package/dist/common-properties-245c4711.js +2 -0
  24. package/dist/common-properties-245c4711.js.map +1 -0
  25. package/dist/common-properties-49e6bb67.js +2 -0
  26. package/dist/common-properties-49e6bb67.js.map +1 -0
  27. package/dist/{datarecord-metadata-v3-schema-d7ad05cd.js → datarecord-metadata-v3-schema-6b6384ff.js} +2 -2
  28. package/dist/{datarecord-metadata-v3-schema-d7ad05cd.js.map → datarecord-metadata-v3-schema-6b6384ff.js.map} +1 -1
  29. package/dist/{datarecord-metadata-v3-schema-bda88f2e.js → datarecord-metadata-v3-schema-81228a9a.js} +2 -2
  30. package/dist/{datarecord-metadata-v3-schema-bda88f2e.js.map → datarecord-metadata-v3-schema-81228a9a.js.map} +1 -1
  31. package/dist/en-7a0f1db1.js +2 -0
  32. package/dist/en-7a0f1db1.js.map +1 -0
  33. package/dist/en-8647c347.js +2 -0
  34. package/dist/en-8647c347.js.map +1 -0
  35. package/dist/{extends-39f57612.js → extends-7fdcdc52.js} +2 -2
  36. package/dist/{extends-39f57612.js.map → extends-7fdcdc52.js.map} +1 -1
  37. package/dist/extends-8d17c85c.js +7 -0
  38. package/dist/extends-8d17c85c.js.map +1 -0
  39. package/dist/flexible-table-50ce600a.js +2 -0
  40. package/dist/flexible-table-50ce600a.js.map +1 -0
  41. package/dist/flexible-table-a13cb7d0.js +2 -0
  42. package/dist/flexible-table-a13cb7d0.js.map +1 -0
  43. package/dist/getPrototypeOf-a1c3fe64.js +2 -0
  44. package/dist/getPrototypeOf-a1c3fe64.js.map +1 -0
  45. package/dist/getPrototypeOf-bf88242f.js +2 -0
  46. package/dist/getPrototypeOf-bf88242f.js.map +1 -0
  47. package/dist/{icon-d6909a68.js → icon-4882a57f.js} +2 -2
  48. package/dist/{icon-d6909a68.js.map → icon-4882a57f.js.map} +1 -1
  49. package/dist/{icon-811ffddd.js → icon-bf77b2aa.js} +2 -2
  50. package/dist/{icon-811ffddd.js.map → icon-bf77b2aa.js.map} +1 -1
  51. package/dist/index-0e6c8b9c.js +2 -0
  52. package/dist/index-0e6c8b9c.js.map +1 -0
  53. package/dist/index-f2c306ba.js +2 -0
  54. package/dist/index-f2c306ba.js.map +1 -0
  55. package/dist/isArrayLikeObject-a9c7973b.js +2 -0
  56. package/dist/isArrayLikeObject-a9c7973b.js.map +1 -0
  57. package/dist/isArrayLikeObject-f3b27f64.js +2 -0
  58. package/dist/isArrayLikeObject-f3b27f64.js.map +1 -0
  59. package/dist/lib/canvas-controller.es.js +1 -1
  60. package/dist/lib/canvas-controller.js +1 -1
  61. package/dist/lib/canvas.es.js +1 -1
  62. package/dist/lib/canvas.js +1 -1
  63. package/dist/lib/command-stack.es.js +1 -1
  64. package/dist/lib/command-stack.es.js.map +1 -1
  65. package/dist/lib/command-stack.js +1 -1
  66. package/dist/lib/command-stack.js.map +1 -1
  67. package/dist/lib/context-menu.es.js +1 -1
  68. package/dist/lib/context-menu.es.js.map +1 -1
  69. package/dist/lib/context-menu.js +1 -1
  70. package/dist/lib/context-menu.js.map +1 -1
  71. package/dist/lib/properties/field-picker.es.js +1 -1
  72. package/dist/lib/properties/field-picker.js +1 -1
  73. package/dist/lib/properties/flexible-table.es.js +1 -1
  74. package/dist/lib/properties/flexible-table.js +1 -1
  75. package/dist/lib/properties.es.js +1 -1
  76. package/dist/lib/properties.js +1 -1
  77. package/dist/lib/tooltip.es.js +1 -1
  78. package/dist/lib/tooltip.es.js.map +1 -1
  79. package/dist/lib/tooltip.js +1 -1
  80. package/dist/lib/tooltip.js.map +1 -1
  81. package/dist/styles/common-canvas.min.css +1 -1
  82. package/dist/styles/common-canvas.min.css.map +1 -1
  83. package/dist/{toolbar-85e6c77b.js → toolbar-1c181339.js} +2 -2
  84. package/dist/{toolbar-335252dd.js.map → toolbar-1c181339.js.map} +1 -1
  85. package/dist/{toolbar-335252dd.js → toolbar-c6fa3cdb.js} +2 -2
  86. package/dist/{toolbar-85e6c77b.js.map → toolbar-c6fa3cdb.js.map} +1 -1
  87. package/locales/command-actions/locales/en.json +8 -8
  88. package/locales/command-actions/locales/eo.json +8 -8
  89. package/locales/common-canvas/locales/en.json +1 -1
  90. package/package.json +9 -10
  91. package/src/command-actions/sizeAndPositionObjectsAction.js +9 -4
  92. package/src/command-stack/command-stack.js +16 -30
  93. package/src/command-stack/internal-stack.js +89 -0
  94. package/src/common-canvas/canvas-controller.js +43 -6
  95. package/src/common-canvas/cc-toolbar.jsx +10 -0
  96. package/src/common-canvas/common-canvas-utils.js +55 -1
  97. package/src/common-canvas/common-canvas.scss +3 -1
  98. package/src/common-canvas/constants/canvas-constants.js +12 -0
  99. package/src/common-canvas/svg-canvas-d3.js +8 -0
  100. package/src/common-canvas/svg-canvas-renderer.js +360 -130
  101. package/src/common-canvas/svg-canvas-utils-links.js +61 -26
  102. package/src/common-properties/actions/button/button.jsx +45 -13
  103. package/src/common-properties/actions/button/button.scss +4 -8
  104. package/src/common-properties/common-properties.jsx +2 -1
  105. package/src/common-properties/components/control-item/control-item.jsx +7 -1
  106. package/src/common-properties/components/field-picker/field-picker.jsx +7 -8
  107. package/src/common-properties/components/flexible-table/flexible-table.jsx +45 -10
  108. package/src/common-properties/components/virtualized-table/virtualized-table.jsx +72 -20
  109. package/src/common-properties/constants/constants.js +18 -0
  110. package/src/common-properties/controls/abstract-table.jsx +3 -3
  111. package/src/common-properties/controls/checkbox/checkbox.jsx +6 -0
  112. package/src/common-properties/controls/expression/expression-builder/expression-builder.jsx +6 -5
  113. package/src/common-properties/controls/expression/expression-builder/expression-select-field-function.jsx +4 -2
  114. package/src/common-properties/controls/expression/expression.jsx +10 -4
  115. package/src/common-properties/controls/expression/expression.scss +2 -0
  116. package/src/common-properties/controls/multiselect/multiselect.jsx +4 -4
  117. package/src/common-properties/controls/someofselect/someofselect.jsx +1 -1
  118. package/src/common-properties/form/ActionInfo.js +3 -1
  119. package/src/common-properties/form/EditorForm.js +7 -3
  120. package/src/common-properties/properties-controller.js +2 -1
  121. package/src/common-properties/properties-main/properties-main.jsx +4 -2
  122. package/src/common-properties/util/L10nProvider.js +3 -0
  123. package/src/object-model/api-pipeline.js +7 -11
  124. package/src/object-model/config-utils.js +1 -0
  125. package/src/object-model/object-model.js +73 -22
  126. package/src/object-model/pipeline-in-handler.js +8 -0
  127. package/src/object-model/pipeline-out-handler.js +5 -0
  128. package/src/object-model/redux/canvas-store.js +16 -0
  129. package/src/object-model/redux/reducers/canvasinfo.js +12 -8
  130. package/src/object-model/redux/reducers/canvastoolbar.js +6 -0
  131. package/src/object-model/redux/reducers/nodes.js +11 -0
  132. package/src/toolbar/toolbar-action-item.jsx +2 -2
  133. package/src/tooltip/tooltip.jsx +30 -1
  134. package/src/tooltip/tooltip.scss +11 -0
  135. package/stats.html +1 -1
  136. package/dist/canvas-constants-7f93e705.js +0 -2
  137. package/dist/canvas-constants-7f93e705.js.map +0 -1
  138. package/dist/canvas-constants-ac5daafb.js +0 -2
  139. package/dist/canvas-constants-ac5daafb.js.map +0 -1
  140. package/dist/canvas-controller-190542a6.js +0 -2
  141. package/dist/canvas-controller-190542a6.js.map +0 -1
  142. package/dist/canvas-controller-cbee8dd7.js +0 -2
  143. package/dist/canvas-controller-cbee8dd7.js.map +0 -1
  144. package/dist/common-canvas-8d2ddc6b.js +0 -2
  145. package/dist/common-canvas-8d2ddc6b.js.map +0 -1
  146. package/dist/common-canvas-e0a57cdb.js +0 -2
  147. package/dist/common-canvas-e0a57cdb.js.map +0 -1
  148. package/dist/common-properties-09ec10eb.js +0 -2
  149. package/dist/common-properties-09ec10eb.js.map +0 -1
  150. package/dist/common-properties-f1b96e0f.js +0 -2
  151. package/dist/common-properties-f1b96e0f.js.map +0 -1
  152. package/dist/en-2ed89528.js +0 -2
  153. package/dist/en-2ed89528.js.map +0 -1
  154. package/dist/en-e93855cc.js +0 -2
  155. package/dist/en-e93855cc.js.map +0 -1
  156. package/dist/extends-51d9ddcc.js +0 -7
  157. package/dist/extends-51d9ddcc.js.map +0 -1
  158. package/dist/flexible-table-154a3359.js +0 -2
  159. package/dist/flexible-table-154a3359.js.map +0 -1
  160. package/dist/flexible-table-5e4a1e6d.js +0 -2
  161. package/dist/flexible-table-5e4a1e6d.js.map +0 -1
  162. package/dist/getPrototypeOf-0bf77a9e.js +0 -2
  163. package/dist/getPrototypeOf-0bf77a9e.js.map +0 -1
  164. package/dist/getPrototypeOf-97272efe.js +0 -2
  165. package/dist/getPrototypeOf-97272efe.js.map +0 -1
  166. package/dist/index-4fd90a14.js +0 -2
  167. package/dist/index-4fd90a14.js.map +0 -1
  168. package/dist/index-6d138021.js +0 -2
  169. package/dist/index-6d138021.js.map +0 -1
  170. package/dist/isArrayLikeObject-31e5e646.js +0 -2
  171. package/dist/isArrayLikeObject-31e5e646.js.map +0 -1
  172. package/dist/isArrayLikeObject-7c33c7fd.js +0 -2
  173. package/dist/isArrayLikeObject-7c33c7fd.js.map +0 -1
@@ -17,7 +17,6 @@
17
17
  /* eslint no-invalid-this: "off" */
18
18
  /* eslint brace-style: "off" */
19
19
  /* eslint no-lonely-if: "off" */
20
- /* eslint max-depth: "off" */
21
20
 
22
21
  // Import just the D3 modules that are needed and combine them as the 'd3' object.
23
22
  import * as d3Drag from "d3-drag";
@@ -27,7 +26,7 @@ import * as d3Fetch from "d3-fetch";
27
26
  import * as d3Zoom from "./d3-zoom-extension/src";
28
27
  const d3 = Object.assign({}, d3Drag, d3Ease, d3Selection, d3Fetch, d3Zoom);
29
28
 
30
- import { cloneDeep, escape as escapeText, get, set,
29
+ import { cloneDeep, escape as escapeText, forOwn, get,
31
30
  unescape as unescapeText } from "lodash";
32
31
  import { ASSOC_RIGHT_SIDE_CURVE, ASSOCIATION_LINK, NODE_LINK, COMMENT_LINK,
33
32
  ASSOC_VAR_CURVE_LEFT, ASSOC_VAR_CURVE_RIGHT, ASSOC_VAR_DOUBLE_BACK_RIGHT,
@@ -39,12 +38,12 @@ import { ASSOC_RIGHT_SIDE_CURVE, ASSOCIATION_LINK, NODE_LINK, COMMENT_LINK,
39
38
  TIP_TYPE_NODE, TIP_TYPE_PORT, TIP_TYPE_DEC, TIP_TYPE_LINK,
40
39
  INTERACTION_MOUSE, INTERACTION_TRACKPAD,
41
40
  USE_DEFAULT_ICON, USE_DEFAULT_EXT_ICON,
42
- SUPER_NODE, SNAP_TO_GRID_AFTER, SNAP_TO_GRID_DURING }
41
+ SUPER_NODE, SNAP_TO_GRID_AFTER, SNAP_TO_GRID_DURING,
42
+ NORTH, SOUTH, EAST, WEST }
43
43
  from "./constants/canvas-constants";
44
44
  import SUPERNODE_ICON from "../../assets/images/supernode.svg";
45
45
  import SUPERNODE_EXT_ICON from "../../assets/images/supernode_ext.svg";
46
46
  import Logger from "../logging/canvas-logger.js";
47
- import LocalStorage from "./local-storage.js";
48
47
  import CanvasUtils from "./common-canvas-utils.js";
49
48
  import SvgCanvasDisplay from "./svg-canvas-utils-display.js";
50
49
  import SvgCanvasNodes from "./svg-canvas-utils-nodes.js";
@@ -111,11 +110,6 @@ export default class SVGCanvasRenderer {
111
110
  this.minScaleExtent = 0.2;
112
111
  this.maxScaleExtent = 1.8;
113
112
 
114
- // Allow us to keep track of the object (node or comment) being sized and
115
- // its initial size and position at the start of the sizing event.
116
- this.resizeObj = null;
117
- this.resizeObjInitialInfo = null;
118
-
119
113
  // Allows us to track the sizing behavior of comments
120
114
  this.commentSizing = false;
121
115
  this.commentSizingDirection = "";
@@ -124,9 +118,14 @@ export default class SVGCanvasRenderer {
124
118
  this.nodeSizing = false;
125
119
  this.nodeSizingDirection = "";
126
120
  this.nodeSizingObjectsInfo = [];
127
- this.nodeSizingLinksInfo = [];
121
+ this.nodeSizingDetLinksInfo = [];
122
+
123
+ // Keeps track of the size and position, at the start of the sizing event,
124
+ // of the object (node or comment) being sized.
125
+ this.resizeObjInitialInfo = null;
128
126
 
129
- // General purpose variables to allow us to handle resize and snap to grid
127
+ // Keeps track of the size and position, during a sizing event, of the
128
+ // object (node or comment) being sized, before it is snapped to grid.
130
129
  this.notSnappedXPos = 0;
131
130
  this.notSnappedYPos = 0;
132
131
  this.notSnappedWidth = 0;
@@ -255,16 +254,12 @@ export default class SVGCanvasRenderer {
255
254
  this.addBackToParentFlowArrow(this.canvasSVG);
256
255
  }
257
256
 
258
- // If we are showing a sub-flow in full screen mode, zoom it to fit the
259
- // screen so it looks similar to the in-place sub-flow view unless there
260
- // is a saved zoom for this pipeline.
261
- if (this.dispUtils.isDisplayingSubFlowFullPage()) {
262
- if (!this.config.enableSaveZoom ||
263
- this.config.enableSaveZoom === "None" ||
264
- (this.config.enableSaveZoom === "LocalStorage" && !this.getSavedZoom()) ||
265
- (this.config.enableSaveZoom === "Pipelineflow" && !this.activePipeline.zoom)) {
266
- this.zoomToFit();
267
- }
257
+ // If we are showing a sub-flow in full screen mode and there
258
+ // is no saved zoom for this pipeline, zoom it to fit the
259
+ // screen so it looks similar to the in-place sub-flow view.
260
+ if (this.dispUtils.isDisplayingSubFlowFullPage() &&
261
+ !this.canvasController.getSavedZoom(this.pipelineId)) {
262
+ this.zoomToFit();
268
263
  }
269
264
 
270
265
  // If we are showing an in-place subflow make sure any already existing
@@ -624,6 +619,27 @@ export default class SVGCanvasRenderer {
624
619
  return this.dragging;
625
620
  }
626
621
 
622
+ // Returns true if the node should be resizeable. Expanded supernodes are
623
+ // always resizabele and all other nodes, except collapsed supernodes, are
624
+ // resizeable when enableResizableNodes is switched on.
625
+ isNodeResizable(node) {
626
+ if (!this.config.enableEditingActions ||
627
+ CanvasUtils.isSuperBindingNode(node) ||
628
+ CanvasUtils.isCollapsedSupernode(node) ||
629
+ (!this.config.enableResizableNodes && !CanvasUtils.isExpandedSupernode(node))) {
630
+ return false;
631
+ }
632
+ return true;
633
+ }
634
+
635
+ // Returns true if the node should have a resizing area. We should display
636
+ // a sizing area even for collapsed supernodes so it is available if/when
637
+ // the supernode is expanded
638
+ shouldDisplayNodeSizingArea(node) {
639
+ return !CanvasUtils.isSuperBindingNode(node) &&
640
+ (CanvasUtils.isSupernode(node) || this.config.enableResizableNodes);
641
+ }
642
+
627
643
  getAllNodeGroupsSelection() {
628
644
  return this.nodesLinksGrp.selectChildren(".d3-node-group");
629
645
  }
@@ -775,9 +791,16 @@ export default class SVGCanvasRenderer {
775
791
  const ghost = this.getGhostDimensions();
776
792
  const node = this.canvasController.convertNodeTemplate(nodeTemplate);
777
793
  node.layout = this.canvasController.getObjectModel().getNodeLayout();
778
- node.width = node.is_expanded ? node.expanded_width : ghost.width;
779
- node.height = node.is_expanded ? node.expanded_height : ghost.height;
780
-
794
+ if (node.is_expanded) {
795
+ node.width = node.expanded_width;
796
+ node.height = node.expanded_height;
797
+ } else if (node.isResized) {
798
+ node.width = node.resizeWidth;
799
+ node.height = node.resizeHeight;
800
+ } else {
801
+ node.width = ghost.width;
802
+ node.height = ghost.height;
803
+ }
781
804
  const nodeImage = this.getNodeImage(node);
782
805
  const nodeImageType = this.getImageType(nodeImage);
783
806
  const ghostDivSel = this.getGhostDivSel();
@@ -1551,18 +1574,7 @@ export default class SVGCanvasRenderer {
1551
1574
  // of 'save zoom' specified in the configuration and, if no saved zoom, was
1552
1575
  // provided pans the canvas area so it is always visible.
1553
1576
  restoreZoom() {
1554
- let newZoom = null;
1555
-
1556
- if (this.config.enableSaveZoom === "Pipelineflow" &&
1557
- this.activePipeline.zoom) {
1558
- newZoom = this.activePipeline.zoom;
1559
-
1560
- } else if (this.config.enableSaveZoom === "LocalStorage") {
1561
- const savedZoom = this.getSavedZoom();
1562
- if (savedZoom) {
1563
- newZoom = savedZoom;
1564
- }
1565
- }
1577
+ let newZoom = this.canvasController.getSavedZoom(this.pipelineId);
1566
1578
 
1567
1579
  // If there's no saved zoom, and enablePanIntoViewOnOpen is set, pan so
1568
1580
  // the canvas area (containing nodes and comments) is visible in the viewport.
@@ -1605,24 +1617,6 @@ export default class SVGCanvasRenderer {
1605
1617
  }
1606
1618
  }
1607
1619
 
1608
- // Saves the zoom object passed in for this pipeline in local storage.
1609
- setSavedZoom(zoom) {
1610
- const canvasSavedZoomValues = LocalStorage.get("canvasSavedZoomValues");
1611
- const savedZooms = canvasSavedZoomValues ? JSON.parse(canvasSavedZoomValues) : {};
1612
- set(savedZooms, [this.canvasInfo.id, this.pipelineId], zoom);
1613
- LocalStorage.set("canvasSavedZoomValues", JSON.stringify(savedZooms));
1614
- }
1615
-
1616
- // Returns the zoom for this pipeline saved in local storage
1617
- getSavedZoom() {
1618
- const canvasSavedZoomValues = LocalStorage.get("canvasSavedZoomValues");
1619
- if (canvasSavedZoomValues) {
1620
- const savedZoom = JSON.parse(canvasSavedZoomValues);
1621
- return get(savedZoom, [this.canvasInfo.id, this.pipelineId]);
1622
- }
1623
- return null;
1624
- }
1625
-
1626
1620
  zoomToFit() {
1627
1621
  const padding = this.getZoomToFitPadding();
1628
1622
  const canvasDimensions = this.getCanvasDimensionsAdjustedForScale(1, padding);
@@ -1688,6 +1682,14 @@ export default class SVGCanvasRenderer {
1688
1682
  }
1689
1683
  }
1690
1684
 
1685
+ isZoomedToMax() {
1686
+ return this.zoomTransform ? this.zoomTransform.k === this.maxScaleExtent : false;
1687
+ }
1688
+
1689
+ isZoomedToMin() {
1690
+ return this.zoomTransform ? this.zoomTransform.k === this.minScaleExtent : false;
1691
+ }
1692
+
1691
1693
  getZoomToReveal(nodeIDs, xPos, yPos) {
1692
1694
  const transformedSVGRect = this.getTransformedViewportDimensions();
1693
1695
  const nodes = this.activePipeline.getNodes(nodeIDs);
@@ -1882,24 +1884,18 @@ export default class SVGCanvasRenderer {
1882
1884
  this.canvasController.setSelections(selections, this.activePipeline.id);
1883
1885
  this.regionSelect = false;
1884
1886
 
1885
- } else if (this.dispUtils.isDisplayingFullPage()) {
1887
+ } else if (this.dispUtils.isDisplayingFullPage() && this.zoomChanged()) {
1886
1888
  // Set the internal zoom value for canvasSVG used by D3. This will be
1887
1889
  // used by d3Event next time a zoom action is initiated.
1888
1890
  this.canvasSVG.property("__zoom", this.zoomTransform);
1889
1891
 
1890
-
1891
- if (this.config.enableSaveZoom === "Pipelineflow") {
1892
- const data = {
1893
- editType: "setPipelineZoom",
1894
- editSource: "canvas",
1895
- zoom: this.zoomTransform,
1896
- pipelineId: this.activePipeline.id
1897
- };
1898
- this.canvasController.editActionHandler(data);
1899
-
1900
- } else if (this.config.enableSaveZoom === "LocalStorage") {
1901
- this.setSavedZoom(this.zoomTransform);
1902
- }
1892
+ const data = {
1893
+ editType: "setZoom",
1894
+ editSource: "canvas",
1895
+ zoom: this.zoomTransform,
1896
+ pipelineId: this.activePipeline.id
1897
+ };
1898
+ this.canvasController.editActionHandler(data);
1903
1899
  }
1904
1900
 
1905
1901
  // Remove the cursor overlay and reset the SVG background rectangle
@@ -1908,6 +1904,14 @@ export default class SVGCanvasRenderer {
1908
1904
  this.removeTempCursorOverlay();
1909
1905
  }
1910
1906
 
1907
+ // Returns true if the current zoom transform is different from the
1908
+ // zoom values at the beginning of the zoom action.
1909
+ zoomChanged() {
1910
+ return (this.zoomTransform.k !== this.zoomStartPoint.k ||
1911
+ this.zoomTransform.x !== this.zoomStartPoint.x ||
1912
+ this.zoomTransform.y !== this.zoomStartPoint.y);
1913
+ }
1914
+
1911
1915
  zoomCanvasBackground(d3Event) {
1912
1916
  this.regionSelect = false;
1913
1917
 
@@ -2090,12 +2094,12 @@ export default class SVGCanvasRenderer {
2090
2094
 
2091
2095
  // Note: Comment and Node resizing is started by the comment/node highlight rectangle.
2092
2096
  if (this.commentSizing) {
2093
- this.resizeObj = this.activePipeline.getComment(d.id);
2094
- this.initializeResizeVariables(this.resizeObj);
2097
+ const resizeObj = this.activePipeline.getComment(d.id);
2098
+ this.initializeResizeVariables(resizeObj);
2095
2099
 
2096
2100
  } else if (this.nodeSizing) {
2097
- this.resizeObj = this.activePipeline.getNode(d.id);
2098
- this.initializeResizeVariables(this.resizeObj);
2101
+ const resizeObj = this.activePipeline.getNode(d.id);
2102
+ this.initializeResizeVariables(resizeObj);
2099
2103
 
2100
2104
  } else {
2101
2105
  this.dragObjectsStart(d3Event, d);
@@ -2103,12 +2107,12 @@ export default class SVGCanvasRenderer {
2103
2107
  this.logger.logEndTimer("dragStart", true);
2104
2108
  }
2105
2109
 
2106
- dragMove(d3Event) {
2110
+ dragMove(d3Event, d) {
2107
2111
  this.logger.logStartTimer("dragMove");
2108
2112
  if (this.commentSizing) {
2109
- this.resizeComment(d3Event);
2113
+ this.resizeComment(d3Event, d);
2110
2114
  } else if (this.nodeSizing) {
2111
- this.resizeNode(d3Event);
2115
+ this.resizeNode(d3Event, d);
2112
2116
  } else {
2113
2117
  this.dragObjectsAction(d3Event);
2114
2118
  }
@@ -2122,10 +2126,10 @@ export default class SVGCanvasRenderer {
2122
2126
  this.removeTempCursorOverlay();
2123
2127
 
2124
2128
  if (this.commentSizing) {
2125
- this.endCommentSizing();
2129
+ this.endCommentSizing(d);
2126
2130
 
2127
2131
  } else if (this.nodeSizing) {
2128
- this.endNodeSizing();
2132
+ this.endNodeSizing(d);
2129
2133
 
2130
2134
  } else if (this.dragging) {
2131
2135
  this.dragObjectsEnd(d3Event, d);
@@ -2513,7 +2517,7 @@ export default class SVGCanvasRenderer {
2513
2517
  }
2514
2518
 
2515
2519
  // Node Sizing Area.
2516
- newNodeGroups.filter((d) => CanvasUtils.isSupernode(d))
2520
+ newNodeGroups.filter((d) => this.shouldDisplayNodeSizingArea(d))
2517
2521
  .append("path")
2518
2522
  .attr("class", "d3-node-sizing")
2519
2523
  .call(this.attachNodeSizingListeners.bind(this));
@@ -2862,7 +2866,7 @@ export default class SVGCanvasRenderer {
2862
2866
  attachNodeSizingListeners(nodeGrps) {
2863
2867
  nodeGrps
2864
2868
  .on("mousedown", (d3Event, d) => {
2865
- if (CanvasUtils.isExpandedSupernode(d)) {
2869
+ if (this.isNodeResizable(d)) {
2866
2870
  this.nodeSizing = true;
2867
2871
  // Note - node resizing and finalization of size is handled by drag functions.
2868
2872
  this.addTempCursorOverlay(this.nodeSizingCursor);
@@ -2875,8 +2879,7 @@ export default class SVGCanvasRenderer {
2875
2879
  // pointer leaves the temporary overlay (which is removed) and enters
2876
2880
  // the node outline.
2877
2881
  .on("mousemove mouseenter", (d3Event, d) => {
2878
- if (this.config.enableEditingActions && // Only set cursor when we are able to edit nodes
2879
- CanvasUtils.isExpandedSupernode(d) &&
2882
+ if (this.isNodeResizable(d) &&
2880
2883
  !this.isRegionSelectOrSizingInProgress()) { // Don't switch sizing direction if we are already sizing
2881
2884
  let cursorType = "pointer";
2882
2885
  if (!this.isPointerCloseToBodyEdge(d3Event, d)) {
@@ -2886,6 +2889,9 @@ export default class SVGCanvasRenderer {
2886
2889
  }
2887
2890
  d3.select(d3Event.currentTarget).style("cursor", cursorType);
2888
2891
  }
2892
+ })
2893
+ .on("mouseleave", (d3Event, d) => {
2894
+ d3.select(d3Event.currentTarget).style("cursor", "pointer");
2889
2895
  });
2890
2896
  }
2891
2897
 
@@ -4688,7 +4694,7 @@ export default class SVGCanvasRenderer {
4688
4694
  }
4689
4695
 
4690
4696
  // Returns a path that will draw the shape for the rectangle node
4691
- // display. This is draw as a path rather than an SVG rectangle to make the
4697
+ // display. This is drawn as a path rather than an SVG rectangle to make the
4692
4698
  // calling code more generic.
4693
4699
  getRectangleNodeShapePath(data, highlightGap) {
4694
4700
  const gap = highlightGap ? highlightGap : 0;
@@ -5638,27 +5644,50 @@ export default class SVGCanvasRenderer {
5638
5644
  return cursorType;
5639
5645
  }
5640
5646
 
5647
+ // Returns the minimum allowed height for the node passed in. For supernodes
5648
+ // this means combining the bigger of the space for the inputs and output ports
5649
+ // with some space for the top of the display frame and the padding at the
5650
+ // bottom of the frame. Then the bigger of that height versus the default
5651
+ // supernode minimum height is retunred.
5652
+ getMinHeight(node) {
5653
+ if (CanvasUtils.isSupernode(node)) {
5654
+ const minHt = Math.max(node.inputPortsHeight, node.outputPortsHeight) +
5655
+ this.canvasLayout.supernodeTopAreaHeight + this.canvasLayout.supernodeSVGAreaPadding;
5656
+ return Math.max(this.canvasLayout.supernodeMinHeight, minHt);
5657
+ }
5658
+ return node.layout.defaultNodeHeight;
5659
+ }
5660
+
5661
+ // Returns the minimum allowed width for the node passed in.
5662
+ getMinWidth(node) {
5663
+ if (CanvasUtils.isSupernode(node)) {
5664
+ return this.canvasLayout.supernodeMinWidth;
5665
+ }
5666
+ return node.layout.defaultNodeWidth;
5667
+ }
5668
+
5641
5669
  // Sets the size and position of the node in the canvasInfo.nodes
5642
5670
  // array based on the position of the pointer during the resize action
5643
5671
  // then redraws the nodes and links (the link positions may move based
5644
5672
  // on the node size change).
5645
- resizeNode(d3Event) {
5646
- const oldSupernode = Object.assign({}, this.resizeObj);
5647
- const minSupernodeHeight = Math.max(this.resizeObj.inputPortsHeight, this.resizeObj.outputPortsHeight) +
5648
- this.canvasLayout.supernodeTopAreaHeight + this.canvasLayout.supernodeSVGAreaPadding;
5673
+ resizeNode(d3Event, d) {
5674
+ const resizeObj = this.activePipeline.getNode(d.id);
5675
+ const oldSupernode = Object.assign({}, resizeObj);
5676
+ const minHeight = this.getMinHeight(resizeObj);
5677
+ const minWidth = this.getMinWidth(resizeObj);
5649
5678
 
5650
- const delta = this.resizeObject(d3Event, this.resizeObj,
5651
- this.nodeSizingDirection, this.canvasLayout.supernodeMinWidth,
5652
- Math.max(this.canvasLayout.supernodeMinHeight, minSupernodeHeight));
5679
+ const delta = this.resizeObject(d3Event, resizeObj,
5680
+ this.nodeSizingDirection, minWidth, minHeight);
5653
5681
 
5654
5682
  if (delta && (delta.x_pos !== 0 || delta.y_pos !== 0 || delta.width !== 0 || delta.height !== 0)) {
5655
- if (this.config.enableMoveNodesOnSupernodeResize) {
5683
+ if (CanvasUtils.isSupernode(resizeObj) &&
5684
+ this.config.enableMoveNodesOnSupernodeResize) {
5656
5685
  const objectsInfo = CanvasUtils.moveSurroundingObjects(
5657
5686
  oldSupernode,
5658
5687
  this.activePipeline.getNodesAndComments(),
5659
5688
  this.nodeSizingDirection,
5660
- this.resizeObj.width,
5661
- this.resizeObj.height,
5689
+ resizeObj.width,
5690
+ resizeObj.height,
5662
5691
  true // Pass true to indicate that object positions should be updated.
5663
5692
  );
5664
5693
 
@@ -5666,22 +5695,26 @@ export default class SVGCanvasRenderer {
5666
5695
  oldSupernode,
5667
5696
  this.activePipeline.links,
5668
5697
  this.nodeSizingDirection,
5669
- this.resizeObj.width,
5670
- this.resizeObj.height,
5698
+ resizeObj.width,
5699
+ resizeObj.height,
5671
5700
  true // Pass true to indicate that link positions should be updated.
5672
5701
  );
5673
5702
 
5674
5703
  // Overwrite the object and link info with any new info.
5675
5704
  this.nodeSizingObjectsInfo = Object.assign(this.nodeSizingObjectsInfo, objectsInfo);
5676
- this.nodeSizingLinksInfo = Object.assign(this.nodeSizingLinksInfo, linksInfo);
5705
+ this.nodeSizingDetLinksInfo = Object.assign(this.nodeSizingDetLinksInfo, linksInfo);
5677
5706
  }
5707
+
5678
5708
  this.displayComments();
5679
5709
  this.displayNodes();
5680
5710
  this.displayLinks();
5681
- if (this.dispUtils.isDisplayingSubFlow()) {
5682
- this.displayBindingNodesToFitSVG();
5711
+
5712
+ if (CanvasUtils.isSupernode(resizeObj)) {
5713
+ if (this.dispUtils.isDisplayingSubFlow()) {
5714
+ this.displayBindingNodesToFitSVG();
5715
+ }
5716
+ this.superRenderers.forEach((renderer) => renderer.displaySVGToFitSupernode());
5683
5717
  }
5684
- this.superRenderers.forEach((renderer) => renderer.displaySVGToFitSupernode());
5685
5718
  }
5686
5719
  }
5687
5720
 
@@ -5689,8 +5722,9 @@ export default class SVGCanvasRenderer {
5689
5722
  // array based on the position of the pointer during the resize action
5690
5723
  // then redraws the comment and links (the link positions may move based
5691
5724
  // on the comment size change).
5692
- resizeComment(d3Event) {
5693
- this.resizeObject(d3Event, this.resizeObj, this.commentSizingDirection, 20, 20);
5725
+ resizeComment(d3Event, d) {
5726
+ const resizeObj = this.activePipeline.getComment(d.id);
5727
+ this.resizeObject(d3Event, resizeObj, this.commentSizingDirection, 20, 20);
5694
5728
  this.displayComments();
5695
5729
  this.displayLinks();
5696
5730
  }
@@ -5768,63 +5802,76 @@ export default class SVGCanvasRenderer {
5768
5802
 
5769
5803
  // Finalises the sizing of a node by calling editActionHandler
5770
5804
  // with an editNode action.
5771
- endNodeSizing() {
5805
+ endNodeSizing(node) {
5806
+ let resizeObj = this.activePipeline.getNode(node.id);
5772
5807
  if (this.config.enableSnapToGridType === SNAP_TO_GRID_AFTER) {
5773
- this.resizeObj = this.snapToGridObject(this.resizeObj);
5808
+ resizeObj = this.snapToGridObject(resizeObj);
5774
5809
  }
5775
5810
 
5776
5811
  // If the dimensions or position has changed, issue the command.
5777
5812
  // Note: x_pos or y_pos might change on resize if the node is sized
5778
5813
  // upwards or to the left.
5779
- if (this.resizeObjInitialInfo.x_pos !== this.resizeObj.x_pos ||
5780
- this.resizeObjInitialInfo.y_pos !== this.resizeObj.y_pos ||
5781
- this.resizeObjInitialInfo.width !== this.resizeObj.width ||
5782
- this.resizeObjInitialInfo.height !== this.resizeObj.height) {
5814
+ if (this.resizeObjInitialInfo.x_pos !== resizeObj.x_pos ||
5815
+ this.resizeObjInitialInfo.y_pos !== resizeObj.y_pos ||
5816
+ this.resizeObjInitialInfo.width !== resizeObj.width ||
5817
+ this.resizeObjInitialInfo.height !== resizeObj.height) {
5783
5818
  // Add the dimensions of the object being resized to the array of object infos.
5784
- this.nodeSizingObjectsInfo[this.resizeObj.id] = {
5785
- width: this.resizeObj.width,
5786
- height: this.resizeObj.height,
5787
- x_pos: this.resizeObj.x_pos,
5788
- y_pos: this.resizeObj.y_pos
5819
+ this.nodeSizingObjectsInfo[resizeObj.id] = {
5820
+ width: resizeObj.width,
5821
+ height: resizeObj.height,
5822
+ x_pos: resizeObj.x_pos,
5823
+ y_pos: resizeObj.y_pos
5789
5824
  };
5790
5825
 
5826
+ // If the node has been resized set the resize properties appropriately.
5827
+ if (this.resizeObjInitialInfo.width !== resizeObj.width ||
5828
+ this.resizeObjInitialInfo.height !== resizeObj.height) {
5829
+ this.nodeSizingObjectsInfo[resizeObj.id].isResized = true;
5830
+ this.nodeSizingObjectsInfo[resizeObj.id].resizeWidth = resizeObj.width;
5831
+ this.nodeSizingObjectsInfo[resizeObj.id].resizeHeight = resizeObj.height;
5832
+ }
5833
+
5791
5834
  this.canvasController.editActionHandler({
5792
5835
  editType: "resizeObjects",
5793
5836
  editSource: "canvas",
5794
5837
  objectsInfo: this.nodeSizingObjectsInfo,
5795
- linksInfo: this.nodeSizingLinksInfo,
5838
+ detachedLinksInfo: this.nodeSizingDetLinksInfo,
5796
5839
  pipelineId: this.pipelineId
5797
5840
  });
5798
5841
  }
5799
- this.resizeObj = null;
5800
5842
  this.nodeSizing = false;
5801
5843
  this.nodeSizingObjectsInfo = [];
5802
- this.nodeSizingLinksInfo = [];
5844
+ this.nodeSizingDetLinksInfo = [];
5803
5845
  }
5804
5846
 
5805
5847
  // Finalises the sizing of a comment by calling editActionHandler
5806
5848
  // with an editComment action.
5807
- endCommentSizing() {
5849
+ endCommentSizing(comment) {
5850
+ let resizeObj = this.activePipeline.getComment(comment.id);
5808
5851
  if (this.config.enableSnapToGridType === SNAP_TO_GRID_AFTER) {
5809
- this.resizeObj = this.snapToGridObject(this.resizeObj);
5852
+ resizeObj = this.snapToGridObject(resizeObj);
5810
5853
  }
5811
5854
 
5812
5855
  // If the dimensions or position has changed, issue the command.
5813
5856
  // Note: x_pos or y_pos might change on resize if the node is sized
5814
5857
  // upwards or to the left.
5815
- if (this.resizeObjInitialInfo.x_pos !== this.resizeObj.x_pos ||
5816
- this.resizeObjInitialInfo.y_pos !== this.resizeObj.y_pos ||
5817
- this.resizeObjInitialInfo.width !== this.resizeObj.width ||
5818
- this.resizeObjInitialInfo.height !== this.resizeObj.height) {
5858
+ if (this.resizeObjInitialInfo.x_pos !== resizeObj.x_pos ||
5859
+ this.resizeObjInitialInfo.y_pos !== resizeObj.y_pos ||
5860
+ this.resizeObjInitialInfo.width !== resizeObj.width ||
5861
+ this.resizeObjInitialInfo.height !== resizeObj.height) {
5862
+ const commentSizingObjectsInfo = [];
5863
+ commentSizingObjectsInfo[resizeObj.id] = {
5864
+ width: resizeObj.width,
5865
+ height: resizeObj.height,
5866
+ x_pos: resizeObj.x_pos,
5867
+ y_pos: resizeObj.y_pos
5868
+ };
5869
+
5819
5870
  const data = {
5820
- editType: "editComment",
5871
+ editType: "resizeObjects",
5821
5872
  editSource: "canvas",
5822
- id: this.resizeObj.id,
5823
- content: this.resizeObj.content,
5824
- width: this.resizeObj.width,
5825
- height: this.resizeObj.height,
5826
- x_pos: this.resizeObj.x_pos,
5827
- y_pos: this.resizeObj.y_pos,
5873
+ objectsInfo: commentSizingObjectsInfo,
5874
+ detachedLinksInfo: {}, // Comments cannot have detached links
5828
5875
  pipelineId: this.pipelineId
5829
5876
  };
5830
5877
  this.canvasController.editActionHandler(data);
@@ -6278,6 +6325,11 @@ export default class SVGCanvasRenderer {
6278
6325
 
6279
6326
  buildLinksArray() {
6280
6327
  let linksArray = [];
6328
+
6329
+ if (this.canvasLayout.linkType === LINK_TYPE_STRAIGHT) {
6330
+ this.updateLinksForNodes();
6331
+ }
6332
+
6281
6333
  this.activePipeline.links.forEach((link) => {
6282
6334
  let linkObj = null;
6283
6335
 
@@ -6331,7 +6383,7 @@ export default class SVGCanvasRenderer {
6331
6383
  link.type === ASSOCIATION_LINK && this.config.enableAssocLinkType === ASSOC_RIGHT_SIDE_CURVE
6332
6384
  ? this.getAssocLinkVariation(srcObj, trgNode)
6333
6385
  : null;
6334
- const coords = this.linkUtils.getLinkCoords(link.type, srcObj, srcPortId, trgNode, trgPortId, assocLinkVariation);
6386
+ const coords = this.linkUtils.getLinkCoords(link, srcObj, srcPortId, trgNode, trgPortId, assocLinkVariation);
6335
6387
 
6336
6388
  link.assocLinkVariation = assocLinkVariation;
6337
6389
  link.srcPortId = srcPortId;
@@ -6364,7 +6416,7 @@ export default class SVGCanvasRenderer {
6364
6416
  } else {
6365
6417
  if (this.canvasLayout.linkType === LINK_TYPE_STRAIGHT) {
6366
6418
  const endPos = { x: link.trgPos.x_pos, y: link.trgPos.y_pos };
6367
- const startPos = this.linkUtils.getNewStraightNodeLinkStartPos(srcObj, endPos);
6419
+ const startPos = this.linkUtils.getNewStraightNodeLinkStartPos(srcObj, endPos, link.srcOriginInfo);
6368
6420
  coords.x1 = startPos.x;
6369
6421
  coords.y1 = startPos.y;
6370
6422
 
@@ -6385,7 +6437,7 @@ export default class SVGCanvasRenderer {
6385
6437
  } else {
6386
6438
  if (this.canvasLayout.linkType === LINK_TYPE_STRAIGHT) {
6387
6439
  const endPos = { x: link.srcPos.x_pos, y: link.srcPos.y_pos };
6388
- const startPos = this.linkUtils.getNewStraightNodeLinkStartPos(trgNode, endPos);
6440
+ const startPos = this.linkUtils.getNewStraightNodeLinkStartPos(trgNode, endPos, link.trgOriginInfo);
6389
6441
  coords.x2 = startPos.x;
6390
6442
  coords.y2 = startPos.y;
6391
6443
 
@@ -6564,6 +6616,184 @@ export default class SVGCanvasRenderer {
6564
6616
  });
6565
6617
  }
6566
6618
 
6619
+ // Updates the data links for all the nodes with two optional fields
6620
+ // (called srcOriginInfo and trgOriginInfo) based on the location of the
6621
+ // nodes the links go from and to. The info in these fields is used to
6622
+ // calculate the starting and ending position of straight line links.
6623
+ // This ensures that input and output links that go in a certain direction
6624
+ // (NORTH, SOUTH, EAST or WEST) are grouped together so they can be
6625
+ // separated out when straight lines are drawn between nodes.
6626
+ updateLinksForNodes() {
6627
+ this.activePipeline.nodes.forEach((n) => this.updateLinksForNode(n));
6628
+ }
6629
+
6630
+ // Updates the links going into and out of the node passed in with up to
6631
+ // two new fields (called srcOriginInfo and trgOriginInfo).
6632
+ updateLinksForNode(node) {
6633
+ const linksInfo = {};
6634
+ linksInfo.n = [];
6635
+ linksInfo.s = [];
6636
+ linksInfo.e = [];
6637
+ linksInfo.w = [];
6638
+
6639
+ // Update the linksInfo arrays for each direction: n, s, e and w.
6640
+ this.activePipeline.links.forEach((link) => {
6641
+ if (link.type === NODE_LINK) {
6642
+ if (link.trgNode && link.trgNode.id === node.id) {
6643
+ if (link.srcObj) {
6644
+ const dir = this.getDirAdjusted(link.trgNode, link.srcObj);
6645
+ linksInfo[dir].push({ type: "in", endNode: link.srcObj, link });
6646
+
6647
+ } else if (link.srcPos) {
6648
+ const dir = this.getDirToEndPos(link.trgNode, link.srcPos.x_pos, link.srcPos.y_pos);
6649
+ linksInfo[dir].push({ type: "in", x: link.srcPos.x_pos, y: link.srcPos.y_pos, link });
6650
+ }
6651
+
6652
+ } else if (link.srcObj && link.srcObj.id === node.id) {
6653
+ if (link.trgNode) {
6654
+ const dir = this.getDirAdjusted(link.srcObj, link.trgNode);
6655
+ linksInfo[dir].push({ type: "out", endNode: link.trgNode, link });
6656
+
6657
+ } else if (link.trgPos) {
6658
+ const dir = this.getDirToEndPos(link.srcObj, link.trgPos.x_pos, link.trgPos.y_pos);
6659
+ linksInfo[dir].push({ type: "out", x: link.trgPos.x_pos, y: link.trgPos.y_pos, link });
6660
+ }
6661
+ }
6662
+ }
6663
+ });
6664
+
6665
+ linksInfo.n = this.sortLinksInfo(linksInfo.n, NORTH);
6666
+ linksInfo.s = this.sortLinksInfo(linksInfo.s, SOUTH);
6667
+ linksInfo.e = this.sortLinksInfo(linksInfo.e, EAST);
6668
+ linksInfo.w = this.sortLinksInfo(linksInfo.w, WEST);
6669
+
6670
+ this.updateLinksInfo(linksInfo.n, NORTH);
6671
+ this.updateLinksInfo(linksInfo.s, SOUTH);
6672
+ this.updateLinksInfo(linksInfo.e, EAST);
6673
+ this.updateLinksInfo(linksInfo.w, WEST);
6674
+ }
6675
+
6676
+ // Returns the direction of a link from the start node to the end node
6677
+ // as either NORTH, SOUTH, EAST or WEST. Some direction combinations
6678
+ // have to be overriden to prevent link lines overlapping.
6679
+ getDirAdjusted(startNode, endNode) {
6680
+ let dir = this.getDirToNode(startNode, endNode);
6681
+
6682
+ // When start -> end is SOUTH and end -> start is WEST the returned direction
6683
+ // becomes EAST instead of SOUTH to prevent link lines overlapping.
6684
+ if (dir === SOUTH) {
6685
+ const dir2 = this.getDirToNode(endNode, startNode);
6686
+ if (dir2 === WEST) {
6687
+ dir = EAST;
6688
+ }
6689
+
6690
+ // When start -> end is NORTH and end -> start is EAST the returned direction
6691
+ // becomes WEST instead of NORTH to prevent link lines overlapping.
6692
+ } else if (dir === NORTH) {
6693
+ const dir2 = this.getDirToNode(endNode, startNode);
6694
+ if (dir2 === EAST) {
6695
+ dir = WEST;
6696
+ }
6697
+ }
6698
+ return dir;
6699
+ }
6700
+
6701
+ // Returns the direction (NORTH, SOUTH, EAST or WEST) from the start node
6702
+ // to the end node.
6703
+ getDirToNode(startNode, endNode) {
6704
+ const endX = this.nodeUtils.getNodeCenterPosX(endNode);
6705
+ const endY = this.nodeUtils.getNodeCenterPosY(endNode);
6706
+ return this.getDirToEndPos(startNode, endX, endY);
6707
+ }
6708
+
6709
+ // Returns the direction (NORTH, SOUTH, EAST or WEST) from the start node
6710
+ // to the end position endX, endY.
6711
+ getDirToEndPos(startNode, endX, endY) {
6712
+ const originX = this.nodeUtils.getNodeCenterPosX(startNode);
6713
+ const originY = this.nodeUtils.getNodeCenterPosY(startNode);
6714
+
6715
+ const x = startNode.x_pos;
6716
+ const y = startNode.y_pos;
6717
+ const w = startNode.width;
6718
+ const h = startNode.height;
6719
+
6720
+ return CanvasUtils.getDir(x, y, w, h, originX, originY, endX, endY);
6721
+ }
6722
+
6723
+ // Returns the linksDirArray passed in with the linkInfo objects in the
6724
+ // array ordered by the position of the end of each link line, depending on
6725
+ // the direction (dir) of the lines. This is achieved by spliting the links
6726
+ // into groups where links in the same group go to/from the same node.
6727
+ sortLinksInfo(linksDirArrayIn, dir) {
6728
+ let linksDirArray = linksDirArrayIn;
6729
+ if (linksDirArray.length > 1) {
6730
+ const groups = this.getLinkInfoGroups(linksDirArray);
6731
+
6732
+ forOwn(groups, (group) => {
6733
+ group.forEach((li, i) => {
6734
+ const node = li.endNode;
6735
+ const parts = group.length + 1;
6736
+
6737
+ if (dir === NORTH || dir === SOUTH) {
6738
+ li.x = node.x_pos + ((node.width / parts) * (i + 1));
6739
+ li.y = this.nodeUtils.getNodeCenterPosY(node);
6740
+ } else {
6741
+ li.x = this.nodeUtils.getNodeCenterPosX(node);
6742
+ li.y = node.y_pos + ((node.height / parts) * (i + 1));
6743
+ }
6744
+ });
6745
+ });
6746
+
6747
+ // For NORTH and SOUTH links we sort linksDirArray by the x co-ordinate
6748
+ // of the end of each link. For EAST and WEST we sort by the y
6749
+ // co-ordinate.
6750
+ if (dir === NORTH || dir === SOUTH) {
6751
+ linksDirArray = linksDirArray.sort((a, b) => (a.x > b.x ? 1 : -1));
6752
+ } else {
6753
+ linksDirArray = linksDirArray.sort((a, b) => (a.y > b.y ? 1 : -1));
6754
+ }
6755
+ }
6756
+ return linksDirArray;
6757
+ }
6758
+
6759
+ // Returns a 'groups' object where each field is index by a node ID and
6760
+ // contains an array of linkInfo objects that go to/from the node.
6761
+ getLinkInfoGroups(linksDirArray) {
6762
+ const groups = {};
6763
+ linksDirArray.forEach((li) => {
6764
+ // Only create groups for attached links.
6765
+ if (li.endNode) {
6766
+ if (!groups[li.endNode.id]) {
6767
+ groups[li.endNode.id] = [];
6768
+ }
6769
+ groups[li.endNode.id].push(li);
6770
+ }
6771
+ });
6772
+ return groups;
6773
+ }
6774
+
6775
+ // Updates the link objects referenced by the linkInfo objects in the
6776
+ // linksDirArray with info to specify the link direction (n, s, e or w),
6777
+ // plus the index and number of connections. This is used when
6778
+ // drawing straight lines to/from nodes to spread out the lines.
6779
+ updateLinksInfo(linksDirArray, dir) {
6780
+ linksDirArray.forEach((li, i) => {
6781
+ if (li.type === "out") {
6782
+ li.link.srcOriginInfo = {
6783
+ dir: dir,
6784
+ idx: i,
6785
+ len: linksDirArray.length
6786
+ };
6787
+ } else {
6788
+ li.link.trgOriginInfo = {
6789
+ dir: dir,
6790
+ idx: i,
6791
+ len: linksDirArray.length
6792
+ };
6793
+ }
6794
+ });
6795
+ }
6796
+
6567
6797
  // Returns a variation of association link to draw when a new link is being
6568
6798
  // drawn outwards from a port. startX is the beginning point of the line
6569
6799
  // at the port. endX is the position where the mouse is currently positioned.