//basic nodes (function(global) { var LiteGraph = global.LiteGraph; //Constant function Time() { this.addOutput("in ms", "number"); this.addOutput("in sec", "number"); } Time.title = "Time"; Time.desc = "Time"; Time.prototype.onExecute = function() { this.setOutputData(0, this.graph.globaltime * 1000); this.setOutputData(1, this.graph.globaltime); }; LiteGraph.registerNodeType("basic/time", Time); //Subgraph: a node that contains a graph function Subgraph() { var that = this; this.size = [140, 80]; this.properties = { enabled: true }; this.enabled = true; //create inner graph this.subgraph = new LiteGraph.LGraph(); this.subgraph._subgraph_node = this; this.subgraph._is_subgraph = true; this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind( this ); this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind( this ); this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); } Subgraph.title = "Subgraph"; Subgraph.desc = "Graph inside a node"; Subgraph.title_color = "#334"; Subgraph.prototype.onGetInputs = function() { return [["enabled", "boolean"]]; }; Subgraph.prototype.onDrawTitle = function(ctx) { if (this.flags.collapsed) { return; } ctx.fillStyle = "#555"; var w = LiteGraph.NODE_TITLE_HEIGHT; var x = this.size[0] - w; ctx.fillRect(x, -w, w, w); ctx.fillStyle = "#333"; ctx.beginPath(); ctx.moveTo(x + w * 0.2, -w * 0.6); ctx.lineTo(x + w * 0.8, -w * 0.6); ctx.lineTo(x + w * 0.5, -w * 0.3); ctx.fill(); }; Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { var that = this; setTimeout(function() { graphcanvas.openSubgraph(that.subgraph); }, 10); }; Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { if ( !this.flags.collapsed && pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) { var that = this; setTimeout(function() { graphcanvas.openSubgraph(that.subgraph); }, 10); } }; Subgraph.prototype.onAction = function(action, param) { this.subgraph.onAction(action, param); }; Subgraph.prototype.onExecute = function() { this.enabled = this.getInputOrProperty("enabled"); if (!this.enabled) { return; } //send inputs to subgraph global inputs if (this.inputs) { for (var i = 0; i < this.inputs.length; i++) { var input = this.inputs[i]; var value = this.getInputData(i); this.subgraph.setInputData(input.name, value); } } //execute this.subgraph.runStep(); //send subgraph global outputs to outputs if (this.outputs) { for (var i = 0; i < this.outputs.length; i++) { var output = this.outputs[i]; var value = this.subgraph.getOutputData(output.name); this.setOutputData(i, value); } } }; Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) { if (this.enabled) { this.subgraph.sendEventToAllNodes(eventname, param, mode); } }; //**** INPUTS *********************************** Subgraph.prototype.onSubgraphTrigger = function(event, param) { var slot = this.findOutputSlot(event); if (slot != -1) { this.triggerSlot(slot); } }; Subgraph.prototype.onSubgraphNewInput = function(name, type) { var slot = this.findInputSlot(name); if (slot == -1) { //add input to the node this.addInput(name, type); } }; Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { var slot = this.findInputSlot(oldname); if (slot == -1) { return; } var info = this.getInputInfo(slot); info.name = name; }; Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { var slot = this.findInputSlot(name); if (slot == -1) { return; } var info = this.getInputInfo(slot); info.type = type; }; Subgraph.prototype.onSubgraphRemovedInput = function(name) { var slot = this.findInputSlot(name); if (slot == -1) { return; } this.removeInput(slot); }; //**** OUTPUTS *********************************** Subgraph.prototype.onSubgraphNewOutput = function(name, type) { var slot = this.findOutputSlot(name); if (slot == -1) { this.addOutput(name, type); } }; Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { var slot = this.findOutputSlot(oldname); if (slot == -1) { return; } var info = this.getOutputInfo(slot); info.name = name; }; Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { var slot = this.findOutputSlot(name); if (slot == -1) { return; } var info = this.getOutputInfo(slot); info.type = type; }; Subgraph.prototype.onSubgraphRemovedOutput = function(name) { var slot = this.findInputSlot(name); if (slot == -1) { return; } this.removeOutput(slot); }; // ***************************************************** Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { var that = this; return [ { content: "Open", callback: function() { graphcanvas.openSubgraph(that.subgraph); } } ]; }; Subgraph.prototype.onResize = function(size) { size[1] += 20; }; Subgraph.prototype.serialize = function() { var data = LGraphNode.prototype.serialize.call(this); data.subgraph = this.subgraph.serialize(); return data; }; //no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() Subgraph.prototype.clone = function() { var node = LiteGraph.createNode(this.type); var data = this.serialize(); delete data["id"]; delete data["inputs"]; delete data["outputs"]; node.configure(data); return node; }; LiteGraph.Subgraph = Subgraph; LiteGraph.registerNodeType("graph/subgraph", Subgraph); //Input for a subgraph function GraphInput() { this.addOutput("", "number"); this.name_in_graph = ""; this.properties = { name: "", type: "number", value: 0 }; var that = this; this.name_widget = this.addWidget( "text", "Name", this.properties.name, function(v) { if (!v) { return; } that.setProperty("name",v); } ); this.type_widget = this.addWidget( "text", "Type", this.properties.type, function(v) { that.setProperty("type",v); } ); this.value_widget = this.addWidget( "number", "Value", this.properties.value, function(v) { that.setProperty("value",v); } ); this.widgets_up = true; this.size = [180, 90]; } GraphInput.title = "Input"; GraphInput.desc = "Input of the graph"; GraphInput.prototype.onConfigure = function() { this.updateType(); } GraphInput.prototype.updateType = function() { var type = this.properties.type; this.type_widget.value = type; if(type == "number") { this.value_widget.type = "number"; this.value_widget.value = 0; } else if(type == "bool") { this.value_widget.type = "toggle"; this.value_widget.value = true; } else if(type == "string") { this.value_widget.type = "text"; this.value_widget.value = ""; } else { this.value_widget.type = null; this.value_widget.value = null; } this.properties.value = this.value_widget.value; } GraphInput.prototype.onPropertyChanged = function(name,v) { if( name == "name" ) { if (v == "" || v == this.name_in_graph || v == "enabled") { return false; } if(this.graph) { if (this.name_in_graph) { //already added this.graph.renameInput( this.name_in_graph, v ); } else { this.graph.addInput( v, this.properties.type ); } } //what if not?! this.name_widget.value = v; this.name_in_graph = v; } else if( name == "type" ) { v = v || ""; this.updateType(v); } else if( name == "value" ) { } } GraphInput.prototype.getTitle = function() { if (this.flags.collapsed) { return this.properties.name; } return this.title; }; GraphInput.prototype.onAction = function(action, param) { if (this.properties.type == LiteGraph.EVENT) { this.triggerSlot(0, param); } }; GraphInput.prototype.onExecute = function() { var name = this.properties.name; //read from global input var data = this.graph.inputs[name]; if (!data) { this.setOutputData(0, this.properties.value ); } this.setOutputData(0, data.value === undefined ? this.properties.value : data.value); }; GraphInput.prototype.onRemoved = function() { if (this.name_in_graph) { this.graph.removeInput(this.name_in_graph); } }; LiteGraph.GraphInput = GraphInput; LiteGraph.registerNodeType("graph/input", GraphInput); //Output for a subgraph function GraphOutput() { this.addInput("", ""); this.name_in_graph = ""; this.properties = {}; var that = this; Object.defineProperty(this.properties, "name", { get: function() { return that.name_in_graph; }, set: function(v) { if (v == "" || v == that.name_in_graph) { return; } if (that.name_in_graph) { //already added that.graph.renameOutput(that.name_in_graph, v); } else { that.graph.addOutput(v, that.properties.type); } that.name_widget.value = v; that.name_in_graph = v; }, enumerable: true }); Object.defineProperty(this.properties, "type", { get: function() { return that.inputs[0].type; }, set: function(v) { if (v == "action" || v == "event") { v = LiteGraph.ACTION; } that.inputs[0].type = v; if (that.name_in_graph) { //already added that.graph.changeOutputType( that.name_in_graph, that.inputs[0].type ); } that.type_widget.value = v || ""; }, enumerable: true }); this.name_widget = this.addWidget( "text", "Name", this.properties.name, function(v) { if (!v) { return; } that.properties.name = v; } ); this.type_widget = this.addWidget( "text", "Type", this.properties.type, function(v) { v = v || ""; that.properties.type = v; } ); this.widgets_up = true; this.size = [180, 60]; } GraphOutput.title = "Output"; GraphOutput.desc = "Output of the graph"; GraphOutput.prototype.onExecute = function() { this._value = this.getInputData(0); this.graph.setOutputData(this.properties.name, this._value); }; GraphOutput.prototype.onAction = function(action, param) { if (this.properties.type == LiteGraph.ACTION) { this.graph.trigger(this.properties.name, param); } }; GraphOutput.prototype.onRemoved = function() { if (this.name_in_graph) { this.graph.removeOutput(this.name_in_graph); } }; GraphOutput.prototype.getTitle = function() { if (this.flags.collapsed) { return this.properties.name; } return this.title; }; LiteGraph.GraphOutput = GraphOutput; LiteGraph.registerNodeType("graph/output", GraphOutput); //Constant function ConstantNumber() { this.addOutput("value", "number"); this.addProperty("value", 1.0); } ConstantNumber.title = "Const Number"; ConstantNumber.desc = "Constant number"; ConstantNumber.prototype.onExecute = function() { this.setOutputData(0, parseFloat(this.properties["value"])); }; ConstantNumber.prototype.getTitle = function() { if (this.flags.collapsed) { return this.properties.value; } return this.title; }; ConstantNumber.prototype.setValue = function(v) { this.properties.value = v; }; ConstantNumber.prototype.onDrawBackground = function(ctx) { //show the current value this.outputs[0].label = this.properties["value"].toFixed(3); }; LiteGraph.registerNodeType("basic/const", ConstantNumber); function ConstantString() { this.addOutput("", "string"); this.addProperty("value", ""); this.widget = this.addWidget( "text", "value", "", this.setValue.bind(this) ); this.widgets_up = true; this.size = [100, 30]; } ConstantString.title = "Const String"; ConstantString.desc = "Constant string"; ConstantString.prototype.setValue = function(v) { this.properties.value = v; }; ConstantString.prototype.onPropertyChanged = function(name, value) { this.widget.value = value; }; ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; ConstantString.prototype.onExecute = function() { this.setOutputData(0, this.properties["value"]); }; LiteGraph.registerNodeType("basic/string", ConstantString); function ConstantData() { this.addOutput("", ""); this.addProperty("value", ""); this.widget = this.addWidget( "text", "json", "", this.setValue.bind(this) ); this.widgets_up = true; this.size = [140, 30]; this._value = null; } ConstantData.title = "Const Data"; ConstantData.desc = "Constant Data"; ConstantData.prototype.setValue = function(v) { this.properties.value = v; this.onPropertyChanged("value", v); }; ConstantData.prototype.onPropertyChanged = function(name, value) { this.widget.value = value; if (value == null || value == "") { return; } try { this._value = JSON.parse(value); this.boxcolor = "#AEA"; } catch (err) { this.boxcolor = "red"; } }; ConstantData.prototype.onExecute = function() { this.setOutputData(0, this._value); }; LiteGraph.registerNodeType("basic/data", ConstantData); function ObjectProperty() { this.addInput("obj", ""); this.addOutput("", ""); this.addProperty("value", ""); this.widget = this.addWidget( "text", "prop.", "", this.setValue.bind(this) ); this.widgets_up = true; this.size = [140, 30]; this._value = null; } ObjectProperty.title = "Object property"; ObjectProperty.desc = "Outputs the property of an object"; ObjectProperty.prototype.setValue = function(v) { this.properties.value = v; this.widget.value = v; }; ObjectProperty.prototype.getTitle = function() { if (this.flags.collapsed) { return "in." + this.properties.value; } return this.title; }; ObjectProperty.prototype.onPropertyChanged = function(name, value) { this.widget.value = value; }; ObjectProperty.prototype.onExecute = function() { var data = this.getInputData(0); if (data != null) { this.setOutputData(0, data[this.properties.value]); } }; LiteGraph.registerNodeType("basic/object_property", ObjectProperty); function ObjectKeys() { this.addInput("obj", ""); this.addOutput("keys", "array"); this.size = [140, 30]; } ObjectKeys.title = "Object keys"; ObjectKeys.desc = "Outputs an array with the keys of an object"; ObjectKeys.prototype.onExecute = function() { var data = this.getInputData(0); if (data != null) { this.setOutputData(0, Object.keys(data) ); } }; LiteGraph.registerNodeType("basic/object_keys", ObjectKeys); function MergeObjects() { this.addInput("A", "object"); this.addInput("B", "object"); this.addOutput("", "object"); this._result = {}; var that = this; this.addWidget("button","clear","",function(){ that._result = {}; }); this.size = this.computeSize(); } MergeObjects.title = "Merge Objects"; MergeObjects.desc = "Creates an object copying properties from others"; MergeObjects.prototype.onExecute = function() { var A = this.getInputData(0); var B = this.getInputData(1); var C = this._result; if(A) for(var i in A) C[i] = A[i]; if(B) for(var i in B) C[i] = B[i]; this.setOutputData(0,C); }; LiteGraph.registerNodeType("basic/merge_objects", MergeObjects ); //Store as variable function Variable() { this.size = [60, 30]; this.addInput("in"); this.addOutput("out"); this.properties = { varname: "myname", global: false }; this.value = null; } Variable.title = "Variable"; Variable.desc = "store/read variable value"; Variable.prototype.onExecute = function() { this.value = this.getInputData(0); if(this.graph) this.graph.vars[ this.properties.varname ] = this.value; if(this.properties.global) global[this.properties.varname] = this.value; this.setOutputData(0, this.value ); }; Variable.prototype.getTitle = function() { return this.properties.varname; }; LiteGraph.registerNodeType("basic/variable", Variable); function DownloadData() { this.size = [60, 30]; this.addInput("data", 0 ); this.addInput("download", LiteGraph.ACTION ); this.properties = { filename: "data.json" }; this.value = null; var that = this; this.addWidget("button","Download","", function(v){ if(!that.value) return; that.downloadAsFile(); }); } DownloadData.title = "Download"; DownloadData.desc = "Download some data"; DownloadData.prototype.downloadAsFile = function() { if(this.value == null) return; var str = null; if(this.value.constructor === String) str = this.value; else str = JSON.stringify(this.value); var file = new Blob([str]); var url = URL.createObjectURL( file ); var element = document.createElement("a"); element.setAttribute('href', url); element.setAttribute('download', this.properties.filename ); element.style.display = 'none'; document.body.appendChild(element); element.click(); document.body.removeChild(element); setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url } DownloadData.prototype.onAction = function(action, param) { var that = this; setTimeout( function(){ that.downloadAsFile(); }, 100); //deferred to avoid blocking the renderer with the popup } DownloadData.prototype.onExecute = function() { if (this.inputs[0]) { this.value = this.getInputData(0); } }; DownloadData.prototype.getTitle = function() { if (this.flags.collapsed) { return this.properties.filename; } return this.title; }; LiteGraph.registerNodeType("basic/download", DownloadData); //Watch a value in the editor function Watch() { this.size = [60, 30]; this.addInput("value", 0, { label: "" }); this.value = 0; } Watch.title = "Watch"; Watch.desc = "Show value of input"; Watch.prototype.onExecute = function() { if (this.inputs[0]) { this.value = this.getInputData(0); } }; Watch.prototype.getTitle = function() { if (this.flags.collapsed) { return this.inputs[0].label; } return this.title; }; Watch.toString = function(o) { if (o == null) { return "null"; } else if (o.constructor === Number) { return o.toFixed(3); } else if (o.constructor === Array) { var str = "["; for (var i = 0; i < o.length; ++i) { str += Watch.toString(o[i]) + (i + 1 != o.length ? "," : ""); } str += "]"; return str; } else { return String(o); } }; Watch.prototype.onDrawBackground = function(ctx) { //show the current value this.inputs[0].label = Watch.toString(this.value); }; LiteGraph.registerNodeType("basic/watch", Watch); //in case one type doesnt match other type but you want to connect them anyway function Cast() { this.addInput("in", 0); this.addOutput("out", 0); this.size = [40, 30]; } Cast.title = "Cast"; Cast.desc = "Allows to connect different types"; Cast.prototype.onExecute = function() { this.setOutputData(0, this.getInputData(0)); }; LiteGraph.registerNodeType("basic/cast", Cast); //Show value inside the debug console function Console() { this.mode = LiteGraph.ON_EVENT; this.size = [80, 30]; this.addProperty("msg", ""); this.addInput("log", LiteGraph.EVENT); this.addInput("msg", 0); } Console.title = "Console"; Console.desc = "Show value inside the console"; Console.prototype.onAction = function(action, param) { if (action == "log") { console.log(param); } else if (action == "warn") { console.warn(param); } else if (action == "error") { console.error(param); } }; Console.prototype.onExecute = function() { var msg = this.getInputData(1); if (msg !== null) { this.properties.msg = msg; } console.log(msg); }; Console.prototype.onGetInputs = function() { return [ ["log", LiteGraph.ACTION], ["warn", LiteGraph.ACTION], ["error", LiteGraph.ACTION] ]; }; LiteGraph.registerNodeType("basic/console", Console); //Show value inside the debug console function Alert() { this.mode = LiteGraph.ON_EVENT; this.addProperty("msg", ""); this.addInput("", LiteGraph.EVENT); var that = this; this.widget = this.addWidget("text", "Text", "", function(v) { that.properties.msg = v; }); this.widgets_up = true; this.size = [200, 30]; } Alert.title = "Alert"; Alert.desc = "Show an alert window"; Alert.color = "#510"; Alert.prototype.onConfigure = function(o) { this.widget.value = o.properties.msg; }; Alert.prototype.onAction = function(action, param) { var msg = this.properties.msg; setTimeout(function() { alert(msg); }, 10); }; LiteGraph.registerNodeType("basic/alert", Alert); //Execites simple code function NodeScript() { this.size = [60, 30]; this.addProperty("onExecute", "return A;"); this.addInput("A", ""); this.addInput("B", ""); this.addOutput("out", ""); this._func = null; this.data = {}; } NodeScript.prototype.onConfigure = function(o) { if (o.properties.onExecute && LiteGraph.allow_scripts) this.compileCode(o.properties.onExecute); else console.warn("Script not compiled, LiteGraph.allow_scripts is false"); }; NodeScript.title = "Script"; NodeScript.desc = "executes a code (max 100 characters)"; NodeScript.widgets_info = { onExecute: { type: "code" } }; NodeScript.prototype.onPropertyChanged = function(name, value) { if (name == "onExecute" && LiteGraph.allow_scripts) this.compileCode(value); else console.warn("Script not compiled, LiteGraph.allow_scripts is false"); }; NodeScript.prototype.compileCode = function(code) { this._func = null; if (code.length > 256) { console.warn("Script too long, max 256 chars"); } else { var code_low = code.toLowerCase(); var forbidden_words = [ "script", "body", "document", "eval", "nodescript", "function" ]; //bad security solution for (var i = 0; i < forbidden_words.length; ++i) { if (code_low.indexOf(forbidden_words[i]) != -1) { console.warn("invalid script"); return; } } try { this._func = new Function("A", "B", "C", "DATA", "node", code); } catch (err) { console.error("Error parsing script"); console.error(err); } } }; NodeScript.prototype.onExecute = function() { if (!this._func) { return; } try { var A = this.getInputData(0); var B = this.getInputData(1); var C = this.getInputData(2); this.setOutputData(0, this._func(A, B, C, this.data, this)); } catch (err) { console.error("Error in script"); console.error(err); } }; NodeScript.prototype.onGetOutputs = function() { return [["C", ""]]; }; LiteGraph.registerNodeType("basic/script", NodeScript); })(this);