@elyra/canvas 12.15.0 → 12.16.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.
Files changed (70) hide show
  1. package/dist/canvas-controller-c420f377.js +2 -0
  2. package/dist/canvas-controller-c420f377.js.map +1 -0
  3. package/dist/canvas-controller-e02a5de5.js +2 -0
  4. package/dist/canvas-controller-e02a5de5.js.map +1 -0
  5. package/dist/common-canvas-51ed78a5.js +2 -0
  6. package/dist/common-canvas-51ed78a5.js.map +1 -0
  7. package/dist/common-canvas-d0230688.js +2 -0
  8. package/dist/common-canvas-d0230688.js.map +1 -0
  9. package/dist/common-canvas.es.js +1 -1
  10. package/dist/common-canvas.js +1 -1
  11. package/dist/{common-properties-86de4c9f.js → common-properties-0a697e6a.js} +2 -2
  12. package/dist/{common-properties-86de4c9f.js.map → common-properties-0a697e6a.js.map} +1 -1
  13. package/dist/{common-properties-9e579309.js → common-properties-233593d2.js} +2 -2
  14. package/dist/{common-properties-9e579309.js.map → common-properties-233593d2.js.map} +1 -1
  15. package/dist/flexible-table-0c7ae548.js +2 -0
  16. package/dist/flexible-table-0c7ae548.js.map +1 -0
  17. package/dist/flexible-table-60c73b88.js +2 -0
  18. package/dist/flexible-table-60c73b88.js.map +1 -0
  19. package/dist/{index-669f95a7.js → index-390d8148.js} +2 -2
  20. package/dist/{index-669f95a7.js.map → index-390d8148.js.map} +1 -1
  21. package/dist/{index-6d3404e1.js → index-e6d8ea9c.js} +2 -2
  22. package/dist/{index-6d3404e1.js.map → index-e6d8ea9c.js.map} +1 -1
  23. package/dist/lib/canvas-controller.es.js +1 -1
  24. package/dist/lib/canvas-controller.js +1 -1
  25. package/dist/lib/canvas.es.js +1 -1
  26. package/dist/lib/canvas.js +1 -1
  27. package/dist/lib/properties/field-picker.es.js +1 -1
  28. package/dist/lib/properties/field-picker.js +1 -1
  29. package/dist/lib/properties/flexible-table.es.js +1 -1
  30. package/dist/lib/properties/flexible-table.js +1 -1
  31. package/dist/lib/properties.es.js +1 -1
  32. package/dist/lib/properties.js +1 -1
  33. package/dist/styles/common-canvas.min.css +1 -1
  34. package/dist/styles/common-canvas.min.css.map +1 -1
  35. package/locales/command-actions/locales/index.js +3 -1
  36. package/locales/command-actions/locales/sv.json +51 -0
  37. package/locales/common-canvas/locales/index.js +3 -1
  38. package/locales/common-canvas/locales/sv.json +37 -0
  39. package/locales/common-properties/locales/index.js +3 -1
  40. package/locales/common-properties/locales/sv.json +93 -0
  41. package/locales/palette/locales/index.js +3 -1
  42. package/locales/palette/locales/sv.json +10 -0
  43. package/locales/toolbar/locales/index.js +3 -1
  44. package/locales/toolbar/locales/sv.json +8 -0
  45. package/package.json +1 -1
  46. package/src/command-actions/createSuperNodeAction.js +358 -268
  47. package/src/common-canvas/canvas-controller.js +7 -3
  48. package/src/common-canvas/common-canvas-utils.js +1 -1
  49. package/src/common-canvas/svg-canvas-renderer.js +44 -19
  50. package/src/common-canvas/svg-canvas-utils-links.js +5 -1
  51. package/src/common-canvas/svg-canvas-utils-nodes.js +23 -0
  52. package/src/common-properties/components/virtualized-table/virtualized-table.jsx +5 -5
  53. package/src/common-properties/controls/radioset/radioset.jsx +12 -10
  54. package/src/common-properties/controls/radioset/radioset.scss +13 -11
  55. package/src/object-model/api-pipeline.js +19 -6
  56. package/src/object-model/config-utils.js +1 -0
  57. package/src/object-model/layout-dimensions.js +5 -3
  58. package/stats.html +1 -1
  59. package/dist/canvas-controller-720a509c.js +0 -2
  60. package/dist/canvas-controller-720a509c.js.map +0 -1
  61. package/dist/canvas-controller-73113a1b.js +0 -2
  62. package/dist/canvas-controller-73113a1b.js.map +0 -1
  63. package/dist/common-canvas-21b6ab50.js +0 -2
  64. package/dist/common-canvas-21b6ab50.js.map +0 -1
  65. package/dist/common-canvas-baef2726.js +0 -2
  66. package/dist/common-canvas-baef2726.js.map +0 -1
  67. package/dist/flexible-table-d3598aa8.js +0 -2
  68. package/dist/flexible-table-d3598aa8.js.map +0 -1
  69. package/dist/flexible-table-fe7fbc13.js +0 -2
  70. package/dist/flexible-table-fe7fbc13.js.map +0 -1
@@ -20,11 +20,14 @@ import { ASSOCIATION_LINK, COMMENT_LINK, NODE_LINK,
20
20
  SUPER_NODE, USE_DEFAULT_ICON, USE_DEFAULT_EXT_ICON }
21
21
  from "../common-canvas/constants/canvas-constants.js";
22
22
 
