diff --git a/HISTORY.md b/HISTORY.md index 5e2c3b1b..1a7d5378 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,11 +11,19 @@ http://visjs.org - Fixed dynamic edges not correctly handling non-existent nodes. - Accepted pull from @killerDJO for fixing selected and hover colors for edges. - Fixed bug with rightmouse button, scroll center and popup positions using the wrong coordinates. +- Fixed click to use. +- Fixed getConnectedEdges method. +- Fixed clustering bug. +- Added getNodesInCluster method. +- Renamed editNodeMode to editNode, editNodeMode now give a deprication log message. +- Added multiselect to the docs. +- Removed depricated dynamic entree, allow any smooth curve style for hierarchical layout. - Fixed bug with the moveTo and getViewPosition methods. ### Graph2d & Timeline -- Fixed #858, removed usage of deprictated unsubscribe from dataset. +- Fixed #858, #872, fixed usage of deprecated `unsubscribe` from DataSet. +- Fixed #869: add className with id to custom time bars ## 2015-05-22, version 4.0.0 diff --git a/docs/network/index.html b/docs/network/index.html index 14b0284c..e2470cde 100644 --- a/docs/network/index.html +++ b/docs/network/index.html @@ -622,6 +622,15 @@ var locales = { Returns: Boolean Returns true if the node whose ID has been supplied is a cluster. + + getNodesInCluster( + String clusterNodeId) + + + Returns: Array + Returns an array of all nodeIds of the nodes that would be released if you open the cluster. + + openCluster( String nodeId) diff --git a/docs/network/interaction.html b/docs/network/interaction.html index 54a306c4..bbd4f1ba 100644 --- a/docs/network/interaction.html +++ b/docs/network/interaction.html @@ -91,6 +91,7 @@ var options = { speed: {x: 10, y: 10, zoom: 0.02}, bindToWindow: true }, + multiselect: false, navigationButtons: false, selectable: true, selectConnectedEdges: true, @@ -127,6 +128,7 @@ network.setOptions(options); keyboard.speed.y Number 1 The speed at which the view moves in the y direction on pressing a key or pressing a navigation button. keyboard.speed.zoom Number 0.02 The speed at which the view zooms in or out pressing a key or pressing a navigation button. keyboard.bindToWindow Boolean true When binding the keyboard shortcuts to the window, they will work regardless of which DOM object has the focus. If you have multiple networks on your page, you could set this to false, making sure the keyboard shortcuts only work on the network that has the focus. + multiselect Boolean false When true, a longheld click (or touch) as well as a control-click will add to the selection. navigationButtons Boolean false When true, navigation buttons are drawn on the network canvas. These are HTML buttons and can be completely customized using CSS. selectable BooleantrueWhen true, the nodes and edges can be selected by the user. selectConnectedEdges BooleantrueWhen true, on selecting a node, its connecting edges are highlighted. diff --git a/docs/timeline/index.html b/docs/timeline/index.html index 84d320b7..4f92289e 100644 --- a/docs/timeline/index.html +++ b/docs/timeline/index.html @@ -935,8 +935,9 @@ function (option, path) { Add new vertical bar representing a custom time that can be dragged by the user. Parameter time can be a Date, Number, or String, and is new Date() by default. - Parameter id can be Number or String and is undefined by default.
- Returns id of the created bar. + Parameter id can be Number or String and is undefined by default. + The idcode> is added as CSS class name of the custom time bar, allowing to style multiple time bars differently. + The method returns id of the created bar. diff --git a/examples/timeline/other/customTimeBars.html b/examples/timeline/other/customTimeBars.html index 4794904c..37310165 100644 --- a/examples/timeline/other/customTimeBars.html +++ b/examples/timeline/other/customTimeBars.html @@ -25,7 +25,7 @@

- +

