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 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 ## 2016-02-04, version 4.14.0
### Timeline ### Timeline

+ 1
- 1
bower.json View File

@ -1,6 +1,6 @@
{ {
"name": "vis", "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.", "description": "A dynamic, browser-based visualization library.",
"homepage": "http://visjs.org/", "homepage": "http://visjs.org/",
"license": ["Apache-2.0", "MIT"], "license": ["Apache-2.0", "MIT"],

+ 1
- 1
dist/vis.css View File

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

+ 237
- 170
dist/vis.js View File

@ -4,8 +4,8 @@
* *
* A dynamic, browser-based visualization library. * A dynamic, browser-based visualization library.
* *
* @version 4.14.0
* @date 2016-02-04
* @version 4.15.0
* @date 2016-02-23
* *
* @license * @license
* Copyright (C) 2011-2016 Almende B.V, http://almende.com * Copyright (C) 2011-2016 Almende B.V, http://almende.com
@ -9343,7 +9343,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (typeof this.showTooltip === 'function') { if (typeof this.showTooltip === 'function') {
content.innerHTML = this.showTooltip(dataPoint.point); content.innerHTML = this.showTooltip(dataPoint.point);
} else { } 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'; content.style.left = '0';
@ -22170,6 +22170,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (interval > 1000) interval = 1000; if (interval > 1000) interval = 1000;
me.redraw(); me.redraw();
me.body.emitter.emit('currentTimeTick');
// start a renderTimer to adjust for the new time // start a renderTimer to adjust for the new time
me.currentTimeTimer = setTimeout(update, interval); me.currentTimeTimer = setTimeout(update, interval);
@ -25592,14 +25593,14 @@ return /******/ (function(modules) { // webpackBootstrap
left: { left: {
range: { min: undefined, max: undefined }, range: { min: undefined, max: undefined },
format: function format(value) { format: function format(value) {
return '' + Number.parseFloat(value.toPrecision(3));
return '' + parseFloat(value.toPrecision(3));
}, },
title: { text: undefined, style: undefined } title: { text: undefined, style: undefined }
}, },
right: { right: {
range: { min: undefined, max: undefined }, range: { min: undefined, max: undefined },
format: function format(value) { format: function format(value) {
return '' + Number.parseFloat(value.toPrecision(3));
return '' + parseFloat(value.toPrecision(3));
}, },
title: { text: undefined, style: undefined } title: { text: undefined, style: undefined }
} }
@ -28552,6 +28553,7 @@ return /******/ (function(modules) { // webpackBootstrap
shapeProperties: { shapeProperties: {
borderDashes: false, // only for borders borderDashes: false, // only for borders
borderRadius: 6, // only for box shape borderRadius: 6, // only for box shape
interpolation: true, // only for image and circularImage shapes
useImageSize: false, // only for image and circularImage shapes useImageSize: false, // only for image and circularImage shapes
useBorderWithImage: false // only for image shape useBorderWithImage: false // only for image shape
}, },
@ -30221,8 +30223,35 @@ return /******/ (function(modules) { // webpackBootstrap
// draw shadow if enabled // draw shadow if enabled
this.enableShadow(ctx); 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. // disable shadows for other elements.
this.disableShadow(ctx); this.disableShadow(ctx);
@ -32129,8 +32158,8 @@ return /******/ (function(modules) { // webpackBootstrap
var arrowData = {}; var arrowData = {};
// restore edge targets to defaults // 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 // from and to arrows give a different end point for edges. we set them here
if (this.options.arrows.from.enabled === true) { if (this.options.arrows.from.enabled === true) {
@ -38286,8 +38315,8 @@ return /******/ (function(modules) { // webpackBootstrap
var _determineIfDifferent2 = this._determineIfDifferent(previousSelection, currentSelection); 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; var nodeSelected = false;
@ -38296,15 +38325,15 @@ return /******/ (function(modules) { // webpackBootstrap
this.selectionHandler._generateClickEvent('selectNode', event, pointer); this.selectionHandler._generateClickEvent('selectNode', event, pointer);
selected = true; selected = true;
nodeSelected = 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('deselectNode', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectNode', event, pointer); this.selectionHandler._generateClickEvent('selectNode', event, pointer);
nodeSelected = true; nodeSelected = true;
selected = true; selected = true;
} else if (selectedNodesCount - previouslySelectedNodeCount < 0) {
// node was deselected
this.selectionHandler._generateClickEvent('deselectNode', event, pointer, previousSelection);
selected = true;
} }
// handle the selected edges // handle the selected edges
@ -38312,13 +38341,13 @@ return /******/ (function(modules) { // webpackBootstrap
// edge was selected // edge was selected
this.selectionHandler._generateClickEvent('selectEdge', event, pointer); this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true; 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('deselectEdge', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true; 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('deselectEdge', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true; selected = true;
} }
@ -38333,37 +38362,37 @@ return /******/ (function(modules) { // webpackBootstrap
* This function checks if the nodes and edges previously selected have changed. * This function checks if the nodes and edges previously selected have changed.
* @param previousSelection * @param previousSelection
* @param currentSelection * @param currentSelection
* @returns {{nodesChanges: boolean, edgesChanges: boolean}}
* @returns {{nodesChanged: boolean, edgesChanged: boolean}}
* @private * @private
*/ */
}, { }, {
key: '_determineIfDifferent', key: '_determineIfDifferent',
value: function _determineIfDifferent(previousSelection, currentSelection) { 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++) { for (var i = 0; i < previousSelection.nodes.length; i++) {
if (currentSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) { if (currentSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) {
nodesChanges = true;
nodesChanged = true;
} }
} }
for (var i = 0; i < currentSelection.nodes.length; i++) { for (var i = 0; i < currentSelection.nodes.length; i++) {
if (previousSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) { if (previousSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) {
nodesChanges = true;
nodesChanged = true;
} }
} }
for (var i = 0; i < previousSelection.edges.length; i++) { for (var i = 0; i < previousSelection.edges.length; i++) {
if (currentSelection.edges.indexOf(previousSelection.edges[i]) === -1) { if (currentSelection.edges.indexOf(previousSelection.edges[i]) === -1) {
edgesChanges = true;
edgesChanged = true;
} }
} }
for (var i = 0; i < currentSelection.edges.length; i++) { for (var i = 0; i < currentSelection.edges.length; i++) {
if (previousSelection.edges.indexOf(previousSelection.edges[i]) === -1) { 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, treeSpacing: 200,
blockShifting: true, blockShifting: true,
edgeMinimization: true, edgeMinimization: true,
parentCentralization: true,
direction: 'UD', // UD, DU, LR, RL direction: 'UD', // UD, DU, LR, RL
sortMethod: 'hubsize' // hubsize, directed sortMethod: 'hubsize' // hubsize, directed
} }
@ -40364,8 +40394,8 @@ return /******/ (function(modules) { // webpackBootstrap
var undefinedLevel = false; var undefinedLevel = false;
this.hierarchicalLevels = {}; this.hierarchicalLevels = {};
this.lastNodeOnLevel = {}; this.lastNodeOnLevel = {};
this.hierarchicalParents = {};
this.hierarchicalChildren = {};
this.hierarchicalChildrenReference = {};
this.hierarchicalParentReference = {};
this.hierarchicalTrees = {}; this.hierarchicalTrees = {};
this.treeIndex = -1; this.treeIndex = -1;
@ -40447,9 +40477,7 @@ return /******/ (function(modules) { // webpackBootstrap
var treeSizes = getTreeSizes(); var treeSizes = getTreeSizes();
for (var i = 0; i < treeSizes.length - 1; i++) { for (var i = 0; i < treeSizes.length - 1; i++) {
var diff = treeSizes[i].max - treeSizes[i + 1].min; 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) { for (var nodeId in _this2.hierarchicalTrees) {
if (_this2.hierarchicalTrees.hasOwnProperty(nodeId)) { if (_this2.hierarchicalTrees.hasOwnProperty(nodeId)) {
if (_this2.hierarchicalTrees[nodeId] === index) { 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 // get the width of all trees
var getTreeSizes = function getTreeSizes() { var getTreeSizes = function getTreeSizes() {
var treeWidths = []; var treeWidths = [];
for (var i = 0; i < _this2.treeIndex; i++) {
for (var i = 0; i <= _this2.treeIndex; i++) {
treeWidths.push(getTreeSize(i)); treeWidths.push(getTreeSize(i));
} }
return treeWidths; return treeWidths;
@ -40492,8 +40522,8 @@ return /******/ (function(modules) { // webpackBootstrap
// get a map of all nodes in this branch // get a map of all nodes in this branch
var getBranchNodes = function getBranchNodes(source, map) { var getBranchNodes = function getBranchNodes(source, map) {
map[source.id] = true; 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) { if (children.length > 0) {
for (var i = 0; i < children.length; i++) { for (var i = 0; i < children.length; i++) {
getBranchNodes(_this2.body.nodes[children[i]], map); getBranchNodes(_this2.body.nodes[children[i]], map);
@ -40543,8 +40573,8 @@ return /******/ (function(modules) { // webpackBootstrap
// get the maximum level of a branch. // get the maximum level of a branch.
var getMaxLevel = function getMaxLevel(nodeId) { var getMaxLevel = function getMaxLevel(nodeId) {
var level = _this2.hierarchicalLevels[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) { if (children.length > 0) {
for (var i = 0; i < children.length; i++) { for (var i = 0; i < children.length; i++) {
level = Math.max(level, getMaxLevel(children[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) // check if two nodes have the same parent(s)
var hasSameParent = function hasSameParent(node1, node2) { 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) { if (parents1 === undefined || parents2 === undefined) {
return false; return false;
} }
parents1 = parents1.parents;
parents2 = parents2.parents;
for (var i = 0; i < parents1.length; i++) { for (var i = 0; i < parents1.length; i++) {
for (var j = 0; j < parents2.length; j++) { for (var j = 0; j < parents2.length; j++) {
if (parents1[i] == parents2[j]) { if (parents1[i] == parents2[j]) {
@ -40782,7 +40811,7 @@ return /******/ (function(modules) { // webpackBootstrap
} }
if (newPosition !== nodePosition) { if (newPosition !== nodePosition) {
//console.log("moving Node:",diff, minSpace, maxSpace)
//console.log("moving Node:",diff, minSpace, maxSpace);
_this2._setPositionForHierarchy(node, newPosition, undefined, true); _this2._setPositionForHierarchy(node, newPosition, undefined, true);
//this.body.emitter.emit("_redraw"); //this.body.emitter.emit("_redraw");
stillShifting = true; 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 shiftBranchesCloserBottomUp = function shiftBranchesCloserBottomUp(iterations) {
var levels = Object.keys(_this2.distributionOrdering); var levels = Object.keys(_this2.distributionOrdering);
levels = levels.reverse(); 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. // the actual work is done here.
if (this.options.hierarchical.blockShifting === true) { if (this.options.hierarchical.blockShifting === true) {
shiftBranchesCloserBottomUp(5); shiftBranchesCloserBottomUp(5);
@ -40848,6 +40890,10 @@ return /******/ (function(modules) { // webpackBootstrap
minimizeEdgeLengthBottomUp(20); minimizeEdgeLengthBottomUp(20);
} }
if (this.options.hierarchical.parentCentralization === true) {
centerAllParentsBottomUp();
}
shiftTrees(); shiftTrees();
} }
@ -40902,16 +40948,16 @@ return /******/ (function(modules) { // webpackBootstrap
}, { }, {
key: '_centerParent', key: '_centerParent',
value: function _centerParent(node) { 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++) { for (var i = 0; i < parents.length; i++) {
var parentId = parents[i]; var parentId = parents[i];
var parentNode = this.body.nodes[parentId]; var parentNode = this.body.nodes[parentId];
if (this.hierarchicalParents[parentId]) {
if (this.hierarchicalChildrenReference[parentId]) {
// get the range of the children // get the range of the children
var minPos = 1e9; var minPos = 1e9;
var maxPos = -1e9; var maxPos = -1e9;
var children = this.hierarchicalParents[parentId].children;
var children = this.hierarchicalChildrenReference[parentId];
if (children.length > 0) { if (children.length > 0) {
for (var _i = 0; _i < children.length; _i++) { for (var _i = 0; _i < children.length; _i++) {
var childNode = this.body.nodes[children[_i]]; var childNode = this.body.nodes[children[_i]];
@ -40956,19 +41002,113 @@ return /******/ (function(modules) { // webpackBootstrap
var nodeArray = Object.keys(distribution[level]); var nodeArray = Object.keys(distribution[level]);
nodeArray = this._indexArrayToNodes(nodeArray); nodeArray = this._indexArrayToNodes(nodeArray);
this._sortNodeArray(nodeArray); this._sortNodeArray(nodeArray);
var handledNodeCount = 0;
for (var i = 0; i < nodeArray.length; i++) { for (var i = 0; i < nodeArray.length; i++) {
var node = nodeArray[i]; var node = nodeArray[i];
if (this.positionedNodes[node.id] === undefined) { 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 * Receives an array with node indices and returns an array with the actual node references. Used for sorting based on
* node properties. * node properties.
@ -41178,14 +41318,14 @@ return /******/ (function(modules) { // webpackBootstrap
if (_this6.hierarchicalLevels[childNode.id] > _this6.hierarchicalLevels[parentNode.id]) { if (_this6.hierarchicalLevels[childNode.id] > _this6.hierarchicalLevels[parentNode.id]) {
var parentNodeId = parentNode.id; var parentNodeId = parentNode.id;
var childNodeId = childNode.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', key: '_crawlNetwork',
value: function _crawlNetwork(callback, startingNodeId) { value: function _crawlNetwork(callback, startingNodeId) {
var _this7 = this;
if (callback === undefined) callback = function () {}; if (callback === undefined) callback = function () {};
var progress = {}; var progress = {};
var crawler = function crawler(node) {
var treeIndex = 0;
var crawler = function crawler(node, tree) {
if (progress[node.id] === undefined) { 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; progress[node.id] = true;
var childNode = undefined; var childNode = undefined;
for (var i = 0; i < node.edges.length; i++) { for (var i = 0; i < node.edges.length; i++) {
@ -41218,7 +41368,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (node.id !== childNode.id) { if (node.id !== childNode.id) {
callback(node, childNode, node.edges[i]); callback(node, childNode, node.edges[i]);
crawler(childNode);
crawler(childNode, tree);
} }
} }
} }
@ -41229,7 +41379,10 @@ return /******/ (function(modules) { // webpackBootstrap
if (startingNodeId === undefined) { if (startingNodeId === undefined) {
for (var i = 0; i < this.body.nodeIndices.length; i++) { for (var i = 0; i < this.body.nodeIndices.length; i++) {
var node = this.body.nodes[this.body.nodeIndices[i]]; var node = this.body.nodes[this.body.nodeIndices[i]];
crawler(node);
if (progress[node.id] === undefined) {
crawler(node, treeIndex);
treeIndex += 1;
}
} }
} else { } else {
var node = this.body.nodes[startingNodeId]; 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 * Shift a branch a certain distance
* @param parentId * @param parentId
@ -41329,9 +41408,9 @@ return /******/ (function(modules) { // webpackBootstrap
} else { } else {
this.body.nodes[parentId].y += diff; 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', key: '_findCommonParent',
value: function _findCommonParent(childA, childB) { value: function _findCommonParent(childA, childB) {
var _this7 = this;
var _this8 = this;
var parents = {}; var parents = {};
var iterateParents = function iterateParents(parents, child) { 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; parents[_parent] = true;
iterateParents(parents, _parent); iterateParents(parents, _parent);
} }
} }
}; };
var findParent = function findParent(parents, child) { 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) { if (parents[_parent2] !== undefined) {
return { foundParent: _parent2, withChild: child }; return { foundParent: _parent2, withChild: child };
} }
@ -41390,6 +41469,7 @@ return /******/ (function(modules) { // webpackBootstrap
value: function _setPositionForHierarchy(node, position, level) { value: function _setPositionForHierarchy(node, position, level) {
var doNotUpdate = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3]; var doNotUpdate = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
//console.log('_setPositionForHierarchy',node.id, position)
if (doNotUpdate !== true) { if (doNotUpdate !== true) {
if (this.distributionOrdering[level] === undefined) { if (this.distributionOrdering[level] === undefined) {
this.distributionOrdering[level] = []; this.distributionOrdering[level] = [];
@ -41401,27 +41481,6 @@ return /******/ (function(modules) { // webpackBootstrap
this.distributionIndex[node.id] = this.distributionOrdering[level].length - 1; this.distributionIndex[node.id] = this.distributionOrdering[level].length - 1;
} }
this.distributionOrderingPresence[level][node.id] = true; 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') { if (this.options.hierarchical.direction === 'UD' || this.options.hierarchical.direction === 'DU') {
@ -41774,7 +41833,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.inMode = 'editNode'; this.inMode = 'editNode';
if (typeof this.options.editNode === 'function') { if (typeof this.options.editNode === 'function') {
if (node.isCluster !== true) { if (node.isCluster !== true) {
var data = util.deepExtend({}, node.options, true);
var data = util.deepExtend({}, node.options, false);
data.x = node.x; data.x = node.x;
data.y = node.y; data.y = node.y;
@ -42395,6 +42454,11 @@ return /******/ (function(modules) { // webpackBootstrap
edge.edgeType.to = to; 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'); this.body.emitter.emit('_redraw');
} }
@ -42409,7 +42473,6 @@ return /******/ (function(modules) { // webpackBootstrap
this.body.emitter.emit('disablePhysics'); this.body.emitter.emit('disablePhysics');
var pointer = this.body.functions.getPointer(event.center); var pointer = this.body.functions.getPointer(event.center);
var pos = this.canvas.DOMtoCanvas(pointer); var pos = this.canvas.DOMtoCanvas(pointer);
if (this.selectedControlNode !== undefined) { if (this.selectedControlNode !== undefined) {
this.selectedControlNode.x = pos.x; this.selectedControlNode.x = pos.x;
this.selectedControlNode.y = pos.y; this.selectedControlNode.y = pos.y;
@ -42433,12 +42496,13 @@ return /******/ (function(modules) { // webpackBootstrap
var pointer = this.body.functions.getPointer(event.center); var pointer = this.body.functions.getPointer(event.center);
var pointerObj = this.selectionHandler._pointerToPositionObject(pointer); var pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
var edge = this.body.edges[this.edgeBeingEditedId]; var edge = this.body.edges[this.edgeBeingEditedId];
// if the node that was dragged is not a control node, return // if the node that was dragged is not a control node, return
if (this.selectedControlNode === undefined) { if (this.selectedControlNode === undefined) {
return; 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 overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj);
var node = undefined; var node = undefined;
for (var i = overlappingNodeIds.length - 1; i >= 0; i--) { for (var i = overlappingNodeIds.length - 1; i >= 0; i--) {
@ -42447,7 +42511,6 @@ return /******/ (function(modules) { // webpackBootstrap
break; break;
} }
} }
// perform the connection // perform the connection
if (node !== undefined && this.selectedControlNode !== undefined) { if (node !== undefined && this.selectedControlNode !== undefined) {
if (node.isCluster === true) { if (node.isCluster === true) {
@ -42825,6 +42888,7 @@ return /******/ (function(modules) { // webpackBootstrap
treeSpacing: { number: number }, treeSpacing: { number: number },
blockShifting: { boolean: boolean }, blockShifting: { boolean: boolean },
edgeMinimization: { boolean: boolean }, edgeMinimization: { boolean: boolean },
parentCentralization: { boolean: boolean },
direction: { string: ['UD', 'DU', 'LR', 'RL'] }, // UD, DU, LR, RL direction: { string: ['UD', 'DU', 'LR', 'RL'] }, // UD, DU, LR, RL
sortMethod: { string: ['hubsize', 'directed'] }, // hubsize, directed sortMethod: { string: ['hubsize', 'directed'] }, // hubsize, directed
__type__: { object: object, boolean: boolean } __type__: { object: object, boolean: boolean }
@ -42918,6 +42982,7 @@ return /******/ (function(modules) { // webpackBootstrap
shapeProperties: { shapeProperties: {
borderDashes: { boolean: boolean, array: array }, borderDashes: { boolean: boolean, array: array },
borderRadius: { number: number }, borderRadius: { number: number },
interpolation: { boolean: boolean },
useImageSize: { boolean: boolean }, useImageSize: { boolean: boolean },
useBorderWithImage: { boolean: boolean }, useBorderWithImage: { boolean: boolean },
__type__: { object: object } __type__: { object: object }
@ -43058,6 +43123,7 @@ return /******/ (function(modules) { // webpackBootstrap
shapeProperties: { shapeProperties: {
borderDashes: false, borderDashes: false,
borderRadius: [6, 0, 20, 1], borderRadius: [6, 0, 20, 1],
interpolation: true,
useImageSize: false useImageSize: false
}, },
size: [25, 0, 200, 1] size: [25, 0, 200, 1]
@ -43128,6 +43194,7 @@ return /******/ (function(modules) { // webpackBootstrap
treeSpacing: [200, 20, 500, 5], treeSpacing: [200, 20, 500, 5],
blockShifting: true, blockShifting: true,
edgeMinimization: true, edgeMinimization: true,
parentCentralization: true,
direction: ['UD', 'DU', 'LR', 'RL'], // UD, DU, LR, RL direction: ['UD', 'DU', 'LR', 'RL'], // UD, DU, LR, RL
sortMethod: ['hubsize', 'directed'] // hubsize, directed 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> </td>
</tr> </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"> <tr id="getEventProperties">
<td>getEventProperties(event)</td> <td>getEventProperties(event)</td>
<td>Object</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> <td>Get the current visible window. Returns an object with properties <code>start: Date</code> and <code>end: Date</code>.</td>
</tr> </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> <tr>
<td>isGroupVisible(groupId)</td> <td>isGroupVisible(groupId)</td>
<td>Boolean</td> <td>Boolean</td>
@ -1305,6 +1304,12 @@ Graph2d.off('rangechanged', onChange);
<th>Properties</th> <th>Properties</th>
<th>Description</th> <th>Description</th>
</tr> </tr>
<tr>
<td>currentTimeTick</td>
<td>Fired when the current time bar redraws. The rate depends on the zoom level.</td>
</tr>
<tr> <tr>
<td>click</td> <td>click</td>
<td> <td>

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

@ -339,49 +339,49 @@ network.setOptions(options);
<tr><td id="event_configure">configure</td> <tr><td id="event_configure">configure</td>
<td>Object</td> <td>Object</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> </td>
</tr> </tr>
<tr><td id="event_edges">edges</td> <tr><td id="event_edges">edges</td>
<td>Object</td> <td>Object</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> </td>
</tr> </tr>
<tr><td id="event_nodes">nodes</td> <tr><td id="event_nodes">nodes</td>
<td>Object</td> <td>Object</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> </td>
</tr> </tr>
<tr><td id="event_groups">groups</td> <tr><td id="event_groups">groups</td>
<td>Object</td> <td>Object</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> </td>
</tr> </tr>
<tr><td id="event_layout">layout</td> <tr><td id="event_layout">layout</td>
<td>Object</td> <td>Object</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> </td>
</tr> </tr>
<tr><td id="event_interaction">interaction</td> <tr><td id="event_interaction">interaction</td>
<td>Object</td> <td>Object</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> </td>
</tr> </tr>
<tr><td id="event_manipulation">manipulation</td> <tr><td id="event_manipulation">manipulation</td>
<td>Object</td> <td>Object</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> </td>
</tr> </tr>
<tr><td id="event_physics">physics</td> <tr><td id="event_physics">physics</td>
<td>Object</td> <td>Object</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> </td>
</tr> </tr>
</table> </table>

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

@ -109,6 +109,7 @@ var options = {
treeSpacing: 200, treeSpacing: 200,
blockShifting: true, blockShifting: true,
edgeMinimization: true, edgeMinimization: true,
parentCentralization: true,
direction: 'UD', // UD, DU, LR, RL direction: 'UD', // UD, DU, LR, RL
sortMethod: 'hubsize' // hubsize, directed 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> 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. <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> 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.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> <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> 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: { shapeProperties: {
borderDashes: false, // only for borders borderDashes: false, // only for borders
borderRadius: 6, // only for box shape borderRadius: 6, // only for box shape
interpolation: false, // only for image and circularImage shapes
useImageSize: false, // only for image and circularImage shapes useImageSize: false, // only for image and circularImage shapes
useBorderWithImage: false // only for image shape 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>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> </td>
</tr> </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"> <tr parent="shapeProperties" class="hidden">
<td class="indent">shapeProperties.useImageSize</td> <td class="indent">shapeProperties.useImageSize</td>
<td>Boolean</td> <td>Boolean</td>

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

@ -1150,6 +1150,12 @@ document.getElementById('myTimeline').onclick = function (event) {
</td> </td>
</tr> </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> <tr>
<td>getSelection()</td> <td>getSelection()</td>
<td>number[]</td> <td>number[]</td>
@ -1342,6 +1348,11 @@ timeline.off('select', onSelect);
<th>Description</th> <th>Description</th>
</tr> </tr>
<tr>
<td>currentTimeTick</td>
<td>Fired when the current time bar redraws. The rate depends on the zoom level.</td>
</tr>
<tr> <tr>
<td>click</td> <td>click</td>
<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. <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> Each node will try to move along its free axis to reduce the total length of it's edges.</td>
</tr> </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> </table>
<br /><br /> <br /><br />
Play with the settings below the network and see how the layout changes! 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> </head>
<body> <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> <div id="mynetwork"></div>
<script type="text/javascript"> <script type="text/javascript">
var color = 'gray';
var len = undefined;
var nodes = [ var nodes = [
{id: 0, label: "0", group: 'source'}, {id: 0, label: "0", group: 'source'},
{id: 1, label: "1", group: 'icons'}, {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> </script>
</body> </body>
</html> </html>

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

@ -2237,9 +2237,9 @@ Graph3d.prototype._showTooltip = function (dataPoint) {
} }
else { else {
content.innerHTML = '<table>' + 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>'; '</table>';
} }

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

@ -6,7 +6,7 @@ div.vis-network-tooltip {
font-family: verdana; font-family: verdana;
font-size:14px; font-size:14px;
font-color:#000000;
color:#000000;
background-color: #f5f4ed; background-color: #f5f4ed;
-moz-border-radius: 3px; -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 selectedNodesCount = this.selectionHandler._getSelectedNodeCount();
let currentSelection = this.selectionHandler.getSelection(); let currentSelection = this.selectionHandler.getSelection();
let {nodesChanges, edgesChanges} = this._determineIfDifferent(previousSelection, currentSelection);
let {nodesChanged, edgesChanged} = this._determineIfDifferent(previousSelection, currentSelection);
let nodeSelected = false; let nodeSelected = false;
if (selectedNodesCount - previouslySelectedNodeCount > 0) { // node was selected if (selectedNodesCount - previouslySelectedNodeCount > 0) { // node was selected
@ -197,32 +197,34 @@ class InteractionHandler {
selected = true; selected = true;
nodeSelected = 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('deselectNode', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectNode', event, pointer);
nodeSelected = true;
selected = 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('deselectNode', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectNode', event, pointer);
nodeSelected = true;
selected = true; selected = true;
} }
// handle the selected edges // handle the selected edges
if (selectedEdgesCount - previouslySelectedEdgeCount > 0 && nodeSelected === false) { // edge was selected if (selectedEdgesCount - previouslySelectedEdgeCount > 0 && nodeSelected === false) { // edge was selected
this.selectionHandler._generateClickEvent('selectEdge', event, pointer); this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true; 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('deselectEdge', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true; 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('deselectEdge', event, pointer, previousSelection);
this.selectionHandler._generateClickEvent('selectEdge', event, pointer);
selected = true; selected = true;
} }
// fire the select event if anything has been selected or deselected // fire the select event if anything has been selected or deselected
if (selected === true) { // select or unselect if (selected === true) { // select or unselect
this.selectionHandler._generateClickEvent('select', event, pointer); this.selectionHandler._generateClickEvent('select', event, pointer);
@ -234,35 +236,35 @@ class InteractionHandler {
* This function checks if the nodes and edges previously selected have changed. * This function checks if the nodes and edges previously selected have changed.
* @param previousSelection * @param previousSelection
* @param currentSelection * @param currentSelection
* @returns {{nodesChanges: boolean, edgesChanges: boolean}}
* @returns {{nodesChanged: boolean, edgesChanged: boolean}}
* @private * @private
*/ */
_determineIfDifferent(previousSelection,currentSelection) { _determineIfDifferent(previousSelection,currentSelection) {
let nodesChanges = false;
let edgesChanges = false;
let nodesChanged = false;
let edgesChanged = false;
for (let i = 0; i < previousSelection.nodes.length; i++) { for (let i = 0; i < previousSelection.nodes.length; i++) {
if (currentSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) { if (currentSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) {
nodesChanges = true;
nodesChanged = true;
} }
} }
for (let i = 0; i < currentSelection.nodes.length; i++) { for (let i = 0; i < currentSelection.nodes.length; i++) {
if (previousSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) { if (previousSelection.nodes.indexOf(previousSelection.nodes[i]) === -1) {
nodesChanges = true;
nodesChanged = true;
} }
} }
for (let i = 0; i < previousSelection.edges.length; i++) { for (let i = 0; i < previousSelection.edges.length; i++) {
if (currentSelection.edges.indexOf(previousSelection.edges[i]) === -1) { if (currentSelection.edges.indexOf(previousSelection.edges[i]) === -1) {
edgesChanges = true;
edgesChanged = true;
} }
} }
for (let i = 0; i < currentSelection.edges.length; i++) { for (let i = 0; i < currentSelection.edges.length; i++) {
if (previousSelection.edges.indexOf(previousSelection.edges[i]) === -1) { 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, treeSpacing: 200,
blockShifting: true, blockShifting: true,
edgeMinimization: true, edgeMinimization: true,
parentCentralization: true,
direction: 'UD', // UD, DU, LR, RL direction: 'UD', // UD, DU, LR, RL
sortMethod: 'hubsize' // hubsize, directed sortMethod: 'hubsize' // hubsize, directed
} }
@ -294,8 +295,8 @@ class LayoutEngine {
let undefinedLevel = false; let undefinedLevel = false;
this.hierarchicalLevels = {}; this.hierarchicalLevels = {};
this.lastNodeOnLevel = {}; this.lastNodeOnLevel = {};
this.hierarchicalParents = {};
this.hierarchicalChildren = {};
this.hierarchicalChildrenReference = {};
this.hierarchicalParentReference = {};
this.hierarchicalTrees = {}; this.hierarchicalTrees = {};
this.treeIndex = -1; this.treeIndex = -1;
@ -379,9 +380,7 @@ class LayoutEngine {
let treeSizes = getTreeSizes(); let treeSizes = getTreeSizes();
for (let i = 0; i < treeSizes.length - 1; i++) { for (let i = 0; i < treeSizes.length - 1; i++) {
let diff = treeSizes[i].max - treeSizes[i+1].min; 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) { for (let nodeId in this.hierarchicalTrees) {
if (this.hierarchicalTrees.hasOwnProperty(nodeId)) { if (this.hierarchicalTrees.hasOwnProperty(nodeId)) {
if (this.hierarchicalTrees[nodeId] === index) { 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 // get the width of all trees
let getTreeSizes = () => { let getTreeSizes = () => {
let treeWidths = []; let treeWidths = [];
for (let i = 0; i < this.treeIndex; i++) {
for (let i = 0; i <= this.treeIndex; i++) {
treeWidths.push(getTreeSize(i)); treeWidths.push(getTreeSize(i));
} }
return treeWidths; return treeWidths;
@ -425,8 +426,8 @@ class LayoutEngine {
// get a map of all nodes in this branch // get a map of all nodes in this branch
let getBranchNodes = (source, map) => { let getBranchNodes = (source, map) => {
map[source.id] = true; 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) { if (children.length > 0) {
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
getBranchNodes(this.body.nodes[children[i]], map); getBranchNodes(this.body.nodes[children[i]], map);
@ -467,8 +468,8 @@ class LayoutEngine {
// get the maximum level of a branch. // get the maximum level of a branch.
let getMaxLevel = (nodeId) => { let getMaxLevel = (nodeId) => {
let level = this.hierarchicalLevels[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) { if (children.length > 0) {
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
level = Math.max(level,getMaxLevel(children[i])); level = Math.max(level,getMaxLevel(children[i]));
@ -487,13 +488,12 @@ class LayoutEngine {
// check if two nodes have the same parent(s) // check if two nodes have the same parent(s)
let hasSameParent = (node1, node2) => { 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) { if (parents1 === undefined || parents2 === undefined) {
return false; return false;
} }
parents1 = parents1.parents;
parents2 = parents2.parents;
for (let i = 0; i < parents1.length; i++) { for (let i = 0; i < parents1.length; i++) {
for (let j = 0; j < parents2.length; j++) { for (let j = 0; j < parents2.length; j++) {
if (parents1[i] == parents2[j]) { if (parents1[i] == parents2[j]) {
@ -676,7 +676,7 @@ class LayoutEngine {
} }
if (newPosition !== nodePosition) { if (newPosition !== nodePosition) {
//console.log("moving Node:",diff, minSpace, maxSpace)
//console.log("moving Node:",diff, minSpace, maxSpace);
this._setPositionForHierarchy(node, newPosition, undefined, true); this._setPositionForHierarchy(node, newPosition, undefined, true);
//this.body.emitter.emit("_redraw"); //this.body.emitter.emit("_redraw");
stillShifting = true; 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 shiftBranchesCloserBottomUp = (iterations) => {
let levels = Object.keys(this.distributionOrdering); let levels = Object.keys(this.distributionOrdering);
levels = levels.reverse(); 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. // the actual work is done here.
if (this.options.hierarchical.blockShifting === true) { if (this.options.hierarchical.blockShifting === true) {
shiftBranchesCloserBottomUp(5); shiftBranchesCloserBottomUp(5);
@ -743,6 +756,10 @@ class LayoutEngine {
minimizeEdgeLengthBottomUp(20); minimizeEdgeLengthBottomUp(20);
} }
if (this.options.hierarchical.parentCentralization === true) {
centerAllParentsBottomUp()
}
shiftTrees(); shiftTrees();
} }
@ -794,16 +811,16 @@ class LayoutEngine {
* @private * @private
*/ */
_centerParent(node) { _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++) { for (var i = 0; i < parents.length; i++) {
let parentId = parents[i]; let parentId = parents[i];
let parentNode = this.body.nodes[parentId]; let parentNode = this.body.nodes[parentId];
if (this.hierarchicalParents[parentId]) {
if (this.hierarchicalChildrenReference[parentId]) {
// get the range of the children // get the range of the children
let minPos = 1e9; let minPos = 1e9;
let maxPos = -1e9; let maxPos = -1e9;
let children = this.hierarchicalParents[parentId].children;
let children = this.hierarchicalChildrenReference[parentId];
if (children.length > 0) { if (children.length > 0) {
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
let childNode = this.body.nodes[children[i]]; let childNode = this.body.nodes[children[i]];
@ -841,19 +858,107 @@ class LayoutEngine {
let nodeArray = Object.keys(distribution[level]); let nodeArray = Object.keys(distribution[level]);
nodeArray = this._indexArrayToNodes(nodeArray); nodeArray = this._indexArrayToNodes(nodeArray);
this._sortNodeArray(nodeArray); this._sortNodeArray(nodeArray);
let handledNodeCount = 0;
for (let i = 0; i < nodeArray.length; i++) { for (let i = 0; i < nodeArray.length; i++) {
let node = nodeArray[i]; let node = nodeArray[i];
if (this.positionedNodes[node.id] === undefined) { 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 * Receives an array with node indices and returns an array with the actual node references. Used for sorting based on
* node properties. * node properties.
@ -1047,14 +1152,14 @@ class LayoutEngine {
if (this.hierarchicalLevels[childNode.id] > this.hierarchicalLevels[parentNode.id]) { if (this.hierarchicalLevels[childNode.id] > this.hierarchicalLevels[parentNode.id]) {
let parentNodeId = parentNode.id; let parentNodeId = parentNode.id;
let childNodeId = childNode.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) { _crawlNetwork(callback = function() {}, startingNodeId) {
let progress = {}; let progress = {};
let crawler = (node) => {
let treeIndex = 0;
let crawler = (node, tree) => {
if (progress[node.id] === undefined) { 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; progress[node.id] = true;
let childNode; let childNode;
for (let i = 0; i < node.edges.length; i++) { for (let i = 0; i < node.edges.length; i++) {
@ -1085,7 +1198,7 @@ class LayoutEngine {
if (node.id !== childNode.id) { if (node.id !== childNode.id) {
callback(node, childNode, node.edges[i]); callback(node, childNode, node.edges[i]);
crawler(childNode);
crawler(childNode, tree);
} }
} }
} }
@ -1097,7 +1210,10 @@ class LayoutEngine {
if (startingNodeId === undefined) { if (startingNodeId === undefined) {
for (let i = 0; i < this.body.nodeIndices.length; i++) { for (let i = 0; i < this.body.nodeIndices.length; i++) {
let node = this.body.nodes[this.body.nodeIndices[i]]; let node = this.body.nodes[this.body.nodeIndices[i]];
crawler(node);
if (progress[node.id] === undefined) {
crawler(node, treeIndex);
treeIndex += 1;
}
} }
} }
else { 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 * Shift a branch a certain distance
* @param parentId * @param parentId
@ -1195,9 +1240,9 @@ class LayoutEngine {
else { else {
this.body.nodes[parentId].y += diff; 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) { _findCommonParent(childA,childB) {
let parents = {}; let parents = {};
let iterateParents = (parents,child) => { 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; parents[parent] = true;
iterateParents(parents, parent) iterateParents(parents, parent)
} }
} }
}; };
let findParent = (parents, child) => { 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) { if (parents[parent] !== undefined) {
return {foundParent:parent, withChild:child}; return {foundParent:parent, withChild:child};
} }
@ -1249,6 +1294,7 @@ class LayoutEngine {
* @private * @private
*/ */
_setPositionForHierarchy(node, position, level, doNotUpdate = false) { _setPositionForHierarchy(node, position, level, doNotUpdate = false) {
//console.log('_setPositionForHierarchy',node.id, position)
if (doNotUpdate !== true) { if (doNotUpdate !== true) {
if (this.distributionOrdering[level] === undefined) { if (this.distributionOrdering[level] === undefined) {
this.distributionOrdering[level] = []; this.distributionOrdering[level] = [];
@ -1260,28 +1306,6 @@ class LayoutEngine {
this.distributionIndex[node.id] = this.distributionOrdering[level].length - 1; this.distributionIndex[node.id] = this.distributionOrdering[level].length - 1;
} }
this.distributionOrderingPresence[level][node.id] = true; 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') { 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'; this.inMode = 'editNode';
if (typeof this.options.editNode === 'function') { if (typeof this.options.editNode === 'function') {
if (node.isCluster !== true) { if (node.isCluster !== true) {
let data = util.deepExtend({}, node.options, true);
let data = util.deepExtend({}, node.options, false);
data.x = node.x; data.x = node.x;
data.y = node.y; data.y = node.y;
@ -848,6 +848,11 @@ class ManipulationSystem {
edge.edgeType.to = to; 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'); this.body.emitter.emit('_redraw');
} }
@ -860,7 +865,6 @@ class ManipulationSystem {
this.body.emitter.emit('disablePhysics'); this.body.emitter.emit('disablePhysics');
let pointer = this.body.functions.getPointer(event.center); let pointer = this.body.functions.getPointer(event.center);
let pos = this.canvas.DOMtoCanvas(pointer); let pos = this.canvas.DOMtoCanvas(pointer);
if (this.selectedControlNode !== undefined) { if (this.selectedControlNode !== undefined) {
this.selectedControlNode.x = pos.x; this.selectedControlNode.x = pos.x;
this.selectedControlNode.y = pos.y; this.selectedControlNode.y = pos.y;
@ -884,12 +888,13 @@ class ManipulationSystem {
let pointer = this.body.functions.getPointer(event.center); let pointer = this.body.functions.getPointer(event.center);
let pointerObj = this.selectionHandler._pointerToPositionObject(pointer); let pointerObj = this.selectionHandler._pointerToPositionObject(pointer);
let edge = this.body.edges[this.edgeBeingEditedId]; let edge = this.body.edges[this.edgeBeingEditedId];
// if the node that was dragged is not a control node, return // if the node that was dragged is not a control node, return
if (this.selectedControlNode === undefined) { if (this.selectedControlNode === undefined) {
return; 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 overlappingNodeIds = this.selectionHandler._getAllNodesOverlappingWith(pointerObj);
let node = undefined; let node = undefined;
for (let i = overlappingNodeIds.length-1; i >= 0; i--) { for (let i = overlappingNodeIds.length-1; i >= 0; i--) {
@ -898,7 +903,6 @@ class ManipulationSystem {
break; break;
} }
} }
// perform the connection // perform the connection
if (node !== undefined && this.selectedControlNode !== undefined) { if (node !== undefined && this.selectedControlNode !== undefined) {
if (node.isCluster === true) { if (node.isCluster === true) {

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

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

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

@ -379,8 +379,8 @@ class Edge {
let arrowData = {}; let arrowData = {};
// restore edge targets to defaults // 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 // from and to arrows give a different end point for edges. we set them here
if (this.options.arrows.from.enabled === true) { 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 // draw shadow if enabled
this.enableShadow(ctx); 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. // disable shadows for other elements.
this.disableShadow(ctx); this.disableShadow(ctx);

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

@ -127,6 +127,7 @@ let allOptions = {
treeSpacing: { number }, treeSpacing: { number },
blockShifting: { boolean }, blockShifting: { boolean },
edgeMinimization: { boolean }, edgeMinimization: { boolean },
parentCentralization: { boolean },
direction: { string: ['UD', 'DU', 'LR', 'RL'] }, // UD, DU, LR, RL direction: { string: ['UD', 'DU', 'LR', 'RL'] }, // UD, DU, LR, RL
sortMethod: { string: ['hubsize', 'directed'] }, // hubsize, directed sortMethod: { string: ['hubsize', 'directed'] }, // hubsize, directed
__type__: { object, boolean } __type__: { object, boolean }
@ -220,6 +221,7 @@ let allOptions = {
shapeProperties: { shapeProperties: {
borderDashes: { boolean, array }, borderDashes: { boolean, array },
borderRadius: { number }, borderRadius: { number },
interpolation: { boolean },
useImageSize: { boolean }, useImageSize: { boolean },
useBorderWithImage: { boolean }, useBorderWithImage: { boolean },
__type__: { object } __type__: { object }
@ -361,6 +363,7 @@ let configureOptions = {
shapeProperties: { shapeProperties: {
borderDashes: false, borderDashes: false,
borderRadius: [6, 0, 20, 1], borderRadius: [6, 0, 20, 1],
interpolation: true,
useImageSize: false useImageSize: false
}, },
size: [25, 0, 200, 1] size: [25, 0, 200, 1]
@ -431,6 +434,7 @@ let configureOptions = {
treeSpacing: [200, 20, 500, 5], treeSpacing: [200, 20, 500, 5],
blockShifting: true, blockShifting: true,
edgeMinimization: true, edgeMinimization: true,
parentCentralization: true,
direction: ['UD', 'DU', 'LR', 'RL'], // UD, DU, LR, RL direction: ['UD', 'DU', 'LR', 'RL'], // UD, DU, LR, RL
sortMethod: ['hubsize', 'directed'] // hubsize, directed 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; if (interval > 1000) interval = 1000;
me.redraw(); me.redraw();
me.body.emitter.emit('currentTimeTick');
// start a renderTimer to adjust for the new time // start a renderTimer to adjust for the new time
me.currentTimeTimer = setTimeout(update, interval); 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, alignZeros: true,
left:{ left:{
range: {min:undefined,max:undefined}, 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} title: {text:undefined,style:undefined}
}, },
right:{ right:{
range: {min:undefined,max:undefined}, 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} title: {text:undefined,style:undefined}
} }
}; };

+ 1
- 1
package.json View File

@ -1,6 +1,6 @@
{ {
"name": "vis", "name": "vis",
"version": "4.14.0",
"version": "4.15.0",
"description": "A dynamic, browser-based visualization library.", "description": "A dynamic, browser-based visualization library.",
"homepage": "http://visjs.org/", "homepage": "http://visjs.org/",
"license": "(Apache-2.0 OR MIT)", "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"/> <link href="../dist/vis.css" rel="stylesheet" type="text/css"/>
<style type="text/css"> <style type="text/css">
#network{ #network{
width: 1900px;
width: 1200px;
height: 800px; height: 800px;
border: 1px solid lightgray; border: 1px solid lightgray;
} }
@ -16,53 +16,171 @@
<body> <body>
<h1>Network Test</h1> <h1>Network Test</h1>
<div id="network"></div> <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> </script>
</body> </body>
</html> </html>

+ 4
- 0
test/timeline.html View File

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

Loading…
Cancel
Save