vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

985 lines
31 KiB

let util = require('../../util');
let Hammer = require('../../module/hammer');
let hammerUtil = require('../../hammerUtil');
let locales = require('../locales');
/**
* clears the toolbar div element of children
*
* @private
*/
class ManipulationSystem {
constructor(body, canvas, selectionHandler) {
this.body = body;
this.canvas = canvas;
this.selectionHandler = selectionHandler;
this.editMode = false;
this.manipulationDiv = undefined;
this.editModeDiv = undefined;
this.closeDiv = undefined;
this.boundFunction = undefined;
this.manipulationHammers = [];
this.cachedFunctions = {};
this.touchTime = 0;
this.temporaryIds = {nodes: [], edges:[]};
this.guiEnabled = false;
this.selectedControlNode = undefined;
this.options = {};
this.defaultOptions = {
enabled: false,
initiallyVisible: false,
locale: 'en',
locales: locales,
functionality:{
addNode: true,
addEdge: true,
editNode: true,
editEdge: true,
deleteNode: true,
deleteEdge: true
},
handlerFunctions: {
addNode: undefined,
addEdge: undefined,
editNode: undefined,
editEdge: undefined,
deleteNode: undefined,
deleteEdge: undefined
}
}
util.extend(this.options, this.defaultOptions);
}
setOptions(options) {
if (options !== undefined) {
if (typeof options == 'boolean') {
this.options.enabled = options;
}
else {
this.options.enabled = true;
for (let prop in options) {
if (options.hasOwnProperty(prop)) {
this.options[prop] = options[prop];
}
}
}
if (this.options.initiallyVisible === true) {
this.editMode = true;
}
this.init();
}
}
init() {
if (this.options.enabled === true) {
// Enable the GUI
this.guiEnabled = true;
// remove override
this.selectionHandler.forceSelectEdges = true;
this.createWrappers();
if (this.editMode === false) {
this.createEditButton();
}
else {
this.createManipulatorBar();
}
}
else {
this.removeManipulationDOM();
// disable the gui
this.guiEnabled = false;
}
}
createWrappers() {
// load the manipulator HTML elements. All styling done in css.
if (this.manipulationDiv === undefined) {
this.manipulationDiv = document.createElement('div');
this.manipulationDiv.className = 'network-manipulationDiv';
if (this.editMode === true) {
this.manipulationDiv.style.display = "block";
}
else {
this.manipulationDiv.style.display = "none";
}
this.canvas.frame.appendChild(this.manipulationDiv);
}
if (this.editModeDiv === undefined) {
this.editModeDiv = document.createElement('div');
this.editModeDiv.className = 'network-manipulation-editMode';
if (this.editMode === true) {
this.editModeDiv.style.display = "none";
}
else {
this.editModeDiv.style.display = "block";
}
this.canvas.frame.appendChild(this.editModeDiv);
}
if (this.closeDiv === undefined) {
this.closeDiv = document.createElement('div');
this.closeDiv.className = 'network-manipulation-closeDiv';
this.closeDiv.style.display = this.manipulationDiv.style.display;
this.canvas.frame.appendChild(this.closeDiv);
}
}
/**
* Create the edit button
*/
createEditButton() {
// restore everything to it's original state (if applicable)
this._clean();
// reset the manipulationDOM
this.manipulationDOM = {};
// empty the editModeDiv
util.recursiveDOMDelete(this.editModeDiv);
// create the contents for the editMode button
let locale = this.options.locales[this.options.locale];
let button = this.createButton('editMode', 'network-manipulationUI edit editmode', locale['edit']);
this.editModeDiv.appendChild(button);
// bind a hammer listener to the button, calling the function toggleEditMode.
this.bindHammerToDiv(button, 'toggleEditMode');
}
removeManipulationDOM() {
// removes all the bindings and overloads
this._clean();
// empty the manipulation divs
util.recursiveDOMDelete(this.manipulationDiv);
util.recursiveDOMDelete(this.editModeDiv);
util.recursiveDOMDelete(this.closeDiv);
// remove the manipulation divs
this.canvas.frame.removeChild(this.manipulationDiv);
this.canvas.frame.removeChild(this.editModeDiv);
this.canvas.frame.removeChild(this.closeDiv);
// set the references to undefined
this.manipulationDiv = undefined;
this.editModeDiv = undefined;
this.closeDiv = undefined;
// remove override
this.selectionHandler.forceSelectEdges = false;
}
//clearManipulatorBar() {
// util._recursiveDOMDelete(this.manipulationDiv);
// this.manipulationDOM = {};
// this._cleanManipulatorHammers();
// this._manipulationReleaseOverload();
//}
_cleanManipulatorHammers() {
// _clean hammer bindings
if (this.manipulationHammers.length != 0) {
for (let i = 0; i < this.manipulationHammers.length; i++) {
this.manipulationHammers[i].destroy();
}
this.manipulationHammers = [];
}
}
/**
* Manipulation UI temporarily overloads certain functions to extend or replace them. To be able to restore
* these functions to their original functionality, we saved them in this.cachedFunctions.
* This function restores these functions to their original function.
*
* @private
*/
_restoreOverloadedFunctions() {
for (let functionName in this.cachedFunctions) {
if (this.cachedFunctions.hasOwnProperty(functionName)) {
this.body.eventListeners[functionName] = this.cachedFunctions[functionName];
delete this.cachedFunctions[functionName];
}
}
this.cachedFunctions = {};
}
/**
* Enable or disable edit-mode.
*
* @private
*/
toggleEditMode() {
this.editMode = !this.editMode;
let toolbar = this.manipulationDiv;
let closeDiv = this.closeDiv;
let editModeDiv = this.editModeDiv;
if (this.editMode === true) {
toolbar.style.display = "block";
closeDiv.style.display = "block";
editModeDiv.style.display = "none";
this.bindHammerToDiv(closeDiv, 'toggleEditMode');
this.createManipulatorBar();
}
else {
toolbar.style.display = "none";
closeDiv.style.display = "none";
editModeDiv.style.display = "block";
this.createEditButton();
}
}
_clean() {
// _clean the divs
if (this.guiEnabled === true) {
util.recursiveDOMDelete(this.editModeDiv);
util.recursiveDOMDelete(this.manipulationDiv);
// removes all the bindings and overloads
this._cleanManipulatorHammers();
}
// remove temporary nodes and edges
this._cleanupTemporaryNodesAndEdges();
// restore overloaded UI functions
this._restoreOverloadedFunctions();
// remove the boundFunction
if (this.boundFunction !== undefined) {
this.body.emitter.off(this.boundFunction.event, this.boundFunction.fn);
}
this.boundFunction = undefined;
}
createSeperator(index = 1) {
this.manipulationDOM['seperatorLineDiv' + index] = document.createElement('div');
this.manipulationDOM['seperatorLineDiv' + index].className = 'network-seperatorLine';
this.manipulationDiv.appendChild(this.manipulationDOM['seperatorLineDiv' + index]);
}
createAddNodeButton(locale) {
let button = this.createButton('addNode', 'network-manipulationUI add', locale['addNode']);
this.manipulationDiv.appendChild(button);
this.bindHammerToDiv(button, 'addNodeMode');
}
createAddEdgeButton(locale) {
let button = this.createButton('addEdge', 'network-manipulationUI connect', locale['addEdge']);
this.manipulationDiv.appendChild(button);
this.bindHammerToDiv(button, 'addEdgeMode');
}
createEditNodeButton(locale) {
let button = this.createButton('editNode', 'network-manipulationUI edit', locale['editNode']);
this.manipulationDiv.appendChild(button);
this.bindHammerToDiv(button, '_editNode');
}
createEditEdgeButton(locale) {
let button = this.createButton('editEdge', 'network-manipulationUI edit', locale['editEdge']);
this.manipulationDiv.appendChild(button);
this.bindHammerToDiv(button, 'editEdgeMode');
}
createDeleteButton(locale) {
let button = this.createButton('delete', 'network-manipulationUI delete', locale['del']);
this.manipulationDiv.appendChild(button);
this.bindHammerToDiv(button, 'deleteSelected');
}
createBackButton(locale) {
let button = this.createButton('back', 'network-manipulationUI back', locale['back']);
this.manipulationDiv.appendChild(button);
this.bindHammerToDiv(button, 'createManipulatorBar');
}
createDescription(label) {
this.manipulationDiv.appendChild(
this.createButton('description', 'network-manipulationUI none', label)
);
}
createButton(id, className, label, labelClassName = 'network-manipulationLabel') {
this.manipulationDOM[id+"Div"] = document.createElement('div');
this.manipulationDOM[id+"Div"].className = className;
this.manipulationDOM[id+"Label"] = document.createElement('div');
this.manipulationDOM[id+"Label"].className = labelClassName;
this.manipulationDOM[id+"Label"].innerHTML = label;
this.manipulationDOM[id+"Div"].appendChild(this.manipulationDOM[id+'Label']);
return this.manipulationDOM[id+"Div"];
}
temporaryBind(fn, event) {
this.boundFunction = {fn:fn.bind(this), event};
this.body.emitter.on(event, this.boundFunction.fn);
}
/**
* main function, creates the main toolbar. Removes functions bound to the select event. Binds all the buttons of the toolbar.
*
* @private
*/
createManipulatorBar() {
this._clean();
// resume calculation
this.body.emitter.emit("restorePhysics");
// reset global letiables
this.manipulationDOM = {};
let selectedNodeCount = this.selectionHandler._getSelectedNodeCount();
let selectedEdgeCount = this.selectionHandler._getSelectedEdgeCount();
let selectedTotalCount = selectedNodeCount + selectedEdgeCount;
let locale = this.options.locales[this.options.locale];
let needSeperator = false;
if (this.options.functionality.addNode === true) {
this.createAddNodeButton(locale);
needSeperator = true;
}
if (this.options.functionality.addEdge === true) {
if (needSeperator === true) {this.createSeperator(1);} else {needSeperator = true;}
this.createAddEdgeButton(locale);
}
if (selectedNodeCount === 1 && typeof this.options.handlerFunctions.editNode === 'function' && this.options.functionality.editNode === true) {
if (needSeperator === true) {this.createSeperator(2);} else {needSeperator = true;}
this.createEditNodeButton(locale);
}
else if (selectedEdgeCount === 1 && selectedNodeCount === 0 && this.options.functionality.editEdge === true) {
if (needSeperator === true) {this.createSeperator(3);} else {needSeperator = true;}
this.createEditEdgeButton(locale);
}
// remove buttons
if (selectedTotalCount !== 0) {
if (selectedNodeCount === 1 && this.options.functionality.deleteNode === true) {
if (needSeperator === true) {this.createSeperator(4);}
this.createDeleteButton(locale);
}
else if (selectedNodeCount === 0 && this.options.functionality.deleteEdge === true) {
if (needSeperator === true) {this.createSeperator(4);}
this.createDeleteButton(locale);
}
}
// bind the close button
this.bindHammerToDiv(this.closeDiv, 'toggleEditMode');
// refresh this bar based on what has been selected
this.temporaryBind(this.createManipulatorBar,'select');
}
/**
* Bind an hammer instance to a DOM element. TODO: remove the double check.
* @param domElement
* @param funct
*/
bindHammerToDiv(domElement, funct) {
let hammer = new Hammer(domElement, {});
hammerUtil.onTouch(hammer, this[funct].bind(this));
this.manipulationHammers.push(hammer);
}
/**
* Create the toolbar for adding Nodes
*
* @private
*/
addNodeMode() {
// clear the toolbar
this._clean();
if (this.guiEnabled === true) {
let locale = this.options.locales[this.options.locale];
this.manipulationDOM = {};
this.createBackButton(locale);
this.createSeperator();
this.createDescription(locale['addDescription'])
// bind the close button
this.bindHammerToDiv(this.closeDiv, 'toggleEditMode');
}
this.temporaryBind(this._addNode,'click');
}
/**
* create the toolbar to connect nodes
*
* @private
*/
addEdgeMode() {
// _clean the system
this._clean();
if (this.guiEnabled === true) {
let locale = this.options.locales[this.options.locale];
this.manipulationDOM = {};
this.createBackButton(locale);
this.createSeperator();
this.createDescription(locale['edgeDescription']);
// bind the close button
this.bindHammerToDiv(this.closeDiv, 'toggleEditMode');
}
// temporarily overload functions
this.cachedFunctions["onTouch"] = this.body.eventListeners.onTouch;
this.cachedFunctions["onDragEnd"] = this.body.eventListeners.onDragEnd;
this.cachedFunctions["onHold"] = this.body.eventListeners.onHold;
this.body.eventListeners.onTouch = this._handleConnect.bind(this);
this.body.eventListeners.onDragEnd = this._finishConnect.bind(this);
this.body.eventListeners.onHold = function () {};
}
/**
* create the toolbar to edit edges
*
* @private
*/
editEdgeMode() {
// clear the system
this._clean();
if (this.guiEnabled === true) {
let locale = this.options.locales[this.options.locale];
this.manipulationDOM = {};
this.createBackButton(locale);
this.createSeperator();
this.createDescription(locale['editEdgeDescription']);
// bind the close button
this.bindHammerToDiv(this.closeDiv, 'toggleEditMode');
}
this.edgeBeingEditedId = this.selectionHandler.getSelectedEdges()[0];
let edge = this.body.edges[this.edgeBeingEditedId];
// create control nodes
let controlNodeFrom = this.body.functions.createNode(this.getTargetNodeProperties(edge.from.x, edge.from.y));
let controlNodeTo = this.body.functions.createNode(this.getTargetNodeProperties(edge.to.x, edge.to.y));
this.temporaryIds.nodes.push(controlNodeFrom.id);
this.temporaryIds.nodes.push(controlNodeTo.id);
this.body.nodes[controlNodeFrom.id] = controlNodeFrom;
this.body.nodeIndices.push(controlNodeFrom.id);
this.body.nodes[controlNodeTo.id] = controlNodeTo;
this.body.nodeIndices.push(controlNodeTo.id);
// temporarily overload functions
this.cachedFunctions['onTouch'] = this.body.eventListeners.onTouch;
this.cachedFunctions['onTap'] = this.body.eventListeners.onTap;
this.cachedFunctions['onHold'] = this.body.eventListeners.onHold;
this.cachedFunctions['onDragStart'] = this.body.eventListeners.onDragStart;
this.cachedFunctions['onDrag'] = this.body.eventListeners.onDrag;
this.cachedFunctions['onDragEnd'] = this.body.eventListeners.onDragEnd;
this.cachedFunctions['onMouseOver'] = this.body.eventListeners.onMouseOver;
this.body.eventListeners.onTouch = this._controlNodeTouch.bind(this);
this.body.eventListeners.onTap = function() {};
this.body.eventListeners.onHold = function() {};
this.body.eventListeners.onDragStart= this._controlNodeDragStart.bind(this);
this.body.eventListeners.onDrag = this._controlNodeDrag.bind(this);
this.body.eventListeners.onDragEnd = this._controlNodeDragEnd.bind(this);
this.body.eventListeners.onMouseOver= function() {}
// create function to position control nodes correctly on movement
let positionControlNodes = (ctx) => {
let positions = edge.edgeType.findBorderPositions(ctx);
if (controlNodeFrom.selected === false) {
controlNodeFrom.x = positions.from.x;
controlNodeFrom.y = positions.from.y;
}
if (controlNodeTo.selected === false) {
controlNodeTo.x = positions.to.x;
controlNodeTo.y = positions.to.y;
}
}
this.temporaryBind(positionControlNodes, "beforeDrawing");
this.body.emitter.emit("_redraw");
}
_controlNodeTouch(event) {
this.lastTouch = this.body.functions.getPointer(event.center);
this.lastTouch.translation = util.extend({},this.body.view.translation); // copy the object
}
_controlNodeDragStart(event) {
let pointer = this.lastTouch;
let pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
let from = this.body.nodes[this.temporaryIds.nodes[0]];
let to = this.body.nodes[this.temporaryIds.nodes[1]];
let edge = this.body.edges[this.edgeBeingEditedId];
this.selectedControlNode = undefined;
let fromSelect = from.isOverlappingWith(pointerObj);
let toSelect = to.isOverlappingWith(pointerObj);
if (fromSelect === true) {
this.selectedControlNode = from;
edge.edgeType.from = from;
}
else if (toSelect === true) {
this.selectedControlNode = to;
edge.edgeType.to = to;
}
this.body.emitter.emit("_redraw");
}
_controlNodeDrag(event) {
this.body.emitter.emit("disablePhysics");
let pointer = this.body.functions.getPointer(event.center);
let pos = this.canvas.DOMtoCanvas(pointer);
if (this.selectedControlNode !== undefined) {
this.selectedControlNode.x = pos.x;
this.selectedControlNode.y = pos.y;
}
else {
// if the drag was not started properly because the click started outside the network div, start it now.
let diffX = pointer.x - this.lastTouch.x;
let diffY = pointer.y - this.lastTouch.y;
this.body.view.translation = {x:this.lastTouch.translation.x + diffX, y:this.lastTouch.translation.y + diffY};
}
this.body.emitter.emit("_redraw");
}
_controlNodeDragEnd(event) {
let pointer = this.body.functions.getPointer(event.center);
let pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
let edge = this.body.edges[this.edgeBeingEditedId];
let overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj);
let node = undefined;
for (let i = overlappingNodeIds.length-1; i >= 0; i--) {
if (overlappingNodeIds[i] !== this.selectedControlNode.id) {
node = this.body.nodes[overlappingNodeIds[i]];
break;
}
}
// perform the connection
if (node !== undefined && this.selectedControlNode !== undefined) {
if (node.isCluster === true) {
alert(this.options.locales[this.options.locale]["createEdgeError"])
}
else {
let from = this.body.nodes[this.temporaryIds.nodes[0]];
if (this.selectedControlNode.id == from.id) {
this._editEdge(node.id, edge.to.id);
}
else {
this._editEdge(edge.from.id, node.id);
}
}
}
else {
edge.updateEdgeType();
this.body.emitter.emit("restorePhysics");
}
this.body.emitter.emit("_redraw");
}
/**
* the function bound to the selection event. It checks if you want to connect a cluster and changes the description
* to walk the user through the process.
*
* @private
*/
_selectControlNode(event) {
}
/**
*
* @param pointer
* @private
*/
_releaseControlNode(pointer) {
if (new Date().valueOf() - this.touchTime > 100) {
console.log("release")
// perform the connection
let node = this.selectionHandler.getNodeAt(pointer);
if (node !== undefined) {
if (node.isCluster === true) {
alert(this.options.locales[this.options.locale]["createEdgeError"])
}
else {
let edge = this.body.edges[this.edgeBeingEditedId];
let targetNodeId = undefined;
if (edge.to.selected === true) {
targetNodeId = edge.toId;
}
else if (edge.from.selected === true) {
targetNodeId = edge.fromId;
}
//this.body.eventListeners.onDrag = this.cachedFunctions["onDrag"];
//this.body.eventListeners.onRelease = this.cachedFunctions["onRelease"];
//delete this.cachedFunctions["onRelease"];
//delete this.cachedFunctions["onDrag"];
////
//
//
//
//
//
//
//if (this.body.nodes[connectFromId] !== undefined && this.body.nodes[node.id] !== undefined) {
// this._createEdge(connectFromId, node.id);
//}
}
}
this.body.emitter.emit("_redraw");
//this.body.emitter.emit("_redraw");
//let newNode = this.getNodeAt(pointer);
//if (newNode !== undefined) {
// if (this.edgeBeingEditedId.controlNodes.from.selected == true) {
// this.edgeBeingEditedId._restoreControlNodes();
// this._editEdge(newNode.id, this.edgeBeingEditedId.to.id);
// this.edgeBeingEditedId.controlNodes.from.unselect();
// }
// if (this.edgeBeingEditedId.controlNodes.to.selected == true) {
// this.edgeBeingEditedId._restoreControlNodes();
// this._editEdge(this.edgeBeingEditedId.from.id, newNode.id);
// this.edgeBeingEditedId.controlNodes.to.unselect();
// }
//}
//else {
// this.edgeBeingEditedId._restoreControlNodes();
//}
this.touchTime = new Date().valueOf();
}
}
/**
* the function bound to the selection event. It checks if you want to connect a cluster and changes the description
* to walk the user through the process.
*
* @private
*/
_handleConnect(event) {
// check to avoid double fireing of this function.
if (new Date().valueOf() - this.touchTime > 100) {
let pointer = this.body.functions.getPointer(event.center);
let node = this.selectionHandler.getNodeAt(pointer);
if (node !== undefined) {
if (node.isCluster === true) {
alert(this.options.locales[this.options.locale]['createEdgeError'])
}
else {
// create a node the temporary line can look at
let targetNode = this.body.functions.createNode(this.getTargetNodeProperties(node.x,node.y));
let targetNodeId = targetNode.id;
this.body.nodes[targetNode.id] = targetNode;
this.body.nodeIndices.push(targetNode.id);
// create a temporary edge
let connectionEdge = this.body.functions.createEdge({
id: "connectionEdge" + util.randomUUID(),
from: node.id,
to: targetNode.id,
physics:false,
smooth: {
enabled: true,
dynamic: false,
type: "continuous",
roundness: 0.5
}
});
this.body.edges[connectionEdge.id] = connectionEdge;
this.body.edgeIndices.push(connectionEdge.id);
this.temporaryIds.nodes.push(targetNode.id);
this.temporaryIds.edges.push(connectionEdge.id);
this.cachedFunctions["onDrag"] = this.body.eventListeners.onDrag;
this.body.eventListeners.onDrag = (event) => {
let pointer = this.body.functions.getPointer(event.center);
let targetNode = this.body.nodes[targetNodeId];
targetNode.x = this.canvas._XconvertDOMtoCanvas(pointer.x);
targetNode.y = this.canvas._YconvertDOMtoCanvas(pointer.y);
this.body.emitter.emit("_redraw");
}
}
}
this.touchTime = new Date().valueOf();
// do the original touch events
this.cachedFunctions["onTouch"](event);
}
}
_finishConnect(event) {
let pointer = this.body.functions.getPointer(event.center);
let pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
// remember the edge id
let connectFromId = undefined;
if (this.temporaryIds.edges[0] !== undefined) {
connectFromId = this.body.edges[this.temporaryIds.edges[0]].fromId;
}
//restore the drag function
if (this.cachedFunctions["onDrag"] !== undefined) {
this.body.eventListeners.onDrag = this.cachedFunctions["onDrag"];
delete this.cachedFunctions["onDrag"];
}
// get the overlapping node but NOT the temporary node;
let overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj);
let node = undefined;
for (let i = overlappingNodeIds.length-1; i >= 0; i--) {
if (this.temporaryIds.nodes.indexOf(overlappingNodeIds[i]) !== -1) {
node = this.body.nodes[overlappingNodeIds[i]];
break;
}
}
// clean temporary nodes and edges.
this._cleanupTemporaryNodesAndEdges();
// perform the connection
if (node !== undefined) {
if (node.isCluster === true) {
alert(this.options.locales[this.options.locale]["createEdgeError"])
}
else {
if (this.body.nodes[connectFromId] !== undefined && this.body.nodes[node.id] !== undefined) {
this._createEdge(connectFromId, node.id);
}
}
}
this.body.emitter.emit("_redraw");
}
_cleanupTemporaryNodesAndEdges() {
// _clean temporary edges
for (let i = 0; i < this.temporaryIds.edges.length; i++) {
this.body.edges[this.temporaryIds.edges[i]].disconnect();
delete this.body.edges[this.temporaryIds.edges[i]];
let indexTempEdge = this.body.edgeIndices.indexOf(this.temporaryIds.edges[i]);
if (indexTempEdge !== -1) {this.body.edgeIndices.splice(indexTempEdge,1);}
}
// _clean temporary nodes
for (let i = 0; i < this.temporaryIds.nodes.length; i++) {
delete this.body.nodes[this.temporaryIds.nodes[i]];
let indexTempNode = this.body.nodeIndices.indexOf(this.temporaryIds.nodes[i]);
if (indexTempNode !== -1) {this.body.nodeIndices.splice(indexTempNode,1);}
}
this.temporaryIds = {nodes: [], edges: []};
}
/**
* Adds a node on the specified location
*/
_addNode(clickData) {
let defaultData = {
id: util.randomUUID(),
x: clickData.pointer.canvas.x,
y: clickData.pointer.canvas.y,
label: "new"
};
if (typeof this.options.handlerFunctions.addNode === 'function') {
if (this.options.handlerFunctions.addNode.length == 2) {
this.options.handlerFunctions.addNode(defaultData, (finalizedData) => {
this.body.data.nodes.add(finalizedData);
this.createManipulatorBar();
});
}
else {
throw new Error('The function for add does not support two arguments (data,callback)');
this.createManipulatorBar();
}
}
else {
this.body.data.nodes.add(defaultData);
this.createManipulatorBar();
}
}
/**
* connect two nodes with a new edge.
*
* @private
*/
_createEdge(sourceNodeId, targetNodeId) {
let defaultData = {from: sourceNodeId, to: targetNodeId};
if (this.options.handlerFunctions.addEdge) {
if (this.options.handlerFunctions.addEdge.length == 2) {
this.options.handlerFunctions.addEdge(defaultData, (finalizedData) => {
this.body.data.edges.add(finalizedData);
this.selectionHandler.unselectAll();
this.createManipulatorBar();
});
}
else {
throw new Error('The function for connect does not support two arguments (data,callback)');
}
}
else {
this.body.data.edges.add(defaultData);
this.selectionHandler.unselectAll();
this.createManipulatorBar();
}
}
/**
* connect two nodes with a new edge.
*
* @private
*/
_editEdge(sourceNodeId, targetNodeId) {
let defaultData = {id: this.edgeBeingEditedId, from: sourceNodeId, to: targetNodeId};
console.log(defaultData)
if (this.options.handlerFunctions.editEdge) {
if (this.options.handlerFunctions.editEdge.length == 2) {
this.options.handlerFunctions.editEdge(defaultData, (finalizedData) => {
this.body.data.edges.update(finalizedData);
this.selectionHandler.unselectAll();
this.createManipulatorBar();
});
}
else {
throw new Error('The function for edit does not support two arguments (data, callback)');
}
}
else {
this.body.data.edges.update(defaultData);
this.selectionHandler.unselectAll();
this.createManipulatorBar();
}
}
/**
* Create the toolbar to edit the selected node. The label and the color can be changed. Other colors are derived from the chosen color.
*
* @private
*/
_editNode() {
if (this.options.handlerFunctions.edit && this.editMode == true) {
let node = this._getSelectedNode();
let data = {
id: node.id,
label: node.label,
group: node.options.group,
shape: node.options.shape,
color: {
background: node.options.color.background,
border: node.options.color.border,
highlight: {
background: node.options.color.highlight.background,
border: node.options.color.highlight.border
}
}
};
if (this.options.handlerFunctions.edit.length == 2) {
let me = this;
this.options.handlerFunctions.edit(data, function (finalizedData) {
me.body.data.nodes.update(finalizedData);
me.createManipulatorBar();
me.moving = true;
me.start();
});
}
else {
throw new Error('The function for edit does not support two arguments (data, callback)');
}
}
else {
throw new Error('No edit function has been bound to this button');
}
}
/**
* delete everything in the selection
*
* @private
*/
deleteSelected() {
let selectedNodes = this.selectionHandler.getSelectedNodes();
let selectedEdges = this.selectionHandler.getSelectedEdges();
let deleteFunction = undefined;
if (selectedNodes.length > 0) {
for (let i = 0; i < selectedNodes.length; i++) {
if (this.body.nodes[selectedNodes[i]].isCluster === true) {
alert("You cannot delete a cluster.");
return;
}
}
if (typeof this.options.handlerFunctions.deleteNode === 'function') {
deleteFunction = this.options.handlerFunctions.deleteNode;
}
}
else if (selectedEdges.length > 0) {
if (typeof this.options.handlerFunctions.deleteEdge === 'function') {
deleteFunction = this.options.handlerFunctions.deleteEdge;
}
}
if (typeof deleteFunction === 'function') {
let data = {nodes: selectedNodes, edges: selectedEdges};
if (deleteFunction.length == 2) {
deleteFunction(data, (finalizedData) => {
this.body.data.edges.remove(finalizedData.edges);
this.body.data.nodes.remove(finalizedData.nodes);
this.body.emitter.emit("startSimulation");
});
}
else {
throw new Error('The function for delete does not support two arguments (data, callback)')
}
}
else {
this.body.data.edges.remove(selectedEdges);
this.body.data.nodes.remove(selectedNodes);
this.body.emitter.emit("startSimulation");
}
}
getTargetNodeProperties(x,y) {
return {
id: 'targetNode' + util.randomUUID(),
hidden: false,
physics: false,
shape:'dot',
size:6,
x:x,
y:y,
color: {background: '#ff0000', border: '#3c3c3c', highlight: {background: '#07f968'}},
borderWidth: 2,
borderWidthSelected: 2
}
}
}
export default ManipulationSystem;