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
|
|
|
|
|
2022-11-26 04:38:23 +07:00
|
|
|
|
'use strict';
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
|
|
|
|
*/
|
|
|
|
|
class graph {
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Идентификатор HTML-элемента-оболочки (instanceof HTMLElement)
|
|
|
|
|
#id = 'graph';
|
|
|
|
|
|
|
|
|
|
// Прочитать идентификатор HTML-элемента-оболочки (instanceof HTMLElement)
|
|
|
|
|
get id() {
|
|
|
|
|
return this.#id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Классы которые будут записаны в HTML-элементы
|
|
|
|
|
classes = {
|
|
|
|
|
node: {
|
|
|
|
|
shell: ['nodes'],
|
|
|
|
|
element: ['node'],
|
|
|
|
|
onmouseenter: ['onmouseenter'],
|
|
|
|
|
title: ['title'],
|
2022-12-03 14:13:30 +07:00
|
|
|
|
cover: ['cover'],
|
|
|
|
|
description: {
|
|
|
|
|
both: ['description'],
|
|
|
|
|
hidden: ['hidden'],
|
|
|
|
|
shown: ['shown']
|
|
|
|
|
},
|
|
|
|
|
close: {
|
|
|
|
|
both: ['close'],
|
|
|
|
|
hidden: ['hidden'],
|
|
|
|
|
shown: ['shown']
|
|
|
|
|
},
|
2022-12-03 12:05:28 +07:00
|
|
|
|
wrappers: {
|
|
|
|
|
both: ['wrapper'],
|
|
|
|
|
left: ['left'],
|
|
|
|
|
right: ['right']
|
2022-12-10 17:41:11 +07:00
|
|
|
|
},
|
|
|
|
|
animation: ['animation']
|
2022-12-03 12:05:28 +07:00
|
|
|
|
},
|
|
|
|
|
connection: {
|
|
|
|
|
shell: ['connections'],
|
|
|
|
|
element: ['connection']
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-11-01 06:19:17 +07:00
|
|
|
|
// Оболочка (instanceof HTMLElement)
|
2022-12-03 12:05:28 +07:00
|
|
|
|
#shell = document.getElementById(this.id);
|
2022-11-22 05:40:30 +07:00
|
|
|
|
get shell() {
|
|
|
|
|
return this.#shell;
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
|
|
|
|
// Реестр узлов
|
2022-12-07 16:31:35 +07:00
|
|
|
|
#nodes = new Set;
|
2022-11-22 06:43:57 +07:00
|
|
|
|
get nodes() {
|
|
|
|
|
return this.#nodes;
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
|
|
|
|
// Реестр соединений
|
2022-12-07 16:31:35 +07:00
|
|
|
|
#connections = new Set;
|
2022-11-22 06:43:57 +07:00
|
|
|
|
get connections() {
|
|
|
|
|
return this.#connections;
|
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
// Статус активации функций взаимодействий узлов
|
|
|
|
|
actions = {
|
|
|
|
|
pushing: true,
|
2022-12-11 14:19:54 +07:00
|
|
|
|
pulling: true,
|
|
|
|
|
move: {
|
|
|
|
|
shell: true,
|
|
|
|
|
node: true
|
|
|
|
|
}
|
2022-12-04 02:13:10 +07:00
|
|
|
|
}
|
|
|
|
|
|
2022-11-01 06:19:17 +07:00
|
|
|
|
// Класс узла
|
2022-11-22 06:43:57 +07:00
|
|
|
|
#node = class node {
|
2022-11-01 06:19:17 +07:00
|
|
|
|
// Реестр входящих соединений
|
2022-11-22 06:43:57 +07:00
|
|
|
|
#inputs = new Set();
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать реестр входящих соединений
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get inputs() { return this.#inputs }
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
|
|
|
|
// Реестр исходящих соединений
|
2022-11-22 06:43:57 +07:00
|
|
|
|
#outputs = new Set();
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать реестр исходящих соединений
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get outputs() { return this.#outputs }
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
|
|
|
|
// Оператор
|
|
|
|
|
#operator;
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать оператора
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get operator() { return this.#operator }
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// HTML-элемент-оболочка
|
|
|
|
|
#shell;
|
|
|
|
|
|
|
|
|
|
// Прочитать HTML-элемент-оболочка
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get shell() { return this.#shell }
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2022-11-22 05:40:30 +07:00
|
|
|
|
// HTML-элемент
|
|
|
|
|
#element;
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать HTML-элемент
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get element() { return this.#element }
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
|
|
|
|
// Наблюдатель
|
|
|
|
|
#observer = null;
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать наблюдатель
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get observer() { return this.#observer }
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
|
|
|
|
// Реестр запрещённых к изменению параметров
|
2022-12-03 12:29:29 +07:00
|
|
|
|
#block = new Set(['events']);
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать реестр запрещённых к изменению параметров
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get block() { return this.#block }
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2022-11-22 18:31:26 +07:00
|
|
|
|
// Диаметр узла
|
2022-11-01 06:19:17 +07:00
|
|
|
|
#diameter = 100;
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать диаметр узла
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get diameter() { return this.#diameter }
|
2022-11-22 18:31:26 +07:00
|
|
|
|
|
|
|
|
|
// Степень увеличения диаметра
|
|
|
|
|
#increase = 0;
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать степень увеличения диаметра
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get increase() { return this.#increase }
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2022-11-22 18:31:26 +07:00
|
|
|
|
// Величина степени увеличения диаметра
|
|
|
|
|
#addition = 12;
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать величину степени увеличения диаметра
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get addition() { return this.#addition }
|
2022-11-22 18:31:26 +07:00
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
// Величина степени увеличения притягивания и отталкивания
|
|
|
|
|
#shift = 0;
|
|
|
|
|
|
|
|
|
|
// Прочитать величину степени увеличения притягивания и отталкивания
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get shift() { return this.#shift }
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
/**
|
|
|
|
|
* Обработка событий
|
|
|
|
|
*
|
|
|
|
|
* max - максимум итераций в процессе
|
|
|
|
|
* current - текущая итерация в процессе
|
|
|
|
|
*/
|
2022-11-22 18:31:26 +07:00
|
|
|
|
actions = {
|
2022-12-11 14:19:54 +07:00
|
|
|
|
move: {
|
|
|
|
|
active: true,
|
|
|
|
|
unlimit: false
|
2022-12-04 02:13:10 +07:00
|
|
|
|
},
|
|
|
|
|
pushing: {
|
2022-12-07 16:31:35 +07:00
|
|
|
|
active: true,
|
2022-12-04 02:13:10 +07:00
|
|
|
|
max: 100,
|
2022-12-10 17:41:11 +07:00
|
|
|
|
current: 0
|
2022-12-04 02:13:10 +07:00
|
|
|
|
},
|
|
|
|
|
pulling: {
|
2022-12-11 14:19:54 +07:00
|
|
|
|
active: true,
|
2022-12-04 02:13:10 +07:00
|
|
|
|
max: 100,
|
2022-12-10 17:41:11 +07:00
|
|
|
|
current: 0
|
2022-12-04 02:13:10 +07:00
|
|
|
|
}
|
2022-11-22 18:31:26 +07:00
|
|
|
|
};
|
|
|
|
|
|
2022-12-04 14:15:17 +07:00
|
|
|
|
/**
|
|
|
|
|
* Отталкивания
|
|
|
|
|
*
|
2022-12-11 14:19:54 +07:00
|
|
|
|
* Реестр узлов которые обработали отталкивание с целевым узлом в потоке
|
2022-12-04 14:15:17 +07:00
|
|
|
|
*/
|
2022-12-07 16:31:35 +07:00
|
|
|
|
pushings = new Set;
|
2022-12-04 14:15:17 +07:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Притягивания
|
|
|
|
|
*
|
|
|
|
|
* Реестр узлов которые обработали притягивание с целевым узлом в потоке
|
|
|
|
|
*/
|
2022-12-07 16:31:35 +07:00
|
|
|
|
pullings = new Set;
|
2022-12-04 14:15:17 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
/**
|
|
|
|
|
* Расчёт времени анимации
|
|
|
|
|
*/
|
|
|
|
|
#timing = 'cubic-bezier(0, 1, 1, 1)';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Прочитать расчёт времени анимации
|
|
|
|
|
*/
|
|
|
|
|
get timing() { return this.#timing }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Длительность анимации (в секундах)
|
|
|
|
|
*/
|
|
|
|
|
#duration = '3';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Прочитать длительность анимации (в секундах)
|
|
|
|
|
*/
|
|
|
|
|
get duration() { return this.#duration }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Анимация (HTMLElement <style>)
|
|
|
|
|
*/
|
|
|
|
|
#animation;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Прочитать анимацию (HTMLElement <style>)
|
|
|
|
|
*/
|
|
|
|
|
get animation() { return this.#animation }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Троттлинг анимации
|
|
|
|
|
*
|
|
|
|
|
* Время после которого будет удалена (готова для перезапуска) анимация при завершении движения узла (остановке)
|
|
|
|
|
*/
|
|
|
|
|
#throttle = 300;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Прочитать троттлинг анимации
|
|
|
|
|
*/
|
|
|
|
|
get throttle() { return this.#throttle }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Движение
|
|
|
|
|
*/
|
|
|
|
|
#movement = {
|
|
|
|
|
status: null,
|
|
|
|
|
observer: null,
|
|
|
|
|
from: { x: null, y: null },
|
|
|
|
|
to: { x: null, y: null }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Прочитать движение
|
|
|
|
|
get movement() { return this.#movement }
|
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
/**
|
|
|
|
|
* Конструктор узла
|
|
|
|
|
*
|
|
|
|
|
* @param {object} operator Инстанция оператора (графика)
|
|
|
|
|
* @param {object} data Данные для генерации
|
|
|
|
|
*/
|
2022-11-22 06:43:57 +07:00
|
|
|
|
constructor(operator, data) {
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Запись в свойство
|
|
|
|
|
this.#operator = operator;
|
|
|
|
|
|
|
|
|
|
// Инициализация ссылки на ядро
|
|
|
|
|
const _this = this;
|
|
|
|
|
|
|
|
|
|
// Инициализация HTML-элемента-оболочки узлов
|
|
|
|
|
if ((this.#shell = document.getElementById(this.#operator.id + '_nodes')) instanceof HTMLElement);
|
|
|
|
|
else {
|
|
|
|
|
// Не найден HTML-элемент-оболочки узлов
|
|
|
|
|
|
|
|
|
|
// Инициализация HTML-элемента-оболочки узлов
|
|
|
|
|
const shell = document.createElement('section');
|
|
|
|
|
shell.id = this.#operator.id + '_nodes';
|
|
|
|
|
shell.classList.add(...this.#operator.classes.node.shell);
|
|
|
|
|
|
|
|
|
|
// Запись в документ
|
|
|
|
|
this.#operator.shell.appendChild(shell);
|
|
|
|
|
|
|
|
|
|
// Запись в свойство
|
|
|
|
|
this.#shell = shell;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-22 18:31:26 +07:00
|
|
|
|
// Инициализация HTML-элемента узла
|
2022-12-03 12:29:29 +07:00
|
|
|
|
const article = document.createElement('article');
|
2022-12-03 12:05:28 +07:00
|
|
|
|
article.id = this.#operator.id + '_node_' + this.#operator.nodes.size;
|
2022-12-03 12:08:21 +07:00
|
|
|
|
article.classList.add(..._this.operator.classes.node.element);
|
2022-12-10 17:41:11 +07:00
|
|
|
|
article.style.animationName = article.id + '_animation';
|
|
|
|
|
article.style.animationFillMode = 'forwards';
|
|
|
|
|
article.style.animationDuration = this.#duration + 's';
|
|
|
|
|
article.style.animationTimingFunction = this.#timing;
|
|
|
|
|
if (typeof data.color === 'string') article.classList.add(data.color);
|
|
|
|
|
if (typeof data.href === 'string') article.href = data.href;
|
|
|
|
|
|
2022-11-24 14:24:23 +07:00
|
|
|
|
|
2022-11-26 23:40:46 +07:00
|
|
|
|
// Запись анимации "выделение обводкой" (чтобы не проигрывалась при открытии страницы)
|
2022-12-10 17:41:11 +07:00
|
|
|
|
article.onmouseenter = fn => article.classList.add(..._this.#operator.classes.node.onmouseenter);
|
2022-11-26 23:40:46 +07:00
|
|
|
|
|
2022-11-26 04:38:23 +07:00
|
|
|
|
// Инициализация заголовка
|
2022-12-03 12:29:29 +07:00
|
|
|
|
const title = document.createElement('h4');
|
2022-12-03 12:05:28 +07:00
|
|
|
|
title.classList.add(..._this.#operator.classes.node.title);
|
2022-11-26 05:04:45 +07:00
|
|
|
|
title.innerText = data.title ?? '';
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2022-11-24 14:24:23 +07:00
|
|
|
|
// Запись в оболочку
|
|
|
|
|
article.appendChild(title);
|
|
|
|
|
|
2022-11-26 04:38:23 +07:00
|
|
|
|
// Инициализация описания
|
2022-12-03 12:29:29 +07:00
|
|
|
|
const description = document.createElement('div');
|
2022-12-03 14:13:30 +07:00
|
|
|
|
description.classList.add(..._this.#operator.classes.node.description.both, ..._this.#operator.classes.node.description.hidden);
|
2022-11-26 05:04:45 +07:00
|
|
|
|
if (typeof data.popup === 'string') description.title = data.popup;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2022-11-26 23:40:46 +07:00
|
|
|
|
// Запись анимации "выделение обводкой" (чтобы не проигрывалась при открытии страницы)
|
2022-12-10 17:41:11 +07:00
|
|
|
|
description.onmouseenter = fn => description.classList.add(..._this.#operator.classes.node.onmouseenter);
|
2022-11-26 23:40:46 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Инициализация блокировки открытия описания в случае, если был перемещён узел
|
2022-11-26 04:38:23 +07:00
|
|
|
|
title.onmousedown = (onmousedown) => {
|
2022-11-26 06:18:53 +07:00
|
|
|
|
// Инициализация координат
|
|
|
|
|
let x = onmousedown.pageX;
|
|
|
|
|
let y = onmousedown.pageY;
|
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
title.onclick = (onclick) => (_this.show(), title.onclick = title.onmousemove = title.style.cursor = null, x = onclick.pageX, y = onclick.pageY, true);
|
2022-11-26 04:38:23 +07:00
|
|
|
|
|
|
|
|
|
title.onmousemove = (onmousemove) => {
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Если курсор движется более чем на 15 пикселей по вертикали или горизонтали, то блокировать открытие описания
|
|
|
|
|
if (Math.abs(x - onmousemove.pageX) > 15 || Math.abs(y - onmousemove.pageY) > 15) (title.style.cursor = 'grabbing', title.onclick = (onclick) => (title.onclick = title.onmousemove = title.style.cursor = null, x = onclick.pageX, y = onclick.pageY, false));
|
|
|
|
|
else title.onclick = (onclick) => (_this.show(), title.onclick = title.onmousemove = title.style.cursor = null, x = onclick.pageX, y = onclick.pageY, true);
|
2022-11-26 04:38:23 +07:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Запись в оболочку
|
|
|
|
|
article.appendChild(description);
|
|
|
|
|
|
|
|
|
|
// Инициализация левой фигуры для обёртки текста
|
2022-12-03 12:29:29 +07:00
|
|
|
|
const left = document.createElement('span');
|
2022-12-03 12:05:28 +07:00
|
|
|
|
left.classList.add(..._this.#operator.classes.node.wrappers.both, ..._this.#operator.classes.node.wrappers.left);
|
2022-11-26 04:38:23 +07:00
|
|
|
|
|
|
|
|
|
// Запись в описание
|
|
|
|
|
description.appendChild(left);
|
|
|
|
|
|
|
|
|
|
// Инициализация правой фигуры для обёртки текста
|
2022-12-03 12:29:29 +07:00
|
|
|
|
const right = document.createElement('span');
|
2022-12-03 12:05:28 +07:00
|
|
|
|
right.classList.add(..._this.#operator.classes.node.wrappers.both, ..._this.#operator.classes.node.wrappers.right);
|
2022-11-26 04:38:23 +07:00
|
|
|
|
|
|
|
|
|
// Запись в описание
|
|
|
|
|
description.appendChild(right);
|
|
|
|
|
|
|
|
|
|
// Инициализация ссылки на источник
|
2022-12-03 12:29:29 +07:00
|
|
|
|
const a = document.createElement('a');
|
2022-11-26 05:04:45 +07:00
|
|
|
|
if (typeof data.link === 'object' && typeof data.link.name === 'string') a.innerText = data.link.name;
|
|
|
|
|
if (typeof data.link === 'object' && typeof data.link.href === 'string') a.href = data.link.href;
|
|
|
|
|
if (typeof data.link === 'object' && typeof data.link.class === 'object') a.classList.add(...data.link.class);
|
|
|
|
|
if (typeof data.link === 'object' && typeof data.link.title === 'string') a.title = data.link.title;
|
2022-11-26 04:38:23 +07:00
|
|
|
|
|
|
|
|
|
// Блокировка событий браузера (чтобы не мешать переноса узла)
|
2022-12-10 17:41:11 +07:00
|
|
|
|
a.ondragstart = a.onselectstart = fn => false;
|
2022-11-26 04:38:23 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Инициализация блокировки перехода по ссылке в случае, если был перемещён узел
|
2022-11-26 04:38:23 +07:00
|
|
|
|
a.onmousedown = (onmousedown) => {
|
2022-11-26 06:18:53 +07:00
|
|
|
|
// Инициализация координат
|
|
|
|
|
let x = onmousedown.pageX;
|
|
|
|
|
let y = onmousedown.pageY;
|
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
a.onclick = (onclick) => (a.onclick = a.onmousemove = a.style.cursor = null, x = onclick.pageX, y = onclick.pageY, true);
|
2022-11-26 06:18:53 +07:00
|
|
|
|
|
2022-11-26 04:38:23 +07:00
|
|
|
|
a.onmousemove = (onmousemove) => {
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Если курсор движется более чем на 15 пикселей по вертикали или горизонтали, то блокировать переход по ссылке
|
|
|
|
|
if (Math.abs(x - onmousemove.pageX) > 15 || Math.abs(y - onmousemove.pageY) > 15) (a.style.cursor = 'grabbing', a.onclick = (onclick) => (a.onclick = a.onmousemove = a.style.cursor = null, x = onclick.pageX, y = onclick.pageY, false));
|
|
|
|
|
else a.onclick = (onclick) => (a.onclick = a.onmousemove = a.style.cursor = null, x = onclick.pageX, y = onclick.pageY, true);
|
2022-11-26 04:38:23 +07:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Запись в описание
|
|
|
|
|
description.appendChild(a);
|
|
|
|
|
|
|
|
|
|
// Запись текста в описание
|
2022-12-03 12:29:29 +07:00
|
|
|
|
const text = document.createElement('p');
|
2022-11-26 05:04:45 +07:00
|
|
|
|
if (typeof data.description === 'string') text.innerText = data.description;
|
2022-11-26 04:38:23 +07:00
|
|
|
|
|
|
|
|
|
// Запись в оболочку
|
|
|
|
|
description.appendChild(text);
|
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
if (typeof data.cover === 'string') {
|
2022-11-26 05:04:45 +07:00
|
|
|
|
// Получено изображение-обложка
|
2022-11-26 04:38:23 +07:00
|
|
|
|
|
2022-11-26 05:04:45 +07:00
|
|
|
|
// Инициализация изображения-обложки
|
2022-12-03 12:29:29 +07:00
|
|
|
|
const cover = document.createElement('img');
|
2022-11-26 05:04:45 +07:00
|
|
|
|
if (typeof cover.src === 'string') cover.src = data.cover;
|
|
|
|
|
if (typeof cover.alt === 'string') cover.alt = data.title;
|
2022-12-03 12:05:28 +07:00
|
|
|
|
cover.classList.add(..._this.#operator.classes.node.cover);
|
2022-11-26 04:38:23 +07:00
|
|
|
|
|
|
|
|
|
// Запись в описание
|
|
|
|
|
description.appendChild(cover);
|
2022-11-24 14:24:23 +07:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Запись в оболочку
|
|
|
|
|
if (typeof data.append === 'HTMLCollection' || typeof data.append === 'HTMLElement') article.appendChild(data.append);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2022-11-26 05:50:15 +07:00
|
|
|
|
// Инициализация кнопки закрытия
|
|
|
|
|
const close = document.createElement('i');
|
2022-12-03 14:13:30 +07:00
|
|
|
|
close.classList.add(..._this.#operator.classes.node.close.both, ..._this.#operator.classes.node.close.hidden);
|
2022-11-26 05:50:15 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Запись блокировки закрытия описания в случае, если был перемещён узел
|
2022-11-26 22:48:25 +07:00
|
|
|
|
close.onmousedown = (onmousedown) => {
|
|
|
|
|
// Инициализация координат
|
|
|
|
|
let x = onmousedown.pageX;
|
|
|
|
|
let y = onmousedown.pageY;
|
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
close.onclick = (onclick) => (_this.hide(), close.onclick = close.onmousemove = close.style.cursor = null, x = onclick.pageX, y = onclick.pageY, true);
|
2022-11-26 22:48:25 +07:00
|
|
|
|
|
|
|
|
|
close.onmousemove = (onmousemove) => {
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Если курсор движется более чем на 15 пикселей по вертикали или горизонтали, то блокировать закрытие описания
|
|
|
|
|
if (Math.abs(x - onmousemove.pageX) > 15 || Math.abs(y - onmousemove.pageY) > 15) (close.style.cursor = 'grabbing', close.onclick = (onclick) => (close.onclick = close.onmousemove = close.style.cursor = null, x = onclick.pageX, y = onclick.pageY, false));
|
|
|
|
|
else close.onclick = (onclick) => (_this.hide(), close.onclick = close.onmousemove = close.style.cursor = null, x = onclick.pageX, y = onclick.pageY, true);
|
2022-11-26 22:48:25 +07:00
|
|
|
|
}
|
|
|
|
|
};
|
2022-11-26 05:50:15 +07:00
|
|
|
|
|
|
|
|
|
// Запись в оболочку
|
|
|
|
|
article.appendChild(close);
|
|
|
|
|
|
2022-11-22 18:31:26 +07:00
|
|
|
|
// Запись в документ
|
2022-12-03 12:05:28 +07:00
|
|
|
|
this.#shell.appendChild(article);
|
2022-11-22 18:31:26 +07:00
|
|
|
|
|
2022-11-26 04:38:23 +07:00
|
|
|
|
// Запись диаметра описания в зависимости от размера заголовка (чтобы вмещался)
|
|
|
|
|
description.style.width = description.style.height = (a.offsetWidth === 0 ? 50 : a.offsetWidth) * 3 + 'px';
|
|
|
|
|
|
|
|
|
|
// Запись отступа заголовка (чтобы был по центру описания)
|
|
|
|
|
a.style.left = description.offsetWidth / 2 - a.offsetWidth / 2 + 'px';
|
|
|
|
|
|
2022-12-07 16:31:35 +07:00
|
|
|
|
// Запись в свойство
|
|
|
|
|
this.#element = article;
|
|
|
|
|
|
|
|
|
|
// Инициализация
|
|
|
|
|
this.init();
|
|
|
|
|
|
2022-11-26 05:50:15 +07:00
|
|
|
|
/**
|
|
|
|
|
* Показать описание
|
|
|
|
|
*/
|
2022-12-03 14:13:30 +07:00
|
|
|
|
this.show = fn => {
|
|
|
|
|
// Отображение описания
|
|
|
|
|
description.classList.add(..._this.#operator.classes.node.description.shown);
|
|
|
|
|
description.classList.remove(..._this.#operator.classes.node.description.hidden);
|
|
|
|
|
|
|
|
|
|
// Отображение кнопки закрытия
|
|
|
|
|
close.classList.add(..._this.#operator.classes.node.close.shown);
|
|
|
|
|
close.classList.remove(..._this.#operator.classes.node.close.hidden);
|
2022-11-26 05:50:15 +07:00
|
|
|
|
|
|
|
|
|
// Сдвиг кнопки закрытия описания
|
|
|
|
|
close.style.top = close.style.right = -(((description.offsetWidth - article.offsetWidth) / 4) + description.offsetWidth / 8) + 'px';
|
|
|
|
|
|
|
|
|
|
// Размер кнопки закрытия описания
|
|
|
|
|
close.style.scale = 1.3;
|
|
|
|
|
|
|
|
|
|
// Прозрачность кнопки закрытия описания (плавное появление)
|
|
|
|
|
close.style.opacity = 1;
|
|
|
|
|
|
|
|
|
|
// Расположение выше остальных узлов
|
|
|
|
|
article.style.zIndex = close.style.zIndex = 1000;
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
|
|
|
|
// Инициализация сдвига отталкивания и притяжения соединённых узлов
|
|
|
|
|
_this.#shift = description.offsetWidth - article.offsetWidth;
|
|
|
|
|
|
2022-12-04 14:15:17 +07:00
|
|
|
|
// Сброс данных потока
|
|
|
|
|
_this.reset();
|
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
// Обработка сдвига
|
2022-12-11 14:19:54 +07:00
|
|
|
|
_this.move();
|
2022-11-26 05:50:15 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Скрыть описание
|
|
|
|
|
*/
|
2022-12-03 14:13:30 +07:00
|
|
|
|
this.hide = fn => {
|
|
|
|
|
// Скрытие описания
|
|
|
|
|
description.classList.add(..._this.#operator.classes.node.description.hidden);
|
|
|
|
|
description.classList.remove(..._this.#operator.classes.node.description.shown);
|
|
|
|
|
|
|
|
|
|
// Скрытие кнопки закрытия
|
|
|
|
|
close.classList.add(..._this.#operator.classes.node.close.hidden);
|
|
|
|
|
close.classList.remove(..._this.#operator.classes.node.close.shown);
|
2022-11-26 05:50:15 +07:00
|
|
|
|
|
|
|
|
|
// Удаление всех изменённых аттрибутов
|
|
|
|
|
close.style.top = close.style.right = article.style.zIndex = close.style.zIndex = close.style.scale = close.style.opacity = null;
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
|
|
|
|
// Деинициализация сдвига отталкивания и притяжения соединённых узлов
|
|
|
|
|
_this.#shift = 0;
|
|
|
|
|
|
2022-12-04 14:15:17 +07:00
|
|
|
|
// Сброс данных потока
|
|
|
|
|
_this.reset();
|
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
// Обработка сдвига
|
2022-12-11 14:19:54 +07:00
|
|
|
|
_this.move();
|
2022-11-26 05:50:15 +07:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-07 16:31:35 +07:00
|
|
|
|
// Запись в реестр
|
|
|
|
|
this.#operator.nodes.add(this);
|
2022-11-22 18:31:26 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Инициализация координат центров
|
|
|
|
|
const horizontal = this.#operator.shell.offsetWidth / 2 - this.#diameter / 2;
|
|
|
|
|
const vertical = this.#operator.shell.offsetHeight / 2 - this.#diameter / 2;
|
|
|
|
|
|
|
|
|
|
// Инициализация начальных координат
|
|
|
|
|
this.element.style.left = this.#movement.from.x = horizontal + (0.5 - Math.random()) * 500 + 'px';
|
|
|
|
|
this.element.style.top = this.#movement.from.y = vertical + (0.5 - Math.random()) * 500 + 'px';
|
2022-12-04 14:15:17 +07:00
|
|
|
|
|
2022-11-22 18:31:26 +07:00
|
|
|
|
// Перемещение
|
2022-12-10 17:41:11 +07:00
|
|
|
|
this.move(horizontal + (0.5 - Math.random()) * 500, vertical + (0.5 - Math.random()) * 500);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
}
|
|
|
|
|
|
2022-11-22 05:40:30 +07:00
|
|
|
|
init(increase = 0) {
|
2022-11-22 18:31:26 +07:00
|
|
|
|
// Запись в свойство
|
|
|
|
|
this.#increase = increase;
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2022-11-22 18:31:26 +07:00
|
|
|
|
// Инициализация диаметра
|
2022-12-10 17:41:11 +07:00
|
|
|
|
if (this.#increase !== 0) this.#diameter += this.#addition ** this.#increase;
|
2022-11-22 18:31:26 +07:00
|
|
|
|
|
|
|
|
|
// Инициализация размера HTML-элемента
|
2022-12-10 17:41:11 +07:00
|
|
|
|
this.element.style.width = this.element.style.height = this.#diameter + 'px';
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2022-11-26 04:38:23 +07:00
|
|
|
|
// Инициализация описания
|
|
|
|
|
const description = this.element.getElementsByClassName('description')[0];
|
|
|
|
|
|
|
|
|
|
// Запись отступа описания (чтобы был по центру узла)
|
|
|
|
|
description.style.marginLeft = description.style.marginTop = (this.element.offsetWidth - description.offsetWidth) / 2 + 'px';
|
2022-11-22 05:40:30 +07:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
/**
|
|
|
|
|
* Переместить узел
|
|
|
|
|
*
|
|
|
|
|
* @param {*} x Координата X (относительно левого верхнего края)
|
|
|
|
|
* @param {*} y Координата Y (относительно левого верхнего края)
|
2022-12-10 17:41:11 +07:00
|
|
|
|
*
|
|
|
|
|
* @return {bool} Статус выполнения
|
2022-12-04 02:13:10 +07:00
|
|
|
|
*/
|
2022-12-10 17:41:11 +07:00
|
|
|
|
move(x, y) {
|
2022-12-11 14:19:54 +07:00
|
|
|
|
if (!this.actions.move.active) return false;
|
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Инициализация конечных координат
|
|
|
|
|
(this.#movement.to.x ??= this.#element.offsetLeft, this.#movement.to.y ??= this.#element.offsetTop);
|
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
// Проверка входящих параметров
|
2022-12-10 17:41:11 +07:00
|
|
|
|
if (typeof x !== 'number' || typeof y !== 'number') (x = this.#movement.to.x, y = this.#movement.to.y);
|
|
|
|
|
|
|
|
|
|
// Округление координат
|
|
|
|
|
(x = Math.round(x), y = Math.round(y));
|
|
|
|
|
|
|
|
|
|
if (this.#movement.status !== 'completed') {
|
|
|
|
|
// Не завершено движение
|
|
|
|
|
|
|
|
|
|
// Запись начальных координат
|
|
|
|
|
this.#movement.from = { x: this.#element.offsetLeft, y: this.#element.offsetTop };
|
|
|
|
|
|
|
|
|
|
// Запись наблюдателя для проверки того, что движение было завершено
|
|
|
|
|
if (this.#movement.observer === null) this.#movement.observer = setInterval(fn => {
|
|
|
|
|
if (this.#element.offsetLeft === this.#movement.to.x && this.#element.offsetTop === this.#movement.to.y) {
|
|
|
|
|
// Завершено движение
|
|
|
|
|
|
|
|
|
|
// Запись координат
|
|
|
|
|
(this.element.style.left = this.#movement.to.x + 'px', this.element.style.top = this.#movement.to.y + 'px');
|
|
|
|
|
|
|
|
|
|
// Удаление координат движения
|
|
|
|
|
this.#movement.from = this.#movement.to;
|
2022-12-04 02:13:10 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Запись статуса
|
|
|
|
|
this.#movement.status = 'completed';
|
|
|
|
|
|
|
|
|
|
// Удаление анимации (для того, чтобы запустить её с начала)
|
|
|
|
|
if (this.#animation instanceof HTMLElement) setTimeout((this.#animation.remove(), this.#animation = undefined), this.#throttle);
|
|
|
|
|
|
|
|
|
|
// Сброс счётчиков
|
2022-12-11 14:19:54 +07:00
|
|
|
|
this.actions.pushing.current = this.actions.pulling.current = 0;
|
2022-12-10 17:41:11 +07:00
|
|
|
|
|
|
|
|
|
// Десинхронизация узла с его соединениями
|
|
|
|
|
for (const connection of this.#inputs) connection.desynchronize(this);
|
|
|
|
|
for (const connection of this.#outputs) connection.desynchronize(this);
|
|
|
|
|
|
|
|
|
|
// Сброс данных потока
|
|
|
|
|
this.reset();
|
|
|
|
|
|
|
|
|
|
// Удаление наблюдателя
|
|
|
|
|
(clearInterval(this.#movement.observer), this.#movement.observer = null);
|
|
|
|
|
}
|
|
|
|
|
}, 10);
|
2022-12-04 14:15:17 +07:00
|
|
|
|
}
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Запись статуса
|
|
|
|
|
this.#movement.status = 'moving';
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Запись конечных координат движения
|
|
|
|
|
this.#movement.to = { x, y };
|
|
|
|
|
|
|
|
|
|
if (typeof this.#animation === 'undefined') {
|
|
|
|
|
// Не найден HTML-элемент с анимацией
|
|
|
|
|
|
|
|
|
|
// Инициализация HTML-элемента с анимацией
|
|
|
|
|
const style = document.createElement('style');
|
|
|
|
|
style.id = this.element.id + '_animation';
|
|
|
|
|
style.classList.add(...this.operator.classes.node.animation);
|
|
|
|
|
|
|
|
|
|
// Запись в документ
|
|
|
|
|
this.#element.appendChild(style);
|
|
|
|
|
|
|
|
|
|
// Запись в свойство
|
|
|
|
|
this.#animation = style;
|
2022-12-04 14:15:17 +07:00
|
|
|
|
}
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Запись анимации
|
|
|
|
|
this.#animation.innerHTML = `@keyframes ${this.#animation.id} {0% { left: ${this.#movement.from.x}px; top: ${this.#movement.from.y}px; } 100% { left: ${this.#movement.to.x}px; top: ${this.#movement.to.y}px; }}`;
|
|
|
|
|
|
2022-11-23 23:50:13 +07:00
|
|
|
|
// Инициализация буфера реестра узлов
|
2022-12-03 12:05:28 +07:00
|
|
|
|
const registry = new Set(this.#operator.nodes);
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2022-12-04 14:15:17 +07:00
|
|
|
|
if (this.pushings && !this.pushings.has(this)) {
|
2022-11-23 23:50:13 +07:00
|
|
|
|
// Активно отталкивание
|
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Обработка отталкивания
|
|
|
|
|
for (const connection of this.outputs) (registry.delete(connection.to), this.pushing(new Set([connection.to])));
|
|
|
|
|
for (const connection of this.inputs) (registry.delete(connection.from), this.pushing(new Set([connection.from])));
|
2022-11-23 23:50:13 +07:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-04 14:15:17 +07:00
|
|
|
|
if (this.pullings && !this.pullings.has(this)) {
|
2022-11-23 23:50:13 +07:00
|
|
|
|
// Активно притягивание
|
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Обработка притягивания
|
|
|
|
|
for (const connection of this.outputs) (registry.delete(connection.to), this.pulling(new Set([connection.to])));
|
|
|
|
|
for (const connection of this.inputs) (registry.delete(connection.from), this.pulling(new Set([connection.from])));
|
2022-11-23 23:50:13 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Обработка отталкивания остальных узлов
|
2022-12-11 14:19:54 +07:00
|
|
|
|
if (this.pushings) this.pushing(registry);
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Синхронизация узла с его соединениями
|
2022-12-03 12:05:28 +07:00
|
|
|
|
for (const connection of this.outputs) connection.synchronize(this);
|
|
|
|
|
for (const connection of this.inputs) connection.synchronize(this);
|
2022-11-22 05:40:30 +07:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-05 08:13:55 +07:00
|
|
|
|
/**
|
2022-12-07 16:31:35 +07:00
|
|
|
|
* Обработать отталкивания
|
2022-12-05 08:13:55 +07:00
|
|
|
|
*
|
|
|
|
|
* @param {*} nodes
|
|
|
|
|
* @param {*} add
|
|
|
|
|
* @param {*} distance
|
|
|
|
|
*
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
2022-12-10 17:41:11 +07:00
|
|
|
|
pushing(nodes = [], add, distance = 100) {
|
2022-12-07 16:31:35 +07:00
|
|
|
|
// Проверка на активность отталкивания целевого узла
|
|
|
|
|
if (!this.#operator.actions.pushing || !this.actions.pushing.active) return false;
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2022-12-07 16:31:35 +07:00
|
|
|
|
// Инициализация буфера реестра узлов
|
|
|
|
|
const registry = new Set(nodes);
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2022-12-07 16:31:35 +07:00
|
|
|
|
// Удаление текущего узла из буфера
|
|
|
|
|
registry.delete(this);
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2022-12-07 16:31:35 +07:00
|
|
|
|
for (const node of registry) {
|
|
|
|
|
// Перебор обрабатываемых узлов
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
if (this.actions.move.unlimit) {
|
|
|
|
|
// Перемещается мышью узел
|
|
|
|
|
|
|
|
|
|
// Запись о том, что узел перемещается мышью (каскадно)
|
|
|
|
|
node.actions.move.unlimit = true;
|
|
|
|
|
} else {
|
|
|
|
|
// Не перемещается мышью узел
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Проверка на превышение ограничения по числу итераций для отталкивания у целевого узла
|
|
|
|
|
if (++this.actions.pushing.current > this.actions.pushing.max) return false;
|
|
|
|
|
|
|
|
|
|
// Проверка на превышение ограничения по числу итераций для отталкивания у целевого узла
|
|
|
|
|
if (++node.actions.pushing.current > node.actions.pushing.max) continue;
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Проверка на активность отталкивания у целевого узла
|
|
|
|
|
if (!this.#operator.actions.pushing || !this.actions.pushing.active) return false;
|
|
|
|
|
|
|
|
|
|
// Проверка на активность отталкивания у обрабатываемого узла
|
2022-12-07 16:31:35 +07:00
|
|
|
|
if (!node.#operator.actions.pushing || !node.actions.pushing.active) continue;
|
|
|
|
|
|
|
|
|
|
// Защита от повторной обработки обрабатываемого узла
|
|
|
|
|
if (typeof this.pushings === 'object' && this.pushings.has(node)) continue;
|
2022-12-10 17:41:11 +07:00
|
|
|
|
else this.pushings.add(node);
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2022-12-05 08:13:55 +07:00
|
|
|
|
// Инициализация вектора между узлами
|
2022-12-11 14:19:54 +07:00
|
|
|
|
const between = new Victor(node.element.offsetLeft - this.element.offsetLeft, node.element.offsetTop - this.element.offsetTop);
|
2022-12-05 08:13:55 +07:00
|
|
|
|
|
2022-12-07 16:31:35 +07:00
|
|
|
|
// Вычисление разницы между необходимым расстоянием и текущим
|
2022-12-11 14:19:54 +07:00
|
|
|
|
const difference = (this.diameter + node.diameter) / 2 + distance + this.shift + node.shift + (typeof add === 'number' ? add : 0) - between.length();
|
2022-12-05 08:13:55 +07:00
|
|
|
|
|
2022-12-07 16:31:35 +07:00
|
|
|
|
// Узлы преодолели расстояние отталкивания?
|
2022-12-10 17:41:11 +07:00
|
|
|
|
if (difference <= 0) continue;
|
2022-12-05 08:13:55 +07:00
|
|
|
|
|
|
|
|
|
// Реинициализация реестра обработанных узлов и запись целевого узла
|
2022-12-11 14:19:54 +07:00
|
|
|
|
node.pushings = new Set([this]);
|
2022-12-05 08:13:55 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Инициализация вектора целевой позиции для перемещения
|
|
|
|
|
const target = new Victor(difference, difference);
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Инициализация вектора новой позиции обрабатываемого узла
|
2022-12-11 14:19:54 +07:00
|
|
|
|
const vector = new Victor(node.element.offsetLeft, node.element.offsetTop).add(target.rotate(between.angle() - target.angle()));
|
2022-12-05 08:13:55 +07:00
|
|
|
|
|
2022-12-07 16:31:35 +07:00
|
|
|
|
// Перемещение
|
2022-12-05 08:13:55 +07:00
|
|
|
|
node.move(vector.x, vector.y);
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Обработать притягивания
|
|
|
|
|
*
|
|
|
|
|
* @param {*} nodes
|
|
|
|
|
* @param {*} add
|
|
|
|
|
* @param {*} distance
|
|
|
|
|
*
|
|
|
|
|
* @returns
|
|
|
|
|
*/
|
2022-12-11 14:19:54 +07:00
|
|
|
|
pulling(nodes = [], add, distance = 150) {
|
2022-12-07 16:31:35 +07:00
|
|
|
|
// Проверка на активность притягивания целевого узла
|
|
|
|
|
if (!this.#operator.actions.pulling || !this.actions.pulling.active) return false;
|
|
|
|
|
|
2022-12-05 08:13:55 +07:00
|
|
|
|
// Инициализация буфера реестра узлов
|
|
|
|
|
const registry = new Set(nodes);
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2022-12-05 08:13:55 +07:00
|
|
|
|
// Удаление текущего узла из буфера
|
|
|
|
|
registry.delete(this);
|
2022-11-23 23:50:13 +07:00
|
|
|
|
|
2022-12-07 16:31:35 +07:00
|
|
|
|
for (const node of registry) {
|
|
|
|
|
// Перебор обрабатываемых узлов
|
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
if (this.actions.move.unlimit) {
|
|
|
|
|
// Перемещается мышью узел
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Запись о том, что узел перемещается мышью (каскадно)
|
|
|
|
|
node.actions.move.unlimit = true;
|
|
|
|
|
} else {
|
|
|
|
|
// Не перемещается мышью узел
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Проверка на превышение ограничения по числу итераций для притягивания у целевого узла
|
|
|
|
|
if (++this.actions.pulling.current > this.actions.pulling.max) return false;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Проверка на превышение ограничения по числу итераций для притягивания у целевого узла
|
|
|
|
|
if (++node.actions.pulling.current > node.actions.pulling.max) continue;
|
|
|
|
|
}
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Проверка на активность отталкивания у целевого узла
|
|
|
|
|
if (!this.#operator.actions.pulling || !this.actions.pulling.active) return false;
|
|
|
|
|
|
|
|
|
|
// Проверка на активность отталкивания у обрабатываемого узла
|
|
|
|
|
if (!node.#operator.actions.pulling || !node.actions.pulling.active) continue;
|
|
|
|
|
|
|
|
|
|
// Защита от повторной обработки целевого узла
|
|
|
|
|
if (typeof this.pullings === 'object' && this.pullings.has(node)) continue;
|
|
|
|
|
else this.pullings.add(node);
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Инициализация вектора между узлами
|
2022-12-11 14:19:54 +07:00
|
|
|
|
const between = new Victor(node.element.offsetLeft - this.element.offsetLeft, node.element.offsetTop - this.element.offsetTop);
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Вычисление разницы между необходимым расстоянием и текущим
|
2022-12-11 14:19:54 +07:00
|
|
|
|
const difference = (node.diameter + this.diameter) / 2 + distance + this.shift + node.shift + (typeof add === 'number' ? add : 0) - between.length();
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Узлы преодолели расстояние отталкивания?
|
|
|
|
|
if (difference > 0) continue;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Реинициализация реестра обработанных узлов и запись целевого узла
|
|
|
|
|
node.pullings = new Set([this]);
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Инициализация вектора целевой позиции для перемещения
|
|
|
|
|
const target = new Victor(difference, difference);
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Инициализация координат обрабатываемого узла
|
2022-12-11 14:19:54 +07:00
|
|
|
|
const vector = new Victor(node.element.offsetLeft, node.element.offsetTop).add(target.rotate(between.angle() - target.angle()).invert());
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Перемещение узла
|
|
|
|
|
node.move(vector.x, vector.y);
|
|
|
|
|
}
|
2022-11-22 18:31:26 +07:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-04 14:15:17 +07:00
|
|
|
|
/**
|
|
|
|
|
* Сброс данных потока
|
|
|
|
|
*/
|
|
|
|
|
reset = fn => {
|
|
|
|
|
// Реинициализация реестров обработанных узлов
|
|
|
|
|
this.pushings = this.#operator.actions.pushing ? new Set() : null;
|
|
|
|
|
this.pullings = this.#operator.actions.pulling ? new Set() : null;
|
|
|
|
|
|
|
|
|
|
// Реинициализация счётчиков итераций
|
2022-12-11 14:19:54 +07:00
|
|
|
|
this.actions.pushing.current = this.actions.pulling.current = 0;
|
2022-12-04 14:15:17 +07:00
|
|
|
|
}
|
2022-11-01 06:19:17 +07:00
|
|
|
|
};
|
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Прочитать класс узла
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get node() { return this.#node }
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
|
|
|
|
// Класс соединения
|
|
|
|
|
#connection = class connection {
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// HTML-элемент-оболочка
|
|
|
|
|
#shell;
|
|
|
|
|
|
|
|
|
|
// Прочитать HTML-элемент-оболочку
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get shell() { return this.#shell }
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
|
|
|
|
// HTML-элемент соединения
|
2022-11-22 06:43:57 +07:00
|
|
|
|
#element;
|
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Прочитать HTML-элемент соединения
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get element() { return this.#element }
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Инстанция this.operator.node от которой начинается соединение
|
2022-11-22 06:43:57 +07:00
|
|
|
|
#from;
|
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Прочитать инстанцию this.operator.node от которой начинается соединение
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get from() { return this.#from }
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Инстанция this.operator.node на которой заканчивается соединение
|
2022-11-22 06:43:57 +07:00
|
|
|
|
#to;
|
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Прочитать инстанцию this.operator.node на которой заканчивается соединение
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get to() { return this.#to }
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
|
|
|
|
// Оператор
|
|
|
|
|
#operator;
|
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Прочитать оператора
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get operator() { return this.#operator }
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
2022-12-07 16:31:35 +07:00
|
|
|
|
// Сессии синхронизации позиции узлов с соединениями
|
|
|
|
|
#sessions = new Map;
|
|
|
|
|
|
|
|
|
|
// Прочитать сессии синхронизации позиции узлов с соединениями
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get sessions() { return this.#sessions }
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Координата X (основной узел)
|
2022-12-11 15:37:49 +07:00
|
|
|
|
#x1;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать координату X (основной узел)
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get x1() { return this.#x1 }
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Координата Y (основной узел)
|
2022-12-11 15:37:49 +07:00
|
|
|
|
#y1;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать координату Y (основной узел)
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get y1() { return this.#y1 }
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Координата X (связанный узел)
|
2022-12-11 15:37:49 +07:00
|
|
|
|
#x2;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать координату X (связанный узел)
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get x2() { return this.#x2 }
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Координата X (связанный узел)
|
2022-12-11 15:37:49 +07:00
|
|
|
|
#y2;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Прочитать координату X (связанный узел)
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get y2() { return this.#y2 }
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-11 15:37:49 +07:00
|
|
|
|
// Дата окончания синхронизации
|
|
|
|
|
#data = Date.now();
|
|
|
|
|
|
|
|
|
|
// Прочитать дату окончания синхронизации
|
|
|
|
|
get data() { return this.#data }
|
|
|
|
|
|
|
|
|
|
// Таймер синхронизации (в милисекундах)
|
|
|
|
|
#timer = 5000;
|
|
|
|
|
|
|
|
|
|
// Прочитать таймер синхронизации (в милисекундах)
|
|
|
|
|
get timer() { return this.#timer }
|
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
/**
|
|
|
|
|
* Конструктор соединения
|
|
|
|
|
*
|
|
|
|
|
* @param {object} operator Инстанция оператора (графика)
|
|
|
|
|
* @param {object} from Инстанция узла от которого идёт соединение
|
|
|
|
|
* @param {object} to Инстанция узла к которому идёт соединения
|
|
|
|
|
*/
|
2022-11-22 06:43:57 +07:00
|
|
|
|
constructor(operator, from, to) {
|
|
|
|
|
// Запись свойства
|
|
|
|
|
this.#operator = operator;
|
|
|
|
|
|
|
|
|
|
// Запись свойства
|
|
|
|
|
this.#from = from;
|
|
|
|
|
|
|
|
|
|
// Запись свойства
|
|
|
|
|
this.#to = to;
|
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Инициализация HTML-элемента-оболочки соединений
|
|
|
|
|
if ((this.#shell = document.getElementById(this.#operator.id + '_connections')) instanceof SVGElement);
|
|
|
|
|
else {
|
|
|
|
|
// Не найден HTML-элемент-оболочки соединений
|
|
|
|
|
|
|
|
|
|
// Инициализация HTML-элемента-оболочки соединений
|
2022-12-03 12:29:29 +07:00
|
|
|
|
const shell = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
2022-12-03 12:05:28 +07:00
|
|
|
|
shell.id = this.#operator.id + '_connections';
|
|
|
|
|
shell.classList.add(...this.#operator.classes.connection.shell);
|
|
|
|
|
|
|
|
|
|
// Запись в документ
|
|
|
|
|
this.#operator.shell.appendChild(shell);
|
|
|
|
|
|
|
|
|
|
// Запись в свойство
|
|
|
|
|
this.#shell = shell;
|
|
|
|
|
}
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
2022-12-07 16:31:35 +07:00
|
|
|
|
// Инициализация координат
|
2022-12-10 17:41:11 +07:00
|
|
|
|
(this.#x1 = from.element.offsetLeft + from.element.offsetWidth / 2, this.#y1 = from.element.offsetTop + from.element.offsetHeight / 2, this.#x2 = to.element.offsetLeft + to.element.offsetWidth / 2, this.#y2 = to.element.offsetTop + to.element.offsetHeight / 2);
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-11-22 06:43:57 +07:00
|
|
|
|
// Инициализация оболочки
|
|
|
|
|
const line = document.createElementNS(
|
2022-12-03 12:29:29 +07:00
|
|
|
|
'http://www.w3.org/2000/svg',
|
2022-12-07 16:31:35 +07:00
|
|
|
|
'path'
|
2022-11-22 06:43:57 +07:00
|
|
|
|
);
|
2022-12-07 16:31:35 +07:00
|
|
|
|
line.setAttribute('d', `M${this.x1} ${this.y1} L${this.x2} ${this.y2}`);
|
2022-12-03 12:29:29 +07:00
|
|
|
|
line.setAttribute('stroke', 'grey');
|
|
|
|
|
line.setAttribute('stroke-width', '8px');
|
2022-12-07 16:31:35 +07:00
|
|
|
|
line.id = this.#operator.id + '_connection_' + this.#operator.connections.size;
|
2022-12-03 12:05:28 +07:00
|
|
|
|
line.classList.add(...this.operator.classes.connection.element);
|
2022-12-03 12:29:29 +07:00
|
|
|
|
line.setAttribute('data-from', from.element.id);
|
|
|
|
|
line.setAttribute('data-to', to.element.id);
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
|
|
|
|
// Запись в оболочку
|
2022-12-03 12:05:28 +07:00
|
|
|
|
this.shell.append(line);
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Запись в свойство
|
|
|
|
|
this.#element = line;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
|
|
|
|
// Запись в реестр
|
|
|
|
|
this.#operator.connections.add(this);
|
2022-11-22 06:43:57 +07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-12-10 17:41:11 +07:00
|
|
|
|
* Синхронизировать c узлом
|
2022-11-22 06:43:57 +07:00
|
|
|
|
*
|
|
|
|
|
* @param {node} node Инстанция узла (связанного с соединением)
|
|
|
|
|
*/
|
2022-12-03 12:05:28 +07:00
|
|
|
|
synchronize(node) {
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Десинхронизация
|
|
|
|
|
this.desynchronize(node);
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
2022-12-11 15:37:49 +07:00
|
|
|
|
// Инициализация времени работы синхронизации
|
|
|
|
|
this.#data = Date.now() + this.#timer;
|
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Синхронизация
|
2022-12-11 15:37:49 +07:00
|
|
|
|
if (node === this.from) this.#sessions.set(node.element.id, setInterval(fn => Date.now() < this.#data ? this.element.setAttribute('d', `M${this.#x1 = node.element.offsetLeft + node.element.offsetWidth / 2} ${this.#y1 = node.element.offsetTop + node.element.offsetHeight / 2} L${this.#x2} ${this.#y2}`) : this.desynchronize(node)), 0);
|
|
|
|
|
else if (node === this.to) this.#sessions.set(node.element.id, setInterval(fn => Date.now() < this.#data ? this.element.setAttribute('d', `M${this.#x1} ${this.#y1} L${this.#x2 = node.element.offsetLeft + node.element.offsetWidth / 2} ${this.#y2 = node.element.offsetTop + node.element.offsetHeight / 2}`) : this.desynchronize(node)), 0);
|
2022-12-10 17:41:11 +07:00
|
|
|
|
}
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
/**
|
|
|
|
|
* Десинхронизировать c узлом
|
|
|
|
|
*
|
|
|
|
|
* @param {node} node Инстанция узла (связанного с соединением)
|
|
|
|
|
*/
|
|
|
|
|
desynchronize(node) {
|
|
|
|
|
// Удаление интервала
|
|
|
|
|
clearInterval(this.#sessions.get(node.element.id));
|
2022-11-22 06:43:57 +07:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Прочитать класс соединения
|
2022-12-10 17:41:11 +07:00
|
|
|
|
get connection() { return this.#connection }
|
2022-11-22 06:43:57 +07:00
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
/**
|
|
|
|
|
* Конструктор графика
|
|
|
|
|
*
|
|
|
|
|
* @param {HTMLElement|string} shell HTML-элемент-оболочка для графика, либо его идентификатор
|
|
|
|
|
* @param {boolean} body Перенос работает на теле документа? (иначе на HTML-элементе-оболочке)
|
2022-12-11 14:19:54 +07:00
|
|
|
|
* @param {boolean} move Активировать перемещение оболочки?
|
2022-12-04 02:13:10 +07:00
|
|
|
|
*/
|
2022-12-11 14:19:54 +07:00
|
|
|
|
constructor(shell, body = true, move = true) {
|
2022-11-01 06:19:17 +07:00
|
|
|
|
// Запись оболочки
|
2022-11-24 05:04:14 +07:00
|
|
|
|
if (shell instanceof HTMLElement) this.#shell = shell;
|
2022-12-03 12:05:28 +07:00
|
|
|
|
else if (typeof shell === 'string') this.#shell = document.getElementById(shell);
|
|
|
|
|
|
|
|
|
|
// Проверка на инициализированность HTML-элемента-оболочки
|
|
|
|
|
if (typeof this.#shell === undefined) return false;
|
|
|
|
|
|
|
|
|
|
// Запись идентификатора
|
|
|
|
|
this.#id = this.#shell.id;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
|
|
|
|
// Инициализация ссылки на обрабатываемый объект
|
|
|
|
|
const _this = this;
|
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Инициализация цели для переноса
|
|
|
|
|
const target = body ? document.body : shell;
|
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
if (move === true) {
|
|
|
|
|
// Инициализировать функцию переноса оболочки?
|
2022-12-10 17:41:11 +07:00
|
|
|
|
|
|
|
|
|
target.onmousedown = (onmousedown) => {
|
2022-11-01 06:19:17 +07:00
|
|
|
|
// Начало переноса
|
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
if (_this.actions.move.shell) {
|
|
|
|
|
// Разрешено двигать оболочку
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2022-12-03 12:05:28 +07:00
|
|
|
|
// Запись иконки курсора
|
|
|
|
|
target.style.cursor = 'move';
|
|
|
|
|
|
2022-11-01 06:19:17 +07:00
|
|
|
|
// Инициализация координат
|
|
|
|
|
const coords = _this.shell.getBoundingClientRect();
|
2022-12-03 12:05:28 +07:00
|
|
|
|
const x = onmousedown.pageX - coords.left + scrollX;
|
|
|
|
|
const y = onmousedown.pageY - coords.top + scrollY;
|
|
|
|
|
|
2022-12-03 12:29:29 +07:00
|
|
|
|
// Инициализация HTML-элемента-оболочки соединений
|
|
|
|
|
const connections = document.getElementById(_this.#id + '_connections');
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2022-11-22 05:40:30 +07:00
|
|
|
|
// Инициализация функции переноса полотна
|
|
|
|
|
function move(onmousemove) {
|
2022-12-03 12:29:29 +07:00
|
|
|
|
// Инициализация буфера
|
|
|
|
|
let buffer;
|
|
|
|
|
|
|
|
|
|
// Запись нового отступа от лева для HTML-элемента оболочки графика
|
|
|
|
|
_this.shell.style.left = (buffer = onmousemove.pageX - x) + 'px';
|
|
|
|
|
|
|
|
|
|
// Запись нового отступа от лева для HTML-элемента оболочки соединений
|
|
|
|
|
connections.style.left = -buffer + 'px';
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2022-12-03 12:29:29 +07:00
|
|
|
|
// Запись аттрибута с координатами для HTML-элемента оболочки соединений
|
|
|
|
|
connections.setAttribute('data-x', -buffer);
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2022-12-03 12:29:29 +07:00
|
|
|
|
// Запись нового отступа от верха для HTML-элемента оболочки графика
|
|
|
|
|
_this.shell.style.top = (buffer = onmousemove.pageY - y) + 'px';
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2022-12-03 12:29:29 +07:00
|
|
|
|
// Запись нового отступа от верха для HTML-элемента оболочки соединений
|
|
|
|
|
connections.style.top = -buffer + 'px';
|
2022-12-03 12:05:28 +07:00
|
|
|
|
|
2022-12-03 12:29:29 +07:00
|
|
|
|
// Запись аттрибута с координатами для HTML-элемента оболочки соединений
|
|
|
|
|
connections.setAttribute('data-y', -buffer);
|
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Синхронизация
|
|
|
|
|
for (const connection of _this.connections) (connection.synchronize(connection.from), connection.synchronize(connection.to));
|
2022-11-01 06:19:17 +07:00
|
|
|
|
}
|
|
|
|
|
|
2022-11-22 05:40:30 +07:00
|
|
|
|
// Запись слушателя события: "перенос полотна"
|
2022-12-03 12:05:28 +07:00
|
|
|
|
target.onmousemove = move;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Конец переноса (деинициализация)
|
|
|
|
|
target.onmouseup = () => target.onmousemove = target.onmouseup = target.style.cursor = null;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
};
|
|
|
|
|
|
2022-11-26 04:38:23 +07:00
|
|
|
|
// Блокировка событий браузера (чтобы не дёргалось)
|
2022-12-03 12:05:28 +07:00
|
|
|
|
target.ondragstart = null;
|
2022-12-11 15:37:49 +07:00
|
|
|
|
|
|
|
|
|
// Каждые 3 секунды обрабатывать взаимодействия между узлами
|
|
|
|
|
setInterval(fn => { for (const node of _this.nodes) node.move() }, 3000);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
write = (data = {}) => {
|
2022-12-03 12:29:29 +07:00
|
|
|
|
if (typeof data === 'object') {
|
2022-11-01 06:19:17 +07:00
|
|
|
|
// Получен обязательный входной параметр в правильном типе
|
|
|
|
|
|
|
|
|
|
// Инициализация узла
|
2022-11-22 06:43:57 +07:00
|
|
|
|
const node = new this.node(this, data);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
|
|
|
|
// Инициализация ссылки на обрабатываемый объект
|
|
|
|
|
const _this = this;
|
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Запрет движения оболочки при наведении на узел (чтобы двигать узел)
|
|
|
|
|
node.element.onmouseover = fn => _this.actions.move.shell = false;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Снятие запрета движения оболочки
|
|
|
|
|
node.element.onmouseout = fn => _this.actions.move.shell = true;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
if (_this.actions.move.node) {
|
2022-11-01 06:19:17 +07:00
|
|
|
|
// Разрешено перемещать узлы
|
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
// Инициализация переноса узла
|
2022-12-10 17:41:11 +07:00
|
|
|
|
node.element.onmousedown = (onmousedown) => {
|
|
|
|
|
// Начало переноса узла
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2022-11-26 22:41:43 +07:00
|
|
|
|
// Инициализация буфера позиционирования
|
|
|
|
|
const z = node.element.style.zIndex;
|
|
|
|
|
|
2022-11-01 06:19:17 +07:00
|
|
|
|
// Позиционирование над остальными узлами
|
2022-11-26 04:38:23 +07:00
|
|
|
|
node.element.style.zIndex = 5000;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
if (!_this.actions.move.shell) {
|
|
|
|
|
// Запрещено двигать оболочку (чтобы не двигать узел и оболочку одновременно)
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
|
|
|
|
// Инициализация координат
|
|
|
|
|
const n = node.element.getBoundingClientRect();
|
|
|
|
|
const s = _this.shell.getBoundingClientRect();
|
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Запись слушателя события: "перенос узла"
|
2022-12-11 14:19:54 +07:00
|
|
|
|
document.onmousemove = (onmousemove) => {
|
2022-12-04 14:15:17 +07:00
|
|
|
|
// Сброс данных потока
|
2022-12-11 14:19:54 +07:00
|
|
|
|
node.reset();
|
|
|
|
|
|
|
|
|
|
// Запись статуса о том, что узел в данный момент перемещается
|
|
|
|
|
node.actions.move.unlimit = true;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-04 02:13:10 +07:00
|
|
|
|
// Перемещение узла
|
2022-12-11 14:19:54 +07:00
|
|
|
|
node.move(onmousemove.pageX - (onmousedown.pageX - n.left + s.left + scrollX), onmousemove.pageY - (onmousedown.pageY - n.top + s.top + scrollY));
|
|
|
|
|
};
|
2022-11-01 06:19:17 +07:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Конец переноса узла
|
2022-12-11 14:19:54 +07:00
|
|
|
|
node.element.onmouseup = fn => {
|
2022-11-01 06:19:17 +07:00
|
|
|
|
// Очистка обработчиков событий
|
2022-12-11 14:19:54 +07:00
|
|
|
|
document.onmousemove = node.element.onmouseup = null;
|
|
|
|
|
|
|
|
|
|
// Запись статуса о том, что узел в данный момент НЕ перемещается
|
|
|
|
|
for (const node of _this.nodes) node.actions.move.unlimit = false;
|
2022-12-07 16:31:35 +07:00
|
|
|
|
|
2022-12-11 15:37:49 +07:00
|
|
|
|
// Обработка взаимодействий между всеми узлами
|
|
|
|
|
for (const node of _this.nodes) node.move();
|
|
|
|
|
|
2022-11-26 22:41:43 +07:00
|
|
|
|
// Возвращение позиционирования
|
2022-12-11 14:19:54 +07:00
|
|
|
|
node.element.style.zIndex = z;
|
|
|
|
|
};
|
2022-11-01 06:19:17 +07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Перещапись событий браузера (чтобы не дёргалось)
|
|
|
|
|
node.element.ondragstart = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Запись в реестр
|
|
|
|
|
this.nodes.add(node);
|
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Обработка взаимодействий с другими узлами
|
|
|
|
|
node.move();
|
|
|
|
|
|
2022-11-01 06:19:17 +07:00
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
connect = (from, to) => {
|
2022-11-01 06:19:17 +07:00
|
|
|
|
if (from instanceof this.node && to instanceof this.node) {
|
|
|
|
|
// Получены обязательные входные параметры в правильном типе
|
|
|
|
|
|
2022-11-22 06:43:57 +07:00
|
|
|
|
// Инициализация соединения
|
|
|
|
|
const connection = new this.connection(this, from, to);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
|
|
|
|
// Запись соединений в реестры узлов
|
2022-11-22 06:43:57 +07:00
|
|
|
|
from.outputs.add(connection);
|
|
|
|
|
to.inputs.add(connection);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
|
|
|
|
// Запись в реестр ядра
|
2022-11-22 06:43:57 +07:00
|
|
|
|
this.connections.add(connection);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
|
|
|
|
// Реинициализация узла-получателя
|
2022-11-22 05:40:30 +07:00
|
|
|
|
to.init(1);
|
2022-11-01 06:19:17 +07:00
|
|
|
|
|
2022-12-11 14:19:54 +07:00
|
|
|
|
// Синхронизация соединения с узлами
|
|
|
|
|
connection.synchronize(from);
|
|
|
|
|
connection.synchronize(to);
|
|
|
|
|
|
2022-11-22 06:43:57 +07:00
|
|
|
|
return connection;
|
2022-11-01 06:19:17 +07:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2022-11-22 05:40:30 +07:00
|
|
|
|
|
2022-12-10 17:41:11 +07:00
|
|
|
|
// Вызов события: "Библиотека загружена и готова к работе"
|
|
|
|
|
document.dispatchEvent(new CustomEvent('graph.loaded', { detail: { graph } }));
|