23
+ const BOUNDING_RECT_PADDING = 80;
24
+
23
25
  export default class CreateSuperNodeAction extends Action {
24
- constructor(data, objectModel, labelUtil) {
26
+ constructor(data, objectModel, labelUtil, useCardFromOriginalPorts) {
25
27
  super(data);
26
28
  this.labelUtil = labelUtil;
27
29
  this.data = data;
30
+ this.useCardFromOriginalPorts = useCardFromOriginalPorts;
28
31
  this.objectModel = objectModel;
29
32
  this.apiPipeline = this.objectModel.getAPIPipeline(data.pipelineId);
30
33
 
@@ -32,232 +35,203 @@ export default class CreateSuperNodeAction extends Action {
32
35
  this.subflowComments = this.objectModel.getSelectedComments();
33
36
  this.subflowRect = this.apiPipeline.getBoundingRectForNodes(this.subflowNodes);
34
37
 
35
- this.subflowLinks = [];
36
38
  this.linksToDelete = [];
37
- this.commentLinks = [];
39
+
40
+ // Get the subflow links - this is all links which go to and from the nodes
41
+ // and comments that are to be included in the subflow. No link is repeated
42
+ // in the arrays.
43
+ const uniqueSubflowLinks = this.getUniqueSubflowLinks(this.data.selectedObjectIds);
44
+ this.subflowNodeLinks = this.getSubflowNodeLinks(uniqueSubflowLinks);
45
+ this.subflowCommentLinks = this.getSubflowCommentLinks(uniqueSubflowLinks);
46
+
47
+ // Filter comment links to remove any un-needed links
48
+ this.subflowCommentLinks = this.filterCommentLinks(this.subflowCommentLinks, this.subflowComments);
49
+
50
+ // Create an array with both node and comment links
51
+ this.subflowLinks = this.subflowNodeLinks.concat(this.subflowCommentLinks);
52
+
53
+ // Determine supernode input and output links. These functions will also
54
+ // add links to this.subFlowLinks and this.linksToDelete arrays.
55
+ this.subflowInputLinks = this.createOrderedSubflowInputLinks(this.subflowNodeLinks);
56
+ this.subflowOutputLinks = this.createOrderedSubflowOutputLinks(this.subflowNodeLinks);
57
+
58
+ // Create the new sub-pipeline which will be referenced by the supernode.
59
+ this.subPipeline = this.objectModel.createPipeline(this.subflowNodes, this.subflowComments, this.subflowLinks);
60
+
61
+ // Create an API pipeline for the new pipeline so we can call helper functions on it.
62
+ this.subAPIPipeline = this.objectModel.getAPIPipeline(this.subPipeline.id);
63
+
64
+ // Create binding input/output data objects cotaining port and link.
65
+ this.bindingInputData = this.getBindingInputData(this.subflowInputLinks, this.subflowRect);
66
+ this.bindingOutputData = this.getBindingOutputData(this.subflowOutputLinks, this.subflowRect);
67
+
68
+ // Create Supernode using previously created input and output ports.
69
+ this.supernode = this.createSupernode(this.bindingInputData, this.bindingOutputData);
70
+
71
+ // Create definitions for links to and from supernode.
72
+ this.supernodeLinkDefs = this.createSupernodeLinkDefs(this.bindingInputData, this.bindingOutputData, this.supernode);
73
+
74
+ // Create links to and from subflow binding nodes.
75
+ this.bindingNodeLinkDefs = this.createBindingNodeLinkDefs(this.bindingInputData, this.bindingOutputData);
76
+
77
+ // Handle external subflows in supernode
78
+ if (this.data.externalUrl) {
79
+ this.handleExternalSubflow();
80
+ }
81
+ }
82
+
83
+ // Returns an object containing two links arrays (node links and comment links)
84
+ // for the selected nodes.
85
+ getUniqueSubflowLinks(selectedObjectIds) {
86
+ const uniqueSubflowLinks = [];
38
87
 
39
88
  // Separate the comment links and node links for each selected object.
40
- this.data.selectedObjectIds.forEach((id) => {
89
+ selectedObjectIds.forEach((id) => {
41
90
  const objectLinks = this.apiPipeline.getLinksContainingId(id);
42
91
  // Ensure each link is only stored once.
43
92
  objectLinks.forEach((objectLink) => {
44
- if (!this.subflowLinks.find((link) => (link.id === objectLink.id))) {
45
- if (objectLink.type === NODE_LINK || objectLink.type === ASSOCIATION_LINK) {
46
- this.subflowLinks.push(objectLink);
47
- // Do not add any comment links to the supernode at this moment.
48
- } else if ((!this.commentLinks.find((link) => (link.id === objectLink.id))) && objectLink.type === COMMENT_LINK) {
49
- this.commentLinks.push(objectLink);
50
- }
93
+ if (!uniqueSubflowLinks.find((link) => (link.id === objectLink.id))) {
94
+ uniqueSubflowLinks.push(objectLink);
51
95
  }
52
96
  });
53
97
  });
54
98
 
99
+ return uniqueSubflowLinks;
100
+ }
101
+
102
+
103
+ // Returns an object containing two links arrays (node links and comment links)
104
+ // for the selected nodes.
105
+ getSubflowNodeLinks(uniqueSubflowLinks) {
106
+ return uniqueSubflowLinks.filter((link) => (link.type === NODE_LINK || link.type === ASSOCIATION_LINK));
107
+ }
108
+
109
+ // Returns an object containing two links arrays (node links and comment links)
110
+ // for the selected nodes.
111
+ getSubflowCommentLinks(uniqueSubflowLinks) {
112
+ return uniqueSubflowLinks.filter((link) => (link.type === COMMENT_LINK));
113
+ }
114
+
115
+ // Filters the array of comment links passed in so that links that should
116
+ // NOT be in the subflow are removed. This method may also adjust
117
+ // this.subflowComments and this.linksToDelete.
118
+ filterCommentLinks(subflowCommentLinks, subflowComments) {
119
+ let filteredLinks = [];
120
+
55
121
  // Determine if the comment should be brought into the supernode.
56
- this.commentLinks.forEach((link) => {
122
+ subflowCommentLinks.forEach((link) => {
57
123
  const comment = this.apiPipeline.getComment(link.srcNodeId);
124
+
125
+ // Include links that are connected to selected comments.
58
126
  if (this.apiPipeline.isObjectIdInObjects(comment.id, this.subflowComments)) {
59
- // Include links that are connected to selected comments.
60
- this.subflowLinks.push(link);
127
+ filteredLinks.push(link);
128
+
129
+ // Include links that are connected to nonselected comments and connected
130
+ // to selected nodes AND include their comment in the subflow as well.
61
131
  } else if (!this.apiPipeline.isCommentLinkedToNonSelectedNodes(comment.id)) {
62
- // Include links that are connected to nonselected comments and connected to selected nodes.
63
- this.subflowLinks.push(link);
64
- this.subflowComments.push(comment); // Add nonselected comment.
132
+ filteredLinks.push(link);
133
+ this.subflowComments.push(comment); // Intrinsically, add nonselected comment.
134
+
135
+ // Do not include any other links in the subflow. These will be links
136
+ // from nonselected comments to nonselected nodes. Such links will have
137
+ // been included in the comment links if the comment has another link
138
+ // to a selected node. These links will be deleted from the main flow
139
+ // instead.
65
140
  } else {
66
- // Do not include link in supernode if nonselected comment have links to nonselected nodes.
67
141
  this.linksToDelete.push(link);
68
- // Remove the link from supernode.
69
- this.subflowLinks = this.subflowLinks.filter((superLink) => superLink.id !== link.id);
70
142
  }
71
143
  });
72
144
 
73
- // Comments
74
- this.commentsToUnlinkFromUnselectedNodes = [];
145
+ filteredLinks = this.removeCommentLinksToUnselectedNodes(filteredLinks, subflowComments);
146
+
147
+ return filteredLinks;
148
+ }
149
+
150
+ // Handles all instances where a subflow comment (that is being included in
151
+ // the subflow) is also linked to a non-selectd node (that is, a node that is
152
+ // NOT being inlcuded in the subflow). This method will alter
153
+ // this.subflowLinks and this.linkToDelete.
154
+ removeCommentLinksToUnselectedNodes(inSubflowCommentLinks, subflowComments) {
155
+ let subflowCommentLinks = inSubflowCommentLinks;
156
+ const commentsToUnlinkFromUnselectedNodes = [];
75
157
 
76
158
  // Do not move a selected comment to supernode if it is linked to an nonselected node or comment.
77
- for (const comment of this.subflowComments) {
159
+ for (const comment of subflowComments) {
78
160
  if (this.apiPipeline.isCommentLinkedToNonSelectedNodes(comment.id)) {
79
- this.commentsToUnlinkFromUnselectedNodes.push(comment);
161
+ commentsToUnlinkFromUnselectedNodes.push(comment);
80
162
  }
81
163
  }
82
164
 
83
165
  // If selected comments have links to nonselected nodes, break the links to nonselected nodes.
84
- this.commentsToUnlinkFromUnselectedNodes.forEach((comment) => {
166
+ commentsToUnlinkFromUnselectedNodes.forEach((comment) => {
85
167
  const commentLinks = this.apiPipeline.getLinksContainingId(comment.id);
86
168
  commentLinks.forEach((link) => {
87
169
  if ((!this.apiPipeline.isObjectIdInObjects(link.trgNodeId, this.subflowNodes)) &&
88
- (!this.apiPipeline.isObjectIdInObjects(link.id, this.linksToDelete))) {
89
- this.removeLinkFromSubflow(link, true);
170
+ (!this.apiPipeline.isObjectIdInObjects(link.id, this.linksToDelete))) {
171
+ subflowCommentLinks = subflowCommentLinks.filter((superLink) => superLink.id !== link.id);
172
+ this.linksToDelete.push(link);
90
173
  }
91
174
  });
92
175
  });
176
+ return subflowCommentLinks;
177
+ }
178
+
179
+ // Returns an array of ordered supernode input links. These are the links
180
+ // on the original flow that input into the set of node selected for the
181
+ // supernode. They are ordered by
182
+ createOrderedSubflowInputLinks(subflowNodeLinks) {
183
+ let subflowInputLinks = [];
93
184
 
94
- // Determine supernode input and output links.
95
- this.supernodeInputLinks = [];
96
- this.supernodeOutputLinks = [];
97
- // This returns a list of links in random order.
98
- const subflowNodeLinks = this.apiPipeline.getNodeLinks(this.subflowNodes);
99
185
  subflowNodeLinks.forEach((link) => {
100
186
  if ((!this.apiPipeline.isObjectIdInObjects(link.srcNodeId, this.subflowNodes)) &&
101
- (!this.supernodeInputLinks.find((supernodeInputLink) => (supernodeInputLink.id === link.trgNodeId)))) {
187
+ (!subflowInputLinks.find((inLink) => (inLink.id === link.trgNodeId)))) {
102
188
  if (link.type === ASSOCIATION_LINK) { // Break off associationLink.
103
189
  this.removeLinkFromSubflow(link, true);
104
190
  } else {
105
- this.supernodeInputLinks.push(link);
191
+ subflowInputLinks.push(link);
192
+ this.removeLinkFromSubflow(link, false);
106
193
  }
107
194
  }
195
+ });
196
+ // Reorder the supernode input links to the correct order.
197
+ subflowInputLinks = this.reorderSubflowLinks(subflowInputLinks, "input");
198
+ return subflowInputLinks;
199
+ }
200
+
201
+ // Determine supernode output links.
202
+ createOrderedSubflowOutputLinks(subflowNodeLinks) {
203
+ let subflowOutputLinks = [];
204
+
205
+ subflowNodeLinks.forEach((link) => {
108
206
  if ((!this.apiPipeline.isObjectIdInObjects(link.trgNodeId, this.subflowNodes)) &&
109
- (!this.supernodeOutputLinks.find((supernodeOutputLink) => (supernodeOutputLink.id === link.srcNodeId)))) {
207
+ (!subflowOutputLinks.find((outLink) => (outLink.id === link.srcNodeId)))) {
110
208
  if (link.type === ASSOCIATION_LINK) { // Break off associationLink.
111
209
  this.removeLinkFromSubflow(link, true);
112
210
  } else {
113
- this.supernodeOutputLinks.push(link);
211
+ subflowOutputLinks.push(link);
212
+ this.removeLinkFromSubflow(link, false);
114
213
  }
115
214
  }
116
215
  });
216
+ // Reorder the supernode output links to the correct order.
217
+ subflowOutputLinks = this.reorderSubflowLinks(subflowOutputLinks, "output");
218
+ return subflowOutputLinks;
219
+ }
117
220
 
118
- // Reorder the supernode input and output links to the correct order.
119
- this.supernodeInputLinksModified = this.reorderSupernodeLinks(this.supernodeInputLinks, "input");
120
- this.supernodeOutputLinksModified = this.reorderSupernodeLinks(this.supernodeOutputLinks, "output");
121
-
122
- // Remove input/output links from edge nodes in sub-pipline.
123
- this.supernodeInputLinksModified.forEach((inputLink) => {
124
- this.removeLinkFromSubflow(inputLink, false);
125
- });
126
- this.supernodeOutputLinksModified.forEach((outputLink) => {
127
- this.removeLinkFromSubflow(outputLink, false);
128
- });
129
-
130
- // Sub-Pipeline
131
- this.subPipeline =
132
- this.objectModel.createPipeline(this.subflowNodes, this.subflowComments, this.subflowLinks);
133
- this.subAPIPipeline = this.objectModel.getAPIPipeline(this.subPipeline.id);
134
-
135
- this.createBindingNodeData = [];
136
-
137
- // Supernode
138
- this.supernode = this.createSupernode();
139
-
140
- // Links to and from supernode.
141
- this.linkSrcDefs = [];
142
- this.linkTrgDefs = [];
143
- this.supernodeInputLinksModified.forEach((link) => {
144
- if (!this.apiPipeline.isObjectIdInObjects(link.srcNodeId, this.subflowNodes)) {
145
- this.linkSrcDefs.push({
146
- id: link.srcNodeId,
147
- portId: link.srcNodePortId
148
- });
149
- this.linkTrgDefs.push({
150
- id: this.supernode.id,
151
- portId: link.trgNodePortId ? link.trgNodeId + "_" + link.trgNodePortId : link.trgNodePortId
152
- });
153
- }
154
- });
155
- this.supernodeOutputLinksModified.forEach((link) => {
156
- if (!this.apiPipeline.isObjectIdInObjects(link.trgNodeId, this.subflowNodes)) {
157
- this.linkSrcDefs.push({
158
- id: this.supernode.id,
159
- portId: link.srcNodePortId ? link.srcNodeId + "_" + link.srcNodePortId : link.srcNodePortId
160
- });
161
- this.linkTrgDefs.push({
162
- id: link.trgNodeId,
163
- portId: link.trgNodePortId
164
- });
165
- }
166
- });
167
-
168
- // Keep a map of which supernode port the subflow binding node links to
169
- // and the link the subflow binding node needs to read from to connect
170
- // to the correct node in the subflow.
171
- this.supernodeBindingNodesMappedToParentFlowData = [];
172
-
173
- // Create subflow binding nodes.
174
- this.supernodeEntryBindingNodes = [];
175
- this.supernodeExitBindingNodes = [];
176
-
177
- // Determine relative position of the binding nodes in the subflow.
178
- const boundingRectPadding = 80;
179
- let entryBindingYPos = this.subflowRect.y - boundingRectPadding;
180
- let exitBindingYPos = this.subflowRect.y - boundingRectPadding;
181
-
182
- this.createBindingNodeData.forEach((bindingNodeData) => {
183
- const bindingNodePort = Object.assign({}, bindingNodeData.port);
184
- if (bindingNodeData.type === "entry") {
185
- const pos = { x: this.subflowRect.x - (boundingRectPadding * 2), y: entryBindingYPos += boundingRectPadding };
186
- bindingNodePort.id = bindingNodePort.id ? "output_" + bindingNodePort.id : bindingNodePort.id;
187
- const inputBindingNode = this.createBindingNode(bindingNodeData.link, { outputs: [bindingNodePort] }, pos);
188
-
189
- this.supernodeBindingNodesMappedToParentFlowData[inputBindingNode.id] = {
190
- portId: bindingNodeData.port.id,
191
- link: bindingNodeData.link
192
- };
193
- inputBindingNode.isSupernodeInputBinding = true;
194
- this.supernodeEntryBindingNodes.push(inputBindingNode);
195
- } else {
196
- const pos = { x: this.subflowRect.x + this.subflowRect.width + boundingRectPadding, y: exitBindingYPos += boundingRectPadding };
197
- bindingNodePort.id = bindingNodePort.id ? "input_" + bindingNodePort.id : bindingNodePort.id;
198
- const outputBindingNode = this.createBindingNode(bindingNodeData.link, { inputs: [bindingNodePort] }, pos);
199
- this.supernodeBindingNodesMappedToParentFlowData[outputBindingNode.id] = {
200
- portId: bindingNodeData.port.id,
201
- link: bindingNodeData.link
202
- };
203
- outputBindingNode.isSupernodeOutputBinding = true;
204
- this.supernodeExitBindingNodes.push(outputBindingNode);
205
- }
206
- });
207
-
208
- // Create links to and from subflow binding nodes.
209
- this.bindingNodeLinkSrcDefs = [];
210
- this.bindingNodeLinkTrgDefs = [];
211
- this.supernodeEntryBindingNodes.forEach((bindingNode) => {
212
- const link = this.supernodeBindingNodesMappedToParentFlowData[bindingNode.id].link;
213
- this.bindingNodeLinkSrcDefs.push({
214
- id: bindingNode.id,
215
- portId: link.trgNodePortId ? "output_" + link.trgNodeId + "_" + link.trgNodePortId : link.trgNodePortId
216
- });
217
- this.bindingNodeLinkTrgDefs.push({
218
- id: link.trgNodeId,
219
- portId: link.trgNodePortId
220
- });
221
- });
222
- this.supernodeExitBindingNodes.forEach((bindingNode) => {
223
- const link = this.supernodeBindingNodesMappedToParentFlowData[bindingNode.id].link;
224
- this.bindingNodeLinkSrcDefs.push({
225
- id: link.srcNodeId,
226
- portId: link.srcNodePortId
227
- });
228
- this.bindingNodeLinkTrgDefs.push({
229
- id: bindingNode.id,
230
- portId: link.srcNodePortId ? "input_" + link.srcNodeId + "_" + link.srcNodePortId : link.srcNodePortId
231
- });
232
- });
233
-
234
- if (this.data.externalUrl) {
235
- const supernodes = CanvasUtils.filterSupernodes(this.subflowNodes);
236
- const descPipelines = this.objectModel.getDescendantPipelinesForSupernodes(supernodes);
237
- this.descPipelines = descPipelines.filter((p) => !p.parentUrl); // Filter the local pipelines
221
+ // Handles the subflow when an external pipeline flow is required.
222
+ handleExternalSubflow() {
223
+ const supernodes = CanvasUtils.filterSupernodes(this.subflowNodes);
224
+ const descPipelines = this.objectModel.getDescendantPipelinesForSupernodes(supernodes);
225
+ this.descPipelines = descPipelines.filter((p) => !p.parentUrl); // Filter the local pipelines
238
226
 
239
- this.newPipelineFlow =
240
- this.objectModel.createExternalPipelineFlowTemplate(
241
- this.data.externalPipelineFlowId, this.supernode.subflow_ref.pipeline_id_ref);
242
- }
227
+ this.newPipelineFlow =
228
+ this.objectModel.createExternalPipelineFlowTemplate(
229
+ this.data.externalPipelineFlowId, this.supernode.subflow_ref.pipeline_id_ref);
243
230
  }
244
231
 
245
- createSupernode() {
246
- // Determine which port from input link's target node should be a supernode input port
247
- // and create a binding node for the port in the supernode.
248
- const supernodeInputPorts = [];
249
- this.supernodeInputLinksModified.forEach((link) => {
250
- const node = this.apiPipeline.getNode(link.trgNodeId);
251
- this.createSupernodePorts(node, link, supernodeInputPorts, "entry");
252
- });
253
-
254
- // Determine which port from output link's source node should be a supernode output port
255
- // and create a binding node for the port in the supernode.
256
- const supernodeOutputPorts = [];
257
- this.supernodeOutputLinksModified.forEach((link) => {
258
- const node = this.apiPipeline.getNode(link.srcNodeId);
259
- this.createSupernodePorts(node, link, supernodeOutputPorts, "exit");
260
- });
232
+ createSupernode(bindingInputData, bindingOutputData) {
233
+ const supernodeInputPorts = bindingInputData.map((bnd) => bnd.supernodePort);
234
+ const supernodeOutputPorts = bindingOutputData.map((bnd) => bnd.supernodePort);
261
235
 
262
236
  const supernodeTemplate = {
263
237
  label: this.labelUtil.getLabel("supernode.template.label"),
@@ -289,56 +263,33 @@ export default class CreateSuperNodeAction extends Action {
289
263
  return this.apiPipeline.createNode(supernodeData);
290
264
  }
291
265
 
292
- // Reorder the links in the same order the ports are defined in the binding nodes.
293
- reorderSupernodeLinks(links, type) {
266
+ // Reorder the links in the same vertical order as the connected nodes on
267
+ // the canvas. This helps us position the ports in the supernode so they
268
+ // will be created so the link lines do not cross.
269
+ reorderSubflowLinks(links, type) {
294
270
  if (links.length > 1) {
295
271
  const supernodeLinks = [...links];
272
+ const nodeId = type === "input" ? "srcNodeId" : "trgNodeId";
273
+ const pos = type === "input" ? "srcPos" : "trgPos";
296
274
 
297
- const portType = type === "input" ? "inputs" : "outputs";
298
- const linkNodeType = type === "input" ? "trgNodeId" : "srcNodeId";
299
- const linkNodePortType = type === "input" ? "trgNodePortId" : "srcNodePortId";
275
+ // Sort the nodes in the order they appear on the screen from top to bottom.
276
+ supernodeLinks.sort((a, b) => {
277
+ const nodeA = this.apiPipeline.getNode(a[nodeId]);
278
+ const nodeB = this.apiPipeline.getNode(b[nodeId]);
300
279
 
301
- const subBindingNodes = [];
302
- supernodeLinks.forEach((link) => {
303
- if (!subBindingNodes.find((subBindingNode) => (subBindingNode.id === link[linkNodeType]))) {
304
- subBindingNodes.push(this.apiPipeline.getNode(link[linkNodeType]));
305
- }
306
- });
280
+ const aYcoord = a[pos] ? a[pos].y_pos : nodeA.y_pos;
281
+ const bYcoord = b[pos] ? b[pos].y_pos : nodeB.y_pos;
307
282
 
308
- // Sort the nodes in the order they appear on the screen from top to bottom.
309
- subBindingNodes.sort((a, b) => {
310
- if (a.y_pos < b.y_pos) {
283
+ if (aYcoord < bYcoord) {
311
284
  return -1;
312
285
  }
313
- if (a.y_pos > b.y_pos) {
286
+ if (aYcoord > bYcoord) {
314
287
  return 1;
315
288
  }
316
289
  return 0;
317
290
  });
291
+ return supernodeLinks;
318
292
 
319
- let reorderedSupernodeLinks = [];
320
- subBindingNodes.forEach((bindingNode) => {
321
- const nodePorts = bindingNode[portType];
322
- const firstPort = nodePorts[0];
323
- nodePorts.forEach((port) => {
324
- let correspondingLinks = supernodeLinks.filter((link) =>
325
- (link[linkNodePortType] === port.id || typeof link[linkNodePortType] === "undefined" || link[linkNodePortType] === null) &&
326
- link[linkNodeType] === bindingNode.id);
327
- // If any link has an undefined nodePortId, assign the first portId.
328
- correspondingLinks = correspondingLinks.map((link) => {
329
- const newLink = Object.assign({}, link);
330
- newLink[linkNodePortType] = newLink[linkNodePortType] ? newLink[linkNodePortType] : firstPort.id;
331
- return newLink;
332
- });
333
-
334
- correspondingLinks.forEach((correspondingLink) => {
335
- if (!reorderedSupernodeLinks.find((reorderedSupernodeLink) => (reorderedSupernodeLink.id === correspondingLink.id))) {
336
- reorderedSupernodeLinks = reorderedSupernodeLinks.concat(correspondingLink);
337
- }
338
- });
339
- });
340
- });
341
- return reorderedSupernodeLinks;
342
293
  }
343
294
  return links;
344
295
  }
@@ -366,54 +317,125 @@ export default class CreateSuperNodeAction extends Action {
366
317
  return Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
367
318
  }
368
319
 
369
- createSupernodePorts(node, link, supernodePorts, type) {
370
- const portType = type === "entry" ? "inputs" : "outputs";
371
- const linkNodePortType = type === "entry" ? "trgNodePortId" : "srcNodePortId";
372
-
373
- if (typeof link[linkNodePortType] !== "undefined" && link[linkNodePortType] !== null) {
374
- node[portType].forEach((port) => {
375
- if (link[linkNodePortType] === port.id) {
376
- const newPort = Object.assign({}, port);
377
- newPort.id = port.id ? node.id + "_" + port.id : port.id;
378
- newPort.label = this.labelUtil.getLabel("supernode.new.port.label");
379
- this.addToCreateBindingNodeData(node.id, newPort, link, supernodePorts, type);
380
- }
320
+ // Returns an array of data objects that for binding the input links. That,
321
+ // is for each input link a binding input data object is created that contains
322
+ // the link and a new port that can be added to the supernode. The port will
323
+ // have a unique ID within the scope of the input ports for the supernode.
324
+ getBindingInputData(subflowInputLinks, subflowRect) {
325
+ const bindingInputData = [];
326
+
327
+ // Determine relative position of the binding nodes in the subflow.
328
+ let entryBindingYPos = subflowRect.y - BOUNDING_RECT_PADDING;
329
+
330
+ subflowInputLinks.forEach((link) => {
331
+ const trgNode = this.apiPipeline.getNode(link.trgNodeId);
332
+
333
+ const supernodePort = Object.assign({}, this.findPort(link.trgNodePortId, trgNode.inputs));
334
+ supernodePort.id = this.generateUniquePortId(supernodePort, link.trgNodeId, bindingInputData);
335
+ supernodePort.label = this.labelUtil.getLabel("supernode.new.port.label");
336
+ if (!this.useCardFromOriginalPorts) {
337
+ supernodePort.cardinality = {
338
+ min: 0,
339
+ max: 1
340
+ };
341
+ }
342
+
343
+ const bindingNodePort = Object.assign({}, supernodePort);
344
+ const pos = {
345
+ x: subflowRect.x - (BOUNDING_RECT_PADDING * 2),
346
+ y: entryBindingYPos += BOUNDING_RECT_PADDING
347
+ };
348
+ bindingNodePort.id = bindingNodePort.id ? "output_" + bindingNodePort.id : bindingNodePort.id;
349
+ const inputBindingNode = this.createBindingNode(link, { outputs: [bindingNodePort], isSupernodeInputBinding: true }, pos);
350
+
351
+ bindingInputData.push({
352
+ link: link,
353
+ supernodePort: supernodePort,
354
+ bindingNode: inputBindingNode
381
355
  });
382
- } else { // Add the first port.
383
- const newPort = Object.assign({}, node[portType][0]);
384
- newPort.id = newPort.id ? node.id + "_" + newPort.id : newPort.id;
385
- newPort.label = this.labelUtil.getLabel("supernode.new.port.label");
386
- this.addToCreateBindingNodeData(node.id, newPort, link, supernodePorts, type);
387
- }
356
+ });
357
+ return bindingInputData;
388
358
  }
389
359
 
390
- addToCreateBindingNodeData(nodeId, newPort, link, supernodePorts, type) {
391
- if (!supernodePorts.find((supernodePort) => (supernodePort.id === newPort.id))) {
392
- supernodePorts.push(newPort);
393
- this.createBindingNodeData.push({
394
- bindindNodeForNodeId: nodeId,
395
- type: type,
360
+ // Returns an array of data objects that for binding the output links. That,
361
+ // is for each output link a binding input data object is created that contains
362
+ // the link and a new port that can be added to the supernode. The port will
363
+ // have a unique ID within the scope of the output ports for the supernode.
364
+ getBindingOutputData(subflowOutputLinks, subflowRect) {
365
+ const bindingOutputData = [];
366
+
367
+ // Determine relative position of the binding nodes in the subflow.
368
+ let exitBindingYPos = subflowRect.y - BOUNDING_RECT_PADDING;
369
+
370
+ subflowOutputLinks.forEach((link) => {
371
+ const srcNode = this.apiPipeline.getNode(link.srcNodeId);
372
+
373
+ const supernodePort = Object.assign({}, this.findPort(link.srcNodePortId, srcNode.outputs));
374
+ supernodePort.id = this.generateUniquePortId(supernodePort, link.srcNodeId, bindingOutputData);
375
+ supernodePort.label = this.labelUtil.getLabel("supernode.new.port.label");
376
+ if (!this.useCardFromOriginalPorts) {
377
+ supernodePort.cardinality = {
378
+ min: 0,
379
+ max: 1
380
+ };
381
+ }
382
+
383
+ const bindingNodePort = Object.assign({}, supernodePort);
384
+ const pos = {
385
+ x: subflowRect.x + subflowRect.width + BOUNDING_RECT_PADDING,
386
+ y: exitBindingYPos += BOUNDING_RECT_PADDING
387
+ };
388
+ bindingNodePort.id = bindingNodePort.id ? "input_" + bindingNodePort.id : bindingNodePort.id;
389
+ const outputBindingNode = this.createBindingNode(link, { inputs: [bindingNodePort], isSupernodeOutputBinding: true }, pos);
390
+
391
+ bindingOutputData.push({
396
392
  link: link,
397
- port: newPort
393
+ supernodePort: supernodePort,
394
+ bindingNode: outputBindingNode
398
395
  });
396
+ });
397
+ return bindingOutputData;
398
+ }
399
+
400
+ findPort(nodePortId, ports) {
401
+ if (typeof nodePortId !== "undefined" && nodePortId !== null) {
402
+ const found = CanvasUtils.getPort(nodePortId, ports);
403
+ if (found) {
404
+ return found;
405
+ }
399
406
  }
407
+ return ports[0];
400
408
  }
401
409
 
402
- createBindingNode(link, bindingNodePort, pos) {
410
+ generateUniquePortId(port, nodeId, bindingNodeData) {
411
+ let newId = port.id ? nodeId + "_" + port.id : port.id;
412
+ const count = this.occurancesStartingWith(newId, bindingNodeData);
413
+ if (count > 0) {
414
+ newId += "_" + count;
415
+ }
416
+ return newId;
417
+ }
418
+
419
+ occurancesStartingWith(id, bindingNodeData) {
420
+ const foundIds = bindingNodeData.filter((bnd) => (bnd.supernodePort.id.startsWith(id)));
421
+ return foundIds.length;
422
+ }
423
+
424
+ createBindingNode(link, portsData, pos) {
403
425
  const bindingNodeTemplate = {
404
426
  description: this.labelUtil.getLabel("supernode.binding.node.description"),
405
427
  label: this.labelUtil.getLabel("supernode.binding.node.label"),
406
428
  type: "binding"
407
429
  };
408
430
 
409
- const bindingNodeData = {
431
+ const actionData = {
410
432
  editType: "createNode",
411
- nodeTemplate: Object.assign(bindingNodeTemplate, bindingNodePort),
433
+ nodeTemplate: Object.assign(bindingNodeTemplate, portsData),
412
434
  offsetX: pos.x,
413
435
  offsetY: pos.y
414
436
  };
415
437
 
416
- return this.subAPIPipeline.createNode(bindingNodeData);
438
+ return this.subAPIPipeline.createNode(actionData);
417
439
  }
418
440
 
419
441
  removeLinkFromSubflow(link, deleteLink) {
@@ -423,6 +445,74 @@ export default class CreateSuperNodeAction extends Action {
423
445
  }
424
446
  }
425
447
 
448
+ // Create an array of link definitions that define the links between the
449
+ // supernode in/out ports and surrounding input/output nodes.
450
+ createSupernodeLinkDefs(bindingInputData, bindingOutputData, supernode) {
451
+ const linkDefs = [];
452
+
453
+ bindingInputData.forEach((bnd) => {
454
+ const linkData = { srcInfo: {}, trgInfo: {} };
455
+ if (bnd.link.srcPos) {
456
+ linkData.srcInfo.srcPos = bnd.link.srcPos;
457
+ } else {
458
+ linkData.srcInfo.id = bnd.link.srcNodeId;
459
+ linkData.srcInfo.portId = bnd.link.srcNodePortId;
460
+ }
461
+
462
+ linkData.trgInfo.id = supernode.id;
463
+ linkData.trgInfo.portId = bnd.supernodePort.id;
464
+ linkDefs.push(linkData);
465
+ });
466
+
467
+ bindingOutputData.forEach((bnd) => {
468
+ const linkData = { srcInfo: {}, trgInfo: {} };
469
+ linkData.srcInfo.id = supernode.id;
470
+ linkData.srcInfo.portId = bnd.supernodePort.id;
471
+
472
+ if (bnd.link.trgPos) {
473
+ linkData.trgInfo.trgPos = bnd.link.trgPos;
474
+ } else {
475
+ linkData.trgInfo.id = bnd.link.trgNodeId;
476
+ linkData.trgInfo.portId = bnd.link.trgNodePortId;
477
+ }
478
+ linkDefs.push(linkData);
479
+ });
480
+
481
+ return linkDefs;
482
+ }
483
+
484
+ // Create links to and from subflow binding nodes.
485
+ createBindingNodeLinkDefs(bindingInputData, bindingOutputData) {
486
+ const linkDefs = [];
487
+
488
+ bindingInputData.forEach((bnd) => {
489
+ linkDefs.push({
490
+ srcInfo: {
491
+ id: bnd.bindingNode.id,
492
+ portId: bnd.bindingNode.outputs[0].id
493
+ },
494
+ trgInfo: {
495
+ id: bnd.link.trgNodeId,
496
+ portId: bnd.link.trgNodePortId
497
+ }
498
+ });
499
+ });
500
+
501
+ bindingOutputData.forEach((bnd) => {
502
+ linkDefs.push({
503
+ srcInfo: {
504
+ id: bnd.link.srcNodeId,
505
+ portId: bnd.link.srcNodePortId
506
+ },
507
+ trgInfo: {
508
+ id: bnd.bindingNode.id,
509
+ portId: bnd.bindingNode.inputs[0].id
510
+ }
511
+ });
512
+ });
513
+ return linkDefs;
514
+ }
515
+
426
516
  // Return augmented command object which will be passed to the client app.
427
517
  getData() {
428
518
  this.data.newNode = this.supernode;
@@ -443,40 +533,40 @@ export default class CreateSuperNodeAction extends Action {
443
533
  this.apiPipeline.addSupernode(this.supernode, [this.subPipeline]);
444
534
 
445
535
  // Add subflow_node_ref to supernode ports.
446
- this.supernodeEntryBindingNodes.forEach((bindingNode) => {
447
- const portId = this.supernodeBindingNodesMappedToParentFlowData[bindingNode.id].portId;
448
- this.apiPipeline.setInputPortSubflowNodeRef(this.supernode.id, portId, bindingNode.id);
536
+ this.bindingInputData.forEach((bnd) => {
537
+ this.apiPipeline.setInputPortSubflowNodeRef(this.supernode.id, bnd.supernodePort.id, bnd.bindingNode.id);
449
538
  });
450
- this.supernodeExitBindingNodes.forEach((bindingNode) => {
451
- const portId = this.supernodeBindingNodesMappedToParentFlowData[bindingNode.id].portId;
452
- this.apiPipeline.setOutputPortSubflowNodeRef(this.supernode.id, portId, bindingNode.id);
539
+ this.bindingOutputData.forEach((bnd) => {
540
+ this.apiPipeline.setOutputPortSubflowNodeRef(this.supernode.id, bnd.supernodePort.id, bnd.bindingNode.id);
453
541
  });
454
542
 
455
- // Create new links to and from supernode in the main flow.
543
+ // Create new links to and from supernode in the main flow. We can only
544
+ // do this AFTER the supernode has been added to the canvas otherwise the
545
+ // links cannot be created in the object model.
456
546
  this.newLinks = [];
457
- for (let idx = 0; idx < this.linkSrcDefs.length; idx++) {
458
- const link = this.apiPipeline.createNodeLink(this.linkSrcDefs[idx], this.linkTrgDefs[idx], { type: NODE_LINK });
459
- if (link) {
460
- this.newLinks.push(link);
461
- }
462
- }
547
+ this.supernodeLinkDefs.forEach((linkDef) => {
548
+ this.newLinks.push(
549
+ this.apiPipeline.createNodeLink(linkDef.srcInfo, linkDef.trgInfo, { type: NODE_LINK })
550
+ );
551
+ });
463
552
  this.apiPipeline.addLinks(this.newLinks);
464
553
 
465
554
  // Add the binding nodes to the subflow.
466
- this.supernodeEntryBindingNodes.forEach((bindingNode) => {
467
- this.subAPIPipeline.addNode(bindingNode);
555
+ this.bindingInputData.forEach((bnd) => {
556
+ this.subAPIPipeline.addNode(bnd.bindingNode);
468
557
  });
469
- this.supernodeExitBindingNodes.forEach((bindingNode) => {
470
- this.subAPIPipeline.addNode(bindingNode);
558
+ this.bindingOutputData.forEach((bnd) => {
559
+ this.subAPIPipeline.addNode(bnd.bindingNode);
471
560
  });
472
561
 
473
562
  // Create links to and from the subflow binding nodes.
474
- this.supernodeNewLinks = [];
475
- for (let idx = 0; idx < this.bindingNodeLinkSrcDefs.length; idx++) {
476
- this.supernodeNewLinks.push(
477
- this.subAPIPipeline.createNodeLink(this.bindingNodeLinkSrcDefs[idx], this.bindingNodeLinkTrgDefs[idx], { type: NODE_LINK }));
478
- }
479
- this.subAPIPipeline.addLinks(this.supernodeNewLinks);
563
+ this.subflowNewLinks = [];
564
+ this.bindingNodeLinkDefs.forEach((linkDef) => {
565
+ this.subflowNewLinks.push(
566
+ this.subAPIPipeline.createNodeLink(linkDef.srcInfo, linkDef.trgInfo, { type: NODE_LINK })
567
+ );
568
+ });
569
+ this.subAPIPipeline.addLinks(this.subflowNewLinks);
480
570
 
481
571
  // If we are creating an external supernode create the external flow
482
572
  // header info and switch the parentUrls for descendant pipelines (including
@@ -510,8 +600,8 @@ export default class CreateSuperNodeAction extends Action {
510
600
  });
511
601
 
512
602
  this.apiPipeline.addLinks(this.subflowLinks);
513
- this.apiPipeline.addLinks(this.supernodeInputLinks);
514
- this.apiPipeline.addLinks(this.supernodeOutputLinks);
603
+ this.apiPipeline.addLinks(this.subflowInputLinks);
604
+ this.apiPipeline.addLinks(this.subflowOutputLinks);
515
605
  this.apiPipeline.addLinks(this.linksToDelete);
516
606
  }
517
607