2022-11-24 05:04:14 +07:00
|
|
|
|
import Victor from "https://cdn.skypack.dev/victor@1.1.0";
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
("use strict");
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
|
|
|
|
/**
|
2025-01-11 15:23:54 +07:00
|
|
|
|
* @name Core
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Core of the module for creating graphs
|
|
|
|
|
*
|
|
|
|
|
* {@link https://git.mirzaev.sexy/mirzaev/graph.mjs}
|
|
|
|
|
*
|
|
|
|
|
* @class
|
|
|
|
|
* @public
|
|
|
|
|
*
|
|
|
|
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
2022-11-01 06:19:17 +07:00
|
|
|
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
2025-01-11 15:23:54 +07:00
|
|
|
|
*
|
|
|
|
|
* @example <caption>Creating a simple graph</caption>
|
|
|
|
|
* // Initializing the graph instance
|
|
|
|
|
* сonst instance = new graph(document.getElementById('graph'));
|
|
|
|
|
*
|
|
|
|
|
* // Writing settings of the graph instance
|
|
|
|
|
* instance.living = 3000;
|
|
|
|
|
* instance.camera = true;
|
|
|
|
|
* instance.operate = true;
|
|
|
|
|
*
|
|
|
|
|
* // Initializing nodes
|
|
|
|
|
* const bebra = instance.node(new node(document.getElementById('bebra')));
|
|
|
|
|
* const feet = instance.node(new node(document.getElementById('feet')));
|
|
|
|
|
*
|
|
|
|
|
* // Writing setting of every node
|
|
|
|
|
* instance.nodes.forEach((node) => { node.variables.get("inputs").type = "deg" });
|
|
|
|
|
*
|
|
|
|
|
* // Initializing edges
|
|
|
|
|
* instance.edge(new edge(feet, bebra));
|
2022-11-01 06:19:17 +07:00
|
|
|
|
*/
|
2025-01-11 15:23:54 +07:00
|
|
|
|
export default class core {
|
|
|
|
|
/**
|
|
|
|
|
* @name Shell
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Shell of nodes
|
|
|
|
|
*
|
|
|
|
|
* @type {HTMLElement}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#shell;
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Shell (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Shell of nodes
|
|
|
|
|
*
|
|
|
|
|
* @type {HTMLElement}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
2022-11-22 05:40:30 +07:00
|
|
|
|
get shell() {
|
|
|
|
|
return this.#shell;
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Left (x-coordinate)
|
|
|
|
|
*
|
|
|
|
|
* @type {number} Value in pixels
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#left;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Left (x-coordinate) (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for `this.#left`
|
|
|
|
|
*
|
|
|
|
|
* @type {number} Value in pixels
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get left() {
|
|
|
|
|
return this.#left;
|
2022-12-04 02:13:10 +07:00
|
|
|
|
}
|
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Top (y-coordinate)
|
|
|
|
|
*
|
|
|
|
|
* @type {number} Value in pixels
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#top;
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Top (y-coordinate) (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for `this.#top`
|
|
|
|
|
*
|
|
|
|
|
* @type {number} Value in pixels
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get top() {
|
|
|
|
|
return this.#top;
|
|
|
|
|
}
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Nodes
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Registry of nodes
|
|
|
|
|
*
|
|
|
|
|
* @type {Set}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#nodes = new Set();
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Nodes (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for `this.#nodes`
|
|
|
|
|
*
|
|
|
|
|
* @type {Set}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get nodes() {
|
|
|
|
|
return this.#nodes;
|
|
|
|
|
}
|
2022-11-22 18:31:26 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Edges
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Registry of edges
|
|
|
|
|
*
|
|
|
|
|
* @type {Set}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#edges = new Set();
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Edges (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for `this.#edges`
|
|
|
|
|
*
|
|
|
|
|
* @type {Set}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get edges() {
|
|
|
|
|
return this.#edges;
|
|
|
|
|
}
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Interactions
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Settings of interactions
|
|
|
|
|
*
|
|
|
|
|
* @type {object}
|
|
|
|
|
*
|
|
|
|
|
* @property {object} moving
|
|
|
|
|
*
|
|
|
|
|
* @property {object} moving.graph
|
|
|
|
|
* @property {boolean} moving.graph.active
|
|
|
|
|
* @property {object} moving.graph.inertion
|
|
|
|
|
* @property {boolean} moving.graph.inertion.active
|
|
|
|
|
*
|
|
|
|
|
* @property {object} moving.nodes
|
|
|
|
|
* @property {boolean} moving.nodes.active
|
|
|
|
|
* @property {object} moving.nodes.inertion
|
|
|
|
|
* @property {boolean} moving.nodes.inertion.active
|
|
|
|
|
*
|
|
|
|
|
* @property {object} pushing
|
|
|
|
|
*
|
|
|
|
|
* @property {boolean} pushing.active
|
|
|
|
|
* @property {object} pushing.iterations
|
|
|
|
|
* @property {number} pushing.iterations.from
|
|
|
|
|
* @property {number} pushing.iterations.to
|
|
|
|
|
*
|
|
|
|
|
* @property {object} pulling
|
|
|
|
|
*
|
|
|
|
|
* @property {boolean} pulling.active
|
|
|
|
|
* @property {object} pulling.iterations
|
|
|
|
|
* @property {number} pulling.iterations.from
|
|
|
|
|
* @property {number} pushing.iterations.to
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
interactions = {
|
|
|
|
|
moving: {
|
|
|
|
|
graph: {
|
2022-12-07 16:31:35 +07:00
|
|
|
|
active: true,
|
2025-01-11 15:23:54 +07:00
|
|
|
|
inertion: {
|
|
|
|
|
active: true
|
|
|
|
|
}
|
2022-12-04 02:13:10 +07:00
|
|
|
|
},
|
2025-01-11 15:23:54 +07:00
|
|
|
|
nodes: {
|
2022-12-11 14:19:54 +07:00
|
|
|
|
active: true,
|
2025-01-11 15:23:54 +07:00
|
|
|
|
inertion: {
|
|
|
|
|
active: true
|
2022-11-26 04:38:23 +07:00
|
|
|
|
}
|
2022-11-24 14:24:23 +07:00
|
|
|
|
}
|
2025-01-11 15:23:54 +07:00
|
|
|
|
},
|
|
|
|
|
pushing: {
|
|
|
|
|
active: true,
|
|
|
|
|
iterations: {
|
|
|
|
|
from: 0,
|
|
|
|
|
to: 100
|
|
|
|
|
},
|
|
|
|
|
cascade: {
|
|
|
|
|
depth: 3
|
2022-11-26 05:50:15 +07:00
|
|
|
|
}
|
2025-01-11 15:23:54 +07:00
|
|
|
|
},
|
|
|
|
|
pulling: {
|
|
|
|
|
active: true,
|
|
|
|
|
iterations: {
|
|
|
|
|
from: 0,
|
|
|
|
|
to: 100
|
|
|
|
|
},
|
|
|
|
|
cascade: {
|
|
|
|
|
depth: 3
|
2022-11-26 05:50:15 +07:00
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
}
|
2025-01-11 15:23:54 +07:00
|
|
|
|
};
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Living
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Interval to execute `this.move` to fix positioning errors
|
|
|
|
|
*
|
|
|
|
|
* @type {number} Value in milliseconds (`0` or `undefined` to disable)
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#living;
|
2022-11-26 04:38:23 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Living (set)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Setter for `this.#living`
|
|
|
|
|
* Reinitializes the `this.#processes.get('living')` process
|
|
|
|
|
*
|
|
|
|
|
* @param {number} value Interval value in milliseconds (`0` or `undefined` to disable)
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
set living(value) {
|
|
|
|
|
if (typeof value === "number" || typeof value === "undefined") {
|
|
|
|
|
// Validated required argument
|
|
|
|
|
|
|
|
|
|
// Writing value to the living property
|
|
|
|
|
this.#living = value;
|
|
|
|
|
|
|
|
|
|
// Deinitializing living process
|
|
|
|
|
clearInterval(this.#processes.get("living"));
|
|
|
|
|
|
|
|
|
|
// Initializing link to the instance
|
|
|
|
|
const instance = this;
|
|
|
|
|
|
|
|
|
|
// Initializing living process
|
|
|
|
|
if (typeof this.#living === "number" && this.#living !== 0)
|
|
|
|
|
this.#processes.set(
|
|
|
|
|
"living",
|
|
|
|
|
setInterval(() => {
|
|
|
|
|
for (const node of this.#nodes) {
|
|
|
|
|
// Iterating over nodes
|
|
|
|
|
|
|
|
|
|
// Processing pushings between nodes
|
|
|
|
|
node.push(
|
|
|
|
|
instance.interactions.pushing.cascade.depth,
|
|
|
|
|
instance.nodes
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Processing pullings between nodes
|
|
|
|
|
node.pull(instance.interactions.pulling.cascade.depth);
|
|
|
|
|
}
|
|
|
|
|
}, this.#living)
|
|
|
|
|
);
|
2022-11-22 05:40:30 +07:00
|
|
|
|
}
|
2025-01-11 15:23:54 +07:00
|
|
|
|
}
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Living (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for `this.#living`
|
|
|
|
|
*
|
|
|
|
|
* @type {number}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get living() {
|
|
|
|
|
return this.#living;
|
|
|
|
|
}
|
2022-12-10 17:41:11 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Camera
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Allowed to moving the shell (like an abstract camera)?
|
|
|
|
|
*
|
|
|
|
|
* @type {boolean}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#camera = false;
|
2022-12-10 17:41:11 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Camera (set)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Setter for `this.#camera`
|
|
|
|
|
* Reinitializes camera moving processes
|
|
|
|
|
*
|
|
|
|
|
* @param {boolean} value Activate moving the shell (like an abstract camera)?
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
set camera(value) {
|
|
|
|
|
if (typeof value === "boolean") {
|
|
|
|
|
// Validated required argument
|
|
|
|
|
|
|
|
|
|
// Writing value to the camera property
|
|
|
|
|
this.#camera = value;
|
|
|
|
|
|
|
|
|
|
if (this.#camera) {
|
|
|
|
|
// Activated moving the shell (like an abstract camera)
|
|
|
|
|
|
|
|
|
|
// Deinitializing the "dragstart" and the "selectstart" event listeners
|
|
|
|
|
this.#shell.ondragstart = this.#shell.onselectstart = null;
|
|
|
|
|
|
|
|
|
|
// Initializing link to the instance
|
|
|
|
|
const instance = this;
|
|
|
|
|
|
|
|
|
|
// Disconnecting deprecated event listener for starting moving the shell element
|
|
|
|
|
document.removeEventListener(
|
|
|
|
|
"mousedown",
|
|
|
|
|
this.#listeners.get("camera.start")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Initializing event listener for starting moving the shell
|
|
|
|
|
this.#listeners.set("camera.start", (start) => {
|
|
|
|
|
// Started moving the shell
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
start.target === instance.#shell ||
|
|
|
|
|
!instance.#shell.contains(event.target)
|
|
|
|
|
) {
|
|
|
|
|
// The mouse cursor is down over the shell element or its ascendant element
|
|
|
|
|
|
|
|
|
|
// Initializing coordinates of the shell element
|
|
|
|
|
const left = start.pageX - instance.#shell.offsetLeft + pageXOffset;
|
|
|
|
|
const top = start.pageY - instance.#shell.offsetTop + pageYOffset;
|
|
|
|
|
|
|
|
|
|
// Disconnecting deprecated event listener for moving the shell element
|
|
|
|
|
document.removeEventListener(
|
|
|
|
|
"mousemove",
|
|
|
|
|
instance.#listeners.get("camera.moving")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Initializing event listener for moving the shell element
|
|
|
|
|
this.#listeners.set("camera.moving", (moving) => {
|
|
|
|
|
// Moving the shell
|
|
|
|
|
|
|
|
|
|
// Writing x-coordinate into the property
|
|
|
|
|
instance.#left = moving.pageX - left;
|
|
|
|
|
|
|
|
|
|
// Writing y-coordinate into the property
|
|
|
|
|
instance.#top = moving.pageY - top;
|
|
|
|
|
|
|
|
|
|
// Writing x-coordinate into the HTML-element attribute
|
|
|
|
|
if (instance.variables.get("left").active)
|
|
|
|
|
instance.#shell.style.setProperty(
|
|
|
|
|
"--graph-shell-left",
|
|
|
|
|
instance.#left + instance.variables.get("left").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing y-coordinate into the HTML-element attribute
|
|
|
|
|
if (instance.variables.get("top").active)
|
|
|
|
|
instance.#shell.style.setProperty(
|
|
|
|
|
"--graph-shell-top",
|
|
|
|
|
instance.#top + instance.variables.get("top").type
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Connecting event listener for moving the shell element
|
|
|
|
|
document.addEventListener(
|
|
|
|
|
"mousemove",
|
|
|
|
|
instance.#listeners.get("camera.moving")
|
|
|
|
|
);
|
2022-12-10 17:41:11 +07:00
|
|
|
|
}
|
2025-01-11 15:23:54 +07:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Connecting event listener for starting moving the shell element
|
|
|
|
|
document.addEventListener(
|
|
|
|
|
"mousedown",
|
|
|
|
|
this.#listeners.get("camera.start")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Disconnecting deprecated event listener for ending moving the shell element
|
|
|
|
|
document.removeEventListener(
|
|
|
|
|
"mouseup",
|
|
|
|
|
this.#listeners.get("camera.end")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Initializing event listener for ending moving the shell
|
|
|
|
|
this.#listeners.set("camera.end", (end) => {
|
|
|
|
|
// Ended moving the shell
|
|
|
|
|
|
|
|
|
|
// Disconnecting event listener for moving the shell element
|
|
|
|
|
document.removeEventListener(
|
|
|
|
|
"mousemove",
|
|
|
|
|
instance.#listeners.get("camera.moving")
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Connecting event listener for ending moving the shell element
|
|
|
|
|
document.addEventListener("mouseup", this.#listeners.get("camera.end"));
|
|
|
|
|
} else {
|
|
|
|
|
// Deactivated moving the shell (like an abstract camera)
|
|
|
|
|
|
|
|
|
|
// Disconnecting event listener for starting moving the shell element
|
|
|
|
|
document.removeEventListener(
|
|
|
|
|
"mousedown",
|
|
|
|
|
this.#listeners.get("camera.start")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Disconnecting event listener for ending moving the shell element
|
|
|
|
|
document.removeEventListener(
|
|
|
|
|
"mouseup",
|
|
|
|
|
this.#listeners.get("camera.end")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Disconnecting event listener for moving the shell element
|
|
|
|
|
document.removeEventListener(
|
|
|
|
|
"mousemove",
|
|
|
|
|
this.#listeners.get("camera.moving")
|
|
|
|
|
);
|
2022-12-04 14:15:17 +07:00
|
|
|
|
}
|
2022-11-22 05:40:30 +07:00
|
|
|
|
}
|
2025-01-11 15:23:54 +07:00
|
|
|
|
}
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Camera (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for `this.#camera`
|
|
|
|
|
*
|
|
|
|
|
* @type {boolean}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get camera() {
|
|
|
|
|
return this.#camera;
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Operate
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Allowed to operate with nodes?
|
|
|
|
|
*
|
|
|
|
|
* @type {boolean}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#operate = false;
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Operate (set)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Setter for `this.#operate`
|
|
|
|
|
* Reinitializes operating with nodes processes
|
|
|
|
|
*
|
|
|
|
|
* @param {boolean} value Allowed to operate with nodes?
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
set operate(value) {
|
|
|
|
|
if (typeof value === "boolean") {
|
|
|
|
|
// Validated required argument
|
2022-12-05 08:13:55 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Writing value to the operate property
|
|
|
|
|
this.#operate = value;
|
2022-12-05 08:13:55 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
if (this.#operate) {
|
|
|
|
|
// Allowed to operate with nodes
|
2022-12-05 08:13:55 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
for (const node of this.#nodes) {
|
|
|
|
|
// Iterating over nodes
|
2022-12-05 08:13:55 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Activating the node
|
|
|
|
|
node.activate(this);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Not allowed to operate with nodes
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
for (const node of this.#nodes) {
|
|
|
|
|
// Iterating over nodes
|
2022-12-05 08:13:55 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Deactivating the node
|
|
|
|
|
node.deactivate();
|
|
|
|
|
}
|
2022-12-05 08:13:55 +07:00
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
}
|
2025-01-11 15:23:54 +07:00
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Operate (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for `this.#operate`
|
|
|
|
|
*
|
|
|
|
|
* @type {boolean}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get operate() {
|
|
|
|
|
return this.#operate;
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Variables
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* The registry of variables that will be added to the `style` attribute of the HTML-elements
|
|
|
|
|
*
|
|
|
|
|
* Editing DOM, including changing argument values,
|
|
|
|
|
* has a significant impact on performance - try to use as few variables as possible
|
|
|
|
|
*
|
|
|
|
|
* @example Write in format without "--graph-shell-"
|
|
|
|
|
* `["left", true]` will be `style="--graph-shell-left: 180px;"`
|
|
|
|
|
*
|
|
|
|
|
* These variables are also stored in the object to avoid reading DOM: "left", "top"
|
|
|
|
|
*
|
|
|
|
|
* @type {Map}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
variables = new Map([
|
|
|
|
|
["left", { active: true, type: "px" }],
|
|
|
|
|
["top", { active: true, type: "px" }]
|
|
|
|
|
]);
|
2022-12-11 14:19:54 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Listeners
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Registry of event listeners
|
|
|
|
|
*
|
|
|
|
|
* @type {Map}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#listeners = new Map();
|
2022-12-11 14:19:54 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Processes
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Registry of running processes
|
|
|
|
|
*
|
|
|
|
|
* @type {Map}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#processes = new Map();
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Constructor
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Initialize a graph instance
|
|
|
|
|
*
|
|
|
|
|
* @param {HTMLElement} shell The shell element
|
|
|
|
|
**/
|
|
|
|
|
constructor(shell) {
|
|
|
|
|
if (shell instanceof HTMLElement) {
|
|
|
|
|
// Initialized the shell
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Writing the shell
|
|
|
|
|
this.#shell = shell;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Deinitializing the "dragstart" and the "selectstart" event listeners
|
|
|
|
|
this.#shell.ondragstart = this.#shell.onselectstart = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Node
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Registrate the node in the system
|
|
|
|
|
*
|
|
|
|
|
* @param {node} target The node instance
|
|
|
|
|
*
|
|
|
|
|
* @return {(node|false)} The node, if initialized
|
|
|
|
|
**/
|
|
|
|
|
node(target) {
|
|
|
|
|
if (target instanceof node) {
|
|
|
|
|
// Validated required arguments
|
|
|
|
|
|
|
|
|
|
// Moving the node element
|
|
|
|
|
target.move(
|
|
|
|
|
this.#shell.offsetWidth / 2 -
|
|
|
|
|
target.augmented / 2 +
|
|
|
|
|
(0.5 - Math.random()) * 500,
|
|
|
|
|
this.#shell.offsetHeight / 2 -
|
|
|
|
|
target.augmented / 2 +
|
|
|
|
|
(0.5 - Math.random()) * 500
|
|
|
|
|
);
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Writing into the nodes registry
|
|
|
|
|
this.#nodes.add(target);
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Activating the node
|
|
|
|
|
if (this.#operate) target.activate(this);
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Exit (success)
|
|
|
|
|
return target;
|
2022-11-22 18:31:26 +07:00
|
|
|
|
}
|
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Exit (fail)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-12-04 14:15:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Edge
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Registrate the edge in the system
|
|
|
|
|
*
|
|
|
|
|
* @param {edge} target The edge instance
|
|
|
|
|
*
|
|
|
|
|
* @return {(edge|false)} The edge, if initialized
|
|
|
|
|
**/
|
|
|
|
|
edge(target) {
|
|
|
|
|
if (target instanceof edge) {
|
|
|
|
|
// Validated required attributes
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
this.#shell instanceof HTMLElement &&
|
|
|
|
|
target.shell instanceof SVGElement
|
|
|
|
|
) {
|
|
|
|
|
// Initialized shell elements
|
|
|
|
|
|
|
|
|
|
// Writing the edge into the shell element
|
|
|
|
|
this.#shell.appendChild(target.shell);
|
|
|
|
|
|
|
|
|
|
// Writing into the edges registry
|
|
|
|
|
this.#edges.add(target);
|
|
|
|
|
|
|
|
|
|
// Exit (success)
|
|
|
|
|
return target;
|
|
|
|
|
}
|
2022-12-04 14:15:17 +07:00
|
|
|
|
}
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Exit (fail)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Node
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Node of the module for creating graphs
|
|
|
|
|
*
|
|
|
|
|
* {@link https://git.mirzaev.sexy/mirzaev/graph.mjs}
|
|
|
|
|
*
|
|
|
|
|
* @class
|
|
|
|
|
* @public
|
|
|
|
|
*
|
|
|
|
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
|
|
|
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
|
|
|
|
*
|
|
|
|
|
* @example <caption>Creating a node</caption>
|
|
|
|
|
* const node = new node(document.getElementById('my_node'));
|
|
|
|
|
*/
|
|
|
|
|
export class node {
|
|
|
|
|
/**
|
|
|
|
|
* @name Shell
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Shell of the node
|
|
|
|
|
*
|
|
|
|
|
* @type {HTMLElement}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#shell;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Shell (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for shell of the node
|
|
|
|
|
*
|
|
|
|
|
* @type {HTMLElement}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get shell() {
|
|
|
|
|
return this.#shell;
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Left (x-coordinate)
|
|
|
|
|
*
|
|
|
|
|
* @type {number} Value in pixels
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#left;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Left (x-coordinate) (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for `this.#left`
|
|
|
|
|
*
|
|
|
|
|
* @type {number} Value in pixels
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get left() {
|
|
|
|
|
return this.#left || 0;
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Top (y-coordinate)
|
|
|
|
|
*
|
|
|
|
|
* @type {number} Value in pixels
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#top;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Top (y-coordinate) (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for `this.#top`
|
|
|
|
|
*
|
|
|
|
|
* @type {number} Value in pixels
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get top() {
|
|
|
|
|
return this.#top || 0;
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Movement
|
|
|
|
|
*
|
|
|
|
|
* @type {object}
|
|
|
|
|
*
|
|
|
|
|
* @property {string} status Status of the movement ("moving", "completed")
|
|
|
|
|
*
|
|
|
|
|
* @property {object} from
|
|
|
|
|
* @property {number} from.left Left-coordinate of the start of the movement
|
|
|
|
|
* @property {number} from.top Top-coordinate of the start of the movement
|
|
|
|
|
*
|
|
|
|
|
* @property {object} to
|
|
|
|
|
* @property {number} to.left Left-coordinate of the end of the movement
|
|
|
|
|
* @property {number} to.top Top-coordinate of the end of the movement
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#movement = {
|
|
|
|
|
status,
|
|
|
|
|
from: { left: 0, top: 0 },
|
|
|
|
|
to: { left: 0, top: 0 }
|
|
|
|
|
};
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Inputs
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* The regitry of input edges
|
|
|
|
|
*
|
|
|
|
|
* @type {Set}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#inputs = new Set();
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Inputs (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for the regitry of input edges
|
|
|
|
|
*
|
|
|
|
|
* @type {Set}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get inputs() {
|
|
|
|
|
return this.#inputs;
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Outputs
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* The regitry of output edges
|
|
|
|
|
*
|
|
|
|
|
* @type {Set}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#outputs = new Set();
|
2022-12-11 15:37:49 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Outputs (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for the regitry of output edges
|
|
|
|
|
*
|
|
|
|
|
* @type {Set}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get outputs() {
|
|
|
|
|
return this.#outputs;
|
|
|
|
|
}
|
2022-12-11 15:37:49 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Button
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Identifier of the mouse button that will perform the movement.
|
|
|
|
|
*
|
|
|
|
|
* 0: Main button pressed, usually the left button or the un-initialized state
|
|
|
|
|
* 1: Auxiliary button pressed, usually the wheel button or the middle button (if present)
|
|
|
|
|
* 2: Secondary button pressed, usually the right button
|
|
|
|
|
* 3: Fourth button, typically the Browser Back button
|
|
|
|
|
* 4: Fifth button, typically the Browser Forward button
|
|
|
|
|
*
|
|
|
|
|
* {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button}
|
|
|
|
|
*
|
|
|
|
|
* @type {number}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
button = 0;
|
2022-12-11 15:37:49 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Size
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* The HTML-element initial size in pixels
|
|
|
|
|
*
|
|
|
|
|
* @type {number}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
size = 100;
|
2022-12-11 15:37:49 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Augmented size
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* The HTML-element augmented size in pixels
|
|
|
|
|
*
|
|
|
|
|
* Will be generated at runtime by formula:
|
|
|
|
|
* `this.size + (this.addition * this.#inputs.size - this.subtraction * this.#outputs.size)`
|
|
|
|
|
*
|
|
|
|
|
* @type {number}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#augmented;
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Augmented size (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for the augmented size
|
|
|
|
|
*
|
|
|
|
|
* The HTML-element augmented size in pixels
|
|
|
|
|
*
|
|
|
|
|
* Will be generated at runtime by formula:
|
|
|
|
|
* `this.size + (this.addition * this.#inputs.size - this.subtraction * this.#outputs.size)`
|
|
|
|
|
*
|
|
|
|
|
* @type {number}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get augmented() {
|
|
|
|
|
return this.#augmented || this.size || 0;
|
|
|
|
|
}
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Radius (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for the augmented radius
|
|
|
|
|
*
|
|
|
|
|
* @type {number}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get radius() {
|
|
|
|
|
return this.augmented / 2 || 0;
|
|
|
|
|
}
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Center (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for the node shell HTML-element center coordinates values
|
|
|
|
|
*
|
|
|
|
|
* @type {object}
|
|
|
|
|
*
|
|
|
|
|
* @property {number} left Left-coordinate from center of the shell HTML-element
|
|
|
|
|
* @property {number} top Top-coordinate from center of the shell HTML-element
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get center() {
|
|
|
|
|
return {
|
|
|
|
|
left: this.radius + this.left,
|
|
|
|
|
top: this.radius + this.top
|
|
|
|
|
};
|
|
|
|
|
}
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Addition
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* The value of diameter addition
|
|
|
|
|
*
|
|
|
|
|
* @type {number}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
addition = 20;
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Subtraction
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* The value of diameter subtraction
|
|
|
|
|
*
|
|
|
|
|
* @type {number}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
subtraction = 5;
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Interactions
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Types of the node interactions
|
|
|
|
|
*
|
|
|
|
|
* @type {object}
|
|
|
|
|
*
|
|
|
|
|
* @property {object} movement Movement of the node
|
|
|
|
|
* @property {boolean} movement.active Is movement enabled?
|
|
|
|
|
* @property {object} movement.synchronization
|
|
|
|
|
* @property {number} movement.synchronization.interval
|
|
|
|
|
*
|
|
|
|
|
* @property {object} pushing Pushing with nodes
|
|
|
|
|
* @property {boolean} pushing.active Is pushing with nodes enabled?
|
|
|
|
|
* @property {number} pushing.distance
|
|
|
|
|
*
|
|
|
|
|
* @property {object} pulling Pulling with nodes
|
|
|
|
|
* @property {boolean} pulling.active Is pulling with nodes enabled?
|
|
|
|
|
* @property {number} pulling.distance
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
interactions = {
|
|
|
|
|
movement: {
|
|
|
|
|
active: true,
|
|
|
|
|
synchronization: {
|
|
|
|
|
interval: 20
|
2022-12-03 12:05:28 +07:00
|
|
|
|
}
|
2025-01-11 15:23:54 +07:00
|
|
|
|
},
|
|
|
|
|
pushing: {
|
|
|
|
|
active: true,
|
|
|
|
|
distance: 100
|
|
|
|
|
},
|
|
|
|
|
pulling: {
|
|
|
|
|
active: true,
|
|
|
|
|
distance: 130
|
2022-11-22 06:43:57 +07:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
/**
|
2025-01-11 15:23:54 +07:00
|
|
|
|
* @name Variables
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* The registry of variables that will be added to the `style` attribute of the HTML-elements
|
|
|
|
|
*
|
|
|
|
|
* Editing DOM, including changing argument values,
|
|
|
|
|
* has a significant impact on performance - try to use as few variables as possible
|
|
|
|
|
*
|
|
|
|
|
* @example Write in format without "--graph-node-"
|
|
|
|
|
* `["left", true]` will be `style="--graph-node-left: 180px;"`
|
2022-12-04 02:13:10 +07:00
|
|
|
|
*
|
2025-01-11 15:23:54 +07:00
|
|
|
|
* These variables are also stored in the object to avoid reading DOM: "left", "top"
|
|
|
|
|
*
|
|
|
|
|
* @type {Map}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
2022-12-04 02:13:10 +07:00
|
|
|
|
*/
|
2025-01-11 15:23:54 +07:00
|
|
|
|
variables = new Map([
|
|
|
|
|
["left", { active: false, type: "px" }],
|
|
|
|
|
["top", { active: false, type: "px" }],
|
|
|
|
|
["from-left", { active: true, type: "px" }],
|
|
|
|
|
["from-top", { active: true, type: "px" }],
|
|
|
|
|
["to-left", { active: true, type: "px" }],
|
|
|
|
|
["to-top", { active: true, type: "px" }],
|
|
|
|
|
["layer", { active: true, type: "" }],
|
|
|
|
|
["size", { active: false, type: "px" }],
|
|
|
|
|
["augmented", { active: true, type: "px" }],
|
|
|
|
|
// ["addition", false],
|
|
|
|
|
// ["subtraction", false],
|
|
|
|
|
["inputs", { active: true, type: "" }],
|
|
|
|
|
["outputs", { active: false, type: "" }]
|
|
|
|
|
]);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Listeners
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Registry of event listeners
|
|
|
|
|
*
|
|
|
|
|
* @type {Map}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#listeners = new Map();
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Processes
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Registry of running processes
|
|
|
|
|
*
|
|
|
|
|
* @type {Map}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#processes = new Map();
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Constructor
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Initialize a node instance
|
|
|
|
|
*
|
|
|
|
|
* @param {HTMLElement} shell The node element
|
|
|
|
|
**/
|
|
|
|
|
constructor(shell) {
|
|
|
|
|
if (shell instanceof HTMLElement) {
|
|
|
|
|
// Validated required arguments
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Writing the HTML-element
|
|
|
|
|
this.#shell = shell;
|
2022-12-03 12:29:29 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Deinitializing the "dragstart" and the "selectstart" event listeners
|
|
|
|
|
this.#shell.ondragstart = this.#shell.onselectstart = null;
|
2022-12-03 12:29:29 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Initializing augmented size variable
|
|
|
|
|
this.augment();
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Initializing edges variables
|
|
|
|
|
this.edges();
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Augment
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Calculate and write augmented size into the node property and argument of the HTML-element
|
|
|
|
|
**/
|
|
|
|
|
augment() {
|
|
|
|
|
// Calculating and writing augmented size into the node property
|
|
|
|
|
this.#augmented =
|
|
|
|
|
this.size +
|
|
|
|
|
(this.addition * this.#inputs.size -
|
|
|
|
|
this.subtraction * this.#outputs.size);
|
|
|
|
|
|
|
|
|
|
// Writing augmented size into the `style` argument of the HTML-element
|
|
|
|
|
if (this.variables.get("augmented").active)
|
|
|
|
|
this.#shell?.style.setProperty(
|
|
|
|
|
"--graph-node-augmented",
|
|
|
|
|
this.#augmented + this.variables.get("augmented").type
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Edges
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Write inputs and outputs edges into the `style` argument of the HTML-element
|
|
|
|
|
**/
|
|
|
|
|
edges() {
|
|
|
|
|
// Writing inputs edges into the `style` argument of the HTML-element
|
|
|
|
|
if (this.variables.get("inputs").active)
|
|
|
|
|
this.#shell?.style.setProperty(
|
|
|
|
|
"--graph-node-inputs",
|
|
|
|
|
this.#inputs.size + this.variables.get("inputs").type
|
|
|
|
|
);
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Writing outputs edges into the `style` argument of the HTML-element
|
|
|
|
|
if (this.variables.get("outputs").active)
|
|
|
|
|
this.#shell?.style.setProperty(
|
|
|
|
|
"--graph-node-outputs",
|
|
|
|
|
this.#outputs.size + this.variables.get("outputs").type
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-12-03 12:29:29 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Activate
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Activate the node for operating relative to `shell`
|
|
|
|
|
*
|
|
|
|
|
* @param {core} graph The graph instance
|
|
|
|
|
**/
|
|
|
|
|
activate(graph) {
|
|
|
|
|
if (graph instanceof core) {
|
|
|
|
|
// Validated required arguments
|
|
|
|
|
|
|
|
|
|
if (this.#shell instanceof HTMLElement) {
|
|
|
|
|
// Initialized the shell element
|
|
|
|
|
|
|
|
|
|
// Initializing link to the instance
|
|
|
|
|
const instance = this;
|
|
|
|
|
|
|
|
|
|
// Disconnecting deprecated event listener for starting moving the node
|
|
|
|
|
this.#shell.removeEventListener(
|
|
|
|
|
"mousedown",
|
|
|
|
|
this.#listeners.get("movement.start")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Initializing event listener for starting moving the node
|
|
|
|
|
this.#listeners.set("movement.start", (start) => {
|
|
|
|
|
// Started moving
|
|
|
|
|
|
|
|
|
|
if (start.type === "touchstart" || start.button === instance.button) {
|
|
|
|
|
// Pressing with a finger or a mouse button specified in `instance.button` by the user (mouse, touch)
|
|
|
|
|
|
|
|
|
|
// Deinitializing process for checking that the movement was completed
|
|
|
|
|
if (this.#processes.has("movement"))
|
|
|
|
|
clearInterval(this.#processes.get("movement"));
|
|
|
|
|
|
|
|
|
|
// Writing "left" coordinate into the property
|
|
|
|
|
this.#left = Math.round(this.#shell.offsetLeft);
|
|
|
|
|
|
|
|
|
|
// Writing "top" coordinate into the property
|
|
|
|
|
this.#top = Math.round(this.#shell.offsetTop);
|
|
|
|
|
|
|
|
|
|
// Writing "left" coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("left").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-left",
|
|
|
|
|
this.#left + this.variables.get("left").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing "top" coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("top").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-top",
|
|
|
|
|
this.#top + this.variables.get("top").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing "from-left" coordinate into the property
|
|
|
|
|
this.#movement.from.left = this.#left;
|
|
|
|
|
|
|
|
|
|
// Writing "from-top" coordinate into the property
|
|
|
|
|
this.#movement.from.top = this.#top;
|
|
|
|
|
|
|
|
|
|
// Writing "from-left" coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("from-left").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-from-left",
|
|
|
|
|
this.#movement.from.left + this.variables.get("from-left").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing "from-top" coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("from-top").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-from-top",
|
|
|
|
|
this.#movement.from.top + this.variables.get("from-top").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing "to-left" coordinate into the property
|
|
|
|
|
this.#movement.to.left = this.#left;
|
|
|
|
|
|
|
|
|
|
// Writing "to-top" coordinate into the property
|
|
|
|
|
this.#movement.to.top = this.#top;
|
|
|
|
|
|
|
|
|
|
// Writing "to-left" coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("to-left").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-to-left",
|
|
|
|
|
this.#movement.to.left + this.variables.get("to-left").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing "to-top" coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("to-top").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-to-top",
|
|
|
|
|
this.#movement.to.top + this.variables.get("to-top").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing z-coordinate into the HTML-element attribute
|
|
|
|
|
if (instance.variables.get("layer").active)
|
|
|
|
|
instance.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-layer",
|
|
|
|
|
50 + instance.variables.get("layer").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Initializing coordinates
|
|
|
|
|
const left = start.pageX - instance.shell.offsetLeft + pageXOffset;
|
|
|
|
|
const top = start.pageY - instance.shell.offsetTop + pageYOffset;
|
|
|
|
|
|
|
|
|
|
// Disconnecting deprecated event listener for moving the node
|
|
|
|
|
document.removeEventListener(
|
|
|
|
|
"mousemove",
|
|
|
|
|
instance.#listeners.get("movement")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Initializing event listener for moving the node
|
|
|
|
|
instance.#listeners.set("movement", (moving) => {
|
|
|
|
|
// Started moving
|
|
|
|
|
|
|
|
|
|
// Moving the node
|
|
|
|
|
instance.move(moving.pageX - left, moving.pageY - top);
|
|
|
|
|
|
|
|
|
|
// Processing pushings by the node
|
|
|
|
|
instance.push(
|
|
|
|
|
graph.interactions.pushing.cascade.depth,
|
|
|
|
|
graph.nodes
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Processing pullings by the node
|
|
|
|
|
instance.pull(graph.interactions.pulling.cascade.depth);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Connecting event listener for moving the node
|
|
|
|
|
document.addEventListener(
|
|
|
|
|
"mousemove",
|
|
|
|
|
instance.#listeners.get("movement")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Disconnecting deprecated event listener for ending moving the node
|
|
|
|
|
document.removeEventListener(
|
|
|
|
|
"mouseup",
|
|
|
|
|
instance.#listeners.get("movement.end")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Initializing event listener for ending movement the node
|
|
|
|
|
instance.#listeners.set("movement.end", (end) => {
|
|
|
|
|
// Ended movement
|
|
|
|
|
|
|
|
|
|
// Disconnecting event listener for moving the node
|
|
|
|
|
document.removeEventListener(
|
|
|
|
|
"mousemove",
|
|
|
|
|
instance.#listeners.get("movement")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Disconnecting event listener for ending moving the node
|
|
|
|
|
document.removeEventListener(
|
|
|
|
|
"mouseup",
|
|
|
|
|
instance.#listeners.get("movement.end")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing status of the movement
|
|
|
|
|
this.#movement.status = "completed";
|
|
|
|
|
|
|
|
|
|
// Writing z-coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("layer").active)
|
|
|
|
|
instance.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-layer",
|
|
|
|
|
0 + this.variables.get("layer").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Dispatching event: "node.movement.ended"
|
|
|
|
|
instance.#shell.dispatchEvent(
|
|
|
|
|
new CustomEvent("graph.node.movement.ended", {
|
|
|
|
|
detail: {
|
|
|
|
|
node: instance
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Connecting event listener for ending moving the node
|
|
|
|
|
document.addEventListener(
|
|
|
|
|
"mouseup",
|
|
|
|
|
instance.#listeners.get("movement.end")
|
|
|
|
|
);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
}
|
2025-01-11 15:23:54 +07:00
|
|
|
|
});
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Connecting event listener for starting moving the node
|
|
|
|
|
this.shell.addEventListener(
|
|
|
|
|
"mousedown",
|
|
|
|
|
this.#listeners.get("movement.start")
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Deactivate
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Deactivate the node for operating relative to `shell`
|
|
|
|
|
**/
|
|
|
|
|
deactivate() {
|
|
|
|
|
// Writing z-coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("layer").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-layer",
|
|
|
|
|
500 + this.variables.get("layer").type
|
|
|
|
|
);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Disconnecting event listener for starting moving the node
|
|
|
|
|
this.#shell.removeEventListener(
|
|
|
|
|
"mousedown",
|
|
|
|
|
this.#listeners.get("moving.start")
|
|
|
|
|
);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Disconnecting event listener for moving the node
|
|
|
|
|
document.removeEventListener("mousemove", this.#listeners.get("moving"));
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Disconnecting event listener for ending moving the node
|
|
|
|
|
document.removeEventListener("mouseup", this.#listeners.get("moving.end"));
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Move
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Move the node and handle interactions with other nodes
|
|
|
|
|
*
|
|
|
|
|
* @param {number} left Offset from the left (px)
|
|
|
|
|
* @param {number} top Offset from the top (px)
|
|
|
|
|
**/
|
|
|
|
|
async move(left, top) {
|
|
|
|
|
if (this.interactions.movement.active) {
|
|
|
|
|
// Activated moving
|
|
|
|
|
|
|
|
|
|
if (typeof left === "number" && typeof top === "number") {
|
|
|
|
|
// Received coordinates arguments
|
|
|
|
|
|
|
|
|
|
// Writing "left" coordinate into the property
|
|
|
|
|
this.#left = Math.round(left);
|
|
|
|
|
|
|
|
|
|
// Writing "top" coordinate into the property
|
|
|
|
|
this.#top = Math.round(top);
|
|
|
|
|
|
|
|
|
|
// Writing "left" coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("left").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-left",
|
|
|
|
|
this.#left + this.variables.get("left").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing "top" coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("top").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-top",
|
|
|
|
|
this.#top + this.variables.get("top").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (this.#movement.status !== "completed") {
|
|
|
|
|
// Not completed the movement
|
|
|
|
|
|
|
|
|
|
// Initializing CSS-class for movement animation
|
|
|
|
|
if (!this.#shell.classList.contains("movement"))
|
|
|
|
|
this.#shell.classList.add("movement");
|
|
|
|
|
|
|
|
|
|
// Deinitializing deprecated process for checking that the movement was completed
|
|
|
|
|
if (this.#processes.has("movement"))
|
|
|
|
|
clearInterval(this.#processes.get("movement"));
|
|
|
|
|
|
|
|
|
|
// Initializing process for checking that the movement was completed
|
|
|
|
|
this.#processes.set(
|
|
|
|
|
"movement",
|
|
|
|
|
setInterval(() => {
|
|
|
|
|
if (
|
|
|
|
|
this.#shell.offsetLeft === this.#movement.to.left &&
|
|
|
|
|
this.#shell.offsetTop === this.#movement.to.top
|
|
|
|
|
) {
|
|
|
|
|
// Completed the movement
|
|
|
|
|
|
|
|
|
|
// Deinitializing CSS-class for movement animation (reset)
|
|
|
|
|
this.#shell.classList.remove("movement");
|
|
|
|
|
|
|
|
|
|
// Writing "from" coordinates into the property
|
|
|
|
|
this.#movement.from = this.#movement.to;
|
|
|
|
|
|
|
|
|
|
// Writing "from-left" coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("from-left").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-from-left",
|
|
|
|
|
this.#movement.from.left +
|
|
|
|
|
this.variables.get("from-left").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing "from-top" coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("from-top").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-from-top",
|
|
|
|
|
this.#movement.from.top +
|
|
|
|
|
this.variables.get("from-top").type
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing statuf of the movement
|
|
|
|
|
this.#movement.status = "completed";
|
|
|
|
|
|
|
|
|
|
// Deinitializing process for checking that the movement was completed
|
|
|
|
|
clearInterval(this.#processes.get("movement"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Synchronize the node edges with the node
|
|
|
|
|
this.synchronization();
|
|
|
|
|
}, this.interactions.movement.synchronization.interval)
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Writing status of the movement
|
|
|
|
|
this.#movement.status = "moving";
|
2022-11-26 22:41:43 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Writing "to-left" coordinate into the property
|
|
|
|
|
this.#movement.to.left = this.left;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Writing "to-top" coordinate into the property
|
|
|
|
|
this.#movement.to.top = this.top;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Writing "to-left" coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("to-left").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-to-left",
|
|
|
|
|
this.#movement.to.left + this.variables.get("to-left").type
|
|
|
|
|
);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Writing "to-top" coordinate into the HTML-element attribute
|
|
|
|
|
if (this.variables.get("to-top").active)
|
|
|
|
|
this.#shell.style.setProperty(
|
|
|
|
|
"--graph-node-to-top",
|
|
|
|
|
this.#movement.to.top + this.variables.get("to-top").type
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-12-11 14:19:54 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Synchronize the node edges with the node
|
|
|
|
|
this.synchronization();
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Push
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Push `nodes` from the node
|
|
|
|
|
*
|
|
|
|
|
* @param {number} [depth=1] Amount of cascade reactions (the value will be reduced to 0 in recursion)
|
|
|
|
|
* @param {(Set|Array|undefined)} [nodes=undefined] Nodes for processing (otherwise `this.inputs` + `this.outputs`)
|
|
|
|
|
*/
|
|
|
|
|
async push(depth = 1, nodes) {
|
|
|
|
|
if (
|
|
|
|
|
typeof depth === "number" &&
|
|
|
|
|
(nodes instanceof Set ||
|
|
|
|
|
nodes instanceof Array ||
|
|
|
|
|
typeof nodes === "undefined")
|
|
|
|
|
) {
|
|
|
|
|
// Validated required argument
|
|
|
|
|
|
|
|
|
|
if (--depth >= 0) {
|
|
|
|
|
// Not reached iterations limit
|
|
|
|
|
|
|
|
|
|
if (this.interactions.pushing.active) {
|
|
|
|
|
// Activated pushing
|
|
|
|
|
|
|
|
|
|
for (const node of (nodes?.size > 0 || nodes?.length > 0
|
|
|
|
|
? [...nodes]
|
|
|
|
|
: [...this.inputs]
|
|
|
|
|
.map((edge) => edge.from)
|
|
|
|
|
.concat([...this.outputs].map((edge) => edge.to))
|
|
|
|
|
).filter((node) => node !== this)) {
|
|
|
|
|
// Iterating over nodes
|
|
|
|
|
|
|
|
|
|
if (node.interactions.pushing.active) {
|
|
|
|
|
// Activated pushing
|
|
|
|
|
|
|
|
|
|
// Initializing the vector between nodes
|
|
|
|
|
const between = new Victor(
|
|
|
|
|
node.center.left - this.center.left,
|
|
|
|
|
node.center.top - this.center.top
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Calculation of the arithmetic mean of nodes pushing distance
|
|
|
|
|
const distance =
|
|
|
|
|
(node.interactions.pushing.distance +
|
|
|
|
|
this.interactions.pushing.distance) /
|
|
|
|
|
2;
|
|
|
|
|
|
|
|
|
|
// Calculating difference between needed distance and actial distance
|
|
|
|
|
const difference =
|
|
|
|
|
node.radius + this.radius + distance - between.length();
|
|
|
|
|
|
|
|
|
|
if (difference > 0) {
|
|
|
|
|
// The node have not overcome the pushing distance
|
|
|
|
|
|
|
|
|
|
// Initializing vector of pushing distance
|
|
|
|
|
const pushing = new Victor(difference, difference);
|
|
|
|
|
|
|
|
|
|
// Generating vector for moving the target node
|
|
|
|
|
const vector = new Victor(node.left, node.top).add(
|
|
|
|
|
pushing.rotate(between.angle() - pushing.angle())
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Moving the target node
|
|
|
|
|
node.move(vector.x, vector.y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Processing pushings by the node (entering into recursion)
|
|
|
|
|
node.push(depth, nodes);
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
}
|
2025-01-11 15:23:54 +07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Pull
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Pull `nodes` to the node
|
|
|
|
|
*
|
|
|
|
|
* @param {number} [depth=1] Amount of cascade reactions (the value will be reduced to 0 in recursion)
|
|
|
|
|
* @param {(Set|Array|undefined)} [nodes=undefined] Nodes for processing (otherwise `this.inputs` + `this.outputs`)
|
|
|
|
|
*/
|
|
|
|
|
async pull(depth = 1, nodes) {
|
|
|
|
|
if (
|
|
|
|
|
typeof depth === "number" &&
|
|
|
|
|
(nodes instanceof Set ||
|
|
|
|
|
nodes instanceof Array ||
|
|
|
|
|
typeof nodes === "undefined")
|
|
|
|
|
) {
|
|
|
|
|
// Validated required argument
|
|
|
|
|
|
|
|
|
|
if (--depth >= 0) {
|
|
|
|
|
// Not reached iterations limit
|
|
|
|
|
|
|
|
|
|
if (this.interactions.pulling.active) {
|
|
|
|
|
// Activated pulling
|
|
|
|
|
|
|
|
|
|
for (const node of (nodes?.size > 0 || nodes?.length > 0
|
|
|
|
|
? [...nodes]
|
|
|
|
|
: [...this.inputs]
|
|
|
|
|
.map((edge) => edge.from)
|
|
|
|
|
.concat([...this.outputs].map((edge) => edge.to))
|
|
|
|
|
).filter((node) => node !== this)) {
|
|
|
|
|
// Iterating over nodes
|
|
|
|
|
|
|
|
|
|
if (node.interactions.pulling.active) {
|
|
|
|
|
// Activated pulling
|
|
|
|
|
|
|
|
|
|
// Initializing the vector between nodes
|
|
|
|
|
const between = new Victor(
|
|
|
|
|
node.center.left - this.center.left,
|
|
|
|
|
node.center.top - this.center.top
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Calculation of the arithmetic mean of nodes pulling distance
|
|
|
|
|
const distance =
|
|
|
|
|
(node.interactions.pulling.distance +
|
|
|
|
|
this.interactions.pulling.distance) /
|
|
|
|
|
2;
|
|
|
|
|
|
|
|
|
|
// Calculating difference between needed distance and actial distance
|
|
|
|
|
const difference =
|
|
|
|
|
node.radius + this.radius + distance - between.length();
|
|
|
|
|
|
|
|
|
|
if (difference <= 0) {
|
|
|
|
|
// The node have not overcome the pulling distance
|
|
|
|
|
|
|
|
|
|
// Initializing vector of pulling distance
|
|
|
|
|
const pulling = new Victor(difference, difference);
|
|
|
|
|
|
|
|
|
|
// Generating vector for moving the target node
|
|
|
|
|
const vector = new Victor(node.left, node.top).add(
|
|
|
|
|
pulling.rotate(between.angle() - pulling.angle()).invert()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Moving the target node
|
|
|
|
|
node.move(vector.x, vector.y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Processing pullings by the node (entering into recursion)
|
|
|
|
|
node.pull(depth, nodes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-11 14:19:54 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Synchronization
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Synchronize all the node edges with the node
|
|
|
|
|
**/
|
|
|
|
|
synchronization() {
|
|
|
|
|
for (const edge of this.outputs) {
|
|
|
|
|
// Iterating over the node outputs edges
|
|
|
|
|
|
|
|
|
|
// Synchronizing the output edge with the node
|
|
|
|
|
edge.synchronization(this);
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
for (const edge of this.inputs) {
|
|
|
|
|
// Iterating over the node inputs edges
|
2022-12-11 15:37:49 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
// Synchronizing the input edge with the node
|
|
|
|
|
edge.synchronization(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Edge
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Edge of the module for creating graphs
|
|
|
|
|
*
|
|
|
|
|
* {@link https://git.mirzaev.sexy/mirzaev/graph.mjs}
|
|
|
|
|
*
|
|
|
|
|
* @class
|
|
|
|
|
* @public
|
|
|
|
|
*
|
|
|
|
|
* @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
|
|
|
|
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
|
|
|
|
*
|
|
|
|
|
* @example <caption>Creating an edge</caption>
|
|
|
|
|
* instance.edge(new edge(node_1, node_2));
|
|
|
|
|
*/
|
|
|
|
|
export class edge {
|
|
|
|
|
/**
|
|
|
|
|
* @name Shell
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Shell of the edge
|
|
|
|
|
*
|
|
|
|
|
* @type {SVGElement}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#shell;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Shell (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for shell of the edge
|
|
|
|
|
*
|
|
|
|
|
* @type {SVGElement}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get shell() {
|
|
|
|
|
return this.#shell;
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Line
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* The <line> element of the edge
|
|
|
|
|
*
|
|
|
|
|
* @type {SVGElement}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#line;
|
2022-12-11 14:19:54 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Line (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter of the <line> element of the edge
|
|
|
|
|
*
|
|
|
|
|
* @type {SVGElement}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get line() {
|
|
|
|
|
return this.#line;
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name From
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* The node from which the edge comes
|
|
|
|
|
*
|
|
|
|
|
* @type {node}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#from;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name From (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for the node from which the edge comes
|
|
|
|
|
*
|
|
|
|
|
* @type {node}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get from() {
|
|
|
|
|
return this.#from;
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name To
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* The node into which the edge enters
|
|
|
|
|
*
|
|
|
|
|
* @type {node}
|
|
|
|
|
*
|
|
|
|
|
* @protected
|
|
|
|
|
*/
|
|
|
|
|
#to;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name To (get)
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Getter for the node into which the edge enters
|
|
|
|
|
*
|
|
|
|
|
* @type {node}
|
|
|
|
|
*
|
|
|
|
|
* @public
|
|
|
|
|
*/
|
|
|
|
|
get to() {
|
|
|
|
|
return this.#to;
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Constructor
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Initialize an edge instance
|
|
|
|
|
*
|
|
|
|
|
* @param {node} from The node from which the edge comes
|
|
|
|
|
* @param {node} to The node into which the edge enters
|
|
|
|
|
**/
|
|
|
|
|
constructor(from, to) {
|
|
|
|
|
if (from instanceof node && to instanceof node) {
|
|
|
|
|
// Validated required arguments
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
from.shell instanceof HTMLElement &&
|
|
|
|
|
to.shell instanceof HTMLElement
|
|
|
|
|
) {
|
|
|
|
|
// Initialized shell elements
|
|
|
|
|
|
|
|
|
|
// Writing nodes into properties
|
|
|
|
|
this.#from = from;
|
|
|
|
|
this.#to = to;
|
|
|
|
|
|
|
|
|
|
// Writing the edge into nodes edges registries
|
|
|
|
|
this.#from.outputs.add(this);
|
|
|
|
|
this.#to.inputs.add(this);
|
|
|
|
|
|
|
|
|
|
// Reinitializing augmented size of nodes
|
|
|
|
|
this.#from.augment();
|
|
|
|
|
this.#to.augment();
|
|
|
|
|
|
|
|
|
|
// Reinitializing edges variables of nodes
|
|
|
|
|
this.#from.edges();
|
|
|
|
|
this.#to.edges();
|
|
|
|
|
|
|
|
|
|
// Initializing the edge shell <svg> element
|
|
|
|
|
const svg = document.createElementNS(
|
|
|
|
|
"http://www.w3.org/2000/svg",
|
|
|
|
|
"svg"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing identifier of the edge shell <svg> element
|
|
|
|
|
svg.setAttribute(
|
|
|
|
|
"id",
|
|
|
|
|
from.shell.getAttribute("id") + "_" + to.shell.getAttribute("id")
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing classes of the edge shell <svg> element
|
|
|
|
|
svg.classList.add("edge");
|
|
|
|
|
|
|
|
|
|
// Deinitializing the "dragstart" and the "selectstart" event listeners of the edge shell <svg> element
|
|
|
|
|
svg.ondragstart = svg.onselectstart = null;
|
|
|
|
|
|
|
|
|
|
// Initializing the edge <line> element
|
|
|
|
|
const line = document.createElementNS(
|
|
|
|
|
"http://www.w3.org/2000/svg",
|
|
|
|
|
"line"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Writing coordinates of the edge <line> element
|
|
|
|
|
line.setAttribute("x1", from.left + from.radius);
|
|
|
|
|
line.setAttribute("y1", from.top + from.radius);
|
|
|
|
|
line.setAttribute("x2", to.left + to.radius);
|
|
|
|
|
line.setAttribute("y2", to.top + to.radius);
|
|
|
|
|
|
|
|
|
|
// Writing the edge shell <svg> element into property
|
|
|
|
|
this.#shell = svg;
|
|
|
|
|
|
|
|
|
|
// Writing the edge <line> element into property
|
|
|
|
|
this.#line = line;
|
|
|
|
|
|
|
|
|
|
// Writing the edge <line> element into the edge shell <svg> element
|
|
|
|
|
svg.append(line);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* Синхронизировать местоположение со связанным узлом
|
|
|
|
|
*
|
|
|
|
|
* @param {node} node Инстанция узла (связанного с соединением)
|
|
|
|
|
*/
|
2022-12-11 14:19:54 +07:00
|
|
|
|
|
2025-01-11 15:23:54 +07:00
|
|
|
|
/**
|
|
|
|
|
* @name Synchronization
|
|
|
|
|
*
|
|
|
|
|
* @description
|
|
|
|
|
* Synchronize node and edge coordinates
|
|
|
|
|
*
|
|
|
|
|
* @param {node} node The node with which the edge will be synchronized
|
|
|
|
|
**/
|
|
|
|
|
async synchronization(node) {
|
|
|
|
|
if (node === this.#from) {
|
|
|
|
|
// Output connection
|
|
|
|
|
|
|
|
|
|
// Writing coordinates (offsetLeft and offsetTop for CSS animations)
|
|
|
|
|
this.#line?.setAttribute("x1", node.shell.offsetLeft + node.radius);
|
|
|
|
|
this.#line?.setAttribute("y1", node.shell.offsetTop + node.radius);
|
|
|
|
|
} else if (node === this.#to) {
|
|
|
|
|
// Input connection
|
|
|
|
|
|
|
|
|
|
// Writing coordinates (offsetLeft and offsetTop for CSS animations)
|
|
|
|
|
this.#line?.setAttribute("x2", node.shell.offsetLeft + node.radius);
|
|
|
|
|
this.#line?.setAttribute("y2", node.shell.offsetTop + node.radius);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
}
|
2025-01-11 15:23:54 +07:00
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
}
|