Files
ComfyUI_frontend/src/nodes/base.js
DABEOM KIM 6d9fa8a41d Resolve "'LGraph' is not defined" issue
Resolve Unable to use SubGraph node with webpack.
2019-11-04 23:47:33 +09:00

995 lines
27 KiB
JavaScript
Executable File

//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);