Browse Source

Merge branch 'develop'

codeClimate v4.15.0
jos 8 years ago
parent
commit
1eaaeb8532
28 changed files with 733 additions and 422 deletions
  1. +29
    -0
      HISTORY.md
  2. +1
    -1
      bower.json
  3. +1
    -1
      dist/vis.css
  4. +237
    -170
      dist/vis.js
  5. +1
    -1
      dist/vis.map
  6. +1
    -1
      dist/vis.min.css
  7. +22
    -22
      dist/vis.min.js
  8. +12
    -7
      docs/graph2d/index.html
  9. +8
    -8
      docs/network/index.html
  10. +2
    -0
      docs/network/layout.html
  11. +8
    -0
      docs/network/nodes.html
  12. +11
    -0
      docs/timeline/index.html
  13. +4
    -0
      examples/network/layout/hierarchicalLayoutWithoutPhysics.html
  14. +3
    -5
      examples/network/nodeStyles/customGroups.html
  15. +3
    -3
      lib/graph3d/Graph3d.js
  16. +1
    -1
      lib/network/css/network-tooltip.css
  17. +18
    -16
      lib/network/modules/InteractionHandler.js
  18. +159
    -135
      lib/network/modules/LayoutEngine.js
  19. +8
    -4
      lib/network/modules/ManipulationSystem.js
  20. +1
    -0
      lib/network/modules/NodesHandler.js
  21. +2
    -2
      lib/network/modules/components/Edge.js
  22. +31
    -2
      lib/network/modules/components/nodes/util/CircleImageBase.js
  23. +4
    -0
      lib/network/options.js
  24. +1
    -0
      lib/timeline/component/CurrentTime.js
  25. +2
    -2
      lib/timeline/component/DataAxis.js
  26. +1
    -1
      package.json
  27. +158
    -40
      test/networkTest.html
  28. +4
    -0
      test/timeline.html

+ 29
- 0
HISTORY.md View File

