@ -35,6 +35,7 @@ function Network (container, data, options) {
throw new SyntaxError ( 'Constructor must be called with the new operator' ) ;
throw new SyntaxError ( 'Constructor must be called with the new operator' ) ;
}
}
this . _determineBrowserMethod ( ) ;
this . _initializeMixinLoaders ( ) ;
this . _initializeMixinLoaders ( ) ;
// create variables and set default values
// create variables and set default values
@ -43,8 +44,8 @@ function Network (container, data, options) {
// render and calculation settings
// render and calculation settings
this . renderRefreshRate = 60 ; // hz (fps)
this . renderRefreshRate = 60 ; // hz (fps)
this . renderTimestep = 1000 / this . renderRefreshRate ; // ms -- saves calculation later on
this . renderTimestep = 1000 / this . renderRefreshRate ; // ms -- saves calculation later on
this . renderTime = 0.5 * this . renderTimestep ; // measured time it takes to render a frame
this . maxPhysicsTicksPerRender = 3 ; // max amount of physics ticks per render step.
this . renderTime = 0 ; // measured time it takes to render a frame
this . physicsTime = 0 ; // measured time it takes to render a frame
this . physicsDiscreteStepsize = 0.50 ; // discrete stepsize of the simulation
this . physicsDiscreteStepsize = 0.50 ; // discrete stepsize of the simulation
this . initializing = true ;
this . initializing = true ;
@ -79,9 +80,6 @@ function Network (container, data, options) {
background : '#D2E5FF'
background : '#D2E5FF'
}
}
} ,
} ,
borderColor : '#2B7CE9' ,
backgroundColor : '#97C2FC' ,
highlightColor : '#D2E5FF' ,
group : undefined ,
group : undefined ,
borderWidth : 1 ,
borderWidth : 1 ,
borderWidthSelected : undefined
borderWidthSelected : undefined
@ -114,7 +112,7 @@ function Network (container, data, options) {
physics : {
physics : {
barnesHut : {
barnesHut : {
enabled : true ,
enabled : true ,
theta : 1 / 0.6 , // inverted to save time during calculation
thetaInverted : 1 / 0.5 , // inverted to save time during calculation
gravitationalConstant : - 2000 ,
gravitationalConstant : - 2000 ,
centralGravity : 0.3 ,
centralGravity : 0.3 ,
springLength : 95 ,
springLength : 95 ,
@ -238,7 +236,7 @@ function Network (container, data, options) {
var network = this ;
var network = this ;
this . groups = new Groups ( ) ; // object with groups
this . groups = new Groups ( ) ; // object with groups
this . images = new Images ( ) ; // object with images
this . images = new Images ( ) ; // object with images
this . images . setOnloadCallback ( function ( ) {
this . images . setOnloadCallback ( function ( status ) {
network . _redraw ( ) ;
network . _redraw ( ) ;
} ) ;
} ) ;
@ -352,6 +350,25 @@ function Network (container, data, options) {
// Extend Network with an Emitter mixin
// Extend Network with an Emitter mixin
Emitter ( Network . prototype ) ;
Emitter ( Network . prototype ) ;
/ * *
* Determine if the browser requires a setTimeout or a requestAnimationFrame . This was required because
* some implementations ( safari and IE9 ) did not support requestAnimationFrame
* @ private
* /
Network . prototype . _determineBrowserMethod = function ( ) {
var browserType = navigator . userAgent . toLowerCase ( ) ;
this . requiresTimeout = false ;
if ( browserType . indexOf ( 'msie 9.0' ) != - 1 ) { // IE 9
this . requiresTimeout = true ;
}
else if ( browserType . indexOf ( 'safari' ) != - 1 ) { // safari
if ( browserType . indexOf ( 'chrome' ) <= - 1 ) {
this . requiresTimeout = true ;
}
}
}
/ * *
/ * *
* Get the script path where the vis . js library is located
* Get the script path where the vis . js library is located
*
*
@ -385,10 +402,10 @@ Network.prototype._getRange = function() {
for ( var nodeId in this . nodes ) {
for ( var nodeId in this . nodes ) {
if ( this . nodes . hasOwnProperty ( nodeId ) ) {
if ( this . nodes . hasOwnProperty ( nodeId ) ) {
node = this . nodes [ nodeId ] ;
node = this . nodes [ nodeId ] ;
if ( minX > ( node . x ) ) { minX = node . x ; }
if ( maxX < ( node . x ) ) { maxX = node . x ; }
if ( minY > ( node . y ) ) { minY = node . y ; }
if ( maxY < ( node . y ) ) { maxY = node . y ; }
if ( minX > ( node . boundingBo x. left ) ) { minX = node . boundingBo x. left ; }
if ( maxX < ( node . boundingBo x. right ) ) { maxX = node . boundingBo x. right ; }
if ( minY > ( node . boundingBox . bottom ) ) { minY = node . boundingBox . bottom ; }
if ( maxY < ( node . boundingBox . top ) ) { maxY = node . boundingBox . top ; }
}
}
}
}
if ( minX == 1 e9 && maxX == - 1 e9 && minY == 1 e9 && maxY == - 1 e9 ) {
if ( minX == 1 e9 && maxX == - 1 e9 && minY == 1 e9 && maxY == - 1 e9 ) {
@ -416,6 +433,8 @@ Network.prototype._findCenter = function(range) {
* @ param { Boolean } [ disableStart ] | If true , start is not called .
* @ param { Boolean } [ disableStart ] | If true , start is not called .
* /
* /
Network . prototype . zoomExtent = function ( animationOptions , initialZoom , disableStart ) {
Network . prototype . zoomExtent = function ( animationOptions , initialZoom , disableStart ) {
this . _redraw ( true ) ;
if ( initialZoom === undefined ) {
if ( initialZoom === undefined ) {
initialZoom = false ;
initialZoom = false ;
}
}
@ -571,7 +590,6 @@ Network.prototype.setData = function(data, disableStart) {
Network . prototype . setOptions = function ( options ) {
Network . prototype . setOptions = function ( options ) {
if ( options ) {
if ( options ) {
var prop ;
var prop ;
var fields = [ 'nodes' , 'edges' , 'smoothCurves' , 'hierarchicalLayout' , 'clustering' , 'navigation' ,
var fields = [ 'nodes' , 'edges' , 'smoothCurves' , 'hierarchicalLayout' , 'clustering' , 'navigation' ,
'keyboard' , 'dataManipulation' , 'onAdd' , 'onEdit' , 'onEditEdge' , 'onConnect' , 'onDelete' , 'clickToUse'
'keyboard' , 'dataManipulation' , 'onAdd' , 'onEdit' , 'onEditEdge' , 'onConnect' , 'onDelete' , 'clickToUse'
] ;
] ;
@ -629,6 +647,7 @@ Network.prototype.setOptions = function (options) {
if ( options . edges . color . highlight !== undefined ) { this . constants . edges . color . highlight = options . edges . color . highlight ; }
if ( options . edges . color . highlight !== undefined ) { this . constants . edges . color . highlight = options . edges . color . highlight ; }
if ( options . edges . color . hover !== undefined ) { this . constants . edges . color . hover = options . edges . color . hover ; }
if ( options . edges . color . hover !== undefined ) { this . constants . edges . color . hover = options . edges . color . hover ; }
}
}
this . constants . edges . inheritColor = false ;
}
}
if ( ! options . edges . fontColor ) {
if ( ! options . edges . fontColor ) {
@ -672,8 +691,10 @@ Network.prototype.setOptions = function (options) {
if ( 'clickToUse' in options ) {
if ( 'clickToUse' in options ) {
if ( options . clickToUse ) {
if ( options . clickToUse ) {
this . activator = new Activator ( this . frame ) ;
this . activator . on ( 'change' , this . _createKeyBinds . bind ( this ) ) ;
if ( ! this . activator ) {
this . activator = new Activator ( this . frame ) ;
this . activator . on ( 'change' , this . _createKeyBinds . bind ( this ) ) ;
}
}
}
else {
else {
if ( this . activator ) {
if ( this . activator ) {
@ -686,24 +707,25 @@ Network.prototype.setOptions = function (options) {
if ( options . labels ) {
if ( options . labels ) {
throw new Error ( 'Option "labels" is deprecated. Use options "locale" and "locales" instead.' ) ;
throw new Error ( 'Option "labels" is deprecated. Use options "locale" and "locales" instead.' ) ;
}
}
}
// (Re)loading the mixins that can be enabled or disabled in the options.
// load the force calculation functions, grouped under the physics system.
this . _loadPhysicsSystem ( ) ;
// load the navigation system.
this . _loadNavigationControls ( ) ;
// load the data manipulation system
this . _loadManipulationSystem ( ) ;
// configure the smooth curves
this . _configureSmoothCurves ( ) ;
// (Re)loading the mixins that can be enabled or disabled in the options.
// load the force calculation functions, grouped under the physics system.
this . _loadPhysicsSystem ( ) ;
// load the navigation system.
this . _loadNavigationControls ( ) ;
// load the data manipulation system
this . _loadManipulationSystem ( ) ;
// configure the smooth curves
this . _configureSmoothCurves ( ) ;
// bind keys. If disabled, this will not do anything;
this . _createKeyBinds ( ) ;
this . setSize ( this . constants . width , this . constants . height ) ;
this . moving = true ;
this . start ( ) ;
// bind keys. If disabled, this will not do anything;
this . _createKeyBinds ( ) ;
this . setSize ( this . constants . width , this . constants . height ) ;
this . moving = true ;
this . start ( ) ;
}
} ;
} ;
@ -730,11 +752,9 @@ Network.prototype._create = function () {
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
this . frame . canvas = document . createElement ( "canvas" ) ;
this . frame . canvas = document . createElement ( "canvas" ) ;
this . frame . canvas . style . position = 'relative' ;
this . frame . canvas . style . position = 'relative' ;
this . frame . appendChild ( this . frame . canvas ) ;
this . frame . appendChild ( this . frame . canvas ) ;
if ( ! this . frame . canvas . getContext ) {
if ( ! this . frame . canvas . getContext ) {
var noCanvas = document . createElement ( 'DIV' ) ;
var noCanvas = document . createElement ( 'DIV' ) ;
noCanvas . style . color = 'red' ;
noCanvas . style . color = 'red' ;
@ -744,17 +764,13 @@ Network.prototype._create = function () {
this . frame . canvas . appendChild ( noCanvas ) ;
this . frame . canvas . appendChild ( noCanvas ) ;
}
}
else {
else {
var ctx = this . frame . canvas . getContext ( "2d" ) ;
var ctx = this . frame . canvas . getContext ( "2d" ) ;
this . pixelRatio = ( window . devicePixelRatio || 1 ) / ( ctx . webkitBackingStorePixelRatio ||
this . pixelRatio = ( window . devicePixelRatio || 1 ) / ( ctx . webkitBackingStorePixelRatio ||
ctx . mozBackingStorePixelRatio ||
ctx . mozBackingStorePixelRatio ||
ctx . msBackingStorePixelRatio ||
ctx . msBackingStorePixelRatio ||
ctx . oBackingStorePixelRatio ||
ctx . oBackingStorePixelRatio ||
ctx . backingStorePixelRatio || 1 ) ;
ctx . backingStorePixelRatio || 1 ) ;
this . frame . canvas . getContext ( "2d" ) . setTransform ( this . pixelRatio , 0 , 0 , this . pixelRatio , 0 , 0 ) ;
this . frame . canvas . getContext ( "2d" ) . setTransform ( this . pixelRatio , 0 , 0 , this . pixelRatio , 0 , 0 ) ;
}
}
@ -782,7 +798,7 @@ Network.prototype._create = function () {
this . hammerFrame = Hammer ( this . frame , {
this . hammerFrame = Hammer ( this . frame , {
prevent_default : true
prevent_default : true
} ) ;
} ) ;
this . hammerFrame . on ( 'release' , me . _onRelease . bind ( me ) ) ;
this . hammerFrame . on ( 'release' , me . _onRelease . bind ( me ) ) ;
// add the frame to the container element
// add the frame to the container element
this . containerElement . appendChild ( this . frame ) ;
this . containerElement . appendChild ( this . frame ) ;
@ -836,8 +852,20 @@ Network.prototype._createKeyBinds = function() {
}
}
} ;
} ;
/ * *
* Cleans up all bindings of the network , removing it fully from the memory IF the variable is set to null after calling this function .
* var network = new vis . Network ( . . ) ;
* network . destroy ( ) ;
* network = null ;
* /
Network . prototype . destroy = function ( ) {
Network . prototype . destroy = function ( ) {
this . start = function ( ) { } ;
this . redraw = function ( ) { } ;
this . timer = false ;
// cleanup physicsConfiguration if it exists
this . _cleanupPhysicsConfiguration ( ) ;
// remove keybindings
// remove keybindings
this . keycharm . reset ( ) ;
this . keycharm . reset ( ) ;
@ -847,7 +875,15 @@ Network.prototype.destroy = function() {
// clear events
// clear events
this . off ( ) ;
this . off ( ) ;
// remove all elements from the container element.
while ( this . frame . hasChildNodes ( ) ) {
this . frame . removeChild ( this . frame . firstChild ) ;
}
// remove all elements from the container element.
while ( this . containerElement . hasChildNodes ( ) ) {
this . containerElement . removeChild ( this . containerElement . firstChild ) ;
}
}
}
@ -1285,6 +1321,7 @@ Network.prototype._checkShowPopup = function (pointer) {
var id ;
var id ;
var lastPopupNode = this . popupObj ;
var lastPopupNode = this . popupObj ;
var nodeUnderCursor = false ;
if ( this . popupObj == undefined ) {
if ( this . popupObj == undefined ) {
// search the nodes for overlap, select the top one in case of multiple nodes
// search the nodes for overlap, select the top one in case of multiple nodes
@ -1292,15 +1329,19 @@ Network.prototype._checkShowPopup = function (pointer) {
for ( id in nodes ) {
for ( id in nodes ) {
if ( nodes . hasOwnProperty ( id ) ) {
if ( nodes . hasOwnProperty ( id ) ) {
var node = nodes [ id ] ;
var node = nodes [ id ] ;
if ( node . getTitle ( ) !== undefined && node . isOverlappingWith ( obj ) ) {
this . popupObj = node ;
break ;
if ( node . isOverlappingWith ( obj ) ) {
if ( node . getTitle ( ) !== undefined ) {
this . popupObj = node ;
break ;
}
// if you hover over a node, the title of the edge is not supposed to be shown.
nodeUnderCursor = true ;
}
}
}
}
}
}
}
}
if ( this . popupObj === undefined ) {
if ( this . popupObj === undefined && nodeUnderCursor == false ) {
// search the edges for overlap
// search the edges for overlap
var edges = this . edges ;
var edges = this . edges ;
for ( id in edges ) {
for ( id in edges ) {
@ -1741,9 +1782,10 @@ Network.prototype.redraw = function() {
/ * *
/ * *
* Redraw the network with the current data
* Redraw the network with the current data
* @ param hidden | used to get the first estimate of the node sizes . only the nodes are drawn after which they are quickly drawn over .
* @ private
* @ private
* /
* /
Network . prototype . _redraw = function ( ) {
Network . prototype . _redraw = function ( hidden ) {
var ctx = this . frame . canvas . getContext ( '2d' ) ;
var ctx = this . frame . canvas . getContext ( '2d' ) ;
ctx . setTransform ( this . pixelRatio , 0 , 0 , this . pixelRatio , 0 , 0 ) ;
ctx . setTransform ( this . pixelRatio , 0 , 0 , this . pixelRatio , 0 , 0 ) ;
@ -1767,18 +1809,21 @@ Network.prototype._redraw = function() {
"y" : this . _YconvertDOMtoCanvas ( this . frame . canvas . clientHeight * this . pixelRatio )
"y" : this . _YconvertDOMtoCanvas ( this . frame . canvas . clientHeight * this . pixelRatio )
} ;
} ;
this . _doInAllSectors ( "_drawAllSectorNodes" , ctx ) ;
if ( this . drag . dragging == false || this . drag . dragging === undefined || this . constants . hideEdgesOnDrag == false ) {
this . _doInAllSectors ( "_drawEdges" , ctx ) ;
if ( ! ( hidden == true ) ) {
this . _doInAllSectors ( "_drawAllSectorNodes" , ctx ) ;
if ( this . drag . dragging == false || this . drag . dragging === undefined || this . constants . hideEdgesOnDrag == false ) {
this . _doInAllSectors ( "_drawEdges" , ctx ) ;
}
}
}
if ( this . drag . dragging == false || this . drag . dragging === undefined || this . constants . hideNodesOnDrag == false ) {
if ( this . drag . dragging == false || this . drag . dragging === undefined || this . constants . hideNodesOnDrag == false ) {
this . _doInAllSectors ( "_drawNodes" , ctx , false ) ;
this . _doInAllSectors ( "_drawNodes" , ctx , false ) ;
}
}
if ( this . controlNodesActive == true ) {
this . _doInAllSectors ( "_drawControlNodes" , ctx ) ;
if ( ! ( hidden == true ) ) {
if ( this . controlNodesActive == true ) {
this . _doInAllSectors ( "_drawControlNodes" , ctx ) ;
}
}
}
// this._doInSupportSector("_drawNodes",ctx,true);
// this._doInSupportSector("_drawNodes",ctx,true);
@ -1786,6 +1831,10 @@ Network.prototype._redraw = function() {
// restore original scaling and translation
// restore original scaling and translation
ctx . restore ( ) ;
ctx . restore ( ) ;
if ( hidden == true ) {
ctx . clearRect ( 0 , 0 , w , h ) ;
}
} ;
} ;
/ * *
/ * *
@ -2116,6 +2165,7 @@ Network.prototype._physicsTick = function() {
if ( this . constants . smoothCurves . enabled == true && this . constants . smoothCurves . dynamic == true ) {
if ( this . constants . smoothCurves . enabled == true && this . constants . smoothCurves . dynamic == true ) {
supportMovingStatus = this . _doInSupportSector ( "_discreteStepNodes" ) ;
supportMovingStatus = this . _doInSupportSector ( "_discreteStepNodes" ) ;
}
}
// gather movement data from all sectors, if one moves, we are NOT stabilzied
// gather movement data from all sectors, if one moves, we are NOT stabilzied
for ( var i = 0 ; i < mainMoving . length ; i ++ ) { mainMovingStatus = mainMoving [ 0 ] || mainMovingStatus ; }
for ( var i = 0 ; i < mainMoving . length ; i ++ ) { mainMovingStatus = mainMoving [ 0 ] || mainMovingStatus ; }
@ -2137,26 +2187,26 @@ Network.prototype._physicsTick = function() {
Network . prototype . _animationStep = function ( ) {
Network . prototype . _animationStep = function ( ) {
// reset the timer so a new scheduled animation step can be set
// reset the timer so a new scheduled animation step can be set
this . timer = undefined ;
this . timer = undefined ;
// handle the keyboad movement
// handle the keyboad movement
this . _handleNavigation ( ) ;
this . _handleNavigation ( ) ;
// this schedules a new animation step
this . start ( ) ;
// start the physics simulation
var calculationTime = Date . now ( ) ;
var maxSteps = 1 ;
var startTime = Date . now ( ) ;
this . _physicsTick ( ) ;
this . _physicsTick ( ) ;
var timeRequired = Date . now ( ) - calculationTime ;
while ( timeRequired < 0.9 * ( this . renderTimestep - this . renderTime ) && maxSteps < this . maxPhysicsTicksPerRender ) {
// run double speed if it is a little graph
if ( this . renderTimestep - this . renderTime > 2 * this . physicsTime ) {
this . _physicsTick ( ) ;
this . _physicsTick ( ) ;
timeRequired = Date . now ( ) - calculationTime ;
maxSteps ++ ;
}
}
// start the rendering process
var renderTime = Date . now ( ) ;
this . physicsTime = Date . now ( ) - startTime ;
var renderStartTime = Date . now ( ) ;
this . _redraw ( ) ;
this . _redraw ( ) ;
this . renderTime = Date . now ( ) - renderTime ;
this . renderTime = Date . now ( ) - renderStartTime ;
// this schedules a new animation step
this . start ( ) ;
} ;
} ;
if ( typeof window !== 'undefined' ) {
if ( typeof window !== 'undefined' ) {
@ -2175,23 +2225,11 @@ Network.prototype.start = function() {
}
}
if ( ! this . timer ) {
if ( ! this . timer ) {
var ua = navigator . userAgent . toLowerCase ( ) ;
var requiresTimeout = false ;
if ( ua . indexOf ( 'msie 9.0' ) != - 1 ) { // IE 9
requiresTimeout = true ;
}
else if ( ua . indexOf ( 'safari' ) != - 1 ) { // safari
if ( ua . indexOf ( 'chrome' ) <= - 1 ) {
requiresTimeout = true ;
}
}
if ( requiresTimeout == true ) {
if ( this . requiresTimeout == true ) {
this . timer = window . setTimeout ( this . _animationStep . bind ( this ) , this . renderTimestep ) ; // wait this.renderTimeStep milliseconds and perform the animation step function
this . timer = window . setTimeout ( this . _animationStep . bind ( this ) , this . renderTimestep ) ; // wait this.renderTimeStep milliseconds and perform the animation step function
}
}
else {
this . timer = window . requestAnimationFrame ( this . _animationStep . bind ( this ) , this . renderTimestep ) ; // wait this.renderTimeStep milliseconds and perform the animation step function
else {
this . timer = window . requestAnimationFrame ( this . _animationStep . bind ( this ) ) ; // wait this.renderTimeStep milliseconds and perform the animation step function
}
}
}
}
}
}
@ -2509,7 +2547,10 @@ Network.prototype.animateView = function (options) {
}
}
} ;
} ;
/ * *
* used to animate smoothly by hijacking the redraw function .
* @ private
* /
Network . prototype . _lockedRedraw = function ( ) {
Network . prototype . _lockedRedraw = function ( ) {
var nodePosition = { x : this . nodes [ this . lockedOnNodeId ] . x , y : this . nodes [ this . lockedOnNodeId ] . y } ;
var nodePosition = { x : this . nodes [ this . lockedOnNodeId ] . x , y : this . nodes [ this . lockedOnNodeId ] . y } ;
var viewCenter = this . DOMtoCanvas ( { x : 0.5 * this . frame . canvas . clientWidth , y : 0.5 * this . frame . canvas . clientHeight } ) ;
var viewCenter = this . DOMtoCanvas ( { x : 0.5 * this . frame . canvas . clientWidth , y : 0.5 * this . frame . canvas . clientHeight } ) ;
@ -2607,4 +2648,11 @@ Network.prototype.getCenterCoordinates = function () {
return this . DOMtoCanvas ( { x : 0.5 * this . frame . canvas . clientWidth , y : 0.5 * this . frame . canvas . clientHeight } ) ;
return this . DOMtoCanvas ( { x : 0.5 * this . frame . canvas . clientWidth , y : 0.5 * this . frame . canvas . clientHeight } ) ;
} ;
} ;
Network . prototype . getBoundingBox = function ( nodeId ) {
if ( this . nodes [ nodeId ] !== undefined ) {
return this . nodes [ nodeId ] . boundingBox ;
}
}
module . exports = Network ;
module . exports = Network ;