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.
 
 
 

269 lines
6.6 KiB

var util = require("../../util");
var DataSet = require('../../DataSet');
var DataView = require('../../DataView');
import Node from "./components/Node";
import Label from "./components/shared/Label";
class NodesHandler {
constructor(body, images, groups, layoutEngine) {
this.body = body;
this.images = images;
this.groups = groups;
this.layoutEngine = layoutEngine;
// create the node API in the body container
this.body.functions.createNode = this.create.bind(this);
this.nodesListeners = {
'add': (event, params) => {this.add(params.items);},
'update': (event, params) => {this.update(params.items, params.data);},
'remove': (event, params) => {this.remove(params.items);}
};
this.options = {};
this.defaultOptions = {
borderWidth: 1,
borderWidthSelected: undefined,
brokenImage:undefined,
color: {
border: '#2B7CE9',
background: '#97C2FC',
highlight: {
border: '#2B7CE9',
background: '#D2E5FF'
},
hover: {
border: '#2B7CE9',
background: '#D2E5FF'
}
},
fixed: {
x:false,
y:false
},
font: {
color: '#343434',
size: 14, // px
face: 'arial',
background: 'none',
stroke: 0, // px
strokeColor: '#ffffff',
align: 'horizontal'
},
group: undefined,
hidden: false,
icon: {
face: 'FontAwesome', //'FontAwesome',
code: undefined, //'\uf007',
size: 50, //50,
color:'#2B7CE9' //'#aa00ff'
},
image: undefined, // --> URL
label: undefined,
level: undefined,
mass: 1,
physics: true,
scaling: {
min: 10,
max: 30,
label: {
enabled: true,
min: 14,
max: 30,
maxVisible: 30,
drawThreshold: 3
},
customScalingFunction: function (min,max,total,value) {
if (max === min) {
return 0.5;
}
else {
var scale = 1 / (max - min);
return Math.max(0,(value - min)*scale);
}
}
},
shape: 'ellipse',
size: 25,
title: undefined,
value: 1,
x: undefined,
y: undefined
};
util.extend(this.options, this.defaultOptions);
}
setOptions(options) {
if (options !== undefined) {
Node.parseOptions(this.options, options);
// update the shape in all nodes
if (options.shape !== undefined) {
for (let nodeId in this.body.nodes) {
if (this.body.nodes.hasOwnProperty(nodeId)) {
this.body.nodes[nodeId].updateShape();
}
}
}
// update the shape size in all nodes
if (options.font !== undefined) {
Label.parseOptions(this.options.font,options);
for (let nodeId in this.body.nodes) {
if (this.body.nodes.hasOwnProperty(nodeId)) {
this.body.nodes[nodeId].updateLabelModule();
this.body.nodes[nodeId]._reset();
}
}
}
// update the shape size in all nodes
if (options.size !== undefined) {
for (let nodeId in this.body.nodes) {
if (this.body.nodes.hasOwnProperty(nodeId)) {
this.body.nodes[nodeId]._reset();
}
}
}
// update the state of the variables if needed
if (options.hidden !== undefined || options.physics !== undefined) {
this.body.emitter.emit('_dataChanged');
}
}
}
/**
* Set a data set with nodes for the network
* @param {Array | DataSet | DataView} nodes The data containing the nodes.
* @private
*/
setData(nodes, doNotEmit = false) {
var oldNodesData = this.body.data.nodes;
if (nodes instanceof DataSet || nodes instanceof DataView) {
this.body.data.nodes = nodes;
}
else if (Array.isArray(nodes)) {
this.body.data.nodes = new DataSet();
this.body.data.nodes.add(nodes);
}
else if (!nodes) {
this.body.data.nodes = new DataSet();
}
else {
throw new TypeError('Array or DataSet expected');
}
if (oldNodesData) {
// unsubscribe from old dataset
util.forEach(this.nodesListeners, function (callback, event) {
oldNodesData.off(event, callback);
});
}
// remove drawn nodes
this.body.nodes = {};
if (this.body.data.nodes) {
// subscribe to new dataset
var me = this;
util.forEach(this.nodesListeners, function (callback, event) {
me.body.data.nodes.on(event, callback);
});
// draw all new nodes
var ids = this.body.data.nodes.getIds();
this.add(ids, true);
}
if (doNotEmit === false) {
this.body.emitter.emit("_dataChanged");
}
}
/**
* Add nodes
* @param {Number[] | String[]} ids
* @private
*/
add(ids, doNotEmit = false) {
var id;
var newNodes = [];
for (var i = 0; i < ids.length; i++) {
id = ids[i];
var properties = this.body.data.nodes.get(id);
var node = this.create(properties);;
newNodes.push(node);
this.body.nodes[id] = node; // note: this may replace an existing node
}
this.layoutEngine.positionInitially(newNodes);
if (doNotEmit === false) {
this.body.emitter.emit("_dataChanged");
}
}
/**
* Update existing nodes, or create them when not yet existing
* @param {Number[] | String[]} ids
* @private
*/
update(ids, changedData) {
var nodes = this.body.nodes;
var dataChanged = false;
for (var i = 0; i < ids.length; i++) {
var id = ids[i];
var node = nodes[id];
var data = changedData[i];
if (node !== undefined) {
// update node
node.setOptions(data, this.constants);
}
else {
dataChanged = true;
// create node
node = this.create(properties);
nodes[id] = node;
}
}
if (dataChanged === true) {
this.body.emitter.emit("_dataChanged");
}
else {
this.body.emitter.emit("_dataUpdated");
}
}
/**
* Remove existing nodes. If nodes do not exist, the method will just ignore it.
* @param {Number[] | String[]} ids
* @private
*/
remove(ids) {
var nodes = this.body.nodes;
for (let i = 0; i < ids.length; i++) {
var id = ids[i];
delete nodes[id];
}
this.body.emitter.emit("_dataChanged");
}
create(properties, constructorClass = Node) {
return new constructorClass(properties, this.body, this.images, this.groups, this.options)
}
}
export default NodesHandler;