LGraph Class
+LGraph Class
-LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop.
Constructor
-LGraph
-
()
-
-
-
-
-
-
-
-
Item Index
-Methods
-
-
- add - - -
- attachCanvas - - -
- clear - - -
- configure - - -
- detachCanvas - - -
- findNodesByClass - - -
- findNodesByName - - -
- findNodesByType - - -
- getElapsedTime - - -
- getFixedTime - - -
- getNodeById - - -
- getNodeOnPos - - -
- getTime - - -
- isLive - - -
- remove - - -
- runStep - - -
- sendEventToAllNodes - - -
- serialize - - -
- setInputData - - -
- setInputData - - -
- start - - -
- stop execution - - -
- updateExecutionOrder - - -
Methods
- -add
-
-
-
-
-
node-
-
Parameters:
-
-
-
-
nodeLGraphNode - --the instance of the node
-
attachCanvas
-
-
-
-
-
graph_canvas-
-
Parameters:
-
-
-
-
graph_canvasGraphCanvas - --
-
clear
-
()
-
-
-
-
-
-
-
-
configure
-
-
-
-
-
str-
-
Parameters:
-
-
-
-
strString - --configure a graph from a JSON string
-
detachCanvas
-
-
-
-
-
graph_canvas-
-
Parameters:
-
-
-
-
graph_canvasGraphCanvas - --
-
findNodesByClass
-
-
-
-
-
classObject-
-
Parameters:
-
-
-
-
classObjectClass - --the class itself (not an string)
-
Returns:
a list with all the nodes of this type
-findNodesByName
-
-
-
-
-
name-
-
Parameters:
-
-
-
-
nameString - --the name of the node to search
-
Returns:
a list with all the nodes with this name
-findNodesByType
-
-
-
-
-
type-
-
Parameters:
-
-
-
-
typeString - --the name of the node type
-
Returns:
a list with all the nodes of this type
-getElapsedTime
-
()
-
-
Number
-
-
-
-
-
-
-
Returns:
number of milliseconds it took the last cycle
-getFixedTime
-
()
-
-
Number
-
-
-
-
-
-
-
Returns:
number of milliseconds the graph has been running
-getNodeById
-
-
-
-
-
id-
-
Parameters:
-
-
-
-
idString - --
-
getNodeOnPos
-
-
-
-
-
x-
-
-
-
y-
-
-
-
nodes_list-
-
Parameters:
-
-
-
-
xNumber - --the x coordinate in canvas space
-
-
-
yNumber - --the y coordinate in canvas space
-
-
-
nodes_listArray - --a list with all the nodes to search from, by default is all the nodes in the graph
-
Returns:
a list with all the nodes that intersect this coordinate
-getTime
-
()
-
-
Number
-
-
-
-
-
-
-
Returns:
number of milliseconds the graph has been running
-isLive
-
()
-
-
-
-
-
-
-
-
remove
-
-
-
-
-
node-
-
Parameters:
-
-
-
-
nodeLGraphNode - --the instance of the node
-
runStep
-
-
-
-
-
num-
-
Parameters:
-
-
-
-
numNumber - --number of steps to run, default is 1
-
sendEventToAllNodes
-
-
-
-
-
eventname-
-
-
-
params-
-
Parameters:
-
-
-
-
eventnameString - --the name of the event (function to be called)
-
-
-
paramsArray - --parameters in array format
-
serialize
-
()
-
-
Object
-
-
-
-
-
-
-
Returns:
value of the node
-setInputData
-
-
-
-
-
name-
-
-
-
value-
-
Parameters:
-
-
-
-
nameString - --the name of the node
-
-
-
value- --value to assign to this node
-
setInputData
-
-
-
-
-
name-
-
Parameters:
-
-
-
-
nameString - --the name of the node
-
Returns:
value of the node
-start
-
-
-
-
-
interval-
-
Parameters:
-
-
-
-
intervalNumber - --amount of milliseconds between executions, default is 1
-
stop execution
-
()
-
-
-
-
-
-
-
-
updateExecutionOrder
-
()
-
-
-
-
-
-
-
-
LGraphCanvas Class
+LGraphCanvas Class
-marks as dirty the canvas, this way it will be rendered again
Constructor
-LGraphCanvas
-
-
-
-
-
canvas-
-
-
-
graph-
-
+ -
+
options+
Parameters:
-
-
-
-
canvasHTMLCanvas - --the canvas where you want to render (it accepts a selector in string format or the canvas itself)
-
-
-
graphLGraph - --[optional]
-
+ -
+
options+ Object + + +++ +[optional] { skip_rendering, autoresize }
+ +
Item Index
-Methods
-
-
- clear - - -
- closeSubgraph - - -
- getCanvasWindow - - -
- openSubgraph - - -
- setCanvas - - -
- setGraph - - -
- startRendering - - -
- stopRendering - - -
Methods
- -clear
-
()
-
-
-
-
-
-
-
-
closeSubgraph
-
-
-
-
-
assigns-
-
Parameters:
-
-
-
-
assignsLGraph - --a graph
-
getCanvasWindow
-
()
-
-
Window
-
-
-
-
-
-
-
Returns:
returns the window where the canvas is attached (the DOM root node)
-openSubgraph
-
-
-
-
-
graph-
-
Parameters:
-
-
-
-
graphLGraph - --
-
setCanvas
-
-
-
-
-
assigns-
-
Parameters:
-
-
-
-
assignsCanvas - --a canvas
-
setGraph
-
-
-
-
-
graph-
-
Parameters:
-
-
-
-
graphLGraph - --
-
startRendering
-
()
-
-
-
-
-
-
-
-
stopRendering
-
()
-
-
-
-
-
-
-
-
LGraphNode Class
+LGraphNode Class
-Base Class for all the node type classes
Item Index
-Methods
-
-
- addConnection - - -
- addInput - - -
- addInputs - - -
- addOutput - - -
- addOutputs - - - +
- + addProperty + +
- collapse - - -
- computeSize - - -
- configure - - -
- connect - - -
- disconnectInput - - -
- disconnectOutput - - -
- findInputSlot - - -
- findOutputSlot - - -
- getBounding - - -
- getConnectionPos - - -
- getInputData - - -
- getInputInfo - - - +
- + getInputNode + +
- getOutputInfo - - -
- getOutputNodes - - -
- getSlotInPosition - - -
- getTitle - - -
- isInputConnected - - -
- isOutputConnected - - -
- isPointInsideNode - - -
- pin - - -
- removeInput - - -
- removeOutput - - -
- serialize - - -
- setOutputData - - -
- toString - - - +
- + trigger + +
Methods
- -addConnection
-
-
-
-
-
name-
-
-
-
type-
-
-
-
pos-
-
-
-
direction-
-
Parameters:
-
-
-
-
nameString - --
-
-
-
typeString - --string defining the input type ("vec3","number",...)
-
-
-
posx,y - --position of the connection inside the node
-
-
-
directionString - --if is input or output
-
addInput
-
-
-
-
-
name-
-
-
-
type-
-
-
-
extra_info-
-
Parameters:
-
-
-
-
nameString - --
-
-
-
typeString - --string defining the input type ("vec3","number",...), it its a generic one use 0
-
-
-
extra_infoObject - --this can be used to have special properties of an input (label, color, position, etc)
-
addInputs
-
-
-
-
-
array-
-
Parameters:
-
-
-
-
arrayArray - --of triplets like [[name,type,extra_info],[...]]
-
addOutput
-
-
-
-
-
name-
-
-
-
type-
-
-
-
extra_info-
-
Parameters:
-
-
-
-
nameString - --
-
-
-
typeString - --string defining the output type ("vec3","number",...)
-
-
-
extra_infoObject - --this can be used to have special properties of an output (label, special color, position, etc)
-
addOutputs
-
-
-
-
-
array-
-
Parameters:
-
-
-
-
arrayArray - --of triplets like [[name,type,extra_info],[...]]
-
addProperty
-
- collapse
+ -
+
-
+
name+
+ -
+
default_value+
+ -
+
type+
+ -
+
extra_info+
+
add a new property to this node
+ +Parameters:
+ +-
+
-
+
name+ String + + ++ ++ +
+ -
+
default_value+ + + ++ ++ +
+ -
+
type+ String + + +++ +string defining the output type ("vec3","number",...)
+ +
+ -
+
extra_info+ Object + + +++ +this can be used to have special properties of the property (like values, etc)
+ +
+
collapse
+
+ ()
+
+
+
+
+
+
+
+
+
computeSize
-
-
-
-
-
minHeight-
-
Parameters:
-
-
-
-
minHeightNumber - --
-
Returns:
the total size
-configure
-
()
-
-
-
-
-
-
-
-
connect
-
-
-
-
-
slot-
-
-
-
node-
-
-
-
target_slot-
-
Parameters:
-
-
-
-
slotNumber_or_string - --(could be the number of the slot or the string with the name of the slot)
-
-
-
nodeLGraphNode - --the target node
-
-
-
target_slotNumber_or_string - ---the input slot of the target node (could be the number of the slot or the string with the name of the slot)
+the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger)
-
Returns:
if it was connected succesfully
-disconnectInput
-
-
-
-
-
slot-
-
Parameters:
-
-
-
-
slotNumber_or_string - --(could be the number of the slot or the string with the name of the slot)
-
Returns:
if it was disconnected succesfully
-disconnectOutput
-
-
-
-
-
slot-
-
-
-
target_node-
-
Parameters:
-
-
-
-
slotNumber_or_string - --(could be the number of the slot or the string with the name of the slot)
-
-
-
target_nodeLGraphNode - --the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected]
-
Returns:
if it was disconnected succesfully
-findInputSlot
-
-
-
-
-
name-
-
Parameters:
-
-
-
-
nameString - --the name of the slot
-
Returns:
the slot (-1 if not found)
-findOutputSlot
-
-
-
-
-
name-
-
Parameters:
-
-
-
-
nameString - --the name of the slot
-
Returns:
the slot (-1 if not found)
-getBounding
-
()
-
-
Float32Array4
-
-
-
-
-
-
-
Returns:
the total size
-getConnectionPos
-
-
-
-
-
is_input-
-
-
-
slot-
-
Parameters:
-
-
-
-
is_inputBoolean - --true if if a input slot, false if it is an output
-
-
-
slotNumber_or_string - --(could be the number of the slot or the string with the name of the slot)
-
Returns:
the position
-getInputData
-
-
-
-
-
slot-
-
Parameters:
-
-
-
-
slotNumber - --
-
Returns:
data or if it is not connected returns undefined
-getInputInfo
-
-
-
-
-
slot-
-
Parameters:
-
-
-
-
slotNumber - --
-
Returns:
object or null
-getInputNode
-
- getOutputInfo
-
-
-
-
-
-
slot-
-
returns the node connected in the input slot
+ +Parameters:
+ +-
+
-
+
slot+ Number + + ++ ++ +
+
Returns:
+ +node or null
+ +getOutputInfo
+
+ -
+
-
+
slot+
+
Parameters:
-
-
-
-
slotNumber - --
-
Returns:
object or null
-getOutputNodes
-
-
-
-
-
slot-
-
Parameters:
-
-
-
-
slotNumber - --
-
Returns:
getSlotInPosition
-
-
-
-
-
x-
-
-
-
y-
-
Parameters:
-
-
-
-
xNumber - --
-
-
-
yNumber - --
-
Returns:
if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] }
-getTitle
-
()
-
-
-
-
-
-
-
-
isInputConnected
-
-
-
-
-
slot-
-
Parameters:
-
-
-
-
slotNumber - --
-
Returns:
isOutputConnected
-
-
-
-
-
slot-
-
Parameters:
-
-
-
-
slotNumber - --
-
Returns:
isPointInsideNode
-
-
-
-
-
x-
-
-
-
y-
-
Parameters:
-
-
-
-
xNumber - --
-
-
-
yNumber - --
-
Returns:
pin
-
()
-
-
-
-
-
-
-
-
removeInput
-
-
-
-
-
slot-
-
Parameters:
-
-
-
-
slotNumber - --
-
removeOutput
-
-
-
-
-
slot-
-
Parameters:
-
-
-
-
slotNumber - --
-
serialize
-
()
-
-
-
-
-
-
-
-
setOutputData
-
-
-
-
-
slot-
-
-
-
data-
-
Parameters:
-
-
-
-
slotNumber - --
-
-
-
data- --
-
toString
-
()
-
-
-
-
-
-
-
-
trigger
-
+ -
+
-
+
event+
+ -
+
param+
+
Triggers an event in this node, this will trigger any output with the same name
+ +Parameters:
+ +-
+
-
+
event+ String + + +++ +name ( "on_play", ... ) if action is equivalent to false then the event is send to all
+ +
+ -
+
param+ + + ++ ++ +
+
LiteGraph Class
+LiteGraph Class
-The Global Scope. It contains all the registered node classes.
Constructor
-LiteGraph
-
()
-
-
-
-
-
-
-
-
Item Index
-Methods
-
-
+
- + addNodeMethod + +
- createNode - - -
- getNodeType - - -
- getNodeType - - -
- getNodeTypesCategories - - -
- registerNodeType - - -
Methods
- -createNode
+addNodeMethod
-
-
-
-
-
-
type- +func
-
- -
-
-
name- -
-
- -
-
-
options- -
-
Adds this method to all nodetypes, existing and to be created +(You can add it to LGraphNode.prototype but then existing node types wont have it)
+ +Parameters:
+ +-
+
-
+
func+ Function + + ++ ++ +
+
createNode
+
+ -
+
-
+
type+
+ -
+
name+
+ -
+
options+
+
Parameters:
-
-
-
-
typeString - --full name of the node class. p.e. "math/sin"
-
-
-
nameString - --a name to distinguish from other nodes
-
-
-
optionsObject - --to set options
-
getNodeType
-
-
-
-
-
type-
-
Parameters:
-
-
-
-
typeString - --full name of the node class. p.e. "math/sin"
-
Returns:
the node class
-getNodeType
-
-
-
-
-
category-
-
Parameters:
-
-
-
-
categoryString - --category name
-
Returns:
array with all the node classes
-getNodeTypesCategories
-
()
-
-
Array
-
-
-
-
-
-
-
Returns:
array with all the names of the categories
-registerNodeType
-
-
-
-
-
type-
-
-
-
base_class-
-
Parameters:
-
-
-
-
typeString - --name of the node and path
-
-
-
base_classClass - --class containing the structure of a node
-
File: ../src/litegraph.js
+File: ../src/litegraph.js
@@ -122,12 +113,23 @@ var LiteGraph = {
DEFAULT_POSITION: [100,100],//default node position
node_images_path: "",
+ //enums
+ INPUT: 1,
+ OUTPUT: 2,
+
+ EVENT: -1, //for outputs
+ ACTION: -1, //for inputs
+
+ ALWAYS: 0,
+ ON_EVENT: 1,
+ NEVER: 2,
+
proxy: null, //used to redirect calls
debug: false,
throw_errors: true,
- registered_node_types: {},
- Nodes: {},
+ registered_node_types: {}, //nodetypes by string
+ Nodes: {}, //node types by classname
/**
* Register a node class so it can be listed when the user wants to create a new one
@@ -160,6 +162,23 @@ var LiteGraph = {
this.registered_node_types[ type ] = base_class;
if(base_class.constructor.name)
this.Nodes[ base_class.constructor.name ] = base_class;
+
+ //warnings
+ if(base_class.prototype.onPropertyChange)
+ console.warn("LiteGraph node class " + type + " has onPropertyChange method, it must be called onPropertyChanged with d at the end");
+ },
+
+ /**
+ * Adds this method to all nodetypes, existing and to be created
+ * (You can add it to LGraphNode.prototype but then existing node types wont have it)
+ * @method addNodeMethod
+ * @param {Function} func
+ */
+ addNodeMethod: function( name, func )
+ {
+ LGraphNode.prototype[name] = func;
+ for(var i in this.registered_node_types)
+ this.registered_node_types[i].prototype[name] = func;
},
/**
@@ -170,7 +189,7 @@ var LiteGraph = {
* @param {Object} options to set options
*/
- createNode: function(type, title, options)
+ createNode: function( type, title, options )
{
var base_class = this.registered_node_types[type];
if (!base_class)
@@ -189,9 +208,11 @@ var LiteGraph = {
node.type = type;
if(!node.title) node.title = title;
if(!node.properties) node.properties = {};
+ if(!node.properties_info) node.properties_info = [];
if(!node.flags) node.flags = {};
if(!node.size) node.size = node.computeSize();
if(!node.pos) node.pos = LiteGraph.DEFAULT_POSITION.concat();
+ if(!node.mode) node.mode = LiteGraph.ALWAYS;
//extra options
if(options)
@@ -308,6 +329,16 @@ var LiteGraph = {
for(var i in r)
target[i] = r[i];
return target;
+ },
+
+ isValidConnection: function( type_a, type_b )
+ {
+ if( !type_a || //generic output
+ !type_b || //generic input
+ type_a == type_a || //same type (is valid for triggers)
+ (type_a !== LiteGraph.EVENT && type_b !== LiteGraph.EVENT && type_a.toLowerCase() == type_b.toLowerCase()) ) //same type
+ return true;
+ return false;
}
};
@@ -421,8 +452,12 @@ LGraph.prototype.attachCanvas = function(graphcanvas)
LGraph.prototype.detachCanvas = function(graphcanvas)
{
- var pos = this.list_of_graphcanvas.indexOf(graphcanvas);
- if(pos == -1) return;
+ if(!this.list_of_graphcanvas)
+ return;
+
+ var pos = this.list_of_graphcanvas.indexOf( graphcanvas );
+ if(pos == -1)
+ return;
graphcanvas.graph = null;
this.list_of_graphcanvas.splice(pos,1);
}
@@ -489,11 +524,22 @@ LGraph.prototype.runStep = function(num)
var start = LiteGraph.getTime();
this.globaltime = 0.001 * (start - this.starttime);
+ var nodes = this._nodes_in_order ? this._nodes_in_order : this._nodes;
+ if(!nodes)
+ return;
+
try
{
+ //iterations
for(var i = 0; i < num; i++)
{
- this.sendEventToAllNodes("onExecute");
+ for( var j = 0, l = nodes.length; j < l; ++j )
+ {
+ var node = nodes[j];
+ if( node.mode == LiteGraph.ALWAYS && node.onExecute )
+ node.onExecute();
+ }
+
this.fixedtime += this.fixedtime_lapse;
if( this.onExecuteStep )
this.onExecuteStep();
@@ -514,7 +560,8 @@ LGraph.prototype.runStep = function(num)
}
var elapsed = LiteGraph.getTime() - start;
- if (elapsed == 0) elapsed = 1;
+ if (elapsed == 0)
+ elapsed = 1;
this.elapsed_time = 0.001 * elapsed;
this.globaltime += 0.001 * elapsed;
this.iteration += 1;
@@ -541,14 +588,14 @@ LGraph.prototype.computeExecutionOrder = function()
var remaining_links = {}; //to a
//search for the nodes without inputs (starting nodes)
- for (var i in this._nodes)
+ for (var i = 0, l = this._nodes.length; i < l; ++i)
{
var n = this._nodes[i];
M[n.id] = n; //add to pending nodes
var num = 0; //num of input connections
if(n.inputs)
- for(var j = 0, l = n.inputs.length; j < l; j++)
+ for(var j = 0, l2 = n.inputs.length; j < l2; j++)
if(n.inputs[j] && n.inputs[j].link != null)
num += 1;
@@ -605,13 +652,13 @@ LGraph.prototype.computeExecutionOrder = function()
//the remaining ones (loops)
for(var i in M)
- L.push(M[i]);
+ L.push( M[i] );
- if(L.length != this._nodes.length && LiteGraph.debug)
- console.log("something went wrong, nodes missing");
+ if( L.length != this._nodes.length && LiteGraph.debug )
+ console.warn("something went wrong, nodes missing");
//save order number in the node
- for(var i in L)
+ for(var i = 0; i < L.length; ++i)
L[i].order = i;
return L;
@@ -659,19 +706,27 @@ LGraph.prototype.getElapsedTime = function()
* @param {Array} params parameters in array format
*/
-LGraph.prototype.sendEventToAllNodes = function(eventname, params)
+LGraph.prototype.sendEventToAllNodes = function( eventname, params, mode )
{
- var M = this._nodes_in_order ? this._nodes_in_order : this._nodes;
- for(var j in M)
- if(M[j][eventname])
+ mode = mode || LiteGraph.ALWAYS;
+
+ var nodes = this._nodes_in_order ? this._nodes_in_order : this._nodes;
+ if(!nodes)
+ return;
+
+ for( var j = 0, l = nodes.length; j < l; ++j )
+ {
+ var node = nodes[j];
+ if(node[eventname] && node.mode == mode )
{
if(params === undefined)
- M[j][eventname]();
+ node[eventname]();
else if(params && params.constructor === Array)
- M[j][eventname].apply(M[j], params);
+ node[eventname].apply( node, params );
else
- M[j][eventname](params);
+ node[eventname](params);
}
+ }
}
LGraph.prototype.sendActionToCanvas = function(action, params)
@@ -679,7 +734,7 @@ LGraph.prototype.sendActionToCanvas = function(action, params)
if(!this.list_of_graphcanvas)
return;
- for(var i in this.list_of_graphcanvas)
+ for(var i = 0; i < this.list_of_graphcanvas.length; ++i)
{
var c = this.list_of_graphcanvas[i];
if( c[action] )
@@ -717,7 +772,7 @@ LGraph.prototype.add = function(node, skip_compute_order)
*/
if(node.onAdded)
- node.onAdded();
+ node.onAdded( this );
if(this.config.align_to_grid)
node.alignToGrid();
@@ -768,7 +823,7 @@ LGraph.prototype.remove = function(node)
node.disconnectOutput(i);
}
- node.id = -1;
+ //node.id = -1; //why?
//callback
if(node.onRemoved)
@@ -777,13 +832,16 @@ LGraph.prototype.remove = function(node)
node.graph = null;
//remove from canvas render
- for(var i in this.list_of_graphcanvas)
+ if(this.list_of_graphcanvas)
{
- var canvas = this.list_of_graphcanvas[i];
- if(canvas.selected_nodes[node.id])
- delete canvas.selected_nodes[node.id];
- if(canvas.node_dragged == node)
- canvas.node_dragged = null;
+ for(var i = 0; i < this.list_of_graphcanvas.length; ++i)
+ {
+ var canvas = this.list_of_graphcanvas[i];
+ if(canvas.selected_nodes[node.id])
+ delete canvas.selected_nodes[node.id];
+ if(canvas.node_dragged == node)
+ canvas.node_dragged = null;
+ }
}
//remove from containers
@@ -824,7 +882,7 @@ LGraph.prototype.getNodeById = function(id)
LGraph.prototype.findNodesByClass = function(classObject)
{
var r = [];
- for(var i in this._nodes)
+ for(var i = 0, l = this._nodes.length; i < l; ++i)
if(this._nodes[i].constructor === classObject)
r.push(this._nodes[i]);
return r;
@@ -841,7 +899,7 @@ LGraph.prototype.findNodesByType = function(type)
{
var type = type.toLowerCase();
var r = [];
- for(var i in this._nodes)
+ for(var i = 0, l = this._nodes.length; i < l; ++i)
if(this._nodes[i].type.toLowerCase() == type )
r.push(this._nodes[i]);
return r;
@@ -857,7 +915,7 @@ LGraph.prototype.findNodesByType = function(type)
LGraph.prototype.findNodesByTitle = function(title)
{
var result = [];
- for (var i in this._nodes)
+ for(var i = 0, l = this._nodes.length; i < l; ++i)
if(this._nodes[i].title == title)
result.push(this._nodes[i]);
return result;
@@ -1060,9 +1118,9 @@ LGraph.prototype.removeGlobalOutput = function(name)
LGraph.prototype.setInputData = function(name,value)
{
- var m = this.findNodesByName(name);
- for(var i in m)
- m[i].setValue(value);
+ var nodes = this.findNodesByName( name );
+ for(var i = 0, l = nodes.length; i < l; ++i)
+ nodes[i].setValue(value);
}
/**
@@ -1084,22 +1142,25 @@ LGraph.prototype.getOutputData = function(name)
LGraph.prototype.triggerInput = function(name,value)
{
- var m = this.findNodesByName(name);
- for(var i in m)
- m[i].onTrigger(value);
+ var nodes = this.findNodesByName(name);
+ for(var i = 0; i < nodes.length; ++i)
+ nodes[i].onTrigger(value);
}
LGraph.prototype.setCallback = function(name,func)
{
- var m = this.findNodesByName(name);
- for(var i in m)
- m[i].setTrigger(func);
+ var nodes = this.findNodesByName(name);
+ for(var i = 0; i < nodes.length; ++i)
+ nodes[i].setTrigger(func);
}
-LGraph.prototype.onConnectionChange = function()
+LGraph.prototype.connectionChange = function( node )
{
this.updateExecutionOrder();
+ if( this.onConnectionChange )
+ this.onConnectionChange( node );
+ this.sendActionToCanvas("onConnectionChange");
}
/**
@@ -1109,10 +1170,14 @@ LGraph.prototype.onConnectionChange = function()
LGraph.prototype.isLive = function()
{
- for(var i in this.list_of_graphcanvas)
+ if(!this.list_of_graphcanvas)
+ return false;
+
+ for(var i = 0; i < this.list_of_graphcanvas.length; ++i)
{
var c = this.list_of_graphcanvas[i];
- if(c.live_mode) return true;
+ if(c.live_mode)
+ return true;
}
return false;
}
@@ -1143,13 +1208,16 @@ LGraph.prototype.setDirtyCanvas = function(fg,bg)
LGraph.prototype.serialize = function()
{
var nodes_info = [];
- for (var i in this._nodes)
+ for(var i = 0, l = this._nodes.length; i < l; ++i)
nodes_info.push( this._nodes[i].serialize() );
//remove data from links, we dont want to store it
- for (var i in this.links)
- this.links[i].data = null;
-
+ for(var i in this.links) //links is an OBJECT
+ {
+ var link = this.links[i];
+ link.data = null;
+ delete link._last_time;
+ }
var data = {
// graph: this.graph,
@@ -1188,7 +1256,7 @@ LGraph.prototype.configure = function(data, keep_old)
//create nodes
this._nodes = [];
- for (var i in nodes)
+ for(var i = 0, l = nodes.length; i < l; ++i)
{
var n_info = nodes[i]; //stored info
var node = LiteGraph.createNode( n_info.type, n_info.title );
@@ -1235,8 +1303,8 @@ LGraph.prototype.onNodeTrace = function(node, msg, color)
supported callbacks:
+ onAdded: when added to graph
+ onRemoved: when removed from graph
- + onStart: when starts playing
- + onStop: when stops playing
+ + onStart: when the graph starts playing
+ + onStop: when the graph stops playing
+ onDrawForeground: render the inside widgets inside the node
+ onDrawBackground: render the background area inside the node (only in edit mode)
+ onMouseDown
@@ -1245,7 +1313,7 @@ LGraph.prototype.onNodeTrace = function(node, msg, color)
+ onMouseEnter
+ onMouseLeave
+ onExecute: execute the node
- + onPropertyChange: when a property is changed in the panel (return true to skip default behaviour)
+ + onPropertyChanged: when a property is changed in the panel (return true to skip default behaviour)
+ onGetInputs: returns an array of possible inputs
+ onGetOutputs: returns an array of possible outputs
+ onDblClick
@@ -1255,6 +1323,7 @@ LGraph.prototype.onNodeTrace = function(node, msg, color)
+ onDropItem : DOM item dropped over the node
+ onDropFile : file dropped over the node
+ onConnectInput : if returns false the incoming connection will be canceled
+ + onConnectionsChange : a connection changed (new one or removed)
*/
/**
@@ -1300,7 +1369,9 @@ LGraphNode.prototype._ctor = function( title )
this.connections = [];
//local data
- this.properties = {};
+ this.properties = {}; //for the values
+ this.properties_info = []; //for the info
+
this.data = null; //persistent local data
this.flags = {
//skip_title_render: true,
@@ -1316,13 +1387,18 @@ LGraphNode.prototype.configure = function(info)
{
for (var j in info)
{
- if(j == "console") continue;
+ if(j == "console")
+ continue;
if(j == "properties")
{
//i dont want to clone properties, I want to reuse the old container
for(var k in info.properties)
+ {
this.properties[k] = info.properties[k];
+ if(this.onPropertyChanged)
+ this.onPropertyChanged(k,info.properties[k]);
+ }
continue;
}
@@ -1339,6 +1415,9 @@ LGraphNode.prototype.configure = function(info)
this[j] = info[j];
}
+ if(this.onConnectionsChange)
+ this.onConnectionsChange();
+
//FOR LEGACY, PLEASE REMOVE ON NEXT VERSION
for(var i in this.inputs)
{
@@ -1365,6 +1444,8 @@ LGraphNode.prototype.configure = function(info)
}
}
+ if( this.onConfigured )
+ this.onConfigured( info );
}
/**
@@ -1383,7 +1464,8 @@ LGraphNode.prototype.serialize = function()
data: this.data,
flags: LiteGraph.cloneObject(this.flags),
inputs: this.inputs,
- outputs: this.outputs
+ outputs: this.outputs,
+ mode: this.mode
};
if(this.properties)
@@ -1413,8 +1495,23 @@ LGraphNode.prototype.clone = function()
{
var node = LiteGraph.createNode(this.type);
- var data = this.serialize();
+ //we clone it because serialize returns shared containers
+ var data = LiteGraph.cloneObject( this.serialize() );
+
+ //remove links
+ if(data.inputs)
+ for(var i = 0; i < data.inputs.length; ++i)
+ data.inputs[i].link = null;
+
+ if(data.outputs)
+ for(var i = 0; i < data.outputs.length; ++i)
+ {
+ if(data.outputs[i].links)
+ data.outputs[i].links.length = 0;
+ }
+
delete data["id"];
+ //remove links
node.configure(data);
return node;
@@ -1473,13 +1570,30 @@ LGraphNode.prototype.setOutputData = function(slot,data)
* @param {number} slot
* @return {*} data or if it is not connected returns undefined
*/
-LGraphNode.prototype.getInputData = function(slot)
+LGraphNode.prototype.getInputData = function( slot, force_update )
{
if(!this.inputs)
return; //undefined;
- if(slot < this.inputs.length && this.inputs[slot].link != null)
- return this.graph.links[ this.inputs[slot].link ].data;
- return; //undefined;
+
+ if(slot >= this.inputs.length || this.inputs[slot].link == null)
+ return;
+
+ var link_id = this.inputs[slot].link;
+ var link = this.graph.links[ link_id ];
+
+ if(!force_update)
+ return link.data;
+
+ var node = this.graph.getNodeById( link.origin_id );
+ if(!node)
+ return link.data;
+
+ if(node.updateOutputData)
+ node.updateOutputData( link.origin_slot );
+ else if(node.onExecute)
+ node.onExecute();
+
+ return link.data;
}
/**
@@ -1510,6 +1624,24 @@ LGraphNode.prototype.getInputInfo = function(slot)
return null;
}
+/**
+* returns the node connected in the input slot
+* @method getInputNode
+* @param {number} slot
+* @return {LGraphNode} node or null
+*/
+LGraphNode.prototype.getInputNode = function( slot )
+{
+ if(!this.inputs)
+ return null;
+ if(slot >= this.inputs.length)
+ return null;
+ var input = this.inputs[slot];
+ if(!input || !input.link)
+ return null;
+ return this.graph.getNodeById( input.link.origin_id );
+}
+
/**
* tells you info about an output connection (which node, type, etc)
@@ -1560,13 +1692,80 @@ LGraphNode.prototype.getOutputNodes = function(slot)
return null;
}
-LGraphNode.prototype.triggerOutput = function(slot,param)
+/**
+* Triggers an event in this node, this will trigger any output with the same name
+* @method trigger
+* @param {String} event name ( "on_play", ... ) if action is equivalent to false then the event is send to all
+* @param {*} param
+*/
+LGraphNode.prototype.trigger = function( action, param )
{
- var n = this.getOutputNode(slot);
- if(n && n.onTrigger)
- n.onTrigger(param);
+ if( !this.outputs || !this.outputs.length )
+ return;
+
+ if(this.graph)
+ this.graph._last_trigger_time = LiteGraph.getTime();
+
+ for(var i = 0; i < this.outputs.length; ++i)
+ {
+ var output = this.outputs[i];
+ if(output.type !== LiteGraph.EVENT || (action && output.name != action) )
+ continue;
+
+ var links = output.links;
+ if(!links || !links.length)
+ continue;
+
+ //for every link attached here
+ for(var k = 0; k < links.length; ++k)
+ {
+ var link_info = this.graph.links[ links[k] ];
+ if(!link_info) //not connected
+ continue;
+ var node = this.graph.getNodeById( link_info.target_id );
+ if(!node) //node not found?
+ continue;
+
+ //used to mark events in graph
+ link_info._last_time = LiteGraph.getTime();
+
+ var target_connection = node.inputs[ link_info.target_slot ];
+
+ if(node.onAction)
+ node.onAction( target_connection.name, param );
+ else if(node.mode === LiteGraph.ON_TRIGGER)
+ {
+ if(node.onExecute)
+ node.onExecute(param);
+ }
+ }
+ }
}
+/**
+* add a new property to this node
+* @method addProperty
+* @param {string} name
+* @param {*} default_value
+* @param {string} type string defining the output type ("vec3","number",...)
+* @param {Object} extra_info this can be used to have special properties of the property (like values, etc)
+*/
+LGraphNode.prototype.addProperty = function( name, default_value, type, extra_info )
+{
+ var o = { name: name, type: type, default_value: default_value };
+ if(extra_info)
+ for(var i in extra_info)
+ o[i] = extra_info[i];
+ if(!this.properties_info)
+ this.properties_info = [];
+ this.properties_info.push(o);
+ if(!this.properties)
+ this.properties = {};
+ this.properties[ name ] = default_value;
+ return o;
+}
+
+
//connections
/**
@@ -1578,16 +1777,18 @@ LGraphNode.prototype.triggerOutput = function(slot,param)
*/
LGraphNode.prototype.addOutput = function(name,type,extra_info)
{
- var o = {name:name,type:type,links:null};
+ var o = { name: name, type: type, links: null };
if(extra_info)
for(var i in extra_info)
o[i] = extra_info[i];
- if(!this.outputs) this.outputs = [];
+ if(!this.outputs)
+ this.outputs = [];
this.outputs.push(o);
if(this.onOutputAdded)
this.onOutputAdded(o);
this.size = this.computeSize();
+ return o;
}
/**
@@ -1597,7 +1798,7 @@ LGraphNode.prototype.addOutput = function(name,type,extra_info)
*/
LGraphNode.prototype.addOutputs = function(array)
{
- for(var i in array)
+ for(var i = 0; i < array.length; ++i)
{
var info = array[i];
var o = {name:info[0],type:info[1],link:null};
@@ -1650,6 +1851,7 @@ LGraphNode.prototype.addInput = function(name,type,extra_info)
this.size = this.computeSize();
if(this.onInputAdded)
this.onInputAdded(o);
+ return o;
}
/**
@@ -1659,7 +1861,7 @@ LGraphNode.prototype.addInput = function(name,type,extra_info)
*/
LGraphNode.prototype.addInputs = function(array)
{
- for(var i in array)
+ for(var i = 0; i < array.length; ++i)
{
var info = array[i];
var o = {name:info[0], type:info[1], link:null};
@@ -1701,7 +1903,15 @@ LGraphNode.prototype.removeInput = function(slot)
*/
LGraphNode.prototype.addConnection = function(name,type,pos,direction)
{
- this.connections.push( {name:name,type:type,pos:pos,direction:direction,links:null});
+ var o = {
+ name: name,
+ type: type,
+ pos: pos,
+ direction: direction,
+ links: null
+ };
+ this.connections.push( o );
+ return o;
}
/**
@@ -1710,10 +1920,10 @@ LGraphNode.prototype.addConnection = function(name,type,pos,direction)
* @param {number} minHeight
* @return {number} the total size
*/
-LGraphNode.prototype.computeSize = function(minHeight)
+LGraphNode.prototype.computeSize = function( minHeight, out )
{
var rows = Math.max( this.inputs ? this.inputs.length : 1, this.outputs ? this.outputs.length : 1);
- var size = new Float32Array([0,0]);
+ var size = out || new Float32Array([0,0]);
rows = Math.max(rows, 1);
size[1] = rows * 14 + 6;
@@ -1855,10 +2065,10 @@ LGraphNode.prototype.findOutputSlot = function(name)
* @method connect
* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)
* @param {LGraphNode} node the target node
-* @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot)
+* @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger)
* @return {boolean} if it was connected succesfully
*/
-LGraphNode.prototype.connect = function(slot, node, target_slot)
+LGraphNode.prototype.connect = function( slot, target_node, target_slot )
{
target_slot = target_slot || 0;
@@ -1880,19 +2090,19 @@ LGraphNode.prototype.connect = function(slot, node, target_slot)
return false;
}
- if(node && node.constructor === Number)
- node = this.graph.getNodeById( node );
- if(!node)
+ if(target_node && target_node.constructor === Number)
+ target_node = this.graph.getNodeById( target_node );
+ if(!target_node)
throw("Node not found");
//avoid loopback
- if(node == this)
+ if(target_node == this)
return false;
- //if( node.constructor != LGraphNode ) throw ("LGraphNode.connect: node is not of type LGraphNode");
+ //you can specify the slot by name
if(target_slot.constructor === String)
{
- target_slot = node.findInputSlot(target_slot);
+ target_slot = target_node.findInputSlot( target_slot );
if(target_slot == -1)
{
if(LiteGraph.debug)
@@ -1900,7 +2110,18 @@ LGraphNode.prototype.connect = function(slot, node, target_slot)
return false;
}
}
- else if(!node.inputs || target_slot >= node.inputs.length)
+ else if( target_slot === LiteGraph.EVENT )
+ {
+ //search for first slot with event?
+ /*
+ //create input for trigger
+ var input = target_node.addInput("onTrigger", LiteGraph.EVENT );
+ target_slot = target_node.inputs.length - 1; //last one is the one created
+ target_node.mode = LiteGraph.ON_TRIGGER;
+ */
+ return false;
+ }
+ else if( !target_node.inputs || target_slot >= target_node.inputs.length )
{
if(LiteGraph.debug)
console.log("Connect: Error, slot number not found");
@@ -1908,41 +2129,51 @@ LGraphNode.prototype.connect = function(slot, node, target_slot)
}
//if there is something already plugged there, disconnect
- if(target_slot != -1 && node.inputs[target_slot].link != null)
- node.disconnectInput(target_slot);
+ if(target_node.inputs[ target_slot ].link != null )
+ target_node.disconnectInput( target_slot );
+ //why here??
this.setDirtyCanvas(false,true);
- this.graph.onConnectionChange();
+ this.graph.connectionChange( this );
- //special case: -1 means node-connection, used for triggers
var output = this.outputs[slot];
- //allows nodes to block connection even if all test passes
- if(node.onConnectInput)
- if( node.onConnectInput( target_slot, output.type, output ) === false)
+ //allows nodes to block connection
+ if(target_node.onConnectInput)
+ if( target_node.onConnectInput( target_slot, output.type, output ) === false)
return false;
- if(target_slot == -1)
+ var input = target_node.inputs[target_slot];
+
+ if( LiteGraph.isValidConnection( output.type, input.type) )
{
+ var link_info = {
+ id: this.graph.last_link_id++,
+ origin_id: this.id,
+ origin_slot: slot,
+ target_id: target_node.id,
+ target_slot: target_slot
+ };
+
+ //add to graph links list
+ this.graph.links[ link_info.id ] = link_info;
+
+ //connect in output
if( output.links == null )
output.links = [];
- output.links.push({id:node.id, slot: -1});
- }
- else if( !output.type || //generic output
- !node.inputs[target_slot].type || //generic input
- output.type.toLowerCase() == node.inputs[target_slot].type.toLowerCase() ) //same type
- {
- //info: link structure => [ 0:link_id, 1:start_node_id, 2:start_slot, 3:end_node_id, 4:end_slot ]
- //var link = [ this.graph.last_link_id++, this.id, slot, node.id, target_slot ];
- var link = { id: this.graph.last_link_id++, origin_id: this.id, origin_slot: slot, target_id: node.id, target_slot: target_slot };
- this.graph.links[ link.id ] = link;
-
- //connect
- if( output.links == null ) output.links = [];
- output.links.push( link.id );
- node.inputs[target_slot].link = link.id;
+ output.links.push( link_info.id );
+ //connect in input
+ target_node.inputs[target_slot].link = link_info.id;
+ if(this.onConnectionsChange)
+ this.onConnectionsChange( LiteGraph.OUTPUT, slot, true, link_info ); //link_info has been created now, so its updated
+ if(target_node.onConnectionsChange)
+ target_node.onConnectionsChange( LiteGraph.INPUT, target_slot, true, link_info );
}
+
+ this.setDirtyCanvas(false,true);
+ this.graph.connectionChange( this );
+
return true;
}
@@ -1953,7 +2184,7 @@ LGraphNode.prototype.connect = function(slot, node, target_slot)
* @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected]
* @return {boolean} if it was disconnected succesfully
*/
-LGraphNode.prototype.disconnectOutput = function(slot, target_node)
+LGraphNode.prototype.disconnectOutput = function( slot, target_node )
{
if( slot.constructor === String )
{
@@ -1977,6 +2208,7 @@ LGraphNode.prototype.disconnectOutput = function(slot, target_node)
if(!output.links || output.links.length == 0)
return false;
+ //one of the links
if(target_node)
{
if(target_node.constructor === Number)
@@ -1995,11 +2227,15 @@ LGraphNode.prototype.disconnectOutput = function(slot, target_node)
output.links.splice(i,1); //remove here
target_node.inputs[ link_info.target_slot ].link = null; //remove there
delete this.graph.links[ link_id ]; //remove the link from the links pool
+ if(target_node.onConnectionsChange)
+ target_node.onConnectionsChange( LiteGraph.INPUT, link_info.target_slot, false, link_info ); //link_info hasnt been modified so its ok
+ if(this.onConnectionsChange)
+ this.onConnectionsChange( LiteGraph.OUTPUT, slot, false, link_info );
break;
}
}
}
- else
+ else //all the links
{
for(var i = 0, l = output.links.length; i < l; i++)
{
@@ -2009,12 +2245,18 @@ LGraphNode.prototype.disconnectOutput = function(slot, target_node)
var target_node = this.graph.getNodeById( link_info.target_id );
if(target_node)
target_node.inputs[ link_info.target_slot ].link = null; //remove other side link
+ delete this.graph.links[ link_id ]; //remove the link from the links pool
+ if(target_node.onConnectionsChange)
+ target_node.onConnectionsChange( LiteGraph.INPUT, link_info.target_slot, false, link_info ); //link_info hasnt been modified so its ok
+ if(this.onConnectionsChange)
+ this.onConnectionsChange( LiteGraph.OUTPUT, slot, false, link_info );
}
output.links = null;
}
+
this.setDirtyCanvas(false,true);
- this.graph.onConnectionChange();
+ this.graph.connectionChange( this );
return true;
}
@@ -2024,7 +2266,7 @@ LGraphNode.prototype.disconnectOutput = function(slot, target_node)
* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)
* @return {boolean} if it was disconnected succesfully
*/
-LGraphNode.prototype.disconnectInput = function(slot)
+LGraphNode.prototype.disconnectInput = function( slot )
{
//seek for the output slot
if( slot.constructor === String )
@@ -2047,6 +2289,7 @@ LGraphNode.prototype.disconnectInput = function(slot)
var input = this.inputs[slot];
if(!input)
return false;
+
var link_id = this.inputs[slot].link;
this.inputs[slot].link = null;
@@ -2054,15 +2297,15 @@ LGraphNode.prototype.disconnectInput = function(slot)
var link_info = this.graph.links[ link_id ];
if( link_info )
{
- var node = this.graph.getNodeById( link_info.origin_id );
- if(!node)
+ var target_node = this.graph.getNodeById( link_info.origin_id );
+ if(!target_node)
return false;
- var output = node.outputs[ link_info.origin_slot ];
+ var output = target_node.outputs[ link_info.origin_slot ];
if(!output || !output.links || output.links.length == 0)
return false;
- //check outputs
+ //search in the inputs list for this link
for(var i = 0, l = output.links.length; i < l; i++)
{
var link_id = output.links[i];
@@ -2073,10 +2316,15 @@ LGraphNode.prototype.disconnectInput = function(slot)
break;
}
}
+
+ if( this.onConnectionsChange )
+ this.onConnectionsChange( LiteGraph.INPUT, slot, false, link_info );
+ if( target_node.onConnectionsChange )
+ target_node.onConnectionsChange( LiteGraph.OUTPUT, i, false, link_info );
}
this.setDirtyCanvas(false,true);
- this.graph.onConnectionChange();
+ this.graph.connectionChange( this );
return true;
}
@@ -2156,6 +2404,7 @@ LGraphNode.prototype.loadImage = function(url)
}
//safe LGraphNode action execution (not sure if safe)
+/*
LGraphNode.prototype.executeAction = function(action)
{
if(action == "") return false;
@@ -2191,6 +2440,7 @@ LGraphNode.prototype.executeAction = function(action)
return true;
}
+*/
/* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */
LGraphNode.prototype.captureInput = function(v)
@@ -2200,7 +2450,7 @@ LGraphNode.prototype.captureInput = function(v)
var list = this.graph.list_of_graphcanvas;
- for(var i in list)
+ for(var i = 0; i < list.length; ++i)
{
var c = list[i];
//releasing somebody elses capture?!
@@ -2252,14 +2502,18 @@ LGraphNode.prototype.localToScreen = function(x,y, graphcanvas)
/**
* The Global Scope. It contains all the registered node classes.
+* Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked
*
* @class LGraphCanvas
* @constructor
* @param {HTMLCanvas} canvas the canvas where you want to render (it accepts a selector in string format or the canvas itself)
* @param {LGraph} graph [optional]
+* @param {Object} options [optional] { skip_rendering, autoresize }
*/
-function LGraphCanvas( canvas, graph, skip_render )
+function LGraphCanvas( canvas, graph, options )
{
+ options = options || {};
+
//if(graph === undefined)
// throw ("No graph assigned");
@@ -2269,6 +2523,30 @@ function LGraphCanvas( canvas, graph, skip_render )
this.max_zoom = 10;
this.min_zoom = 0.1;
+ this.title_text_font = "bold 14px Arial";
+ this.inner_text_font = "normal 12px Arial";
+ this.default_link_color = "#AAC";
+
+ this.highquality_render = true;
+ this.editor_alpha = 1; //used for transition
+ this.pause_rendering = false;
+ this.render_shadows = true;
+ this.clear_background = true;
+
+ this.render_only_selected = true;
+ this.live_mode = false;
+ this.show_info = true;
+ this.allow_dragcanvas = true;
+ this.allow_dragnodes = true;
+
+ this.always_render_background = false;
+ this.render_connections_shadows = false; //too much cpu
+ this.render_connections_border = true;
+ this.render_curved_connections = true;
+ this.render_connection_arrows = true;
+
+ this.connections_width = 4;
+
//link canvas and graph
if(graph)
graph.attachCanvas(this);
@@ -2276,11 +2554,13 @@ function LGraphCanvas( canvas, graph, skip_render )
this.setCanvas( canvas );
this.clear();
- if(!skip_render)
+ if(!options.skip_render)
this.startRendering();
+
+ this.autoresize = options.autoresize;
}
-LGraphCanvas.link_type_colors = {'number':"#AAC",'node':"#DCA"};
+LGraphCanvas.link_type_colors = {"-1":"#F85",'number':"#AAC","node":"#DCA"};
/**
@@ -2304,18 +2584,6 @@ LGraphCanvas.prototype.clear = function()
this.node_capturing_input = null;
this.connecting_node = null;
- this.highquality_render = true;
- this.editor_alpha = 1; //used for transition
- this.pause_rendering = false;
- this.render_shadows = true;
- this.clear_background = true;
-
- this.render_only_selected = true;
- this.live_mode = false;
- this.show_info = true;
- this.allow_dragcanvas = true;
- this.allow_dragnodes = true;
-
this.dirty_canvas = true;
this.dirty_bgcanvas = true;
this.dirty_area = null;
@@ -2325,17 +2593,8 @@ LGraphCanvas.prototype.clear = function()
this.last_mouse = [0,0];
this.last_mouseclick = 0;
- this.title_text_font = "bold 14px Arial";
- this.inner_text_font = "normal 12px Arial";
-
- this.render_connections_shadows = false; //too much cpu
- this.render_connections_border = true;
- this.render_curved_connections = true;
- this.render_connection_arrows = true;
-
- this.connections_width = 4;
-
- if(this.onClear) this.onClear();
+ if(this.onClear)
+ this.onClear();
//this.UIinit();
}
@@ -2666,6 +2925,8 @@ LGraphCanvas.prototype.setDirty = function(fgcanvas,bgcanvas)
*/
LGraphCanvas.prototype.getCanvasWindow = function()
{
+ if(!this.canvas)
+ return window;
var doc = this.canvas.ownerDocument;
return doc.defaultView || doc.parentWindow;
}
@@ -2729,19 +2990,24 @@ LGraphCanvas.prototype.processMouseDown = function(e)
var n = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes );
var skip_dragging = false;
+
+ LiteGraph.closeAllContextMenus( ref_window );
if(e.which == 1) //left button mouse
{
- //another node selected
if(!e.shiftKey) //REFACTOR: integrate with function
{
- var todeselect = [];
- for(var i in this.selected_nodes)
- if (this.selected_nodes[i] != n)
- todeselect.push(this.selected_nodes[i]);
- //two passes to avoid problems modifying the container
- for(var i in todeselect)
- this.processNodeDeselected(todeselect[i]);
+ //no node or another node selected
+ if (!n || !this.selected_nodes[n.id]) {
+
+ var todeselect = [];
+ for (var i in this.selected_nodes)
+ if (this.selected_nodes[i] != n)
+ todeselect.push(this.selected_nodes[i]);
+ //two passes to avoid problems modifying the container
+ for (var i in todeselect)
+ this.processNodeDeselected(todeselect[i]);
+ }
}
var clicking_canvas_bg = false;
@@ -2825,7 +3091,7 @@ LGraphCanvas.prototype.processMouseDown = function(e)
//if do not capture mouse
- if( n.onMouseDown && n.onMouseDown(e) )
+ if( n.onMouseDown && n.onMouseDown(e, [e.canvasX - n.pos[0], e.canvasY - n.pos[1]] ) )
block_drag_node = true;
else if(this.live_mode)
{
@@ -2859,7 +3125,7 @@ LGraphCanvas.prototype.processMouseDown = function(e)
}
else if (e.which == 3) //right button
{
- this.processContextualMenu(n,e);
+ this.processContextMenu(n,e);
}
//TODO
@@ -2882,12 +3148,20 @@ LGraphCanvas.prototype.processMouseDown = function(e)
if(!ref_window.document.activeElement || (ref_window.document.activeElement.nodeName.toLowerCase() != "input" && ref_window.document.activeElement.nodeName.toLowerCase() != "textarea"))
e.preventDefault();
e.stopPropagation();
+
+ if(this.onMouseDown)
+ this.onMouseDown(e);
+
return false;
}
LGraphCanvas.prototype.processMouseMove = function(e)
{
- if(!this.graph) return;
+ if(this.autoresize)
+ this.resize();
+
+ if(!this.graph)
+ return;
this.adjustMouseEvent(e);
var mouse = [e.localX, e.localY];
@@ -2911,7 +3185,7 @@ LGraphCanvas.prototype.processMouseMove = function(e)
var n = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes);
//remove mouseover flag
- for(var i in this.graph._nodes)
+ for(var i = 0, l = this.graph._nodes.length; i < l; ++i)
{
if(this.graph._nodes[i].mouseOver && n != this.graph._nodes[i])
{
@@ -2940,19 +3214,27 @@ LGraphCanvas.prototype.processMouseMove = function(e)
if(n.onMouseMove) n.onMouseMove(e);
- //ontop of input
+ //on top of input
if(this.connecting_node)
{
- var pos = this._highlight_input || [0,0];
- var slot = this.isOverNodeInput(n, e.canvasX, e.canvasY, pos);
- if(slot != -1 && n.inputs[slot])
- {
- var slot_type = n.inputs[slot].type;
- if( !this.connecting_output.type || !slot_type || slot_type.toLowerCase() == this.connecting_output.type.toLowerCase() )
- this._highlight_input = pos;
+ var pos = this._highlight_input || [0,0]; //to store the output of isOverNodeInput
+
+ if( this.isOverNodeBox( n, e.canvasX, e.canvasY ) )
+ {
+ //mouse on top of the corner box, dont know what to do
}
else
- this._highlight_input = null;
+ {
+ var slot = this.isOverNodeInput( n, e.canvasX, e.canvasY, pos );
+ if(slot != -1 && n.inputs[slot])
+ {
+ var slot_type = n.inputs[slot].type;
+ if( LiteGraph.isValidConnection( this.connecting_output.type, slot_type ) )
+ this._highlight_input = pos;
+ }
+ else
+ this._highlight_input = null;
+ }
}
//Search for corner
@@ -3044,15 +3326,14 @@ LGraphCanvas.prototype.processMouseUp = function(e)
this.dirty_canvas = true;
this.dirty_bgcanvas = true;
- var node = this.graph.getNodeOnPos(e.canvasX, e.canvasY, this.visible_nodes);
+ var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes );
//node below mouse
if(node)
{
-
- if(this.connecting_output.type == 'node')
+ if( this.connecting_output.type == LiteGraph.EVENT && this.isOverNodeBox( node, e.canvasX, e.canvasY ) )
{
- this.connecting_node.connect(this.connecting_slot, node, -1);
+ this.connecting_node.connect( this.connecting_slot, node, LiteGraph.EVENT );
}
else
{
@@ -3065,9 +3346,12 @@ LGraphCanvas.prototype.processMouseUp = function(e)
else
{ //not on top of an input
var input = node.getInputInfo(0);
- //simple connect
- if(input && !input.link && input.type == this.connecting_output.type) //toLowerCase missing
- this.connecting_node.connect(this.connecting_slot, node, 0);
+ //auto connect
+ if(this.connecting_output.type == LiteGraph.EVENT)
+ this.connecting_node.connect( this.connecting_slot, node, LiteGraph.EVENT );
+ else
+ if(input && !input.link && input.type == this.connecting_output.type) //toLowerCase missing
+ this.connecting_node.connect(this.connecting_slot, node, 0);
}
}
}
@@ -3100,9 +3384,9 @@ LGraphCanvas.prototype.processMouseUp = function(e)
this.dragging_canvas = false;
if( this.node_over && this.node_over.onMouseUp )
- this.node_over.onMouseUp(e);
+ this.node_over.onMouseUp(e, [e.canvasX - this.node_over.pos[0], e.canvasY - this.node_over.pos[1]] );
if( this.node_capturing_input && this.node_capturing_input.onMouseUp )
- this.node_capturing_input.onMouseUp(e);
+ this.node_capturing_input.onMouseUp(e, [e.canvasX - this.node_capturing_input.pos[0], e.canvasY - this.node_capturing_input.pos[1]] );
}
}
else if (e.which == 2) //middle button
@@ -3160,7 +3444,15 @@ LGraphCanvas.prototype.processMouseWheel = function(e)
return false; // prevent default
}
-LGraphCanvas.prototype.isOverNodeInput = function(node, canvasx, canvasy, slot_pos)
+LGraphCanvas.prototype.isOverNodeBox = function( node, canvasx, canvasy )
+{
+ var title_height = LiteGraph.NODE_TITLE_HEIGHT;
+ if( isInsideRectangle( canvasx, canvasy, node.pos[0] + 2, node.pos[1] + 2 - title_height, title_height - 4,title_height - 4) )
+ return true;
+ return false;
+}
+
+LGraphCanvas.prototype.isOverNodeInput = function(node, canvasx, canvasy, slot_pos )
{
if(node.inputs)
for(var i = 0, l = node.inputs.length; i < l; ++i)
@@ -3169,7 +3461,11 @@ LGraphCanvas.prototype.isOverNodeInput = function(node, canvasx, canvasy, slot_p
var link_pos = node.getConnectionPos(true,i);
if( isInsideRectangle(canvasx, canvasy, link_pos[0] - 10, link_pos[1] - 5, 20,10) )
{
- if(slot_pos) { slot_pos[0] = link_pos[0]; slot_pos[1] = link_pos[1] };
+ if(slot_pos)
+ {
+ slot_pos[0] = link_pos[0];
+ slot_pos[1] = link_pos[1];
+ }
return i;
}
}
@@ -3316,11 +3612,9 @@ LGraphCanvas.prototype.processNodeDeselected = function(n)
delete this.selected_nodes[n.id];
if(this.onNodeDeselected)
- this.onNodeDeselected();
+ this.onNodeDeselected(n);
this.dirty_canvas = true;
-
- //this.showNodePanel(null);
}
LGraphCanvas.prototype.processNodeDblClicked = function(n)
@@ -3350,7 +3644,7 @@ LGraphCanvas.prototype.selectNode = function(node)
LGraphCanvas.prototype.selectAllNodes = function()
{
- for(var i in this.graph._nodes)
+ for(var i = 0; i < this.graph._nodes.length; ++i)
{
var n = this.graph._nodes[i];
if(!n.selected && n.onSelected)
@@ -3472,7 +3766,7 @@ LGraphCanvas.prototype.sendToBack = function(n)
LGraphCanvas.prototype.computeVisibleNodes = function()
{
var visible_nodes = [];
- for (var i in this.graph._nodes)
+ for(var i = 0, l = this.graph._nodes.length; i < l; ++i)
{
var n = this.graph._nodes[i];
@@ -3490,6 +3784,9 @@ LGraphCanvas.prototype.computeVisibleNodes = function()
LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas)
{
+ if(!this.canvas)
+ return;
+
//fps counting
var now = LiteGraph.getTime();
this.render_time = (now - this.last_draw_time)*0.001;
@@ -3502,7 +3799,7 @@ LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas)
this.visible_area = new Float32Array([start[0],start[1],end[0],end[1]]);
}
- if(this.dirty_bgcanvas || force_bgcanvas)
+ if(this.dirty_bgcanvas || force_bgcanvas || this.always_render_background || (this.graph && this.graph._last_trigger_time && (now - this.graph._last_trigger_time) < 1000) )
this.drawBackCanvas();
if(this.dirty_canvas || force_canvas)
@@ -3569,7 +3866,7 @@ LGraphCanvas.prototype.drawFrontCanvas = function()
var visible_nodes = this.computeVisibleNodes();
this.visible_nodes = visible_nodes;
- for (var i in visible_nodes)
+ for (var i = 0; i < visible_nodes.length; ++i)
{
var node = visible_nodes[i];
@@ -3594,11 +3891,22 @@ LGraphCanvas.prototype.drawFrontCanvas = function()
if(this.connecting_pos != null)
{
ctx.lineWidth = this.connections_width;
- var link_color = this.connecting_output.type == 'node' ? "#F85" : "#AFA";
+ var link_color = null;
+ switch( this.connecting_output.type )
+ {
+ case LiteGraph.EVENT: link_color = "#F85"; break;
+ default:
+ link_color = "#AFA";
+ }
this.renderLink(ctx, this.connecting_pos, [this.canvas_mouse[0],this.canvas_mouse[1]], link_color );
ctx.beginPath();
- ctx.arc( this.connecting_pos[0], this.connecting_pos[1],4,0,Math.PI*2);
+
+ if( this.connecting_output.type === LiteGraph.EVENT )
+ ctx.rect( (this.connecting_pos[0] - 6) + 0.5, (this.connecting_pos[1] - 5) + 0.5,14,10);
+ else
+ ctx.arc( this.connecting_pos[0], this.connecting_pos[1],4,0,Math.PI*2);
+
/*
if( this.connecting_output.round)
ctx.arc( this.connecting_pos[0], this.connecting_pos[1],4,0,Math.PI*2);
@@ -3655,6 +3963,13 @@ LGraphCanvas.prototype.renderInfo = function( ctx, x, y )
LGraphCanvas.prototype.drawBackCanvas = function()
{
var canvas = this.bgcanvas;
+ if(canvas.width != this.canvas.width ||
+ canvas.height != this.canvas.height)
+ {
+ canvas.width = this.canvas.width;
+ canvas.height = this.canvas.height;
+ }
+
if(!this.bgctx)
this.bgctx = this.bgcanvas.getContext("2d");
var ctx = this.bgctx;
@@ -3693,7 +4008,7 @@ LGraphCanvas.prototype.drawBackCanvas = function()
}
var pattern = null;
- if(this._bg_img != this._pattern_img && this._bg_img.width > 0)
+ if(this._pattern == null && this._bg_img.width > 0)
{
pattern = ctx.createPattern( this._bg_img, 'repeat' );
this._pattern_img = this._bg_img;
@@ -3854,6 +4169,8 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
var render_text = this.scale > 0.6;
+ var out_slot = this.connecting_output;
+
//render inputs and outputs
if(!node.flags.collapsed)
{
@@ -3864,7 +4181,8 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
var slot = node.inputs[i];
ctx.globalAlpha = editor_alpha;
- if (this.connecting_node != null && this.connecting_output.type && node.inputs[i].type && this.connecting_output.type.toLowerCase() != node.inputs[i].type.toLowerCase() )
+ //change opacity of incompatible slots
+ if ( this.connecting_node && LiteGraph.isValidConnection( slot.type && out_slot.type ) )
ctx.globalAlpha = 0.4 * editor_alpha;
ctx.fillStyle = slot.link != null ? "#7F7" : "#AAA";
@@ -3875,10 +4193,10 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
ctx.beginPath();
- if (1 || slot.round)
+ if (slot.type === LiteGraph.EVENT)
+ ctx.rect((pos[0] - 6) + 0.5, (pos[1] - 5) + 0.5,14,10);
+ else
ctx.arc(pos[0],pos[1],4,0,Math.PI*2);
- //else
- // ctx.rect((pos[0] - 6) + 0.5, (pos[1] - 5) + 0.5,14,10);
ctx.fill();
@@ -3915,10 +4233,10 @@ LGraphCanvas.prototype.drawNode = function(node, ctx )
ctx.beginPath();
//ctx.rect( node.size[0] - 14,i*14,10,10);
- if (1 || slot.round)
- ctx.arc(pos[0],pos[1],4,0,Math.PI*2);
- //else
- // ctx.rect((pos[0] - 6) + 0.5,(pos[1] - 5) + 0.5,14,10);
+ if (slot.type === LiteGraph.EVENT)
+ ctx.rect((pos[0] - 6) + 0.5,(pos[1] - 5) + 0.5,14,10);
+ else
+ ctx.arc( pos[0],pos[1],4,0, Math.PI*2 );
//trigger
//if(slot.node_id != null && slot.slot == -1)
@@ -4012,7 +4330,7 @@ LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolo
if(node.onDrawBackground)
node.onDrawBackground(ctx);
- //title bg
+ //title bg (remember, it is rendered ABOVE the node
if(!no_title)
{
ctx.fillStyle = fgcolor || LiteGraph.NODE_DEFAULT_COLOR;
@@ -4033,7 +4351,7 @@ LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolo
//ctx.stroke();
}
- //box
+ //title box
ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;
ctx.beginPath();
if (shape == "round")
@@ -4108,10 +4426,11 @@ LGraphCanvas.prototype.drawNodeCollapsed = function(node, ctx, fgcolor, bgcolor)
}
}
-LGraphCanvas.link_colors = ["#AAC","#ACA","#CAA"];
-
+//OPTIMIZE THIS: precatch connections position instead of recomputing them every time
LGraphCanvas.prototype.drawConnections = function(ctx)
{
+ var now = LiteGraph.getTime();
+
//draw connections
ctx.lineWidth = this.connections_width;
@@ -4119,19 +4438,20 @@ LGraphCanvas.prototype.drawConnections = function(ctx)
ctx.strokeStyle = "#AAA";
ctx.globalAlpha = this.editor_alpha;
//for every node
- for (var n in this.graph._nodes)
+ for (var n = 0, l = this.graph._nodes.length; n < l; ++n)
{
var node = this.graph._nodes[n];
//for every input (we render just inputs because it is easier as every slot can only have one input)
if(node.inputs && node.inputs.length)
- for(var i in node.inputs)
+ for(var i = 0; i < node.inputs.length; ++i)
{
var input = node.inputs[i];
if(!input || input.link == null)
continue;
var link_id = input.link;
var link = this.graph.links[ link_id ];
- if(!link) continue;
+ if(!link)
+ continue;
var start_node = this.graph.getNodeById( link.origin_id );
if(start_node == null) continue;
@@ -4143,16 +4463,22 @@ LGraphCanvas.prototype.drawConnections = function(ctx)
else
start_node_slotpos = start_node.getConnectionPos(false, start_node_slot);
- var color = LGraphCanvas.link_type_colors[node.inputs[i].type];
- if(color == null)
- color = LGraphCanvas.link_colors[node.id % LGraphCanvas.link_colors.length];
+ var color = LGraphCanvas.link_type_colors[ node.inputs[i].type ] || this.default_link_color;
+
this.renderLink(ctx, start_node_slotpos, node.getConnectionPos(true,i), color );
+
+ if(link && link._last_time && now - link._last_time < 1000 )
+ {
+ var f = 2.0 - (now - link._last_time) * 0.002;
+ var color = "rgba(255,255,255, " + f.toFixed(2) + ")";
+ this.renderLink( ctx, start_node_slotpos, node.getConnectionPos(true,i) , color, true, f );
+ }
}
}
ctx.globalAlpha = 1;
}
-LGraphCanvas.prototype.renderLink = function(ctx,a,b,color)
+LGraphCanvas.prototype.renderLink = function(ctx,a,b,color, skip_border, flow )
{
if(!this.highquality_render)
{
@@ -4185,7 +4511,7 @@ LGraphCanvas.prototype.renderLink = function(ctx,a,b,color)
ctx.lineTo(b[0]-10,b[1]);
}
- if(this.render_connections_border && this.scale > 0.6)
+ if(this.render_connections_border && this.scale > 0.6 && !skip_border)
{
ctx.strokeStyle = "rgba(0,0,0,0.5)";
ctx.stroke();
@@ -4195,12 +4521,17 @@ LGraphCanvas.prototype.renderLink = function(ctx,a,b,color)
ctx.fillStyle = ctx.strokeStyle = color;
ctx.stroke();
+ //no symbols
+ if(!this.render_connection_arrows || this.scale < 0.6)
+ return;
+
//render arrow
if(this.render_connection_arrows && this.scale > 0.6)
{
- //get two points in the bezier curve
var pos = this.computeConnectionPoint(a,b,0.5);
var pos2 = this.computeConnectionPoint(a,b,0.51);
+
+ //get two points in the bezier curve
var angle = 0;
if(this.render_curved_connections)
angle = -Math.atan2( pos2[0] - pos[0], pos2[1] - pos[1]);
@@ -4217,6 +4548,18 @@ LGraphCanvas.prototype.renderLink = function(ctx,a,b,color)
ctx.fill();
ctx.restore();
}
+
+ if(flow)
+ {
+ for(var i = 0; i < 5; ++i)
+ {
+ var f = (LiteGraph.getTime() * 0.001 + (i * 0.2)) % 1;
+ var pos = this.computeConnectionPoint(a,b,f);
+ ctx.beginPath();
+ ctx.arc(pos[0],pos[1],5,0,2*Math.PI);
+ ctx.fill();
+ }
+ }
}
LGraphCanvas.prototype.computeConnectionPoint = function(a,b,t)
@@ -4348,7 +4691,7 @@ LGraphCanvas.prototype.touchHandler = function(event)
LGraphCanvas.onMenuAdd = function(node, e, prev_menu, canvas, first_event )
{
- var window = canvas.getCanvasWindow();
+ var ref_window = canvas.getCanvasWindow();
var values = LiteGraph.getNodeTypesCategories();
var entries = {};
@@ -4356,7 +4699,7 @@ LGraphCanvas.onMenuAdd = function(node, e, prev_menu, canvas, first_event )
if(values[i])
entries[ i ] = { value: values[i], content: values[i] , is_menu: true };
- var menu = LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}, window);
+ var menu = LiteGraph.createContextMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}, ref_window);
function inner_clicked(v, e)
{
@@ -4366,7 +4709,7 @@ LGraphCanvas.onMenuAdd = function(node, e, prev_menu, canvas, first_event )
for(var i in node_types)
values.push( { content: node_types[i].title, value: node_types[i].type });
- LiteGraph.createContextualMenu(values, {event: e, callback: inner_create, from: menu}, window);
+ LiteGraph.createContextMenu(values, {event: e, callback: inner_create, from: menu}, ref_window);
return false;
}
@@ -4394,16 +4737,20 @@ LGraphCanvas.onMenuNodeEdit = function()
}
-LGraphCanvas.onMenuNodeInputs = function(node, e, prev_menu)
+LGraphCanvas.showMenuNodeInputs = function(node, e, prev_menu)
{
- if(!node) return;
+ if(!node)
+ return;
+
+ var that = this;
+ var ref_window = this.getCanvasWindow();
var options = node.optional_inputs;
if(node.onGetInputs)
options = node.onGetInputs();
+
+ var entries = [];
if(options)
- {
- var entries = [];
for (var i in options)
{
var entry = options[i];
@@ -4412,55 +4759,91 @@ LGraphCanvas.onMenuNodeInputs = function(node, e, prev_menu)
label = entry[2].label;
entries.push({content: label, value: entry});
}
- var menu = LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu});
- }
- function inner_clicked(v)
+ if(this.onMenuNodeInputs)
+ entries = this.onMenuNodeInputs( entries );
+
+ if(!entries.length)
+ return;
+
+ var menu = LiteGraph.createContextMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}, ref_window);
+
+ function inner_clicked(v, e, prev)
{
- if(!node) return;
- node.addInput(v.value[0],v.value[1], v.value[2]);
+ if(!node)
+ return;
+
+ if(v.callback)
+ v.callback.call(that, node, v, e, prev);
+
+ if(v.value)
+ node.addInput(v.value[0],v.value[1], v.value[2]);
}
return false;
}
-LGraphCanvas.onMenuNodeOutputs = function(node, e, prev_menu)
+LGraphCanvas.showMenuNodeOutputs = function(node, e, prev_menu)
{
- if(!node) return;
+ if(!node)
+ return;
+
+ var that = this;
+ var ref_window = this.getCanvasWindow();
var options = node.optional_outputs;
if(node.onGetOutputs)
options = node.onGetOutputs();
+
+ var entries = [];
if(options)
- {
- var entries = [];
for (var i in options)
{
var entry = options[i];
+ if(!entry) //separator?
+ {
+ entries.push(null);
+ continue;
+ }
+
if(node.findOutputSlot(entry[0]) != -1)
continue; //skip the ones already on
var label = entry[0];
if(entry[2] && entry[2].label)
label = entry[2].label;
- entries.push({content: label, value: entry});
+ var data = {content: label, value: entry};
+ if(entry[1] == LiteGraph.EVENT)
+ data.className = "event";
+ entries.push(data);
}
- if(entries.length)
- var menu = LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu});
- }
- function inner_clicked(v)
+ if(this.onMenuNodeOutputs)
+ entries = this.onMenuNodeOutputs( entries );
+
+ if(!entries.length)
+ return;
+
+ var menu = LiteGraph.createContextMenu(entries, {event: e, callback: inner_clicked, from: prev_menu}, ref_window);
+
+ function inner_clicked( v, e, prev )
{
if(!node)
return;
+ if(v.callback)
+ v.callback.call(that, node, v, e, prev);
+
+ if(!v.value)
+ return;
+
var value = v.value[1];
- if(value && (value.constructor === Object || value.constructor === Array)) //submenu
+ if(value && (value.constructor === Object || value.constructor === Array)) //submenu why?
{
var entries = [];
for(var i in value)
entries.push({content: i, value: value[i]});
- LiteGraph.createContextualMenu(entries, {event: e, callback: inner_clicked, from: prev_menu});
+ LiteGraph.createContextMenu(entries, {event: e, callback: inner_clicked, from: prev_menu});
return false;
}
else
@@ -4470,6 +4853,169 @@ LGraphCanvas.onMenuNodeOutputs = function(node, e, prev_menu)
return false;
}
+LGraphCanvas.onShowMenuNodeProperties = function(node,e, prev_menu)
+{
+ if(!node || !node.properties)
+ return;
+
+ var that = this;
+ var ref_window = this.getCanvasWindow();
+
+ var entries = [];
+ for (var i in node.properties)
+ {
+ var value = node.properties[i] !== undefined ? node.properties[i] : " ";
+ entries.push({content: "<span class='property_name'>" + i + "</span>" + "<span class='property_value'>" + value + "</span>", value: i});
+ }
+ if(!entries.length)
+ return;
+
+ var menu = LiteGraph.createContextMenu(entries, {event: e, callback: inner_clicked, from: prev_menu},ref_window);
+
+ function inner_clicked( v, e, prev )
+ {
+ if(!node)
+ return;
+ that.showEditPropertyValue( node, v.value, { event: e });
+ }
+
+ return false;
+}
+
+LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options )
+{
+ if(!node || node.properties[ property ] === undefined )
+ return;
+
+ options = options || {};
+ var that = this;
+
+ var type = "string";
+
+ if(node.properties[ property ] !== null)
+ type = typeof(node.properties[ property ]);
+
+ var info = null;
+ if(node.getPropertyInfo)
+ info = node.getPropertyInfo(property);
+ if(node.properties_info)
+ {
+ for(var i = 0; i < node.properties_info.length; ++i)
+ {
+ if( node.properties_info[i].name == property )
+ {
+ info = node.properties_info[i];
+ break;
+ }
+ }
+ }
+
+ if(info !== undefined && info !== null && info.type )
+ type = info.type;
+
+ var input_html = "";
+
+ if(type == "string" || type == "number")
+ input_html = "<input autofocus type='text' class='value'/>";
+ else if(type == "enum" && info.values)
+ {
+ input_html = "<select autofocus type='text' class='value'>";
+ for(var i in info.values)
+ {
+ var v = info.values.constructor === Array ? info.values[i] : i;
+ input_html += "<option value='"+v+"' "+(v == node.properties[property] ? "selected" : "")+">"+info.values[i]+"</option>";
+ }
+ input_html += "</select>";
+ }
+ else if(type == "boolean")
+ {
+ input_html = "<input autofocus type='checkbox' class='value' "+(node.properties[property] ? "checked" : "")+"/>";
+ }
+
+
+ var dialog = document.createElement("div");
+ dialog.className = "graphdialog";
+ dialog.innerHTML = "<span class='name'>" + property + "</span>"+input_html+"<button>OK</button>";
+
+ if(type == "enum" && info.values)
+ {
+ var input = dialog.querySelector("select");
+ input.addEventListener("change", function(e){
+ setValue( e.target.value );
+ //var index = e.target.value;
+ //setValue( e.options[e.selectedIndex].value );
+ });
+ }
+ else if(type == "boolean")
+ {
+ var input = dialog.querySelector("input");
+ if(input)
+ {
+ input.addEventListener("click", function(e){
+ setValue( !!input.checked );
+ });
+ }
+ }
+ else
+ {
+ var input = dialog.querySelector("input");
+ if(input)
+ {
+ input.value = node.properties[ property ] !== undefined ? node.properties[ property ] : "";
+ input.addEventListener("keydown", function(e){
+ if(e.keyCode != 13)
+ return;
+ inner();
+ e.preventDefault();
+ e.stopPropagation();
+ });
+ }
+ }
+
+ var rect = this.canvas.getClientRects()[0];
+ var offsetx = -20;
+ var offsety = -20;
+ if(rect)
+ {
+ offsetx -= rect.left;
+ offsety -= rect.top;
+ }
+
+ if( options.event )
+ {
+ dialog.style.left = (options.event.pageX + offsetx) + "px";
+ dialog.style.top = (options.event.pageY + offsety)+ "px";
+ }
+ else
+ {
+ dialog.style.left = (this.canvas.width * 0.5 + offsetx) + "px";
+ dialog.style.top = (this.canvas.height * 0.5 + offsety) + "px";
+ }
+
+ var button = dialog.querySelector("button");
+ button.addEventListener("click", inner );
+
+ this.canvas.parentNode.appendChild( dialog );
+
+
+ function inner()
+ {
+ setValue( input.value );
+ }
+
+ function setValue(value)
+ {
+ if(typeof( node.properties[ property ] ) == "number")
+ value = Number(value);
+ node.properties[ property ] = value;
+
+ if(node.onPropertyChanged)
+ node.onPropertyChanged( property, value );
+ dialog.parentNode.removeChild( dialog );
+ node.setDirtyCanvas(true,true);
+ }
+}
+
LGraphCanvas.onMenuNodeCollapse = function(node)
{
node.flags.collapsed = !node.flags.collapsed;
@@ -4481,6 +5027,27 @@ LGraphCanvas.onMenuNodePin = function(node)
node.pin();
}
+LGraphCanvas.onMenuNodeMode = function(node, e, prev_menu)
+{
+ LiteGraph.createContextMenu(["Always","On Event","Never"], {event: e, callback: inner_clicked, from: prev_menu});
+
+ function inner_clicked(v)
+ {
+ if(!node)
+ return;
+ switch(v)
+ {
+ case "On Event": node.mode = LiteGraph.ON_EVENT; break;
+ case "Never": node.mode = LiteGraph.NEVER; break;
+ case "Always":
+ default:
+ node.mode = LiteGraph.ALWAYS; break;
+ }
+ }
+
+ return false;
+}
+
LGraphCanvas.onMenuNodeColors = function(node, e, prev_menu)
{
var values = [];
@@ -4490,7 +5057,7 @@ LGraphCanvas.onMenuNodeColors = function(node, e, prev_menu)
var value = {value:i, content:"<span style='display: block; color:"+color.color+"; background-color:"+color.bgcolor+"'>"+i+"</span>"};
values.push(value);
}
- LiteGraph.createContextualMenu(values, {event: e, callback: inner_clicked, from: prev_menu});
+ LiteGraph.createContextMenu(values, {event: e, callback: inner_clicked, from: prev_menu});
function inner_clicked(v)
{
@@ -4509,7 +5076,7 @@ LGraphCanvas.onMenuNodeColors = function(node, e, prev_menu)
LGraphCanvas.onMenuNodeShapes = function(node,e)
{
- LiteGraph.createContextualMenu(["box","round"], {event: e, callback: inner_clicked});
+ LiteGraph.createContextMenu(["box","round"], {event: e, callback: inner_clicked});
function inner_clicked(v)
{
@@ -4563,12 +5130,9 @@ LGraphCanvas.prototype.getCanvasMenuOptions = function()
if(this.getExtraMenuOptions)
{
- var extra = this.getExtraMenuOptions(this);
+ var extra = this.getExtraMenuOptions(this,options);
if(extra)
- {
- extra.push(null);
- options = extra.concat( options );
- }
+ options = options.concat( extra );
}
return options;
@@ -4582,9 +5146,12 @@ LGraphCanvas.prototype.getNodeMenuOptions = function(node)
options = node.getMenuOptions(this);
else
options = [
- {content:"Inputs", is_menu: true, disabled:true, callback: LGraphCanvas.onMenuNodeInputs },
- {content:"Outputs", is_menu: true, disabled:true, callback: LGraphCanvas.onMenuNodeOutputs },
+ {content:"Inputs", is_menu: true, disabled:true, callback: LGraphCanvas.showMenuNodeInputs },
+ {content:"Outputs", is_menu: true, disabled:true, callback: LGraphCanvas.showMenuNodeOutputs },
null,
+ {content:"Properties", is_menu: true, callback: LGraphCanvas.onShowMenuNodeProperties },
+ null,
+ {content:"Mode", is_menu: true, callback: LGraphCanvas.onMenuNodeMode },
{content:"Collapse", callback: LGraphCanvas.onMenuNodeCollapse },
{content:"Pin", callback: LGraphCanvas.onMenuNodePin },
{content:"Colors", is_menu: true, callback: LGraphCanvas.onMenuNodeColors },
@@ -4624,7 +5191,7 @@ LGraphCanvas.prototype.getNodeMenuOptions = function(node)
return options;
}
-LGraphCanvas.prototype.processContextualMenu = function(node, event)
+LGraphCanvas.prototype.processContextMenu = function(node, event)
{
var that = this;
var win = this.getCanvasWindow();
@@ -4641,6 +5208,8 @@ LGraphCanvas.prototype.processContextualMenu = function(node, event)
{
menu_info = slot.locked ? [ "Cannot remove" ] : { "Remove Slot": slot };
options.title = slot.input ? slot.input.type : slot.output.type;
+ if(slot.input && slot.input.type == LiteGraph.EVENT)
+ options.title = "Event";
}
else
menu_info = node ? this.getNodeMenuOptions(node) : this.getCanvasMenuOptions();
@@ -4650,7 +5219,7 @@ LGraphCanvas.prototype.processContextualMenu = function(node, event)
if(!menu_info)
return;
- var menu = LiteGraph.createContextualMenu( menu_info, options, win);
+ var menu = LiteGraph.createContextMenu( menu_info, options, win);
function inner_option_clicked(v,e)
{
@@ -4667,7 +5236,7 @@ LGraphCanvas.prototype.processContextualMenu = function(node, event)
}
if(v.callback)
- return v.callback(node, e, menu, that, event );
+ return v.callback.call(that, node, e, menu, that, event );
}
}
@@ -4796,7 +5365,7 @@ function num2hex(triplet) {
/* LiteGraph GUI elements *************************************/
-LiteGraph.createContextualMenu = function(values,options, ref_window)
+LiteGraph.createContextMenu = function(values,options, ref_window)
{
options = options || {};
this.options = options;
@@ -4804,11 +5373,19 @@ LiteGraph.createContextualMenu = function(values,options, ref_window)
//allows to create graph canvas in separate window
ref_window = ref_window || window;
- if(!options.from)
- LiteGraph.closeAllContextualMenus();
+ if (!options.from)
+ LiteGraph.closeAllContextMenus( ref_window );
+ else {
+ //closing submenus
+ var menus = document.querySelectorAll(".graphcontextmenu");
+ for (var key in menus) {
+ if (menus[key].previousSibling == options.from)
+ menus[key].closeMenu();
+ }
+ }
var root = ref_window.document.createElement("div");
- root.className = "graphcontextualmenu graphmenubar-panel";
+ root.className = "graphcontextmenu graphmenubar-panel";
this.root = root;
var style = root.style;
@@ -4822,12 +5399,13 @@ LiteGraph.createContextualMenu = function(values,options, ref_window)
style.padding = "2px";
style.borderBottom = "2px solid #AAF";
style.backgroundColor = "#444";
+ style.zIndex = 10;
//title
if(options.title)
{
var element = document.createElement("div");
- element.className = "graphcontextualmenu-title";
+ element.className = "graphcontextmenu-title";
element.innerHTML = options.title;
root.appendChild(element);
}
@@ -4843,7 +5421,7 @@ LiteGraph.createContextualMenu = function(values,options, ref_window)
if(item == null)
{
- element.className = "graphmenu-entry separator";
+ element.className += " separator";
root.appendChild(element);
continue;
}
@@ -4854,6 +5432,9 @@ LiteGraph.createContextualMenu = function(values,options, ref_window)
if(item.disabled)
element.className += " disabled";
+ if(item.className)
+ element.className += " " + item.className;
+
element.style.cursor = "pointer";
element.dataset["value"] = typeof(item) == "string" ? item : item.value;
element.data = item;
@@ -4872,11 +5453,13 @@ LiteGraph.createContextualMenu = function(values,options, ref_window)
root.addEventListener("mouseout", function(e) {
//console.log("OUT!");
- var aux = e.toElement;
+ //check if mouse leave a inner element
+ var aux = e.relatedTarget || e.toElement;
while(aux != this && aux != ref_window.document)
aux = aux.parentNode;
- if(aux == this) return;
+ if(aux == this)
+ return;
this.mouse_inside = false;
if(!this.block_close)
this.closeMenu();
@@ -4930,7 +5513,7 @@ LiteGraph.createContextualMenu = function(values,options, ref_window)
}
if(close)
- LiteGraph.closeAllContextualMenus();
+ LiteGraph.closeAllContextMenus( ref_window );
//root.closeMenu();
}
@@ -4949,9 +5532,11 @@ LiteGraph.createContextualMenu = function(values,options, ref_window)
return root;
}
-LiteGraph.closeAllContextualMenus = function()
+LiteGraph.closeAllContextMenus = function( ref_window )
{
- var elements = document.querySelectorAll(".graphcontextualmenu");
+ ref_window = ref_window || window;
+
+ var elements = ref_window.document.querySelectorAll(".graphcontextmenu");
if(!elements.length) return;
var result = [];
@@ -5014,7 +5599,6 @@ if( !window["requestAnimationFrame"] )
Browse to a module or class using the sidebar to view its API documentation. @@ -109,7 +100,6 @@

