@ -4,8 +4,8 @@
*
*
* A dynamic , browser - based visualization library .
* A dynamic , browser - based visualization library .
*
*
* @ version 1.0 .1 - SNAPSHOT
* @ date 2014 - 05 - 02
* @ version @ @ version
* @ date @ @ date
*
*
* @ license
* @ license
* Copyright ( C ) 2011 - 2014 Almende B . V , http : //almende.com
* Copyright ( C ) 2011 - 2014 Almende B . V , http : //almende.com
@ -417,7 +417,7 @@ util.extend = function (a, b) {
util . equalArray = function ( a , b ) {
util . equalArray = function ( a , b ) {
if ( a . length != b . length ) return false ;
if ( a . length != b . length ) return false ;
for ( var i = 1 , len = a . length ; i < len ; i ++ ) {
for ( var i = 0 , len = a . length ; i < len ; i ++ ) {
if ( a [ i ] != b [ i ] ) return false ;
if ( a [ i ] != b [ i ] ) return false ;
}
}
@ -2595,7 +2595,7 @@ stack.collision = function collision (a, b, margin) {
* @ param { Date } [ end ] The end date
* @ param { Date } [ end ] The end date
* @ param { Number } [ minimumStep ] Optional . Minimum step size in milliseconds
* @ param { Number } [ minimumStep ] Optional . Minimum step size in milliseconds
* /
* /
TimeStep = function ( start , end , minimumStep ) {
function TimeStep ( start , end , minimumStep ) {
// variables
// variables
this . current = new Date ( ) ;
this . current = new Date ( ) ;
this . _start = new Date ( ) ;
this . _start = new Date ( ) ;
@ -2607,7 +2607,7 @@ TimeStep = function(start, end, minimumStep) {
// initialize the range
// initialize the range
this . setRange ( start , end , minimumStep ) ;
this . setRange ( start , end , minimumStep ) ;
} ;
}
/// enum scale
/// enum scale
TimeStep . SCALE = {
TimeStep . SCALE = {
@ -4669,6 +4669,301 @@ CustomTime.prototype._onDragEnd = function (event) {
event . preventDefault ( ) ;
event . preventDefault ( ) ;
} ;
} ;
/ * *
* Created by Alex on 5 / 6 / 14.
* /
var UNGROUPED = '__ungrouped__' ; // reserved group id for ungrouped items
/ * *
* An ItemSet holds a set of items and ranges which can be displayed in a
* range . The width is determined by the parent of the ItemSet , and the height
* is determined by the size of the items .
* @ param { Panel } backgroundPanel Panel which can be used to display the
* vertical lines of box items .
* @ param { Panel } axisPanel Panel on the axis where the dots of box - items
* can be displayed .
* @ param { Panel } sidePanel Left side panel holding labels
* @ param { Object } [ options ] See ItemSet . setOptions for the available options .
* @ constructor ItemSet
* @ extends Panel
* /
function Linegraph ( backgroundPanel , axisPanel , sidePanel , options ) {
this . id = util . randomUUID ( ) ;
// one options object is shared by this itemset and all its items
this . options = options || { } ;
this . backgroundPanel = backgroundPanel ;
this . axisPanel = axisPanel ;
this . sidePanel = sidePanel ;
this . itemOptions = Object . create ( this . options ) ;
this . dom = { } ;
this . hammer = null ;
var me = this ;
this . itemsData = null ; // DataSet
this . groupsData = null ; // DataSet
this . range = null ; // Range or Object {start: number, end: number}
// listeners for the DataSet of the items
// this.itemListeners = {
// 'add': function (event, params, senderId) {
// if (senderId != me.id) me._onAdd(params.items);
// },
// 'update': function (event, params, senderId) {
// if (senderId != me.id) me._onUpdate(params.items);
// },
// 'remove': function (event, params, senderId) {
// if (senderId != me.id) me._onRemove(params.items);
// }
// };
//
// // listeners for the DataSet of the groups
// this.groupListeners = {
// 'add': function (event, params, senderId) {
// if (senderId != me.id) me._onAddGroups(params.items);
// },
// 'update': function (event, params, senderId) {
// if (senderId != me.id) me._onUpdateGroups(params.items);
// },
// 'remove': function (event, params, senderId) {
// if (senderId != me.id) me._onRemoveGroups(params.items);
// }
// };
this . items = { } ; // object with an Item for every data item
this . groups = { } ; // Group object for every group
this . groupIds = [ ] ;
this . selection = [ ] ; // list with the ids of all selected nodes
this . stackDirty = true ; // if true, all items will be restacked on next repaint
this . touchParams = { } ; // stores properties while dragging
// create the HTML DOM
this . _create ( ) ;
}
Linegraph . prototype = new Panel ( ) ;
/ * *
* Create the HTML DOM for the ItemSet
* /
Linegraph . prototype . _create = function _create ( ) {
var frame = document . createElement ( 'div' ) ;
frame [ 'timeline-linegraph' ] = this ;
this . frame = frame ;
this . frame . className = 'itemset' ;
// create background panel
var background = document . createElement ( 'div' ) ;
background . className = 'background' ;
this . backgroundPanel . frame . appendChild ( background ) ;
this . dom . background = background ;
// create foreground panel
var foreground = document . createElement ( 'div' ) ;
foreground . className = 'foreground' ;
frame . appendChild ( foreground ) ;
this . dom . foreground = foreground ;
// // create axis panel
// var axis = document.createElement('div');
// axis.className = 'axis';
// this.dom.axis = axis;
// this.axisPanel.frame.appendChild(axis);
//
// // create labelset
// var labelSet = document.createElement('div');
// labelSet.className = 'labelset';
// this.dom.labelSet = labelSet;
// this.sidePanel.frame.appendChild(labelSet);
console . log ( this . frame , this . frame . offsetWidth ) ;
this . svg = document . createElementNS ( 'http://www.w3.org/2000/svg' , "svg" ) ;
this . svg . style . position = "relative"
this . svg . style . height = "300px" ;
this . path = document . createElementNS ( 'http://www.w3.org/2000/svg' , "path" ) ;
this . path . setAttributeNS ( null , "fill" , "none" ) ;
this . path . setAttributeNS ( null , "stroke" , "blue" ) ;
this . path . setAttributeNS ( null , "stroke-width" , "1" ) ;
this . path2 = document . createElementNS ( 'http://www.w3.org/2000/svg' , "path" ) ;
this . path2 . setAttributeNS ( null , "fill" , "none" ) ;
this . path2 . setAttributeNS ( null , "stroke" , "red" ) ;
this . path2 . setAttributeNS ( null , "stroke-width" , "2" ) ;
this . dom . foreground . appendChild ( this . svg ) ;
this . svg . appendChild ( this . path2 ) ;
this . svg . appendChild ( this . path ) ;
} ;
Linegraph . prototype . setData = function setData ( ) {
console . log ( this . frame , this . frame . offsetWidth ) ;
var data = [ ] ;
this . startTime = this . range . start ;
var min = 0 ;
var max = 1920 ;
var count = 10 ;
var scale = 30 ;
var offset = 0 ;
var step = ( max - min ) / count ;
for ( var i = 0 ; i < count ; i ++ ) {
data . push ( { x : Date . now ( ) + i * step , y : 300 * ( i % 2 ) } )
}
console . log ( data , this . width , this . _previousWidth ) ;
// catmull rom
var p0 , p1 , p2 , p3 , bp1 , bp2 , bp3 ;
var d2 = "M" + data [ 0 ] . x + "," + data [ 0 ] . y + " " ;
for ( var i = 0 ; i < data . length - 2 ; i ++ ) {
if ( i == 0 ) {
p0 = data [ 0 ]
}
else {
p0 = data [ i - 1 ] ;
}
p1 = data [ i ] ;
p2 = data [ i + 1 ] ;
p3 = data [ i + 2 ] ;
// Catmull-Rom to Cubic Bezier conversion matrix
// 0 1 0 0
// -1/6 1 1/6 0
// 0 1/6 1 -1/6
// 0 0 1 0
// bp0 = { x: p1.x, y: p1.y };
bp1 = { x : ( ( - p0 . x + 6 * p1 . x + p2 . x ) / 6 ) , y : ( ( - p0 . y + 6 * p1 . y + p2 . y ) / 6 ) } ;
bp2 = { x : ( ( p1 . x + 6 * p2 . x - p3 . x ) / 6 ) , y : ( ( p1 . y + 6 * p2 . y - p3 . y ) / 6 ) } ;
bp3 = { x : p2 . x , y : p2 . y } ;
d2 += "C" + bp1 . x + "," + bp1 . y + " " + bp2 . x + "," + bp2 . y + " " + bp3 . x + "," + bp3 . y + " " ;
}
// linear
var d = "" ;
for ( var i = 0 ; i < data . length - 1 ; i ++ ) {
if ( i == 0 ) {
d += "M" + data [ i ] . x + "," + data [ i ] . y ;
}
else {
d += " " + data [ i ] . x + "," + data [ i ] . y ;
}
}
this . path . setAttributeNS ( null , "d" , d ) ;
this . path2 . setAttributeNS ( null , "d" , d2 ) ;
//
}
/ * *
* Set options for the Linegraph . Existing options will be extended / overwritten .
* @ param { Object } [ options ] The following options are available :
* { String | function } [ className ]
* class name for the itemset
* { String } [ type ]
* Default type for the items . Choose from 'box'
* ( default ) , 'point' , or 'range' . The default
* Style can be overwritten by individual items .
* { String } align
* Alignment for the items , only applicable for
* ItemBox . Choose 'center' ( default ) , 'left' , or
* 'right' .
* { String } orientation
* Orientation of the item set . Choose 'top' or
* 'bottom' ( default ) .
* { Number } margin . axis
* Margin between the axis and the items in pixels .
* Default is 20.
* { Number } margin . item
* Margin between items in pixels . Default is 10.
* { Number } padding
* Padding of the contents of an item in pixels .
* Must correspond with the items css . Default is 5.
* { Function } snap
* Function to let items snap to nice dates when
* dragging items .
* /
Linegraph . prototype . setOptions = function setOptions ( options ) {
Component . prototype . setOptions . call ( this , options ) ;
} ;
/ * *
* Set range ( start and end ) .
* @ param { Range | Object } range A Range or an object containing start and end .
* /
Linegraph . prototype . setRange = function setRange ( range ) {
if ( ! ( range instanceof Range ) && ( ! range || ! range . start || ! range . end ) ) {
throw new TypeError ( 'Range must be an instance of Range, ' +
'or an object containing start and end.' ) ;
}
this . range = range ;
} ;
Linegraph . prototype . repaint = function repaint ( ) {
var margin = this . options . margin ,
range = this . range ,
asSize = util . option . asSize ,
asString = util . option . asString ,
options = this . options ,
orientation = this . getOption ( 'orientation' ) ,
resized = false ,
frame = this . frame ;
// TODO: document this feature to specify one margin for both item and axis distance
if ( typeof margin === 'number' ) {
margin = {
item : margin ,
axis : margin
} ;
}
// update className
this . frame . className = 'itemset' + ( options . className ? ( ' ' + asString ( options . className ) ) : '' ) ;
// check whether zoomed (in that case we need to re-stack everything)
// TODO: would be nicer to get this as a trigger from Range
var visibleInterval = this . range . end - this . range . start ;
var zoomed = ( visibleInterval != this . lastVisibleInterval ) || ( this . width != this . lastWidth ) ;
if ( zoomed ) this . stackDirty = true ;
this . lastVisibleInterval = visibleInterval ;
this . lastWidth = this . width ;
// reposition frame
this . frame . style . left = asSize ( options . left , '' ) ;
this . frame . style . right = asSize ( options . right , '' ) ;
this . frame . style . top = asSize ( ( orientation == 'top' ) ? '0' : '' ) ;
this . frame . style . bottom = asSize ( ( orientation == 'top' ) ? '' : '0' ) ;
this . frame . style . width = asSize ( options . width , '100%' ) ;
// frame.style.height = asSize(height);
//frame.style.height = asSize('height' in options ? options.height : height); // TODO: reckon with height
// calculate actual size and position
this . top = this . frame . offsetTop ;
this . left = this . frame . offsetLeft ;
this . width = this . frame . offsetWidth ;
// this.height = height;
// check if this component is resized
resized = this . _isResized ( ) || resized ;
if ( resized ) {
this . svg . style . width = asSize ( 3 * this . width )
}
this . setData ( ) ;
}
var UNGROUPED = '__ungrouped__' ; // reserved group id for ungrouped items
var UNGROUPED = '__ungrouped__' ; // reserved group id for ungrouped items
/ * *
/ * *
@ -4976,6 +5271,7 @@ ItemSet.prototype.repaint = function repaint() {
} ;
} ;
}
}
// update className
// update className
frame . className = 'itemset' + ( options . className ? ( ' ' + asString ( options . className ) ) : '' ) ;
frame . className = 'itemset' + ( options . className ? ( ' ' + asString ( options . className ) ) : '' ) ;
@ -7399,6 +7695,11 @@ function Timeline (container, items, options) {
this . itemSet . on ( 'change' , me . rootPanel . repaint . bind ( me . rootPanel ) ) ;
this . itemSet . on ( 'change' , me . rootPanel . repaint . bind ( me . rootPanel ) ) ;
this . contentPanel . appendChild ( this . itemSet ) ;
this . contentPanel . appendChild ( this . itemSet ) ;
this . linegraph = new Linegraph ( this . backgroundPanel , this . axisPanel , this . sideContentPanel , itemOptions ) ;
this . linegraph . setRange ( this . range ) ;
this . linegraph . on ( 'change' , me . rootPanel . repaint . bind ( me . rootPanel ) ) ;
this . contentPanel . appendChild ( this . linegraph ) ;
this . itemsData = null ; // DataSet
this . itemsData = null ; // DataSet
this . groupsData = null ; // DataSet
this . groupsData = null ; // DataSet
@ -10779,10 +11080,10 @@ Popup.prototype.hide = function () {
* @ class Groups
* @ class Groups
* This class can store groups and properties specific for groups .
* This class can store groups and properties specific for groups .
* /
* /
Groups = function ( ) {
function Groups ( ) {
this . clear ( ) ;
this . clear ( ) ;
this . defaultIndex = 0 ;
this . defaultIndex = 0 ;
} ;
}
/ * *
/ * *
@ -10860,11 +11161,11 @@ Groups.prototype.add = function (groupname, style) {
* @ class Images
* @ class Images
* This class loads images and keeps them stored .
* This class loads images and keeps them stored .
* /
* /
Images = function ( ) {
function Images ( ) {
this . images = { } ;
this . images = { } ;
this . callback = undefined ;
this . callback = undefined ;
} ;
}
/ * *
/ * *
* Set an onload callback function . This will be called each time an image
* Set an onload callback function . This will be called each time an image
@ -11541,10 +11842,12 @@ function switchConfigurations () {
this . constants . physics . barnesHut . enabled = false ;
this . constants . physics . barnesHut . enabled = false ;
}
}
else if ( radioButton == "H" ) {
else if ( radioButton == "H" ) {
this . constants . hierarchicalLayout . enabled = true ;
this . constants . physics . hierarchicalRepulsion . enabled = true ;
this . constants . physics . barnesHut . enabled = false ;
this . _setupHierarchicalLayout ( ) ;
if ( this . constants . hierarchicalLayout . enabled == false ) {
this . constants . hierarchicalLayout . enabled = true ;
this . constants . physics . hierarchicalRepulsion . enabled = true ;
this . constants . physics . barnesHut . enabled = false ;
this . _setupHierarchicalLayout ( ) ;
}
}
}
else {
else {
this . constants . hierarchicalLayout . enabled = false ;
this . constants . hierarchicalLayout . enabled = false ;
@ -11628,7 +11931,6 @@ var hierarchalRepulsionMixin = {
node1 = nodes [ nodeIndices [ i ] ] ;
node1 = nodes [ nodeIndices [ i ] ] ;
for ( j = i + 1 ; j < nodeIndices . length ; j ++ ) {
for ( j = i + 1 ; j < nodeIndices . length ; j ++ ) {
node2 = nodes [ nodeIndices [ j ] ] ;
node2 = nodes [ nodeIndices [ j ] ] ;
dx = node2 . x - node1 . x ;
dx = node2 . x - node1 . x ;
dy = node2 . y - node1 . y ;
dy = node2 . y - node1 . y ;
distance = Math . sqrt ( dx * dx + dy * dy ) ;
distance = Math . sqrt ( dx * dx + dy * dy ) ;
@ -12206,6 +12508,7 @@ var HierarchicalLayoutMixin = {
* @ private
* @ private
* /
* /
_placeNodesByHierarchy : function ( distribution ) {
_placeNodesByHierarchy : function ( distribution ) {
var nodeId , node ;
var nodeId , node ;
// start placing all the level 0 nodes first. Then recursively position their branches.
// start placing all the level 0 nodes first. Then recursively position their branches.
@ -16096,6 +16399,17 @@ Graph.prototype.setOptions = function (options) {
}
}
}
}
}
}
if ( options . physics . hierarchicalRepulsion ) {
this . constants . hierarchicalLayout . enabled = true ;
this . constants . physics . hierarchicalRepulsion . enabled = true ;
this . constants . physics . barnesHut . enabled = false ;
for ( prop in options . physics . hierarchicalRepulsion ) {
if ( options . physics . hierarchicalRepulsion . hasOwnProperty ( prop ) ) {
this . constants . physics . hierarchicalRepulsion [ prop ] = options . physics . hierarchicalRepulsion [ prop ] ;
}
}
}
}
}
if ( options . hierarchicalLayout ) {
if ( options . hierarchicalLayout ) {
@ -16499,6 +16813,7 @@ Graph.prototype._handleOnDrag = function(event) {
this . drag . translation . y + diffY ) ;
this . drag . translation . y + diffY ) ;
this . _redraw ( ) ;
this . _redraw ( ) ;
this . moving = true ;
this . moving = true ;
this . start ( ) ;
}
}
} ;
} ;
@ -17273,6 +17588,27 @@ Graph.prototype._yToCanvas = function(y) {
return y * this . scale + this . translation . y ;
return y * this . scale + this . translation . y ;
} ;
} ;
/ * *
*
* @ param { object } pos = { x : number , y : number }
* @ returns { { x : number , y : number } }
* @ constructor
* /
Graph . prototype . DOMtoCanvas = function ( pos ) {
return { x : this . _xToCanvas ( pos . x ) , y : this . _yToCanvas ( pos . y ) } ;
}
/ * *
*
* @ param { object } pos = { x : number , y : number }
* @ returns { { x : number , y : number } }
* @ constructor
* /
Graph . prototype . canvasToDOM = function ( pos ) {
return { x : this . _canvasToX ( pos . x ) , y : this . _canvasToY ( pos . y ) } ;
}
/ * *
/ * *
* Redraw all nodes
* Redraw all nodes
* The 2 d context of a HTML canvas can be retrieved by canvas . getContext ( '2d' ) ;
* The 2 d context of a HTML canvas can be retrieved by canvas . getContext ( '2d' ) ;