@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,2167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Implementing the Model of the GraphEditor
|
|
3
|
+
* Everything related to Dualbox Intelligence and validation should be here
|
|
4
|
+
*/
|
|
5
|
+
const History = require('./History');
|
|
6
|
+
const AppParser = require('@dualbox/dualbox-lib-appparser');
|
|
7
|
+
const utils = require('./DualboxUtils');
|
|
8
|
+
const _ = require('lodash');
|
|
9
|
+
const idx = require('idx');
|
|
10
|
+
const swal = require('sweetalert2');
|
|
11
|
+
|
|
12
|
+
class GraphModel {
|
|
13
|
+
constructor(editor) {
|
|
14
|
+
this.e = editor;
|
|
15
|
+
|
|
16
|
+
// the internal structure
|
|
17
|
+
this.data = {
|
|
18
|
+
root: {
|
|
19
|
+
modules : {},
|
|
20
|
+
input : {},
|
|
21
|
+
output : {},
|
|
22
|
+
metanodes : {},
|
|
23
|
+
ui : {},
|
|
24
|
+
events : {},
|
|
25
|
+
css : "",
|
|
26
|
+
interface : {},
|
|
27
|
+
dependencies : {},
|
|
28
|
+
libs: {}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
// Pointer that changes when we view/edit metanodes
|
|
32
|
+
// All methodes are relative to this pointer, so we just need to change it
|
|
33
|
+
// to enter/exit a metanode
|
|
34
|
+
app : null,
|
|
35
|
+
|
|
36
|
+
// Array of pointers to manage navigation between metanodes
|
|
37
|
+
windows : null
|
|
38
|
+
};
|
|
39
|
+
this.data.app = this.data.root;
|
|
40
|
+
this.data.windows = [[ "Application", this.data.app ]];
|
|
41
|
+
|
|
42
|
+
// An object to save/restore different states of this.data (ctrl-z/ctrl-y)
|
|
43
|
+
this.history = new History(this);
|
|
44
|
+
this.history.save();
|
|
45
|
+
|
|
46
|
+
this.inputPrefix = "in-";
|
|
47
|
+
this.outputPrefix = "out-";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/***
|
|
51
|
+
* JSON Import/Export
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
async load( json ) {
|
|
55
|
+
this.check(json);
|
|
56
|
+
|
|
57
|
+
this.data.root = json;
|
|
58
|
+
this.data.app = this.data.root; // reset ptr
|
|
59
|
+
this.data.windows = [[ "Application", this.data.app ]];
|
|
60
|
+
|
|
61
|
+
await this.addNativeTypes();
|
|
62
|
+
|
|
63
|
+
this.history.save();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
addNativeTypes() {
|
|
67
|
+
var natives = [
|
|
68
|
+
"@dualbox/dualbox-type-number",
|
|
69
|
+
"@dualbox/dualbox-type-boolean",
|
|
70
|
+
"@dualbox/dualbox-type-string",
|
|
71
|
+
"@dualbox/dualbox-type-array",
|
|
72
|
+
"@dualbox/dualbox-type-object",
|
|
73
|
+
"@dualbox/dualbox-type-buffer",
|
|
74
|
+
"@dualbox/dualbox-type-map",
|
|
75
|
+
"@dualbox/dualbox-type-json",
|
|
76
|
+
"@dualbox/dualbox-type-regexp",
|
|
77
|
+
"@dualbox/dualbox-type-nativearray",
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
var promises = [];
|
|
81
|
+
_.each(natives, (pkgName) => {
|
|
82
|
+
promises.push( new Promise((resolve, reject) => {
|
|
83
|
+
this.e.loadPackage(pkgName).then((pkg) => {
|
|
84
|
+
if( !idx(this.data.root, o=>o.libs[pkgName]) ) {
|
|
85
|
+
this.data.root.libs = this.data.root.libs || {};
|
|
86
|
+
this.data.root.libs[pkgName] = pkg.version;
|
|
87
|
+
}
|
|
88
|
+
resolve();
|
|
89
|
+
}).catch((e) => {
|
|
90
|
+
console.error('Could not load package ' + pkgName + ': ' + e);
|
|
91
|
+
});
|
|
92
|
+
}));
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return Promise.all(promises);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get() {
|
|
99
|
+
return this.data.root;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getCurrentApp() {
|
|
103
|
+
return new Application( this, this.getCurrentMetanode(), this.getCurrentMetanodeName() );
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
getCurrentMetanode() {
|
|
107
|
+
return this.data.app;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getCurrentMetanodeName() {
|
|
111
|
+
var windows = this.data.windows;
|
|
112
|
+
return windows[ windows.length-1 ][0];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getTypes() {
|
|
116
|
+
var libs = _.cloneDeep(this.data.root.libs);
|
|
117
|
+
libs = _.filter(_.keys(libs), l => l.startsWith('@dualbox/dualbox-type') );
|
|
118
|
+
|
|
119
|
+
// now require Dualbox, then all the types, and extract the list from DualBox.Type.types
|
|
120
|
+
var DualBox = require('@dualbox/dualbox');
|
|
121
|
+
for(var i=0; i<libs.length; i++) { // use for loop to require in the same context as DualBox object
|
|
122
|
+
require(libs[i]);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return _.cloneDeep(DualBox.Type.types);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// return a editor-compatible json version of the app
|
|
129
|
+
// - remove the web workers
|
|
130
|
+
getEditorCompatibleJson() {
|
|
131
|
+
var json = _.cloneDeep(this.data.root);
|
|
132
|
+
var parser = new AppParser(json);
|
|
133
|
+
parser.eachModule((json, id) => {
|
|
134
|
+
if( json.parallel ) {
|
|
135
|
+
json.parallel = false;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
if( json.snapshot == true ) {
|
|
140
|
+
parser.eachNode((json, id) => {
|
|
141
|
+
delete json.snapshot;
|
|
142
|
+
});
|
|
143
|
+
json.snapshot = false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return json;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
getCleanJson() {
|
|
150
|
+
var json = _.cloneDeep(this.data.root);
|
|
151
|
+
var parser = new AppParser(json);
|
|
152
|
+
console.log('Getting clean json');
|
|
153
|
+
parser.eachNode((json, id) => {
|
|
154
|
+
delete json.snapshot;
|
|
155
|
+
});
|
|
156
|
+
parser.eachMetanode((json) => {
|
|
157
|
+
delete json.snapshot;
|
|
158
|
+
});
|
|
159
|
+
delete json.snapshot;
|
|
160
|
+
delete json.snapshotObjects;
|
|
161
|
+
|
|
162
|
+
return json;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
getWindows() {
|
|
166
|
+
return this.data.windows;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
getCurrentWindow() {
|
|
170
|
+
return this.data.app;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
getPanels() {
|
|
174
|
+
return _.keys(this.data.root.interface);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
setWindow(name) {
|
|
178
|
+
var newWindows = [];
|
|
179
|
+
_.each(this.data.windows, (w) => {
|
|
180
|
+
newWindows.push(w);
|
|
181
|
+
if( w[0] === name ) {
|
|
182
|
+
this.data.app = w[1];
|
|
183
|
+
return false; // end of loop
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
this.data.windows = newWindows;
|
|
188
|
+
this.history.save();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// performs some checks on the json
|
|
192
|
+
check( json ) {
|
|
193
|
+
var parser = new AppParser(json);
|
|
194
|
+
var nbErrors = 0;
|
|
195
|
+
parser.eachInput((json, id) => {
|
|
196
|
+
if( _.keys(json.events).length !== 0 ) {
|
|
197
|
+
console.error("Input " + id + " has events but shouldnt");
|
|
198
|
+
nbErrors++;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
parser.eachOutput((json, id) => {
|
|
202
|
+
if( _.keys(json.events).length !== 0 ) {
|
|
203
|
+
console.error("Output " + id + " has events but shouldnt");
|
|
204
|
+
nbErrors++;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
parser.eachModule((json, id) => {
|
|
208
|
+
if( _.keys(json.events).length !== 0 ) {
|
|
209
|
+
console.error("Module " + id + " has events but shouldnt");
|
|
210
|
+
nbErrors++;
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
if( nbErrors ) {
|
|
214
|
+
swal("Errors found!", nbErrors + " errors found while parsing the loaded app. Please check console.", "error");
|
|
215
|
+
throw nbErrors + " errors found while parsing the loaded app";
|
|
216
|
+
}
|
|
217
|
+
return json;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Json security methods
|
|
222
|
+
*/
|
|
223
|
+
ensure( field ) {
|
|
224
|
+
this.data.app[field] = this.data.app[field] || {};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Metanodes navigation
|
|
229
|
+
*/
|
|
230
|
+
enterMetanode(id, savedZoom) {
|
|
231
|
+
var node = this.data.app.modules[id];
|
|
232
|
+
if( !node ) {
|
|
233
|
+
throw "couldn't find node " + id;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
var name = node.module;
|
|
237
|
+
var metanode = this.data.app.metanodes[name];
|
|
238
|
+
if( !metanode ) {
|
|
239
|
+
throw "couldn't find definition for metanode " + name;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// save current zoom and position in the current window
|
|
243
|
+
this.data.windows[ this.data.windows.length - 1 ][2] = savedZoom;
|
|
244
|
+
|
|
245
|
+
// switch to the next window
|
|
246
|
+
this.data.app = metanode;
|
|
247
|
+
this.data.windows.push([ name, metanode ]);
|
|
248
|
+
this.history.save();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/***
|
|
252
|
+
* Dualbox model
|
|
253
|
+
*/
|
|
254
|
+
addMetanode(name, def) {
|
|
255
|
+
this.ensure('metanodes');
|
|
256
|
+
this.data.app.metanodes[name] = def;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
addInput(name, type, desc) {
|
|
260
|
+
var input = {
|
|
261
|
+
"type": type,
|
|
262
|
+
"desc": desc
|
|
263
|
+
};
|
|
264
|
+
this.ensure('input');
|
|
265
|
+
this.data.app.input[name] = input;
|
|
266
|
+
this.history.save();
|
|
267
|
+
return input;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
addOutput(name, type, desc) {
|
|
271
|
+
var output = {
|
|
272
|
+
"type": type,
|
|
273
|
+
"desc": desc
|
|
274
|
+
};
|
|
275
|
+
this.ensure('output');
|
|
276
|
+
this.data.app.output[name] = output;
|
|
277
|
+
this.history.save();
|
|
278
|
+
return output;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
hasMetanode(name) {
|
|
282
|
+
return idx(this.data, o=>o.app.metanodes[name]) !== undefined;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
isRootApp( app ) {
|
|
286
|
+
return this.data.root == app;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
getMetanodeInstances( app ) {
|
|
290
|
+
// first identify metanode name
|
|
291
|
+
var metanodeName = null;
|
|
292
|
+
_.each(this.data.app.metanodes, (metanode, name) => {
|
|
293
|
+
if( app == metanode ) {
|
|
294
|
+
metanodeName = name;
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
var instances = [];
|
|
300
|
+
_.each(this.data.app.modules, (json, id) => {
|
|
301
|
+
if( json.module == metanodeName ) {
|
|
302
|
+
instances.push( this.getNode(id) );
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
return instances;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
addNode(id, pkg) {
|
|
310
|
+
console.log('adding node: ' + id);
|
|
311
|
+
|
|
312
|
+
var desc = {
|
|
313
|
+
"module" : pkg.name,
|
|
314
|
+
"version": pkg.version || "*",
|
|
315
|
+
"id" : id
|
|
316
|
+
}
|
|
317
|
+
if( utils.isModule( pkg.name ) ) {
|
|
318
|
+
this.ensure('modules');
|
|
319
|
+
if( this.data.app.modules[id] ) {
|
|
320
|
+
console.error('Could not add module ' + id + ': already exists');
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
var node = this.data.app.modules[id] = desc;
|
|
324
|
+
}
|
|
325
|
+
else if( utils.isUI( pkg.name ) ) {
|
|
326
|
+
this.ensure('ui');
|
|
327
|
+
if( this.data.app.ui[id] ) {
|
|
328
|
+
console.error('Could not add ui ' + id + ': already exists');
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
var node = this.data.app.ui[id] = desc;
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
// may be a metanode
|
|
335
|
+
var nodeDef = idx(this.data.app, o => o.metanodes[pkg.name]);
|
|
336
|
+
if( nodeDef ) {
|
|
337
|
+
this.ensure('modules');
|
|
338
|
+
var node = this.data.app.modules[id] = desc;
|
|
339
|
+
return node;
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
throw pkg.name + " is not module or ui, can't add node !";
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
this.history.save();
|
|
347
|
+
return node;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
addNodeToPanel(id, panelName) {
|
|
351
|
+
var panel = this.data.root.interface[panelName];
|
|
352
|
+
if( !panel ) {
|
|
353
|
+
throw "Panel " + panelName + " doesn't exist in the application";
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if( !panel.children ) panel.children = [];
|
|
357
|
+
panel.children.push({
|
|
358
|
+
"type": "Element",
|
|
359
|
+
"tagName": "div",
|
|
360
|
+
"attributes": {
|
|
361
|
+
"className": [
|
|
362
|
+
"dualbox",
|
|
363
|
+
"dualbox-component"
|
|
364
|
+
],
|
|
365
|
+
"dataset": {
|
|
366
|
+
"node": id
|
|
367
|
+
},
|
|
368
|
+
"style": {
|
|
369
|
+
"width": "auto",
|
|
370
|
+
"height": "auto"
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
"children": []
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
addNodeFromDef(id, type, def) {
|
|
378
|
+
switch( type ) {
|
|
379
|
+
case "module":
|
|
380
|
+
this.ensure('modules');
|
|
381
|
+
this.data.app.modules[id] = def;
|
|
382
|
+
break;
|
|
383
|
+
case "ui":
|
|
384
|
+
this.ensure('ui');
|
|
385
|
+
this.data.app.ui[id] = def;
|
|
386
|
+
break;
|
|
387
|
+
default:
|
|
388
|
+
throw type + " is not module or ui, error";
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
addDataLink( sourceId, sourceOutput, targetId, targetInput ) {
|
|
393
|
+
var link = new DataLink(this, sourceId, sourceOutput, targetId, targetInput);
|
|
394
|
+
link.attach();
|
|
395
|
+
this.history.save();
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
removeDataLink( sourceId, sourceOutput, targetId, targetInput ) {
|
|
400
|
+
var link = new DataLink(this, sourceId, sourceOutput, targetId, targetInput);
|
|
401
|
+
link.detach();
|
|
402
|
+
this.history.save();
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
getDataLink( sourceId, sourceOutput, targetId, targetInput ) {
|
|
407
|
+
return new DataLink(this, sourceId, sourceOutput, targetId, targetInput);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
addEventLink( sourceId, targetId, eventName ) {
|
|
411
|
+
var link = new EventLink(this, sourceId, targetId, eventName);
|
|
412
|
+
link.attach();
|
|
413
|
+
this.history.save();
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
removeEventLink( sourceId, targetId, eventName ) {
|
|
418
|
+
var link = new EventLink(this, sourceId, targetId, eventName);
|
|
419
|
+
link.detach();
|
|
420
|
+
this.history.save();
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
getEventLink( sourceId, targetId, eventName ) {
|
|
425
|
+
return new EventLink(this, sourceId, targetId, eventName);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// check if metanode def is used by at least 1 module
|
|
429
|
+
isMetanodeUsed( name ) {
|
|
430
|
+
var stillUsed = false;
|
|
431
|
+
_.each(this.data.app.modules, (n) => {
|
|
432
|
+
if( n.module === name ) {
|
|
433
|
+
stillUsed = true;
|
|
434
|
+
return false; // eol
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
return stillUsed;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
/***
|
|
442
|
+
* Helpers
|
|
443
|
+
*/
|
|
444
|
+
undo() {
|
|
445
|
+
this.history.undo();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
redo() {
|
|
449
|
+
this.history.redo();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
getNode(id, example=false) {
|
|
453
|
+
return new GraphNode(this, id);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
getNodes( type ) {
|
|
457
|
+
var keys = null;
|
|
458
|
+
switch(type) {
|
|
459
|
+
case "input":
|
|
460
|
+
keys = _.keys( this.data.app.input );
|
|
461
|
+
keys = _.map(keys, k => this.inputPrefix + k);
|
|
462
|
+
break;
|
|
463
|
+
case "output":
|
|
464
|
+
keys = _.keys( this.data.app.output );
|
|
465
|
+
keys = _.map(keys, k => this.outputPrefix + k);
|
|
466
|
+
break;
|
|
467
|
+
case "modules":
|
|
468
|
+
keys = _.keys( this.data.app.modules );
|
|
469
|
+
break;
|
|
470
|
+
case "ui":
|
|
471
|
+
keys = _.keys( this.data.app.ui );
|
|
472
|
+
break;
|
|
473
|
+
default:
|
|
474
|
+
if( type === undefined ) {
|
|
475
|
+
keys = [].concat( _.map(_.keys(this.data.app.input), k=>this.inputPrefix+k) )
|
|
476
|
+
.concat( _.map(_.keys(this.data.app.output), k=>this.outputPrefix+k) )
|
|
477
|
+
.concat( _.keys(this.data.app.modules) )
|
|
478
|
+
.concat( _.keys(this.data.app.ui) );
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
throw new Error('unknown node type: ' + type);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
var nodes = [];
|
|
486
|
+
_.each(keys, (k) => {
|
|
487
|
+
nodes.push( this.getNode(k) );
|
|
488
|
+
});
|
|
489
|
+
return nodes;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
getSpecialUINodes( type ) {
|
|
493
|
+
var specialNodes = [];
|
|
494
|
+
var nodes = this.getNodes('ui');
|
|
495
|
+
_.each(nodes, (n) => {
|
|
496
|
+
if( n.getSpecialType() == type ) {
|
|
497
|
+
specialNodes.push(n);
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
return specialNodes;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
createExampleNode(pkgName, id) {
|
|
504
|
+
var model = _.cloneDeep(this);
|
|
505
|
+
|
|
506
|
+
try {
|
|
507
|
+
return model.getNode(id);
|
|
508
|
+
}
|
|
509
|
+
catch(e) {
|
|
510
|
+
model.addNode(id, this.e.getPackage(pkgName));
|
|
511
|
+
return model.getNode(id);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
compareTypes(t1, t2) {
|
|
517
|
+
t1 = t1.toLowerCase();
|
|
518
|
+
t2 = t2.toLowerCase();
|
|
519
|
+
var deepCompare = function(t1,t2){
|
|
520
|
+
if(t1 === t2 || t1==="*" || t2==="*"){
|
|
521
|
+
return true;
|
|
522
|
+
} else if(t1.search("array<") === 0 && t2.search("array<") === 0) {
|
|
523
|
+
return deepCompare(t1.slice(6,t1.length-1), t2.slice(6,t2.length-1));
|
|
524
|
+
} else if(t1.search("map<string,") === 0 && t2.search("map<string,") === 0) {
|
|
525
|
+
return deepCompare(t1.slice(11,t1.length-1), t2.slice(11,t2.length-1));
|
|
526
|
+
} else {
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return deepCompare(t1, t2);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
prefixId( id, type ) {
|
|
535
|
+
if( type == "input" && !id.startsWith('in-') ) {
|
|
536
|
+
return this.inputPrefix + id;
|
|
537
|
+
}
|
|
538
|
+
else if( type == "output" && !id.startsWith('out-') ) {
|
|
539
|
+
return this.outputPrefix + id;
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
return id;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
getSnapshotValue( v ) {
|
|
547
|
+
if( typeof v == "string" && v.startsWith('____dualbox_storage') ) {
|
|
548
|
+
var id = v.replace("____dualbox_storage(", "").slice(0, -1);
|
|
549
|
+
return this.data.root.snapshotObjects[id];
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
return v;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
/***
|
|
559
|
+
* Class to gather all utility functions for a node
|
|
560
|
+
*/
|
|
561
|
+
class GraphNode {
|
|
562
|
+
constructor(m, id) {
|
|
563
|
+
this.m = m;
|
|
564
|
+
this.app = this.m.data.app;
|
|
565
|
+
this.id = id;
|
|
566
|
+
|
|
567
|
+
if( id.startsWith(this.m.inputPrefix) ) {
|
|
568
|
+
this.type = "input";
|
|
569
|
+
this.graphId = id.substring(3); // the id to identify it in the dualbox graph
|
|
570
|
+
this.def = this.app.input[this.graphId];
|
|
571
|
+
}
|
|
572
|
+
else if( id.startsWith(this.m.outputPrefix) ) {
|
|
573
|
+
this.type = "output";
|
|
574
|
+
this.graphId = id.substring(4);
|
|
575
|
+
this.def = this.app.output[this.graphId];
|
|
576
|
+
}
|
|
577
|
+
else if(this.app.modules && this.app.modules[id]) {
|
|
578
|
+
this.graphId = id;
|
|
579
|
+
this.type = "module";
|
|
580
|
+
this.def = this.app.modules[this.graphId];
|
|
581
|
+
}
|
|
582
|
+
else if(this.app.ui && this.app.ui[id]) {
|
|
583
|
+
this.graphId = id;
|
|
584
|
+
this.type = "ui";
|
|
585
|
+
this.def = this.app.ui[this.graphId];
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
throw "Cant find node " + id;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/***
|
|
593
|
+
* Getters
|
|
594
|
+
*/
|
|
595
|
+
|
|
596
|
+
hasSnapshot() {
|
|
597
|
+
return this.def.snapshot !== undefined;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
getSnapshot() {
|
|
601
|
+
return this.def.snapshot;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
isSnapshotStatus( status ) {
|
|
605
|
+
return this.def.snapshot.state.status == status;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
getGraphId() {
|
|
609
|
+
return this.graphId;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
getPackageName() {
|
|
613
|
+
if( this.isInput() ) return "input (" + this.getType() + ")";
|
|
614
|
+
else if( this.isOutput() ) return "output (" + this.getType() + ")";
|
|
615
|
+
else if( this.isUI() ) return "UI (" + utils.shortName(this.getPackage().name) + ")";
|
|
616
|
+
else if( this.isMetanode() ) return "Metabox (" + utils.shortName(this.getPackage().name) + ")";
|
|
617
|
+
else return "Module (" + utils.shortName(this.getPackage().name) + ")";
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// ONLY VALID for input/output nodes
|
|
621
|
+
getType() {
|
|
622
|
+
if( !this.isInput() && !this.isOutput() ) throw this.id + " is not a I/O node...";
|
|
623
|
+
return this.def.type;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// return the input links for a node
|
|
627
|
+
getNormalizeLinksDef() {
|
|
628
|
+
return this.type == "input" || this.type == "output" ?
|
|
629
|
+
{ "value" : this.def.link } :
|
|
630
|
+
this.def.links;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// return the app definition of the node
|
|
634
|
+
getDef() {
|
|
635
|
+
return this.def;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
getInputDef(input) {
|
|
639
|
+
var pkg = this.getPackage();
|
|
640
|
+
return idx(pkg, o => o.dualbox.input[input]);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
getInputDesc(input) {
|
|
644
|
+
var pkg = this.getPackage();
|
|
645
|
+
return idx(pkg, o => o.dualbox.input[input].desc);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
getOutputDesc(output) {
|
|
649
|
+
var pkg = this.getPackage();
|
|
650
|
+
return idx(pkg, o => o.dualbox.output[output].desc);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// return the package/metanode description
|
|
654
|
+
getPackage() {
|
|
655
|
+
if( this.isMetanode() ) {
|
|
656
|
+
var def = this.app.metanodes[this.def.module];
|
|
657
|
+
if( def === undefined ) {
|
|
658
|
+
throw id + " is not a metanode";
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
var r = {
|
|
662
|
+
name: this.def.module,
|
|
663
|
+
version: "*",
|
|
664
|
+
description: def.description,
|
|
665
|
+
dualbox: {
|
|
666
|
+
input: def.input,
|
|
667
|
+
output: def.output,
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return r;
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
switch(this.type) {
|
|
674
|
+
case "input":
|
|
675
|
+
case "output":
|
|
676
|
+
return this.m.e.getPackage('dualbox-core-value');
|
|
677
|
+
default: // "module", "ui"
|
|
678
|
+
return this.m.e.getPackage(this.def.module);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
getInputType(inputName) {
|
|
684
|
+
if( (this.isInput() || this.isOutput()) && inputName == "value" ) {
|
|
685
|
+
return this.getType();
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
var pkg = this.getPackage();
|
|
689
|
+
var type = pkg && pkg.dualbox && pkg.dualbox.input && pkg.dualbox.input[inputName] && pkg.dualbox.input[inputName].type || "*";
|
|
690
|
+
return this.hasIterator(inputName) ? "Array<"+type+">" : type;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
hasInputDesc(inputName) {
|
|
695
|
+
return this.getInputDesc(inputName) !== "";
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
getInputDesc(inputName) {
|
|
699
|
+
var pkg = this.getPackage();
|
|
700
|
+
return pkg && pkg.dualbox && pkg.dualbox.input && pkg.dualbox.input[inputName] && pkg.dualbox.input[inputName].desc || "";
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// only if this.isInput() == true
|
|
704
|
+
getDefaultValue() {
|
|
705
|
+
return this.def.value;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// only if this.isInput() == true
|
|
709
|
+
setDefaultValue(v) {
|
|
710
|
+
this.def.value = v;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// only if this.isInput() == true, or this.isOutput() == true
|
|
714
|
+
getDescription() {
|
|
715
|
+
if( this.def.desc ) {
|
|
716
|
+
return this.def.desc;
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
var pkg = this.getPackage();
|
|
720
|
+
return pkg && pkg.description;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
getInputDefaultValue(inputName) {
|
|
725
|
+
var pkg = this.getPackage();
|
|
726
|
+
var pkgDefault = pkg && pkg.dualbox && pkg.dualbox.input && pkg.dualbox.input[inputName] && pkg.dualbox.input[inputName].value;
|
|
727
|
+
var appDefault = this.def.defaultInputs && this.def.defaultInputs[inputName];
|
|
728
|
+
|
|
729
|
+
if( appDefault !== undefined ) { return appDefault; }
|
|
730
|
+
else if( pkgDefault !== undefined ) { return pkgDefault; }
|
|
731
|
+
else return undefined;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
setInputDefaultValue(inputName, val) {
|
|
735
|
+
this.def.defaultInputs = this.def.defaultInputs || {};
|
|
736
|
+
this.def.defaultInputs[inputName] = val;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
hasDefaultValue(inputName) {
|
|
740
|
+
var pkg = this.getPackage();
|
|
741
|
+
var pkgDefault = pkg && pkg.dualbox && pkg.dualbox.input && pkg.dualbox.input[inputName] && pkg.dualbox.input[inputName].value;
|
|
742
|
+
var appDefault = this.def.defaultInputs && this.def.defaultInputs[inputName];
|
|
743
|
+
return appDefault !== undefined || pkgDefault !== undefined;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
getOutputType(outputName) {
|
|
747
|
+
if( (this.isInput() || this.isOutput()) && outputName == "value" ) {
|
|
748
|
+
return this.getType();
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
var pkg = this.getPackage();
|
|
752
|
+
var type = idx(pkg, o => o.dualbox.output[outputName].type) || "*";
|
|
753
|
+
if( this.hasLoop() ) {
|
|
754
|
+
// if this output is not a feedback, "arrayize" it
|
|
755
|
+
return this.hasFeedback(outputName) ? type : "Array<" + type + ">";
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
return type;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
getOutputDesc(outputName) {
|
|
764
|
+
var pkg = this.getPackage();
|
|
765
|
+
return pkg && pkg.dualbox && pkg.dualbox.output && pkg.dualbox.output[outputName] && pkg.dualbox.output[outputName].desc || "";
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
hasOutputDesc(outputName) {
|
|
769
|
+
return this.getOutputDesc(outputName) !== "";
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
getAttributeDesc( attrName ) {
|
|
773
|
+
var pkg = this.getPackage();
|
|
774
|
+
return pkg && pkg.dualbox && pkg.dualbox.attr && pkg.dualbox.attr[attrName] && pkg.dualbox.attr[attrName].desc || "";
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
hasAttributeDesc( attrName ) {
|
|
778
|
+
return this.getAttributeDesc(attrName) !== "";
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
getAttributeType( attrName ) {
|
|
782
|
+
var pkg = this.getPackage();
|
|
783
|
+
return pkg && pkg.dualbox && pkg.dualbox.output && pkg.dualbox.attr[attrName] && pkg.dualbox.attr[attrName].type || "*";
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
hasAttributeValue( attrName ) {
|
|
787
|
+
var pkg = this.getPackage();
|
|
788
|
+
var pkgDefault = pkg && pkg.dualbox && pkg.dualbox.attr && pkg.dualbox.attr[attrName].value;
|
|
789
|
+
var appDefault = this.def.attr && this.def.attr[attrName];
|
|
790
|
+
return appDefault !== undefined || pkgDefault !== undefined;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
getAttributeValue( attrName ) {
|
|
794
|
+
var pkg = this.getPackage();
|
|
795
|
+
var pkgDefault = pkg && pkg.dualbox && pkg.dualbox.attr && pkg.dualbox.attr[attrName].value;
|
|
796
|
+
var appDefault = this.def.attr && this.def.attr[attrName];
|
|
797
|
+
if( appDefault !== undefined ) { return appDefault; }
|
|
798
|
+
else if( pkgDefault !== undefined ) { return pkgDefault; }
|
|
799
|
+
else return undefined;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
setAttributeValue( attrName, val ) {
|
|
803
|
+
this.def.attr = this.def.attr || {};
|
|
804
|
+
this.def.attr[attrName] = val;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// only if this.isUI() == true
|
|
808
|
+
// return true if the UI exists in a panel, false otherwise
|
|
809
|
+
isOnAPanel() {
|
|
810
|
+
return this.getPanel() !== undefined;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// only if this.isUI() == true
|
|
814
|
+
// return the panel where the UI is placed
|
|
815
|
+
getPanel() {
|
|
816
|
+
var parser = new AppParser(this.m.data.root);
|
|
817
|
+
return parser.findNodePanel(this.id);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// srcType: "input" or "attr"
|
|
821
|
+
val( srcType, name, val ) {
|
|
822
|
+
if( val === undefined ) {
|
|
823
|
+
// it's a get
|
|
824
|
+
if( srcType === "input" ) {
|
|
825
|
+
return this.getInputDefaultValue(name);
|
|
826
|
+
}
|
|
827
|
+
else if( srcType === "attr" ) {
|
|
828
|
+
return this.getAttributeValue(name);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
// it's a set
|
|
833
|
+
if( srcType === "input" ) {
|
|
834
|
+
return this.setInputDefaultValue(name, val);
|
|
835
|
+
}
|
|
836
|
+
else if( srcType === "attr" ) {
|
|
837
|
+
return this.setAttributeValue(name, val);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
deleteVal(srcType, name){
|
|
843
|
+
if( srcType === "input" && this.def.defaultInputs){
|
|
844
|
+
delete this.def.defaultInputs[name];
|
|
845
|
+
}else if( srcType === "attr" && this.def.attr){
|
|
846
|
+
delete this.def.attr[name];
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
getValueType( srcType, name ) {
|
|
851
|
+
if( srcType === "input" ) {
|
|
852
|
+
return this.getInputType(name);
|
|
853
|
+
}
|
|
854
|
+
else if( srcType === "attr" ) {
|
|
855
|
+
return this.getAttributeType(name);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
hasLoop() {
|
|
860
|
+
var r = false;
|
|
861
|
+
_.each( this.getInputsNames(), (inputName) => {
|
|
862
|
+
if( this.hasIterator(inputName) ) {
|
|
863
|
+
r = true;
|
|
864
|
+
return false;
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
return r;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// true if input has an iterator
|
|
871
|
+
hasIterator( inputName ) {
|
|
872
|
+
var r = false;
|
|
873
|
+
_.each(this.def.loops, (loop) => {
|
|
874
|
+
if( loop.iterators && loop.iterators[inputName] ) {
|
|
875
|
+
r = true;
|
|
876
|
+
return false; // eol
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
return r;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// true if output has feedback
|
|
883
|
+
hasFeedback( outputName ) {
|
|
884
|
+
return this.getFeedback( outputName ) !== null;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// get the feedback input for this output
|
|
888
|
+
getFeedback( outputName ) {
|
|
889
|
+
var r = null;
|
|
890
|
+
_.each(this.def.loops, (loop) => {
|
|
891
|
+
if( loop.feedback && loop.feedback[outputName] ) {
|
|
892
|
+
r = loop.feedback[outputName];
|
|
893
|
+
return false; // eol
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
return r;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
isFeedbackTarget( inputName ) {
|
|
900
|
+
var r = false;
|
|
901
|
+
_.each(this.def.loops, (loop) => {
|
|
902
|
+
_.each( loop.feedback, (v, k) => {
|
|
903
|
+
if( v === inputName ) {
|
|
904
|
+
r = true;
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
});
|
|
908
|
+
return r;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
setIterator(input) {
|
|
912
|
+
this.def.loops = this.def.loops || [];
|
|
913
|
+
this.def.loops[0] = this.def.loops[0] || {};
|
|
914
|
+
this.def.loops[0]["iterators"] = this.def.loops[0]["iterators"] || {};
|
|
915
|
+
this.def.loops[0]["iterators"][input] = 1;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
unsetIterator(input) {
|
|
919
|
+
delete this.def.loops[0]["iterators"][input];
|
|
920
|
+
if( _.isEmpty(this.def.loops[0]["iterators"])) delete this.def.loops[0]["iterators"];
|
|
921
|
+
if( _.isEmpty(this.def.loops[0])) delete this.def.loops;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
setFeedback(output, input) {
|
|
925
|
+
this.def.loops = this.def.loops || [];
|
|
926
|
+
this.def.loops[0] = this.def.loops[0] || {};
|
|
927
|
+
this.def.loops[0]["feedback"] = this.def.loops[0]["feedback"] || {};
|
|
928
|
+
this.def.loops[0]["feedback"][output] = input;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
unsetFeedback(output) {
|
|
932
|
+
delete this.def.loops[0]["feedback"][output];
|
|
933
|
+
if( _.isEmpty(this.def.loops[0]["feedback"])) delete this.def.loops[0]["feedback"];
|
|
934
|
+
if( _.isEmpty(this.def.loops[0])) delete this.def.loops[0];
|
|
935
|
+
if( _.isEmpty(this.def.loops)) delete this.def.loops;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
isMetanode() {
|
|
939
|
+
return this.getMetanodeDefinition() !== undefined;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
getMetanodeDefinition() {
|
|
943
|
+
return idx(this.app, o => o.metanodes[this.def.module]);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
isUI() {
|
|
947
|
+
return this.type == "ui";
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
isModule() {
|
|
951
|
+
return this.type == "module";
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
isInput() {
|
|
955
|
+
return this.type == "input";
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
isOutput() {
|
|
959
|
+
return this.type == "output";
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
isWidget() {
|
|
963
|
+
return this.isUI() && this.getSpecialType() == "Widget";
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
getRegisterType() {
|
|
967
|
+
return this.getPackage().dualbox.registerTo;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// return "Widget", "Canvas3D" or any other special type
|
|
971
|
+
getSpecialType() {
|
|
972
|
+
return this.getPackage().dualbox.type;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// only if this.isWidget() == true
|
|
976
|
+
registerWidget( targetId ) {
|
|
977
|
+
if( !this.def.registerTo ) this.def.registerTo = {};
|
|
978
|
+
this.def.registerTo = targetId;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
getWidgetRegistration() {
|
|
982
|
+
return this.def.registerTo;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
hasCacheActivated() {
|
|
988
|
+
return idx(this.getDef(), o => o.cache) == true;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
isParallel() {
|
|
992
|
+
return idx(this.getDef(), o => o.parallel) == true;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
setCache(b) {
|
|
996
|
+
this.def.cache = b;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
setParallel(b) {
|
|
1000
|
+
this.def.parallel = b;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
getInputsNames() {
|
|
1004
|
+
return _.keys( idx(this.getPackage(), o => o.dualbox.input) );
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
getOutputsNames() {
|
|
1008
|
+
return _.keys( idx(this.getPackage(), o => o.dualbox.output) );
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
getAttributesNames() {
|
|
1012
|
+
return _.keys( idx(this.getPackage(), o => o.dualbox.attr) );
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
getEventsNames() {
|
|
1016
|
+
return _.keys( idx(this.getPackage(), o => o.dualbox.events) );
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// return the list of nodes that can be targeted by an event
|
|
1020
|
+
getEventTargets() {
|
|
1021
|
+
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
getOutboundEvents() {
|
|
1025
|
+
return this.def.events || [];
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
getInputDef( name ) {
|
|
1029
|
+
return idx(this.getPackage(), o => o.dualbox.input[name]);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
getOutputDef( name ) {
|
|
1033
|
+
return idx(this.getPackage(), o => o.dualbox.output[name]);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
getAttrDef( name ) {
|
|
1037
|
+
return idx(this.getPackage(), o => o.dualbox.attr[name]);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
getEventDef( name ) {
|
|
1041
|
+
return idx(this.getPackage(), o => o.dualbox.events[name]);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
hasInputs() {
|
|
1045
|
+
return this.getInputsNames().length > 0;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
hasOutputs() {
|
|
1049
|
+
return this.getOutputsNames().length > 0;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
hasAttributes() {
|
|
1053
|
+
return this.getAttributesNames().length > 0;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
hasInEvents() {
|
|
1057
|
+
return this.getEventsNames().length > 0;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
hasOutEvents() {
|
|
1061
|
+
var evts = idx(this, o => o.def.events);
|
|
1062
|
+
return evts && evts.length !== 0;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
isInputConst(name) {
|
|
1066
|
+
var desc = this.getInputDef(name);
|
|
1067
|
+
if( !desc ) {
|
|
1068
|
+
throw "Input " + name + " not found in node " + this.id;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
var type = desc.type.toLowerCase();
|
|
1072
|
+
switch( type ) {
|
|
1073
|
+
case "number":
|
|
1074
|
+
case "boolean":
|
|
1075
|
+
return true;
|
|
1076
|
+
default:
|
|
1077
|
+
return desc.const;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
isInputVisible(inputName) {
|
|
1082
|
+
if( this.isInput() || this.isOutput() ) {
|
|
1083
|
+
return true;
|
|
1084
|
+
}
|
|
1085
|
+
else {
|
|
1086
|
+
if( this.isInputConnected(inputName) ) {
|
|
1087
|
+
return true;
|
|
1088
|
+
}
|
|
1089
|
+
else {
|
|
1090
|
+
var val = idx(this.def, o => o.graph.in.visible[inputName]);
|
|
1091
|
+
if( val !== undefined ) {
|
|
1092
|
+
return val !== false;
|
|
1093
|
+
}
|
|
1094
|
+
else {
|
|
1095
|
+
// check if the app did set a default value for this input
|
|
1096
|
+
var defaultVal = idx(this.def, o => o.defaultInputs[inputName]);
|
|
1097
|
+
if( defaultVal !== undefined ) {
|
|
1098
|
+
return false; // make not visible when default value is set by the app
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// else, make the input visible if the package didnt define a default value for it
|
|
1102
|
+
var pkg = this.getPackage();
|
|
1103
|
+
var pkgDef = idx(pkg, o => o.dualbox.input[inputName]);
|
|
1104
|
+
if( pkgDef ) {
|
|
1105
|
+
if( pkgDef.visible === false ) {
|
|
1106
|
+
return false;
|
|
1107
|
+
}
|
|
1108
|
+
else if( pkgDef.value !== undefined ) {
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1111
|
+
else {
|
|
1112
|
+
return true;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
else {
|
|
1116
|
+
return true;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
isOutputVisible(outputName) {
|
|
1124
|
+
if( this.isInput() || this.isOutput() ) {
|
|
1125
|
+
return true;
|
|
1126
|
+
}
|
|
1127
|
+
else {
|
|
1128
|
+
if( this.isOutputConnected(outputName) ) {
|
|
1129
|
+
return true;
|
|
1130
|
+
}
|
|
1131
|
+
else {
|
|
1132
|
+
var val = idx(this.def, o => o.graph.out.visible[outputName]);
|
|
1133
|
+
if( val === false ) {
|
|
1134
|
+
return false;
|
|
1135
|
+
}
|
|
1136
|
+
else {
|
|
1137
|
+
var pkg = this.getPackage();
|
|
1138
|
+
var pkgDef = idx(pkg, o => o.dualbox.output[outputName]);
|
|
1139
|
+
return pkgDef.visible !== false;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
setInputVisibility(inputName, b) {
|
|
1146
|
+
this.def.graph = this.def.graph || {};
|
|
1147
|
+
this.def.graph.in = this.def.graph.in || {};
|
|
1148
|
+
this.def.graph.in.visible = this.def.graph.in.visible || {};
|
|
1149
|
+
this.def.graph.in.visible[inputName] = b;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
setOutputVisibility(outputName, b) {
|
|
1153
|
+
this.def.graph = this.def.graph || {};
|
|
1154
|
+
this.def.graph.out = this.def.graph.out || {};
|
|
1155
|
+
this.def.graph.out.visible = this.def.graph.out.visible || {};
|
|
1156
|
+
this.def.graph.out.visible[outputName] = b;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// return true if the node has an input link to the node given in id
|
|
1160
|
+
hasInputLinkTo( id ) {
|
|
1161
|
+
var hasLink = false;
|
|
1162
|
+
_.each( this.getNormalizeLinksDef(), (link, inputName) => {
|
|
1163
|
+
_.each(link, (outputName, nodeId) => {
|
|
1164
|
+
if( nodeId == "input" || nodeId == "output" ) {
|
|
1165
|
+
nodeId = outputName;
|
|
1166
|
+
}
|
|
1167
|
+
if( nodeId == id ) {
|
|
1168
|
+
hasLink = true;
|
|
1169
|
+
}
|
|
1170
|
+
});
|
|
1171
|
+
});
|
|
1172
|
+
return hasLink;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// return true if the node has an output link to the node given in id
|
|
1176
|
+
hasOutputLinkTo( id ) {
|
|
1177
|
+
var n = this.m.getNode(id);
|
|
1178
|
+
return n.hasInputLinkTo(this.id);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// true if the node is connected to "nodeId" through input "nodeInput"
|
|
1182
|
+
isConnectedToInput(nodeId, nodeInput) {
|
|
1183
|
+
var connected = false;
|
|
1184
|
+
var node = this.m.getNode(nodeId);
|
|
1185
|
+
_.each( this.listOutputs(), (outputName) => {
|
|
1186
|
+
if( node.isConnectedToOutput( this.id, outputName ) ) {
|
|
1187
|
+
connected = true;
|
|
1188
|
+
return false; // eol
|
|
1189
|
+
}
|
|
1190
|
+
});
|
|
1191
|
+
return connected;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
// true if the node is connected to "nodeId" through output "nodeOutput"
|
|
1195
|
+
isConnectedToOutput(nodeId, nodeOutput) {
|
|
1196
|
+
var connected = false;
|
|
1197
|
+
_.each(this.getNormalizeLinksDef(), (link) => {
|
|
1198
|
+
_.each(link, (outputName, outputNodeId) => {
|
|
1199
|
+
if( nodeId === outputNodeId ) {
|
|
1200
|
+
connected = true;
|
|
1201
|
+
return false; // eol
|
|
1202
|
+
}
|
|
1203
|
+
});
|
|
1204
|
+
});
|
|
1205
|
+
return connected;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
listInputs() {
|
|
1209
|
+
var pkg = this.getPackage();
|
|
1210
|
+
return pkg && pkg.dualbox && pkg.dualbox.input && Object.keys(pkg.dualbox.input);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
listOutputs() {
|
|
1214
|
+
var pkg = this.getPackage();
|
|
1215
|
+
return pkg && pkg.dualbox && pkg.dualbox.output && Object.keys(pkg.dualbox.output);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// get the list of DataLink for inbound connections of this node
|
|
1219
|
+
getInboundLinks() {
|
|
1220
|
+
var links = [];
|
|
1221
|
+
|
|
1222
|
+
_.each( this.getNormalizeLinksDef(), (link, inputName) => {
|
|
1223
|
+
_.each( link, (outputName, outputNodeId) => {
|
|
1224
|
+
if( outputNodeId !== "graph" ) {
|
|
1225
|
+
links.push( new DataLink(this.m, outputNodeId, outputName, this.id, inputName) );
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
return links;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// get the list of DataLink for outbound connections of this node
|
|
1234
|
+
getOutboundLinks() {
|
|
1235
|
+
var links = [];
|
|
1236
|
+
var collectLinks = (def, id) => {
|
|
1237
|
+
var node = this.m.getNode(id);
|
|
1238
|
+
_.each( node.getInboundLinks(), (link) => {
|
|
1239
|
+
if( link.isSourceNode(this) ) {
|
|
1240
|
+
links.push(link);
|
|
1241
|
+
}
|
|
1242
|
+
});
|
|
1243
|
+
};
|
|
1244
|
+
var collectInputLinks = (def, id) => collectLinks(def, this.m.inputPrefix + id);
|
|
1245
|
+
var collectOutputLinks = (def, id) => collectLinks(def, this.m.outputPrefix + id);
|
|
1246
|
+
|
|
1247
|
+
_.each( this.m.data.app.modules, collectLinks );
|
|
1248
|
+
_.each( this.m.data.app.ui, collectLinks );
|
|
1249
|
+
_.each( this.m.data.app.input, collectInputLinks );
|
|
1250
|
+
_.each( this.m.data.app.output, collectOutputLinks );
|
|
1251
|
+
return links;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
getInboundEvents() {
|
|
1255
|
+
var events = [];
|
|
1256
|
+
|
|
1257
|
+
var collectEvents = (def, id) => {
|
|
1258
|
+
var node = this.m.getNode(id);
|
|
1259
|
+
_.each( node.def.events, (eventDef, index) => {
|
|
1260
|
+
if( eventDef.node == this.id ) {
|
|
1261
|
+
events.push( new EventLink(this.m, node.id, this.id, eventDef.event, eventDef.node, eventDef.cond, eventDef.data) );
|
|
1262
|
+
}
|
|
1263
|
+
});
|
|
1264
|
+
};
|
|
1265
|
+
_.each( this.m.data.app.ui, collectEvents );
|
|
1266
|
+
return events;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
isInputConnected(inputName) {
|
|
1270
|
+
var r = false;
|
|
1271
|
+
|
|
1272
|
+
if( (this.isInput() || this.isOutput()) && outputName === "value" ) {
|
|
1273
|
+
var targetId = this.isInput() ? "input" : "output";
|
|
1274
|
+
var targetInput = this.graphId;
|
|
1275
|
+
}
|
|
1276
|
+
else {
|
|
1277
|
+
var targetId = this.graphId;
|
|
1278
|
+
var targetInput = inputName;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
_.each(this.getInboundLinks(), (link) => {
|
|
1282
|
+
if( link.targetId == targetId && link.targetInput === targetInput ) {
|
|
1283
|
+
r = true;
|
|
1284
|
+
return false;
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1287
|
+
return r;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
isOutputConnected(outputName) {
|
|
1291
|
+
var r = false;
|
|
1292
|
+
|
|
1293
|
+
if( (this.isInput() || this.isOutput()) && outputName === "value" ) {
|
|
1294
|
+
var sourceId = this.isInput() ? "input" : "output";
|
|
1295
|
+
var sourceOutput = this.graphId;
|
|
1296
|
+
}
|
|
1297
|
+
else {
|
|
1298
|
+
var sourceId = this.graphId;
|
|
1299
|
+
var sourceOutput = outputName;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
_.each(this.getOutboundLinks(), (link) => {
|
|
1303
|
+
if( link.sourceId == sourceId && link.sourceOutput === sourceOutput ) {
|
|
1304
|
+
r = true;
|
|
1305
|
+
return false;
|
|
1306
|
+
}
|
|
1307
|
+
});
|
|
1308
|
+
return r;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// return true if the node is in a metanode
|
|
1312
|
+
isInMetanode() {
|
|
1313
|
+
return this.m.getCurrentMetanodeName() != "Application";
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// Works only is this.isInMetanode() == true
|
|
1317
|
+
getParentMetanode() {
|
|
1318
|
+
return this.m.data.windows[this.m.data.windows.length - 2][1];
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
getParentMetanodeName() {
|
|
1322
|
+
return this.m.data.windows[this.m.data.windows.length - 2][0];
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// Works only is this.isInMetanode() == true
|
|
1326
|
+
execInParentWindow(cb) {
|
|
1327
|
+
this.m.history.holdSaving(true);
|
|
1328
|
+
var savedWindow = this.m.data.windows[ this.m.data.windows.length - 1];
|
|
1329
|
+
|
|
1330
|
+
// set the parent window
|
|
1331
|
+
var parentName = this.getParentMetanodeName();
|
|
1332
|
+
this.m.setWindow(parentName);
|
|
1333
|
+
|
|
1334
|
+
// exec our callback
|
|
1335
|
+
var ret = cb();
|
|
1336
|
+
|
|
1337
|
+
// reinstall the saved state
|
|
1338
|
+
this.m.data.app = savedWindow[1];
|
|
1339
|
+
this.m.data.windows.push(savedWindow);
|
|
1340
|
+
|
|
1341
|
+
this.m.history.holdSaving(false);
|
|
1342
|
+
|
|
1343
|
+
return ret;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// get the event at index i
|
|
1347
|
+
getEvent(i) {
|
|
1348
|
+
return idx(this.def, o => o.events[i]);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
hasComment() {
|
|
1352
|
+
return this.getComment() !== undefined;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
getComment() {
|
|
1356
|
+
return idx(this.def, o => o.comment);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
deleteComment() {
|
|
1360
|
+
delete this.def.comment;
|
|
1361
|
+
this.m.history.save();
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
setComment(s) {
|
|
1365
|
+
this.def.comment = s;
|
|
1366
|
+
this.m.history.save();
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
setEvent(i, def) {
|
|
1370
|
+
this.def.events[i] = def;
|
|
1371
|
+
this.m.history.save();
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
removeEvent(i) {
|
|
1375
|
+
this.def.events.splice(i, 1);
|
|
1376
|
+
this.m.history.save();
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
setPosition(pos) {
|
|
1380
|
+
this.def.graph = this.def.graph || {};
|
|
1381
|
+
this.def.graph.position = pos;
|
|
1382
|
+
this.m.history.save();
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
getPosition() {
|
|
1386
|
+
var pos = this.def.graph && this.def.graph.position || { left: 0, top: 0 };
|
|
1387
|
+
pos.top = parseInt(pos.top);
|
|
1388
|
+
pos.left = parseInt(pos.left);
|
|
1389
|
+
return pos;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
deleteInputLink( inputName ) {
|
|
1393
|
+
delete this.def.links[inputName];
|
|
1394
|
+
this.m.history.save();
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
_deleteInputLinksTo(id) {
|
|
1398
|
+
var n = this.m.getNode(id);
|
|
1399
|
+
_.each( this.getNormalizeLinksDef(), (link, inputName) => {
|
|
1400
|
+
_.each(link, (outputName, nodeId) => {
|
|
1401
|
+
if( nodeId == "input" || nodeId == "output" ) {
|
|
1402
|
+
nodeId = outputName;
|
|
1403
|
+
}
|
|
1404
|
+
if( nodeId == id ) {
|
|
1405
|
+
if( this.isInput() || this.isOutput() ) {
|
|
1406
|
+
delete this.def.link;
|
|
1407
|
+
}
|
|
1408
|
+
else {
|
|
1409
|
+
delete this.def.links[inputName];
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
});
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
// detach connexion on a specific input
|
|
1417
|
+
detachInput( inputName ) {
|
|
1418
|
+
_.each( this.getInboundLinks(), (link) => {
|
|
1419
|
+
if( link.targetInput == inputName ) {
|
|
1420
|
+
link.detach();
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// detach connexion on a specific output
|
|
1426
|
+
detachOutput( outputName ) {
|
|
1427
|
+
_.each( this.getOutboundLinks(), (link) => {
|
|
1428
|
+
if( link.sourceOutput == outputName ) {
|
|
1429
|
+
link.detach();
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
// delete all links from and to this node
|
|
1435
|
+
detach() {
|
|
1436
|
+
_.each( this.getInboundLinks(), (link) => {
|
|
1437
|
+
link.detach();
|
|
1438
|
+
});
|
|
1439
|
+
_.each( this.getOutboundLinks(), (link) => {
|
|
1440
|
+
link.detach();
|
|
1441
|
+
});
|
|
1442
|
+
this.m.history.save();
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
detachEvents() {
|
|
1446
|
+
_.each( this.getInboundEvents(), (link) => {
|
|
1447
|
+
link.detach();
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
addTo( app ) {
|
|
1452
|
+
switch(this.type) {
|
|
1453
|
+
case "module": app.modules[this.graphId] = this.def; break;
|
|
1454
|
+
case "ui": app.ui[this.graphId] = this.def; break;
|
|
1455
|
+
case "input": app.input[this.graphId] = this.def; break;
|
|
1456
|
+
case "output": app.output[this.graphId] = this.def; break;
|
|
1457
|
+
default:
|
|
1458
|
+
throw "wtf";
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
removeFrom( app ) {
|
|
1463
|
+
this.detach();
|
|
1464
|
+
this.detachEvents();
|
|
1465
|
+
|
|
1466
|
+
switch(this.type) {
|
|
1467
|
+
case "module": delete app.modules[this.graphId]; break;
|
|
1468
|
+
case "ui": delete app.ui[this.graphId]; break;
|
|
1469
|
+
case "input":
|
|
1470
|
+
delete app.input[this.graphId];
|
|
1471
|
+
if( !this.m.isRootApp(app) ) {
|
|
1472
|
+
// we are changing the signature of a metamodule
|
|
1473
|
+
// ensure we disconnect this input in all instances of it
|
|
1474
|
+
// and remove all loops on it
|
|
1475
|
+
this.execInParentWindow(() => {
|
|
1476
|
+
var instances = this.m.getMetanodeInstances(app);
|
|
1477
|
+
|
|
1478
|
+
// for each instance of this metanode
|
|
1479
|
+
// disconnect the input, and remove loops
|
|
1480
|
+
_.each(instances, (n) => {
|
|
1481
|
+
if( n.isInputConnected(this.graphId) ) {
|
|
1482
|
+
n.detachInput(this.graphId);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
if( n.hasIterator(this.graphId) ) {
|
|
1486
|
+
n.unsetIterator(this.graphId);
|
|
1487
|
+
}
|
|
1488
|
+
});
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
break;
|
|
1492
|
+
case "output":
|
|
1493
|
+
delete app.output[this.graphId];
|
|
1494
|
+
if( !this.m.isRootApp(app) ) {
|
|
1495
|
+
// we are changing the signature of a metamodule
|
|
1496
|
+
// ensure we disconnect this output in all instances of it
|
|
1497
|
+
// and remove all loops on it
|
|
1498
|
+
this.execInParentWindow(() => {
|
|
1499
|
+
var instances = this.m.getMetanodeInstances(app);
|
|
1500
|
+
|
|
1501
|
+
_.each(instances, (n) => {
|
|
1502
|
+
if( n.isOutputConnected(this.graphId) ) {
|
|
1503
|
+
n.detachOutput(this.graphId);
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
if( n.hasFeedback(this.graphId) ) {
|
|
1507
|
+
n.unsetFeedback(this.graphId);
|
|
1508
|
+
}
|
|
1509
|
+
});
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
break;
|
|
1513
|
+
default:
|
|
1514
|
+
throw "wtf";
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
// if the node is an UI, we need to change the "data-node" links in the HTML
|
|
1518
|
+
if( this.isUI() ) {
|
|
1519
|
+
// Deep search and replaces the given property value "prevVal" with "newVal"
|
|
1520
|
+
var removeFromInterface = (o, id) => {
|
|
1521
|
+
const newObject = _.clone(o);
|
|
1522
|
+
|
|
1523
|
+
_.each(o, (val, key) => {
|
|
1524
|
+
if(key == "node" && val === id) {
|
|
1525
|
+
delete newObject[key];
|
|
1526
|
+
} else if (typeof(val) === 'object' || typeof(val) === 'array') {
|
|
1527
|
+
newObject[key] = removeFromInterface(val, id);
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
|
|
1531
|
+
return newObject;
|
|
1532
|
+
}
|
|
1533
|
+
app.interface = removeFromInterface(app.interface, this.id);
|
|
1534
|
+
|
|
1535
|
+
// remove this node from all app events (API) using it
|
|
1536
|
+
var removeFromAppEvents = (o, id) => {
|
|
1537
|
+
const newObject = _.clone(o);
|
|
1538
|
+
_.each(newObject, (eventDef, eventName) => {
|
|
1539
|
+
if( eventDef.in ) {
|
|
1540
|
+
_.remove(eventDef.in, o => o.node == this.id);
|
|
1541
|
+
}
|
|
1542
|
+
if( eventDef.out && eventDef.out.node == this.id ) {
|
|
1543
|
+
delete eventDef.out;
|
|
1544
|
+
}
|
|
1545
|
+
});
|
|
1546
|
+
return newObject;
|
|
1547
|
+
}
|
|
1548
|
+
app.events = removeFromAppEvents(app.events, this.id);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
// remove from current app
|
|
1553
|
+
remove() {
|
|
1554
|
+
this.m.history.holdSaving(true);
|
|
1555
|
+
this.removeFrom( this.app );
|
|
1556
|
+
this.m.history.holdSaving(false);
|
|
1557
|
+
this.m.history.save();
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
rename(newId) {
|
|
1561
|
+
this.m.history.batch(() => {
|
|
1562
|
+
var newGraphId;
|
|
1563
|
+
if( this.isInput() ) {
|
|
1564
|
+
if( newId.startsWith(this.m.inputPrefix) ) {
|
|
1565
|
+
newGraphId = newId.substring(3);
|
|
1566
|
+
}
|
|
1567
|
+
else {
|
|
1568
|
+
newGraphId = newId;
|
|
1569
|
+
newId = this.m.inputPrefix + newId;
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
else if( this.isOutput() ) {
|
|
1573
|
+
if( newId.startsWith(this.m.outputPrefix) ) {
|
|
1574
|
+
newGraphId = newId.substring(4);
|
|
1575
|
+
}
|
|
1576
|
+
else {
|
|
1577
|
+
newGraphId = newId;
|
|
1578
|
+
newId = this.m.outputPrefix + newId;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
else {
|
|
1582
|
+
newGraphId = newId;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// collect (and detach) all links to this node
|
|
1586
|
+
var links2node = [];
|
|
1587
|
+
_.each(this.m.getNodes(), (n) => {
|
|
1588
|
+
var links = n.getInboundLinks();
|
|
1589
|
+
_.each( links, (link) => {
|
|
1590
|
+
if( link.isSourceNode(this) ) {
|
|
1591
|
+
link.detach();
|
|
1592
|
+
links2node.push(link);
|
|
1593
|
+
}
|
|
1594
|
+
});
|
|
1595
|
+
});
|
|
1596
|
+
|
|
1597
|
+
// if it's an UI, we also want the events to this node
|
|
1598
|
+
if( this.isUI() ) {
|
|
1599
|
+
var events2node = [];
|
|
1600
|
+
_.each(this.m.getNodes("ui"), (n) => {
|
|
1601
|
+
var events = n.getOutboundEvents();
|
|
1602
|
+
_.each(events, (e) => {
|
|
1603
|
+
if( e.node === this.getGraphId() ) {
|
|
1604
|
+
events2node.push(e);
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// if we are renaming an input or output in a metanode, we need to disconnect
|
|
1611
|
+
// all links to that input/output and reconnect them later
|
|
1612
|
+
if( this.isInMetanode() && (this.isInput() || this.isOutput()) ) {
|
|
1613
|
+
var metanodeName = this.m.getCurrentMetanodeName();
|
|
1614
|
+
|
|
1615
|
+
// detach the links of parent node connected to this input
|
|
1616
|
+
var parentLinks = this.execInParentWindow(() => {
|
|
1617
|
+
var links = [];
|
|
1618
|
+
var parentNodes = [];
|
|
1619
|
+
var nodes = this.m.getNodes("modules");
|
|
1620
|
+
_.each(nodes, (n) => {
|
|
1621
|
+
if( n.def.module == metanodeName ) {
|
|
1622
|
+
parentNodes.push(n);
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1625
|
+
|
|
1626
|
+
if( this.isInput() ) {
|
|
1627
|
+
// get inboundLinks of this node that target this input, and detach them
|
|
1628
|
+
_.each(parentNodes, (n) => {
|
|
1629
|
+
_.each(n.getInboundLinks(), (l) => {
|
|
1630
|
+
if( l.targetInput == this.graphId ) {
|
|
1631
|
+
l.detach();
|
|
1632
|
+
l.targetInput = newGraphId; // modify the link with the new id
|
|
1633
|
+
links.push(l);
|
|
1634
|
+
}
|
|
1635
|
+
});
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
else if( this.isOutput() ) {
|
|
1639
|
+
// get inboundLinks of this node that target this input, and detach them
|
|
1640
|
+
_.each(parentNodes, (n) => {
|
|
1641
|
+
_.each(n.getOutboundLinks(), (l) => {
|
|
1642
|
+
if( l.sourceOutput == this.graphId ) {
|
|
1643
|
+
l.detach();
|
|
1644
|
+
l.sourceOutput = newGraphId; // modify the link with the new id
|
|
1645
|
+
links.push(l);
|
|
1646
|
+
}
|
|
1647
|
+
});
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
return links;
|
|
1652
|
+
});
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// rename the node
|
|
1656
|
+
switch( this.type ) {
|
|
1657
|
+
case "input":
|
|
1658
|
+
this.app.input[newGraphId] = this.app.input[this.graphId];
|
|
1659
|
+
delete this.app.input[this.graphId];
|
|
1660
|
+
break;
|
|
1661
|
+
case "output":
|
|
1662
|
+
this.app.output[newGraphId] = this.app.output[this.graphId];
|
|
1663
|
+
delete this.app.output[this.graphId];
|
|
1664
|
+
break;
|
|
1665
|
+
case "module":
|
|
1666
|
+
this.app.modules[newGraphId] = this.app.modules[this.graphId];
|
|
1667
|
+
delete this.app.modules[this.graphId];
|
|
1668
|
+
break;
|
|
1669
|
+
case "ui":
|
|
1670
|
+
this.app.ui[newGraphId] = this.app.ui[this.graphId];
|
|
1671
|
+
delete this.app.ui[this.graphId];
|
|
1672
|
+
break;
|
|
1673
|
+
default:
|
|
1674
|
+
throw new Error("Could not identify node " + this.graphId + " in graph");
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
// reattach the links to the new node
|
|
1678
|
+
_.each(links2node, (link) => {
|
|
1679
|
+
if( link.sourceId == "input" || link.sourceId == "output" ) {
|
|
1680
|
+
link.sourceOutput = newId;
|
|
1681
|
+
}
|
|
1682
|
+
else {
|
|
1683
|
+
link.sourceId = newId;
|
|
1684
|
+
}
|
|
1685
|
+
link.attach();
|
|
1686
|
+
});
|
|
1687
|
+
|
|
1688
|
+
// redirect the events
|
|
1689
|
+
_.each(events2node, (evt) => {
|
|
1690
|
+
evt.node = newId;
|
|
1691
|
+
});
|
|
1692
|
+
|
|
1693
|
+
// if the node is an UI, we need to change the "data-node" links in the HTML
|
|
1694
|
+
if( this.isUI() ) {
|
|
1695
|
+
// Deep search and replaces the given property value "prevVal" with "newVal"
|
|
1696
|
+
function replaceInInterface(o, oldId, newId) {
|
|
1697
|
+
const newObject = _.clone(o);
|
|
1698
|
+
|
|
1699
|
+
_.each(o, (val, key) => {
|
|
1700
|
+
if(key == "node" && val === oldId) {
|
|
1701
|
+
newObject[key] = newId;
|
|
1702
|
+
console.log("replacing!");
|
|
1703
|
+
} else if (typeof(val) === 'object' || typeof(val) === 'array') {
|
|
1704
|
+
newObject[key] = replaceInInterface(val, oldId, newId);
|
|
1705
|
+
}
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
return newObject;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
this.app.interface = replaceInInterface(this.app.interface, this.graphId, newGraphId);
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
// if we are renaming an input or output in a metanode, we now reattach the link we detached earlier
|
|
1715
|
+
if( this.isInMetanode() && (this.isInput() || this.isOutput()) ) {
|
|
1716
|
+
this.execInParentWindow(() => {
|
|
1717
|
+
_.each(parentLinks, (l) => {
|
|
1718
|
+
l.attach();
|
|
1719
|
+
});
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
|
|
1727
|
+
/* representing a data connection between 2 nodes */
|
|
1728
|
+
class DataLink {
|
|
1729
|
+
constructor(m, sourceId, sourceOutput, targetId, targetInput) {
|
|
1730
|
+
this.m = m;
|
|
1731
|
+
this.sourceId = sourceId;
|
|
1732
|
+
this.sourceOutput = sourceOutput;
|
|
1733
|
+
this.targetId = targetId;
|
|
1734
|
+
this.targetInput = targetInput;
|
|
1735
|
+
|
|
1736
|
+
this.normalize();
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
normalize() {
|
|
1740
|
+
// normalize the input/output syntax in here
|
|
1741
|
+
var sourceNode = this.getSourceNode();
|
|
1742
|
+
if( sourceNode.isInput() ) {
|
|
1743
|
+
this.sourceId = "input";
|
|
1744
|
+
this.sourceOutput = this.m.inputPrefix + sourceNode.graphId;
|
|
1745
|
+
}
|
|
1746
|
+
else if( sourceNode.isOutput() ) {
|
|
1747
|
+
this.sourceId = "output";
|
|
1748
|
+
this.sourceOutput = this.m.outputPrefix + sourceNode.graphId;
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
var targetNode = this.getTargetNode();
|
|
1752
|
+
if( targetNode.isInput() ) {
|
|
1753
|
+
this.targetId = "input";
|
|
1754
|
+
this.targetInput = this.m.inputPrefix + targetNode.graphId;
|
|
1755
|
+
}
|
|
1756
|
+
else if( targetNode.isOutput() ) {
|
|
1757
|
+
this.targetId = "output";
|
|
1758
|
+
this.targetInput = this.m.outputPrefix + targetNode.graphId;
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
getSourceNode() {
|
|
1763
|
+
if( this.sourceId === "input" ) {
|
|
1764
|
+
if( this.sourceOutput.startsWith(this.m.inputPrefix) ) {
|
|
1765
|
+
return this.m.getNode(this.sourceOutput);
|
|
1766
|
+
}
|
|
1767
|
+
else {
|
|
1768
|
+
return this.m.getNode(this.m.inputPrefix + this.sourceOutput);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
else if( this.sourceId === "output" ) {
|
|
1772
|
+
if( this.sourceOutput.startsWith(this.m.outputPrefix) ) {
|
|
1773
|
+
return this.m.getNode(this.sourceOutput);
|
|
1774
|
+
}
|
|
1775
|
+
else {
|
|
1776
|
+
return this.m.getNode(this.m.outputPrefix + this.sourceOutput);
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
else {
|
|
1780
|
+
return this.m.getNode(this.sourceId);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
getTargetNode() {
|
|
1785
|
+
if( this.targetId === "input" ) {
|
|
1786
|
+
if( this.targetInput.startsWith(this.m.inputPrefix) ) {
|
|
1787
|
+
return this.m.getNode(this.targetInput);
|
|
1788
|
+
}
|
|
1789
|
+
else {
|
|
1790
|
+
return this.m.getNode(this.m.inputPrefix + this.targetInput);
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
else if( this.targetId == "output" ) {
|
|
1794
|
+
if( this.targetInput.startsWith(this.m.outputPrefix) ) {
|
|
1795
|
+
return this.m.getNode(this.targetInput);
|
|
1796
|
+
}
|
|
1797
|
+
else {
|
|
1798
|
+
return this.m.getNode(this.m.outputPrefix + this.targetInput);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
else {
|
|
1802
|
+
return this.m.getNode(this.targetId);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
isSourceNode( n ) {
|
|
1807
|
+
return n.id === this.getSourceNode().id;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
isTargetNode( n ) {
|
|
1811
|
+
return n.id === this.getTargetNode().id;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// splitted link: add a step between pos1 and pos2
|
|
1815
|
+
// prevPos: the previous position object ({ left: xxx, top: xxx}) or "source"
|
|
1816
|
+
// pos: the new step
|
|
1817
|
+
addPathStep( prevPos, pos ) {
|
|
1818
|
+
var def = this.getDef();
|
|
1819
|
+
if( prevPos == "source" ) {
|
|
1820
|
+
def.graph = def.graph || {};
|
|
1821
|
+
def.graph.path = def.graph.path || [];
|
|
1822
|
+
def.graph.path.unshift(pos);
|
|
1823
|
+
}
|
|
1824
|
+
else {
|
|
1825
|
+
// we need to find pos1 in the array to add pos at the right location
|
|
1826
|
+
var index;
|
|
1827
|
+
_.each( def.graph.path, (p, i) => {
|
|
1828
|
+
if( p.left == prevPos.left && p.top == prevPos.top ) {
|
|
1829
|
+
index = i+1;
|
|
1830
|
+
return false;
|
|
1831
|
+
}
|
|
1832
|
+
});
|
|
1833
|
+
def.graph.path.splice(index, 0, pos);
|
|
1834
|
+
}
|
|
1835
|
+
this.m.history.save();
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
updatePathStep( oldPos, newPos ) {
|
|
1839
|
+
var def = this.getDef();
|
|
1840
|
+
var index;
|
|
1841
|
+
_.each( def.graph.path, (p, i) => {
|
|
1842
|
+
if( p.left == oldPos.left && p.top == oldPos.top ) {
|
|
1843
|
+
index = i;
|
|
1844
|
+
return false;
|
|
1845
|
+
}
|
|
1846
|
+
});
|
|
1847
|
+
def.graph.path[index] = newPos;
|
|
1848
|
+
this.m.history.save();
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
removePathStep( pos ) {
|
|
1852
|
+
var def = this.getDef();
|
|
1853
|
+
var index;
|
|
1854
|
+
_.each( def.graph.path, (p, i) => {
|
|
1855
|
+
if( p.left == pos.left && p.top == pos.top ) {
|
|
1856
|
+
index = i;
|
|
1857
|
+
return false;
|
|
1858
|
+
}
|
|
1859
|
+
});
|
|
1860
|
+
def.graph.path.splice(index, 1);
|
|
1861
|
+
if( def.graph.path.length == 0 ) {
|
|
1862
|
+
delete def.graph.path;
|
|
1863
|
+
}
|
|
1864
|
+
this.m.history.save();
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
// get the link definition object
|
|
1868
|
+
getDef() {
|
|
1869
|
+
var targetNode = this.getTargetNode();
|
|
1870
|
+
if( targetNode.isInput() ) {
|
|
1871
|
+
return targetNode.def.link;
|
|
1872
|
+
}
|
|
1873
|
+
else if( targetNode.isOutput() ) {
|
|
1874
|
+
return targetNode.def.link;
|
|
1875
|
+
}
|
|
1876
|
+
else {
|
|
1877
|
+
return targetNode.def.links && targetNode.def.links[this.targetInput] || {};
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
getPath() {
|
|
1882
|
+
return idx(this.getDef(), o => o.graph.path);
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
setDef(def) {
|
|
1886
|
+
var targetNode = this.getTargetNode();
|
|
1887
|
+
if( targetNode.isInput() ) {
|
|
1888
|
+
targetNode.def.link = def;
|
|
1889
|
+
}
|
|
1890
|
+
else if( targetNode.isOutput() ) {
|
|
1891
|
+
targetNode.def.link = def;
|
|
1892
|
+
}
|
|
1893
|
+
else {
|
|
1894
|
+
targetNode.def.links = targetNode.def.links || {};
|
|
1895
|
+
targetNode.def.links[this.targetInput] = def;
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
equals( dl ) {
|
|
1900
|
+
return this.sourceId == dl.sourceId &&
|
|
1901
|
+
this.targetId == dl.targetId &&
|
|
1902
|
+
this.sourceOutput == dl.sourceOutput &&
|
|
1903
|
+
this.targetInput == dl.targetInput;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
// set the connection in the app
|
|
1907
|
+
attach() {
|
|
1908
|
+
// build the connection object
|
|
1909
|
+
var sourceNode = this.getSourceNode();
|
|
1910
|
+
if( sourceNode.isInput() ) {
|
|
1911
|
+
var connection = { "input" : sourceNode.graphId }
|
|
1912
|
+
}
|
|
1913
|
+
else if( sourceNode.isOutput() ) {
|
|
1914
|
+
var connection = { "output" : sourceNode.graphId }
|
|
1915
|
+
}
|
|
1916
|
+
else {
|
|
1917
|
+
var connection = {};
|
|
1918
|
+
connection[ sourceNode.graphId ] = this.sourceOutput;
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
// add it at the right place in target node
|
|
1922
|
+
var targetNode = this.getTargetNode();
|
|
1923
|
+
if( targetNode.def.links && targetNode.def.links[this.targetInput] !== undefined ) {
|
|
1924
|
+
// if it's the same link we are trying to add, it's ok.
|
|
1925
|
+
var linkDef = targetNode.def.links[this.targetInput];
|
|
1926
|
+
var key = Object.keys(linkDef)[0];
|
|
1927
|
+
var val = linkDef[key];
|
|
1928
|
+
var l = new DataLink(this.m, key, val, this.targetId, this.targetInput);
|
|
1929
|
+
if( !this.equals(l) ) {
|
|
1930
|
+
throw "Can not add link " + this.sourceId + ":" + this.sourceOutput + " -> " +
|
|
1931
|
+
this.targetId + ":" + this.targetInput + " : a link already exist to target input";
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
this.setDef( connection );
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
// remove the connection from the app
|
|
1939
|
+
detach() {
|
|
1940
|
+
var targetNode = this.getTargetNode();
|
|
1941
|
+
if( targetNode.isInput() || targetNode.isOutput() ) {
|
|
1942
|
+
delete targetNode.def.link;
|
|
1943
|
+
}
|
|
1944
|
+
else {
|
|
1945
|
+
delete targetNode.def.links[this.targetInput];
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
/* representing a connection between 2 UIs */
|
|
1951
|
+
class EventLink {
|
|
1952
|
+
constructor(m, sourceId, targetId, eventName, eventCond, eventData) {
|
|
1953
|
+
this.m = m;
|
|
1954
|
+
this.sourceId = sourceId;
|
|
1955
|
+
this.targetId = targetId;
|
|
1956
|
+
this.eventName = eventName;
|
|
1957
|
+
this.eventCond = eventCond;
|
|
1958
|
+
this.eventData = eventData;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
toString() {
|
|
1962
|
+
return this.sourceId + " --(" + this.eventName + ")--> " + this.targetId;
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
getDef() {
|
|
1966
|
+
var n = this.m.getNode(this.sourceId);
|
|
1967
|
+
var def;
|
|
1968
|
+
_.each(n.def.events, (evt) => {
|
|
1969
|
+
if( evt.node == this.targetId && evt.event == this.eventName ) {
|
|
1970
|
+
def = evt;
|
|
1971
|
+
return false;
|
|
1972
|
+
}
|
|
1973
|
+
});
|
|
1974
|
+
return def || {};
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
setDef(def) {
|
|
1978
|
+
// check if event already exists
|
|
1979
|
+
var n = this.m.getNode(this.sourceId);
|
|
1980
|
+
var index;
|
|
1981
|
+
_.each(n.def.events, (evt, i) => {
|
|
1982
|
+
if( evt.node == this.targetId && evt.event == this.eventName ) {
|
|
1983
|
+
index = i
|
|
1984
|
+
return false;
|
|
1985
|
+
}
|
|
1986
|
+
});
|
|
1987
|
+
|
|
1988
|
+
if( !n.def.events ) n.def.events = [];
|
|
1989
|
+
|
|
1990
|
+
if( index ) {
|
|
1991
|
+
n.def.events[index] = def;
|
|
1992
|
+
}
|
|
1993
|
+
else {
|
|
1994
|
+
n.def.events.push(def);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
getPath() {
|
|
1999
|
+
return idx(this.getDef(), o => o.graph.path);
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
attach() {
|
|
2003
|
+
this.setDef({
|
|
2004
|
+
"node" : this.targetId,
|
|
2005
|
+
"event" : this.eventName
|
|
2006
|
+
});
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
detach() {
|
|
2010
|
+
var n = this.m.getNode(this.sourceId);
|
|
2011
|
+
_.remove(n.def.events, (evt) => {
|
|
2012
|
+
return evt.node === this.targetId && evt.event === this.eventName;
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
// splitted link: add a step between pos1 and pos2
|
|
2017
|
+
// prevPos: the previous position object ({ left: xxx, top: xxx}) or "source"
|
|
2018
|
+
// pos: the new step
|
|
2019
|
+
addPathStep( prevPos, pos ) {
|
|
2020
|
+
var def = this.getDef();
|
|
2021
|
+
if( _.isEmpty(def) ) {
|
|
2022
|
+
console.error('Definition of ' + this.toString() + ' shouldnt be empty');
|
|
2023
|
+
}
|
|
2024
|
+
if( prevPos == "source" ) {
|
|
2025
|
+
def.graph = def.graph || {};
|
|
2026
|
+
def.graph.path = def.graph.path || [];
|
|
2027
|
+
def.graph.path.unshift(pos);
|
|
2028
|
+
}
|
|
2029
|
+
else {
|
|
2030
|
+
// we need to find pos1 in the array to add pos at the right location
|
|
2031
|
+
var index;
|
|
2032
|
+
_.each( def.graph.path, (p, i) => {
|
|
2033
|
+
if( p.left == prevPos.left && p.top == prevPos.top ) {
|
|
2034
|
+
index = i+1;
|
|
2035
|
+
return false;
|
|
2036
|
+
}
|
|
2037
|
+
});
|
|
2038
|
+
def.graph.path.splice(index, 0, pos);
|
|
2039
|
+
}
|
|
2040
|
+
this.m.history.save();
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
updatePathStep( oldPos, newPos ) {
|
|
2044
|
+
var def = this.getDef();
|
|
2045
|
+
var index;
|
|
2046
|
+
_.each( def.graph.path, (p, i) => {
|
|
2047
|
+
if( p.left == oldPos.left && p.top == oldPos.top ) {
|
|
2048
|
+
index = i;
|
|
2049
|
+
return false;
|
|
2050
|
+
}
|
|
2051
|
+
});
|
|
2052
|
+
def.graph.path[index] = newPos;
|
|
2053
|
+
this.m.history.save();
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
removePathStep( pos ) {
|
|
2057
|
+
var def = this.getDef();
|
|
2058
|
+
var index;
|
|
2059
|
+
_.each( def.graph.path, (p, i) => {
|
|
2060
|
+
if( p.left == pos.left && p.top == pos.top ) {
|
|
2061
|
+
index = i;
|
|
2062
|
+
return false;
|
|
2063
|
+
}
|
|
2064
|
+
});
|
|
2065
|
+
def.graph.path.splice(index, 1);
|
|
2066
|
+
if( def.graph.path.length == 0 ) {
|
|
2067
|
+
delete def.graph.path;
|
|
2068
|
+
}
|
|
2069
|
+
this.m.history.save();
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
class Application {
|
|
2074
|
+
constructor(m, json) {
|
|
2075
|
+
this.m = m;
|
|
2076
|
+
this.json = json;
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
getDescription() {
|
|
2080
|
+
return idx(this.json, o => o.description);
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
setDescription(desc) {
|
|
2084
|
+
this.json.description = desc;
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
getInputsNames() {
|
|
2088
|
+
return _.keys( idx(this.json, o => o.input) );
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
getOutputsNames() {
|
|
2092
|
+
return _.keys( idx(this.json, o => o.output) );
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
getAttributesNames() {
|
|
2096
|
+
return _.keys( idx(this.json, o => o.attr) );
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
getEventsNames() {
|
|
2100
|
+
return _.keys( idx(this.json, o => o.events) );
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
hasInputs() {
|
|
2104
|
+
return this.getInputsNames().length > 0;
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
hasOutputs() {
|
|
2108
|
+
return this.getOutputsNames().length > 0;
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
hasAttributes() {
|
|
2112
|
+
return this.getAttributesNames().length > 0;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
hasEvents() {
|
|
2116
|
+
return this.getEventsNames().length > 0;
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
getEventDescription(name) {
|
|
2120
|
+
return idx(this.json, o => o.events[name].desc);
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
setEventDescription(name, desc) {
|
|
2124
|
+
this.json.events = this.json.events || {};
|
|
2125
|
+
this.json.events[name] = this.json.events[name] || {};
|
|
2126
|
+
this.json.events[name].desc = desc;
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
getEventIn(name) {
|
|
2130
|
+
return idx(this.json, o => o.events[name].in);
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
getEventOut(name) {
|
|
2134
|
+
return idx(this.json, o => o.events[name].out);
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
removeAppEvent(name) {
|
|
2138
|
+
delete this.json.events[name];
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
renameAppEvent(oldName, newName) {
|
|
2142
|
+
this.json.events[newName] = this.json.events[oldName];
|
|
2143
|
+
delete this.json.events[oldName];
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
removeAppInEvent(name, index) {
|
|
2147
|
+
this.json.events[name].in.splice(index, 1);
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
removeAppOutEvent(name) {
|
|
2151
|
+
delete this.json.events[name].out;
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
addSubEvent(name, nodeName, evtName) {
|
|
2155
|
+
this.json.events[name].in.push({ "node": nodeName, "event": evtName});
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
addCallback(name) {
|
|
2159
|
+
this.json.events[name].out = {};
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
addAppEvent(name) {
|
|
2163
|
+
this.json.events[name] = { "in" : [] };
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
module.exports = GraphModel;
|