diff --git a/dist/vis.js b/dist/vis.js index afc9d0af..ce3074a0 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -5,7 +5,7 @@ * A dynamic, browser-based visualization library. * * @version 0.6.0-SNAPSHOT - * @date 2014-02-27 + * @date 2014-03-03 * * @license * Copyright (C) 2011-2014 Almende B.V, http://almende.com @@ -2429,7 +2429,7 @@ SvgAxis.prototype._update = function() { var yearScale = [1,2,3,4,5,6,7,8,9,10,15,20,25,50,75,100,150,250,500,1000]; var multipliers = [1,1000,60000,3600000,24*3600000,30*24*3600000,365*24*3600000]; var scales = [milliSecondScale,secondScale,minuteScale,hourScale,dayScale,monthScale,yearScale] - var formats = ["SSS","mm:ss","hh:mm:ss","DD HH:mm","MM-DD","MM","YYYY"] + var formats = ["SSS","mm:ss","hh:mm:ss","DD HH:mm","DD-MM","MM-YYYY","YYYY"] var indices = this._getAppropriateScale(scales,multipliers); var scale = scales[indices[0]][indices[1]] * multipliers[indices[0]]; @@ -2487,16 +2487,25 @@ SvgAxis.prototype._getAppropriateScale = function(scales,multipliers) { * @param {Object} options Options */ function SvgTimeline (container, items, options) { - var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); - - this.range = {start:now.clone().add('days', -3).valueOf(), - end: now.clone().add('days', 4).valueOf()} this.constants = { width:1400, height:400, barHeight: 60 } + var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); + this.range = { + start:now.clone().add('days', -3).valueOf(), + end: now.clone().add('days', 4).valueOf() + } + + this.items = {}; + this.sortedItems = []; + this.activeItems = {}; + this.sortedActiveItems = []; + + this._createItems(items); + this.container = container; this._createSVG(); @@ -2521,6 +2530,8 @@ function SvgTimeline (container, items, options) { this.hammer.on('mousemove', me._onMouseMoveTitle.bind(me) ); //this._drawLines(); + this._update(); + } SvgTimeline.prototype._createSVG = function() { @@ -2531,14 +2542,18 @@ SvgTimeline.prototype._createSVG = function() { .attr("style","border:1px solid black") }; +SvgTimeline.prototype._createItems = function (items) { + for (var i = 0; i < items.length; i++) { + this.items[items[i].id] = new Item(items[i], this.constants); + this.sortedItems.push(this.items[items[i].id]); + } + this._sortItems(this.sortedItems); +} +SvgTimeline.prototype._sortItems = function (items) { + items.sort(function(a,b) {return a.start - b.start}); +} -/** - * Get the pointer location from a touch location - * @param {{pageX: Number, pageY: Number}} touch - * @return {{x: Number, y: Number}} pointer - * @private - */ SvgTimeline.prototype._getPointer = function (touch) { return { x: touch.pageX, @@ -2599,9 +2614,115 @@ SvgTimeline.prototype._onMouseWheel = function(event) { }; SvgTimeline.prototype._onMouseMoveTitle = function() {}; - SvgTimeline.prototype._update = function() { this.axis._update(); + this._getActiveItems(); + this._updateItems(); +}; + +SvgTimeline.prototype._getActiveItems = function() { + // reset all currently active items to inactive + for (var itemId in this.activeItems) { + if (this.activeItems.hasOwnProperty(itemId)) { + this.activeItems[itemId].active = false; + } + } + + this.sortedActiveItems = [] + var rangeStart = this.range.start-200*this.axis.msPerPixel + var rangeEnd = (this.range.end+200*this.axis.msPerPixel) + for (var itemId in this.items) { + if (this.items.hasOwnProperty(itemId)) { + if (this.items[itemId].start >= rangeStart && this.items[itemId].start < rangeEnd || + this.items[itemId].end >= rangeStart && this.items[itemId].end < rangeEnd) { + if (this.items[itemId].active == false) { + this.activeItems[itemId] = this.items[itemId]; + } + this.activeItems[itemId].active = true; + this.sortedActiveItems.push(this.activeItems[itemId]); + } + } + } + this._sortItems(this.sortedActiveItems); + + // cleanup + for (var itemId in this.activeItems) { + if (this.activeItems.hasOwnProperty(itemId)) { + if (this.activeItems[itemId].active == false) { + this.activeItems[itemId].svg.remove(); + this.activeItems[itemId].svg = null; + this.activeItems[itemId].svgLine.remove(); + this.activeItems[itemId].svgLine = null; + delete this.activeItems[itemId]; + } + } + } +}; + + +SvgTimeline.prototype._updateItems = function() { + for (var i = 0; i < this.sortedActiveItems.length; i++) { + var item = this.sortedActiveItems[i]; + if (item.svg == null) { +// item.svg = d3.select("svg#main") +// .append("rect") +// .attr("class","item") +// .style("stroke", "rgb(6,120,155)") +// .style("fill", "rgb(6,120,155)"); + item.svg = d3.select("svg#main") + .append("foreignObject") + item.svgContent = item.svg.append("xhtml:body") + .style("font", "14px 'Helvetica Neue'") + .style("background-color", "#ff00ff") + .html("
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu enim quam. Quisque nisi risus, sagittis quis tempor nec, aliquam eget neque. Nulla bibendum semper lorem non ullamcorper. Nulla non ligula lorem. Praesent porttitor, tellus nec suscipit aliquam, enim elit posuere lorem, at laoreet enim ligula sed tortor. Ut sodales, urna a aliquam semper, nibh diam gravida sapien, sit amet fermentum purus lacus eget massa. Donec ac arcu vel magna consequat pretium et vel ligula. Donec sit amet erat elit. Vivamus eu metus eget est hendrerit rutrum. Curabitur vitae orci et leo interdum egestas ut sit amet dui. In varius enim ut sem posuere in tristique metus ultrices.
Integer mollis massa at orci porta vestibulum. Pellentesque dignissim turpis ut tortor ultricies condimentum et quis nibh. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer euismod lorem vulputate dui pharetra luctus. Sed vulputate, nunc quis porttitor scelerisque, dui est varius ipsum, eu blandit mauris nibh pellentesque tortor. Vivamus ultricies ante eget ipsum pulvinar ac tempor turpis mollis. Morbi tortor orci, euismod vel sagittis ac, lobortis nec est. Quisque euismod venenatis felis at dapibus. Vestibulum dignissim nulla ut nisi tristique porttitor. Proin et nunc id arcu cursus dapibus non quis libero. Nunc ligula mi, bibendum non mattis nec, luctus id neque. Suspendisse ut eros lacus. Praesent eget lacus eget risus congue vestibulum. Morbi tincidunt pulvinar lacus sed faucibus. Phasellus sed vestibulum sapien."); + + + + if (item.end == 0) { + item.svgLine = d3.select("svg#main") + .append("line") + .attr("y1",this.constants.barHeight) + .style("stroke", "rgb(200,200,255)") + .style("stroke-width", 3) + } + } + item.svg.attr('width',item.getLength(this.axis.msPerPixel)) + .attr("x",this._getXforItem(item)) + .attr("y",this._getYforItem(item, i)) + + + .attr('height',25) + if (item.end == 0) { + item.svgLine.attr('y2',item.y) + .attr('x1',item.timeX) + .attr('x2',item.timeX) + } + } +}; + +SvgTimeline.prototype._getXforItem = function(item) { + item.timeX = (item.start - this.range.start)/this.axis.msPerPixel; + if (item.end == 0) { + item.drawX = item.timeX - item.width * 0.5; + } + else { + item.drawX = item.timeX; + } + return item.drawX; +} + +SvgTimeline.prototype._getYforItem = function(item, index) { + var bounds = 10; + var startIndex = Math.max(0,index-bounds); + item.level = 0; + for (var i = startIndex; i < index; i++) { + var item2 = this.sortedActiveItems[i]; + if (item.drawX <= (item2.drawX + item2.width + 5) && item2.level == item.level) { + item.level += 1; + } + } + item.y = 100 + 50*item.level; + return item.y; } /** * @constructor TimeStep @@ -10698,6 +10819,7 @@ function Edge (properties, graph, constants) { this.width = constants.edges.width; this.value = undefined; this.length = constants.physics.springLength; + this.customLength = false; this.selected = false; this.smooth = constants.smoothCurves; @@ -10753,7 +10875,8 @@ Edge.prototype.setProperties = function(properties, constants) { if (properties.title !== undefined) {this.title = properties.title;} if (properties.width !== undefined) {this.width = properties.width;} if (properties.value !== undefined) {this.value = properties.value;} - if (properties.length !== undefined) {this.length = properties.length;} + if (properties.length !== undefined) {this.length = properties.length; + this.customLength = true;} // Added to support dashed lines // David Jordan @@ -11641,6 +11764,50 @@ var physicsMixin = { }, + + /** + * This loads the node force solver based on the barnes hut or repulsion algorithm + * + * @private + */ + _loadSelectedForceSolver : function() { + // this overloads the this._calculateNodeForces + if (this.constants.physics.barnesHut.enabled == true) { + this._clearMixin(repulsionMixin); + this._clearMixin(hierarchalRepulsionMixin); + + this.constants.physics.centralGravity = this.constants.physics.barnesHut.centralGravity; + this.constants.physics.springLength = this.constants.physics.barnesHut.springLength; + this.constants.physics.springConstant = this.constants.physics.barnesHut.springConstant; + this.constants.physics.damping = this.constants.physics.barnesHut.damping; + + this._loadMixin(barnesHutMixin); + } + else if (this.constants.physics.hierarchicalRepulsion.enabled == true) { + this._clearMixin(barnesHutMixin); + this._clearMixin(repulsionMixin); + + this.constants.physics.centralGravity = this.constants.physics.hierarchicalRepulsion.centralGravity; + this.constants.physics.springLength = this.constants.physics.hierarchicalRepulsion.springLength; + this.constants.physics.springConstant = this.constants.physics.hierarchicalRepulsion.springConstant; + this.constants.physics.damping = this.constants.physics.hierarchicalRepulsion.damping; + + this._loadMixin(hierarchalRepulsionMixin); + } + else { + this._clearMixin(barnesHutMixin); + this._clearMixin(hierarchalRepulsionMixin); + this.barnesHutTree = undefined; + + this.constants.physics.centralGravity = this.constants.physics.repulsion.centralGravity; + this.constants.physics.springLength = this.constants.physics.repulsion.springLength; + this.constants.physics.springConstant = this.constants.physics.repulsion.springConstant; + this.constants.physics.damping = this.constants.physics.repulsion.damping; + + this._loadMixin(repulsionMixin); + } + }, + /** * Before calculating the forces, we check if we need to cluster to keep up performance and we check * if there is more than one node. If it is just one node, we dont calculate anything. @@ -11779,7 +11946,7 @@ var physicsMixin = { if (edge.connected) { // only calculate forces if nodes are in the same sector if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - edgeLength = edge.length; + edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; // this implies that the edges between big clusters are longer edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; @@ -11828,7 +11995,7 @@ var physicsMixin = { var node2 = edge.via; var node3 = edge.from; - edgeLength = edge.length; + edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; @@ -11861,6 +12028,10 @@ var physicsMixin = { springForce = this.constants.physics.springConstant * (edgeLength - length) / length; + if (length == 0) { + length = 0.01; + } + fx = dx * springForce; fy = dy * springForce; @@ -11868,8 +12039,242 @@ var physicsMixin = { node1.fy += fy; node2.fx -= fx; node2.fy -= fy; + }, + + + /** + * Load the HTML for the physics config and bind it + * @private + */ + _loadPhysicsConfiguration : function() { + if (this.physicsConfiguration === undefined) { + this.physicsConfiguration = document.createElement('div'); + this.physicsConfiguration.className = "PhysicsConfiguration"; + this.physicsConfiguration.innerHTML = '' + + '
Simulation Mode: | ||
Barnes Hut | ' + + 'Repulsion | '+ + 'Hierarchical | ' + + '
// These variables must be defined in an options object named physics. @@ -1708,7 +1717,8 @@ var options: {nodeSpacing Number 100 -This defines the space between nodes in the same level (in the X-direction, considering UP-DOWN direction). +This defines the space between nodes in the same level (in the X-direction, considering UP-DOWN direction). + This is only relevant during the initial placing of nodes.
Barnes Hut | -||||
gravitationalConstant | -500 | -20000 | - | |
centralGravity | 0.0 | 3 | - | |
springLength | 0 | 500 | - | |
springConstant | 0 | 0.5 | - | |
damping | 0 | 0.3 | - |
Repulsion | -||||
nodeDistance | 0 | 300 | - | |
centralGravity | 0.0 | 3 | - | |
springLength | 0 | 500 | - | |
springConstant | 0 | 0.5 | - | |
damping | 0 | 0.3 | - |
Hierarchical Repulsion | -||||
nodeDistance | 0 | 300 | - | |
centralGravity | 0.0 | 3 | - | |
springLength | 0 | 500 | - | |
springConstant | 0 | 0.5 | - | |
damping | 0 | 0.3 | - |
- - - -
- - - - - \ No newline at end of file diff --git a/src/graph/Edge.js b/src/graph/Edge.js index 221c2778..50a51196 100644 --- a/src/graph/Edge.js +++ b/src/graph/Edge.js @@ -32,6 +32,7 @@ function Edge (properties, graph, constants) { this.width = constants.edges.width; this.value = undefined; this.length = constants.physics.springLength; + this.customLength = false; this.selected = false; this.smooth = constants.smoothCurves; @@ -87,7 +88,8 @@ Edge.prototype.setProperties = function(properties, constants) { if (properties.title !== undefined) {this.title = properties.title;} if (properties.width !== undefined) {this.width = properties.width;} if (properties.value !== undefined) {this.value = properties.value;} - if (properties.length !== undefined) {this.length = properties.length;} + if (properties.length !== undefined) {this.length = properties.length; + this.customLength = true;} // Added to support dashed lines // David Jordan diff --git a/src/graph/Graph.js b/src/graph/Graph.js index 8e13055c..e2020cc9 100644 --- a/src/graph/Graph.js +++ b/src/graph/Graph.js @@ -73,6 +73,7 @@ function Graph (container, data, options) { altLength: undefined } }, + configurePhysics:false, physics: { barnesHut: { enabled: true, @@ -471,6 +472,7 @@ Graph.prototype.setOptions = function (options) { if (options.stabilize !== undefined) {this.stabilize = options.stabilize;} if (options.selectable !== undefined) {this.selectable = options.selectable;} if (options.smoothCurves !== undefined) {this.constants.smoothCurves = options.smoothCurves;} + if (options.configurePhysics !== undefined){this.constants.configurePhysics = options.configurePhysics;} if (options.onAdd) { this.triggerFunctions.add = options.onAdd; diff --git a/src/graph/graphMixins/HierarchicalLayoutMixin.js b/src/graph/graphMixins/HierarchicalLayoutMixin.js index 63ebf556..9879daa3 100644 --- a/src/graph/graphMixins/HierarchicalLayoutMixin.js +++ b/src/graph/graphMixins/HierarchicalLayoutMixin.js @@ -275,5 +275,22 @@ var HierarchicalLayoutMixin = { } } } + }, + + + /** + * Unfix nodes + * + * @private + */ + _restoreNodes : function() { + for (nodeId in this.nodes) { + if (this.nodes.hasOwnProperty(nodeId)) { + this.nodes[nodeId].xFixed = false; + this.nodes[nodeId].yFixed = false; + } + } } + + }; \ No newline at end of file diff --git a/src/graph/graphMixins/MixinLoader.js b/src/graph/graphMixins/MixinLoader.js index 6ea1c553..6cf4f2dd 100644 --- a/src/graph/graphMixins/MixinLoader.js +++ b/src/graph/graphMixins/MixinLoader.js @@ -43,6 +43,9 @@ var graphMixinLoaders = { _loadPhysicsSystem : function() { this._loadMixin(physicsMixin); this._loadSelectedForceSolver(); + if (this.constants.configurePhysics == true) { + this._loadPhysicsConfiguration(); + } }, diff --git a/src/graph/graphMixins/physics/HierarchialRepulsion.js b/src/graph/graphMixins/physics/HierarchialRepulsion.js index bc6b55c8..37037fcf 100644 --- a/src/graph/graphMixins/physics/HierarchialRepulsion.js +++ b/src/graph/graphMixins/physics/HierarchialRepulsion.js @@ -24,7 +24,7 @@ var hierarchalRepulsionMixin = { // repulsing forces between nodes - var nodeDistance = this.constants.physics.repulsion.nodeDistance; + var nodeDistance = this.constants.physics.hierarchicalRepulsion.nodeDistance; var minimumDistance = nodeDistance; // we loop from i over all but the last entree in the array diff --git a/src/graph/graphMixins/physics/PhysicsMixin.js b/src/graph/graphMixins/physics/PhysicsMixin.js index f492d500..80499489 100644 --- a/src/graph/graphMixins/physics/PhysicsMixin.js +++ b/src/graph/graphMixins/physics/PhysicsMixin.js @@ -200,7 +200,7 @@ var physicsMixin = { if (edge.connected) { // only calculate forces if nodes are in the same sector if (this.nodes.hasOwnProperty(edge.toId) && this.nodes.hasOwnProperty(edge.fromId)) { - edgeLength = edge.length; + edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; // this implies that the edges between big clusters are longer edgeLength += (edge.to.clusterSize + edge.from.clusterSize - 2) * this.constants.clustering.edgeGrowth; @@ -249,7 +249,7 @@ var physicsMixin = { var node2 = edge.via; var node3 = edge.from; - edgeLength = edge.length; + edgeLength = edge.customLength ? edge.length : this.constants.physics.springLength; combinedClusterSize = node1.clusterSize + node3.clusterSize - 2; @@ -282,6 +282,10 @@ var physicsMixin = { springForce = this.constants.physics.springConstant * (edgeLength - length) / length; + if (length == 0) { + length = 0.01; + } + fx = dx * springForce; fy = dy * springForce; @@ -289,5 +293,238 @@ var physicsMixin = { node1.fy += fy; node2.fx -= fx; node2.fy -= fy; + }, + + + /** + * Load the HTML for the physics config and bind it + * @private + */ + _loadPhysicsConfiguration : function() { + if (this.physicsConfiguration === undefined) { + this.physicsConfiguration = document.createElement('div'); + this.physicsConfiguration.className = "PhysicsConfiguration"; + this.physicsConfiguration.innerHTML = '' + + 'Simulation Mode: | ||
Barnes Hut | ' + + 'Repulsion | '+ + 'Hierarchical | ' + + '
=o.length)return n;var r=[],u=a[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,i={},o=[],a=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(Xo.map,e,0),0)},i.key=function(n){return o.push(n),i},i.sortKeys=function(n){return a[o.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},Xo.set=function(n){var t=new l;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},r(l,{has:i,add:function(n){return this[aa+n]=!0,n},remove:function(n){return n=aa+n,n in this&&delete this[n]},values:a,size:c,empty:s,forEach:function(n){for(var t in this)t.charCodeAt(0)===ca&&n.call(this,t.substring(1))}}),Xo.behavior={},Xo.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r=0&&(r=n.substring(e+1),n=n.substring(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},Xo.event=null,Xo.requote=function(n){return n.replace(la,"\\$&")};var la=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,fa={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},ha=function(n,t){return t.querySelector(n)},ga=function(n,t){return t.querySelectorAll(n)},pa=Jo[h(Jo,"matchesSelector")],va=function(n,t){return pa.call(n,t)};"function"==typeof Sizzle&&(ha=function(n,t){return Sizzle(n,t)[0]||null},ga=function(n,t){return Sizzle.uniqueSort(Sizzle(n,t))},va=Sizzle.matchesSelector),Xo.selection=function(){return xa};var da=Xo.selection.prototype=[];da.select=function(n){var t,e,r,u,i=[];n=M(n);for(var o=-1,a=this.length;++o=0&&(e=n.substring(0,t),n=n.substring(t+1)),ma.hasOwnProperty(e)?{space:ma[e],local:n}:n}},da.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=Xo.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(b(t,n[t]));return this}return this.each(b(n,t))},da.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=k(n)).length,u=-1;if(t=e.classList){for(;++u =0?n.substring(0,t):n,r=t>=0?n.substring(t+1):"in";return e=ts.get(e)||ns,r=es.get(r)||bt,gu(r(e.apply(null,$o.call(arguments,1))))},Xo.interpolateHcl=Eu,Xo.interpolateHsl=Au,Xo.interpolateLab=Cu,Xo.interpolateRound=Nu,Xo.transform=function(n){var t=Wo.createElementNS(Xo.ns.prefix.svg,"g");return(Xo.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Lu(e?e.matrix:rs)})(n)},Lu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var rs={a:1,b:0,c:0,d:1,e:0,f:0};Xo.interpolateTransform=Ru,Xo.layout={},Xo.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eu enim quam. Quisque nisi risus, sagittis quis tempor nec, aliquam eget neque. Nulla bibendum semper lorem non ullamcorper. Nulla non ligula lorem. Praesent porttitor, tellus nec suscipit aliquam, enim elit posuere lorem, at laoreet enim ligula sed tortor. Ut sodales, urna a aliquam semper, nibh diam gravida sapien, sit amet fermentum purus lacus eget massa. Donec ac arcu vel magna consequat pretium et vel ligula. Donec sit amet erat elit. Vivamus eu metus eget est hendrerit rutrum. Curabitur vitae orci et leo interdum egestas ut sit amet dui. In varius enim ut sem posuere in tristique metus ultrices. Integer mollis massa at orci porta vestibulum. Pellentesque dignissim turpis ut tortor ultricies condimentum et quis nibh. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer euismod lorem vulputate dui pharetra luctus. Sed vulputate, nunc quis porttitor scelerisque, dui est varius ipsum, eu blandit mauris nibh pellentesque tortor. Vivamus ultricies ante eget ipsum pulvinar ac tempor turpis mollis. Morbi tortor orci, euismod vel sagittis ac, lobortis nec est. Quisque euismod venenatis felis at dapibus. Vestibulum dignissim nulla ut nisi tristique porttitor. Proin et nunc id arcu cursus dapibus non quis libero. Nunc ligula mi, bibendum non mattis nec, luctus id neque. Suspendisse ut eros lacus. Praesent eget lacus eget risus congue vestibulum. Morbi tincidunt pulvinar lacus sed faucibus. Phasellus sed vestibulum sapien.");
-
-
-
- if (item.end == 0) {
- item.svgLine = d3.select("svg#main")
- .append("line")
- .attr("y1",this.constants.barHeight)
- .style("stroke", "rgb(200,200,255)")
- .style("stroke-width", 3)
- }
- }
- item.svg.attr('width',item.getLength(this.axis.msPerPixel))
- .attr("x",this._getXforItem(item))
- .attr("y",this._getYforItem(item, i))
-
-
- .attr('height',25)
- if (item.end == 0) {
- item.svgLine.attr('y2',item.y)
- .attr('x1',item.timeX)
- .attr('x2',item.timeX)
- }
- }
-};
-
-SvgTimeline.prototype._getXforItem = function(item) {
- item.timeX = (item.start - this.range.start)/this.axis.msPerPixel;
- if (item.end == 0) {
- item.drawX = item.timeX - item.width * 0.5;
- }
- else {
- item.drawX = item.timeX;
- }
- return item.drawX;
-}
-
-SvgTimeline.prototype._getYforItem = function(item, index) {
- var bounds = 10;
- var startIndex = Math.max(0,index-bounds);
- item.level = 0;
- for (var i = startIndex; i < index; i++) {
- var item2 = this.sortedActiveItems[i];
- if (item.drawX <= (item2.drawX + item2.width + 5) && item2.level == item.level) {
- item.level += 1;
- }
- }
- item.y = 100 + 50*item.level;
- return item.y;
-}
\ No newline at end of file
diff --git a/src/svgTimeline/util.js b/src/svgTimeline/util.js
deleted file mode 100644
index 2749183d..00000000
--- a/src/svgTimeline/util.js
+++ /dev/null
@@ -1,812 +0,0 @@
-/**
- * utility functions
- */
-var util = {};
-
-/**
- * Test whether given object is a number
- * @param {*} object
- * @return {Boolean} isNumber
- */
-util.isNumber = function isNumber(object) {
- return (object instanceof Number || typeof object == 'number');
-};
-
-/**
- * Test whether given object is a string
- * @param {*} object
- * @return {Boolean} isString
- */
-util.isString = function isString(object) {
- return (object instanceof String || typeof object == 'string');
-};
-
-/**
- * Test whether given object is a Date, or a String containing a Date
- * @param {Date | String} object
- * @return {Boolean} isDate
- */
-util.isDate = function isDate(object) {
- if (object instanceof Date) {
- return true;
- }
- else if (util.isString(object)) {
- // test whether this string contains a date
- var match = ASPDateRegex.exec(object);
- if (match) {
- return true;
- }
- else if (!isNaN(Date.parse(object))) {
- return true;
- }
- }
-
- return false;
-};
-
-/**
- * Test whether given object is an instance of google.visualization.DataTable
- * @param {*} object
- * @return {Boolean} isDataTable
- */
-util.isDataTable = function isDataTable(object) {
- return (typeof (google) !== 'undefined') &&
- (google.visualization) &&
- (google.visualization.DataTable) &&
- (object instanceof google.visualization.DataTable);
-};
-
-/**
- * Create a semi UUID
- * source: http://stackoverflow.com/a/105074/1262753
- * @return {String} uuid
- */
-util.randomUUID = function randomUUID () {
- var S4 = function () {
- return Math.floor(
- Math.random() * 0x10000 /* 65536 */
- ).toString(16);
- };
-
- return (
- S4() + S4() + '-' +
- S4() + '-' +
- S4() + '-' +
- S4() + '-' +
- S4() + S4() + S4()
- );
-};
-
-/**
- * Extend object a with the properties of object b or a series of objects
- * Only properties with defined values are copied
- * @param {Object} a
- * @param {... Object} b
- * @return {Object} a
- */
-util.extend = function (a, b) {
- for (var i = 1, len = arguments.length; i < len; i++) {
- var other = arguments[i];
- for (var prop in other) {
- if (other.hasOwnProperty(prop) && other[prop] !== undefined) {
- a[prop] = other[prop];
- }
- }
- }
-
- return a;
-};
-
-/**
- * Convert an object to another type
- * @param {Boolean | Number | String | Date | Moment | Null | undefined} object
- * @param {String | undefined} type Name of the type. Available types:
- * 'Boolean', 'Number', 'String',
- * 'Date', 'Moment', ISODate', 'ASPDate'.
- * @return {*} object
- * @throws Error
- */
-util.convert = function convert(object, type) {
- var match;
-
- if (object === undefined) {
- return undefined;
- }
- if (object === null) {
- return null;
- }
-
- if (!type) {
- return object;
- }
- if (!(typeof type === 'string') && !(type instanceof String)) {
- throw new Error('Type must be a string');
- }
-
- //noinspection FallthroughInSwitchStatementJS
- switch (type) {
- case 'boolean':
- case 'Boolean':
- return Boolean(object);
-
- case 'number':
- case 'Number':
- return Number(object.valueOf());
-
- case 'string':
- case 'String':
- return String(object);
-
- case 'Date':
- if (util.isNumber(object)) {
- return new Date(object);
- }
- if (object instanceof Date) {
- return new Date(object.valueOf());
- }
- else if (moment.isMoment(object)) {
- return new Date(object.valueOf());
- }
- if (util.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return new Date(Number(match[1])); // parse number
- }
- else {
- return moment(object).toDate(); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + util.getType(object) +
- ' to type Date');
- }
-
- case 'Moment':
- if (util.isNumber(object)) {
- return moment(object);
- }
- if (object instanceof Date) {
- return moment(object.valueOf());
- }
- else if (moment.isMoment(object)) {
- return moment(object);
- }
- if (util.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return moment(Number(match[1])); // parse number
- }
- else {
- return moment(object); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + util.getType(object) +
- ' to type Date');
- }
-
- case 'ISODate':
- if (util.isNumber(object)) {
- return new Date(object);
- }
- else if (object instanceof Date) {
- return object.toISOString();
- }
- else if (moment.isMoment(object)) {
- return object.toDate().toISOString();
- }
- else if (util.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return new Date(Number(match[1])).toISOString(); // parse number
- }
- else {
- return new Date(object).toISOString(); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + util.getType(object) +
- ' to type ISODate');
- }
-
- case 'ASPDate':
- if (util.isNumber(object)) {
- return '/Date(' + object + ')/';
- }
- else if (object instanceof Date) {
- return '/Date(' + object.valueOf() + ')/';
- }
- else if (util.isString(object)) {
- match = ASPDateRegex.exec(object);
- var value;
- if (match) {
- // object is an ASP date
- value = new Date(Number(match[1])).valueOf(); // parse number
- }
- else {
- value = new Date(object).valueOf(); // parse string
- }
- return '/Date(' + value + ')/';
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + util.getType(object) +
- ' to type ASPDate');
- }
-
- default:
- throw new Error('Cannot convert object of type ' + util.getType(object) +
- ' to type "' + type + '"');
- }
-};
-
-// parse ASP.Net Date pattern,
-// for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/'
-// code from http://momentjs.com/
-var ASPDateRegex = /^\/?Date\((\-?\d+)/i;
-
-/**
- * Get the type of an object, for example util.getType([]) returns 'Array'
- * @param {*} object
- * @return {String} type
- */
-util.getType = function getType(object) {
- var type = typeof object;
-
- if (type == 'object') {
- if (object == null) {
- return 'null';
- }
- if (object instanceof Boolean) {
- return 'Boolean';
- }
- if (object instanceof Number) {
- return 'Number';
- }
- if (object instanceof String) {
- return 'String';
- }
- if (object instanceof Array) {
- return 'Array';
- }
- if (object instanceof Date) {
- return 'Date';
- }
- return 'Object';
- }
- else if (type == 'number') {
- return 'Number';
- }
- else if (type == 'boolean') {
- return 'Boolean';
- }
- else if (type == 'string') {
- return 'String';
- }
-
- return type;
-};
-
-/**
- * Retrieve the absolute left value of a DOM element
- * @param {Element} elem A dom element, for example a div
- * @return {number} left The absolute left position of this element
- * in the browser page.
- */
-util.getAbsoluteLeft = function getAbsoluteLeft (elem) {
- var doc = document.documentElement;
- var body = document.body;
-
- var left = elem.offsetLeft;
- var e = elem.offsetParent;
- while (e != null && e != body && e != doc) {
- left += e.offsetLeft;
- left -= e.scrollLeft;
- e = e.offsetParent;
- }
- return left;
-};
-
-/**
- * Retrieve the absolute top value of a DOM element
- * @param {Element} elem A dom element, for example a div
- * @return {number} top The absolute top position of this element
- * in the browser page.
- */
-util.getAbsoluteTop = function getAbsoluteTop (elem) {
- var doc = document.documentElement;
- var body = document.body;
-
- var top = elem.offsetTop;
- var e = elem.offsetParent;
- while (e != null && e != body && e != doc) {
- top += e.offsetTop;
- top -= e.scrollTop;
- e = e.offsetParent;
- }
- return top;
-};
-
-/**
- * Get the absolute, vertical mouse position from an event.
- * @param {Event} event
- * @return {Number} pageY
- */
-util.getPageY = function getPageY (event) {
- if ('pageY' in event) {
- return event.pageY;
- }
- else {
- var clientY;
- if (('targetTouches' in event) && event.targetTouches.length) {
- clientY = event.targetTouches[0].clientY;
- }
- else {
- clientY = event.clientY;
- }
-
- var doc = document.documentElement;
- var body = document.body;
- return clientY +
- ( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
- ( doc && doc.clientTop || body && body.clientTop || 0 );
- }
-};
-
-/**
- * Get the absolute, horizontal mouse position from an event.
- * @param {Event} event
- * @return {Number} pageX
- */
-util.getPageX = function getPageX (event) {
- if ('pageY' in event) {
- return event.pageX;
- }
- else {
- var clientX;
- if (('targetTouches' in event) && event.targetTouches.length) {
- clientX = event.targetTouches[0].clientX;
- }
- else {
- clientX = event.clientX;
- }
-
- var doc = document.documentElement;
- var body = document.body;
- return clientX +
- ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
- ( doc && doc.clientLeft || body && body.clientLeft || 0 );
- }
-};
-
-/**
- * add a className to the given elements style
- * @param {Element} elem
- * @param {String} className
- */
-util.addClassName = function addClassName(elem, className) {
- var classes = elem.className.split(' ');
- if (classes.indexOf(className) == -1) {
- classes.push(className); // add the class to the array
- elem.className = classes.join(' ');
- }
-};
-
-/**
- * add a className to the given elements style
- * @param {Element} elem
- * @param {String} className
- */
-util.removeClassName = function removeClassname(elem, className) {
- var classes = elem.className.split(' ');
- var index = classes.indexOf(className);
- if (index != -1) {
- classes.splice(index, 1); // remove the class from the array
- elem.className = classes.join(' ');
- }
-};
-
-/**
- * For each method for both arrays and objects.
- * In case of an array, the built-in Array.forEach() is applied.
- * In case of an Object, the method loops over all properties of the object.
- * @param {Object | Array} object An Object or Array
- * @param {function} callback Callback method, called for each item in
- * the object or array with three parameters:
- * callback(value, index, object)
- */
-util.forEach = function forEach (object, callback) {
- var i,
- len;
- if (object instanceof Array) {
- // array
- for (i = 0, len = object.length; i < len; i++) {
- callback(object[i], i, object);
- }
- }
- else {
- // object
- for (i in object) {
- if (object.hasOwnProperty(i)) {
- callback(object[i], i, object);
- }
- }
- }
-};
-
-/**
- * Update a property in an object
- * @param {Object} object
- * @param {String} key
- * @param {*} value
- * @return {Boolean} changed
- */
-util.updateProperty = function updateProp (object, key, value) {
- if (object[key] !== value) {
- object[key] = value;
- return true;
- }
- else {
- return false;
- }
-};
-
-/**
- * Add and event listener. Works for all browsers
- * @param {Element} element An html element
- * @param {string} action The action, for example "click",
- * without the prefix "on"
- * @param {function} listener The callback function to be executed
- * @param {boolean} [useCapture]
- */
-util.addEventListener = function addEventListener(element, action, listener, useCapture) {
- if (element.addEventListener) {
- if (useCapture === undefined)
- useCapture = false;
-
- if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
- action = "DOMMouseScroll"; // For Firefox
- }
-
- element.addEventListener(action, listener, useCapture);
- } else {
- element.attachEvent("on" + action, listener); // IE browsers
- }
-};
-
-/**
- * Remove an event listener from an element
- * @param {Element} element An html dom element
- * @param {string} action The name of the event, for example "mousedown"
- * @param {function} listener The listener function
- * @param {boolean} [useCapture]
- */
-util.removeEventListener = function removeEventListener(element, action, listener, useCapture) {
- if (element.removeEventListener) {
- // non-IE browsers
- if (useCapture === undefined)
- useCapture = false;
-
- if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
- action = "DOMMouseScroll"; // For Firefox
- }
-
- element.removeEventListener(action, listener, useCapture);
- } else {
- // IE browsers
- element.detachEvent("on" + action, listener);
- }
-};
-
-
-/**
- * Get HTML element which is the target of the event
- * @param {Event} event
- * @return {Element} target element
- */
-util.getTarget = function getTarget(event) {
- // code from http://www.quirksmode.org/js/events_properties.html
- if (!event) {
- event = window.event;
- }
-
- var target;
-
- if (event.target) {
- target = event.target;
- }
- else if (event.srcElement) {
- target = event.srcElement;
- }
-
- if (target.nodeType != undefined && target.nodeType == 3) {
- // defeat Safari bug
- target = target.parentNode;
- }
-
- return target;
-};
-
-/**
- * Fake a hammer.js gesture. Event can be a ScrollEvent or MouseMoveEvent
- * @param {Element} element
- * @param {Event} event
- */
-util.fakeGesture = function fakeGesture (element, event) {
- var eventType = null;
-
- // for hammer.js 1.0.5
- var gesture = Hammer.event.collectEventData(this, eventType, event);
-
- // for hammer.js 1.0.6
- //var touches = Hammer.event.getTouchList(event, eventType);
- // var gesture = Hammer.event.collectEventData(this, eventType, touches, event);
-
- // on IE in standards mode, no touches are recognized by hammer.js,
- // resulting in NaN values for center.pageX and center.pageY
- if (isNaN(gesture.center.pageX)) {
- gesture.center.pageX = event.pageX;
- }
- if (isNaN(gesture.center.pageY)) {
- gesture.center.pageY = event.pageY;
- }
-
- return gesture;
-};
-
-util.option = {};
-
-/**
- * Convert a value into a boolean
- * @param {Boolean | function | undefined} value
- * @param {Boolean} [defaultValue]
- * @returns {Boolean} bool
- */
-util.option.asBoolean = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (value != null) {
- return (value != false);
- }
-
- return defaultValue || null;
-};
-
-/**
- * Convert a value into a number
- * @param {Boolean | function | undefined} value
- * @param {Number} [defaultValue]
- * @returns {Number} number
- */
-util.option.asNumber = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (value != null) {
- return Number(value) || defaultValue || null;
- }
-
- return defaultValue || null;
-};
-
-/**
- * Convert a value into a string
- * @param {String | function | undefined} value
- * @param {String} [defaultValue]
- * @returns {String} str
- */
-util.option.asString = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (value != null) {
- return String(value);
- }
-
- return defaultValue || null;
-};
-
-/**
- * Convert a size or location into a string with pixels or a percentage
- * @param {String | Number | function | undefined} value
- * @param {String} [defaultValue]
- * @returns {String} size
- */
-util.option.asSize = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (util.isString(value)) {
- return value;
- }
- else if (util.isNumber(value)) {
- return value + 'px';
- }
- else {
- return defaultValue || null;
- }
-};
-
-/**
- * Convert a value into a DOM element
- * @param {HTMLElement | function | undefined} value
- * @param {HTMLElement} [defaultValue]
- * @returns {HTMLElement | null} dom
- */
-util.option.asElement = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- return value || defaultValue || null;
-};
-
-
-
-util.GiveDec = function GiveDec(Hex)
-{
- if(Hex == "A")
- Value = 10;
- else
- if(Hex == "B")
- Value = 11;
- else
- if(Hex == "C")
- Value = 12;
- else
- if(Hex == "D")
- Value = 13;
- else
- if(Hex == "E")
- Value = 14;
- else
- if(Hex == "F")
- Value = 15;
- else
- Value = eval(Hex)
- return Value;
-}
-
-util.GiveHex = function GiveHex(Dec)
-{
- if(Dec == 10)
- Value = "A";
- else
- if(Dec == 11)
- Value = "B";
- else
- if(Dec == 12)
- Value = "C";
- else
- if(Dec == 13)
- Value = "D";
- else
- if(Dec == 14)
- Value = "E";
- else
- if(Dec == 15)
- Value = "F";
- else
- Value = "" + Dec;
- return Value;
-}
-
-/**
- * http://www.yellowpipe.com/yis/tools/hex-to-rgb/color-converter.php
- *
- * @param {String} hex
- * @returns {{r: *, g: *, b: *}}
- */
-util.hexToRGB = function hexToRGB(hex) {
- hex = hex.replace("#","").toUpperCase();
-
- var a = util.GiveDec(hex.substring(0, 1));
- var b = util.GiveDec(hex.substring(1, 2));
- var c = util.GiveDec(hex.substring(2, 3));
- var d = util.GiveDec(hex.substring(3, 4));
- var e = util.GiveDec(hex.substring(4, 5));
- var f = util.GiveDec(hex.substring(5, 6));
-
- var r = (a * 16) + b;
- var g = (c * 16) + d;
- var b = (e * 16) + f;
-
- return {r:r,g:g,b:b};
-};
-
-util.RGBToHex = function RGBToHex(red,green,blue) {
- var a = util.GiveHex(Math.floor(red / 16));
- var b = util.GiveHex(red % 16);
- var c = util.GiveHex(Math.floor(green / 16));
- var d = util.GiveHex(green % 16);
- var e = util.GiveHex(Math.floor(blue / 16));
- var f = util.GiveHex(blue % 16);
-
- var hex = a + b + c + d + e + f;
- return "#" + hex;
-};
-
-
-/**
- * http://www.javascripter.net/faq/rgb2hsv.htm
- *
- * @param red
- * @param green
- * @param blue
- * @returns {*}
- * @constructor
- */
-util.RGBToHSV = function RGBToHSV (red,green,blue) {
- red=red/255; green=green/255; blue=blue/255;
- var minRGB = Math.min(red,Math.min(green,blue));
- var maxRGB = Math.max(red,Math.max(green,blue));
-
- // Black-gray-white
- if (minRGB == maxRGB) {
- return {h:0,s:0,v:minRGB};
- }
-
- // Colors other than black-gray-white:
- var d = (red==minRGB) ? green-blue : ((blue==minRGB) ? red-green : blue-red);
- var h = (red==minRGB) ? 3 : ((blue==minRGB) ? 1 : 5);
- var hue = 60*(h - d/(maxRGB - minRGB))/360;
- var saturation = (maxRGB - minRGB)/maxRGB;
- var value = maxRGB;
- return {h:hue,s:saturation,v:value};
-};
-
-
-/**
- * https://gist.github.com/mjijackson/5311256
- * @param hue
- * @param saturation
- * @param value
- * @returns {{r: number, g: number, b: number}}
- * @constructor
- */
-util.HSVToRGB = function HSVToRGB(h, s, v) {
- var r, g, b;
-
- var i = Math.floor(h * 6);
- var f = h * 6 - i;
- var p = v * (1 - s);
- var q = v * (1 - f * s);
- var t = v * (1 - (1 - f) * s);
-
- switch (i % 6) {
- case 0: r = v, g = t, b = p; break;
- case 1: r = q, g = v, b = p; break;
- case 2: r = p, g = v, b = t; break;
- case 3: r = p, g = q, b = v; break;
- case 4: r = t, g = p, b = v; break;
- case 5: r = v, g = p, b = q; break;
- }
-
- return {r:Math.floor(r * 255), g:Math.floor(g * 255), b:Math.floor(b * 255) };
-};
-
-util.HSVToHex = function HSVToHex(h,s,v) {
- var rgb = util.HSVToRGB(h,s,v);
- return util.RGBToHex(rgb.r,rgb.g,rgb.b);
-}
-
-util.hexToHSV = function hexToHSV(hex) {
- var rgb = util.hexToRGB(hex);
- return util.RGBToHSV(rgb.r,rgb.g,rgb.b);
-}
-
-util.isValidHex = function isValidHex(hex) {
- var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex);
- return isOk;
-}
l;){var r=n.charCodeAt(l++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(l)&&(++l,++a);else if(r!==c)continue;return n.substring(t,l-a)}return n.substring(t)}for(var r,u,i={},o={},a=[],s=n.length,l=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();(!t||(h=t(h,f++)))&&a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new l,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},Xo.csv=Xo.dsv(",","text/csv"),Xo.tsv=Xo.dsv(" ","text/tab-separated-values");var Xa,$a,Ba,Wa,Ja,Ga=Go[h(Go,"requestAnimationFrame")]||function(n){setTimeout(n,17)};Xo.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};$a?$a.n=i:Xa=i,$a=i,Ba||(Wa=clearTimeout(Wa),Ba=1,Ga(Et))},Xo.timer.flush=function(){At(),Ct()},Xo.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var Ka=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Lt);Xo.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=Xo.round(n,Nt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((0>=e?e+1:e-1)/3)))),Ka[8+e/3]};var Qa=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,nc=Xo.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=Xo.round(n,Nt(n,t))).toFixed(Math.max(0,Math.min(20,Nt(n*(1+1e-15),t))))}}),tc=Xo.time={},ec=Date;Tt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){rc.setUTCDate.apply(this._,arguments)},setDay:function(){rc.setUTCDay.apply(this._,arguments)},setFullYear:function(){rc.setUTCFullYear.apply(this._,arguments)},setHours:function(){rc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){rc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){rc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){rc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){rc.setUTCSeconds.apply(this._,arguments)},setTime:function(){rc.setTime.apply(this._,arguments)}};var rc=Date.prototype;tc.year=Rt(function(n){return n=tc.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),tc.years=tc.year.range,tc.years.utc=tc.year.utc.range,tc.day=Rt(function(n){var t=new ec(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),tc.days=tc.day.range,tc.days.utc=tc.day.utc.range,tc.dayOfYear=function(n){var t=tc.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=tc[n]=Rt(function(n){return(n=tc.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=tc.year(n).getDay();return Math.floor((tc.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});tc[n+"s"]=e.range,tc[n+"s"].utc=e.utc.range,tc[n+"OfYear"]=function(n){var e=tc.year(n).getDay();return Math.floor((tc.dayOfYear(n)+(e+t)%7)/7)}}),tc.week=tc.sunday,tc.weeks=tc.sunday.range,tc.weeks.utc=tc.sunday.utc.range,tc.weekOfYear=tc.sundayOfYear;var uc={"-":"",_:" ",0:"0"},ic=/^\s*\d+/,oc=/^%/;Xo.locale=function(n){return{numberFormat:zt(n),timeFormat:Pt(n)}};var ac=Xo.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});Xo.format=ac.numberFormat,Xo.geo={},re.prototype={s:0,t:0,add:function(n){ue(n,this.t,cc),ue(cc.s,this.s,this),this.s?this.t+=cc.t:this.s=cc.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var cc=new re;Xo.geo.stream=function(n,t){n&&sc.hasOwnProperty(n.type)?sc[n.type](n,t):ie(n,t)};var sc={Feature:function(n,t){ie(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++rn?4*Sa+n:n,gc.lineStart=gc.lineEnd=gc.point=g}};Xo.geo.bounds=function(){function n(n,t){x.push(M=[l=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=se([t*Na,e*Na]);if(m){var u=fe(m,r),i=[u[1],-u[0],0],o=fe(i,u);pe(o),o=ve(o);var c=t-p,s=c>0?1:-1,v=o[0]*La*s,d=oa(c)>180;if(d^(v>s*p&&s*t>v)){var y=o[1]*La;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>s*p&&s*t>v)){var y=-o[1]*La;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t):h>=l?(l>t&&(l=t),t>h&&(h=t)):t>p?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t)}else n(t,e);m=r,p=t}function e(){_.point=t}function r(){M[0]=l,M[1]=h,_.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=oa(r)>180?r+(r>0?360:-360):r}else v=n,d=e;gc.point(n,e),t(n,e)}function i(){gc.lineStart()}function o(){u(v,d),gc.lineEnd(),oa(y)>Aa&&(l=-(h=180)),M[0]=l,M[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function s(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n=s,h=r>=l,g=(h<<1)+f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=iu()),f?u=s:a=s,h?o=l:c=l,i(n,t,e,r,u,o,a,c)}var l,f,h,g,p,v,d,m,y,x=_t(a),M=_t(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)l=n[g],l.xt;++t)(r=m[t]).index=t,r.weight=0;for(t=0;l>t;++t)r=y[t],"number"==typeof r.source&&(r.source=m[r.source]),"number"==typeof r.target&&(r.target=m[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;l>t;++t)u[t]=+f.call(this,y[t],t);else for(t=0;l>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;l>t;++t)i[t]=+h.call(this,y[t],t);else for(t=0;l>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=Xo.behavior.drag().origin(bt).on("dragstart.force",Fu).on("drag.force",t).on("dragend.force",Ou)),arguments.length?(this.on("mouseover.force",Yu).on("mouseout.force",Iu).call(e),void 0):e},Xo.rebind(a,c,"on")};var us=20,is=1,os=1/0;Xo.layout.hierarchy=function(){function n(t,o,a){var c=u.call(e,t,o);if(t.depth=o,a.push(t),c&&(s=c.length)){for(var s,l,f=-1,h=t.children=new Array(s),g=0,p=o+1;++fg;++g)for(u.call(n,s[0][g],p=v[g],l[0][g][1]),h=1;d>h;++h)u.call(n,s[h][g],p+=l[h-1][g][1],l[h][g][1]);return a}var t=bt,e=Qu,r=ni,u=Ku,i=Ju,o=Gu;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:cs.get(t)||Qu,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:ss.get(t)||ni,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var cs=Xo.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(ti),i=n.map(ei),o=Xo.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,s=[],l=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],s.push(e)):(c+=i[e],l.push(e));return l.reverse().concat(s)},reverse:function(n){return Xo.range(n.length).reverse()},"default":Qu}),ss=Xo.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,s,l=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=s=0,e=1;h>e;++e){for(t=0,u=0;l>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];l>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,s>c&&(s=c)}for(e=0;h>e;++e)g[e]-=s;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ni});Xo.layout.histogram=function(){function n(n,i){for(var o,a,c=[],s=n.map(e,this),l=r.call(this,s,i),f=u.call(this,l,s,i),i=-1,h=s.length,g=f.length-1,p=t?1:1/h;++iAn HTML Foreign Object in SVG