mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-03 06:47:33 +00:00
Small fix and new features
- nodes_executing, nodes_actioning initial set - slot_types_default (in and out) helps in having defaults nodes for specific slot_types - slot_types_default are filled as an example in src/nodes/others.js - middle_click_slot_add_default_node allows auto-placing defaults nodes next to a slot - release_link_on_empty_shows_menu conditions having realease-in-empty-space functionality - keypress for slot name - do include new js/defaults.js in editor and leave the Lib with new functionalities off by default - new html with mobile editor (working on) - separate defaults - optional thirdy-party html console
This commit is contained in:
144
editor/editor_mobile.html
Normal file
144
editor/editor_mobile.html
Normal file
@@ -0,0 +1,144 @@
|
||||
<!-- Javi Agenjo (@tamat) on 31/9/2011 -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>LiteGraph</title>
|
||||
<!--<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">-->
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../css/litegraph.css">
|
||||
<link rel="stylesheet" type="text/css" href="../css/litegraph-editor.css">
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../external/jquery-1.6.2.min.js"></script>
|
||||
<script type="text/javascript" src="https://tamats.com/projects/sillyserver/src/sillyclient.js"></script>
|
||||
<!-- <script type="text/javascript" src="https://unpkg.com/codeflask/build/codeflask.min.js"></script> -->
|
||||
<script type="text/javascript" src="js/libs/gl-matrix-min.js"></script>
|
||||
<script type="text/javascript" src="js/libs/audiosynth.js"></script>
|
||||
<script type="text/javascript" src="js/libs/midi-parser.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../src/litegraph.js"></script>
|
||||
<script type="text/javascript" src="../src/litegraph-editor.js"></script>
|
||||
<script type="text/javascript" src="js/defaults_mobile.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../src/nodes/base.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/logic.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/events.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/math.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/math3d.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/strings.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/interface.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/geometry.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/graphics.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/input.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/midi.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/audio.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/network.js"></script>
|
||||
|
||||
<script type="text/javascript" src="js/demos.js"></script>
|
||||
<script type="text/javascript" src="js/code.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../src/nodes/others.js"></script>
|
||||
|
||||
<!-- htmlConsole use to debug on mobile, include and set editorUseHtmlConsole in defaults.js -->
|
||||
<!-- enable console style, html, js enabling/disabling this comment here-> -->
|
||||
|
||||
<link rel="stylesheet" href="//htmlacademy.github.io/console.js/latest/css/style.css">
|
||||
<style>
|
||||
.invisible{ display: none; }
|
||||
.console__row{
|
||||
margin: 1px;
|
||||
padding: 2px;
|
||||
}
|
||||
.console-container{
|
||||
min-width: 200px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
position: fixed;
|
||||
top: 38px;
|
||||
left: 0;
|
||||
overflow: auto;
|
||||
height: calc(100%-38px);
|
||||
}
|
||||
.console-container.small{
|
||||
max-width: 30%;
|
||||
}
|
||||
.graphcanvas{
|
||||
/*WONT WORK touch-action: manipulation;*/
|
||||
/*touch-action: none;*/
|
||||
touch-action: pinch-zoom;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="console-container" class="litegraph-editor console-container small invisible" style="">
|
||||
<div class="console-tools" style="position: absolute; top: 0; right:0; z-index:2;">
|
||||
<button class='btn' id='btn_console_close'>close</button>
|
||||
<button class='btn' id='btn_console_clear'>clear</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="//htmlacademy.github.io/console.js/latest/js/index.js"></script>
|
||||
<script>
|
||||
|
||||
var editorUseHtmlConsole = true; // enable html console to debug on mobile
|
||||
|
||||
// ToBarSelector
|
||||
if(editorUseHtmlConsole){
|
||||
document.getElementById("LGEditorTopBarSelector").innerHTML = "<button class='btn' id='btn_console'>Console</button> "
|
||||
+document.getElementById("LGEditorTopBarSelector").innerHTML;
|
||||
}
|
||||
|
||||
// html console
|
||||
if(editorUseHtmlConsole){
|
||||
elem.querySelector("#btn_console").addEventListener("click", function(){
|
||||
var consoleCnt = document.getElementById('console-container');
|
||||
if (consoleCnt.classList.contains("invisible")){
|
||||
consoleCnt.classList.remove("invisible");
|
||||
}else{
|
||||
jsConsole.clean();
|
||||
consoleCnt.classList.add("invisible");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const params = {
|
||||
expandDepth : 1,
|
||||
common : {
|
||||
excludeProperties : ['__proto__'],
|
||||
removeProperties: ['__proto__'],
|
||||
maxFieldsInHead : 5,
|
||||
minFieldsToAutoexpand : 5,
|
||||
maxFieldsToAutoexpand : 15
|
||||
}
|
||||
};
|
||||
var jsConsole = new Console(document.querySelector('.console-container'), params);
|
||||
jsConsole.log("Here is console.log!");
|
||||
|
||||
// map console log-debug to jsConsole
|
||||
console.log = function(par){
|
||||
jsConsole.log(par);
|
||||
var objDiv = document.getElementById("console-container");
|
||||
objDiv.scrollTop = objDiv.scrollHeight;
|
||||
}
|
||||
console.debug = console.log;
|
||||
|
||||
console.log("going into html console");
|
||||
|
||||
document.getElementById("btn_console_clear").addEventListener("click", function(){
|
||||
var consoleCnt = document.getElementById('console-container');
|
||||
jsConsole.clean();
|
||||
});
|
||||
document.getElementById("btn_console_close").addEventListener("click", function(){
|
||||
var consoleCnt = document.getElementById('console-container');
|
||||
consoleCnt.classList.add("invisible");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<!-- -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,44 +1,47 @@
|
||||
<!-- Javi Agenjo (@tamat) on 31/9/2011 -->
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<title>LiteGraph</title>
|
||||
<!--<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">-->
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../css/litegraph.css">
|
||||
<link rel="stylesheet" type="text/css" href="../css/litegraph-editor.css">
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../external/jquery-1.6.2.min.js"></script>
|
||||
<script type="text/javascript" src="https://tamats.com/projects/sillyserver/src/sillyclient.js"></script>
|
||||
<!-- <script type="text/javascript" src="https://unpkg.com/codeflask/build/codeflask.min.js"></script> -->
|
||||
<script type="text/javascript" src="js/libs/gl-matrix-min.js"></script>
|
||||
<script type="text/javascript" src="js/libs/audiosynth.js"></script>
|
||||
<script type="text/javascript" src="js/libs/midi-parser.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../src/litegraph.js"></script>
|
||||
<script type="text/javascript" src="../src/litegraph-editor.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../src/nodes/base.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/logic.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/events.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/math.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/math3d.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/strings.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/interface.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/geometry.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/graphics.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/input.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/midi.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/audio.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/network.js"></script>
|
||||
|
||||
<script type="text/javascript" src="js/demos.js"></script>
|
||||
<script type="text/javascript" src="js/code.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<!-- Javi Agenjo (@tamat) on 31/9/2011 -->
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<title>LiteGraph</title>
|
||||
<!--<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">-->
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../css/litegraph.css">
|
||||
<link rel="stylesheet" type="text/css" href="../css/litegraph-editor.css">
|
||||
<link rel="stylesheet" type="text/css" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="main">
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../external/jquery-1.6.2.min.js"></script>
|
||||
<script type="text/javascript" src="https://tamats.com/projects/sillyserver/src/sillyclient.js"></script>
|
||||
<!-- <script type="text/javascript" src="https://unpkg.com/codeflask/build/codeflask.min.js"></script> -->
|
||||
<script type="text/javascript" src="js/libs/gl-matrix-min.js"></script>
|
||||
<script type="text/javascript" src="js/libs/audiosynth.js"></script>
|
||||
<script type="text/javascript" src="js/libs/midi-parser.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../src/litegraph.js"></script>
|
||||
<script type="text/javascript" src="../src/litegraph-editor.js"></script>
|
||||
<script type="text/javascript" src="js/defaults.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../src/nodes/base.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/logic.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/events.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/math.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/math3d.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/strings.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/interface.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/geometry.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/graphics.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/input.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/midi.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/audio.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/network.js"></script>
|
||||
|
||||
<script type="text/javascript" src="js/demos.js"></script>
|
||||
<script type="text/javascript" src="js/code.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../src/nodes/others.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var webgl_canvas = null;
|
||||
|
||||
LiteGraph.node_images_path = "../nodes_data/";
|
||||
|
||||
var editor = new LiteGraph.Editor("main",{miniwindow:false});
|
||||
window.graphcanvas = editor.graphcanvas;
|
||||
window.graph = editor.graph;
|
||||
@@ -19,8 +20,10 @@ LiteGraph.allow_scripts = true;
|
||||
|
||||
//create scene selector
|
||||
var elem = document.createElement("span");
|
||||
elem.id = "LGEditorTopBarSelector";
|
||||
elem.className = "selector";
|
||||
elem.innerHTML = "Demo <select><option>Empty</option></select> <button class='btn' id='save'>Save</button><button class='btn' id='load'>Load</button><button class='btn' id='download'>Download</button> | <button class='btn' id='webgl'>WebGL</button> <button class='btn' id='multiview'>Multiview</button>";
|
||||
elem.innerHTML = "";
|
||||
elem.innerHTML += "Demo <select><option>Empty</option></select> <button class='btn' id='save'>Save</button><button class='btn' id='load'>Load</button><button class='btn' id='download'>Download</button> | <button class='btn' id='webgl'>WebGL</button> <button class='btn' id='multiview'>Multiview</button>";
|
||||
editor.tools.appendChild(elem);
|
||||
var select = elem.querySelector("select");
|
||||
select.addEventListener("change", function(e){
|
||||
@@ -167,4 +170,4 @@ function enableWebGL()
|
||||
gl.viewport(0,0,gl.canvas.width, gl.canvas.height );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
editor/js/defaults.js
Normal file
31
editor/js/defaults.js
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
LiteGraph.debug = false;
|
||||
LiteGraph.catch_exceptions = true;
|
||||
LiteGraph.throw_errors = true;
|
||||
LiteGraph.allow_scripts = false; //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration); which could lead to exploits
|
||||
|
||||
LiteGraph.searchbox_extras = {}; //used to add extra features to the search box
|
||||
LiteGraph.auto_sort_node_types = true; // [true!] If set to true; will automatically sort node types / categories in the context menus
|
||||
LiteGraph.node_box_coloured_when_on = true; // [true!] this make the nodes box (top left circle) coloured when triggered (execute/action); visual feedback
|
||||
LiteGraph.node_box_coloured_by_mode = true; // [true!] nodebox based on node mode; visual feedback
|
||||
LiteGraph.dialog_close_on_mouse_leave = true; // [false on mobile] better true if not touch device;
|
||||
LiteGraph.dialog_close_on_mouse_leave_delay = 500;
|
||||
LiteGraph.shift_click_do_break_link_from = false; // [false!] prefer false if results too easy to break links
|
||||
LiteGraph.click_do_break_link_to = false; // [false!]prefer false; way too easy to break links
|
||||
LiteGraph.search_hide_on_mouse_leave = true; // [false on mobile] better true if not touch device;
|
||||
LiteGraph.search_filter_enabled = true; // [true!] enable filtering slots type in the search widget; !requires auto_load_slot_types or manual set registered_slot_[in/out]_types and slot_types_[in/out]
|
||||
LiteGraph.search_show_all_on_open = true; // [true!] opens the results list when opening the search widget
|
||||
|
||||
LiteGraph.auto_load_slot_types = true; // [if want false; use true; run; get vars values to be statically set; than disable] nodes types and nodeclass association with node types need to be calculated; if dont want this; calculate once and set registered_slot_[in/out]_types and slot_types_[in/out]
|
||||
/*// set these values if not using auto_load_slot_types
|
||||
LiteGraph.registered_slot_in_types = {}; // slot types for nodeclass
|
||||
LiteGraph.registered_slot_out_types = {}; // slot types for nodeclass
|
||||
LiteGraph.slot_types_in = []; // slot types IN
|
||||
LiteGraph.slot_types_out = []; // slot types OUT*/
|
||||
|
||||
LiteGraph.alt_drag_do_clone_nodes = true; // [true!] very handy; ALT click to clone and drag the new node
|
||||
LiteGraph.do_add_triggers_slots = true; // [true!] will create and connect event slots when using action/events connections; !WILL CHANGE node mode when using onTrigger (enable mode colors); onExecuted does not need this
|
||||
LiteGraph.allow_multi_output_for_events = false; // [false!] being events; it is strongly reccomended to use them sequentually; one by one
|
||||
LiteGraph.middle_click_slot_add_default_node = true; //[true!] allows to create and connect a ndoe clicking with the third button (wheel)
|
||||
LiteGraph.release_link_on_empty_shows_menu = true; //[true!] dragging a link to empty space will open a menu, add from list, search or defaults
|
||||
LiteGraph.pointerevents_method = "pointer"; // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now)
|
||||
31
editor/js/defaults_mobile.js
Normal file
31
editor/js/defaults_mobile.js
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
LiteGraph.debug = false;
|
||||
LiteGraph.catch_exceptions = true;
|
||||
LiteGraph.throw_errors = true;
|
||||
LiteGraph.allow_scripts = false; //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration); which could lead to exploits
|
||||
|
||||
LiteGraph.searchbox_extras = {}; //used to add extra features to the search box
|
||||
LiteGraph.auto_sort_node_types = true; // [true!] If set to true; will automatically sort node types / categories in the context menus
|
||||
LiteGraph.node_box_coloured_when_on = true; // [true!] this make the nodes box (top left circle) coloured when triggered (execute/action); visual feedback
|
||||
LiteGraph.node_box_coloured_by_mode = true; // [true!] nodebox based on node mode; visual feedback
|
||||
LiteGraph.dialog_close_on_mouse_leave = false; // [false on mobile] better true if not touch device;
|
||||
LiteGraph.dialog_close_on_mouse_leave_delay = 500;
|
||||
LiteGraph.shift_click_do_break_link_from = false; // [false!] prefer false if results too easy to break links
|
||||
LiteGraph.click_do_break_link_to = false; // [false!]prefer false; way too easy to break links
|
||||
LiteGraph.search_hide_on_mouse_leave = false; // [false on mobile] better true if not touch device;
|
||||
LiteGraph.search_filter_enabled = true; // [true!] enable filtering slots type in the search widget; !requires auto_load_slot_types or manual set registered_slot_[in/out]_types and slot_types_[in/out]
|
||||
LiteGraph.search_show_all_on_open = true; // [true!] opens the results list when opening the search widget
|
||||
|
||||
LiteGraph.auto_load_slot_types = true; // [if want false; use true; run; get vars values to be statically set; than disable] nodes types and nodeclass association with node types need to be calculated; if dont want this; calculate once and set registered_slot_[in/out]_types and slot_types_[in/out]
|
||||
/*// set these values if not using auto_load_slot_types
|
||||
LiteGraph.registered_slot_in_types = {}; // slot types for nodeclass
|
||||
LiteGraph.registered_slot_out_types = {}; // slot types for nodeclass
|
||||
LiteGraph.slot_types_in = []; // slot types IN
|
||||
LiteGraph.slot_types_out = []; // slot types OUT*/
|
||||
|
||||
LiteGraph.alt_drag_do_clone_nodes = true; // [true!] very handy; ALT click to clone and drag the new node
|
||||
LiteGraph.do_add_triggers_slots = true; // [true!] will create and connect event slots when using action/events connections; !WILL CHANGE node mode when using onTrigger (enable mode colors); onExecuted does not need this
|
||||
LiteGraph.allow_multi_output_for_events = false; // [false!] being events; it is strongly reccomended to use them sequentually; one by one
|
||||
LiteGraph.middle_click_slot_add_default_node = true; //[true!] allows to create and connect a ndoe clicking with the third button (wheel)
|
||||
LiteGraph.release_link_on_empty_shows_menu = true; //[true!] dragging a link to empty space will open a menu, add from list, search or defaults
|
||||
LiteGraph.pointerevents_method = "pointer"; // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now)
|
||||
388
src/litegraph.js
388
src/litegraph.js
@@ -123,13 +123,19 @@
|
||||
registered_slot_out_types: {}, // slot types for nodeclass
|
||||
slot_types_in: [], // slot types IN
|
||||
slot_types_out: [], // slot types OUT
|
||||
|
||||
slot_types_default_in: [], // specify for each IN slot type a(/many) deafult node(s), use single string, array, or object (with node, title, parameters, ..) like for search
|
||||
slot_types_default_out: [], // specify for each OUT slot type a(/many) deafult node(s), use single string, array, or object (with node, title, parameters, ..) like for search
|
||||
|
||||
alt_drag_do_clone_nodes: false, // [true!] very handy, ALT click to clone and drag the new node
|
||||
|
||||
do_add_triggers_slots: false, // [true!] will create and connect event slots when using action/events connections, !WILL CHANGE node mode when using onTrigger (enable mode colors), onExecuted does not need this
|
||||
|
||||
allowMultiOutputForEvents: true, // [false!] being events, it is strongly reccomended to use them sequentually, one by one
|
||||
allow_multi_output_for_events: true, // [false!] being events, it is strongly reccomended to use them sequentually, one by one
|
||||
|
||||
middle_click_slot_add_default_node: false, //[true!] allows to create and connect a ndoe clicking with the third button (wheel)
|
||||
|
||||
release_link_on_empty_shows_menu: false, //[true!] dragging a link to empty space will open a menu, add from list, search or defaults
|
||||
|
||||
pointerevents_method: "pointer", // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now)
|
||||
// TODO implement pointercancel, gotpointercapture, lostpointercapture, (pointerover, pointerout if necessary)
|
||||
|
||||
@@ -288,7 +294,7 @@
|
||||
if (typeof slot_type == "string"){
|
||||
var aTypes = slot_type.split(",");
|
||||
}else if (slot_type == this.EVENT || slot_type == this.ACTION){
|
||||
var aTypes = ["EVENT/ACTION"];
|
||||
var aTypes = ["_event_"];
|
||||
}else{
|
||||
var aTypes = ["*"];
|
||||
}
|
||||
@@ -304,13 +310,13 @@
|
||||
|
||||
// check if is a new type
|
||||
if (!out){
|
||||
if (!this.slot_types_in.includes(sT)){
|
||||
this.slot_types_in.push(sT);
|
||||
if (!this.slot_types_in.includes(sT.toLowerCase())){
|
||||
this.slot_types_in.push(sT.toLowerCase());
|
||||
this.slot_types_in.sort();
|
||||
}
|
||||
}else{
|
||||
if (!this.slot_types_out.includes(sT)){
|
||||
this.slot_types_out.push(sT);
|
||||
if (!this.slot_types_out.includes(sT.toLowerCase())){
|
||||
this.slot_types_out.push(sT.toLowerCase());
|
||||
this.slot_types_out.sort();
|
||||
}
|
||||
}
|
||||
@@ -832,6 +838,10 @@
|
||||
|
||||
this.catch_errors = true;
|
||||
|
||||
this.nodes_executing = [];
|
||||
this.nodes_actioning = [];
|
||||
this.nodes_executedAction = [];
|
||||
|
||||
//subgraph_data
|
||||
this.inputs = {};
|
||||
this.outputs = {};
|
||||
@@ -3098,12 +3108,8 @@
|
||||
// enable this to give the event an ID
|
||||
if (!options.action_call) options.action_call = this.id+"_exec_"+Math.floor(Math.random()*9999);
|
||||
|
||||
if (this.graph.nodes_executing) this.graph.nodes_executing[this.id] = true; //.push(this.id);
|
||||
else{
|
||||
this.graph.nodes_executing = {};
|
||||
this.graph.nodes_executing[this.id] = true;
|
||||
}
|
||||
|
||||
this.graph.nodes_executing[this.id] = true; //.push(this.id);
|
||||
|
||||
this.onExecute(param, options);
|
||||
|
||||
this.graph.nodes_executing[this.id] = false; //.pop();
|
||||
@@ -4213,7 +4219,7 @@
|
||||
if (output.links !== null && output.links.length){
|
||||
switch(output.type){
|
||||
case LiteGraph.EVENT:
|
||||
if (!LiteGraph.allowMultiOutputForEvents){
|
||||
if (!LiteGraph.allow_multi_output_for_events){
|
||||
this.graph.beforeChange();
|
||||
this.disconnectOutput(slot, false, {doProcessChange: false}); // Input(target_slot, {doProcessChange: false});
|
||||
changed = true;
|
||||
@@ -6083,6 +6089,148 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
|
||||
} else if (e.which == 2) {
|
||||
//middle button
|
||||
|
||||
if (LiteGraph.middle_click_slot_add_default_node){
|
||||
if (node && this.allow_interaction && !skip_action && !this.read_only){
|
||||
//not dragging mouse to connect two slots
|
||||
if (
|
||||
!this.connecting_node &&
|
||||
!node.flags.collapsed &&
|
||||
!this.live_mode
|
||||
) {
|
||||
var middleClickedSlot = false;
|
||||
var middleClickedSlot_index = false;
|
||||
var middleClickedSlot_isOut = false;
|
||||
//search for outputs
|
||||
if (node.outputs) {
|
||||
for ( var i = 0, l = node.outputs.length; i < l; ++i ) {
|
||||
var output = node.outputs[i];
|
||||
var link_pos = node.getConnectionPos(false, i);
|
||||
if (
|
||||
isInsideRectangle(
|
||||
e.canvasX,
|
||||
e.canvasY,
|
||||
link_pos[0] - 15,
|
||||
link_pos[1] - 10,
|
||||
30,
|
||||
20
|
||||
)
|
||||
) {
|
||||
middleClickedSlot = output;
|
||||
middleClickedSlot_index = i;
|
||||
middleClickedSlot_isOut = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//search for inputs
|
||||
if (node.inputs) {
|
||||
for ( var i = 0, l = node.inputs.length; i < l; ++i ) {
|
||||
var input = node.inputs[i];
|
||||
var link_pos = node.getConnectionPos(true, i);
|
||||
if (
|
||||
isInsideRectangle(
|
||||
e.canvasX,
|
||||
e.canvasY,
|
||||
link_pos[0] - 15,
|
||||
link_pos[1] - 10,
|
||||
30,
|
||||
20
|
||||
)
|
||||
) {
|
||||
middleClickedSlot = input;
|
||||
middleClickedSlot_index = i;
|
||||
middleClickedSlot_isOut = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//console.log("middleClickSlots? "+middleClickedSlot+" & "+(middleClickedSlot_index!==false));
|
||||
if (middleClickedSlot && middleClickedSlot_index!==false){
|
||||
// check for defaults nodes for this slottype
|
||||
var slotTypesDefault = middleClickedSlot_isOut ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in;
|
||||
//console.log("middleClickSlots "+middleClickedSlot.type+" in "+slotTypesDefault[middleClickedSlot.type]);
|
||||
if(slotTypesDefault && slotTypesDefault[middleClickedSlot.type]){
|
||||
if (middleClickedSlot.link !== null) {
|
||||
// is connected
|
||||
}else{
|
||||
// is not not connected
|
||||
}
|
||||
nodeNewType = false;
|
||||
if(typeof slotTypesDefault[middleClickedSlot.type] == "object" || typeof slotTypesDefault[middleClickedSlot.type] == "array"){
|
||||
for (typeX in slotTypesDefault[middleClickedSlot.type]){
|
||||
nodeNewType = slotTypesDefault[middleClickedSlot.type][typeX];
|
||||
break; // --------
|
||||
}
|
||||
}else{
|
||||
nodeNewType = slotTypesDefault[middleClickedSlot.type];
|
||||
}
|
||||
if (nodeNewType) {
|
||||
var nodeNewOpts = false;
|
||||
if (typeof nodeNewType == "object" && nodeNewType.node){
|
||||
nodeNewOpts = nodeNewType;
|
||||
nodeNewType = nodeNewType.node;
|
||||
}
|
||||
//that.graph.beforeChange();
|
||||
var newNode = LiteGraph.createNode(nodeNewType);
|
||||
if(newNode){
|
||||
// if is object pass options
|
||||
if (nodeNewOpts){
|
||||
if (nodeNewOpts.properties) {
|
||||
for (var i in nodeNewOpts.properties) {
|
||||
newNode.addProperty( i, nodeNewOpts.properties[i] );
|
||||
}
|
||||
}
|
||||
if (nodeNewOpts.inputs) {
|
||||
newNode.inputs = [];
|
||||
for (var i in nodeNewOpts.inputs) {
|
||||
newNode.addOutput(
|
||||
nodeNewOpts.inputs[i][0],
|
||||
nodeNewOpts.inputs[i][1]
|
||||
);
|
||||
}
|
||||
}
|
||||
if (nodeNewOpts.outputs) {
|
||||
newNode.outputs = [];
|
||||
for (var i in nodeNewOpts.outputs) {
|
||||
newNode.addOutput(
|
||||
nodeNewOpts.outputs[i][0],
|
||||
nodeNewOpts.outputs[i][1]
|
||||
);
|
||||
}
|
||||
}
|
||||
if (nodeNewOpts.title) {
|
||||
newNode.title = nodeNewOpts.title;
|
||||
}
|
||||
if (nodeNewOpts.json) {
|
||||
newNode.configure(nodeNewOpts.json);
|
||||
}
|
||||
|
||||
}
|
||||
// add the node
|
||||
that.graph.add(newNode);
|
||||
// connect the two!
|
||||
if (middleClickedSlot_isOut){
|
||||
newNode.pos = [e.canvasX+30,e.canvasY]; //that.last_click_position; //[e.canvasX+30, e.canvasX+5];
|
||||
node.connectByType( middleClickedSlot_index, newNode, middleClickedSlot.type );
|
||||
}else{
|
||||
newNode.pos = [e.canvasX-30-(newNode.size?newNode.size[0]:0),e.canvasY]; //that.last_click_position; //[e.canvasX+30, e.canvasX+5];
|
||||
node.connectByTypeOutput( middleClickedSlot_index, newNode, middleClickedSlot.type );
|
||||
}
|
||||
//that.graph.afterChange();
|
||||
}else{
|
||||
console.log("failed creating "+nodeNewType);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//console.log("no slotTypesDefault for MIDDLECLICK "+middleClickedSlot.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (e.which == 3 || this.pointer_is_double) {
|
||||
//right button
|
||||
if(!this.read_only)
|
||||
@@ -6570,20 +6718,21 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
}else{
|
||||
|
||||
// add menu when releasing link in empty space
|
||||
|
||||
if (e.shiftKey && this.allow_searchbox){
|
||||
if(this.connecting_output){
|
||||
this.showSearchBox(e,{node_from: this.connecting_node, slot_from: this.connecting_output, type_filter_in: this.connecting_output.type});
|
||||
}else if(this.connecting_input){
|
||||
this.showSearchBox(e,{node_to: this.connecting_node, slot_from: this.connecting_input, type_filter_out: this.connecting_input.type});
|
||||
}
|
||||
}else{
|
||||
if(this.connecting_output){
|
||||
this.showConnectionMenu({nodeFrom: this.connecting_node, slotFrom: this.connecting_output, e: e});
|
||||
}else if(this.connecting_input){
|
||||
this.showConnectionMenu({nodeTo: this.connecting_node, slotTo: this.connecting_input, e: e});
|
||||
}
|
||||
}
|
||||
if (LiteGraph.release_link_on_empty_shows_menu){
|
||||
if (e.shiftKey && this.allow_searchbox){
|
||||
if(this.connecting_output){
|
||||
this.showSearchBox(e,{node_from: this.connecting_node, slot_from: this.connecting_output, type_filter_in: this.connecting_output.type});
|
||||
}else if(this.connecting_input){
|
||||
this.showSearchBox(e,{node_to: this.connecting_node, slot_from: this.connecting_input, type_filter_out: this.connecting_input.type});
|
||||
}
|
||||
}else{
|
||||
if(this.connecting_output){
|
||||
this.showConnectionMenu({nodeFrom: this.connecting_node, slotFrom: this.connecting_output, e: e});
|
||||
}else if(this.connecting_input){
|
||||
this.showConnectionMenu({nodeTo: this.connecting_node, slotTo: this.connecting_input, e: e});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.connecting_output = null;
|
||||
@@ -10569,14 +10718,40 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
}
|
||||
|
||||
var options = ["Add Node",null];
|
||||
|
||||
if (that.allow_searchbox){
|
||||
options.push("Search");
|
||||
options.push(null);
|
||||
}
|
||||
|
||||
// get defaults nodes for this slottype
|
||||
var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in;
|
||||
if(slotTypesDefault && slotTypesDefault[slotX.type]){
|
||||
//console.log("TypeDefaulMenu (OUT?"+isFrom+") "+slotX.type+" :: "+slotTypesDefault[slotX.type]);
|
||||
if (slotX.link !== null) {
|
||||
// is connected
|
||||
}else{
|
||||
// is not not connected
|
||||
}
|
||||
//console.log(typeof slotTypesDefault[slotX.type]);
|
||||
if(typeof slotTypesDefault[slotX.type] == "object" || typeof slotTypesDefault[slotX.type] == "array"){
|
||||
for(var typeX in slotTypesDefault[slotX.type]){
|
||||
//console.log(slotX.type+" has default "+slotTypesDefault[slotX.type][typeX]);
|
||||
options.push(slotTypesDefault[slotX.type][typeX]);
|
||||
}
|
||||
}else{
|
||||
options.push(slotTypesDefault[slotX.type]);
|
||||
}
|
||||
}
|
||||
|
||||
var menu = new LiteGraph.ContextMenu(options, {
|
||||
event: opts.e,
|
||||
title: slotX && slotX.name ? slotX.name : null,
|
||||
title: (slotX && slotX.name!="" ? (slotX.name + (slotX.type?" | ":"")) : "")+(slotX && slotX.type ? slotX.type : ""),
|
||||
callback: inner_clicked
|
||||
});
|
||||
|
||||
function inner_clicked(v,options,e) {
|
||||
console.debug("menuadd");
|
||||
//console.log("Process showConnectionMenu selection");
|
||||
switch (v) {
|
||||
case "Add Node":
|
||||
LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){
|
||||
@@ -10587,10 +10762,90 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
}
|
||||
});
|
||||
break;
|
||||
/*case "Delete":
|
||||
that.graph.removeLink(link.id);
|
||||
break;*/
|
||||
case "Search":
|
||||
if(isFrom){
|
||||
that.showSearchBox(e,{node_from: opts.nodeFrom, slot_from: slotX, type_filter_in: slotX.type});
|
||||
}else{
|
||||
that.showSearchBox(e,{node_to: opts.nodeTo, slot_from: slotX, type_filter_out: slotX.type});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// check for defaults nodes for this slottype
|
||||
var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in;
|
||||
if(slotTypesDefault && slotTypesDefault[slotX.type]){
|
||||
if (slotX.link !== null) {
|
||||
// is connected
|
||||
}else{
|
||||
// is not not connected
|
||||
}
|
||||
nodeNewType = false;
|
||||
if(typeof slotTypesDefault[slotX.type] == "object" || typeof slotTypesDefault[slotX.type] == "array"){
|
||||
for(var typeX in slotTypesDefault[slotX.type]){
|
||||
if (v == slotTypesDefault[slotX.type][typeX]) nodeNewType = slotTypesDefault[slotX.type][typeX];
|
||||
}
|
||||
}else{
|
||||
if (v == slotTypesDefault[slotX.type]) nodeNewType = slotTypesDefault[slotX.type];
|
||||
}
|
||||
if (nodeNewType) {
|
||||
var nodeNewOpts = false;
|
||||
if (typeof nodeNewType == "object" && nodeNewType.node){
|
||||
nodeNewOpts = nodeNewType;
|
||||
nodeNewType = nodeNewType.node;
|
||||
}
|
||||
//that.graph.beforeChange();
|
||||
var newNode = LiteGraph.createNode(nodeNewType);
|
||||
if(newNode){
|
||||
// if is object pass options
|
||||
if (nodeNewOpts){
|
||||
if (nodeNewOpts.properties) {
|
||||
for (var i in nodeNewOpts.properties) {
|
||||
newNode.addProperty( i, nodeNewOpts.properties[i] );
|
||||
}
|
||||
}
|
||||
if (nodeNewOpts.inputs) {
|
||||
newNode.inputs = [];
|
||||
for (var i in nodeNewOpts.inputs) {
|
||||
newNode.addOutput(
|
||||
nodeNewOpts.inputs[i][0],
|
||||
nodeNewOpts.inputs[i][1]
|
||||
);
|
||||
}
|
||||
}
|
||||
if (nodeNewOpts.outputs) {
|
||||
newNode.outputs = [];
|
||||
for (var i in nodeNewOpts.outputs) {
|
||||
newNode.addOutput(
|
||||
nodeNewOpts.outputs[i][0],
|
||||
nodeNewOpts.outputs[i][1]
|
||||
);
|
||||
}
|
||||
}
|
||||
if (nodeNewOpts.title) {
|
||||
newNode.title = nodeNewOpts.title;
|
||||
}
|
||||
if (nodeNewOpts.json) {
|
||||
newNode.configure(nodeNewOpts.json);
|
||||
}
|
||||
|
||||
}
|
||||
// add the node
|
||||
newNode.pos = [opts.e.canvasX,opts.e.canvasY]; //that.last_click_position; //[e.canvasX+30, e.canvasX+5];
|
||||
that.graph.add(newNode);
|
||||
//that.graph.afterChange();
|
||||
// connect the two!
|
||||
if (isFrom){
|
||||
opts.nodeFrom.connectByType( iSlotConn, newNode, slotX.type );
|
||||
}else{
|
||||
opts.nodeTo.connectByTypeOutput( iSlotConn, newNode, slotX.type );
|
||||
}
|
||||
}else{
|
||||
console.log("failed creating "+nodeNewType);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//console.log("no slotTypesDefault for "+slotX.type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10974,7 +11229,7 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
var nSlots = aSlots.length; // this for object :: Object.keys(aSlots).length;
|
||||
|
||||
if (options.type_filter_in == LiteGraph.EVENT || options.type_filter_in == LiteGraph.ACTION)
|
||||
options.type_filter_in = "event/action";
|
||||
options.type_filter_in = "_event_";
|
||||
/* this will filter on * .. but better do it manually in case
|
||||
else if(options.type_filter_in === "" || options.type_filter_in === 0)
|
||||
options.type_filter_in = "*";*/
|
||||
@@ -11001,7 +11256,7 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
var nSlots = aSlots.length; // this for object :: Object.keys(aSlots).length;
|
||||
|
||||
if (options.type_filter_out == LiteGraph.EVENT || options.type_filter_out == LiteGraph.ACTION)
|
||||
options.type_filter_out = "event/action";
|
||||
options.type_filter_out = "_event_";
|
||||
/* this will filter on * .. but better do it manually in case
|
||||
else if(options.type_filter_out === "" || options.type_filter_out === 0)
|
||||
options.type_filter_out = "*";*/
|
||||
@@ -11317,7 +11572,7 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
|
||||
var sV = sIn.value;
|
||||
if (opts.inTypeOverride!==false) sV = opts.inTypeOverride;
|
||||
//if (sV.toLowerCase() == "event/action") sV = LiteGraph.EVENT; // -1
|
||||
//if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1
|
||||
|
||||
if(sIn && sV){
|
||||
//console.log("will check filter against "+sV);
|
||||
@@ -11336,7 +11591,7 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
|
||||
var sV = sOut.value;
|
||||
if (opts.outTypeOverride!==false) sV = opts.outTypeOverride;
|
||||
//if (sV.toLowerCase() == "event/action") sV = LiteGraph.EVENT; // -1
|
||||
//if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1
|
||||
|
||||
if(sOut && sV){
|
||||
//console.log("search will check filter against "+sV);
|
||||
@@ -11846,8 +12101,8 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
// assume coming from the menu event click
|
||||
if (!obEv || !obEv.event || !obEv.event.target || !obEv.event.target.lgraphcanvas){
|
||||
console.warn("Canvas not found"); // need a ref to canvas obj
|
||||
console.debug(event);
|
||||
console.debug(event.target);
|
||||
/*console.debug(event);
|
||||
console.debug(event.target);*/
|
||||
return;
|
||||
}
|
||||
var graphcanvas = obEv.event.target.lgraphcanvas;
|
||||
@@ -12597,16 +12852,16 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
menu_info.push({ content: "Disconnect Links", slot: slot });
|
||||
}
|
||||
var _slot = slot.input || slot.output;
|
||||
menu_info.push(
|
||||
_slot.locked || !_slot.removable
|
||||
? "Cannot remove"
|
||||
: { content: "Remove Slot", slot: slot }
|
||||
);
|
||||
menu_info.push(
|
||||
_slot.nameLocked
|
||||
? "Cannot rename"
|
||||
: { content: "Rename Slot", slot: slot }
|
||||
);
|
||||
if (_slot.removable){
|
||||
menu_info.push(
|
||||
_slot.locked
|
||||
? "Cannot remove"
|
||||
: { content: "Remove Slot", slot: slot }
|
||||
);
|
||||
}
|
||||
if (!_slot.nameLocked){
|
||||
menu_info.push({ content: "Rename Slot", slot: slot });
|
||||
}
|
||||
|
||||
}
|
||||
options.title =
|
||||
@@ -12687,19 +12942,32 @@ LGraphNode.prototype.executeAction = function(action)
|
||||
if (input && slot_info) {
|
||||
input.value = slot_info.label || "";
|
||||
}
|
||||
dialog
|
||||
.querySelector("button")
|
||||
.addEventListener("click", function(e) {
|
||||
node.graph.beforeChange();
|
||||
if (input.value) {
|
||||
if (slot_info) {
|
||||
slot_info.label = input.value;
|
||||
}
|
||||
that.setDirty(true);
|
||||
var inner = function(){
|
||||
node.graph.beforeChange();
|
||||
if (input.value) {
|
||||
if (slot_info) {
|
||||
slot_info.label = input.value;
|
||||
}
|
||||
that.setDirty(true);
|
||||
}
|
||||
dialog.close();
|
||||
node.graph.afterChange();
|
||||
}
|
||||
dialog.querySelector("button").addEventListener("click", inner);
|
||||
input.addEventListener("keydown", function(e) {
|
||||
dialog.is_modified = true;
|
||||
if (e.keyCode == 27) {
|
||||
//ESC
|
||||
dialog.close();
|
||||
node.graph.afterChange();
|
||||
});
|
||||
} else if (e.keyCode == 13) {
|
||||
inner(); // save
|
||||
} else if (e.keyCode != 13 && e.target.localName != "textarea") {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
});
|
||||
input.focus();
|
||||
}
|
||||
|
||||
//if(v.callback)
|
||||
|
||||
@@ -1 +1,37 @@
|
||||
// extra generic nodes
|
||||
(function(global) {
|
||||
var LiteGraph = global.LiteGraph;
|
||||
|
||||
/* in types :: run in console :: var s=""; LiteGraph.slot_types_in.forEach(function(el){s+=el+"\n";}); console.log(s); */
|
||||
|
||||
if(typeof LiteGraph.slot_types_default_in == "undefined") LiteGraph.slot_types_default_in = {};
|
||||
LiteGraph.slot_types_default_in["_event_"] = "widget/button";
|
||||
LiteGraph.slot_types_default_in["array"] = "basic/array";
|
||||
LiteGraph.slot_types_default_in["boolean"] = "basic/boolean";
|
||||
LiteGraph.slot_types_default_in["number"] = "widget/number";
|
||||
LiteGraph.slot_types_default_in["object"] = "basic/data";
|
||||
LiteGraph.slot_types_default_in["string"] = ["basic/string","string/concatenate"];
|
||||
LiteGraph.slot_types_default_in["vec2"] = "math3d/xy-to-vec2";
|
||||
LiteGraph.slot_types_default_in["vec3"] = "math3d/xyz-to-vec3";
|
||||
LiteGraph.slot_types_default_in["vec4"] = "math3d/xyzw-to-vec4";
|
||||
|
||||
/* out types :: run in console :: var s=""; LiteGraph.slot_types_out.forEach(function(el){s+=el+"\n";}); console.log(s); */
|
||||
if(typeof LiteGraph.slot_types_default_out == "undefined") LiteGraph.slot_types_default_out = {};
|
||||
LiteGraph.slot_types_default_out["_event_"] = ["logic/IF","events/sequence","events/log","events/counter"];
|
||||
LiteGraph.slot_types_default_out["array"] = ["basic/watch","basic/set_array","basic/array[]"];
|
||||
LiteGraph.slot_types_default_out["boolean"] = ["logic/IF","basic/watch","math/branch","math/gate"];
|
||||
LiteGraph.slot_types_default_out["number"] = ["basic/watch"
|
||||
,{node:"math/operation",properties:{OP:"*"},title:"A*B"}
|
||||
,{node:"math/operation",properties:{OP:"/"},title:"A/B"}
|
||||
,{node:"math/operation",properties:{OP:"+"},title:"A+B"}
|
||||
,{node:"math/operation",properties:{OP:"-"},title:"A-B"}
|
||||
,{node:"math/compare",outputs:[["A==B", "boolean"]],title:"A==B"}
|
||||
,{node:"math/compare",outputs:[["A>B", "boolean"]],title:"A>B"}
|
||||
,{node:"math/compare",outputs:[["A<B", "boolean"]],title:"A<B"}
|
||||
];
|
||||
LiteGraph.slot_types_default_out["object"] = ["basic/object_property","basic/keys",["string/toString","basic/watch"]];
|
||||
LiteGraph.slot_types_default_out["string"] = ["basic/watch","string/compare","string/concatenate","string/contains"];
|
||||
LiteGraph.slot_types_default_out["vec2"] = "math3d/vec2-to-xy";
|
||||
LiteGraph.slot_types_default_out["vec3"] = "math3d/vec3-to-xyz";
|
||||
LiteGraph.slot_types_default_out["vec4"] = "math3d/vec4-to-xyzw";
|
||||
|
||||
})(this);
|
||||
Reference in New Issue
Block a user