@ -2,6 +2,35 @@
http://visjs.org
## 2016-02-23, version 4.15.0
### Timeline
- Implemented `currentTimeTick` event (see #1683).
- Fixed #1630: method `getItemRange` missing in docs.
### Graph2d
- Fixed #1630: method `getDataRange` was wrongly called `getItemRange` in docs.
- Fixed #1655: use parseFloat instead of Number.parseFloat, as the latter is
not supported in IE. Thanks @ttjoseph.
### Graph3d
- Changed the built-in tooltip to show the provided `xLabel`, `yLabel`, and
`zLabel` instead of `'x'`, `'y'`, and `'z'`. Thanks @jacklightbody.
### Network
- Implemented interpolation option for interpolation of images, default true.
- Implemented parentCentralization option for hierarchical layout.
- Fixed #1635: edges are now referring to the correct points.
- Fixed #1644, #1631: overlapping nodes in hierarchical layout should no longer occur.
- Fixed #1575: fixed selection events
- Fixed #1677: updating groups through manipulation now works as it should.
- Fixed #1672: Implemented stepped scaling for nice interpolation of images.
## 2016-02-04, version 4.14.0
### Timeline

+ 1
- 1
bower.json View File

@ -1,6 +1,6 @@
{
"name": "vis",
"main": ["dist/vis.min.js", "dist/vis.min.css"],
"main": ["dist/vis.js", "dist/vis.css"],
"description": "A dynamic, browser-based visualization library.",
"homepage": "http://visjs.org/",
"license": ["Apache-2.0", "MIT"],

+ 1
- 1
dist/vis.css View File

@ -1056,7 +1056,7 @@ div.vis-network-tooltip {
font-family: verdana;
font-size:14px;
font-color:#000000;
color:#000000;
background-color: #f5f4ed;
-moz-border-radius: 3px;

+ 237
- 170
dist/vis.js View File

@ -4,8 +4,8 @@
*
* A dynamic, browser-based visualization library.
*
* @version 4.14.0
* @date 2016-02-04
* @version 4.15.0
* @date 2016-02-23
*
* @license
* Copyright (C) 2011-2016 Almende B.V, http://almende.com
@ -9343,7 +9343,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (typeof this.showTooltip === 'function') {
content.innerHTML = this.showTooltip(dataPoint.point);
} else {
content.innerHTML = '<table>' + '<tr><td>x:</td><td>' + dataPoint.point.x + '</td></tr>' + '<tr><td>y:</td><td>' + dataPoint.point.y + '</td></tr>' + '<tr><td>z:</td><td>' + dataPoint.point.z + '</td></tr>' + '</table>';
content.innerHTML = '<table>' + '<tr><td>' + this.xLabel + ':</td><td>' + dataPoint.point.x + '</td></tr>' + '<tr><td>' + this.yLabel + ':</td><td>' + dataPoint.point.y + '</td></tr>' + '<tr><td>' + this.zLabel + ':</td><td>' + dataPoint.point.z + '</td></tr>' + '</table>';
}
content.style.left = '0';
@ -22170,6 +22170,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (interval > 1000) interval = 1000;
me.redraw();
me.body.emitter.emit('currentTimeTick');
// start a renderTimer to adjust for the new time
me.currentTimeTimer = setTimeout(update, interval);
@ -25592,14 +25593,14 @@ return /******/ (function(modules) { // webpackBootstrap
left: {
range: { min: undefined, max: undefined },
format: function format(value) {
return '' + Number.parseFloat(value.toPrecision(3));
return '' + parseFloat(value.toPrecision(3));
},
title: { text: undefined, style: undefined }
},
right: {
range: { min: undefined, max: undefined },
format: function format(value) {
return '' + Number.parseFloat(value.toPrecision(3));
return '' + parseFloat(value.toPrecision(3));
},
title: { text: undefined, style: undefined }
}
@ -28552,6 +28553,7 @@ return /******/ (function(modules) { // webpackBootstrap
shapeProperties: {
borderDashes: false, // only for borders
borderRadius: 6, // only for box shape
interpolation: true, // only for image and circularImage shapes
useImageSize: false, // only for image and circularImage shapes
useBorderWithImage: false // only for image shape
},
@ -30221,8 +30223,35 @@ return /******/ (function(modules) { // webpackBootstrap
// draw shadow if enabled
this.enableShadow(ctx);
// draw image
ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
var factor = this.imageObj.width / this.width / this.body.view.scale;
if (factor > 2 && this.options.shapeProperties.interpolation === true) {
var w = this.imageObj.width;
var h = this.imageObj.height;
var can2 = document.createElement('canvas');
can2.width = w;
can2.height = w;
var ctx2 = can2.getContext('2d');
factor *= 0.5;
w *= 0.5;
h *= 0.5;
ctx2.drawImage(this.imageObj, 0, 0, w, h);
var distance = 0;
var iterations = 1;
while (factor > 2 && iterations < 4) {
ctx2.drawImage(can2, distance, 0, w, h, distance + w, 0, w / 2, h / 2);
distance += w;
factor *= 0.5;
w *= 0.5;
h *= 0.5;
iterations += 1;
}
ctx.drawImage(can2, distance, 0, w, h, this.left, this.top, this.width, this.height);
} else {
// draw image
ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
}
// disable shadows for other elements.
this.disableShadow(ctx);
@ -32129,8 +32158,8 @@ return /******/ (function(modules) { // webpackBootstrap
var arrowData = {};
// restore edge targets to defaults
this.edgeType.fromPoint = this.from;
this.edgeType.toPoint = this.to;
this.edgeType.fromPoint = this.edgeType.from;
this.edgeType.toPoint = this.edgeType.to;
// from and to arrows give a different end point for edges. we set them here
if (this.options.arrows.from.enabled === true) {
@ -38286,8 +38315,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _determineIfDifferent2 = this._determineIfDifferent(previousSelection, currentSelection);
var nodesChanges = _determineIfDifferent2.nodesChanges;
var edgesChanges = _determineIfDifferent2.edgesChanges;
var nodesChanged = _determineIfDifferent2.nodesChanged;
var edgesChanged = _determineIfDifferent2.edgesChanged;
var nodeSelected = false;
@ -38296,15 +38325,15 @@ return /******/ (function(modules) { // webpackBootstrap
this.selectionHandler._generateClickEvent('selectNode', event, pointer);
selected = true;
nodeSelected = true;
} else if (selectedNodesCount - previouslySelectedNodeCount < 0) {
// node was deselected
this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection);
selected = true;
} else if (selectedNodesCount === previouslySelectedNodeCount && nodesChanges === true) {
} else if (nodesChanged === true && selectedNodesCount > 0) {
this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectNode', event, pointer);
nodeSelected = true;
selected = true;
} else if (selectedNodesCount - previouslySelectedNodeCount < 0) {
// node was deselected
this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection);
selected = true;
}
// handle the selected edges
@ -38312,13 +38341,13 @@ return /******/ (function(modules) { // webpackBootstrap
// edge was selected
this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true;
} else if (selectedEdgesCount - previouslySelectedEdgeCount < 0) {
// edge was deselected
} else if (selectedEdgesCount > 0 && edgesChanged === true) {
this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true;
} else if (selectedEdgesCount === previouslySelectedEdgeCount && edgesChanges === true) {
} else if (selectedEdgesCount - previouslySelectedEdgeCount < 0) {
// edge was deselected
this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true;
}
@ -38333,37 +38362,37 @@ return /******/ (function(modules) { // webpackBootstrap
* This function checks if the nodes and edges previously selected have changed.
* @param previousSelection
* @param currentSelection
* @returns {{nodesChanges: boolean, edgesChanges: boolean}}
* @returns {{nodesChanged: boolean, edgesChanged: boolean}}
* @private
*/
}, {
key: '_determineIfDifferent',
value: function _determineIfDifferent(previousSelection, currentSelection) {
var nodesChanges = false;
var edgesChanges = false;
var nodesChanged = false;
var edgesChanged = false;
for (var i = 0; i < previousSelection.nodes.length; i++) {
if (currentSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) {
nodesChanges = true;
nodesChanged = true;
}
}
for (var i = 0; i < currentSelection.nodes.length; i++) {
if (previousSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) {
nodesChanges = true;
nodesChanged = true;
}
}
for (var i = 0; i < previousSelection.edges.length; i++) {
if (currentSelection.edges.indexOf(previousSelection.edges[i]) === -1) {
edgesChanges = true;
edgesChanged = true;
}
}
for (var i = 0; i < currentSelection.edges.length; i++) {
if (previousSelection.edges.indexOf(previousSelection.edges[i]) === -1) {
edgesChanges = true;
edgesChanged = true;
}
}
return { nodesChanges: nodesChanges, edgesChanges: edgesChanges };
return { nodesChanged: nodesChanged, edgesChanged: edgesChanged };
}
/**
@ -40083,6 +40112,7 @@ return /******/ (function(modules) { // webpackBootstrap
treeSpacing: 200,
blockShifting: true,
edgeMinimization: true,
parentCentralization: true,
direction: 'UD', // UD, DU, LR, RL
sortMethod: 'hubsize' // hubsize, directed
}
@ -40364,8 +40394,8 @@ return /******/ (function(modules) { // webpackBootstrap
var undefinedLevel = false;
this.hierarchicalLevels = {};
this.lastNodeOnLevel = {};
this.hierarchicalParents = {};
this.hierarchicalChildren = {};
this.hierarchicalChildrenReference = {};
this.hierarchicalParentReference = {};
this.hierarchicalTrees = {};
this.treeIndex = -1;
@ -40447,9 +40477,7 @@ return /******/ (function(modules) { // webpackBootstrap
var treeSizes = getTreeSizes();
for (var i = 0; i < treeSizes.length - 1; i++) {
var diff = treeSizes[i].max - treeSizes[i + 1].min;
if (diff !== _this2.options.hierarchical.treeSpacing) {
shiftTree(i + 1, diff - _this2.options.hierarchical.treeSpacing);
}
shiftTree(i + 1, diff + _this2.options.hierarchical.treeSpacing);
}
};
@ -40458,7 +40486,9 @@ return /******/ (function(modules) { // webpackBootstrap
for (var nodeId in _this2.hierarchicalTrees) {
if (_this2.hierarchicalTrees.hasOwnProperty(nodeId)) {
if (_this2.hierarchicalTrees[nodeId] === index) {
_this2._setPositionForHierarchy(_this2.body.nodes[nodeId], offset, undefined, true);
var node = _this2.body.nodes[nodeId];
var pos = _this2._getPositionForHierarchy(node);
_this2._setPositionForHierarchy(node, pos + offset, undefined, true);
}
}
}
@ -40483,7 +40513,7 @@ return /******/ (function(modules) { // webpackBootstrap
// get the width of all trees
var getTreeSizes = function getTreeSizes() {
var treeWidths = [];
for (var i = 0; i < _this2.treeIndex; i++) {
for (var i = 0; i <= _this2.treeIndex; i++) {
treeWidths.push(getTreeSize(i));
}
return treeWidths;
@ -40492,8 +40522,8 @@ return /******/ (function(modules) { // webpackBootstrap
// get a map of all nodes in this branch
var getBranchNodes = function getBranchNodes(source, map) {
map[source.id] = true;
if (_this2.hierarchicalParents[source.id]) {
var children = _this2.hierarchicalParents[source.id].children;
if (_this2.hierarchicalChildrenReference[source.id]) {
var children = _this2.hierarchicalChildrenReference[source.id];
if (children.length > 0) {
for (var i = 0; i < children.length; i++) {
getBranchNodes(_this2.body.nodes[children[i]], map);
@ -40543,8 +40573,8 @@ return /******/ (function(modules) { // webpackBootstrap
// get the maximum level of a branch.
var getMaxLevel = function getMaxLevel(nodeId) {
var level = _this2.hierarchicalLevels[nodeId];
if (_this2.hierarchicalParents[nodeId]) {
var children = _this2.hierarchicalParents[nodeId].children;
if (_this2.hierarchicalChildrenReference[nodeId]) {
var children = _this2.hierarchicalChildrenReference[nodeId];
if (children.length > 0) {
for (var i = 0; i < children.length; i++) {
level = Math.max(level, getMaxLevel(children[i]));
@ -40563,13 +40593,12 @@ return /******/ (function(modules) { // webpackBootstrap
// check if two nodes have the same parent(s)
var hasSameParent = function hasSameParent(node1, node2) {
var parents1 = _this2.hierarchicalChildren[node1.id];
var parents2 = _this2.hierarchicalChildren[node2.id];
var parents1 = _this2.hierarchicalParentReference[node1.id];
var parents2 = _this2.hierarchicalParentReference[node2.id];
if (parents1 === undefined || parents2 === undefined) {
return false;
}
parents1 = parents1.parents;
parents2 = parents2.parents;
for (var i = 0; i < parents1.length; i++) {
for (var j = 0; j < parents2.length; j++) {
if (parents1[i] == parents2[j]) {
@ -40782,7 +40811,7 @@ return /******/ (function(modules) { // webpackBootstrap
}
if (newPosition !== nodePosition) {
//console.log("moving Node:",diff, minSpace, maxSpace)
//console.log("moving Node:",diff, minSpace, maxSpace);
_this2._setPositionForHierarchy(node, newPosition, undefined, true);
//this.body.emitter.emit("_redraw");
stillShifting = true;
@ -40816,7 +40845,7 @@ return /******/ (function(modules) { // webpackBootstrap
}
};
//// method to remove whitespace between branches. Because we do bottom up, we can center the parents.
// method to remove whitespace between branches. Because we do bottom up, we can center the parents.
var shiftBranchesCloserBottomUp = function shiftBranchesCloserBottomUp(iterations) {
var levels = Object.keys(_this2.distributionOrdering);
levels = levels.reverse();
@ -40837,6 +40866,19 @@ return /******/ (function(modules) { // webpackBootstrap
}
};
// center all parents
var centerAllParentsBottomUp = function centerAllParentsBottomUp() {
var levels = Object.keys(_this2.distributionOrdering);
levels = levels.reverse();
for (var i = 0; i < levels.length; i++) {
var level = levels[i];
var levelNodes = _this2.distributionOrdering[level];
for (var j = 0; j < levelNodes.length; j++) {
_this2._centerParent(levelNodes[j]);
}
}
};
// the actual work is done here.
if (this.options.hierarchical.blockShifting === true) {
shiftBranchesCloserBottomUp(5);
@ -40848,6 +40890,10 @@ return /******/ (function(modules) { // webpackBootstrap
minimizeEdgeLengthBottomUp(20);
}
if (this.options.hierarchical.parentCentralization === true) {
centerAllParentsBottomUp();
}
shiftTrees();
}
@ -40902,16 +40948,16 @@ return /******/ (function(modules) { // webpackBootstrap
}, {
key: '_centerParent',
value: function _centerParent(node) {
if (this.hierarchicalChildren[node.id]) {
var parents = this.hierarchicalChildren[node.id].parents;
if (this.hierarchicalParentReference[node.id]) {
var parents = this.hierarchicalParentReference[node.id];
for (var i = 0; i < parents.length; i++) {
var parentId = parents[i];
var parentNode = this.body.nodes[parentId];
if (this.hierarchicalParents[parentId]) {
if (this.hierarchicalChildrenReference[parentId]) {
// get the range of the children
var minPos = 1e9;
var maxPos = -1e9;
var children = this.hierarchicalParents[parentId].children;
var children = this.hierarchicalChildrenReference[parentId];
if (children.length > 0) {
for (var _i = 0; _i < children.length; _i++) {
var childNode = this.body.nodes[children[_i]];
@ -40956,19 +41002,113 @@ return /******/ (function(modules) { // webpackBootstrap
var nodeArray = Object.keys(distribution[level]);
nodeArray = this._indexArrayToNodes(nodeArray);
this._sortNodeArray(nodeArray);
var handledNodeCount = 0;
for (var i = 0; i < nodeArray.length; i++) {
var node = nodeArray[i];
if (this.positionedNodes[node.id] === undefined) {
this._setPositionForHierarchy(node, this.options.hierarchical.nodeSpacing * i, level);
this.positionedNodes[node.id] = true;
this._placeBranchNodes(node.id, level);
var pos = this.options.hierarchical.nodeSpacing * handledNodeCount;
// we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y
if (handledNodeCount > 0) {
pos = this._getPositionForHierarchy(nodeArray[i - 1]) + this.options.hierarchical.nodeSpacing;
}
this._setPositionForHierarchy(node, pos, level);
this._validataPositionAndContinue(node, level, pos);
handledNodeCount++;
}
}
}
}
}
/**
* This is a recursively called function to enumerate the branches from the largest hubs and place the nodes
* on a X position that ensures there will be no overlap.
*
* @param parentId
* @param parentLevel
* @private
*/
}, {
key: '_placeBranchNodes',
value: function _placeBranchNodes(parentId, parentLevel) {
// if this is not a parent, cancel the placing. This can happen with multiple parents to one child.
if (this.hierarchicalChildrenReference[parentId] === undefined) {
return;
}
// get a list of childNodes
var childNodes = [];
for (var i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) {
childNodes.push(this.body.nodes[this.hierarchicalChildrenReference[parentId][i]]);
}
// use the positions to order the nodes.
this._sortNodeArray(childNodes);
// position the childNodes
for (var i = 0; i < childNodes.length; i++) {
var childNode = childNodes[i];
var childNodeLevel = this.hierarchicalLevels[childNode.id];
// check if the child node is below the parent node and if it has already been positioned.
if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) {
// get the amount of space required for this node. If parent the width is based on the amount of children.
var pos = undefined;
// we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y
if (i === 0) {
pos = this._getPositionForHierarchy(this.body.nodes[parentId]);
} else {
pos = this._getPositionForHierarchy(childNodes[i - 1]) + this.options.hierarchical.nodeSpacing;
}
this._setPositionForHierarchy(childNode, pos, childNodeLevel);
this._validataPositionAndContinue(childNode, childNodeLevel, pos);
} else {
return;
}
}
// center the parent nodes.
var minPos = 1e9;
var maxPos = -1e9;
for (var i = 0; i < childNodes.length; i++) {
var childNodeId = childNodes[i].id;
minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
}
this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel);
}
/**
* This method checks for overlap and if required shifts the branch. It also keeps records of positioned nodes.
* Finally it will call _placeBranchNodes to place the branch nodes.
* @param node
* @param level
* @param pos
* @private
*/
}, {
key: '_validataPositionAndContinue',
value: function _validataPositionAndContinue(node, level, pos) {
// if overlap has been detected, we shift the branch
if (this.lastNodeOnLevel[level] !== undefined) {
var previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[level]]);
if (pos - previousPos < this.options.hierarchical.nodeSpacing) {
var diff = previousPos + this.options.hierarchical.nodeSpacing - pos;
var sharedParent = this._findCommonParent(this.lastNodeOnLevel[level], node.id);
this._shiftBlock(sharedParent.withChild, diff);
}
}
// store change in position.
this.lastNodeOnLevel[level] = node.id;
this.positionedNodes[node.id] = true;
this._placeBranchNodes(node.id, level);
}
/**
* Receives an array with node indices and returns an array with the actual node references. Used for sorting based on
* node properties.
@ -41178,14 +41318,14 @@ return /******/ (function(modules) { // webpackBootstrap
if (_this6.hierarchicalLevels[childNode.id] > _this6.hierarchicalLevels[parentNode.id]) {
var parentNodeId = parentNode.id;
var childNodeId = childNode.id;
if (_this6.hierarchicalParents[parentNodeId] === undefined) {
_this6.hierarchicalParents[parentNodeId] = { children: [], amount: 0 };
if (_this6.hierarchicalChildrenReference[parentNodeId] === undefined) {
_this6.hierarchicalChildrenReference[parentNodeId] = [];
}
_this6.hierarchicalParents[parentNodeId].children.push(childNodeId);
if (_this6.hierarchicalChildren[childNodeId] === undefined) {
_this6.hierarchicalChildren[childNodeId] = { parents: [], amount: 0 };
_this6.hierarchicalChildrenReference[parentNodeId].push(childNodeId);
if (_this6.hierarchicalParentReference[childNodeId] === undefined) {
_this6.hierarchicalParentReference[childNodeId] = [];
}
_this6.hierarchicalChildren[childNodeId].parents.push(parentNodeId);
_this6.hierarchicalParentReference[childNodeId].push(parentNodeId);
}
};
@ -41201,11 +41341,21 @@ return /******/ (function(modules) { // webpackBootstrap
}, {
key: '_crawlNetwork',
value: function _crawlNetwork(callback, startingNodeId) {
var _this7 = this;
if (callback === undefined) callback = function () {};
var progress = {};
var crawler = function crawler(node) {
var treeIndex = 0;
var crawler = function crawler(node, tree) {
if (progress[node.id] === undefined) {
if (_this7.hierarchicalTrees[node.id] === undefined) {
_this7.hierarchicalTrees[node.id] = tree;
_this7.treeIndex = Math.max(tree, _this7.treeIndex);
}
progress[node.id] = true;
var childNode = undefined;
for (var i = 0; i < node.edges.length; i++) {
@ -41218,7 +41368,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (node.id !== childNode.id) {
callback(node, childNode, node.edges[i]);
crawler(childNode);
crawler(childNode, tree);
}
}
}
@ -41229,7 +41379,10 @@ return /******/ (function(modules) { // webpackBootstrap
if (startingNodeId === undefined) {
for (var i = 0; i < this.body.nodeIndices.length; i++) {
var node = this.body.nodes[this.body.nodeIndices[i]];
crawler(node);
if (progress[node.id] === undefined) {
crawler(node, treeIndex);
treeIndex += 1;
}
}
} else {
var node = this.body.nodes[startingNodeId];
@ -41241,80 +41394,6 @@ return /******/ (function(modules) { // webpackBootstrap
}
}
/**
* This is a recursively called function to enumerate the branches from the largest hubs and place the nodes
* on a X position that ensures there will be no overlap.
*
* @param parentId
* @param parentLevel
* @private
*/
}, {
key: '_placeBranchNodes',
value: function _placeBranchNodes(parentId, parentLevel) {
// if this is not a parent, cancel the placing. This can happen with multiple parents to one child.
if (this.hierarchicalParents[parentId] === undefined) {
return;
}
// get a list of childNodes
var childNodes = [];
for (var i = 0; i < this.hierarchicalParents[parentId].children.length; i++) {
childNodes.push(this.body.nodes[this.hierarchicalParents[parentId].children[i]]);
}
// use the positions to order the nodes.
this._sortNodeArray(childNodes);
// position the childNodes
for (var i = 0; i < childNodes.length; i++) {
var childNode = childNodes[i];
var childNodeLevel = this.hierarchicalLevels[childNode.id];
// check if the child node is below the parent node and if it has already been positioned.
if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) {
// get the amount of space required for this node. If parent the width is based on the amount of children.
var pos = undefined;
// we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y
if (i === 0) {
pos = this._getPositionForHierarchy(this.body.nodes[parentId]);
} else {
pos = this._getPositionForHierarchy(childNodes[i - 1]) + this.options.hierarchical.nodeSpacing;
}
this._setPositionForHierarchy(childNode, pos, childNodeLevel);
// if overlap has been detected, we shift the branch
if (this.lastNodeOnLevel[childNodeLevel] !== undefined) {
var previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[childNodeLevel]]);
if (pos - previousPos < this.options.hierarchical.nodeSpacing) {
var diff = previousPos + this.options.hierarchical.nodeSpacing - pos;
var sharedParent = this._findCommonParent(this.lastNodeOnLevel[childNodeLevel], childNode.id);
this._shiftBlock(sharedParent.withChild, diff);
}
}
// store change in position.
this.lastNodeOnLevel[childNodeLevel] = childNode.id;
this.positionedNodes[childNode.id] = true;
this._placeBranchNodes(childNode.id, childNodeLevel);
} else {
return;
}
}
// center the parent nodes.
var minPos = 1e9;
var maxPos = -1e9;
for (var i = 0; i < childNodes.length; i++) {
var childNodeId = childNodes[i].id;
minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
}
this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel);
}
/**
* Shift a branch a certain distance
* @param parentId
@ -41329,9 +41408,9 @@ return /******/ (function(modules) { // webpackBootstrap
} else {
this.body.nodes[parentId].y += diff;
}
if (this.hierarchicalParents[parentId] !== undefined) {
for (var i = 0; i < this.hierarchicalParents[parentId].children.length; i++) {
this._shiftBlock(this.hierarchicalParents[parentId].children[i], diff);
if (this.hierarchicalChildrenReference[parentId] !== undefined) {
for (var i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) {
this._shiftBlock(this.hierarchicalChildrenReference[parentId][i], diff);
}
}
}
@ -41346,22 +41425,22 @@ return /******/ (function(modules) { // webpackBootstrap
}, {
key: '_findCommonParent',
value: function _findCommonParent(childA, childB) {
var _this7 = this;
var _this8 = this;
var parents = {};
var iterateParents = function iterateParents(parents, child) {
if (_this7.hierarchicalChildren[child] !== undefined) {
for (var i = 0; i < _this7.hierarchicalChildren[child].parents.length; i++) {
var _parent = _this7.hierarchicalChildren[child].parents[i];
if (_this8.hierarchicalParentReference[child] !== undefined) {
for (var i = 0; i < _this8.hierarchicalParentReference[child].length; i++) {
var _parent = _this8.hierarchicalParentReference[child][i];
parents[_parent] = true;
iterateParents(parents, _parent);
}
}
};
var findParent = function findParent(parents, child) {
if (_this7.hierarchicalChildren[child] !== undefined) {
for (var i = 0; i < _this7.hierarchicalChildren[child].parents.length; i++) {
var _parent2 = _this7.hierarchicalChildren[child].parents[i];
if (_this8.hierarchicalParentReference[child] !== undefined) {
for (var i = 0; i < _this8.hierarchicalParentReference[child].length; i++) {
var _parent2 = _this8.hierarchicalParentReference[child][i];
if (parents[_parent2] !== undefined) {
return { foundParent: _parent2, withChild: child };
}
@ -41390,6 +41469,7 @@ return /******/ (function(modules) { // webpackBootstrap
value: function _setPositionForHierarchy(node, position, level) {
var doNotUpdate = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
//console.log('_setPositionForHierarchy',node.id, position)
if (doNotUpdate !== true) {
if (this.distributionOrdering[level] === undefined) {
this.distributionOrdering[level] = [];
@ -41401,27 +41481,6 @@ return /******/ (function(modules) { // webpackBootstrap
this.distributionIndex[node.id] = this.distributionOrdering[level].length - 1;
}
this.distributionOrderingPresence[level][node.id] = true;
if (this.hierarchicalTrees[node.id] === undefined) {
if (this.hierarchicalChildren[node.id] !== undefined) {
var tree = 1;
// get the lowest tree denominator.
for (var i = 0; i < this.hierarchicalChildren[node.id].parents.length; i++) {
var parentId = this.hierarchicalChildren[node.id].parents[i];
if (this.hierarchicalTrees[parentId] !== undefined) {
//tree = Math.min(tree,this.hierarchicalTrees[parentId]);
tree = this.hierarchicalTrees[parentId];
}
}
//for (let i = 0; i < this.hierarchicalChildren.parents.length; i++) {
// let parentId = this.hierarchicalChildren.parents[i];
// this.hierarchicalTrees[parentId] = tree;
//}
this.hierarchicalTrees[node.id] = tree;
} else {
this.hierarchicalTrees[node.id] = ++this.treeIndex;
}
}
}
if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') {
@ -41774,7 +41833,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.inMode = 'editNode';
if (typeof this.options.editNode === 'function') {
if (node.isCluster !== true) {
var data = util.deepExtend({}, node.options, true);
var data = util.deepExtend({}, node.options, false);
data.x = node.x;
data.y = node.y;
@ -42395,6 +42454,11 @@ return /******/ (function(modules) { // webpackBootstrap
edge.edgeType.to = to;
}
// we use the selection to find the node that is being dragged. We explicitly select it here.
if (this.selectedControlNode !== undefined) {
this.selectionHandler.selectObject(this.selectedControlNode);
}
this.body.emitter.emit('_redraw');
}
@ -42409,7 +42473,6 @@ return /******/ (function(modules) { // webpackBootstrap
this.body.emitter.emit('disablePhysics');
var pointer = this.body.functions.getPointer(event.center);
var pos = this.canvas.DOMtoCanvas(pointer);
if (this.selectedControlNode !== undefined) {
this.selectedControlNode.x = pos.x;
this.selectedControlNode.y = pos.y;
@ -42433,12 +42496,13 @@ return /******/ (function(modules) { // webpackBootstrap
var pointer = this.body.functions.getPointer(event.center);
var pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
var edge = this.body.edges[this.edgeBeingEditedId];
// if the node that was dragged is not a control node, return
if (this.selectedControlNode === undefined) {
return;
}
// we use the selection to find the node that is being dragged. We explicitly DEselect the control node here.
this.selectionHandler.unselectAll();
var overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj);
var node = undefined;
for (var i = overlappingNodeIds.length - 1; i >= 0; i--) {
@ -42447,7 +42511,6 @@ return /******/ (function(modules) { // webpackBootstrap
break;
}
}
// perform the connection
if (node !== undefined && this.selectedControlNode !== undefined) {
if (node.isCluster === true) {
@ -42825,6 +42888,7 @@ return /******/ (function(modules) { // webpackBootstrap
treeSpacing: { number: number },
blockShifting: { boolean: boolean },
edgeMinimization: { boolean: boolean },
parentCentralization: { boolean: boolean },
direction: { string: ['UD', 'DU', 'LR', 'RL'] }, // UD, DU, LR, RL
sortMethod: { string: ['hubsize', 'directed'] }, // hubsize, directed
__type__: { object: object, boolean: boolean }
@ -42918,6 +42982,7 @@ return /******/ (function(modules) { // webpackBootstrap
shapeProperties: {
borderDashes: { boolean: boolean, array: array },
borderRadius: { number: number },
interpolation: { boolean: boolean },
useImageSize: { boolean: boolean },
useBorderWithImage: { boolean: boolean },
__type__: { object: object }
@ -43058,6 +43123,7 @@ return /******/ (function(modules) { // webpackBootstrap
shapeProperties: {
borderDashes: false,
borderRadius: [6, 0, 20, 1],
interpolation: true,
useImageSize: false
},
size: [25, 0, 200, 1]
@ -43128,6 +43194,7 @@ return /******/ (function(modules) { // webpackBootstrap
treeSpacing: [200, 20, 500, 5],
blockShifting: true,
edgeMinimization: true,
parentCentralization: true,
direction: ['UD', 'DU', 'LR', 'RL'], // UD, DU, LR, RL
sortMethod: ['hubsize', 'directed'] // hubsize, directed
}

+ 1
- 1
dist/vis.map
File diff suppressed because it is too large
View File


+ 1
- 1
dist/vis.min.css
File diff suppressed because it is too large
View File


+ 22
- 22
dist/vis.min.js
File diff suppressed because it is too large
View File


+ 12
- 7
docs/graph2d/index.html View File

@ -1145,6 +1145,12 @@ function (option, path) {
</td>
</tr>
<tr>
<td>getDataRange()</td>
<td>Object</td>
<td>Get the range of all the items as an object containing <code>min: Date</code> and <code>max: Date</code>.</td>
</tr>
<tr id="getEventProperties">
<td>getEventProperties(event)</td>
<td>Object</td>
@ -1176,13 +1182,6 @@ function (option, path) {
<td>Get the current visible window. Returns an object with properties <code>start: Date</code> and <code>end: Date</code>.</td>
</tr>
<tr>
<td>getItemRange()</td>
<td>Object</td>
<td>Get the range of all the items as an object containing <code>min: Date</code> and <code>max: Date</code>.</td>
</tr>
<tr>
<td>isGroupVisible(groupId)</td>
<td>Boolean</td>
@ -1305,6 +1304,12 @@ Graph2d.off('rangechanged', onChange);
<th>Properties</th>
<th>Description</th>
</tr>
<tr>
<td>currentTimeTick</td>
<td>Fired when the current time bar redraws. The rate depends on the zoom level.</td>
</tr>
<tr>
<td>click</td>
<td>

+ 8
- 8
docs/network/index.html View File

@ -339,49 +339,49 @@ network.setOptions(options);
<tr><td id="event_configure">configure</td>
<td>Object</td>
<td>Object</td>
<td href="./configure.html">All options in this object are explained in the configure module.</a>
<td>All options in this object are explained in the <a href="./configure.html">configure module</a>.
</td>
</tr>
<tr><td id="event_edges">edges</td>
<td>Object</td>
<td>Object</td>
<td href="./edges.html">All options in this object are explained in the edges module.</a>
<td>All options in this object are explained in the <a href="./edges.html">edges module</a>.
</td>
</tr>
<tr><td id="event_nodes">nodes</td>
<td>Object</td>
<td>Object</td>
<td href="./nodes.html">All options in this object are explained in the nodes module.</a>
<td>All options in this object are explained in the <a href="./nodes.html">nodes module</a>.
</td>
</tr>
<tr><td id="event_groups">groups</td>
<td>Object</td>
<td>Object</td>
<td href="./groups.html">All options in this object are explained in the groups module.</a>
<td>All options in this object are explained in the <a href="./groups.html">groups module</a>.
</td>
</tr>
<tr><td id="event_layout">layout</td>
<td>Object</td>
<td>Object</td>
<td href="./layout.html">All options in this object are explained in the layout module.</a>
<td>All options in this object are explained in the <a href="./layout.html">layout module</a>.
</td>
</tr>
<tr><td id="event_interaction">interaction</td>
<td>Object</td>
<td>Object</td>
<td href="./interaction.html">All options in this object are explained in the interaction module.</a>
<td>All options in this object are explained in the <a href="./interaction.html">interaction module</a>.
</td>
</tr>
<tr><td id="event_manipulation">manipulation</td>
<td>Object</td>
<td>Object</td>
<td href="./manipulation.html">All options in this object are explained in the manipulation module.</a>
<td>All options in this object are explained in the <a href="./manipulation.html">manipulation module</a>.
</td>
</tr>
<tr><td id="event_physics">physics</td>
<td>Object</td>
<td>Object</td>
<td href="./physics.html">All options in this object are explained in the physics module.</a>
<td>All options in this object are explained in the <a href="./physics.html">physics module</a>.
</td>
</tr>
</table>

+ 2
- 0
docs/network/layout.html View File

@ -109,6 +109,7 @@ var options = {
treeSpacing: 200,
blockShifting: true,
edgeMinimization: true,
parentCentralization: true,
direction: 'UD', // UD, DU, LR, RL
sortMethod: 'hubsize' // hubsize, directed
}
@ -142,6 +143,7 @@ network.setOptions(options);
it's branch along with it for as far as it can, respecting the nodeSpacing on any level. This is mainly for the initial layout. If you enable physics, they layout will be determined by the physics. This will greatly speed up the stabilization time though!</td></tr>
<tr parent="hierarchical" class="hidden"><td class="indent">hierarchical.edgeMinimization</td><td>Boolean</td><td><code>true</code></td> <td>Method for reducing whitespace. Can be used alone or together with block shifting. Enabling block shifting will usually speed up the layout process.
Each node will try to move along its free axis to reduce the total length of it's edges. This is mainly for the initial layout. If you enable physics, they layout will be determined by the physics. This will greatly speed up the stabilization time though!</td></tr>
<tr parent="hierarchical" class="hidden"><td class="indent">hierarchical.parentCentralization</td><td>Boolean</td><td><code>true</code></td> <td>When true, the parents nodes will be centered again after the the layout algorithm has been finished.</td></tr>
<tr parent="hierarchical" class="hidden"><td class="indent">hierarchical.direction</td><td>String</td><td><code>'UD'</code></td> <td>The direction of the hierarchical layout. The available options are: <code>UD, DU, LR, RL</code>. To simplify: up-down, down-up, left-right, right-left.</td></tr>
<tr parent="hierarchical" class="hidden"><td class="indent">hierarchical.sortMethod</td><td>String</td><td><code>'hubsize'</code></td> <td>The algorithm used to ascertain the levels of the nodes based on the data. The possible options are: <code>hubsize, directed</code>. <br><br>
Hubsize takes the nodes with the most edges and puts them at the top. From that the rest of the hierarchy is evaluated. <br><br>

+ 8
- 0
docs/network/nodes.html View File

@ -180,6 +180,7 @@ var options = {
shapeProperties: {
borderDashes: false, // only for borders
borderRadius: 6, // only for box shape
interpolation: false, // only for image and circularImage shapes
useImageSize: false, // only for image and circularImage shapes
useBorderWithImage: false // only for image shape
}
@ -645,6 +646,13 @@ mySize = minSize + diff * scale;
<td>This property is used only for the <code>box</code> shape. It allows you to determine the roundness of the corners of the shape.
</td>
</tr>
<tr parent="shapeProperties" class="hidden">
<td class="indent">shapeProperties.interpolation</td>
<td>Boolean</td>
<td><code>true</code></td>
<td>This property only applies to the <code>image</code> and <code>circularImage</code> shapes. When true, the image is resampled when scaled down, resulting in a nicer image at the cost of computional time.</i>
</td>
</tr>
<tr parent="shapeProperties" class="hidden">
<td class="indent">shapeProperties.useImageSize</td>
<td>Boolean</td>

+ 11
- 0
docs/timeline/index.html View File

@ -1150,6 +1150,12 @@ document.getElementById('myTimeline').onclick = function (event) {
</td>
</tr>
<tr>
<td>getItemRange()</td>
<td>Object</td>
<td>Get the range of all the items as an object containing <code>min: Date</code> and <code>max: Date</code>.</td>
</tr>
<tr>
<td>getSelection()</td>
<td>number[]</td>
@ -1342,6 +1348,11 @@ timeline.off('select', onSelect);
<th>Description</th>
</tr>
<tr>
<td>currentTimeTick</td>
<td>Fired when the current time bar redraws. The rate depends on the zoom level.</td>
</tr>
<tr>
<td>click</td>
<td>

+ 4
- 0
examples/network/layout/hierarchicalLayoutWithoutPhysics.html View File

@ -47,6 +47,10 @@ The hierarchical layout can now be controlled without the use of physics. This i
<td>Method for reducing whitespace. Can be used alone or together with block shifting. Enabling block shifting will usually speed up the layout process.
Each node will try to move along its free axis to reduce the total length of it's edges.</td>
</tr>
<tr>
<td><code>parentCentralization</code></td>
<td>When true, the parents nodes will be centered again after the the layout algorithm has been finished.</td>
</tr>
</table>
<br /><br />
Play with the settings below the network and see how the layout changes!

+ 3
- 5
examples/network/nodeStyles/customGroups.html View File

@ -26,12 +26,9 @@
</head>
<body>
<i class="fa fa-flag"></i> We use an icon once in the DOM so the CSS for fontAwesome is loaded.</h2>
<div id="mynetwork"></div>
<script type="text/javascript">
var color = 'gray';
var len = undefined;
var nodes = [
{id: 0, label: "0", group: 'source'},
{id: 1, label: "1", group: 'icons'},
@ -128,7 +125,8 @@
}
}
};
network = new vis.Network(container, data, options);
var network = new vis.Network(container, data, options);
</script>
</body>
</html>

+ 3
- 3
lib/graph3d/Graph3d.js View File

@ -2237,9 +2237,9 @@ Graph3d.prototype._showTooltip = function (dataPoint) {
}
else {
content.innerHTML = '<table>' +
'<tr><td>x:</td><td>' + dataPoint.point.x + '</td></tr>' +
'<tr><td>y:</td><td>' + dataPoint.point.y + '</td></tr>' +
'<tr><td>z:</td><td>' + dataPoint.point.z + '</td></tr>' +
'<tr><td>' + this.xLabel + ':</td><td>' + dataPoint.point.x + '</td></tr>' +
'<tr><td>' + this.yLabel + ':</td><td>' + dataPoint.point.y + '</td></tr>' +
'<tr><td>' + this.zLabel + ':</td><td>' + dataPoint.point.z + '</td></tr>' +
'</table>';
}

+ 1
- 1
lib/network/css/network-tooltip.css View File

@ -6,7 +6,7 @@ div.vis-network-tooltip {
font-family: verdana;
font-size:14px;
font-color:#000000;
color:#000000;
background-color: #f5f4ed;
-moz-border-radius: 3px;

+ 18
- 16
lib/network/modules/InteractionHandler.js View File

@ -189,7 +189,7 @@ class InteractionHandler {
let selectedNodesCount = this.selectionHandler._getSelectedNodeCount();
let currentSelection = this.selectionHandler.getSelection();
let {nodesChanges, edgesChanges} = this._determineIfDifferent(previousSelection, currentSelection);
let {nodesChanged, edgesChanged} = this._determineIfDifferent(previousSelection, currentSelection);
let nodeSelected = false;
if (selectedNodesCount - previouslySelectedNodeCount > 0) { // node was selected
@ -197,32 +197,34 @@ class InteractionHandler {
selected = true;
nodeSelected = true;
}
else if (selectedNodesCount - previouslySelectedNodeCount < 0) { // node was deselected
else if (nodesChanged === true && selectedNodesCount > 0) {
this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectNode', event, pointer);
nodeSelected = true;
selected = true;
}
else if (selectedNodesCount === previouslySelectedNodeCount && nodesChanges === true) {
else if (selectedNodesCount - previouslySelectedNodeCount < 0) { // node was deselected
this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectNode', event, pointer);
nodeSelected = true;
selected = true;
}
// handle the selected edges
if (selectedEdgesCount - previouslySelectedEdgeCount > 0 && nodeSelected === false) { // edge was selected
this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true;
}
else if (selectedEdgesCount - previouslySelectedEdgeCount < 0) { // edge was deselected
else if (selectedEdgesCount > 0 && edgesChanged === true) {
this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true;
}
else if (selectedEdgesCount === previouslySelectedEdgeCount && edgesChanges === true) {
else if (selectedEdgesCount - previouslySelectedEdgeCount < 0) { // edge was deselected
this.selectionHandler._generateClickEvent('deselectEdge', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true;
}
// fire the select event if anything has been selected or deselected
if (selected === true) { // select or unselect
this.selectionHandler._generateClickEvent('select', event, pointer);
@ -234,35 +236,35 @@ class InteractionHandler {
* This function checks if the nodes and edges previously selected have changed.
* @param previousSelection
* @param currentSelection
* @returns {{nodesChanges: boolean, edgesChanges: boolean}}
* @returns {{nodesChanged: boolean, edgesChanged: boolean}}
* @private
*/
_determineIfDifferent(previousSelection,currentSelection) {
let nodesChanges = false;
let edgesChanges = false;
let nodesChanged = false;
let edgesChanged = false;
for (let i = 0; i < previousSelection.nodes.length; i++) {
if (currentSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) {
nodesChanges = true;
nodesChanged = true;
}
}
for (let i = 0; i < currentSelection.nodes.length; i++) {
if (previousSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) {
nodesChanges = true;
nodesChanged = true;
}
}
for (let i = 0; i < previousSelection.edges.length; i++) {
if (currentSelection.edges.indexOf(previousSelection.edges[i]) === -1) {
edgesChanges = true;
edgesChanged = true;
}
}
for (let i = 0; i < currentSelection.edges.length; i++) {
if (previousSelection.edges.indexOf(previousSelection.edges[i]) === -1) {
edgesChanges = true;
edgesChanged = true;
}
}
return {nodesChanges, edgesChanges};
return {nodesChanged, edgesChanged};
}

+ 159
- 135
lib/network/modules/LayoutEngine.js View File

@ -23,6 +23,7 @@ class LayoutEngine {
treeSpacing: 200,
blockShifting: true,
edgeMinimization: true,
parentCentralization: true,
direction: 'UD', // UD, DU, LR, RL
sortMethod: 'hubsize' // hubsize, directed
}
@ -294,8 +295,8 @@ class LayoutEngine {
let undefinedLevel = false;
this.hierarchicalLevels = {};
this.lastNodeOnLevel = {};
this.hierarchicalParents = {};
this.hierarchicalChildren = {};
this.hierarchicalChildrenReference = {};
this.hierarchicalParentReference = {};
this.hierarchicalTrees = {};
this.treeIndex = -1;
@ -379,9 +380,7 @@ class LayoutEngine {
let treeSizes = getTreeSizes();
for (let i = 0; i < treeSizes.length - 1; i++) {
let diff = treeSizes[i].max - treeSizes[i+1].min;
if (diff !== this.options.hierarchical.treeSpacing) {
shiftTree(i + 1, diff - this.options.hierarchical.treeSpacing);
}
shiftTree(i + 1, diff + this.options.hierarchical.treeSpacing);
}
};
@ -390,7 +389,9 @@ class LayoutEngine {
for (let nodeId in this.hierarchicalTrees) {
if (this.hierarchicalTrees.hasOwnProperty(nodeId)) {
if (this.hierarchicalTrees[nodeId] === index) {
this._setPositionForHierarchy(this.body.nodes[nodeId], offset, undefined, true);
let node = this.body.nodes[nodeId];
let pos = this._getPositionForHierarchy(node);
this._setPositionForHierarchy(node, pos + offset, undefined, true);
}
}
}
@ -415,7 +416,7 @@ class LayoutEngine {
// get the width of all trees
let getTreeSizes = () => {
let treeWidths = [];
for (let i = 0; i < this.treeIndex; i++) {
for (let i = 0; i <= this.treeIndex; i++) {
treeWidths.push(getTreeSize(i));
}
return treeWidths;
@ -425,8 +426,8 @@ class LayoutEngine {
// get a map of all nodes in this branch
let getBranchNodes = (source, map) => {
map[source.id] = true;
if (this.hierarchicalParents[source.id]) {
let children = this.hierarchicalParents[source.id].children;
if (this.hierarchicalChildrenReference[source.id]) {
let children = this.hierarchicalChildrenReference[source.id];
if (children.length > 0) {
for (let i = 0; i < children.length; i++) {
getBranchNodes(this.body.nodes[children[i]], map);
@ -467,8 +468,8 @@ class LayoutEngine {
// get the maximum level of a branch.
let getMaxLevel = (nodeId) => {
let level = this.hierarchicalLevels[nodeId];
if (this.hierarchicalParents[nodeId]) {
let children = this.hierarchicalParents[nodeId].children;
if (this.hierarchicalChildrenReference[nodeId]) {
let children = this.hierarchicalChildrenReference[nodeId];
if (children.length > 0) {
for (let i = 0; i < children.length; i++) {
level = Math.max(level,getMaxLevel(children[i]));
@ -487,13 +488,12 @@ class LayoutEngine {
// check if two nodes have the same parent(s)
let hasSameParent = (node1, node2) => {
let parents1 = this.hierarchicalChildren[node1.id];
let parents2 = this.hierarchicalChildren[node2.id];
let parents1 = this.hierarchicalParentReference[node1.id];
let parents2 = this.hierarchicalParentReference[node2.id];
if (parents1 === undefined || parents2 === undefined) {
return false;
}
parents1 = parents1.parents;
parents2 = parents2.parents;
for (let i = 0; i < parents1.length; i++) {
for (let j = 0; j < parents2.length; j++) {
if (parents1[i] == parents2[j]) {
@ -676,7 +676,7 @@ class LayoutEngine {
}
if (newPosition !== nodePosition) {
//console.log("moving Node:",diff, minSpace, maxSpace)
//console.log("moving Node:",diff, minSpace, maxSpace);
this._setPositionForHierarchy(node, newPosition, undefined, true);
//this.body.emitter.emit("_redraw");
stillShifting = true;
@ -710,7 +710,7 @@ class LayoutEngine {
}
};
//// method to remove whitespace between branches. Because we do bottom up, we can center the parents.
// method to remove whitespace between branches. Because we do bottom up, we can center the parents.
let shiftBranchesCloserBottomUp = (iterations) => {
let levels = Object.keys(this.distributionOrdering);
levels = levels.reverse();
@ -732,6 +732,19 @@ class LayoutEngine {
}
};
// center all parents
let centerAllParentsBottomUp = () => {
let levels = Object.keys(this.distributionOrdering);
levels = levels.reverse();
for (let i = 0; i < levels.length; i++) {
let level = levels[i];
let levelNodes = this.distributionOrdering[level];
for (let j = 0; j < levelNodes.length; j++) {
this._centerParent(levelNodes[j]);
}
}
};
// the actual work is done here.
if (this.options.hierarchical.blockShifting === true) {
shiftBranchesCloserBottomUp(5);
@ -743,6 +756,10 @@ class LayoutEngine {
minimizeEdgeLengthBottomUp(20);
}
if (this.options.hierarchical.parentCentralization === true) {
centerAllParentsBottomUp()
}
shiftTrees();
}
@ -794,16 +811,16 @@ class LayoutEngine {
* @private
*/
_centerParent(node) {
if (this.hierarchicalChildren[node.id]) {
let parents = this.hierarchicalChildren[node.id].parents;
if (this.hierarchicalParentReference[node.id]) {
let parents = this.hierarchicalParentReference[node.id];
for (var i = 0; i < parents.length; i++) {
let parentId = parents[i];
let parentNode = this.body.nodes[parentId];
if (this.hierarchicalParents[parentId]) {
if (this.hierarchicalChildrenReference[parentId]) {
// get the range of the children
let minPos = 1e9;
let maxPos = -1e9;
let children = this.hierarchicalParents[parentId].children;
let children = this.hierarchicalChildrenReference[parentId];
if (children.length > 0) {
for (let i = 0; i < children.length; i++) {
let childNode = this.body.nodes[children[i]];
@ -841,19 +858,107 @@ class LayoutEngine {
let nodeArray = Object.keys(distribution[level]);
nodeArray = this._indexArrayToNodes(nodeArray);
this._sortNodeArray(nodeArray);
let handledNodeCount = 0;
for (let i = 0; i < nodeArray.length; i++) {
let node = nodeArray[i];
if (this.positionedNodes[node.id] === undefined) {
this._setPositionForHierarchy(node, this.options.hierarchical.nodeSpacing * i, level);
this.positionedNodes[node.id] = true;
this._placeBranchNodes(node.id, level);
let pos = this.options.hierarchical.nodeSpacing * handledNodeCount;
// we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y
if (handledNodeCount > 0) {pos = this._getPositionForHierarchy(nodeArray[i-1]) + this.options.hierarchical.nodeSpacing;}
this._setPositionForHierarchy(node, pos, level);
this._validataPositionAndContinue(node, level, pos);
handledNodeCount++;
}
}
}
}
}
/**
* This is a recursively called function to enumerate the branches from the largest hubs and place the nodes
* on a X position that ensures there will be no overlap.
*
* @param parentId
* @param parentLevel
* @private
*/
_placeBranchNodes(parentId, parentLevel) {
// if this is not a parent, cancel the placing. This can happen with multiple parents to one child.
if (this.hierarchicalChildrenReference[parentId] === undefined) {
return;
}
// get a list of childNodes
let childNodes = [];
for (let i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) {
childNodes.push(this.body.nodes[this.hierarchicalChildrenReference[parentId][i]]);
}
// use the positions to order the nodes.
this._sortNodeArray(childNodes);
// position the childNodes
for (let i = 0; i < childNodes.length; i++) {
let childNode = childNodes[i];
let childNodeLevel = this.hierarchicalLevels[childNode.id];
// check if the child node is below the parent node and if it has already been positioned.
if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) {
// get the amount of space required for this node. If parent the width is based on the amount of children.
let pos;
// we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y
if (i === 0) {pos = this._getPositionForHierarchy(this.body.nodes[parentId]);}
else {pos = this._getPositionForHierarchy(childNodes[i-1]) + this.options.hierarchical.nodeSpacing;}
this._setPositionForHierarchy(childNode, pos, childNodeLevel);
this._validataPositionAndContinue(childNode, childNodeLevel, pos);
}
else {
return;
}
}
// center the parent nodes.
let minPos = 1e9;
let maxPos = -1e9;
for (let i = 0; i < childNodes.length; i++) {
let childNodeId = childNodes[i].id;
minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
}
this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel);
}
/**
* This method checks for overlap and if required shifts the branch. It also keeps records of positioned nodes.
* Finally it will call _placeBranchNodes to place the branch nodes.
* @param node
* @param level
* @param pos
* @private
*/
_validataPositionAndContinue(node, level, pos) {
// if overlap has been detected, we shift the branch
if (this.lastNodeOnLevel[level] !== undefined) {
let previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[level]]);
if (pos - previousPos < this.options.hierarchical.nodeSpacing) {
let diff = (previousPos + this.options.hierarchical.nodeSpacing) - pos;
let sharedParent = this._findCommonParent(this.lastNodeOnLevel[level], node.id);
this._shiftBlock(sharedParent.withChild, diff);
}
}
// store change in position.
this.lastNodeOnLevel[level] = node.id;
this.positionedNodes[node.id] = true;
this._placeBranchNodes(node.id, level);
}
/**
* Receives an array with node indices and returns an array with the actual node references. Used for sorting based on
* node properties.
@ -1047,14 +1152,14 @@ class LayoutEngine {
if (this.hierarchicalLevels[childNode.id] > this.hierarchicalLevels[parentNode.id]) {
let parentNodeId = parentNode.id;
let childNodeId = childNode.id;
if (this.hierarchicalParents[parentNodeId] === undefined) {
this.hierarchicalParents[parentNodeId] = {children: [], amount: 0};
if (this.hierarchicalChildrenReference[parentNodeId] === undefined) {
this.hierarchicalChildrenReference[parentNodeId] = [];
}
this.hierarchicalParents[parentNodeId].children.push(childNodeId);
if (this.hierarchicalChildren[childNodeId] === undefined) {
this.hierarchicalChildren[childNodeId] = {parents: [], amount: 0};
this.hierarchicalChildrenReference[parentNodeId].push(childNodeId);
if (this.hierarchicalParentReference[childNodeId] === undefined) {
this.hierarchicalParentReference[childNodeId] = [];
}
this.hierarchicalChildren[childNodeId].parents.push(parentNodeId);
this.hierarchicalParentReference[childNodeId].push(parentNodeId);
}
};
@ -1070,8 +1175,16 @@ class LayoutEngine {
*/
_crawlNetwork(callback = function() {}, startingNodeId) {
let progress = {};
let crawler = (node) => {
let treeIndex = 0;
let crawler = (node, tree) => {
if (progress[node.id] === undefined) {
if (this.hierarchicalTrees[node.id] === undefined) {
this.hierarchicalTrees[node.id] = tree;
this.treeIndex = Math.max(tree, this.treeIndex);
}
progress[node.id] = true;
let childNode;
for (let i = 0; i < node.edges.length; i++) {
@ -1085,7 +1198,7 @@ class LayoutEngine {
if (node.id !== childNode.id) {
callback(node, childNode, node.edges[i]);
crawler(childNode);
crawler(childNode, tree);
}
}
}
@ -1097,7 +1210,10 @@ class LayoutEngine {
if (startingNodeId === undefined) {
for (let i = 0; i < this.body.nodeIndices.length; i++) {
let node = this.body.nodes[this.body.nodeIndices[i]];
crawler(node);
if (progress[node.id] === undefined) {
crawler(node, treeIndex);
treeIndex += 1;
}
}
}
else {
@ -1111,77 +1227,6 @@ class LayoutEngine {
}
/**
* This is a recursively called function to enumerate the branches from the largest hubs and place the nodes
* on a X position that ensures there will be no overlap.
*
* @param parentId
* @param parentLevel
* @private
*/
_placeBranchNodes(parentId, parentLevel) {
// if this is not a parent, cancel the placing. This can happen with multiple parents to one child.
if (this.hierarchicalParents[parentId] === undefined) {
return;
}
// get a list of childNodes
let childNodes = [];
for (let i = 0; i < this.hierarchicalParents[parentId].children.length; i++) {
childNodes.push(this.body.nodes[this.hierarchicalParents[parentId].children[i]]);
}
// use the positions to order the nodes.
this._sortNodeArray(childNodes);
// position the childNodes
for (let i = 0; i < childNodes.length; i++) {
let childNode = childNodes[i];
let childNodeLevel = this.hierarchicalLevels[childNode.id];
// check if the child node is below the parent node and if it has already been positioned.
if (childNodeLevel > parentLevel && this.positionedNodes[childNode.id] === undefined) {
// get the amount of space required for this node. If parent the width is based on the amount of children.
let pos;
// we get the X or Y values we need and store them in pos and previousPos. The get and set make sure we get X or Y
if (i === 0) {pos = this._getPositionForHierarchy(this.body.nodes[parentId]);}
else {pos = this._getPositionForHierarchy(childNodes[i-1]) + this.options.hierarchical.nodeSpacing;}
this._setPositionForHierarchy(childNode, pos, childNodeLevel);
// if overlap has been detected, we shift the branch
if (this.lastNodeOnLevel[childNodeLevel] !== undefined) {
let previousPos = this._getPositionForHierarchy(this.body.nodes[this.lastNodeOnLevel[childNodeLevel]]);
if (pos - previousPos < this.options.hierarchical.nodeSpacing) {
let diff = (previousPos + this.options.hierarchical.nodeSpacing) - pos;
let sharedParent = this._findCommonParent(this.lastNodeOnLevel[childNodeLevel], childNode.id);
this._shiftBlock(sharedParent.withChild, diff);
}
}
// store change in position.
this.lastNodeOnLevel[childNodeLevel] = childNode.id;
this.positionedNodes[childNode.id] = true;
this._placeBranchNodes(childNode.id, childNodeLevel);
}
else {
return;
}
}
// center the parent nodes.
let minPos = 1e9;
let maxPos = -1e9;
for (let i = 0; i < childNodes.length; i++) {
let childNodeId = childNodes[i].id;
minPos = Math.min(minPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
maxPos = Math.max(maxPos, this._getPositionForHierarchy(this.body.nodes[childNodeId]));
}
this._setPositionForHierarchy(this.body.nodes[parentId], 0.5 * (minPos + maxPos), parentLevel);
}
/**
* Shift a branch a certain distance
* @param parentId
@ -1195,9 +1240,9 @@ class LayoutEngine {
else {
this.body.nodes[parentId].y += diff;
}
if (this.hierarchicalParents[parentId] !== undefined) {
for (let i = 0; i < this.hierarchicalParents[parentId].children.length; i++) {
this._shiftBlock(this.hierarchicalParents[parentId].children[i], diff);
if (this.hierarchicalChildrenReference[parentId] !== undefined) {
for (let i = 0; i < this.hierarchicalChildrenReference[parentId].length; i++) {
this._shiftBlock(this.hierarchicalChildrenReference[parentId][i], diff);
}
}
}
@ -1213,18 +1258,18 @@ class LayoutEngine {
_findCommonParent(childA,childB) {
let parents = {};
let iterateParents = (parents,child) => {
if (this.hierarchicalChildren[child] !== undefined) {
for (let i = 0; i < this.hierarchicalChildren[child].parents.length; i++) {
let parent = this.hierarchicalChildren[child].parents[i];
if (this.hierarchicalParentReference[child] !== undefined) {
for (let i = 0; i < this.hierarchicalParentReference[child].length; i++) {
let parent = this.hierarchicalParentReference[child][i];
parents[parent] = true;
iterateParents(parents, parent)
}
}
};
let findParent = (parents, child) => {
if (this.hierarchicalChildren[child] !== undefined) {
for (let i = 0; i < this.hierarchicalChildren[child].parents.length; i++) {
let parent = this.hierarchicalChildren[child].parents[i];
if (this.hierarchicalParentReference[child] !== undefined) {
for (let i = 0; i < this.hierarchicalParentReference[child].length; i++) {
let parent = this.hierarchicalParentReference[child][i];
if (parents[parent] !== undefined) {
return {foundParent:parent, withChild:child};
}
@ -1249,6 +1294,7 @@ class LayoutEngine {
* @private
*/
_setPositionForHierarchy(node, position, level, doNotUpdate = false) {
//console.log('_setPositionForHierarchy',node.id, position)
if (doNotUpdate !== true) {
if (this.distributionOrdering[level] === undefined) {
this.distributionOrdering[level] = [];
@ -1260,28 +1306,6 @@ class LayoutEngine {
this.distributionIndex[node.id] = this.distributionOrdering[level].length - 1;
}
this.distributionOrderingPresence[level][node.id] = true;
if (this.hierarchicalTrees[node.id] === undefined) {
if (this.hierarchicalChildren[node.id] !== undefined) {
let tree = 1;
// get the lowest tree denominator.
for (let i = 0; i < this.hierarchicalChildren[node.id].parents.length; i++) {
let parentId = this.hierarchicalChildren[node.id].parents[i];
if (this.hierarchicalTrees[parentId] !== undefined) {
//tree = Math.min(tree,this.hierarchicalTrees[parentId]);
tree = this.hierarchicalTrees[parentId];
}
}
//for (let i = 0; i < this.hierarchicalChildren.parents.length; i++) {
// let parentId = this.hierarchicalChildren.parents[i];
// this.hierarchicalTrees[parentId] = tree;
//}
this.hierarchicalTrees[node.id] = tree;
}
else {
this.hierarchicalTrees[node.id] = ++this.treeIndex;
}
}
}
if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') {

+ 8
- 4
lib/network/modules/ManipulationSystem.js View File

@ -263,7 +263,7 @@ class ManipulationSystem {
this.inMode = 'editNode';
if (typeof this.options.editNode === 'function') {
if (node.isCluster !== true) {
let data = util.deepExtend({}, node.options, true);
let data = util.deepExtend({}, node.options, false);
data.x = node.x;
data.y = node.y;
@ -848,6 +848,11 @@ class ManipulationSystem {
edge.edgeType.to = to;
}
// we use the selection to find the node that is being dragged. We explicitly select it here.
if (this.selectedControlNode !== undefined) {
this.selectionHandler.selectObject(this.selectedControlNode)
}
this.body.emitter.emit('_redraw');
}
@ -860,7 +865,6 @@ class ManipulationSystem {
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;
@ -884,12 +888,13 @@ class ManipulationSystem {
let pointer = this.body.functions.getPointer(event.center);
let pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
let edge = this.body.edges[this.edgeBeingEditedId];
// if the node that was dragged is not a control node, return
if (this.selectedControlNode === undefined) {
return;
}
// we use the selection to find the node that is being dragged. We explicitly DEselect the control node here.
this.selectionHandler.unselectAll();
let overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj);
let node = undefined;
for (let i = overlappingNodeIds.length-1; i >= 0; i--) {
@ -898,7 +903,6 @@ class ManipulationSystem {
break;
}
}
// perform the connection
if (node !== undefined && this.selectedControlNode !== undefined) {
if (node.isCluster === true) {

+ 1
- 0
lib/network/modules/NodesHandler.js View File

@ -96,6 +96,7 @@ class NodesHandler {
shapeProperties: {
borderDashes: false, // only for borders
borderRadius: 6, // only for box shape
interpolation: true, // only for image and circularImage shapes
useImageSize: false, // only for image and circularImage shapes
useBorderWithImage: false // only for image shape
},

+ 2
- 2
lib/network/modules/components/Edge.js View File

@ -379,8 +379,8 @@ class Edge {
let arrowData = {};
// restore edge targets to defaults
this.edgeType.fromPoint = this.from;
this.edgeType.toPoint = this.to;
this.edgeType.fromPoint = this.edgeType.from;
this.edgeType.toPoint = this.edgeType.to;
// from and to arrows give a different end point for edges. we set them here
if (this.options.arrows.from.enabled === true) {

+ 31
- 2
lib/network/modules/components/nodes/util/CircleImageBase.js View File

@ -103,8 +103,37 @@ class CircleImageBase extends NodeBase {
// draw shadow if enabled
this.enableShadow(ctx);
// draw image
ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
let factor = (this.imageObj.width / this.width) / this.body.view.scale;
if (factor > 2 && this.options.shapeProperties.interpolation === true) {
let w = this.imageObj.width;
let h = this.imageObj.height;
var can2 = document.createElement('canvas');
can2.width = w;
can2.height = w;
var ctx2 = can2.getContext('2d');
factor *= 0.5;
w *= 0.5;
h *= 0.5;
ctx2.drawImage(this.imageObj, 0, 0, w, h);
let distance = 0;
let iterations = 1;
while (factor > 2 && iterations < 4) {
ctx2.drawImage(can2, distance, 0, w, h, distance+w, 0, w/2, h/2);
distance += w;
factor *= 0.5;
w *= 0.5;
h *= 0.5;
iterations += 1;
}
ctx.drawImage(can2, distance, 0, w, h, this.left, this.top, this.width, this.height);
}
else {
// draw image
ctx.drawImage(this.imageObj, this.left, this.top, this.width, this.height);
}
// disable shadows for other elements.
this.disableShadow(ctx);

+ 4
- 0
lib/network/options.js View File

@ -127,6 +127,7 @@ let allOptions = {
treeSpacing: { number },
blockShifting: { boolean },
edgeMinimization: { boolean },
parentCentralization: { boolean },
direction: { string: ['UD', 'DU', 'LR', 'RL'] }, // UD, DU, LR, RL
sortMethod: { string: ['hubsize', 'directed'] }, // hubsize, directed
__type__: { object, boolean }
@ -220,6 +221,7 @@ let allOptions = {
shapeProperties: {
borderDashes: { boolean, array },
borderRadius: { number },
interpolation: { boolean },
useImageSize: { boolean },
useBorderWithImage: { boolean },
__type__: { object }
@ -361,6 +363,7 @@ let configureOptions = {
shapeProperties: {
borderDashes: false,
borderRadius: [6, 0, 20, 1],
interpolation: true,
useImageSize: false
},
size: [25, 0, 200, 1]
@ -431,6 +434,7 @@ let configureOptions = {
treeSpacing: [200, 20, 500, 5],
blockShifting: true,
edgeMinimization: true,
parentCentralization: true,
direction: ['UD', 'DU', 'LR', 'RL'], // UD, DU, LR, RL
sortMethod: ['hubsize', 'directed'] // hubsize, directed
}

+ 1
- 0
lib/timeline/component/CurrentTime.js View File

@ -129,6 +129,7 @@ CurrentTime.prototype.start = function() {
if (interval > 1000) interval = 1000;
me.redraw();
me.body.emitter.emit('currentTimeTick');
// start a renderTimer to adjust for the new time
me.currentTimeTimer = setTimeout(update, interval);

+ 2
- 2
lib/timeline/component/DataAxis.js View File

@ -29,12 +29,12 @@ function DataAxis (body, options, svg, linegraphOptions) {
alignZeros: true,
left:{
range: {min:undefined,max:undefined},
format: function (value) {return ''+Number.parseFloat(value.toPrecision(3));},
format: function (value) {return ''+parseFloat(value.toPrecision(3));},
title: {text:undefined,style:undefined}
},
right:{
range: {min:undefined,max:undefined},
format: function (value) {return ''+Number.parseFloat(value.toPrecision(3));},
format: function (value) {return ''+parseFloat(value.toPrecision(3));},
title: {text:undefined,style:undefined}
}
};

+ 1
- 1
package.json View File

@ -1,6 +1,6 @@
{
"name": "vis",
"version": "4.14.0",
"version": "4.15.0",
"description": "A dynamic, browser-based visualization library.",
"homepage": "http://visjs.org/",
"license": "(Apache-2.0 OR MIT)",

+ 158
- 40
test/networkTest.html View File

@ -7,7 +7,7 @@
<link href="../dist/vis.css" rel="stylesheet" type="text/css"/>
<style type="text/css">
#network{
width: 1900px;
width: 1200px;
height: 800px;
border: 1px solid lightgray;
}
@ -16,53 +16,171 @@
<body>
<h1>Network Test</h1>
<div id="network"></div>
<script>
var network = null;
var idmap = {};
for (var i = 0; i < nodes.length; i++) {
nodes[i].label = i;
idmap[nodes[i].id] = i;
nodes[i].id = i;
<script type="text/javascript">
// create a network
var container = document.getElementById('network');
var data = {
nodes: [{
"id": "59acc308-fc78-11e3-a451-005056967662_1",
"level": 1,
"shape": "box",
"label": "737464 (HALB)\nLenks\u00e4ule\n0 | M1182 - TK-PF\n0 | M2676 - TK-PF",
}, {
"id": "bc5ada61-e297-11e3-a03f-005056967662_2",
"level": 2,
"shape": "box",
"label": "578647 C (HALB)\nF\u00fchrungskasten komplett\n0000 | M2731 - TK-PF",
}, {
"id": "6f8a780a-e28e-11e3-a03f-005056967662_2",
"level": 2,
"shape": "box",
"label": "615430 C (HALB)\nF\u00fchrungskasten komplett\n0000 | M2781 - TK-PF\n0000 | M2788 - TK-PF",
}, {
"id": "49bb72b1-e2b2-11e3-a03f-005056967662_2",
"level": 2,
"shape": "box",
"label": "678478 D (HALB)\nF\u00fchrungskasten montiert\n0 | M1085 - TK-PFC",
}, {
"id": "e7bf7c7e-e28e-11e3-a03f-005056967662_2",
"level": 2,
"shape": "box",
"label": "727672 B (HALB)\nF\u00fchrungskasten komplett\n0 | M9999 - TK-PTH\n0000 | M1155 - TK-PTH",
}, {
"id": "623e8501-e2e2-11e3-a03f-005056967662_2",
"level": 2,
"shape": "box",
"label": "760550 A (HALB)\nF\u00fchrungskasten komplett\n0000 | M1556 - TK-PHS",
}, {
"id": "e7c6dcdf-e28e-11e3-a03f-005056967662_2",
"level": 2,
"shape": "box",
"label": "793641 A (HALB)\nF\u00fchrungskasten komplett\n0000 | M1155 - TK-PTH",
}, {
"id": "623edd04-e2e2-11e3-a03f-005056967662_2",
"level": 2,
"shape": "box",
"label": "802797 A (HALB)\nF\u00fchrungskasten komplett\n0 | M1556 - TK-PHS",
}, {
"id": "a986a9ab-fc82-11e3-a451-005056967662_2",
"level": 2,
"shape": "box",
"label": "821943 (HALB)\nF\u00fchrungskasten komplett\n0 | M2781 - TK-PF",
}, {
"id": "c22dda1e-fc84-11e3-a451-005056967662_2",
"level": 2,
"shape": "box",
"label": "821944 (HALB)\nF\u00fchrungskasten komplett\n0 | M2781 - TK-PF",
}, {
"id": "a05fcb83-29c6-11e4-9203-005056967662_2",
"level": 2,
"shape": "box",
"label": "828431 (HALB)\nF\u00fchrungskasten komplett",
}, {
"id": "eb9b67b7-6e3f-11e4-9203-005056967662_2",
"level": 2,
"shape": "box",
"label": "829382 A (HALB)\nF\u00fchrungskasten komplett\n0 | M1556 - TK-PHS",
}, {
"id": "084ec1e3-e447-11e3-a03f-005056967662_3",
"level": 3,
"shape": "box",
"color": "#00ff00",
"label": "310759 C (ROH)\nSchr\u00e4gkugellager",
}],
edges: [{
"from": "59acc308-fc78-11e3-a451-005056967662_1",
"to": "a986a9ab-fc82-11e3-a451-005056967662_2"
}, {
"from": "bc5ada61-e297-11e3-a03f-005056967662_2",
"to": "084ec1e3-e447-11e3-a03f-005056967662_3"
}, {
"from": "6f8a780a-e28e-11e3-a03f-005056967662_2",
"to": "084ec1e3-e447-11e3-a03f-005056967662_3"
}, {
"from": "49bb72b1-e2b2-11e3-a03f-005056967662_2",
"to": "084ec1e3-e447-11e3-a03f-005056967662_3"
}, {
"from": "e7bf7c7e-e28e-11e3-a03f-005056967662_2",
"to": "084ec1e3-e447-11e3-a03f-005056967662_3"
}, {
"from": "623e8501-e2e2-11e3-a03f-005056967662_2",
"to": "084ec1e3-e447-11e3-a03f-005056967662_3"
}, {
"from": "e7c6dcdf-e28e-11e3-a03f-005056967662_2",
"to": "084ec1e3-e447-11e3-a03f-005056967662_3"
}, {
"from": "623edd04-e2e2-11e3-a03f-005056967662_2",
"to": "084ec1e3-e447-11e3-a03f-005056967662_3"
}, {
"from": "a986a9ab-fc82-11e3-a451-005056967662_2",
"to": "084ec1e3-e447-11e3-a03f-005056967662_3"
}, {
"from": "c22dda1e-fc84-11e3-a451-005056967662_2",
"to": "084ec1e3-e447-11e3-a03f-005056967662_3"
}, {
"from": "a05fcb83-29c6-11e4-9203-005056967662_2",
"to": "084ec1e3-e447-11e3-a03f-005056967662_3"
}, {
"from": "eb9b67b7-6e3f-11e4-9203-005056967662_2",
"to": "084ec1e3-e447-11e3-a03f-005056967662_3"
}]
};
var ids = {};
for ( var i = 0; i < data.nodes.length; i++) {
ids[data.nodes[i].id] = i;
data.nodes[i].id = i
data.nodes[i].label = i
}
for (var i = 0; i < edges.length; i++) {
edges[i].from = idmap[edges[i].from];
edges[i].to = idmap[edges[i].to];
edges[i].id = 'e'+i;
for ( var i = 0; i < data.edges.length; i++) {
data.edges[i].from = ids[data.edges[i].from]
data.edges[i].to = ids[data.edges[i].to]
}
var options = {
console.log(JSON.stringify(nodes), JSON.stringify(edges));
layout: {
hierarchical: {
//enabled: true,
nodeSpacing: 100,
//blockShifting: false,
//edgeMinimization: false,
direction: 'UD',
sortMethod: 'directed'
// randomly create some nodes and edges
var data = {
nodes: nodes,
edges: edges
}
// create a network
var status = document.getElementById('status');
var container = document.getElementById('network');
var options = {
layout: {
hierarchical: {
direction: "UD",
sortMethod: "directed"
}
},
physics: {
enabled: false,
stabilization:false
},
interaction: {
dragNodes: true,
hover: true
},
physics: {
enabled: false
}
};
var network = new vis.Network(container, data, options);
network.on('click', function(params) {
if (params.nodes !== undefined) {
var firstNode = params.nodes[0];
if (firstNode !== undefined) {
var splitted = firstNode.split('_');
Lisp.request.get('/dataStore/article/detail/id/' + splitted[0]);
}
};
network = new vis.Network(container, data, options);
network.on("stabilizationProgress", function(params) {
var prog = params.iterations/params.total;
status.innerText = Math.round(prog*100)+'%';
});
network.on("stabilizationIterationsDone", function() {
status.innerText = "stabilized";
});
}
});
</script>
</body>
</html>

+ 4
- 0
test/timeline.html View File

@ -216,6 +216,10 @@
items.on('update', console.log.bind(console));
items.on('remove', console.log.bind(console));
timeline.on('currentTimeTick', function () {
console.log('currentTimeTick')
});
// timeline.setOptions({timeAxis:{scale: 'minute', step: 5}})
</script>

Loading…
Cancel
Save