timechange event, index: , time: @@ -49,7 +49,7 @@ // Set first time bar customDate = new Date(customDate.getFullYear(), customDate.getMonth(), customDate.getDate() + 1); - timeline.addCustomTime(customDate, '1'); + timeline.addCustomTime(customDate, 't1'); document.getElementById('add').onclick = function () { try { diff --git a/lib/network/Network.js b/lib/network/Network.js index 7cf529ce..662226cf 100644 --- a/lib/network/Network.js +++ b/lib/network/Network.js @@ -204,8 +204,8 @@ Network.prototype.setOptions = function (options) { if (options.clickToUse !== undefined) { if (options.clickToUse === true) { if (this.activator === undefined) { - this.activator = new Activator(this.frame); - this.activator.on('change', this._createKeyBinds.bind(this)); + this.activator = new Activator(this.canvas.frame); + this.activator.on('change', () => {this.body.emitter.emit("activate")}); } } else { @@ -435,6 +435,7 @@ Network.prototype.findNode = function() {return this.clustering.findN Network.prototype.isCluster = function() {return this.clustering.isCluster.apply(this.clustering,arguments);}; Network.prototype.openCluster = function() {return this.clustering.openCluster.apply(this.clustering,arguments);}; Network.prototype.cluster = function() {return this.clustering.cluster.apply(this.clustering,arguments);}; +Network.prototype.getNodesInCluster = function() {return this.clustering.getNodesInCluster.apply(this.clustering,arguments);}; Network.prototype.clusterByConnection = function() {return this.clustering.clusterByConnection.apply(this.clustering,arguments);}; Network.prototype.clusterByHubsize = function() {return this.clustering.clusterByHubsize.apply(this.clustering,arguments);}; Network.prototype.clusterOutliers = function() {return this.clustering.clusterOutliers.apply(this.clustering,arguments);}; @@ -443,7 +444,7 @@ Network.prototype.enableEditMode = function() {return this.manipulation.ena Network.prototype.disableEditMode = function() {return this.manipulation.disableEditMode.apply(this.manipulation,arguments);}; Network.prototype.addNodeMode = function() {return this.manipulation.addNodeMode.apply(this.manipulation,arguments);}; Network.prototype.editNode = function() {return this.manipulation.editNode.apply(this.manipulation,arguments);}; -Network.prototype.editNodeMode = function() {console.log("please use editNode instead of editNodeMode"); return this.manipulation.editNode.apply(this.manipulation,arguments);}; +Network.prototype.editNodeMode = function() {console.log("Depricated: Please use editNode instead of editNodeMode."); return this.manipulation.editNode.apply(this.manipulation,arguments);}; Network.prototype.addEdgeMode = function() {return this.manipulation.addEdgeMode.apply(this.manipulation,arguments);}; Network.prototype.editEdgeMode = function() {return this.manipulation.editEdgeMode.apply(this.manipulation,arguments);}; Network.prototype.deleteSelected = function() {return this.manipulation.deleteSelected.apply(this.manipulation,arguments);}; @@ -458,7 +459,7 @@ Network.prototype.getConnectedNodes = function(objectId) { return this.edgesHandler.getConnectedNodes.apply(this.edgesHandler,arguments); } }; -Network.prototype.getEdges = function() {return this.nodesHandler.getEdges.apply(this.nodesHandler,arguments);}; +Network.prototype.getConnectedEdges = function() {return this.nodesHandler.getConnectedEdges.apply(this.nodesHandler,arguments);}; Network.prototype.startSimulation = function() {return this.physics.startSimulation.apply(this.physics,arguments);}; Network.prototype.stopSimulation = function() {return this.physics.stopSimulation.apply(this.physics,arguments);}; Network.prototype.stabilize = function() {return this.physics.stabilize.apply(this.physics,arguments);}; diff --git a/lib/network/modules/Clustering.js b/lib/network/modules/Clustering.js index e68234cc..071215b2 100644 --- a/lib/network/modules/Clustering.js +++ b/lib/network/modules/Clustering.js @@ -498,7 +498,6 @@ class ClusterEngine { delete this.body.edges[edgeId]; } else { - // one of the nodes connected to this edge is in a cluster. We give the edge to that cluster so it will be released when that cluster is opened. if (this.clusteredNodes[edge.fromId] !== undefined || this.clusteredNodes[edge.toId] !== undefined) { let fromId, toId; @@ -507,17 +506,17 @@ class ClusterEngine { let clusterNode = this.body.nodes[clusterId]; clusterNode.containedEdges[edgeId] = edge; - // if both from and to nodes are visible, we create a new temporary edge - if (edge.from.options.hidden !== true && edge.to.options.hidden !== true) { - if (this.clusteredNodes[edge.fromId] !== undefined) { - fromId = clusterId; - toId = edge.toId; - } - else { - fromId = edge.fromId; - toId = clusterId; - } + if (this.clusteredNodes[edge.fromId] !== undefined) { + fromId = clusterId; + toId = edge.toId; + } + else { + fromId = edge.fromId; + toId = clusterId; + } + // if both from and to nodes are visible, we create a new temporary edge + if (this.body.nodes[fromId].options.hidden !== true && this.body.nodes[toId].options.hidden !== true) { let clonedOptions = this._cloneOptions(edge, 'edge'); let id = 'clusterEdge:' + util.randomUUID(); util.deepExtend(clonedOptions, clusterNode.clusterEdgeProperties); @@ -553,30 +552,18 @@ class ClusterEngine { } } - - /** - * Connect an edge that was previously contained from cluster A to cluster B if the node that it was originally connected to - * is currently residing in cluster B - * @param edge - * @param nodeId - * @param from - * @private - */ - _connectEdge(edge, nodeId, from) { - let clusterStack = this.findNode(nodeId); - if (from === true) { - edge.from = clusterStack[clusterStack.length - 1]; - edge.fromId = clusterStack[clusterStack.length - 1].id; - clusterStack.pop(); - edge.fromArray = clusterStack; - } - else { - edge.to = clusterStack[clusterStack.length - 1]; - edge.toId = clusterStack[clusterStack.length - 1].id; - clusterStack.pop(); - edge.toArray = clusterStack; + getNodesInCluster(clusterId) { + let nodesArray = [] + if (this.isCluster(clusterId) === true) { + let containedNodes = this.body.nodes[clusterId].containedNodes; + for (let nodeId in containedNodes) { + if (containedNodes.hasOwnProperty(nodeId)) { + nodesArray.push(nodeId) + } + } } - edge.connect(); + + return nodesArray; } /** diff --git a/lib/network/modules/EdgesHandler.js b/lib/network/modules/EdgesHandler.js index 0eb18e6a..25f5a05d 100644 --- a/lib/network/modules/EdgesHandler.js +++ b/lib/network/modules/EdgesHandler.js @@ -95,6 +95,9 @@ class EdgesHandler { bindEventListeners() { // this allows external modules to force all dynamic curves to turn static. this.body.emitter.on("_forceDisableDynamicCurves", (type) => { + if (type === 'dynamic') { + type = 'continuous'; + } let emitChange = false; for (let edgeId in this.body.edges) { if (this.body.edges.hasOwnProperty(edgeId)) { @@ -106,12 +109,12 @@ class EdgesHandler { if (edgeData !== undefined) { let edgeOptions = edgeData.smooth; if (edgeOptions !== undefined) { - if (edgeOptions.enabled === true && edgeOptions.dynamic === true) { + if (edgeOptions.enabled === true && edgeOptions.type === 'dynamic') { if (type === undefined) { edge.setOptions({smooth: false}); } else { - edge.setOptions({smooth: {dynamic: false, type: type}}); + edge.setOptions({smooth: {type: type}}); } emitChange = true; } diff --git a/lib/network/modules/LayoutEngine.js b/lib/network/modules/LayoutEngine.js index 9748834b..65b8f691 100644 --- a/lib/network/modules/LayoutEngine.js +++ b/lib/network/modules/LayoutEngine.js @@ -100,21 +100,34 @@ class LayoutEngine { // disable smooth curves if nothing is defined. If smooth curves have been turned on, turn them into static smooth curves. if (allOptions.edges === undefined) { - this.optionsBackup.edges = {smooth:true, dynamic:true}; + this.optionsBackup.edges = {smooth:{enabled:true, type:'dynamic'}}; allOptions.edges = {smooth: false}; } else if (allOptions.edges.smooth === undefined) { - this.optionsBackup.edges = {smooth:true, dynamic:true}; + this.optionsBackup.edges = {smooth:{enabled:true, type:'dynamic'}}; allOptions.edges.smooth = false; } else { if (typeof allOptions.edges.smooth === 'boolean') { - this.optionsBackup.edges = {smooth:allOptions.edges.smooth, dynamic:true}; - allOptions.edges.smooth = {enabled: allOptions.edges.smooth, dynamic: false, type:type} + this.optionsBackup.edges = {smooth:allOptions.edges.smooth}; + allOptions.edges.smooth = {enabled: allOptions.edges.smooth, type:type} } else { - this.optionsBackup.edges = {smooth: allOptions.edges.smooth.enabled === undefined ? true : allOptions.edges.smooth.enabled, dynamic:true}; - allOptions.edges.smooth = {enabled: allOptions.edges.smooth.enabled === undefined ? true : allOptions.edges.smooth.enabled, dynamic: false, type:type} + // allow custom types except for dynamic + if (allOptions.edges.smooth.type !== undefined && allOptions.edges.smooth.type !== 'dynamic') { + type = allOptions.edges.smooth.type; + } + + this.optionsBackup.edges = { + smooth: allOptions.edges.smooth.enabled === undefined ? true : allOptions.edges.smooth.enabled, + type:allOptions.edges.smooth.type === undefined ? 'dynamic' : allOptions.edges.smooth.type, + roundness: allOptions.edges.smooth.roundness === undefined ? 0.5 : allOptions.edges.smooth.roundness + }; + allOptions.edges.smooth = { + enabled: allOptions.edges.smooth.enabled === undefined ? true : allOptions.edges.smooth.enabled, + type:type, + roundness: allOptions.edges.smooth.roundness === undefined ? 0.5 : allOptions.edges.smooth.roundness + } } } @@ -318,7 +331,7 @@ class LayoutEngine { if (this.body.nodes.hasOwnProperty(nodeId)) { node = this.body.nodes[nodeId]; if (node.edges.length === hubSize) { - this._setLevel(0, node); + this._setLevelByHubsize(0, node); } } } @@ -334,7 +347,7 @@ class LayoutEngine { * @param parentId * @private */ - _setLevel(level, node) { + _setLevelByHubsize(level, node) { if (this.hierarchicalLevels[node.id] !== undefined) return; @@ -347,7 +360,7 @@ class LayoutEngine { else { childNode = node.edges[i].to; } - this._setLevel(level + 1, childNode); + this._setLevelByHubsize(level + 1, childNode); } } diff --git a/lib/network/modules/ManipulationSystem.js b/lib/network/modules/ManipulationSystem.js index 48f9e42b..16b855df 100644 --- a/lib/network/modules/ManipulationSystem.js +++ b/lib/network/modules/ManipulationSystem.js @@ -946,7 +946,6 @@ class ManipulationSystem { physics: false, smooth: { enabled: true, - dynamic: false, type: 'continuous', roundness: 0.5 } diff --git a/lib/network/modules/NodesHandler.js b/lib/network/modules/NodesHandler.js index 17459a57..70648a82 100644 --- a/lib/network/modules/NodesHandler.js +++ b/lib/network/modules/NodesHandler.js @@ -400,7 +400,7 @@ class NodesHandler { * @param nodeId * @returns {*} */ - getEdges(nodeId) { + getConnectedEdges(nodeId) { let edgeList = []; if (this.body.nodes[nodeId] !== undefined) { let node = this.body.nodes[nodeId]; @@ -408,7 +408,11 @@ class NodesHandler { edgeList.push(node.edges[i].id) } } - return nodeList; + else { + console.log("NodeId provided for getConnectedEdges does not exist. Provided: ", nodeId) + } + console.log(edgeList) + return edgeList; } } diff --git a/lib/network/modules/components/NavigationHandler.js b/lib/network/modules/components/NavigationHandler.js index 880c9cdb..1ef2fb72 100644 --- a/lib/network/modules/components/NavigationHandler.js +++ b/lib/network/modules/components/NavigationHandler.js @@ -52,8 +52,6 @@ class NavigationHandler { this.navigationHammers = []; } - this._navigationReleaseOverload = function() {}; - // clean up previous navigation items if (this.navigationDOM && this.navigationDOM['wrapper'] && this.navigationDOM['wrapper'].parentNode) { this.navigationDOM['wrapper'].parentNode.removeChild(this.navigationDOM['wrapper']); @@ -160,7 +158,6 @@ class NavigationHandler { } if (this.options.keyboard.enabled === true) { - if (this.options.keyboard.bindToWindow === true) { this.keycharm = keycharm({container: window, preventDefault: true}); } diff --git a/lib/shared/Configurator.js b/lib/shared/Configurator.js index 2083ef7c..8401495d 100644 --- a/lib/shared/Configurator.js +++ b/lib/shared/Configurator.js @@ -313,7 +313,10 @@ class Configurator { range.step = step; if (value !== undefined) { - if (value * 0.1 < min) { + if (value < 0 && value * 2 < min) { + range.min = value*2; + } + else if (value * 0.1 < min) { range.min = value / 10; } if (value * 2 > max && max !== 1) { diff --git a/lib/timeline/component/CustomTime.js b/lib/timeline/component/CustomTime.js index 8efdb8ca..86aec043 100644 --- a/lib/timeline/component/CustomTime.js +++ b/lib/timeline/component/CustomTime.js @@ -34,10 +34,10 @@ function CustomTime (body, options) { this.eventParams = {}; // stores state parameters while dragging the bar + this.setOptions(options); + // create the DOM this._create(); - - this.setOptions(options); } CustomTime.prototype = new Component(); @@ -63,7 +63,7 @@ CustomTime.prototype.setOptions = function(options) { CustomTime.prototype._create = function() { var bar = document.createElement('div'); bar['custom-time'] = this; - bar.className = 'vis-custom-time'; + bar.className = 'vis-custom-time ' + (this.options.id || ''); bar.style.position = 'absolute'; bar.style.top = '0px'; bar.style.height = '100%'; diff --git a/test/networkTest.html b/test/networkTest.html index 4d2005b3..792b9947 100644 --- a/test/networkTest.html +++ b/test/networkTest.html @@ -7,7 +7,7 @@