|
98 | 98 | catch_exceptions: true, |
99 | 99 | throw_errors: true, |
100 | 100 | allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits |
| 101 | + use_deferred_actions: true, //executes actions during the graph execution flow |
101 | 102 | registered_node_types: {}, //nodetypes by string |
102 | 103 | node_types_by_file_extension: {}, //used for dropping files in the canvas |
103 | 104 | Nodes: {}, //node types by classname |
|
327 | 328 | } |
328 | 329 | }, |
329 | 330 |
|
| 331 | + /** |
| 332 | + * Create a new nodetype by passing an object with some properties |
| 333 | + * like onCreate, inputs:Array, outputs:Array, properties, onExecute |
| 334 | + * @method buildNodeClassFromObject |
| 335 | + * @param {String} name node name with namespace (p.e.: 'math/sum') |
| 336 | + * @param {Object} object methods expected onCreate, inputs, outputs, properties, onExecute |
| 337 | + */ |
| 338 | + buildNodeClassFromObject: function( |
| 339 | + name, |
| 340 | + object |
| 341 | + ) { |
| 342 | + var ctor_code = ""; |
| 343 | + if(object.inputs) |
| 344 | + for(var i=0; i < object.inputs.length; ++i) |
| 345 | + { |
| 346 | + var _name = object.inputs[i][0]; |
| 347 | + var _type = object.inputs[i][1]; |
| 348 | + if(_type && _type.constructor === String) |
| 349 | + _type = '"'+_type+'"'; |
| 350 | + ctor_code += "this.addInput('"+_name+"',"+_type+");\n"; |
| 351 | + } |
| 352 | + if(object.outputs) |
| 353 | + for(var i=0; i < object.outputs.length; ++i) |
| 354 | + { |
| 355 | + var _name = object.outputs[i][0]; |
| 356 | + var _type = object.outputs[i][1]; |
| 357 | + if(_type && _type.constructor === String) |
| 358 | + _type = '"'+_type+'"'; |
| 359 | + ctor_code += "this.addOutput('"+_name+"',"+_type+");\n"; |
| 360 | + } |
| 361 | + if(object.properties) |
| 362 | + for(var i in object.properties) |
| 363 | + { |
| 364 | + var prop = object.properties[i]; |
| 365 | + if(prop && prop.constructor === String) |
| 366 | + prop = '"'+prop+'"'; |
| 367 | + ctor_code += "this.addProperty('"+i+"',"+prop+");\n"; |
| 368 | + } |
| 369 | + ctor_code += "if(this.onCreate)this.onCreate()"; |
| 370 | + var classobj = Function(ctor_code); |
| 371 | + for(var i in object) |
| 372 | + if(i!="inputs" && i!="outputs" && i!="properties") |
| 373 | + classobj.prototype[i] = object[i]; |
| 374 | + classobj.title = object.title || name.split("/").pop(); |
| 375 | + classobj.desc = object.desc || "Generated from object"; |
| 376 | + this.registerNodeType(name, classobj); |
| 377 | + return classobj; |
| 378 | + }, |
| 379 | + |
330 | 380 | /** |
331 | 381 | * Create a new nodetype by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function. |
332 | 382 | * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output. |
|
346 | 396 | ) { |
347 | 397 | var params = Array(func.length); |
348 | 398 | var code = ""; |
349 | | - var names = LiteGraph.getParameterNames(func); |
350 | | - for (var i = 0; i < names.length; ++i) { |
351 | | - code += |
352 | | - "this.addInput('" + |
353 | | - names[i] + |
354 | | - "'," + |
355 | | - (param_types && param_types[i] |
356 | | - ? "'" + param_types[i] + "'" |
357 | | - : "0") + |
358 | | - ");\n"; |
| 399 | + if(param_types !== null) //null means no inputs |
| 400 | + { |
| 401 | + var names = LiteGraph.getParameterNames(func); |
| 402 | + for (var i = 0; i < names.length; ++i) { |
| 403 | + var type = 0; |
| 404 | + if(param_types) |
| 405 | + { |
| 406 | + //type = param_types[i] != null ? "'" + param_types[i] + "'" : "0"; |
| 407 | + if( param_types[i] != null && param_types[i].constructor === String ) |
| 408 | + type = "'" + param_types[i] + "'" ; |
| 409 | + else if( param_types[i] != null ) |
| 410 | + type = param_types[i]; |
| 411 | + } |
| 412 | + code += |
| 413 | + "this.addInput('" + |
| 414 | + names[i] + |
| 415 | + "'," + |
| 416 | + type + |
| 417 | + ");\n"; |
| 418 | + } |
359 | 419 | } |
| 420 | + if(return_type !== null) //null means no output |
360 | 421 | code += |
361 | 422 | "this.addOutput('out'," + |
362 | | - (return_type ? "'" + return_type + "'" : 0) + |
| 423 | + (return_type != null ? (return_type.constructor === String ? "'" + return_type + "'" : return_type) : 0) + |
363 | 424 | ");\n"; |
364 | 425 | if (properties) { |
365 | 426 | code += |
|
376 | 437 | this.setOutputData(0, r); |
377 | 438 | }; |
378 | 439 | this.registerNodeType(name, classobj); |
| 440 | + return classobj; |
379 | 441 | }, |
380 | 442 |
|
381 | 443 | /** |
|
3142 | 3204 | // enable this to give the event an ID |
3143 | 3205 | if (!options.action_call) options.action_call = this.id+"_exec_"+Math.floor(Math.random()*9999); |
3144 | 3206 |
|
| 3207 | + if(this._waiting_actions && this._waiting_actions.length) |
| 3208 | + for(var i = 0; i < this._waiting_actions.length;++i) |
| 3209 | + { |
| 3210 | + var p = this._waiting_actions[i]; |
| 3211 | + this.onAction(p[0],p[1],p[2],p[3],p[4]); |
| 3212 | + } |
| 3213 | + |
3145 | 3214 | this.graph.nodes_executing[this.id] = true; //.push(this.id); |
3146 | 3215 |
|
3147 | 3216 | this.onExecute(param, options); |
|
3155 | 3224 | this.graph.nodes_executedAction[this.id] = options.action_call; |
3156 | 3225 | } |
3157 | 3226 | } |
| 3227 | + else { |
| 3228 | + if(this._waiting_actions && this._waiting_actions.length) |
| 3229 | + for(var i = 0; i < this._waiting_actions.length;++i) |
| 3230 | + { |
| 3231 | + var p = this._waiting_actions[i]; |
| 3232 | + this.onAction(p[0],p[1],p[2],p[3],p[4]); |
| 3233 | + } |
| 3234 | + } |
3158 | 3235 | this.execute_triggered = 2; // the nFrames it will be used (-- each step), means "how old" is the event |
3159 | 3236 | if(this.onAfterExecuteNode) this.onAfterExecuteNode(param, options); // callback |
3160 | 3237 | }; |
|
3165 | 3242 | * @param {String} action name |
3166 | 3243 | * @param {*} param |
3167 | 3244 | */ |
3168 | | - LGraphNode.prototype.actionDo = function(action, param, options) { |
| 3245 | + LGraphNode.prototype.actionDo = function(action, param, options, action_slot ) { |
3169 | 3246 | options = options || {}; |
3170 | 3247 | if (this.onAction){ |
3171 | 3248 |
|
|
3174 | 3251 |
|
3175 | 3252 | this.graph.nodes_actioning[this.id] = (action?action:"actioning"); //.push(this.id); |
3176 | 3253 |
|
3177 | | - this.onAction(action, param, options); |
| 3254 | + this.onAction(action, param, options, action_slot); |
3178 | 3255 |
|
3179 | 3256 | this.graph.nodes_actioning[this.id] = false; //.pop(); |
3180 | 3257 |
|
|
3282 | 3359 | if (!options.action_call) options.action_call = this.id+"_act_"+Math.floor(Math.random()*9999); |
3283 | 3360 | //pass the action name |
3284 | 3361 | var target_connection = node.inputs[link_info.target_slot]; |
3285 | | - // wrap node.onAction(target_connection.name, param); |
3286 | | - node.actionDo(target_connection.name, param, options); |
| 3362 | + |
| 3363 | + //instead of executing them now, it will be executed in the next graph loop, to ensure data flow |
| 3364 | + if(LiteGraph.use_deferred_actions) |
| 3365 | + { |
| 3366 | + if(!node._waiting_actions) |
| 3367 | + node._waiting_actions = []; |
| 3368 | + node._waiting_actions.push([target_connection.name, param, options, link_info.target_slot]); |
| 3369 | + } |
| 3370 | + else |
| 3371 | + { |
| 3372 | + // wrap node.onAction(target_connection.name, param); |
| 3373 | + node.actionDo( target_connection.name, param, options, link_info.target_slot ); |
| 3374 | + } |
3287 | 3375 | } |
3288 | 3376 | } |
3289 | 3377 | }; |
@@ -5646,7 +5734,7 @@ LGraphNode.prototype.executeAction = function(action) |
5646 | 5734 |
|
5647 | 5735 | //Keyboard ****************** |
5648 | 5736 | this._key_callback = this.processKey.bind(this); |
5649 | | - |
| 5737 | + canvas.setAttribute("tabindex",1); //otherwise key events are ignored |
5650 | 5738 | canvas.addEventListener("keydown", this._key_callback, true); |
5651 | 5739 | document.addEventListener("keyup", this._key_callback, true); //in document, otherwise it doesn't fire keyup |
5652 | 5740 |
|
@@ -7150,6 +7238,8 @@ LGraphNode.prototype.executeAction = function(action) |
7150 | 7238 |
|
7151 | 7239 | for (var i = 0; i < selected_nodes_array.length; ++i) { |
7152 | 7240 | var node = selected_nodes_array[i]; |
| 7241 | + if(node.clonable === false) |
| 7242 | + continue; |
7153 | 7243 | var cloned = node.clone(); |
7154 | 7244 | if(!cloned) |
7155 | 7245 | { |
@@ -11540,6 +11630,7 @@ LGraphNode.prototype.executeAction = function(action) |
11540 | 11630 | //ESC |
11541 | 11631 | dialog.close(); |
11542 | 11632 | } else if (e.keyCode == 13) { |
| 11633 | + refreshHelper(); |
11543 | 11634 | if (selected) { |
11544 | 11635 | select(selected.innerHTML); |
11545 | 11636 | } else if (first) { |
|
0 commit comments