@dualbox/editor 1.0.1 → 1.0.3
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/js/src/GraphEditor.js +159 -0
- package/js/src/c/GraphController.js +646 -0
- package/js/src/libs/CodeMirror.js +8 -0
- package/js/src/libs/fontawesome.js +1 -0
- package/js/src/libs/jsoneditor.css +2 -0
- package/js/src/libs/jsoneditor.js +4 -0
- package/js/src/m/DualboxUtils.js +35 -0
- package/js/src/m/GraphModel.js +2167 -0
- package/js/src/m/History.js +94 -0
- package/js/src/m/Merger.js +357 -0
- package/js/src/v/AppManager.js +61 -0
- package/js/src/v/CanvasSizeHandler.js +136 -0
- package/js/src/v/ContextMenu.css +45 -0
- package/js/src/v/ContextMenu.js +239 -0
- package/js/src/v/GraphView.js +928 -0
- package/js/src/v/PlumbStyle.js +254 -0
- package/js/src/v/Selector.js +239 -0
- package/js/src/v/TemplateManager.js +79 -0
- package/js/src/v/Translater.js +174 -0
- package/js/src/v/Utils.js +7 -0
- package/js/src/v/Zoomer.js +201 -0
- package/js/src/v/templates/addNode.css +45 -0
- package/js/src/v/templates/addNode.html +62 -0
- package/js/src/v/templates/addNode.js +34 -0
- package/js/src/v/templates/debugNodeInfos.css +5 -0
- package/js/src/v/templates/debugNodeInfos.html +336 -0
- package/js/src/v/templates/debugNodeInfos.js +31 -0
- package/js/src/v/templates/editMainSettings.css +67 -0
- package/js/src/v/templates/editMainSettings.html +265 -0
- package/js/src/v/templates/editMainSettings.js +240 -0
- package/js/src/v/templates/editNodeSettings.css +86 -0
- package/js/src/v/templates/editNodeSettings.html +539 -0
- package/js/src/v/templates/editNodeSettings.js +356 -0
- package/js/src/v/templates/graphNode.css +333 -0
- package/js/src/v/templates/graphNode.html +227 -0
- package/js/src/v/templates/graphNode.js +412 -0
- package/js/src/v/templates/main.css +353 -0
- package/js/src/v/templates/main.html +149 -0
- package/js/src/v/templates/main.js +511 -0
- package/js/src/v/templates/searchResults.css +50 -0
- package/js/src/v/templates/searchResults.html +46 -0
- package/js/src/v/templates/searchResults.js +176 -0
- package/package.json +3 -2
|
@@ -0,0 +1,928 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implementing the View of the GraphEditor
|
|
3
|
+
* Everything related to display should be here
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const _ = require('lodash');
|
|
7
|
+
const swal = require('sweetalert2');
|
|
8
|
+
const PlumbStyle = require('./PlumbStyle');
|
|
9
|
+
|
|
10
|
+
// Manage the start/stop lifetime of the application
|
|
11
|
+
const AppManager = require('./AppManager');
|
|
12
|
+
|
|
13
|
+
// for instanciation and adding templates to DOM
|
|
14
|
+
const TemplateManager = require('./TemplateManager');
|
|
15
|
+
|
|
16
|
+
// Tools for the graph
|
|
17
|
+
const Zoomer = require('./Zoomer');
|
|
18
|
+
const Selector = require('./Selector');
|
|
19
|
+
const Translater = require('./Translater');
|
|
20
|
+
const CanvasSizeHandler = require('./CanvasSizeHandler');
|
|
21
|
+
const ContextMenu = require('./ContextMenu');
|
|
22
|
+
const Utils = require('./Utils');
|
|
23
|
+
const dbUtils = require('../m/DualboxUtils');
|
|
24
|
+
|
|
25
|
+
class GraphView {
|
|
26
|
+
constructor(editor, div, attrs) {
|
|
27
|
+
this.e = editor;
|
|
28
|
+
this.m = editor.m;
|
|
29
|
+
this.c = editor.c;
|
|
30
|
+
|
|
31
|
+
this.utils = Utils;
|
|
32
|
+
|
|
33
|
+
this.div = $(div);
|
|
34
|
+
this.canvas = null;
|
|
35
|
+
this.attrs = attrs;
|
|
36
|
+
|
|
37
|
+
// Manage the different HTML templates
|
|
38
|
+
this.templateMgr = new TemplateManager(this);
|
|
39
|
+
|
|
40
|
+
// jsplumb
|
|
41
|
+
this.jsPlumbInstance = null; // main canvas graph
|
|
42
|
+
|
|
43
|
+
// encapsulate the JSPlumb styles
|
|
44
|
+
this.style = new PlumbStyle(this);
|
|
45
|
+
|
|
46
|
+
// Controls for different views
|
|
47
|
+
this.showEvents = false;
|
|
48
|
+
|
|
49
|
+
// Controls helpers utilities
|
|
50
|
+
this.selector = null;
|
|
51
|
+
this.translater = null;
|
|
52
|
+
this.zoomer = null;
|
|
53
|
+
this.canvasSizeHandler = null;
|
|
54
|
+
|
|
55
|
+
// Application manager
|
|
56
|
+
this.appManager = null;
|
|
57
|
+
|
|
58
|
+
// Code mirror boxes
|
|
59
|
+
this.htmlCode = null;
|
|
60
|
+
this.cssCode = null;
|
|
61
|
+
|
|
62
|
+
// load the main template
|
|
63
|
+
this.initialize();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
hideCanvas() {
|
|
67
|
+
this.canvas.css('visibility', 'hidden');
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
this.canvas.ready(resolve);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
showCanvas() {
|
|
74
|
+
this.canvas.css('visibility', 'visible');
|
|
75
|
+
return new Promise( (resolve) => {
|
|
76
|
+
this.canvas.ready(resolve);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
initialize() {
|
|
81
|
+
// add main template
|
|
82
|
+
this.templateMgr.appendTemplate(this.div, "main", {showLoadSaveButtons: this.attrs.showLoadSaveButtons}, () => {
|
|
83
|
+
// for convenience, define thoses once the main div is ready
|
|
84
|
+
this.div.ready(() => {
|
|
85
|
+
this.graphContainer = this.div.find('.dualbox-graph-container');
|
|
86
|
+
this.canvas = this.div.find('.dualbox-graph-canvas');
|
|
87
|
+
this.selector = new Selector(this, this.graphContainer);
|
|
88
|
+
this.translater = new Translater(this, this.graphContainer, this.canvas);
|
|
89
|
+
this.zoomer = new Zoomer(this, this.graphContainer, this.canvas);
|
|
90
|
+
this.jsPlumbInstance = jsPlumb.getInstance({
|
|
91
|
+
DragOptions: { cursor: 'pointer', zIndex: 2500 }, // default drag options
|
|
92
|
+
Container: "dualbox-graph-canvas"
|
|
93
|
+
});
|
|
94
|
+
this.style.setDefault();
|
|
95
|
+
this.initializeListeners();
|
|
96
|
+
this.setNavigation();
|
|
97
|
+
this.canvasSizeHandler = new CanvasSizeHandler(this, this.canvas);
|
|
98
|
+
this.appManager = new AppManager(this, this.div.find('.application') );
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
this.templateMgr.appendTemplate(this.div, "addNode"); // "add node" modal
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// bind jsplumb events
|
|
106
|
+
initializeListeners() {
|
|
107
|
+
this.selector.initialize();
|
|
108
|
+
this.zoomer.initialize();
|
|
109
|
+
ContextMenu.initialize(); // important to make this after the translater (right-clic conflicts)
|
|
110
|
+
this.translater.initialize();
|
|
111
|
+
|
|
112
|
+
// show selected nodes
|
|
113
|
+
this.selector.onSelecting((divs) => {
|
|
114
|
+
$(this.canvas).find('.card').removeClass('selected');
|
|
115
|
+
_.each(divs, (div) => {
|
|
116
|
+
$(div).addClass('selected');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Add every node in the selected area to JsPlumb selection
|
|
121
|
+
this.selector.onSelect((divs) => {
|
|
122
|
+
_.each(divs, (div) => {
|
|
123
|
+
$(div).addClass('selected');
|
|
124
|
+
this.jsPlumbInstance.addToDragSelection($(div)[0]);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// remove the selection
|
|
129
|
+
this.selector.onDeselect((divs) => {
|
|
130
|
+
_.each(divs, (div) => {
|
|
131
|
+
$(div).removeClass('selected');
|
|
132
|
+
this.jsPlumbInstance.removeFromDragSelection($(div)[0]);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
this.jsPlumbInstance.bind("connectionDrag", (connection) => {
|
|
137
|
+
// hide the type of the source (output endpoint)
|
|
138
|
+
var endpoints = connection.getAttachedElements();
|
|
139
|
+
if( endpoints && endpoints[0] ) this.style.hideType(endpoints[0]);
|
|
140
|
+
|
|
141
|
+
// if the connection linked a source to a target, remove the connection
|
|
142
|
+
var c = connection.getParameters();
|
|
143
|
+
if( c.target ) {
|
|
144
|
+
if( c.type === "data" ) {
|
|
145
|
+
// remove the link
|
|
146
|
+
this.m.removeDataLink(c.source.id, c.source.output, c.target.id, c.target.input);
|
|
147
|
+
}
|
|
148
|
+
else if( c.type === "event" ) {
|
|
149
|
+
this.m.removeEventLink(c.source.id, c.target.id, c.event);
|
|
150
|
+
this.setBoxMenu(c.source.id);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
this.jsPlumbInstance.bind("connectionDragStop", (connection) => {
|
|
156
|
+
// if the connection has not been made, show back the type of the source
|
|
157
|
+
var endpoints = connection.getAttachedElements();
|
|
158
|
+
if( endpoints && endpoints.length == 2 ) {
|
|
159
|
+
var sourceEP = endpoints[0];
|
|
160
|
+
var targetEP = endpoints[1];
|
|
161
|
+
if( targetEP && targetEP.elementId.startsWith('jsPlumb')) {
|
|
162
|
+
if( targetEP.elementId.startsWith('jsPlumb') ) {
|
|
163
|
+
// no connection has been made, show back our output type
|
|
164
|
+
var sourceOutput = connection.getParameters().source.output;
|
|
165
|
+
if( !this.m.getNode(sourceEP.elementId).isOutputConnected(sourceOutput) ) {
|
|
166
|
+
this.style.showType(sourceEP);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
this.jsPlumbInstance.bind("connection", (info, event) => {
|
|
174
|
+
// only do this for connection established by user, no programmatically
|
|
175
|
+
if( event ) {
|
|
176
|
+
var c = info.connection.getParameters();
|
|
177
|
+
if( c.type === "data" ) {
|
|
178
|
+
// check the types
|
|
179
|
+
var sourceType = this.m.getNode(c.source.id).getOutputType(c.source.output);
|
|
180
|
+
var targetType = this.m.getNode(c.target.id).getInputType(c.target.input);
|
|
181
|
+
if( this.m.compareTypes(sourceType, targetType) ) {
|
|
182
|
+
this.m.addDataLink(c.source.id, c.source.output, c.target.id, c.target.input);
|
|
183
|
+
|
|
184
|
+
// hide source and target types
|
|
185
|
+
this.style.hideType(info.sourceEndpoint);
|
|
186
|
+
this.style.hideType(info.targetEndpoint);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
// Error
|
|
190
|
+
this.jsPlumbInstance.detach(info.connection, {
|
|
191
|
+
fireEvent: false, //fire a connection detached event?
|
|
192
|
+
forceDetach: false //override any beforeDetach listeners
|
|
193
|
+
});
|
|
194
|
+
this.style.showType(info.targetEndpoint);
|
|
195
|
+
if( !this.m.getNode(info.sourceId).isOutputConnected(c.source.output) ) {
|
|
196
|
+
this.style.showType(info.sourceEndpoint);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
swal({
|
|
200
|
+
title: "Nope.",
|
|
201
|
+
html: "<div style='text-align: left;'>Output <b style='font-weight: bold;'>" + c.source.output + "</b> of <b style='font-weight: bold;'>" + info.sourceId + "</b> is of type <u>" + Utils.htmlentities(sourceType) + "</u><br/>Input <b style='font-weight: bold;'>" + c.target.input + "</b> of <b style='font-weight: bold;'>" + info.targetId + "</b> is of type <u>" + Utils.htmlentities(targetType) + "</u>.</div><br/>Type mismatch.",
|
|
202
|
+
type: "error",
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else if( c.type === "event" ) {
|
|
207
|
+
var target = this.m.getNode(c.target.id);
|
|
208
|
+
var eventNames = target.getEventsNames();
|
|
209
|
+
var options = {};
|
|
210
|
+
_.each(eventNames, (name) => options[name] = name );
|
|
211
|
+
|
|
212
|
+
swal({
|
|
213
|
+
input: 'select',
|
|
214
|
+
title: 'Select an event',
|
|
215
|
+
inputOptions: options
|
|
216
|
+
}).then( (result) => {
|
|
217
|
+
if( result.value ) {
|
|
218
|
+
info.connection.setParameter('event', result.value);
|
|
219
|
+
info.connection.setLabel({ label: result.value, cssClass: "connection-label noselect", location: 0.5 });
|
|
220
|
+
this.m.addEventLink(c.source.id, c.target.id, result.value);
|
|
221
|
+
|
|
222
|
+
this.setBoxMenu(c.source.id);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
this.jsPlumbInstance.bind("connectionDetached", (info, event) => {
|
|
230
|
+
var c = info.connection.getParameters();
|
|
231
|
+
if( c.type === "data" ) {
|
|
232
|
+
// remove the link
|
|
233
|
+
this.m.removeDataLink(c.source.id, c.source.output, c.target.id, c.target.input);
|
|
234
|
+
|
|
235
|
+
// show source and target type
|
|
236
|
+
this.style.showType(info.targetEndpoint);
|
|
237
|
+
if( !info.source.id.startsWith('connection-split') &&
|
|
238
|
+
!this.m.getNode(info.source.id).isOutputConnected(c.source.output) ) {
|
|
239
|
+
this.style.showType(info.sourceEndpoint);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
else if( c.type === "event" ) {
|
|
243
|
+
this.m.removeEventLink(c.source.id, c.target.id, c.event);
|
|
244
|
+
//this.style.showEventName(info.targetEndpoint);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// if the connection is splitted, remove all other connections/endpoint
|
|
248
|
+
if( c.splitted ) {
|
|
249
|
+
var connections = this.jsPlumbInstance.getAllConnections();
|
|
250
|
+
var connToDelete = [];
|
|
251
|
+
var EPToDelete = {};
|
|
252
|
+
_.each(connections, conn => {
|
|
253
|
+
var p = conn.getParameters();
|
|
254
|
+
if( p.source.id === c.source.id &&
|
|
255
|
+
p.target.id === c.target.id &&
|
|
256
|
+
((c.type == "event" && p.event === c.event) ||
|
|
257
|
+
(c.type == "data" && p.source.sourceOutput == c.source.sourceOutput &&
|
|
258
|
+
p.target.targetInput === p.target.targetInput))) {
|
|
259
|
+
// collect the connection and the endpoints to delete
|
|
260
|
+
connToDelete.push(conn);
|
|
261
|
+
|
|
262
|
+
let [sourceEP, targetEP] = conn.getAttachedElements();
|
|
263
|
+
if( sourceEP.getUuid().startsWith('connection-split-') ) {
|
|
264
|
+
EPToDelete[ sourceEP.getUuid() ] = sourceEP;
|
|
265
|
+
}
|
|
266
|
+
if( targetEP.getUuid().startsWith('connection-split-') ) {
|
|
267
|
+
EPToDelete[ targetEP.getUuid() ] = targetEP;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// clean all theses connections and endpoints once this call is ended
|
|
273
|
+
_.each(connToDelete, conn => {
|
|
274
|
+
_.defer(()=> {
|
|
275
|
+
this.jsPlumbInstance.detach(conn, {
|
|
276
|
+
fireEvent: false, //fire a connection detached event?
|
|
277
|
+
forceDetach: false //override any beforeDetach listeners
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
_.each(EPToDelete, (endpoint, uuid) => {
|
|
283
|
+
_.defer(()=> {
|
|
284
|
+
var div = endpoint.getElement();
|
|
285
|
+
this.jsPlumbInstance.deleteEndpoint(endpoint);
|
|
286
|
+
$(div).remove();
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
this.jsPlumbInstance.bind("dblclick", async (connection, e) => {
|
|
293
|
+
let [sourceEP, targetEP] = connection.getAttachedElements();
|
|
294
|
+
let c = connection.getParameters();
|
|
295
|
+
|
|
296
|
+
// split this connection into 2 by creating an endpoint at the event coordinate
|
|
297
|
+
// remove 7px to align it to the center
|
|
298
|
+
var pos = {
|
|
299
|
+
"top" : e.pageY - $(this.canvas).offset().top -7,
|
|
300
|
+
"left": e.pageX - $(this.canvas).offset().left - 7
|
|
301
|
+
};
|
|
302
|
+
this.splitConnection(connection, pos); // return the endpoint exact position
|
|
303
|
+
|
|
304
|
+
// save to the graph
|
|
305
|
+
if( c.type == "data" ) {
|
|
306
|
+
var link = this.m.getDataLink(c.source.id, c.source.output, c.target.id, c.target.input);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
var link = this.m.getEventLink(c.source.id, c.target.id, c.event);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if( sourceEP.getUuid().startsWith('connection-split-') ) {
|
|
313
|
+
var el = sourceEP.getElement();
|
|
314
|
+
var prevPos = this.jsPlumbInstance.getPosition(el);
|
|
315
|
+
link.addPathStep(prevPos, pos);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
link.addPathStep( "source", pos );
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
detachConnection(info) {
|
|
324
|
+
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
setEventsVisibility( b ) {
|
|
328
|
+
this.showEvents = b;
|
|
329
|
+
this.repaint();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// set some transparency to show event better
|
|
333
|
+
setEventMode(b) {
|
|
334
|
+
if( b ) {
|
|
335
|
+
// add opacity to non-events
|
|
336
|
+
$(this.canvas).find('.card:not(.card-ui)').addClass('transparent');
|
|
337
|
+
$(this.canvas).find('.data-connection').each(function() {
|
|
338
|
+
if( $(this).is('svg') ) {
|
|
339
|
+
// addclass doesnt work with svg
|
|
340
|
+
$(this).attr('class', $(this).attr('class') + " transparent");
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
$(this).addClass('transparent');;
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
// add opacity to non-events
|
|
349
|
+
$(this.canvas).find('.card:not(.card-ui)').removeClass('transparent');
|
|
350
|
+
$(this.canvas).find('.data-connection').removeClass('transparent');;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return new Promise((resolve) => {
|
|
354
|
+
$(this.canvas).ready(resolve);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
addNode(id, pkg, def) {
|
|
359
|
+
return new Promise( (resolve, reject) => {
|
|
360
|
+
this.templateMgr.appendTemplate(this.canvas, "graphNode", { id: id, pkg: pkg, def:def, n: this.m.getNode(id), utils: this.utils }, (div) => {
|
|
361
|
+
div.ready(() => resolve());
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
addLink(sourceId, sourceOutput, targetId, targetInput) {
|
|
367
|
+
var sourceUuid = [sourceId, "output", sourceOutput].join('#');
|
|
368
|
+
var targetUuid = [targetId, "input", targetInput].join('#');
|
|
369
|
+
|
|
370
|
+
// hide source and target types
|
|
371
|
+
this.style.hideType(sourceUuid); // works with endpoint or uuid
|
|
372
|
+
this.style.hideType(targetUuid);
|
|
373
|
+
|
|
374
|
+
var connection = this.jsPlumbInstance.connect({
|
|
375
|
+
uuids: [sourceUuid, targetUuid],
|
|
376
|
+
editable: true,
|
|
377
|
+
paintStyle: this.style.connectorData.paintStyle,
|
|
378
|
+
cssClass: 'data-connection',
|
|
379
|
+
connectorClass: 'data-connection'
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// split the connection if we need to
|
|
383
|
+
var datalink = this.m.getDataLink(sourceId, sourceOutput, targetId, targetInput);
|
|
384
|
+
var path = datalink.getPath();
|
|
385
|
+
_.each( path, (pos) => {
|
|
386
|
+
var r = this.splitConnection(connection, pos);
|
|
387
|
+
connection = r.c2; // switch to the 2nd child connection
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
addEvent(sourceId, targetId, name) {
|
|
392
|
+
var sourceUuid = [sourceId, "event-out"].join('#');
|
|
393
|
+
var sourceEP = this.jsPlumbInstance.getEndpoint(sourceUuid);
|
|
394
|
+
|
|
395
|
+
var connection = this.jsPlumbInstance.connect({
|
|
396
|
+
source: sourceEP,
|
|
397
|
+
target: targetId,
|
|
398
|
+
editable: true,
|
|
399
|
+
paintStyle: this.style.connectorData.paintStyle,
|
|
400
|
+
cssClass: 'event-connection',
|
|
401
|
+
connectorClass: 'event-connection',
|
|
402
|
+
overlays: [
|
|
403
|
+
[ "PlainArrow", { location: 1, width:10, length:10, gap: 12 }],
|
|
404
|
+
[ "Label", { label: name, location:0.50, labelStyle: { cssClass: "connection-label noselect" } } ]
|
|
405
|
+
]
|
|
406
|
+
});
|
|
407
|
+
connection.setParameter("event", name);
|
|
408
|
+
|
|
409
|
+
// split the connection if we need to
|
|
410
|
+
var eventlink = this.m.getEventLink(sourceId, targetId, name);
|
|
411
|
+
var path = eventlink.getPath();
|
|
412
|
+
_.each( path, (pos) => {
|
|
413
|
+
var r = this.splitConnection(connection, pos);
|
|
414
|
+
connection = r.c2; // switch to the 2nd child connection
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// split a connection by adding an endpoint at given pos
|
|
419
|
+
splitConnection( connection, pos ) {
|
|
420
|
+
var c = connection.getParameters();
|
|
421
|
+
|
|
422
|
+
// create the endpoint div
|
|
423
|
+
var id = "connection-split-" + dbUtils.randomString(8);
|
|
424
|
+
var div = $('<div/>', {
|
|
425
|
+
id: id,
|
|
426
|
+
class: "connection-control capture-left-click capture-right-click"
|
|
427
|
+
});
|
|
428
|
+
$(this.canvas).append(div);
|
|
429
|
+
|
|
430
|
+
// position it at pos (adjust for the height of the div)
|
|
431
|
+
var jsPlumbElement = this.jsPlumbInstance.getElement(id);
|
|
432
|
+
this.jsPlumbInstance.setPosition(jsPlumbElement, pos);
|
|
433
|
+
|
|
434
|
+
// make this div draggable
|
|
435
|
+
var startPosition = pos;
|
|
436
|
+
this.jsPlumbInstance.draggable(div, {
|
|
437
|
+
start: (e) => {
|
|
438
|
+
startPosition = this.jsPlumbInstance.getPosition(jsPlumbElement);
|
|
439
|
+
},
|
|
440
|
+
stop: (e) => {
|
|
441
|
+
var stopPosition = this.jsPlumbInstance.getPosition(jsPlumbElement);
|
|
442
|
+
if( c.type == "data" ) {
|
|
443
|
+
var link = this.m.getDataLink(c.source.id, c.source.output, c.target.id, c.target.input);
|
|
444
|
+
}
|
|
445
|
+
else if( c.type == "event" ) {
|
|
446
|
+
var link = this.m.getEventLink(c.source.id, c.target.id, c.event);
|
|
447
|
+
}
|
|
448
|
+
link.updatePathStep(startPosition, stopPosition);
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
// determine the style of the 2 connections
|
|
453
|
+
c.splitted = true; // indicates that this connection is splitted
|
|
454
|
+
if( c.type == "data" ) {
|
|
455
|
+
var connectionStyle = {
|
|
456
|
+
paintStyle: this.style.splittedConnectorData.paintStyle,
|
|
457
|
+
connector: this.style.splittedConnectorData.connector,
|
|
458
|
+
connectorOverlays:this.style.splittedConnectorData.overlays,
|
|
459
|
+
connectorStyle: this.style.splittedConnectorData.paintStyle,
|
|
460
|
+
hoverPaintStyle: this.style.endpointData.hoverStyle,
|
|
461
|
+
cssClass : 'data-connection'
|
|
462
|
+
}
|
|
463
|
+
var epStyle = this.style.dataLineSplitterEndpoint;
|
|
464
|
+
}
|
|
465
|
+
else if( c.type == "event" ) {
|
|
466
|
+
var connectionStyle = {
|
|
467
|
+
paintStyle: this.style.eventConnectorData.paintStyle,
|
|
468
|
+
connector: this.style.eventConnectorData.connector,
|
|
469
|
+
connectorOverlays:this.style.eventConnectorData.overlays,
|
|
470
|
+
connectorStyle: this.style.eventConnectorData.paintStyle,
|
|
471
|
+
hoverPaintStyle: this.style.eventEndpointData.hoverStyle,
|
|
472
|
+
cssClass : 'event-connection',
|
|
473
|
+
};
|
|
474
|
+
var epStyle = this.style.eventLineSplitterEndpoint;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// create the div endpoint
|
|
478
|
+
var ep = this.jsPlumbInstance.addEndpoint(id, {
|
|
479
|
+
isSource : false,
|
|
480
|
+
isTarget : false,
|
|
481
|
+
uuid : id,
|
|
482
|
+
anchor : "Center",
|
|
483
|
+
maxConnections : 2,
|
|
484
|
+
parameters : c
|
|
485
|
+
}, epStyle);
|
|
486
|
+
|
|
487
|
+
// make the ep transparent to pointer events, left click on it must drag the div
|
|
488
|
+
$(ep.canvas).css('pointer-events', 'none');
|
|
489
|
+
$(ep.canvas).css('cursor', 'move');
|
|
490
|
+
$(ep.canvas).addClass('capture-left-click');
|
|
491
|
+
$(ep.canvas).addClass('capture-right-click');
|
|
492
|
+
$(ep.canvas).find('svg').attr('pointer-events', 'none');
|
|
493
|
+
$(ep.canvas).addClass(id);
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
// save the connection overlays
|
|
497
|
+
var overlays = connection.getOverlays();
|
|
498
|
+
var overlaysParams = [];
|
|
499
|
+
_.each(overlays, (o)=> {
|
|
500
|
+
overlaysParams.push([o.type, {
|
|
501
|
+
"location" : o.loc,
|
|
502
|
+
"width" : o.width,
|
|
503
|
+
"length" : o.length,
|
|
504
|
+
"label" : o.getLabel ? o.getLabel() : undefined,
|
|
505
|
+
"id" : o.id,
|
|
506
|
+
"labelStyle": o.labelStyle
|
|
507
|
+
}]);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
let [sourceEP, targetEP] = connection.getAttachedElements();
|
|
511
|
+
let targetId = connection.targetId;
|
|
512
|
+
|
|
513
|
+
// detach the original connection
|
|
514
|
+
this.jsPlumbInstance.detach(connection, { fireEvent: false, forceDetach: false });
|
|
515
|
+
|
|
516
|
+
// now reattach it into 2 connections
|
|
517
|
+
var c1 = this.jsPlumbInstance.connect({
|
|
518
|
+
source: sourceEP,
|
|
519
|
+
target: ep,
|
|
520
|
+
editable: true,
|
|
521
|
+
}, connectionStyle);
|
|
522
|
+
|
|
523
|
+
var c2 = this.jsPlumbInstance.connect({
|
|
524
|
+
source: ep,
|
|
525
|
+
target: targetEP._jsPlumb ? targetEP : targetId /* EP has been destroyed, reattach to window */,
|
|
526
|
+
editable: true,
|
|
527
|
+
}, connectionStyle);
|
|
528
|
+
|
|
529
|
+
sourceEP.setParameter("targetConnection", c1);
|
|
530
|
+
ep.setParameter("sourceConnection", c1);
|
|
531
|
+
ep.setParameter("targetConnection", c2);
|
|
532
|
+
targetEP.setParameter("sourceConnection", c2);
|
|
533
|
+
|
|
534
|
+
// add the overlays to c2
|
|
535
|
+
_.each(overlaysParams, (p)=> {
|
|
536
|
+
c2.addOverlay(p);
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// add menu to remove this split
|
|
540
|
+
$(ep.canvas).ready(() => {
|
|
541
|
+
var menu = new ContextMenu("#" + id, [
|
|
542
|
+
{
|
|
543
|
+
name: 'Remove the split',
|
|
544
|
+
fn: () => {
|
|
545
|
+
this.unsplitConnection(ep);
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
]);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
return {
|
|
552
|
+
pos: pos,
|
|
553
|
+
c1 : c1,
|
|
554
|
+
c2 : c2
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Remove a split in a connection
|
|
559
|
+
// ep: entrypoint
|
|
560
|
+
// c1: connection 1
|
|
561
|
+
// c2: connection 2
|
|
562
|
+
unsplitConnection(ep) {
|
|
563
|
+
var c = ep.getParameters();
|
|
564
|
+
var c1 = c.sourceConnection;
|
|
565
|
+
var c2 = c.targetConnection;
|
|
566
|
+
var sourceEP = c1.getAttachedElements()[0];
|
|
567
|
+
var targetEP = c2.getAttachedElements()[1];
|
|
568
|
+
|
|
569
|
+
// detach and delete the endpoint
|
|
570
|
+
this.jsPlumbInstance.detach(c1, { fireEvent: false, forceDetach: false });
|
|
571
|
+
this.jsPlumbInstance.detach(c2, { fireEvent: false, forceDetach: false });
|
|
572
|
+
this.jsPlumbInstance.deleteEndpoint(ep);
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
// reconnect directly the source and the target
|
|
576
|
+
var connection = this.jsPlumbInstance.connect({
|
|
577
|
+
source: sourceEP,
|
|
578
|
+
target: targetEP,
|
|
579
|
+
editable: true,
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
sourceEP.setParameter('targetConnection', connection);
|
|
583
|
+
targetEP.setParameter('sourceConnection', connection);
|
|
584
|
+
|
|
585
|
+
// find the position of the split endpoint to remove
|
|
586
|
+
var id = c1.targetId;
|
|
587
|
+
var element = this.jsPlumbInstance.getElement(id)
|
|
588
|
+
var position = this.jsPlumbInstance.getPosition(element);
|
|
589
|
+
|
|
590
|
+
// remove the element in the path
|
|
591
|
+
// 1. find the link
|
|
592
|
+
// 2. find the position of the split endpoint
|
|
593
|
+
if( c.type == "data" ) {
|
|
594
|
+
var link = this.m.getDataLink(c.source.id, c.source.output, c.target.id, c.target.input);
|
|
595
|
+
link.removePathStep(position);
|
|
596
|
+
}
|
|
597
|
+
else if( c.type == "event" ) {
|
|
598
|
+
var link = this.m.getEventLink();
|
|
599
|
+
link.removePathStep(position);
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
throw "unknown connection type";
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// remove the div
|
|
606
|
+
$("#" + id).remove(); // delete the div
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// set the navigation breadcrumb into the canvas
|
|
610
|
+
setNavigation() {
|
|
611
|
+
var appNav = this.div.find('.dualbox-app-navigation');
|
|
612
|
+
var container = appNav.parent();
|
|
613
|
+
appNav.remove();
|
|
614
|
+
|
|
615
|
+
var crumb = $('<ol/>', { class: 'breadcrumb dualbox-app-navigation'});
|
|
616
|
+
_.each(this.m.getWindows(), (w, index, array) => {
|
|
617
|
+
if( index === array.length - 1 ) {
|
|
618
|
+
// last element, don't make it a link
|
|
619
|
+
var li = $('<li/>', { class:"breadcrumb-item" }).append(w[0])
|
|
620
|
+
if( index !== 0 ) {
|
|
621
|
+
// we're in a metanode, add a download button
|
|
622
|
+
var downloadButton = $('<button class="btn btn-xs btn-secondary btn-download-metanode ml-2">' +
|
|
623
|
+
'<i class="fa fa-download"></i></button>');
|
|
624
|
+
downloadButton.click((e) => {
|
|
625
|
+
e.preventDefault();
|
|
626
|
+
e.stopPropagation();
|
|
627
|
+
|
|
628
|
+
var app = this.m.getCurrentMetanode();
|
|
629
|
+
var text = JSON.stringify(app, null, 2);
|
|
630
|
+
var blob = new Blob([text], { "type" : "application/octet-stream" });
|
|
631
|
+
|
|
632
|
+
var a = document.createElement('a');
|
|
633
|
+
a.href = window.URL.createObjectURL(blob);
|
|
634
|
+
a.download = this.m.getCurrentMetanodeName() + ".json";
|
|
635
|
+
|
|
636
|
+
// simulate a click on the link
|
|
637
|
+
if (document.createEvent) {
|
|
638
|
+
var event = document.createEvent("MouseEvents");
|
|
639
|
+
event.initEvent("click", true, true);
|
|
640
|
+
a.dispatchEvent(event);
|
|
641
|
+
} else {
|
|
642
|
+
a.click();
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
li.append(downloadButton)
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
crumb.append(li);
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
// append a link to this metabox
|
|
652
|
+
var link = $('<a/>', { href: '#', class:"text-light" }).append(w[0]);
|
|
653
|
+
link.click((e) => {
|
|
654
|
+
e.preventDefault();
|
|
655
|
+
e.stopPropagation();
|
|
656
|
+
this.c.setWindow(w);
|
|
657
|
+
});
|
|
658
|
+
crumb.append($('<li/>', { class:"breadcrumb-item" }).append(link));
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
container.append(crumb);
|
|
662
|
+
return new Promise((resolve) => { container.ready(resolve) });
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
isLeftMenuExpanded() {
|
|
666
|
+
return this.div.find('.btn-toggle-left-window').data('expanded')
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
isRightMenuExpanded() {
|
|
670
|
+
return this.div.find('.btn-toggle-right-window').data('expanded')
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
openBoxSettings(id) {
|
|
674
|
+
var expandSettings = () => {
|
|
675
|
+
this.div.find('.dualbox-editor-body').trigger('expandSettings');
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
if( id ) {
|
|
679
|
+
this.setBoxMenu(id, expandSettings);
|
|
680
|
+
}
|
|
681
|
+
else {
|
|
682
|
+
this.setMainMenu(expandSettings);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
openDebug(id) {
|
|
687
|
+
var expandDebug = () => {
|
|
688
|
+
this.div.find('.dualbox-editor-body').trigger('expandDebug');
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
this.setDebugMenu(id, expandDebug);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
setBoxMenu(id, cb) {
|
|
695
|
+
var targetDiv = this.div.find('.dualbox-graph-left-panel');
|
|
696
|
+
var expanded = this.getLeftMenuTarget();
|
|
697
|
+
targetDiv.html('');
|
|
698
|
+
this.templateMgr.appendTemplate(targetDiv, "editNodeSettings", { node: this.m.getNode(id) }, () => {
|
|
699
|
+
this.setLeftMenuTarget(expanded);
|
|
700
|
+
if(cb) cb();
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
setMainMenu(cb) {
|
|
705
|
+
var targetDiv = this.div.find('.dualbox-graph-left-panel');
|
|
706
|
+
var expanded = this.getLeftMenuTarget();
|
|
707
|
+
targetDiv.html('');
|
|
708
|
+
this.templateMgr.appendTemplate(targetDiv, "editMainSettings", { app : this.m.getCurrentApp() }, () => {
|
|
709
|
+
this.setLeftMenuTarget(expanded);
|
|
710
|
+
if(cb) cb();
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
setDebugMenu(id, cb) {
|
|
715
|
+
var targetDiv = this.div.find('.dualbox-graph-right-panel');
|
|
716
|
+
var expanded = this.getRightMenuTarget();
|
|
717
|
+
targetDiv.html('');
|
|
718
|
+
this.templateMgr.appendTemplate(targetDiv, "debugNodeInfos", { m : this.m, snapshot : this.m.get(), node: this.m.getNode(id) }, () => {
|
|
719
|
+
this.setRightMenuTarget(expanded);
|
|
720
|
+
if(cb) cb();
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// save the actual position of where we are in the menu
|
|
725
|
+
getLeftMenuTarget() {
|
|
726
|
+
var expanded = [];
|
|
727
|
+
var panel = this.div.find('.dualbox-graph-left-panel');
|
|
728
|
+
panel.find('div[aria-expanded="true"]').each(function() {
|
|
729
|
+
expanded.push($(this).attr('id'));
|
|
730
|
+
});
|
|
731
|
+
return expanded;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// restore where we were before in the menu
|
|
735
|
+
setLeftMenuTarget(expanded) {
|
|
736
|
+
var panel = this.div.find('.dualbox-graph-left-panel');
|
|
737
|
+
var divs = panel.find('div[data-toggle="collapse"]').each(function() {
|
|
738
|
+
if( expanded.indexOf( $(this).attr('id') ) !== -1 ) {
|
|
739
|
+
// this menu was open, restore it
|
|
740
|
+
var dataTarget = $(this).attr('data-target');
|
|
741
|
+
$(dataTarget).collapse('show');
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// save the actual position of where we are in the menu
|
|
747
|
+
getRightMenuTarget() {
|
|
748
|
+
var expanded = [];
|
|
749
|
+
var panel = this.div.find('.dualbox-graph-right-panel');
|
|
750
|
+
panel.find('div[aria-expanded="true"]').each(function() {
|
|
751
|
+
expanded.push($(this).attr('id'));
|
|
752
|
+
});
|
|
753
|
+
return expanded;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// restore where we were before in the menu
|
|
757
|
+
setRightMenuTarget(expanded) {
|
|
758
|
+
var panel = this.div.find('.dualbox-graph-right-panel');
|
|
759
|
+
var divs = panel.find('div[data-toggle="collapse"]').each(function() {
|
|
760
|
+
if( expanded.indexOf( $(this).attr('id') ) !== -1 ) {
|
|
761
|
+
// this menu was open, restore it
|
|
762
|
+
var dataTarget = $(this).attr('data-target');
|
|
763
|
+
$(dataTarget).collapse('show');
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// reinitialize the view
|
|
769
|
+
reset() {
|
|
770
|
+
this.jsPlumbInstance.reset();
|
|
771
|
+
this.jsPlumbInstance = jsPlumb.getInstance({
|
|
772
|
+
DragOptions: { cursor: 'pointer', zIndex: 2500 }, // default drag options
|
|
773
|
+
Container: "dualbox-graph-canvas"
|
|
774
|
+
});
|
|
775
|
+
this.style.setDefault();
|
|
776
|
+
this.canvas.empty();
|
|
777
|
+
ContextMenu.clean();
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
// reset graph view and repaint from the graph model
|
|
782
|
+
async repaint() {
|
|
783
|
+
await this.hideCanvas();
|
|
784
|
+
|
|
785
|
+
this.reset();
|
|
786
|
+
await this.setNavigation();
|
|
787
|
+
|
|
788
|
+
// Function to gather all links from a node into the "links" array
|
|
789
|
+
var links = []; // array of links
|
|
790
|
+
var gatherLinks = (targetId, sourceDef) => {
|
|
791
|
+
// for nodes and uis
|
|
792
|
+
_.each(sourceDef.links, (sources, targetInput) => {
|
|
793
|
+
_.each(sources, (sourceOutput, sourceId) => {
|
|
794
|
+
if( sourceId === "graph" ) {
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if( sourceId === "input" || sourceId === "output" ) {
|
|
799
|
+
sourceId = this.m.prefixId(sourceOutput, sourceId);
|
|
800
|
+
sourceOutput = "value";
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
links.push({
|
|
804
|
+
sourceId: sourceId,
|
|
805
|
+
sourceOutput: sourceOutput,
|
|
806
|
+
targetId: targetId,
|
|
807
|
+
targetInput: targetInput
|
|
808
|
+
});
|
|
809
|
+
});
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
// for inputs and outputs
|
|
813
|
+
_.each(sourceDef.link, (sourceOutput, sourceId) => {
|
|
814
|
+
if( sourceId === "graph" ) return;
|
|
815
|
+
if( sourceId === "input" || sourceId === "output" ) {
|
|
816
|
+
sourceId = this.m.prefixId(sourceOutput, sourceId);
|
|
817
|
+
sourceOutput = "value";
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
links.push({
|
|
821
|
+
sourceId: sourceId,
|
|
822
|
+
sourceOutput: sourceOutput,
|
|
823
|
+
targetId: targetId,
|
|
824
|
+
targetInput: "value"
|
|
825
|
+
});
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// Function to gather all links from a node into the "links" array
|
|
830
|
+
var events = [];
|
|
831
|
+
var gatherEvents = (sourceId, def) => {
|
|
832
|
+
_.each(def.events, (evt) => {
|
|
833
|
+
if( evt.node ) {
|
|
834
|
+
events.push({
|
|
835
|
+
sourceId: sourceId,
|
|
836
|
+
targetId: evt.node,
|
|
837
|
+
name: evt.event
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
var app = this.m.getCurrentWindow();
|
|
844
|
+
var addNodePromises = []; // array of promises that will be resolved once all node are added
|
|
845
|
+
|
|
846
|
+
var processModule = (def, id) => {
|
|
847
|
+
addNodePromises.push( new Promise( async (resolve) => {
|
|
848
|
+
var pkg = this.m.getNode(id).getPackage();
|
|
849
|
+
await this.addNode(id, pkg, def); // add the node
|
|
850
|
+
gatherLinks(id, def); // add all data links in our array
|
|
851
|
+
gatherEvents(id, def); // add all event links in our array
|
|
852
|
+
resolve();
|
|
853
|
+
}));
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
var processInput = (def, id) => {
|
|
857
|
+
addNodePromises.push( new Promise( async (resolve) => {
|
|
858
|
+
id = this.m.prefixId(id, "input");
|
|
859
|
+
var pkg = this.m.getNode(id).getPackage();
|
|
860
|
+
await this.addNode(id, pkg, def); // add the node
|
|
861
|
+
gatherLinks(id, def); // add all data links in our array
|
|
862
|
+
gatherEvents(id, def); // add all event links in our array
|
|
863
|
+
resolve();
|
|
864
|
+
}));
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
var processOutput = (def, id) => {
|
|
868
|
+
addNodePromises.push( new Promise( async (resolve) => {
|
|
869
|
+
id = this.m.prefixId(id, "output");
|
|
870
|
+
var pkg = this.m.getNode(id).getPackage();
|
|
871
|
+
await this.addNode(id, pkg, def); // add the node
|
|
872
|
+
gatherLinks(id, def); // add all data links in our array
|
|
873
|
+
gatherEvents(id, def); // add all event links in our array
|
|
874
|
+
resolve();
|
|
875
|
+
}));
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
return new Promise(async (resolve) => {
|
|
879
|
+
// All nodes are now added
|
|
880
|
+
await this.zoomer.deactivate();
|
|
881
|
+
_.each(app.modules, processModule);
|
|
882
|
+
_.each(app.ui, processModule);
|
|
883
|
+
_.each(app.input, processInput);
|
|
884
|
+
_.each(app.output, processOutput);
|
|
885
|
+
|
|
886
|
+
Promise.all(addNodePromises).then(async () => {
|
|
887
|
+
// add the links
|
|
888
|
+
_.each(links, (link) => {
|
|
889
|
+
this.addLink(link.sourceId, link.sourceOutput, link.targetId, link.targetInput);
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
// add the events
|
|
893
|
+
if( this.showEvents ) {
|
|
894
|
+
_.each(events, (e) => {
|
|
895
|
+
this.addEvent(e.sourceId, e.targetId, e.name);
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
await this.canvasSizeHandler.resize();
|
|
900
|
+
await this.zoomer.activate();
|
|
901
|
+
// Initialize all listeners again
|
|
902
|
+
this.initializeListeners();
|
|
903
|
+
await this.setEventMode(this.showEvents);
|
|
904
|
+
|
|
905
|
+
await this.showCanvas();
|
|
906
|
+
resolve();
|
|
907
|
+
});
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
runApp(options) {
|
|
912
|
+
var json = this.m.getEditorCompatibleJson();
|
|
913
|
+
this.appManager.run(json, options);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
takeAndLoadSnapshot() {
|
|
917
|
+
var json = this.appManager.getSnapshot();
|
|
918
|
+
// if we're not on the 1st tab, go there
|
|
919
|
+
this.div.find("a[data-toggle='tab'][href='#1']").click();
|
|
920
|
+
this.e.setApp(json);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
killApp() {
|
|
924
|
+
this.appManager.kill();
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
module.exports = GraphView;
|