vendor/assets/javascripts/nvd3.js in chart-0.1.5.0.pre vs vendor/assets/javascripts/nvd3.js in chart-0.1.5.1
- old
+ new
@@ -1,10859 +1,10954 @@
-/* nvd3 version 1.8.4 (https://github.com/novus/nvd3) 2016-07-03 */
+/* nvd3 version 1.8.5 (https://github.com/novus/nvd3) 2016-12-01 */
(function(){
// set up main nv object
-var nv = {};
+ var nv = {};
// the major global objects under the nv namespace
-nv.dev = false; //set false when in production
-nv.tooltip = nv.tooltip || {}; // For the tooltip system
-nv.utils = nv.utils || {}; // Utility subsystem
-nv.models = nv.models || {}; //stores all the possible models/components
-nv.charts = {}; //stores all the ready to use charts
-nv.logs = {}; //stores some statistics and potential error messages
-nv.dom = {}; //DOM manipulation functions
+ nv.dev = false; //set false when in production
+ nv.tooltip = nv.tooltip || {}; // For the tooltip system
+ nv.utils = nv.utils || {}; // Utility subsystem
+ nv.models = nv.models || {}; //stores all the possible models/components
+ nv.charts = {}; //stores all the ready to use charts
+ nv.logs = {}; //stores some statistics and potential error messages
+ nv.dom = {}; //DOM manipulation functions
// Node/CommonJS - require D3
-if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined' && typeof(d3) == 'undefined') {
- d3 = require('d3');
-}
+ if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined' && typeof(d3) == 'undefined') {
+ d3 = require('d3');
+ }
-nv.dispatch = d3.dispatch('render_start', 'render_end');
+ nv.dispatch = d3.dispatch('render_start', 'render_end');
// Function bind polyfill
// Needed ONLY for phantomJS as it's missing until version 2.0 which is unreleased as of this comment
// https://github.com/ariya/phantomjs/issues/10522
// http://kangax.github.io/compat-table/es5/#Function.prototype.bind
// phantomJS is used for running the test suite
-if (!Function.prototype.bind) {
- Function.prototype.bind = function (oThis) {
- if (typeof this !== "function") {
- // closest thing possible to the ECMAScript 5 internal IsCallable function
- throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
- }
+ if (!Function.prototype.bind) {
+ Function.prototype.bind = function (oThis) {
+ if (typeof this !== "function") {
+ // closest thing possible to the ECMAScript 5 internal IsCallable function
+ throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
+ }
- var aArgs = Array.prototype.slice.call(arguments, 1),
- fToBind = this,
- fNOP = function () {},
- fBound = function () {
- return fToBind.apply(this instanceof fNOP && oThis
- ? this
- : oThis,
- aArgs.concat(Array.prototype.slice.call(arguments)));
- };
+ var aArgs = Array.prototype.slice.call(arguments, 1),
+ fToBind = this,
+ fNOP = function () {},
+ fBound = function () {
+ return fToBind.apply(this instanceof fNOP && oThis
+ ? this
+ : oThis,
+ aArgs.concat(Array.prototype.slice.call(arguments)));
+ };
- fNOP.prototype = this.prototype;
- fBound.prototype = new fNOP();
- return fBound;
- };
-}
+ fNOP.prototype = this.prototype;
+ fBound.prototype = new fNOP();
+ return fBound;
+ };
+ }
// Development render timers - disabled if dev = false
-if (nv.dev) {
- nv.dispatch.on('render_start', function(e) {
- nv.logs.startTime = +new Date();
- });
+ if (nv.dev) {
+ nv.dispatch.on('render_start', function(e) {
+ nv.logs.startTime = +new Date();
+ });
- nv.dispatch.on('render_end', function(e) {
- nv.logs.endTime = +new Date();
- nv.logs.totalTime = nv.logs.endTime - nv.logs.startTime;
- nv.log('total', nv.logs.totalTime); // used for development, to keep track of graph generation times
- });
-}
+ nv.dispatch.on('render_end', function(e) {
+ nv.logs.endTime = +new Date();
+ nv.logs.totalTime = nv.logs.endTime - nv.logs.startTime;
+ nv.log('total', nv.logs.totalTime); // used for development, to keep track of graph generation times
+ });
+ }
// Logs all arguments, and returns the last so you can test things in place
// Note: in IE8 console.log is an object not a function, and if modernizr is used
// then calling Function.prototype.bind with with anything other than a function
// causes a TypeError to be thrown.
-nv.log = function() {
- if (nv.dev && window.console && console.log && console.log.apply)
- console.log.apply(console, arguments);
- else if (nv.dev && window.console && typeof console.log == "function" && Function.prototype.bind) {
- var log = Function.prototype.bind.call(console.log, console);
- log.apply(console, arguments);
- }
- return arguments[arguments.length - 1];
-};
+ nv.log = function() {
+ if (nv.dev && window.console && console.log && console.log.apply)
+ console.log.apply(console, arguments);
+ else if (nv.dev && window.console && typeof console.log == "function" && Function.prototype.bind) {
+ var log = Function.prototype.bind.call(console.log, console);
+ log.apply(console, arguments);
+ }
+ return arguments[arguments.length - 1];
+ };
// print console warning, should be used by deprecated functions
-nv.deprecated = function(name, info) {
- if (console && console.warn) {
- console.warn('nvd3 warning: `' + name + '` has been deprecated. ', info || '');
- }
-};
+ nv.deprecated = function(name, info) {
+ if (console && console.warn) {
+ console.warn('nvd3 warning: `' + name + '` has been deprecated. ', info || '');
+ }
+ };
// The nv.render function is used to queue up chart rendering
// in non-blocking async functions.
// When all queued charts are done rendering, nv.dispatch.render_end is invoked.
-nv.render = function render(step) {
- // number of graphs to generate in each timeout loop
- step = step || 1;
+ nv.render = function render(step) {
+ // number of graphs to generate in each timeout loop
+ step = step || 1;
- nv.render.active = true;
- nv.dispatch.render_start();
+ nv.render.active = true;
+ nv.dispatch.render_start();
- var renderLoop = function() {
- var chart, graph;
+ var renderLoop = function() {
+ var chart, graph;
- for (var i = 0; i < step && (graph = nv.render.queue[i]); i++) {
- chart = graph.generate();
- if (typeof graph.callback == typeof(Function)) graph.callback(chart);
- }
+ for (var i = 0; i < step && (graph = nv.render.queue[i]); i++) {
+ chart = graph.generate();
+ if (typeof graph.callback == typeof(Function)) graph.callback(chart);
+ }
- nv.render.queue.splice(0, i);
+ nv.render.queue.splice(0, i);
- if (nv.render.queue.length) {
- setTimeout(renderLoop);
- }
- else {
- nv.dispatch.render_end();
- nv.render.active = false;
- }
+ if (nv.render.queue.length) {
+ setTimeout(renderLoop);
+ }
+ else {
+ nv.dispatch.render_end();
+ nv.render.active = false;
+ }
+ };
+
+ setTimeout(renderLoop);
};
- setTimeout(renderLoop);
-};
+ nv.render.active = false;
+ nv.render.queue = [];
-nv.render.active = false;
-nv.render.queue = [];
+ /*
+ Adds a chart to the async rendering queue. This method can take arguments in two forms:
+ nv.addGraph({
+ generate: <Function>
+ callback: <Function>
+ })
-/*
-Adds a chart to the async rendering queue. This method can take arguments in two forms:
-nv.addGraph({
- generate: <Function>
- callback: <Function>
-})
+ or
-or
+ nv.addGraph(<generate Function>, <callback Function>)
-nv.addGraph(<generate Function>, <callback Function>)
+ The generate function should contain code that creates the NVD3 model, sets options
+ on it, adds data to an SVG element, and invokes the chart model. The generate function
+ should return the chart model. See examples/lineChart.html for a usage example.
-The generate function should contain code that creates the NVD3 model, sets options
-on it, adds data to an SVG element, and invokes the chart model. The generate function
-should return the chart model. See examples/lineChart.html for a usage example.
+ The callback function is optional, and it is called when the generate function completes.
+ */
+ nv.addGraph = function(obj) {
+ if (typeof arguments[0] === typeof(Function)) {
+ obj = {generate: arguments[0], callback: arguments[1]};
+ }
-The callback function is optional, and it is called when the generate function completes.
-*/
-nv.addGraph = function(obj) {
- if (typeof arguments[0] === typeof(Function)) {
- obj = {generate: arguments[0], callback: arguments[1]};
- }
+ nv.render.queue.push(obj);
- nv.render.queue.push(obj);
+ if (!nv.render.active) {
+ nv.render();
+ }
+ };
- if (!nv.render.active) {
- nv.render();
+// Node/CommonJS exports
+ if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined') {
+ module.exports = nv;
}
-};
-// Node/CommonJS exports
-if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined') {
- module.exports = nv;
-}
+ if (typeof(window) !== 'undefined') {
+ window.nv = nv;
+ }
+ /* Facade for queueing DOM write operations
+ * with Fastdom (https://github.com/wilsonpage/fastdom)
+ * if available.
+ * This could easily be extended to support alternate
+ * implementations in the future.
+ */
+ nv.dom.write = function(callback) {
+ if (window.fastdom !== undefined) {
+ return fastdom.mutate(callback);
+ }
+ return callback();
+ };
-if (typeof(window) !== 'undefined') {
- window.nv = nv;
-}
-/* Facade for queueing DOM write operations
- * with Fastdom (https://github.com/wilsonpage/fastdom)
- * if available.
- * This could easily be extended to support alternate
- * implementations in the future.
- */
-nv.dom.write = function(callback) {
- if (window.fastdom !== undefined) {
- return fastdom.mutate(callback);
- }
- return callback();
-};
+ /* Facade for queueing DOM read operations
+ * with Fastdom (https://github.com/wilsonpage/fastdom)
+ * if available.
+ * This could easily be extended to support alternate
+ * implementations in the future.
+ */
+ nv.dom.read = function(callback) {
+ if (window.fastdom !== undefined) {
+ return fastdom.measure(callback);
+ }
+ return callback();
+ };
+ /* Utility class to handle creation of an interactive layer.
+ This places a rectangle on top of the chart. When you mouse move over it, it sends a dispatch
+ containing the X-coordinate. It can also render a vertical line where the mouse is located.
-/* Facade for queueing DOM read operations
- * with Fastdom (https://github.com/wilsonpage/fastdom)
- * if available.
- * This could easily be extended to support alternate
- * implementations in the future.
- */
-nv.dom.read = function(callback) {
- if (window.fastdom !== undefined) {
- return fastdom.measure(callback);
- }
- return callback();
-};
-/* Utility class to handle creation of an interactive layer.
- This places a rectangle on top of the chart. When you mouse move over it, it sends a dispatch
- containing the X-coordinate. It can also render a vertical line where the mouse is located.
+ dispatch.elementMousemove is the important event to latch onto. It is fired whenever the mouse moves over
+ the rectangle. The dispatch is given one object which contains the mouseX/Y location.
+ It also has 'pointXValue', which is the conversion of mouseX to the x-axis scale.
+ */
+ nv.interactiveGuideline = function() {
+ "use strict";
- dispatch.elementMousemove is the important event to latch onto. It is fired whenever the mouse moves over
- the rectangle. The dispatch is given one object which contains the mouseX/Y location.
- It also has 'pointXValue', which is the conversion of mouseX to the x-axis scale.
- */
-nv.interactiveGuideline = function() {
- "use strict";
+ var margin = { left: 0, top: 0 } //Pass the chart's top and left magins. Used to calculate the mouseX/Y.
+ , width = null
+ , height = null
+ , xScale = d3.scale.linear()
+ , dispatch = d3.dispatch('elementMousemove', 'elementMouseout', 'elementClick', 'elementDblclick', 'elementMouseDown', 'elementMouseUp')
+ , showGuideLine = true
+ , svgContainer = null // Must pass the chart's svg, we'll use its mousemove event.
+ , tooltip = nv.models.tooltip()
+ , isMSIE = window.ActiveXObject// Checkt if IE by looking for activeX. (excludes IE11)
+ ;
- var margin = { left: 0, top: 0 } //Pass the chart's top and left magins. Used to calculate the mouseX/Y.
- , width = null
- , height = null
- , xScale = d3.scale.linear()
- , dispatch = d3.dispatch('elementMousemove', 'elementMouseout', 'elementClick', 'elementDblclick', 'elementMouseDown', 'elementMouseUp')
- , showGuideLine = true
- , svgContainer = null // Must pass the chart's svg, we'll use its mousemove event.
- , tooltip = nv.models.tooltip()
- , isMSIE = window.ActiveXObject// Checkt if IE by looking for activeX. (excludes IE11)
- ;
+ tooltip
+ .duration(0)
+ .hideDelay(0)
+ .hidden(false);
- tooltip
- .duration(0)
- .hideDelay(0)
- .hidden(false);
+ function layer(selection) {
+ selection.each(function(data) {
+ var container = d3.select(this);
+ var availableWidth = (width || 960), availableHeight = (height || 400);
+ var wrap = container.selectAll("g.nv-wrap.nv-interactiveLineLayer")
+ .data([data]);
+ var wrapEnter = wrap.enter()
+ .append("g").attr("class", " nv-wrap nv-interactiveLineLayer");
+ wrapEnter.append("g").attr("class","nv-interactiveGuideLine");
- function layer(selection) {
- selection.each(function(data) {
- var container = d3.select(this);
- var availableWidth = (width || 960), availableHeight = (height || 400);
- var wrap = container.selectAll("g.nv-wrap.nv-interactiveLineLayer")
- .data([data]);
- var wrapEnter = wrap.enter()
- .append("g").attr("class", " nv-wrap nv-interactiveLineLayer");
- wrapEnter.append("g").attr("class","nv-interactiveGuideLine");
+ if (!svgContainer) {
+ return;
+ }
- if (!svgContainer) {
- return;
- }
+ function mouseHandler() {
+ var d3mouse = d3.mouse(this);
+ var mouseX = d3mouse[0];
+ var mouseY = d3mouse[1];
+ var subtractMargin = true;
+ var mouseOutAnyReason = false;
+ if (isMSIE) {
+ /*
+ D3.js (or maybe SVG.getScreenCTM) has a nasty bug in Internet Explorer 10.
+ d3.mouse() returns incorrect X,Y mouse coordinates when mouse moving
+ over a rect in IE 10.
+ However, d3.event.offsetX/Y also returns the mouse coordinates
+ relative to the triggering <rect>. So we use offsetX/Y on IE.
+ */
+ mouseX = d3.event.offsetX;
+ mouseY = d3.event.offsetY;
- function mouseHandler() {
- var d3mouse = d3.mouse(this);
- var mouseX = d3mouse[0];
- var mouseY = d3mouse[1];
- var subtractMargin = true;
- var mouseOutAnyReason = false;
- if (isMSIE) {
- /*
- D3.js (or maybe SVG.getScreenCTM) has a nasty bug in Internet Explorer 10.
- d3.mouse() returns incorrect X,Y mouse coordinates when mouse moving
- over a rect in IE 10.
- However, d3.event.offsetX/Y also returns the mouse coordinates
- relative to the triggering <rect>. So we use offsetX/Y on IE.
- */
- mouseX = d3.event.offsetX;
- mouseY = d3.event.offsetY;
+ /*
+ On IE, if you attach a mouse event listener to the <svg> container,
+ it will actually trigger it for all the child elements (like <path>, <circle>, etc).
+ When this happens on IE, the offsetX/Y is set to where ever the child element
+ is located.
+ As a result, we do NOT need to subtract margins to figure out the mouse X/Y
+ position under this scenario. Removing the line below *will* cause
+ the interactive layer to not work right on IE.
+ */
+ if(d3.event.target.tagName !== "svg") {
+ subtractMargin = false;
+ }
- /*
- On IE, if you attach a mouse event listener to the <svg> container,
- it will actually trigger it for all the child elements (like <path>, <circle>, etc).
- When this happens on IE, the offsetX/Y is set to where ever the child element
- is located.
- As a result, we do NOT need to subtract margins to figure out the mouse X/Y
- position under this scenario. Removing the line below *will* cause
- the interactive layer to not work right on IE.
- */
- if(d3.event.target.tagName !== "svg") {
- subtractMargin = false;
+ if (d3.event.target.className.baseVal.match("nv-legend")) {
+ mouseOutAnyReason = true;
+ }
+
}
- if (d3.event.target.className.baseVal.match("nv-legend")) {
- mouseOutAnyReason = true;
+ if(subtractMargin) {
+ mouseX -= margin.left;
+ mouseY -= margin.top;
}
- }
-
- if(subtractMargin) {
- mouseX -= margin.left;
- mouseY -= margin.top;
- }
-
- /* If mouseX/Y is outside of the chart's bounds,
- trigger a mouseOut event.
- */
- if (d3.event.type === 'mouseout'
- || mouseX < 0 || mouseY < 0
- || mouseX > availableWidth || mouseY > availableHeight
- || (d3.event.relatedTarget && d3.event.relatedTarget.ownerSVGElement === undefined)
- || mouseOutAnyReason
+ /* If mouseX/Y is outside of the chart's bounds,
+ trigger a mouseOut event.
+ */
+ if (d3.event.type === 'mouseout'
+ || mouseX < 0 || mouseY < 0
+ || mouseX > availableWidth || mouseY > availableHeight
+ || (d3.event.relatedTarget && d3.event.relatedTarget.ownerSVGElement === undefined)
+ || mouseOutAnyReason
) {
- if (isMSIE) {
- if (d3.event.relatedTarget
- && d3.event.relatedTarget.ownerSVGElement === undefined
- && (d3.event.relatedTarget.className === undefined
+ if (isMSIE) {
+ if (d3.event.relatedTarget
+ && d3.event.relatedTarget.ownerSVGElement === undefined
+ && (d3.event.relatedTarget.className === undefined
|| d3.event.relatedTarget.className.match(tooltip.nvPointerEventsClass))) {
- return;
+ return;
+ }
}
- }
- dispatch.elementMouseout({
- mouseX: mouseX,
- mouseY: mouseY
- });
- layer.renderGuideLine(null); //hide the guideline
- tooltip.hidden(true);
- return;
- } else {
- tooltip.hidden(false);
- }
-
-
- var scaleIsOrdinal = typeof xScale.rangeBands === 'function';
- var pointXValue = undefined;
-
- // Ordinal scale has no invert method
- if (scaleIsOrdinal) {
- var elementIndex = d3.bisect(xScale.range(), mouseX) - 1;
- // Check if mouseX is in the range band
- if (xScale.range()[elementIndex] + xScale.rangeBand() >= mouseX) {
- pointXValue = xScale.domain()[d3.bisect(xScale.range(), mouseX) - 1];
- }
- else {
dispatch.elementMouseout({
mouseX: mouseX,
mouseY: mouseY
});
layer.renderGuideLine(null); //hide the guideline
tooltip.hidden(true);
return;
+ } else {
+ tooltip.hidden(false);
}
- }
- else {
- pointXValue = xScale.invert(mouseX);
- }
- dispatch.elementMousemove({
- mouseX: mouseX,
- mouseY: mouseY,
- pointXValue: pointXValue
- });
- //If user double clicks the layer, fire a elementDblclick
- if (d3.event.type === "dblclick") {
- dispatch.elementDblclick({
- mouseX: mouseX,
- mouseY: mouseY,
- pointXValue: pointXValue
- });
- }
+ var scaleIsOrdinal = typeof xScale.rangeBands === 'function';
+ var pointXValue = undefined;
- // if user single clicks the layer, fire elementClick
- if (d3.event.type === 'click') {
- dispatch.elementClick({
+ // Ordinal scale has no invert method
+ if (scaleIsOrdinal) {
+ var elementIndex = d3.bisect(xScale.range(), mouseX) - 1;
+ // Check if mouseX is in the range band
+ if (xScale.range()[elementIndex] + xScale.rangeBand() >= mouseX) {
+ pointXValue = xScale.domain()[d3.bisect(xScale.range(), mouseX) - 1];
+ }
+ else {
+ dispatch.elementMouseout({
+ mouseX: mouseX,
+ mouseY: mouseY
+ });
+ layer.renderGuideLine(null); //hide the guideline
+ tooltip.hidden(true);
+ return;
+ }
+ }
+ else {
+ pointXValue = xScale.invert(mouseX);
+ }
+
+ dispatch.elementMousemove({
mouseX: mouseX,
mouseY: mouseY,
pointXValue: pointXValue
});
- }
- // if user presses mouse down the layer, fire elementMouseDown
- if (d3.event.type === 'mousedown') {
- dispatch.elementMouseDown({
- mouseX: mouseX,
- mouseY: mouseY,
- pointXValue: pointXValue
- });
+ //If user double clicks the layer, fire a elementDblclick
+ if (d3.event.type === "dblclick") {
+ dispatch.elementDblclick({
+ mouseX: mouseX,
+ mouseY: mouseY,
+ pointXValue: pointXValue
+ });
+ }
+
+ // if user single clicks the layer, fire elementClick
+ if (d3.event.type === 'click') {
+ dispatch.elementClick({
+ mouseX: mouseX,
+ mouseY: mouseY,
+ pointXValue: pointXValue
+ });
+ }
+
+ // if user presses mouse down the layer, fire elementMouseDown
+ if (d3.event.type === 'mousedown') {
+ dispatch.elementMouseDown({
+ mouseX: mouseX,
+ mouseY: mouseY,
+ pointXValue: pointXValue
+ });
+ }
+
+ // if user presses mouse down the layer, fire elementMouseUp
+ if (d3.event.type === 'mouseup') {
+ dispatch.elementMouseUp({
+ mouseX: mouseX,
+ mouseY: mouseY,
+ pointXValue: pointXValue
+ });
+ }
}
- // if user presses mouse down the layer, fire elementMouseUp
- if (d3.event.type === 'mouseup') {
- dispatch.elementMouseUp({
- mouseX: mouseX,
- mouseY: mouseY,
- pointXValue: pointXValue
- });
+ svgContainer
+ .on("touchmove",mouseHandler)
+ .on("mousemove",mouseHandler, true)
+ .on("mouseout" ,mouseHandler,true)
+ .on("mousedown" ,mouseHandler,true)
+ .on("mouseup" ,mouseHandler,true)
+ .on("dblclick" ,mouseHandler)
+ .on("click", mouseHandler)
+ ;
+
+ layer.guideLine = null;
+ //Draws a vertical guideline at the given X postion.
+ layer.renderGuideLine = function(x) {
+ if (!showGuideLine) return;
+ if (layer.guideLine && layer.guideLine.attr("x1") === x) return;
+ nv.dom.write(function() {
+ var line = wrap.select(".nv-interactiveGuideLine")
+ .selectAll("line")
+ .data((x != null) ? [nv.utils.NaNtoZero(x)] : [], String);
+ line.enter()
+ .append("line")
+ .attr("class", "nv-guideline")
+ .attr("x1", function(d) { return d;})
+ .attr("x2", function(d) { return d;})
+ .attr("y1", availableHeight)
+ .attr("y2",0);
+ line.exit().remove();
+ });
}
- }
+ });
+ }
- svgContainer
- .on("touchmove",mouseHandler)
- .on("mousemove",mouseHandler, true)
- .on("mouseout" ,mouseHandler,true)
- .on("mousedown" ,mouseHandler,true)
- .on("mouseup" ,mouseHandler,true)
- .on("dblclick" ,mouseHandler)
- .on("click", mouseHandler)
- ;
+ layer.dispatch = dispatch;
+ layer.tooltip = tooltip;
- layer.guideLine = null;
- //Draws a vertical guideline at the given X postion.
- layer.renderGuideLine = function(x) {
- if (!showGuideLine) return;
- if (layer.guideLine && layer.guideLine.attr("x1") === x) return;
- nv.dom.write(function() {
- var line = wrap.select(".nv-interactiveGuideLine")
- .selectAll("line")
- .data((x != null) ? [nv.utils.NaNtoZero(x)] : [], String);
- line.enter()
- .append("line")
- .attr("class", "nv-guideline")
- .attr("x1", function(d) { return d;})
- .attr("x2", function(d) { return d;})
- .attr("y1", availableHeight)
- .attr("y2",0);
- line.exit().remove();
- });
- }
- });
- }
+ layer.margin = function(_) {
+ if (!arguments.length) return margin;
+ margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
+ margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
+ return layer;
+ };
- layer.dispatch = dispatch;
- layer.tooltip = tooltip;
+ layer.width = function(_) {
+ if (!arguments.length) return width;
+ width = _;
+ return layer;
+ };
- layer.margin = function(_) {
- if (!arguments.length) return margin;
- margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
- margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
- return layer;
- };
+ layer.height = function(_) {
+ if (!arguments.length) return height;
+ height = _;
+ return layer;
+ };
- layer.width = function(_) {
- if (!arguments.length) return width;
- width = _;
- return layer;
- };
+ layer.xScale = function(_) {
+ if (!arguments.length) return xScale;
+ xScale = _;
+ return layer;
+ };
- layer.height = function(_) {
- if (!arguments.length) return height;
- height = _;
- return layer;
- };
+ layer.showGuideLine = function(_) {
+ if (!arguments.length) return showGuideLine;
+ showGuideLine = _;
+ return layer;
+ };
- layer.xScale = function(_) {
- if (!arguments.length) return xScale;
- xScale = _;
- return layer;
- };
+ layer.svgContainer = function(_) {
+ if (!arguments.length) return svgContainer;
+ svgContainer = _;
+ return layer;
+ };
- layer.showGuideLine = function(_) {
- if (!arguments.length) return showGuideLine;
- showGuideLine = _;
return layer;
};
- layer.svgContainer = function(_) {
- if (!arguments.length) return svgContainer;
- svgContainer = _;
- return layer;
- };
+ /* Utility class that uses d3.bisect to find the index in a given array, where a search value can be inserted.
+ This is different from normal bisectLeft; this function finds the nearest index to insert the search value.
- return layer;
-};
+ For instance, lets say your array is [1,2,3,5,10,30], and you search for 28.
+ Normal d3.bisectLeft will return 4, because 28 is inserted after the number 10. But interactiveBisect will return 5
+ because 28 is closer to 30 than 10.
-/* Utility class that uses d3.bisect to find the index in a given array, where a search value can be inserted.
- This is different from normal bisectLeft; this function finds the nearest index to insert the search value.
+ Unit tests can be found in: interactiveBisectTest.html
- For instance, lets say your array is [1,2,3,5,10,30], and you search for 28.
- Normal d3.bisectLeft will return 4, because 28 is inserted after the number 10. But interactiveBisect will return 5
- because 28 is closer to 30 than 10.
+ Has the following known issues:
+ * Will not work if the data points move backwards (ie, 10,9,8,7, etc) or if the data points are in random order.
+ * Won't work if there are duplicate x coordinate values.
+ */
+ nv.interactiveBisect = function (values, searchVal, xAccessor) {
+ "use strict";
+ if (! (values instanceof Array)) {
+ return null;
+ }
+ var _xAccessor;
+ if (typeof xAccessor !== 'function') {
+ _xAccessor = function(d) {
+ return d.x;
+ }
+ } else {
+ _xAccessor = xAccessor;
+ }
+ var _cmp = function(d, v) {
+ // Accessors are no longer passed the index of the element along with
+ // the element itself when invoked by d3.bisector.
+ //
+ // Starting at D3 v3.4.4, d3.bisector() started inspecting the
+ // function passed to determine if it should consider it an accessor
+ // or a comparator. This meant that accessors that take two arguments
+ // (expecting an index as the second parameter) are treated as
+ // comparators where the second argument is the search value against
+ // which the first argument is compared.
+ return _xAccessor(d) - v;
+ };
- Unit tests can be found in: interactiveBisectTest.html
+ var bisect = d3.bisector(_cmp).left;
+ var index = d3.max([0, bisect(values,searchVal) - 1]);
+ var currentValue = _xAccessor(values[index]);
- Has the following known issues:
- * Will not work if the data points move backwards (ie, 10,9,8,7, etc) or if the data points are in random order.
- * Won't work if there are duplicate x coordinate values.
- */
-nv.interactiveBisect = function (values, searchVal, xAccessor) {
- "use strict";
- if (! (values instanceof Array)) {
- return null;
- }
- var _xAccessor;
- if (typeof xAccessor !== 'function') {
- _xAccessor = function(d) {
- return d.x;
+ if (typeof currentValue === 'undefined') {
+ currentValue = index;
}
- } else {
- _xAccessor = xAccessor;
- }
- var _cmp = function(d, v) {
- // Accessors are no longer passed the index of the element along with
- // the element itself when invoked by d3.bisector.
- //
- // Starting at D3 v3.4.4, d3.bisector() started inspecting the
- // function passed to determine if it should consider it an accessor
- // or a comparator. This meant that accessors that take two arguments
- // (expecting an index as the second parameter) are treated as
- // comparators where the second argument is the search value against
- // which the first argument is compared.
- return _xAccessor(d) - v;
- };
- var bisect = d3.bisector(_cmp).left;
- var index = d3.max([0, bisect(values,searchVal) - 1]);
- var currentValue = _xAccessor(values[index]);
+ if (currentValue === searchVal) {
+ return index; //found exact match
+ }
- if (typeof currentValue === 'undefined') {
- currentValue = index;
- }
+ var nextIndex = d3.min([index+1, values.length - 1]);
+ var nextValue = _xAccessor(values[nextIndex]);
- if (currentValue === searchVal) {
- return index; //found exact match
- }
+ if (typeof nextValue === 'undefined') {
+ nextValue = nextIndex;
+ }
- var nextIndex = d3.min([index+1, values.length - 1]);
- var nextValue = _xAccessor(values[nextIndex]);
+ if (Math.abs(nextValue - searchVal) >= Math.abs(currentValue - searchVal)) {
+ return index;
+ } else {
+ return nextIndex
+ }
+ };
- if (typeof nextValue === 'undefined') {
- nextValue = nextIndex;
- }
+ /*
+ Returns the index in the array "values" that is closest to searchVal.
+ Only returns an index if searchVal is within some "threshold".
+ Otherwise, returns null.
+ */
+ nv.nearestValueIndex = function (values, searchVal, threshold) {
+ "use strict";
+ var yDistMax = Infinity, indexToHighlight = null;
+ values.forEach(function(d,i) {
+ var delta = Math.abs(searchVal - d);
+ if ( d != null && delta <= yDistMax && delta < threshold) {
+ yDistMax = delta;
+ indexToHighlight = i;
+ }
+ });
+ return indexToHighlight;
+ };
- if (Math.abs(nextValue - searchVal) >= Math.abs(currentValue - searchVal)) {
- return index;
- } else {
- return nextIndex
- }
-};
+ /* Model which can be instantiated to handle tooltip rendering.
+ Example usage:
+ var tip = nv.models.tooltip().gravity('w').distance(23)
+ .data(myDataObject);
-/*
- Returns the index in the array "values" that is closest to searchVal.
- Only returns an index if searchVal is within some "threshold".
- Otherwise, returns null.
- */
-nv.nearestValueIndex = function (values, searchVal, threshold) {
- "use strict";
- var yDistMax = Infinity, indexToHighlight = null;
- values.forEach(function(d,i) {
- var delta = Math.abs(searchVal - d);
- if ( d != null && delta <= yDistMax && delta < threshold) {
- yDistMax = delta;
- indexToHighlight = i;
- }
- });
- return indexToHighlight;
-};
+ tip(); //just invoke the returned function to render tooltip.
+ */
+ nv.models.tooltip = function() {
+ "use strict";
-/* Model which can be instantiated to handle tooltip rendering.
- Example usage:
- var tip = nv.models.tooltip().gravity('w').distance(23)
- .data(myDataObject);
+ /*
+ Tooltip data. If data is given in the proper format, a consistent tooltip is generated.
+ Example Format of data:
+ {
+ key: "Date",
+ value: "August 2009",
+ series: [
+ {key: "Series 1", value: "Value 1", color: "#000"},
+ {key: "Series 2", value: "Value 2", color: "#00f"}
+ ]
+ }
+ */
+ var id = "nvtooltip-" + Math.floor(Math.random() * 100000) // Generates a unique id when you create a new tooltip() object.
+ , data = null
+ , gravity = 'w' // Can be 'n','s','e','w'. Determines how tooltip is positioned.
+ , distance = 25 // Distance to offset tooltip from the mouse location.
+ , snapDistance = 0 // Tolerance allowed before tooltip is moved from its current position (creates 'snapping' effect)
+ , classes = null // Attaches additional CSS classes to the tooltip DIV that is created.
+ , hidden = true // Start off hidden, toggle with hide/show functions below.
+ , hideDelay = 200 // Delay (in ms) before the tooltip hides after calling hide().
+ , tooltip = null // d3 select of the tooltip div.
+ , lastPosition = { left: null, top: null } // Last position the tooltip was in.
+ , enabled = true // True -> tooltips are rendered. False -> don't render tooltips.
+ , duration = 100 // Tooltip movement duration, in ms.
+ , headerEnabled = true // If is to show the tooltip header.
+ , nvPointerEventsClass = "nv-pointer-events-none" // CSS class to specify whether element should not have mouse events.
+ ;
- tip(); //just invoke the returned function to render tooltip.
- */
-nv.models.tooltip = function() {
- "use strict";
+ // Format function for the tooltip values column.
+ var valueFormatter = function(d, i) {
+ return d;
+ };
- /*
- Tooltip data. If data is given in the proper format, a consistent tooltip is generated.
- Example Format of data:
- {
- key: "Date",
- value: "August 2009",
- series: [
- {key: "Series 1", value: "Value 1", color: "#000"},
- {key: "Series 2", value: "Value 2", color: "#00f"}
- ]
- }
- */
- var id = "nvtooltip-" + Math.floor(Math.random() * 100000) // Generates a unique id when you create a new tooltip() object.
- , data = null
- , gravity = 'w' // Can be 'n','s','e','w'. Determines how tooltip is positioned.
- , distance = 25 // Distance to offset tooltip from the mouse location.
- , snapDistance = 0 // Tolerance allowed before tooltip is moved from its current position (creates 'snapping' effect)
- , classes = null // Attaches additional CSS classes to the tooltip DIV that is created.
- , hidden = true // Start off hidden, toggle with hide/show functions below.
- , hideDelay = 200 // Delay (in ms) before the tooltip hides after calling hide().
- , tooltip = null // d3 select of the tooltip div.
- , lastPosition = { left: null, top: null } // Last position the tooltip was in.
- , enabled = true // True -> tooltips are rendered. False -> don't render tooltips.
- , duration = 100 // Tooltip movement duration, in ms.
- , headerEnabled = true // If is to show the tooltip header.
- , nvPointerEventsClass = "nv-pointer-events-none" // CSS class to specify whether element should not have mouse events.
- ;
+ // Format function for the tooltip header value.
+ var headerFormatter = function(d) {
+ return d;
+ };
- // Format function for the tooltip values column.
- var valueFormatter = function(d, i) {
- return d;
- };
+ var keyFormatter = function(d, i) {
+ return d;
+ };
- // Format function for the tooltip header value.
- var headerFormatter = function(d) {
- return d;
- };
+ // By default, the tooltip model renders a beautiful table inside a DIV.
+ // You can override this function if a custom tooltip is desired.
+ var contentGenerator = function(d) {
+ if (d === null) {
+ return '';
+ }
- var keyFormatter = function(d, i) {
- return d;
- };
+ var table = d3.select(document.createElement("table"));
+ if (headerEnabled) {
+ var theadEnter = table.selectAll("thead")
+ .data([d])
+ .enter().append("thead");
- // By default, the tooltip model renders a beautiful table inside a DIV.
- // You can override this function if a custom tooltip is desired.
- var contentGenerator = function(d) {
- if (d === null) {
- return '';
- }
+ theadEnter.append("tr")
+ .append("td")
+ .attr("colspan", 3)
+ .append("strong")
+ .classed("x-value", true)
+ .html(headerFormatter(d.value));
+ }
- var table = d3.select(document.createElement("table"));
- if (headerEnabled) {
- var theadEnter = table.selectAll("thead")
+ var tbodyEnter = table.selectAll("tbody")
.data([d])
- .enter().append("thead");
+ .enter().append("tbody");
- theadEnter.append("tr")
- .append("td")
- .attr("colspan", 3)
- .append("strong")
- .classed("x-value", true)
- .html(headerFormatter(d.value));
- }
-
- var tbodyEnter = table.selectAll("tbody")
- .data([d])
- .enter().append("tbody");
-
- var trowEnter = tbodyEnter.selectAll("tr")
+ var trowEnter = tbodyEnter.selectAll("tr")
.data(function(p) { return p.series})
.enter()
.append("tr")
.classed("highlight", function(p) { return p.highlight});
- trowEnter.append("td")
- .classed("legend-color-guide",true)
- .append("div")
- .style("background-color", function(p) { return p.color});
+ trowEnter.append("td")
+ .classed("legend-color-guide",true)
+ .append("div")
+ .style("background-color", function(p) { return p.color});
- trowEnter.append("td")
- .classed("key",true)
- .classed("total",function(p) { return !!p.total})
- .html(function(p, i) { return keyFormatter(p.key, i)});
+ trowEnter.append("td")
+ .classed("key",true)
+ .classed("total",function(p) { return !!p.total})
+ .html(function(p, i) { return keyFormatter(p.key, i)});
- trowEnter.append("td")
- .classed("value",true)
- .html(function(p, i) { return valueFormatter(p.value, i) });
+ trowEnter.append("td")
+ .classed("value",true)
+ .html(function(p, i) { return valueFormatter(p.value, i) });
- trowEnter.filter(function (p,i) { return p.percent !== undefined }).append("td")
- .classed("percent", true)
- .html(function(p, i) { return "(" + d3.format('%')(p.percent) + ")" });
+ trowEnter.filter(function (p,i) { return p.percent !== undefined }).append("td")
+ .classed("percent", true)
+ .html(function(p, i) { return "(" + d3.format('%')(p.percent) + ")" });
- trowEnter.selectAll("td").each(function(p) {
- if (p.highlight) {
- var opacityScale = d3.scale.linear().domain([0,1]).range(["#fff",p.color]);
- var opacity = 0.6;
- d3.select(this)
- .style("border-bottom-color", opacityScale(opacity))
- .style("border-top-color", opacityScale(opacity))
- ;
- }
- });
+ trowEnter.selectAll("td").each(function(p) {
+ if (p.highlight) {
+ var opacityScale = d3.scale.linear().domain([0,1]).range(["#fff",p.color]);
+ var opacity = 0.6;
+ d3.select(this)
+ .style("border-bottom-color", opacityScale(opacity))
+ .style("border-top-color", opacityScale(opacity))
+ ;
+ }
+ });
- var html = table.node().outerHTML;
- if (d.footer !== undefined)
- html += "<div class='footer'>" + d.footer + "</div>";
- return html;
+ var html = table.node().outerHTML;
+ if (d.footer !== undefined)
+ html += "<div class='footer'>" + d.footer + "</div>";
+ return html;
- };
-
- /*
- Function that returns the position (relative to the viewport/document.body)
- the tooltip should be placed in.
- Should return: {
- left: <leftPos>,
- top: <topPos>
- }
- */
- var position = function() {
- var pos = {
- left: d3.event !== null ? d3.event.clientX : 0,
- top: d3.event !== null ? d3.event.clientY : 0
};
- if(getComputedStyle(document.body).transform != 'none') {
- // Take the offset into account, as now the tooltip is relative
- // to document.body.
- var client = document.body.getBoundingClientRect();
- pos.left -= client.left;
- pos.top -= client.top;
- }
+ /*
+ Function that returns the position (relative to the viewport/document.body)
+ the tooltip should be placed in.
+ Should return: {
+ left: <leftPos>,
+ top: <topPos>
+ }
+ */
+ var position = function() {
+ var pos = {
+ left: d3.event !== null ? d3.event.clientX : 0,
+ top: d3.event !== null ? d3.event.clientY : 0
+ };
- return pos;
- };
+ if(getComputedStyle(document.body).transform != 'none') {
+ // Take the offset into account, as now the tooltip is relative
+ // to document.body.
+ var client = document.body.getBoundingClientRect();
+ pos.left -= client.left;
+ pos.top -= client.top;
+ }
- var dataSeriesExists = function(d) {
- if (d && d.series) {
- if (nv.utils.isArray(d.series)) {
- return true;
+ return pos;
+ };
+
+ var dataSeriesExists = function(d) {
+ if (d && d.series) {
+ if (nv.utils.isArray(d.series)) {
+ return true;
+ }
+ // if object, it's okay just convert to array of the object
+ if (nv.utils.isObject(d.series)) {
+ d.series = [d.series];
+ return true;
+ }
}
- // if object, it's okay just convert to array of the object
- if (nv.utils.isObject(d.series)) {
- d.series = [d.series];
- return true;
- }
- }
- return false;
- };
+ return false;
+ };
- // Calculates the gravity offset of the tooltip. Parameter is position of tooltip
- // relative to the viewport.
- var calcGravityOffset = function(pos) {
- var height = tooltip.node().offsetHeight,
- width = tooltip.node().offsetWidth,
- clientWidth = document.documentElement.clientWidth, // Don't want scrollbars.
- clientHeight = document.documentElement.clientHeight, // Don't want scrollbars.
- left, top, tmp;
+ // Calculates the gravity offset of the tooltip. Parameter is position of tooltip
+ // relative to the viewport.
+ var calcGravityOffset = function(pos) {
+ var height = tooltip.node().offsetHeight,
+ width = tooltip.node().offsetWidth,
+ clientWidth = document.documentElement.clientWidth, // Don't want scrollbars.
+ clientHeight = document.documentElement.clientHeight, // Don't want scrollbars.
+ left, top, tmp;
- // calculate position based on gravity
- switch (gravity) {
- case 'e':
- left = - width - distance;
- top = - (height / 2);
- if(pos.left + left < 0) left = distance;
- if((tmp = pos.top + top) < 0) top -= tmp;
- if((tmp = pos.top + top + height) > clientHeight) top -= tmp - clientHeight;
- break;
- case 'w':
- left = distance;
- top = - (height / 2);
- if (pos.left + left + width > clientWidth) left = - width - distance;
- if ((tmp = pos.top + top) < 0) top -= tmp;
- if ((tmp = pos.top + top + height) > clientHeight) top -= tmp - clientHeight;
- break;
- case 'n':
- left = - (width / 2) - 5; // - 5 is an approximation of the mouse's height.
- top = distance;
- if (pos.top + top + height > clientHeight) top = - height - distance;
- if ((tmp = pos.left + left) < 0) left -= tmp;
- if ((tmp = pos.left + left + width) > clientWidth) left -= tmp - clientWidth;
- break;
- case 's':
- left = - (width / 2);
- top = - height - distance;
- if (pos.top + top < 0) top = distance;
- if ((tmp = pos.left + left) < 0) left -= tmp;
- if ((tmp = pos.left + left + width) > clientWidth) left -= tmp - clientWidth;
- break;
- case 'center':
- left = - (width / 2);
- top = - (height / 2);
- break;
- default:
- left = 0;
- top = 0;
- break;
- }
+ // calculate position based on gravity
+ switch (gravity) {
+ case 'e':
+ left = - width - distance;
+ top = - (height / 2);
+ if(pos.left + left < 0) left = distance;
+ if((tmp = pos.top + top) < 0) top -= tmp;
+ if((tmp = pos.top + top + height) > clientHeight) top -= tmp - clientHeight;
+ break;
+ case 'w':
+ left = distance;
+ top = - (height / 2);
+ if (pos.left + left + width > clientWidth) left = - width - distance;
+ if ((tmp = pos.top + top) < 0) top -= tmp;
+ if ((tmp = pos.top + top + height) > clientHeight) top -= tmp - clientHeight;
+ break;
+ case 'n':
+ left = - (width / 2) - 5; // - 5 is an approximation of the mouse's height.
+ top = distance;
+ if (pos.top + top + height > clientHeight) top = - height - distance;
+ if ((tmp = pos.left + left) < 0) left -= tmp;
+ if ((tmp = pos.left + left + width) > clientWidth) left -= tmp - clientWidth;
+ break;
+ case 's':
+ left = - (width / 2);
+ top = - height - distance;
+ if (pos.top + top < 0) top = distance;
+ if ((tmp = pos.left + left) < 0) left -= tmp;
+ if ((tmp = pos.left + left + width) > clientWidth) left -= tmp - clientWidth;
+ break;
+ case 'center':
+ left = - (width / 2);
+ top = - (height / 2);
+ break;
+ default:
+ left = 0;
+ top = 0;
+ break;
+ }
- return { 'left': left, 'top': top };
- };
+ return { 'left': left, 'top': top };
+ };
- /*
- Positions the tooltip in the correct place, as given by the position() function.
- */
- var positionTooltip = function() {
- nv.dom.read(function() {
- var pos = position(),
- gravityOffset = calcGravityOffset(pos),
- left = pos.left + gravityOffset.left,
- top = pos.top + gravityOffset.top;
+ /*
+ Positions the tooltip in the correct place, as given by the position() function.
+ */
+ var positionTooltip = function() {
+ nv.dom.read(function() {
+ var pos = position(),
+ gravityOffset = calcGravityOffset(pos),
+ left = pos.left + gravityOffset.left,
+ top = pos.top + gravityOffset.top;
- // delay hiding a bit to avoid flickering
- if (hidden) {
- tooltip
- .interrupt()
- .transition()
- .delay(hideDelay)
- .duration(0)
- .style('opacity', 0);
- } else {
- // using tooltip.style('transform') returns values un-usable for tween
- var old_translate = 'translate(' + lastPosition.left + 'px, ' + lastPosition.top + 'px)';
- var new_translate = 'translate(' + Math.round(left) + 'px, ' + Math.round(top) + 'px)';
- var translateInterpolator = d3.interpolateString(old_translate, new_translate);
- var is_hidden = tooltip.style('opacity') < 0.1;
+ // delay hiding a bit to avoid flickering
+ if (hidden) {
+ tooltip
+ .interrupt()
+ .transition()
+ .delay(hideDelay)
+ .duration(0)
+ .style('opacity', 0);
+ } else {
+ // using tooltip.style('transform') returns values un-usable for tween
+ var old_translate = 'translate(' + lastPosition.left + 'px, ' + lastPosition.top + 'px)';
+ var new_translate = 'translate(' + Math.round(left) + 'px, ' + Math.round(top) + 'px)';
+ var translateInterpolator = d3.interpolateString(old_translate, new_translate);
+ var is_hidden = tooltip.style('opacity') < 0.1;
- tooltip
- .interrupt() // cancel running transitions
- .transition()
- .duration(is_hidden ? 0 : duration)
- // using tween since some versions of d3 can't auto-tween a translate on a div
- .styleTween('transform', function (d) {
- return translateInterpolator;
- }, 'important')
- // Safari has its own `-webkit-transform` and does not support `transform`
- .styleTween('-webkit-transform', function (d) {
- return translateInterpolator;
- })
- .style('-ms-transform', new_translate)
- .style('opacity', 1);
- }
+ tooltip
+ .interrupt() // cancel running transitions
+ .transition()
+ .duration(is_hidden ? 0 : duration)
+ // using tween since some versions of d3 can't auto-tween a translate on a div
+ .styleTween('transform', function (d) {
+ return translateInterpolator;
+ }, 'important')
+ // Safari has its own `-webkit-transform` and does not support `transform`
+ .styleTween('-webkit-transform', function (d) {
+ return translateInterpolator;
+ })
+ .style('-ms-transform', new_translate)
+ .style('opacity', 1);
+ }
- lastPosition.left = left;
- lastPosition.top = top;
- });
- };
+ lastPosition.left = left;
+ lastPosition.top = top;
+ });
+ };
- // Creates new tooltip container, or uses existing one on DOM.
- function initTooltip() {
- if (!tooltip || !tooltip.node()) {
- // Create new tooltip div if it doesn't exist on DOM.
+ // Creates new tooltip container, or uses existing one on DOM.
+ function initTooltip() {
+ if (!tooltip || !tooltip.node()) {
+ // Create new tooltip div if it doesn't exist on DOM.
- var data = [1];
- tooltip = d3.select(document.body).selectAll('.nvtooltip').data(data);
+ var data = [1];
+ tooltip = d3.select(document.body).select('#'+id).data(data);
- tooltip.enter().append('div')
- .attr("class", "nvtooltip " + (classes ? classes : "xy-tooltip"))
- .attr("id", id)
- .style("top", 0).style("left", 0)
- .style('opacity', 0)
- .style('position', 'fixed')
- .selectAll("div, table, td, tr").classed(nvPointerEventsClass, true)
- .classed(nvPointerEventsClass, true);
+ tooltip.enter().append('div')
+ .attr("class", "nvtooltip " + (classes ? classes : "xy-tooltip"))
+ .attr("id", id)
+ .style("top", 0).style("left", 0)
+ .style('opacity', 0)
+ .style('position', 'fixed')
+ .selectAll("div, table, td, tr").classed(nvPointerEventsClass, true)
+ .classed(nvPointerEventsClass, true);
- tooltip.exit().remove()
+ tooltip.exit().remove()
+ }
}
- }
- // Draw the tooltip onto the DOM.
- function nvtooltip() {
- if (!enabled) return;
- if (!dataSeriesExists(data)) return;
+ // Draw the tooltip onto the DOM.
+ function nvtooltip() {
+ if (!enabled) return;
+ if (!dataSeriesExists(data)) return;
- nv.dom.write(function () {
- initTooltip();
- // Generate data and set it into tooltip.
- // Bonus - If you override contentGenerator and return falsey you can use something like
- // React or Knockout to bind the data for your tooltip.
- var newContent = contentGenerator(data);
- if (newContent) {
- tooltip.node().innerHTML = newContent;
- }
+ nv.dom.write(function () {
+ initTooltip();
+ // Generate data and set it into tooltip.
+ // Bonus - If you override contentGenerator and return falsey you can use something like
+ // React or Knockout to bind the data for your tooltip.
+ var newContent = contentGenerator(data);
+ if (newContent) {
+ tooltip.node().innerHTML = newContent;
+ }
- positionTooltip();
- });
+ positionTooltip();
+ });
- return nvtooltip;
- }
+ return nvtooltip;
+ }
- nvtooltip.nvPointerEventsClass = nvPointerEventsClass;
- nvtooltip.options = nv.utils.optionsFunc.bind(nvtooltip);
+ nvtooltip.nvPointerEventsClass = nvPointerEventsClass;
+ nvtooltip.options = nv.utils.optionsFunc.bind(nvtooltip);
- nvtooltip._options = Object.create({}, {
- // simple read/write options
- duration: {get: function(){return duration;}, set: function(_){duration=_;}},
- gravity: {get: function(){return gravity;}, set: function(_){gravity=_;}},
- distance: {get: function(){return distance;}, set: function(_){distance=_;}},
- snapDistance: {get: function(){return snapDistance;}, set: function(_){snapDistance=_;}},
- classes: {get: function(){return classes;}, set: function(_){classes=_;}},
- enabled: {get: function(){return enabled;}, set: function(_){enabled=_;}},
- hideDelay: {get: function(){return hideDelay;}, set: function(_){hideDelay=_;}},
- contentGenerator: {get: function(){return contentGenerator;}, set: function(_){contentGenerator=_;}},
- valueFormatter: {get: function(){return valueFormatter;}, set: function(_){valueFormatter=_;}},
- headerFormatter: {get: function(){return headerFormatter;}, set: function(_){headerFormatter=_;}},
- keyFormatter: {get: function(){return keyFormatter;}, set: function(_){keyFormatter=_;}},
- headerEnabled: {get: function(){return headerEnabled;}, set: function(_){headerEnabled=_;}},
- position: {get: function(){return position;}, set: function(_){position=_;}},
+ nvtooltip._options = Object.create({}, {
+ // simple read/write options
+ duration: {get: function(){return duration;}, set: function(_){duration=_;}},
+ gravity: {get: function(){return gravity;}, set: function(_){gravity=_;}},
+ distance: {get: function(){return distance;}, set: function(_){distance=_;}},
+ snapDistance: {get: function(){return snapDistance;}, set: function(_){snapDistance=_;}},
+ classes: {get: function(){return classes;}, set: function(_){classes=_;}},
+ enabled: {get: function(){return enabled;}, set: function(_){enabled=_;}},
+ hideDelay: {get: function(){return hideDelay;}, set: function(_){hideDelay=_;}},
+ contentGenerator: {get: function(){return contentGenerator;}, set: function(_){contentGenerator=_;}},
+ valueFormatter: {get: function(){return valueFormatter;}, set: function(_){valueFormatter=_;}},
+ headerFormatter: {get: function(){return headerFormatter;}, set: function(_){headerFormatter=_;}},
+ keyFormatter: {get: function(){return keyFormatter;}, set: function(_){keyFormatter=_;}},
+ headerEnabled: {get: function(){return headerEnabled;}, set: function(_){headerEnabled=_;}},
+ position: {get: function(){return position;}, set: function(_){position=_;}},
- // Deprecated options
- chartContainer: {get: function(){return document.body;}, set: function(_){
- // deprecated after 1.8.3
- nv.deprecated('chartContainer', 'feature removed after 1.8.3');
- }},
- fixedTop: {get: function(){return null;}, set: function(_){
- // deprecated after 1.8.1
- nv.deprecated('fixedTop', 'feature removed after 1.8.1');
- }},
- offset: {get: function(){return {left: 0, top: 0};}, set: function(_){
- // deprecated after 1.8.1
- nv.deprecated('offset', 'use chart.tooltip.distance() instead');
- }},
+ // Deprecated options
+ chartContainer: {get: function(){return document.body;}, set: function(_){
+ // deprecated after 1.8.3
+ nv.deprecated('chartContainer', 'feature removed after 1.8.3');
+ }},
+ fixedTop: {get: function(){return null;}, set: function(_){
+ // deprecated after 1.8.1
+ nv.deprecated('fixedTop', 'feature removed after 1.8.1');
+ }},
+ offset: {get: function(){return {left: 0, top: 0};}, set: function(_){
+ // deprecated after 1.8.1
+ nv.deprecated('offset', 'use chart.tooltip.distance() instead');
+ }},
- // options with extra logic
- hidden: {get: function(){return hidden;}, set: function(_){
- if (hidden != _) {
- hidden = !!_;
- nvtooltip();
- }
- }},
- data: {get: function(){return data;}, set: function(_){
- // if showing a single data point, adjust data format with that
- if (_.point) {
- _.value = _.point.x;
- _.series = _.series || {};
- _.series.value = _.point.y;
- _.series.color = _.point.color || _.series.color;
- }
- data = _;
- }},
+ // options with extra logic
+ hidden: {get: function(){return hidden;}, set: function(_){
+ if (hidden != _) {
+ hidden = !!_;
+ nvtooltip();
+ }
+ }},
+ data: {get: function(){return data;}, set: function(_){
+ // if showing a single data point, adjust data format with that
+ if (_.point) {
+ _.value = _.point.x;
+ _.series = _.series || {};
+ _.series.value = _.point.y;
+ _.series.color = _.point.color || _.series.color;
+ }
+ data = _;
+ }},
- // read only properties
- node: {get: function(){return tooltip.node();}, set: function(_){}},
- id: {get: function(){return id;}, set: function(_){}}
- });
+ // read only properties
+ node: {get: function(){return tooltip.node();}, set: function(_){}},
+ id: {get: function(){return id;}, set: function(_){}}
+ });
- nv.utils.initOptions(nvtooltip);
- return nvtooltip;
-};
+ nv.utils.initOptions(nvtooltip);
+ return nvtooltip;
+ };
-/*
-Gets the browser window size
+ /*
+ Gets the browser window size
-Returns object with height and width properties
- */
-nv.utils.windowSize = function() {
- // Sane defaults
- var size = {width: 640, height: 480};
+ Returns object with height and width properties
+ */
+ nv.utils.windowSize = function() {
+ // Sane defaults
+ var size = {width: 640, height: 480};
- // Most recent browsers use
- if (window.innerWidth && window.innerHeight) {
- size.width = window.innerWidth;
- size.height = window.innerHeight;
- return (size);
- }
+ // Most recent browsers use
+ if (window.innerWidth && window.innerHeight) {
+ size.width = window.innerWidth;
+ size.height = window.innerHeight;
+ return (size);
+ }
- // IE can use depending on mode it is in
- if (document.compatMode=='CSS1Compat' &&
- document.documentElement &&
- document.documentElement.offsetWidth ) {
+ // IE can use depending on mode it is in
+ if (document.compatMode=='CSS1Compat' &&
+ document.documentElement &&
+ document.documentElement.offsetWidth ) {
- size.width = document.documentElement.offsetWidth;
- size.height = document.documentElement.offsetHeight;
- return (size);
- }
+ size.width = document.documentElement.offsetWidth;
+ size.height = document.documentElement.offsetHeight;
+ return (size);
+ }
- // Earlier IE uses Doc.body
- if (document.body && document.body.offsetWidth) {
- size.width = document.body.offsetWidth;
- size.height = document.body.offsetHeight;
+ // Earlier IE uses Doc.body
+ if (document.body && document.body.offsetWidth) {
+ size.width = document.body.offsetWidth;
+ size.height = document.body.offsetHeight;
+ return (size);
+ }
+
return (size);
- }
+ };
- return (size);
-};
+ /* handle dumb browser quirks... isinstance breaks if you use frames
+ typeof returns 'object' for null, NaN is a number, etc.
+ */
+ nv.utils.isArray = Array.isArray;
+ nv.utils.isObject = function(a) {
+ return a !== null && typeof a === 'object';
+ };
+ nv.utils.isFunction = function(a) {
+ return typeof a === 'function';
+ };
+ nv.utils.isDate = function(a) {
+ return toString.call(a) === '[object Date]';
+ };
+ nv.utils.isNumber = function(a) {
+ return !isNaN(a) && typeof a === 'number';
+ };
-/* handle dumb browser quirks... isinstance breaks if you use frames
-typeof returns 'object' for null, NaN is a number, etc.
- */
-nv.utils.isArray = Array.isArray;
-nv.utils.isObject = function(a) {
- return a !== null && typeof a === 'object';
-};
-nv.utils.isFunction = function(a) {
- return typeof a === 'function';
-};
-nv.utils.isDate = function(a) {
- return toString.call(a) === '[object Date]';
-};
-nv.utils.isNumber = function(a) {
- return !isNaN(a) && typeof a === 'number';
-};
-
-/*
-Binds callback function to run when window is resized
- */
-nv.utils.windowResize = function(handler) {
- if (window.addEventListener) {
- window.addEventListener('resize', handler);
- } else {
- nv.log("ERROR: Failed to bind to window.resize with: ", handler);
- }
- // return object with clear function to remove the single added callback.
- return {
- callback: handler,
- clear: function() {
- window.removeEventListener('resize', handler);
+ /*
+ Binds callback function to run when window is resized
+ */
+ nv.utils.windowResize = function(handler) {
+ if (window.addEventListener) {
+ window.addEventListener('resize', handler);
+ } else {
+ nv.log("ERROR: Failed to bind to window.resize with: ", handler);
}
- }
-};
+ // return object with clear function to remove the single added callback.
+ return {
+ callback: handler,
+ clear: function() {
+ window.removeEventListener('resize', handler);
+ }
+ }
+ };
-/*
-Backwards compatible way to implement more d3-like coloring of graphs.
-Can take in nothing, an array, or a function/scale
-To use a normal scale, get the range and pass that because we must be able
-to take two arguments and use the index to keep backward compatibility
-*/
-nv.utils.getColor = function(color) {
- //if you pass in nothing, get default colors back
- if (color === undefined) {
- return nv.utils.defaultColor();
+ /*
+ Backwards compatible way to implement more d3-like coloring of graphs.
+ Can take in nothing, an array, or a function/scale
+ To use a normal scale, get the range and pass that because we must be able
+ to take two arguments and use the index to keep backward compatibility
+ */
+ nv.utils.getColor = function(color) {
+ //if you pass in nothing, get default colors back
+ if (color === undefined) {
+ return nv.utils.defaultColor();
- //if passed an array, turn it into a color scale
- } else if(nv.utils.isArray(color)) {
- var color_scale = d3.scale.ordinal().range(color);
- return function(d, i) {
- var key = i === undefined ? d : i;
- return d.color || color_scale(key);
- };
+ //if passed an array, turn it into a color scale
+ } else if(nv.utils.isArray(color)) {
+ var color_scale = d3.scale.ordinal().range(color);
+ return function(d, i) {
+ var key = i === undefined ? d : i;
+ return d.color || color_scale(key);
+ };
- //if passed a function or scale, return it, or whatever it may be
- //external libs, such as angularjs-nvd3-directives use this
- } else {
- //can't really help it if someone passes rubbish as color
- return color;
- }
-};
+ //if passed a function or scale, return it, or whatever it may be
+ //external libs, such as angularjs-nvd3-directives use this
+ } else {
+ //can't really help it if someone passes rubbish as color
+ return color;
+ }
+ };
-/*
-Default color chooser uses a color scale of 20 colors from D3
- https://github.com/mbostock/d3/wiki/Ordinal-Scales#categorical-colors
- */
-nv.utils.defaultColor = function() {
- // get range of the scale so we'll turn it into our own function.
- return nv.utils.getColor(d3.scale.category20().range());
-};
+ /*
+ Default color chooser uses a color scale of 20 colors from D3
+ https://github.com/mbostock/d3/wiki/Ordinal-Scales#categorical-colors
+ */
+ nv.utils.defaultColor = function() {
+ // get range of the scale so we'll turn it into our own function.
+ return nv.utils.getColor(d3.scale.category20().range());
+ };
-/*
-Returns a color function that takes the result of 'getKey' for each series and
-looks for a corresponding color from the dictionary
-*/
-nv.utils.customTheme = function(dictionary, getKey, defaultColors) {
- // use default series.key if getKey is undefined
- getKey = getKey || function(series) { return series.key };
- defaultColors = defaultColors || d3.scale.category20().range();
+ /*
+ Returns a color function that takes the result of 'getKey' for each series and
+ looks for a corresponding color from the dictionary
+ */
+ nv.utils.customTheme = function(dictionary, getKey, defaultColors) {
+ // use default series.key if getKey is undefined
+ getKey = getKey || function(series) { return series.key };
+ defaultColors = defaultColors || d3.scale.category20().range();
- // start at end of default color list and walk back to index 0
- var defIndex = defaultColors.length;
+ // start at end of default color list and walk back to index 0
+ var defIndex = defaultColors.length;
- return function(series, index) {
- var key = getKey(series);
- if (nv.utils.isFunction(dictionary[key])) {
- return dictionary[key]();
- } else if (dictionary[key] !== undefined) {
- return dictionary[key];
- } else {
- // no match in dictionary, use a default color
- if (!defIndex) {
- // used all the default colors, start over
- defIndex = defaultColors.length;
+ return function(series, index) {
+ var key = getKey(series);
+ if (nv.utils.isFunction(dictionary[key])) {
+ return dictionary[key]();
+ } else if (dictionary[key] !== undefined) {
+ return dictionary[key];
+ } else {
+ // no match in dictionary, use a default color
+ if (!defIndex) {
+ // used all the default colors, start over
+ defIndex = defaultColors.length;
+ }
+ defIndex = defIndex - 1;
+ return defaultColors[defIndex];
}
- defIndex = defIndex - 1;
- return defaultColors[defIndex];
- }
+ };
};
-};
-/*
-From the PJAX example on d3js.org, while this is not really directly needed
-it's a very cool method for doing pjax, I may expand upon it a little bit,
-open to suggestions on anything that may be useful
-*/
-nv.utils.pjax = function(links, content) {
+ /*
+ From the PJAX example on d3js.org, while this is not really directly needed
+ it's a very cool method for doing pjax, I may expand upon it a little bit,
+ open to suggestions on anything that may be useful
+ */
+ nv.utils.pjax = function(links, content) {
- var load = function(href) {
- d3.html(href, function(fragment) {
- var target = d3.select(content).node();
- target.parentNode.replaceChild(
- d3.select(fragment).select(content).node(),
- target);
- nv.utils.pjax(links, content);
+ var load = function(href) {
+ d3.html(href, function(fragment) {
+ var target = d3.select(content).node();
+ target.parentNode.replaceChild(
+ d3.select(fragment).select(content).node(),
+ target);
+ nv.utils.pjax(links, content);
+ });
+ };
+
+ d3.selectAll(links).on("click", function() {
+ history.pushState(this.href, this.textContent, this.href);
+ load(this.href);
+ d3.event.preventDefault();
});
+
+ d3.select(window).on("popstate", function() {
+ if (d3.event.state) {
+ load(d3.event.state);
+ }
+ });
};
- d3.selectAll(links).on("click", function() {
- history.pushState(this.href, this.textContent, this.href);
- load(this.href);
- d3.event.preventDefault();
- });
- d3.select(window).on("popstate", function() {
- if (d3.event.state) {
- load(d3.event.state);
+ /*
+ For when we want to approximate the width in pixels for an SVG:text element.
+ Most common instance is when the element is in a display:none; container.
+ Forumla is : text.length * font-size * constant_factor
+ */
+ nv.utils.calcApproxTextWidth = function (svgTextElem) {
+ if (nv.utils.isFunction(svgTextElem.style) && nv.utils.isFunction(svgTextElem.text)) {
+ var fontSize = parseInt(svgTextElem.style("font-size").replace("px",""), 10);
+ var textLength = svgTextElem.text().length;
+ return nv.utils.NaNtoZero(textLength * fontSize * 0.5);
}
- });
-};
+ return 0;
+ };
-/*
-For when we want to approximate the width in pixels for an SVG:text element.
-Most common instance is when the element is in a display:none; container.
-Forumla is : text.length * font-size * constant_factor
-*/
-nv.utils.calcApproxTextWidth = function (svgTextElem) {
- if (nv.utils.isFunction(svgTextElem.style) && nv.utils.isFunction(svgTextElem.text)) {
- var fontSize = parseInt(svgTextElem.style("font-size").replace("px",""), 10);
- var textLength = svgTextElem.text().length;
- return nv.utils.NaNtoZero(textLength * fontSize * 0.5);
- }
- return 0;
-};
+ /*
+ Numbers that are undefined, null or NaN, convert them to zeros.
+ */
+ nv.utils.NaNtoZero = function(n) {
+ if (!nv.utils.isNumber(n)
+ || isNaN(n)
+ || n === null
+ || n === Infinity
+ || n === -Infinity) {
+ return 0;
+ }
+ return n;
+ };
-/*
-Numbers that are undefined, null or NaN, convert them to zeros.
-*/
-nv.utils.NaNtoZero = function(n) {
- if (!nv.utils.isNumber(n)
- || isNaN(n)
- || n === null
- || n === Infinity
- || n === -Infinity) {
+ /*
+ Add a way to watch for d3 transition ends to d3
+ */
+ d3.selection.prototype.watchTransition = function(renderWatch){
+ var args = [this].concat([].slice.call(arguments, 1));
+ return renderWatch.transition.apply(renderWatch, args);
+ };
- return 0;
- }
- return n;
-};
-/*
-Add a way to watch for d3 transition ends to d3
-*/
-d3.selection.prototype.watchTransition = function(renderWatch){
- var args = [this].concat([].slice.call(arguments, 1));
- return renderWatch.transition.apply(renderWatch, args);
-};
+ /*
+ Helper object to watch when d3 has rendered something
+ */
+ nv.utils.renderWatch = function(dispatch, duration) {
+ if (!(this instanceof nv.utils.renderWatch)) {
+ return new nv.utils.renderWatch(dispatch, duration);
+ }
+ var _duration = duration !== undefined ? duration : 250;
+ var renderStack = [];
+ var self = this;
-/*
-Helper object to watch when d3 has rendered something
-*/
-nv.utils.renderWatch = function(dispatch, duration) {
- if (!(this instanceof nv.utils.renderWatch)) {
- return new nv.utils.renderWatch(dispatch, duration);
- }
+ this.models = function(models) {
+ models = [].slice.call(arguments, 0);
+ models.forEach(function(model){
+ model.__rendered = false;
+ (function(m){
+ m.dispatch.on('renderEnd', function(arg){
+ m.__rendered = true;
+ self.renderEnd('model');
+ });
+ })(model);
- var _duration = duration !== undefined ? duration : 250;
- var renderStack = [];
- var self = this;
+ if (renderStack.indexOf(model) < 0) {
+ renderStack.push(model);
+ }
+ });
+ return this;
+ };
- this.models = function(models) {
- models = [].slice.call(arguments, 0);
- models.forEach(function(model){
- model.__rendered = false;
- (function(m){
- m.dispatch.on('renderEnd', function(arg){
- m.__rendered = true;
- self.renderEnd('model');
- });
- })(model);
-
- if (renderStack.indexOf(model) < 0) {
- renderStack.push(model);
+ this.reset = function(duration) {
+ if (duration !== undefined) {
+ _duration = duration;
}
- });
- return this;
- };
+ renderStack = [];
+ };
- this.reset = function(duration) {
- if (duration !== undefined) {
- _duration = duration;
- }
- renderStack = [];
- };
+ this.transition = function(selection, args, duration) {
+ args = arguments.length > 1 ? [].slice.call(arguments, 1) : [];
- this.transition = function(selection, args, duration) {
- args = arguments.length > 1 ? [].slice.call(arguments, 1) : [];
+ if (args.length > 1) {
+ duration = args.pop();
+ } else {
+ duration = _duration !== undefined ? _duration : 250;
+ }
+ selection.__rendered = false;
- if (args.length > 1) {
- duration = args.pop();
- } else {
- duration = _duration !== undefined ? _duration : 250;
- }
- selection.__rendered = false;
+ if (renderStack.indexOf(selection) < 0) {
+ renderStack.push(selection);
+ }
- if (renderStack.indexOf(selection) < 0) {
- renderStack.push(selection);
- }
-
- if (duration === 0) {
- selection.__rendered = true;
- selection.delay = function() { return this; };
- selection.duration = function() { return this; };
- return selection;
- } else {
- if (selection.length === 0) {
+ if (duration === 0) {
selection.__rendered = true;
- } else if (selection.every( function(d){ return !d.length; } )) {
- selection.__rendered = true;
+ selection.delay = function() { return this; };
+ selection.duration = function() { return this; };
+ return selection;
} else {
- selection.__rendered = false;
+ if (selection.length === 0) {
+ selection.__rendered = true;
+ } else if (selection.every( function(d){ return !d.length; } )) {
+ selection.__rendered = true;
+ } else {
+ selection.__rendered = false;
+ }
+
+ var n = 0;
+ return selection
+ .transition()
+ .duration(duration)
+ .each(function(){ ++n; })
+ .each('end', function(d, i) {
+ if (--n === 0) {
+ selection.__rendered = true;
+ self.renderEnd.apply(this, args);
+ }
+ });
}
+ };
- var n = 0;
- return selection
- .transition()
- .duration(duration)
- .each(function(){ ++n; })
- .each('end', function(d, i) {
- if (--n === 0) {
- selection.__rendered = true;
- self.renderEnd.apply(this, args);
- }
- });
+ this.renderEnd = function() {
+ if (renderStack.every( function(d){ return d.__rendered; } )) {
+ renderStack.forEach( function(d){ d.__rendered = false; });
+ dispatch.renderEnd.apply(this, arguments);
+ }
}
+
};
- this.renderEnd = function() {
- if (renderStack.every( function(d){ return d.__rendered; } )) {
- renderStack.forEach( function(d){ d.__rendered = false; });
- dispatch.renderEnd.apply(this, arguments);
- }
- }
-};
+ /*
+ Takes multiple objects and combines them into the first one (dst)
+ example: nv.utils.deepExtend({a: 1}, {a: 2, b: 3}, {c: 4});
+ gives: {a: 2, b: 3, c: 4}
+ */
+ nv.utils.deepExtend = function(dst){
+ var sources = arguments.length > 1 ? [].slice.call(arguments, 1) : [];
+ sources.forEach(function(source) {
+ for (var key in source) {
+ var isArray = nv.utils.isArray(dst[key]);
+ var isObject = nv.utils.isObject(dst[key]);
+ var srcObj = nv.utils.isObject(source[key]);
+ if (isObject && !isArray && srcObj) {
+ nv.utils.deepExtend(dst[key], source[key]);
+ } else {
+ dst[key] = source[key];
+ }
+ }
+ });
+ };
-/*
-Takes multiple objects and combines them into the first one (dst)
-example: nv.utils.deepExtend({a: 1}, {a: 2, b: 3}, {c: 4});
-gives: {a: 2, b: 3, c: 4}
-*/
-nv.utils.deepExtend = function(dst){
- var sources = arguments.length > 1 ? [].slice.call(arguments, 1) : [];
- sources.forEach(function(source) {
- for (var key in source) {
- var isArray = nv.utils.isArray(dst[key]);
- var isObject = nv.utils.isObject(dst[key]);
- var srcObj = nv.utils.isObject(source[key]);
- if (isObject && !isArray && srcObj) {
- nv.utils.deepExtend(dst[key], source[key]);
- } else {
- dst[key] = source[key];
- }
+ /*
+ state utility object, used to track d3 states in the models
+ */
+ nv.utils.state = function(){
+ if (!(this instanceof nv.utils.state)) {
+ return new nv.utils.state();
}
- });
-};
+ var state = {};
+ var _self = this;
+ var _setState = function(){};
+ var _getState = function(){ return {}; };
+ var init = null;
+ var changed = null;
+ this.dispatch = d3.dispatch('change', 'set');
-/*
-state utility object, used to track d3 states in the models
-*/
-nv.utils.state = function(){
- if (!(this instanceof nv.utils.state)) {
- return new nv.utils.state();
- }
- var state = {};
- var _self = this;
- var _setState = function(){};
- var _getState = function(){ return {}; };
- var init = null;
- var changed = null;
+ this.dispatch.on('set', function(state){
+ _setState(state, true);
+ });
- this.dispatch = d3.dispatch('change', 'set');
+ this.getter = function(fn){
+ _getState = fn;
+ return this;
+ };
- this.dispatch.on('set', function(state){
- _setState(state, true);
- });
-
- this.getter = function(fn){
- _getState = fn;
- return this;
- };
-
- this.setter = function(fn, callback) {
- if (!callback) {
- callback = function(){};
- }
- _setState = function(state, update){
- fn(state);
- if (update) {
- callback();
+ this.setter = function(fn, callback) {
+ if (!callback) {
+ callback = function(){};
}
+ _setState = function(state, update){
+ fn(state);
+ if (update) {
+ callback();
+ }
+ };
+ return this;
};
- return this;
- };
- this.init = function(state){
- init = init || {};
- nv.utils.deepExtend(init, state);
- };
+ this.init = function(state){
+ init = init || {};
+ nv.utils.deepExtend(init, state);
+ };
- var _set = function(){
- var settings = _getState();
+ var _set = function(){
+ var settings = _getState();
- if (JSON.stringify(settings) === JSON.stringify(state)) {
- return false;
- }
+ if (JSON.stringify(settings) === JSON.stringify(state)) {
+ return false;
+ }
- for (var key in settings) {
- if (state[key] === undefined) {
- state[key] = {};
+ for (var key in settings) {
+ if (state[key] === undefined) {
+ state[key] = {};
+ }
+ state[key] = settings[key];
+ changed = true;
}
- state[key] = settings[key];
- changed = true;
- }
- return true;
- };
+ return true;
+ };
- this.update = function(){
- if (init) {
- _setState(init, false);
- init = null;
- }
- if (_set.call(this)) {
- this.dispatch.change(state);
- }
+ this.update = function(){
+ if (init) {
+ _setState(init, false);
+ init = null;
+ }
+ if (_set.call(this)) {
+ this.dispatch.change(state);
+ }
+ };
+
};
-};
+ /*
+ Snippet of code you can insert into each nv.models.* to give you the ability to
+ do things like:
+ chart.options({
+ showXAxis: true,
+ tooltips: true
+ });
-/*
-Snippet of code you can insert into each nv.models.* to give you the ability to
-do things like:
-chart.options({
- showXAxis: true,
- tooltips: true
-});
+ To enable in the chart:
+ chart.options = nv.utils.optionsFunc.bind(chart);
+ */
+ nv.utils.optionsFunc = function(args) {
+ if (args) {
+ d3.map(args).forEach((function(key,value) {
+ if (nv.utils.isFunction(this[key])) {
+ this[key](value);
+ }
+ }).bind(this));
+ }
+ return this;
+ };
-To enable in the chart:
-chart.options = nv.utils.optionsFunc.bind(chart);
-*/
-nv.utils.optionsFunc = function(args) {
- if (args) {
- d3.map(args).forEach((function(key,value) {
- if (nv.utils.isFunction(this[key])) {
- this[key](value);
- }
- }).bind(this));
- }
- return this;
-};
+ /*
+ numTicks: requested number of ticks
+ data: the chart data
-/*
-numTicks: requested number of ticks
-data: the chart data
+ returns the number of ticks to actually use on X axis, based on chart data
+ to avoid duplicate ticks with the same value
+ */
+ nv.utils.calcTicksX = function(numTicks, data) {
+ // find max number of values from all data streams
+ var numValues = 1;
+ var i = 0;
+ for (i; i < data.length; i += 1) {
+ var stream_len = data[i] && data[i].values ? data[i].values.length : 0;
+ numValues = stream_len > numValues ? stream_len : numValues;
+ }
+ nv.log("Requested number of ticks: ", numTicks);
+ nv.log("Calculated max values to be: ", numValues);
+ // make sure we don't have more ticks than values to avoid duplicates
+ numTicks = numTicks > numValues ? numTicks = numValues - 1 : numTicks;
+ // make sure we have at least one tick
+ numTicks = numTicks < 1 ? 1 : numTicks;
+ // make sure it's an integer
+ numTicks = Math.floor(numTicks);
+ nv.log("Calculating tick count as: ", numTicks);
+ return numTicks;
+ };
-returns the number of ticks to actually use on X axis, based on chart data
-to avoid duplicate ticks with the same value
-*/
-nv.utils.calcTicksX = function(numTicks, data) {
- // find max number of values from all data streams
- var numValues = 1;
- var i = 0;
- for (i; i < data.length; i += 1) {
- var stream_len = data[i] && data[i].values ? data[i].values.length : 0;
- numValues = stream_len > numValues ? stream_len : numValues;
- }
- nv.log("Requested number of ticks: ", numTicks);
- nv.log("Calculated max values to be: ", numValues);
- // make sure we don't have more ticks than values to avoid duplicates
- numTicks = numTicks > numValues ? numTicks = numValues - 1 : numTicks;
- // make sure we have at least one tick
- numTicks = numTicks < 1 ? 1 : numTicks;
- // make sure it's an integer
- numTicks = Math.floor(numTicks);
- nv.log("Calculating tick count as: ", numTicks);
- return numTicks;
-};
+ /*
+ returns number of ticks to actually use on Y axis, based on chart data
+ */
+ nv.utils.calcTicksY = function(numTicks, data) {
+ // currently uses the same logic but we can adjust here if needed later
+ return nv.utils.calcTicksX(numTicks, data);
+ };
-/*
-returns number of ticks to actually use on Y axis, based on chart data
-*/
-nv.utils.calcTicksY = function(numTicks, data) {
- // currently uses the same logic but we can adjust here if needed later
- return nv.utils.calcTicksX(numTicks, data);
-};
+ /*
+ Add a particular option from an options object onto chart
+ Options exposed on a chart are a getter/setter function that returns chart
+ on set to mimic typical d3 option chaining, e.g. svg.option1('a').option2('b');
-/*
-Add a particular option from an options object onto chart
-Options exposed on a chart are a getter/setter function that returns chart
-on set to mimic typical d3 option chaining, e.g. svg.option1('a').option2('b');
-
-option objects should be generated via Object.create() to provide
-the option of manipulating data via get/set functions.
-*/
-nv.utils.initOption = function(chart, name) {
- // if it's a call option, just call it directly, otherwise do get/set
- if (chart._calls && chart._calls[name]) {
- chart[name] = chart._calls[name];
- } else {
- chart[name] = function (_) {
- if (!arguments.length) return chart._options[name];
- chart._overrides[name] = true;
- chart._options[name] = _;
- return chart;
- };
- // calling the option as _option will ignore if set by option already
- // so nvd3 can set options internally but the stop if set manually
- chart['_' + name] = function(_) {
- if (!arguments.length) return chart._options[name];
- if (!chart._overrides[name]) {
+ option objects should be generated via Object.create() to provide
+ the option of manipulating data via get/set functions.
+ */
+ nv.utils.initOption = function(chart, name) {
+ // if it's a call option, just call it directly, otherwise do get/set
+ if (chart._calls && chart._calls[name]) {
+ chart[name] = chart._calls[name];
+ } else {
+ chart[name] = function (_) {
+ if (!arguments.length) return chart._options[name];
+ chart._overrides[name] = true;
chart._options[name] = _;
+ return chart;
+ };
+ // calling the option as _option will ignore if set by option already
+ // so nvd3 can set options internally but the stop if set manually
+ chart['_' + name] = function(_) {
+ if (!arguments.length) return chart._options[name];
+ if (!chart._overrides[name]) {
+ chart._options[name] = _;
+ }
+ return chart;
}
- return chart;
}
- }
-};
+ };
-/*
-Add all options in an options object to the chart
-*/
-nv.utils.initOptions = function(chart) {
- chart._overrides = chart._overrides || {};
- var ops = Object.getOwnPropertyNames(chart._options || {});
- var calls = Object.getOwnPropertyNames(chart._calls || {});
- ops = ops.concat(calls);
- for (var i in ops) {
- nv.utils.initOption(chart, ops[i]);
- }
-};
+ /*
+ Add all options in an options object to the chart
+ */
+ nv.utils.initOptions = function(chart) {
+ chart._overrides = chart._overrides || {};
+ var ops = Object.getOwnPropertyNames(chart._options || {});
+ var calls = Object.getOwnPropertyNames(chart._calls || {});
+ ops = ops.concat(calls);
+ for (var i in ops) {
+ nv.utils.initOption(chart, ops[i]);
+ }
+ };
-/*
-Inherit options from a D3 object
-d3.rebind makes calling the function on target actually call it on source
-Also use _d3options so we can track what we inherit for documentation and chained inheritance
-*/
-nv.utils.inheritOptionsD3 = function(target, d3_source, oplist) {
- target._d3options = oplist.concat(target._d3options || []);
- oplist.unshift(d3_source);
- oplist.unshift(target);
- d3.rebind.apply(this, oplist);
-};
+ /*
+ Inherit options from a D3 object
+ d3.rebind makes calling the function on target actually call it on source
+ Also use _d3options so we can track what we inherit for documentation and chained inheritance
+ */
+ nv.utils.inheritOptionsD3 = function(target, d3_source, oplist) {
+ target._d3options = oplist.concat(target._d3options || []);
+ oplist.unshift(d3_source);
+ oplist.unshift(target);
+ d3.rebind.apply(this, oplist);
+ };
-/*
-Remove duplicates from an array
-*/
-nv.utils.arrayUnique = function(a) {
- return a.sort().filter(function(item, pos) {
- return !pos || item != a[pos - 1];
- });
-};
+ /*
+ Remove duplicates from an array
+ */
+ nv.utils.arrayUnique = function(a) {
+ return a.sort().filter(function(item, pos) {
+ return !pos || item != a[pos - 1];
+ });
+ };
-/*
-Keeps a list of custom symbols to draw from in addition to d3.svg.symbol
-Necessary since d3 doesn't let you extend its list -_-
-Add new symbols by doing nv.utils.symbols.set('name', function(size){...});
-*/
-nv.utils.symbolMap = d3.map();
+ /*
+ Keeps a list of custom symbols to draw from in addition to d3.svg.symbol
+ Necessary since d3 doesn't let you extend its list -_-
+ Add new symbols by doing nv.utils.symbols.set('name', function(size){...});
+ */
+ nv.utils.symbolMap = d3.map();
-/*
-Replaces d3.svg.symbol so that we can look both there and our own map
- */
-nv.utils.symbol = function() {
- var type,
- size = 64;
- function symbol(d,i) {
- var t = type.call(this,d,i);
- var s = size.call(this,d,i);
- if (d3.svg.symbolTypes.indexOf(t) !== -1) {
- return d3.svg.symbol().type(t).size(s)();
- } else {
- return nv.utils.symbolMap.get(t)(s);
+ /*
+ Replaces d3.svg.symbol so that we can look both there and our own map
+ */
+ nv.utils.symbol = function() {
+ var type,
+ size = 64;
+ function symbol(d,i) {
+ var t = type.call(this,d,i);
+ var s = size.call(this,d,i);
+ if (d3.svg.symbolTypes.indexOf(t) !== -1) {
+ return d3.svg.symbol().type(t).size(s)();
+ } else {
+ return nv.utils.symbolMap.get(t)(s);
+ }
}
- }
- symbol.type = function(_) {
- if (!arguments.length) return type;
- type = d3.functor(_);
+ symbol.type = function(_) {
+ if (!arguments.length) return type;
+ type = d3.functor(_);
+ return symbol;
+ };
+ symbol.size = function(_) {
+ if (!arguments.length) return size;
+ size = d3.functor(_);
+ return symbol;
+ };
return symbol;
};
- symbol.size = function(_) {
- if (!arguments.length) return size;
- size = d3.functor(_);
- return symbol;
- };
- return symbol;
-};
-/*
-Inherit option getter/setter functions from source to target
-d3.rebind makes calling the function on target actually call it on source
-Also track via _inherited and _d3options so we can track what we inherit
-for documentation generation purposes and chained inheritance
-*/
-nv.utils.inheritOptions = function(target, source) {
- // inherit all the things
- var ops = Object.getOwnPropertyNames(source._options || {});
- var calls = Object.getOwnPropertyNames(source._calls || {});
- var inherited = source._inherited || [];
- var d3ops = source._d3options || [];
- var args = ops.concat(calls).concat(inherited).concat(d3ops);
- args.unshift(source);
- args.unshift(target);
- d3.rebind.apply(this, args);
- // pass along the lists to keep track of them, don't allow duplicates
- target._inherited = nv.utils.arrayUnique(ops.concat(calls).concat(inherited).concat(ops).concat(target._inherited || []));
- target._d3options = nv.utils.arrayUnique(d3ops.concat(target._d3options || []));
-};
+ /*
+ Inherit option getter/setter functions from source to target
+ d3.rebind makes calling the function on target actually call it on source
+ Also track via _inherited and _d3options so we can track what we inherit
+ for documentation generation purposes and chained inheritance
+ */
+ nv.utils.inheritOptions = function(target, source) {
+ // inherit all the things
+ var ops = Object.getOwnPropertyNames(source._options || {});
+ var calls = Object.getOwnPropertyNames(source._calls || {});
+ var inherited = source._inherited || [];
+ var d3ops = source._d3options || [];
+ var args = ops.concat(calls).concat(inherited).concat(d3ops);
+ args.unshift(source);
+ args.unshift(target);
+ d3.rebind.apply(this, args);
+ // pass along the lists to keep track of them, don't allow duplicates
+ target._inherited = nv.utils.arrayUnique(ops.concat(calls).concat(inherited).concat(ops).concat(target._inherited || []));
+ target._d3options = nv.utils.arrayUnique(d3ops.concat(target._d3options || []));
+ };
-/*
-Runs common initialize code on the svg before the chart builds
-*/
-nv.utils.initSVG = function(svg) {
- svg.classed({'nvd3-svg':true});
-};
+ /*
+ Runs common initialize code on the svg before the chart builds
+ */
+ nv.utils.initSVG = function(svg) {
+ svg.classed({'nvd3-svg':true});
+ };
-/*
-Sanitize and provide default for the container height.
-*/
-nv.utils.sanitizeHeight = function(height, container) {
- return (height || parseInt(container.style('height'), 10) || 400);
-};
+ /*
+ Sanitize and provide default for the container height.
+ */
+ nv.utils.sanitizeHeight = function(height, container) {
+ return (height || parseInt(container.style('height'), 10) || 400);
+ };
-/*
-Sanitize and provide default for the container width.
-*/
-nv.utils.sanitizeWidth = function(width, container) {
- return (width || parseInt(container.style('width'), 10) || 960);
-};
+ /*
+ Sanitize and provide default for the container width.
+ */
+ nv.utils.sanitizeWidth = function(width, container) {
+ return (width || parseInt(container.style('width'), 10) || 960);
+ };
-/*
-Calculate the available height for a chart.
-*/
-nv.utils.availableHeight = function(height, container, margin) {
- return Math.max(0,nv.utils.sanitizeHeight(height, container) - margin.top - margin.bottom);
-};
+ /*
+ Calculate the available height for a chart.
+ */
+ nv.utils.availableHeight = function(height, container, margin) {
+ return Math.max(0,nv.utils.sanitizeHeight(height, container) - margin.top - margin.bottom);
+ };
-/*
-Calculate the available width for a chart.
-*/
-nv.utils.availableWidth = function(width, container, margin) {
- return Math.max(0,nv.utils.sanitizeWidth(width, container) - margin.left - margin.right);
-};
+ /*
+ Calculate the available width for a chart.
+ */
+ nv.utils.availableWidth = function(width, container, margin) {
+ return Math.max(0,nv.utils.sanitizeWidth(width, container) - margin.left - margin.right);
+ };
-/*
-Clear any rendered chart components and display a chart's 'noData' message
-*/
-nv.utils.noData = function(chart, container) {
- var opt = chart.options(),
- margin = opt.margin(),
- noData = opt.noData(),
- data = (noData == null) ? ["No Data Available."] : [noData],
- height = nv.utils.availableHeight(null, container, margin),
- width = nv.utils.availableWidth(null, container, margin),
- x = margin.left + width/2,
- y = margin.top + height/2;
+ /*
+ Clear any rendered chart components and display a chart's 'noData' message
+ */
+ nv.utils.noData = function(chart, container) {
+ var opt = chart.options(),
+ margin = opt.margin(),
+ noData = opt.noData(),
+ data = (noData == null) ? ["No Data Available."] : [noData],
+ height = nv.utils.availableHeight(null, container, margin),
+ width = nv.utils.availableWidth(null, container, margin),
+ x = margin.left + width/2,
+ y = margin.top + height/2;
- //Remove any previously created chart components
- container.selectAll('g').remove();
+ //Remove any previously created chart components
+ container.selectAll('g').remove();
- var noDataText = container.selectAll('.nv-noData').data(data);
+ var noDataText = container.selectAll('.nv-noData').data(data);
- noDataText.enter().append('text')
- .attr('class', 'nvd3 nv-noData')
- .attr('dy', '-.7em')
- .style('text-anchor', 'middle');
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
- noDataText
- .attr('x', x)
- .attr('y', y)
- .text(function(t){ return t; });
-};
+ noDataText
+ .attr('x', x)
+ .attr('y', y)
+ .text(function(t){ return t; });
+ };
-/*
- Wrap long labels.
- */
-nv.utils.wrapTicks = function (text, width) {
- text.each(function() {
- var text = d3.select(this),
- words = text.text().split(/\s+/).reverse(),
- word,
- line = [],
- lineNumber = 0,
- lineHeight = 1.1,
- y = text.attr("y"),
- dy = parseFloat(text.attr("dy")),
- tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
- while (word = words.pop()) {
- line.push(word);
- tspan.text(line.join(" "));
- if (tspan.node().getComputedTextLength() > width) {
- line.pop();
+ /*
+ Wrap long labels.
+ */
+ nv.utils.wrapTicks = function (text, width) {
+ text.each(function() {
+ var text = d3.select(this),
+ words = text.text().split(/\s+/).reverse(),
+ word,
+ line = [],
+ lineNumber = 0,
+ lineHeight = 1.1,
+ y = text.attr("y"),
+ dy = parseFloat(text.attr("dy")),
+ tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
+ while (word = words.pop()) {
+ line.push(word);
tspan.text(line.join(" "));
- line = [word];
- tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
+ if (tspan.node().getComputedTextLength() > width) {
+ line.pop();
+ tspan.text(line.join(" "));
+ line = [word];
+ tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
+ }
}
- }
- });
-};
+ });
+ };
-/*
-Check equality of 2 array
-*/
-nv.utils.arrayEquals = function (array1, array2) {
- if (array1 === array2)
- return true;
+ /*
+ Check equality of 2 array
+ */
+ nv.utils.arrayEquals = function (array1, array2) {
+ if (array1 === array2)
+ return true;
- if (!array1 || !array2)
- return false;
+ if (!array1 || !array2)
+ return false;
- // compare lengths - can save a lot of time
- if (array1.length != array2.length)
- return false;
+ // compare lengths - can save a lot of time
+ if (array1.length != array2.length)
+ return false;
- for (var i = 0,
- l = array1.length; i < l; i++) {
- // Check if we have nested arrays
- if (array1[i] instanceof Array && array2[i] instanceof Array) {
- // recurse into the nested arrays
- if (!nv.arrayEquals(array1[i], array2[i]))
+ for (var i = 0,
+ l = array1.length; i < l; i++) {
+ // Check if we have nested arrays
+ if (array1[i] instanceof Array && array2[i] instanceof Array) {
+ // recurse into the nested arrays
+ if (!nv.arrayEquals(array1[i], array2[i]))
+ return false;
+ } else if (array1[i] != array2[i]) {
+ // Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
- } else if (array1[i] != array2[i]) {
- // Warning - two different object instances will never be equal: {x:20} != {x:20}
- return false;
+ }
}
- }
- return true;
-};
-nv.models.axis = function() {
- "use strict";
+ return true;
+ };
+ nv.models.axis = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var axis = d3.svg.axis();
- var scale = d3.scale.linear();
+ var axis = d3.svg.axis();
+ var scale = d3.scale.linear();
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 75 //only used for tickLabel currently
- , height = 60 //only used for tickLabel currently
- , axisLabelText = null
- , showMaxMin = true //TODO: showMaxMin should be disabled on all ordinal scaled axes
- , rotateLabels = 0
- , rotateYLabel = true
- , staggerLabels = false
- , isOrdinal = false
- , ticks = null
- , axisLabelDistance = 0
- , fontSize = undefined
- , duration = 250
- , dispatch = d3.dispatch('renderEnd')
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 75 //only used for tickLabel currently
+ , height = 60 //only used for tickLabel currently
+ , axisLabelText = null
+ , showMaxMin = true //TODO: showMaxMin should be disabled on all ordinal scaled axes
+ , rotateLabels = 0
+ , rotateYLabel = true
+ , staggerLabels = false
+ , isOrdinal = false
+ , ticks = null
+ , axisLabelDistance = 0
+ , fontSize = undefined
+ , duration = 250
+ , dispatch = d3.dispatch('renderEnd')
+ ;
+ axis
+ .scale(scale)
+ .orient('bottom')
+ .tickFormat(function(d) { return d })
;
- axis
- .scale(scale)
- .orient('bottom')
- .tickFormat(function(d) { return d })
- ;
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var scale0;
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
+ var scale0;
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
- function chart(selection) {
- renderWatch.reset();
- selection.each(function(data) {
- var container = d3.select(this);
- nv.utils.initSVG(container);
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var container = d3.select(this);
+ nv.utils.initSVG(container);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-axis').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-axis');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-axis').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-axis');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- if (ticks !== null)
- axis.ticks(ticks);
- else if (axis.orient() == 'top' || axis.orient() == 'bottom')
- axis.ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 100);
+ if (ticks !== null)
+ axis.ticks(ticks);
+ else if (axis.orient() == 'top' || axis.orient() == 'bottom')
+ axis.ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 100);
- //TODO: consider calculating width/height based on whether or not label is added, for reference in charts using this component
- g.watchTransition(renderWatch, 'axis').call(axis);
+ //TODO: consider calculating width/height based on whether or not label is added, for reference in charts using this component
+ g.watchTransition(renderWatch, 'axis').call(axis);
- scale0 = scale0 || axis.scale();
+ scale0 = scale0 || axis.scale();
- var fmt = axis.tickFormat();
- if (fmt == null) {
- fmt = scale0.tickFormat();
- }
+ var fmt = axis.tickFormat();
+ if (fmt == null) {
+ fmt = scale0.tickFormat();
+ }
- var axisLabel = g.selectAll('text.nv-axislabel')
- .data([axisLabelText || null]);
- axisLabel.exit().remove();
+ var axisLabel = g.selectAll('text.nv-axislabel')
+ .data([axisLabelText || null]);
+ axisLabel.exit().remove();
- //only skip when fontSize is undefined so it can be cleared with a null or blank string
- if (fontSize !== undefined) {
- g.selectAll('g').select("text").style('font-size', fontSize);
- }
+ //only skip when fontSize is undefined so it can be cleared with a null or blank string
+ if (fontSize !== undefined) {
+ g.selectAll('g').select("text").style('font-size', fontSize);
+ }
- var xLabelMargin;
- var axisMaxMin;
- var w;
- switch (axis.orient()) {
- case 'top':
- axisLabel.enter().append('text').attr('class', 'nv-axislabel');
- w = 0;
- if (scale.range().length === 1) {
- w = isOrdinal ? scale.range()[0] * 2 + scale.rangeBand() : 0;
- } else if (scale.range().length === 2) {
- w = isOrdinal ? scale.range()[0] + scale.range()[1] + scale.rangeBand() : scale.range()[1];
- } else if ( scale.range().length > 2){
- w = scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]);
- };
- axisLabel
- .attr('text-anchor', 'middle')
- .attr('y', 0)
- .attr('x', w/2);
- if (showMaxMin) {
- axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
- .data(scale.domain());
- axisMaxMin.enter().append('g').attr('class',function(d,i){
- return ['nv-axisMaxMin','nv-axisMaxMin-x',(i == 0 ? 'nv-axisMin-x':'nv-axisMax-x')].join(' ')
- }).append('text');
- axisMaxMin.exit().remove();
- axisMaxMin
- .attr('transform', function(d,i) {
- return 'translate(' + nv.utils.NaNtoZero(scale(d)) + ',0)'
- })
- .select('text')
- .attr('dy', '-0.5em')
- .attr('y', -axis.tickPadding())
+ var xLabelMargin;
+ var axisMaxMin;
+ var w;
+ switch (axis.orient()) {
+ case 'top':
+ axisLabel.enter().append('text').attr('class', 'nv-axislabel');
+ w = 0;
+ if (scale.range().length === 1) {
+ w = isOrdinal ? scale.range()[0] * 2 + scale.rangeBand() : 0;
+ } else if (scale.range().length === 2) {
+ w = isOrdinal ? scale.range()[0] + scale.range()[1] + scale.rangeBand() : scale.range()[1];
+ } else if ( scale.range().length > 2){
+ w = scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]);
+ };
+ axisLabel
.attr('text-anchor', 'middle')
- .text(function(d,i) {
- var v = fmt(d);
- return ('' + v).match('NaN') ? '' : v;
- });
- axisMaxMin.watchTransition(renderWatch, 'min-max top')
- .attr('transform', function(d,i) {
- return 'translate(' + nv.utils.NaNtoZero(scale.range()[i]) + ',0)'
- });
- }
- break;
- case 'bottom':
- xLabelMargin = axisLabelDistance + 36;
- var maxTextWidth = 30;
- var textHeight = 0;
- var xTicks = g.selectAll('g').select("text");
- var rotateLabelsRule = '';
- if (rotateLabels%360) {
- //Reset transform on ticks so textHeight can be calculated correctly
- xTicks.attr('transform', '');
- //Calculate the longest xTick width
- xTicks.each(function(d,i){
- var box = this.getBoundingClientRect();
- var width = box.width;
- textHeight = box.height;
- if(width > maxTextWidth) maxTextWidth = width;
- });
- rotateLabelsRule = 'rotate(' + rotateLabels + ' 0,' + (textHeight/2 + axis.tickPadding()) + ')';
- //Convert to radians before calculating sin. Add 30 to margin for healthy padding.
- var sin = Math.abs(Math.sin(rotateLabels*Math.PI/180));
- xLabelMargin = (sin ? sin*maxTextWidth : maxTextWidth)+30;
- //Rotate all xTicks
- xTicks
- .attr('transform', rotateLabelsRule)
- .style('text-anchor', rotateLabels%360 > 0 ? 'start' : 'end');
- } else {
- if (staggerLabels) {
- xTicks
+ .attr('y', 0)
+ .attr('x', w/2);
+ if (showMaxMin) {
+ axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
+ .data(scale.domain());
+ axisMaxMin.enter().append('g').attr('class',function(d,i){
+ return ['nv-axisMaxMin','nv-axisMaxMin-x',(i == 0 ? 'nv-axisMin-x':'nv-axisMax-x')].join(' ')
+ }).append('text');
+ axisMaxMin.exit().remove();
+ axisMaxMin
.attr('transform', function(d,i) {
- return 'translate(0,' + (i % 2 == 0 ? '0' : '12') + ')'
+ return 'translate(' + nv.utils.NaNtoZero(scale(d)) + ',0)'
+ })
+ .select('text')
+ .attr('dy', '-0.5em')
+ .attr('y', -axis.tickPadding())
+ .attr('text-anchor', 'middle')
+ .text(function(d,i) {
+ var v = fmt(d);
+ return ('' + v).match('NaN') ? '' : v;
});
+ axisMaxMin.watchTransition(renderWatch, 'min-max top')
+ .attr('transform', function(d,i) {
+ return 'translate(' + nv.utils.NaNtoZero(scale.range()[i]) + ',0)'
+ });
+ }
+ break;
+ case 'bottom':
+ xLabelMargin = axisLabelDistance + 36;
+ var maxTextWidth = 30;
+ var textHeight = 0;
+ var xTicks = g.selectAll('g').select("text");
+ var rotateLabelsRule = '';
+ if (rotateLabels%360) {
+ //Reset transform on ticks so textHeight can be calculated correctly
+ xTicks.attr('transform', '');
+ //Calculate the longest xTick width
+ xTicks.each(function(d,i){
+ var box = this.getBoundingClientRect();
+ var width = box.width;
+ textHeight = box.height;
+ if(width > maxTextWidth) maxTextWidth = width;
+ });
+ rotateLabelsRule = 'rotate(' + rotateLabels + ' 0,' + (textHeight/2 + axis.tickPadding()) + ')';
+ //Convert to radians before calculating sin. Add 30 to margin for healthy padding.
+ var sin = Math.abs(Math.sin(rotateLabels*Math.PI/180));
+ xLabelMargin = (sin ? sin*maxTextWidth : maxTextWidth)+30;
+ //Rotate all xTicks
+ xTicks
+ .attr('transform', rotateLabelsRule)
+ .style('text-anchor', rotateLabels%360 > 0 ? 'start' : 'end');
} else {
- xTicks.attr('transform', "translate(0,0)");
+ if (staggerLabels) {
+ xTicks
+ .attr('transform', function(d,i) {
+ return 'translate(0,' + (i % 2 == 0 ? '0' : '12') + ')'
+ });
+ } else {
+ xTicks.attr('transform', "translate(0,0)");
+ }
}
- }
- axisLabel.enter().append('text').attr('class', 'nv-axislabel');
- w = 0;
- if (scale.range().length === 1) {
- w = isOrdinal ? scale.range()[0] * 2 + scale.rangeBand() : 0;
- } else if (scale.range().length === 2) {
- w = isOrdinal ? scale.range()[0] + scale.range()[1] + scale.rangeBand() : scale.range()[1];
- } else if ( scale.range().length > 2){
- w = scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]);
- };
- axisLabel
- .attr('text-anchor', 'middle')
- .attr('y', xLabelMargin)
- .attr('x', w/2);
- if (showMaxMin) {
- //if (showMaxMin && !isOrdinal) {
- axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
+ axisLabel.enter().append('text').attr('class', 'nv-axislabel');
+ w = 0;
+ if (scale.range().length === 1) {
+ w = isOrdinal ? scale.range()[0] * 2 + scale.rangeBand() : 0;
+ } else if (scale.range().length === 2) {
+ w = isOrdinal ? scale.range()[0] + scale.range()[1] + scale.rangeBand() : scale.range()[1];
+ } else if ( scale.range().length > 2){
+ w = scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]);
+ };
+ axisLabel
+ .attr('text-anchor', 'middle')
+ .attr('y', xLabelMargin)
+ .attr('x', w/2);
+ if (showMaxMin) {
+ //if (showMaxMin && !isOrdinal) {
+ axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
//.data(scale.domain())
- .data([scale.domain()[0], scale.domain()[scale.domain().length - 1]]);
- axisMaxMin.enter().append('g').attr('class',function(d,i){
+ .data([scale.domain()[0], scale.domain()[scale.domain().length - 1]]);
+ axisMaxMin.enter().append('g').attr('class',function(d,i){
return ['nv-axisMaxMin','nv-axisMaxMin-x',(i == 0 ? 'nv-axisMin-x':'nv-axisMax-x')].join(' ')
- }).append('text');
- axisMaxMin.exit().remove();
- axisMaxMin
- .attr('transform', function(d,i) {
- return 'translate(' + nv.utils.NaNtoZero((scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0))) + ',0)'
- })
- .select('text')
- .attr('dy', '.71em')
- .attr('y', axis.tickPadding())
- .attr('transform', rotateLabelsRule)
- .style('text-anchor', rotateLabels ? (rotateLabels%360 > 0 ? 'start' : 'end') : 'middle')
- .text(function(d,i) {
- var v = fmt(d);
- return ('' + v).match('NaN') ? '' : v;
- });
- axisMaxMin.watchTransition(renderWatch, 'min-max bottom')
- .attr('transform', function(d,i) {
- return 'translate(' + nv.utils.NaNtoZero((scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0))) + ',0)'
- });
- }
+ }).append('text');
+ axisMaxMin.exit().remove();
+ axisMaxMin
+ .attr('transform', function(d,i) {
+ return 'translate(' + nv.utils.NaNtoZero((scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0))) + ',0)'
+ })
+ .select('text')
+ .attr('dy', '.71em')
+ .attr('y', axis.tickPadding())
+ .attr('transform', rotateLabelsRule)
+ .style('text-anchor', rotateLabels ? (rotateLabels%360 > 0 ? 'start' : 'end') : 'middle')
+ .text(function(d,i) {
+ var v = fmt(d);
+ return ('' + v).match('NaN') ? '' : v;
+ });
+ axisMaxMin.watchTransition(renderWatch, 'min-max bottom')
+ .attr('transform', function(d,i) {
+ return 'translate(' + nv.utils.NaNtoZero((scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0))) + ',0)'
+ });
+ }
- break;
- case 'right':
- axisLabel.enter().append('text').attr('class', 'nv-axislabel');
- axisLabel
- .style('text-anchor', rotateYLabel ? 'middle' : 'begin')
- .attr('transform', rotateYLabel ? 'rotate(90)' : '')
- .attr('y', rotateYLabel ? (-Math.max(margin.right, width) + 12 - (axisLabelDistance || 0)) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
- .attr('x', rotateYLabel ? (d3.max(scale.range()) / 2) : axis.tickPadding());
- if (showMaxMin) {
- axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
- .data(scale.domain());
- axisMaxMin.enter().append('g').attr('class',function(d,i){
+ break;
+ case 'right':
+ axisLabel.enter().append('text').attr('class', 'nv-axislabel');
+ axisLabel
+ .style('text-anchor', rotateYLabel ? 'middle' : 'begin')
+ .attr('transform', rotateYLabel ? 'rotate(90)' : '')
+ .attr('y', rotateYLabel ? (-Math.max(margin.right, width) + 12 - (axisLabelDistance || 0)) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
+ .attr('x', rotateYLabel ? (d3.max(scale.range()) / 2) : axis.tickPadding());
+ if (showMaxMin) {
+ axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
+ .data(scale.domain());
+ axisMaxMin.enter().append('g').attr('class',function(d,i){
return ['nv-axisMaxMin','nv-axisMaxMin-y',(i == 0 ? 'nv-axisMin-y':'nv-axisMax-y')].join(' ')
- }).append('text')
- .style('opacity', 0);
- axisMaxMin.exit().remove();
- axisMaxMin
- .attr('transform', function(d,i) {
- return 'translate(0,' + nv.utils.NaNtoZero(scale(d)) + ')'
- })
- .select('text')
- .attr('dy', '.32em')
- .attr('y', 0)
- .attr('x', axis.tickPadding())
- .style('text-anchor', 'start')
- .text(function(d, i) {
- var v = fmt(d);
- return ('' + v).match('NaN') ? '' : v;
- });
- axisMaxMin.watchTransition(renderWatch, 'min-max right')
- .attr('transform', function(d,i) {
- return 'translate(0,' + nv.utils.NaNtoZero(scale.range()[i]) + ')'
- })
- .select('text')
- .style('opacity', 1);
- }
- break;
- case 'left':
- /*
- //For dynamically placing the label. Can be used with dynamically-sized chart axis margins
- var yTicks = g.selectAll('g').select("text");
- yTicks.each(function(d,i){
- var labelPadding = this.getBoundingClientRect().width + axis.tickPadding() + 16;
- if(labelPadding > width) width = labelPadding;
- });
- */
- axisLabel.enter().append('text').attr('class', 'nv-axislabel');
- axisLabel
- .style('text-anchor', rotateYLabel ? 'middle' : 'end')
- .attr('transform', rotateYLabel ? 'rotate(-90)' : '')
- .attr('y', rotateYLabel ? (-Math.max(margin.left, width) + 25 - (axisLabelDistance || 0)) : -10)
- .attr('x', rotateYLabel ? (-d3.max(scale.range()) / 2) : -axis.tickPadding());
- if (showMaxMin) {
- axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
- .data(scale.domain());
- axisMaxMin.enter().append('g').attr('class',function(d,i){
+ }).append('text')
+ .style('opacity', 0);
+ axisMaxMin.exit().remove();
+ axisMaxMin
+ .attr('transform', function(d,i) {
+ return 'translate(0,' + nv.utils.NaNtoZero(scale(d)) + ')'
+ })
+ .select('text')
+ .attr('dy', '.32em')
+ .attr('y', 0)
+ .attr('x', axis.tickPadding())
+ .style('text-anchor', 'start')
+ .text(function(d, i) {
+ var v = fmt(d);
+ return ('' + v).match('NaN') ? '' : v;
+ });
+ axisMaxMin.watchTransition(renderWatch, 'min-max right')
+ .attr('transform', function(d,i) {
+ return 'translate(0,' + nv.utils.NaNtoZero(scale.range()[i]) + ')'
+ })
+ .select('text')
+ .style('opacity', 1);
+ }
+ break;
+ case 'left':
+ /*
+ //For dynamically placing the label. Can be used with dynamically-sized chart axis margins
+ var yTicks = g.selectAll('g').select("text");
+ yTicks.each(function(d,i){
+ var labelPadding = this.getBoundingClientRect().width + axis.tickPadding() + 16;
+ if(labelPadding > width) width = labelPadding;
+ });
+ */
+ axisLabel.enter().append('text').attr('class', 'nv-axislabel');
+ axisLabel
+ .style('text-anchor', rotateYLabel ? 'middle' : 'end')
+ .attr('transform', rotateYLabel ? 'rotate(-90)' : '')
+ .attr('y', rotateYLabel ? (-Math.max(margin.left, width) + 25 - (axisLabelDistance || 0)) : -10)
+ .attr('x', rotateYLabel ? (-d3.max(scale.range()) / 2) : -axis.tickPadding());
+ if (showMaxMin) {
+ axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
+ .data(scale.domain());
+ axisMaxMin.enter().append('g').attr('class',function(d,i){
return ['nv-axisMaxMin','nv-axisMaxMin-y',(i == 0 ? 'nv-axisMin-y':'nv-axisMax-y')].join(' ')
- }).append('text')
- .style('opacity', 0);
- axisMaxMin.exit().remove();
- axisMaxMin
- .attr('transform', function(d,i) {
- return 'translate(0,' + nv.utils.NaNtoZero(scale0(d)) + ')'
- })
- .select('text')
- .attr('dy', '.32em')
- .attr('y', 0)
- .attr('x', -axis.tickPadding())
- .attr('text-anchor', 'end')
- .text(function(d,i) {
- var v = fmt(d);
- return ('' + v).match('NaN') ? '' : v;
- });
- axisMaxMin.watchTransition(renderWatch, 'min-max right')
- .attr('transform', function(d,i) {
- return 'translate(0,' + nv.utils.NaNtoZero(scale.range()[i]) + ')'
- })
- .select('text')
- .style('opacity', 1);
- }
- break;
- }
- axisLabel.text(function(d) { return d });
+ }).append('text')
+ .style('opacity', 0);
+ axisMaxMin.exit().remove();
+ axisMaxMin
+ .attr('transform', function(d,i) {
+ return 'translate(0,' + nv.utils.NaNtoZero(scale0(d)) + ')'
+ })
+ .select('text')
+ .attr('dy', '.32em')
+ .attr('y', 0)
+ .attr('x', -axis.tickPadding())
+ .attr('text-anchor', 'end')
+ .text(function(d,i) {
+ var v = fmt(d);
+ return ('' + v).match('NaN') ? '' : v;
+ });
+ axisMaxMin.watchTransition(renderWatch, 'min-max right')
+ .attr('transform', function(d,i) {
+ return 'translate(0,' + nv.utils.NaNtoZero(scale.range()[i]) + ')'
+ })
+ .select('text')
+ .style('opacity', 1);
+ }
+ break;
+ }
+ axisLabel.text(function(d) { return d });
- if (showMaxMin && (axis.orient() === 'left' || axis.orient() === 'right')) {
- //check if max and min overlap other values, if so, hide the values that overlap
- g.selectAll('g') // the g's wrapping each tick
- .each(function(d,i) {
- d3.select(this).select('text').attr('opacity', 1);
- if (scale(d) < scale.range()[1] + 10 || scale(d) > scale.range()[0] - 10) { // 10 is assuming text height is 16... if d is 0, leave it!
- if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
- d3.select(this).attr('opacity', 0);
+ if (showMaxMin && (axis.orient() === 'left' || axis.orient() === 'right')) {
+ //check if max and min overlap other values, if so, hide the values that overlap
+ g.selectAll('g') // the g's wrapping each tick
+ .each(function(d,i) {
+ d3.select(this).select('text').attr('opacity', 1);
+ if (scale(d) < scale.range()[1] + 10 || scale(d) > scale.range()[0] - 10) { // 10 is assuming text height is 16... if d is 0, leave it!
+ if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
+ d3.select(this).attr('opacity', 0);
- d3.select(this).select('text').attr('opacity', 0); // Don't remove the ZERO line!!
- }
- });
+ d3.select(this).select('text').attr('opacity', 0); // Don't remove the ZERO line!!
+ }
+ });
- //if Max and Min = 0 only show min, Issue #281
- if (scale.domain()[0] == scale.domain()[1] && scale.domain()[0] == 0) {
- wrap.selectAll('g.nv-axisMaxMin').style('opacity', function (d, i) {
- return !i ? 1 : 0
- });
+ //if Max and Min = 0 only show min, Issue #281
+ if (scale.domain()[0] == scale.domain()[1] && scale.domain()[0] == 0) {
+ wrap.selectAll('g.nv-axisMaxMin').style('opacity', function (d, i) {
+ return !i ? 1 : 0
+ });
+ }
}
- }
- if (showMaxMin && (axis.orient() === 'top' || axis.orient() === 'bottom')) {
- var maxMinRange = [];
- wrap.selectAll('g.nv-axisMaxMin')
- .each(function(d,i) {
- try {
- if (i) // i== 1, max position
- maxMinRange.push(scale(d) - this.getBoundingClientRect().width - 4); //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
- else // i==0, min position
- maxMinRange.push(scale(d) + this.getBoundingClientRect().width + 4)
- }catch (err) {
- if (i) // i== 1, max position
- maxMinRange.push(scale(d) - 4); //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
- else // i==0, min position
- maxMinRange.push(scale(d) + 4);
+ if (showMaxMin && (axis.orient() === 'top' || axis.orient() === 'bottom')) {
+ var maxMinRange = [];
+ wrap.selectAll('g.nv-axisMaxMin')
+ .each(function(d,i) {
+ try {
+ if (i) // i== 1, max position
+ maxMinRange.push(scale(d) - this.getBoundingClientRect().width - 4); //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
+ else // i==0, min position
+ maxMinRange.push(scale(d) + this.getBoundingClientRect().width + 4)
+ }catch (err) {
+ if (i) // i== 1, max position
+ maxMinRange.push(scale(d) - 4); //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
+ else // i==0, min position
+ maxMinRange.push(scale(d) + 4);
+ }
+ });
+ // the g's wrapping each tick
+ g.selectAll('g').each(function(d, i) {
+ if (scale(d) < maxMinRange[0] || scale(d) > maxMinRange[1]) {
+ if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
+ d3.select(this).remove();
+ else
+ d3.select(this).select('text').remove(); // Don't remove the ZERO line!!
}
});
- // the g's wrapping each tick
- g.selectAll('g').each(function(d, i) {
- if (scale(d) < maxMinRange[0] || scale(d) > maxMinRange[1]) {
- if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
- d3.select(this).remove();
- else
- d3.select(this).select('text').remove(); // Don't remove the ZERO line!!
- }
- });
- }
+ }
- //Highlight zero tick line
- g.selectAll('.tick')
- .filter(function (d) {
- /*
- The filter needs to return only ticks at or near zero.
- Numbers like 0.00001 need to count as zero as well,
- and the arithmetic trick below solves that.
- */
- return !parseFloat(Math.round(d * 100000) / 1000000) && (d !== undefined)
- })
- .classed('zero', true);
+ //Highlight zero tick line
+ g.selectAll('.tick')
+ .filter(function (d) {
+ /*
+ The filter needs to return only ticks at or near zero.
+ Numbers like 0.00001 need to count as zero as well,
+ and the arithmetic trick below solves that.
+ */
+ return !parseFloat(Math.round(d * 100000) / 1000000) && (d !== undefined)
+ })
+ .classed('zero', true);
- //store old scales for use in transitions on update
- scale0 = scale.copy();
+ //store old scales for use in transitions on update
+ scale0 = scale.copy();
- });
+ });
- renderWatch.renderEnd('axis immediate');
- return chart;
- }
+ renderWatch.renderEnd('axis immediate');
+ return chart;
+ }
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- // expose chart's sub-components
- chart.axis = axis;
- chart.dispatch = dispatch;
+ // expose chart's sub-components
+ chart.axis = axis;
+ chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- axisLabelDistance: {get: function(){return axisLabelDistance;}, set: function(_){axisLabelDistance=_;}},
- staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},
- rotateLabels: {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}},
- rotateYLabel: {get: function(){return rotateYLabel;}, set: function(_){rotateYLabel=_;}},
- showMaxMin: {get: function(){return showMaxMin;}, set: function(_){showMaxMin=_;}},
- axisLabel: {get: function(){return axisLabelText;}, set: function(_){axisLabelText=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- ticks: {get: function(){return ticks;}, set: function(_){ticks=_;}},
- width: {get: function(){return width;}, set: function(_){width=_;}},
- fontSize: {get: function(){return fontSize;}, set: function(_){fontSize=_;}},
+ chart.options = nv.utils.optionsFunc.bind(chart);
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ axisLabelDistance: {get: function(){return axisLabelDistance;}, set: function(_){axisLabelDistance=_;}},
+ staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},
+ rotateLabels: {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}},
+ rotateYLabel: {get: function(){return rotateYLabel;}, set: function(_){rotateYLabel=_;}},
+ showMaxMin: {get: function(){return showMaxMin;}, set: function(_){showMaxMin=_;}},
+ axisLabel: {get: function(){return axisLabelText;}, set: function(_){axisLabelText=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ ticks: {get: function(){return ticks;}, set: function(_){ticks=_;}},
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ fontSize: {get: function(){return fontSize;}, set: function(_){fontSize=_;}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration=_;
- renderWatch.reset(duration);
- }},
- scale: {get: function(){return scale;}, set: function(_){
- scale = _;
- axis.scale(scale);
- isOrdinal = typeof scale.rangeBands === 'function';
- nv.utils.inheritOptionsD3(chart, scale, ['domain', 'range', 'rangeBand', 'rangeBands']);
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration=_;
+ renderWatch.reset(duration);
+ }},
+ scale: {get: function(){return scale;}, set: function(_){
+ scale = _;
+ axis.scale(scale);
+ isOrdinal = typeof scale.rangeBands === 'function';
+ nv.utils.inheritOptionsD3(chart, scale, ['domain', 'range', 'rangeBand', 'rangeBands']);
+ }}
+ });
- nv.utils.initOptions(chart);
- nv.utils.inheritOptionsD3(chart, axis, ['orient', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat']);
- nv.utils.inheritOptionsD3(chart, scale, ['domain', 'range', 'rangeBand', 'rangeBands']);
+ nv.utils.initOptions(chart);
+ nv.utils.inheritOptionsD3(chart, axis, ['orient', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat']);
+ nv.utils.inheritOptionsD3(chart, scale, ['domain', 'range', 'rangeBand', 'rangeBands']);
- return chart;
-};
-nv.models.boxPlot = function() {
- "use strict";
+ return chart;
+ };
+ nv.models.boxPlot = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var margin = {top: 0, right: 0, bottom: 0, left: 0},
- width = 960,
- height = 500,
- id = Math.floor(Math.random() * 10000), // Create semi-unique ID in case user doesn't select one
- xScale = d3.scale.ordinal(),
- yScale = d3.scale.linear(),
- getX = function(d) { return d.label }, // Default data model selectors.
- getQ1 = function(d) { return d.values.Q1 },
- getQ2 = function(d) { return d.values.Q2 },
- getQ3 = function(d) { return d.values.Q3 },
- getWl = function(d) { return d.values.whisker_low },
- getWh = function(d) { return d.values.whisker_high },
- getColor = function(d) { return d.color },
- getOlItems = function(d) { return d.values.outliers },
- getOlValue = function(d, i, j) { return d },
- getOlLabel = function(d, i, j) { return d },
- getOlColor = function(d, i, j) { return undefined },
- color = nv.utils.defaultColor(),
- container = null,
- xDomain, xRange,
- yDomain, yRange,
- dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd'),
- duration = 250,
- maxBoxWidth = null;
+ var margin = {top: 0, right: 0, bottom: 0, left: 0},
+ width = 960,
+ height = 500,
+ id = Math.floor(Math.random() * 10000), // Create semi-unique ID in case user doesn't select one
+ xScale = d3.scale.ordinal(),
+ yScale = d3.scale.linear(),
+ getX = function(d) { return d.label }, // Default data model selectors.
+ getQ1 = function(d) { return d.values.Q1 },
+ getQ2 = function(d) { return d.values.Q2 },
+ getQ3 = function(d) { return d.values.Q3 },
+ getWl = function(d) { return d.values.whisker_low },
+ getWh = function(d) { return d.values.whisker_high },
+ getColor = function(d) { return d.color },
+ getOlItems = function(d) { return d.values.outliers },
+ getOlValue = function(d, i, j) { return d },
+ getOlLabel = function(d, i, j) { return d },
+ getOlColor = function(d, i, j) { return undefined },
+ color = nv.utils.defaultColor(),
+ container = null,
+ xDomain, xRange,
+ yDomain, yRange,
+ dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd'),
+ duration = 250,
+ maxBoxWidth = null;
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var xScale0, yScale0;
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
+ var xScale0, yScale0;
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
- function chart(selection) {
- renderWatch.reset();
- selection.each(function(data) {
- var availableWidth = width - margin.left - margin.right,
- availableHeight = height - margin.top - margin.bottom;
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom;
- container = d3.select(this);
- nv.utils.initSVG(container);
+ container = d3.select(this);
+ nv.utils.initSVG(container);
- // Setup Scales
- xScale.domain(xDomain || data.map(function(d,i) { return getX(d,i); }))
- .rangeBands(xRange || [0, availableWidth], 0.1);
+ // Setup Scales
+ xScale.domain(xDomain || data.map(function(d,i) { return getX(d,i); }))
+ .rangeBands(xRange || [0, availableWidth], 0.1);
- // if we know yDomain, no need to calculate
- var yData = []
- if (!yDomain) {
- // (y-range is based on quartiles, whiskers and outliers)
- var values = [], yMin, yMax;
- data.forEach(function (d, i) {
- var q1 = getQ1(d), q3 = getQ3(d), wl = getWl(d), wh = getWh(d);
- var olItems = getOlItems(d);
- if (olItems) {
- olItems.forEach(function (e, i) {
- values.push(getOlValue(e, i, undefined));
- });
- }
- if (wl) { values.push(wl) }
- if (q1) { values.push(q1) }
- if (q3) { values.push(q3) }
- if (wh) { values.push(wh) }
- });
- yMin = d3.min(values);
- yMax = d3.max(values);
- yData = [ yMin, yMax ] ;
- }
+ // if we know yDomain, no need to calculate
+ var yData = []
+ if (!yDomain) {
+ // (y-range is based on quartiles, whiskers and outliers)
+ var values = [], yMin, yMax;
+ data.forEach(function (d, i) {
+ var q1 = getQ1(d), q3 = getQ3(d), wl = getWl(d), wh = getWh(d);
+ var olItems = getOlItems(d);
+ if (olItems) {
+ olItems.forEach(function (e, i) {
+ values.push(getOlValue(e, i, undefined));
+ });
+ }
+ if (wl) { values.push(wl) }
+ if (q1) { values.push(q1) }
+ if (q3) { values.push(q3) }
+ if (wh) { values.push(wh) }
+ });
+ yMin = d3.min(values);
+ yMax = d3.max(values);
+ yData = [ yMin, yMax ] ;
+ }
- yScale.domain(yDomain || yData);
- yScale.range(yRange || [availableHeight, 0]);
+ yScale.domain(yDomain || yData);
+ yScale.range(yRange || [availableHeight, 0]);
- //store old scales if they exist
- xScale0 = xScale0 || xScale;
- yScale0 = yScale0 || yScale.copy().range([yScale(0),yScale(0)]);
+ //store old scales if they exist
+ xScale0 = xScale0 || xScale;
+ yScale0 = yScale0 || yScale.copy().range([yScale(0),yScale(0)]);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- var boxplots = wrap.selectAll('.nv-boxplot').data(function(d) { return d });
- var boxEnter = boxplots.enter().append('g').style('stroke-opacity', 1e-6).style('fill-opacity', 1e-6);
- boxplots
- .attr('class', 'nv-boxplot')
- .attr('transform', function(d,i,j) { return 'translate(' + (xScale(getX(d,i)) + xScale.rangeBand() * 0.05) + ', 0)'; })
- .classed('hover', function(d) { return d.hover });
- boxplots
- .watchTransition(renderWatch, 'nv-boxplot: boxplots')
- .style('stroke-opacity', 1)
- .style('fill-opacity', 0.75)
- .delay(function(d,i) { return i * duration / data.length })
- .attr('transform', function(d,i) {
- return 'translate(' + (xScale(getX(d,i)) + xScale.rangeBand() * 0.05) + ', 0)';
+ var boxplots = wrap.selectAll('.nv-boxplot').data(function(d) { return d });
+ var boxEnter = boxplots.enter().append('g').style('stroke-opacity', 1e-6).style('fill-opacity', 1e-6);
+ boxplots
+ .attr('class', 'nv-boxplot')
+ .attr('transform', function(d,i,j) { return 'translate(' + (xScale(getX(d,i)) + xScale.rangeBand() * 0.05) + ', 0)'; })
+ .classed('hover', function(d) { return d.hover });
+ boxplots
+ .watchTransition(renderWatch, 'nv-boxplot: boxplots')
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', 0.75)
+ .delay(function(d,i) { return i * duration / data.length })
+ .attr('transform', function(d,i) {
+ return 'translate(' + (xScale(getX(d,i)) + xScale.rangeBand() * 0.05) + ', 0)';
+ });
+ boxplots.exit().remove();
+
+ // ----- add the SVG elements for each boxPlot -----
+
+ // conditionally append whisker lines
+ boxEnter.each(function(d,i) {
+ var box = d3.select(this);
+ [getWl, getWh].forEach(function (f) {
+ if (f(d) !== undefined && f(d) !== null) {
+ var key = (f === getWl) ? 'low' : 'high';
+ box.append('line')
+ .style('stroke', getColor(d) || color(d,i))
+ .attr('class', 'nv-boxplot-whisker nv-boxplot-' + key);
+ box.append('line')
+ .style('stroke', getColor(d) || color(d,i))
+ .attr('class', 'nv-boxplot-tick nv-boxplot-' + key);
+ }
+ });
});
- boxplots.exit().remove();
- // ----- add the SVG elements for each boxPlot -----
+ var box_width = function() { return (maxBoxWidth === null ? xScale.rangeBand() * 0.9 : Math.min(75, xScale.rangeBand() * 0.9)); };
+ var box_left = function() { return xScale.rangeBand() * 0.45 - box_width()/2; };
+ var box_right = function() { return xScale.rangeBand() * 0.45 + box_width()/2; };
- // conditionally append whisker lines
- boxEnter.each(function(d,i) {
- var box = d3.select(this);
+ // update whisker lines and ticks
[getWl, getWh].forEach(function (f) {
- if (f(d)) {
- var key = (f === getWl) ? 'low' : 'high';
- box.append('line')
- .style('stroke', getColor(d) || color(d,i))
- .attr('class', 'nv-boxplot-whisker nv-boxplot-' + key);
- box.append('line')
- .style('stroke', getColor(d) || color(d,i))
- .attr('class', 'nv-boxplot-tick nv-boxplot-' + key);
- }
+ var key = (f === getWl) ? 'low' : 'high';
+ var endpoint = (f === getWl) ? getQ1 : getQ3;
+ boxplots.select('line.nv-boxplot-whisker.nv-boxplot-' + key)
+ .watchTransition(renderWatch, 'nv-boxplot: boxplots')
+ .attr('x1', xScale.rangeBand() * 0.45 )
+ .attr('y1', function(d,i) { return yScale(f(d)); })
+ .attr('x2', xScale.rangeBand() * 0.45 )
+ .attr('y2', function(d,i) { return yScale(endpoint(d)); });
+ boxplots.select('line.nv-boxplot-tick.nv-boxplot-' + key)
+ .watchTransition(renderWatch, 'nv-boxplot: boxplots')
+ .attr('x1', box_left )
+ .attr('y1', function(d,i) { return yScale(f(d)); })
+ .attr('x2', box_right )
+ .attr('y2', function(d,i) { return yScale(f(d)); });
});
- });
- var box_width = function() { return (maxBoxWidth === null ? xScale.rangeBand() * 0.9 : Math.min(75, xScale.rangeBand() * 0.9)); };
- var box_left = function() { return xScale.rangeBand() * 0.45 - box_width()/2; };
- var box_right = function() { return xScale.rangeBand() * 0.45 + box_width()/2; };
+ [getWl, getWh].forEach(function (f) {
+ var key = (f === getWl) ? 'low' : 'high';
+ boxEnter.selectAll('.nv-boxplot-' + key)
+ .on('mouseover', function(d,i,j) {
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ series: { key: f(d), color: getColor(d) || color(d,j) },
+ e: d3.event
+ });
+ })
+ .on('mouseout', function(d,i,j) {
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ series: { key: f(d), color: getColor(d) || color(d,j) },
+ e: d3.event
+ });
+ })
+ .on('mousemove', function(d,i) {
+ dispatch.elementMousemove({e: d3.event});
+ });
+ });
- // update whisker lines and ticks
- [getWl, getWh].forEach(function (f) {
- var key = (f === getWl) ? 'low' : 'high';
- var endpoint = (f === getWl) ? getQ1 : getQ3;
- boxplots.select('line.nv-boxplot-whisker.nv-boxplot-' + key)
- .watchTransition(renderWatch, 'nv-boxplot: boxplots')
- .attr('x1', xScale.rangeBand() * 0.45 )
- .attr('y1', function(d,i) { return yScale(f(d)); })
- .attr('x2', xScale.rangeBand() * 0.45 )
- .attr('y2', function(d,i) { return yScale(endpoint(d)); });
- boxplots.select('line.nv-boxplot-tick.nv-boxplot-' + key)
- .watchTransition(renderWatch, 'nv-boxplot: boxplots')
- .attr('x1', box_left )
- .attr('y1', function(d,i) { return yScale(f(d)); })
- .attr('x2', box_right )
- .attr('y2', function(d,i) { return yScale(f(d)); });
- });
-
- [getWl, getWh].forEach(function (f) {
- var key = (f === getWl) ? 'low' : 'high';
- boxEnter.selectAll('.nv-boxplot-' + key)
- .on('mouseover', function(d,i,j) {
- d3.select(this).classed('hover', true);
- dispatch.elementMouseover({
- series: { key: f(d), color: getColor(d) || color(d,j) },
- e: d3.event
- });
- })
- .on('mouseout', function(d,i,j) {
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- series: { key: f(d), color: getColor(d) || color(d,j) },
- e: d3.event
- });
- })
- .on('mousemove', function(d,i) {
- dispatch.elementMousemove({e: d3.event});
- });
- });
-
- // boxes
- boxEnter.append('rect')
- .attr('class', 'nv-boxplot-box')
- // tooltip events
- .on('mouseover', function(d,i) {
- d3.select(this).classed('hover', true);
- dispatch.elementMouseover({
- key: getX(d),
- value: getX(d),
- series: [
- { key: 'Q3', value: getQ3(d), color: getColor(d) || color(d,i) },
- { key: 'Q2', value: getQ2(d), color: getColor(d) || color(d,i) },
- { key: 'Q1', value: getQ1(d), color: getColor(d) || color(d,i) }
- ],
- data: d,
- index: i,
- e: d3.event
+ // boxes
+ boxEnter.append('rect')
+ .attr('class', 'nv-boxplot-box')
+ // tooltip events
+ .on('mouseover', function(d,i) {
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ key: getX(d),
+ value: getX(d),
+ series: [
+ { key: 'Q3', value: getQ3(d), color: getColor(d) || color(d,i) },
+ { key: 'Q2', value: getQ2(d), color: getColor(d) || color(d,i) },
+ { key: 'Q1', value: getQ1(d), color: getColor(d) || color(d,i) }
+ ],
+ data: d,
+ index: i,
+ e: d3.event
+ });
+ })
+ .on('mouseout', function(d,i) {
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ key: getX(d),
+ value: getX(d),
+ series: [
+ { key: 'Q3', value: getQ3(d), color: getColor(d) || color(d,i) },
+ { key: 'Q2', value: getQ2(d), color: getColor(d) || color(d,i) },
+ { key: 'Q1', value: getQ1(d), color: getColor(d) || color(d,i) }
+ ],
+ data: d,
+ index: i,
+ e: d3.event
+ });
+ })
+ .on('mousemove', function(d,i) {
+ dispatch.elementMousemove({e: d3.event});
});
- })
- .on('mouseout', function(d,i) {
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- key: getX(d),
- value: getX(d),
- series: [
- { key: 'Q3', value: getQ3(d), color: getColor(d) || color(d,i) },
- { key: 'Q2', value: getQ2(d), color: getColor(d) || color(d,i) },
- { key: 'Q1', value: getQ1(d), color: getColor(d) || color(d,i) }
- ],
- data: d,
- index: i,
- e: d3.event
- });
- })
- .on('mousemove', function(d,i) {
- dispatch.elementMousemove({e: d3.event});
- });
- // box transitions
- boxplots.select('rect.nv-boxplot-box')
- .watchTransition(renderWatch, 'nv-boxplot: boxes')
- .attr('y', function(d,i) { return yScale(getQ3(d)); })
- .attr('width', box_width)
- .attr('x', box_left )
- .attr('height', function(d,i) { return Math.abs(yScale(getQ3(d)) - yScale(getQ1(d))) || 1 })
- .style('fill', function(d,i) { return getColor(d) || color(d,i) })
- .style('stroke', function(d,i) { return getColor(d) || color(d,i) });
+ // box transitions
+ boxplots.select('rect.nv-boxplot-box')
+ .watchTransition(renderWatch, 'nv-boxplot: boxes')
+ .attr('y', function(d,i) { return yScale(getQ3(d)); })
+ .attr('width', box_width)
+ .attr('x', box_left )
+ .attr('height', function(d,i) { return Math.abs(yScale(getQ3(d)) - yScale(getQ1(d))) || 1 })
+ .style('fill', function(d,i) { return getColor(d) || color(d,i) })
+ .style('stroke', function(d,i) { return getColor(d) || color(d,i) });
- // median line
- boxEnter.append('line').attr('class', 'nv-boxplot-median');
+ // median line
+ boxEnter.append('line').attr('class', 'nv-boxplot-median');
- boxplots.select('line.nv-boxplot-median')
- .watchTransition(renderWatch, 'nv-boxplot: boxplots line')
- .attr('x1', box_left)
- .attr('y1', function(d,i) { return yScale(getQ2(d)); })
- .attr('x2', box_right)
- .attr('y2', function(d,i) { return yScale(getQ2(d)); });
+ boxplots.select('line.nv-boxplot-median')
+ .watchTransition(renderWatch, 'nv-boxplot: boxplots line')
+ .attr('x1', box_left)
+ .attr('y1', function(d,i) { return yScale(getQ2(d)); })
+ .attr('x2', box_right)
+ .attr('y2', function(d,i) { return yScale(getQ2(d)); });
- // outliers
- var outliers = boxplots.selectAll('.nv-boxplot-outlier').data(function(d) {
- return getOlItems(d) || [];
- });
- outliers.enter().append('circle')
- .style('fill', function(d,i,j) { return getOlColor(d,i,j) || color(d,j) })
- .style('stroke', function(d,i,j) { return getOlColor(d,i,j) || color(d,j) })
- .style('z-index', 9000)
- .on('mouseover', function(d,i,j) {
- d3.select(this).classed('hover', true);
- dispatch.elementMouseover({
- series: { key: getOlLabel(d,i,j), color: getOlColor(d,i,j) || color(d,j) },
- e: d3.event
- });
- })
- .on('mouseout', function(d,i,j) {
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- series: { key: getOlLabel(d,i,j), color: getOlColor(d,i,j) || color(d,j) },
- e: d3.event
- });
- })
- .on('mousemove', function(d,i) {
- dispatch.elementMousemove({e: d3.event});
+ // outliers
+ var outliers = boxplots.selectAll('.nv-boxplot-outlier').data(function(d) {
+ return getOlItems(d) || [];
});
- outliers.attr('class', 'nv-boxplot-outlier');
- outliers
- .watchTransition(renderWatch, 'nv-boxplot: nv-boxplot-outlier')
- .attr('cx', xScale.rangeBand() * 0.45)
- .attr('cy', function(d,i,j) { return yScale(getOlValue(d,i,j)); })
- .attr('r', '3');
- outliers.exit().remove();
+ outliers.enter().append('circle')
+ .style('fill', function(d,i,j) { return getOlColor(d,i,j) || color(d,j) })
+ .style('stroke', function(d,i,j) { return getOlColor(d,i,j) || color(d,j) })
+ .style('z-index', 9000)
+ .on('mouseover', function(d,i,j) {
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ series: { key: getOlLabel(d,i,j), color: getOlColor(d,i,j) || color(d,j) },
+ e: d3.event
+ });
+ })
+ .on('mouseout', function(d,i,j) {
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ series: { key: getOlLabel(d,i,j), color: getOlColor(d,i,j) || color(d,j) },
+ e: d3.event
+ });
+ })
+ .on('mousemove', function(d,i) {
+ dispatch.elementMousemove({e: d3.event});
+ });
+ outliers.attr('class', 'nv-boxplot-outlier');
+ outliers
+ .watchTransition(renderWatch, 'nv-boxplot: nv-boxplot-outlier')
+ .attr('cx', xScale.rangeBand() * 0.45)
+ .attr('cy', function(d,i,j) { return yScale(getOlValue(d,i,j)); })
+ .attr('r', '3');
+ outliers.exit().remove();
- //store old scales for use in transitions on update
- xScale0 = xScale.copy();
- yScale0 = yScale.copy();
- });
+ //store old scales for use in transitions on update
+ xScale0 = xScale.copy();
+ yScale0 = yScale.copy();
+ });
- renderWatch.renderEnd('nv-boxplot immediate');
- return chart;
- }
+ renderWatch.renderEnd('nv-boxplot immediate');
+ return chart;
+ }
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- maxBoxWidth: {get: function(){return maxBoxWidth;}, set: function(_){maxBoxWidth=_;}},
- x: {get: function(){return getX;}, set: function(_){getX=_;}},
- q1: {get: function(){return getQ1;}, set: function(_){getQ1=_;}},
- q2: {get: function(){return getQ2;}, set: function(_){getQ2=_;}},
- q3: {get: function(){return getQ3;}, set: function(_){getQ3=_;}},
- wl: {get: function(){return getWl;}, set: function(_){getWl=_;}},
- wh: {get: function(){return getWh;}, set: function(_){getWh=_;}},
- itemColor: {get: function(){return getColor;}, set: function(_){getColor=_;}},
- outliers: {get: function(){return getOlItems;}, set: function(_){getOlItems=_;}},
- outlierValue: {get: function(){return getOlValue;}, set: function(_){getOlValue=_;}},
- outlierLabel: {get: function(){return getOlLabel;}, set: function(_){getOlLabel=_;}},
- outlierColor: {get: function(){return getOlColor;}, set: function(_){getOlColor=_;}},
- xScale: {get: function(){return xScale;}, set: function(_){xScale=_;}},
- yScale: {get: function(){return yScale;}, set: function(_){yScale=_;}},
- xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
- yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
- xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
- yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
- id: {get: function(){return id;}, set: function(_){id=_;}},
- // rectClass: {get: function(){return rectClass;}, set: function(_){rectClass=_;}},
- y: {
- get: function() {
- console.warn('BoxPlot \'y\' chart option is deprecated. Please use model overrides instead.');
- return {};
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ maxBoxWidth: {get: function(){return maxBoxWidth;}, set: function(_){maxBoxWidth=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ q1: {get: function(){return getQ1;}, set: function(_){getQ1=_;}},
+ q2: {get: function(){return getQ2;}, set: function(_){getQ2=_;}},
+ q3: {get: function(){return getQ3;}, set: function(_){getQ3=_;}},
+ wl: {get: function(){return getWl;}, set: function(_){getWl=_;}},
+ wh: {get: function(){return getWh;}, set: function(_){getWh=_;}},
+ itemColor: {get: function(){return getColor;}, set: function(_){getColor=_;}},
+ outliers: {get: function(){return getOlItems;}, set: function(_){getOlItems=_;}},
+ outlierValue: {get: function(){return getOlValue;}, set: function(_){getOlValue=_;}},
+ outlierLabel: {get: function(){return getOlLabel;}, set: function(_){getOlLabel=_;}},
+ outlierColor: {get: function(){return getOlColor;}, set: function(_){getOlColor=_;}},
+ xScale: {get: function(){return xScale;}, set: function(_){xScale=_;}},
+ yScale: {get: function(){return yScale;}, set: function(_){yScale=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ // rectClass: {get: function(){return rectClass;}, set: function(_){rectClass=_;}},
+ y: {
+ get: function() {
+ console.warn('BoxPlot \'y\' chart option is deprecated. Please use model overrides instead.');
+ return {};
+ },
+ set: function(_) {
+ console.warn('BoxPlot \'y\' chart option is deprecated. Please use model overrides instead.');
+ }
},
- set: function(_) {
- console.warn('BoxPlot \'y\' chart option is deprecated. Please use model overrides instead.');
- }
- },
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ }}
+ });
- nv.utils.initOptions(chart);
+ nv.utils.initOptions(chart);
- return chart;
-};
-nv.models.boxPlotChart = function() {
- "use strict";
+ return chart;
+ };
+ nv.models.boxPlotChart = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var boxplot = nv.models.boxPlot(),
- xAxis = nv.models.axis(),
- yAxis = nv.models.axis();
+ var boxplot = nv.models.boxPlot(),
+ xAxis = nv.models.axis(),
+ yAxis = nv.models.axis();
- var margin = {top: 15, right: 10, bottom: 50, left: 60},
- width = null,
- height = null,
- color = nv.utils.getColor(),
- showXAxis = true,
- showYAxis = true,
- rightAlignYAxis = false,
- staggerLabels = false,
- tooltip = nv.models.tooltip(),
- x, y,
- noData = 'No Data Available.',
- dispatch = d3.dispatch('beforeUpdate', 'renderEnd'),
- duration = 250;
+ var margin = {top: 15, right: 10, bottom: 50, left: 60},
+ width = null,
+ height = null,
+ color = nv.utils.getColor(),
+ showXAxis = true,
+ showYAxis = true,
+ rightAlignYAxis = false,
+ staggerLabels = false,
+ tooltip = nv.models.tooltip(),
+ x, y,
+ noData = 'No Data Available.',
+ dispatch = d3.dispatch('beforeUpdate', 'renderEnd'),
+ duration = 250;
- xAxis
- .orient('bottom')
- .showMaxMin(false)
- .tickFormat(function(d) { return d })
- ;
- yAxis
- .orient((rightAlignYAxis) ? 'right' : 'left')
- .tickFormat(d3.format(',.1f'))
- ;
+ xAxis
+ .orient('bottom')
+ .showMaxMin(false)
+ .tickFormat(function(d) { return d })
+ ;
+ yAxis
+ .orient((rightAlignYAxis) ? 'right' : 'left')
+ .tickFormat(d3.format(',.1f'))
+ ;
- tooltip.duration(0);
+ tooltip.duration(0);
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(boxplot);
- if (showXAxis) renderWatch.models(xAxis);
- if (showYAxis) renderWatch.models(yAxis);
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(boxplot);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
- selection.each(function(data) {
- var container = d3.select(this), that = this;
- nv.utils.initSVG(container);
- var availableWidth = (width || parseInt(container.style('width')) || 960) - margin.left - margin.right;
- var availableHeight = (height || parseInt(container.style('height')) || 400) - margin.top - margin.bottom;
+ selection.each(function(data) {
+ var container = d3.select(this), that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = (width || parseInt(container.style('width')) || 960) - margin.left - margin.right;
+ var availableHeight = (height || parseInt(container.style('height')) || 400) - margin.top - margin.bottom;
- chart.update = function() {
- dispatch.beforeUpdate();
- container.transition().duration(duration).call(chart);
- };
- chart.container = this;
+ chart.update = function() {
+ dispatch.beforeUpdate();
+ container.transition().duration(duration).call(chart);
+ };
+ chart.container = this;
- // TODO still need to find a way to validate quartile data presence using boxPlot callbacks.
- // Display No Data message if there's nothing to show. (quartiles required at minimum).
- if (!data || !data.length) {
- var noDataText = container.selectAll('.nv-noData').data([noData]);
+ // TODO still need to find a way to validate quartile data presence using boxPlot callbacks.
+ // Display No Data message if there's nothing to show. (quartiles required at minimum).
+ if (!data || !data.length) {
+ var noDataText = container.selectAll('.nv-noData').data([noData]);
- noDataText.enter().append('text')
- .attr('class', 'nvd3 nv-noData')
- .attr('dy', '-.7em')
- .style('text-anchor', 'middle');
+ noDataText.enter().append('text')
+ .attr('class', 'nvd3 nv-noData')
+ .attr('dy', '-.7em')
+ .style('text-anchor', 'middle');
- noDataText
- .attr('x', margin.left + availableWidth / 2)
- .attr('y', margin.top + availableHeight / 2)
- .text(function(d) { return d });
+ noDataText
+ .attr('x', margin.left + availableWidth / 2)
+ .attr('y', margin.top + availableHeight / 2)
+ .text(function(d) { return d });
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
- // Setup Scales
- x = boxplot.xScale();
- y = boxplot.yScale().clamp(true);
+ // Setup Scales
+ x = boxplot.xScale();
+ y = boxplot.yScale().clamp(true);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-boxPlotWithAxes').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-boxPlotWithAxes').append('g');
- var defsEnter = gEnter.append('defs');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-boxPlotWithAxes').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-boxPlotWithAxes').append('g');
+ var defsEnter = gEnter.append('defs');
+ var g = wrap.select('g');
- gEnter.append('g').attr('class', 'nv-x nv-axis');
- gEnter.append('g').attr('class', 'nv-y nv-axis')
- .append('g').attr('class', 'nv-zeroLine')
- .append('line');
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis')
+ .append('g').attr('class', 'nv-zeroLine')
+ .append('line');
- gEnter.append('g').attr('class', 'nv-barsWrap');
- g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ gEnter.append('g').attr('class', 'nv-barsWrap');
+ g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- if (rightAlignYAxis) {
- g.select('.nv-y.nv-axis')
- .attr('transform', 'translate(' + availableWidth + ',0)');
- }
+ if (rightAlignYAxis) {
+ g.select('.nv-y.nv-axis')
+ .attr('transform', 'translate(' + availableWidth + ',0)');
+ }
- // Main Chart Component(s)
- boxplot.width(availableWidth).height(availableHeight);
+ // Main Chart Component(s)
+ boxplot.width(availableWidth).height(availableHeight);
- var barsWrap = g.select('.nv-barsWrap')
- .datum(data.filter(function(d) { return !d.disabled }))
+ var barsWrap = g.select('.nv-barsWrap')
+ .datum(data.filter(function(d) { return !d.disabled }))
- barsWrap.transition().call(boxplot);
+ barsWrap.transition().call(boxplot);
- defsEnter.append('clipPath')
- .attr('id', 'nv-x-label-clip-' + boxplot.id())
- .append('rect');
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-x-label-clip-' + boxplot.id())
+ .append('rect');
- g.select('#nv-x-label-clip-' + boxplot.id() + ' rect')
- .attr('width', x.rangeBand() * (staggerLabels ? 2 : 1))
- .attr('height', 16)
- .attr('x', -x.rangeBand() / (staggerLabels ? 1 : 2 ));
+ g.select('#nv-x-label-clip-' + boxplot.id() + ' rect')
+ .attr('width', x.rangeBand() * (staggerLabels ? 2 : 1))
+ .attr('height', 16)
+ .attr('x', -x.rangeBand() / (staggerLabels ? 1 : 2 ));
- // Setup Axes
- if (showXAxis) {
- xAxis
- .scale(x)
- .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize(-availableHeight, 0);
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight, 0);
- g.select('.nv-x.nv-axis').attr('transform', 'translate(0,' + y.range()[0] + ')');
- g.select('.nv-x.nv-axis').call(xAxis);
+ g.select('.nv-x.nv-axis').attr('transform', 'translate(0,' + y.range()[0] + ')');
+ g.select('.nv-x.nv-axis').call(xAxis);
- var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
- if (staggerLabels) {
- xTicks
- .selectAll('text')
- .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 === 0 ? '5' : '17') + ')' })
+ var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
+ if (staggerLabels) {
+ xTicks
+ .selectAll('text')
+ .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 === 0 ? '5' : '17') + ')' })
+ }
}
- }
- if (showYAxis) {
- yAxis
- .scale(y)
- .ticks( Math.floor(availableHeight/36) ) // can't use nv.utils.calcTicksY with Object data
- .tickSize( -availableWidth, 0);
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ .ticks( Math.floor(availableHeight/36) ) // can't use nv.utils.calcTicksY with Object data
+ .tickSize( -availableWidth, 0);
- g.select('.nv-y.nv-axis').call(yAxis);
- }
+ g.select('.nv-y.nv-axis').call(yAxis);
+ }
- // Zero line
- g.select('.nv-zeroLine line')
- .attr('x1',0)
- .attr('x2',availableWidth)
- .attr('y1', y(0))
- .attr('y2', y(0))
- ;
+ // Zero line
+ g.select('.nv-zeroLine line')
+ .attr('x1',0)
+ .attr('x2',availableWidth)
+ .attr('y1', y(0))
+ .attr('y2', y(0))
+ ;
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
- });
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+ });
- renderWatch.renderEnd('nv-boxplot chart immediate');
- return chart;
- }
+ renderWatch.renderEnd('nv-boxplot chart immediate');
+ return chart;
+ }
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
- boxplot.dispatch.on('elementMouseover.tooltip', function(evt) {
- tooltip.data(evt).hidden(false);
- });
+ boxplot.dispatch.on('elementMouseover.tooltip', function(evt) {
+ tooltip.data(evt).hidden(false);
+ });
- boxplot.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.data(evt).hidden(true);
- });
+ boxplot.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.data(evt).hidden(true);
+ });
- boxplot.dispatch.on('elementMousemove.tooltip', function(evt) {
- tooltip();
- });
+ boxplot.dispatch.on('elementMousemove.tooltip', function(evt) {
+ tooltip();
+ });
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
- chart.boxplot = boxplot;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.tooltip = tooltip;
+ chart.dispatch = dispatch;
+ chart.boxplot = boxplot;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.tooltip = tooltip;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- boxplot.duration(duration);
- xAxis.duration(duration);
- yAxis.duration(duration);
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- boxplot.color(color);
- }},
- rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
- rightAlignYAxis = _;
- yAxis.orient( (_) ? 'right' : 'left');
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ boxplot.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ boxplot.color(color);
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( (_) ? 'right' : 'left');
+ }}
+ });
- nv.utils.inheritOptions(chart, boxplot);
- nv.utils.initOptions(chart);
+ nv.utils.inheritOptions(chart, boxplot);
+ nv.utils.initOptions(chart);
- return chart;
-}
+ return chart;
+ }
// Chart design based on the recommendations of Stephen Few. Implementation
// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
// http://projects.instantcognition.com/protovis/bulletchart/
-nv.models.bullet = function() {
- "use strict";
+ nv.models.bullet = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , orient = 'left' // TODO top & bottom
- , reverse = false
- , ranges = function(d) { return d.ranges }
- , markers = function(d) { return d.markers ? d.markers : [] }
- , markerLines = function(d) { return d.markerLines ? d.markerLines : [0] }
- , measures = function(d) { return d.measures }
- , rangeLabels = function(d) { return d.rangeLabels ? d.rangeLabels : [] }
- , markerLabels = function(d) { return d.markerLabels ? d.markerLabels : [] }
- , markerLineLabels = function(d) { return d.markerLineLabels ? d.markerLineLabels : [] }
- , measureLabels = function(d) { return d.measureLabels ? d.measureLabels : [] }
- , forceX = [0] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
- , width = 380
- , height = 30
- , container = null
- , tickFormat = null
- , color = nv.utils.getColor(['#1f77b4'])
- , dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove')
- , defaultRangeLabels = ["Maximum", "Mean", "Minimum"]
- , legacyRangeClassNames = ["Max", "Avg", "Min"]
- ;
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , orient = 'left' // TODO top & bottom
+ , reverse = false
+ , ranges = function(d) { return d.ranges }
+ , markers = function(d) { return d.markers ? d.markers : [] }
+ , markerLines = function(d) { return d.markerLines ? d.markerLines : [0] }
+ , measures = function(d) { return d.measures }
+ , rangeLabels = function(d) { return d.rangeLabels ? d.rangeLabels : [] }
+ , markerLabels = function(d) { return d.markerLabels ? d.markerLabels : [] }
+ , markerLineLabels = function(d) { return d.markerLineLabels ? d.markerLineLabels : [] }
+ , measureLabels = function(d) { return d.measureLabels ? d.measureLabels : [] }
+ , forceX = [0] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
+ , width = 380
+ , height = 30
+ , container = null
+ , tickFormat = null
+ , color = nv.utils.getColor(['#1f77b4'])
+ , dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove')
+ , defaultRangeLabels = ["Maximum", "Mean", "Minimum"]
+ , legacyRangeClassNames = ["Max", "Avg", "Min"]
+ , duration = 1000
+ ;
- function sortLabels(labels, values){
- var lz = labels.slice();
- labels.sort(function(a, b){
- var iA = lz.indexOf(a);
- var iB = lz.indexOf(b);
- return d3.descending(values[iA], values[iB]);
- });
- };
+ function sortLabels(labels, values){
+ var lz = labels.slice();
+ labels.sort(function(a, b){
+ var iA = lz.indexOf(a);
+ var iB = lz.indexOf(b);
+ return d3.descending(values[iA], values[iB]);
+ });
+ };
- function chart(selection) {
- selection.each(function(d, i) {
- var availableWidth = width - margin.left - margin.right,
- availableHeight = height - margin.top - margin.bottom;
+ function chart(selection) {
+ selection.each(function(d, i) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom;
- container = d3.select(this);
- nv.utils.initSVG(container);
+ container = d3.select(this);
+ nv.utils.initSVG(container);
- var rangez = ranges.call(this, d, i).slice(),
- markerz = markers.call(this, d, i).slice(),
- markerLinez = markerLines.call(this, d, i).slice().sort(d3.descending),
- measurez = measures.call(this, d, i).slice(),
- rangeLabelz = rangeLabels.call(this, d, i).slice(),
- markerLabelz = markerLabels.call(this, d, i).slice(),
- markerLineLabelz = markerLineLabels.call(this, d, i).slice(),
- measureLabelz = measureLabels.call(this, d, i).slice();
+ var rangez = ranges.call(this, d, i).slice(),
+ markerz = markers.call(this, d, i).slice(),
+ markerLinez = markerLines.call(this, d, i).slice(),
+ measurez = measures.call(this, d, i).slice(),
+ rangeLabelz = rangeLabels.call(this, d, i).slice(),
+ markerLabelz = markerLabels.call(this, d, i).slice(),
+ markerLineLabelz = markerLineLabels.call(this, d, i).slice(),
+ measureLabelz = measureLabels.call(this, d, i).slice();
- // Sort labels according to their sorted values
- sortLabels(rangeLabelz, rangez);
- sortLabels(markerLabelz, markerz);
- sortLabels(markerLineLabelz, markerLinez);
- sortLabels(measureLabelz, measurez);
+ // Sort labels according to their sorted values
+ sortLabels(rangeLabelz, rangez);
+ sortLabels(markerLabelz, markerz);
+ sortLabels(markerLineLabelz, markerLinez);
+ sortLabels(measureLabelz, measurez);
- // sort values descending
- rangez.sort(d3.descending);
- markerz.sort(d3.descending);
- measurez.sort(d3.descending);
+ // sort values descending
+ rangez.sort(d3.descending);
+ markerz.sort(d3.descending);
+ markerLinez.sort(d3.descending);
+ measurez.sort(d3.descending);
- // Setup Scales
- // Compute the new x-scale.
- var x1 = d3.scale.linear()
- .domain( d3.extent(d3.merge([forceX, rangez])) )
- .range(reverse ? [availableWidth, 0] : [0, availableWidth]);
+ // Setup Scales
+ // Compute the new x-scale.
+ var x1 = d3.scale.linear()
+ .domain( d3.extent(d3.merge([forceX, rangez])) )
+ .range(reverse ? [availableWidth, 0] : [0, availableWidth]);
- // Retrieve the old x-scale, if this is an update.
- var x0 = this.__chart__ || d3.scale.linear()
- .domain([0, Infinity])
- .range(x1.range());
+ // Retrieve the old x-scale, if this is an update.
+ var x0 = this.__chart__ || d3.scale.linear()
+ .domain([0, Infinity])
+ .range(x1.range());
- // Stash the new scale.
- this.__chart__ = x1;
+ // Stash the new scale.
+ this.__chart__ = x1;
- var rangeMin = d3.min(rangez), //rangez[2]
- rangeMax = d3.max(rangez), //rangez[0]
- rangeAvg = rangez[1];
+ var rangeMin = d3.min(rangez), //rangez[2]
+ rangeMax = d3.max(rangez), //rangez[0]
+ rangeAvg = rangez[1];
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-bullet').data([d]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bullet');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-bullet').data([d]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bullet');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- for(var i=0,il=rangez.length; i<il; i++){
- var rangeClassNames = 'nv-range nv-range'+i;
- if(i <= 2){
- rangeClassNames = rangeClassNames + ' nv-range'+legacyRangeClassNames[i];
+ for(var i=0,il=rangez.length; i<il; i++){
+ var rangeClassNames = 'nv-range nv-range'+i;
+ if(i <= 2){
+ rangeClassNames = rangeClassNames + ' nv-range'+legacyRangeClassNames[i];
+ }
+ gEnter.append('rect').attr('class', rangeClassNames);
}
- gEnter.append('rect').attr('class', rangeClassNames);
- }
- gEnter.append('rect').attr('class', 'nv-measure');
+ gEnter.append('rect').attr('class', 'nv-measure');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
- w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
- var xp0 = function(d) { return d < 0 ? x0(d) : x0(0) },
- xp1 = function(d) { return d < 0 ? x1(d) : x1(0) };
+ var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
+ w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
+ var xp0 = function(d) { return d < 0 ? x0(d) : x0(0) },
+ xp1 = function(d) { return d < 0 ? x1(d) : x1(0) };
- for(var i=0,il=rangez.length; i<il; i++){
- var range = rangez[i];
- g.select('rect.nv-range'+i)
- .attr('height', availableHeight)
- .attr('width', w1(range))
- .attr('x', xp1(range))
- .datum(range)
- }
+ for(var i=0,il=rangez.length; i<il; i++){
+ var range = rangez[i];
+ g.select('rect.nv-range'+i)
+ .datum(range)
+ .attr('height', availableHeight)
+ .transition()
+ .duration(duration)
+ .attr('width', w1(range))
+ .attr('x', xp1(range))
+ }
- g.select('rect.nv-measure')
- .style('fill', color)
- .attr('height', availableHeight / 3)
- .attr('y', availableHeight / 3)
- .attr('width', measurez < 0 ?
- x1(0) - x1(measurez[0])
- : x1(measurez[0]) - x1(0))
- .attr('x', xp1(measurez))
- .on('mouseover', function() {
- dispatch.elementMouseover({
- value: measurez[0],
- label: measureLabelz[0] || 'Current',
- color: d3.select(this).style("fill")
+ g.select('rect.nv-measure')
+ .style('fill', color)
+ .attr('height', availableHeight / 3)
+ .attr('y', availableHeight / 3)
+ .on('mouseover', function() {
+ dispatch.elementMouseover({
+ value: measurez[0],
+ label: measureLabelz[0] || 'Current',
+ color: d3.select(this).style("fill")
+ })
})
- })
- .on('mousemove', function() {
- dispatch.elementMousemove({
- value: measurez[0],
- label: measureLabelz[0] || 'Current',
- color: d3.select(this).style("fill")
+ .on('mousemove', function() {
+ dispatch.elementMousemove({
+ value: measurez[0],
+ label: measureLabelz[0] || 'Current',
+ color: d3.select(this).style("fill")
+ })
})
- })
- .on('mouseout', function() {
- dispatch.elementMouseout({
- value: measurez[0],
- label: measureLabelz[0] || 'Current',
- color: d3.select(this).style("fill")
+ .on('mouseout', function() {
+ dispatch.elementMouseout({
+ value: measurez[0],
+ label: measureLabelz[0] || 'Current',
+ color: d3.select(this).style("fill")
+ })
})
- });
+ .transition()
+ .duration(duration)
+ .attr('width', measurez < 0 ?
+ x1(0) - x1(measurez[0])
+ : x1(measurez[0]) - x1(0))
+ .attr('x', xp1(measurez));
- var h3 = availableHeight / 6;
+ var h3 = availableHeight / 6;
- var markerData = markerz.map( function(marker, index) {
- return {value: marker, label: markerLabelz[index]}
- });
- gEnter
- .selectAll("path.nv-markerTriangle")
- .data(markerData)
- .enter()
- .append('path')
- .attr('class', 'nv-markerTriangle')
- .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z')
- .on('mouseover', function(d) {
- dispatch.elementMouseover({
- value: d.value,
- label: d.label || 'Previous',
- color: d3.select(this).style("fill"),
- pos: [x1(d.value), availableHeight/2]
- })
+ var markerData = markerz.map( function(marker, index) {
+ return {value: marker, label: markerLabelz[index]}
+ });
+ gEnter
+ .selectAll("path.nv-markerTriangle")
+ .data(markerData)
+ .enter()
+ .append('path')
+ .attr('class', 'nv-markerTriangle')
+ .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z')
+ .on('mouseover', function(d) {
+ dispatch.elementMouseover({
+ value: d.value,
+ label: d.label || 'Previous',
+ color: d3.select(this).style("fill"),
+ pos: [x1(d.value), availableHeight/2]
+ })
- })
- .on('mousemove', function(d) {
- dispatch.elementMousemove({
- value: d.value,
- label: d.label || 'Previous',
- color: d3.select(this).style("fill")
- })
- })
- .on('mouseout', function(d, i) {
- dispatch.elementMouseout({
- value: d.value,
- label: d.label || 'Previous',
- color: d3.select(this).style("fill")
- })
- });
+ })
+ .on('mousemove', function(d) {
+ dispatch.elementMousemove({
+ value: d.value,
+ label: d.label || 'Previous',
+ color: d3.select(this).style("fill")
+ })
+ })
+ .on('mouseout', function(d, i) {
+ dispatch.elementMouseout({
+ value: d.value,
+ label: d.label || 'Previous',
+ color: d3.select(this).style("fill")
+ })
+ });
- g.selectAll("path.nv-markerTriangle")
- .data(markerData)
- .attr('transform', function(d) { return 'translate(' + x1(d.value) + ',' + (availableHeight / 2) + ')' });
+ g.selectAll("path.nv-markerTriangle")
+ .data(markerData)
+ .transition()
+ .duration(duration)
+ .attr('transform', function(d) { return 'translate(' + x1(d.value) + ',' + (availableHeight / 2) + ')' });
- var markerLinesData = markerLinez.map( function(marker, index) {
- return {value: marker, label: markerLineLabelz[index]}
- });
- gEnter
- .selectAll("path.nv-markerLine")
- .data(markerLinesData)
- .enter()
- .append('line')
- .attr('cursor', '')
- .attr('class', 'nv-markerLine')
- .attr('x1', function(d) { return x1(d.value) })
- .attr('y1', '2')
- .attr('x2', function(d) { return x1(d.value) })
- .attr('y2', availableHeight - 2)
- .on('mouseover', function(d) {
- dispatch.elementMouseover({
- value: d.value,
- label: d.label || 'Previous',
- color: d3.select(this).style("fill"),
- pos: [x1(d.value), availableHeight/2]
- })
+ var markerLinesData = markerLinez.map( function(marker, index) {
+ return {value: marker, label: markerLineLabelz[index]}
+ });
+ gEnter
+ .selectAll("line.nv-markerLine")
+ .data(markerLinesData)
+ .enter()
+ .append('line')
+ .attr('cursor', '')
+ .attr('class', 'nv-markerLine')
+ .attr('x1', function(d) { return x1(d.value) })
+ .attr('y1', '2')
+ .attr('x2', function(d) { return x1(d.value) })
+ .attr('y2', availableHeight - 2)
+ .on('mouseover', function(d) {
+ dispatch.elementMouseover({
+ value: d.value,
+ label: d.label || 'Previous',
+ color: d3.select(this).style("fill"),
+ pos: [x1(d.value), availableHeight/2]
+ })
- })
- .on('mousemove', function(d) {
- dispatch.elementMousemove({
- value: d.value,
- label: d.label || 'Previous',
- color: d3.select(this).style("fill")
- })
- })
- .on('mouseout', function(d, i) {
- dispatch.elementMouseout({
- value: d.value,
- label: d.label || 'Previous',
- color: d3.select(this).style("fill")
- })
- });
+ })
+ .on('mousemove', function(d) {
+ dispatch.elementMousemove({
+ value: d.value,
+ label: d.label || 'Previous',
+ color: d3.select(this).style("fill")
+ })
+ })
+ .on('mouseout', function(d, i) {
+ dispatch.elementMouseout({
+ value: d.value,
+ label: d.label || 'Previous',
+ color: d3.select(this).style("fill")
+ })
+ });
- g.selectAll("path.nv-markerLines")
- .data(markerLinesData)
- .attr('transform', function(d) { return 'translate(' + x1(d.value) + ',' + (availableHeight / 2) + ')' });
+ g.selectAll("line.nv-markerLine")
+ .data(markerLinesData)
+ .transition()
+ .duration(duration)
+ .attr('x1', function(d) { return x1(d.value) })
+ .attr('x2', function(d) { return x1(d.value) });
- wrap.selectAll('.nv-range')
- .on('mouseover', function(d,i) {
- var label = rangeLabelz[i] || defaultRangeLabels[i];
- dispatch.elementMouseover({
- value: d,
- label: label,
- color: d3.select(this).style("fill")
+ wrap.selectAll('.nv-range')
+ .on('mouseover', function(d,i) {
+ var label = rangeLabelz[i] || defaultRangeLabels[i];
+ dispatch.elementMouseover({
+ value: d,
+ label: label,
+ color: d3.select(this).style("fill")
+ })
})
- })
- .on('mousemove', function() {
- dispatch.elementMousemove({
- value: measurez[0],
- label: measureLabelz[0] || 'Previous',
- color: d3.select(this).style("fill")
+ .on('mousemove', function() {
+ dispatch.elementMousemove({
+ value: measurez[0],
+ label: measureLabelz[0] || 'Previous',
+ color: d3.select(this).style("fill")
+ })
})
- })
- .on('mouseout', function(d,i) {
- var label = rangeLabelz[i] || defaultRangeLabels[i];
- dispatch.elementMouseout({
- value: d,
- label: label,
- color: d3.select(this).style("fill")
- })
- });
- });
+ .on('mouseout', function(d,i) {
+ var label = rangeLabelz[i] || defaultRangeLabels[i];
+ dispatch.elementMouseout({
+ value: d,
+ label: label,
+ color: d3.select(this).style("fill")
+ })
+ });
+ });
- return chart;
- }
+ return chart;
+ }
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- ranges: {get: function(){return ranges;}, set: function(_){ranges=_;}}, // ranges (bad, satisfactory, good)
- markers: {get: function(){return markers;}, set: function(_){markers=_;}}, // markers (previous, goal)
- measures: {get: function(){return measures;}, set: function(_){measures=_;}}, // measures (actual, forecast)
- forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- tickFormat: {get: function(){return tickFormat;}, set: function(_){tickFormat=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ ranges: {get: function(){return ranges;}, set: function(_){ranges=_;}}, // ranges (bad, satisfactory, good)
+ markers: {get: function(){return markers;}, set: function(_){markers=_;}}, // markers (previous, goal)
+ measures: {get: function(){return measures;}, set: function(_){measures=_;}}, // measures (actual, forecast)
+ forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ tickFormat: {get: function(){return tickFormat;}, set: function(_){tickFormat=_;}},
+ duration: {get: function(){return duration;}, set: function(_){duration=_;}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- orient: {get: function(){return orient;}, set: function(_){ // left, right, top, bottom
- orient = _;
- reverse = orient == 'right' || orient == 'bottom';
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ orient: {get: function(){return orient;}, set: function(_){ // left, right, top, bottom
+ orient = _;
+ reverse = orient == 'right' || orient == 'bottom';
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
- nv.utils.initOptions(chart);
- return chart;
-};
+ nv.utils.initOptions(chart);
+ return chart;
+ };
// Chart design based on the recommendations of Stephen Few. Implementation
// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
// http://projects.instantcognition.com/protovis/bulletchart/
-nv.models.bulletChart = function() {
- "use strict";
+ nv.models.bulletChart = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var bullet = nv.models.bullet();
- var tooltip = nv.models.tooltip();
+ var bullet = nv.models.bullet();
+ var tooltip = nv.models.tooltip();
- var orient = 'left' // TODO top & bottom
- , reverse = false
- , margin = {top: 5, right: 40, bottom: 20, left: 120}
- , ranges = function(d) { return d.ranges }
- , markers = function(d) { return d.markers ? d.markers : [] }
- , measures = function(d) { return d.measures }
- , width = null
- , height = 55
- , tickFormat = null
- , ticks = null
- , noData = null
- , dispatch = d3.dispatch()
- ;
+ var orient = 'left' // TODO top & bottom
+ , reverse = false
+ , margin = {top: 5, right: 40, bottom: 20, left: 120}
+ , ranges = function(d) { return d.ranges }
+ , markers = function(d) { return d.markers ? d.markers : [] }
+ , measures = function(d) { return d.measures }
+ , width = null
+ , height = 55
+ , tickFormat = null
+ , ticks = null
+ , noData = null
+ , dispatch = d3.dispatch()
+ ;
- tooltip
- .duration(0)
- .headerEnabled(false);
+ tooltip
+ .duration(0)
+ .headerEnabled(false);
- function chart(selection) {
- selection.each(function(d, i) {
- var container = d3.select(this);
- nv.utils.initSVG(container);
+ function chart(selection) {
+ selection.each(function(d, i) {
+ var container = d3.select(this);
+ nv.utils.initSVG(container);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = height - margin.top - margin.bottom,
- that = this;
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = height - margin.top - margin.bottom,
+ that = this;
- chart.update = function() { chart(selection) };
- chart.container = this;
+ chart.update = function() { chart(selection) };
+ chart.container = this;
- // Display No Data message if there's nothing to show.
- if (!d || !ranges.call(this, d, i)) {
- nv.utils.noData(chart, container)
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
+ // Display No Data message if there's nothing to show.
+ if (!d || !ranges.call(this, d, i)) {
+ nv.utils.noData(chart, container)
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
- var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
- markerz = markers.call(this, d, i).slice().sort(d3.descending),
- measurez = measures.call(this, d, i).slice().sort(d3.descending);
+ var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
+ markerz = markers.call(this, d, i).slice().sort(d3.descending),
+ measurez = measures.call(this, d, i).slice().sort(d3.descending);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-bulletChart').data([d]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bulletChart');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-bulletChart').data([d]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bulletChart');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- gEnter.append('g').attr('class', 'nv-bulletWrap');
- gEnter.append('g').attr('class', 'nv-titles');
+ gEnter.append('g').attr('class', 'nv-bulletWrap');
+ gEnter.append('g').attr('class', 'nv-titles');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- // Compute the new x-scale.
- var x1 = d3.scale.linear()
- .domain([0, Math.max(rangez[0], (markerz[0] || 0), measurez[0])]) // TODO: need to allow forceX and forceY, and xDomain, yDomain
- .range(reverse ? [availableWidth, 0] : [0, availableWidth]);
+ // Compute the new x-scale.
+ var x1 = d3.scale.linear()
+ .domain([0, Math.max(rangez[0], (markerz[0] || 0), measurez[0])]) // TODO: need to allow forceX and forceY, and xDomain, yDomain
+ .range(reverse ? [availableWidth, 0] : [0, availableWidth]);
- // Retrieve the old x-scale, if this is an update.
- var x0 = this.__chart__ || d3.scale.linear()
- .domain([0, Infinity])
- .range(x1.range());
+ // Retrieve the old x-scale, if this is an update.
+ var x0 = this.__chart__ || d3.scale.linear()
+ .domain([0, Infinity])
+ .range(x1.range());
- // Stash the new scale.
- this.__chart__ = x1;
+ // Stash the new scale.
+ this.__chart__ = x1;
- var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
- w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
+ var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
+ w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
- var title = gEnter.select('.nv-titles').append('g')
- .attr('text-anchor', 'end')
- .attr('transform', 'translate(-6,' + (height - margin.top - margin.bottom) / 2 + ')');
- title.append('text')
- .attr('class', 'nv-title')
- .text(function(d) { return d.title; });
+ var title = gEnter.select('.nv-titles').append('g')
+ .attr('text-anchor', 'end')
+ .attr('transform', 'translate(-6,' + (height - margin.top - margin.bottom) / 2 + ')');
+ title.append('text')
+ .attr('class', 'nv-title')
+ .text(function(d) { return d.title; });
- title.append('text')
- .attr('class', 'nv-subtitle')
- .attr('dy', '1em')
- .text(function(d) { return d.subtitle; });
+ title.append('text')
+ .attr('class', 'nv-subtitle')
+ .attr('dy', '1em')
+ .text(function(d) { return d.subtitle; });
- bullet
- .width(availableWidth)
- .height(availableHeight)
+ bullet
+ .width(availableWidth)
+ .height(availableHeight);
- var bulletWrap = g.select('.nv-bulletWrap');
- d3.transition(bulletWrap).call(bullet);
+ var bulletWrap = g.select('.nv-bulletWrap');
+ d3.transition(bulletWrap).call(bullet);
- // Compute the tick format.
- var format = tickFormat || x1.tickFormat( availableWidth / 100 );
+ // Compute the tick format.
+ var format = tickFormat || x1.tickFormat( availableWidth / 100 );
- // Update the tick groups.
- var tick = g.selectAll('g.nv-tick')
- .data(x1.ticks( ticks ? ticks : (availableWidth / 50) ), function(d) {
- return this.textContent || format(d);
- });
+ // Update the tick groups.
+ var tick = g.selectAll('g.nv-tick')
+ .data(x1.ticks( ticks ? ticks : (availableWidth / 50) ), function(d) {
+ return this.textContent || format(d);
+ });
- // Initialize the ticks with the old scale, x0.
- var tickEnter = tick.enter().append('g')
- .attr('class', 'nv-tick')
- .attr('transform', function(d) { return 'translate(' + x0(d) + ',0)' })
- .style('opacity', 1e-6);
+ // Initialize the ticks with the old scale, x0.
+ var tickEnter = tick.enter().append('g')
+ .attr('class', 'nv-tick')
+ .attr('transform', function(d) { return 'translate(' + x0(d) + ',0)' })
+ .style('opacity', 1e-6);
- tickEnter.append('line')
- .attr('y1', availableHeight)
- .attr('y2', availableHeight * 7 / 6);
+ tickEnter.append('line')
+ .attr('y1', availableHeight)
+ .attr('y2', availableHeight * 7 / 6);
- tickEnter.append('text')
- .attr('text-anchor', 'middle')
- .attr('dy', '1em')
- .attr('y', availableHeight * 7 / 6)
- .text(format);
+ tickEnter.append('text')
+ .attr('text-anchor', 'middle')
+ .attr('dy', '1em')
+ .attr('y', availableHeight * 7 / 6)
+ .text(format);
- // Transition the updating ticks to the new scale, x1.
- var tickUpdate = d3.transition(tick)
- .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
- .style('opacity', 1);
+ // Transition the updating ticks to the new scale, x1.
+ var tickUpdate = d3.transition(tick)
+ .transition()
+ .duration(bullet.duration())
+ .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
+ .style('opacity', 1);
- tickUpdate.select('line')
- .attr('y1', availableHeight)
- .attr('y2', availableHeight * 7 / 6);
+ tickUpdate.select('line')
+ .attr('y1', availableHeight)
+ .attr('y2', availableHeight * 7 / 6);
- tickUpdate.select('text')
- .attr('y', availableHeight * 7 / 6);
+ tickUpdate.select('text')
+ .attr('y', availableHeight * 7 / 6);
- // Transition the exiting ticks to the new scale, x1.
- d3.transition(tick.exit())
- .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
- .style('opacity', 1e-6)
- .remove();
- });
+ // Transition the exiting ticks to the new scale, x1.
+ d3.transition(tick.exit())
+ .transition()
+ .duration(bullet.duration())
+ .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
+ .style('opacity', 1e-6)
+ .remove();
+ });
- d3.timer.flush();
- return chart;
- }
+ d3.timer.flush();
+ return chart;
+ }
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
- bullet.dispatch.on('elementMouseover.tooltip', function(evt) {
- evt['series'] = {
- key: evt.label,
- value: evt.value,
- color: evt.color
- };
- tooltip.data(evt).hidden(false);
- });
+ bullet.dispatch.on('elementMouseover.tooltip', function(evt) {
+ evt['series'] = {
+ key: evt.label,
+ value: evt.value,
+ color: evt.color
+ };
+ tooltip.data(evt).hidden(false);
+ });
- bullet.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true);
- });
+ bullet.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true);
+ });
- bullet.dispatch.on('elementMousemove.tooltip', function(evt) {
- tooltip();
- });
+ bullet.dispatch.on('elementMousemove.tooltip', function(evt) {
+ tooltip();
+ });
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.bullet = bullet;
- chart.dispatch = dispatch;
- chart.tooltip = tooltip;
+ chart.bullet = bullet;
+ chart.dispatch = dispatch;
+ chart.tooltip = tooltip;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- ranges: {get: function(){return ranges;}, set: function(_){ranges=_;}}, // ranges (bad, satisfactory, good)
- markers: {get: function(){return markers;}, set: function(_){markers=_;}}, // markers (previous, goal)
- measures: {get: function(){return measures;}, set: function(_){measures=_;}}, // measures (actual, forecast)
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- tickFormat: {get: function(){return tickFormat;}, set: function(_){tickFormat=_;}},
- ticks: {get: function(){return ticks;}, set: function(_){ticks=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ ranges: {get: function(){return ranges;}, set: function(_){ranges=_;}}, // ranges (bad, satisfactory, good)
+ markers: {get: function(){return markers;}, set: function(_){markers=_;}}, // markers (previous, goal)
+ measures: {get: function(){return measures;}, set: function(_){measures=_;}}, // measures (actual, forecast)
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ tickFormat: {get: function(){return tickFormat;}, set: function(_){tickFormat=_;}},
+ ticks: {get: function(){return ticks;}, set: function(_){ticks=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- orient: {get: function(){return orient;}, set: function(_){ // left, right, top, bottom
- orient = _;
- reverse = orient == 'right' || orient == 'bottom';
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ orient: {get: function(){return orient;}, set: function(_){ // left, right, top, bottom
+ orient = _;
+ reverse = orient == 'right' || orient == 'bottom';
+ }}
+ });
- nv.utils.inheritOptions(chart, bullet);
- nv.utils.initOptions(chart);
+ nv.utils.inheritOptions(chart, bullet);
+ nv.utils.initOptions(chart);
- return chart;
-};
+ return chart;
+ };
-nv.models.candlestickBar = function() {
- "use strict";
+ nv.models.candlestickBar = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = null
- , height = null
- , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
- , container
- , x = d3.scale.linear()
- , y = d3.scale.linear()
- , getX = function(d) { return d.x }
- , getY = function(d) { return d.y }
- , getOpen = function(d) { return d.open }
- , getClose = function(d) { return d.close }
- , getHigh = function(d) { return d.high }
- , getLow = function(d) { return d.low }
- , forceX = []
- , forceY = []
- , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
- , clipEdge = true
- , color = nv.utils.defaultColor()
- , interactive = false
- , xDomain
- , yDomain
- , xRange
- , yRange
- , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd', 'chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove')
- ;
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = null
+ , height = null
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , container
+ , x = d3.scale.linear()
+ , y = d3.scale.linear()
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , getOpen = function(d) { return d.open }
+ , getClose = function(d) { return d.close }
+ , getHigh = function(d) { return d.high }
+ , getLow = function(d) { return d.low }
+ , forceX = []
+ , forceY = []
+ , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
+ , clipEdge = true
+ , color = nv.utils.defaultColor()
+ , interactive = false
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd', 'chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove')
+ ;
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- function chart(selection) {
- selection.each(function(data) {
- container = d3.select(this);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ function chart(selection) {
+ selection.each(function(data) {
+ container = d3.select(this);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
- nv.utils.initSVG(container);
+ nv.utils.initSVG(container);
- // Width of the candlestick bars.
- var barWidth = (availableWidth / data[0].values.length) * .45;
+ // Width of the candlestick bars.
+ var barWidth = (availableWidth / data[0].values.length) * .45;
- // Setup Scales
- x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ));
+ // Setup Scales
+ x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ));
- if (padData)
- x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
- else
- x.range(xRange || [5 + barWidth / 2, availableWidth - barWidth / 2 - 5]);
+ if (padData)
+ x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
+ else
+ x.range(xRange || [5 + barWidth / 2, availableWidth - barWidth / 2 - 5]);
- y.domain(yDomain || [
- d3.min(data[0].values.map(getLow).concat(forceY)),
- d3.max(data[0].values.map(getHigh).concat(forceY))
- ]
- ).range(yRange || [availableHeight, 0]);
+ y.domain(yDomain || [
+ d3.min(data[0].values.map(getLow).concat(forceY)),
+ d3.max(data[0].values.map(getHigh).concat(forceY))
+ ]
+ ).range(yRange || [availableHeight, 0]);
- // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
- if (x.domain()[0] === x.domain()[1])
- x.domain()[0] ?
- x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
- : x.domain([-1,1]);
+ // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
+ if (x.domain()[0] === x.domain()[1])
+ x.domain()[0] ?
+ x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
+ : x.domain([-1,1]);
- if (y.domain()[0] === y.domain()[1])
- y.domain()[0] ?
- y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
- : y.domain([-1,1]);
+ if (y.domain()[0] === y.domain()[1])
+ y.domain()[0] ?
+ y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
+ : y.domain([-1,1]);
- // Setup containers and skeleton of chart
- var wrap = d3.select(this).selectAll('g.nv-wrap.nv-candlestickBar').data([data[0].values]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-candlestickBar');
- var defsEnter = wrapEnter.append('defs');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = d3.select(this).selectAll('g.nv-wrap.nv-candlestickBar').data([data[0].values]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-candlestickBar');
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- gEnter.append('g').attr('class', 'nv-ticks');
+ gEnter.append('g').attr('class', 'nv-ticks');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- container
- .on('click', function(d,i) {
- dispatch.chartClick({
- data: d,
- index: i,
- pos: d3.event,
- id: id
+ container
+ .on('click', function(d,i) {
+ dispatch.chartClick({
+ data: d,
+ index: i,
+ pos: d3.event,
+ id: id
+ });
});
- });
- defsEnter.append('clipPath')
- .attr('id', 'nv-chart-clip-path-' + id)
- .append('rect');
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-chart-clip-path-' + id)
+ .append('rect');
- wrap.select('#nv-chart-clip-path-' + id + ' rect')
- .attr('width', availableWidth)
- .attr('height', availableHeight);
+ wrap.select('#nv-chart-clip-path-' + id + ' rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
- g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
+ g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
- var ticks = wrap.select('.nv-ticks').selectAll('.nv-tick')
- .data(function(d) { return d });
- ticks.exit().remove();
+ var ticks = wrap.select('.nv-ticks').selectAll('.nv-tick')
+ .data(function(d) { return d });
+ ticks.exit().remove();
- var tickGroups = ticks.enter().append('g');
+ var tickGroups = ticks.enter().append('g');
- // The colors are currently controlled by CSS.
- ticks
- .attr('class', function(d, i, j) { return (getOpen(d, i) > getClose(d, i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i});
+ // The colors are currently controlled by CSS.
+ ticks
+ .attr('class', function(d, i, j) { return (getOpen(d, i) > getClose(d, i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i});
- var lines = tickGroups.append('line')
- .attr('class', 'nv-candlestick-lines')
- .attr('transform', function(d, i) { return 'translate(' + x(getX(d, i)) + ',0)'; })
- .attr('x1', 0)
- .attr('y1', function(d, i) { return y(getHigh(d, i)); })
- .attr('x2', 0)
- .attr('y2', function(d, i) { return y(getLow(d, i)); });
+ var lines = tickGroups.append('line')
+ .attr('class', 'nv-candlestick-lines')
+ .attr('transform', function(d, i) { return 'translate(' + x(getX(d, i)) + ',0)'; })
+ .attr('x1', 0)
+ .attr('y1', function(d, i) { return y(getHigh(d, i)); })
+ .attr('x2', 0)
+ .attr('y2', function(d, i) { return y(getLow(d, i)); });
- var rects = tickGroups.append('rect')
- .attr('class', 'nv-candlestick-rects nv-bars')
- .attr('transform', function(d, i) {
- return 'translate(' + (x(getX(d, i)) - barWidth/2) + ','
- + (y(getY(d, i)) - (getOpen(d, i) > getClose(d, i) ? (y(getClose(d, i)) - y(getOpen(d, i))) : 0))
- + ')';
- })
- .attr('x', 0)
- .attr('y', 0)
- .attr('width', barWidth)
- .attr('height', function(d, i) {
- var open = getOpen(d, i);
- var close = getClose(d, i);
- return open > close ? y(close) - y(open) : y(open) - y(close);
- });
+ var rects = tickGroups.append('rect')
+ .attr('class', 'nv-candlestick-rects nv-bars')
+ .attr('transform', function(d, i) {
+ return 'translate(' + (x(getX(d, i)) - barWidth/2) + ','
+ + (y(getY(d, i)) - (getOpen(d, i) > getClose(d, i) ? (y(getClose(d, i)) - y(getOpen(d, i))) : 0))
+ + ')';
+ })
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('width', barWidth)
+ .attr('height', function(d, i) {
+ var open = getOpen(d, i);
+ var close = getClose(d, i);
+ return open > close ? y(close) - y(open) : y(open) - y(close);
+ });
- ticks.select('.nv-candlestick-lines').transition()
- .attr('transform', function(d, i) { return 'translate(' + x(getX(d, i)) + ',0)'; })
- .attr('x1', 0)
- .attr('y1', function(d, i) { return y(getHigh(d, i)); })
- .attr('x2', 0)
- .attr('y2', function(d, i) { return y(getLow(d, i)); });
+ ticks.select('.nv-candlestick-lines').transition()
+ .attr('transform', function(d, i) { return 'translate(' + x(getX(d, i)) + ',0)'; })
+ .attr('x1', 0)
+ .attr('y1', function(d, i) { return y(getHigh(d, i)); })
+ .attr('x2', 0)
+ .attr('y2', function(d, i) { return y(getLow(d, i)); });
- ticks.select('.nv-candlestick-rects').transition()
- .attr('transform', function(d, i) {
- return 'translate(' + (x(getX(d, i)) - barWidth/2) + ','
- + (y(getY(d, i)) - (getOpen(d, i) > getClose(d, i) ? (y(getClose(d, i)) - y(getOpen(d, i))) : 0))
- + ')';
- })
- .attr('x', 0)
- .attr('y', 0)
- .attr('width', barWidth)
- .attr('height', function(d, i) {
- var open = getOpen(d, i);
- var close = getClose(d, i);
- return open > close ? y(close) - y(open) : y(open) - y(close);
- });
- });
+ ticks.select('.nv-candlestick-rects').transition()
+ .attr('transform', function(d, i) {
+ return 'translate(' + (x(getX(d, i)) - barWidth/2) + ','
+ + (y(getY(d, i)) - (getOpen(d, i) > getClose(d, i) ? (y(getClose(d, i)) - y(getOpen(d, i))) : 0))
+ + ')';
+ })
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('width', barWidth)
+ .attr('height', function(d, i) {
+ var open = getOpen(d, i);
+ var close = getClose(d, i);
+ return open > close ? y(close) - y(open) : y(open) - y(close);
+ });
+ });
- return chart;
- }
+ return chart;
+ }
- //Create methods to allow outside functions to highlight a specific bar.
- chart.highlightPoint = function(pointIndex, isHoverOver) {
- chart.clearHighlights();
- container.select(".nv-candlestickBar .nv-tick-0-" + pointIndex)
- .classed("hover", isHoverOver)
- ;
- };
+ //Create methods to allow outside functions to highlight a specific bar.
+ chart.highlightPoint = function(pointIndex, isHoverOver) {
+ chart.clearHighlights();
+ container.select(".nv-candlestickBar .nv-tick-0-" + pointIndex)
+ .classed("hover", isHoverOver)
+ ;
+ };
- chart.clearHighlights = function() {
- container.select(".nv-candlestickBar .nv-tick.hover")
- .classed("hover", false)
- ;
- };
+ chart.clearHighlights = function() {
+ container.select(".nv-candlestickBar .nv-tick.hover")
+ .classed("hover", false)
+ ;
+ };
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- xScale: {get: function(){return x;}, set: function(_){x=_;}},
- yScale: {get: function(){return y;}, set: function(_){y=_;}},
- xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
- yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
- xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
- yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
- forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
- forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
- padData: {get: function(){return padData;}, set: function(_){padData=_;}},
- clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
- id: {get: function(){return id;}, set: function(_){id=_;}},
- interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ padData: {get: function(){return padData;}, set: function(_){padData=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}},
- x: {get: function(){return getX;}, set: function(_){getX=_;}},
- y: {get: function(){return getY;}, set: function(_){getY=_;}},
- open: {get: function(){return getOpen();}, set: function(_){getOpen=_;}},
- close: {get: function(){return getClose();}, set: function(_){getClose=_;}},
- high: {get: function(){return getHigh;}, set: function(_){getHigh=_;}},
- low: {get: function(){return getLow;}, set: function(_){getLow=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ y: {get: function(){return getY;}, set: function(_){getY=_;}},
+ open: {get: function(){return getOpen();}, set: function(_){getOpen=_;}},
+ close: {get: function(){return getClose();}, set: function(_){getClose=_;}},
+ high: {get: function(){return getHigh;}, set: function(_){getHigh=_;}},
+ low: {get: function(){return getLow;}, set: function(_){getLow=_;}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top != undefined ? _.top : margin.top;
- margin.right = _.right != undefined ? _.right : margin.right;
- margin.bottom = _.bottom != undefined ? _.bottom : margin.bottom;
- margin.left = _.left != undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top != undefined ? _.top : margin.top;
+ margin.right = _.right != undefined ? _.right : margin.right;
+ margin.bottom = _.bottom != undefined ? _.bottom : margin.bottom;
+ margin.left = _.left != undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
- nv.utils.initOptions(chart);
- return chart;
-};
+ nv.utils.initOptions(chart);
+ return chart;
+ };
-nv.models.cumulativeLineChart = function() {
- "use strict";
+ nv.models.cumulativeLineChart = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var lines = nv.models.line()
- , xAxis = nv.models.axis()
- , yAxis = nv.models.axis()
- , legend = nv.models.legend()
- , controls = nv.models.legend()
- , interactiveLayer = nv.interactiveGuideline()
- , tooltip = nv.models.tooltip()
- ;
+ var lines = nv.models.line()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend()
+ , controls = nv.models.legend()
+ , interactiveLayer = nv.interactiveGuideline()
+ , tooltip = nv.models.tooltip()
+ ;
- var margin = {top: 30, right: 30, bottom: 50, left: 60}
- , color = nv.utils.defaultColor()
- , width = null
- , height = null
- , showLegend = true
- , showXAxis = true
- , showYAxis = true
- , rightAlignYAxis = false
- , showControls = true
- , useInteractiveGuideline = false
- , rescaleY = true
- , x //can be accessed via chart.xScale()
- , y //can be accessed via chart.yScale()
- , id = lines.id()
- , state = nv.utils.state()
- , defaultState = null
- , noData = null
- , average = function(d) { return d.average }
- , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd')
- , transitionDuration = 250
- , duration = 250
- , noErrorCheck = false //if set to TRUE, will bypass an error check in the indexify function.
- ;
+ var margin = {top: 30, right: 30, bottom: 50, left: 60}
+ , marginTop = null
+ , color = nv.utils.defaultColor()
+ , width = null
+ , height = null
+ , showLegend = true
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , showControls = true
+ , useInteractiveGuideline = false
+ , rescaleY = true
+ , x //can be accessed via chart.xScale()
+ , y //can be accessed via chart.yScale()
+ , id = lines.id()
+ , state = nv.utils.state()
+ , defaultState = null
+ , noData = null
+ , average = function(d) { return d.average }
+ , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd')
+ , transitionDuration = 250
+ , duration = 250
+ , noErrorCheck = false //if set to TRUE, will bypass an error check in the indexify function.
+ ;
- state.index = 0;
- state.rescaleY = rescaleY;
+ state.index = 0;
+ state.rescaleY = rescaleY;
- xAxis.orient('bottom').tickPadding(7);
- yAxis.orient((rightAlignYAxis) ? 'right' : 'left');
+ xAxis.orient('bottom').tickPadding(7);
+ yAxis.orient((rightAlignYAxis) ? 'right' : 'left');
- tooltip.valueFormatter(function(d, i) {
- return yAxis.tickFormat()(d, i);
- }).headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- });
+ tooltip.valueFormatter(function(d, i) {
+ return yAxis.tickFormat()(d, i);
+ }).headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ });
- controls.updateState(false);
+ controls.updateState(false);
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var dx = d3.scale.linear()
- , index = {i: 0, x: 0}
- , renderWatch = nv.utils.renderWatch(dispatch, duration)
- ;
+ var dx = d3.scale.linear()
+ , index = {i: 0, x: 0}
+ , renderWatch = nv.utils.renderWatch(dispatch, duration)
+ ;
- var stateGetter = function(data) {
- return function(){
- return {
- active: data.map(function(d) { return !d.disabled }),
- index: index.i,
- rescaleY: rescaleY
- };
- }
- };
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled }),
+ index: index.i,
+ rescaleY: rescaleY
+ };
+ }
+ };
- var stateSetter = function(data) {
- return function(state) {
- if (state.index !== undefined)
- index.i = state.index;
- if (state.rescaleY !== undefined)
- rescaleY = state.rescaleY;
- if (state.active !== undefined)
- data.forEach(function(series,i) {
- series.disabled = !state.active[i];
- });
- }
- };
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.index !== undefined)
+ index.i = state.index;
+ if (state.rescaleY !== undefined)
+ rescaleY = state.rescaleY;
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ };
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(lines);
- if (showXAxis) renderWatch.models(xAxis);
- if (showYAxis) renderWatch.models(yAxis);
- selection.each(function(data) {
- var container = d3.select(this);
- nv.utils.initSVG(container);
- container.classed('nv-chart-' + id, true);
- var that = this;
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(lines);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
+ selection.each(function(data) {
+ var container = d3.select(this);
+ nv.utils.initSVG(container);
+ container.classed('nv-chart-' + id, true);
+ var that = this;
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
- chart.update = function() {
- if (duration === 0)
- container.call(chart);
- else
- container.transition().duration(duration).call(chart)
- };
- chart.container = this;
+ chart.update = function() {
+ if (duration === 0)
+ container.call(chart);
+ else
+ container.transition().duration(duration).call(chart)
+ };
+ chart.container = this;
- state
- .setter(stateSetter(data), chart.update)
- .getter(stateGetter(data))
- .update();
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
- // DEPRECATED set state.disableddisabled
- state.disabled = data.map(function(d) { return !!d.disabled });
+ // DEPRECATED set state.disableddisabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
- if (!defaultState) {
- var key;
- defaultState = {};
- for (key in state) {
- if (state[key] instanceof Array)
- defaultState[key] = state[key].slice(0);
- else
- defaultState[key] = state[key];
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
}
- }
- var indexDrag = d3.behavior.drag()
- .on('dragstart', dragStart)
- .on('drag', dragMove)
- .on('dragend', dragEnd);
+ var indexDrag = d3.behavior.drag()
+ .on('dragstart', dragStart)
+ .on('drag', dragMove)
+ .on('dragend', dragEnd);
- function dragStart(d,i) {
- d3.select(chart.container)
- .style('cursor', 'ew-resize');
- }
+ function dragStart(d,i) {
+ d3.select(chart.container)
+ .style('cursor', 'ew-resize');
+ }
- function dragMove(d,i) {
- index.x = d3.event.x;
- index.i = Math.round(dx.invert(index.x));
- updateZero();
- }
+ function dragMove(d,i) {
+ index.x = d3.event.x;
+ index.i = Math.round(dx.invert(index.x));
+ updateZero();
+ }
- function dragEnd(d,i) {
- d3.select(chart.container)
- .style('cursor', 'auto');
+ function dragEnd(d,i) {
+ d3.select(chart.container)
+ .style('cursor', 'auto');
- // update state and send stateChange with new index
- state.index = index.i;
- dispatch.stateChange(state);
- }
+ // update state and send stateChange with new index
+ state.index = index.i;
+ dispatch.stateChange(state);
+ }
- // Display No Data message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
- nv.utils.noData(chart, container)
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ nv.utils.noData(chart, container)
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
- // Setup Scales
- x = lines.xScale();
- y = lines.yScale();
+ // Setup Scales
+ x = lines.xScale();
+ y = lines.yScale();
- if (!rescaleY) {
- var seriesDomains = data
- .filter(function(series) { return !series.disabled })
- .map(function(series,i) {
- var initialDomain = d3.extent(series.values, lines.y());
+ if (!rescaleY) {
+ var seriesDomains = data
+ .filter(function(series) { return !series.disabled })
+ .map(function(series,i) {
+ var initialDomain = d3.extent(series.values, lines.y());
- //account for series being disabled when losing 95% or more
- if (initialDomain[0] < -.95) initialDomain[0] = -.95;
+ //account for series being disabled when losing 95% or more
+ if (initialDomain[0] < -.95) initialDomain[0] = -.95;
- return [
+ return [
(initialDomain[0] - initialDomain[1]) / (1 + initialDomain[1]),
(initialDomain[1] - initialDomain[0]) / (1 + initialDomain[0])
- ];
- });
+ ];
+ });
- var completeDomain = [
- d3.min(seriesDomains, function(d) { return d[0] }),
- d3.max(seriesDomains, function(d) { return d[1] })
- ];
+ var completeDomain = [
+ d3.min(seriesDomains, function(d) { return d[0] }),
+ d3.max(seriesDomains, function(d) { return d[1] })
+ ];
- lines.yDomain(completeDomain);
- } else {
- lines.yDomain(null);
- }
+ lines.yDomain(completeDomain);
+ } else {
+ lines.yDomain(null);
+ }
- dx.domain([0, data[0].values.length - 1]) //Assumes all series have same length
- .range([0, availableWidth])
- .clamp(true);
+ dx.domain([0, data[0].values.length - 1]) //Assumes all series have same length
+ .range([0, availableWidth])
+ .clamp(true);
- var data = indexify(index.i, data);
+ var data = indexify(index.i, data);
- // Setup containers and skeleton of chart
- var interactivePointerEvents = (useInteractiveGuideline) ? "none" : "all";
- var wrap = container.selectAll('g.nv-wrap.nv-cumulativeLine').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-cumulativeLine').append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var interactivePointerEvents = (useInteractiveGuideline) ? "none" : "all";
+ var wrap = container.selectAll('g.nv-wrap.nv-cumulativeLine').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-cumulativeLine').append('g');
+ var g = wrap.select('g');
- gEnter.append('g').attr('class', 'nv-interactive');
- gEnter.append('g').attr('class', 'nv-x nv-axis').style("pointer-events","none");
- gEnter.append('g').attr('class', 'nv-y nv-axis');
- gEnter.append('g').attr('class', 'nv-background');
- gEnter.append('g').attr('class', 'nv-linesWrap').style("pointer-events",interactivePointerEvents);
- gEnter.append('g').attr('class', 'nv-avgLinesWrap').style("pointer-events","none");
- gEnter.append('g').attr('class', 'nv-legendWrap');
- gEnter.append('g').attr('class', 'nv-controlsWrap');
+ gEnter.append('g').attr('class', 'nv-interactive');
+ gEnter.append('g').attr('class', 'nv-x nv-axis').style("pointer-events","none");
+ gEnter.append('g').attr('class', 'nv-y nv-axis');
+ gEnter.append('g').attr('class', 'nv-background');
+ gEnter.append('g').attr('class', 'nv-linesWrap').style("pointer-events",interactivePointerEvents);
+ gEnter.append('g').attr('class', 'nv-avgLinesWrap').style("pointer-events","none");
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+ gEnter.append('g').attr('class', 'nv-controlsWrap');
- // Legend
- if (!showLegend) {
- g.select('.nv-legendWrap').selectAll('*').remove();
- } else {
- legend.width(availableWidth);
+ // Legend
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
+ legend.width(availableWidth);
- g.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
+ g.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
- if (legend.height() > margin.top) {
- margin.top = legend.height();
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ if (!marginTop && legend.height() !== margin.top) {
+ margin.top = legend.height();
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+ }
+
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
}
- g.select('.nv-legendWrap')
- .attr('transform', 'translate(0,' + (-margin.top) +')')
- }
+ // Controls
+ if (!showControls) {
+ g.select('.nv-controlsWrap').selectAll('*').remove();
+ } else {
+ var controlsData = [
+ { key: 'Re-scale y-axis', disabled: !rescaleY }
+ ];
- // Controls
- if (!showControls) {
- g.select('.nv-controlsWrap').selectAll('*').remove();
- } else {
- var controlsData = [
- { key: 'Re-scale y-axis', disabled: !rescaleY }
- ];
+ controls
+ .width(140)
+ .color(['#444', '#444', '#444'])
+ .rightAlign(false)
+ .margin({top: 5, right: 0, bottom: 5, left: 20})
+ ;
- controls
- .width(140)
- .color(['#444', '#444', '#444'])
- .rightAlign(false)
- .margin({top: 5, right: 0, bottom: 5, left: 20})
- ;
+ g.select('.nv-controlsWrap')
+ .datum(controlsData)
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
+ .call(controls);
+ }
- g.select('.nv-controlsWrap')
- .datum(controlsData)
- .attr('transform', 'translate(0,' + (-margin.top) +')')
- .call(controls);
- }
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
- if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
- }
+ // Show error if series goes below 100%
+ var tempDisabled = data.filter(function(d) { return d.tempDisabled });
- // Show error if series goes below 100%
- var tempDisabled = data.filter(function(d) { return d.tempDisabled });
+ wrap.select('.tempDisabled').remove(); //clean-up and prevent duplicates
+ if (tempDisabled.length) {
+ wrap.append('text').attr('class', 'tempDisabled')
+ .attr('x', availableWidth / 2)
+ .attr('y', '-.71em')
+ .style('text-anchor', 'end')
+ .text(tempDisabled.map(function(d) { return d.key }).join(', ') + ' values cannot be calculated for this time period.');
+ }
- wrap.select('.tempDisabled').remove(); //clean-up and prevent duplicates
- if (tempDisabled.length) {
- wrap.append('text').attr('class', 'tempDisabled')
- .attr('x', availableWidth / 2)
- .attr('y', '-.71em')
- .style('text-anchor', 'end')
- .text(tempDisabled.map(function(d) { return d.key }).join(', ') + ' values cannot be calculated for this time period.');
- }
+ //Set up interactive layer
+ if (useInteractiveGuideline) {
+ interactiveLayer
+ .width(availableWidth)
+ .height(availableHeight)
+ .margin({left:margin.left,top:margin.top})
+ .svgContainer(container)
+ .xScale(x);
+ wrap.select(".nv-interactive").call(interactiveLayer);
+ }
- //Set up interactive layer
- if (useInteractiveGuideline) {
- interactiveLayer
+ gEnter.select('.nv-background')
+ .append('rect');
+
+ g.select('.nv-background rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
+
+ lines
+ //.x(function(d) { return d.x })
+ .y(function(d) { return d.display.y })
.width(availableWidth)
.height(availableHeight)
- .margin({left:margin.left,top:margin.top})
- .svgContainer(container)
- .xScale(x);
- wrap.select(".nv-interactive").call(interactiveLayer);
- }
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled && !data[i].tempDisabled; }));
- gEnter.select('.nv-background')
- .append('rect');
+ var linesWrap = g.select('.nv-linesWrap')
+ .datum(data.filter(function(d) { return !d.disabled && !d.tempDisabled }));
- g.select('.nv-background rect')
- .attr('width', availableWidth)
- .attr('height', availableHeight);
+ linesWrap.call(lines);
- lines
- //.x(function(d) { return d.x })
- .y(function(d) { return d.display.y })
- .width(availableWidth)
- .height(availableHeight)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled && !data[i].tempDisabled; }));
+ //Store a series index number in the data array.
+ data.forEach(function(d,i) {
+ d.seriesIndex = i;
+ });
- var linesWrap = g.select('.nv-linesWrap')
- .datum(data.filter(function(d) { return !d.disabled && !d.tempDisabled }));
+ var avgLineData = data.filter(function(d) {
+ return !d.disabled && !!average(d);
+ });
- linesWrap.call(lines);
+ var avgLines = g.select(".nv-avgLinesWrap").selectAll("line")
+ .data(avgLineData, function(d) { return d.key; });
- //Store a series index number in the data array.
- data.forEach(function(d,i) {
- d.seriesIndex = i;
- });
+ var getAvgLineY = function(d) {
+ //If average lines go off the svg element, clamp them to the svg bounds.
+ var yVal = y(average(d));
+ if (yVal < 0) return 0;
+ if (yVal > availableHeight) return availableHeight;
+ return yVal;
+ };
- var avgLineData = data.filter(function(d) {
- return !d.disabled && !!average(d);
- });
+ avgLines.enter()
+ .append('line')
+ .style('stroke-width',2)
+ .style('stroke-dasharray','10,10')
+ .style('stroke',function (d,i) {
+ return lines.color()(d,d.seriesIndex);
+ })
+ .attr('x1',0)
+ .attr('x2',availableWidth)
+ .attr('y1', getAvgLineY)
+ .attr('y2', getAvgLineY);
- var avgLines = g.select(".nv-avgLinesWrap").selectAll("line")
- .data(avgLineData, function(d) { return d.key; });
+ avgLines
+ .style('stroke-opacity',function(d){
+ //If average lines go offscreen, make them transparent
+ var yVal = y(average(d));
+ if (yVal < 0 || yVal > availableHeight) return 0;
+ return 1;
+ })
+ .attr('x1',0)
+ .attr('x2',availableWidth)
+ .attr('y1', getAvgLineY)
+ .attr('y2', getAvgLineY);
- var getAvgLineY = function(d) {
- //If average lines go off the svg element, clamp them to the svg bounds.
- var yVal = y(average(d));
- if (yVal < 0) return 0;
- if (yVal > availableHeight) return availableHeight;
- return yVal;
- };
+ avgLines.exit().remove();
- avgLines.enter()
- .append('line')
- .style('stroke-width',2)
- .style('stroke-dasharray','10,10')
- .style('stroke',function (d,i) {
- return lines.color()(d,d.seriesIndex);
- })
- .attr('x1',0)
- .attr('x2',availableWidth)
- .attr('y1', getAvgLineY)
- .attr('y2', getAvgLineY);
+ //Create index line
+ var indexLine = linesWrap.selectAll('.nv-indexLine')
+ .data([index]);
+ indexLine.enter().append('rect').attr('class', 'nv-indexLine')
+ .attr('width', 3)
+ .attr('x', -2)
+ .attr('fill', 'red')
+ .attr('fill-opacity', .5)
+ .style("pointer-events","all")
+ .call(indexDrag);
- avgLines
- .style('stroke-opacity',function(d){
- //If average lines go offscreen, make them transparent
- var yVal = y(average(d));
- if (yVal < 0 || yVal > availableHeight) return 0;
- return 1;
- })
- .attr('x1',0)
- .attr('x2',availableWidth)
- .attr('y1', getAvgLineY)
- .attr('y2', getAvgLineY);
+ indexLine
+ .attr('transform', function(d) { return 'translate(' + dx(d.i) + ',0)' })
+ .attr('height', availableHeight);
- avgLines.exit().remove();
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ ._ticks( nv.utils.calcTicksX(availableWidth/70, data) )
+ .tickSize(-availableHeight, 0);
- //Create index line
- var indexLine = linesWrap.selectAll('.nv-indexLine')
- .data([index]);
- indexLine.enter().append('rect').attr('class', 'nv-indexLine')
- .attr('width', 3)
- .attr('x', -2)
- .attr('fill', 'red')
- .attr('fill-opacity', .5)
- .style("pointer-events","all")
- .call(indexDrag);
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y.range()[0] + ')');
+ g.select('.nv-x.nv-axis')
+ .call(xAxis);
+ }
- indexLine
- .attr('transform', function(d) { return 'translate(' + dx(d.i) + ',0)' })
- .attr('height', availableHeight);
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
- // Setup Axes
- if (showXAxis) {
- xAxis
- .scale(x)
- ._ticks( nv.utils.calcTicksX(availableWidth/70, data) )
- .tickSize(-availableHeight, 0);
+ g.select('.nv-y.nv-axis')
+ .call(yAxis);
+ }
- g.select('.nv-x.nv-axis')
- .attr('transform', 'translate(0,' + y.range()[0] + ')');
- g.select('.nv-x.nv-axis')
- .call(xAxis);
- }
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
- if (showYAxis) {
- yAxis
- .scale(y)
- ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
+ function updateZero() {
+ indexLine
+ .data([index]);
- g.select('.nv-y.nv-axis')
- .call(yAxis);
- }
+ //When dragging the index line, turn off line transitions.
+ // Then turn them back on when done dragging.
+ var oldDuration = chart.duration();
+ chart.duration(0);
+ chart.update();
+ chart.duration(oldDuration);
+ }
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
+ g.select('.nv-background rect')
+ .on('click', function() {
+ index.x = d3.mouse(this)[0];
+ index.i = Math.round(dx.invert(index.x));
- function updateZero() {
- indexLine
- .data([index]);
+ // update state and send stateChange with new index
+ state.index = index.i;
+ dispatch.stateChange(state);
- //When dragging the index line, turn off line transitions.
- // Then turn them back on when done dragging.
- var oldDuration = chart.duration();
- chart.duration(0);
- chart.update();
- chart.duration(oldDuration);
- }
+ updateZero();
+ });
- g.select('.nv-background rect')
- .on('click', function() {
- index.x = d3.mouse(this)[0];
- index.i = Math.round(dx.invert(index.x));
+ lines.dispatch.on('elementClick', function(e) {
+ index.i = e.pointIndex;
+ index.x = dx(index.i);
// update state and send stateChange with new index
state.index = index.i;
dispatch.stateChange(state);
updateZero();
});
- lines.dispatch.on('elementClick', function(e) {
- index.i = e.pointIndex;
- index.x = dx(index.i);
+ controls.dispatch.on('legendClick', function(d,i) {
+ d.disabled = !d.disabled;
+ rescaleY = !d.disabled;
- // update state and send stateChange with new index
- state.index = index.i;
- dispatch.stateChange(state);
+ state.rescaleY = rescaleY;
+ dispatch.stateChange(state);
+ chart.update();
+ });
- updateZero();
- });
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
- controls.dispatch.on('legendClick', function(d,i) {
- d.disabled = !d.disabled;
- rescaleY = !d.disabled;
+ interactiveLayer.dispatch.on('elementMousemove', function(e) {
+ lines.clearHighlights();
+ var singlePoint, pointIndex, pointXLocation, allData = [];
- state.rescaleY = rescaleY;
- dispatch.stateChange(state);
- chart.update();
- });
+ data
+ .filter(function(series, i) {
+ series.seriesIndex = i;
+ return !series.disabled;
+ })
+ .forEach(function(series,i) {
+ pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
+ lines.highlightPoint(i, pointIndex, true);
+ var point = series.values[pointIndex];
+ if (typeof point === 'undefined') return;
+ if (typeof singlePoint === 'undefined') singlePoint = point;
+ if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
+ allData.push({
+ key: series.key,
+ value: chart.y()(point, pointIndex),
+ color: color(series,series.seriesIndex)
+ });
+ });
- legend.dispatch.on('stateChange', function(newState) {
- for (var key in newState)
- state[key] = newState[key];
- dispatch.stateChange(state);
- chart.update();
- });
+ //Highlight the tooltip entry based on which point the mouse is closest to.
+ if (allData.length > 2) {
+ var yValue = chart.yScale().invert(e.mouseY);
+ var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]);
+ var threshold = 0.03 * domainExtent;
+ var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value}),yValue,threshold);
+ if (indexToHighlight !== null)
+ allData[indexToHighlight].highlight = true;
+ }
- interactiveLayer.dispatch.on('elementMousemove', function(e) {
- lines.clearHighlights();
- var singlePoint, pointIndex, pointXLocation, allData = [];
+ var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex), pointIndex);
+ interactiveLayer.tooltip
+ .valueFormatter(function(d,i) {
+ return yAxis.tickFormat()(d);
+ })
+ .data(
+ {
+ value: xValue,
+ series: allData
+ }
+ )();
- data
- .filter(function(series, i) {
- series.seriesIndex = i;
- return !series.disabled;
- })
- .forEach(function(series,i) {
- pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
- lines.highlightPoint(i, pointIndex, true);
- var point = series.values[pointIndex];
- if (typeof point === 'undefined') return;
- if (typeof singlePoint === 'undefined') singlePoint = point;
- if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
- allData.push({
- key: series.key,
- value: chart.y()(point, pointIndex),
- color: color(series,series.seriesIndex)
- });
- });
+ interactiveLayer.renderGuideLine(pointXLocation);
+ });
- //Highlight the tooltip entry based on which point the mouse is closest to.
- if (allData.length > 2) {
- var yValue = chart.yScale().invert(e.mouseY);
- var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]);
- var threshold = 0.03 * domainExtent;
- var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value}),yValue,threshold);
- if (indexToHighlight !== null)
- allData[indexToHighlight].highlight = true;
- }
+ interactiveLayer.dispatch.on("elementMouseout",function(e) {
+ lines.clearHighlights();
+ });
- var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex), pointIndex);
- interactiveLayer.tooltip
- .valueFormatter(function(d,i) {
- return yAxis.tickFormat()(d);
- })
- .data(
- {
- value: xValue,
- series: allData
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
}
- )();
- interactiveLayer.renderGuideLine(pointXLocation);
- });
+ if (typeof e.index !== 'undefined') {
+ index.i = e.index;
+ index.x = dx(index.i);
- interactiveLayer.dispatch.on("elementMouseout",function(e) {
- lines.clearHighlights();
- });
+ state.index = e.index;
- // Update chart from a state object passed to event handler
- dispatch.on('changeState', function(e) {
- if (typeof e.disabled !== 'undefined') {
- data.forEach(function(series,i) {
- series.disabled = e.disabled[i];
- });
+ indexLine
+ .data([index]);
+ }
- state.disabled = e.disabled;
- }
+ if (typeof e.rescaleY !== 'undefined') {
+ rescaleY = e.rescaleY;
+ }
- if (typeof e.index !== 'undefined') {
- index.i = e.index;
- index.x = dx(index.i);
+ chart.update();
+ });
- state.index = e.index;
+ });
- indexLine
- .data([index]);
- }
+ renderWatch.renderEnd('cumulativeLineChart immediate');
- if (typeof e.rescaleY !== 'undefined') {
- rescaleY = e.rescaleY;
- }
+ return chart;
+ }
- chart.update();
- });
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+ lines.dispatch.on('elementMouseover.tooltip', function(evt) {
+ var point = {
+ x: chart.x()(evt.point),
+ y: chart.y()(evt.point),
+ color: evt.point.color
+ };
+ evt.point = point;
+ tooltip.data(evt).hidden(false);
});
- renderWatch.renderEnd('cumulativeLineChart immediate');
+ lines.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true)
+ });
- return chart;
- }
+ //============================================================
+ // Functions
+ //------------------------------------------------------------
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
+ var indexifyYGetter = null;
+ /* Normalize the data according to an index point. */
+ function indexify(idx, data) {
+ if (!indexifyYGetter) indexifyYGetter = lines.y();
+ return data.map(function(line, i) {
+ if (!line.values) {
+ return line;
+ }
+ var indexValue = line.values[idx];
+ if (indexValue == null) {
+ return line;
+ }
+ var v = indexifyYGetter(indexValue, idx);
- lines.dispatch.on('elementMouseover.tooltip', function(evt) {
- var point = {
- x: chart.x()(evt.point),
- y: chart.y()(evt.point),
- color: evt.point.color
- };
- evt.point = point;
- tooltip.data(evt).hidden(false);
- });
+ //TODO: implement check below, and disable series if series loses 100% or more cause divide by 0 issue
+ if (v < -.95 && !noErrorCheck) {
+ //if a series loses more than 100%, calculations fail.. anything close can cause major distortion (but is mathematically correct till it hits 100)
- lines.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true)
- });
+ line.tempDisabled = true;
+ return line;
+ }
- //============================================================
- // Functions
- //------------------------------------------------------------
+ line.tempDisabled = false;
- var indexifyYGetter = null;
- /* Normalize the data according to an index point. */
- function indexify(idx, data) {
- if (!indexifyYGetter) indexifyYGetter = lines.y();
- return data.map(function(line, i) {
- if (!line.values) {
- return line;
- }
- var indexValue = line.values[idx];
- if (indexValue == null) {
- return line;
- }
- var v = indexifyYGetter(indexValue, idx);
+ line.values = line.values.map(function(point, pointIndex) {
+ point.display = {'y': (indexifyYGetter(point, pointIndex) - v) / (1 + v) };
+ return point;
+ });
- //TODO: implement check below, and disable series if series loses 100% or more cause divide by 0 issue
- if (v < -.95 && !noErrorCheck) {
- //if a series loses more than 100%, calculations fail.. anything close can cause major distortion (but is mathematically correct till it hits 100)
-
- line.tempDisabled = true;
return line;
- }
+ })
+ }
- line.tempDisabled = false;
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- line.values = line.values.map(function(point, pointIndex) {
- point.display = {'y': (indexifyYGetter(point, pointIndex) - v) / (1 + v) };
- return point;
- });
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.lines = lines;
+ chart.legend = legend;
+ chart.controls = controls;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.interactiveLayer = interactiveLayer;
+ chart.state = state;
+ chart.tooltip = tooltip;
- return line;
- })
- }
+ chart.options = nv.utils.optionsFunc.bind(chart);
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ rescaleY: {get: function(){return rescaleY;}, set: function(_){rescaleY=_;}},
+ showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ average: {get: function(){return average;}, set: function(_){average=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ noErrorCheck: {get: function(){return noErrorCheck;}, set: function(_){noErrorCheck=_;}},
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.lines = lines;
- chart.legend = legend;
- chart.controls = controls;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.interactiveLayer = interactiveLayer;
- chart.state = state;
- chart.tooltip = tooltip;
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ if (_.top !== undefined) {
+ margin.top = _.top;
+ marginTop = _.top;
+ }
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ }},
+ useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
+ useInteractiveGuideline = _;
+ if (_ === true) {
+ chart.interactive(false);
+ chart.useVoronoi(false);
+ }
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( (_) ? 'right' : 'left');
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ lines.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ renderWatch.reset(duration);
+ }}
+ });
- chart.options = nv.utils.optionsFunc.bind(chart);
+ nv.utils.inheritOptions(chart, lines);
+ nv.utils.initOptions(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- rescaleY: {get: function(){return rescaleY;}, set: function(_){rescaleY=_;}},
- showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- average: {get: function(){return average;}, set: function(_){average=_;}},
- defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- noErrorCheck: {get: function(){return noErrorCheck;}, set: function(_){noErrorCheck=_;}},
-
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- legend.color(color);
- }},
- useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
- useInteractiveGuideline = _;
- if (_ === true) {
- chart.interactive(false);
- chart.useVoronoi(false);
- }
- }},
- rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
- rightAlignYAxis = _;
- yAxis.orient( (_) ? 'right' : 'left');
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- lines.duration(duration);
- xAxis.duration(duration);
- yAxis.duration(duration);
- renderWatch.reset(duration);
- }}
- });
-
- nv.utils.inheritOptions(chart, lines);
- nv.utils.initOptions(chart);
-
- return chart;
-};
+ return chart;
+ };
//TODO: consider deprecating by adding necessary features to multiBar model
-nv.models.discreteBar = function() {
- "use strict";
+ nv.models.discreteBar = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 960
- , height = 500
- , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
- , container
- , x = d3.scale.ordinal()
- , y = d3.scale.linear()
- , getX = function(d) { return d.x }
- , getY = function(d) { return d.y }
- , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
- , color = nv.utils.defaultColor()
- , showValues = false
- , valueFormat = d3.format(',.2f')
- , xDomain
- , yDomain
- , xRange
- , yRange
- , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd')
- , rectClass = 'discreteBar'
- , duration = 250
- ;
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 960
+ , height = 500
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , container
+ , x = d3.scale.ordinal()
+ , y = d3.scale.linear()
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
+ , color = nv.utils.defaultColor()
+ , showValues = false
+ , valueFormat = d3.format(',.2f')
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd')
+ , rectClass = 'discreteBar'
+ , duration = 250
+ ;
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var x0, y0;
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
+ var x0, y0;
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
- function chart(selection) {
- renderWatch.reset();
- selection.each(function(data) {
- var availableWidth = width - margin.left - margin.right,
- availableHeight = height - margin.top - margin.bottom;
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom;
- container = d3.select(this);
- nv.utils.initSVG(container);
+ container = d3.select(this);
+ nv.utils.initSVG(container);
- //add series index to each data point for reference
- data.forEach(function(series, i) {
- series.values.forEach(function(point) {
- point.series = i;
+ //add series index to each data point for reference
+ data.forEach(function(series, i) {
+ series.values.forEach(function(point) {
+ point.series = i;
+ });
});
- });
- // Setup Scales
- // remap and flatten the data for use in calculating the scales' domains
- var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
- data.map(function(d) {
- return d.values.map(function(d,i) {
- return { x: getX(d,i), y: getY(d,i), y0: d.y0 }
- })
- });
+ // Setup Scales
+ // remap and flatten the data for use in calculating the scales' domains
+ var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
+ data.map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i), y0: d.y0 }
+ })
+ });
- x .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
- .rangeBands(xRange || [0, availableWidth], .1);
- y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y }).concat(forceY)));
+ x .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
+ .rangeBands(xRange || [0, availableWidth], .1);
+ y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y }).concat(forceY)));
- // If showValues, pad the Y axis range to account for label height
- if (showValues) y.range(yRange || [availableHeight - (y.domain()[0] < 0 ? 12 : 0), y.domain()[1] > 0 ? 12 : 0]);
- else y.range(yRange || [availableHeight, 0]);
+ // If showValues, pad the Y axis range to account for label height
+ if (showValues) y.range(yRange || [availableHeight - (y.domain()[0] < 0 ? 12 : 0), y.domain()[1] > 0 ? 12 : 0]);
+ else y.range(yRange || [availableHeight, 0]);
- //store old scales if they exist
- x0 = x0 || x;
- y0 = y0 || y.copy().range([y(0),y(0)]);
+ //store old scales if they exist
+ x0 = x0 || x;
+ y0 = y0 || y.copy().range([y(0),y(0)]);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-discretebar').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-discretebar');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-discretebar').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-discretebar');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- gEnter.append('g').attr('class', 'nv-groups');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ gEnter.append('g').attr('class', 'nv-groups');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- //TODO: by definition, the discrete bar should not have multiple groups, will modify/remove later
- var groups = wrap.select('.nv-groups').selectAll('.nv-group')
- .data(function(d) { return d }, function(d) { return d.key });
- groups.enter().append('g')
- .style('stroke-opacity', 1e-6)
- .style('fill-opacity', 1e-6);
- groups.exit()
- .watchTransition(renderWatch, 'discreteBar: exit groups')
- .style('stroke-opacity', 1e-6)
- .style('fill-opacity', 1e-6)
- .remove();
- groups
- .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
- .classed('hover', function(d) { return d.hover });
- groups
- .watchTransition(renderWatch, 'discreteBar: groups')
- .style('stroke-opacity', 1)
- .style('fill-opacity', .75);
+ //TODO: by definition, the discrete bar should not have multiple groups, will modify/remove later
+ var groups = wrap.select('.nv-groups').selectAll('.nv-group')
+ .data(function(d) { return d }, function(d) { return d.key });
+ groups.enter().append('g')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6);
+ groups.exit()
+ .watchTransition(renderWatch, 'discreteBar: exit groups')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6)
+ .remove();
+ groups
+ .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
+ .classed('hover', function(d) { return d.hover });
+ groups
+ .watchTransition(renderWatch, 'discreteBar: groups')
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', .75);
- var bars = groups.selectAll('g.nv-bar')
- .data(function(d) { return d.values });
- bars.exit().remove();
+ var bars = groups.selectAll('g.nv-bar')
+ .data(function(d) { return d.values });
+ bars.exit().remove();
- var barsEnter = bars.enter().append('g')
- .attr('transform', function(d,i,j) {
- return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05 ) + ', ' + y(0) + ')'
- })
- .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
- d3.select(this).classed('hover', true);
- dispatch.elementMouseover({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
+ var barsEnter = bars.enter().append('g')
+ .attr('transform', function(d,i,j) {
+ return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05 ) + ', ' + y(0) + ')'
+ })
+ .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ })
+ .on('mouseout', function(d,i) {
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ })
+ .on('mousemove', function(d,i) {
+ dispatch.elementMousemove({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ })
+ .on('click', function(d,i) {
+ var element = this;
+ dispatch.elementClick({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill"),
+ event: d3.event,
+ element: element
+ });
+ d3.event.stopPropagation();
+ })
+ .on('dblclick', function(d,i) {
+ dispatch.elementDblClick({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ d3.event.stopPropagation();
});
- })
- .on('mouseout', function(d,i) {
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- })
- .on('mousemove', function(d,i) {
- dispatch.elementMousemove({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- })
- .on('click', function(d,i) {
- var element = this;
- dispatch.elementClick({
- data: d,
- index: i,
- color: d3.select(this).style("fill"),
- event: d3.event,
- element: element
- });
- d3.event.stopPropagation();
- })
- .on('dblclick', function(d,i) {
- dispatch.elementDblClick({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- d3.event.stopPropagation();
- });
- barsEnter.append('rect')
- .attr('height', 0)
- .attr('width', x.rangeBand() * .9 / data.length )
+ barsEnter.append('rect')
+ .attr('height', 0)
+ .attr('width', x.rangeBand() * .9 / data.length )
- if (showValues) {
- barsEnter.append('text')
- .attr('text-anchor', 'middle')
- ;
+ if (showValues) {
+ barsEnter.append('text')
+ .attr('text-anchor', 'middle')
+ ;
- bars.select('text')
- .text(function(d,i) { return valueFormat(getY(d,i)) })
- .watchTransition(renderWatch, 'discreteBar: bars text')
- .attr('x', x.rangeBand() * .9 / 2)
- .attr('y', function(d,i) { return getY(d,i) < 0 ? y(getY(d,i)) - y(0) + 12 : -4 })
+ bars.select('text')
+ .text(function(d,i) { return valueFormat(getY(d,i)) })
+ .watchTransition(renderWatch, 'discreteBar: bars text')
+ .attr('x', x.rangeBand() * .9 / 2)
+ .attr('y', function(d,i) { return getY(d,i) < 0 ? y(getY(d,i)) - y(0) + 12 : -4 })
- ;
- } else {
- bars.selectAll('text').remove();
- }
+ ;
+ } else {
+ bars.selectAll('text').remove();
+ }
- bars
- .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive' })
- .style('fill', function(d,i) { return d.color || color(d,i) })
- .style('stroke', function(d,i) { return d.color || color(d,i) })
- .select('rect')
- .attr('class', rectClass)
- .watchTransition(renderWatch, 'discreteBar: bars rect')
- .attr('width', x.rangeBand() * .9 / data.length);
- bars.watchTransition(renderWatch, 'discreteBar: bars')
+ bars
+ .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive' })
+ .style('fill', function(d,i) { return d.color || color(d,i) })
+ .style('stroke', function(d,i) { return d.color || color(d,i) })
+ .select('rect')
+ .attr('class', rectClass)
+ .watchTransition(renderWatch, 'discreteBar: bars rect')
+ .attr('width', x.rangeBand() * .9 / data.length);
+ bars.watchTransition(renderWatch, 'discreteBar: bars')
//.delay(function(d,i) { return i * 1200 / data[0].values.length })
- .attr('transform', function(d,i) {
- var left = x(getX(d,i)) + x.rangeBand() * .05,
- top = getY(d,i) < 0 ?
- y(0) :
+ .attr('transform', function(d,i) {
+ var left = x(getX(d,i)) + x.rangeBand() * .05,
+ top = getY(d,i) < 0 ?
+ y(0) :
y(0) - y(getY(d,i)) < 1 ?
- y(0) - 1 : //make 1 px positive bars show up above y=0
- y(getY(d,i));
+ y(0) - 1 : //make 1 px positive bars show up above y=0
+ y(getY(d,i));
- return 'translate(' + left + ', ' + top + ')'
- })
- .select('rect')
- .attr('height', function(d,i) {
- return Math.max(Math.abs(y(getY(d,i)) - y(0)), 1)
- });
+ return 'translate(' + left + ', ' + top + ')'
+ })
+ .select('rect')
+ .attr('height', function(d,i) {
+ return Math.max(Math.abs(y(getY(d,i)) - y(0)), 1)
+ });
- //store old scales for use in transitions on update
- x0 = x.copy();
- y0 = y.copy();
+ //store old scales for use in transitions on update
+ x0 = x.copy();
+ y0 = y.copy();
- });
+ });
- renderWatch.renderEnd('discreteBar immediate');
- return chart;
- }
+ renderWatch.renderEnd('discreteBar immediate');
+ return chart;
+ }
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
- showValues: {get: function(){return showValues;}, set: function(_){showValues=_;}},
- x: {get: function(){return getX;}, set: function(_){getX=_;}},
- y: {get: function(){return getY;}, set: function(_){getY=_;}},
- xScale: {get: function(){return x;}, set: function(_){x=_;}},
- yScale: {get: function(){return y;}, set: function(_){y=_;}},
- xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
- yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
- xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
- yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
- valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}},
- id: {get: function(){return id;}, set: function(_){id=_;}},
- rectClass: {get: function(){return rectClass;}, set: function(_){rectClass=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ showValues: {get: function(){return showValues;}, set: function(_){showValues=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ y: {get: function(){return getY;}, set: function(_){getY=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ rectClass: {get: function(){return rectClass;}, set: function(_){rectClass=_;}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ }}
+ });
- nv.utils.initOptions(chart);
+ nv.utils.initOptions(chart);
- return chart;
-};
+ return chart;
+ };
-nv.models.discreteBarChart = function() {
- "use strict";
+ nv.models.discreteBarChart = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var discretebar = nv.models.discreteBar()
- , xAxis = nv.models.axis()
- , yAxis = nv.models.axis()
- , legend = nv.models.legend()
- , tooltip = nv.models.tooltip()
- ;
+ var discretebar = nv.models.discreteBar()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend()
+ , tooltip = nv.models.tooltip()
+ ;
- var margin = {top: 15, right: 10, bottom: 50, left: 60}
- , width = null
- , height = null
- , color = nv.utils.getColor()
- , showLegend = false
- , showXAxis = true
- , showYAxis = true
- , rightAlignYAxis = false
- , staggerLabels = false
- , wrapLabels = false
- , rotateLabels = 0
- , x
- , y
- , noData = null
- , dispatch = d3.dispatch('beforeUpdate','renderEnd')
- , duration = 250
+ var margin = {top: 15, right: 10, bottom: 50, left: 60}
+ , marginTop = null
+ , width = null
+ , height = null
+ , color = nv.utils.getColor()
+ , showLegend = false
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , staggerLabels = false
+ , wrapLabels = false
+ , rotateLabels = 0
+ , x
+ , y
+ , noData = null
+ , dispatch = d3.dispatch('beforeUpdate','renderEnd')
+ , duration = 250
+ ;
+
+ xAxis
+ .orient('bottom')
+ .showMaxMin(false)
+ .tickFormat(function(d) { return d })
;
+ yAxis
+ .orient((rightAlignYAxis) ? 'right' : 'left')
+ .tickFormat(d3.format(',.1f'))
+ ;
- xAxis
- .orient('bottom')
- .showMaxMin(false)
- .tickFormat(function(d) { return d })
- ;
- yAxis
- .orient((rightAlignYAxis) ? 'right' : 'left')
- .tickFormat(d3.format(',.1f'))
- ;
+ tooltip
+ .duration(0)
+ .headerEnabled(false)
+ .valueFormatter(function(d, i) {
+ return yAxis.tickFormat()(d, i);
+ })
+ .keyFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ });
- tooltip
- .duration(0)
- .headerEnabled(false)
- .valueFormatter(function(d, i) {
- return yAxis.tickFormat()(d, i);
- })
- .keyFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- });
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(discretebar);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(discretebar);
- if (showXAxis) renderWatch.models(xAxis);
- if (showYAxis) renderWatch.models(yAxis);
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
- selection.each(function(data) {
- var container = d3.select(this),
- that = this;
- nv.utils.initSVG(container);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ chart.update = function() {
+ dispatch.beforeUpdate();
+ container.transition().duration(duration).call(chart);
+ };
+ chart.container = this;
- chart.update = function() {
- dispatch.beforeUpdate();
- container.transition().duration(duration).call(chart);
- };
- chart.container = this;
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ nv.utils.noData(chart, container);
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
- // Display No Data message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
- nv.utils.noData(chart, container);
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
+ // Setup Scales
+ x = discretebar.xScale();
+ y = discretebar.yScale().clamp(true);
- // Setup Scales
- x = discretebar.xScale();
- y = discretebar.yScale().clamp(true);
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-discreteBarWithAxes').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-discreteBarWithAxes').append('g');
+ var defsEnter = gEnter.append('defs');
+ var g = wrap.select('g');
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-discreteBarWithAxes').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-discreteBarWithAxes').append('g');
- var defsEnter = gEnter.append('defs');
- var g = wrap.select('g');
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis')
+ .append('g').attr('class', 'nv-zeroLine')
+ .append('line');
- gEnter.append('g').attr('class', 'nv-x nv-axis');
- gEnter.append('g').attr('class', 'nv-y nv-axis')
- .append('g').attr('class', 'nv-zeroLine')
- .append('line');
+ gEnter.append('g').attr('class', 'nv-barsWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
- gEnter.append('g').attr('class', 'nv-barsWrap');
- gEnter.append('g').attr('class', 'nv-legendWrap');
+ g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ // Legend
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
+ legend.width(availableWidth);
- // Legend
- if (!showLegend) {
- g.select('.nv-legendWrap').selectAll('*').remove();
- } else {
- legend.width(availableWidth);
+ g.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
- g.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
+ if (!marginTop && legend.height() !== margin.top) {
+ margin.top = legend.height();
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+ }
- if (legend.height() > margin.top) {
- margin.top = legend.height();
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ wrap.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
}
- wrap.select('.nv-legendWrap')
- .attr('transform', 'translate(0,' + (-margin.top) +')')
- }
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
- if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
- }
+ // Main Chart Component(s)
+ discretebar
+ .width(availableWidth)
+ .height(availableHeight);
- // Main Chart Component(s)
- discretebar
- .width(availableWidth)
- .height(availableHeight);
+ var barsWrap = g.select('.nv-barsWrap')
+ .datum(data.filter(function(d) { return !d.disabled }));
- var barsWrap = g.select('.nv-barsWrap')
- .datum(data.filter(function(d) { return !d.disabled }));
+ barsWrap.transition().call(discretebar);
- barsWrap.transition().call(discretebar);
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-x-label-clip-' + discretebar.id())
+ .append('rect');
- defsEnter.append('clipPath')
- .attr('id', 'nv-x-label-clip-' + discretebar.id())
- .append('rect');
+ g.select('#nv-x-label-clip-' + discretebar.id() + ' rect')
+ .attr('width', x.rangeBand() * (staggerLabels ? 2 : 1))
+ .attr('height', 16)
+ .attr('x', -x.rangeBand() / (staggerLabels ? 1 : 2 ));
- g.select('#nv-x-label-clip-' + discretebar.id() + ' rect')
- .attr('width', x.rangeBand() * (staggerLabels ? 2 : 1))
- .attr('height', 16)
- .attr('x', -x.rangeBand() / (staggerLabels ? 1 : 2 ));
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight, 0);
- // Setup Axes
- if (showXAxis) {
- xAxis
- .scale(x)
- ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize(-availableHeight, 0);
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + (y.range()[0] + ((discretebar.showValues() && y.domain()[0] < 0) ? 16 : 0)) + ')');
+ g.select('.nv-x.nv-axis').call(xAxis);
- g.select('.nv-x.nv-axis')
- .attr('transform', 'translate(0,' + (y.range()[0] + ((discretebar.showValues() && y.domain()[0] < 0) ? 16 : 0)) + ')');
- g.select('.nv-x.nv-axis').call(xAxis);
+ var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
+ if (staggerLabels) {
+ xTicks
+ .selectAll('text')
+ .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 == 0 ? '5' : '17') + ')' })
+ }
- var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
- if (staggerLabels) {
- xTicks
- .selectAll('text')
- .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 == 0 ? '5' : '17') + ')' })
- }
+ if (rotateLabels) {
+ xTicks
+ .selectAll('.tick text')
+ .attr('transform', 'rotate(' + rotateLabels + ' 0,0)')
+ .style('text-anchor', rotateLabels > 0 ? 'start' : 'end');
+ }
- if (rotateLabels) {
- xTicks
- .selectAll('.tick text')
- .attr('transform', 'rotate(' + rotateLabels + ' 0,0)')
- .style('text-anchor', rotateLabels > 0 ? 'start' : 'end');
+ if (wrapLabels) {
+ g.selectAll('.tick text')
+ .call(nv.utils.wrapTicks, chart.xAxis.rangeBand())
+ }
}
- if (wrapLabels) {
- g.selectAll('.tick text')
- .call(nv.utils.wrapTicks, chart.xAxis.rangeBand())
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
+
+ g.select('.nv-y.nv-axis').call(yAxis);
}
- }
- if (showYAxis) {
- yAxis
- .scale(y)
- ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
+ // Zero line
+ g.select(".nv-zeroLine line")
+ .attr("x1",0)
+ .attr("x2",(rightAlignYAxis) ? -availableWidth : availableWidth)
+ .attr("y1", y(0))
+ .attr("y2", y(0))
+ ;
+ });
- g.select('.nv-y.nv-axis').call(yAxis);
- }
+ renderWatch.renderEnd('discreteBar chart immediate');
+ return chart;
+ }
- // Zero line
- g.select(".nv-zeroLine line")
- .attr("x1",0)
- .attr("x2",(rightAlignYAxis) ? -availableWidth : availableWidth)
- .attr("y1", y(0))
- .attr("y2", y(0))
- ;
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
+
+ discretebar.dispatch.on('elementMouseover.tooltip', function(evt) {
+ evt['series'] = {
+ key: chart.x()(evt.data),
+ value: chart.y()(evt.data),
+ color: evt.color
+ };
+ tooltip.data(evt).hidden(false);
});
- renderWatch.renderEnd('discreteBar chart immediate');
- return chart;
- }
+ discretebar.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true);
+ });
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
+ discretebar.dispatch.on('elementMousemove.tooltip', function(evt) {
+ tooltip();
+ });
- discretebar.dispatch.on('elementMouseover.tooltip', function(evt) {
- evt['series'] = {
- key: chart.x()(evt.data),
- value: chart.y()(evt.data),
- color: evt.color
- };
- tooltip.data(evt).hidden(false);
- });
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- discretebar.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true);
- });
+ chart.dispatch = dispatch;
+ chart.discretebar = discretebar;
+ chart.legend = legend;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.tooltip = tooltip;
- discretebar.dispatch.on('elementMousemove.tooltip', function(evt) {
- tooltip();
- });
+ chart.options = nv.utils.optionsFunc.bind(chart);
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},
+ rotateLabels: {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}},
+ wrapLabels: {get: function(){return wrapLabels;}, set: function(_){wrapLabels=!!_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- chart.dispatch = dispatch;
- chart.discretebar = discretebar;
- chart.legend = legend;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.tooltip = tooltip;
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ if (_.top !== undefined) {
+ margin.top = _.top;
+ marginTop = _.top;
+ }
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ discretebar.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ discretebar.color(color);
+ legend.color(color);
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( (_) ? 'right' : 'left');
+ }}
+ });
- chart.options = nv.utils.optionsFunc.bind(chart);
+ nv.utils.inheritOptions(chart, discretebar);
+ nv.utils.initOptions(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},
- rotateLabels: {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}},
- wrapLabels: {get: function(){return wrapLabels;}, set: function(_){wrapLabels=!!_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ return chart;
+ }
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- discretebar.duration(duration);
- xAxis.duration(duration);
- yAxis.duration(duration);
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- discretebar.color(color);
- legend.color(color);
- }},
- rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
- rightAlignYAxis = _;
- yAxis.orient( (_) ? 'right' : 'left');
- }}
- });
+ nv.models.distribution = function() {
+ "use strict";
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- nv.utils.inheritOptions(chart, discretebar);
- nv.utils.initOptions(chart);
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 400 //technically width or height depending on x or y....
+ , size = 8
+ , axis = 'x' // 'x' or 'y'... horizontal or vertical
+ , getData = function(d) { return d[axis] } // defaults d.x or d.y
+ , color = nv.utils.defaultColor()
+ , scale = d3.scale.linear()
+ , domain
+ , duration = 250
+ , dispatch = d3.dispatch('renderEnd')
+ ;
- return chart;
-}
+ //============================================================
-nv.models.distribution = function() {
- "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 400 //technically width or height depending on x or y....
- , size = 8
- , axis = 'x' // 'x' or 'y'... horizontal or vertical
- , getData = function(d) { return d[axis] } // defaults d.x or d.y
- , color = nv.utils.defaultColor()
- , scale = d3.scale.linear()
- , domain
- , duration = 250
- , dispatch = d3.dispatch('renderEnd')
- ;
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- //============================================================
+ var scale0;
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
+ //============================================================
- //============================================================
- // Private Variables
- //------------------------------------------------------------
- var scale0;
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var availableLength = width - (axis === 'x' ? margin.left + margin.right : margin.top + margin.bottom),
+ naxis = axis == 'x' ? 'y' : 'x',
+ container = d3.select(this);
+ nv.utils.initSVG(container);
- //============================================================
+ //------------------------------------------------------------
+ // Setup Scales
+ scale0 = scale0 || scale;
- function chart(selection) {
- renderWatch.reset();
- selection.each(function(data) {
- var availableLength = width - (axis === 'x' ? margin.left + margin.right : margin.top + margin.bottom),
- naxis = axis == 'x' ? 'y' : 'x',
- container = d3.select(this);
- nv.utils.initSVG(container);
+ //------------------------------------------------------------
- //------------------------------------------------------------
- // Setup Scales
- scale0 = scale0 || scale;
+ //------------------------------------------------------------
+ // Setup containers and skeleton of chart
- //------------------------------------------------------------
+ var wrap = container.selectAll('g.nv-distribution').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-distribution');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
- //------------------------------------------------------------
- // Setup containers and skeleton of chart
+ //------------------------------------------------------------
- var wrap = container.selectAll('g.nv-distribution').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-distribution');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
+ var distWrap = g.selectAll('g.nv-dist')
+ .data(function(d) { return d }, function(d) { return d.key });
- //------------------------------------------------------------
+ distWrap.enter().append('g');
+ distWrap
+ .attr('class', function(d,i) { return 'nv-dist nv-series-' + i })
+ .style('stroke', function(d,i) { return color(d, i) });
+ var dist = distWrap.selectAll('line.nv-dist' + axis)
+ .data(function(d) { return d.values })
+ dist.enter().append('line')
+ .attr(axis + '1', function(d,i) { return scale0(getData(d,i)) })
+ .attr(axis + '2', function(d,i) { return scale0(getData(d,i)) })
+ renderWatch.transition(distWrap.exit().selectAll('line.nv-dist' + axis), 'dist exit')
+ // .transition()
+ .attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
+ .attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
+ .style('stroke-opacity', 0)
+ .remove();
+ dist
+ .attr('class', function(d,i) { return 'nv-dist' + axis + ' nv-dist' + axis + '-' + i })
+ .attr(naxis + '1', 0)
+ .attr(naxis + '2', size);
+ renderWatch.transition(dist, 'dist')
+ // .transition()
+ .attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
+ .attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
- var distWrap = g.selectAll('g.nv-dist')
- .data(function(d) { return d }, function(d) { return d.key });
- distWrap.enter().append('g');
- distWrap
- .attr('class', function(d,i) { return 'nv-dist nv-series-' + i })
- .style('stroke', function(d,i) { return color(d, i) });
+ scale0 = scale.copy();
- var dist = distWrap.selectAll('line.nv-dist' + axis)
- .data(function(d) { return d.values })
- dist.enter().append('line')
- .attr(axis + '1', function(d,i) { return scale0(getData(d,i)) })
- .attr(axis + '2', function(d,i) { return scale0(getData(d,i)) })
- renderWatch.transition(distWrap.exit().selectAll('line.nv-dist' + axis), 'dist exit')
- // .transition()
- .attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
- .attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
- .style('stroke-opacity', 0)
- .remove();
- dist
- .attr('class', function(d,i) { return 'nv-dist' + axis + ' nv-dist' + axis + '-' + i })
- .attr(naxis + '1', 0)
- .attr(naxis + '2', size);
- renderWatch.transition(dist, 'dist')
- // .transition()
- .attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
- .attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
+ });
+ renderWatch.renderEnd('distribution immediate');
+ return chart;
+ }
- scale0 = scale.copy();
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+ chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.dispatch = dispatch;
- });
- renderWatch.renderEnd('distribution immediate');
- return chart;
- }
+ chart.margin = function(_) {
+ if (!arguments.length) return margin;
+ margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
+ margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
+ margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
+ margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
+ return chart;
+ };
+ chart.width = function(_) {
+ if (!arguments.length) return width;
+ width = _;
+ return chart;
+ };
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
- chart.options = nv.utils.optionsFunc.bind(chart);
- chart.dispatch = dispatch;
+ chart.axis = function(_) {
+ if (!arguments.length) return axis;
+ axis = _;
+ return chart;
+ };
- chart.margin = function(_) {
- if (!arguments.length) return margin;
- margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
- margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
- margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
- margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
- return chart;
- };
+ chart.size = function(_) {
+ if (!arguments.length) return size;
+ size = _;
+ return chart;
+ };
- chart.width = function(_) {
- if (!arguments.length) return width;
- width = _;
- return chart;
- };
+ chart.getData = function(_) {
+ if (!arguments.length) return getData;
+ getData = d3.functor(_);
+ return chart;
+ };
- chart.axis = function(_) {
- if (!arguments.length) return axis;
- axis = _;
- return chart;
- };
+ chart.scale = function(_) {
+ if (!arguments.length) return scale;
+ scale = _;
+ return chart;
+ };
- chart.size = function(_) {
- if (!arguments.length) return size;
- size = _;
- return chart;
- };
+ chart.color = function(_) {
+ if (!arguments.length) return color;
+ color = nv.utils.getColor(_);
+ return chart;
+ };
- chart.getData = function(_) {
- if (!arguments.length) return getData;
- getData = d3.functor(_);
- return chart;
- };
+ chart.duration = function(_) {
+ if (!arguments.length) return duration;
+ duration = _;
+ renderWatch.reset(duration);
+ return chart;
+ };
+ //============================================================
- chart.scale = function(_) {
- if (!arguments.length) return scale;
- scale = _;
- return chart;
- };
- chart.color = function(_) {
- if (!arguments.length) return color;
- color = nv.utils.getColor(_);
return chart;
- };
+ }
+ nv.models.focus = function(content) {
+ "use strict";
- chart.duration = function(_) {
- if (!arguments.length) return duration;
- duration = _;
- renderWatch.reset(duration);
- return chart;
- };
- //============================================================
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+ var content = content || nv.models.line()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , brush = d3.svg.brush()
+ ;
- return chart;
-}
-nv.models.focus = function(content) {
- "use strict";
+ var margin = {top: 10, right: 0, bottom: 30, left: 0}
+ , color = nv.utils.defaultColor()
+ , width = null
+ , height = 70
+ , showXAxis = true
+ , showYAxis = false
+ , rightAlignYAxis = false
+ , ticks = null
+ , x
+ , y
+ , brushExtent = null
+ , duration = 250
+ , dispatch = d3.dispatch('brush', 'onBrush', 'renderEnd')
+ , syncBrushing = true
+ ;
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ content.interactive(false);
+ content.pointActive(function(d) { return false; });
- var content = content || nv.models.line()
- , xAxis = nv.models.axis()
- , yAxis = nv.models.axis()
- , brush = d3.svg.brush()
- ;
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var margin = {top: 10, right: 0, bottom: 30, left: 0}
- , color = nv.utils.defaultColor()
- , width = null
- , height = 70
- , showXAxis = true
- , showYAxis = false
- , rightAlignYAxis = false
- , ticks = null
- , x
- , y
- , brushExtent = null
- , duration = 250
- , dispatch = d3.dispatch('brush', 'onBrush', 'renderEnd')
- ;
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
- content.interactive(false);
- content.pointActive(function(d) { return false; });
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(content);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ selection.each(function(data) {
+ var container = d3.select(this);
+ nv.utils.initSVG(container);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = height - margin.top - margin.bottom;
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
+ chart.update = function() {
+ if( duration === 0 ) {
+ container.call( chart );
+ } else {
+ container.transition().duration(duration).call(chart);
+ }
+ };
+ chart.container = this;
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(content);
- if (showXAxis) renderWatch.models(xAxis);
- if (showYAxis) renderWatch.models(yAxis);
+ // Setup Scales
+ x = content.xScale();
+ y = content.yScale();
- selection.each(function(data) {
- var container = d3.select(this);
- nv.utils.initSVG(container);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = height - margin.top - margin.bottom;
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-focus').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-focus').append('g');
+ var g = wrap.select('g');
- chart.update = function() {
- if( duration === 0 ) {
- container.call( chart );
- } else {
- container.transition().duration(duration).call(chart);
- }
- };
- chart.container = this;
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- // Setup Scales
- x = content.xScale();
- y = content.yScale();
+ gEnter.append('g').attr('class', 'nv-background').append('rect');
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis');
+ gEnter.append('g').attr('class', 'nv-contentWrap');
+ gEnter.append('g').attr('class', 'nv-brushBackground');
+ gEnter.append('g').attr('class', 'nv-x nv-brush');
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-focus').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-focus').append('g');
- var g = wrap.select('g');
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ g.select('.nv-background rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
- gEnter.append('g').attr('class', 'nv-background').append('rect');
- gEnter.append('g').attr('class', 'nv-x nv-axis');
- gEnter.append('g').attr('class', 'nv-y nv-axis');
- gEnter.append('g').attr('class', 'nv-contentWrap');
- gEnter.append('g').attr('class', 'nv-brushBackground');
- gEnter.append('g').attr('class', 'nv-x nv-brush');
+ content
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled; }));
- if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
- }
+ var contentWrap = g.select('.nv-contentWrap')
+ .datum(data.filter(function(d) { return !d.disabled; }));
- g.select('.nv-background rect')
- .attr('width', availableWidth)
- .attr('height', availableHeight);
+ d3.transition(contentWrap).call(content);
- content
- .width(availableWidth)
- .height(availableHeight)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled; }));
+ // Setup Brush
+ brush
+ .x(x)
+ .on('brush', function() {
+ onBrush(syncBrushing);
+ });
- var contentWrap = g.select('.nv-contentWrap')
- .datum(data.filter(function(d) { return !d.disabled; }));
+ brush.on('brushend', function () {
+ if (!syncBrushing) {
+ dispatch.onBrush(brush.empty() ? x.domain() : brush.extent());
+ }
+ });
- d3.transition(contentWrap).call(content);
+ if (brushExtent) brush.extent(brushExtent);
- // Setup Brush
- brush
- .x(x)
- .on('brush', function() {
- onBrush();
- });
+ var brushBG = g.select('.nv-brushBackground').selectAll('g')
+ .data([brushExtent || brush.extent()]);
- if (brushExtent) brush.extent(brushExtent);
+ var brushBGenter = brushBG.enter()
+ .append('g');
- var brushBG = g.select('.nv-brushBackground').selectAll('g')
- .data([brushExtent || brush.extent()]);
+ brushBGenter.append('rect')
+ .attr('class', 'left')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('height', availableHeight);
- var brushBGenter = brushBG.enter()
- .append('g');
+ brushBGenter.append('rect')
+ .attr('class', 'right')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('height', availableHeight);
- brushBGenter.append('rect')
- .attr('class', 'left')
- .attr('x', 0)
- .attr('y', 0)
- .attr('height', availableHeight);
+ var gBrush = g.select('.nv-x.nv-brush')
+ .call(brush);
+ gBrush.selectAll('rect')
+ .attr('height', availableHeight);
+ gBrush.selectAll('.resize').append('path').attr('d', resizePath);
- brushBGenter.append('rect')
- .attr('class', 'right')
- .attr('x', 0)
- .attr('y', 0)
- .attr('height', availableHeight);
+ onBrush(true);
- var gBrush = g.select('.nv-x.nv-brush')
- .call(brush);
- gBrush.selectAll('rect')
- .attr('height', availableHeight);
- gBrush.selectAll('.resize').append('path').attr('d', resizePath);
+ g.select('.nv-background rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
- onBrush();
+ if (showXAxis) {
+ xAxis.scale(x)
+ ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight, 0);
- g.select('.nv-background rect')
- .attr('width', availableWidth)
- .attr('height', availableHeight);
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y.range()[0] + ')');
+ d3.transition(g.select('.nv-x.nv-axis'))
+ .call(xAxis);
+ }
- if (showXAxis) {
- xAxis.scale(x)
- ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize(-availableHeight, 0);
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
+ d3.transition(g.select('.nv-y.nv-axis'))
+ .call(yAxis);
+ }
+
g.select('.nv-x.nv-axis')
.attr('transform', 'translate(0,' + y.range()[0] + ')');
- d3.transition(g.select('.nv-x.nv-axis'))
- .call(xAxis);
- }
- if (showYAxis) {
- yAxis
- .scale(y)
- ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
- d3.transition(g.select('.nv-y.nv-axis'))
- .call(yAxis);
- }
+ //============================================================
+ // Functions
+ //------------------------------------------------------------
- g.select('.nv-x.nv-axis')
- .attr('transform', 'translate(0,' + y.range()[0] + ')');
+ // Taken from crossfilter (http://square.github.com/crossfilter/)
+ function resizePath(d) {
+ var e = +(d == 'e'),
+ x = e ? 1 : -1,
+ y = availableHeight / 3;
+ return 'M' + (0.5 * x) + ',' + y
+ + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
+ + 'V' + (2 * y - 6)
+ + 'A6,6 0 0 ' + e + ' ' + (0.5 * x) + ',' + (2 * y)
+ + 'Z'
+ + 'M' + (2.5 * x) + ',' + (y + 8)
+ + 'V' + (2 * y - 8)
+ + 'M' + (4.5 * x) + ',' + (y + 8)
+ + 'V' + (2 * y - 8);
+ }
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
- //============================================================
- // Functions
- //------------------------------------------------------------
+ function updateBrushBG() {
+ if (!brush.empty()) brush.extent(brushExtent);
+ brushBG
+ .data([brush.empty() ? x.domain() : brushExtent])
+ .each(function(d,i) {
+ var leftWidth = x(d[0]) - x.range()[0],
+ rightWidth = availableWidth - x(d[1]);
+ d3.select(this).select('.left')
+ .attr('width', leftWidth < 0 ? 0 : leftWidth);
- // Taken from crossfilter (http://square.github.com/crossfilter/)
- function resizePath(d) {
- var e = +(d == 'e'),
- x = e ? 1 : -1,
- y = availableHeight / 3;
- return 'M' + (0.5 * x) + ',' + y
- + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
- + 'V' + (2 * y - 6)
- + 'A6,6 0 0 ' + e + ' ' + (0.5 * x) + ',' + (2 * y)
- + 'Z'
- + 'M' + (2.5 * x) + ',' + (y + 8)
- + 'V' + (2 * y - 8)
- + 'M' + (4.5 * x) + ',' + (y + 8)
- + 'V' + (2 * y - 8);
- }
+ d3.select(this).select('.right')
+ .attr('x', x(d[1]))
+ .attr('width', rightWidth < 0 ? 0 : rightWidth);
+ });
+ }
- function updateBrushBG() {
- if (!brush.empty()) brush.extent(brushExtent);
- brushBG
- .data([brush.empty() ? x.domain() : brushExtent])
- .each(function(d,i) {
- var leftWidth = x(d[0]) - x.range()[0],
- rightWidth = availableWidth - x(d[1]);
- d3.select(this).select('.left')
- .attr('width', leftWidth < 0 ? 0 : leftWidth);
+ function onBrush(shouldDispatch) {
+ brushExtent = brush.empty() ? null : brush.extent();
+ var extent = brush.empty() ? x.domain() : brush.extent();
+ dispatch.brush({extent: extent, brush: brush});
+ updateBrushBG();
+ if (shouldDispatch) {
+ dispatch.onBrush(extent);
+ }
+ }
+ });
- d3.select(this).select('.right')
- .attr('x', x(d[1]))
- .attr('width', rightWidth < 0 ? 0 : rightWidth);
- });
- }
+ renderWatch.renderEnd('focus immediate');
+ return chart;
+ }
- function onBrush() {
- brushExtent = brush.empty() ? null : brush.extent();
- var extent = brush.empty() ? x.domain() : brush.extent();
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
- //The brush extent cannot be less than one. If it is, don't update the line chart.
- if (Math.abs(extent[0] - extent[1]) <= 1) {
- return;
- }
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- dispatch.brush({extent: extent, brush: brush});
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.content = content;
+ chart.brush = brush;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- updateBrushBG();
- dispatch.onBrush(extent);
- }
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ brushExtent: {get: function(){return brushExtent;}, set: function(_){brushExtent=_;}},
+ syncBrushing: {get: function(){return syncBrushing;}, set: function(_){syncBrushing=_;}},
-
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ content.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ content.color(color);
+ }},
+ interpolate: {get: function(){return content.interpolate();}, set: function(_){
+ content.interpolate(_);
+ }},
+ xTickFormat: {get: function(){return xAxis.tickFormat();}, set: function(_){
+ xAxis.tickFormat(_);
+ }},
+ yTickFormat: {get: function(){return yAxis.tickFormat();}, set: function(_){
+ yAxis.tickFormat(_);
+ }},
+ x: {get: function(){return content.x();}, set: function(_){
+ content.x(_);
+ }},
+ y: {get: function(){return content.y();}, set: function(_){
+ content.y(_);
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( rightAlignYAxis ? 'right' : 'left');
+ }}
});
- renderWatch.renderEnd('focus immediate');
+ nv.utils.inheritOptions(chart, content);
+ nv.utils.initOptions(chart);
+
return chart;
- }
+ };
+ nv.models.forceDirectedGraph = function() {
+ "use strict";
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+ var margin = {top: 2, right: 0, bottom: 2, left: 0}
+ , width = 400
+ , height = 32
+ , container = null
+ , dispatch = d3.dispatch('renderEnd')
+ , color = nv.utils.getColor(['#000'])
+ , tooltip = nv.models.tooltip()
+ , noData = null
+ // Force directed graph specific parameters [default values]
+ , linkStrength = 0.1
+ , friction = 0.9
+ , linkDist = 30
+ , charge = -120
+ , gravity = 0.1
+ , theta = 0.8
+ , alpha = 0.1
+ , radius = 5
+ // These functions allow to add extra attributes to ndes and links
+ ,nodeExtras = function(nodes) { /* Do nothing */ }
+ ,linkExtras = function(links) { /* Do nothing */ }
+ ;
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.content = content;
- chart.brush = brush;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ var renderWatch = nv.utils.renderWatch(dispatch);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- brushExtent: {get: function(){return brushExtent;}, set: function(_){brushExtent=_;}},
+ function chart(selection) {
+ renderWatch.reset();
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- content.duration(duration);
- xAxis.duration(duration);
- yAxis.duration(duration);
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- content.color(color);
- }},
- interpolate: {get: function(){return content.interpolate();}, set: function(_){
- content.interpolate(_);
- }},
- xTickFormat: {get: function(){return xAxis.tickFormat();}, set: function(_){
- xAxis.tickFormat(_);
- }},
- yTickFormat: {get: function(){return yAxis.tickFormat();}, set: function(_){
- yAxis.tickFormat(_);
- }},
- x: {get: function(){return content.x();}, set: function(_){
- content.x(_);
- }},
- y: {get: function(){return content.y();}, set: function(_){
- content.y(_);
- }},
- rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
- rightAlignYAxis = _;
- yAxis.orient( rightAlignYAxis ? 'right' : 'left');
- }},
- });
+ selection.each(function(data) {
+ container = d3.select(this);
+ nv.utils.initSVG(container);
- nv.utils.inheritOptions(chart, content);
- nv.utils.initOptions(chart);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
- return chart;
-};
-nv.models.forceDirectedGraph = function() {
- "use strict";
+ container
+ .attr("width", availableWidth)
+ .attr("height", availableHeight);
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
- var margin = {top: 2, right: 0, bottom: 2, left: 0}
- , width = 400
- , height = 32
- , container = null
- , dispatch = d3.dispatch('renderEnd')
- , color = nv.utils.getColor(['#000'])
- , tooltip = nv.models.tooltip()
- , noData = null
- // Force directed graph specific parameters [default values]
- , linkStrength = 0.1
- , friction = 0.9
- , linkDist = 30
- , charge = -120
- , gravity = 0.1
- , theta = 0.8
- , alpha = 0.1
- , radius = 5
- // These functions allow to add extra attributes to ndes and links
- ,nodeExtras = function(nodes) { /* Do nothing */ }
- ,linkExtras = function(links) { /* Do nothing */ }
- ;
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.links || !data.nodes) {
+ nv.utils.noData(chart, container)
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+ container.selectAll('*').remove();
+ // Collect names of all fields in the nodes
+ var nodeFieldSet = new Set();
+ data.nodes.forEach(function(node) {
+ var keys = Object.keys(node);
+ keys.forEach(function(key) {
+ nodeFieldSet.add(key);
+ });
+ });
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ var force = d3.layout.force()
+ .nodes(data.nodes)
+ .links(data.links)
+ .size([availableWidth, availableHeight])
+ .linkStrength(linkStrength)
+ .friction(friction)
+ .linkDistance(linkDist)
+ .charge(charge)
+ .gravity(gravity)
+ .theta(theta)
+ .alpha(alpha)
+ .start();
- var renderWatch = nv.utils.renderWatch(dispatch);
+ var link = container.selectAll(".link")
+ .data(data.links)
+ .enter().append("line")
+ .attr("class", "nv-force-link")
+ .style("stroke-width", function(d) { return Math.sqrt(d.value); });
- function chart(selection) {
- renderWatch.reset();
+ var node = container.selectAll(".node")
+ .data(data.nodes)
+ .enter()
+ .append("g")
+ .attr("class", "nv-force-node")
+ .call(force.drag);
- selection.each(function(data) {
- container = d3.select(this);
- nv.utils.initSVG(container);
+ node
+ .append("circle")
+ .attr("r", radius)
+ .style("fill", function(d) { return color(d) } )
+ .on("mouseover", function(evt) {
+ container.select('.nv-series-' + evt.seriesIndex + ' .nv-distx-' + evt.pointIndex)
+ .attr('y1', evt.py);
+ container.select('.nv-series-' + evt.seriesIndex + ' .nv-disty-' + evt.pointIndex)
+ .attr('x2', evt.px);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ // Add 'series' object to
+ var nodeColor = color(evt);
+ evt.series = [];
+ nodeFieldSet.forEach(function(field) {
+ evt.series.push({
+ color: nodeColor,
+ key: field,
+ value: evt[field]
+ });
+ });
+ tooltip.data(evt).hidden(false);
+ })
+ .on("mouseout", function(d) {
+ tooltip.hidden(true);
+ });
- container
- .attr("width", availableWidth)
- .attr("height", availableHeight);
+ tooltip.headerFormatter(function(d) {return "Node";});
- // Display No Data message if there's nothing to show.
- if (!data || !data.links || !data.nodes) {
- nv.utils.noData(chart, container)
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
- container.selectAll('*').remove();
+ // Apply extra attributes to nodes and links (if any)
+ linkExtras(link);
+ nodeExtras(node);
- // Collect names of all fields in the nodes
- var nodeFieldSet = new Set();
- data.nodes.forEach(function(node) {
- var keys = Object.keys(node);
- keys.forEach(function(key) {
- nodeFieldSet.add(key);
+ force.on("tick", function() {
+ link.attr("x1", function(d) { return d.source.x; })
+ .attr("y1", function(d) { return d.source.y; })
+ .attr("x2", function(d) { return d.target.x; })
+ .attr("y2", function(d) { return d.target.y; });
+
+ node.attr("transform", function(d) {
+ return "translate(" + d.x + ", " + d.y + ")";
+ });
+ });
});
- });
- var force = d3.layout.force()
- .nodes(data.nodes)
- .links(data.links)
- .size([availableWidth, availableHeight])
- .linkStrength(linkStrength)
- .friction(friction)
- .linkDistance(linkDist)
- .charge(charge)
- .gravity(gravity)
- .theta(theta)
- .alpha(alpha)
- .start();
+ return chart;
+ }
- var link = container.selectAll(".link")
- .data(data.links)
- .enter().append("line")
- .attr("class", "nv-force-link")
- .style("stroke-width", function(d) { return Math.sqrt(d.value); });
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- var node = container.selectAll(".node")
- .data(data.nodes)
- .enter()
- .append("g")
- .attr("class", "nv-force-node")
- .call(force.drag);
+ chart.options = nv.utils.optionsFunc.bind(chart);
- node
- .append("circle")
- .attr("r", radius)
- .style("fill", function(d) { return color(d) } )
- .on("mouseover", function(evt) {
- container.select('.nv-series-' + evt.seriesIndex + ' .nv-distx-' + evt.pointIndex)
- .attr('y1', evt.py);
- container.select('.nv-series-' + evt.seriesIndex + ' .nv-disty-' + evt.pointIndex)
- .attr('x2', evt.px);
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
- // Add 'series' object to
- var nodeColor = color(evt);
- evt.series = [];
- nodeFieldSet.forEach(function(field) {
- evt.series.push({
- color: nodeColor,
- key: field,
- value: evt[field]
- });
- });
- tooltip.data(evt).hidden(false);
- })
- .on("mouseout", function(d) {
- tooltip.hidden(true);
- });
+ // Force directed graph specific parameters
+ linkStrength:{get: function(){return linkStrength;}, set: function(_){linkStrength=_;}},
+ friction: {get: function(){return friction;}, set: function(_){friction=_;}},
+ linkDist: {get: function(){return linkDist;}, set: function(_){linkDist=_;}},
+ charge: {get: function(){return charge;}, set: function(_){charge=_;}},
+ gravity: {get: function(){return gravity;}, set: function(_){gravity=_;}},
+ theta: {get: function(){return theta;}, set: function(_){theta=_;}},
+ alpha: {get: function(){return alpha;}, set: function(_){alpha=_;}},
+ radius: {get: function(){return radius;}, set: function(_){radius=_;}},
- tooltip.headerFormatter(function(d) {return "Node";});
+ //functor options
+ x: {get: function(){return getX;}, set: function(_){getX=d3.functor(_);}},
+ y: {get: function(){return getY;}, set: function(_){getY=d3.functor(_);}},
- // Apply extra attributes to nodes and links (if any)
- linkExtras(link);
- nodeExtras(node);
-
- force.on("tick", function() {
- link.attr("x1", function(d) { return d.source.x; })
- .attr("y1", function(d) { return d.source.y; })
- .attr("x2", function(d) { return d.target.x; })
- .attr("y2", function(d) { return d.target.y; });
-
- node.attr("transform", function(d) {
- return "translate(" + d.x + ", " + d.y + ")";
- });
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ nodeExtras: {get: function(){return nodeExtras;}, set: function(_){
+ nodeExtras = _;
+ }},
+ linkExtras: {get: function(){return linkExtras;}, set: function(_){
+ linkExtras = _;
+ }}
});
+ chart.dispatch = dispatch;
+ chart.tooltip = tooltip;
+ nv.utils.initOptions(chart);
return chart;
- }
+ };
+ nv.models.furiousLegend = function() {
+ "use strict";
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- chart.options = nv.utils.optionsFunc.bind(chart);
+ var margin = {top: 5, right: 0, bottom: 5, left: 0}
+ , width = 400
+ , height = 20
+ , getKey = function(d) { return d.key }
+ , keyFormatter = function (d) { return d }
+ , color = nv.utils.getColor()
+ , maxKeyLength = 20 //default value for key lengths
+ , align = true
+ , padding = 28 //define how much space between legend items. - recommend 32 for furious version
+ , rightAlign = true
+ , updateState = true //If true, legend will update data.disabled and trigger a 'stateChange' dispatch.
+ , radioButtonMode = false //If true, clicking legend items will cause it to behave like a radio button. (only one can be selected at a time)
+ , expanded = false
+ , dispatch = d3.dispatch('legendClick', 'legendDblclick', 'legendMouseover', 'legendMouseout', 'stateChange')
+ , vers = 'classic' //Options are "classic" and "furious"
+ ;
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
+ function chart(selection) {
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ container = d3.select(this);
+ nv.utils.initSVG(container);
- // Force directed graph specific parameters
- linkStrength:{get: function(){return linkStrength;}, set: function(_){linkStrength=_;}},
- friction: {get: function(){return friction;}, set: function(_){friction=_;}},
- linkDist: {get: function(){return linkDist;}, set: function(_){linkDist=_;}},
- charge: {get: function(){return charge;}, set: function(_){charge=_;}},
- gravity: {get: function(){return gravity;}, set: function(_){gravity=_;}},
- theta: {get: function(){return theta;}, set: function(_){theta=_;}},
- alpha: {get: function(){return alpha;}, set: function(_){alpha=_;}},
- radius: {get: function(){return radius;}, set: function(_){radius=_;}},
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-legend').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-legend').append('g');
+ var g = wrap.select('g');
- //functor options
- x: {get: function(){return getX;}, set: function(_){getX=d3.functor(_);}},
- y: {get: function(){return getY;}, set: function(_){getY=d3.functor(_);}},
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- nodeExtras: {get: function(){return nodeExtras;}, set: function(_){
- nodeExtras = _;
- }},
- linkExtras: {get: function(){return linkExtras;}, set: function(_){
- linkExtras = _;
- }}
- });
+ var series = g.selectAll('.nv-series')
+ .data(function(d) {
+ if(vers != 'furious') return d;
- chart.dispatch = dispatch;
- chart.tooltip = tooltip;
- nv.utils.initOptions(chart);
- return chart;
-};
-nv.models.furiousLegend = function() {
- "use strict";
+ return d.filter(function(n) {
+ return expanded ? true : !n.disengaged;
+ });
+ });
+ var seriesEnter = series.enter().append('g').attr('class', 'nv-series')
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ var seriesShape;
- var margin = {top: 5, right: 0, bottom: 5, left: 0}
- , width = 400
- , height = 20
- , getKey = function(d) { return d.key }
- , keyFormatter = function (d) { return d }
- , color = nv.utils.getColor()
- , maxKeyLength = 20 //default value for key lengths
- , align = true
- , padding = 28 //define how much space between legend items. - recommend 32 for furious version
- , rightAlign = true
- , updateState = true //If true, legend will update data.disabled and trigger a 'stateChange' dispatch.
- , radioButtonMode = false //If true, clicking legend items will cause it to behave like a radio button. (only one can be selected at a time)
- , expanded = false
- , dispatch = d3.dispatch('legendClick', 'legendDblclick', 'legendMouseover', 'legendMouseout', 'stateChange')
- , vers = 'classic' //Options are "classic" and "furious"
- ;
+ if(vers == 'classic') {
+ seriesEnter.append('circle')
+ .style('stroke-width', 2)
+ .attr('class','nv-legend-symbol')
+ .attr('r', 5);
- function chart(selection) {
- selection.each(function(data) {
- var availableWidth = width - margin.left - margin.right,
- container = d3.select(this);
- nv.utils.initSVG(container);
+ seriesShape = series.select('circle');
+ } else if (vers == 'furious') {
+ seriesEnter.append('rect')
+ .style('stroke-width', 2)
+ .attr('class','nv-legend-symbol')
+ .attr('rx', 3)
+ .attr('ry', 3);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-legend').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-legend').append('g');
- var g = wrap.select('g');
+ seriesShape = series.select('rect');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ seriesEnter.append('g')
+ .attr('class', 'nv-check-box')
+ .property('innerHTML','<path d="M0.5,5 L22.5,5 L22.5,26.5 L0.5,26.5 L0.5,5 Z" class="nv-box"></path><path d="M5.5,12.8618467 L11.9185089,19.2803556 L31,0.198864511" class="nv-check"></path>')
+ .attr('transform', 'translate(-10,-8)scale(0.5)');
- var series = g.selectAll('.nv-series')
- .data(function(d) {
- if(vers != 'furious') return d;
+ var seriesCheckbox = series.select('.nv-check-box');
- return d.filter(function(n) {
- return expanded ? true : !n.disengaged;
+ seriesCheckbox.each(function(d,i) {
+ d3.select(this).selectAll('path')
+ .attr('stroke', setTextColor(d,i));
});
- });
- var seriesEnter = series.enter().append('g').attr('class', 'nv-series')
+ }
- var seriesShape;
+ seriesEnter.append('text')
+ .attr('text-anchor', 'start')
+ .attr('class','nv-legend-text')
+ .attr('dy', '.32em')
+ .attr('dx', '8');
- if(vers == 'classic') {
- seriesEnter.append('circle')
- .style('stroke-width', 2)
- .attr('class','nv-legend-symbol')
- .attr('r', 5);
+ var seriesText = series.select('text.nv-legend-text');
- seriesShape = series.select('circle');
- } else if (vers == 'furious') {
- seriesEnter.append('rect')
- .style('stroke-width', 2)
- .attr('class','nv-legend-symbol')
- .attr('rx', 3)
- .attr('ry', 3);
-
- seriesShape = series.select('rect');
-
- seriesEnter.append('g')
- .attr('class', 'nv-check-box')
- .property('innerHTML','<path d="M0.5,5 L22.5,5 L22.5,26.5 L0.5,26.5 L0.5,5 Z" class="nv-box"></path><path d="M5.5,12.8618467 L11.9185089,19.2803556 L31,0.198864511" class="nv-check"></path>')
- .attr('transform', 'translate(-10,-8)scale(0.5)');
-
- var seriesCheckbox = series.select('.nv-check-box');
-
- seriesCheckbox.each(function(d,i) {
- d3.select(this).selectAll('path')
- .attr('stroke', setTextColor(d,i));
- });
- }
-
- seriesEnter.append('text')
- .attr('text-anchor', 'start')
- .attr('class','nv-legend-text')
- .attr('dy', '.32em')
- .attr('dx', '8');
-
- var seriesText = series.select('text.nv-legend-text');
-
- series
- .on('mouseover', function(d,i) {
- dispatch.legendMouseover(d,i); //TODO: Make consistent with other event objects
- })
- .on('mouseout', function(d,i) {
- dispatch.legendMouseout(d,i);
- })
- .on('click', function(d,i) {
- dispatch.legendClick(d,i);
- // make sure we re-get data in case it was modified
- var data = series.data();
- if (updateState) {
- if(vers =='classic') {
- if (radioButtonMode) {
- //Radio button mode: set every series to disabled,
- // and enable the clicked series.
- data.forEach(function(series) { series.disabled = true});
- d.disabled = false;
- }
- else {
- d.disabled = !d.disabled;
- if (data.every(function(series) { return series.disabled})) {
- //the default behavior of NVD3 legends is, if every single series
- // is disabled, turn all series' back on.
- data.forEach(function(series) { series.disabled = false});
+ series
+ .on('mouseover', function(d,i) {
+ dispatch.legendMouseover(d,i); //TODO: Make consistent with other event objects
+ })
+ .on('mouseout', function(d,i) {
+ dispatch.legendMouseout(d,i);
+ })
+ .on('click', function(d,i) {
+ dispatch.legendClick(d,i);
+ // make sure we re-get data in case it was modified
+ var data = series.data();
+ if (updateState) {
+ if(vers =='classic') {
+ if (radioButtonMode) {
+ //Radio button mode: set every series to disabled,
+ // and enable the clicked series.
+ data.forEach(function(series) { series.disabled = true});
+ d.disabled = false;
}
- }
- } else if(vers == 'furious') {
- if(expanded) {
- d.disengaged = !d.disengaged;
- d.userDisabled = d.userDisabled == undefined ? !!d.disabled : d.userDisabled;
- d.disabled = d.disengaged || d.userDisabled;
- } else if (!expanded) {
- d.disabled = !d.disabled;
- d.userDisabled = d.disabled;
- var engaged = data.filter(function(d) { return !d.disengaged; });
- if (engaged.every(function(series) { return series.userDisabled })) {
- //the default behavior of NVD3 legends is, if every single series
- // is disabled, turn all series' back on.
- data.forEach(function(series) {
- series.disabled = series.userDisabled = false;
- });
+ else {
+ d.disabled = !d.disabled;
+ if (data.every(function(series) { return series.disabled})) {
+ //the default behavior of NVD3 legends is, if every single series
+ // is disabled, turn all series' back on.
+ data.forEach(function(series) { series.disabled = false});
+ }
}
+ } else if(vers == 'furious') {
+ if(expanded) {
+ d.disengaged = !d.disengaged;
+ d.userDisabled = d.userDisabled == undefined ? !!d.disabled : d.userDisabled;
+ d.disabled = d.disengaged || d.userDisabled;
+ } else if (!expanded) {
+ d.disabled = !d.disabled;
+ d.userDisabled = d.disabled;
+ var engaged = data.filter(function(d) { return !d.disengaged; });
+ if (engaged.every(function(series) { return series.userDisabled })) {
+ //the default behavior of NVD3 legends is, if every single series
+ // is disabled, turn all series' back on.
+ data.forEach(function(series) {
+ series.disabled = series.userDisabled = false;
+ });
+ }
+ }
}
+ dispatch.stateChange({
+ disabled: data.map(function(d) { return !!d.disabled }),
+ disengaged: data.map(function(d) { return !!d.disengaged })
+ });
+
}
- dispatch.stateChange({
- disabled: data.map(function(d) { return !!d.disabled }),
- disengaged: data.map(function(d) { return !!d.disengaged })
- });
+ })
+ .on('dblclick', function(d,i) {
+ if(vers == 'furious' && expanded) return;
+ dispatch.legendDblclick(d,i);
+ if (updateState) {
+ // make sure we re-get data in case it was modified
+ var data = series.data();
+ //the default behavior of NVD3 legends, when double clicking one,
+ // is to set all other series' to false, and make the double clicked series enabled.
+ data.forEach(function(series) {
+ series.disabled = true;
+ if(vers == 'furious') series.userDisabled = series.disabled;
+ });
+ d.disabled = false;
+ if(vers == 'furious') d.userDisabled = d.disabled;
+ dispatch.stateChange({
+ disabled: data.map(function(d) { return !!d.disabled })
+ });
+ }
+ });
- }
- })
- .on('dblclick', function(d,i) {
- if(vers == 'furious' && expanded) return;
- dispatch.legendDblclick(d,i);
- if (updateState) {
- // make sure we re-get data in case it was modified
- var data = series.data();
- //the default behavior of NVD3 legends, when double clicking one,
- // is to set all other series' to false, and make the double clicked series enabled.
- data.forEach(function(series) {
- series.disabled = true;
- if(vers == 'furious') series.userDisabled = series.disabled;
- });
- d.disabled = false;
- if(vers == 'furious') d.userDisabled = d.disabled;
- dispatch.stateChange({
- disabled: data.map(function(d) { return !!d.disabled })
- });
- }
- });
+ series.classed('nv-disabled', function(d) { return d.userDisabled });
+ series.exit().remove();
- series.classed('nv-disabled', function(d) { return d.userDisabled });
- series.exit().remove();
+ seriesText
+ .attr('fill', setTextColor)
+ .text(function (d) { return keyFormatter(getKey(d)) });
- seriesText
- .attr('fill', setTextColor)
- .text(function (d) { return keyFormatter(getKey(d)) });
+ //TODO: implement fixed-width and max-width options (max-width is especially useful with the align option)
+ // NEW ALIGNING CODE, TODO: clean up
- //TODO: implement fixed-width and max-width options (max-width is especially useful with the align option)
- // NEW ALIGNING CODE, TODO: clean up
+ var versPadding;
+ switch(vers) {
+ case 'furious' :
+ versPadding = 23;
+ break;
+ case 'classic' :
+ versPadding = 20;
+ }
- var versPadding;
- switch(vers) {
- case 'furious' :
- versPadding = 23;
- break;
- case 'classic' :
- versPadding = 20;
- }
+ if (align) {
- if (align) {
+ var seriesWidths = [];
+ series.each(function(d,i) {
+ var legendText;
+ if (keyFormatter(getKey(d)) && keyFormatter(getKey(d)).length > maxKeyLength) {
+ var trimmedKey = keyFormatter(getKey(d)).substring(0, maxKeyLength);
+ legendText = d3.select(this).select('text').text(trimmedKey + "...");
+ d3.select(this).append("svg:title").text(keyFormatter(getKey(d)));
+ } else {
+ legendText = d3.select(this).select('text');
+ }
+ var nodeTextLength;
+ try {
+ nodeTextLength = legendText.node().getComputedTextLength();
+ // If the legendText is display:none'd (nodeTextLength == 0), simulate an error so we approximate, instead
+ if(nodeTextLength <= 0) throw Error();
+ }
+ catch(e) {
+ nodeTextLength = nv.utils.calcApproxTextWidth(legendText);
+ }
- var seriesWidths = [];
- series.each(function(d,i) {
- var legendText;
- if (keyFormatter(getKey(d)) && keyFormatter(getKey(d)).length > maxKeyLength) {
- var trimmedKey = keyFormatter(getKey(d)).substring(0, maxKeyLength);
- legendText = d3.select(this).select('text').text(trimmedKey + "...");
- d3.select(this).append("svg:title").text(keyFormatter(getKey(d)));
- } else {
- legendText = d3.select(this).select('text');
- }
- var nodeTextLength;
- try {
- nodeTextLength = legendText.node().getComputedTextLength();
- // If the legendText is display:none'd (nodeTextLength == 0), simulate an error so we approximate, instead
- if(nodeTextLength <= 0) throw Error();
- }
- catch(e) {
- nodeTextLength = nv.utils.calcApproxTextWidth(legendText);
- }
+ seriesWidths.push(nodeTextLength + padding);
+ });
- seriesWidths.push(nodeTextLength + padding);
- });
+ var seriesPerRow = 0;
+ var legendWidth = 0;
+ var columnWidths = [];
- var seriesPerRow = 0;
- var legendWidth = 0;
- var columnWidths = [];
+ while ( legendWidth < availableWidth && seriesPerRow < seriesWidths.length) {
+ columnWidths[seriesPerRow] = seriesWidths[seriesPerRow];
+ legendWidth += seriesWidths[seriesPerRow++];
+ }
+ if (seriesPerRow === 0) seriesPerRow = 1; //minimum of one series per row
- while ( legendWidth < availableWidth && seriesPerRow < seriesWidths.length) {
- columnWidths[seriesPerRow] = seriesWidths[seriesPerRow];
- legendWidth += seriesWidths[seriesPerRow++];
- }
- if (seriesPerRow === 0) seriesPerRow = 1; //minimum of one series per row
+ while ( legendWidth > availableWidth && seriesPerRow > 1 ) {
+ columnWidths = [];
+ seriesPerRow--;
- while ( legendWidth > availableWidth && seriesPerRow > 1 ) {
- columnWidths = [];
- seriesPerRow--;
+ for (var k = 0; k < seriesWidths.length; k++) {
+ if (seriesWidths[k] > (columnWidths[k % seriesPerRow] || 0) )
+ columnWidths[k % seriesPerRow] = seriesWidths[k];
+ }
- for (var k = 0; k < seriesWidths.length; k++) {
- if (seriesWidths[k] > (columnWidths[k % seriesPerRow] || 0) )
- columnWidths[k % seriesPerRow] = seriesWidths[k];
+ legendWidth = columnWidths.reduce(function(prev, cur, index, array) {
+ return prev + cur;
+ });
}
- legendWidth = columnWidths.reduce(function(prev, cur, index, array) {
- return prev + cur;
- });
- }
+ var xPositions = [];
+ for (var i = 0, curX = 0; i < seriesPerRow; i++) {
+ xPositions[i] = curX;
+ curX += columnWidths[i];
+ }
- var xPositions = [];
- for (var i = 0, curX = 0; i < seriesPerRow; i++) {
- xPositions[i] = curX;
- curX += columnWidths[i];
- }
+ series
+ .attr('transform', function(d, i) {
+ return 'translate(' + xPositions[i % seriesPerRow] + ',' + (5 + Math.floor(i / seriesPerRow) * versPadding) + ')';
+ });
- series
- .attr('transform', function(d, i) {
- return 'translate(' + xPositions[i % seriesPerRow] + ',' + (5 + Math.floor(i / seriesPerRow) * versPadding) + ')';
- });
+ //position legend as far right as possible within the total width
+ if (rightAlign) {
+ g.attr('transform', 'translate(' + (width - margin.right - legendWidth) + ',' + margin.top + ')');
+ }
+ else {
+ g.attr('transform', 'translate(0' + ',' + margin.top + ')');
+ }
- //position legend as far right as possible within the total width
- if (rightAlign) {
- g.attr('transform', 'translate(' + (width - margin.right - legendWidth) + ',' + margin.top + ')');
- }
- else {
- g.attr('transform', 'translate(0' + ',' + margin.top + ')');
- }
+ height = margin.top + margin.bottom + (Math.ceil(seriesWidths.length / seriesPerRow) * versPadding);
- height = margin.top + margin.bottom + (Math.ceil(seriesWidths.length / seriesPerRow) * versPadding);
+ } else {
- } else {
+ var ypos = 5,
+ newxpos = 5,
+ maxwidth = 0,
+ xpos;
+ series
+ .attr('transform', function(d, i) {
+ var length = d3.select(this).select('text').node().getComputedTextLength() + padding;
+ xpos = newxpos;
- var ypos = 5,
- newxpos = 5,
- maxwidth = 0,
- xpos;
- series
- .attr('transform', function(d, i) {
- var length = d3.select(this).select('text').node().getComputedTextLength() + padding;
- xpos = newxpos;
+ if (width < margin.left + margin.right + xpos + length) {
+ newxpos = xpos = 5;
+ ypos += versPadding;
+ }
- if (width < margin.left + margin.right + xpos + length) {
- newxpos = xpos = 5;
- ypos += versPadding;
- }
+ newxpos += length;
+ if (newxpos > maxwidth) maxwidth = newxpos;
- newxpos += length;
- if (newxpos > maxwidth) maxwidth = newxpos;
+ return 'translate(' + xpos + ',' + ypos + ')';
+ });
- return 'translate(' + xpos + ',' + ypos + ')';
- });
+ //position legend as far right as possible within the total width
+ g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')');
- //position legend as far right as possible within the total width
- g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')');
+ height = margin.top + margin.bottom + ypos + 15;
+ }
- height = margin.top + margin.bottom + ypos + 15;
- }
+ if(vers == 'furious') {
+ // Size rectangles after text is placed
+ seriesShape
+ .attr('width', function(d,i) {
+ return seriesText[0][i].getComputedTextLength() + 27;
+ })
+ .attr('height', 18)
+ .attr('y', -9)
+ .attr('x', -15)
+ }
- if(vers == 'furious') {
- // Size rectangles after text is placed
seriesShape
- .attr('width', function(d,i) {
- return seriesText[0][i].getComputedTextLength() + 27;
- })
- .attr('height', 18)
- .attr('y', -9)
- .attr('x', -15)
- }
+ .style('fill', setBGColor)
+ .style('stroke', function(d,i) { return d.color || color(d, i) });
+ });
- seriesShape
- .style('fill', setBGColor)
- .style('stroke', function(d,i) { return d.color || color(d, i) });
- });
-
- function setTextColor(d,i) {
- if(vers != 'furious') return '#000';
- if(expanded) {
- return d.disengaged ? color(d,i) : '#fff';
- } else if (!expanded) {
- return !!d.disabled ? color(d,i) : '#fff';
+ function setTextColor(d,i) {
+ if(vers != 'furious') return '#000';
+ if(expanded) {
+ return d.disengaged ? color(d,i) : '#fff';
+ } else if (!expanded) {
+ return !!d.disabled ? color(d,i) : '#fff';
+ }
}
- }
- function setBGColor(d,i) {
- if(expanded && vers == 'furious') {
- return d.disengaged ? '#fff' : color(d,i);
- } else {
- return !!d.disabled ? '#fff' : color(d,i);
+ function setBGColor(d,i) {
+ if(expanded && vers == 'furious') {
+ return d.disengaged ? '#fff' : color(d,i);
+ } else {
+ return !!d.disabled ? '#fff' : color(d,i);
+ }
}
+
+ return chart;
}
- return chart;
- }
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ key: {get: function(){return getKey;}, set: function(_){getKey=_;}},
+ keyFormatter: {get: function(){return keyFormatter;}, set: function(_){keyFormatter=_;}},
+ align: {get: function(){return align;}, set: function(_){align=_;}},
+ rightAlign: {get: function(){return rightAlign;}, set: function(_){rightAlign=_;}},
+ maxKeyLength: {get: function(){return maxKeyLength;}, set: function(_){maxKeyLength=_;}},
+ padding: {get: function(){return padding;}, set: function(_){padding=_;}},
+ updateState: {get: function(){return updateState;}, set: function(_){updateState=_;}},
+ radioButtonMode:{get: function(){return radioButtonMode;}, set: function(_){radioButtonMode=_;}},
+ expanded: {get: function(){return expanded;}, set: function(_){expanded=_;}},
+ vers: {get: function(){return vers;}, set: function(_){vers=_;}},
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- key: {get: function(){return getKey;}, set: function(_){getKey=_;}},
- keyFormatter: {get: function(){return keyFormatter;}, set: function(_){keyFormatter=_;}},
- align: {get: function(){return align;}, set: function(_){align=_;}},
- rightAlign: {get: function(){return rightAlign;}, set: function(_){rightAlign=_;}},
- maxKeyLength: {get: function(){return maxKeyLength;}, set: function(_){maxKeyLength=_;}},
- padding: {get: function(){return padding;}, set: function(_){padding=_;}},
- updateState: {get: function(){return updateState;}, set: function(_){updateState=_;}},
- radioButtonMode:{get: function(){return radioButtonMode;}, set: function(_){radioButtonMode=_;}},
- expanded: {get: function(){return expanded;}, set: function(_){expanded=_;}},
- vers: {get: function(){return vers;}, set: function(_){vers=_;}},
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }}
- });
+ nv.utils.initOptions(chart);
- nv.utils.initOptions(chart);
-
- return chart;
-};
+ return chart;
+ };
//TODO: consider deprecating and using multibar with single series for this
-nv.models.historicalBar = function() {
- "use strict";
+ nv.models.historicalBar = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = null
- , height = null
- , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
- , container = null
- , x = d3.scale.linear()
- , y = d3.scale.linear()
- , getX = function(d) { return d.x }
- , getY = function(d) { return d.y }
- , forceX = []
- , forceY = [0]
- , padData = false
- , clipEdge = true
- , color = nv.utils.defaultColor()
- , xDomain
- , yDomain
- , xRange
- , yRange
- , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd')
- , interactive = true
- ;
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = null
+ , height = null
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , container = null
+ , x = d3.scale.linear()
+ , y = d3.scale.linear()
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , forceX = []
+ , forceY = [0]
+ , padData = false
+ , clipEdge = true
+ , color = nv.utils.defaultColor()
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd')
+ , interactive = true
+ ;
- var renderWatch = nv.utils.renderWatch(dispatch, 0);
+ var renderWatch = nv.utils.renderWatch(dispatch, 0);
- function chart(selection) {
- selection.each(function(data) {
- renderWatch.reset();
+ function chart(selection) {
+ selection.each(function(data) {
+ renderWatch.reset();
- container = d3.select(this);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ container = d3.select(this);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
- nv.utils.initSVG(container);
+ nv.utils.initSVG(container);
- // Setup Scales
- x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ));
+ // Setup Scales
+ x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ));
- if (padData)
- x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
- else
- x.range(xRange || [0, availableWidth]);
+ if (padData)
+ x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
+ else
+ x.range(xRange || [0, availableWidth]);
- y.domain(yDomain || d3.extent(data[0].values.map(getY).concat(forceY) ))
- .range(yRange || [availableHeight, 0]);
+ y.domain(yDomain || d3.extent(data[0].values.map(getY).concat(forceY) ))
+ .range(yRange || [availableHeight, 0]);
- // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
- if (x.domain()[0] === x.domain()[1])
- x.domain()[0] ?
- x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
- : x.domain([-1,1]);
+ // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
+ if (x.domain()[0] === x.domain()[1])
+ x.domain()[0] ?
+ x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
+ : x.domain([-1,1]);
- if (y.domain()[0] === y.domain()[1])
- y.domain()[0] ?
- y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
- : y.domain([-1,1]);
+ if (y.domain()[0] === y.domain()[1])
+ y.domain()[0] ?
+ y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
+ : y.domain([-1,1]);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-historicalBar-' + id).data([data[0].values]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBar-' + id);
- var defsEnter = wrapEnter.append('defs');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-historicalBar-' + id).data([data[0].values]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBar-' + id);
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- gEnter.append('g').attr('class', 'nv-bars');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ gEnter.append('g').attr('class', 'nv-bars');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- container
- .on('click', function(d,i) {
- dispatch.chartClick({
- data: d,
- index: i,
- pos: d3.event,
- id: id
+ container
+ .on('click', function(d,i) {
+ dispatch.chartClick({
+ data: d,
+ index: i,
+ pos: d3.event,
+ id: id
+ });
});
- });
- defsEnter.append('clipPath')
- .attr('id', 'nv-chart-clip-path-' + id)
- .append('rect');
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-chart-clip-path-' + id)
+ .append('rect');
- wrap.select('#nv-chart-clip-path-' + id + ' rect')
- .attr('width', availableWidth)
- .attr('height', availableHeight);
+ wrap.select('#nv-chart-clip-path-' + id + ' rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
- g.attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
+ g.attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
- var bars = wrap.select('.nv-bars').selectAll('.nv-bar')
- .data(function(d) { return d }, function(d,i) {return getX(d,i)});
- bars.exit().remove();
+ var bars = wrap.select('.nv-bars').selectAll('.nv-bar')
+ .data(function(d) { return d }, function(d,i) {return getX(d,i)});
+ bars.exit().remove();
- bars.enter().append('rect')
- .attr('x', 0 )
- .attr('y', function(d,i) { return nv.utils.NaNtoZero(y(Math.max(0, getY(d,i)))) })
- .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.abs(y(getY(d,i)) - y(0))) })
- .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; })
- .on('mouseover', function(d,i) {
- if (!interactive) return;
- d3.select(this).classed('hover', true);
- dispatch.elementMouseover({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
+ bars.enter().append('rect')
+ .attr('x', 0 )
+ .attr('y', function(d,i) { return nv.utils.NaNtoZero(y(Math.max(0, getY(d,i)))) })
+ .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.abs(y(getY(d,i)) - y(0))) })
+ .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; })
+ .on('mouseover', function(d,i) {
+ if (!interactive) return;
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
- })
- .on('mouseout', function(d,i) {
- if (!interactive) return;
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
+ })
+ .on('mouseout', function(d,i) {
+ if (!interactive) return;
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ })
+ .on('mousemove', function(d,i) {
+ if (!interactive) return;
+ dispatch.elementMousemove({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ })
+ .on('click', function(d,i) {
+ if (!interactive) return;
+ var element = this;
+ dispatch.elementClick({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill"),
+ event: d3.event,
+ element: element
+ });
+ d3.event.stopPropagation();
+ })
+ .on('dblclick', function(d,i) {
+ if (!interactive) return;
+ dispatch.elementDblClick({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ d3.event.stopPropagation();
});
- })
- .on('mousemove', function(d,i) {
- if (!interactive) return;
- dispatch.elementMousemove({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- })
- .on('click', function(d,i) {
- if (!interactive) return;
- dispatch.elementClick({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- d3.event.stopPropagation();
- })
- .on('dblclick', function(d,i) {
- if (!interactive) return;
- dispatch.elementDblClick({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- d3.event.stopPropagation();
- });
- bars
- .attr('fill', function(d,i) { return color(d, i); })
- .attr('class', function(d,i,j) { return (getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive') + ' nv-bar-' + j + '-' + i })
- .watchTransition(renderWatch, 'bars')
- .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; })
- //TODO: better width calculations that don't assume always uniform data spacing;w
- .attr('width', (availableWidth / data[0].values.length) * .9 );
+ bars
+ .attr('fill', function(d,i) { return color(d, i); })
+ .attr('class', function(d,i,j) { return (getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive') + ' nv-bar-' + j + '-' + i })
+ .watchTransition(renderWatch, 'bars')
+ .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; })
+ //TODO: better width calculations that don't assume always uniform data spacing;w
+ .attr('width', (availableWidth / data[0].values.length) * .9 );
- bars.watchTransition(renderWatch, 'bars')
- .attr('y', function(d,i) {
- var rval = getY(d,i) < 0 ?
- y(0) :
+ bars.watchTransition(renderWatch, 'bars')
+ .attr('y', function(d,i) {
+ var rval = getY(d,i) < 0 ?
+ y(0) :
y(0) - y(getY(d,i)) < 1 ?
- y(0) - 1 :
- y(getY(d,i));
- return nv.utils.NaNtoZero(rval);
- })
- .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.max(Math.abs(y(getY(d,i)) - y(0)),1)) });
+ y(0) - 1 :
+ y(getY(d,i));
+ return nv.utils.NaNtoZero(rval);
+ })
+ .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.max(Math.abs(y(getY(d,i)) - y(0)),1)) });
- });
+ });
- renderWatch.renderEnd('historicalBar immediate');
- return chart;
- }
+ renderWatch.renderEnd('historicalBar immediate');
+ return chart;
+ }
- //Create methods to allow outside functions to highlight a specific bar.
- chart.highlightPoint = function(pointIndex, isHoverOver) {
- container
- .select(".nv-bars .nv-bar-0-" + pointIndex)
- .classed("hover", isHoverOver)
- ;
- };
+ //Create methods to allow outside functions to highlight a specific bar.
+ chart.highlightPoint = function(pointIndex, isHoverOver) {
+ container
+ .select(".nv-bars .nv-bar-0-" + pointIndex)
+ .classed("hover", isHoverOver)
+ ;
+ };
- chart.clearHighlights = function() {
- container
- .select(".nv-bars .nv-bar.hover")
- .classed("hover", false)
- ;
- };
+ chart.clearHighlights = function() {
+ container
+ .select(".nv-bars .nv-bar.hover")
+ .classed("hover", false)
+ ;
+ };
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
- forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
- padData: {get: function(){return padData;}, set: function(_){padData=_;}},
- x: {get: function(){return getX;}, set: function(_){getX=_;}},
- y: {get: function(){return getY;}, set: function(_){getY=_;}},
- xScale: {get: function(){return x;}, set: function(_){x=_;}},
- yScale: {get: function(){return y;}, set: function(_){y=_;}},
- xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
- yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
- xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
- yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
- clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
- id: {get: function(){return id;}, set: function(_){id=_;}},
- interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ padData: {get: function(){return padData;}, set: function(_){padData=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ y: {get: function(){return getY;}, set: function(_){getY=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
- nv.utils.initOptions(chart);
+ nv.utils.initOptions(chart);
- return chart;
-};
+ return chart;
+ };
-nv.models.historicalBarChart = function(bar_model) {
- "use strict";
+ nv.models.historicalBarChart = function(bar_model) {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var bars = bar_model || nv.models.historicalBar()
- , xAxis = nv.models.axis()
- , yAxis = nv.models.axis()
- , legend = nv.models.legend()
- , interactiveLayer = nv.interactiveGuideline()
- , tooltip = nv.models.tooltip()
- ;
+ var bars = bar_model || nv.models.historicalBar()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend()
+ , interactiveLayer = nv.interactiveGuideline()
+ , tooltip = nv.models.tooltip()
+ ;
- var margin = {top: 30, right: 90, bottom: 50, left: 90}
- , color = nv.utils.defaultColor()
- , width = null
- , height = null
- , showLegend = false
- , showXAxis = true
- , showYAxis = true
- , rightAlignYAxis = false
- , useInteractiveGuideline = false
- , x
- , y
- , state = {}
- , defaultState = null
- , noData = null
- , dispatch = d3.dispatch('tooltipHide', 'stateChange', 'changeState', 'renderEnd')
- , transitionDuration = 250
- ;
+ var margin = {top: 30, right: 90, bottom: 50, left: 90}
+ , marginTop = null
+ , color = nv.utils.defaultColor()
+ , width = null
+ , height = null
+ , showLegend = false
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , useInteractiveGuideline = false
+ , x
+ , y
+ , state = {}
+ , defaultState = null
+ , noData = null
+ , dispatch = d3.dispatch('tooltipHide', 'stateChange', 'changeState', 'renderEnd')
+ , transitionDuration = 250
+ ;
- xAxis.orient('bottom').tickPadding(7);
- yAxis.orient( (rightAlignYAxis) ? 'right' : 'left');
- tooltip
- .duration(0)
- .headerEnabled(false)
- .valueFormatter(function(d, i) {
- return yAxis.tickFormat()(d, i);
- })
- .headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- });
+ xAxis.orient('bottom').tickPadding(7);
+ yAxis.orient( (rightAlignYAxis) ? 'right' : 'left');
+ tooltip
+ .duration(0)
+ .headerEnabled(false)
+ .valueFormatter(function(d, i) {
+ return yAxis.tickFormat()(d, i);
+ })
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ });
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var renderWatch = nv.utils.renderWatch(dispatch, 0);
+ var renderWatch = nv.utils.renderWatch(dispatch, 0);
- function chart(selection) {
- selection.each(function(data) {
- renderWatch.reset();
- renderWatch.models(bars);
- if (showXAxis) renderWatch.models(xAxis);
- if (showYAxis) renderWatch.models(yAxis);
+ function chart(selection) {
+ selection.each(function(data) {
+ renderWatch.reset();
+ renderWatch.models(bars);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
- var container = d3.select(this),
- that = this;
- nv.utils.initSVG(container);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
- chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
- chart.container = this;
+ chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
+ chart.container = this;
- //set state.disabled
- state.disabled = data.map(function(d) { return !!d.disabled });
+ //set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
- if (!defaultState) {
- var key;
- defaultState = {};
- for (key in state) {
- if (state[key] instanceof Array)
- defaultState[key] = state[key].slice(0);
- else
- defaultState[key] = state[key];
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
}
- }
- // Display noData message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
- nv.utils.noData(chart, container)
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
+ // Display noData message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ nv.utils.noData(chart, container)
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
- // Setup Scales
- x = bars.xScale();
- y = bars.yScale();
+ // Setup Scales
+ x = bars.xScale();
+ y = bars.yScale();
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-historicalBarChart').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBarChart').append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-historicalBarChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBarChart').append('g');
+ var g = wrap.select('g');
- gEnter.append('g').attr('class', 'nv-x nv-axis');
- gEnter.append('g').attr('class', 'nv-y nv-axis');
- gEnter.append('g').attr('class', 'nv-barsWrap');
- gEnter.append('g').attr('class', 'nv-legendWrap');
- gEnter.append('g').attr('class', 'nv-interactive');
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis');
+ gEnter.append('g').attr('class', 'nv-barsWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+ gEnter.append('g').attr('class', 'nv-interactive');
- // Legend
- if (!showLegend) {
- g.select('.nv-legendWrap').selectAll('*').remove();
- } else {
- legend.width(availableWidth);
+ // Legend
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
+ legend.width(availableWidth);
- g.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
+ g.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
- if (legend.height() > margin.top) {
- margin.top = legend.height();
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ if (!marginTop && legend.height() !== margin.top) {
+ margin.top = legend.height();
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+ }
+
+ wrap.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
}
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- wrap.select('.nv-legendWrap')
- .attr('transform', 'translate(0,' + (-margin.top) +')')
- }
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
- if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
- }
-
- //Set up interactive layer
- if (useInteractiveGuideline) {
- interactiveLayer
+ //Set up interactive layer
+ if (useInteractiveGuideline) {
+ interactiveLayer
+ .width(availableWidth)
+ .height(availableHeight)
+ .margin({left:margin.left, top:margin.top})
+ .svgContainer(container)
+ .xScale(x);
+ wrap.select(".nv-interactive").call(interactiveLayer);
+ }
+ bars
.width(availableWidth)
.height(availableHeight)
- .margin({left:margin.left, top:margin.top})
- .svgContainer(container)
- .xScale(x);
- wrap.select(".nv-interactive").call(interactiveLayer);
- }
- bars
- .width(availableWidth)
- .height(availableHeight)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled }));
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled }));
- var barsWrap = g.select('.nv-barsWrap')
- .datum(data.filter(function(d) { return !d.disabled }));
- barsWrap.transition().call(bars);
+ var barsWrap = g.select('.nv-barsWrap')
+ .datum(data.filter(function(d) { return !d.disabled }));
+ barsWrap.transition().call(bars);
- // Setup Axes
- if (showXAxis) {
- xAxis
- .scale(x)
- ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize(-availableHeight, 0);
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight, 0);
- g.select('.nv-x.nv-axis')
- .attr('transform', 'translate(0,' + y.range()[0] + ')');
- g.select('.nv-x.nv-axis')
- .transition()
- .call(xAxis);
- }
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y.range()[0] + ')');
+ g.select('.nv-x.nv-axis')
+ .transition()
+ .call(xAxis);
+ }
- if (showYAxis) {
- yAxis
- .scale(y)
- ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
- g.select('.nv-y.nv-axis')
- .transition()
- .call(yAxis);
- }
+ g.select('.nv-y.nv-axis')
+ .transition()
+ .call(yAxis);
+ }
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
- interactiveLayer.dispatch.on('elementMousemove', function(e) {
- bars.clearHighlights();
+ interactiveLayer.dispatch.on('elementMousemove', function(e) {
+ bars.clearHighlights();
- var singlePoint, pointIndex, pointXLocation, allData = [];
- data
- .filter(function(series, i) {
- series.seriesIndex = i;
- return !series.disabled;
- })
- .forEach(function(series,i) {
- pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
- bars.highlightPoint(pointIndex,true);
- var point = series.values[pointIndex];
- if (point === undefined) return;
- if (singlePoint === undefined) singlePoint = point;
- if (pointXLocation === undefined) pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
- allData.push({
- key: series.key,
- value: chart.y()(point, pointIndex),
- color: color(series,series.seriesIndex),
- data: series.values[pointIndex]
+ var singlePoint, pointIndex, pointXLocation, allData = [];
+ data
+ .filter(function(series, i) {
+ series.seriesIndex = i;
+ return !series.disabled;
+ })
+ .forEach(function(series,i) {
+ pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
+ bars.highlightPoint(pointIndex,true);
+ var point = series.values[pointIndex];
+ if (point === undefined) return;
+ if (singlePoint === undefined) singlePoint = point;
+ if (pointXLocation === undefined) pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
+ allData.push({
+ key: series.key,
+ value: chart.y()(point, pointIndex),
+ color: color(series,series.seriesIndex),
+ data: series.values[pointIndex]
+ });
});
- });
- var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
- interactiveLayer.tooltip
- .valueFormatter(function(d,i) {
- return yAxis.tickFormat()(d);
- })
- .data({
- value: xValue,
- index: pointIndex,
- series: allData
- })();
+ var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
+ interactiveLayer.tooltip
+ .valueFormatter(function(d,i) {
+ return yAxis.tickFormat()(d);
+ })
+ .data({
+ value: xValue,
+ index: pointIndex,
+ series: allData
+ })();
- interactiveLayer.renderGuideLine(pointXLocation);
+ interactiveLayer.renderGuideLine(pointXLocation);
- });
+ });
- interactiveLayer.dispatch.on("elementMouseout",function(e) {
- dispatch.tooltipHide();
- bars.clearHighlights();
- });
+ interactiveLayer.dispatch.on("elementMouseout",function(e) {
+ dispatch.tooltipHide();
+ bars.clearHighlights();
+ });
- legend.dispatch.on('legendClick', function(d,i) {
- d.disabled = !d.disabled;
+ legend.dispatch.on('legendClick', function(d,i) {
+ d.disabled = !d.disabled;
- if (!data.filter(function(d) { return !d.disabled }).length) {
- data.map(function(d) {
- d.disabled = false;
- wrap.selectAll('.nv-series').classed('disabled', false);
- return d;
- });
- }
+ if (!data.filter(function(d) { return !d.disabled }).length) {
+ data.map(function(d) {
+ d.disabled = false;
+ wrap.selectAll('.nv-series').classed('disabled', false);
+ return d;
+ });
+ }
- state.disabled = data.map(function(d) { return !!d.disabled });
- dispatch.stateChange(state);
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
- selection.transition().call(chart);
- });
-
- legend.dispatch.on('legendDblclick', function(d) {
- //Double clicking should always enable current series, and disabled all others.
- data.forEach(function(d) {
- d.disabled = true;
+ selection.transition().call(chart);
});
- d.disabled = false;
- state.disabled = data.map(function(d) { return !!d.disabled });
- dispatch.stateChange(state);
- chart.update();
- });
-
- dispatch.on('changeState', function(e) {
- if (typeof e.disabled !== 'undefined') {
- data.forEach(function(series,i) {
- series.disabled = e.disabled[i];
+ legend.dispatch.on('legendDblclick', function(d) {
+ //Double clicking should always enable current series, and disabled all others.
+ data.forEach(function(d) {
+ d.disabled = true;
});
+ d.disabled = false;
- state.disabled = e.disabled;
- }
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
+ chart.update();
+ });
- chart.update();
+ dispatch.on('changeState', function(e) {
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
+ }
+
+ chart.update();
+ });
});
- });
- renderWatch.renderEnd('historicalBarChart immediate');
- return chart;
- }
+ renderWatch.renderEnd('historicalBarChart immediate');
+ return chart;
+ }
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
- bars.dispatch.on('elementMouseover.tooltip', function(evt) {
- evt['series'] = {
- key: chart.x()(evt.data),
- value: chart.y()(evt.data),
- color: evt.color
- };
- tooltip.data(evt).hidden(false);
- });
+ bars.dispatch.on('elementMouseover.tooltip', function(evt) {
+ evt['series'] = {
+ key: chart.x()(evt.data),
+ value: chart.y()(evt.data),
+ color: evt.color
+ };
+ tooltip.data(evt).hidden(false);
+ });
- bars.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true);
- });
+ bars.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true);
+ });
- bars.dispatch.on('elementMousemove.tooltip', function(evt) {
- tooltip();
- });
+ bars.dispatch.on('elementMousemove.tooltip', function(evt) {
+ tooltip();
+ });
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.bars = bars;
- chart.legend = legend;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.interactiveLayer = interactiveLayer;
- chart.tooltip = tooltip;
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.bars = bars;
+ chart.legend = legend;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.interactiveLayer = interactiveLayer;
+ chart.tooltip = tooltip;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- legend.color(color);
- bars.color(color);
- }},
- duration: {get: function(){return transitionDuration;}, set: function(_){
- transitionDuration=_;
- renderWatch.reset(transitionDuration);
- yAxis.duration(transitionDuration);
- xAxis.duration(transitionDuration);
- }},
- rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
- rightAlignYAxis = _;
- yAxis.orient( (_) ? 'right' : 'left');
- }},
- useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
- useInteractiveGuideline = _;
- if (_ === true) {
- chart.interactive(false);
- }
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ if (_.top !== undefined) {
+ margin.top = _.top;
+ marginTop = _.top;
+ }
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ bars.color(color);
+ }},
+ duration: {get: function(){return transitionDuration;}, set: function(_){
+ transitionDuration=_;
+ renderWatch.reset(transitionDuration);
+ yAxis.duration(transitionDuration);
+ xAxis.duration(transitionDuration);
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( (_) ? 'right' : 'left');
+ }},
+ useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
+ useInteractiveGuideline = _;
+ if (_ === true) {
+ chart.interactive(false);
+ }
+ }}
+ });
- nv.utils.inheritOptions(chart, bars);
- nv.utils.initOptions(chart);
+ nv.utils.inheritOptions(chart, bars);
+ nv.utils.initOptions(chart);
- return chart;
-};
+ return chart;
+ };
// ohlcChart is just a historical chart with ohlc bars and some tweaks
-nv.models.ohlcBarChart = function() {
- var chart = nv.models.historicalBarChart(nv.models.ohlcBar());
+ nv.models.ohlcBarChart = function() {
+ var chart = nv.models.historicalBarChart(nv.models.ohlcBar());
- // special default tooltip since we show multiple values per x
- chart.useInteractiveGuideline(true);
- chart.interactiveLayer.tooltip.contentGenerator(function(data) {
- // we assume only one series exists for this chart
- var d = data.series[0].data;
- // match line colors as defined in nv.d3.css
- var color = d.open < d.close ? "2ca02c" : "d62728";
- return '' +
- '<h3 style="color: #' + color + '">' + data.value + '</h3>' +
- '<table>' +
- '<tr><td>open:</td><td>' + chart.yAxis.tickFormat()(d.open) + '</td></tr>' +
- '<tr><td>close:</td><td>' + chart.yAxis.tickFormat()(d.close) + '</td></tr>' +
- '<tr><td>high</td><td>' + chart.yAxis.tickFormat()(d.high) + '</td></tr>' +
- '<tr><td>low:</td><td>' + chart.yAxis.tickFormat()(d.low) + '</td></tr>' +
- '</table>';
- });
- return chart;
-};
+ // special default tooltip since we show multiple values per x
+ chart.useInteractiveGuideline(true);
+ chart.interactiveLayer.tooltip.contentGenerator(function(data) {
+ // we assume only one series exists for this chart
+ var d = data.series[0].data;
+ // match line colors as defined in nv.d3.css
+ var color = d.open < d.close ? "2ca02c" : "d62728";
+ return '' +
+ '<h3 style="color: #' + color + '">' + data.value + '</h3>' +
+ '<table>' +
+ '<tr><td>open:</td><td>' + chart.yAxis.tickFormat()(d.open) + '</td></tr>' +
+ '<tr><td>close:</td><td>' + chart.yAxis.tickFormat()(d.close) + '</td></tr>' +
+ '<tr><td>high</td><td>' + chart.yAxis.tickFormat()(d.high) + '</td></tr>' +
+ '<tr><td>low:</td><td>' + chart.yAxis.tickFormat()(d.low) + '</td></tr>' +
+ '</table>';
+ });
+ return chart;
+ };
// candlestickChart is just a historical chart with candlestick bars and some tweaks
-nv.models.candlestickBarChart = function() {
- var chart = nv.models.historicalBarChart(nv.models.candlestickBar());
+ nv.models.candlestickBarChart = function() {
+ var chart = nv.models.historicalBarChart(nv.models.candlestickBar());
- // special default tooltip since we show multiple values per x
- chart.useInteractiveGuideline(true);
- chart.interactiveLayer.tooltip.contentGenerator(function(data) {
- // we assume only one series exists for this chart
- var d = data.series[0].data;
- // match line colors as defined in nv.d3.css
- var color = d.open < d.close ? "2ca02c" : "d62728";
- return '' +
- '<h3 style="color: #' + color + '">' + data.value + '</h3>' +
- '<table>' +
- '<tr><td>open:</td><td>' + chart.yAxis.tickFormat()(d.open) + '</td></tr>' +
- '<tr><td>close:</td><td>' + chart.yAxis.tickFormat()(d.close) + '</td></tr>' +
- '<tr><td>high</td><td>' + chart.yAxis.tickFormat()(d.high) + '</td></tr>' +
- '<tr><td>low:</td><td>' + chart.yAxis.tickFormat()(d.low) + '</td></tr>' +
- '</table>';
- });
- return chart;
-};
-nv.models.legend = function() {
- "use strict";
+ // special default tooltip since we show multiple values per x
+ chart.useInteractiveGuideline(true);
+ chart.interactiveLayer.tooltip.contentGenerator(function(data) {
+ // we assume only one series exists for this chart
+ var d = data.series[0].data;
+ // match line colors as defined in nv.d3.css
+ var color = d.open < d.close ? "2ca02c" : "d62728";
+ return '' +
+ '<h3 style="color: #' + color + '">' + data.value + '</h3>' +
+ '<table>' +
+ '<tr><td>open:</td><td>' + chart.yAxis.tickFormat()(d.open) + '</td></tr>' +
+ '<tr><td>close:</td><td>' + chart.yAxis.tickFormat()(d.close) + '</td></tr>' +
+ '<tr><td>high</td><td>' + chart.yAxis.tickFormat()(d.high) + '</td></tr>' +
+ '<tr><td>low:</td><td>' + chart.yAxis.tickFormat()(d.low) + '</td></tr>' +
+ '</table>';
+ });
+ return chart;
+ };
+ nv.models.legend = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var margin = {top: 5, right: 0, bottom: 5, left: 0}
- , width = 400
- , height = 20
- , getKey = function(d) { return d.key }
- , keyFormatter = function (d) { return d }
- , color = nv.utils.getColor()
- , maxKeyLength = 20 //default value for key lengths
- , align = true
- , padding = 32 //define how much space between legend items. - recommend 32 for furious version
- , rightAlign = true
- , updateState = true //If true, legend will update data.disabled and trigger a 'stateChange' dispatch.
- , radioButtonMode = false //If true, clicking legend items will cause it to behave like a radio button. (only one can be selected at a time)
- , expanded = false
- , dispatch = d3.dispatch('legendClick', 'legendDblclick', 'legendMouseover', 'legendMouseout', 'stateChange')
- , vers = 'classic' //Options are "classic" and "furious"
- ;
+ var margin = {top: 5, right: 0, bottom: 5, left: 0}
+ , width = 400
+ , height = 20
+ , getKey = function(d) { return d.key }
+ , keyFormatter = function (d) { return d }
+ , color = nv.utils.getColor()
+ , maxKeyLength = 20 //default value for key lengths
+ , align = true
+ , padding = 32 //define how much space between legend items. - recommend 32 for furious version
+ , rightAlign = true
+ , updateState = true //If true, legend will update data.disabled and trigger a 'stateChange' dispatch.
+ , radioButtonMode = false //If true, clicking legend items will cause it to behave like a radio button. (only one can be selected at a time)
+ , expanded = false
+ , dispatch = d3.dispatch('legendClick', 'legendDblclick', 'legendMouseover', 'legendMouseout', 'stateChange')
+ , vers = 'classic' //Options are "classic" and "furious"
+ ;
- function chart(selection) {
- selection.each(function(data) {
- var availableWidth = width - margin.left - margin.right,
- container = d3.select(this);
- nv.utils.initSVG(container);
+ function chart(selection) {
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ container = d3.select(this);
+ nv.utils.initSVG(container);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-legend').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-legend').append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-legend').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-legend').append('g');
+ var g = wrap.select('g');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ if (rightAlign)
+ wrap.attr('transform', 'translate(' + (- margin.right) + ',' + margin.top + ')');
+ else
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- var series = g.selectAll('.nv-series')
- .data(function(d) {
- if(vers != 'furious') return d;
+ var series = g.selectAll('.nv-series')
+ .data(function(d) {
+ if(vers != 'furious') return d;
- return d.filter(function(n) {
- return expanded ? true : !n.disengaged;
+ return d.filter(function(n) {
+ return expanded ? true : !n.disengaged;
+ });
});
- });
- var seriesEnter = series.enter().append('g').attr('class', 'nv-series');
- var seriesShape;
+ var seriesEnter = series.enter().append('g').attr('class', 'nv-series');
+ var seriesShape;
- var versPadding;
- switch(vers) {
- case 'furious' :
- versPadding = 23;
- break;
- case 'classic' :
- versPadding = 20;
- }
+ var versPadding;
+ switch(vers) {
+ case 'furious' :
+ versPadding = 23;
+ break;
+ case 'classic' :
+ versPadding = 20;
+ }
- if(vers == 'classic') {
- seriesEnter.append('circle')
- .style('stroke-width', 2)
- .attr('class','nv-legend-symbol')
- .attr('r', 5);
+ if(vers == 'classic') {
+ seriesEnter.append('circle')
+ .style('stroke-width', 2)
+ .attr('class','nv-legend-symbol')
+ .attr('r', 5);
- seriesShape = series.select('.nv-legend-symbol');
- } else if (vers == 'furious') {
- seriesEnter.append('rect')
- .style('stroke-width', 2)
- .attr('class','nv-legend-symbol')
- .attr('rx', 3)
- .attr('ry', 3);
- seriesShape = series.select('.nv-legend-symbol');
+ seriesShape = series.select('.nv-legend-symbol');
+ } else if (vers == 'furious') {
+ seriesEnter.append('rect')
+ .style('stroke-width', 2)
+ .attr('class','nv-legend-symbol')
+ .attr('rx', 3)
+ .attr('ry', 3);
+ seriesShape = series.select('.nv-legend-symbol');
- seriesEnter.append('g')
- .attr('class', 'nv-check-box')
- .property('innerHTML','<path d="M0.5,5 L22.5,5 L22.5,26.5 L0.5,26.5 L0.5,5 Z" class="nv-box"></path><path d="M5.5,12.8618467 L11.9185089,19.2803556 L31,0.198864511" class="nv-check"></path>')
- .attr('transform', 'translate(-10,-8)scale(0.5)');
+ seriesEnter.append('g')
+ .attr('class', 'nv-check-box')
+ .property('innerHTML','<path d="M0.5,5 L22.5,5 L22.5,26.5 L0.5,26.5 L0.5,5 Z" class="nv-box"></path><path d="M5.5,12.8618467 L11.9185089,19.2803556 L31,0.198864511" class="nv-check"></path>')
+ .attr('transform', 'translate(-10,-8)scale(0.5)');
- var seriesCheckbox = series.select('.nv-check-box');
+ var seriesCheckbox = series.select('.nv-check-box');
- seriesCheckbox.each(function(d,i) {
- d3.select(this).selectAll('path')
- .attr('stroke', setTextColor(d,i));
- });
- }
+ seriesCheckbox.each(function(d,i) {
+ d3.select(this).selectAll('path')
+ .attr('stroke', setTextColor(d,i));
+ });
+ }
- seriesEnter.append('text')
- .attr('text-anchor', 'start')
- .attr('class','nv-legend-text')
- .attr('dy', '.32em')
- .attr('dx', '8');
+ seriesEnter.append('text')
+ .attr('text-anchor', 'start')
+ .attr('class','nv-legend-text')
+ .attr('dy', '.32em')
+ .attr('dx', '8');
- var seriesText = series.select('text.nv-legend-text');
+ var seriesText = series.select('text.nv-legend-text');
- series
- .on('mouseover', function(d,i) {
- dispatch.legendMouseover(d,i); //TODO: Make consistent with other event objects
- })
- .on('mouseout', function(d,i) {
- dispatch.legendMouseout(d,i);
- })
- .on('click', function(d,i) {
- dispatch.legendClick(d,i);
- // make sure we re-get data in case it was modified
- var data = series.data();
- if (updateState) {
- if(vers =='classic') {
- if (radioButtonMode) {
- //Radio button mode: set every series to disabled,
- // and enable the clicked series.
- data.forEach(function(series) { series.disabled = true});
- d.disabled = false;
- }
- else {
- d.disabled = !d.disabled;
- if (data.every(function(series) { return series.disabled})) {
- //the default behavior of NVD3 legends is, if every single series
- // is disabled, turn all series' back on.
- data.forEach(function(series) { series.disabled = false});
+ series
+ .on('mouseover', function(d,i) {
+ dispatch.legendMouseover(d,i); //TODO: Make consistent with other event objects
+ })
+ .on('mouseout', function(d,i) {
+ dispatch.legendMouseout(d,i);
+ })
+ .on('click', function(d,i) {
+ dispatch.legendClick(d,i);
+ // make sure we re-get data in case it was modified
+ var data = series.data();
+ if (updateState) {
+ if(vers =='classic') {
+ if (radioButtonMode) {
+ //Radio button mode: set every series to disabled,
+ // and enable the clicked series.
+ data.forEach(function(series) { series.disabled = true});
+ d.disabled = false;
}
- }
- } else if(vers == 'furious') {
- if(expanded) {
- d.disengaged = !d.disengaged;
- d.userDisabled = d.userDisabled == undefined ? !!d.disabled : d.userDisabled;
- d.disabled = d.disengaged || d.userDisabled;
- } else if (!expanded) {
- d.disabled = !d.disabled;
- d.userDisabled = d.disabled;
- var engaged = data.filter(function(d) { return !d.disengaged; });
- if (engaged.every(function(series) { return series.userDisabled })) {
- //the default behavior of NVD3 legends is, if every single series
- // is disabled, turn all series' back on.
- data.forEach(function(series) {
- series.disabled = series.userDisabled = false;
- });
+ else {
+ d.disabled = !d.disabled;
+ if (data.every(function(series) { return series.disabled})) {
+ //the default behavior of NVD3 legends is, if every single series
+ // is disabled, turn all series' back on.
+ data.forEach(function(series) { series.disabled = false});
+ }
}
+ } else if(vers == 'furious') {
+ if(expanded) {
+ d.disengaged = !d.disengaged;
+ d.userDisabled = d.userDisabled == undefined ? !!d.disabled : d.userDisabled;
+ d.disabled = d.disengaged || d.userDisabled;
+ } else if (!expanded) {
+ d.disabled = !d.disabled;
+ d.userDisabled = d.disabled;
+ var engaged = data.filter(function(d) { return !d.disengaged; });
+ if (engaged.every(function(series) { return series.userDisabled })) {
+ //the default behavior of NVD3 legends is, if every single series
+ // is disabled, turn all series' back on.
+ data.forEach(function(series) {
+ series.disabled = series.userDisabled = false;
+ });
+ }
+ }
}
+ dispatch.stateChange({
+ disabled: data.map(function(d) { return !!d.disabled }),
+ disengaged: data.map(function(d) { return !!d.disengaged })
+ });
+
}
- dispatch.stateChange({
- disabled: data.map(function(d) { return !!d.disabled }),
- disengaged: data.map(function(d) { return !!d.disengaged })
- });
+ })
+ .on('dblclick', function(d,i) {
+ if(vers == 'furious' && expanded) return;
+ dispatch.legendDblclick(d,i);
+ if (updateState) {
+ // make sure we re-get data in case it was modified
+ var data = series.data();
+ //the default behavior of NVD3 legends, when double clicking one,
+ // is to set all other series' to false, and make the double clicked series enabled.
+ data.forEach(function(series) {
+ series.disabled = true;
+ if(vers == 'furious') series.userDisabled = series.disabled;
+ });
+ d.disabled = false;
+ if(vers == 'furious') d.userDisabled = d.disabled;
+ dispatch.stateChange({
+ disabled: data.map(function(d) { return !!d.disabled })
+ });
+ }
+ });
- }
- })
- .on('dblclick', function(d,i) {
- if(vers == 'furious' && expanded) return;
- dispatch.legendDblclick(d,i);
- if (updateState) {
- // make sure we re-get data in case it was modified
- var data = series.data();
- //the default behavior of NVD3 legends, when double clicking one,
- // is to set all other series' to false, and make the double clicked series enabled.
- data.forEach(function(series) {
- series.disabled = true;
- if(vers == 'furious') series.userDisabled = series.disabled;
- });
- d.disabled = false;
- if(vers == 'furious') d.userDisabled = d.disabled;
- dispatch.stateChange({
- disabled: data.map(function(d) { return !!d.disabled })
- });
- }
- });
+ series.classed('nv-disabled', function(d) { return d.userDisabled });
+ series.exit().remove();
- series.classed('nv-disabled', function(d) { return d.userDisabled });
- series.exit().remove();
+ seriesText
+ .attr('fill', setTextColor)
+ .text(function (d) { return keyFormatter(getKey(d)) });
- seriesText
- .attr('fill', setTextColor)
- .text(function (d) { return keyFormatter(getKey(d)) });
+ //TODO: implement fixed-width and max-width options (max-width is especially useful with the align option)
+ // NEW ALIGNING CODE, TODO: clean up
+ var legendWidth = 0;
+ if (align) {
- //TODO: implement fixed-width and max-width options (max-width is especially useful with the align option)
- // NEW ALIGNING CODE, TODO: clean up
- var legendWidth = 0;
- if (align) {
+ var seriesWidths = [];
+ series.each(function(d,i) {
+ var legendText;
+ if (keyFormatter(getKey(d)) && keyFormatter(getKey(d)).length > maxKeyLength) {
+ var trimmedKey = keyFormatter(getKey(d)).substring(0, maxKeyLength);
+ legendText = d3.select(this).select('text').text(trimmedKey + "...");
+ d3.select(this).append("svg:title").text(keyFormatter(getKey(d)));
+ } else {
+ legendText = d3.select(this).select('text');
+ }
+ var nodeTextLength;
+ try {
+ nodeTextLength = legendText.node().getComputedTextLength();
+ // If the legendText is display:none'd (nodeTextLength == 0), simulate an error so we approximate, instead
+ if(nodeTextLength <= 0) throw Error();
+ }
+ catch(e) {
+ nodeTextLength = nv.utils.calcApproxTextWidth(legendText);
+ }
- var seriesWidths = [];
- series.each(function(d,i) {
- var legendText;
- if (keyFormatter(getKey(d)) && keyFormatter(getKey(d)).length > maxKeyLength) {
- var trimmedKey = keyFormatter(getKey(d)).substring(0, maxKeyLength);
- legendText = d3.select(this).select('text').text(trimmedKey + "...");
- d3.select(this).append("svg:title").text(keyFormatter(getKey(d)));
- } else {
- legendText = d3.select(this).select('text');
- }
- var nodeTextLength;
- try {
- nodeTextLength = legendText.node().getComputedTextLength();
- // If the legendText is display:none'd (nodeTextLength == 0), simulate an error so we approximate, instead
- if(nodeTextLength <= 0) throw Error();
- }
- catch(e) {
- nodeTextLength = nv.utils.calcApproxTextWidth(legendText);
- }
+ seriesWidths.push(nodeTextLength + padding);
+ });
- seriesWidths.push(nodeTextLength + padding);
- });
+ var seriesPerRow = 0;
+ var columnWidths = [];
+ legendWidth = 0;
- var seriesPerRow = 0;
- var columnWidths = [];
- legendWidth = 0;
+ while ( legendWidth < availableWidth && seriesPerRow < seriesWidths.length) {
+ columnWidths[seriesPerRow] = seriesWidths[seriesPerRow];
+ legendWidth += seriesWidths[seriesPerRow++];
+ }
+ if (seriesPerRow === 0) seriesPerRow = 1; //minimum of one series per row
- while ( legendWidth < availableWidth && seriesPerRow < seriesWidths.length) {
- columnWidths[seriesPerRow] = seriesWidths[seriesPerRow];
- legendWidth += seriesWidths[seriesPerRow++];
- }
- if (seriesPerRow === 0) seriesPerRow = 1; //minimum of one series per row
+ while ( legendWidth > availableWidth && seriesPerRow > 1 ) {
+ columnWidths = [];
+ seriesPerRow--;
- while ( legendWidth > availableWidth && seriesPerRow > 1 ) {
- columnWidths = [];
- seriesPerRow--;
+ for (var k = 0; k < seriesWidths.length; k++) {
+ if (seriesWidths[k] > (columnWidths[k % seriesPerRow] || 0) )
+ columnWidths[k % seriesPerRow] = seriesWidths[k];
+ }
- for (var k = 0; k < seriesWidths.length; k++) {
- if (seriesWidths[k] > (columnWidths[k % seriesPerRow] || 0) )
- columnWidths[k % seriesPerRow] = seriesWidths[k];
+ legendWidth = columnWidths.reduce(function(prev, cur, index, array) {
+ return prev + cur;
+ });
}
- legendWidth = columnWidths.reduce(function(prev, cur, index, array) {
- return prev + cur;
- });
- }
+ var xPositions = [];
+ for (var i = 0, curX = 0; i < seriesPerRow; i++) {
+ xPositions[i] = curX;
+ curX += columnWidths[i];
+ }
- var xPositions = [];
- for (var i = 0, curX = 0; i < seriesPerRow; i++) {
- xPositions[i] = curX;
- curX += columnWidths[i];
- }
+ series
+ .attr('transform', function(d, i) {
+ return 'translate(' + xPositions[i % seriesPerRow] + ',' + (5 + Math.floor(i / seriesPerRow) * versPadding) + ')';
+ });
- series
- .attr('transform', function(d, i) {
- return 'translate(' + xPositions[i % seriesPerRow] + ',' + (5 + Math.floor(i / seriesPerRow) * versPadding) + ')';
- });
+ //position legend as far right as possible within the total width
+ if (rightAlign) {
+ g.attr('transform', 'translate(' + (width - margin.right - legendWidth) + ',' + margin.top + ')');
+ }
+ else {
+ g.attr('transform', 'translate(0' + ',' + margin.top + ')');
+ }
- //position legend as far right as possible within the total width
- if (rightAlign) {
- g.attr('transform', 'translate(' + (width - margin.right - legendWidth) + ',' + margin.top + ')');
- }
- else {
- g.attr('transform', 'translate(0' + ',' + margin.top + ')');
- }
+ height = margin.top + margin.bottom + (Math.ceil(seriesWidths.length / seriesPerRow) * versPadding);
- height = margin.top + margin.bottom + (Math.ceil(seriesWidths.length / seriesPerRow) * versPadding);
+ } else {
- } else {
+ var ypos = 5,
+ newxpos = 5,
+ maxwidth = 0,
+ xpos;
+ series
+ .attr('transform', function(d, i) {
+ var length = d3.select(this).select('text').node().getComputedTextLength() + padding;
+ xpos = newxpos;
- var ypos = 5,
- newxpos = 5,
- maxwidth = 0,
- xpos;
- series
- .attr('transform', function(d, i) {
- var length = d3.select(this).select('text').node().getComputedTextLength() + padding;
- xpos = newxpos;
+ if (width < margin.left + margin.right + xpos + length) {
+ newxpos = xpos = 5;
+ ypos += versPadding;
+ }
- if (width < margin.left + margin.right + xpos + length) {
- newxpos = xpos = 5;
- ypos += versPadding;
- }
+ newxpos += length;
+ if (newxpos > maxwidth) maxwidth = newxpos;
- newxpos += length;
- if (newxpos > maxwidth) maxwidth = newxpos;
+ if(legendWidth < xpos + maxwidth) {
+ legendWidth = xpos + maxwidth;
+ }
+ return 'translate(' + xpos + ',' + ypos + ')';
+ });
- if(legendWidth < xpos + maxwidth) {
- legendWidth = xpos + maxwidth;
- }
- return 'translate(' + xpos + ',' + ypos + ')';
- });
+ //position legend as far right as possible within the total width
+ g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')');
- //position legend as far right as possible within the total width
- g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')');
+ height = margin.top + margin.bottom + ypos + 15;
+ }
- height = margin.top + margin.bottom + ypos + 15;
- }
+ if(vers == 'furious') {
+ // Size rectangles after text is placed
+ seriesShape
+ .attr('width', function(d,i) {
+ return seriesText[0][i].getComputedTextLength() + 27;
+ })
+ .attr('height', 18)
+ .attr('y', -9)
+ .attr('x', -15);
- if(vers == 'furious') {
- // Size rectangles after text is placed
- seriesShape
- .attr('width', function(d,i) {
- return seriesText[0][i].getComputedTextLength() + 27;
- })
- .attr('height', 18)
- .attr('y', -9)
- .attr('x', -15);
+ // The background for the expanded legend (UI)
+ gEnter.insert('rect',':first-child')
+ .attr('class', 'nv-legend-bg')
+ .attr('fill', '#eee')
+ // .attr('stroke', '#444')
+ .attr('opacity',0);
- // The background for the expanded legend (UI)
- gEnter.insert('rect',':first-child')
- .attr('class', 'nv-legend-bg')
- .attr('fill', '#eee')
- // .attr('stroke', '#444')
- .attr('opacity',0);
+ var seriesBG = g.select('.nv-legend-bg');
- var seriesBG = g.select('.nv-legend-bg');
+ seriesBG
+ .transition().duration(300)
+ .attr('x', -versPadding )
+ .attr('width', legendWidth + versPadding - 12)
+ .attr('height', height + 10)
+ .attr('y', -margin.top - 10)
+ .attr('opacity', expanded ? 1 : 0);
- seriesBG
- .transition().duration(300)
- .attr('x', -versPadding )
- .attr('width', legendWidth + versPadding - 12)
- .attr('height', height + 10)
- .attr('y', -margin.top - 10)
- .attr('opacity', expanded ? 1 : 0);
+ }
- }
+ seriesShape
+ .style('fill', setBGColor)
+ .style('fill-opacity', setBGOpacity)
+ .style('stroke', setBGColor);
+ });
- seriesShape
- .style('fill', setBGColor)
- .style('fill-opacity', setBGOpacity)
- .style('stroke', setBGColor);
- });
-
- function setTextColor(d,i) {
- if(vers != 'furious') return '#000';
- if(expanded) {
- return d.disengaged ? '#000' : '#fff';
- } else if (!expanded) {
- if(!d.color) d.color = color(d,i);
- return !!d.disabled ? d.color : '#fff';
+ function setTextColor(d,i) {
+ if(vers != 'furious') return '#000';
+ if(expanded) {
+ return d.disengaged ? '#000' : '#fff';
+ } else if (!expanded) {
+ if(!d.color) d.color = color(d,i);
+ return !!d.disabled ? d.color : '#fff';
+ }
}
- }
- function setBGColor(d,i) {
- if(expanded && vers == 'furious') {
- return d.disengaged ? '#eee' : d.color || color(d,i);
- } else {
- return d.color || color(d,i);
+ function setBGColor(d,i) {
+ if(expanded && vers == 'furious') {
+ return d.disengaged ? '#eee' : d.color || color(d,i);
+ } else {
+ return d.color || color(d,i);
+ }
}
- }
- function setBGOpacity(d,i) {
- if(expanded && vers == 'furious') {
- return 1;
- } else {
- return !!d.disabled ? 0 : 1;
+ function setBGOpacity(d,i) {
+ if(expanded && vers == 'furious') {
+ return 1;
+ } else {
+ return !!d.disabled ? 0 : 1;
+ }
}
+
+ return chart;
}
- return chart;
- }
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ key: {get: function(){return getKey;}, set: function(_){getKey=_;}},
+ keyFormatter: {get: function(){return keyFormatter;}, set: function(_){keyFormatter=_;}},
+ align: {get: function(){return align;}, set: function(_){align=_;}},
+ maxKeyLength: {get: function(){return maxKeyLength;}, set: function(_){maxKeyLength=_;}},
+ rightAlign: {get: function(){return rightAlign;}, set: function(_){rightAlign=_;}},
+ padding: {get: function(){return padding;}, set: function(_){padding=_;}},
+ updateState: {get: function(){return updateState;}, set: function(_){updateState=_;}},
+ radioButtonMode:{get: function(){return radioButtonMode;}, set: function(_){radioButtonMode=_;}},
+ expanded: {get: function(){return expanded;}, set: function(_){expanded=_;}},
+ vers: {get: function(){return vers;}, set: function(_){vers=_;}},
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- key: {get: function(){return getKey;}, set: function(_){getKey=_;}},
- keyFormatter: {get: function(){return keyFormatter;}, set: function(_){keyFormatter=_;}},
- align: {get: function(){return align;}, set: function(_){align=_;}},
- maxKeyLength: {get: function(){return maxKeyLength;}, set: function(_){maxKeyLength=_;}},
- rightAlign: {get: function(){return rightAlign;}, set: function(_){rightAlign=_;}},
- padding: {get: function(){return padding;}, set: function(_){padding=_;}},
- updateState: {get: function(){return updateState;}, set: function(_){updateState=_;}},
- radioButtonMode:{get: function(){return radioButtonMode;}, set: function(_){radioButtonMode=_;}},
- expanded: {get: function(){return expanded;}, set: function(_){expanded=_;}},
- vers: {get: function(){return vers;}, set: function(_){vers=_;}},
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }}
- });
+ nv.utils.initOptions(chart);
- nv.utils.initOptions(chart);
+ return chart;
+ };
- return chart;
-};
+ nv.models.line = function() {
+ "use strict";
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
-nv.models.line = function() {
- "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ var scatter = nv.models.scatter()
+ ;
- var scatter = nv.models.scatter()
- ;
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 960
+ , height = 500
+ , container = null
+ , strokeWidth = 1.5
+ , color = nv.utils.defaultColor() // a function that returns a color
+ , getX = function(d) { return d.x } // accessor to get the x value from a data point
+ , getY = function(d) { return d.y } // accessor to get the y value from a data point
+ , defined = function(d,i) { return !isNaN(getY(d,i)) && getY(d,i) !== null } // allows a line to be not continuous when it is not defined
+ , isArea = function(d) { return d.area } // decides if a line is an area or just a line
+ , clipEdge = false // if true, masks lines within x and y scale
+ , x //can be accessed via chart.xScale()
+ , y //can be accessed via chart.yScale()
+ , interpolate = "linear" // controls the line interpolation
+ , duration = 250
+ , dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
+ ;
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 960
- , height = 500
- , container = null
- , strokeWidth = 1.5
- , color = nv.utils.defaultColor() // a function that returns a color
- , getX = function(d) { return d.x } // accessor to get the x value from a data point
- , getY = function(d) { return d.y } // accessor to get the y value from a data point
- , defined = function(d,i) { return !isNaN(getY(d,i)) && getY(d,i) !== null } // allows a line to be not continuous when it is not defined
- , isArea = function(d) { return d.area } // decides if a line is an area or just a line
- , clipEdge = false // if true, masks lines within x and y scale
- , x //can be accessed via chart.xScale()
- , y //can be accessed via chart.yScale()
- , interpolate = "linear" // controls the line interpolation
- , duration = 250
- , dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
+ scatter
+ .pointSize(16) // default size
+ .pointDomain([16,256]) //set to speed up calculation, needs to be unset if there is a custom size accessor
;
- scatter
- .pointSize(16) // default size
- .pointDomain([16,256]) //set to speed up calculation, needs to be unset if there is a custom size accessor
- ;
+ //============================================================
- //============================================================
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ var x0, y0 //used to store previous scales
+ , renderWatch = nv.utils.renderWatch(dispatch, duration)
+ ;
- var x0, y0 //used to store previous scales
- , renderWatch = nv.utils.renderWatch(dispatch, duration)
- ;
+ //============================================================
- //============================================================
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(scatter);
+ selection.each(function(data) {
+ container = d3.select(this);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+ nv.utils.initSVG(container);
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(scatter);
- selection.each(function(data) {
- container = d3.select(this);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
- nv.utils.initSVG(container);
+ // Setup Scales
+ x = scatter.xScale();
+ y = scatter.yScale();
- // Setup Scales
- x = scatter.xScale();
- y = scatter.yScale();
+ x0 = x0 || x;
+ y0 = y0 || y;
- x0 = x0 || x;
- y0 = y0 || y;
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-line').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-line');
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-line').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-line');
- var defsEnter = wrapEnter.append('defs');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ gEnter.append('g').attr('class', 'nv-groups');
+ gEnter.append('g').attr('class', 'nv-scatterWrap');
- gEnter.append('g').attr('class', 'nv-groups');
- gEnter.append('g').attr('class', 'nv-scatterWrap');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ scatter
+ .width(availableWidth)
+ .height(availableHeight);
- scatter
- .width(availableWidth)
- .height(availableHeight);
+ var scatterWrap = wrap.select('.nv-scatterWrap');
+ scatterWrap.call(scatter);
- var scatterWrap = wrap.select('.nv-scatterWrap');
- scatterWrap.call(scatter);
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-edge-clip-' + scatter.id())
+ .append('rect');
- defsEnter.append('clipPath')
- .attr('id', 'nv-edge-clip-' + scatter.id())
- .append('rect');
+ wrap.select('#nv-edge-clip-' + scatter.id() + ' rect')
+ .attr('width', availableWidth)
+ .attr('height', (availableHeight > 0) ? availableHeight : 0);
- wrap.select('#nv-edge-clip-' + scatter.id() + ' rect')
- .attr('width', availableWidth)
- .attr('height', (availableHeight > 0) ? availableHeight : 0);
+ g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : '');
+ scatterWrap
+ .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : '');
- g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : '');
- scatterWrap
- .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : '');
+ var groups = wrap.select('.nv-groups').selectAll('.nv-group')
+ .data(function(d) { return d }, function(d) { return d.key });
+ groups.enter().append('g')
+ .style('stroke-opacity', 1e-6)
+ .style('stroke-width', function(d) { return d.strokeWidth || strokeWidth })
+ .style('fill-opacity', 1e-6);
- var groups = wrap.select('.nv-groups').selectAll('.nv-group')
- .data(function(d) { return d }, function(d) { return d.key });
- groups.enter().append('g')
- .style('stroke-opacity', 1e-6)
- .style('stroke-width', function(d) { return d.strokeWidth || strokeWidth })
- .style('fill-opacity', 1e-6);
+ groups.exit().remove();
- groups.exit().remove();
+ groups
+ .attr('class', function(d,i) {
+ return (d.classed || '') + ' nv-group nv-series-' + i;
+ })
+ .classed('hover', function(d) { return d.hover })
+ .style('fill', function(d,i){ return color(d, i) })
+ .style('stroke', function(d,i){ return color(d, i)});
+ groups.watchTransition(renderWatch, 'line: groups')
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', function(d) { return d.fillOpacity || .5});
- groups
- .attr('class', function(d,i) {
- return (d.classed || '') + ' nv-group nv-series-' + i;
- })
- .classed('hover', function(d) { return d.hover })
- .style('fill', function(d,i){ return color(d, i) })
- .style('stroke', function(d,i){ return color(d, i)});
- groups.watchTransition(renderWatch, 'line: groups')
- .style('stroke-opacity', 1)
- .style('fill-opacity', function(d) { return d.fillOpacity || .5});
+ var areaPaths = groups.selectAll('path.nv-area')
+ .data(function(d) { return isArea(d) ? [d] : [] }); // this is done differently than lines because I need to check if series is an area
+ areaPaths.enter().append('path')
+ .attr('class', 'nv-area')
+ .attr('d', function(d) {
+ return d3.svg.area()
+ .interpolate(interpolate)
+ .defined(defined)
+ .x(function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) })
+ .y0(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
+ .y1(function(d,i) { return y0( y.domain()[0] <= 0 ? y.domain()[1] >= 0 ? 0 : y.domain()[1] : y.domain()[0] ) })
+ //.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
+ .apply(this, [d.values])
+ });
+ groups.exit().selectAll('path.nv-area')
+ .remove();
- var areaPaths = groups.selectAll('path.nv-area')
- .data(function(d) { return isArea(d) ? [d] : [] }); // this is done differently than lines because I need to check if series is an area
- areaPaths.enter().append('path')
- .attr('class', 'nv-area')
- .attr('d', function(d) {
- return d3.svg.area()
- .interpolate(interpolate)
- .defined(defined)
- .x(function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) })
- .y0(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
- .y1(function(d,i) { return y0( y.domain()[0] <= 0 ? y.domain()[1] >= 0 ? 0 : y.domain()[1] : y.domain()[0] ) })
- //.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
- .apply(this, [d.values])
- });
- groups.exit().selectAll('path.nv-area')
- .remove();
+ areaPaths.watchTransition(renderWatch, 'line: areaPaths')
+ .attr('d', function(d) {
+ return d3.svg.area()
+ .interpolate(interpolate)
+ .defined(defined)
+ .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
+ .y0(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
+ .y1(function(d,i) { return y( y.domain()[0] <= 0 ? y.domain()[1] >= 0 ? 0 : y.domain()[1] : y.domain()[0] ) })
+ //.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
+ .apply(this, [d.values])
+ });
- areaPaths.watchTransition(renderWatch, 'line: areaPaths')
- .attr('d', function(d) {
- return d3.svg.area()
- .interpolate(interpolate)
- .defined(defined)
- .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
- .y0(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
- .y1(function(d,i) { return y( y.domain()[0] <= 0 ? y.domain()[1] >= 0 ? 0 : y.domain()[1] : y.domain()[0] ) })
- //.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
- .apply(this, [d.values])
- });
+ var linePaths = groups.selectAll('path.nv-line')
+ .data(function(d) { return [d.values] });
- var linePaths = groups.selectAll('path.nv-line')
- .data(function(d) { return [d.values] });
+ linePaths.enter().append('path')
+ .attr('class', 'nv-line')
+ .attr('d',
+ d3.svg.line()
+ .interpolate(interpolate)
+ .defined(defined)
+ .x(function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) })
+ .y(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
+ );
- linePaths.enter().append('path')
- .attr('class', 'nv-line')
- .attr('d',
- d3.svg.line()
- .interpolate(interpolate)
- .defined(defined)
- .x(function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) })
- .y(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
- );
+ linePaths.watchTransition(renderWatch, 'line: linePaths')
+ .attr('d',
+ d3.svg.line()
+ .interpolate(interpolate)
+ .defined(defined)
+ .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
+ .y(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
+ );
- linePaths.watchTransition(renderWatch, 'line: linePaths')
- .attr('d',
- d3.svg.line()
- .interpolate(interpolate)
- .defined(defined)
- .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
- .y(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
- );
+ //store old scales for use in transitions on update
+ x0 = x.copy();
+ y0 = y.copy();
+ });
+ renderWatch.renderEnd('line immediate');
+ return chart;
+ }
- //store old scales for use in transitions on update
- x0 = x.copy();
- y0 = y.copy();
- });
- renderWatch.renderEnd('line immediate');
- return chart;
- }
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ chart.dispatch = dispatch;
+ chart.scatter = scatter;
+ // Pass through events
+ scatter.dispatch.on('elementClick', function(){ dispatch.elementClick.apply(this, arguments); });
+ scatter.dispatch.on('elementMouseover', function(){ dispatch.elementMouseover.apply(this, arguments); });
+ scatter.dispatch.on('elementMouseout', function(){ dispatch.elementMouseout.apply(this, arguments); });
- chart.dispatch = dispatch;
- chart.scatter = scatter;
- // Pass through events
- scatter.dispatch.on('elementClick', function(){ dispatch.elementClick.apply(this, arguments); });
- scatter.dispatch.on('elementMouseover', function(){ dispatch.elementMouseover.apply(this, arguments); });
- scatter.dispatch.on('elementMouseout', function(){ dispatch.elementMouseout.apply(this, arguments); });
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ defined: {get: function(){return defined;}, set: function(_){defined=_;}},
+ interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- defined: {get: function(){return defined;}, set: function(_){defined=_;}},
- interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}},
- clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ scatter.duration(duration);
+ }},
+ isArea: {get: function(){return isArea;}, set: function(_){
+ isArea = d3.functor(_);
+ }},
+ x: {get: function(){return getX;}, set: function(_){
+ getX = _;
+ scatter.x(_);
+ }},
+ y: {get: function(){return getY;}, set: function(_){
+ getY = _;
+ scatter.y(_);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ scatter.color(color);
+ }}
+ });
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- scatter.duration(duration);
- }},
- isArea: {get: function(){return isArea;}, set: function(_){
- isArea = d3.functor(_);
- }},
- x: {get: function(){return getX;}, set: function(_){
- getX = _;
- scatter.x(_);
- }},
- y: {get: function(){return getY;}, set: function(_){
- getY = _;
- scatter.y(_);
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- scatter.color(color);
- }}
- });
+ nv.utils.inheritOptions(chart, scatter);
+ nv.utils.initOptions(chart);
- nv.utils.inheritOptions(chart, scatter);
- nv.utils.initOptions(chart);
+ return chart;
+ };
+ nv.models.lineChart = function() {
+ "use strict";
- return chart;
-};
-nv.models.lineChart = function() {
- "use strict";
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ var lines = nv.models.line()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend()
+ , interactiveLayer = nv.interactiveGuideline()
+ , tooltip = nv.models.tooltip()
+ , focus = nv.models.focus(nv.models.line())
+ ;
- var lines = nv.models.line()
- , xAxis = nv.models.axis()
- , yAxis = nv.models.axis()
- , legend = nv.models.legend()
- , interactiveLayer = nv.interactiveGuideline()
- , tooltip = nv.models.tooltip()
- , focus = nv.models.focus(nv.models.line())
- ;
+ var margin = {top: 30, right: 20, bottom: 50, left: 60}
+ , marginTop = null
+ , color = nv.utils.defaultColor()
+ , width = null
+ , height = null
+ , showLegend = true
+ , legendPosition = 'top'
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , useInteractiveGuideline = false
+ , x
+ , y
+ , focusEnable = false
+ , state = nv.utils.state()
+ , defaultState = null
+ , noData = null
+ , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
+ , duration = 250
+ ;
- var margin = {top: 30, right: 20, bottom: 50, left: 60}
- , color = nv.utils.defaultColor()
- , width = null
- , height = null
- , showLegend = true
- , legendPosition = 'top'
- , showXAxis = true
- , showYAxis = true
- , rightAlignYAxis = false
- , useInteractiveGuideline = false
- , x
- , y
- , focusEnable = false
- , state = nv.utils.state()
- , defaultState = null
- , noData = null
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
- , duration = 250
- ;
+ // set options on sub-objects for this chart
+ xAxis.orient('bottom').tickPadding(7);
+ yAxis.orient(rightAlignYAxis ? 'right' : 'left');
- // set options on sub-objects for this chart
- xAxis.orient('bottom').tickPadding(7);
- yAxis.orient(rightAlignYAxis ? 'right' : 'left');
+ lines.clipEdge(true).duration(0);
- lines.clipEdge(true).duration(0);
+ tooltip.valueFormatter(function(d, i) {
+ return yAxis.tickFormat()(d, i);
+ }).headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ });
- tooltip.valueFormatter(function(d, i) {
- return yAxis.tickFormat()(d, i);
- }).headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- });
+ interactiveLayer.tooltip.valueFormatter(function(d, i) {
+ return yAxis.tickFormat()(d, i);
+ }).headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ });
- interactiveLayer.tooltip.valueFormatter(function(d, i) {
- return yAxis.tickFormat()(d, i);
- }).headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- });
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
-
- var stateGetter = function(data) {
- return function(){
- return {
- active: data.map(function(d) { return !d.disabled; })
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled; })
+ };
};
};
- };
- var stateSetter = function(data) {
- return function(state) {
- if (state.active !== undefined)
- data.forEach(function(series,i) {
- series.disabled = !state.active[i];
- });
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ };
};
- };
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(lines);
- if (showXAxis) renderWatch.models(xAxis);
- if (showYAxis) renderWatch.models(yAxis);
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(lines);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
- selection.each(function(data) {
- var container = d3.select(this);
- nv.utils.initSVG(container);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
- chart.update = function() {
- if( duration === 0 ) {
- container.call( chart );
- } else {
- container.transition().duration(duration).call(chart);
- }
- };
- chart.container = this;
+ selection.each(function(data) {
+ var container = d3.select(this);
+ nv.utils.initSVG(container);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
+ chart.update = function() {
+ if( duration === 0 ) {
+ container.call( chart );
+ } else {
+ container.transition().duration(duration).call(chart);
+ }
+ };
+ chart.container = this;
- state
- .setter(stateSetter(data), chart.update)
- .getter(stateGetter(data))
- .update();
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
- // DEPRECATED set state.disabled
- state.disabled = data.map(function(d) { return !!d.disabled; });
+ // DEPRECATED set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled; });
- if (!defaultState) {
- var key;
- defaultState = {};
- for (key in state) {
- if (state[key] instanceof Array)
- defaultState[key] = state[key].slice(0);
- else
- defaultState[key] = state[key];
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
}
- }
- // Display noData message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length; }).length) {
- nv.utils.noData(chart, container);
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
+ // Display noData message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length; }).length) {
+ nv.utils.noData(chart, container);
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
- /* Update `main' graph on brush update. */
- focus.dispatch.on("onBrush", function(extent) {
- onBrush(extent);
- });
+ /* Update `main' graph on brush update. */
+ focus.dispatch.on("onBrush", function(extent) {
+ onBrush(extent);
+ });
- // Setup Scales
- x = lines.xScale();
- y = lines.yScale();
+ // Setup Scales
+ x = lines.xScale();
+ y = lines.yScale();
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-lineChart').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineChart').append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-lineChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineChart').append('g');
+ var g = wrap.select('g');
- gEnter.append('g').attr('class', 'nv-legendWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
- var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
- focusEnter.append('g').attr('class', 'nv-background').append('rect');
- focusEnter.append('g').attr('class', 'nv-x nv-axis');
- focusEnter.append('g').attr('class', 'nv-y nv-axis');
- focusEnter.append('g').attr('class', 'nv-linesWrap');
- focusEnter.append('g').attr('class', 'nv-interactive');
+ var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
+ focusEnter.append('g').attr('class', 'nv-background').append('rect');
+ focusEnter.append('g').attr('class', 'nv-x nv-axis');
+ focusEnter.append('g').attr('class', 'nv-y nv-axis');
+ focusEnter.append('g').attr('class', 'nv-linesWrap');
+ focusEnter.append('g').attr('class', 'nv-interactive');
- var contextEnter = gEnter.append('g').attr('class', 'nv-focusWrap');
+ var contextEnter = gEnter.append('g').attr('class', 'nv-focusWrap');
- // Legend
- if (!showLegend) {
- g.select('.nv-legendWrap').selectAll('*').remove();
- } else {
- legend.width(availableWidth);
+ // Legend
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
+ legend.width(availableWidth);
- g.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
+ g.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
- if (legendPosition === 'bottom') {
- wrap.select('.nv-legendWrap')
- .attr('transform', 'translate(0,' + availableHeight +')');
- } else if (legendPosition === 'top') {
- if (legend.height() > margin.top) {
- margin.top = legend.height();
- availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
+ if (legendPosition === 'bottom') {
+ wrap.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + availableHeight +')');
+ } else if (legendPosition === 'top') {
+ if (!marginTop && legend.height() !== margin.top) {
+ margin.top = legend.height();
+ availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
+ }
+
+ wrap.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')');
}
+ }
- wrap.select('.nv-legendWrap')
- .attr('transform', 'translate(0,' + (-margin.top) +')');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
}
- }
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ //Set up interactive layer
+ if (useInteractiveGuideline) {
+ interactiveLayer
+ .width(availableWidth)
+ .height(availableHeight)
+ .margin({left:margin.left, top:margin.top})
+ .svgContainer(container)
+ .xScale(x);
+ wrap.select(".nv-interactive").call(interactiveLayer);
+ }
- if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
- }
+ g.select('.nv-focus .nv-background rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
- //Set up interactive layer
- if (useInteractiveGuideline) {
- interactiveLayer
+ lines
.width(availableWidth)
.height(availableHeight)
- .margin({left:margin.left, top:margin.top})
- .svgContainer(container)
- .xScale(x);
- wrap.select(".nv-interactive").call(interactiveLayer);
- }
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled; }));
- g.select('.nv-focus .nv-background rect')
- .attr('width', availableWidth)
- .attr('height', availableHeight);
+ var linesWrap = g.select('.nv-linesWrap')
+ .datum(data.filter(function(d) { return !d.disabled; }));
- lines
- .width(availableWidth)
- .height(availableHeight)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled; }));
- var linesWrap = g.select('.nv-linesWrap')
- .datum(data.filter(function(d) { return !d.disabled; }));
+ // Setup Main (Focus) Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ ._ticks(nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight, 0);
+ }
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
+ }
- // Setup Main (Focus) Axes
- if (showXAxis) {
- xAxis
- .scale(x)
- ._ticks(nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize(-availableHeight, 0);
- }
+ //============================================================
+ // Update Axes
+ //============================================================
+ function updateXAxis() {
+ if(showXAxis) {
+ g.select('.nv-focus .nv-x.nv-axis')
+ .transition()
+ .duration(duration)
+ .call(xAxis)
+ ;
+ }
+ }
- if (showYAxis) {
- yAxis
- .scale(y)
- ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
- }
+ function updateYAxis() {
+ if(showYAxis) {
+ g.select('.nv-focus .nv-y.nv-axis')
+ .transition()
+ .duration(duration)
+ .call(yAxis)
+ ;
+ }
+ }
- //============================================================
- // Update Axes
- //============================================================
- function updateXAxis() {
- if(showXAxis) {
g.select('.nv-focus .nv-x.nv-axis')
- .transition()
- .duration(duration)
- .call(xAxis)
- ;
- }
- }
+ .attr('transform', 'translate(0,' + availableHeight + ')');
- function updateYAxis() {
- if(showYAxis) {
- g.select('.nv-focus .nv-y.nv-axis')
- .transition()
- .duration(duration)
- .call(yAxis)
- ;
- }
- }
-
- g.select('.nv-focus .nv-x.nv-axis')
- .attr('transform', 'translate(0,' + availableHeight + ')');
-
- //============================================================
- // Update Focus
- //============================================================
- if(!focusEnable) {
- linesWrap.call(lines);
- updateXAxis();
- updateYAxis();
- } else {
- focus.width(availableWidth);
- g.select('.nv-focusWrap')
- .attr('transform', 'translate(0,' + ( availableHeight + margin.bottom + focus.margin().top) + ')')
- .datum(data.filter(function(d) { return !d.disabled; }))
- .call(focus);
- var extent = focus.brush.empty() ? focus.xDomain() : focus.brush.extent();
- if(extent !== null){
- onBrush(extent);
+ //============================================================
+ // Update Focus
+ //============================================================
+ if(!focusEnable) {
+ linesWrap.call(lines);
+ updateXAxis();
+ updateYAxis();
+ } else {
+ focus.width(availableWidth);
+ g.select('.nv-focusWrap')
+ .attr('transform', 'translate(0,' + ( availableHeight + margin.bottom + focus.margin().top) + ')')
+ .datum(data.filter(function(d) { return !d.disabled; }))
+ .call(focus);
+ var extent = focus.brush.empty() ? focus.xDomain() : focus.brush.extent();
+ if(extent !== null){
+ onBrush(extent);
+ }
}
- }
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
- legend.dispatch.on('stateChange', function(newState) {
- for (var key in newState)
- state[key] = newState[key];
- dispatch.stateChange(state);
- chart.update();
- });
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
- interactiveLayer.dispatch.on('elementMousemove', function(e) {
- lines.clearHighlights();
- var singlePoint, pointIndex, pointXLocation, allData = [];
- data
- .filter(function(series, i) {
- series.seriesIndex = i;
- return !series.disabled && !series.disableTooltip;
- })
- .forEach(function(series,i) {
- var extent = focusEnable ? (focus.brush.empty() ? focus.xScale().domain() : focus.brush.extent()) : x.domain();
- var currentValues = series.values.filter(function(d,i) {
- return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
- });
+ interactiveLayer.dispatch.on('elementMousemove', function(e) {
+ lines.clearHighlights();
+ var singlePoint, pointIndex, pointXLocation, allData = [];
+ data
+ .filter(function(series, i) {
+ series.seriesIndex = i;
+ return !series.disabled && !series.disableTooltip;
+ })
+ .forEach(function(series,i) {
+ var extent = focusEnable ? (focus.brush.empty() ? focus.xScale().domain() : focus.brush.extent()) : x.domain();
+ var currentValues = series.values.filter(function(d,i) {
+ // Checks if the x point is between the extents, handling case where extent[0] is greater than extent[1]
+ // (e.g. x domain is manually set to reverse the x-axis)
+ if(extent[0] <= extent[1]) {
+ return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
+ } else {
+ return lines.x()(d,i) >= extent[1] && lines.x()(d,i) <= extent[0];
+ }
+ });
- pointIndex = nv.interactiveBisect(currentValues, e.pointXValue, lines.x());
- var point = currentValues[pointIndex];
- var pointYValue = chart.y()(point, pointIndex);
- if (pointYValue !== null) {
- lines.highlightPoint(series.seriesIndex, pointIndex, true);
- }
- if (point === undefined) return;
- if (singlePoint === undefined) singlePoint = point;
- if (pointXLocation === undefined) pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
- allData.push({
- key: series.key,
- value: pointYValue,
- color: color(series,series.seriesIndex),
- data: point
+ pointIndex = nv.interactiveBisect(currentValues, e.pointXValue, lines.x());
+ var point = currentValues[pointIndex];
+ var pointYValue = chart.y()(point, pointIndex);
+ if (pointYValue !== null) {
+ lines.highlightPoint(i, pointIndex, true);
+ }
+ if (point === undefined) return;
+ if (singlePoint === undefined) singlePoint = point;
+ if (pointXLocation === undefined) pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
+ allData.push({
+ key: series.key,
+ value: pointYValue,
+ color: color(series,series.seriesIndex),
+ data: point
+ });
});
- });
- //Highlight the tooltip entry based on which point the mouse is closest to.
- if (allData.length > 2) {
- var yValue = chart.yScale().invert(e.mouseY);
- var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]);
- var threshold = 0.03 * domainExtent;
- var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value;}),yValue,threshold);
- if (indexToHighlight !== null)
- allData[indexToHighlight].highlight = true;
- }
+ //Highlight the tooltip entry based on which point the mouse is closest to.
+ if (allData.length > 2) {
+ var yValue = chart.yScale().invert(e.mouseY);
+ var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]);
+ var threshold = 0.03 * domainExtent;
+ var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value;}),yValue,threshold);
+ if (indexToHighlight !== null)
+ allData[indexToHighlight].highlight = true;
+ }
- var defaultValueFormatter = function(d,i) {
- return d == null ? "N/A" : yAxis.tickFormat()(d);
- };
+ var defaultValueFormatter = function(d,i) {
+ return d == null ? "N/A" : yAxis.tickFormat()(d);
+ };
- interactiveLayer.tooltip
- .valueFormatter(interactiveLayer.tooltip.valueFormatter() || defaultValueFormatter)
- .data({
- value: chart.x()( singlePoint,pointIndex ),
- index: pointIndex,
- series: allData
- })();
+ interactiveLayer.tooltip
+ .valueFormatter(interactiveLayer.tooltip.valueFormatter() || defaultValueFormatter)
+ .data({
+ value: chart.x()( singlePoint,pointIndex ),
+ index: pointIndex,
+ series: allData
+ })();
- interactiveLayer.renderGuideLine(pointXLocation);
+ interactiveLayer.renderGuideLine(pointXLocation);
- });
+ });
- interactiveLayer.dispatch.on('elementClick', function(e) {
- var pointXLocation, allData = [];
+ interactiveLayer.dispatch.on('elementClick', function(e) {
+ var pointXLocation, allData = [];
- data.filter(function(series, i) {
- series.seriesIndex = i;
- return !series.disabled;
- }).forEach(function(series) {
- var pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
- var point = series.values[pointIndex];
- if (typeof point === 'undefined') return;
- if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
- var yPos = chart.yScale()(chart.y()(point,pointIndex));
- allData.push({
- point: point,
- pointIndex: pointIndex,
- pos: [pointXLocation, yPos],
- seriesIndex: series.seriesIndex,
- series: series
+ data.filter(function(series, i) {
+ series.seriesIndex = i;
+ return !series.disabled;
+ }).forEach(function(series) {
+ var pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
+ var point = series.values[pointIndex];
+ if (typeof point === 'undefined') return;
+ if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
+ var yPos = chart.yScale()(chart.y()(point,pointIndex));
+ allData.push({
+ point: point,
+ pointIndex: pointIndex,
+ pos: [pointXLocation, yPos],
+ seriesIndex: series.seriesIndex,
+ series: series
+ });
});
+
+ lines.dispatch.elementClick(allData);
});
- lines.dispatch.elementClick(allData);
- });
+ interactiveLayer.dispatch.on("elementMouseout",function(e) {
+ lines.clearHighlights();
+ });
- interactiveLayer.dispatch.on("elementMouseout",function(e) {
- lines.clearHighlights();
- });
+ dispatch.on('changeState', function(e) {
+ if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
- dispatch.on('changeState', function(e) {
- if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) {
- data.forEach(function(series,i) {
- series.disabled = e.disabled[i];
- });
+ state.disabled = e.disabled;
+ }
+ chart.update();
+ });
- state.disabled = e.disabled;
+ //============================================================
+ // Functions
+ //------------------------------------------------------------
+
+ // Taken from crossfilter (http://square.github.com/crossfilter/)
+ function resizePath(d) {
+ var e = +(d == 'e'),
+ x = e ? 1 : -1,
+ y = availableHeight / 3;
+ return 'M' + (0.5 * x) + ',' + y
+ + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
+ + 'V' + (2 * y - 6)
+ + 'A6,6 0 0 ' + e + ' ' + (0.5 * x) + ',' + (2 * y)
+ + 'Z'
+ + 'M' + (2.5 * x) + ',' + (y + 8)
+ + 'V' + (2 * y - 8)
+ + 'M' + (4.5 * x) + ',' + (y + 8)
+ + 'V' + (2 * y - 8);
}
- chart.update();
+
+ function onBrush(extent) {
+ // Update Main (Focus)
+ var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
+ .datum(
+ data.filter(function(d) { return !d.disabled; })
+ .map(function(d,i) {
+ return {
+ key: d.key,
+ area: d.area,
+ classed: d.classed,
+ values: d.values.filter(function(d,i) {
+ return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
+ }),
+ disableTooltip: d.disableTooltip
+ };
+ })
+ );
+ focusLinesWrap.transition().duration(duration).call(lines);
+
+ // Update Main (Focus) Axes
+ updateXAxis();
+ updateYAxis();
+ }
});
- //============================================================
- // Functions
- //------------------------------------------------------------
+ renderWatch.renderEnd('lineChart immediate');
+ return chart;
+ }
- // Taken from crossfilter (http://square.github.com/crossfilter/)
- function resizePath(d) {
- var e = +(d == 'e'),
- x = e ? 1 : -1,
- y = availableHeight / 3;
- return 'M' + (0.5 * x) + ',' + y
- + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
- + 'V' + (2 * y - 6)
- + 'A6,6 0 0 ' + e + ' ' + (0.5 * x) + ',' + (2 * y)
- + 'Z'
- + 'M' + (2.5 * x) + ',' + (y + 8)
- + 'V' + (2 * y - 8)
- + 'M' + (4.5 * x) + ',' + (y + 8)
- + 'V' + (2 * y - 8);
- }
- function onBrush(extent) {
- // Update Main (Focus)
- var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
- .datum(
- data.filter(function(d) { return !d.disabled; })
- .map(function(d,i) {
- return {
- key: d.key,
- area: d.area,
- classed: d.classed,
- values: d.values.filter(function(d,i) {
- return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
- }),
- disableTooltip: d.disableTooltip
- };
- })
- );
- focusLinesWrap.transition().duration(duration).call(lines);
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
- // Update Main (Focus) Axes
- updateXAxis();
- updateYAxis();
+ lines.dispatch.on('elementMouseover.tooltip', function(evt) {
+ if(!evt.series.disableTooltip){
+ tooltip.data(evt).hidden(false);
}
});
- renderWatch.renderEnd('lineChart immediate');
- return chart;
- }
+ lines.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true);
+ });
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.lines = lines;
+ chart.legend = legend;
+ chart.focus = focus;
+ chart.xAxis = xAxis;
+ chart.x2Axis = focus.xAxis
+ chart.yAxis = yAxis;
+ chart.y2Axis = focus.yAxis
+ chart.interactiveLayer = interactiveLayer;
+ chart.tooltip = tooltip;
+ chart.state = state;
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- lines.dispatch.on('elementMouseover.tooltip', function(evt) {
- if(!evt.series.disableTooltip){
- tooltip.data(evt).hidden(false);
- }
- });
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ // Focus options, mostly passed onto focus model.
+ focusEnable: {get: function(){return focusEnable;}, set: function(_){focusEnable=_;}},
+ focusHeight: {get: function(){return focus.height();}, set: function(_){focus.height(_);}},
+ focusShowAxisX: {get: function(){return focus.showXAxis();}, set: function(_){focus.showXAxis(_);}},
+ focusShowAxisY: {get: function(){return focus.showYAxis();}, set: function(_){focus.showYAxis(_);}},
+ brushExtent: {get: function(){return focus.brushExtent();}, set: function(_){focus.brushExtent(_);}},
- lines.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true);
- });
+ // options that require extra logic in the setter
+ focusMargin: {get: function(){return focus.margin}, set: function(_){
+ if (_.top !== undefined) {
+ margin.top = _.top;
+ marginTop = _.top;
+ }
+ focus.margin.right = _.right !== undefined ? _.right : focus.margin.right;
+ focus.margin.bottom = _.bottom !== undefined ? _.bottom : focus.margin.bottom;
+ focus.margin.left = _.left !== undefined ? _.left : focus.margin.left;
+ }},
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ lines.duration(duration);
+ focus.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ lines.color(color);
+ focus.color(color);
+ }},
+ interpolate: {get: function(){return lines.interpolate();}, set: function(_){
+ lines.interpolate(_);
+ focus.interpolate(_);
+ }},
+ xTickFormat: {get: function(){return xAxis.tickFormat();}, set: function(_){
+ xAxis.tickFormat(_);
+ focus.xTickFormat(_);
+ }},
+ yTickFormat: {get: function(){return yAxis.tickFormat();}, set: function(_){
+ yAxis.tickFormat(_);
+ focus.yTickFormat(_);
+ }},
+ x: {get: function(){return lines.x();}, set: function(_){
+ lines.x(_);
+ focus.x(_);
+ }},
+ y: {get: function(){return lines.y();}, set: function(_){
+ lines.y(_);
+ focus.y(_);
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( rightAlignYAxis ? 'right' : 'left');
+ }},
+ useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
+ useInteractiveGuideline = _;
+ if (useInteractiveGuideline) {
+ lines.interactive(false);
+ lines.useVoronoi(false);
+ }
+ }}
+ });
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ nv.utils.inheritOptions(chart, lines);
+ nv.utils.initOptions(chart);
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.lines = lines;
- chart.legend = legend;
- chart.focus = focus;
- chart.xAxis = xAxis;
- chart.x2Axis = focus.xAxis
- chart.yAxis = yAxis;
- chart.y2Axis = focus.yAxis
- chart.interactiveLayer = interactiveLayer;
- chart.tooltip = tooltip;
- chart.state = state;
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ return chart;
+ };
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- // Focus options, mostly passed onto focus model.
- focusEnable: {get: function(){return focusEnable;}, set: function(_){focusEnable=_;}},
- focusHeight: {get: function(){return focus.height();}, set: function(_){focus.height(_);}},
- focusShowAxisX: {get: function(){return focus.showXAxis();}, set: function(_){focus.showXAxis(_);}},
- focusShowAxisY: {get: function(){return focus.showYAxis();}, set: function(_){focus.showYAxis(_);}},
- brushExtent: {get: function(){return focus.brushExtent();}, set: function(_){focus.brushExtent(_);}},
+ nv.models.lineWithFocusChart = function() {
+ return nv.models.lineChart()
+ .margin({ bottom: 30 })
+ .focusEnable( true );
+ };
+ nv.models.linePlusBarChart = function() {
+ "use strict";
- // options that require extra logic in the setter
- focusMargin: {get: function(){return focus.margin}, set: function(_){
- focus.margin.top = _.top !== undefined ? _.top : focus.margin.top;
- focus.margin.right = _.right !== undefined ? _.right : focus.margin.right;
- focus.margin.bottom = _.bottom !== undefined ? _.bottom : focus.margin.bottom;
- focus.margin.left = _.left !== undefined ? _.left : focus.margin.left;
- }},
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- lines.duration(duration);
- focus.duration(duration);
- xAxis.duration(duration);
- yAxis.duration(duration);
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- legend.color(color);
- lines.color(color);
- focus.color(color);
- }},
- interpolate: {get: function(){return lines.interpolate();}, set: function(_){
- lines.interpolate(_);
- focus.interpolate(_);
- }},
- xTickFormat: {get: function(){return xAxis.tickFormat();}, set: function(_){
- xAxis.tickFormat(_);
- focus.xTickFormat(_);
- }},
- yTickFormat: {get: function(){return yAxis.tickFormat();}, set: function(_){
- yAxis.tickFormat(_);
- focus.yTickFormat(_);
- }},
- x: {get: function(){return lines.x();}, set: function(_){
- lines.x(_);
- focus.x(_);
- }},
- y: {get: function(){return lines.y();}, set: function(_){
- lines.y(_);
- focus.y(_);
- }},
- rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
- rightAlignYAxis = _;
- yAxis.orient( rightAlignYAxis ? 'right' : 'left');
- }},
- useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
- useInteractiveGuideline = _;
- if (useInteractiveGuideline) {
- lines.interactive(false);
- lines.useVoronoi(false);
- }
- }}
- });
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- nv.utils.inheritOptions(chart, lines);
- nv.utils.initOptions(chart);
+ var lines = nv.models.line()
+ , lines2 = nv.models.line()
+ , bars = nv.models.historicalBar()
+ , bars2 = nv.models.historicalBar()
+ , xAxis = nv.models.axis()
+ , x2Axis = nv.models.axis()
+ , y1Axis = nv.models.axis()
+ , y2Axis = nv.models.axis()
+ , y3Axis = nv.models.axis()
+ , y4Axis = nv.models.axis()
+ , legend = nv.models.legend()
+ , brush = d3.svg.brush()
+ , tooltip = nv.models.tooltip()
+ ;
- return chart;
-};
+ var margin = {top: 30, right: 30, bottom: 30, left: 60}
+ , marginTop = null
+ , margin2 = {top: 0, right: 30, bottom: 20, left: 60}
+ , width = null
+ , height = null
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , color = nv.utils.defaultColor()
+ , showLegend = true
+ , focusEnable = true
+ , focusShowAxisY = false
+ , focusShowAxisX = true
+ , focusHeight = 50
+ , extent
+ , brushExtent = null
+ , x
+ , x2
+ , y1
+ , y2
+ , y3
+ , y4
+ , noData = null
+ , dispatch = d3.dispatch('brush', 'stateChange', 'changeState')
+ , transitionDuration = 0
+ , state = nv.utils.state()
+ , defaultState = null
+ , legendLeftAxisHint = ' (left axis)'
+ , legendRightAxisHint = ' (right axis)'
+ , switchYAxisOrder = false
+ ;
-nv.models.lineWithFocusChart = function() {
- return nv.models.lineChart()
- .margin({ bottom: 30 })
- .focusEnable( true );
-};
-nv.models.linePlusBarChart = function() {
- "use strict";
+ lines.clipEdge(true);
+ lines2.interactive(false);
+ // We don't want any points emitted for the focus chart's scatter graph.
+ lines2.pointActive(function(d) { return false });
+ xAxis.orient('bottom').tickPadding(5);
+ y1Axis.orient('left');
+ y2Axis.orient('right');
+ x2Axis.orient('bottom').tickPadding(5);
+ y3Axis.orient('left');
+ y4Axis.orient('right');
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ tooltip.headerEnabled(true).headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ });
- var lines = nv.models.line()
- , lines2 = nv.models.line()
- , bars = nv.models.historicalBar()
- , bars2 = nv.models.historicalBar()
- , xAxis = nv.models.axis()
- , x2Axis = nv.models.axis()
- , y1Axis = nv.models.axis()
- , y2Axis = nv.models.axis()
- , y3Axis = nv.models.axis()
- , y4Axis = nv.models.axis()
- , legend = nv.models.legend()
- , brush = d3.svg.brush()
- , tooltip = nv.models.tooltip()
- ;
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var margin = {top: 30, right: 30, bottom: 30, left: 60}
- , margin2 = {top: 0, right: 30, bottom: 20, left: 60}
- , width = null
- , height = null
- , getX = function(d) { return d.x }
- , getY = function(d) { return d.y }
- , color = nv.utils.defaultColor()
- , showLegend = true
- , focusEnable = true
- , focusShowAxisY = false
- , focusShowAxisX = true
- , focusHeight = 50
- , extent
- , brushExtent = null
- , x
- , x2
- , y1
- , y2
- , y3
- , y4
- , noData = null
- , dispatch = d3.dispatch('brush', 'stateChange', 'changeState')
- , transitionDuration = 0
- , state = nv.utils.state()
- , defaultState = null
- , legendLeftAxisHint = ' (left axis)'
- , legendRightAxisHint = ' (right axis)'
- , switchYAxisOrder = false
- ;
+ var getBarsAxis = function() {
+ return switchYAxisOrder
+ ? { main: y2Axis, focus: y4Axis }
+ : { main: y1Axis, focus: y3Axis }
+ }
- lines.clipEdge(true);
- lines2.interactive(false);
- // We don't want any points emitted for the focus chart's scatter graph.
- lines2.pointActive(function(d) { return false });
- xAxis.orient('bottom').tickPadding(5);
- y1Axis.orient('left');
- y2Axis.orient('right');
- x2Axis.orient('bottom').tickPadding(5);
- y3Axis.orient('left');
- y4Axis.orient('right');
+ var getLinesAxis = function() {
+ return switchYAxisOrder
+ ? { main: y1Axis, focus: y3Axis }
+ : { main: y2Axis, focus: y4Axis }
+ }
- tooltip.headerEnabled(true).headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- });
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled })
+ };
+ }
+ };
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ };
- var getBarsAxis = function() {
- return switchYAxisOrder
- ? { main: y2Axis, focus: y4Axis }
- : { main: y1Axis, focus: y3Axis }
- }
-
- var getLinesAxis = function() {
- return switchYAxisOrder
- ? { main: y1Axis, focus: y3Axis }
- : { main: y2Axis, focus: y4Axis }
- }
-
- var stateGetter = function(data) {
- return function(){
- return {
- active: data.map(function(d) { return !d.disabled })
- };
+ var allDisabled = function(data) {
+ return data.every(function(series) {
+ return series.disabled;
+ });
}
- };
- var stateSetter = function(data) {
- return function(state) {
- if (state.active !== undefined)
- data.forEach(function(series,i) {
- series.disabled = !state.active[i];
- });
- }
- };
+ function chart(selection) {
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight1 = nv.utils.availableHeight(height, container, margin)
+ - (focusEnable ? focusHeight : 0),
+ availableHeight2 = focusHeight - margin2.top - margin2.bottom;
- var allDisabled = function(data) {
- return data.every(function(series) {
- return series.disabled;
- });
- }
+ chart.update = function() { container.transition().duration(transitionDuration).call(chart); };
+ chart.container = this;
- function chart(selection) {
- selection.each(function(data) {
- var container = d3.select(this),
- that = this;
- nv.utils.initSVG(container);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight1 = nv.utils.availableHeight(height, container, margin)
- - (focusEnable ? focusHeight : 0),
- availableHeight2 = focusHeight - margin2.top - margin2.bottom;
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
- chart.update = function() { container.transition().duration(transitionDuration).call(chart); };
- chart.container = this;
+ // DEPRECATED set state.disableddisabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
- state
- .setter(stateSetter(data), chart.update)
- .getter(stateGetter(data))
- .update();
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
- // DEPRECATED set state.disableddisabled
- state.disabled = data.map(function(d) { return !!d.disabled });
-
- if (!defaultState) {
- var key;
- defaultState = {};
- for (key in state) {
- if (state[key] instanceof Array)
- defaultState[key] = state[key].slice(0);
- else
- defaultState[key] = state[key];
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ nv.utils.noData(chart, container)
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
}
- }
- // Display No Data message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
- nv.utils.noData(chart, container)
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
+ // Setup Scales
+ var dataBars = data.filter(function(d) { return !d.disabled && d.bar });
+ var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240
- // Setup Scales
- var dataBars = data.filter(function(d) { return !d.disabled && d.bar });
- var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240
+ if (dataBars.length && !switchYAxisOrder) {
+ x = bars.xScale();
+ } else {
+ x = lines.xScale();
+ }
- if (dataBars.length && !switchYAxisOrder) {
- x = bars.xScale();
- } else {
- x = lines.xScale();
- }
+ x2 = x2Axis.scale();
- x2 = x2Axis.scale();
+ // select the scales and series based on the position of the yAxis
+ y1 = switchYAxisOrder ? lines.yScale() : bars.yScale();
+ y2 = switchYAxisOrder ? bars.yScale() : lines.yScale();
+ y3 = switchYAxisOrder ? lines2.yScale() : bars2.yScale();
+ y4 = switchYAxisOrder ? bars2.yScale() : lines2.yScale();
- // select the scales and series based on the position of the yAxis
- y1 = switchYAxisOrder ? lines.yScale() : bars.yScale();
- y2 = switchYAxisOrder ? bars.yScale() : lines.yScale();
- y3 = switchYAxisOrder ? lines2.yScale() : bars2.yScale();
- y4 = switchYAxisOrder ? bars2.yScale() : lines2.yScale();
+ var series1 = data
+ .filter(function(d) { return !d.disabled && (switchYAxisOrder ? !d.bar : d.bar) })
+ .map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i) }
+ })
+ });
- var series1 = data
- .filter(function(d) { return !d.disabled && (switchYAxisOrder ? !d.bar : d.bar) })
- .map(function(d) {
- return d.values.map(function(d,i) {
- return { x: getX(d,i), y: getY(d,i) }
- })
- });
+ var series2 = data
+ .filter(function(d) { return !d.disabled && (switchYAxisOrder ? d.bar : !d.bar) })
+ .map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i) }
+ })
+ });
- var series2 = data
- .filter(function(d) { return !d.disabled && (switchYAxisOrder ? d.bar : !d.bar) })
- .map(function(d) {
- return d.values.map(function(d,i) {
- return { x: getX(d,i), y: getY(d,i) }
- })
- });
+ x.range([0, availableWidth]);
- x.range([0, availableWidth]);
+ x2 .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
+ .range([0, availableWidth]);
- x2 .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
- .range([0, availableWidth]);
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-linePlusBar').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-linePlusBar').append('g');
+ var g = wrap.select('g');
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-linePlusBar').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-linePlusBar').append('g');
- var g = wrap.select('g');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
- gEnter.append('g').attr('class', 'nv-legendWrap');
+ // this is the main chart
+ var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
+ focusEnter.append('g').attr('class', 'nv-x nv-axis');
+ focusEnter.append('g').attr('class', 'nv-y1 nv-axis');
+ focusEnter.append('g').attr('class', 'nv-y2 nv-axis');
+ focusEnter.append('g').attr('class', 'nv-barsWrap');
+ focusEnter.append('g').attr('class', 'nv-linesWrap');
- // this is the main chart
- var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
- focusEnter.append('g').attr('class', 'nv-x nv-axis');
- focusEnter.append('g').attr('class', 'nv-y1 nv-axis');
- focusEnter.append('g').attr('class', 'nv-y2 nv-axis');
- focusEnter.append('g').attr('class', 'nv-barsWrap');
- focusEnter.append('g').attr('class', 'nv-linesWrap');
+ // context chart is where you can focus in
+ var contextEnter = gEnter.append('g').attr('class', 'nv-context');
+ contextEnter.append('g').attr('class', 'nv-x nv-axis');
+ contextEnter.append('g').attr('class', 'nv-y1 nv-axis');
+ contextEnter.append('g').attr('class', 'nv-y2 nv-axis');
+ contextEnter.append('g').attr('class', 'nv-barsWrap');
+ contextEnter.append('g').attr('class', 'nv-linesWrap');
+ contextEnter.append('g').attr('class', 'nv-brushBackground');
+ contextEnter.append('g').attr('class', 'nv-x nv-brush');
- // context chart is where you can focus in
- var contextEnter = gEnter.append('g').attr('class', 'nv-context');
- contextEnter.append('g').attr('class', 'nv-x nv-axis');
- contextEnter.append('g').attr('class', 'nv-y1 nv-axis');
- contextEnter.append('g').attr('class', 'nv-y2 nv-axis');
- contextEnter.append('g').attr('class', 'nv-barsWrap');
- contextEnter.append('g').attr('class', 'nv-linesWrap');
- contextEnter.append('g').attr('class', 'nv-brushBackground');
- contextEnter.append('g').attr('class', 'nv-x nv-brush');
+ //============================================================
+ // Legend
+ //------------------------------------------------------------
- //============================================================
- // Legend
- //------------------------------------------------------------
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
+ var legendWidth = legend.align() ? availableWidth / 2 : availableWidth;
+ var legendXPosition = legend.align() ? legendWidth : 0;
- if (!showLegend) {
- g.select('.nv-legendWrap').selectAll('*').remove();
- } else {
- var legendWidth = legend.align() ? availableWidth / 2 : availableWidth;
- var legendXPosition = legend.align() ? legendWidth : 0;
+ legend.width(legendWidth);
- legend.width(legendWidth);
+ g.select('.nv-legendWrap')
+ .datum(data.map(function(series) {
+ series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
+ if(switchYAxisOrder) {
+ series.key = series.originalKey + (series.bar ? legendRightAxisHint : legendLeftAxisHint);
+ } else {
+ series.key = series.originalKey + (series.bar ? legendLeftAxisHint : legendRightAxisHint);
+ }
+ return series;
+ }))
+ .call(legend);
- g.select('.nv-legendWrap')
- .datum(data.map(function(series) {
- series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
- if(switchYAxisOrder) {
- series.key = series.originalKey + (series.bar ? legendRightAxisHint : legendLeftAxisHint);
- } else {
- series.key = series.originalKey + (series.bar ? legendLeftAxisHint : legendRightAxisHint);
- }
- return series;
- }))
- .call(legend);
+ if (!marginTop && legend.height() !== margin.top) {
+ margin.top = legend.height();
+ // FIXME: shouldn't this be "- (focusEnabled ? focusHeight : 0)"?
+ availableHeight1 = nv.utils.availableHeight(height, container, margin) - focusHeight;
+ }
- if (legend.height() > margin.top) {
- margin.top = legend.height();
- // FIXME: shouldn't this be "- (focusEnabled ? focusHeight : 0)"?
- availableHeight1 = nv.utils.availableHeight(height, container, margin) - focusHeight;
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(' + legendXPosition + ',' + (-margin.top) +')');
}
- g.select('.nv-legendWrap')
- .attr('transform', 'translate(' + legendXPosition + ',' + (-margin.top) +')');
- }
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ //============================================================
+ // Context chart (focus chart) components
+ //------------------------------------------------------------
- //============================================================
- // Context chart (focus chart) components
- //------------------------------------------------------------
+ // hide or show the focus context chart
+ g.select('.nv-context').style('display', focusEnable ? 'initial' : 'none');
- // hide or show the focus context chart
- g.select('.nv-context').style('display', focusEnable ? 'initial' : 'none');
+ bars2
+ .width(availableWidth)
+ .height(availableHeight2)
+ .color(data.map(function (d, i) {
+ return d.color || color(d, i);
+ }).filter(function (d, i) {
+ return !data[i].disabled && data[i].bar
+ }));
+ lines2
+ .width(availableWidth)
+ .height(availableHeight2)
+ .color(data.map(function (d, i) {
+ return d.color || color(d, i);
+ }).filter(function (d, i) {
+ return !data[i].disabled && !data[i].bar
+ }));
- bars2
- .width(availableWidth)
- .height(availableHeight2)
- .color(data.map(function (d, i) {
- return d.color || color(d, i);
- }).filter(function (d, i) {
- return !data[i].disabled && data[i].bar
- }));
- lines2
- .width(availableWidth)
- .height(availableHeight2)
- .color(data.map(function (d, i) {
- return d.color || color(d, i);
- }).filter(function (d, i) {
- return !data[i].disabled && !data[i].bar
- }));
+ var bars2Wrap = g.select('.nv-context .nv-barsWrap')
+ .datum(dataBars.length ? dataBars : [
+ {values: []}
+ ]);
+ var lines2Wrap = g.select('.nv-context .nv-linesWrap')
+ .datum(allDisabled(dataLines) ?
+ [{values: []}] :
+ dataLines.filter(function(dataLine) {
+ return !dataLine.disabled;
+ }));
- var bars2Wrap = g.select('.nv-context .nv-barsWrap')
- .datum(dataBars.length ? dataBars : [
- {values: []}
- ]);
- var lines2Wrap = g.select('.nv-context .nv-linesWrap')
- .datum(allDisabled(dataLines) ?
- [{values: []}] :
- dataLines.filter(function(dataLine) {
- return !dataLine.disabled;
- }));
+ g.select('.nv-context')
+ .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')');
- g.select('.nv-context')
- .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')');
+ bars2Wrap.transition().call(bars2);
+ lines2Wrap.transition().call(lines2);
- bars2Wrap.transition().call(bars2);
- lines2Wrap.transition().call(lines2);
+ // context (focus chart) axis controls
+ if (focusShowAxisX) {
+ x2Axis
+ ._ticks( nv.utils.calcTicksX(availableWidth / 100, data))
+ .tickSize(-availableHeight2, 0);
+ g.select('.nv-context .nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y3.range()[0] + ')');
+ g.select('.nv-context .nv-x.nv-axis').transition()
+ .call(x2Axis);
+ }
- // context (focus chart) axis controls
- if (focusShowAxisX) {
- x2Axis
- ._ticks( nv.utils.calcTicksX(availableWidth / 100, data))
- .tickSize(-availableHeight2, 0);
- g.select('.nv-context .nv-x.nv-axis')
- .attr('transform', 'translate(0,' + y3.range()[0] + ')');
- g.select('.nv-context .nv-x.nv-axis').transition()
- .call(x2Axis);
- }
+ if (focusShowAxisY) {
+ y3Axis
+ .scale(y3)
+ ._ticks( availableHeight2 / 36 )
+ .tickSize( -availableWidth, 0);
+ y4Axis
+ .scale(y4)
+ ._ticks( availableHeight2 / 36 )
+ .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
- if (focusShowAxisY) {
- y3Axis
- .scale(y3)
- ._ticks( availableHeight2 / 36 )
- .tickSize( -availableWidth, 0);
- y4Axis
- .scale(y4)
- ._ticks( availableHeight2 / 36 )
- .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
+ g.select('.nv-context .nv-y3.nv-axis')
+ .style('opacity', dataBars.length ? 1 : 0)
+ .attr('transform', 'translate(0,' + x2.range()[0] + ')');
+ g.select('.nv-context .nv-y2.nv-axis')
+ .style('opacity', dataLines.length ? 1 : 0)
+ .attr('transform', 'translate(' + x2.range()[1] + ',0)');
- g.select('.nv-context .nv-y3.nv-axis')
- .style('opacity', dataBars.length ? 1 : 0)
- .attr('transform', 'translate(0,' + x2.range()[0] + ')');
- g.select('.nv-context .nv-y2.nv-axis')
- .style('opacity', dataLines.length ? 1 : 0)
- .attr('transform', 'translate(' + x2.range()[1] + ',0)');
+ g.select('.nv-context .nv-y1.nv-axis').transition()
+ .call(y3Axis);
+ g.select('.nv-context .nv-y2.nv-axis').transition()
+ .call(y4Axis);
+ }
- g.select('.nv-context .nv-y1.nv-axis').transition()
- .call(y3Axis);
- g.select('.nv-context .nv-y2.nv-axis').transition()
- .call(y4Axis);
- }
+ // Setup Brush
+ brush.x(x2).on('brush', onBrush);
- // Setup Brush
- brush.x(x2).on('brush', onBrush);
+ if (brushExtent) brush.extent(brushExtent);
- if (brushExtent) brush.extent(brushExtent);
+ var brushBG = g.select('.nv-brushBackground').selectAll('g')
+ .data([brushExtent || brush.extent()]);
- var brushBG = g.select('.nv-brushBackground').selectAll('g')
- .data([brushExtent || brush.extent()]);
+ var brushBGenter = brushBG.enter()
+ .append('g');
- var brushBGenter = brushBG.enter()
- .append('g');
+ brushBGenter.append('rect')
+ .attr('class', 'left')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('height', availableHeight2);
- brushBGenter.append('rect')
- .attr('class', 'left')
- .attr('x', 0)
- .attr('y', 0)
- .attr('height', availableHeight2);
+ brushBGenter.append('rect')
+ .attr('class', 'right')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('height', availableHeight2);
- brushBGenter.append('rect')
- .attr('class', 'right')
- .attr('x', 0)
- .attr('y', 0)
- .attr('height', availableHeight2);
-
- var gBrush = g.select('.nv-x.nv-brush')
- .call(brush);
- gBrush.selectAll('rect')
+ var gBrush = g.select('.nv-x.nv-brush')
+ .call(brush);
+ gBrush.selectAll('rect')
//.attr('y', -5)
- .attr('height', availableHeight2);
- gBrush.selectAll('.resize').append('path').attr('d', resizePath);
+ .attr('height', availableHeight2);
+ gBrush.selectAll('.resize').append('path').attr('d', resizePath);
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
- legend.dispatch.on('stateChange', function(newState) {
- for (var key in newState)
- state[key] = newState[key];
- dispatch.stateChange(state);
- chart.update();
- });
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
- // Update chart from a state object passed to event handler
- dispatch.on('changeState', function(e) {
- if (typeof e.disabled !== 'undefined') {
- data.forEach(function(series,i) {
- series.disabled = e.disabled[i];
- });
- state.disabled = e.disabled;
- }
- chart.update();
- });
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+ state.disabled = e.disabled;
+ }
+ chart.update();
+ });
- //============================================================
- // Functions
- //------------------------------------------------------------
+ //============================================================
+ // Functions
+ //------------------------------------------------------------
- // Taken from crossfilter (http://square.github.com/crossfilter/)
- function resizePath(d) {
- var e = +(d == 'e'),
- x = e ? 1 : -1,
- y = availableHeight2 / 3;
- return 'M' + (.5 * x) + ',' + y
- + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
- + 'V' + (2 * y - 6)
- + 'A6,6 0 0 ' + e + ' ' + (.5 * x) + ',' + (2 * y)
- + 'Z'
- + 'M' + (2.5 * x) + ',' + (y + 8)
- + 'V' + (2 * y - 8)
- + 'M' + (4.5 * x) + ',' + (y + 8)
- + 'V' + (2 * y - 8);
- }
+ // Taken from crossfilter (http://square.github.com/crossfilter/)
+ function resizePath(d) {
+ var e = +(d == 'e'),
+ x = e ? 1 : -1,
+ y = availableHeight2 / 3;
+ return 'M' + (.5 * x) + ',' + y
+ + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
+ + 'V' + (2 * y - 6)
+ + 'A6,6 0 0 ' + e + ' ' + (.5 * x) + ',' + (2 * y)
+ + 'Z'
+ + 'M' + (2.5 * x) + ',' + (y + 8)
+ + 'V' + (2 * y - 8)
+ + 'M' + (4.5 * x) + ',' + (y + 8)
+ + 'V' + (2 * y - 8);
+ }
- function updateBrushBG() {
- if (!brush.empty()) brush.extent(brushExtent);
- brushBG
- .data([brush.empty() ? x2.domain() : brushExtent])
- .each(function(d,i) {
- var leftWidth = x2(d[0]) - x2.range()[0],
- rightWidth = x2.range()[1] - x2(d[1]);
- d3.select(this).select('.left')
- .attr('width', leftWidth < 0 ? 0 : leftWidth);
+ function updateBrushBG() {
+ if (!brush.empty()) brush.extent(brushExtent);
+ brushBG
+ .data([brush.empty() ? x2.domain() : brushExtent])
+ .each(function(d,i) {
+ var leftWidth = x2(d[0]) - x2.range()[0],
+ rightWidth = x2.range()[1] - x2(d[1]);
+ d3.select(this).select('.left')
+ .attr('width', leftWidth < 0 ? 0 : leftWidth);
- d3.select(this).select('.right')
- .attr('x', x2(d[1]))
- .attr('width', rightWidth < 0 ? 0 : rightWidth);
- });
- }
+ d3.select(this).select('.right')
+ .attr('x', x2(d[1]))
+ .attr('width', rightWidth < 0 ? 0 : rightWidth);
+ });
+ }
- function onBrush() {
- brushExtent = brush.empty() ? null : brush.extent();
- extent = brush.empty() ? x2.domain() : brush.extent();
- dispatch.brush({extent: extent, brush: brush});
- updateBrushBG();
+ function onBrush() {
+ brushExtent = brush.empty() ? null : brush.extent();
+ extent = brush.empty() ? x2.domain() : brush.extent();
+ dispatch.brush({extent: extent, brush: brush});
+ updateBrushBG();
- // Prepare Main (Focus) Bars and Lines
- bars
- .width(availableWidth)
- .height(availableHeight1)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled && data[i].bar }));
+ // Prepare Main (Focus) Bars and Lines
+ bars
+ .width(availableWidth)
+ .height(availableHeight1)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled && data[i].bar }));
- lines
- .width(availableWidth)
- .height(availableHeight1)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled && !data[i].bar }));
+ lines
+ .width(availableWidth)
+ .height(availableHeight1)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled && !data[i].bar }));
- var focusBarsWrap = g.select('.nv-focus .nv-barsWrap')
- .datum(!dataBars.length ? [{values:[]}] :
- dataBars
- .map(function(d,i) {
- return {
- key: d.key,
- values: d.values.filter(function(d,i) {
- return bars.x()(d,i) >= extent[0] && bars.x()(d,i) <= extent[1];
- })
- }
- })
- );
+ var focusBarsWrap = g.select('.nv-focus .nv-barsWrap')
+ .datum(!dataBars.length ? [{values:[]}] :
+ dataBars
+ .map(function(d,i) {
+ return {
+ key: d.key,
+ values: d.values.filter(function(d,i) {
+ return bars.x()(d,i) >= extent[0] && bars.x()(d,i) <= extent[1];
+ })
+ }
+ })
+ );
- var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
- .datum(allDisabled(dataLines) ? [{values:[]}] :
- dataLines
- .filter(function(dataLine) { return !dataLine.disabled; })
- .map(function(d,i) {
- return {
- area: d.area,
- fillOpacity: d.fillOpacity,
- strokeWidth: d.strokeWidth,
- key: d.key,
- values: d.values.filter(function(d,i) {
- return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
- })
- }
- })
- );
+ var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
+ .datum(allDisabled(dataLines) ? [{values:[]}] :
+ dataLines
+ .filter(function(dataLine) { return !dataLine.disabled; })
+ .map(function(d,i) {
+ return {
+ area: d.area,
+ fillOpacity: d.fillOpacity,
+ strokeWidth: d.strokeWidth,
+ key: d.key,
+ values: d.values.filter(function(d,i) {
+ return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
+ })
+ }
+ })
+ );
- // Update Main (Focus) X Axis
- if (dataBars.length && !switchYAxisOrder) {
- x = bars.xScale();
- } else {
- x = lines.xScale();
- }
+ // Update Main (Focus) X Axis
+ if (dataBars.length && !switchYAxisOrder) {
+ x = bars.xScale();
+ } else {
+ x = lines.xScale();
+ }
- xAxis
- .scale(x)
- ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize(-availableHeight1, 0);
+ xAxis
+ .scale(x)
+ ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight1, 0);
- xAxis.domain([Math.ceil(extent[0]), Math.floor(extent[1])]);
+ xAxis.domain([Math.ceil(extent[0]), Math.floor(extent[1])]);
- g.select('.nv-x.nv-axis').transition().duration(transitionDuration)
- .call(xAxis);
+ g.select('.nv-x.nv-axis').transition().duration(transitionDuration)
+ .call(xAxis);
- // Update Main (Focus) Bars and Lines
- focusBarsWrap.transition().duration(transitionDuration).call(bars);
- focusLinesWrap.transition().duration(transitionDuration).call(lines);
+ // Update Main (Focus) Bars and Lines
+ focusBarsWrap.transition().duration(transitionDuration).call(bars);
+ focusLinesWrap.transition().duration(transitionDuration).call(lines);
- // Setup and Update Main (Focus) Y Axes
- g.select('.nv-focus .nv-x.nv-axis')
- .attr('transform', 'translate(0,' + y1.range()[0] + ')');
+ // Setup and Update Main (Focus) Y Axes
+ g.select('.nv-focus .nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y1.range()[0] + ')');
- y1Axis
- .scale(y1)
- ._ticks( nv.utils.calcTicksY(availableHeight1/36, data) )
- .tickSize(-availableWidth, 0);
- y2Axis
- .scale(y2)
- ._ticks( nv.utils.calcTicksY(availableHeight1/36, data) );
+ y1Axis
+ .scale(y1)
+ ._ticks( nv.utils.calcTicksY(availableHeight1/36, data) )
+ .tickSize(-availableWidth, 0);
+ y2Axis
+ .scale(y2)
+ ._ticks( nv.utils.calcTicksY(availableHeight1/36, data) );
- // Show the y2 rules only if y1 has none
- if(!switchYAxisOrder) {
- y2Axis.tickSize(dataBars.length ? 0 : -availableWidth, 0);
- } else {
- y2Axis.tickSize(dataLines.length ? 0 : -availableWidth, 0);
- }
+ // Show the y2 rules only if y1 has none
+ if(!switchYAxisOrder) {
+ y2Axis.tickSize(dataBars.length ? 0 : -availableWidth, 0);
+ } else {
+ y2Axis.tickSize(dataLines.length ? 0 : -availableWidth, 0);
+ }
- // Calculate opacity of the axis
- var barsOpacity = dataBars.length ? 1 : 0;
- var linesOpacity = dataLines.length && !allDisabled(dataLines) ? 1 : 0;
+ // Calculate opacity of the axis
+ var barsOpacity = dataBars.length ? 1 : 0;
+ var linesOpacity = dataLines.length && !allDisabled(dataLines) ? 1 : 0;
- var y1Opacity = switchYAxisOrder ? linesOpacity : barsOpacity;
- var y2Opacity = switchYAxisOrder ? barsOpacity : linesOpacity;
+ var y1Opacity = switchYAxisOrder ? linesOpacity : barsOpacity;
+ var y2Opacity = switchYAxisOrder ? barsOpacity : linesOpacity;
- g.select('.nv-focus .nv-y1.nv-axis')
- .style('opacity', y1Opacity);
- g.select('.nv-focus .nv-y2.nv-axis')
- .style('opacity', y2Opacity)
- .attr('transform', 'translate(' + x.range()[1] + ',0)');
+ g.select('.nv-focus .nv-y1.nv-axis')
+ .style('opacity', y1Opacity);
+ g.select('.nv-focus .nv-y2.nv-axis')
+ .style('opacity', y2Opacity)
+ .attr('transform', 'translate(' + x.range()[1] + ',0)');
- g.select('.nv-focus .nv-y1.nv-axis').transition().duration(transitionDuration)
- .call(y1Axis);
- g.select('.nv-focus .nv-y2.nv-axis').transition().duration(transitionDuration)
- .call(y2Axis);
- }
+ g.select('.nv-focus .nv-y1.nv-axis').transition().duration(transitionDuration)
+ .call(y1Axis);
+ g.select('.nv-focus .nv-y2.nv-axis').transition().duration(transitionDuration)
+ .call(y2Axis);
+ }
- onBrush();
+ onBrush();
- });
+ });
- return chart;
- }
+ return chart;
+ }
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
- lines.dispatch.on('elementMouseover.tooltip', function(evt) {
- tooltip
- .duration(100)
- .valueFormatter(function(d, i) {
- return getLinesAxis().main.tickFormat()(d, i);
- })
- .data(evt)
- .hidden(false);
- });
+ lines.dispatch.on('elementMouseover.tooltip', function(evt) {
+ tooltip
+ .duration(100)
+ .valueFormatter(function(d, i) {
+ return getLinesAxis().main.tickFormat()(d, i);
+ })
+ .data(evt)
+ .hidden(false);
+ });
- lines.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true)
- });
+ lines.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true)
+ });
- bars.dispatch.on('elementMouseover.tooltip', function(evt) {
- evt.value = chart.x()(evt.data);
- evt['series'] = {
- value: chart.y()(evt.data),
- color: evt.color
- };
- tooltip
- .duration(0)
- .valueFormatter(function(d, i) {
- return getBarsAxis().main.tickFormat()(d, i);
- })
- .data(evt)
- .hidden(false);
- });
+ bars.dispatch.on('elementMouseover.tooltip', function(evt) {
+ evt.value = chart.x()(evt.data);
+ evt['series'] = {
+ value: chart.y()(evt.data),
+ color: evt.color
+ };
+ tooltip
+ .duration(0)
+ .valueFormatter(function(d, i) {
+ return getBarsAxis().main.tickFormat()(d, i);
+ })
+ .data(evt)
+ .hidden(false);
+ });
- bars.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true);
- });
+ bars.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true);
+ });
- bars.dispatch.on('elementMousemove.tooltip', function(evt) {
- tooltip();
- });
+ bars.dispatch.on('elementMousemove.tooltip', function(evt) {
+ tooltip();
+ });
- //============================================================
+ //============================================================
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.legend = legend;
- chart.lines = lines;
- chart.lines2 = lines2;
- chart.bars = bars;
- chart.bars2 = bars2;
- chart.xAxis = xAxis;
- chart.x2Axis = x2Axis;
- chart.y1Axis = y1Axis;
- chart.y2Axis = y2Axis;
- chart.y3Axis = y3Axis;
- chart.y4Axis = y4Axis;
- chart.tooltip = tooltip;
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.legend = legend;
+ chart.lines = lines;
+ chart.lines2 = lines2;
+ chart.bars = bars;
+ chart.bars2 = bars2;
+ chart.xAxis = xAxis;
+ chart.x2Axis = x2Axis;
+ chart.y1Axis = y1Axis;
+ chart.y2Axis = y2Axis;
+ chart.y3Axis = y3Axis;
+ chart.y4Axis = y4Axis;
+ chart.tooltip = tooltip;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- brushExtent: {get: function(){return brushExtent;}, set: function(_){brushExtent=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- focusEnable: {get: function(){return focusEnable;}, set: function(_){focusEnable=_;}},
- focusHeight: {get: function(){return focusHeight;}, set: function(_){focusHeight=_;}},
- focusShowAxisX: {get: function(){return focusShowAxisX;}, set: function(_){focusShowAxisX=_;}},
- focusShowAxisY: {get: function(){return focusShowAxisY;}, set: function(_){focusShowAxisY=_;}},
- legendLeftAxisHint: {get: function(){return legendLeftAxisHint;}, set: function(_){legendLeftAxisHint=_;}},
- legendRightAxisHint: {get: function(){return legendRightAxisHint;}, set: function(_){legendRightAxisHint=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ brushExtent: {get: function(){return brushExtent;}, set: function(_){brushExtent=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ focusEnable: {get: function(){return focusEnable;}, set: function(_){focusEnable=_;}},
+ focusHeight: {get: function(){return focusHeight;}, set: function(_){focusHeight=_;}},
+ focusShowAxisX: {get: function(){return focusShowAxisX;}, set: function(_){focusShowAxisX=_;}},
+ focusShowAxisY: {get: function(){return focusShowAxisY;}, set: function(_){focusShowAxisY=_;}},
+ legendLeftAxisHint: {get: function(){return legendLeftAxisHint;}, set: function(_){legendLeftAxisHint=_;}},
+ legendRightAxisHint: {get: function(){return legendRightAxisHint;}, set: function(_){legendRightAxisHint=_;}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- focusMargin: {get: function(){return margin2;}, set: function(_){
- margin2.top = _.top !== undefined ? _.top : margin2.top;
- margin2.right = _.right !== undefined ? _.right : margin2.right;
- margin2.bottom = _.bottom !== undefined ? _.bottom : margin2.bottom;
- margin2.left = _.left !== undefined ? _.left : margin2.left;
- }},
- duration: {get: function(){return transitionDuration;}, set: function(_){
- transitionDuration = _;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- legend.color(color);
- }},
- x: {get: function(){return getX;}, set: function(_){
- getX = _;
- lines.x(_);
- lines2.x(_);
- bars.x(_);
- bars2.x(_);
- }},
- y: {get: function(){return getY;}, set: function(_){
- getY = _;
- lines.y(_);
- lines2.y(_);
- bars.y(_);
- bars2.y(_);
- }},
- switchYAxisOrder: {get: function(){return switchYAxisOrder;}, set: function(_){
- // Switch the tick format for the yAxis
- if(switchYAxisOrder !== _) {
- var y1 = y1Axis;
- y1Axis = y2Axis;
- y2Axis = y1;
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ if (_.top !== undefined) {
+ margin.top = _.top;
+ marginTop = _.top;
+ }
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ focusMargin: {get: function(){return margin2;}, set: function(_){
+ margin2.top = _.top !== undefined ? _.top : margin2.top;
+ margin2.right = _.right !== undefined ? _.right : margin2.right;
+ margin2.bottom = _.bottom !== undefined ? _.bottom : margin2.bottom;
+ margin2.left = _.left !== undefined ? _.left : margin2.left;
+ }},
+ duration: {get: function(){return transitionDuration;}, set: function(_){
+ transitionDuration = _;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ }},
+ x: {get: function(){return getX;}, set: function(_){
+ getX = _;
+ lines.x(_);
+ lines2.x(_);
+ bars.x(_);
+ bars2.x(_);
+ }},
+ y: {get: function(){return getY;}, set: function(_){
+ getY = _;
+ lines.y(_);
+ lines2.y(_);
+ bars.y(_);
+ bars2.y(_);
+ }},
+ switchYAxisOrder: {get: function(){return switchYAxisOrder;}, set: function(_){
+ // Switch the tick format for the yAxis
+ if(switchYAxisOrder !== _) {
+ var y1 = y1Axis;
+ y1Axis = y2Axis;
+ y2Axis = y1;
- var y3 = y3Axis;
- y3Axis = y4Axis;
- y4Axis = y3;
- }
- switchYAxisOrder=_;
+ var y3 = y3Axis;
+ y3Axis = y4Axis;
+ y4Axis = y3;
+ }
+ switchYAxisOrder=_;
- y1Axis.orient('left');
- y2Axis.orient('right');
- y3Axis.orient('left');
- y4Axis.orient('right');
- }}
- });
+ y1Axis.orient('left');
+ y2Axis.orient('right');
+ y3Axis.orient('left');
+ y4Axis.orient('right');
+ }}
+ });
- nv.utils.inheritOptions(chart, lines);
- nv.utils.initOptions(chart);
+ nv.utils.inheritOptions(chart, lines);
+ nv.utils.initOptions(chart);
- return chart;
-};
+ return chart;
+ };
-nv.models.multiBar = function() {
- "use strict";
+ nv.models.multiBar = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 960
- , height = 500
- , x = d3.scale.ordinal()
- , y = d3.scale.linear()
- , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
- , container = null
- , getX = function(d) { return d.x }
- , getY = function(d) { return d.y }
- , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
- , clipEdge = true
- , stacked = false
- , stackOffset = 'zero' // options include 'silhouette', 'wiggle', 'expand', 'zero', or a custom function
- , color = nv.utils.defaultColor()
- , hideable = false
- , barColor = null // adding the ability to set the color for each rather than the whole group
- , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
- , duration = 500
- , xDomain
- , yDomain
- , xRange
- , yRange
- , groupSpacing = 0.1
- , fillOpacity = 0.75
- , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd')
- ;
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 960
+ , height = 500
+ , x = d3.scale.ordinal()
+ , y = d3.scale.linear()
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , container = null
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
+ , clipEdge = true
+ , stacked = false
+ , stackOffset = 'zero' // options include 'silhouette', 'wiggle', 'expand', 'zero', or a custom function
+ , color = nv.utils.defaultColor()
+ , hideable = false
+ , barColor = null // adding the ability to set the color for each rather than the whole group
+ , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
+ , duration = 500
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ , groupSpacing = 0.1
+ , fillOpacity = 0.75
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd')
+ ;
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var x0, y0 //used to store previous scales
- , renderWatch = nv.utils.renderWatch(dispatch, duration)
- ;
+ var x0, y0 //used to store previous scales
+ , renderWatch = nv.utils.renderWatch(dispatch, duration)
+ ;
- var last_datalength = 0;
+ var last_datalength = 0;
- function chart(selection) {
- renderWatch.reset();
- selection.each(function(data) {
- var availableWidth = width - margin.left - margin.right,
- availableHeight = height - margin.top - margin.bottom;
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom;
- container = d3.select(this);
- nv.utils.initSVG(container);
- var nonStackableCount = 0;
- // This function defines the requirements for render complete
- var endFn = function(d, i) {
- if (d.series === data.length - 1 && i === data[0].values.length - 1)
- return true;
- return false;
- };
+ container = d3.select(this);
+ nv.utils.initSVG(container);
+ var nonStackableCount = 0;
+ // This function defines the requirements for render complete
+ var endFn = function(d, i) {
+ if (d.series === data.length - 1 && i === data[0].values.length - 1)
+ return true;
+ return false;
+ };
- if(hideable && data.length) hideable = [{
- values: data[0].values.map(function(d) {
+ if(hideable && data.length) hideable = [{
+ values: data[0].values.map(function(d) {
return {
x: d.x,
y: 0,
series: d.series,
size: 0.01
};}
- )}];
+ )}];
- if (stacked) {
- var parsed = d3.layout.stack()
- .offset(stackOffset)
- .values(function(d){ return d.values })
- .y(getY)
- (!data.length && hideable ? hideable : data);
+ if (stacked) {
+ var parsed = d3.layout.stack()
+ .offset(stackOffset)
+ .values(function(d){ return d.values })
+ .y(getY)
+ (!data.length && hideable ? hideable : data);
- parsed.forEach(function(series, i){
- // if series is non-stackable, use un-parsed data
- if (series.nonStackable) {
- data[i].nonStackableSeries = nonStackableCount++;
- parsed[i] = data[i];
- } else {
- // don't stack this seires on top of the nonStackable seriees
- if (i > 0 && parsed[i - 1].nonStackable){
- parsed[i].values.map(function(d,j){
- d.y0 -= parsed[i - 1].values[j].y;
- d.y1 = d.y0 + d.y;
- });
- }
- }
- });
- data = parsed;
- }
- //add series index and key to each data point for reference
- data.forEach(function(series, i) {
- series.values.forEach(function(point) {
- point.series = i;
- point.key = series.key;
- });
- });
-
- // HACK for negative value stacking
- if (stacked && data.length > 0) {
- data[0].values.map(function(d,i) {
- var posBase = 0, negBase = 0;
- data.map(function(d, idx) {
- if (!data[idx].nonStackable) {
- var f = d.values[i]
- f.size = Math.abs(f.y);
- if (f.y<0) {
- f.y1 = negBase;
- negBase = negBase - f.size;
- } else
- {
- f.y1 = f.size + posBase;
- posBase = posBase + f.size;
+ parsed.forEach(function(series, i){
+ // if series is non-stackable, use un-parsed data
+ if (series.nonStackable) {
+ data[i].nonStackableSeries = nonStackableCount++;
+ parsed[i] = data[i];
+ } else {
+ // don't stack this seires on top of the nonStackable seriees
+ if (i > 0 && parsed[i - 1].nonStackable){
+ parsed[i].values.map(function(d,j){
+ d.y0 -= parsed[i - 1].values[j].y;
+ d.y1 = d.y0 + d.y;
+ });
}
}
-
});
+ data = parsed;
+ }
+ //add series index and key to each data point for reference
+ data.forEach(function(series, i) {
+ series.values.forEach(function(point) {
+ point.series = i;
+ point.key = series.key;
+ });
});
- }
- // Setup Scales
- // remap and flatten the data for use in calculating the scales' domains
- var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
- data.map(function(d, idx) {
- return d.values.map(function(d,i) {
- return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1, idx:idx }
- })
- });
- x.domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
- .rangeBands(xRange || [0, availableWidth], groupSpacing);
+ // HACK for negative value stacking
+ if (stacked && data.length > 0) {
+ data[0].values.map(function(d,i) {
+ var posBase = 0, negBase = 0;
+ data.map(function(d, idx) {
+ if (!data[idx].nonStackable) {
+ var f = d.values[i]
+ f.size = Math.abs(f.y);
+ if (f.y<0) {
+ f.y1 = negBase;
+ negBase = negBase - f.size;
+ } else
+ {
+ f.y1 = f.size + posBase;
+ posBase = posBase + f.size;
+ }
+ }
- y.domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) {
- var domain = d.y;
- // increase the domain range if this series is stackable
- if (stacked && !data[d.idx].nonStackable) {
- if (d.y > 0){
- domain = d.y1
- } else {
- domain = d.y1 + d.y
- }
+ });
+ });
}
- return domain;
- }).concat(forceY)))
- .range(yRange || [availableHeight, 0]);
+ // Setup Scales
+ // remap and flatten the data for use in calculating the scales' domains
+ var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
+ data.map(function(d, idx) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1, idx:idx }
+ })
+ });
- // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
- if (x.domain()[0] === x.domain()[1])
- x.domain()[0] ?
- x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
- : x.domain([-1,1]);
+ x.domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
+ .rangeBands(xRange || [0, availableWidth], groupSpacing);
- if (y.domain()[0] === y.domain()[1])
- y.domain()[0] ?
- y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
- : y.domain([-1,1]);
+ y.domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) {
+ var domain = d.y;
+ // increase the domain range if this series is stackable
+ if (stacked && !data[d.idx].nonStackable) {
+ if (d.y > 0){
+ domain = d.y1
+ } else {
+ domain = d.y1 + d.y
+ }
+ }
+ return domain;
+ }).concat(forceY)))
+ .range(yRange || [availableHeight, 0]);
- x0 = x0 || x;
- y0 = y0 || y;
+ // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
+ if (x.domain()[0] === x.domain()[1])
+ x.domain()[0] ?
+ x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
+ : x.domain([-1,1]);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-multibar').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibar');
- var defsEnter = wrapEnter.append('defs');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ if (y.domain()[0] === y.domain()[1])
+ y.domain()[0] ?
+ y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
+ : y.domain([-1,1]);
- gEnter.append('g').attr('class', 'nv-groups');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ x0 = x0 || x;
+ y0 = y0 || y;
- defsEnter.append('clipPath')
- .attr('id', 'nv-edge-clip-' + id)
- .append('rect');
- wrap.select('#nv-edge-clip-' + id + ' rect')
- .attr('width', availableWidth)
- .attr('height', availableHeight);
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-multibar').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibar');
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
+ gEnter.append('g').attr('class', 'nv-groups');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- var groups = wrap.select('.nv-groups').selectAll('.nv-group')
- .data(function(d) { return d }, function(d,i) { return i });
- groups.enter().append('g')
- .style('stroke-opacity', 1e-6)
- .style('fill-opacity', 1e-6);
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-edge-clip-' + id)
+ .append('rect');
+ wrap.select('#nv-edge-clip-' + id + ' rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
- var exitTransition = renderWatch
- .transition(groups.exit().selectAll('rect.nv-bar'), 'multibarExit', Math.min(100, duration))
- .attr('y', function(d, i, j) {
- var yVal = y0(0) || 0;
- if (stacked) {
- if (data[d.series] && !data[d.series].nonStackable) {
- yVal = y0(d.y0);
- }
- }
- return yVal;
- })
- .attr('height', 0)
- .remove();
- if (exitTransition.delay)
- exitTransition.delay(function(d,i) {
- var delay = i * (duration / (last_datalength + 1)) - i;
- return delay;
- });
- groups
- .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
- .classed('hover', function(d) { return d.hover })
- .style('fill', function(d,i){ return color(d, i) })
- .style('stroke', function(d,i){ return color(d, i) });
- groups
- .style('stroke-opacity', 1)
- .style('fill-opacity', fillOpacity);
+ g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
- var bars = groups.selectAll('rect.nv-bar')
- .data(function(d) { return (hideable && !data.length) ? hideable.values : d.values });
- bars.exit().remove();
+ var groups = wrap.select('.nv-groups').selectAll('.nv-group')
+ .data(function(d) { return d }, function(d,i) { return i });
+ groups.enter().append('g')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6);
- var barsEnter = bars.enter().append('rect')
- .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
- .attr('x', function(d,i,j) {
- return stacked && !data[j].nonStackable ? 0 : (j * x.rangeBand() / data.length )
+ var exitTransition = renderWatch
+ .transition(groups.exit().selectAll('rect.nv-bar'), 'multibarExit', Math.min(100, duration))
+ .attr('y', function(d, i, j) {
+ var yVal = y0(0) || 0;
+ if (stacked) {
+ if (data[d.series] && !data[d.series].nonStackable) {
+ yVal = y0(d.y0);
+ }
+ }
+ return yVal;
})
- .attr('y', function(d,i,j) { return y0(stacked && !data[j].nonStackable ? d.y0 : 0) || 0 })
.attr('height', 0)
- .attr('width', function(d,i,j) { return x.rangeBand() / (stacked && !data[j].nonStackable ? 1 : data.length) })
- .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
- ;
- bars
- .style('fill', function(d,i,j){ return color(d, j, i); })
- .style('stroke', function(d,i,j){ return color(d, j, i); })
- .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
- d3.select(this).classed('hover', true);
- dispatch.elementMouseover({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
+ .remove();
+ if (exitTransition.delay)
+ exitTransition.delay(function(d,i) {
+ var delay = i * (duration / (last_datalength + 1)) - i;
+ return delay;
});
- })
- .on('mouseout', function(d,i) {
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- })
- .on('mousemove', function(d,i) {
- dispatch.elementMousemove({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- })
- .on('click', function(d,i) {
- var element = this;
- dispatch.elementClick({
- data: d,
- index: i,
- color: d3.select(this).style("fill"),
- event: d3.event,
- element: element
- });
- d3.event.stopPropagation();
- })
- .on('dblclick', function(d,i) {
- dispatch.elementDblClick({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- d3.event.stopPropagation();
- });
- bars
- .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
- .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
+ groups
+ .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
+ .classed('hover', function(d) { return d.hover })
+ .style('fill', function(d,i){ return color(d, i) })
+ .style('stroke', function(d,i){ return color(d, i) });
+ groups
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', fillOpacity);
- if (barColor) {
- if (!disabled) disabled = data.map(function() { return true });
- bars
- .style('fill', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); })
- .style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); });
- }
+ var bars = groups.selectAll('rect.nv-bar')
+ .data(function(d) { return (hideable && !data.length) ? hideable.values : d.values });
+ bars.exit().remove();
- var barSelection =
- bars.watchTransition(renderWatch, 'multibar', Math.min(250, duration))
- .delay(function(d,i) {
- return i * duration / data[0].values.length;
+ var barsEnter = bars.enter().append('rect')
+ .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
+ .attr('x', function(d,i,j) {
+ return stacked && !data[j].nonStackable ? 0 : (j * x.rangeBand() / data.length )
+ })
+ .attr('y', function(d,i,j) { return y0(stacked && !data[j].nonStackable ? d.y0 : 0) || 0 })
+ .attr('height', 0)
+ .attr('width', function(d,i,j) { return x.rangeBand() / (stacked && !data[j].nonStackable ? 1 : data.length) })
+ .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
+ ;
+ bars
+ .style('fill', function(d,i,j){ return color(d, j, i); })
+ .style('stroke', function(d,i,j){ return color(d, j, i); })
+ .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ })
+ .on('mouseout', function(d,i) {
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ })
+ .on('mousemove', function(d,i) {
+ dispatch.elementMousemove({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ })
+ .on('click', function(d,i) {
+ var element = this;
+ dispatch.elementClick({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill"),
+ event: d3.event,
+ element: element
+ });
+ d3.event.stopPropagation();
+ })
+ .on('dblclick', function(d,i) {
+ dispatch.elementDblClick({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ d3.event.stopPropagation();
});
- if (stacked){
- barSelection
- .attr('y', function(d,i,j) {
- var yVal = 0;
- // if stackable, stack it on top of the previous series
- if (!data[j].nonStackable) {
- yVal = y(d.y1);
- } else {
- if (getY(d,i) < 0){
- yVal = y(0);
+ bars
+ .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
+ .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
+
+ if (barColor) {
+ if (!disabled) disabled = data.map(function() { return true });
+ bars
+ .style('fill', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); })
+ .style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); });
+ }
+
+ var barSelection =
+ bars.watchTransition(renderWatch, 'multibar', Math.min(250, duration))
+ .delay(function(d,i) {
+ return i * duration / data[0].values.length;
+ });
+ if (stacked){
+ barSelection
+ .attr('y', function(d,i,j) {
+ var yVal = 0;
+ // if stackable, stack it on top of the previous series
+ if (!data[j].nonStackable) {
+ yVal = y(d.y1);
} else {
- if (y(0) - y(getY(d,i)) < -1){
- yVal = y(0) - 1;
+ if (getY(d,i) < 0){
+ yVal = y(0);
} else {
- yVal = y(getY(d, i)) || 0;
+ if (y(0) - y(getY(d,i)) < -1){
+ yVal = y(0) - 1;
+ } else {
+ yVal = y(getY(d, i)) || 0;
+ }
}
}
- }
- return yVal;
- })
- .attr('height', function(d,i,j) {
- if (!data[j].nonStackable) {
- return Math.max(Math.abs(y(d.y+d.y0) - y(d.y0)), 0);
- } else {
- return Math.max(Math.abs(y(getY(d,i)) - y(0)), 0) || 0;
- }
- })
- .attr('x', function(d,i,j) {
- var width = 0;
- if (data[j].nonStackable) {
- width = d.series * x.rangeBand() / data.length;
- if (data.length !== nonStackableCount){
- width = data[j].nonStackableSeries * x.rangeBand()/(nonStackableCount*2);
+ return yVal;
+ })
+ .attr('height', function(d,i,j) {
+ if (!data[j].nonStackable) {
+ return Math.max(Math.abs(y(d.y+d.y0) - y(d.y0)), 0);
+ } else {
+ return Math.max(Math.abs(y(getY(d,i)) - y(0)), 0) || 0;
}
- }
- return width;
- })
- .attr('width', function(d,i,j){
- if (!data[j].nonStackable) {
- return x.rangeBand();
- } else {
- // if all series are nonStacable, take the full width
- var width = (x.rangeBand() / nonStackableCount);
- // otherwise, nonStackable graph will be only taking the half-width
- // of the x rangeBand
- if (data.length !== nonStackableCount) {
- width = x.rangeBand()/(nonStackableCount*2);
+ })
+ .attr('x', function(d,i,j) {
+ var width = 0;
+ if (data[j].nonStackable) {
+ width = d.series * x.rangeBand() / data.length;
+ if (data.length !== nonStackableCount){
+ width = data[j].nonStackableSeries * x.rangeBand()/(nonStackableCount*2);
+ }
}
return width;
- }
- });
- }
- else {
- barSelection
- .attr('x', function(d,i) {
- return d.series * x.rangeBand() / data.length;
- })
- .attr('width', x.rangeBand() / data.length)
- .attr('y', function(d,i) {
- return getY(d,i) < 0 ?
- y(0) :
+ })
+ .attr('width', function(d,i,j){
+ if (!data[j].nonStackable) {
+ return x.rangeBand();
+ } else {
+ // if all series are nonStacable, take the full width
+ var width = (x.rangeBand() / nonStackableCount);
+ // otherwise, nonStackable graph will be only taking the half-width
+ // of the x rangeBand
+ if (data.length !== nonStackableCount) {
+ width = x.rangeBand()/(nonStackableCount*2);
+ }
+ return width;
+ }
+ });
+ }
+ else {
+ barSelection
+ .attr('x', function(d,i) {
+ return d.series * x.rangeBand() / data.length;
+ })
+ .attr('width', x.rangeBand() / data.length)
+ .attr('y', function(d,i) {
+ return getY(d,i) < 0 ?
+ y(0) :
y(0) - y(getY(d,i)) < 1 ?
- y(0) - 1 :
- y(getY(d,i)) || 0;
- })
- .attr('height', function(d,i) {
- return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) || 0;
- });
- }
+ y(0) - 1 :
+ y(getY(d,i)) || 0;
+ })
+ .attr('height', function(d,i) {
+ return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) || 0;
+ });
+ }
- //store old scales for use in transitions on update
- x0 = x.copy();
- y0 = y.copy();
+ //store old scales for use in transitions on update
+ x0 = x.copy();
+ y0 = y.copy();
- // keep track of the last data value length for transition calculations
- if (data[0] && data[0].values) {
- last_datalength = data[0].values.length;
- }
+ // keep track of the last data value length for transition calculations
+ if (data[0] && data[0].values) {
+ last_datalength = data[0].values.length;
+ }
- });
+ });
- renderWatch.renderEnd('multibar immediate');
+ renderWatch.renderEnd('multibar immediate');
- return chart;
- }
+ return chart;
+ }
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
+ chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- x: {get: function(){return getX;}, set: function(_){getX=_;}},
- y: {get: function(){return getY;}, set: function(_){getY=_;}},
- xScale: {get: function(){return x;}, set: function(_){x=_;}},
- yScale: {get: function(){return y;}, set: function(_){y=_;}},
- xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
- yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
- xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
- yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
- forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
- stacked: {get: function(){return stacked;}, set: function(_){stacked=_;}},
- stackOffset: {get: function(){return stackOffset;}, set: function(_){stackOffset=_;}},
- clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
- disabled: {get: function(){return disabled;}, set: function(_){disabled=_;}},
- id: {get: function(){return id;}, set: function(_){id=_;}},
- hideable: {get: function(){return hideable;}, set: function(_){hideable=_;}},
- groupSpacing:{get: function(){return groupSpacing;}, set: function(_){groupSpacing=_;}},
- fillOpacity: {get: function(){return fillOpacity;}, set: function(_){fillOpacity=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ y: {get: function(){return getY;}, set: function(_){getY=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ stacked: {get: function(){return stacked;}, set: function(_){stacked=_;}},
+ stackOffset: {get: function(){return stackOffset;}, set: function(_){stackOffset=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+ disabled: {get: function(){return disabled;}, set: function(_){disabled=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ hideable: {get: function(){return hideable;}, set: function(_){hideable=_;}},
+ groupSpacing:{get: function(){return groupSpacing;}, set: function(_){groupSpacing=_;}},
+ fillOpacity: {get: function(){return fillOpacity;}, set: function(_){fillOpacity=_;}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }},
- barColor: {get: function(){return barColor;}, set: function(_){
- barColor = _ ? nv.utils.getColor(_) : null;
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ barColor: {get: function(){return barColor;}, set: function(_){
+ barColor = _ ? nv.utils.getColor(_) : null;
+ }}
+ });
- nv.utils.initOptions(chart);
+ nv.utils.initOptions(chart);
- return chart;
-};
-nv.models.multiBarChart = function() {
- "use strict";
+ return chart;
+ };
+ nv.models.multiBarChart = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var multibar = nv.models.multiBar()
- , xAxis = nv.models.axis()
- , yAxis = nv.models.axis()
- , interactiveLayer = nv.interactiveGuideline()
- , legend = nv.models.legend()
- , controls = nv.models.legend()
- , tooltip = nv.models.tooltip()
- ;
+ var multibar = nv.models.multiBar()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , interactiveLayer = nv.interactiveGuideline()
+ , legend = nv.models.legend()
+ , controls = nv.models.legend()
+ , tooltip = nv.models.tooltip()
+ ;
- var margin = {top: 30, right: 20, bottom: 50, left: 60}
- , width = null
- , height = null
- , color = nv.utils.defaultColor()
- , showControls = true
- , controlLabels = {}
- , showLegend = true
- , showXAxis = true
- , showYAxis = true
- , rightAlignYAxis = false
- , reduceXTicks = true // if false a tick will show for every data point
- , staggerLabels = false
- , wrapLabels = false
- , rotateLabels = 0
- , x //can be accessed via chart.xScale()
- , y //can be accessed via chart.yScale()
- , state = nv.utils.state()
- , defaultState = null
- , noData = null
- , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd')
- , controlWidth = function() { return showControls ? 180 : 0 }
- , duration = 250
- , useInteractiveGuideline = false
- ;
+ var margin = {top: 30, right: 20, bottom: 50, left: 60}
+ , marginTop = null
+ , width = null
+ , height = null
+ , color = nv.utils.defaultColor()
+ , showControls = true
+ , controlLabels = {}
+ , showLegend = true
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , reduceXTicks = true // if false a tick will show for every data point
+ , staggerLabels = false
+ , wrapLabels = false
+ , rotateLabels = 0
+ , x //can be accessed via chart.xScale()
+ , y //can be accessed via chart.yScale()
+ , state = nv.utils.state()
+ , defaultState = null
+ , noData = null
+ , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd')
+ , controlWidth = function() { return showControls ? 180 : 0 }
+ , duration = 250
+ , useInteractiveGuideline = false
+ ;
- state.stacked = false // DEPRECATED Maintained for backward compatibility
+ state.stacked = false // DEPRECATED Maintained for backward compatibility
- multibar.stacked(false);
- xAxis
- .orient('bottom')
- .tickPadding(7)
- .showMaxMin(false)
- .tickFormat(function(d) { return d })
- ;
- yAxis
- .orient((rightAlignYAxis) ? 'right' : 'left')
- .tickFormat(d3.format(',.1f'))
- ;
+ multibar.stacked(false);
+ xAxis
+ .orient('bottom')
+ .tickPadding(7)
+ .showMaxMin(false)
+ .tickFormat(function(d) { return d })
+ ;
+ yAxis
+ .orient((rightAlignYAxis) ? 'right' : 'left')
+ .tickFormat(d3.format(',.1f'))
+ ;
- tooltip
- .duration(0)
- .valueFormatter(function(d, i) {
- return yAxis.tickFormat()(d, i);
- })
- .headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- });
+ tooltip
+ .duration(0)
+ .valueFormatter(function(d, i) {
+ return yAxis.tickFormat()(d, i);
+ })
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ });
- controls.updateState(false);
+ interactiveLayer.tooltip
+ .valueFormatter(function(d, i) {
+ return d == null ? "N/A" : yAxis.tickFormat()(d, i);
+ })
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ });
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ interactiveLayer.tooltip
+ .valueFormatter(function (d, i) {
+ return d == null ? "N/A" : yAxis.tickFormat()(d, i);
+ })
+ .headerFormatter(function (d, i) {
+ return xAxis.tickFormat()(d, i);
+ });
- var renderWatch = nv.utils.renderWatch(dispatch);
- var stacked = false;
+ interactiveLayer.tooltip
+ .duration(0)
+ .valueFormatter(function(d, i) {
+ return yAxis.tickFormat()(d, i);
+ })
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ });
- var stateGetter = function(data) {
- return function(){
- return {
- active: data.map(function(d) { return !d.disabled }),
- stacked: stacked
- };
- }
- };
+ controls.updateState(false);
- var stateSetter = function(data) {
- return function(state) {
- if (state.stacked !== undefined)
- stacked = state.stacked;
- if (state.active !== undefined)
- data.forEach(function(series,i) {
- series.disabled = !state.active[i];
- });
- }
- };
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(multibar);
- if (showXAxis) renderWatch.models(xAxis);
- if (showYAxis) renderWatch.models(yAxis);
+ var renderWatch = nv.utils.renderWatch(dispatch);
+ var stacked = false;
- selection.each(function(data) {
- var container = d3.select(this),
- that = this;
- nv.utils.initSVG(container);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
-
- chart.update = function() {
- if (duration === 0)
- container.call(chart);
- else
- container.transition()
- .duration(duration)
- .call(chart);
- };
- chart.container = this;
-
- state
- .setter(stateSetter(data), chart.update)
- .getter(stateGetter(data))
- .update();
-
- // DEPRECATED set state.disableddisabled
- state.disabled = data.map(function(d) { return !!d.disabled });
-
- if (!defaultState) {
- var key;
- defaultState = {};
- for (key in state) {
- if (state[key] instanceof Array)
- defaultState[key] = state[key].slice(0);
- else
- defaultState[key] = state[key];
- }
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled }),
+ stacked: stacked
+ };
}
+ };
- // Display noData message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
- nv.utils.noData(chart, container)
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.stacked !== undefined)
+ stacked = state.stacked;
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
}
+ };
- // Setup Scales
- x = multibar.xScale();
- y = multibar.yScale();
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(multibar);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-multiBarWithLegend').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarWithLegend').append('g');
- var g = wrap.select('g');
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
- gEnter.append('g').attr('class', 'nv-x nv-axis');
- gEnter.append('g').attr('class', 'nv-y nv-axis');
- gEnter.append('g').attr('class', 'nv-barsWrap');
- gEnter.append('g').attr('class', 'nv-legendWrap');
- gEnter.append('g').attr('class', 'nv-controlsWrap');
- gEnter.append('g').attr('class', 'nv-interactive');
+ chart.update = function() {
+ if (duration === 0)
+ container.call(chart);
+ else
+ container.transition()
+ .duration(duration)
+ .call(chart);
+ };
+ chart.container = this;
- // Legend
- if (!showLegend) {
- g.select('.nv-legendWrap').selectAll('*').remove();
- } else {
- legend.width(availableWidth - controlWidth());
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
- g.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
+ // DEPRECATED set state.disableddisabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
- if (legend.height() > margin.top) {
- margin.top = legend.height();
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
}
- g.select('.nv-legendWrap')
- .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
- }
+ // Display noData message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ nv.utils.noData(chart, container)
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
- // Controls
- if (!showControls) {
- g.select('.nv-controlsWrap').selectAll('*').remove();
- } else {
- var controlsData = [
- { key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() },
- { key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() }
- ];
+ // Setup Scales
+ x = multibar.xScale();
+ y = multibar.yScale();
- controls.width(controlWidth()).color(['#444', '#444', '#444']);
- g.select('.nv-controlsWrap')
- .datum(controlsData)
- .attr('transform', 'translate(0,' + (-margin.top) +')')
- .call(controls);
- }
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-multiBarWithLegend').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarWithLegend').append('g');
+ var g = wrap.select('g');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
- }
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis');
+ gEnter.append('g').attr('class', 'nv-barsWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+ gEnter.append('g').attr('class', 'nv-controlsWrap');
+ gEnter.append('g').attr('class', 'nv-interactive');
- // Main Chart Component(s)
- multibar
- .disabled(data.map(function(series) { return series.disabled }))
- .width(availableWidth)
- .height(availableHeight)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled }));
+ // Legend
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
+ legend.width(availableWidth - controlWidth());
+ g.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
- var barsWrap = g.select('.nv-barsWrap')
- .datum(data.filter(function(d) { return !d.disabled }));
+ if (!marginTop && legend.height() !== margin.top) {
+ margin.top = legend.height();
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+ }
- barsWrap.call(multibar);
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
+ }
- // Setup Axes
- if (showXAxis) {
- xAxis
- .scale(x)
- ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize(-availableHeight, 0);
+ // Controls
+ if (!showControls) {
+ g.select('.nv-controlsWrap').selectAll('*').remove();
+ } else {
+ var controlsData = [
+ { key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() },
+ { key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() }
+ ];
- g.select('.nv-x.nv-axis')
- .attr('transform', 'translate(0,' + y.range()[0] + ')');
- g.select('.nv-x.nv-axis')
- .call(xAxis);
+ controls.width(controlWidth()).color(['#444', '#444', '#444']);
+ g.select('.nv-controlsWrap')
+ .datum(controlsData)
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
+ .call(controls);
+ }
- var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
- xTicks
- .selectAll('line, text')
- .style('opacity', 1)
+ // Main Chart Component(s)
+ multibar
+ .disabled(data.map(function(series) { return series.disabled }))
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled }));
- if (staggerLabels) {
- var getTranslate = function(x,y) {
- return "translate(" + x + "," + y + ")";
- };
- var staggerUp = 5, staggerDown = 17; //pixels to stagger by
- // Issue #140
- xTicks
- .selectAll("text")
- .attr('transform', function(d,i,j) {
- return getTranslate(0, (j % 2 == 0 ? staggerUp : staggerDown));
- });
+ var barsWrap = g.select('.nv-barsWrap')
+ .datum(data.filter(function(d) { return !d.disabled }));
- var totalInBetweenTicks = d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;
- g.selectAll(".nv-x.nv-axis .nv-axisMaxMin text")
- .attr("transform", function(d,i) {
- return getTranslate(0, (i === 0 || totalInBetweenTicks % 2 !== 0) ? staggerDown : staggerUp);
- });
- }
+ barsWrap.call(multibar);
- if (wrapLabels) {
- g.selectAll('.tick text')
- .call(nv.utils.wrapTicks, chart.xAxis.rangeBand())
- }
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight, 0);
- if (reduceXTicks)
- xTicks
- .filter(function(d,i) {
- return i % Math.ceil(data[0].values.length / (availableWidth / 100)) !== 0;
- })
- .selectAll('text, line')
- .style('opacity', 0);
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y.range()[0] + ')');
+ g.select('.nv-x.nv-axis')
+ .call(xAxis);
- if(rotateLabels)
+ var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g');
+
xTicks
- .selectAll('.tick text')
- .attr('transform', 'rotate(' + rotateLabels + ' 0,0)')
- .style('text-anchor', rotateLabels > 0 ? 'start' : 'end');
+ .selectAll('line, text')
+ .style('opacity', 1)
- g.select('.nv-x.nv-axis').selectAll('g.nv-axisMaxMin text')
- .style('opacity', 1);
- }
+ if (staggerLabels) {
+ var getTranslate = function(x,y) {
+ return "translate(" + x + "," + y + ")";
+ };
- if (showYAxis) {
- yAxis
- .scale(y)
- ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
+ var staggerUp = 5, staggerDown = 17; //pixels to stagger by
+ // Issue #140
+ xTicks
+ .selectAll("text")
+ .attr('transform', function(d,i,j) {
+ return getTranslate(0, (j % 2 == 0 ? staggerUp : staggerDown));
+ });
- g.select('.nv-y.nv-axis')
- .call(yAxis);
- }
+ var totalInBetweenTicks = d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;
+ g.selectAll(".nv-x.nv-axis .nv-axisMaxMin text")
+ .attr("transform", function(d,i) {
+ return getTranslate(0, (i === 0 || totalInBetweenTicks % 2 !== 0) ? staggerDown : staggerUp);
+ });
+ }
- //Set up interactive layer
- if (useInteractiveGuideline) {
- interactiveLayer
- .width(availableWidth)
- .height(availableHeight)
- .margin({left:margin.left, top:margin.top})
- .svgContainer(container)
- .xScale(x);
- wrap.select(".nv-interactive").call(interactiveLayer);
- }
+ if (wrapLabels) {
+ g.selectAll('.tick text')
+ .call(nv.utils.wrapTicks, chart.xAxis.rangeBand())
+ }
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
+ if (reduceXTicks)
+ xTicks
+ .filter(function(d,i) {
+ return i % Math.ceil(data[0].values.length / (availableWidth / 100)) !== 0;
+ })
+ .selectAll('text, line')
+ .style('opacity', 0);
- legend.dispatch.on('stateChange', function(newState) {
- for (var key in newState)
- state[key] = newState[key];
- dispatch.stateChange(state);
- chart.update();
- });
+ if(rotateLabels)
+ xTicks
+ .selectAll('.tick text')
+ .attr('transform', 'rotate(' + rotateLabels + ' 0,0)')
+ .style('text-anchor', rotateLabels > 0 ? 'start' : 'end');
- controls.dispatch.on('legendClick', function(d,i) {
- if (!d.disabled) return;
- controlsData = controlsData.map(function(s) {
- s.disabled = true;
- return s;
- });
- d.disabled = false;
-
- switch (d.key) {
- case 'Grouped':
- case controlLabels.grouped:
- multibar.stacked(false);
- break;
- case 'Stacked':
- case controlLabels.stacked:
- multibar.stacked(true);
- break;
+ g.select('.nv-x.nv-axis').selectAll('g.nv-axisMaxMin text')
+ .style('opacity', 1);
}
- state.stacked = multibar.stacked();
- dispatch.stateChange(state);
- chart.update();
- });
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
- // Update chart from a state object passed to event handler
- dispatch.on('changeState', function(e) {
- if (typeof e.disabled !== 'undefined') {
- data.forEach(function(series,i) {
- series.disabled = e.disabled[i];
- });
- state.disabled = e.disabled;
+ g.select('.nv-y.nv-axis')
+ .call(yAxis);
}
- if (typeof e.stacked !== 'undefined') {
- multibar.stacked(e.stacked);
- state.stacked = e.stacked;
- stacked = e.stacked;
+
+ //Set up interactive layer
+ if (useInteractiveGuideline) {
+ interactiveLayer
+ .width(availableWidth)
+ .height(availableHeight)
+ .margin({left:margin.left, top:margin.top})
+ .svgContainer(container)
+ .xScale(x);
+ wrap.select(".nv-interactive").call(interactiveLayer);
}
- chart.update();
- });
- if (useInteractiveGuideline) {
- interactiveLayer.dispatch.on('elementMousemove', function(e) {
- if (e.pointXValue == undefined) return;
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
- var singlePoint, pointIndex, pointXLocation, xValue, allData = [];
- data
- .filter(function(series, i) {
- series.seriesIndex = i;
- return !series.disabled;
- })
- .forEach(function(series,i) {
- pointIndex = x.domain().indexOf(e.pointXValue)
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
- var point = series.values[pointIndex];
- if (point === undefined) return;
+ controls.dispatch.on('legendClick', function(d,i) {
+ if (!d.disabled) return;
+ controlsData = controlsData.map(function(s) {
+ s.disabled = true;
+ return s;
+ });
+ d.disabled = false;
- xValue = point.x;
- if (singlePoint === undefined) singlePoint = point;
- if (pointXLocation === undefined) pointXLocation = e.mouseX
- allData.push({
- key: series.key,
- value: chart.y()(point, pointIndex),
- color: color(series,series.seriesIndex),
- data: series.values[pointIndex]
- });
- });
+ switch (d.key) {
+ case 'Grouped':
+ case controlLabels.grouped:
+ multibar.stacked(false);
+ break;
+ case 'Stacked':
+ case controlLabels.stacked:
+ multibar.stacked(true);
+ break;
+ }
- interactiveLayer.tooltip
- .data({
- value: xValue,
- index: pointIndex,
- series: allData
- })();
-
- interactiveLayer.renderGuideLine(pointXLocation);
+ state.stacked = multibar.stacked();
+ dispatch.stateChange(state);
+ chart.update();
});
- interactiveLayer.dispatch.on("elementMouseout",function(e) {
- interactiveLayer.tooltip.hidden(true);
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+ state.disabled = e.disabled;
+ }
+ if (typeof e.stacked !== 'undefined') {
+ multibar.stacked(e.stacked);
+ state.stacked = e.stacked;
+ stacked = e.stacked;
+ }
+ chart.update();
});
- }
- else {
- multibar.dispatch.on('elementMouseover.tooltip', function(evt) {
- evt.value = chart.x()(evt.data);
- evt['series'] = {
- key: evt.data.key,
- value: chart.y()(evt.data),
- color: evt.color
- };
- tooltip.data(evt).hidden(false);
- });
- multibar.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true);
- });
+ if (useInteractiveGuideline) {
+ interactiveLayer.dispatch.on('elementMousemove', function(e) {
+ if (e.pointXValue == undefined) return;
- multibar.dispatch.on('elementMousemove.tooltip', function(evt) {
- tooltip();
- });
- }
- });
+ var singlePoint, pointIndex, pointXLocation, xValue, allData = [];
+ data
+ .filter(function(series, i) {
+ series.seriesIndex = i;
+ return !series.disabled;
+ })
+ .forEach(function(series,i) {
+ pointIndex = x.domain().indexOf(e.pointXValue)
- renderWatch.renderEnd('multibarchart immediate');
- return chart;
- }
+ var point = series.values[pointIndex];
+ if (point === undefined) return;
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ xValue = point.x;
+ if (singlePoint === undefined) singlePoint = point;
+ if (pointXLocation === undefined) pointXLocation = e.mouseX
+ allData.push({
+ key: series.key,
+ value: chart.y()(point, pointIndex),
+ color: color(series,series.seriesIndex),
+ data: series.values[pointIndex]
+ });
+ });
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.multibar = multibar;
- chart.legend = legend;
- chart.controls = controls;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.state = state;
- chart.tooltip = tooltip;
- chart.interactiveLayer = interactiveLayer;
+ interactiveLayer.tooltip
+ .data({
+ value: xValue,
+ index: pointIndex,
+ series: allData
+ })();
- chart.options = nv.utils.optionsFunc.bind(chart);
+ interactiveLayer.renderGuideLine(pointXLocation);
+ });
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
- controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- reduceXTicks: {get: function(){return reduceXTicks;}, set: function(_){reduceXTicks=_;}},
- rotateLabels: {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}},
- staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},
- wrapLabels: {get: function(){return wrapLabels;}, set: function(_){wrapLabels=!!_;}},
+ interactiveLayer.dispatch.on("elementMouseout",function(e) {
+ interactiveLayer.tooltip.hidden(true);
+ });
+ }
+ else {
+ multibar.dispatch.on('elementMouseover.tooltip', function(evt) {
+ evt.value = chart.x()(evt.data);
+ evt['series'] = {
+ key: evt.data.key,
+ value: chart.y()(evt.data),
+ color: evt.color
+ };
+ tooltip.data(evt).hidden(false);
+ });
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- multibar.duration(duration);
- xAxis.duration(duration);
- yAxis.duration(duration);
- renderWatch.reset(duration);
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- legend.color(color);
- }},
- rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
- rightAlignYAxis = _;
- yAxis.orient( rightAlignYAxis ? 'right' : 'left');
- }},
- useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
- useInteractiveGuideline = _;
- }},
- barColor: {get: function(){return multibar.barColor;}, set: function(_){
- multibar.barColor(_);
- legend.color(function(d,i) {return d3.rgb('#ccc').darker(i * 1.5).toString();})
- }}
- });
+ multibar.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true);
+ });
- nv.utils.inheritOptions(chart, multibar);
- nv.utils.initOptions(chart);
+ multibar.dispatch.on('elementMousemove.tooltip', function(evt) {
+ tooltip();
+ });
+ }
+ });
- return chart;
-};
+ renderWatch.renderEnd('multibarchart immediate');
+ return chart;
+ }
-nv.models.multiBarHorizontal = function() {
- "use strict";
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.multibar = multibar;
+ chart.legend = legend;
+ chart.controls = controls;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.state = state;
+ chart.tooltip = tooltip;
+ chart.interactiveLayer = interactiveLayer;
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 960
- , height = 500
- , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
- , container = null
- , x = d3.scale.ordinal()
- , y = d3.scale.linear()
- , getX = function(d) { return d.x }
- , getY = function(d) { return d.y }
- , getYerr = function(d) { return d.yErr }
- , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
- , color = nv.utils.defaultColor()
- , barColor = null // adding the ability to set the color for each rather than the whole group
- , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
- , stacked = false
- , showValues = false
- , showBarLabels = false
- , valuePadding = 60
- , groupSpacing = 0.1
- , fillOpacity = 0.75
- , valueFormat = d3.format(',.2f')
- , delay = 1200
- , xDomain
- , yDomain
- , xRange
- , yRange
- , duration = 250
- , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd')
- ;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
+ controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ reduceXTicks: {get: function(){return reduceXTicks;}, set: function(_){reduceXTicks=_;}},
+ rotateLabels: {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}},
+ staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}},
+ wrapLabels: {get: function(){return wrapLabels;}, set: function(_){wrapLabels=!!_;}},
- var x0, y0; //used to store previous scales
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ if (_.top !== undefined) {
+ margin.top = _.top;
+ marginTop = _.top;
+ }
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ multibar.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ renderWatch.reset(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( rightAlignYAxis ? 'right' : 'left');
+ }},
+ useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
+ useInteractiveGuideline = _;
+ }},
+ barColor: {get: function(){return multibar.barColor;}, set: function(_){
+ multibar.barColor(_);
+ legend.color(function(d,i) {return d3.rgb('#ccc').darker(i * 1.5).toString();})
+ }}
+ });
- function chart(selection) {
- renderWatch.reset();
- selection.each(function(data) {
- var availableWidth = width - margin.left - margin.right,
- availableHeight = height - margin.top - margin.bottom;
+ nv.utils.inheritOptions(chart, multibar);
+ nv.utils.initOptions(chart);
- container = d3.select(this);
- nv.utils.initSVG(container);
+ return chart;
+ };
- if (stacked)
- data = d3.layout.stack()
- .offset('zero')
- .values(function(d){ return d.values })
- .y(getY)
- (data);
+ nv.models.multiBarHorizontal = function() {
+ "use strict";
- //add series index and key to each data point for reference
- data.forEach(function(series, i) {
- series.values.forEach(function(point) {
- point.series = i;
- point.key = series.key;
- });
- });
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- // HACK for negative value stacking
- if (stacked)
- data[0].values.map(function(d,i) {
- var posBase = 0, negBase = 0;
- data.map(function(d) {
- var f = d.values[i]
- f.size = Math.abs(f.y);
- if (f.y<0) {
- f.y1 = negBase - f.size;
- negBase = negBase - f.size;
- } else
- {
- f.y1 = posBase;
- posBase = posBase + f.size;
- }
- });
- });
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 960
+ , height = 500
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , container = null
+ , x = d3.scale.ordinal()
+ , y = d3.scale.linear()
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , getYerr = function(d) { return d.yErr }
+ , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
+ , color = nv.utils.defaultColor()
+ , barColor = null // adding the ability to set the color for each rather than the whole group
+ , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
+ , stacked = false
+ , showValues = false
+ , showBarLabels = false
+ , valuePadding = 60
+ , groupSpacing = 0.1
+ , fillOpacity = 0.75
+ , valueFormat = d3.format(',.2f')
+ , delay = 1200
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ , duration = 250
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd')
+ ;
- // Setup Scales
- // remap and flatten the data for use in calculating the scales' domains
- var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
- data.map(function(d) {
- return d.values.map(function(d,i) {
- return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1 }
- })
- });
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- x.domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
- .rangeBands(xRange || [0, availableHeight], groupSpacing);
+ var x0, y0; //used to store previous scales
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
- y.domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return stacked ? (d.y > 0 ? d.y1 + d.y : d.y1 ) : d.y }).concat(forceY)))
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom;
- if (showValues && !stacked)
- y.range(yRange || [(y.domain()[0] < 0 ? valuePadding : 0), availableWidth - (y.domain()[1] > 0 ? valuePadding : 0) ]);
- else
- y.range(yRange || [0, availableWidth]);
+ container = d3.select(this);
+ nv.utils.initSVG(container);
- x0 = x0 || x;
- y0 = y0 || d3.scale.linear().domain(y.domain()).range([y(0),y(0)]);
+ if (stacked)
+ data = d3.layout.stack()
+ .offset('zero')
+ .values(function(d){ return d.values })
+ .y(getY)
+ (data);
- // Setup containers and skeleton of chart
- var wrap = d3.select(this).selectAll('g.nv-wrap.nv-multibarHorizontal').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibarHorizontal');
- var defsEnter = wrapEnter.append('defs');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ //add series index and key to each data point for reference
+ data.forEach(function(series, i) {
+ series.values.forEach(function(point) {
+ point.series = i;
+ point.key = series.key;
+ });
+ });
- gEnter.append('g').attr('class', 'nv-groups');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ // HACK for negative value stacking
+ if (stacked)
+ data[0].values.map(function(d,i) {
+ var posBase = 0, negBase = 0;
+ data.map(function(d) {
+ var f = d.values[i]
+ f.size = Math.abs(f.y);
+ if (f.y<0) {
+ f.y1 = negBase - f.size;
+ negBase = negBase - f.size;
+ } else
+ {
+ f.y1 = posBase;
+ posBase = posBase + f.size;
+ }
+ });
+ });
- var groups = wrap.select('.nv-groups').selectAll('.nv-group')
- .data(function(d) { return d }, function(d,i) { return i });
- groups.enter().append('g')
- .style('stroke-opacity', 1e-6)
- .style('fill-opacity', 1e-6);
- groups.exit().watchTransition(renderWatch, 'multibarhorizontal: exit groups')
- .style('stroke-opacity', 1e-6)
- .style('fill-opacity', 1e-6)
- .remove();
- groups
- .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
- .classed('hover', function(d) { return d.hover })
- .style('fill', function(d,i){ return color(d, i) })
- .style('stroke', function(d,i){ return color(d, i) });
- groups.watchTransition(renderWatch, 'multibarhorizontal: groups')
- .style('stroke-opacity', 1)
- .style('fill-opacity', fillOpacity);
+ // Setup Scales
+ // remap and flatten the data for use in calculating the scales' domains
+ var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
+ data.map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1 }
+ })
+ });
- var bars = groups.selectAll('g.nv-bar')
- .data(function(d) { return d.values });
- bars.exit().remove();
+ x.domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
+ .rangeBands(xRange || [0, availableHeight], groupSpacing);
- var barsEnter = bars.enter().append('g')
- .attr('transform', function(d,i,j) {
- return 'translate(' + y0(stacked ? d.y0 : 0) + ',' + (stacked ? 0 : (j * x.rangeBand() / data.length ) + x(getX(d,i))) + ')'
- });
+ y.domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return stacked ? (d.y > 0 ? d.y1 + d.y : d.y1 ) : d.y }).concat(forceY)))
- barsEnter.append('rect')
- .attr('width', 0)
- .attr('height', x.rangeBand() / (stacked ? 1 : data.length) )
+ if (showValues && !stacked)
+ y.range(yRange || [(y.domain()[0] < 0 ? valuePadding : 0), availableWidth - (y.domain()[1] > 0 ? valuePadding : 0) ]);
+ else
+ y.range(yRange || [0, availableWidth]);
- bars
- .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
- d3.select(this).classed('hover', true);
- dispatch.elementMouseover({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- })
- .on('mouseout', function(d,i) {
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- })
- .on('mouseout', function(d,i) {
- dispatch.elementMouseout({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- })
- .on('mousemove', function(d,i) {
- dispatch.elementMousemove({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- })
- .on('click', function(d,i) {
- var element = this;
- dispatch.elementClick({
- data: d,
- index: i,
- color: d3.select(this).style("fill"),
- event: d3.event,
- element: element
- });
- d3.event.stopPropagation();
- })
- .on('dblclick', function(d,i) {
- dispatch.elementDblClick({
- data: d,
- index: i,
- color: d3.select(this).style("fill")
- });
- d3.event.stopPropagation();
- });
+ x0 = x0 || x;
+ y0 = y0 || d3.scale.linear().domain(y.domain()).range([y(0),y(0)]);
- if (getYerr(data[0],0)) {
- barsEnter.append('polyline');
+ // Setup containers and skeleton of chart
+ var wrap = d3.select(this).selectAll('g.nv-wrap.nv-multibarHorizontal').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibarHorizontal');
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- bars.select('polyline')
- .attr('fill', 'none')
- .attr('points', function(d,i) {
- var xerr = getYerr(d,i)
- , mid = 0.8 * x.rangeBand() / ((stacked ? 1 : data.length) * 2);
- xerr = xerr.length ? xerr : [-Math.abs(xerr), Math.abs(xerr)];
- xerr = xerr.map(function(e) { return y(e) - y(0); });
- var a = [[xerr[0],-mid], [xerr[0],mid], [xerr[0],0], [xerr[1],0], [xerr[1],-mid], [xerr[1],mid]];
- return a.map(function (path) { return path.join(',') }).join(' ');
- })
- .attr('transform', function(d,i) {
- var mid = x.rangeBand() / ((stacked ? 1 : data.length) * 2);
- return 'translate(' + (getY(d,i) < 0 ? 0 : y(getY(d,i)) - y(0)) + ', ' + mid + ')'
- });
- }
+ gEnter.append('g').attr('class', 'nv-groups');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- barsEnter.append('text');
+ var groups = wrap.select('.nv-groups').selectAll('.nv-group')
+ .data(function(d) { return d }, function(d,i) { return i });
+ groups.enter().append('g')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6);
+ groups.exit().watchTransition(renderWatch, 'multibarhorizontal: exit groups')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6)
+ .remove();
+ groups
+ .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
+ .classed('hover', function(d) { return d.hover })
+ .style('fill', function(d,i){ return color(d, i) })
+ .style('stroke', function(d,i){ return color(d, i) });
+ groups.watchTransition(renderWatch, 'multibarhorizontal: groups')
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', fillOpacity);
- if (showValues && !stacked) {
- bars.select('text')
- .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'end' : 'start' })
- .attr('y', x.rangeBand() / (data.length * 2))
- .attr('dy', '.32em')
- .text(function(d,i) {
- var t = valueFormat(getY(d,i))
- , yerr = getYerr(d,i);
- if (yerr === undefined)
- return t;
- if (!yerr.length)
- return t + '±' + valueFormat(Math.abs(yerr));
- return t + '+' + valueFormat(Math.abs(yerr[1])) + '-' + valueFormat(Math.abs(yerr[0]));
+ var bars = groups.selectAll('g.nv-bar')
+ .data(function(d) { return d.values });
+ bars.exit().remove();
+
+ var barsEnter = bars.enter().append('g')
+ .attr('transform', function(d,i,j) {
+ return 'translate(' + y0(stacked ? d.y0 : 0) + ',' + (stacked ? 0 : (j * x.rangeBand() / data.length ) + x(getX(d,i))) + ')'
});
- bars.watchTransition(renderWatch, 'multibarhorizontal: bars')
- .select('text')
- .attr('x', function(d,i) { return getY(d,i) < 0 ? -4 : y(getY(d,i)) - y(0) + 4 })
- } else {
- bars.selectAll('text').text('');
- }
- if (showBarLabels && !stacked) {
- barsEnter.append('text').classed('nv-bar-label',true);
- bars.select('text.nv-bar-label')
- .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'start' : 'end' })
- .attr('y', x.rangeBand() / (data.length * 2))
- .attr('dy', '.32em')
- .text(function(d,i) { return getX(d,i) });
- bars.watchTransition(renderWatch, 'multibarhorizontal: bars')
- .select('text.nv-bar-label')
- .attr('x', function(d,i) { return getY(d,i) < 0 ? y(0) - y(getY(d,i)) + 4 : -4 });
- }
- else {
- bars.selectAll('text.nv-bar-label').text('');
- }
+ barsEnter.append('rect')
+ .attr('width', 0)
+ .attr('height', x.rangeBand() / (stacked ? 1 : data.length) )
- bars
- .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
-
- if (barColor) {
- if (!disabled) disabled = data.map(function() { return true });
bars
- .style('fill', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); })
- .style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); });
- }
-
- if (stacked)
- bars.watchTransition(renderWatch, 'multibarhorizontal: bars')
- .attr('transform', function(d,i) {
- return 'translate(' + y(d.y1) + ',' + x(getX(d,i)) + ')'
+ .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
})
- .select('rect')
- .attr('width', function(d,i) {
- return Math.abs(y(getY(d,i) + d.y0) - y(d.y0)) || 0
+ .on('mouseout', function(d,i) {
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
})
- .attr('height', x.rangeBand() );
- else
- bars.watchTransition(renderWatch, 'multibarhorizontal: bars')
- .attr('transform', function(d,i) {
- //TODO: stacked must be all positive or all negative, not both?
- return 'translate(' +
- (getY(d,i) < 0 ? y(getY(d,i)) : y(0))
- + ',' +
- (d.series * x.rangeBand() / data.length
- +
- x(getX(d,i)) )
- + ')'
+ .on('mouseout', function(d,i) {
+ dispatch.elementMouseout({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
})
- .select('rect')
- .attr('height', x.rangeBand() / data.length )
- .attr('width', function(d,i) {
- return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) || 0
+ .on('mousemove', function(d,i) {
+ dispatch.elementMousemove({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ })
+ .on('click', function(d,i) {
+ var element = this;
+ dispatch.elementClick({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill"),
+ event: d3.event,
+ element: element
+ });
+ d3.event.stopPropagation();
+ })
+ .on('dblclick', function(d,i) {
+ dispatch.elementDblClick({
+ data: d,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ d3.event.stopPropagation();
});
- //store old scales for use in transitions on update
- x0 = x.copy();
- y0 = y.copy();
+ if (getYerr(data[0],0)) {
+ barsEnter.append('polyline');
- });
+ bars.select('polyline')
+ .attr('fill', 'none')
+ .attr('points', function(d,i) {
+ var xerr = getYerr(d,i)
+ , mid = 0.8 * x.rangeBand() / ((stacked ? 1 : data.length) * 2);
+ xerr = xerr.length ? xerr : [-Math.abs(xerr), Math.abs(xerr)];
+ xerr = xerr.map(function(e) { return y(e) - y(0); });
+ var a = [[xerr[0],-mid], [xerr[0],mid], [xerr[0],0], [xerr[1],0], [xerr[1],-mid], [xerr[1],mid]];
+ return a.map(function (path) { return path.join(',') }).join(' ');
+ })
+ .attr('transform', function(d,i) {
+ var mid = x.rangeBand() / ((stacked ? 1 : data.length) * 2);
+ return 'translate(' + (getY(d,i) < 0 ? 0 : y(getY(d,i)) - y(0)) + ', ' + mid + ')'
+ });
+ }
- renderWatch.renderEnd('multibarHorizontal immediate');
- return chart;
- }
+ barsEnter.append('text');
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ if (showValues && !stacked) {
+ bars.select('text')
+ .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'end' : 'start' })
+ .attr('y', x.rangeBand() / (data.length * 2))
+ .attr('dy', '.32em')
+ .text(function(d,i) {
+ var t = valueFormat(getY(d,i))
+ , yerr = getYerr(d,i);
+ if (yerr === undefined)
+ return t;
+ if (!yerr.length)
+ return t + '±' + valueFormat(Math.abs(yerr));
+ return t + '+' + valueFormat(Math.abs(yerr[1])) + '-' + valueFormat(Math.abs(yerr[0]));
+ });
+ bars.watchTransition(renderWatch, 'multibarhorizontal: bars')
+ .select('text')
+ .attr('x', function(d,i) { return getY(d,i) < 0 ? -4 : y(getY(d,i)) - y(0) + 4 })
+ } else {
+ bars.selectAll('text').text('');
+ }
- chart.dispatch = dispatch;
+ if (showBarLabels && !stacked) {
+ barsEnter.append('text').classed('nv-bar-label',true);
+ bars.select('text.nv-bar-label')
+ .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'start' : 'end' })
+ .attr('y', x.rangeBand() / (data.length * 2))
+ .attr('dy', '.32em')
+ .text(function(d,i) { return getX(d,i) });
+ bars.watchTransition(renderWatch, 'multibarhorizontal: bars')
+ .select('text.nv-bar-label')
+ .attr('x', function(d,i) { return getY(d,i) < 0 ? y(0) - y(getY(d,i)) + 4 : -4 });
+ }
+ else {
+ bars.selectAll('text.nv-bar-label').text('');
+ }
- chart.options = nv.utils.optionsFunc.bind(chart);
+ bars
+ .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- x: {get: function(){return getX;}, set: function(_){getX=_;}},
- y: {get: function(){return getY;}, set: function(_){getY=_;}},
- yErr: {get: function(){return getYerr;}, set: function(_){getYerr=_;}},
- xScale: {get: function(){return x;}, set: function(_){x=_;}},
- yScale: {get: function(){return y;}, set: function(_){y=_;}},
- xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
- yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
- xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
- yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
- forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
- stacked: {get: function(){return stacked;}, set: function(_){stacked=_;}},
- showValues: {get: function(){return showValues;}, set: function(_){showValues=_;}},
- // this shows the group name, seems pointless?
- //showBarLabels: {get: function(){return showBarLabels;}, set: function(_){showBarLabels=_;}},
- disabled: {get: function(){return disabled;}, set: function(_){disabled=_;}},
- id: {get: function(){return id;}, set: function(_){id=_;}},
- valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}},
- valuePadding: {get: function(){return valuePadding;}, set: function(_){valuePadding=_;}},
- groupSpacing: {get: function(){return groupSpacing;}, set: function(_){groupSpacing=_;}},
- fillOpacity: {get: function(){return fillOpacity;}, set: function(_){fillOpacity=_;}},
+ if (barColor) {
+ if (!disabled) disabled = data.map(function() { return true });
+ bars
+ .style('fill', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); })
+ .style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); });
+ }
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }},
- barColor: {get: function(){return barColor;}, set: function(_){
- barColor = _ ? nv.utils.getColor(_) : null;
- }}
- });
+ if (stacked)
+ bars.watchTransition(renderWatch, 'multibarhorizontal: bars')
+ .attr('transform', function(d,i) {
+ return 'translate(' + y(d.y1) + ',' + x(getX(d,i)) + ')'
+ })
+ .select('rect')
+ .attr('width', function(d,i) {
+ return Math.abs(y(getY(d,i) + d.y0) - y(d.y0)) || 0
+ })
+ .attr('height', x.rangeBand() );
+ else
+ bars.watchTransition(renderWatch, 'multibarhorizontal: bars')
+ .attr('transform', function(d,i) {
+ //TODO: stacked must be all positive or all negative, not both?
+ return 'translate(' +
+ (getY(d,i) < 0 ? y(getY(d,i)) : y(0))
+ + ',' +
+ (d.series * x.rangeBand() / data.length
+ +
+ x(getX(d,i)) )
+ + ')'
+ })
+ .select('rect')
+ .attr('height', x.rangeBand() / data.length )
+ .attr('width', function(d,i) {
+ return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) || 0
+ });
- nv.utils.initOptions(chart);
+ //store old scales for use in transitions on update
+ x0 = x.copy();
+ y0 = y.copy();
- return chart;
-};
+ });
-nv.models.multiBarHorizontalChart = function() {
- "use strict";
+ renderWatch.renderEnd('multibarHorizontal immediate');
+ return chart;
+ }
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- var multibar = nv.models.multiBarHorizontal()
- , xAxis = nv.models.axis()
- , yAxis = nv.models.axis()
- , legend = nv.models.legend().height(30)
- , controls = nv.models.legend().height(30)
- , tooltip = nv.models.tooltip()
- ;
+ chart.dispatch = dispatch;
- var margin = {top: 30, right: 20, bottom: 50, left: 60}
- , width = null
- , height = null
- , color = nv.utils.defaultColor()
- , showControls = true
- , controlLabels = {}
- , showLegend = true
- , showXAxis = true
- , showYAxis = true
- , stacked = false
- , x //can be accessed via chart.xScale()
- , y //can be accessed via chart.yScale()
- , state = nv.utils.state()
- , defaultState = null
- , noData = null
- , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd')
- , controlWidth = function() { return showControls ? 180 : 0 }
- , duration = 250
- ;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- state.stacked = false; // DEPRECATED Maintained for backward compatibility
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ y: {get: function(){return getY;}, set: function(_){getY=_;}},
+ yErr: {get: function(){return getYerr;}, set: function(_){getYerr=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ stacked: {get: function(){return stacked;}, set: function(_){stacked=_;}},
+ showValues: {get: function(){return showValues;}, set: function(_){showValues=_;}},
+ // this shows the group name, seems pointless?
+ //showBarLabels: {get: function(){return showBarLabels;}, set: function(_){showBarLabels=_;}},
+ disabled: {get: function(){return disabled;}, set: function(_){disabled=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}},
+ valuePadding: {get: function(){return valuePadding;}, set: function(_){valuePadding=_;}},
+ groupSpacing: {get: function(){return groupSpacing;}, set: function(_){groupSpacing=_;}},
+ fillOpacity: {get: function(){return fillOpacity;}, set: function(_){fillOpacity=_;}},
- multibar.stacked(stacked);
-
- xAxis
- .orient('left')
- .tickPadding(5)
- .showMaxMin(false)
- .tickFormat(function(d) { return d })
- ;
- yAxis
- .orient('bottom')
- .tickFormat(d3.format(',.1f'))
- ;
-
- tooltip
- .duration(0)
- .valueFormatter(function(d, i) {
- return yAxis.tickFormat()(d, i);
- })
- .headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ barColor: {get: function(){return barColor;}, set: function(_){
+ barColor = _ ? nv.utils.getColor(_) : null;
+ }}
});
- controls.updateState(false);
+ nv.utils.initOptions(chart);
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var stateGetter = function(data) {
- return function(){
- return {
- active: data.map(function(d) { return !d.disabled }),
- stacked: stacked
- };
- }
+ return chart;
};
- var stateSetter = function(data) {
- return function(state) {
- if (state.stacked !== undefined)
- stacked = state.stacked;
- if (state.active !== undefined)
- data.forEach(function(series,i) {
- series.disabled = !state.active[i];
- });
- }
- };
+ nv.models.multiBarHorizontalChart = function() {
+ "use strict";
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(multibar);
- if (showXAxis) renderWatch.models(xAxis);
- if (showYAxis) renderWatch.models(yAxis);
+ var multibar = nv.models.multiBarHorizontal()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend().height(30)
+ , controls = nv.models.legend().height(30)
+ , tooltip = nv.models.tooltip()
+ ;
- selection.each(function(data) {
- var container = d3.select(this),
- that = this;
- nv.utils.initSVG(container);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ var margin = {top: 30, right: 20, bottom: 50, left: 60}
+ , marginTop = null
+ , width = null
+ , height = null
+ , color = nv.utils.defaultColor()
+ , showControls = true
+ , controlLabels = {}
+ , showLegend = true
+ , showXAxis = true
+ , showYAxis = true
+ , stacked = false
+ , x //can be accessed via chart.xScale()
+ , y //can be accessed via chart.yScale()
+ , state = nv.utils.state()
+ , defaultState = null
+ , noData = null
+ , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd')
+ , controlWidth = function() { return showControls ? 180 : 0 }
+ , duration = 250
+ ;
- chart.update = function() { container.transition().duration(duration).call(chart) };
- chart.container = this;
+ state.stacked = false; // DEPRECATED Maintained for backward compatibility
- stacked = multibar.stacked();
+ multibar.stacked(stacked);
- state
- .setter(stateSetter(data), chart.update)
- .getter(stateGetter(data))
- .update();
+ xAxis
+ .orient('left')
+ .tickPadding(5)
+ .showMaxMin(false)
+ .tickFormat(function(d) { return d })
+ ;
+ yAxis
+ .orient('bottom')
+ .tickFormat(d3.format(',.1f'))
+ ;
- // DEPRECATED set state.disableddisabled
- state.disabled = data.map(function(d) { return !!d.disabled });
+ tooltip
+ .duration(0)
+ .valueFormatter(function(d, i) {
+ return yAxis.tickFormat()(d, i);
+ })
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ });
- if (!defaultState) {
- var key;
- defaultState = {};
- for (key in state) {
- if (state[key] instanceof Array)
- defaultState[key] = state[key].slice(0);
- else
- defaultState[key] = state[key];
- }
+ controls.updateState(false);
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled }),
+ stacked: stacked
+ };
}
+ };
- // Display No Data message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
- nv.utils.noData(chart, container)
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.stacked !== undefined)
+ stacked = state.stacked;
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
}
+ };
- // Setup Scales
- x = multibar.xScale();
- y = multibar.yScale().clamp(true);
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-multiBarHorizontalChart').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarHorizontalChart').append('g');
- var g = wrap.select('g');
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(multibar);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
- gEnter.append('g').attr('class', 'nv-x nv-axis');
- gEnter.append('g').attr('class', 'nv-y nv-axis')
- .append('g').attr('class', 'nv-zeroLine')
- .append('line');
- gEnter.append('g').attr('class', 'nv-barsWrap');
- gEnter.append('g').attr('class', 'nv-legendWrap');
- gEnter.append('g').attr('class', 'nv-controlsWrap');
-
- // Legend
- if (!showLegend) {
- g.select('.nv-legendWrap').selectAll('*').remove();
- } else {
- legend.width(availableWidth - controlWidth());
-
- g.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
-
- if (legend.height() > margin.top) {
- margin.top = legend.height();
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
availableHeight = nv.utils.availableHeight(height, container, margin);
- }
- g.select('.nv-legendWrap')
- .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
- }
+ chart.update = function() { container.transition().duration(duration).call(chart) };
+ chart.container = this;
- // Controls
- if (!showControls) {
- g.select('.nv-controlsWrap').selectAll('*').remove();
- } else {
- var controlsData = [
- { key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() },
- { key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() }
- ];
+ stacked = multibar.stacked();
- controls.width(controlWidth()).color(['#444', '#444', '#444']);
- g.select('.nv-controlsWrap')
- .datum(controlsData)
- .attr('transform', 'translate(0,' + (-margin.top) +')')
- .call(controls);
- }
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ // DEPRECATED set state.disableddisabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
- // Main Chart Component(s)
- multibar
- .disabled(data.map(function(series) { return series.disabled }))
- .width(availableWidth)
- .height(availableHeight)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled }));
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
- var barsWrap = g.select('.nv-barsWrap')
- .datum(data.filter(function(d) { return !d.disabled }));
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ nv.utils.noData(chart, container)
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
- barsWrap.transition().call(multibar);
+ // Setup Scales
+ x = multibar.xScale();
+ y = multibar.yScale().clamp(true);
- // Setup Axes
- if (showXAxis) {
- xAxis
- .scale(x)
- ._ticks( nv.utils.calcTicksY(availableHeight/24, data) )
- .tickSize(-availableWidth, 0);
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-multiBarHorizontalChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarHorizontalChart').append('g');
+ var g = wrap.select('g');
- g.select('.nv-x.nv-axis').call(xAxis);
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis')
+ .append('g').attr('class', 'nv-zeroLine')
+ .append('line');
+ gEnter.append('g').attr('class', 'nv-barsWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+ gEnter.append('g').attr('class', 'nv-controlsWrap');
- var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
+ // Legend
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
+ legend.width(availableWidth - controlWidth());
- xTicks
- .selectAll('line, text');
- }
+ g.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
- if (showYAxis) {
- yAxis
- .scale(y)
- ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize( -availableHeight, 0);
+ if (!marginTop && legend.height() !== margin.top) {
+ margin.top = legend.height();
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+ }
- g.select('.nv-y.nv-axis')
- .attr('transform', 'translate(0,' + availableHeight + ')');
- g.select('.nv-y.nv-axis').call(yAxis);
- }
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
+ }
- // Zero line
- g.select(".nv-zeroLine line")
- .attr("x1", y(0))
- .attr("x2", y(0))
- .attr("y1", 0)
- .attr("y2", -availableHeight)
- ;
+ // Controls
+ if (!showControls) {
+ g.select('.nv-controlsWrap').selectAll('*').remove();
+ } else {
+ var controlsData = [
+ { key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() },
+ { key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() }
+ ];
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
+ controls.width(controlWidth()).color(['#444', '#444', '#444']);
+ g.select('.nv-controlsWrap')
+ .datum(controlsData)
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
+ .call(controls);
+ }
- legend.dispatch.on('stateChange', function(newState) {
- for (var key in newState)
- state[key] = newState[key];
- dispatch.stateChange(state);
- chart.update();
- });
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- controls.dispatch.on('legendClick', function(d,i) {
- if (!d.disabled) return;
- controlsData = controlsData.map(function(s) {
- s.disabled = true;
- return s;
- });
- d.disabled = false;
+ // Main Chart Component(s)
+ multibar
+ .disabled(data.map(function(series) { return series.disabled }))
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled }));
- switch (d.key) {
- case 'Grouped':
- case controlLabels.grouped:
- multibar.stacked(false);
- break;
- case 'Stacked':
- case controlLabels.stacked:
- multibar.stacked(true);
- break;
- }
+ var barsWrap = g.select('.nv-barsWrap')
+ .datum(data.filter(function(d) { return !d.disabled }));
- state.stacked = multibar.stacked();
- dispatch.stateChange(state);
- stacked = multibar.stacked();
+ barsWrap.transition().call(multibar);
- chart.update();
- });
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ ._ticks( nv.utils.calcTicksY(availableHeight/24, data) )
+ .tickSize(-availableWidth, 0);
- // Update chart from a state object passed to event handler
- dispatch.on('changeState', function(e) {
+ g.select('.nv-x.nv-axis').call(xAxis);
- if (typeof e.disabled !== 'undefined') {
- data.forEach(function(series,i) {
- series.disabled = e.disabled[i];
- });
+ var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
- state.disabled = e.disabled;
+ xTicks
+ .selectAll('line, text');
}
- if (typeof e.stacked !== 'undefined') {
- multibar.stacked(e.stacked);
- state.stacked = e.stacked;
- stacked = e.stacked;
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize( -availableHeight, 0);
+
+ g.select('.nv-y.nv-axis')
+ .attr('transform', 'translate(0,' + availableHeight + ')');
+ g.select('.nv-y.nv-axis').call(yAxis);
}
- chart.update();
- });
- });
- renderWatch.renderEnd('multibar horizontal chart immediate');
- return chart;
- }
+ // Zero line
+ g.select(".nv-zeroLine line")
+ .attr("x1", y(0))
+ .attr("x2", y(0))
+ .attr("y1", 0)
+ .attr("y2", -availableHeight)
+ ;
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
- multibar.dispatch.on('elementMouseover.tooltip', function(evt) {
- evt.value = chart.x()(evt.data);
- evt['series'] = {
- key: evt.data.key,
- value: chart.y()(evt.data),
- color: evt.color
- };
- tooltip.data(evt).hidden(false);
- });
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
- multibar.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true);
- });
+ controls.dispatch.on('legendClick', function(d,i) {
+ if (!d.disabled) return;
+ controlsData = controlsData.map(function(s) {
+ s.disabled = true;
+ return s;
+ });
+ d.disabled = false;
- multibar.dispatch.on('elementMousemove.tooltip', function(evt) {
- tooltip();
- });
+ switch (d.key) {
+ case 'Grouped':
+ case controlLabels.grouped:
+ multibar.stacked(false);
+ break;
+ case 'Stacked':
+ case controlLabels.stacked:
+ multibar.stacked(true);
+ break;
+ }
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ state.stacked = multibar.stacked();
+ dispatch.stateChange(state);
+ stacked = multibar.stacked();
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.multibar = multibar;
- chart.legend = legend;
- chart.controls = controls;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.state = state;
- chart.tooltip = tooltip;
+ chart.update();
+ });
- chart.options = nv.utils.optionsFunc.bind(chart);
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
- controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- multibar.duration(duration);
- xAxis.duration(duration);
- yAxis.duration(duration);
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- legend.color(color);
- }},
- barColor: {get: function(){return multibar.barColor;}, set: function(_){
- multibar.barColor(_);
- legend.color(function(d,i) {return d3.rgb('#ccc').darker(i * 1.5).toString();})
- }}
- });
+ state.disabled = e.disabled;
+ }
- nv.utils.inheritOptions(chart, multibar);
- nv.utils.initOptions(chart);
+ if (typeof e.stacked !== 'undefined') {
+ multibar.stacked(e.stacked);
+ state.stacked = e.stacked;
+ stacked = e.stacked;
+ }
- return chart;
-};
-nv.models.multiChart = function() {
- "use strict";
+ chart.update();
+ });
+ });
+ renderWatch.renderEnd('multibar horizontal chart immediate');
+ return chart;
+ }
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
- var margin = {top: 30, right: 20, bottom: 50, left: 60},
- color = nv.utils.defaultColor(),
- width = null,
- height = null,
- showLegend = true,
- noData = null,
- yDomain1,
- yDomain2,
- getX = function(d) { return d.x },
- getY = function(d) { return d.y},
- interpolate = 'linear',
- useVoronoi = true,
- interactiveLayer = nv.interactiveGuideline(),
- useInteractiveGuideline = false,
- legendRightAxisHint = ' (right axis)'
- ;
+ multibar.dispatch.on('elementMouseover.tooltip', function(evt) {
+ evt.value = chart.x()(evt.data);
+ evt['series'] = {
+ key: evt.data.key,
+ value: chart.y()(evt.data),
+ color: evt.color
+ };
+ tooltip.data(evt).hidden(false);
+ });
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ multibar.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true);
+ });
- var x = d3.scale.linear(),
- yScale1 = d3.scale.linear(),
- yScale2 = d3.scale.linear(),
+ multibar.dispatch.on('elementMousemove.tooltip', function(evt) {
+ tooltip();
+ });
- lines1 = nv.models.line().yScale(yScale1),
- lines2 = nv.models.line().yScale(yScale2),
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- scatters1 = nv.models.scatter().yScale(yScale1),
- scatters2 = nv.models.scatter().yScale(yScale2),
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.multibar = multibar;
+ chart.legend = legend;
+ chart.controls = controls;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.state = state;
+ chart.tooltip = tooltip;
- bars1 = nv.models.multiBar().stacked(false).yScale(yScale1),
- bars2 = nv.models.multiBar().stacked(false).yScale(yScale2),
+ chart.options = nv.utils.optionsFunc.bind(chart);
- stack1 = nv.models.stackedArea().yScale(yScale1),
- stack2 = nv.models.stackedArea().yScale(yScale2),
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
+ controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- xAxis = nv.models.axis().scale(x).orient('bottom').tickPadding(5),
- yAxis1 = nv.models.axis().scale(yScale1).orient('left'),
- yAxis2 = nv.models.axis().scale(yScale2).orient('right'),
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ if (_.top !== undefined) {
+ margin.top = _.top;
+ marginTop = _.top;
+ }
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ multibar.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ }},
+ barColor: {get: function(){return multibar.barColor;}, set: function(_){
+ multibar.barColor(_);
+ legend.color(function(d,i) {return d3.rgb('#ccc').darker(i * 1.5).toString();})
+ }}
+ });
- legend = nv.models.legend().height(30),
- tooltip = nv.models.tooltip(),
- dispatch = d3.dispatch();
+ nv.utils.inheritOptions(chart, multibar);
+ nv.utils.initOptions(chart);
- var charts = [lines1, lines2, scatters1, scatters2, bars1, bars2, stack1, stack2];
+ return chart;
+ };
+ nv.models.multiChart = function() {
+ "use strict";
- function chart(selection) {
- selection.each(function(data) {
- var container = d3.select(this),
- that = this;
- nv.utils.initSVG(container);
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- chart.update = function() { container.transition().call(chart); };
- chart.container = this;
+ var margin = {top: 30, right: 20, bottom: 50, left: 60},
+ marginTop = null,
+ color = nv.utils.defaultColor(),
+ width = null,
+ height = null,
+ showLegend = true,
+ noData = null,
+ yDomain1,
+ yDomain2,
+ getX = function(d) { return d.x },
+ getY = function(d) { return d.y},
+ interpolate = 'linear',
+ useVoronoi = true,
+ interactiveLayer = nv.interactiveGuideline(),
+ useInteractiveGuideline = false,
+ legendRightAxisHint = ' (right axis)',
+ duration = 250
+ ;
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var dataLines1 = data.filter(function(d) {return d.type == 'line' && d.yAxis == 1});
- var dataLines2 = data.filter(function(d) {return d.type == 'line' && d.yAxis == 2});
- var dataScatters1 = data.filter(function(d) {return d.type == 'scatter' && d.yAxis == 1});
- var dataScatters2 = data.filter(function(d) {return d.type == 'scatter' && d.yAxis == 2});
- var dataBars1 = data.filter(function(d) {return d.type == 'bar' && d.yAxis == 1});
- var dataBars2 = data.filter(function(d) {return d.type == 'bar' && d.yAxis == 2});
- var dataStack1 = data.filter(function(d) {return d.type == 'area' && d.yAxis == 1});
- var dataStack2 = data.filter(function(d) {return d.type == 'area' && d.yAxis == 2});
+ var x = d3.scale.linear(),
+ yScale1 = d3.scale.linear(),
+ yScale2 = d3.scale.linear(),
- // Display noData message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
- nv.utils.noData(chart, container);
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
+ lines1 = nv.models.line().yScale(yScale1).duration(duration),
+ lines2 = nv.models.line().yScale(yScale2).duration(duration),
- var series1 = data.filter(function(d) {return !d.disabled && d.yAxis == 1})
- .map(function(d) {
- return d.values.map(function(d,i) {
- return { x: getX(d), y: getY(d) }
- })
- });
+ scatters1 = nv.models.scatter().yScale(yScale1).duration(duration),
+ scatters2 = nv.models.scatter().yScale(yScale2).duration(duration),
- var series2 = data.filter(function(d) {return !d.disabled && d.yAxis == 2})
- .map(function(d) {
- return d.values.map(function(d,i) {
- return { x: getX(d), y: getY(d) }
- })
- });
+ bars1 = nv.models.multiBar().stacked(false).yScale(yScale1).duration(duration),
+ bars2 = nv.models.multiBar().stacked(false).yScale(yScale2).duration(duration),
- x .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x }))
- .range([0, availableWidth]);
+ stack1 = nv.models.stackedArea().yScale(yScale1).duration(duration),
+ stack2 = nv.models.stackedArea().yScale(yScale2).duration(duration),
- var wrap = container.selectAll('g.wrap.multiChart').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'wrap nvd3 multiChart').append('g');
+ xAxis = nv.models.axis().scale(x).orient('bottom').tickPadding(5).duration(duration),
+ yAxis1 = nv.models.axis().scale(yScale1).orient('left').duration(duration),
+ yAxis2 = nv.models.axis().scale(yScale2).orient('right').duration(duration),
- gEnter.append('g').attr('class', 'nv-x nv-axis');
- gEnter.append('g').attr('class', 'nv-y1 nv-axis');
- gEnter.append('g').attr('class', 'nv-y2 nv-axis');
- gEnter.append('g').attr('class', 'stack1Wrap');
- gEnter.append('g').attr('class', 'stack2Wrap');
- gEnter.append('g').attr('class', 'bars1Wrap');
- gEnter.append('g').attr('class', 'bars2Wrap');
- gEnter.append('g').attr('class', 'scatters1Wrap');
- gEnter.append('g').attr('class', 'scatters2Wrap');
- gEnter.append('g').attr('class', 'lines1Wrap');
- gEnter.append('g').attr('class', 'lines2Wrap');
- gEnter.append('g').attr('class', 'legendWrap');
- gEnter.append('g').attr('class', 'nv-interactive');
+ legend = nv.models.legend().height(30),
+ tooltip = nv.models.tooltip(),
+ dispatch = d3.dispatch();
- var g = wrap.select('g');
+ var charts = [lines1, lines2, scatters1, scatters2, bars1, bars2, stack1, stack2];
- var color_array = data.map(function(d,i) {
- return data[i].color || color(d, i);
- });
+ function chart(selection) {
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
- // Legend
- if (!showLegend) {
- g.select('.legendWrap').selectAll('*').remove();
- } else {
- var legendWidth = legend.align() ? availableWidth / 2 : availableWidth;
- var legendXPosition = legend.align() ? legendWidth : 0;
+ chart.update = function() { container.transition().call(chart); };
+ chart.container = this;
- legend.width(legendWidth);
- legend.color(color_array);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
- g.select('.legendWrap')
- .datum(data.map(function(series) {
- series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
- series.key = series.originalKey + (series.yAxis == 1 ? '' : legendRightAxisHint);
- return series;
- }))
- .call(legend);
+ var dataLines1 = data.filter(function(d) {return d.type == 'line' && d.yAxis == 1});
+ var dataLines2 = data.filter(function(d) {return d.type == 'line' && d.yAxis == 2});
+ var dataScatters1 = data.filter(function(d) {return d.type == 'scatter' && d.yAxis == 1});
+ var dataScatters2 = data.filter(function(d) {return d.type == 'scatter' && d.yAxis == 2});
+ var dataBars1 = data.filter(function(d) {return d.type == 'bar' && d.yAxis == 1});
+ var dataBars2 = data.filter(function(d) {return d.type == 'bar' && d.yAxis == 2});
+ var dataStack1 = data.filter(function(d) {return d.type == 'area' && d.yAxis == 1});
+ var dataStack2 = data.filter(function(d) {return d.type == 'area' && d.yAxis == 2});
- if (legend.height() > margin.top) {
- margin.top = legend.height();
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ // Display noData message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ nv.utils.noData(chart, container);
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
}
- g.select('.legendWrap')
- .attr('transform', 'translate(' + legendXPosition + ',' + (-margin.top) +')');
- }
+ var series1 = data.filter(function(d) {return !d.disabled && d.yAxis == 1})
+ .map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d), y: getY(d) }
+ })
+ });
- lines1
- .width(availableWidth)
- .height(availableHeight)
- .interpolate(interpolate)
- .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'line'}));
- lines2
- .width(availableWidth)
- .height(availableHeight)
- .interpolate(interpolate)
- .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'line'}));
- scatters1
- .width(availableWidth)
- .height(availableHeight)
- .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'scatter'}));
- scatters2
- .width(availableWidth)
- .height(availableHeight)
- .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'scatter'}));
- bars1
- .width(availableWidth)
- .height(availableHeight)
- .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'bar'}));
- bars2
- .width(availableWidth)
- .height(availableHeight)
- .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'bar'}));
- stack1
- .width(availableWidth)
- .height(availableHeight)
- .interpolate(interpolate)
- .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'area'}));
- stack2
- .width(availableWidth)
- .height(availableHeight)
- .interpolate(interpolate)
- .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'area'}));
+ var series2 = data.filter(function(d) {return !d.disabled && d.yAxis == 2})
+ .map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d), y: getY(d) }
+ })
+ });
- g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ x .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x }))
+ .range([0, availableWidth]);
- var lines1Wrap = g.select('.lines1Wrap')
- .datum(dataLines1.filter(function(d){return !d.disabled}));
- var scatters1Wrap = g.select('.scatters1Wrap')
- .datum(dataScatters1.filter(function(d){return !d.disabled}));
- var bars1Wrap = g.select('.bars1Wrap')
- .datum(dataBars1.filter(function(d){return !d.disabled}));
- var stack1Wrap = g.select('.stack1Wrap')
- .datum(dataStack1.filter(function(d){return !d.disabled}));
- var lines2Wrap = g.select('.lines2Wrap')
- .datum(dataLines2.filter(function(d){return !d.disabled}));
- var scatters2Wrap = g.select('.scatters2Wrap')
- .datum(dataScatters2.filter(function(d){return !d.disabled}));
- var bars2Wrap = g.select('.bars2Wrap')
- .datum(dataBars2.filter(function(d){return !d.disabled}));
- var stack2Wrap = g.select('.stack2Wrap')
- .datum(dataStack2.filter(function(d){return !d.disabled}));
+ var wrap = container.selectAll('g.wrap.multiChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'wrap nvd3 multiChart').append('g');
- var extraValue1 = dataStack1.length ? dataStack1.map(function(a){return a.values}).reduce(function(a,b){
- return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
- }).concat([{x:0, y:0}]) : [];
- var extraValue2 = dataStack2.length ? dataStack2.map(function(a){return a.values}).reduce(function(a,b){
- return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
- }).concat([{x:0, y:0}]) : [];
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y1 nv-axis');
+ gEnter.append('g').attr('class', 'nv-y2 nv-axis');
+ gEnter.append('g').attr('class', 'stack1Wrap');
+ gEnter.append('g').attr('class', 'stack2Wrap');
+ gEnter.append('g').attr('class', 'bars1Wrap');
+ gEnter.append('g').attr('class', 'bars2Wrap');
+ gEnter.append('g').attr('class', 'scatters1Wrap');
+ gEnter.append('g').attr('class', 'scatters2Wrap');
+ gEnter.append('g').attr('class', 'lines1Wrap');
+ gEnter.append('g').attr('class', 'lines2Wrap');
+ gEnter.append('g').attr('class', 'legendWrap');
+ gEnter.append('g').attr('class', 'nv-interactive');
- yScale1 .domain(yDomain1 || d3.extent(d3.merge(series1).concat(extraValue1), function(d) { return d.y } ))
- .range([0, availableHeight]);
+ var g = wrap.select('g');
- yScale2 .domain(yDomain2 || d3.extent(d3.merge(series2).concat(extraValue2), function(d) { return d.y } ))
- .range([0, availableHeight]);
+ var color_array = data.map(function(d,i) {
+ return data[i].color || color(d, i);
+ });
- lines1.yDomain(yScale1.domain());
- scatters1.yDomain(yScale1.domain());
- bars1.yDomain(yScale1.domain());
- stack1.yDomain(yScale1.domain());
+ // Legend
+ if (!showLegend) {
+ g.select('.legendWrap').selectAll('*').remove();
+ } else {
+ var legendWidth = legend.align() ? availableWidth / 2 : availableWidth;
+ var legendXPosition = legend.align() ? legendWidth : 0;
- lines2.yDomain(yScale2.domain());
- scatters2.yDomain(yScale2.domain());
- bars2.yDomain(yScale2.domain());
- stack2.yDomain(yScale2.domain());
+ legend.width(legendWidth);
+ legend.color(color_array);
- if(dataStack1.length){d3.transition(stack1Wrap).call(stack1);}
- if(dataStack2.length){d3.transition(stack2Wrap).call(stack2);}
+ g.select('.legendWrap')
+ .datum(data.map(function(series) {
+ series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
+ series.key = series.originalKey + (series.yAxis == 1 ? '' : legendRightAxisHint);
+ return series;
+ }))
+ .call(legend);
- if(dataBars1.length){d3.transition(bars1Wrap).call(bars1);}
- if(dataBars2.length){d3.transition(bars2Wrap).call(bars2);}
+ if (!marginTop && legend.height() !== margin.top) {
+ margin.top = legend.height();
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+ }
- if(dataLines1.length){d3.transition(lines1Wrap).call(lines1);}
- if(dataLines2.length){d3.transition(lines2Wrap).call(lines2);}
+ g.select('.legendWrap')
+ .attr('transform', 'translate(' + legendXPosition + ',' + (-margin.top) +')');
+ }
- if(dataScatters1.length){d3.transition(scatters1Wrap).call(scatters1);}
- if(dataScatters2.length){d3.transition(scatters2Wrap).call(scatters2);}
+ lines1
+ .width(availableWidth)
+ .height(availableHeight)
+ .interpolate(interpolate)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'line'}));
+ lines2
+ .width(availableWidth)
+ .height(availableHeight)
+ .interpolate(interpolate)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'line'}));
+ scatters1
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'scatter'}));
+ scatters2
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'scatter'}));
+ bars1
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'bar'}));
+ bars2
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'bar'}));
+ stack1
+ .width(availableWidth)
+ .height(availableHeight)
+ .interpolate(interpolate)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'area'}));
+ stack2
+ .width(availableWidth)
+ .height(availableHeight)
+ .interpolate(interpolate)
+ .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'area'}));
- xAxis
- ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize(-availableHeight, 0);
+ g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- g.select('.nv-x.nv-axis')
- .attr('transform', 'translate(0,' + availableHeight + ')');
- d3.transition(g.select('.nv-x.nv-axis'))
- .call(xAxis);
+ var lines1Wrap = g.select('.lines1Wrap')
+ .datum(dataLines1.filter(function(d){return !d.disabled}));
+ var scatters1Wrap = g.select('.scatters1Wrap')
+ .datum(dataScatters1.filter(function(d){return !d.disabled}));
+ var bars1Wrap = g.select('.bars1Wrap')
+ .datum(dataBars1.filter(function(d){return !d.disabled}));
+ var stack1Wrap = g.select('.stack1Wrap')
+ .datum(dataStack1.filter(function(d){return !d.disabled}));
+ var lines2Wrap = g.select('.lines2Wrap')
+ .datum(dataLines2.filter(function(d){return !d.disabled}));
+ var scatters2Wrap = g.select('.scatters2Wrap')
+ .datum(dataScatters2.filter(function(d){return !d.disabled}));
+ var bars2Wrap = g.select('.bars2Wrap')
+ .datum(dataBars2.filter(function(d){return !d.disabled}));
+ var stack2Wrap = g.select('.stack2Wrap')
+ .datum(dataStack2.filter(function(d){return !d.disabled}));
- yAxis1
- ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
+ var extraValue1 = dataStack1.length ? dataStack1.map(function(a){return a.values}).reduce(function(a,b){
+ return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
+ }).concat([{x:0, y:0}]) : [];
+ var extraValue2 = dataStack2.length ? dataStack2.map(function(a){return a.values}).reduce(function(a,b){
+ return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
+ }).concat([{x:0, y:0}]) : [];
+ yScale1 .domain(yDomain1 || d3.extent(d3.merge(series1).concat(extraValue1), function(d) { return d.y } ))
+ .range([0, availableHeight]);
- d3.transition(g.select('.nv-y1.nv-axis'))
- .call(yAxis1);
+ yScale2 .domain(yDomain2 || d3.extent(d3.merge(series2).concat(extraValue2), function(d) { return d.y } ))
+ .range([0, availableHeight]);
- yAxis2
- ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
+ lines1.yDomain(yScale1.domain());
+ scatters1.yDomain(yScale1.domain());
+ bars1.yDomain(yScale1.domain());
+ stack1.yDomain(yScale1.domain());
- d3.transition(g.select('.nv-y2.nv-axis'))
- .call(yAxis2);
+ lines2.yDomain(yScale2.domain());
+ scatters2.yDomain(yScale2.domain());
+ bars2.yDomain(yScale2.domain());
+ stack2.yDomain(yScale2.domain());
- g.select('.nv-y1.nv-axis')
- .classed('nv-disabled', series1.length ? false : true)
- .attr('transform', 'translate(' + x.range()[0] + ',0)');
+ if(dataStack1.length){d3.transition(stack1Wrap).call(stack1);}
+ if(dataStack2.length){d3.transition(stack2Wrap).call(stack2);}
- g.select('.nv-y2.nv-axis')
- .classed('nv-disabled', series2.length ? false : true)
- .attr('transform', 'translate(' + x.range()[1] + ',0)');
+ if(dataBars1.length){d3.transition(bars1Wrap).call(bars1);}
+ if(dataBars2.length){d3.transition(bars2Wrap).call(bars2);}
- legend.dispatch.on('stateChange', function(newState) {
- chart.update();
- });
+ if(dataLines1.length){d3.transition(lines1Wrap).call(lines1);}
+ if(dataLines2.length){d3.transition(lines2Wrap).call(lines2);}
- if(useInteractiveGuideline){
- interactiveLayer
- .width(availableWidth)
- .height(availableHeight)
- .margin({left:margin.left, top:margin.top})
- .svgContainer(container)
- .xScale(x);
- wrap.select(".nv-interactive").call(interactiveLayer);
- }
+ if(dataScatters1.length){d3.transition(scatters1Wrap).call(scatters1);}
+ if(dataScatters2.length){d3.transition(scatters2Wrap).call(scatters2);}
- //============================================================
- // Event Handling/Dispatching
- //------------------------------------------------------------
+ xAxis
+ ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize(-availableHeight, 0);
- function mouseover_line(evt) {
- var yaxis = data[evt.seriesIndex].yAxis === 2 ? yAxis2 : yAxis1;
- evt.value = evt.point.x;
- evt.series = {
- value: evt.point.y,
- color: evt.point.color,
- key: evt.series.key
- };
- tooltip
- .duration(0)
- .headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- })
- .valueFormatter(function(d, i) {
- return yaxis.tickFormat()(d, i);
- })
- .data(evt)
- .hidden(false);
- }
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + availableHeight + ')');
+ d3.transition(g.select('.nv-x.nv-axis'))
+ .call(xAxis);
- function mouseover_scatter(evt) {
- var yaxis = data[evt.seriesIndex].yAxis === 2 ? yAxis2 : yAxis1;
- evt.value = evt.point.x;
- evt.series = {
- value: evt.point.y,
- color: evt.point.color,
- key: evt.series.key
- };
- tooltip
- .duration(100)
- .headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- })
- .valueFormatter(function(d, i) {
- return yaxis.tickFormat()(d, i);
- })
- .data(evt)
- .hidden(false);
- }
+ yAxis1
+ ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
- function mouseover_stack(evt) {
- var yaxis = data[evt.seriesIndex].yAxis === 2 ? yAxis2 : yAxis1;
- evt.point['x'] = stack1.x()(evt.point);
- evt.point['y'] = stack1.y()(evt.point);
- tooltip
- .duration(0)
- .headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- })
- .valueFormatter(function(d, i) {
- return yaxis.tickFormat()(d, i);
- })
- .data(evt)
- .hidden(false);
- }
- function mouseover_bar(evt) {
- var yaxis = data[evt.data.series].yAxis === 2 ? yAxis2 : yAxis1;
+ d3.transition(g.select('.nv-y1.nv-axis'))
+ .call(yAxis1);
- evt.value = bars1.x()(evt.data);
- evt['series'] = {
- value: bars1.y()(evt.data),
- color: evt.color,
- key: evt.data.key
- };
- tooltip
- .duration(0)
- .headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- })
- .valueFormatter(function(d, i) {
- return yaxis.tickFormat()(d, i);
- })
- .data(evt)
- .hidden(false);
- }
+ yAxis2
+ ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
+ d3.transition(g.select('.nv-y2.nv-axis'))
+ .call(yAxis2);
+ g.select('.nv-y1.nv-axis')
+ .classed('nv-disabled', series1.length ? false : true)
+ .attr('transform', 'translate(' + x.range()[0] + ',0)');
- function clearHighlights() {
- for(var i=0, il=charts.length; i < il; i++){
- var chart = charts[i];
- try {
- chart.clearHighlights();
- } catch(e){}
- }
- }
+ g.select('.nv-y2.nv-axis')
+ .classed('nv-disabled', series2.length ? false : true)
+ .attr('transform', 'translate(' + x.range()[1] + ',0)');
- function highlightPoint(serieIndex, pointIndex, b){
- for(var i=0, il=charts.length; i < il; i++){
- var chart = charts[i];
- try {
- chart.highlightPoint(serieIndex, pointIndex, b);
- } catch(e){}
- }
- }
+ legend.dispatch.on('stateChange', function(newState) {
+ chart.update();
+ });
- if(useInteractiveGuideline){
- interactiveLayer.dispatch.on('elementMousemove', function(e) {
- clearHighlights();
- var singlePoint, pointIndex, pointXLocation, allData = [];
- data
- .filter(function(series, i) {
- series.seriesIndex = i;
- return !series.disabled;
- })
- .forEach(function(series,i) {
- var extent = x.domain();
- var currentValues = series.values.filter(function(d,i) {
- return chart.x()(d,i) >= extent[0] && chart.x()(d,i) <= extent[1];
- });
+ if(useInteractiveGuideline){
+ interactiveLayer
+ .width(availableWidth)
+ .height(availableHeight)
+ .margin({left:margin.left, top:margin.top})
+ .svgContainer(container)
+ .xScale(x);
+ wrap.select(".nv-interactive").call(interactiveLayer);
+ }
- pointIndex = nv.interactiveBisect(currentValues, e.pointXValue, chart.x());
- var point = currentValues[pointIndex];
- var pointYValue = chart.y()(point, pointIndex);
- if (pointYValue !== null) {
- highlightPoint(i, pointIndex, true);
- }
- if (point === undefined) return;
- if (singlePoint === undefined) singlePoint = point;
- if (pointXLocation === undefined) pointXLocation = x(chart.x()(point,pointIndex));
- allData.push({
- key: series.key,
- value: pointYValue,
- color: color(series,series.seriesIndex),
- data: point,
- yAxis: series.yAxis == 2 ? yAxis2 : yAxis1
- });
- });
+ //============================================================
+ // Event Handling/Dispatching
+ //------------------------------------------------------------
- var defaultValueFormatter = function(d,i) {
- var yAxis = allData[i].yAxis;
- return d == null ? "N/A" : yAxis.tickFormat()(d);
+ function mouseover_line(evt) {
+ var yaxis = data[evt.seriesIndex].yAxis === 2 ? yAxis2 : yAxis1;
+ evt.value = evt.point.x;
+ evt.series = {
+ value: evt.point.y,
+ color: evt.point.color,
+ key: evt.series.key
};
+ tooltip
+ .duration(0)
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ })
+ .valueFormatter(function(d, i) {
+ return yaxis.tickFormat()(d, i);
+ })
+ .data(evt)
+ .hidden(false);
+ }
- interactiveLayer.tooltip
+ function mouseover_scatter(evt) {
+ var yaxis = data[evt.seriesIndex].yAxis === 2 ? yAxis2 : yAxis1;
+ evt.value = evt.point.x;
+ evt.series = {
+ value: evt.point.y,
+ color: evt.point.color,
+ key: evt.series.key
+ };
+ tooltip
+ .duration(100)
.headerFormatter(function(d, i) {
return xAxis.tickFormat()(d, i);
})
- .valueFormatter(interactiveLayer.tooltip.valueFormatter() || defaultValueFormatter)
- .data({
- value: chart.x()( singlePoint,pointIndex ),
- index: pointIndex,
- series: allData
- })();
+ .valueFormatter(function(d, i) {
+ return yaxis.tickFormat()(d, i);
+ })
+ .data(evt)
+ .hidden(false);
+ }
- interactiveLayer.renderGuideLine(pointXLocation);
- });
+ function mouseover_stack(evt) {
+ var yaxis = data[evt.seriesIndex].yAxis === 2 ? yAxis2 : yAxis1;
+ evt.point['x'] = stack1.x()(evt.point);
+ evt.point['y'] = stack1.y()(evt.point);
+ tooltip
+ .duration(0)
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ })
+ .valueFormatter(function(d, i) {
+ return yaxis.tickFormat()(d, i);
+ })
+ .data(evt)
+ .hidden(false);
+ }
- interactiveLayer.dispatch.on("elementMouseout",function(e) {
- clearHighlights();
- });
- } else {
- lines1.dispatch.on('elementMouseover.tooltip', mouseover_line);
- lines2.dispatch.on('elementMouseover.tooltip', mouseover_line);
- lines1.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true)
- });
- lines2.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true)
- });
+ function mouseover_bar(evt) {
+ var yaxis = data[evt.data.series].yAxis === 2 ? yAxis2 : yAxis1;
- scatters1.dispatch.on('elementMouseover.tooltip', mouseover_scatter);
- scatters2.dispatch.on('elementMouseover.tooltip', mouseover_scatter);
- scatters1.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true)
- });
- scatters2.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true)
- });
+ evt.value = bars1.x()(evt.data);
+ evt['series'] = {
+ value: bars1.y()(evt.data),
+ color: evt.color,
+ key: evt.data.key
+ };
+ tooltip
+ .duration(0)
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ })
+ .valueFormatter(function(d, i) {
+ return yaxis.tickFormat()(d, i);
+ })
+ .data(evt)
+ .hidden(false);
+ }
- stack1.dispatch.on('elementMouseover.tooltip', mouseover_stack);
- stack2.dispatch.on('elementMouseover.tooltip', mouseover_stack);
- stack1.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true)
- });
- stack2.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true)
- });
- bars1.dispatch.on('elementMouseover.tooltip', mouseover_bar);
- bars2.dispatch.on('elementMouseover.tooltip', mouseover_bar);
- bars1.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true);
- });
- bars2.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true);
- });
- bars1.dispatch.on('elementMousemove.tooltip', function(evt) {
- tooltip();
- });
- bars2.dispatch.on('elementMousemove.tooltip', function(evt) {
- tooltip();
- });
- }
- });
+ function clearHighlights() {
+ for(var i=0, il=charts.length; i < il; i++){
+ var chart = charts[i];
+ try {
+ chart.clearHighlights();
+ } catch(e){}
+ }
+ }
- return chart;
- }
+ function highlightPoint(serieIndex, pointIndex, b){
+ for(var i=0, il=charts.length; i < il; i++){
+ var chart = charts[i];
+ try {
+ chart.highlightPoint(serieIndex, pointIndex, b);
+ } catch(e){}
+ }
+ }
- //============================================================
- // Global getters and setters
- //------------------------------------------------------------
+ if(useInteractiveGuideline){
+ interactiveLayer.dispatch.on('elementMousemove', function(e) {
+ clearHighlights();
+ var singlePoint, pointIndex, pointXLocation, allData = [];
+ data
+ .filter(function(series, i) {
+ series.seriesIndex = i;
+ return !series.disabled;
+ })
+ .forEach(function(series,i) {
+ var extent = x.domain();
+ var currentValues = series.values.filter(function(d,i) {
+ return chart.x()(d,i) >= extent[0] && chart.x()(d,i) <= extent[1];
+ });
- chart.dispatch = dispatch;
- chart.legend = legend;
- chart.lines1 = lines1;
- chart.lines2 = lines2;
- chart.scatters1 = scatters1;
- chart.scatters2 = scatters2;
- chart.bars1 = bars1;
- chart.bars2 = bars2;
- chart.stack1 = stack1;
- chart.stack2 = stack2;
- chart.xAxis = xAxis;
- chart.yAxis1 = yAxis1;
- chart.yAxis2 = yAxis2;
- chart.tooltip = tooltip;
- chart.interactiveLayer = interactiveLayer;
+ pointIndex = nv.interactiveBisect(currentValues, e.pointXValue, chart.x());
+ var point = currentValues[pointIndex];
+ var pointYValue = chart.y()(point, pointIndex);
+ if (pointYValue !== null) {
+ highlightPoint(i, pointIndex, true);
+ }
+ if (point === undefined) return;
+ if (singlePoint === undefined) singlePoint = point;
+ if (pointXLocation === undefined) pointXLocation = x(chart.x()(point,pointIndex));
+ allData.push({
+ key: series.key,
+ value: pointYValue,
+ color: color(series,series.seriesIndex),
+ data: point,
+ yAxis: series.yAxis == 2 ? yAxis2 : yAxis1
+ });
+ });
- chart.options = nv.utils.optionsFunc.bind(chart);
+ var defaultValueFormatter = function(d,i) {
+ var yAxis = allData[i].yAxis;
+ return d == null ? "N/A" : yAxis.tickFormat()(d);
+ };
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- yDomain1: {get: function(){return yDomain1;}, set: function(_){yDomain1=_;}},
- yDomain2: {get: function(){return yDomain2;}, set: function(_){yDomain2=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}},
- legendRightAxisHint: {get: function(){return legendRightAxisHint;}, set: function(_){legendRightAxisHint=_;}},
+ interactiveLayer.tooltip
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ })
+ .valueFormatter(interactiveLayer.tooltip.valueFormatter() || defaultValueFormatter)
+ .data({
+ value: chart.x()( singlePoint,pointIndex ),
+ index: pointIndex,
+ series: allData
+ })();
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }},
- x: {get: function(){return getX;}, set: function(_){
- getX = _;
- lines1.x(_);
- lines2.x(_);
- scatters1.x(_);
- scatters2.x(_);
- bars1.x(_);
- bars2.x(_);
- stack1.x(_);
- stack2.x(_);
- }},
- y: {get: function(){return getY;}, set: function(_){
- getY = _;
- lines1.y(_);
- lines2.y(_);
- scatters1.y(_);
- scatters2.y(_);
- stack1.y(_);
- stack2.y(_);
- bars1.y(_);
- bars2.y(_);
- }},
- useVoronoi: {get: function(){return useVoronoi;}, set: function(_){
- useVoronoi=_;
- lines1.useVoronoi(_);
- lines2.useVoronoi(_);
- stack1.useVoronoi(_);
- stack2.useVoronoi(_);
- }},
+ interactiveLayer.renderGuideLine(pointXLocation);
+ });
- useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
- useInteractiveGuideline = _;
- if (useInteractiveGuideline) {
- lines1.interactive(false);
- lines1.useVoronoi(false);
- lines2.interactive(false);
- lines2.useVoronoi(false);
- stack1.interactive(false);
- stack1.useVoronoi(false);
- stack2.interactive(false);
- stack2.useVoronoi(false);
- scatters1.interactive(false);
- scatters2.interactive(false);
- }
- }}
- });
+ interactiveLayer.dispatch.on("elementMouseout",function(e) {
+ clearHighlights();
+ });
+ } else {
+ lines1.dispatch.on('elementMouseover.tooltip', mouseover_line);
+ lines2.dispatch.on('elementMouseover.tooltip', mouseover_line);
+ lines1.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true)
+ });
+ lines2.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true)
+ });
- nv.utils.initOptions(chart);
+ scatters1.dispatch.on('elementMouseover.tooltip', mouseover_scatter);
+ scatters2.dispatch.on('elementMouseover.tooltip', mouseover_scatter);
+ scatters1.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true)
+ });
+ scatters2.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true)
+ });
- return chart;
-};
+ stack1.dispatch.on('elementMouseover.tooltip', mouseover_stack);
+ stack2.dispatch.on('elementMouseover.tooltip', mouseover_stack);
+ stack1.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true)
+ });
+ stack2.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true)
+ });
-nv.models.ohlcBar = function() {
- "use strict";
+ bars1.dispatch.on('elementMouseover.tooltip', mouseover_bar);
+ bars2.dispatch.on('elementMouseover.tooltip', mouseover_bar);
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ bars1.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true);
+ });
+ bars2.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true);
+ });
+ bars1.dispatch.on('elementMousemove.tooltip', function(evt) {
+ tooltip();
+ });
+ bars2.dispatch.on('elementMousemove.tooltip', function(evt) {
+ tooltip();
+ });
+ }
+ });
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = null
- , height = null
- , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
- , container = null
- , x = d3.scale.linear()
- , y = d3.scale.linear()
- , getX = function(d) { return d.x }
- , getY = function(d) { return d.y }
- , getOpen = function(d) { return d.open }
- , getClose = function(d) { return d.close }
- , getHigh = function(d) { return d.high }
- , getLow = function(d) { return d.low }
- , forceX = []
- , forceY = []
- , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
- , clipEdge = true
- , color = nv.utils.defaultColor()
- , interactive = false
- , xDomain
- , yDomain
- , xRange
- , yRange
- , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd', 'chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove')
- ;
+ return chart;
+ }
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Global getters and setters
+ //------------------------------------------------------------
- function chart(selection) {
- selection.each(function(data) {
- container = d3.select(this);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ chart.dispatch = dispatch;
+ chart.legend = legend;
+ chart.lines1 = lines1;
+ chart.lines2 = lines2;
+ chart.scatters1 = scatters1;
+ chart.scatters2 = scatters2;
+ chart.bars1 = bars1;
+ chart.bars2 = bars2;
+ chart.stack1 = stack1;
+ chart.stack2 = stack2;
+ chart.xAxis = xAxis;
+ chart.yAxis1 = yAxis1;
+ chart.yAxis2 = yAxis2;
+ chart.tooltip = tooltip;
+ chart.interactiveLayer = interactiveLayer;
- nv.utils.initSVG(container);
+ chart.options = nv.utils.optionsFunc.bind(chart);
- // ohlc bar width.
- var w = (availableWidth / data[0].values.length) * .9;
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ yDomain1: {get: function(){return yDomain1;}, set: function(_){yDomain1=_;}},
+ yDomain2: {get: function(){return yDomain2;}, set: function(_){yDomain2=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}},
+ legendRightAxisHint: {get: function(){return legendRightAxisHint;}, set: function(_){legendRightAxisHint=_;}},
- // Setup Scales
- x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ));
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ if (_.top !== undefined) {
+ margin.top = _.top;
+ marginTop = _.top;
+ }
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ x: {get: function(){return getX;}, set: function(_){
+ getX = _;
+ lines1.x(_);
+ lines2.x(_);
+ scatters1.x(_);
+ scatters2.x(_);
+ bars1.x(_);
+ bars2.x(_);
+ stack1.x(_);
+ stack2.x(_);
+ }},
+ y: {get: function(){return getY;}, set: function(_){
+ getY = _;
+ lines1.y(_);
+ lines2.y(_);
+ scatters1.y(_);
+ scatters2.y(_);
+ stack1.y(_);
+ stack2.y(_);
+ bars1.y(_);
+ bars2.y(_);
+ }},
+ useVoronoi: {get: function(){return useVoronoi;}, set: function(_){
+ useVoronoi=_;
+ lines1.useVoronoi(_);
+ lines2.useVoronoi(_);
+ stack1.useVoronoi(_);
+ stack2.useVoronoi(_);
+ }},
- if (padData)
- x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
- else
- x.range(xRange || [5 + w/2, availableWidth - w/2 - 5]);
+ useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
+ useInteractiveGuideline = _;
+ if (useInteractiveGuideline) {
+ lines1.interactive(false);
+ lines1.useVoronoi(false);
+ lines2.interactive(false);
+ lines2.useVoronoi(false);
+ stack1.interactive(false);
+ stack1.useVoronoi(false);
+ stack2.interactive(false);
+ stack2.useVoronoi(false);
+ scatters1.interactive(false);
+ scatters2.interactive(false);
+ }
+ }},
- y.domain(yDomain || [
- d3.min(data[0].values.map(getLow).concat(forceY)),
- d3.max(data[0].values.map(getHigh).concat(forceY))
- ]
- ).range(yRange || [availableHeight, 0]);
+ duration: {get: function(){return duration;}, set: function(_) {
+ duration = _;
+ [lines1, lines2, stack1, stack2, scatters1, scatters2, xAxis, yAxis1, yAxis2].forEach(function(model){
+ model.duration(duration);
+ });
+ }}
+ });
- // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
- if (x.domain()[0] === x.domain()[1])
- x.domain()[0] ?
- x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
- : x.domain([-1,1]);
+ nv.utils.initOptions(chart);
- if (y.domain()[0] === y.domain()[1])
- y.domain()[0] ?
- y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
- : y.domain([-1,1]);
+ return chart;
+ };
- // Setup containers and skeleton of chart
- var wrap = d3.select(this).selectAll('g.nv-wrap.nv-ohlcBar').data([data[0].values]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-ohlcBar');
- var defsEnter = wrapEnter.append('defs');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ nv.models.ohlcBar = function() {
+ "use strict";
- gEnter.append('g').attr('class', 'nv-ticks');
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = null
+ , height = null
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , container = null
+ , x = d3.scale.linear()
+ , y = d3.scale.linear()
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , getOpen = function(d) { return d.open }
+ , getClose = function(d) { return d.close }
+ , getHigh = function(d) { return d.high }
+ , getLow = function(d) { return d.low }
+ , forceX = []
+ , forceY = []
+ , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
+ , clipEdge = true
+ , color = nv.utils.defaultColor()
+ , interactive = false
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd', 'chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove')
+ ;
- container
- .on('click', function(d,i) {
- dispatch.chartClick({
- data: d,
- index: i,
- pos: d3.event,
- id: id
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ function chart(selection) {
+ selection.each(function(data) {
+ container = d3.select(this);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+
+ nv.utils.initSVG(container);
+
+ // ohlc bar width.
+ var w = (availableWidth / data[0].values.length) * .9;
+
+ // Setup Scales
+ x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ));
+
+ if (padData)
+ x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
+ else
+ x.range(xRange || [5 + w/2, availableWidth - w/2 - 5]);
+
+ y.domain(yDomain || [
+ d3.min(data[0].values.map(getLow).concat(forceY)),
+ d3.max(data[0].values.map(getHigh).concat(forceY))
+ ]
+ ).range(yRange || [availableHeight, 0]);
+
+ // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
+ if (x.domain()[0] === x.domain()[1])
+ x.domain()[0] ?
+ x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
+ : x.domain([-1,1]);
+
+ if (y.domain()[0] === y.domain()[1])
+ y.domain()[0] ?
+ y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
+ : y.domain([-1,1]);
+
+ // Setup containers and skeleton of chart
+ var wrap = d3.select(this).selectAll('g.nv-wrap.nv-ohlcBar').data([data[0].values]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-ohlcBar');
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-ticks');
+
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ container
+ .on('click', function(d,i) {
+ dispatch.chartClick({
+ data: d,
+ index: i,
+ pos: d3.event,
+ id: id
+ });
});
- });
- defsEnter.append('clipPath')
- .attr('id', 'nv-chart-clip-path-' + id)
- .append('rect');
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-chart-clip-path-' + id)
+ .append('rect');
- wrap.select('#nv-chart-clip-path-' + id + ' rect')
- .attr('width', availableWidth)
- .attr('height', availableHeight);
+ wrap.select('#nv-chart-clip-path-' + id + ' rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
- g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
+ g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
- var ticks = wrap.select('.nv-ticks').selectAll('.nv-tick')
- .data(function(d) { return d });
- ticks.exit().remove();
+ var ticks = wrap.select('.nv-ticks').selectAll('.nv-tick')
+ .data(function(d) { return d });
+ ticks.exit().remove();
- ticks.enter().append('path')
- .attr('class', function(d,i,j) { return (getOpen(d,i) > getClose(d,i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i })
- .attr('d', function(d,i) {
- return 'm0,0l0,'
- + (y(getOpen(d,i))
+ ticks.enter().append('path')
+ .attr('class', function(d,i,j) { return (getOpen(d,i) > getClose(d,i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i })
+ .attr('d', function(d,i) {
+ return 'm0,0l0,'
+ + (y(getOpen(d,i))
- y(getHigh(d,i)))
- + 'l'
- + (-w/2)
- + ',0l'
- + (w/2)
- + ',0l0,'
- + (y(getLow(d,i)) - y(getOpen(d,i)))
- + 'l0,'
- + (y(getClose(d,i))
+ + 'l'
+ + (-w/2)
+ + ',0l'
+ + (w/2)
+ + ',0l0,'
+ + (y(getLow(d,i)) - y(getOpen(d,i)))
+ + 'l0,'
+ + (y(getClose(d,i))
- y(getLow(d,i)))
- + 'l'
- + (w/2)
- + ',0l'
- + (-w/2)
- + ',0z';
- })
- .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',' + y(getHigh(d,i)) + ')'; })
- .attr('fill', function(d,i) { return color[0]; })
- .attr('stroke', function(d,i) { return color[0]; })
- .attr('x', 0 )
- .attr('y', function(d,i) { return y(Math.max(0, getY(d,i))) })
- .attr('height', function(d,i) { return Math.abs(y(getY(d,i)) - y(0)) });
+ + 'l'
+ + (w/2)
+ + ',0l'
+ + (-w/2)
+ + ',0z';
+ })
+ .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',' + y(getHigh(d,i)) + ')'; })
+ .attr('fill', function(d,i) { return color[0]; })
+ .attr('stroke', function(d,i) { return color[0]; })
+ .attr('x', 0 )
+ .attr('y', function(d,i) { return y(Math.max(0, getY(d,i))) })
+ .attr('height', function(d,i) { return Math.abs(y(getY(d,i)) - y(0)) });
- // the bar colors are controlled by CSS currently
- ticks.attr('class', function(d,i,j) {
- return (getOpen(d,i) > getClose(d,i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i;
- });
+ // the bar colors are controlled by CSS currently
+ ticks.attr('class', function(d,i,j) {
+ return (getOpen(d,i) > getClose(d,i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i;
+ });
- d3.transition(ticks)
- .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',' + y(getHigh(d,i)) + ')'; })
- .attr('d', function(d,i) {
- var w = (availableWidth / data[0].values.length) * .9;
- return 'm0,0l0,'
- + (y(getOpen(d,i))
+ d3.transition(ticks)
+ .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',' + y(getHigh(d,i)) + ')'; })
+ .attr('d', function(d,i) {
+ var w = (availableWidth / data[0].values.length) * .9;
+ return 'm0,0l0,'
+ + (y(getOpen(d,i))
- y(getHigh(d,i)))
- + 'l'
- + (-w/2)
- + ',0l'
- + (w/2)
- + ',0l0,'
- + (y(getLow(d,i))
+ + 'l'
+ + (-w/2)
+ + ',0l'
+ + (w/2)
+ + ',0l0,'
+ + (y(getLow(d,i))
- y(getOpen(d,i)))
- + 'l0,'
- + (y(getClose(d,i))
+ + 'l0,'
+ + (y(getClose(d,i))
- y(getLow(d,i)))
- + 'l'
- + (w/2)
- + ',0l'
- + (-w/2)
- + ',0z';
- });
- });
+ + 'l'
+ + (w/2)
+ + ',0l'
+ + (-w/2)
+ + ',0z';
+ });
+ });
- return chart;
- }
+ return chart;
+ }
- //Create methods to allow outside functions to highlight a specific bar.
- chart.highlightPoint = function(pointIndex, isHoverOver) {
- chart.clearHighlights();
- container.select(".nv-ohlcBar .nv-tick-0-" + pointIndex)
- .classed("hover", isHoverOver)
- ;
- };
+ //Create methods to allow outside functions to highlight a specific bar.
+ chart.highlightPoint = function(pointIndex, isHoverOver) {
+ chart.clearHighlights();
+ container.select(".nv-ohlcBar .nv-tick-0-" + pointIndex)
+ .classed("hover", isHoverOver)
+ ;
+ };
- chart.clearHighlights = function() {
- container.select(".nv-ohlcBar .nv-tick.hover")
- .classed("hover", false)
- ;
- };
+ chart.clearHighlights = function() {
+ container.select(".nv-ohlcBar .nv-tick.hover")
+ .classed("hover", false)
+ ;
+ };
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- xScale: {get: function(){return x;}, set: function(_){x=_;}},
- yScale: {get: function(){return y;}, set: function(_){y=_;}},
- xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
- yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
- xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
- yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
- forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
- forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
- padData: {get: function(){return padData;}, set: function(_){padData=_;}},
- clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
- id: {get: function(){return id;}, set: function(_){id=_;}},
- interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ padData: {get: function(){return padData;}, set: function(_){padData=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}},
- x: {get: function(){return getX;}, set: function(_){getX=_;}},
- y: {get: function(){return getY;}, set: function(_){getY=_;}},
- open: {get: function(){return getOpen();}, set: function(_){getOpen=_;}},
- close: {get: function(){return getClose();}, set: function(_){getClose=_;}},
- high: {get: function(){return getHigh;}, set: function(_){getHigh=_;}},
- low: {get: function(){return getLow;}, set: function(_){getLow=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ y: {get: function(){return getY;}, set: function(_){getY=_;}},
+ open: {get: function(){return getOpen();}, set: function(_){getOpen=_;}},
+ close: {get: function(){return getClose();}, set: function(_){getClose=_;}},
+ high: {get: function(){return getHigh;}, set: function(_){getHigh=_;}},
+ low: {get: function(){return getLow;}, set: function(_){getLow=_;}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top != undefined ? _.top : margin.top;
- margin.right = _.right != undefined ? _.right : margin.right;
- margin.bottom = _.bottom != undefined ? _.bottom : margin.bottom;
- margin.left = _.left != undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top != undefined ? _.top : margin.top;
+ margin.right = _.right != undefined ? _.right : margin.right;
+ margin.bottom = _.bottom != undefined ? _.bottom : margin.bottom;
+ margin.left = _.left != undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
- nv.utils.initOptions(chart);
- return chart;
-};
+ nv.utils.initOptions(chart);
+ return chart;
+ };
// Code adapted from Jason Davies' "Parallel Coordinates"
// http://bl.ocks.org/jasondavies/1341281
-nv.models.parallelCoordinates = function() {
- "use strict";
+ nv.models.parallelCoordinates = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var margin = {top: 30, right: 0, bottom: 10, left: 0}
- , width = null
- , height = null
- , availableWidth = null
- , availableHeight = null
- , x = d3.scale.ordinal()
- , y = {}
- , undefinedValuesLabel = "undefined values"
- , dimensionData = []
- , enabledDimensions = []
- , dimensionNames = []
- , displayBrush = true
- , color = nv.utils.defaultColor()
- , filters = []
- , active = []
- , dragging = []
- , axisWithUndefinedValues = []
- , lineTension = 1
- , foreground
- , background
- , dimensions
- , line = d3.svg.line()
- , axis = d3.svg.axis()
- , dispatch = d3.dispatch('brushstart', 'brush', 'brushEnd', 'dimensionsOrder', "stateChange", 'elementClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd', 'activeChanged')
- ;
+ var margin = {top: 30, right: 0, bottom: 10, left: 0}
+ , width = null
+ , height = null
+ , availableWidth = null
+ , availableHeight = null
+ , x = d3.scale.ordinal()
+ , y = {}
+ , undefinedValuesLabel = "undefined values"
+ , dimensionData = []
+ , enabledDimensions = []
+ , dimensionNames = []
+ , displayBrush = true
+ , color = nv.utils.defaultColor()
+ , filters = []
+ , active = []
+ , dragging = []
+ , axisWithUndefinedValues = []
+ , lineTension = 1
+ , foreground
+ , background
+ , dimensions
+ , line = d3.svg.line()
+ , axis = d3.svg.axis()
+ , dispatch = d3.dispatch('brushstart', 'brush', 'brushEnd', 'dimensionsOrder', "stateChange", 'elementClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd', 'activeChanged')
+ ;
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var renderWatch = nv.utils.renderWatch(dispatch);
+ var renderWatch = nv.utils.renderWatch(dispatch);
- function chart(selection) {
- renderWatch.reset();
- selection.each(function(data) {
- var container = d3.select(this);
- availableWidth = nv.utils.availableWidth(width, container, margin);
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var container = d3.select(this);
+ availableWidth = nv.utils.availableWidth(width, container, margin);
+ availableHeight = nv.utils.availableHeight(height, container, margin);
- nv.utils.initSVG(container);
+ nv.utils.initSVG(container);
- //Convert old data to new format (name, values)
- if (data[0].values === undefined) {
- var newData = [];
- data.forEach(function (d) {
+ //Convert old data to new format (name, values)
+ if (data[0].values === undefined) {
+ var newData = [];
+ data.forEach(function (d) {
var val = {};
var key = Object.keys(d);
key.forEach(function (k) { if (k !== "name") val[k] = d[k] });
newData.push({ key: d.name, values: val });
- });
- data = newData;
- }
+ });
+ data = newData;
+ }
- var dataValues = data.map(function (d) {return d.values});
- if (active.length === 0) {
- active = data;
- }; //set all active before first brush call
+ var dataValues = data.map(function (d) {return d.values});
+ if (active.length === 0) {
+ active = data;
+ }; //set all active before first brush call
- dimensionNames = dimensionData.sort(function (a, b) { return a.currentPosition - b.currentPosition; }).map(function (d) { return d.key });
- enabledDimensions = dimensionData.filter(function (d) { return !d.disabled; });
+ dimensionNames = dimensionData.sort(function (a, b) { return a.currentPosition - b.currentPosition; }).map(function (d) { return d.key });
+ enabledDimensions = dimensionData.filter(function (d) { return !d.disabled; });
- // Setup Scales
- x.rangePoints([0, availableWidth], 1).domain(enabledDimensions.map(function (d) { return d.key; }));
+ // Setup Scales
+ x.rangePoints([0, availableWidth], 1).domain(enabledDimensions.map(function (d) { return d.key; }));
- //Set as true if all values on an axis are missing.
- // Extract the list of dimensions and create a scale for each.
- var oldDomainMaxValue = {};
- var displayMissingValuesline = false;
- var currentTicks = [];
+ //Set as true if all values on an axis are missing.
+ // Extract the list of dimensions and create a scale for each.
+ var oldDomainMaxValue = {};
+ var displayMissingValuesline = false;
+ var currentTicks = [];
- dimensionNames.forEach(function(d) {
- var extent = d3.extent(dataValues, function (p) { return +p[d]; });
- var min = extent[0];
- var max = extent[1];
- var onlyUndefinedValues = false;
- //If there is no values to display on an axis, set the extent to 0
- if (isNaN(min) || isNaN(max)) {
- onlyUndefinedValues = true;
- min = 0;
- max = 0;
- }
- //Scale axis if there is only one value
- if (min === max) {
- min = min - 1;
- max = max + 1;
- }
- var f = filters.filter(function (k) { return k.dimension == d; });
- if (f.length !== 0) {
- //If there is only NaN values, keep the existing domain.
- if (onlyUndefinedValues) {
- min = y[d].domain()[0];
- max = y[d].domain()[1];
+ dimensionNames.forEach(function(d) {
+ var extent = d3.extent(dataValues, function (p) { return +p[d]; });
+ var min = extent[0];
+ var max = extent[1];
+ var onlyUndefinedValues = false;
+ //If there is no values to display on an axis, set the extent to 0
+ if (isNaN(min) || isNaN(max)) {
+ onlyUndefinedValues = true;
+ min = 0;
+ max = 0;
}
- //If the brush extent is > max (< min), keep the extent value.
- else if (!f[0].hasOnlyNaN && displayBrush) {
- min = min > f[0].extent[0] ? f[0].extent[0] : min;
- max = max < f[0].extent[1] ? f[0].extent[1] : max;
+ //Scale axis if there is only one value
+ if (min === max) {
+ min = min - 1;
+ max = max + 1;
}
+ var f = filters.filter(function (k) { return k.dimension == d; });
+ if (f.length !== 0) {
+ //If there is only NaN values, keep the existing domain.
+ if (onlyUndefinedValues) {
+ min = y[d].domain()[0];
+ max = y[d].domain()[1];
+ }
+ //If the brush extent is > max (< min), keep the extent value.
+ else if (!f[0].hasOnlyNaN && displayBrush) {
+ min = min > f[0].extent[0] ? f[0].extent[0] : min;
+ max = max < f[0].extent[1] ? f[0].extent[1] : max;
+ }
//If there is NaN values brushed be sure the brush extent is on the domain.
- else if (f[0].hasNaN) {
- max = max < f[0].extent[1] ? f[0].extent[1] : max;
- oldDomainMaxValue[d] = y[d].domain()[1];
- displayMissingValuesline = true;
+ else if (f[0].hasNaN) {
+ max = max < f[0].extent[1] ? f[0].extent[1] : max;
+ oldDomainMaxValue[d] = y[d].domain()[1];
+ displayMissingValuesline = true;
+ }
}
- }
- //Use 90% of (availableHeight - 12) for the axis range, 12 reprensenting the space necessary to display "undefined values" text.
- //The remaining 10% are used to display the missingValue line.
- y[d] = d3.scale.linear()
- .domain([min, max])
- .range([(availableHeight - 12) * 0.9, 0]);
+ //Use 90% of (availableHeight - 12) for the axis range, 12 reprensenting the space necessary to display "undefined values" text.
+ //The remaining 10% are used to display the missingValue line.
+ y[d] = d3.scale.linear()
+ .domain([min, max])
+ .range([(availableHeight - 12) * 0.9, 0]);
- axisWithUndefinedValues = [];
- y[d].brush = d3.svg.brush().y(y[d]).on('brushstart', brushstart).on('brush', brush).on('brushend', brushend);
- });
+ axisWithUndefinedValues = [];
+ y[d].brush = d3.svg.brush().y(y[d]).on('brushstart', brushstart).on('brush', brush).on('brushend', brushend);
+ });
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-parallelCoordinates').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-parallelCoordinates');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-parallelCoordinates').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-parallelCoordinates');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- gEnter.append('g').attr('class', 'nv-parallelCoordinates background');
- gEnter.append('g').attr('class', 'nv-parallelCoordinates foreground');
- gEnter.append('g').attr('class', 'nv-parallelCoordinates missingValuesline');
+ gEnter.append('g').attr('class', 'nv-parallelCoordinates background');
+ gEnter.append('g').attr('class', 'nv-parallelCoordinates foreground');
+ gEnter.append('g').attr('class', 'nv-parallelCoordinates missingValuesline');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- line.interpolate('cardinal').tension(lineTension);
- axis.orient('left');
- var axisDrag = d3.behavior.drag()
- .on('dragstart', dragStart)
- .on('drag', dragMove)
- .on('dragend', dragEnd);
+ line.interpolate('cardinal').tension(lineTension);
+ axis.orient('left');
+ var axisDrag = d3.behavior.drag()
+ .on('dragstart', dragStart)
+ .on('drag', dragMove)
+ .on('dragend', dragEnd);
- //Add missing value line at the bottom of the chart
- var missingValuesline, missingValueslineText;
- var step = x.range()[1] - x.range()[0];
- if (!isNaN(step)) {
- var lineData = [0 + step / 2, availableHeight - 12, availableWidth - step / 2, availableHeight - 12];
- missingValuesline = wrap.select('.missingValuesline').selectAll('line').data([lineData]);
- missingValuesline.enter().append('line');
- missingValuesline.exit().remove();
- missingValuesline.attr("x1", function(d) { return d[0]; })
+ //Add missing value line at the bottom of the chart
+ var missingValuesline, missingValueslineText;
+ var step = x.range()[1] - x.range()[0];
+ step = isNaN(step) ? x.range()[0] : step;
+ if (!isNaN(step)) {
+ var lineData = [0 + step / 2, availableHeight - 12, availableWidth - step / 2, availableHeight - 12];
+ missingValuesline = wrap.select('.missingValuesline').selectAll('line').data([lineData]);
+ missingValuesline.enter().append('line');
+ missingValuesline.exit().remove();
+ missingValuesline.attr("x1", function(d) { return d[0]; })
.attr("y1", function(d) { return d[1]; })
.attr("x2", function(d) { return d[2]; })
.attr("y2", function(d) { return d[3]; });
- //Add the text "undefined values" under the missing value line
- missingValueslineText = wrap.select('.missingValuesline').selectAll('text').data([undefinedValuesLabel]);
- missingValueslineText.append('text').data([undefinedValuesLabel]);
- missingValueslineText.enter().append('text');
- missingValueslineText.exit().remove();
- missingValueslineText.attr("y", availableHeight)
- //To have the text right align with the missingValues line, substract 92 representing the text size.
+ //Add the text "undefined values" under the missing value line
+ missingValueslineText = wrap.select('.missingValuesline').selectAll('text').data([undefinedValuesLabel]);
+ missingValueslineText.append('text').data([undefinedValuesLabel]);
+ missingValueslineText.enter().append('text');
+ missingValueslineText.exit().remove();
+ missingValueslineText.attr("y", availableHeight)
+ //To have the text right align with the missingValues line, substract 92 representing the text size.
.attr("x", availableWidth - 92 - step / 2)
.text(function(d) { return d; });
- }
- // Add grey background lines for context.
- background = wrap.select('.background').selectAll('path').data(data);
- background.enter().append('path');
- background.exit().remove();
- background.attr('d', path);
+ }
+ // Add grey background lines for context.
+ background = wrap.select('.background').selectAll('path').data(data);
+ background.enter().append('path');
+ background.exit().remove();
+ background.attr('d', path);
- // Add blue foreground lines for focus.
- foreground = wrap.select('.foreground').selectAll('path').data(data);
- foreground.enter().append('path')
- foreground.exit().remove();
- foreground.attr('d', path)
- .style("stroke-width", function (d, i) {
- if (isNaN(d.strokeWidth)) { d.strokeWidth = 1;} return d.strokeWidth;})
- .attr('stroke', function (d, i) { return d.color || color(d, i); });
- foreground.on("mouseover", function (d, i) {
- d3.select(this).classed('hover', true).style("stroke-width", d.strokeWidth + 2 + "px").style("stroke-opacity", 1);
- dispatch.elementMouseover({
- label: d.name,
- color: d.color || color(d, i),
- values: d.values,
- dimensions: enabledDimensions
- });
-
- });
- foreground.on("mouseout", function (d, i) {
- d3.select(this).classed('hover', false).style("stroke-width", d.strokeWidth + "px").style("stroke-opacity", 0.7);
- dispatch.elementMouseout({
- label: d.name,
- index: i
- });
- });
- foreground.on('mousemove', function (d, i) {
- dispatch.elementMousemove();
- });
- foreground.on('click', function (d) {
- dispatch.elementClick({
- id: d.id
- });
- });
- // Add a group element for each dimension.
- dimensions = g.selectAll('.dimension').data(enabledDimensions);
- var dimensionsEnter = dimensions.enter().append('g').attr('class', 'nv-parallelCoordinates dimension');
-
- dimensions.attr('transform', function(d) { return 'translate(' + x(d.key) + ',0)'; });
- dimensionsEnter.append('g').attr('class', 'nv-axis');
-
- // Add an axis and title.
- dimensionsEnter.append('text')
- .attr('class', 'nv-label')
- .style("cursor", "move")
- .attr('dy', '-1em')
- .attr('text-anchor', 'middle')
- .on("mouseover", function(d, i) {
+ // Add blue foreground lines for focus.
+ foreground = wrap.select('.foreground').selectAll('path').data(data);
+ foreground.enter().append('path')
+ foreground.exit().remove();
+ foreground.attr('d', path)
+ .style("stroke-width", function (d, i) {
+ if (isNaN(d.strokeWidth)) { d.strokeWidth = 1;} return d.strokeWidth;})
+ .attr('stroke', function (d, i) { return d.color || color(d, i); });
+ foreground.on("mouseover", function (d, i) {
+ d3.select(this).classed('hover', true).style("stroke-width", d.strokeWidth + 2 + "px").style("stroke-opacity", 1);
dispatch.elementMouseover({
- label: d.tooltip || d.key,
- color: d.color
+ label: d.name,
+ color: d.color || color(d, i),
+ values: d.values,
+ dimensions: enabledDimensions
});
- })
- .on("mouseout", function(d, i) {
+
+ });
+ foreground.on("mouseout", function (d, i) {
+ d3.select(this).classed('hover', false).style("stroke-width", d.strokeWidth + "px").style("stroke-opacity", 0.7);
dispatch.elementMouseout({
- label: d.tooltip
+ label: d.name,
+ index: i
});
- })
- .on('mousemove', function (d, i) {
+ });
+ foreground.on('mousemove', function (d, i) {
dispatch.elementMousemove();
- })
- .call(axisDrag);
+ });
+ foreground.on('click', function (d) {
+ dispatch.elementClick({
+ id: d.id
+ });
+ });
+ // Add a group element for each dimension.
+ dimensions = g.selectAll('.dimension').data(enabledDimensions);
+ var dimensionsEnter = dimensions.enter().append('g').attr('class', 'nv-parallelCoordinates dimension');
- dimensionsEnter.append('g').attr('class', 'nv-brushBackground');
- dimensions.exit().remove();
- dimensions.select('.nv-label').text(function (d) { return d.key });
+ dimensions.attr('transform', function(d) { return 'translate(' + x(d.key) + ',0)'; });
+ dimensionsEnter.append('g').attr('class', 'nv-axis');
- // Add and store a brush for each axis.
- restoreBrush(displayBrush);
+ // Add an axis and title.
+ dimensionsEnter.append('text')
+ .attr('class', 'nv-label')
+ .style("cursor", "move")
+ .attr('dy', '-1em')
+ .attr('text-anchor', 'middle')
+ .on("mouseover", function(d, i) {
+ dispatch.elementMouseover({
+ label: d.tooltip || d.key,
+ color: d.color
+ });
+ })
+ .on("mouseout", function(d, i) {
+ dispatch.elementMouseout({
+ label: d.tooltip
+ });
+ })
+ .on('mousemove', function (d, i) {
+ dispatch.elementMousemove();
+ })
+ .call(axisDrag);
- var actives = dimensionNames.filter(function (p) { return !y[p].brush.empty(); }),
+ dimensionsEnter.append('g').attr('class', 'nv-brushBackground');
+ dimensions.exit().remove();
+ dimensions.select('.nv-label').text(function (d) { return d.key });
+
+ // Add and store a brush for each axis.
+ restoreBrush(displayBrush);
+
+ var actives = dimensionNames.filter(function (p) { return !y[p].brush.empty(); }),
extents = actives.map(function (p) { return y[p].brush.extent(); });
- var formerActive = active.slice(0);
+ var formerActive = active.slice(0);
- //Restore active values
- active = [];
- foreground.style("display", function (d) {
- var isActive = actives.every(function (p, i) {
- if ((isNaN(d.values[p]) || isNaN(parseFloat(d.values[p]))) && extents[i][0] == y[p].brush.y().domain()[0]) {
- return true;
- }
- return (extents[i][0] <= d.values[p] && d.values[p] <= extents[i][1]) && !isNaN(parseFloat(d.values[p]));
+ //Restore active values
+ active = [];
+ foreground.style("display", function (d) {
+ var isActive = actives.every(function (p, i) {
+ if ((isNaN(d.values[p]) || isNaN(parseFloat(d.values[p]))) && extents[i][0] == y[p].brush.y().domain()[0]) {
+ return true;
+ }
+ return (extents[i][0] <= d.values[p] && d.values[p] <= extents[i][1]) && !isNaN(parseFloat(d.values[p]));
+ });
+ if (isActive)
+ active.push(d);
+ return !isActive ? "none" : null;
+
});
- if (isActive)
- active.push(d);
- return !isActive ? "none" : null;
- });
+ if (filters.length > 0 || !nv.utils.arrayEquals(active, formerActive)) {
+ dispatch.activeChanged(active);
+ }
- if (filters.length > 0 || !nv.utils.arrayEquals(active, formerActive)) {
- dispatch.activeChanged(active);
- }
+ // Returns the path for a given data point.
+ function path(d) {
+ return line(enabledDimensions.map(function (p) {
+ //If value if missing, put the value on the missing value line
+ if (isNaN(d.values[p.key]) || isNaN(parseFloat(d.values[p.key])) || displayMissingValuesline) {
+ var domain = y[p.key].domain();
+ var range = y[p.key].range();
+ var min = domain[0] - (domain[1] - domain[0]) / 9;
- // Returns the path for a given data point.
- function path(d) {
- return line(enabledDimensions.map(function (p) {
- //If value if missing, put the value on the missing value line
- if (isNaN(d.values[p.key]) || isNaN(parseFloat(d.values[p.key])) || displayMissingValuesline) {
- var domain = y[p.key].domain();
- var range = y[p.key].range();
- var min = domain[0] - (domain[1] - domain[0]) / 9;
+ //If it's not already the case, allow brush to select undefined values
+ if (axisWithUndefinedValues.indexOf(p.key) < 0) {
- //If it's not already the case, allow brush to select undefined values
- if (axisWithUndefinedValues.indexOf(p.key) < 0) {
+ var newscale = d3.scale.linear().domain([min, domain[1]]).range([availableHeight - 12, range[1]]);
+ y[p.key].brush.y(newscale);
+ axisWithUndefinedValues.push(p.key);
+ }
+ if (isNaN(d.values[p.key]) || isNaN(parseFloat(d.values[p.key]))) {
+ return [x(p.key), y[p.key](min)];
+ }
+ }
- var newscale = d3.scale.linear().domain([min, domain[1]]).range([availableHeight - 12, range[1]]);
- y[p.key].brush.y(newscale);
- axisWithUndefinedValues.push(p.key);
+ //If parallelCoordinate contain missing values show the missing values line otherwise, hide it.
+ if (missingValuesline !== undefined) {
+ if (axisWithUndefinedValues.length > 0 || displayMissingValuesline) {
+ missingValuesline.style("display", "inline");
+ missingValueslineText.style("display", "inline");
+ } else {
+ missingValuesline.style("display", "none");
+ missingValueslineText.style("display", "none");
+ }
}
- if (isNaN(d.values[p.key]) || isNaN(parseFloat(d.values[p.key]))) {
- return [x(p.key), y[p.key](min)];
- }
- }
+ return [x(p.key), y[p.key](d.values[p.key])];
+ }));
+ }
- //If parallelCoordinate contain missing values show the missing values line otherwise, hide it.
- if (missingValuesline !== undefined) {
- if (axisWithUndefinedValues.length > 0 || displayMissingValuesline) {
- missingValuesline.style("display", "inline");
- missingValueslineText.style("display", "inline");
- } else {
- missingValuesline.style("display", "none");
- missingValueslineText.style("display", "none");
+ function restoreBrush(visible) {
+ filters.forEach(function (f) {
+ //If filter brushed NaN values, keep the brush on the bottom of the axis.
+ var brushDomain = y[f.dimension].brush.y().domain();
+ if (f.hasOnlyNaN) {
+ f.extent[1] = (y[f.dimension].domain()[1] - brushDomain[0]) * (f.extent[1] - f.extent[0]) / (oldDomainMaxValue[f.dimension] - f.extent[0]) + brushDomain[0];
}
- }
- return [x(p.key), y[p.key](d.values[p.key])];
- }));
- }
+ if (f.hasNaN) {
+ f.extent[0] = brushDomain[0];
+ }
+ if (visible)
+ y[f.dimension].brush.extent(f.extent);
+ });
- function restoreBrush(visible) {
- filters.forEach(function (f) {
- //If filter brushed NaN values, keep the brush on the bottom of the axis.
- var brushDomain = y[f.dimension].brush.y().domain();
- if (f.hasOnlyNaN) {
- f.extent[1] = (y[f.dimension].domain()[1] - brushDomain[0]) * (f.extent[1] - f.extent[0]) / (oldDomainMaxValue[f.dimension] - f.extent[0]) + brushDomain[0];
- }
- if (f.hasNaN) {
- f.extent[0] = brushDomain[0];
- }
- if (visible)
- y[f.dimension].brush.extent(f.extent);
- });
+ dimensions.select('.nv-brushBackground')
+ .each(function (d) {
+ d3.select(this).call(y[d.key].brush);
- dimensions.select('.nv-brushBackground')
- .each(function (d) {
- d3.select(this).call(y[d.key].brush);
+ })
+ .selectAll('rect')
+ .attr('x', -8)
+ .attr('width', 16);
- })
- .selectAll('rect')
- .attr('x', -8)
- .attr('width', 16);
+ updateTicks();
+ }
- updateTicks();
- }
-
- // Handles a brush event, toggling the display of foreground lines.
- function brushstart() {
- //If brush aren't visible, show it before brushing again.
- if (displayBrush === false) {
- displayBrush = true;
- restoreBrush(true);
+ // Handles a brush event, toggling the display of foreground lines.
+ function brushstart() {
+ //If brush aren't visible, show it before brushing again.
+ if (displayBrush === false) {
+ displayBrush = true;
+ restoreBrush(true);
+ }
}
- }
- // Handles a brush event, toggling the display of foreground lines.
- function brush() {
- actives = dimensionNames.filter(function (p) { return !y[p].brush.empty(); });
- extents = actives.map(function(p) { return y[p].brush.extent(); });
+ // Handles a brush event, toggling the display of foreground lines.
+ function brush() {
+ actives = dimensionNames.filter(function (p) { return !y[p].brush.empty(); });
+ extents = actives.map(function(p) { return y[p].brush.extent(); });
- filters = []; //erase current filters
- actives.forEach(function(d,i) {
- filters[i] = {
- dimension: d,
- extent: extents[i],
- hasNaN: false,
- hasOnlyNaN: false
- }
- });
+ filters = []; //erase current filters
+ actives.forEach(function(d,i) {
+ filters[i] = {
+ dimension: d,
+ extent: extents[i],
+ hasNaN: false,
+ hasOnlyNaN: false
+ }
+ });
- active = []; //erase current active list
- foreground.style('display', function(d) {
- var isActive = actives.every(function(p, i) {
- if ((isNaN(d.values[p]) || isNaN(parseFloat(d.values[p]))) && extents[i][0] == y[p].brush.y().domain()[0]) return true;
- return (extents[i][0] <= d.values[p] && d.values[p] <= extents[i][1]) && !isNaN(parseFloat(d.values[p]));
+ active = []; //erase current active list
+ foreground.style('display', function(d) {
+ var isActive = actives.every(function(p, i) {
+ if ((isNaN(d.values[p]) || isNaN(parseFloat(d.values[p]))) && extents[i][0] == y[p].brush.y().domain()[0]) return true;
+ return (extents[i][0] <= d.values[p] && d.values[p] <= extents[i][1]) && !isNaN(parseFloat(d.values[p]));
+ });
+ if (isActive) active.push(d);
+ return isActive ? null : 'none';
});
- if (isActive) active.push(d);
- return isActive ? null : 'none';
- });
- updateTicks();
+ updateTicks();
- dispatch.brush({
- filters: filters,
- active: active
- });
- }
- function brushend() {
- var hasActiveBrush = actives.length > 0 ? true : false;
- filters.forEach(function (f) {
- if (f.extent[0] === y[f.dimension].brush.y().domain()[0] && axisWithUndefinedValues.indexOf(f.dimension) >= 0)
- f.hasNaN = true;
- if (f.extent[1] < y[f.dimension].domain()[0])
- f.hasOnlyNaN = true;
- });
- dispatch.brushEnd(active, hasActiveBrush);
- }
- function updateTicks() {
- dimensions.select('.nv-axis')
- .each(function (d, i) {
- var f = filters.filter(function (k) { return k.dimension == d.key; });
- currentTicks[d.key] = y[d.key].domain();
+ dispatch.brush({
+ filters: filters,
+ active: active
+ });
+ }
+ function brushend() {
+ var hasActiveBrush = actives.length > 0 ? true : false;
+ filters.forEach(function (f) {
+ if (f.extent[0] === y[f.dimension].brush.y().domain()[0] && axisWithUndefinedValues.indexOf(f.dimension) >= 0)
+ f.hasNaN = true;
+ if (f.extent[1] < y[f.dimension].domain()[0])
+ f.hasOnlyNaN = true;
+ });
+ dispatch.brushEnd(active, hasActiveBrush);
+ }
+ function updateTicks() {
+ dimensions.select('.nv-axis')
+ .each(function (d, i) {
+ var f = filters.filter(function (k) { return k.dimension == d.key; });
+ currentTicks[d.key] = y[d.key].domain();
- //If brush are available, display brush extent
- if (f.length != 0 && displayBrush)
- {
- currentTicks[d.key] = [];
- if (f[0].extent[1] > y[d.key].domain()[0])
- currentTicks[d.key] = [f[0].extent[1]];
- if (f[0].extent[0] >= y[d.key].domain()[0])
- currentTicks[d.key].push(f[0].extent[0]);
- }
+ //If brush are available, display brush extent
+ if (f.length != 0 && displayBrush)
+ {
+ currentTicks[d.key] = [];
+ if (f[0].extent[1] > y[d.key].domain()[0])
+ currentTicks[d.key] = [f[0].extent[1]];
+ if (f[0].extent[0] >= y[d.key].domain()[0])
+ currentTicks[d.key].push(f[0].extent[0]);
+ }
- d3.select(this).call(axis.scale(y[d.key]).tickFormat(d.format).tickValues(currentTicks[d.key]));
- });
- }
- function dragStart(d) {
- dragging[d.key] = this.parentNode.__origin__ = x(d.key);
- background.attr("visibility", "hidden");
- }
- function dragMove(d) {
- dragging[d.key] = Math.min(availableWidth, Math.max(0, this.parentNode.__origin__ += d3.event.x));
- foreground.attr("d", path);
- enabledDimensions.sort(function (a, b) { return dimensionPosition(a.key) - dimensionPosition(b.key); });
- enabledDimensions.forEach(function (d, i) { return d.currentPosition = i; });
- x.domain(enabledDimensions.map(function (d) { return d.key; }));
- dimensions.attr("transform", function(d) { return "translate(" + dimensionPosition(d.key) + ")"; });
- }
- function dragEnd(d, i) {
- delete this.parentNode.__origin__;
- delete dragging[d.key];
- d3.select(this.parentNode).attr("transform", "translate(" + x(d.key) + ")");
- foreground
- .attr("d", path);
- background
- .attr("d", path)
- .attr("visibility", null);
+ d3.select(this).call(axis.scale(y[d.key]).tickFormat(d.format).tickValues(currentTicks[d.key]));
+ });
+ }
+ function dragStart(d) {
+ dragging[d.key] = this.parentNode.__origin__ = x(d.key);
+ background.attr("visibility", "hidden");
+ }
+ function dragMove(d) {
+ dragging[d.key] = Math.min(availableWidth, Math.max(0, this.parentNode.__origin__ += d3.event.x));
+ foreground.attr("d", path);
+ enabledDimensions.sort(function (a, b) { return dimensionPosition(a.key) - dimensionPosition(b.key); });
+ enabledDimensions.forEach(function (d, i) { return d.currentPosition = i; });
+ x.domain(enabledDimensions.map(function (d) { return d.key; }));
+ dimensions.attr("transform", function(d) { return "translate(" + dimensionPosition(d.key) + ")"; });
+ }
+ function dragEnd(d, i) {
+ delete this.parentNode.__origin__;
+ delete dragging[d.key];
+ d3.select(this.parentNode).attr("transform", "translate(" + x(d.key) + ")");
+ foreground
+ .attr("d", path);
+ background
+ .attr("d", path)
+ .attr("visibility", null);
- dispatch.dimensionsOrder(enabledDimensions);
- }
- function dimensionPosition(d) {
- var v = dragging[d];
- return v == null ? x(d) : v;
- }
- });
- return chart;
- }
+ dispatch.dimensionsOrder(enabledDimensions);
+ }
+ function dimensionPosition(d) {
+ var v = dragging[d];
+ return v == null ? x(d) : v;
+ }
+ });
+ return chart;
+ }
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width= _;}},
- height: {get: function(){return height;}, set: function(_){height= _;}},
- dimensionData: { get: function () { return dimensionData; }, set: function (_) { dimensionData = _; } },
- displayBrush: { get: function () { return displayBrush; }, set: function (_) { displayBrush = _; } },
- filters: { get: function () { return filters; }, set: function (_) { filters = _; } },
- active: { get: function () { return active; }, set: function (_) { active = _; } },
- lineTension: {get: function(){return lineTension;}, set: function(_){lineTension = _;}},
- undefinedValuesLabel : {get: function(){return undefinedValuesLabel;}, set: function(_){undefinedValuesLabel=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width= _;}},
+ height: {get: function(){return height;}, set: function(_){height= _;}},
+ dimensionData: { get: function () { return dimensionData; }, set: function (_) { dimensionData = _; } },
+ displayBrush: { get: function () { return displayBrush; }, set: function (_) { displayBrush = _; } },
+ filters: { get: function () { return filters; }, set: function (_) { filters = _; } },
+ active: { get: function () { return active; }, set: function (_) { active = _; } },
+ lineTension: {get: function(){return lineTension;}, set: function(_){lineTension = _;}},
+ undefinedValuesLabel : {get: function(){return undefinedValuesLabel;}, set: function(_){undefinedValuesLabel=_;}},
- // deprecated options
- dimensions: {get: function () { return dimensionData.map(function (d){return d.key}); }, set: function (_) {
- // deprecated after 1.8.1
- nv.deprecated('dimensions', 'use dimensionData instead');
- if (dimensionData.length === 0) {
- _.forEach(function (k) { dimensionData.push({ key: k }) })
- } else {
- _.forEach(function (k, i) { dimensionData[i].key= k })
- }
- }},
- dimensionNames: {get: function () { return dimensionData.map(function (d){return d.key}); }, set: function (_) {
- // deprecated after 1.8.1
- nv.deprecated('dimensionNames', 'use dimensionData instead');
- dimensionNames = [];
- if (dimensionData.length === 0) {
- _.forEach(function (k) { dimensionData.push({ key: k }) })
- } else {
- _.forEach(function (k, i) { dimensionData[i].key = k })
- }
+ // deprecated options
+ dimensions: {get: function () { return dimensionData.map(function (d){return d.key}); }, set: function (_) {
+ // deprecated after 1.8.1
+ nv.deprecated('dimensions', 'use dimensionData instead');
+ if (dimensionData.length === 0) {
+ _.forEach(function (k) { dimensionData.push({ key: k }) })
+ } else {
+ _.forEach(function (k, i) { dimensionData[i].key= k })
+ }
+ }},
+ dimensionNames: {get: function () { return dimensionData.map(function (d){return d.key}); }, set: function (_) {
+ // deprecated after 1.8.1
+ nv.deprecated('dimensionNames', 'use dimensionData instead');
+ dimensionNames = [];
+ if (dimensionData.length === 0) {
+ _.forEach(function (k) { dimensionData.push({ key: k }) })
+ } else {
+ _.forEach(function (k, i) { dimensionData[i].key = k })
+ }
- }},
- dimensionFormats: {get: function () { return dimensionData.map(function (d) { return d.format }); }, set: function (_) {
- // deprecated after 1.8.1
- nv.deprecated('dimensionFormats', 'use dimensionData instead');
- if (dimensionData.length === 0) {
- _.forEach(function (f) { dimensionData.push({ format: f }) })
- } else {
- _.forEach(function (f, i) { dimensionData[i].format = f })
- }
+ }},
+ dimensionFormats: {get: function () { return dimensionData.map(function (d) { return d.format }); }, set: function (_) {
+ // deprecated after 1.8.1
+ nv.deprecated('dimensionFormats', 'use dimensionData instead');
+ if (dimensionData.length === 0) {
+ _.forEach(function (f) { dimensionData.push({ format: f }) })
+ } else {
+ _.forEach(function (f, i) { dimensionData[i].format = f })
+ }
- }},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }}
- });
- nv.utils.initOptions(chart);
- return chart;
-};
-nv.models.parallelCoordinatesChart = function () {
+ }},
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
+ nv.utils.initOptions(chart);
+ return chart;
+ };
+ nv.models.parallelCoordinatesChart = function () {
"use strict";
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
@@ -10861,27 +10956,28 @@
var legend = nv.models.legend()
var tooltip = nv.models.tooltip();
var dimensionTooltip = nv.models.tooltip();
var margin = { top: 0, right: 0, bottom: 0, left: 0 }
- , width = null
- , height = null
- , showLegend = true
- , color = nv.utils.defaultColor()
- , state = nv.utils.state()
- , dimensionData = []
- , displayBrush = true
- , defaultState = null
- , noData = null
- , nanValue = "undefined"
- , dispatch = d3.dispatch('dimensionsOrder', 'brushEnd', 'stateChange', 'changeState', 'renderEnd')
- , controlWidth = function () { return showControls ? 180 : 0 }
- ;
+ , marginTop = null
+ , width = null
+ , height = null
+ , showLegend = true
+ , color = nv.utils.defaultColor()
+ , state = nv.utils.state()
+ , dimensionData = []
+ , displayBrush = true
+ , defaultState = null
+ , noData = null
+ , nanValue = "undefined"
+ , dispatch = d3.dispatch('dimensionsOrder', 'brushEnd', 'stateChange', 'changeState', 'renderEnd')
+ , controlWidth = function () { return showControls ? 180 : 0 }
+ ;
- //============================================================
+ //============================================================
- //============================================================
+ //============================================================
// Private Variables
//------------------------------------------------------------
var renderWatch = nv.utils.renderWatch(dispatch);
@@ -10949,11 +11045,11 @@
dimensionData.forEach(function (d, i) {
d.originalPosition = isNaN(d.originalPosition) ? i : d.originalPosition;
d.currentPosition = isNaN(d.currentPosition) ? i : d.currentPosition;
});
- if (!defaultState) {
+ if (!defaultState) {
var key;
defaultState = {};
for(key in state) {
if(state[key] instanceof Array)
defaultState[key] = state[key].slice(0);
@@ -10994,66 +11090,66 @@
g.select('.nv-legendWrap')
.datum(dimensionData.sort(function (a, b) { return a.originalPosition - b.originalPosition; }))
.call(legend);
- if (legend.height() > margin.top) {
+ if (!marginTop && legend.height() !== margin.top) {
margin.top = legend.height();
availableHeight = nv.utils.availableHeight(height, container, margin);
}
wrap.select('.nv-legendWrap')
- .attr('transform', 'translate( 0 ,' + (-margin.top) + ')');
+ .attr('transform', 'translate( 0 ,' + (-margin.top) + ')');
}
wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// Main Chart Component(s)
parallelCoordinates
.width(availableWidth)
.height(availableHeight)
.dimensionData(dimensionData)
.displayBrush(displayBrush);
- var parallelCoordinatesWrap = g.select('.nv-parallelCoordinatesWrap ')
- .datum(data);
+ var parallelCoordinatesWrap = g.select('.nv-parallelCoordinatesWrap ')
+ .datum(data);
- parallelCoordinatesWrap.transition().call(parallelCoordinates);
+ parallelCoordinatesWrap.transition().call(parallelCoordinates);
- //============================================================
+ //============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
//Display reset brush button
- parallelCoordinates.dispatch.on('brushEnd', function (active, hasActiveBrush) {
- if (hasActiveBrush) {
- displayBrush = true;
- dispatch.brushEnd(active);
- } else {
+ parallelCoordinates.dispatch.on('brushEnd', function (active, hasActiveBrush) {
+ if (hasActiveBrush) {
+ displayBrush = true;
+ dispatch.brushEnd(active);
+ } else {
- displayBrush = false;
- }
- });
+ displayBrush = false;
+ }
+ });
- legend.dispatch.on('stateChange', function(newState) {
- for(var key in newState) {
- state[key] = newState[key];
- }
- dispatch.stateChange(state);
- chart.update();
- });
+ legend.dispatch.on('stateChange', function(newState) {
+ for(var key in newState) {
+ state[key] = newState[key];
+ }
+ dispatch.stateChange(state);
+ chart.update();
+ });
//Update dimensions order and display reset sorting button
- parallelCoordinates.dispatch.on('dimensionsOrder', function (e) {
- dimensionData.sort(function (a, b) { return a.currentPosition - b.currentPosition; });
- var isSorted = false;
- dimensionData.forEach(function (d, i) {
- d.currentPosition = i;
- if (d.currentPosition !== d.originalPosition)
- isSorted = true;
- });
- dispatch.dimensionsOrder(dimensionData, isSorted);
- });
+ parallelCoordinates.dispatch.on('dimensionsOrder', function (e) {
+ dimensionData.sort(function (a, b) { return a.currentPosition - b.currentPosition; });
+ var isSorted = false;
+ dimensionData.forEach(function (d, i) {
+ d.currentPosition = i;
+ if (d.currentPosition !== d.originalPosition)
+ isSorted = true;
+ });
+ dispatch.dimensionsOrder(dimensionData, isSorted);
+ });
- // Update chart from a state object passed to event handler
+ // Update chart from a state object passed to event handler
dispatch.on('changeState', function (e) {
if (typeof e.disabled !== 'undefined') {
dimensionData.forEach(function (series, i) {
series.disabled = e.disabled[i];
@@ -11066,20 +11162,20 @@
renderWatch.renderEnd('parraleleCoordinateChart immediate');
return chart;
}
- //============================================================
+ //============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
parallelCoordinates.dispatch.on('elementMouseover.tooltip', function (evt) {
var tp = {
key: evt.label,
color: evt.color,
series: []
- }
+ }
if(evt.values){
Object.keys(evt.values).forEach(function (d) {
var dim = evt.dimensions.filter(function (dd) {return dd.key === d;})[0];
if(dim){
var v;
@@ -11090,26 +11186,26 @@
}
tp.series.push({ idx: dim.currentPosition, key: d, value: v, color: dim.color });
}
});
tp.series.sort(function(a,b) {return a.idx - b.idx});
- }
+ }
tooltip.data(tp).hidden(false);
});
parallelCoordinates.dispatch.on('elementMouseout.tooltip', function(evt) {
tooltip.hidden(true)
});
parallelCoordinates.dispatch.on('elementMousemove.tooltip', function () {
tooltip();
});
- //============================================================
+ //============================================================
// Expose Public Variables
//------------------------------------------------------------
- // expose chart's sub-components
+ // expose chart's sub-components
chart.dispatch = dispatch;
chart.parallelCoordinates = parallelCoordinates;
chart.legend = legend;
chart.tooltip = tooltip;
chart.options = nv.utils.optionsFunc.bind(chart);
@@ -11127,266 +11223,308 @@
// options that require extra logic in the setter
margin: {
get: function () { return margin; },
set: function (_) {
- margin.top = _.top !== undefined ? _.top : margin.top;
+ if (_.top !== undefined) {
+ margin.top = _.top;
+ marginTop = _.top;
+ }
margin.right = _.right !== undefined ? _.right : margin.right;
margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
margin.left = _.left !== undefined ? _.left : margin.left;
}
},
color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- legend.color(color);
- parallelCoordinates.color(color);
- }}
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ parallelCoordinates.color(color);
+ }}
});
nv.utils.inheritOptions(chart, parallelCoordinates);
nv.utils.initOptions(chart);
return chart;
};
-nv.models.pie = function() {
- "use strict";
+ nv.models.pie = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 500
- , height = 500
- , getX = function(d) { return d.x }
- , getY = function(d) { return d.y }
- , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
- , container = null
- , color = nv.utils.defaultColor()
- , valueFormat = d3.format(',.2f')
- , showLabels = true
- , labelsOutside = false
- , labelType = "key"
- , labelThreshold = .02 //if slice percentage is under this, don't show label
- , donut = false
- , title = false
- , growOnHover = true
- , titleOffset = 0
- , labelSunbeamLayout = false
- , startAngle = false
- , padAngle = false
- , endAngle = false
- , cornerRadius = 0
- , donutRatio = 0.5
- , duration = 250
- , arcsRadius = []
- , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd')
- ;
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 500
+ , height = 500
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , container = null
+ , color = nv.utils.defaultColor()
+ , valueFormat = d3.format(',.2f')
+ , showLabels = true
+ , labelsOutside = false
+ , labelType = "key"
+ , labelThreshold = .02 //if slice percentage is under this, don't show label
+ , donut = false
+ , title = false
+ , growOnHover = true
+ , titleOffset = 0
+ , labelSunbeamLayout = false
+ , startAngle = false
+ , padAngle = false
+ , endAngle = false
+ , cornerRadius = 0
+ , donutRatio = 0.5
+ , duration = 250
+ , arcsRadius = []
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd')
+ ;
- var arcs = [];
- var arcsOver = [];
+ var arcs = [];
+ var arcsOver = [];
- //============================================================
- // chart function
- //------------------------------------------------------------
+ //============================================================
+ // chart function
+ //------------------------------------------------------------
- var renderWatch = nv.utils.renderWatch(dispatch);
+ var renderWatch = nv.utils.renderWatch(dispatch);
- function chart(selection) {
- renderWatch.reset();
- selection.each(function(data) {
- var availableWidth = width - margin.left - margin.right
- , availableHeight = height - margin.top - margin.bottom
- , radius = Math.min(availableWidth, availableHeight) / 2
- , arcsRadiusOuter = []
- , arcsRadiusInner = []
- ;
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right
+ , availableHeight = height - margin.top - margin.bottom
+ , radius = Math.min(availableWidth, availableHeight) / 2
+ , arcsRadiusOuter = []
+ , arcsRadiusInner = []
+ ;
- container = d3.select(this)
- if (arcsRadius.length === 0) {
- var outer = radius - radius / 5;
- var inner = donutRatio * radius;
- for (var i = 0; i < data[0].length; i++) {
- arcsRadiusOuter.push(outer);
- arcsRadiusInner.push(inner);
- }
- } else {
- if(growOnHover){
- arcsRadiusOuter = arcsRadius.map(function (d) { return (d.outer - d.outer / 5) * radius; });
- arcsRadiusInner = arcsRadius.map(function (d) { return (d.inner - d.inner / 5) * radius; });
- donutRatio = d3.min(arcsRadius.map(function (d) { return (d.inner - d.inner / 5); }));
+ container = d3.select(this)
+ if (arcsRadius.length === 0) {
+ var outer = radius - radius / 5;
+ var inner = donutRatio * radius;
+ for (var i = 0; i < data[0].length; i++) {
+ arcsRadiusOuter.push(outer);
+ arcsRadiusInner.push(inner);
+ }
} else {
- arcsRadiusOuter = arcsRadius.map(function (d) { return d.outer * radius; });
- arcsRadiusInner = arcsRadius.map(function (d) { return d.inner * radius; });
- donutRatio = d3.min(arcsRadius.map(function (d) { return d.inner; }));
+ if(growOnHover){
+ arcsRadiusOuter = arcsRadius.map(function (d) { return (d.outer - d.outer / 5) * radius; });
+ arcsRadiusInner = arcsRadius.map(function (d) { return (d.inner - d.inner / 5) * radius; });
+ donutRatio = d3.min(arcsRadius.map(function (d) { return (d.inner - d.inner / 5); }));
+ } else {
+ arcsRadiusOuter = arcsRadius.map(function (d) { return d.outer * radius; });
+ arcsRadiusInner = arcsRadius.map(function (d) { return d.inner * radius; });
+ donutRatio = d3.min(arcsRadius.map(function (d) { return d.inner; }));
+ }
}
- }
- nv.utils.initSVG(container);
+ nv.utils.initSVG(container);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('.nv-wrap.nv-pie').data(data);
- var wrapEnter = wrap.enter().append('g').attr('class','nvd3 nv-wrap nv-pie nv-chart-' + id);
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
- var g_pie = gEnter.append('g').attr('class', 'nv-pie');
- gEnter.append('g').attr('class', 'nv-pieLabels');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('.nv-wrap.nv-pie').data(data);
+ var wrapEnter = wrap.enter().append('g').attr('class','nvd3 nv-wrap nv-pie nv-chart-' + id);
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
+ var g_pie = gEnter.append('g').attr('class', 'nv-pie');
+ gEnter.append('g').attr('class', 'nv-pieLabels');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- g.select('.nv-pie').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
- g.select('.nv-pieLabels').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ g.select('.nv-pie').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
+ g.select('.nv-pieLabels').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
- //
- container.on('click', function(d,i) {
- dispatch.chartClick({
- data: d,
- index: i,
- pos: d3.event,
- id: id
+ //
+ container.on('click', function(d,i) {
+ dispatch.chartClick({
+ data: d,
+ index: i,
+ pos: d3.event,
+ id: id
+ });
});
- });
- arcs = [];
- arcsOver = [];
- for (var i = 0; i < data[0].length; i++) {
+ arcs = [];
+ arcsOver = [];
+ for (var i = 0; i < data[0].length; i++) {
- var arc = d3.svg.arc().outerRadius(arcsRadiusOuter[i]);
- var arcOver = d3.svg.arc().outerRadius(arcsRadiusOuter[i] + 5);
+ var arc = d3.svg.arc().outerRadius(arcsRadiusOuter[i]);
+ var arcOver = d3.svg.arc().outerRadius(arcsRadiusOuter[i] + 5);
- if (startAngle !== false) {
- arc.startAngle(startAngle);
- arcOver.startAngle(startAngle);
- }
- if (endAngle !== false) {
- arc.endAngle(endAngle);
- arcOver.endAngle(endAngle);
- }
- if (donut) {
- arc.innerRadius(arcsRadiusInner[i]);
- arcOver.innerRadius(arcsRadiusInner[i]);
- }
+ if (startAngle !== false) {
+ arc.startAngle(startAngle);
+ arcOver.startAngle(startAngle);
+ }
+ if (endAngle !== false) {
+ arc.endAngle(endAngle);
+ arcOver.endAngle(endAngle);
+ }
+ if (donut) {
+ arc.innerRadius(arcsRadiusInner[i]);
+ arcOver.innerRadius(arcsRadiusInner[i]);
+ }
- if (arc.cornerRadius && cornerRadius) {
- arc.cornerRadius(cornerRadius);
- arcOver.cornerRadius(cornerRadius);
+ if (arc.cornerRadius && cornerRadius) {
+ arc.cornerRadius(cornerRadius);
+ arcOver.cornerRadius(cornerRadius);
+ }
+
+ arcs.push(arc);
+ arcsOver.push(arcOver);
}
- arcs.push(arc);
- arcsOver.push(arcOver);
- }
+ // Setup the Pie chart and choose the data element
+ var pie = d3.layout.pie()
+ .sort(null)
+ .value(function(d) { return d.disabled ? 0 : getY(d) });
- // Setup the Pie chart and choose the data element
- var pie = d3.layout.pie()
- .sort(null)
- .value(function(d) { return d.disabled ? 0 : getY(d) });
+ // padAngle added in d3 3.5
+ if (pie.padAngle && padAngle) {
+ pie.padAngle(padAngle);
+ }
- // padAngle added in d3 3.5
- if (pie.padAngle && padAngle) {
- pie.padAngle(padAngle);
- }
+ // if title is specified and donut, put it in the middle
+ if (donut && title) {
+ g_pie.append("text").attr('class', 'nv-pie-title');
- // if title is specified and donut, put it in the middle
- if (donut && title) {
- g_pie.append("text").attr('class', 'nv-pie-title');
+ wrap.select('.nv-pie-title')
+ .style("text-anchor", "middle")
+ .text(function (d) {
+ return title;
+ })
+ .style("font-size", (Math.min(availableWidth, availableHeight)) * donutRatio * 2 / (title.length + 2) + "px")
+ .attr("dy", "0.35em") // trick to vertically center text
+ .attr('transform', function(d, i) {
+ return 'translate(0, '+ titleOffset + ')';
+ });
+ }
- wrap.select('.nv-pie-title')
- .style("text-anchor", "middle")
- .text(function (d) {
- return title;
- })
- .style("font-size", (Math.min(availableWidth, availableHeight)) * donutRatio * 2 / (title.length + 2) + "px")
- .attr("dy", "0.35em") // trick to vertically center text
- .attr('transform', function(d, i) {
- return 'translate(0, '+ titleOffset + ')';
- });
- }
+ var slices = wrap.select('.nv-pie').selectAll('.nv-slice').data(pie);
+ var pieLabels = wrap.select('.nv-pieLabels').selectAll('.nv-label').data(pie);
- var slices = wrap.select('.nv-pie').selectAll('.nv-slice').data(pie);
- var pieLabels = wrap.select('.nv-pieLabels').selectAll('.nv-label').data(pie);
+ slices.exit().remove();
+ pieLabels.exit().remove();
- slices.exit().remove();
- pieLabels.exit().remove();
-
- var ae = slices.enter().append('g');
- ae.attr('class', 'nv-slice');
- ae.on('mouseover', function(d, i) {
- d3.select(this).classed('hover', true);
- if (growOnHover) {
- d3.select(this).select("path").transition()
- .duration(70)
- .attr("d", arcsOver[i]);
- }
- dispatch.elementMouseover({
- data: d.data,
- index: i,
- color: d3.select(this).style("fill"),
- percent: (d.endAngle - d.startAngle) / (2 * Math.PI)
+ var ae = slices.enter().append('g');
+ ae.attr('class', 'nv-slice');
+ ae.on('mouseover', function(d, i) {
+ d3.select(this).classed('hover', true);
+ if (growOnHover) {
+ d3.select(this).select("path").transition()
+ .duration(70)
+ .attr("d", arcsOver[i]);
+ }
+ dispatch.elementMouseover({
+ data: d.data,
+ index: i,
+ color: d3.select(this).style("fill"),
+ percent: (d.endAngle - d.startAngle) / (2 * Math.PI)
+ });
});
- });
- ae.on('mouseout', function(d, i) {
- d3.select(this).classed('hover', false);
- if (growOnHover) {
- d3.select(this).select("path").transition()
- .duration(50)
- .attr("d", arcs[i]);
- }
- dispatch.elementMouseout({data: d.data, index: i});
- });
- ae.on('mousemove', function(d, i) {
- dispatch.elementMousemove({data: d.data, index: i});
- });
- ae.on('click', function(d, i) {
- var element = this;
- dispatch.elementClick({
- data: d.data,
- index: i,
- color: d3.select(this).style("fill"),
- event: d3.event,
- element: element
+ ae.on('mouseout', function(d, i) {
+ d3.select(this).classed('hover', false);
+ if (growOnHover) {
+ d3.select(this).select("path").transition()
+ .duration(50)
+ .attr("d", arcs[i]);
+ }
+ dispatch.elementMouseout({data: d.data, index: i});
});
- });
- ae.on('dblclick', function(d, i) {
- dispatch.elementDblClick({
- data: d.data,
- index: i,
- color: d3.select(this).style("fill")
+ ae.on('mousemove', function(d, i) {
+ dispatch.elementMousemove({data: d.data, index: i});
});
- });
+ ae.on('click', function(d, i) {
+ var element = this;
+ dispatch.elementClick({
+ data: d.data,
+ index: i,
+ color: d3.select(this).style("fill"),
+ event: d3.event,
+ element: element
+ });
+ });
+ ae.on('dblclick', function(d, i) {
+ dispatch.elementDblClick({
+ data: d.data,
+ index: i,
+ color: d3.select(this).style("fill")
+ });
+ });
- slices.attr('fill', function(d,i) { return color(d.data, i); });
- slices.attr('stroke', function(d,i) { return color(d.data, i); });
+ slices.attr('fill', function(d,i) { return color(d.data, i); });
+ slices.attr('stroke', function(d,i) { return color(d.data, i); });
- var paths = ae.append('path').each(function(d) {
- this._current = d;
- });
+ var paths = ae.append('path').each(function(d) {
+ this._current = d;
+ });
- slices.select('path')
- .transition()
- .duration(duration)
- .attr('d', function (d, i) { return arcs[i](d); })
- .attrTween('d', arcTween);
+ slices.select('path')
+ .transition()
+ .duration(duration)
+ .attr('d', function (d, i) { return arcs[i](d); })
+ .attrTween('d', arcTween);
- if (showLabels) {
- // This does the normal label
- var labelsArc = [];
- for (var i = 0; i < data[0].length; i++) {
- labelsArc.push(arcs[i]);
+ if (showLabels) {
+ // This does the normal label
+ var labelsArc = [];
+ for (var i = 0; i < data[0].length; i++) {
+ labelsArc.push(arcs[i]);
- if (labelsOutside) {
- if (donut) {
- labelsArc[i] = d3.svg.arc().outerRadius(arcs[i].outerRadius());
- if (startAngle !== false) labelsArc[i].startAngle(startAngle);
- if (endAngle !== false) labelsArc[i].endAngle(endAngle);
- }
- } else if (!donut) {
+ if (labelsOutside) {
+ if (donut) {
+ labelsArc[i] = d3.svg.arc().outerRadius(arcs[i].outerRadius());
+ if (startAngle !== false) labelsArc[i].startAngle(startAngle);
+ if (endAngle !== false) labelsArc[i].endAngle(endAngle);
+ }
+ } else if (!donut) {
labelsArc[i].innerRadius(0);
+ }
}
- }
- pieLabels.enter().append("g").classed("nv-label",true).each(function(d,i) {
- var group = d3.select(this);
+ pieLabels.enter().append("g").classed("nv-label",true).each(function(d,i) {
+ var group = d3.select(this);
- group.attr('transform', function (d, i) {
+ group.attr('transform', function (d, i) {
+ if (labelSunbeamLayout) {
+ d.outerRadius = arcsRadiusOuter[i] + 10; // Set Outer Coordinate
+ d.innerRadius = arcsRadiusOuter[i] + 15; // Set Inner Coordinate
+ var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
+ if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
+ rotateAngle -= 90;
+ } else {
+ rotateAngle += 90;
+ }
+ return 'translate(' + labelsArc[i].centroid(d) + ') rotate(' + rotateAngle + ')';
+ } else {
+ d.outerRadius = radius + 10; // Set Outer Coordinate
+ d.innerRadius = radius + 15; // Set Inner Coordinate
+ return 'translate(' + labelsArc[i].centroid(d) + ')'
+ }
+ });
+
+ group.append('rect')
+ .style('stroke', '#fff')
+ .style('fill', '#fff')
+ .attr("rx", 3)
+ .attr("ry", 3);
+
+ group.append('text')
+ .style('text-anchor', labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle') //center the text on it's origin or begin/end if orthogonal aligned
+ .style('fill', '#000')
+ });
+
+ var labelLocationHash = {};
+ var avgHeight = 14;
+ var avgWidth = 140;
+ var createHashKey = function(coordinates) {
+ return Math.floor(coordinates[0]/avgWidth) * avgWidth + ',' + Math.floor(coordinates[1]/avgHeight) * avgHeight;
+ };
+ var getSlicePercentage = function(d) {
+ return (d.endAngle - d.startAngle) / (2 * Math.PI);
+ };
+
+ pieLabels.watchTransition(renderWatch, 'pie labels').attr('transform', function (d, i) {
if (labelSunbeamLayout) {
d.outerRadius = arcsRadiusOuter[i] + 10; // Set Outer Coordinate
d.innerRadius = arcsRadiusOuter[i] + 15; // Set Inner Coordinate
var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
@@ -11396,3316 +11534,3892 @@
}
return 'translate(' + labelsArc[i].centroid(d) + ') rotate(' + rotateAngle + ')';
} else {
d.outerRadius = radius + 10; // Set Outer Coordinate
d.innerRadius = radius + 15; // Set Inner Coordinate
- return 'translate(' + labelsArc[i].centroid(d) + ')'
+
+ /*
+ Overlapping pie labels are not good. What this attempts to do is, prevent overlapping.
+ Each label location is hashed, and if a hash collision occurs, we assume an overlap.
+ Adjust the label's y-position to remove the overlap.
+ */
+ var center = labelsArc[i].centroid(d);
+ var percent = getSlicePercentage(d);
+ if (d.value && percent >= labelThreshold) {
+ var hashKey = createHashKey(center);
+ if (labelLocationHash[hashKey]) {
+ center[1] -= avgHeight;
+ }
+ labelLocationHash[createHashKey(center)] = true;
+ }
+ return 'translate(' + center + ')'
}
});
- group.append('rect')
- .style('stroke', '#fff')
- .style('fill', '#fff')
- .attr("rx", 3)
- .attr("ry", 3);
+ pieLabels.select(".nv-label text")
+ .style('text-anchor', function(d,i) {
+ //center the text on it's origin or begin/end if orthogonal aligned
+ return labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle';
+ })
+ .text(function(d, i) {
+ var percent = getSlicePercentage(d);
+ var label = '';
+ if (!d.value || percent < labelThreshold) return '';
- group.append('text')
- .style('text-anchor', labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle') //center the text on it's origin or begin/end if orthogonal aligned
- .style('fill', '#000')
- });
+ if(typeof labelType === 'function') {
+ label = labelType(d, i, {
+ 'key': getX(d.data),
+ 'value': getY(d.data),
+ 'percent': valueFormat(percent)
+ });
+ } else {
+ switch (labelType) {
+ case 'key':
+ label = getX(d.data);
+ break;
+ case 'value':
+ label = valueFormat(getY(d.data));
+ break;
+ case 'percent':
+ label = d3.format('%')(percent);
+ break;
+ }
+ }
+ return label;
+ })
+ ;
+ }
- var labelLocationHash = {};
- var avgHeight = 14;
- var avgWidth = 140;
- var createHashKey = function(coordinates) {
- return Math.floor(coordinates[0]/avgWidth) * avgWidth + ',' + Math.floor(coordinates[1]/avgHeight) * avgHeight;
+
+ // Computes the angle of an arc, converting from radians to degrees.
+ function angle(d) {
+ var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
+ return a > 90 ? a - 180 : a;
+ }
+
+ function arcTween(a, idx) {
+ a.endAngle = isNaN(a.endAngle) ? 0 : a.endAngle;
+ a.startAngle = isNaN(a.startAngle) ? 0 : a.startAngle;
+ if (!donut) a.innerRadius = 0;
+ var i = d3.interpolate(this._current, a);
+ this._current = i(0);
+ return function (t) {
+ return arcs[idx](i(t));
+ };
+ }
+ });
+
+ renderWatch.renderEnd('pie immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ arcsRadius: { get: function () { return arcsRadius; }, set: function (_) { arcsRadius = _; } },
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLabels: {get: function(){return showLabels;}, set: function(_){showLabels=_;}},
+ title: {get: function(){return title;}, set: function(_){title=_;}},
+ titleOffset: {get: function(){return titleOffset;}, set: function(_){titleOffset=_;}},
+ labelThreshold: {get: function(){return labelThreshold;}, set: function(_){labelThreshold=_;}},
+ valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ endAngle: {get: function(){return endAngle;}, set: function(_){endAngle=_;}},
+ startAngle: {get: function(){return startAngle;}, set: function(_){startAngle=_;}},
+ padAngle: {get: function(){return padAngle;}, set: function(_){padAngle=_;}},
+ cornerRadius: {get: function(){return cornerRadius;}, set: function(_){cornerRadius=_;}},
+ donutRatio: {get: function(){return donutRatio;}, set: function(_){donutRatio=_;}},
+ labelsOutside: {get: function(){return labelsOutside;}, set: function(_){labelsOutside=_;}},
+ labelSunbeamLayout: {get: function(){return labelSunbeamLayout;}, set: function(_){labelSunbeamLayout=_;}},
+ donut: {get: function(){return donut;}, set: function(_){donut=_;}},
+ growOnHover: {get: function(){return growOnHover;}, set: function(_){growOnHover=_;}},
+
+ // depreciated after 1.7.1
+ pieLabelsOutside: {get: function(){return labelsOutside;}, set: function(_){
+ labelsOutside=_;
+ nv.deprecated('pieLabelsOutside', 'use labelsOutside instead');
+ }},
+ // depreciated after 1.7.1
+ donutLabelsOutside: {get: function(){return labelsOutside;}, set: function(_){
+ labelsOutside=_;
+ nv.deprecated('donutLabelsOutside', 'use labelsOutside instead');
+ }},
+ // deprecated after 1.7.1
+ labelFormat: {get: function(){ return valueFormat;}, set: function(_) {
+ valueFormat=_;
+ nv.deprecated('labelFormat','use valueFormat instead');
+ }},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
+ margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
+ margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
+ margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ }},
+ y: {get: function(){return getY;}, set: function(_){
+ getY=d3.functor(_);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color=nv.utils.getColor(_);
+ }},
+ labelType: {get: function(){return labelType;}, set: function(_){
+ labelType= _ || 'key';
+ }}
+ });
+
+ nv.utils.initOptions(chart);
+ return chart;
+ };
+ nv.models.pieChart = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var pie = nv.models.pie();
+ var legend = nv.models.legend();
+ var tooltip = nv.models.tooltip();
+
+ var margin = {top: 30, right: 20, bottom: 20, left: 20}
+ , marginTop = null
+ , width = null
+ , height = null
+ , showTooltipPercent = false
+ , showLegend = true
+ , legendPosition = "top"
+ , color = nv.utils.defaultColor()
+ , state = nv.utils.state()
+ , defaultState = null
+ , noData = null
+ , duration = 250
+ , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd')
+ ;
+
+ tooltip
+ .duration(0)
+ .headerEnabled(false)
+ .valueFormatter(function(d, i) {
+ return pie.valueFormat()(d, i);
+ });
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var renderWatch = nv.utils.renderWatch(dispatch);
+
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled })
};
- var getSlicePercentage = function(d) {
- return (d.endAngle - d.startAngle) / (2 * Math.PI);
- };
+ }
+ };
- pieLabels.watchTransition(renderWatch, 'pie labels').attr('transform', function (d, i) {
- if (labelSunbeamLayout) {
- d.outerRadius = arcsRadiusOuter[i] + 10; // Set Outer Coordinate
- d.innerRadius = arcsRadiusOuter[i] + 15; // Set Inner Coordinate
- var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
- if ((d.startAngle + d.endAngle) / 2 < Math.PI) {
- rotateAngle -= 90;
- } else {
- rotateAngle += 90;
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.active !== undefined) {
+ data.forEach(function (series, i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ }
+ };
+
+ //============================================================
+ // Chart function
+ //------------------------------------------------------------
+
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(pie);
+
+ selection.each(function(data) {
+ var container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ var that = this;
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+
+ chart.update = function() { container.transition().call(chart); };
+ chart.container = this;
+
+ state.setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
+
+ //set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
+
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
+
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length) {
+ nv.utils.noData(chart, container);
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-pieChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-pieChart').append('g');
+ var g = wrap.select('g');
+
+ gEnter.append('g').attr('class', 'nv-pieWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+
+ // Legend
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
+ if (legendPosition === "top") {
+ legend.width( availableWidth ).key(pie.x());
+
+ wrap.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
+
+ if (!marginTop && legend.height() !== margin.top) {
+ margin.top = legend.height();
+ availableHeight = nv.utils.availableHeight(height, container, margin);
}
- return 'translate(' + labelsArc[i].centroid(d) + ') rotate(' + rotateAngle + ')';
- } else {
- d.outerRadius = radius + 10; // Set Outer Coordinate
- d.innerRadius = radius + 15; // Set Inner Coordinate
- /*
- Overlapping pie labels are not good. What this attempts to do is, prevent overlapping.
- Each label location is hashed, and if a hash collision occurs, we assume an overlap.
- Adjust the label's y-position to remove the overlap.
- */
- var center = labelsArc[i].centroid(d);
- var percent = getSlicePercentage(d);
- if (d.value && percent >= labelThreshold) {
- var hashKey = createHashKey(center);
- if (labelLocationHash[hashKey]) {
- center[1] -= avgHeight;
- }
- labelLocationHash[createHashKey(center)] = true;
+ wrap.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')');
+ } else if (legendPosition === "right") {
+ var legendWidth = nv.models.legend().width();
+ if (availableWidth / 2 < legendWidth) {
+ legendWidth = (availableWidth / 2)
}
- return 'translate(' + center + ')'
+ legend.height(availableHeight).key(pie.x());
+ legend.width(legendWidth);
+ availableWidth -= legend.width();
+
+ wrap.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend)
+ .attr('transform', 'translate(' + (availableWidth) +',0)');
}
+ }
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ // Main Chart Component(s)
+ pie.width(availableWidth).height(availableHeight);
+ var pieWrap = g.select('.nv-pieWrap').datum([data]);
+ d3.transition(pieWrap).call(pie);
+
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
+
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState) {
+ state[key] = newState[key];
+ }
+ dispatch.stateChange(state);
+ chart.update();
});
- pieLabels.select(".nv-label text")
- .style('text-anchor', function(d,i) {
- //center the text on it's origin or begin/end if orthogonal aligned
- return labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle';
- })
- .text(function(d, i) {
- var percent = getSlicePercentage(d);
- var label = '';
- if (!d.value || percent < labelThreshold) return '';
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+ state.disabled = e.disabled;
+ }
+ chart.update();
+ });
+ });
- if(typeof labelType === 'function') {
- label = labelType(d, i, {
- 'key': getX(d.data),
- 'value': getY(d.data),
- 'percent': valueFormat(percent)
- });
- } else {
- switch (labelType) {
- case 'key':
- label = getX(d.data);
- break;
- case 'value':
- label = valueFormat(getY(d.data));
- break;
- case 'percent':
- label = d3.format('%')(percent);
- break;
- }
- }
- return label;
- })
- ;
- }
+ renderWatch.renderEnd('pieChart immediate');
+ return chart;
+ }
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
- // Computes the angle of an arc, converting from radians to degrees.
- function angle(d) {
- var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
- return a > 90 ? a - 180 : a;
+ pie.dispatch.on('elementMouseover.tooltip', function(evt) {
+ evt['series'] = {
+ key: chart.x()(evt.data),
+ value: chart.y()(evt.data),
+ color: evt.color,
+ percent: evt.percent
+ };
+ if (!showTooltipPercent) {
+ delete evt.percent;
+ delete evt.series.percent;
}
+ tooltip.data(evt).hidden(false);
+ });
- function arcTween(a, idx) {
- a.endAngle = isNaN(a.endAngle) ? 0 : a.endAngle;
- a.startAngle = isNaN(a.startAngle) ? 0 : a.startAngle;
- if (!donut) a.innerRadius = 0;
- var i = d3.interpolate(this._current, a);
- this._current = i(0);
- return function (t) {
- return arcs[idx](i(t));
- };
- }
+ pie.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true);
});
- renderWatch.renderEnd('pie immediate');
- return chart;
- }
+ pie.dispatch.on('elementMousemove.tooltip', function(evt) {
+ tooltip();
+ });
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ // expose chart's sub-components
+ chart.legend = legend;
+ chart.dispatch = dispatch;
+ chart.pie = pie;
+ chart.tooltip = tooltip;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- arcsRadius: { get: function () { return arcsRadius; }, set: function (_) { arcsRadius = _; } },
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- showLabels: {get: function(){return showLabels;}, set: function(_){showLabels=_;}},
- title: {get: function(){return title;}, set: function(_){title=_;}},
- titleOffset: {get: function(){return titleOffset;}, set: function(_){titleOffset=_;}},
- labelThreshold: {get: function(){return labelThreshold;}, set: function(_){labelThreshold=_;}},
- valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}},
- x: {get: function(){return getX;}, set: function(_){getX=_;}},
- id: {get: function(){return id;}, set: function(_){id=_;}},
- endAngle: {get: function(){return endAngle;}, set: function(_){endAngle=_;}},
- startAngle: {get: function(){return startAngle;}, set: function(_){startAngle=_;}},
- padAngle: {get: function(){return padAngle;}, set: function(_){padAngle=_;}},
- cornerRadius: {get: function(){return cornerRadius;}, set: function(_){cornerRadius=_;}},
- donutRatio: {get: function(){return donutRatio;}, set: function(_){donutRatio=_;}},
- labelsOutside: {get: function(){return labelsOutside;}, set: function(_){labelsOutside=_;}},
- labelSunbeamLayout: {get: function(){return labelSunbeamLayout;}, set: function(_){labelSunbeamLayout=_;}},
- donut: {get: function(){return donut;}, set: function(_){donut=_;}},
- growOnHover: {get: function(){return growOnHover;}, set: function(_){growOnHover=_;}},
+ // use Object get/set functionality to map between vars and chart functions
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ showTooltipPercent: {get: function(){return showTooltipPercent;}, set: function(_){showTooltipPercent=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
- // depreciated after 1.7.1
- pieLabelsOutside: {get: function(){return labelsOutside;}, set: function(_){
- labelsOutside=_;
- nv.deprecated('pieLabelsOutside', 'use labelsOutside instead');
- }},
- // depreciated after 1.7.1
- donutLabelsOutside: {get: function(){return labelsOutside;}, set: function(_){
- labelsOutside=_;
- nv.deprecated('donutLabelsOutside', 'use labelsOutside instead');
- }},
- // deprecated after 1.7.1
- labelFormat: {get: function(){ return valueFormat;}, set: function(_) {
- valueFormat=_;
- nv.deprecated('labelFormat','use valueFormat instead');
- }},
+ // options that require extra logic in the setter
+ color: {get: function(){return color;}, set: function(_){
+ color = _;
+ legend.color(color);
+ pie.color(color);
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ pie.duration(duration);
+ }},
+ margin: {get: function(){return margin;}, set: function(_){
+ if (_.top !== undefined) {
+ margin.top = _.top;
+ marginTop = _.top;
+ }
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }}
+ });
+ nv.utils.inheritOptions(chart, pie);
+ nv.utils.initOptions(chart);
+ return chart;
+ };
+ nv.models.sankey = function() {
+ 'use strict';
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
- margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
- margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
- margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- }},
- y: {get: function(){return getY;}, set: function(_){
- getY=d3.functor(_);
- }},
- color: {get: function(){return color;}, set: function(_){
- color=nv.utils.getColor(_);
- }},
- labelType: {get: function(){return labelType;}, set: function(_){
- labelType= _ || 'key';
- }}
- });
+ // Sources:
+ // - https://bost.ocks.org/mike/sankey/
+ // - https://github.com/soxofaan/d3-plugin-captain-sankey
- nv.utils.initOptions(chart);
- return chart;
-};
-nv.models.pieChart = function() {
- "use strict";
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ var sankey = {},
+ nodeWidth = 24,
+ nodePadding = 8,
+ size = [1, 1],
+ nodes = [],
+ links = [],
+ sinksRight = true;
- var pie = nv.models.pie();
- var legend = nv.models.legend();
- var tooltip = nv.models.tooltip();
+ var layout = function(iterations) {
+ computeNodeLinks();
+ computeNodeValues();
+ computeNodeBreadths();
+ computeNodeDepths(iterations);
+ };
- var margin = {top: 30, right: 20, bottom: 20, left: 20}
- , width = null
- , height = null
- , showTooltipPercent = false
- , showLegend = true
- , legendPosition = "top"
- , color = nv.utils.defaultColor()
- , state = nv.utils.state()
- , defaultState = null
- , noData = null
- , duration = 250
- , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd')
- ;
+ var relayout = function() {
+ computeLinkDepths();
+ };
- tooltip
- .duration(0)
- .headerEnabled(false)
- .valueFormatter(function(d, i) {
- return pie.valueFormat()(d, i);
- });
+ // SVG path data generator, to be used as 'd' attribute on 'path' element selection.
+ var link = function() {
+ var curvature = .5;
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ function link(d) {
- var renderWatch = nv.utils.renderWatch(dispatch);
+ var x0 = d.source.x + d.source.dx,
+ x1 = d.target.x,
+ xi = d3.interpolateNumber(x0, x1),
+ x2 = xi(curvature),
+ x3 = xi(1 - curvature),
+ y0 = d.source.y + d.sy + d.dy / 2,
+ y1 = d.target.y + d.ty + d.dy / 2;
+ var linkPath = 'M' + x0 + ',' + y0
+ + 'C' + x2 + ',' + y0
+ + ' ' + x3 + ',' + y1
+ + ' ' + x1 + ',' + y1;
+ return linkPath;
+ }
- var stateGetter = function(data) {
- return function(){
- return {
- active: data.map(function(d) { return !d.disabled })
+ link.curvature = function(_) {
+ if (!arguments.length) return curvature;
+ curvature = +_;
+ return link;
};
+
+ return link;
+ };
+
+ // Y-position of the middle of a node.
+ var center = function(node) {
+ return node.y + node.dy / 2;
+ };
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ // Populate the sourceLinks and targetLinks for each node.
+ // Also, if the source and target are not objects, assume they are indices.
+ function computeNodeLinks() {
+ nodes.forEach(function(node) {
+ // Links that have this node as source.
+ node.sourceLinks = [];
+ // Links that have this node as target.
+ node.targetLinks = [];
+ });
+ links.forEach(function(link) {
+ var source = link.source,
+ target = link.target;
+ if (typeof source === 'number') source = link.source = nodes[link.source];
+ if (typeof target === 'number') target = link.target = nodes[link.target];
+ source.sourceLinks.push(link);
+ target.targetLinks.push(link);
+ });
}
- };
- var stateSetter = function(data) {
- return function(state) {
- if (state.active !== undefined) {
- data.forEach(function (series, i) {
- series.disabled = !state.active[i];
+ // Compute the value (size) of each node by summing the associated links.
+ function computeNodeValues() {
+ nodes.forEach(function(node) {
+ node.value = Math.max(
+ d3.sum(node.sourceLinks, value),
+ d3.sum(node.targetLinks, value)
+ );
+ });
+ }
+
+ // Iteratively assign the breadth (x-position) for each node.
+ // Nodes are assigned the maximum breadth of incoming neighbors plus one;
+ // nodes with no incoming links are assigned breadth zero, while
+ // nodes with no outgoing links are assigned the maximum breadth.
+ function computeNodeBreadths() {
+ //
+ var remainingNodes = nodes,
+ nextNodes,
+ x = 0;
+
+ // Work from left to right.
+ // Keep updating the breath (x-position) of nodes that are target of recently updated nodes.
+ //
+ while (remainingNodes.length && x < nodes.length) {
+ nextNodes = [];
+ remainingNodes.forEach(function(node) {
+ node.x = x;
+ node.dx = nodeWidth;
+ node.sourceLinks.forEach(function(link) {
+ if (nextNodes.indexOf(link.target) < 0) {
+ nextNodes.push(link.target);
+ }
+ });
});
+ remainingNodes = nextNodes;
+ ++x;
+ //
}
+
+ // Optionally move pure sinks always to the right.
+ if (sinksRight) {
+ moveSinksRight(x);
+ }
+
+ scaleNodeBreadths((size[0] - nodeWidth) / (x - 1));
}
- };
- //============================================================
- // Chart function
- //------------------------------------------------------------
+ function moveSourcesRight() {
+ nodes.forEach(function(node) {
+ if (!node.targetLinks.length) {
+ node.x = d3.min(node.sourceLinks, function(d) { return d.target.x; }) - 1;
+ }
+ });
+ }
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(pie);
+ function moveSinksRight(x) {
+ nodes.forEach(function(node) {
+ if (!node.sourceLinks.length) {
+ node.x = x - 1;
+ }
+ });
+ }
- selection.each(function(data) {
- var container = d3.select(this);
- nv.utils.initSVG(container);
+ function scaleNodeBreadths(kx) {
+ nodes.forEach(function(node) {
+ node.x *= kx;
+ });
+ }
- var that = this;
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ // Compute the depth (y-position) for each node.
+ function computeNodeDepths(iterations) {
+ // Group nodes by breath.
+ var nodesByBreadth = d3.nest()
+ .key(function(d) { return d.x; })
+ .sortKeys(d3.ascending)
+ .entries(nodes)
+ .map(function(d) { return d.values; });
- chart.update = function() { container.transition().call(chart); };
- chart.container = this;
+ //
+ initializeNodeDepth();
+ resolveCollisions();
+ computeLinkDepths();
+ for (var alpha = 1; iterations > 0; --iterations) {
+ relaxRightToLeft(alpha *= .99);
+ resolveCollisions();
+ computeLinkDepths();
+ relaxLeftToRight(alpha);
+ resolveCollisions();
+ computeLinkDepths();
+ }
- state.setter(stateSetter(data), chart.update)
- .getter(stateGetter(data))
- .update();
+ function initializeNodeDepth() {
+ // Calculate vertical scaling factor.
+ var ky = d3.min(nodesByBreadth, function(nodes) {
+ return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value);
+ });
- //set state.disabled
- state.disabled = data.map(function(d) { return !!d.disabled });
+ nodesByBreadth.forEach(function(nodes) {
+ nodes.forEach(function(node, i) {
+ node.y = i;
+ node.dy = node.value * ky;
+ });
+ });
- if (!defaultState) {
- var key;
- defaultState = {};
- for (key in state) {
- if (state[key] instanceof Array)
- defaultState[key] = state[key].slice(0);
- else
- defaultState[key] = state[key];
- }
+ links.forEach(function(link) {
+ link.dy = link.value * ky;
+ });
}
- // Display No Data message if there's nothing to show.
- if (!data || !data.length) {
- nv.utils.noData(chart, container);
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
+ function relaxLeftToRight(alpha) {
+ nodesByBreadth.forEach(function(nodes, breadth) {
+ nodes.forEach(function(node) {
+ if (node.targetLinks.length) {
+ // Value-weighted average of the y-position of source node centers linked to this node.
+ var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value);
+ node.y += (y - center(node)) * alpha;
+ }
+ });
+ });
+
+ function weightedSource(link) {
+ return (link.source.y + link.sy + link.dy / 2) * link.value;
+ }
}
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-pieChart').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-pieChart').append('g');
- var g = wrap.select('g');
+ function relaxRightToLeft(alpha) {
+ nodesByBreadth.slice().reverse().forEach(function(nodes) {
+ nodes.forEach(function(node) {
+ if (node.sourceLinks.length) {
+ // Value-weighted average of the y-positions of target nodes linked to this node.
+ var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value);
+ node.y += (y - center(node)) * alpha;
+ }
+ });
+ });
- gEnter.append('g').attr('class', 'nv-pieWrap');
- gEnter.append('g').attr('class', 'nv-legendWrap');
+ function weightedTarget(link) {
+ return (link.target.y + link.ty + link.dy / 2) * link.value;
+ }
+ }
- // Legend
- if (!showLegend) {
- g.select('.nv-legendWrap').selectAll('*').remove();
- } else {
- if (legendPosition === "top") {
- legend.width( availableWidth ).key(pie.x());
+ function resolveCollisions() {
+ nodesByBreadth.forEach(function(nodes) {
+ var node,
+ dy,
+ y0 = 0,
+ n = nodes.length,
+ i;
- wrap.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
-
- if (legend.height() > margin.top) {
- margin.top = legend.height();
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ // Push any overlapping nodes down.
+ nodes.sort(ascendingDepth);
+ for (i = 0; i < n; ++i) {
+ node = nodes[i];
+ dy = y0 - node.y;
+ if (dy > 0) node.y += dy;
+ y0 = node.y + node.dy + nodePadding;
}
- wrap.select('.nv-legendWrap')
- .attr('transform', 'translate(0,' + (-margin.top) +')');
- } else if (legendPosition === "right") {
- var legendWidth = nv.models.legend().width();
- if (availableWidth / 2 < legendWidth) {
- legendWidth = (availableWidth / 2)
+ // If the bottommost node goes outside the bounds, push it back up.
+ dy = y0 - nodePadding - size[1];
+ if (dy > 0) {
+ y0 = node.y -= dy;
+
+ // Push any overlapping nodes back up.
+ for (i = n - 2; i >= 0; --i) {
+ node = nodes[i];
+ dy = node.y + node.dy + nodePadding - y0;
+ if (dy > 0) node.y -= dy;
+ y0 = node.y;
+ }
}
- legend.height(availableHeight).key(pie.x());
- legend.width(legendWidth);
- availableWidth -= legend.width();
+ });
+ }
- wrap.select('.nv-legendWrap')
- .datum(data)
- .call(legend)
- .attr('transform', 'translate(' + (availableWidth) +',0)');
- }
+ function ascendingDepth(a, b) {
+ return a.y - b.y;
}
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ }
- // Main Chart Component(s)
- pie.width(availableWidth).height(availableHeight);
- var pieWrap = g.select('.nv-pieWrap').datum([data]);
- d3.transition(pieWrap).call(pie);
+ // Compute y-offset of the source endpoint (sy) and target endpoints (ty) of links,
+ // relative to the source/target node's y-position.
+ function computeLinkDepths() {
+ nodes.forEach(function(node) {
+ node.sourceLinks.sort(ascendingTargetDepth);
+ node.targetLinks.sort(ascendingSourceDepth);
+ });
+ nodes.forEach(function(node) {
+ var sy = 0, ty = 0;
+ node.sourceLinks.forEach(function(link) {
+ link.sy = sy;
+ sy += link.dy;
+ });
+ node.targetLinks.forEach(function(link) {
+ link.ty = ty;
+ ty += link.dy;
+ });
+ });
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
+ function ascendingSourceDepth(a, b) {
+ return a.source.y - b.source.y;
+ }
- legend.dispatch.on('stateChange', function(newState) {
- for (var key in newState) {
- state[key] = newState[key];
- }
- dispatch.stateChange(state);
- chart.update();
- });
+ function ascendingTargetDepth(a, b) {
+ return a.target.y - b.target.y;
+ }
+ }
- // Update chart from a state object passed to event handler
- dispatch.on('changeState', function(e) {
- if (typeof e.disabled !== 'undefined') {
- data.forEach(function(series,i) {
- series.disabled = e.disabled[i];
- });
- state.disabled = e.disabled;
+ // Value property accessor.
+ function value(x) {
+ return x.value;
+ }
+
+ sankey.options = nv.utils.optionsFunc.bind(sankey);
+ sankey._options = Object.create({}, {
+ nodeWidth: {get: function(){return nodeWidth;}, set: function(_){nodeWidth=+_;}},
+ nodePadding: {get: function(){return nodePadding;}, set: function(_){nodePadding=_;}},
+ nodes: {get: function(){return nodes;}, set: function(_){nodes=_;}},
+ links: {get: function(){return links ;}, set: function(_){links=_;}},
+ size: {get: function(){return size;}, set: function(_){size=_;}},
+ sinksRight: {get: function(){return sinksRight;}, set: function(_){sinksRight=_;}},
+
+ layout: {get: function(){layout(32);}, set: function(_){layout(_);}},
+ relayout: {get: function(){relayout();}, set: function(_){}},
+ center: {get: function(){return center();}, set: function(_){
+ if(typeof _ === 'function'){
+ center=_;
}
- chart.update();
- });
+ }},
+ link: {get: function(){return link();}, set: function(_){
+ if(typeof _ === 'function'){
+ link=_;
+ }
+ return link();
+ }}
});
- renderWatch.renderEnd('pieChart immediate');
- return chart;
- }
+ nv.utils.initOptions(sankey);
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
+ return sankey;
+ };
+ nv.models.sankeyChart = function() {
+ "use strict";
- pie.dispatch.on('elementMouseover.tooltip', function(evt) {
- evt['series'] = {
- key: chart.x()(evt.data),
- value: chart.y()(evt.data),
- color: evt.color,
- percent: evt.percent
+ // Sources:
+ // - https://bost.ocks.org/mike/sankey/
+ // - https://github.com/soxofaan/d3-plugin-captain-sankey
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+
+ var margin = {top: 5, right: 0, bottom: 5, left: 0}
+ , sankey = nv.models.sankey()
+ , width = 600
+ , height = 400
+ , nodeWidth = 36
+ , nodePadding = 40
+ , units = 'units'
+ , center = undefined
+ ;
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var formatNumber = d3.format(',.0f'); // zero decimal places
+ var format = function(d) {
+ return formatNumber(d) + ' ' + units;
};
- if (!showTooltipPercent) {
- delete evt.percent;
- delete evt.series.percent;
+ var color = d3.scale.category20();
+ var linkTitle = function(d){
+ return d.source.name + ' → ' + d.target.name + '\n' + format(d.value);
+ };
+ var nodeFillColor = function(d){
+ return d.color = color(d.name.replace(/ .*/, ''));
+ };
+ var nodeStrokeColor = function(d){
+ return d3.rgb(d.color).darker(2);
+ };
+ var nodeTitle = function(d){
+ return d.name + '\n' + format(d.value);
+ };
+
+ var showError = function(element, message) {
+ element.append('text')
+ .attr('x', 0)
+ .attr('y', 0)
+ .attr('class', 'nvd3-sankey-chart-error')
+ .attr('text-anchor', 'middle')
+ .text(message);
+ };
+
+ function chart(selection) {
+ selection.each(function(data) {
+
+ var testData = {
+ nodes:
+ [
+ {'node': 1, 'name': 'Test 1'},
+ {'node': 2, 'name': 'Test 2'},
+ {'node': 3, 'name': 'Test 3'},
+ {'node': 4, 'name': 'Test 4'},
+ {'node': 5, 'name': 'Test 5'},
+ {'node': 6, 'name': 'Test 6'}
+ ],
+ links:
+ [
+ {'source': 0, 'target': 1, 'value': 2295},
+ {'source': 0, 'target': 5, 'value': 1199},
+ {'source': 1, 'target': 2, 'value': 1119},
+ {'source': 1, 'target': 5, 'value': 1176},
+ {'source': 2, 'target': 3, 'value': 487},
+ {'source': 2, 'target': 5, 'value': 632},
+ {'source': 3, 'target': 4, 'value': 301},
+ {'source': 3, 'target': 5, 'value': 186}
+ ]
+ };
+
+ // Error handling
+ var isDataValid = false;
+ var dataAvailable = false;
+
+ // check if data is valid
+ if(
+ (typeof data['nodes'] === 'object' && data['nodes'].length) >= 0 &&
+ (typeof data['links'] === 'object' && data['links'].length) >= 0
+ ){
+ isDataValid = true;
+ }
+
+ // check if data is available
+ if(
+ data['nodes'] && data['nodes'].length > 0 &&
+ data['links'] && data['links'].length > 0
+ ) {
+ dataAvailable = true;
+ }
+
+ // show error
+ if(!isDataValid) {
+ console.error('NVD3 Sankey chart error:', 'invalid data format for', data);
+ console.info('Valid data format is: ', testData, JSON.stringify(testData));
+ showError(selection, 'Error loading chart, data is invalid');
+ return false;
+ }
+
+ // TODO use nv.utils.noData
+ if(!dataAvailable) {
+ showError(selection, 'No data available');
+ return false;
+ }
+
+ // No errors, continue
+
+ // append the svg canvas to the page
+ var svg = selection.append('svg')
+ .attr('width', width)
+ .attr('height', height)
+ .append('g')
+ .attr('class', 'nvd3 nv-wrap nv-sankeyChart');
+
+ // Set the sankey diagram properties
+ sankey
+ .nodeWidth(nodeWidth)
+ .nodePadding(nodePadding)
+ .size([width, height]);
+
+ var path = sankey.link();
+
+ sankey
+ .nodes(data.nodes)
+ .links(data.links)
+ .layout(32)
+ .center(center);
+
+ // add in the links
+ var link = svg.append('g').selectAll('.link')
+ .data(data.links)
+ .enter().append('path')
+ .attr('class', 'link')
+ .attr('d', path)
+ .style('stroke-width', function(d) { return Math.max(1, d.dy); })
+ .sort(function(a,b) { return b.dy - a.dy; });
+
+ // add the link titles
+ link.append('title')
+ .text(linkTitle);
+
+ // add in the nodes
+ var node = svg.append('g').selectAll('.node')
+ .data(data.nodes)
+ .enter().append('g')
+ .attr('class', 'node')
+ .attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; })
+ .call(
+ d3.behavior
+ .drag()
+ .origin(function(d) { return d; })
+ .on('dragstart', function() {
+ this.parentNode.appendChild(this);
+ })
+ .on('drag', dragmove)
+ );
+
+ // add the rectangles for the nodes
+ node.append('rect')
+ .attr('height', function(d) { return d.dy; })
+ .attr('width', sankey.nodeWidth())
+ .style('fill', nodeFillColor)
+ .style('stroke', nodeStrokeColor)
+ .append('title')
+ .text(nodeTitle);
+
+ // add in the title for the nodes
+ node.append('text')
+ .attr('x', -6)
+ .attr('y', function(d) { return d.dy / 2; })
+ .attr('dy', '.35em')
+ .attr('text-anchor', 'end')
+ .attr('transform', null)
+ .text(function(d) { return d.name; })
+ .filter(function(d) { return d.x < width / 2; })
+ .attr('x', 6 + sankey.nodeWidth())
+ .attr('text-anchor', 'start');
+
+ // the function for moving the nodes
+ function dragmove(d) {
+ d3.select(this).attr('transform',
+ 'translate(' + d.x + ',' + (
+ d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
+ ) + ')');
+ sankey.relayout();
+ link.attr('d', path);
+ }
+ });
+
+ return chart;
}
- tooltip.data(evt).hidden(false);
- });
- pie.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true);
- });
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- pie.dispatch.on('elementMousemove.tooltip', function(evt) {
- tooltip();
- });
+ chart.options = nv.utils.optionsFunc.bind(chart);
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ units: {get: function(){return units;}, set: function(_){units=_;}},
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ format: {get: function(){return format;}, set: function(_){format=_;}},
+ linkTitle: {get: function(){return linkTitle;}, set: function(_){linkTitle=_;}},
+ nodeWidth: {get: function(){return nodeWidth;}, set: function(_){nodeWidth=_;}},
+ nodePadding: {get: function(){return nodePadding;}, set: function(_){nodePadding=_;}},
+ center: {get: function(){return center}, set: function(_){center=_}},
- // expose chart's sub-components
- chart.legend = legend;
- chart.dispatch = dispatch;
- chart.pie = pie;
- chart.tooltip = tooltip;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ nodeStyle: {get: function(){return {};}, set: function(_){
+ nodeFillColor = _.fillColor !== undefined ? _.fillColor : nodeFillColor;
+ nodeStrokeColor = _.strokeColor !== undefined ? _.strokeColor : nodeStrokeColor;
+ nodeTitle = _.title !== undefined ? _.title : nodeTitle;
+ }}
- // use Object get/set functionality to map between vars and chart functions
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- showTooltipPercent: {get: function(){return showTooltipPercent;}, set: function(_){showTooltipPercent=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}},
- defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ });
- // options that require extra logic in the setter
- color: {get: function(){return color;}, set: function(_){
- color = _;
- legend.color(color);
- pie.color(color);
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- pie.duration(duration);
- }},
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }}
- });
- nv.utils.inheritOptions(chart, pie);
- nv.utils.initOptions(chart);
- return chart;
-};
+ nv.utils.initOptions(chart);
-nv.models.scatter = function() {
- "use strict";
+ return chart;
+ };
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ nv.models.scatter = function() {
+ "use strict";
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = null
- , height = null
- , color = nv.utils.defaultColor() // chooses color
- , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't select one
- , container = null
- , x = d3.scale.linear()
- , y = d3.scale.linear()
- , z = d3.scale.linear() //linear because d3.svg.shape.size is treated as area
- , getX = function(d) { return d.x } // accessor to get the x value
- , getY = function(d) { return d.y } // accessor to get the y value
- , getSize = function(d) { return d.size || 1} // accessor to get the point size
- , getShape = function(d) { return d.shape || 'circle' } // accessor to get point shape
- , forceX = [] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
- , forceY = [] // List of numbers to Force into the Y scale
- , forceSize = [] // List of numbers to Force into the Size scale
- , interactive = true // If true, plots a voronoi overlay for advanced point intersection
- , pointActive = function(d) { return !d.notActive } // any points that return false will be filtered out
- , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
- , padDataOuter = .1 //outerPadding to imitate ordinal scale outer padding
- , clipEdge = false // if true, masks points within x and y scale
- , clipVoronoi = true // if true, masks each point with a circle... can turn off to slightly increase performance
- , showVoronoi = false // display the voronoi areas
- , clipRadius = function() { return 25 } // function to get the radius for voronoi point clips
- , xDomain = null // Override x domain (skips the calculation from data)
- , yDomain = null // Override y domain
- , xRange = null // Override x range
- , yRange = null // Override y range
- , sizeDomain = null // Override point size domain
- , sizeRange = null
- , singlePoint = false
- , dispatch = d3.dispatch('elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
- , useVoronoi = true
- , duration = 250
- , interactiveUpdateDelay = 300
- , showLabels = false
- ;
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = null
+ , height = null
+ , color = nv.utils.defaultColor() // chooses color
+ , pointBorderColor = null
+ , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't select one
+ , container = null
+ , x = d3.scale.linear()
+ , y = d3.scale.linear()
+ , z = d3.scale.linear() //linear because d3.svg.shape.size is treated as area
+ , getX = function(d) { return d.x } // accessor to get the x value
+ , getY = function(d) { return d.y } // accessor to get the y value
+ , getSize = function(d) { return d.size || 1} // accessor to get the point size
+ , getShape = function(d) { return d.shape || 'circle' } // accessor to get point shape
+ , forceX = [] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
+ , forceY = [] // List of numbers to Force into the Y scale
+ , forceSize = [] // List of numbers to Force into the Size scale
+ , interactive = true // If true, plots a voronoi overlay for advanced point intersection
+ , pointActive = function(d) { return !d.notActive } // any points that return false will be filtered out
+ , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
+ , padDataOuter = .1 //outerPadding to imitate ordinal scale outer padding
+ , clipEdge = false // if true, masks points within x and y scale
+ , clipVoronoi = true // if true, masks each point with a circle... can turn off to slightly increase performance
+ , showVoronoi = false // display the voronoi areas
+ , clipRadius = function() { return 25 } // function to get the radius for voronoi point clips
+ , xDomain = null // Override x domain (skips the calculation from data)
+ , yDomain = null // Override y domain
+ , xRange = null // Override x range
+ , yRange = null // Override y range
+ , sizeDomain = null // Override point size domain
+ , sizeRange = null
+ , singlePoint = false
+ , dispatch = d3.dispatch('elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
+ , useVoronoi = true
+ , duration = 250
+ , interactiveUpdateDelay = 300
+ , showLabels = false
+ ;
- //============================================================
- // Private Variables
- //------------------------------------------------------------
- var x0, y0, z0 // used to store previous scales
- , timeoutID
- , needsUpdate = false // Flag for when the points are visually updating, but the interactive layer is behind, to disable tooltips
- , renderWatch = nv.utils.renderWatch(dispatch, duration)
- , _sizeRange_def = [16, 256]
- , _caches
- ;
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- function getCache(d) {
- var cache, i;
- cache = _caches = _caches || {};
- i = d[0].series;
- cache = cache[i] = cache[i] || {};
- i = d[1];
- cache = cache[i] = cache[i] || {};
- return cache;
- }
+ var x0, y0, z0 // used to store previous scales
+ , width0
+ , height0
+ , timeoutID
+ , needsUpdate = false // Flag for when the points are visually updating, but the interactive layer is behind, to disable tooltips
+ , renderWatch = nv.utils.renderWatch(dispatch, duration)
+ , _sizeRange_def = [16, 256]
+ , _cache = {}
+ ;
- function getDiffs(d) {
- var i, key,
- point = d[0],
- cache = getCache(d),
- diffs = false;
- for (i = 1; i < arguments.length; i ++) {
- key = arguments[i];
- if (cache[key] !== point[key] || !cache.hasOwnProperty(key)) {
- cache[key] = point[key];
- diffs = true;
+ function getCache(d) {
+ var key, val;
+ key = d[0].series + ':' + d[1];
+ val = _cache[key] = _cache[key] || {};
+ return val;
+ }
+
+ function delCache(d) {
+ var key, val;
+ key = d[0].series + ':' + d[1];
+ delete _cache[key];
+ }
+
+ function getDiffs(d) {
+ var i, key, val,
+ cache = getCache(d),
+ diffs = false;
+ for (i = 1; i < arguments.length; i += 2) {
+ key = arguments[i];
+ val = arguments[i + 1](d[0], d[1]);
+ if (cache[key] !== val || !cache.hasOwnProperty(key)) {
+ cache[key] = val;
+ diffs = true;
+ }
}
+ return diffs;
}
- return diffs;
- }
- function chart(selection) {
- renderWatch.reset();
- selection.each(function(data) {
- container = d3.select(this);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ container = d3.select(this);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
- nv.utils.initSVG(container);
+ nv.utils.initSVG(container);
- //add series index to each data point for reference
- data.forEach(function(series, i) {
- series.values.forEach(function(point) {
- point.series = i;
+ //add series index to each data point for reference
+ data.forEach(function(series, i) {
+ series.values.forEach(function(point) {
+ point.series = i;
+ });
});
- });
- // Setup Scales
- var logScale = chart.yScale().name === d3.scale.log().name ? true : false;
- // remap and flatten the data for use in calculating the scales' domains
- var seriesData = (xDomain && yDomain && sizeDomain) ? [] : // if we know xDomain and yDomain and sizeDomain, no need to calculate.... if Size is constant remember to set sizeDomain to speed up performance
- d3.merge(
- data.map(function(d) {
- return d.values.map(function(d,i) {
- return { x: getX(d,i), y: getY(d,i), size: getSize(d,i) }
+ // Setup Scales
+ var logScale = chart.yScale().name === d3.scale.log().name ? true : false;
+ // remap and flatten the data for use in calculating the scales' domains
+ var seriesData = (xDomain && yDomain && sizeDomain) ? [] : // if we know xDomain and yDomain and sizeDomain, no need to calculate.... if Size is constant remember to set sizeDomain to speed up performance
+ d3.merge(
+ data.map(function(d) {
+ return d.values.map(function(d,i) {
+ return { x: getX(d,i), y: getY(d,i), size: getSize(d,i) }
+ })
})
- })
- );
+ );
- x .domain(xDomain || d3.extent(seriesData.map(function(d) { return d.x; }).concat(forceX)))
+ x .domain(xDomain || d3.extent(seriesData.map(function(d) { return d.x; }).concat(forceX)))
- if (padData && data[0])
- x.range(xRange || [(availableWidth * padDataOuter + availableWidth) / (2 *data[0].values.length), availableWidth - availableWidth * (1 + padDataOuter) / (2 * data[0].values.length) ]);
- //x.range([availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
- else
- x.range(xRange || [0, availableWidth]);
+ if (padData && data[0])
+ x.range(xRange || [(availableWidth * padDataOuter + availableWidth) / (2 *data[0].values.length), availableWidth - availableWidth * (1 + padDataOuter) / (2 * data[0].values.length) ]);
+ //x.range([availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]);
+ else
+ x.range(xRange || [0, availableWidth]);
- if (logScale) {
+ if (logScale) {
var min = d3.min(seriesData.map(function(d) { if (d.y !== 0) return d.y; }));
y.clamp(true)
.domain(yDomain || d3.extent(seriesData.map(function(d) {
- if (d.y !== 0) return d.y;
- else return min * 0.1;
- }).concat(forceY)))
+ if (d.y !== 0) return d.y;
+ else return min * 0.1;
+ }).concat(forceY)))
.range(yRange || [availableHeight, 0]);
} else {
- y.domain(yDomain || d3.extent(seriesData.map(function (d) { return d.y;}).concat(forceY)))
+ y.domain(yDomain || d3.extent(seriesData.map(function (d) { return d.y;}).concat(forceY)))
.range(yRange || [availableHeight, 0]);
}
- z .domain(sizeDomain || d3.extent(seriesData.map(function(d) { return d.size }).concat(forceSize)))
- .range(sizeRange || _sizeRange_def);
+ z .domain(sizeDomain || d3.extent(seriesData.map(function(d) { return d.size }).concat(forceSize)))
+ .range(sizeRange || _sizeRange_def);
- // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
- singlePoint = x.domain()[0] === x.domain()[1] || y.domain()[0] === y.domain()[1];
+ // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
+ singlePoint = x.domain()[0] === x.domain()[1] || y.domain()[0] === y.domain()[1];
- if (x.domain()[0] === x.domain()[1])
- x.domain()[0] ?
- x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
- : x.domain([-1,1]);
+ if (x.domain()[0] === x.domain()[1])
+ x.domain()[0] ?
+ x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
+ : x.domain([-1,1]);
- if (y.domain()[0] === y.domain()[1])
- y.domain()[0] ?
- y.domain([y.domain()[0] - y.domain()[0] * 0.01, y.domain()[1] + y.domain()[1] * 0.01])
- : y.domain([-1,1]);
+ if (y.domain()[0] === y.domain()[1])
+ y.domain()[0] ?
+ y.domain([y.domain()[0] - y.domain()[0] * 0.01, y.domain()[1] + y.domain()[1] * 0.01])
+ : y.domain([-1,1]);
- if ( isNaN(x.domain()[0])) {
- x.domain([-1,1]);
- }
+ if ( isNaN(x.domain()[0])) {
+ x.domain([-1,1]);
+ }
- if ( isNaN(y.domain()[0])) {
- y.domain([-1,1]);
- }
+ if ( isNaN(y.domain()[0])) {
+ y.domain([-1,1]);
+ }
- x0 = x0 || x;
- y0 = y0 || y;
- z0 = z0 || z;
+ x0 = x0 || x;
+ y0 = y0 || y;
+ z0 = z0 || z;
- var scaleDiff = x(1) !== x0(1) || y(1) !== y0(1) || z(1) !== z0(1);
+ var scaleDiff = x(1) !== x0(1) || y(1) !== y0(1) || z(1) !== z0(1);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-scatter').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatter nv-chart-' + id);
- var defsEnter = wrapEnter.append('defs');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ width0 = width0 || width;
+ height0 = height0 || height;
- wrap.classed('nv-single-point', singlePoint);
- gEnter.append('g').attr('class', 'nv-groups');
- gEnter.append('g').attr('class', 'nv-point-paths');
- wrapEnter.append('g').attr('class', 'nv-point-clips');
+ var sizeDiff = width0 !== width || height0 !== height;
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-scatter').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatter nv-chart-' + id);
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- defsEnter.append('clipPath')
- .attr('id', 'nv-edge-clip-' + id)
- .append('rect');
+ wrap.classed('nv-single-point', singlePoint);
+ gEnter.append('g').attr('class', 'nv-groups');
+ gEnter.append('g').attr('class', 'nv-point-paths');
+ wrapEnter.append('g').attr('class', 'nv-point-clips');
- wrap.select('#nv-edge-clip-' + id + ' rect')
- .attr('width', availableWidth)
- .attr('height', (availableHeight > 0) ? availableHeight : 0);
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-edge-clip-' + id)
+ .append('rect')
+ .attr('transform', 'translate( -10, -10)');
- function updateInteractiveLayer() {
- // Always clear needs-update flag regardless of whether or not
- // we will actually do anything (avoids needless invocations).
- needsUpdate = false;
+ wrap.select('#nv-edge-clip-' + id + ' rect')
+ .attr('width', availableWidth + 20)
+ .attr('height', (availableHeight > 0) ? availableHeight + 20 : 0);
- if (!interactive) return false;
+ g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
- // inject series and point index for reference into voronoi
- if (useVoronoi === true) {
- var vertices = d3.merge(data.map(function(group, groupIndex) {
- return group.values
- .map(function(point, pointIndex) {
- // *Adding noise to make duplicates very unlikely
- // *Injecting series and point index for reference
- /* *Adding a 'jitter' to the points, because there's an issue in d3.geom.voronoi.
- */
- var pX = getX(point,pointIndex);
- var pY = getY(point,pointIndex);
+ function updateInteractiveLayer() {
+ // Always clear needs-update flag regardless of whether or not
+ // we will actually do anything (avoids needless invocations).
+ needsUpdate = false;
- return [nv.utils.NaNtoZero(x(pX))+ Math.random() * 1e-4,
- nv.utils.NaNtoZero(y(pY))+ Math.random() * 1e-4,
- groupIndex,
- pointIndex, point]; //temp hack to add noise until I think of a better way so there are no duplicates
- })
- .filter(function(pointArray, pointIndex) {
- return pointActive(pointArray[4], pointIndex); // Issue #237.. move filter to after map, so pointIndex is correct!
- })
- })
- );
+ if (!interactive) return false;
- if (vertices.length == 0) return false; // No active points, we're done
- if (vertices.length < 3) {
- // Issue #283 - Adding 2 dummy points to the voronoi b/c voronoi requires min 3 points to work
- vertices.push([x.range()[0] - 20, y.range()[0] - 20, null, null]);
- vertices.push([x.range()[1] + 20, y.range()[1] + 20, null, null]);
- vertices.push([x.range()[0] - 20, y.range()[0] + 20, null, null]);
- vertices.push([x.range()[1] + 20, y.range()[1] - 20, null, null]);
- }
+ // inject series and point index for reference into voronoi
+ if (useVoronoi === true) {
+ var vertices = d3.merge(data.map(function(group, groupIndex) {
+ return group.values
+ .map(function(point, pointIndex) {
+ // *Adding noise to make duplicates very unlikely
+ // *Injecting series and point index for reference
+ /* *Adding a 'jitter' to the points, because there's an issue in d3.geom.voronoi.
+ */
+ var pX = getX(point,pointIndex);
+ var pY = getY(point,pointIndex);
- // keep voronoi sections from going more than 10 outside of graph
- // to avoid overlap with other things like legend etc
- var bounds = d3.geom.polygon([
- [-10,-10],
- [-10,height + 10],
- [width + 10,height + 10],
- [width + 10,-10]
- ]);
+ return [nv.utils.NaNtoZero(x(pX))+ Math.random() * 1e-4,
+ nv.utils.NaNtoZero(y(pY))+ Math.random() * 1e-4,
+ groupIndex,
+ pointIndex, point]; //temp hack to add noise until I think of a better way so there are no duplicates
+ })
+ .filter(function(pointArray, pointIndex) {
+ return pointActive(pointArray[4], pointIndex); // Issue #237.. move filter to after map, so pointIndex is correct!
+ })
+ })
+ );
- var voronoi = d3.geom.voronoi(vertices).map(function(d, i) {
- return {
- 'data': bounds.clip(d),
- 'series': vertices[i][2],
- 'point': vertices[i][3]
+ if (vertices.length == 0) return false; // No active points, we're done
+ if (vertices.length < 3) {
+ // Issue #283 - Adding 2 dummy points to the voronoi b/c voronoi requires min 3 points to work
+ vertices.push([x.range()[0] - 20, y.range()[0] - 20, null, null]);
+ vertices.push([x.range()[1] + 20, y.range()[1] + 20, null, null]);
+ vertices.push([x.range()[0] - 20, y.range()[0] + 20, null, null]);
+ vertices.push([x.range()[1] + 20, y.range()[1] - 20, null, null]);
}
- });
- // nuke all voronoi paths on reload and recreate them
- wrap.select('.nv-point-paths').selectAll('path').remove();
- var pointPaths = wrap.select('.nv-point-paths').selectAll('path').data(voronoi);
- var vPointPaths = pointPaths
- .enter().append("svg:path")
- .attr("d", function(d) {
- if (!d || !d.data || d.data.length === 0)
- return 'M 0 0';
- else
- return "M" + d.data.join(",") + "Z";
- })
- .attr("id", function(d,i) {
- return "nv-path-"+i; })
- .attr("clip-path", function(d,i) { return "url(#nv-clip-"+id+"-"+i+")"; })
- ;
+ // keep voronoi sections from going more than 10 outside of graph
+ // to avoid overlap with other things like legend etc
+ var bounds = d3.geom.polygon([
+ [-10,-10],
+ [-10,height + 10],
+ [width + 10,height + 10],
+ [width + 10,-10]
+ ]);
- // good for debugging point hover issues
- if (showVoronoi) {
- vPointPaths.style("fill", d3.rgb(230, 230, 230))
- .style('fill-opacity', 0.4)
- .style('stroke-opacity', 1)
- .style("stroke", d3.rgb(200,200,200));
- }
+ var voronoi = d3.geom.voronoi(vertices).map(function(d, i) {
+ return {
+ 'data': bounds.clip(d),
+ 'series': vertices[i][2],
+ 'point': vertices[i][3]
+ }
+ });
- if (clipVoronoi) {
- // voronoi sections are already set to clip,
- // just create the circles with the IDs they expect
- wrap.select('.nv-point-clips').selectAll('*').remove(); // must do * since it has sub-dom
- var pointClips = wrap.select('.nv-point-clips').selectAll('clipPath').data(vertices);
- var vPointClips = pointClips
- .enter().append("svg:clipPath")
- .attr("id", function(d, i) { return "nv-clip-"+id+"-"+i;})
- .append("svg:circle")
- .attr('cx', function(d) { return d[0]; })
- .attr('cy', function(d) { return d[1]; })
- .attr('r', clipRadius);
- }
+ // nuke all voronoi paths on reload and recreate them
+ wrap.select('.nv-point-paths').selectAll('path').remove();
+ var pointPaths = wrap.select('.nv-point-paths').selectAll('path').data(voronoi);
+ var vPointPaths = pointPaths
+ .enter().append("svg:path")
+ .attr("d", function(d) {
+ if (!d || !d.data || d.data.length === 0)
+ return 'M 0 0';
+ else
+ return "M" + d.data.join(",") + "Z";
+ })
+ .attr("id", function(d,i) {
+ return "nv-path-"+i; })
+ .attr("clip-path", function(d,i) { return "url(#nv-clip-"+id+"-"+i+")"; })
+ ;
- var mouseEventCallback = function(d, mDispatch) {
- if (needsUpdate) return 0;
- var series = data[d.series];
- if (series === undefined) return;
- var point = series.values[d.point];
- point['color'] = color(series, d.series);
+ // good for debugging point hover issues
+ if (showVoronoi) {
+ vPointPaths.style("fill", d3.rgb(230, 230, 230))
+ .style('fill-opacity', 0.4)
+ .style('stroke-opacity', 1)
+ .style("stroke", d3.rgb(200,200,200));
+ }
- // standardize attributes for tooltip.
- point['x'] = getX(point);
- point['y'] = getY(point);
+ if (clipVoronoi) {
+ // voronoi sections are already set to clip,
+ // just create the circles with the IDs they expect
+ wrap.select('.nv-point-clips').selectAll('*').remove(); // must do * since it has sub-dom
+ var pointClips = wrap.select('.nv-point-clips').selectAll('clipPath').data(vertices);
+ var vPointClips = pointClips
+ .enter().append("svg:clipPath")
+ .attr("id", function(d, i) { return "nv-clip-"+id+"-"+i;})
+ .append("svg:circle")
+ .attr('cx', function(d) { return d[0]; })
+ .attr('cy', function(d) { return d[1]; })
+ .attr('r', clipRadius);
+ }
- // can't just get box of event node since it's actually a voronoi polygon
- var box = container.node().getBoundingClientRect();
- var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
- var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
+ var mouseEventCallback = function(el, d, mDispatch) {
+ if (needsUpdate) return 0;
+ var series = data[d.series];
+ if (series === undefined) return;
+ var point = series.values[d.point];
+ point['color'] = color(series, d.series);
- var pos = {
- left: x(getX(point, d.point)) + box.left + scrollLeft + margin.left + 10,
- top: y(getY(point, d.point)) + box.top + scrollTop + margin.top + 10
- };
+ // standardize attributes for tooltip.
+ point['x'] = getX(point);
+ point['y'] = getY(point);
- mDispatch({
- point: point,
- series: series,
- pos: pos,
- relativePos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
- seriesIndex: d.series,
- pointIndex: d.point
- });
- };
+ // can't just get box of event node since it's actually a voronoi polygon
+ var box = container.node().getBoundingClientRect();
+ var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
+ var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
- pointPaths
- .on('click', function(d) {
- mouseEventCallback(d, dispatch.elementClick);
- })
- .on('dblclick', function(d) {
- mouseEventCallback(d, dispatch.elementDblClick);
- })
- .on('mouseover', function(d) {
- mouseEventCallback(d, dispatch.elementMouseover);
- })
- .on('mouseout', function(d, i) {
- mouseEventCallback(d, dispatch.elementMouseout);
- });
+ var pos = {
+ left: x(getX(point, d.point)) + box.left + scrollLeft + margin.left + 10,
+ top: y(getY(point, d.point)) + box.top + scrollTop + margin.top + 10
+ };
- } else {
- // add event handlers to points instead voronoi paths
- wrap.select('.nv-groups').selectAll('.nv-group')
- .selectAll('.nv-point')
- //.data(dataWithPoints)
- //.style('pointer-events', 'auto') // recativate events, disabled by css
- .on('click', function(d,i) {
- //nv.log('test', d, i);
- if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
- var series = data[d.series],
- point = series.values[i];
- var element = this;
- dispatch.elementClick({
+ mDispatch({
point: point,
series: series,
- pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top], //TODO: make this pos base on the page
- relativePos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
+ pos: pos,
+ relativePos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
seriesIndex: d.series,
- pointIndex: i,
+ pointIndex: d.point,
event: d3.event,
- element: element
+ element: el
});
- })
- .on('dblclick', function(d,i) {
- if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
- var series = data[d.series],
- point = series.values[i];
+ };
- dispatch.elementDblClick({
- point: point,
- series: series,
- pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],//TODO: make this pos base on the page
- relativePos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
- seriesIndex: d.series,
- pointIndex: i
+ pointPaths
+ .on('click', function(d) {
+ mouseEventCallback(this, d, dispatch.elementClick);
+ })
+ .on('dblclick', function(d) {
+ mouseEventCallback(this, d, dispatch.elementDblClick);
+ })
+ .on('mouseover', function(d) {
+ mouseEventCallback(this, d, dispatch.elementMouseover);
+ })
+ .on('mouseout', function(d, i) {
+ mouseEventCallback(this, d, dispatch.elementMouseout);
});
- })
- .on('mouseover', function(d,i) {
- if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
- var series = data[d.series],
- point = series.values[i];
- dispatch.elementMouseover({
- point: point,
- series: series,
- pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],//TODO: make this pos base on the page
- relativePos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
- seriesIndex: d.series,
- pointIndex: i,
- color: color(d, i)
- });
- })
- .on('mouseout', function(d,i) {
- if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
- var series = data[d.series],
- point = series.values[i];
+ } else {
+ // add event handlers to points instead voronoi paths
+ wrap.select('.nv-groups').selectAll('.nv-group')
+ .selectAll('.nv-point')
+ //.data(dataWithPoints)
+ //.style('pointer-events', 'auto') // recativate events, disabled by css
+ .on('click', function(d,i) {
+ //nv.log('test', d, i);
+ if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
+ var series = data[d.series],
+ point = series.values[i];
+ var element = this;
+ dispatch.elementClick({
+ point: point,
+ series: series,
+ pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top], //TODO: make this pos base on the page
+ relativePos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
+ seriesIndex: d.series,
+ pointIndex: i,
+ event: d3.event,
+ element: element
+ });
+ })
+ .on('dblclick', function(d,i) {
+ if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
+ var series = data[d.series],
+ point = series.values[i];
- dispatch.elementMouseout({
- point: point,
- series: series,
- pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],//TODO: make this pos base on the page
- relativePos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
- seriesIndex: d.series,
- pointIndex: i,
- color: color(d, i)
+ dispatch.elementDblClick({
+ point: point,
+ series: series,
+ pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],//TODO: make this pos base on the page
+ relativePos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
+ seriesIndex: d.series,
+ pointIndex: i
+ });
+ })
+ .on('mouseover', function(d,i) {
+ if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
+ var series = data[d.series],
+ point = series.values[i];
+
+ dispatch.elementMouseover({
+ point: point,
+ series: series,
+ pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],//TODO: make this pos base on the page
+ relativePos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
+ seriesIndex: d.series,
+ pointIndex: i,
+ color: color(d, i)
+ });
+ })
+ .on('mouseout', function(d,i) {
+ if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
+ var series = data[d.series],
+ point = series.values[i];
+
+ dispatch.elementMouseout({
+ point: point,
+ series: series,
+ pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],//TODO: make this pos base on the page
+ relativePos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
+ seriesIndex: d.series,
+ pointIndex: i,
+ color: color(d, i)
+ });
});
- });
+ }
}
- }
- needsUpdate = true;
- var groups = wrap.select('.nv-groups').selectAll('.nv-group')
- .data(function(d) { return d }, function(d) { return d.key });
- groups.enter().append('g')
- .style('stroke-opacity', 1e-6)
- .style('fill-opacity', 1e-6);
- groups.exit()
- .remove();
- groups
- .attr('class', function(d,i) {
- return (d.classed || '') + ' nv-group nv-series-' + i;
- })
- .classed('nv-noninteractive', !interactive)
- .classed('hover', function(d) { return d.hover });
- groups.watchTransition(renderWatch, 'scatter: groups')
- .style('fill', function(d,i) { return color(d, i) })
- .style('stroke', function(d,i) { return color(d, i) })
- .style('stroke-opacity', 1)
- .style('fill-opacity', .5);
+ needsUpdate = true;
+ var groups = wrap.select('.nv-groups').selectAll('.nv-group')
+ .data(function(d) { return d }, function(d) { return d.key });
+ groups.enter().append('g')
+ .style('stroke-opacity', 1e-6)
+ .style('fill-opacity', 1e-6);
+ groups.exit()
+ .remove();
+ groups
+ .attr('class', function(d,i) {
+ return (d.classed || '') + ' nv-group nv-series-' + i;
+ })
+ .classed('nv-noninteractive', !interactive)
+ .classed('hover', function(d) { return d.hover });
+ groups.watchTransition(renderWatch, 'scatter: groups')
+ .style('fill', function(d,i) { return color(d, i) })
+ .style('stroke', function(d,i) { return d.pointBorderColor || pointBorderColor || color(d, i) })
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', .5);
- // create the points, maintaining their IDs from the original data set
- var points = groups.selectAll('path.nv-point')
- .data(function(d) {
- return d.values.map(
- function (point, pointIndex) {
- return [point, pointIndex]
- }).filter(
- function(pointArray, pointIndex) {
- return pointActive(pointArray[0], pointIndex)
- })
- });
- points.enter().append('path')
- .attr('class', function (d) {
- return 'nv-point nv-point-' + d[1];
- })
- .style('fill', function (d) { return d.color })
- .style('stroke', function (d) { return d.color })
- .attr('transform', function(d) {
- return 'translate(' + nv.utils.NaNtoZero(x0(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y0(getY(d[0],d[1]))) + ')'
- })
- .attr('d',
- nv.utils.symbol()
- .type(function(d) { return getShape(d[0]); })
- .size(function(d) { return z(getSize(d[0],d[1])) })
- );
- points.exit().remove();
- groups.exit().selectAll('path.nv-point')
- .watchTransition(renderWatch, 'scatter exit')
- .attr('transform', function(d) {
- return 'translate(' + nv.utils.NaNtoZero(x(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
- })
- .remove();
- points.filter(function (d) { return scaleDiff || getDiffs(d, 'x', 'y'); })
- .watchTransition(renderWatch, 'scatter points')
- .attr('transform', function(d) {
- //nv.log(d, getX(d[0],d[1]), x(getX(d[0],d[1])));
- return 'translate(' + nv.utils.NaNtoZero(x(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
- });
- points.filter(function (d) { return scaleDiff || getDiffs(d, 'shape', 'size'); })
- .watchTransition(renderWatch, 'scatter points')
- .attr('d',
- nv.utils.symbol()
- .type(function(d) { return getShape(d[0]); })
- .size(function(d) { return z(getSize(d[0],d[1])) })
- );
-
- // add label a label to scatter chart
- if(showLabels)
- {
- var titles = groups.selectAll('.nv-label')
+ // create the points, maintaining their IDs from the original data set
+ var points = groups.selectAll('path.nv-point')
.data(function(d) {
return d.values.map(
function (point, pointIndex) {
return [point, pointIndex]
}).filter(
- function(pointArray, pointIndex) {
- return pointActive(pointArray[0], pointIndex)
- })
- });
-
- titles.enter().append('text')
- .style('fill', function (d,i) {
- return d.color })
- .style('stroke-opacity', 0)
- .style('fill-opacity', 1)
+ function(pointArray, pointIndex) {
+ return pointActive(pointArray[0], pointIndex)
+ })
+ });
+ points.enter().append('path')
+ .attr('class', function (d) {
+ return 'nv-point nv-point-' + d[1];
+ })
+ .style('fill', function (d) { return d.color })
+ .style('stroke', function (d) { return d.color })
.attr('transform', function(d) {
- var dx = nv.utils.NaNtoZero(x0(getX(d[0],d[1]))) + Math.sqrt(z(getSize(d[0],d[1]))/Math.PI) + 2;
- return 'translate(' + dx + ',' + nv.utils.NaNtoZero(y0(getY(d[0],d[1]))) + ')';
+ return 'translate(' + nv.utils.NaNtoZero(x0(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y0(getY(d[0],d[1]))) + ')'
})
- .text(function(d,i){
- return d[0].label;});
-
- titles.exit().remove();
- groups.exit().selectAll('path.nv-label')
+ .attr('d',
+ nv.utils.symbol()
+ .type(function(d) { return getShape(d[0]); })
+ .size(function(d) { return z(getSize(d[0],d[1])) })
+ );
+ points.exit().each(delCache).remove();
+ groups.exit().selectAll('path.nv-point')
.watchTransition(renderWatch, 'scatter exit')
.attr('transform', function(d) {
- var dx = nv.utils.NaNtoZero(x(getX(d[0],d[1])))+ Math.sqrt(z(getSize(d[0],d[1]))/Math.PI)+2;
- return 'translate(' + dx + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')';
+ return 'translate(' + nv.utils.NaNtoZero(x(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
})
.remove();
- titles.each(function(d) {
- d3.select(this)
- .classed('nv-label', true)
- .classed('nv-label-' + d[1], false)
- .classed('hover',false);
- });
- titles.watchTransition(renderWatch, 'scatter labels')
+ // Update points position only if "x" or "y" have changed
+ points.filter(function (d) { return scaleDiff || sizeDiff || getDiffs(d, 'x', getX, 'y', getY); })
+ .watchTransition(renderWatch, 'scatter points')
.attr('transform', function(d) {
- var dx = nv.utils.NaNtoZero(x(getX(d[0],d[1])))+ Math.sqrt(z(getSize(d[0],d[1]))/Math.PI)+2;
- return 'translate(' + dx + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
+ //nv.log(d, getX(d[0],d[1]), x(getX(d[0],d[1])));
+ return 'translate(' + nv.utils.NaNtoZero(x(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
});
- }
+ // Update points appearance only if "shape" or "size" have changed
+ points.filter(function (d) { return scaleDiff || sizeDiff || getDiffs(d, 'shape', getShape, 'size', getSize); })
+ .watchTransition(renderWatch, 'scatter points')
+ .attr('d',
+ nv.utils.symbol()
+ .type(function(d) { return getShape(d[0]); })
+ .size(function(d) { return z(getSize(d[0],d[1])) })
+ );
- // Delay updating the invisible interactive layer for smoother animation
- if( interactiveUpdateDelay )
- {
- clearTimeout(timeoutID); // stop repeat calls to updateInteractiveLayer
- timeoutID = setTimeout(updateInteractiveLayer, interactiveUpdateDelay );
- }
- else
- {
- updateInteractiveLayer();
- }
+ // add label a label to scatter chart
+ if(showLabels)
+ {
+ var titles = groups.selectAll('.nv-label')
+ .data(function(d) {
+ return d.values.map(
+ function (point, pointIndex) {
+ return [point, pointIndex]
+ }).filter(
+ function(pointArray, pointIndex) {
+ return pointActive(pointArray[0], pointIndex)
+ })
+ });
- //store old scales for use in transitions on update
- x0 = x.copy();
- y0 = y.copy();
- z0 = z.copy();
+ titles.enter().append('text')
+ .style('fill', function (d,i) {
+ return d.color })
+ .style('stroke-opacity', 0)
+ .style('fill-opacity', 1)
+ .attr('transform', function(d) {
+ var dx = nv.utils.NaNtoZero(x0(getX(d[0],d[1]))) + Math.sqrt(z(getSize(d[0],d[1]))/Math.PI) + 2;
+ return 'translate(' + dx + ',' + nv.utils.NaNtoZero(y0(getY(d[0],d[1]))) + ')';
+ })
+ .text(function(d,i){
+ return d[0].label;});
- });
- renderWatch.renderEnd('scatter immediate');
- return chart;
- }
+ titles.exit().remove();
+ groups.exit().selectAll('path.nv-label')
+ .watchTransition(renderWatch, 'scatter exit')
+ .attr('transform', function(d) {
+ var dx = nv.utils.NaNtoZero(x(getX(d[0],d[1])))+ Math.sqrt(z(getSize(d[0],d[1]))/Math.PI)+2;
+ return 'translate(' + dx + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')';
+ })
+ .remove();
+ titles.each(function(d) {
+ d3.select(this)
+ .classed('nv-label', true)
+ .classed('nv-label-' + d[1], false)
+ .classed('hover',false);
+ });
+ titles.watchTransition(renderWatch, 'scatter labels')
+ .attr('transform', function(d) {
+ var dx = nv.utils.NaNtoZero(x(getX(d[0],d[1])))+ Math.sqrt(z(getSize(d[0],d[1]))/Math.PI)+2;
+ return 'translate(' + dx + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
+ });
+ }
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ // Delay updating the invisible interactive layer for smoother animation
+ if( interactiveUpdateDelay )
+ {
+ clearTimeout(timeoutID); // stop repeat calls to updateInteractiveLayer
+ timeoutID = setTimeout(updateInteractiveLayer, interactiveUpdateDelay );
+ }
+ else
+ {
+ updateInteractiveLayer();
+ }
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ //store old scales for use in transitions on update
+ x0 = x.copy();
+ y0 = y.copy();
+ z0 = z.copy();
- // utility function calls provided by this chart
- chart._calls = new function() {
- this.clearHighlights = function () {
- nv.dom.write(function() {
- container.selectAll(".nv-point.hover").classed("hover", false);
+ width0 = width;
+ height0 = height;
+
});
- return null;
+ renderWatch.renderEnd('scatter immediate');
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ // utility function calls provided by this chart
+ chart._calls = new function() {
+ this.clearHighlights = function () {
+ nv.dom.write(function() {
+ container.selectAll(".nv-point.hover").classed("hover", false);
+ });
+ return null;
+ };
+ this.highlightPoint = function (seriesIndex, pointIndex, isHoverOver) {
+ nv.dom.write(function() {
+ container.select('.nv-groups')
+ .selectAll(".nv-series-" + seriesIndex)
+ .selectAll(".nv-point-" + pointIndex)
+ .classed("hover", isHoverOver);
+ });
+ };
};
- this.highlightPoint = function (seriesIndex, pointIndex, isHoverOver) {
- nv.dom.write(function() {
- container.select('.nv-groups')
- .selectAll(".nv-series-" + seriesIndex)
- .selectAll(".nv-point-" + pointIndex)
- .classed("hover", isHoverOver);
- });
- };
- };
- // trigger calls from events too
- dispatch.on('elementMouseover.point', function(d) {
- if (interactive) chart._calls.highlightPoint(d.seriesIndex,d.pointIndex,true);
- });
+ // trigger calls from events too
+ dispatch.on('elementMouseover.point', function(d) {
+ if (interactive) chart._calls.highlightPoint(d.seriesIndex,d.pointIndex,true);
+ });
- dispatch.on('elementMouseout.point', function(d) {
- if (interactive) chart._calls.highlightPoint(d.seriesIndex,d.pointIndex,false);
- });
+ dispatch.on('elementMouseout.point', function(d) {
+ if (interactive) chart._calls.highlightPoint(d.seriesIndex,d.pointIndex,false);
+ });
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- xScale: {get: function(){return x;}, set: function(_){x=_;}},
- yScale: {get: function(){return y;}, set: function(_){y=_;}},
- pointScale: {get: function(){return z;}, set: function(_){z=_;}},
- xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
- yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
- pointDomain: {get: function(){return sizeDomain;}, set: function(_){sizeDomain=_;}},
- xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
- yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
- pointRange: {get: function(){return sizeRange;}, set: function(_){sizeRange=_;}},
- forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
- forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
- forcePoint: {get: function(){return forceSize;}, set: function(_){forceSize=_;}},
- interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}},
- pointActive: {get: function(){return pointActive;}, set: function(_){pointActive=_;}},
- padDataOuter: {get: function(){return padDataOuter;}, set: function(_){padDataOuter=_;}},
- padData: {get: function(){return padData;}, set: function(_){padData=_;}},
- clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
- clipVoronoi: {get: function(){return clipVoronoi;}, set: function(_){clipVoronoi=_;}},
- clipRadius: {get: function(){return clipRadius;}, set: function(_){clipRadius=_;}},
- showVoronoi: {get: function(){return showVoronoi;}, set: function(_){showVoronoi=_;}},
- id: {get: function(){return id;}, set: function(_){id=_;}},
- interactiveUpdateDelay: {get:function(){return interactiveUpdateDelay;}, set: function(_){interactiveUpdateDelay=_;}},
- showLabels: {get: function(){return showLabels;}, set: function(_){ showLabels = _;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ pointScale: {get: function(){return z;}, set: function(_){z=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ pointDomain: {get: function(){return sizeDomain;}, set: function(_){sizeDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ pointRange: {get: function(){return sizeRange;}, set: function(_){sizeRange=_;}},
+ forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}},
+ forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}},
+ forcePoint: {get: function(){return forceSize;}, set: function(_){forceSize=_;}},
+ interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}},
+ pointActive: {get: function(){return pointActive;}, set: function(_){pointActive=_;}},
+ padDataOuter: {get: function(){return padDataOuter;}, set: function(_){padDataOuter=_;}},
+ padData: {get: function(){return padData;}, set: function(_){padData=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+ clipVoronoi: {get: function(){return clipVoronoi;}, set: function(_){clipVoronoi=_;}},
+ clipRadius: {get: function(){return clipRadius;}, set: function(_){clipRadius=_;}},
+ showVoronoi: {get: function(){return showVoronoi;}, set: function(_){showVoronoi=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ interactiveUpdateDelay: {get:function(){return interactiveUpdateDelay;}, set: function(_){interactiveUpdateDelay=_;}},
+ showLabels: {get: function(){return showLabels;}, set: function(_){ showLabels = _;}},
+ pointBorderColor: {get: function(){return pointBorderColor;}, set: function(_){pointBorderColor=_;}},
- // simple functor options
- x: {get: function(){return getX;}, set: function(_){getX = d3.functor(_);}},
- y: {get: function(){return getY;}, set: function(_){getY = d3.functor(_);}},
- pointSize: {get: function(){return getSize;}, set: function(_){getSize = d3.functor(_);}},
- pointShape: {get: function(){return getShape;}, set: function(_){getShape = d3.functor(_);}},
+ // simple functor options
+ x: {get: function(){return getX;}, set: function(_){getX = d3.functor(_);}},
+ y: {get: function(){return getY;}, set: function(_){getY = d3.functor(_);}},
+ pointSize: {get: function(){return getSize;}, set: function(_){getSize = d3.functor(_);}},
+ pointShape: {get: function(){return getShape;}, set: function(_){getShape = d3.functor(_);}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }},
- useVoronoi: {get: function(){return useVoronoi;}, set: function(_){
- useVoronoi = _;
- if (useVoronoi === false) {
- clipVoronoi = false;
- }
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ useVoronoi: {get: function(){return useVoronoi;}, set: function(_){
+ useVoronoi = _;
+ if (useVoronoi === false) {
+ clipVoronoi = false;
+ }
+ }}
+ });
- nv.utils.initOptions(chart);
- return chart;
-};
+ nv.utils.initOptions(chart);
+ return chart;
+ };
-nv.models.scatterChart = function() {
- "use strict";
+ nv.models.scatterChart = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var scatter = nv.models.scatter()
- , xAxis = nv.models.axis()
- , yAxis = nv.models.axis()
- , legend = nv.models.legend()
- , distX = nv.models.distribution()
- , distY = nv.models.distribution()
- , tooltip = nv.models.tooltip()
- ;
+ var scatter = nv.models.scatter()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend()
+ , distX = nv.models.distribution()
+ , distY = nv.models.distribution()
+ , tooltip = nv.models.tooltip()
+ ;
- var margin = {top: 30, right: 20, bottom: 50, left: 75}
- , width = null
- , height = null
- , container = null
- , color = nv.utils.defaultColor()
- , x = scatter.xScale()
- , y = scatter.yScale()
- , showDistX = false
- , showDistY = false
- , showLegend = true
- , showXAxis = true
- , showYAxis = true
- , rightAlignYAxis = false
- , state = nv.utils.state()
- , defaultState = null
- , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd')
- , noData = null
- , duration = 250
- , showLabels = false
+ var margin = {top: 30, right: 20, bottom: 50, left: 75}
+ , marginTop = null
+ , width = null
+ , height = null
+ , container = null
+ , color = nv.utils.defaultColor()
+ , x = scatter.xScale()
+ , y = scatter.yScale()
+ , showDistX = false
+ , showDistY = false
+ , showLegend = true
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , state = nv.utils.state()
+ , defaultState = null
+ , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd')
+ , noData = null
+ , duration = 250
+ , showLabels = false
+ ;
+
+ scatter.xScale(x).yScale(y);
+ xAxis.orient('bottom').tickPadding(10);
+ yAxis
+ .orient((rightAlignYAxis) ? 'right' : 'left')
+ .tickPadding(10)
;
+ distX.axis('x');
+ distY.axis('y');
+ tooltip
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ })
+ .valueFormatter(function(d, i) {
+ return yAxis.tickFormat()(d, i);
+ });
- scatter.xScale(x).yScale(y);
- xAxis.orient('bottom').tickPadding(10);
- yAxis
- .orient((rightAlignYAxis) ? 'right' : 'left')
- .tickPadding(10)
- ;
- distX.axis('x');
- distY.axis('y');
- tooltip
- .headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- })
- .valueFormatter(function(d, i) {
- return yAxis.tickFormat()(d, i);
- });
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ var x0, y0
+ , renderWatch = nv.utils.renderWatch(dispatch, duration);
- var x0, y0
- , renderWatch = nv.utils.renderWatch(dispatch, duration);
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled })
+ };
+ }
+ };
- var stateGetter = function(data) {
- return function(){
- return {
- active: data.map(function(d) { return !d.disabled })
- };
- }
- };
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ };
- var stateSetter = function(data) {
- return function(state) {
- if (state.active !== undefined)
- data.forEach(function(series,i) {
- series.disabled = !state.active[i];
- });
- }
- };
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(scatter);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
+ if (showDistX) renderWatch.models(distX);
+ if (showDistY) renderWatch.models(distY);
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(scatter);
- if (showXAxis) renderWatch.models(xAxis);
- if (showYAxis) renderWatch.models(yAxis);
- if (showDistX) renderWatch.models(distX);
- if (showDistY) renderWatch.models(distY);
+ selection.each(function(data) {
+ var that = this;
- selection.each(function(data) {
- var that = this;
+ container = d3.select(this);
+ nv.utils.initSVG(container);
- container = d3.select(this);
- nv.utils.initSVG(container);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ chart.update = function() {
+ if (duration === 0)
+ container.call(chart);
+ else
+ container.transition().duration(duration).call(chart);
+ };
+ chart.container = this;
- chart.update = function() {
- if (duration === 0)
- container.call(chart);
- else
- container.transition().duration(duration).call(chart);
- };
- chart.container = this;
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
- state
- .setter(stateSetter(data), chart.update)
- .getter(stateGetter(data))
- .update();
+ // DEPRECATED set state.disableddisabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
- // DEPRECATED set state.disableddisabled
- state.disabled = data.map(function(d) { return !!d.disabled });
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
+ }
- if (!defaultState) {
- var key;
- defaultState = {};
- for (key in state) {
- if (state[key] instanceof Array)
- defaultState[key] = state[key].slice(0);
- else
- defaultState[key] = state[key];
+ // Display noData message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ nv.utils.noData(chart, container);
+ renderWatch.renderEnd('scatter immediate');
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
}
- }
- // Display noData message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
- nv.utils.noData(chart, container);
- renderWatch.renderEnd('scatter immediate');
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
+ // Setup Scales
+ x = scatter.xScale();
+ y = scatter.yScale();
- // Setup Scales
- x = scatter.xScale();
- y = scatter.yScale();
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-scatterChart').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatterChart nv-chart-' + scatter.id());
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-scatterChart').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatterChart nv-chart-' + scatter.id());
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ // background for pointer events
+ gEnter.append('rect').attr('class', 'nvd3 nv-background').style("pointer-events","none");
- // background for pointer events
- gEnter.append('rect').attr('class', 'nvd3 nv-background').style("pointer-events","none");
+ gEnter.append('g').attr('class', 'nv-x nv-axis');
+ gEnter.append('g').attr('class', 'nv-y nv-axis');
+ gEnter.append('g').attr('class', 'nv-scatterWrap');
+ gEnter.append('g').attr('class', 'nv-regressionLinesWrap');
+ gEnter.append('g').attr('class', 'nv-distWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
- gEnter.append('g').attr('class', 'nv-x nv-axis');
- gEnter.append('g').attr('class', 'nv-y nv-axis');
- gEnter.append('g').attr('class', 'nv-scatterWrap');
- gEnter.append('g').attr('class', 'nv-regressionLinesWrap');
- gEnter.append('g').attr('class', 'nv-distWrap');
- gEnter.append('g').attr('class', 'nv-legendWrap');
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
- if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
- }
+ // Legend
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
+ var legendWidth = availableWidth;
+ legend.width(legendWidth);
- // Legend
- if (!showLegend) {
- g.select('.nv-legendWrap').selectAll('*').remove();
- } else {
- var legendWidth = availableWidth;
- legend.width(legendWidth);
+ wrap.select('.nv-legendWrap')
+ .datum(data)
+ .call(legend);
- wrap.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
+ if (!marginTop && legend.height() !== margin.top) {
+ margin.top = legend.height();
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+ }
- if (legend.height() > margin.top) {
- margin.top = legend.height();
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ wrap.select('.nv-legendWrap')
+ .attr('transform', 'translate(0' + ',' + (-margin.top) +')');
}
- wrap.select('.nv-legendWrap')
- .attr('transform', 'translate(0' + ',' + (-margin.top) +')');
- }
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ // Main Chart Component(s)
+ scatter
+ .width(availableWidth)
+ .height(availableHeight)
+ .color(data.map(function(d,i) {
+ d.color = d.color || color(d, i);
+ return d.color;
+ }).filter(function(d,i) { return !data[i].disabled }))
+ .showLabels(showLabels);
- // Main Chart Component(s)
- scatter
- .width(availableWidth)
- .height(availableHeight)
- .color(data.map(function(d,i) {
- d.color = d.color || color(d, i);
- return d.color;
- }).filter(function(d,i) { return !data[i].disabled }))
- .showLabels(showLabels);
+ wrap.select('.nv-scatterWrap')
+ .datum(data.filter(function(d) { return !d.disabled }))
+ .call(scatter);
- wrap.select('.nv-scatterWrap')
- .datum(data.filter(function(d) { return !d.disabled }))
- .call(scatter);
+ wrap.select('.nv-regressionLinesWrap')
+ .attr('clip-path', 'url(#nv-edge-clip-' + scatter.id() + ')');
- wrap.select('.nv-regressionLinesWrap')
- .attr('clip-path', 'url(#nv-edge-clip-' + scatter.id() + ')');
+ var regWrap = wrap.select('.nv-regressionLinesWrap').selectAll('.nv-regLines')
+ .data(function (d) {
+ return d;
+ });
- var regWrap = wrap.select('.nv-regressionLinesWrap').selectAll('.nv-regLines')
- .data(function (d) {
- return d;
- });
+ regWrap.enter().append('g').attr('class', 'nv-regLines');
- regWrap.enter().append('g').attr('class', 'nv-regLines');
+ var regLine = regWrap.selectAll('.nv-regLine')
+ .data(function (d) {
+ return [d]
+ });
- var regLine = regWrap.selectAll('.nv-regLine')
- .data(function (d) {
- return [d]
- });
+ regLine.enter()
+ .append('line').attr('class', 'nv-regLine')
+ .style('stroke-opacity', 0);
- regLine.enter()
- .append('line').attr('class', 'nv-regLine')
- .style('stroke-opacity', 0);
-
- // don't add lines unless we have slope and intercept to use
- regLine.filter(function(d) {
- return d.intercept && d.slope;
- })
- .watchTransition(renderWatch, 'scatterPlusLineChart: regline')
- .attr('x1', x.range()[0])
- .attr('x2', x.range()[1])
- .attr('y1', function (d, i) {
- return y(x.domain()[0] * d.slope + d.intercept)
+ // don't add lines unless we have slope and intercept to use
+ regLine.filter(function(d) {
+ return d.intercept && d.slope;
})
- .attr('y2', function (d, i) {
- return y(x.domain()[1] * d.slope + d.intercept)
- })
- .style('stroke', function (d, i, j) {
- return color(d, j)
- })
- .style('stroke-opacity', function (d, i) {
- return (d.disabled || typeof d.slope === 'undefined' || typeof d.intercept === 'undefined') ? 0 : 1
- });
+ .watchTransition(renderWatch, 'scatterPlusLineChart: regline')
+ .attr('x1', x.range()[0])
+ .attr('x2', x.range()[1])
+ .attr('y1', function (d, i) {
+ return y(x.domain()[0] * d.slope + d.intercept)
+ })
+ .attr('y2', function (d, i) {
+ return y(x.domain()[1] * d.slope + d.intercept)
+ })
+ .style('stroke', function (d, i, j) {
+ return color(d, j)
+ })
+ .style('stroke-opacity', function (d, i) {
+ return (d.disabled || typeof d.slope === 'undefined' || typeof d.intercept === 'undefined') ? 0 : 1
+ });
- // Setup Axes
- if (showXAxis) {
- xAxis
- .scale(x)
- ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize( -availableHeight , 0);
+ // Setup Axes
+ if (showXAxis) {
+ xAxis
+ .scale(x)
+ ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize( -availableHeight , 0);
- g.select('.nv-x.nv-axis')
- .attr('transform', 'translate(0,' + y.range()[0] + ')')
- .call(xAxis);
- }
+ g.select('.nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + y.range()[0] + ')')
+ .call(xAxis);
+ }
- if (showYAxis) {
- yAxis
- .scale(y)
- ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
+ if (showYAxis) {
+ yAxis
+ .scale(y)
+ ._ticks( nv.utils.calcTicksY(availableHeight/36, data) )
+ .tickSize( -availableWidth, 0);
- g.select('.nv-y.nv-axis')
- .call(yAxis);
- }
+ g.select('.nv-y.nv-axis')
+ .call(yAxis);
+ }
- // Setup Distribution
- if (showDistX) {
- distX
- .getData(scatter.x())
- .scale(x)
- .width(availableWidth)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled }));
- gEnter.select('.nv-distWrap').append('g')
- .attr('class', 'nv-distributionX');
- g.select('.nv-distributionX')
- .attr('transform', 'translate(0,' + y.range()[0] + ')')
- .datum(data.filter(function(d) { return !d.disabled }))
- .call(distX);
- }
+ // Setup Distribution
+ if (showDistX) {
+ distX
+ .getData(scatter.x())
+ .scale(x)
+ .width(availableWidth)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled }));
+ gEnter.select('.nv-distWrap').append('g')
+ .attr('class', 'nv-distributionX');
+ g.select('.nv-distributionX')
+ .attr('transform', 'translate(0,' + y.range()[0] + ')')
+ .datum(data.filter(function(d) { return !d.disabled }))
+ .call(distX);
+ }
- if (showDistY) {
- distY
- .getData(scatter.y())
- .scale(y)
- .width(availableHeight)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled }));
- gEnter.select('.nv-distWrap').append('g')
- .attr('class', 'nv-distributionY');
- g.select('.nv-distributionY')
- .attr('transform', 'translate(' + (rightAlignYAxis ? availableWidth : -distY.size() ) + ',0)')
- .datum(data.filter(function(d) { return !d.disabled }))
- .call(distY);
- }
+ if (showDistY) {
+ distY
+ .getData(scatter.y())
+ .scale(y)
+ .width(availableHeight)
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled }));
+ gEnter.select('.nv-distWrap').append('g')
+ .attr('class', 'nv-distributionY');
+ g.select('.nv-distributionY')
+ .attr('transform', 'translate(' + (rightAlignYAxis ? availableWidth : -distY.size() ) + ',0)')
+ .datum(data.filter(function(d) { return !d.disabled }))
+ .call(distY);
+ }
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
- legend.dispatch.on('stateChange', function(newState) {
- for (var key in newState)
- state[key] = newState[key];
- dispatch.stateChange(state);
- chart.update();
- });
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
- // Update chart from a state object passed to event handler
- dispatch.on('changeState', function(e) {
- if (typeof e.disabled !== 'undefined') {
- data.forEach(function(series,i) {
- series.disabled = e.disabled[i];
- });
- state.disabled = e.disabled;
- }
- chart.update();
- });
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+ if (typeof e.disabled !== 'undefined') {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+ state.disabled = e.disabled;
+ }
+ chart.update();
+ });
- // mouseover needs availableHeight so we just keep scatter mouse events inside the chart block
- scatter.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true);
- container.select('.nv-chart-' + scatter.id() + ' .nv-series-' + evt.seriesIndex + ' .nv-distx-' + evt.pointIndex)
- .attr('y1', 0);
- container.select('.nv-chart-' + scatter.id() + ' .nv-series-' + evt.seriesIndex + ' .nv-disty-' + evt.pointIndex)
- .attr('x2', distY.size());
- });
+ // mouseover needs availableHeight so we just keep scatter mouse events inside the chart block
+ scatter.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true);
+ container.select('.nv-chart-' + scatter.id() + ' .nv-series-' + evt.seriesIndex + ' .nv-distx-' + evt.pointIndex)
+ .attr('y1', 0);
+ container.select('.nv-chart-' + scatter.id() + ' .nv-series-' + evt.seriesIndex + ' .nv-disty-' + evt.pointIndex)
+ .attr('x2', distY.size());
+ });
- scatter.dispatch.on('elementMouseover.tooltip', function(evt) {
- container.select('.nv-series-' + evt.seriesIndex + ' .nv-distx-' + evt.pointIndex)
- .attr('y1', evt.relativePos[1] - availableHeight);
- container.select('.nv-series-' + evt.seriesIndex + ' .nv-disty-' + evt.pointIndex)
- .attr('x2', evt.relativePos[0] + distX.size());
- tooltip.data(evt).hidden(false);
- });
+ scatter.dispatch.on('elementMouseover.tooltip', function(evt) {
+ container.select('.nv-series-' + evt.seriesIndex + ' .nv-distx-' + evt.pointIndex)
+ .attr('y1', evt.relativePos[1] - availableHeight);
+ container.select('.nv-series-' + evt.seriesIndex + ' .nv-disty-' + evt.pointIndex)
+ .attr('x2', evt.relativePos[0] + distX.size());
+ tooltip.data(evt).hidden(false);
+ });
- //store old scales for use in transitions on update
- x0 = x.copy();
- y0 = y.copy();
+ //store old scales for use in transitions on update
+ x0 = x.copy();
+ y0 = y.copy();
- });
+ });
- renderWatch.renderEnd('scatter with line immediate');
- return chart;
- }
+ renderWatch.renderEnd('scatter with line immediate');
+ return chart;
+ }
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.scatter = scatter;
- chart.legend = legend;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.distX = distX;
- chart.distY = distY;
- chart.tooltip = tooltip;
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.scatter = scatter;
+ chart.legend = legend;
+ chart.xAxis = xAxis;
+ chart.yAxis = yAxis;
+ chart.distX = distX;
+ chart.distY = distY;
+ chart.tooltip = tooltip;
- chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- container: {get: function(){return container;}, set: function(_){container=_;}},
- showDistX: {get: function(){return showDistX;}, set: function(_){showDistX=_;}},
- showDistY: {get: function(){return showDistY;}, set: function(_){showDistY=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- duration: {get: function(){return duration;}, set: function(_){duration=_;}},
- showLabels: {get: function(){return showLabels;}, set: function(_){showLabels=_;}},
+ chart.options = nv.utils.optionsFunc.bind(chart);
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ container: {get: function(){return container;}, set: function(_){container=_;}},
+ showDistX: {get: function(){return showDistX;}, set: function(_){showDistX=_;}},
+ showDistY: {get: function(){return showDistY;}, set: function(_){showDistY=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ duration: {get: function(){return duration;}, set: function(_){duration=_;}},
+ showLabels: {get: function(){return showLabels;}, set: function(_){showLabels=_;}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
- rightAlignYAxis = _;
- yAxis.orient( (_) ? 'right' : 'left');
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- legend.color(color);
- distX.color(color);
- distY.color(color);
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ if (_.top !== undefined) {
+ margin.top = _.top;
+ marginTop = _.top;
+ }
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( (_) ? 'right' : 'left');
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ distX.color(color);
+ distY.color(color);
+ }}
+ });
- nv.utils.inheritOptions(chart, scatter);
- nv.utils.initOptions(chart);
- return chart;
-};
+ nv.utils.inheritOptions(chart, scatter);
+ nv.utils.initOptions(chart);
+ return chart;
+ };
-nv.models.sparkline = function() {
- "use strict";
+ nv.models.sparkline = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var margin = {top: 2, right: 0, bottom: 2, left: 0}
- , width = 400
- , height = 32
- , container = null
- , animate = true
- , x = d3.scale.linear()
- , y = d3.scale.linear()
- , getX = function(d) { return d.x }
- , getY = function(d) { return d.y }
- , color = nv.utils.getColor(['#000'])
- , xDomain
- , yDomain
- , xRange
- , yRange
- , showMinMaxPoints = true
- , showCurrentPoint = true
- , dispatch = d3.dispatch('renderEnd')
- ;
+ var margin = {top: 2, right: 0, bottom: 2, left: 0}
+ , width = 400
+ , height = 32
+ , container = null
+ , animate = true
+ , x = d3.scale.linear()
+ , y = d3.scale.linear()
+ , getX = function(d) { return d.x }
+ , getY = function(d) { return d.y }
+ , color = nv.utils.getColor(['#000'])
+ , xDomain
+ , yDomain
+ , xRange
+ , yRange
+ , showMinMaxPoints = true
+ , showCurrentPoint = true
+ , dispatch = d3.dispatch('renderEnd')
+ ;
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var renderWatch = nv.utils.renderWatch(dispatch);
+ var renderWatch = nv.utils.renderWatch(dispatch);
- function chart(selection) {
- renderWatch.reset();
- selection.each(function(data) {
- var availableWidth = width - margin.left - margin.right,
- availableHeight = height - margin.top - margin.bottom;
+ function chart(selection) {
+ renderWatch.reset();
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom;
- container = d3.select(this);
- nv.utils.initSVG(container);
+ container = d3.select(this);
+ nv.utils.initSVG(container);
- // Setup Scales
- x .domain(xDomain || d3.extent(data, getX ))
- .range(xRange || [0, availableWidth]);
+ // Setup Scales
+ x .domain(xDomain || d3.extent(data, getX ))
+ .range(xRange || [0, availableWidth]);
- y .domain(yDomain || d3.extent(data, getY ))
- .range(yRange || [availableHeight, 0]);
+ y .domain(yDomain || d3.extent(data, getY ))
+ .range(yRange || [availableHeight, 0]);
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-sparkline').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparkline');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-sparkline').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparkline');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
- var paths = wrap.selectAll('path')
- .data(function(d) { return [d] });
- paths.enter().append('path');
- paths.exit().remove();
- paths
- .style('stroke', function(d,i) { return d.color || color(d, i) })
- .attr('d', d3.svg.line()
- .x(function(d,i) { return x(getX(d,i)) })
- .y(function(d,i) { return y(getY(d,i)) })
- );
+ var paths = wrap.selectAll('path')
+ .data(function(d) { return [d] });
+ paths.enter().append('path');
+ paths.exit().remove();
+ paths
+ .style('stroke', function(d,i) { return d.color || color(d, i) })
+ .attr('d', d3.svg.line()
+ .x(function(d,i) { return x(getX(d,i)) })
+ .y(function(d,i) { return y(getY(d,i)) })
+ );
- // TODO: Add CURRENT data point (Need Min, Mac, Current / Most recent)
- var points = wrap.selectAll('circle.nv-point')
- .data(function(data) {
- var yValues = data.map(function(d, i) { return getY(d,i); });
- function pointIndex(index) {
- if (index != -1) {
- var result = data[index];
- result.pointIndex = index;
- return result;
- } else {
- return null;
+ // TODO: Add CURRENT data point (Need Min, Mac, Current / Most recent)
+ var points = wrap.selectAll('circle.nv-point')
+ .data(function(data) {
+ var yValues = data.map(function(d, i) { return getY(d,i); });
+ function pointIndex(index) {
+ if (index != -1) {
+ var result = data[index];
+ result.pointIndex = index;
+ return result;
+ } else {
+ return null;
+ }
}
- }
- var maxPoint = pointIndex(yValues.lastIndexOf(y.domain()[1])),
- minPoint = pointIndex(yValues.indexOf(y.domain()[0])),
- currentPoint = pointIndex(yValues.length - 1);
- return [(showMinMaxPoints ? minPoint : null), (showMinMaxPoints ? maxPoint : null), (showCurrentPoint ? currentPoint : null)].filter(function (d) {return d != null;});
- });
- points.enter().append('circle');
- points.exit().remove();
- points
- .attr('cx', function(d,i) { return x(getX(d,d.pointIndex)) })
- .attr('cy', function(d,i) { return y(getY(d,d.pointIndex)) })
- .attr('r', 2)
- .attr('class', function(d,i) {
- return getX(d, d.pointIndex) == x.domain()[1] ? 'nv-point nv-currentValue' :
+ var maxPoint = pointIndex(yValues.lastIndexOf(y.domain()[1])),
+ minPoint = pointIndex(yValues.indexOf(y.domain()[0])),
+ currentPoint = pointIndex(yValues.length - 1);
+ return [(showMinMaxPoints ? minPoint : null), (showMinMaxPoints ? maxPoint : null), (showCurrentPoint ? currentPoint : null)].filter(function (d) {return d != null;});
+ });
+ points.enter().append('circle');
+ points.exit().remove();
+ points
+ .attr('cx', function(d,i) { return x(getX(d,d.pointIndex)) })
+ .attr('cy', function(d,i) { return y(getY(d,d.pointIndex)) })
+ .attr('r', 2)
+ .attr('class', function(d,i) {
+ return getX(d, d.pointIndex) == x.domain()[1] ? 'nv-point nv-currentValue' :
getY(d, d.pointIndex) == y.domain()[0] ? 'nv-point nv-minValue' : 'nv-point nv-maxValue'
- });
- });
+ });
+ });
- renderWatch.renderEnd('sparkline immediate');
- return chart;
- }
+ renderWatch.renderEnd('sparkline immediate');
+ return chart;
+ }
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
- yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
- xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
- yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
- xScale: {get: function(){return x;}, set: function(_){x=_;}},
- yScale: {get: function(){return y;}, set: function(_){y=_;}},
- animate: {get: function(){return animate;}, set: function(_){animate=_;}},
- showMinMaxPoints: {get: function(){return showMinMaxPoints;}, set: function(_){showMinMaxPoints=_;}},
- showCurrentPoint: {get: function(){return showCurrentPoint;}, set: function(_){showCurrentPoint=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ animate: {get: function(){return animate;}, set: function(_){animate=_;}},
+ showMinMaxPoints: {get: function(){return showMinMaxPoints;}, set: function(_){showMinMaxPoints=_;}},
+ showCurrentPoint: {get: function(){return showCurrentPoint;}, set: function(_){showCurrentPoint=_;}},
- //functor options
- x: {get: function(){return getX;}, set: function(_){getX=d3.functor(_);}},
- y: {get: function(){return getY;}, set: function(_){getY=d3.functor(_);}},
+ //functor options
+ x: {get: function(){return getX;}, set: function(_){getX=d3.functor(_);}},
+ y: {get: function(){return getY;}, set: function(_){getY=d3.functor(_);}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }}
+ });
- chart.dispatch = dispatch;
- nv.utils.initOptions(chart);
- return chart;
-};
+ chart.dispatch = dispatch;
+ nv.utils.initOptions(chart);
+ return chart;
+ };
-nv.models.sparklinePlus = function() {
- "use strict";
+ nv.models.sparklinePlus = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var sparkline = nv.models.sparkline();
+ var sparkline = nv.models.sparkline();
- var margin = {top: 15, right: 100, bottom: 10, left: 50}
- , width = null
- , height = null
- , x
- , y
- , index = []
- , paused = false
- , xTickFormat = d3.format(',r')
- , yTickFormat = d3.format(',.2f')
- , showLastValue = true
- , alignValue = true
- , rightAlignValue = false
- , noData = null
- , dispatch = d3.dispatch('renderEnd')
- ;
+ var margin = {top: 15, right: 100, bottom: 10, left: 50}
+ , width = null
+ , height = null
+ , x
+ , y
+ , index = []
+ , paused = false
+ , xTickFormat = d3.format(',r')
+ , yTickFormat = d3.format(',.2f')
+ , showLastValue = true
+ , alignValue = true
+ , rightAlignValue = false
+ , noData = null
+ , dispatch = d3.dispatch('renderEnd')
+ ;
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var renderWatch = nv.utils.renderWatch(dispatch);
+ var renderWatch = nv.utils.renderWatch(dispatch);
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(sparkline);
- selection.each(function(data) {
- var container = d3.select(this);
- nv.utils.initSVG(container);
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(sparkline);
+ selection.each(function(data) {
+ var container = d3.select(this);
+ nv.utils.initSVG(container);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
- chart.update = function() { container.call(chart); };
- chart.container = this;
+ chart.update = function() { container.call(chart); };
+ chart.container = this;
- // Display No Data message if there's nothing to show.
- if (!data || !data.length) {
- nv.utils.noData(chart, container)
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length) {
+ nv.utils.noData(chart, container)
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
- var currentValue = sparkline.y()(data[data.length-1], data.length-1);
+ var currentValue = sparkline.y()(data[data.length-1], data.length-1);
- // Setup Scales
- x = sparkline.xScale();
- y = sparkline.yScale();
+ // Setup Scales
+ x = sparkline.xScale();
+ y = sparkline.yScale();
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-sparklineplus').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparklineplus');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-sparklineplus').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparklineplus');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- gEnter.append('g').attr('class', 'nv-sparklineWrap');
- gEnter.append('g').attr('class', 'nv-valueWrap');
- gEnter.append('g').attr('class', 'nv-hoverArea');
+ gEnter.append('g').attr('class', 'nv-sparklineWrap');
+ gEnter.append('g').attr('class', 'nv-valueWrap');
+ gEnter.append('g').attr('class', 'nv-hoverArea');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- // Main Chart Component(s)
- var sparklineWrap = g.select('.nv-sparklineWrap');
+ // Main Chart Component(s)
+ var sparklineWrap = g.select('.nv-sparklineWrap');
- sparkline.width(availableWidth).height(availableHeight);
- sparklineWrap.call(sparkline);
+ sparkline.width(availableWidth).height(availableHeight);
+ sparklineWrap.call(sparkline);
- if (showLastValue) {
- var valueWrap = g.select('.nv-valueWrap');
- var value = valueWrap.selectAll('.nv-currentValue')
- .data([currentValue]);
+ if (showLastValue) {
+ var valueWrap = g.select('.nv-valueWrap');
+ var value = valueWrap.selectAll('.nv-currentValue')
+ .data([currentValue]);
- value.enter().append('text').attr('class', 'nv-currentValue')
- .attr('dx', rightAlignValue ? -8 : 8)
- .attr('dy', '.9em')
- .style('text-anchor', rightAlignValue ? 'end' : 'start');
+ value.enter().append('text').attr('class', 'nv-currentValue')
+ .attr('dx', rightAlignValue ? -8 : 8)
+ .attr('dy', '.9em')
+ .style('text-anchor', rightAlignValue ? 'end' : 'start');
- value
- .attr('x', availableWidth + (rightAlignValue ? margin.right : 0))
- .attr('y', alignValue ? function (d) {
- return y(d)
- } : 0)
- .style('fill', sparkline.color()(data[data.length - 1], data.length - 1))
- .text(yTickFormat(currentValue));
- }
+ value
+ .attr('x', availableWidth + (rightAlignValue ? margin.right : 0))
+ .attr('y', alignValue ? function (d) {
+ return y(d)
+ } : 0)
+ .style('fill', sparkline.color()(data[data.length - 1], data.length - 1))
+ .text(yTickFormat(currentValue));
+ }
- gEnter.select('.nv-hoverArea').append('rect')
- .on('mousemove', sparklineHover)
- .on('click', function() { paused = !paused })
- .on('mouseout', function() { index = []; updateValueLine(); });
+ gEnter.select('.nv-hoverArea').append('rect')
+ .on('mousemove', sparklineHover)
+ .on('click', function() { paused = !paused })
+ .on('mouseout', function() { index = []; updateValueLine(); });
- g.select('.nv-hoverArea rect')
- .attr('transform', function(d) { return 'translate(' + -margin.left + ',' + -margin.top + ')' })
- .attr('width', availableWidth + margin.left + margin.right)
- .attr('height', availableHeight + margin.top);
+ g.select('.nv-hoverArea rect')
+ .attr('transform', function(d) { return 'translate(' + -margin.left + ',' + -margin.top + ')' })
+ .attr('width', availableWidth + margin.left + margin.right)
+ .attr('height', availableHeight + margin.top);
- //index is currently global (within the chart), may or may not keep it that way
- function updateValueLine() {
- if (paused) return;
+ //index is currently global (within the chart), may or may not keep it that way
+ function updateValueLine() {
+ if (paused) return;
- var hoverValue = g.selectAll('.nv-hoverValue').data(index);
+ var hoverValue = g.selectAll('.nv-hoverValue').data(index);
- var hoverEnter = hoverValue.enter()
- .append('g').attr('class', 'nv-hoverValue')
- .style('stroke-opacity', 0)
- .style('fill-opacity', 0);
+ var hoverEnter = hoverValue.enter()
+ .append('g').attr('class', 'nv-hoverValue')
+ .style('stroke-opacity', 0)
+ .style('fill-opacity', 0);
- hoverValue.exit()
- .transition().duration(250)
- .style('stroke-opacity', 0)
- .style('fill-opacity', 0)
- .remove();
+ hoverValue.exit()
+ .transition().duration(250)
+ .style('stroke-opacity', 0)
+ .style('fill-opacity', 0)
+ .remove();
- hoverValue
- .attr('transform', function(d) { return 'translate(' + x(sparkline.x()(data[d],d)) + ',0)' })
- .transition().duration(250)
- .style('stroke-opacity', 1)
- .style('fill-opacity', 1);
+ hoverValue
+ .attr('transform', function(d) { return 'translate(' + x(sparkline.x()(data[d],d)) + ',0)' })
+ .transition().duration(250)
+ .style('stroke-opacity', 1)
+ .style('fill-opacity', 1);
- if (!index.length) return;
+ if (!index.length) return;
- hoverEnter.append('line')
- .attr('x1', 0)
- .attr('y1', -margin.top)
- .attr('x2', 0)
- .attr('y2', availableHeight);
+ hoverEnter.append('line')
+ .attr('x1', 0)
+ .attr('y1', -margin.top)
+ .attr('x2', 0)
+ .attr('y2', availableHeight);
- hoverEnter.append('text').attr('class', 'nv-xValue')
- .attr('x', -6)
- .attr('y', -margin.top)
- .attr('text-anchor', 'end')
- .attr('dy', '.9em');
+ hoverEnter.append('text').attr('class', 'nv-xValue')
+ .attr('x', -6)
+ .attr('y', -margin.top)
+ .attr('text-anchor', 'end')
+ .attr('dy', '.9em');
- g.select('.nv-hoverValue .nv-xValue')
- .text(xTickFormat(sparkline.x()(data[index[0]], index[0])));
+ g.select('.nv-hoverValue .nv-xValue')
+ .text(xTickFormat(sparkline.x()(data[index[0]], index[0])));
- hoverEnter.append('text').attr('class', 'nv-yValue')
- .attr('x', 6)
- .attr('y', -margin.top)
- .attr('text-anchor', 'start')
- .attr('dy', '.9em');
+ hoverEnter.append('text').attr('class', 'nv-yValue')
+ .attr('x', 6)
+ .attr('y', -margin.top)
+ .attr('text-anchor', 'start')
+ .attr('dy', '.9em');
- g.select('.nv-hoverValue .nv-yValue')
- .text(yTickFormat(sparkline.y()(data[index[0]], index[0])));
- }
+ g.select('.nv-hoverValue .nv-yValue')
+ .text(yTickFormat(sparkline.y()(data[index[0]], index[0])));
+ }
- function sparklineHover() {
- if (paused) return;
+ function sparklineHover() {
+ if (paused) return;
- var pos = d3.mouse(this)[0] - margin.left;
+ var pos = d3.mouse(this)[0] - margin.left;
- function getClosestIndex(data, x) {
- var distance = Math.abs(sparkline.x()(data[0], 0) - x);
- var closestIndex = 0;
- for (var i = 0; i < data.length; i++){
- if (Math.abs(sparkline.x()(data[i], i) - x) < distance) {
- distance = Math.abs(sparkline.x()(data[i], i) - x);
- closestIndex = i;
+ function getClosestIndex(data, x) {
+ var distance = Math.abs(sparkline.x()(data[0], 0) - x);
+ var closestIndex = 0;
+ for (var i = 0; i < data.length; i++){
+ if (Math.abs(sparkline.x()(data[i], i) - x) < distance) {
+ distance = Math.abs(sparkline.x()(data[i], i) - x);
+ closestIndex = i;
+ }
}
+ return closestIndex;
}
- return closestIndex;
+
+ index = [getClosestIndex(data, Math.round(x.invert(pos)))];
+ updateValueLine();
}
- index = [getClosestIndex(data, Math.round(x.invert(pos)))];
- updateValueLine();
- }
+ });
+ renderWatch.renderEnd('sparklinePlus immediate');
+ return chart;
+ }
- });
- renderWatch.renderEnd('sparklinePlus immediate');
- return chart;
- }
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.sparkline = sparkline;
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.sparkline = sparkline;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ xTickFormat: {get: function(){return xTickFormat;}, set: function(_){xTickFormat=_;}},
+ yTickFormat: {get: function(){return yTickFormat;}, set: function(_){yTickFormat=_;}},
+ showLastValue: {get: function(){return showLastValue;}, set: function(_){showLastValue=_;}},
+ alignValue: {get: function(){return alignValue;}, set: function(_){alignValue=_;}},
+ rightAlignValue: {get: function(){return rightAlignValue;}, set: function(_){rightAlignValue=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- xTickFormat: {get: function(){return xTickFormat;}, set: function(_){xTickFormat=_;}},
- yTickFormat: {get: function(){return yTickFormat;}, set: function(_){yTickFormat=_;}},
- showLastValue: {get: function(){return showLastValue;}, set: function(_){showLastValue=_;}},
- alignValue: {get: function(){return alignValue;}, set: function(_){alignValue=_;}},
- rightAlignValue: {get: function(){return rightAlignValue;}, set: function(_){rightAlignValue=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }}
+ });
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }}
- });
+ nv.utils.inheritOptions(chart, sparkline);
+ nv.utils.initOptions(chart);
- nv.utils.inheritOptions(chart, sparkline);
- nv.utils.initOptions(chart);
+ return chart;
+ };
- return chart;
-};
+ nv.models.stackedArea = function() {
+ "use strict";
-nv.models.stackedArea = function() {
- "use strict";
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 960
+ , height = 500
+ , color = nv.utils.defaultColor() // a function that computes the color
+ , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't selet one
+ , container = null
+ , getX = function(d) { return d.x } // accessor to get the x value from a data point
+ , getY = function(d) { return d.y } // accessor to get the y value from a data point
+ , defined = function(d,i) { return !isNaN(getY(d,i)) && getY(d,i) !== null } // allows a line to be not continuous when it is not defined
+ , style = 'stack'
+ , offset = 'zero'
+ , order = 'default'
+ , interpolate = 'linear' // controls the line interpolation
+ , clipEdge = false // if true, masks lines within x and y scale
+ , x //can be accessed via chart.xScale()
+ , y //can be accessed via chart.yScale()
+ , scatter = nv.models.scatter()
+ , duration = 250
+ , dispatch = d3.dispatch('areaClick', 'areaMouseover', 'areaMouseout','renderEnd', 'elementClick', 'elementMouseover', 'elementMouseout')
+ ;
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 960
- , height = 500
- , color = nv.utils.defaultColor() // a function that computes the color
- , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't selet one
- , container = null
- , getX = function(d) { return d.x } // accessor to get the x value from a data point
- , getY = function(d) { return d.y } // accessor to get the y value from a data point
- , defined = function(d,i) { return !isNaN(getY(d,i)) && getY(d,i) !== null } // allows a line to be not continuous when it is not defined
- , style = 'stack'
- , offset = 'zero'
- , order = 'default'
- , interpolate = 'linear' // controls the line interpolation
- , clipEdge = false // if true, masks lines within x and y scale
- , x //can be accessed via chart.xScale()
- , y //can be accessed via chart.yScale()
- , scatter = nv.models.scatter()
- , duration = 250
- , dispatch = d3.dispatch('areaClick', 'areaMouseover', 'areaMouseout','renderEnd', 'elementClick', 'elementMouseover', 'elementMouseout')
+ scatter
+ .pointSize(2.2) // default size
+ .pointDomain([2.2, 2.2]) // all the same size by default
;
- scatter
- .pointSize(2.2) // default size
- .pointDomain([2.2, 2.2]) // all the same size by default
- ;
+ /************************************
+ * offset:
+ * 'wiggle' (stream)
+ * 'zero' (stacked)
+ * 'expand' (normalize to 100%)
+ * 'silhouette' (simple centered)
+ *
+ * order:
+ * 'inside-out' (stream)
+ * 'default' (input order)
+ ************************************/
- /************************************
- * offset:
- * 'wiggle' (stream)
- * 'zero' (stacked)
- * 'expand' (normalize to 100%)
- * 'silhouette' (simple centered)
- *
- * order:
- * 'inside-out' (stream)
- * 'default' (input order)
- ************************************/
+ var renderWatch = nv.utils.renderWatch(dispatch, duration);
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(scatter);
+ selection.each(function(data) {
+ var availableWidth = width - margin.left - margin.right,
+ availableHeight = height - margin.top - margin.bottom;
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(scatter);
- selection.each(function(data) {
- var availableWidth = width - margin.left - margin.right,
- availableHeight = height - margin.top - margin.bottom;
+ container = d3.select(this);
+ nv.utils.initSVG(container);
- container = d3.select(this);
- nv.utils.initSVG(container);
+ // Setup Scales
+ x = scatter.xScale();
+ y = scatter.yScale();
- // Setup Scales
- x = scatter.xScale();
- y = scatter.yScale();
+ var dataRaw = data;
+ // Injecting point index into each point because d3.layout.stack().out does not give index
+ data.forEach(function(aseries, i) {
+ aseries.seriesIndex = i;
+ aseries.values = aseries.values.map(function(d, j) {
+ d.index = j;
+ d.seriesIndex = i;
+ return d;
+ });
+ });
- var dataRaw = data;
- // Injecting point index into each point because d3.layout.stack().out does not give index
- data.forEach(function(aseries, i) {
- aseries.seriesIndex = i;
- aseries.values = aseries.values.map(function(d, j) {
- d.index = j;
- d.seriesIndex = i;
- return d;
+ var dataFiltered = data.filter(function(series) {
+ return !series.disabled;
});
- });
- var dataFiltered = data.filter(function(series) {
- return !series.disabled;
- });
+ data = d3.layout.stack()
+ .order(order)
+ .offset(offset)
+ .values(function(d) { return d.values }) //TODO: make values customizeable in EVERY model in this fashion
+ .x(getX)
+ .y(getY)
+ .out(function(d, y0, y) {
+ d.display = {
+ y: y,
+ y0: y0
+ };
+ })
+ (dataFiltered);
- data = d3.layout.stack()
- .order(order)
- .offset(offset)
- .values(function(d) { return d.values }) //TODO: make values customizeable in EVERY model in this fashion
- .x(getX)
- .y(getY)
- .out(function(d, y0, y) {
- d.display = {
- y: y,
- y0: y0
- };
- })
- (dataFiltered);
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-stackedarea').data([data]);
+ var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedarea');
+ var defsEnter = wrapEnter.append('defs');
+ var gEnter = wrapEnter.append('g');
+ var g = wrap.select('g');
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-stackedarea').data([data]);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedarea');
- var defsEnter = wrapEnter.append('defs');
- var gEnter = wrapEnter.append('g');
- var g = wrap.select('g');
+ gEnter.append('g').attr('class', 'nv-areaWrap');
+ gEnter.append('g').attr('class', 'nv-scatterWrap');
- gEnter.append('g').attr('class', 'nv-areaWrap');
- gEnter.append('g').attr('class', 'nv-scatterWrap');
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ // If the user has not specified forceY, make sure 0 is included in the domain
+ // Otherwise, use user-specified values for forceY
+ if (scatter.forceY().length == 0) {
+ scatter.forceY().push(0);
+ }
- // If the user has not specified forceY, make sure 0 is included in the domain
- // Otherwise, use user-specified values for forceY
- if (scatter.forceY().length == 0) {
- scatter.forceY().push(0);
- }
+ scatter
+ .width(availableWidth)
+ .height(availableHeight)
+ .x(getX)
+ .y(function(d) {
+ if (d.display !== undefined) { return d.display.y + d.display.y0; }
+ })
+ .color(data.map(function(d,i) {
+ d.color = d.color || color(d, d.seriesIndex);
+ return d.color;
+ }));
- scatter
- .width(availableWidth)
- .height(availableHeight)
- .x(getX)
- .y(function(d) {
- if (d.display !== undefined) { return d.display.y + d.display.y0; }
- })
- .color(data.map(function(d,i) {
- d.color = d.color || color(d, d.seriesIndex);
- return d.color;
- }));
+ var scatterWrap = g.select('.nv-scatterWrap')
+ .datum(data);
- var scatterWrap = g.select('.nv-scatterWrap')
- .datum(data);
+ scatterWrap.call(scatter);
- scatterWrap.call(scatter);
+ defsEnter.append('clipPath')
+ .attr('id', 'nv-edge-clip-' + id)
+ .append('rect');
- defsEnter.append('clipPath')
- .attr('id', 'nv-edge-clip-' + id)
- .append('rect');
+ wrap.select('#nv-edge-clip-' + id + ' rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
- wrap.select('#nv-edge-clip-' + id + ' rect')
- .attr('width', availableWidth)
- .attr('height', availableHeight);
+ g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
- g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
+ var area = d3.svg.area()
+ .defined(defined)
+ .x(function(d,i) { return x(getX(d,i)) })
+ .y0(function(d) {
+ return y(d.display.y0)
+ })
+ .y1(function(d) {
+ return y(d.display.y + d.display.y0)
+ })
+ .interpolate(interpolate);
- var area = d3.svg.area()
- .defined(defined)
- .x(function(d,i) { return x(getX(d,i)) })
- .y0(function(d) {
- return y(d.display.y0)
- })
- .y1(function(d) {
- return y(d.display.y + d.display.y0)
- })
- .interpolate(interpolate);
+ var zeroArea = d3.svg.area()
+ .defined(defined)
+ .x(function(d,i) { return x(getX(d,i)) })
+ .y0(function(d) { return y(d.display.y0) })
+ .y1(function(d) { return y(d.display.y0) });
- var zeroArea = d3.svg.area()
- .defined(defined)
- .x(function(d,i) { return x(getX(d,i)) })
- .y0(function(d) { return y(d.display.y0) })
- .y1(function(d) { return y(d.display.y0) });
+ var path = g.select('.nv-areaWrap').selectAll('path.nv-area')
+ .data(function(d) { return d });
- var path = g.select('.nv-areaWrap').selectAll('path.nv-area')
- .data(function(d) { return d });
-
- path.enter().append('path').attr('class', function(d,i) { return 'nv-area nv-area-' + i })
- .attr('d', function(d,i){
- return zeroArea(d.values, d.seriesIndex);
- })
- .on('mouseover', function(d,i) {
- d3.select(this).classed('hover', true);
- dispatch.areaMouseover({
- point: d,
- series: d.key,
- pos: [d3.event.pageX, d3.event.pageY],
- seriesIndex: d.seriesIndex
+ path.enter().append('path').attr('class', function(d,i) { return 'nv-area nv-area-' + i })
+ .attr('d', function(d,i){
+ return zeroArea(d.values, d.seriesIndex);
+ })
+ .on('mouseover', function(d,i) {
+ d3.select(this).classed('hover', true);
+ dispatch.areaMouseover({
+ point: d,
+ series: d.key,
+ pos: [d3.event.pageX, d3.event.pageY],
+ seriesIndex: d.seriesIndex
+ });
+ })
+ .on('mouseout', function(d,i) {
+ d3.select(this).classed('hover', false);
+ dispatch.areaMouseout({
+ point: d,
+ series: d.key,
+ pos: [d3.event.pageX, d3.event.pageY],
+ seriesIndex: d.seriesIndex
+ });
+ })
+ .on('click', function(d,i) {
+ d3.select(this).classed('hover', false);
+ dispatch.areaClick({
+ point: d,
+ series: d.key,
+ pos: [d3.event.pageX, d3.event.pageY],
+ seriesIndex: d.seriesIndex
+ });
});
- })
- .on('mouseout', function(d,i) {
- d3.select(this).classed('hover', false);
- dispatch.areaMouseout({
- point: d,
- series: d.key,
- pos: [d3.event.pageX, d3.event.pageY],
- seriesIndex: d.seriesIndex
- });
- })
- .on('click', function(d,i) {
- d3.select(this).classed('hover', false);
- dispatch.areaClick({
- point: d,
- series: d.key,
- pos: [d3.event.pageX, d3.event.pageY],
- seriesIndex: d.seriesIndex
- });
- });
- path.exit().remove();
- path.style('fill', function(d,i){
+ path.exit().remove();
+ path.style('fill', function(d,i){
return d.color || color(d, d.seriesIndex)
})
- .style('stroke', function(d,i){ return d.color || color(d, d.seriesIndex) });
- path.watchTransition(renderWatch,'stackedArea path')
- .attr('d', function(d,i) {
- return area(d.values,i)
- });
+ .style('stroke', function(d,i){ return d.color || color(d, d.seriesIndex) });
+ path.watchTransition(renderWatch,'stackedArea path')
+ .attr('d', function(d,i) {
+ return area(d.values,i)
+ });
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
- scatter.dispatch.on('elementMouseover.area', function(e) {
- g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', true);
- });
- scatter.dispatch.on('elementMouseout.area', function(e) {
- g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', false);
- });
+ scatter.dispatch.on('elementMouseover.area', function(e) {
+ g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', true);
+ });
+ scatter.dispatch.on('elementMouseout.area', function(e) {
+ g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', false);
+ });
- //Special offset functions
- chart.d3_stackedOffset_stackPercent = function(stackData) {
- var n = stackData.length, //How many series
- m = stackData[0].length, //how many points per series
- i,
- j,
- o,
- y0 = [];
+ //Special offset functions
+ chart.d3_stackedOffset_stackPercent = function(stackData) {
+ var n = stackData.length, //How many series
+ m = stackData[0].length, //how many points per series
+ i,
+ j,
+ o,
+ y0 = [];
- for (j = 0; j < m; ++j) { //Looping through all points
- for (i = 0, o = 0; i < dataRaw.length; i++) { //looping through all series
- o += getY(dataRaw[i].values[j]); //total y value of all series at a certian point in time.
- }
+ for (j = 0; j < m; ++j) { //Looping through all points
+ for (i = 0, o = 0; i < dataRaw.length; i++) { //looping through all series
+ o += getY(dataRaw[i].values[j]); //total y value of all series at a certian point in time.
+ }
- if (o) for (i = 0; i < n; i++) { //(total y value of all series at point in time i) != 0
- stackData[i][j][1] /= o;
- } else { //(total y value of all series at point in time i) == 0
- for (i = 0; i < n; i++) {
- stackData[i][j][1] = 0;
+ if (o) for (i = 0; i < n; i++) { //(total y value of all series at point in time i) != 0
+ stackData[i][j][1] /= o;
+ } else { //(total y value of all series at point in time i) == 0
+ for (i = 0; i < n; i++) {
+ stackData[i][j][1] = 0;
+ }
}
}
- }
- for (j = 0; j < m; ++j) y0[j] = 0;
- return y0;
- };
+ for (j = 0; j < m; ++j) y0[j] = 0;
+ return y0;
+ };
- });
+ });
- renderWatch.renderEnd('stackedArea immediate');
- return chart;
- }
+ renderWatch.renderEnd('stackedArea immediate');
+ return chart;
+ }
- //============================================================
- // Global getters and setters
- //------------------------------------------------------------
+ //============================================================
+ // Global getters and setters
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
- chart.scatter = scatter;
+ chart.dispatch = dispatch;
+ chart.scatter = scatter;
- scatter.dispatch.on('elementClick', function(){ dispatch.elementClick.apply(this, arguments); });
- scatter.dispatch.on('elementMouseover', function(){ dispatch.elementMouseover.apply(this, arguments); });
- scatter.dispatch.on('elementMouseout', function(){ dispatch.elementMouseout.apply(this, arguments); });
+ scatter.dispatch.on('elementClick', function(){ dispatch.elementClick.apply(this, arguments); });
+ scatter.dispatch.on('elementMouseover', function(){ dispatch.elementMouseover.apply(this, arguments); });
+ scatter.dispatch.on('elementMouseout', function(){ dispatch.elementMouseout.apply(this, arguments); });
- chart.interpolate = function(_) {
- if (!arguments.length) return interpolate;
- interpolate = _;
- return chart;
- };
+ chart.interpolate = function(_) {
+ if (!arguments.length) return interpolate;
+ interpolate = _;
+ return chart;
+ };
- chart.duration = function(_) {
- if (!arguments.length) return duration;
- duration = _;
- renderWatch.reset(duration);
- scatter.duration(duration);
- return chart;
- };
+ chart.duration = function(_) {
+ if (!arguments.length) return duration;
+ duration = _;
+ renderWatch.reset(duration);
+ scatter.duration(duration);
+ return chart;
+ };
- chart.dispatch = dispatch;
- chart.scatter = scatter;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ chart.dispatch = dispatch;
+ chart.scatter = scatter;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- defined: {get: function(){return defined;}, set: function(_){defined=_;}},
- clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
- offset: {get: function(){return offset;}, set: function(_){offset=_;}},
- order: {get: function(){return order;}, set: function(_){order=_;}},
- interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}},
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ defined: {get: function(){return defined;}, set: function(_){defined=_;}},
+ clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}},
+ offset: {get: function(){return offset;}, set: function(_){offset=_;}},
+ order: {get: function(){return order;}, set: function(_){order=_;}},
+ interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}},
- // simple functor options
- x: {get: function(){return getX;}, set: function(_){getX = d3.functor(_);}},
- y: {get: function(){return getY;}, set: function(_){getY = d3.functor(_);}},
+ // simple functor options
+ x: {get: function(){return getX;}, set: function(_){getX = d3.functor(_);}},
+ y: {get: function(){return getY;}, set: function(_){getY = d3.functor(_);}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }},
- style: {get: function(){return style;}, set: function(_){
- style = _;
- switch (style) {
- case 'stack':
- chart.offset('zero');
- chart.order('default');
- break;
- case 'stream':
- chart.offset('wiggle');
- chart.order('inside-out');
- break;
- case 'stream-center':
- chart.offset('silhouette');
- chart.order('inside-out');
- break;
- case 'expand':
- chart.offset('expand');
- chart.order('default');
- break;
- case 'stack_percent':
- chart.offset(chart.d3_stackedOffset_stackPercent);
- chart.order('default');
- break;
- }
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- scatter.duration(duration);
- }}
- });
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ style: {get: function(){return style;}, set: function(_){
+ style = _;
+ switch (style) {
+ case 'stack':
+ chart.offset('zero');
+ chart.order('default');
+ break;
+ case 'stream':
+ chart.offset('wiggle');
+ chart.order('inside-out');
+ break;
+ case 'stream-center':
+ chart.offset('silhouette');
+ chart.order('inside-out');
+ break;
+ case 'expand':
+ chart.offset('expand');
+ chart.order('default');
+ break;
+ case 'stack_percent':
+ chart.offset(chart.d3_stackedOffset_stackPercent);
+ chart.order('default');
+ break;
+ }
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ scatter.duration(duration);
+ }}
+ });
- nv.utils.inheritOptions(chart, scatter);
- nv.utils.initOptions(chart);
+ nv.utils.inheritOptions(chart, scatter);
+ nv.utils.initOptions(chart);
- return chart;
-};
+ return chart;
+ };
-nv.models.stackedAreaChart = function() {
- "use strict";
+ nv.models.stackedAreaChart = function() {
+ "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- var stacked = nv.models.stackedArea()
- , xAxis = nv.models.axis()
- , yAxis = nv.models.axis()
- , legend = nv.models.legend()
- , controls = nv.models.legend()
- , interactiveLayer = nv.interactiveGuideline()
- , tooltip = nv.models.tooltip()
- , focus = nv.models.focus(nv.models.stackedArea())
- ;
+ var stacked = nv.models.stackedArea()
+ , xAxis = nv.models.axis()
+ , yAxis = nv.models.axis()
+ , legend = nv.models.legend()
+ , controls = nv.models.legend()
+ , interactiveLayer = nv.interactiveGuideline()
+ , tooltip = nv.models.tooltip()
+ , focus = nv.models.focus(nv.models.stackedArea())
+ ;
- var margin = {top: 30, right: 25, bottom: 50, left: 60}
- , width = null
- , height = null
- , color = nv.utils.defaultColor()
- , showControls = true
- , showLegend = true
- , showXAxis = true
- , showYAxis = true
- , rightAlignYAxis = false
- , focusEnable = false
- , useInteractiveGuideline = false
- , showTotalInTooltip = true
- , totalLabel = 'TOTAL'
- , x //can be accessed via chart.xScale()
- , y //can be accessed via chart.yScale()
- , state = nv.utils.state()
- , defaultState = null
- , noData = null
- , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd')
- , controlWidth = 250
- , controlOptions = ['Stacked','Stream','Expanded']
- , controlLabels = {}
- , duration = 250
- ;
+ var margin = {top: 10, right: 25, bottom: 50, left: 60}
+ , marginTop = null
+ , width = null
+ , height = null
+ , color = nv.utils.defaultColor()
+ , showControls = true
+ , showLegend = true
+ , legendPosition = 'top'
+ , showXAxis = true
+ , showYAxis = true
+ , rightAlignYAxis = false
+ , focusEnable = false
+ , useInteractiveGuideline = false
+ , showTotalInTooltip = true
+ , totalLabel = 'TOTAL'
+ , x //can be accessed via chart.xScale()
+ , y //can be accessed via chart.yScale()
+ , state = nv.utils.state()
+ , defaultState = null
+ , noData = null
+ , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd')
+ , controlWidth = 250
+ , controlOptions = ['Stacked','Stream','Expanded']
+ , controlLabels = {}
+ , duration = 250
+ ;
- state.style = stacked.style();
- xAxis.orient('bottom').tickPadding(7);
- yAxis.orient((rightAlignYAxis) ? 'right' : 'left');
+ state.style = stacked.style();
+ xAxis.orient('bottom').tickPadding(7);
+ yAxis.orient((rightAlignYAxis) ? 'right' : 'left');
- tooltip
- .headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- })
- .valueFormatter(function(d, i) {
- return yAxis.tickFormat()(d, i);
- });
+ tooltip
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ })
+ .valueFormatter(function(d, i) {
+ return yAxis.tickFormat()(d, i);
+ });
- interactiveLayer.tooltip
- .headerFormatter(function(d, i) {
- return xAxis.tickFormat()(d, i);
- })
- .valueFormatter(function(d, i) {
- return d == null ? "N/A" : yAxis.tickFormat()(d, i);
- });
+ interactiveLayer.tooltip
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ })
+ .valueFormatter(function(d, i) {
+ return d == null ? "N/A" : yAxis.tickFormat()(d, i);
+ });
- var oldYTickFormat = null,
- oldValueFormatter = null;
+ var oldYTickFormat = null,
+ oldValueFormatter = null;
- controls.updateState(false);
+ controls.updateState(false);
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var renderWatch = nv.utils.renderWatch(dispatch);
- var style = stacked.style();
+ var renderWatch = nv.utils.renderWatch(dispatch);
+ var style = stacked.style();
- var stateGetter = function(data) {
- return function(){
- return {
- active: data.map(function(d) { return !d.disabled }),
- style: stacked.style()
- };
- }
- };
+ var stateGetter = function(data) {
+ return function(){
+ return {
+ active: data.map(function(d) { return !d.disabled }),
+ style: stacked.style()
+ };
+ }
+ };
- var stateSetter = function(data) {
- return function(state) {
- if (state.style !== undefined)
- style = state.style;
- if (state.active !== undefined)
- data.forEach(function(series,i) {
- series.disabled = !state.active[i];
- });
- }
- };
+ var stateSetter = function(data) {
+ return function(state) {
+ if (state.style !== undefined)
+ style = state.style;
+ if (state.active !== undefined)
+ data.forEach(function(series,i) {
+ series.disabled = !state.active[i];
+ });
+ }
+ };
- var percentFormatter = d3.format('%');
+ var percentFormatter = d3.format('%');
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(stacked);
- if (showXAxis) renderWatch.models(xAxis);
- if (showYAxis) renderWatch.models(yAxis);
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(stacked);
+ if (showXAxis) renderWatch.models(xAxis);
+ if (showYAxis) renderWatch.models(yAxis);
- selection.each(function(data) {
- var container = d3.select(this),
- that = this;
- nv.utils.initSVG(container);
+ selection.each(function(data) {
+ var container = d3.select(this),
+ that = this;
+ nv.utils.initSVG(container);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
- chart.update = function() { container.transition().duration(duration).call(chart); };
- chart.container = this;
+ chart.update = function() { container.transition().duration(duration).call(chart); };
+ chart.container = this;
- state
- .setter(stateSetter(data), chart.update)
- .getter(stateGetter(data))
- .update();
+ state
+ .setter(stateSetter(data), chart.update)
+ .getter(stateGetter(data))
+ .update();
- // DEPRECATED set state.disabled
- state.disabled = data.map(function(d) { return !!d.disabled });
+ // DEPRECATED set state.disabled
+ state.disabled = data.map(function(d) { return !!d.disabled });
- if (!defaultState) {
- var key;
- defaultState = {};
- for (key in state) {
- if (state[key] instanceof Array)
- defaultState[key] = state[key].slice(0);
- else
- defaultState[key] = state[key];
+ if (!defaultState) {
+ var key;
+ defaultState = {};
+ for (key in state) {
+ if (state[key] instanceof Array)
+ defaultState[key] = state[key].slice(0);
+ else
+ defaultState[key] = state[key];
+ }
}
- }
- // Display No Data message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
- nv.utils.noData(chart, container)
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
- // Setup Scales
- x = stacked.xScale();
- y = stacked.yScale();
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
+ nv.utils.noData(chart, container)
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+ // Setup Scales
+ x = stacked.xScale();
+ y = stacked.yScale();
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-stackedAreaChart').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedAreaChart').append('g');
- var g = wrap.select('g');
+ // Setup containers and skeleton of chart
+ var wrap = container.selectAll('g.nv-wrap.nv-stackedAreaChart').data([data]);
+ var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedAreaChart').append('g');
+ var g = wrap.select('g');
- gEnter.append('g').attr('class', 'nv-legendWrap');
- gEnter.append('g').attr('class', 'nv-controlsWrap');
+ gEnter.append('g').attr('class', 'nv-legendWrap');
+ gEnter.append('g').attr('class', 'nv-controlsWrap');
- var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
- focusEnter.append('g').attr('class', 'nv-background').append('rect');
- focusEnter.append('g').attr('class', 'nv-x nv-axis');
- focusEnter.append('g').attr('class', 'nv-y nv-axis');
- focusEnter.append('g').attr('class', 'nv-stackedWrap');
- focusEnter.append('g').attr('class', 'nv-interactive');
+ var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
+ focusEnter.append('g').attr('class', 'nv-background').append('rect');
+ focusEnter.append('g').attr('class', 'nv-x nv-axis');
+ focusEnter.append('g').attr('class', 'nv-y nv-axis');
+ focusEnter.append('g').attr('class', 'nv-stackedWrap');
+ focusEnter.append('g').attr('class', 'nv-interactive');
- // g.select("rect").attr("width",availableWidth).attr("height",availableHeight);
+ // g.select("rect").attr("width",availableWidth).attr("height",availableHeight);
- var contextEnter = gEnter.append('g').attr('class', 'nv-focusWrap');
+ var contextEnter = gEnter.append('g').attr('class', 'nv-focusWrap');
- // Legend
- if (!showLegend) {
- g.select('.nv-legendWrap').selectAll('*').remove();
- } else {
- var legendWidth = (showControls) ? availableWidth - controlWidth : availableWidth;
+ // Legend
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
+ var legendWidth = (showControls && legendPosition === 'top') ? availableWidth - controlWidth : availableWidth;
- legend.width(legendWidth);
- g.select('.nv-legendWrap').datum(data).call(legend);
+ legend.width(legendWidth);
+ g.select('.nv-legendWrap').datum(data).call(legend);
- if (legend.height() > margin.top) {
- margin.top = legend.height();
- availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
+ if (legendPosition === 'bottom') {
+ // constant from axis.js, plus some margin for better layout
+ var xAxisHeight = (showXAxis ? 12 : 0) + 10;
+ margin.bottom = Math.max(legend.height() + xAxisHeight, margin.bottom);
+ availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
+ var legendTop = availableHeight + xAxisHeight;
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + legendTop +')');
+ } else if (legendPosition === 'top') {
+ if (!marginTop && margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
+ }
+
+ g.select('.nv-legendWrap')
+ .attr('transform', 'translate(' + (availableWidth-legendWidth) + ',' + (-margin.top) +')');
+ }
}
- g.select('.nv-legendWrap')
- .attr('transform', 'translate(' + (availableWidth-legendWidth) + ',' + (-margin.top) +')');
- }
+ // Controls
+ if (!showControls) {
+ g.select('.nv-controlsWrap').selectAll('*').remove();
+ } else {
+ var controlsData = [
+ {
+ key: controlLabels.stacked || 'Stacked',
+ metaKey: 'Stacked',
+ disabled: stacked.style() != 'stack',
+ style: 'stack'
+ },
+ {
+ key: controlLabels.stream || 'Stream',
+ metaKey: 'Stream',
+ disabled: stacked.style() != 'stream',
+ style: 'stream'
+ },
+ {
+ key: controlLabels.expanded || 'Expanded',
+ metaKey: 'Expanded',
+ disabled: stacked.style() != 'expand',
+ style: 'expand'
+ },
+ {
+ key: controlLabels.stack_percent || 'Stack %',
+ metaKey: 'Stack_Percent',
+ disabled: stacked.style() != 'stack_percent',
+ style: 'stack_percent'
+ }
+ ];
- // Controls
- if (!showControls) {
- g.select('.nv-controlsWrap').selectAll('*').remove();
- } else {
- var controlsData = [
- {
- key: controlLabels.stacked || 'Stacked',
- metaKey: 'Stacked',
- disabled: stacked.style() != 'stack',
- style: 'stack'
- },
- {
- key: controlLabels.stream || 'Stream',
- metaKey: 'Stream',
- disabled: stacked.style() != 'stream',
- style: 'stream'
- },
- {
- key: controlLabels.expanded || 'Expanded',
- metaKey: 'Expanded',
- disabled: stacked.style() != 'expand',
- style: 'expand'
- },
- {
- key: controlLabels.stack_percent || 'Stack %',
- metaKey: 'Stack_Percent',
- disabled: stacked.style() != 'stack_percent',
- style: 'stack_percent'
- }
- ];
+ controlWidth = (controlOptions.length/3) * 260;
+ controlsData = controlsData.filter(function(d) {
+ return controlOptions.indexOf(d.metaKey) !== -1;
+ });
- controlWidth = (controlOptions.length/3) * 260;
- controlsData = controlsData.filter(function(d) {
- return controlOptions.indexOf(d.metaKey) !== -1;
- });
+ controls
+ .width( controlWidth )
+ .color(['#444', '#444', '#444']);
- controls
- .width( controlWidth )
- .color(['#444', '#444', '#444']);
+ g.select('.nv-controlsWrap')
+ .datum(controlsData)
+ .call(controls);
- g.select('.nv-controlsWrap')
- .datum(controlsData)
- .call(controls);
+ var requiredTop = Math.max(controls.height(), showLegend && (legendPosition === 'top') ? legend.height() : 0);
- if (Math.max(controls.height(), legend.height()) > margin.top) {
- margin.top = Math.max(controls.height(), legend.height());
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ if ( margin.top != requiredTop ) {
+ margin.top = requiredTop;
+ availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
+ }
+
+ g.select('.nv-controlsWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')');
}
- g.select('.nv-controlsWrap')
- .attr('transform', 'translate(0,' + (-margin.top) +')');
- }
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+ if (rightAlignYAxis) {
+ g.select(".nv-y.nv-axis")
+ .attr("transform", "translate(" + availableWidth + ",0)");
+ }
- if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
- }
+ //Set up interactive layer
+ if (useInteractiveGuideline) {
+ interactiveLayer
+ .width(availableWidth)
+ .height(availableHeight)
+ .margin({left: margin.left, top: margin.top})
+ .svgContainer(container)
+ .xScale(x);
+ wrap.select(".nv-interactive").call(interactiveLayer);
+ }
- //Set up interactive layer
- if (useInteractiveGuideline) {
- interactiveLayer
+ g.select('.nv-focus .nv-background rect')
+ .attr('width', availableWidth)
+ .attr('height', availableHeight);
+
+ stacked
.width(availableWidth)
.height(availableHeight)
- .margin({left: margin.left, top: margin.top})
- .svgContainer(container)
- .xScale(x);
- wrap.select(".nv-interactive").call(interactiveLayer);
- }
+ .color(data.map(function(d,i) {
+ return d.color || color(d, i);
+ }).filter(function(d,i) { return !data[i].disabled; }));
- g.select('.nv-focus .nv-background rect')
- .attr('width', availableWidth)
- .attr('height', availableHeight);
+ var stackedWrap = g.select('.nv-focus .nv-stackedWrap')
+ .datum(data.filter(function(d) { return !d.disabled; }));
- stacked
- .width(availableWidth)
- .height(availableHeight)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled; }));
+ // Setup Axes
+ if (showXAxis) {
+ xAxis.scale(x)
+ ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
+ .tickSize( -availableHeight, 0);
+ }
- var stackedWrap = g.select('.nv-focus .nv-stackedWrap')
- .datum(data.filter(function(d) { return !d.disabled; }));
-
- // Setup Axes
- if (showXAxis) {
- xAxis.scale(x)
- ._ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize( -availableHeight, 0);
- }
-
- if (showYAxis) {
- var ticks;
- if (stacked.offset() === 'wiggle') {
- ticks = 0;
+ if (showYAxis) {
+ var ticks;
+ if (stacked.offset() === 'wiggle') {
+ ticks = 0;
+ }
+ else {
+ ticks = nv.utils.calcTicksY(availableHeight/36, data);
+ }
+ yAxis.scale(y)
+ ._ticks(ticks)
+ .tickSize(-availableWidth, 0);
}
- else {
- ticks = nv.utils.calcTicksY(availableHeight/36, data);
- }
- yAxis.scale(y)
- ._ticks(ticks)
- .tickSize(-availableWidth, 0);
- }
- //============================================================
- // Update Axes
- //============================================================
- function updateXAxis() {
- if(showXAxis) {
- g.select('.nv-focus .nv-x.nv-axis')
- .attr('transform', 'translate(0,' + availableHeight + ')')
- .transition()
- .duration(duration)
- .call(xAxis)
+ //============================================================
+ // Update Axes
+ //============================================================
+ function updateXAxis() {
+ if(showXAxis) {
+ g.select('.nv-focus .nv-x.nv-axis')
+ .attr('transform', 'translate(0,' + availableHeight + ')')
+ .transition()
+ .duration(duration)
+ .call(xAxis)
;
+ }
}
- }
- function updateYAxis() {
- if(showYAxis) {
- if (stacked.style() === 'expand' || stacked.style() === 'stack_percent') {
- var currentFormat = yAxis.tickFormat();
+ function updateYAxis() {
+ if(showYAxis) {
+ if (stacked.style() === 'expand' || stacked.style() === 'stack_percent') {
+ var currentFormat = yAxis.tickFormat();
- if ( !oldYTickFormat || currentFormat !== percentFormatter )
- oldYTickFormat = currentFormat;
+ if ( !oldYTickFormat || currentFormat !== percentFormatter )
+ oldYTickFormat = currentFormat;
- //Forces the yAxis to use percentage in 'expand' mode.
- yAxis.tickFormat(percentFormatter);
- }
- else {
- if (oldYTickFormat) {
- yAxis.tickFormat(oldYTickFormat);
- oldYTickFormat = null;
+ //Forces the yAxis to use percentage in 'expand' mode.
+ yAxis.tickFormat(percentFormatter);
}
- }
+ else {
+ if (oldYTickFormat) {
+ yAxis.tickFormat(oldYTickFormat);
+ oldYTickFormat = null;
+ }
+ }
- g.select('.nv-focus .nv-y.nv-axis')
- .transition().duration(0)
- .call(yAxis);
+ g.select('.nv-focus .nv-y.nv-axis')
+ .transition().duration(0)
+ .call(yAxis);
+ }
}
- }
- //============================================================
- // Update Focus
- //============================================================
- if(!focusEnable) {
- stackedWrap.transition().call(stacked);
- updateXAxis();
- updateYAxis();
- } else {
- focus.width(availableWidth);
- g.select('.nv-focusWrap')
- .attr('transform', 'translate(0,' + ( availableHeight + margin.bottom + focus.margin().top) + ')')
- .datum(data.filter(function(d) { return !d.disabled; }))
- .call(focus);
- var extent = focus.brush.empty() ? focus.xDomain() : focus.brush.extent();
- if(extent !== null){
- onBrush(extent);
+ //============================================================
+ // Update Focus
+ //============================================================
+ if(!focusEnable) {
+ stackedWrap.transition().call(stacked);
+ updateXAxis();
+ updateYAxis();
+ } else {
+ focus.width(availableWidth);
+ g.select('.nv-focusWrap')
+ .attr('transform', 'translate(0,' + ( availableHeight + margin.bottom + focus.margin().top) + ')')
+ .datum(data.filter(function(d) { return !d.disabled; }))
+ .call(focus);
+ var extent = focus.brush.empty() ? focus.xDomain() : focus.brush.extent();
+ if(extent !== null){
+ onBrush(extent);
+ }
}
- }
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
+ //============================================================
+ // Event Handling/Dispatching (in chart's scope)
+ //------------------------------------------------------------
- stacked.dispatch.on('areaClick.toggle', function(e) {
- if (data.filter(function(d) { return !d.disabled }).length === 1)
- data.forEach(function(d) {
- d.disabled = false;
- });
- else
- data.forEach(function(d,i) {
- d.disabled = (i != e.seriesIndex);
- });
+ stacked.dispatch.on('areaClick.toggle', function(e) {
+ if (data.filter(function(d) { return !d.disabled }).length === 1)
+ data.forEach(function(d) {
+ d.disabled = false;
+ });
+ else
+ data.forEach(function(d,i) {
+ d.disabled = (i != e.seriesIndex);
+ });
- state.disabled = data.map(function(d) { return !!d.disabled });
- dispatch.stateChange(state);
+ state.disabled = data.map(function(d) { return !!d.disabled });
+ dispatch.stateChange(state);
- chart.update();
- });
+ chart.update();
+ });
- legend.dispatch.on('stateChange', function(newState) {
- for (var key in newState)
- state[key] = newState[key];
- dispatch.stateChange(state);
- chart.update();
- });
+ legend.dispatch.on('stateChange', function(newState) {
+ for (var key in newState)
+ state[key] = newState[key];
+ dispatch.stateChange(state);
+ chart.update();
+ });
- controls.dispatch.on('legendClick', function(d,i) {
- if (!d.disabled) return;
+ controls.dispatch.on('legendClick', function(d,i) {
+ if (!d.disabled) return;
- controlsData = controlsData.map(function(s) {
- s.disabled = true;
- return s;
+ controlsData = controlsData.map(function(s) {
+ s.disabled = true;
+ return s;
+ });
+ d.disabled = false;
+
+ stacked.style(d.style);
+
+
+ state.style = stacked.style();
+ dispatch.stateChange(state);
+
+ chart.update();
});
- d.disabled = false;
- stacked.style(d.style);
+ interactiveLayer.dispatch.on('elementMousemove', function(e) {
+ stacked.clearHighlights();
+ var singlePoint, pointIndex, pointXLocation, allData = [], valueSum = 0, allNullValues = true;
+ data
+ .filter(function(series, i) {
+ series.seriesIndex = i;
+ return !series.disabled;
+ })
+ .forEach(function(series,i) {
+ pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
+ var point = series.values[pointIndex];
+ var pointYValue = chart.y()(point, pointIndex);
+ if (pointYValue != null) {
+ stacked.highlightPoint(i, pointIndex, true);
+ }
+ if (typeof point === 'undefined') return;
+ if (typeof singlePoint === 'undefined') singlePoint = point;
+ if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
+ //If we are in 'expand' mode, use the stacked percent value instead of raw value.
+ var tooltipValue = (stacked.style() == 'expand') ? point.display.y : chart.y()(point,pointIndex);
+ allData.push({
+ key: series.key,
+ value: tooltipValue,
+ color: color(series,series.seriesIndex),
+ point: point
+ });
- state.style = stacked.style();
- dispatch.stateChange(state);
+ if (showTotalInTooltip && stacked.style() != 'expand' && tooltipValue != null) {
+ valueSum += tooltipValue;
+ allNullValues = false;
+ };
+ });
- chart.update();
- });
+ allData.reverse();
- interactiveLayer.dispatch.on('elementMousemove', function(e) {
- stacked.clearHighlights();
- var singlePoint, pointIndex, pointXLocation, allData = [], valueSum = 0, allNullValues = true;
- data
- .filter(function(series, i) {
- series.seriesIndex = i;
- return !series.disabled;
- })
- .forEach(function(series,i) {
- pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
- var point = series.values[pointIndex];
- var pointYValue = chart.y()(point, pointIndex);
- if (pointYValue != null) {
- stacked.highlightPoint(i, pointIndex, true);
- }
- if (typeof point === 'undefined') return;
- if (typeof singlePoint === 'undefined') singlePoint = point;
- if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
+ //Highlight the tooltip entry based on which stack the mouse is closest to.
+ if (allData.length > 2) {
+ var yValue = chart.yScale().invert(e.mouseY);
+ var yDistMax = Infinity, indexToHighlight = null;
+ allData.forEach(function(series,i) {
- //If we are in 'expand' mode, use the stacked percent value instead of raw value.
- var tooltipValue = (stacked.style() == 'expand') ? point.display.y : chart.y()(point,pointIndex);
+ //To handle situation where the stacked area chart is negative, we need to use absolute values
+ //when checking if the mouse Y value is within the stack area.
+ yValue = Math.abs(yValue);
+ var stackedY0 = Math.abs(series.point.display.y0);
+ var stackedY = Math.abs(series.point.display.y);
+ if ( yValue >= stackedY0 && yValue <= (stackedY + stackedY0))
+ {
+ indexToHighlight = i;
+ return;
+ }
+ });
+ if (indexToHighlight != null)
+ allData[indexToHighlight].highlight = true;
+ }
+
+ //If we are not in 'expand' mode, add a 'Total' row to the tooltip.
+ if (showTotalInTooltip && stacked.style() != 'expand' && allData.length >= 2 && !allNullValues) {
allData.push({
- key: series.key,
- value: tooltipValue,
- color: color(series,series.seriesIndex),
- point: point
+ key: totalLabel,
+ value: valueSum,
+ total: true
});
+ }
- if (showTotalInTooltip && stacked.style() != 'expand' && tooltipValue != null) {
- valueSum += tooltipValue;
- allNullValues = false;
- };
- });
+ var xValue = chart.x()(singlePoint,pointIndex);
- allData.reverse();
+ var valueFormatter = interactiveLayer.tooltip.valueFormatter();
+ // Keeps track of the tooltip valueFormatter if the chart changes to expanded view
+ if (stacked.style() === 'expand' || stacked.style() === 'stack_percent') {
+ if ( !oldValueFormatter ) {
+ oldValueFormatter = valueFormatter;
+ }
+ //Forces the tooltip to use percentage in 'expand' mode.
+ valueFormatter = d3.format(".1%");
+ }
+ else {
+ if (oldValueFormatter) {
+ valueFormatter = oldValueFormatter;
+ oldValueFormatter = null;
+ }
+ }
- //Highlight the tooltip entry based on which stack the mouse is closest to.
- if (allData.length > 2) {
- var yValue = chart.yScale().invert(e.mouseY);
- var yDistMax = Infinity, indexToHighlight = null;
- allData.forEach(function(series,i) {
+ interactiveLayer.tooltip
+ .valueFormatter(valueFormatter)
+ .data(
+ {
+ value: xValue,
+ series: allData
+ }
+ )();
- //To handle situation where the stacked area chart is negative, we need to use absolute values
- //when checking if the mouse Y value is within the stack area.
- yValue = Math.abs(yValue);
- var stackedY0 = Math.abs(series.point.display.y0);
- var stackedY = Math.abs(series.point.display.y);
- if ( yValue >= stackedY0 && yValue <= (stackedY + stackedY0))
- {
- indexToHighlight = i;
- return;
- }
- });
- if (indexToHighlight != null)
- allData[indexToHighlight].highlight = true;
- }
+ interactiveLayer.renderGuideLine(pointXLocation);
- //If we are not in 'expand' mode, add a 'Total' row to the tooltip.
- if (showTotalInTooltip && stacked.style() != 'expand' && allData.length >= 2 && !allNullValues) {
- allData.push({
- key: totalLabel,
- value: valueSum,
- total: true
- });
- }
+ });
- var xValue = chart.x()(singlePoint,pointIndex);
+ interactiveLayer.dispatch.on("elementMouseout",function(e) {
+ stacked.clearHighlights();
+ });
- var valueFormatter = interactiveLayer.tooltip.valueFormatter();
- // Keeps track of the tooltip valueFormatter if the chart changes to expanded view
- if (stacked.style() === 'expand' || stacked.style() === 'stack_percent') {
- if ( !oldValueFormatter ) {
- oldValueFormatter = valueFormatter;
+ /* Update `main' graph on brush update. */
+ focus.dispatch.on("onBrush", function(extent) {
+ onBrush(extent);
+ });
+
+ // Update chart from a state object passed to event handler
+ dispatch.on('changeState', function(e) {
+
+ if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) {
+ data.forEach(function(series,i) {
+ series.disabled = e.disabled[i];
+ });
+
+ state.disabled = e.disabled;
}
- //Forces the tooltip to use percentage in 'expand' mode.
- valueFormatter = d3.format(".1%");
- }
- else {
- if (oldValueFormatter) {
- valueFormatter = oldValueFormatter;
- oldValueFormatter = null;
- }
- }
- interactiveLayer.tooltip
- .valueFormatter(valueFormatter)
- .data(
- {
- value: xValue,
- series: allData
+ if (typeof e.style !== 'undefined') {
+ stacked.style(e.style);
+ style = e.style;
}
- )();
- interactiveLayer.renderGuideLine(pointXLocation);
+ chart.update();
+ });
- });
+ //============================================================
+ // Functions
+ //------------------------------------------------------------
- interactiveLayer.dispatch.on("elementMouseout",function(e) {
- stacked.clearHighlights();
- });
+ function onBrush(extent) {
+ // Update Main (Focus)
+ var stackedWrap = g.select('.nv-focus .nv-stackedWrap')
+ .datum(
+ data.filter(function(d) { return !d.disabled; })
+ .map(function(d,i) {
+ return {
+ key: d.key,
+ area: d.area,
+ classed: d.classed,
+ values: d.values.filter(function(d,i) {
+ return stacked.x()(d,i) >= extent[0] && stacked.x()(d,i) <= extent[1];
+ }),
+ disableTooltip: d.disableTooltip
+ };
+ })
+ );
+ stackedWrap.transition().duration(duration).call(stacked);
- /* Update `main' graph on brush update. */
- focus.dispatch.on("onBrush", function(extent) {
- onBrush(extent);
+ // Update Main (Focus) Axes
+ updateXAxis();
+ updateYAxis();
+ }
+
});
- // Update chart from a state object passed to event handler
- dispatch.on('changeState', function(e) {
+ renderWatch.renderEnd('stacked Area chart immediate');
+ return chart;
+ }
- if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) {
- data.forEach(function(series,i) {
- series.disabled = e.disabled[i];
- });
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
- state.disabled = e.disabled;
- }
+ stacked.dispatch.on('elementMouseover.tooltip', function(evt) {
+ evt.point['x'] = stacked.x()(evt.point);
+ evt.point['y'] = stacked.y()(evt.point);
+ tooltip.data(evt).hidden(false);
+ });
- if (typeof e.style !== 'undefined') {
- stacked.style(e.style);
- style = e.style;
- }
+ stacked.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true)
+ });
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- chart.update();
- });
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.stacked = stacked;
+ chart.legend = legend;
+ chart.controls = controls;
+ chart.xAxis = xAxis;
+ chart.x2Axis = focus.xAxis;
+ chart.yAxis = yAxis;
+ chart.y2Axis = focus.yAxis;
+ chart.interactiveLayer = interactiveLayer;
+ chart.tooltip = tooltip;
+ chart.focus = focus;
- //============================================================
- // Functions
- //------------------------------------------------------------
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- function onBrush(extent) {
- // Update Main (Focus)
- var stackedWrap = g.select('.nv-focus .nv-stackedWrap')
- .datum(
- data.filter(function(d) { return !d.disabled; })
- .map(function(d,i) {
- return {
- key: d.key,
- area: d.area,
- classed: d.classed,
- values: d.values.filter(function(d,i) {
- return stacked.x()(d,i) >= extent[0] && stacked.x()(d,i) <= extent[1];
- }),
- disableTooltip: d.disableTooltip
- };
- })
- );
- stackedWrap.transition().duration(duration).call(stacked);
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}},
+ showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
+ showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
+ controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
+ controlOptions: {get: function(){return controlOptions;}, set: function(_){controlOptions=_;}},
+ showTotalInTooltip: {get: function(){return showTotalInTooltip;}, set: function(_){showTotalInTooltip=_;}},
+ totalLabel: {get: function(){return totalLabel;}, set: function(_){totalLabel=_;}},
+ focusEnable: {get: function(){return focusEnable;}, set: function(_){focusEnable=_;}},
+ focusHeight: {get: function(){return focus.height();}, set: function(_){focus.height(_);}},
+ brushExtent: {get: function(){return focus.brushExtent();}, set: function(_){focus.brushExtent(_);}},
- // Update Main (Focus) Axes
- updateXAxis();
- updateYAxis();
- }
-
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ if (_.top !== undefined) {
+ margin.top = _.top;
+ marginTop = _.top;
+ }
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ focusMargin: {get: function(){return focus.margin}, set: function(_){
+ focus.margin.top = _.top !== undefined ? _.top : focus.margin.top;
+ focus.margin.right = _.right !== undefined ? _.right : focus.margin.right;
+ focus.margin.bottom = _.bottom !== undefined ? _.bottom : focus.margin.bottom;
+ focus.margin.left = _.left !== undefined ? _.left : focus.margin.left;
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ stacked.duration(duration);
+ xAxis.duration(duration);
+ yAxis.duration(duration);
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ legend.color(color);
+ stacked.color(color);
+ focus.color(color);
+ }},
+ x: {get: function(){return stacked.x();}, set: function(_){
+ stacked.x(_);
+ focus.x(_);
+ }},
+ y: {get: function(){return stacked.y();}, set: function(_){
+ stacked.y(_);
+ focus.y(_);
+ }},
+ rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
+ rightAlignYAxis = _;
+ yAxis.orient( rightAlignYAxis ? 'right' : 'left');
+ }},
+ useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
+ useInteractiveGuideline = !!_;
+ chart.interactive(!_);
+ chart.useVoronoi(!_);
+ stacked.scatter.interactive(!_);
+ }}
});
- renderWatch.renderEnd('stacked Area chart immediate');
+ nv.utils.inheritOptions(chart, stacked);
+ nv.utils.initOptions(chart);
+
return chart;
- }
+ };
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
+ nv.models.stackedAreaWithFocusChart = function() {
+ return nv.models.stackedAreaChart()
+ .margin({ bottom: 30 })
+ .focusEnable( true );
+ };
+// based on http://bl.ocks.org/kerryrodden/477c1bfb081b783f80ad
+ nv.models.sunburst = function() {
+ "use strict";
- stacked.dispatch.on('elementMouseover.tooltip', function(evt) {
- evt.point['x'] = stacked.x()(evt.point);
- evt.point['y'] = stacked.y()(evt.point);
- tooltip.data(evt).hidden(false);
- });
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- stacked.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true)
- });
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ var margin = {top: 0, right: 0, bottom: 0, left: 0}
+ , width = 600
+ , height = 600
+ , mode = "count"
+ , modes = {count: function(d) { return 1; }, value: function(d) { return d.value || d.size }, size: function(d) { return d.value || d.size }}
+ , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
+ , container = null
+ , color = nv.utils.defaultColor()
+ , showLabels = false
+ , labelFormat = function(d){if(mode === 'count'){return d.name + ' #' + d.value}else{return d.name + ' ' + (d.value || d.size)}}
+ , labelThreshold = 0.02
+ , sort = function(d1, d2){return d1.name > d2.name;}
+ , key = function(d,i){return d.name;}
+ , groupColorByParent = true
+ , duration = 500
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMousemove', 'elementMouseover', 'elementMouseout', 'renderEnd');
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.stacked = stacked;
- chart.legend = legend;
- chart.controls = controls;
- chart.xAxis = xAxis;
- chart.x2Axis = focus.xAxis;
- chart.yAxis = yAxis;
- chart.y2Axis = focus.yAxis;
- chart.interactiveLayer = interactiveLayer;
- chart.tooltip = tooltip;
- chart.focus = focus;
+ //============================================================
+ // aux functions and setup
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ var x = d3.scale.linear().range([0, 2 * Math.PI]);
+ var y = d3.scale.sqrt();
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
- controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
- controlOptions: {get: function(){return controlOptions;}, set: function(_){controlOptions=_;}},
- showTotalInTooltip: {get: function(){return showTotalInTooltip;}, set: function(_){showTotalInTooltip=_;}},
- totalLabel: {get: function(){return totalLabel;}, set: function(_){totalLabel=_;}},
- focusEnable: {get: function(){return focusEnable;}, set: function(_){focusEnable=_;}},
- focusHeight: {get: function(){return focus.height();}, set: function(_){focus.height(_);}},
- brushExtent: {get: function(){return focus.brushExtent();}, set: function(_){focus.brushExtent(_);}},
+ var partition = d3.layout.partition().sort(sort);
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- }},
- focusMargin: {get: function(){return focus.margin}, set: function(_){
- focus.margin.top = _.top !== undefined ? _.top : focus.margin.top;
- focus.margin.right = _.right !== undefined ? _.right : focus.margin.right;
- focus.margin.bottom = _.bottom !== undefined ? _.bottom : focus.margin.bottom;
- focus.margin.left = _.left !== undefined ? _.left : focus.margin.left;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- stacked.duration(duration);
- xAxis.duration(duration);
- yAxis.duration(duration);
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- legend.color(color);
- stacked.color(color);
- focus.color(color);
- }},
- x: {get: function(){return stacked.x();}, set: function(_){
- stacked.x(_);
- focus.x(_);
- }},
- y: {get: function(){return stacked.y();}, set: function(_){
- stacked.y(_);
- focus.y(_);
- }},
- rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
- rightAlignYAxis = _;
- yAxis.orient( rightAlignYAxis ? 'right' : 'left');
- }},
- useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
- useInteractiveGuideline = !!_;
- chart.interactive(!_);
- chart.useVoronoi(!_);
- stacked.scatter.interactive(!_);
- }}
- });
+ var node, availableWidth, availableHeight, radius;
+ var prevPositions = {};
- nv.utils.inheritOptions(chart, stacked);
- nv.utils.initOptions(chart);
+ var arc = d3.svg.arc()
+ .startAngle(function(d) {return Math.max(0, Math.min(2 * Math.PI, x(d.x))) })
+ .endAngle(function(d) {return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))) })
+ .innerRadius(function(d) {return Math.max(0, y(d.y)) })
+ .outerRadius(function(d) {return Math.max(0, y(d.y + d.dy)) });
- return chart;
-};
+ function rotationToAvoidUpsideDown(d) {
+ var centerAngle = computeCenterAngle(d);
+ if(centerAngle > 90){
+ return 180;
+ }
+ else {
+ return 0;
+ }
+ }
-nv.models.stackedAreaWithFocusChart = function() {
- return nv.models.stackedAreaChart()
- .margin({ bottom: 30 })
- .focusEnable( true );
-};
-// based on http://bl.ocks.org/kerryrodden/477c1bfb081b783f80ad
-nv.models.sunburst = function() {
- "use strict";
+ function computeCenterAngle(d) {
+ var startAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x)));
+ var endAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
+ var centerAngle = (((startAngle + endAngle) / 2) * (180 / Math.PI)) - 90;
+ return centerAngle;
+ }
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ function computeNodePercentage(d) {
+ var startAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x)));
+ var endAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
+ return (endAngle - startAngle) / (2 * Math.PI);
+ }
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 600
- , height = 600
- , mode = "count"
- , modes = {count: function(d) { return 1; }, value: function(d) { return d.value || d.size }, size: function(d) { return d.value || d.size }}
- , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
- , container = null
- , color = nv.utils.defaultColor()
- , showLabels = false
- , labelFormat = function(d){if(mode === 'count'){return d.name + ' #' + d.value}else{return d.name + ' ' + (d.value || d.size)}}
- , labelThreshold = 0.02
- , sort = function(d1, d2){return d1.name > d2.name;}
- , key = function(d,i){return d.name;}
- , groupColorByParent = true
- , duration = 500
- , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMousemove', 'elementMouseover', 'elementMouseout', 'renderEnd');
+ function labelThresholdMatched(d) {
+ var startAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x)));
+ var endAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
- //============================================================
- // aux functions and setup
- //------------------------------------------------------------
+ var size = endAngle - startAngle;
+ return size > labelThreshold;
+ }
- var x = d3.scale.linear().range([0, 2 * Math.PI]);
- var y = d3.scale.sqrt();
+ // When zooming: interpolate the scales.
+ function arcTweenZoom(e,i) {
+ var xd = d3.interpolate(x.domain(), [node.x, node.x + node.dx]),
+ yd = d3.interpolate(y.domain(), [node.y, 1]),
+ yr = d3.interpolate(y.range(), [node.y ? 20 : 0, radius]);
- var partition = d3.layout.partition().sort(sort);
+ if (i === 0) {
+ return function() {return arc(e);}
+ }
+ else {
+ return function (t) {
+ x.domain(xd(t));
+ y.domain(yd(t)).range(yr(t));
+ return arc(e);
+ }
+ };
+ }
- var node, availableWidth, availableHeight, radius;
- var prevPositions = {};
+ function arcTweenUpdate(d) {
+ var ipo = d3.interpolate({x: d.x0, dx: d.dx0, y: d.y0, dy: d.dy0}, d);
- var arc = d3.svg.arc()
- .startAngle(function(d) {return Math.max(0, Math.min(2 * Math.PI, x(d.x))) })
- .endAngle(function(d) {return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))) })
- .innerRadius(function(d) {return Math.max(0, y(d.y)) })
- .outerRadius(function(d) {return Math.max(0, y(d.y + d.dy)) });
+ return function (t) {
+ var b = ipo(t);
- function rotationToAvoidUpsideDown(d) {
- var centerAngle = computeCenterAngle(d);
- if(centerAngle > 90){
- return 180;
+ d.x0 = b.x;
+ d.dx0 = b.dx;
+ d.y0 = b.y;
+ d.dy0 = b.dy;
+
+ return arc(b);
+ };
}
- else {
- return 0;
+
+ function updatePrevPosition(node) {
+ var k = key(node);
+ if(! prevPositions[k]) prevPositions[k] = {};
+ var pP = prevPositions[k];
+ pP.dx = node.dx;
+ pP.x = node.x;
+ pP.dy = node.dy;
+ pP.y = node.y;
}
- }
- function computeCenterAngle(d) {
- var startAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x)));
- var endAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
- var centerAngle = (((startAngle + endAngle) / 2) * (180 / Math.PI)) - 90;
- return centerAngle;
- }
+ function storeRetrievePrevPositions(nodes) {
+ nodes.forEach(function(n){
+ var k = key(n);
+ var pP = prevPositions[k];
+ //console.log(k,n,pP);
+ if( pP ){
+ n.dx0 = pP.dx;
+ n.x0 = pP.x;
+ n.dy0 = pP.dy;
+ n.y0 = pP.y;
+ }
+ else {
+ n.dx0 = n.dx;
+ n.x0 = n.x;
+ n.dy0 = n.dy;
+ n.y0 = n.y;
+ }
+ updatePrevPosition(n);
+ });
+ }
- function computeNodePercentage(d) {
- var startAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x)));
- var endAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
- return (endAngle - startAngle) / (2 * Math.PI);
- }
+ function zoomClick(d) {
+ var labels = container.selectAll('text')
+ var path = container.selectAll('path')
- function labelThresholdMatched(d) {
- var startAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x)));
- var endAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
+ // fade out all text elements
+ labels.transition().attr("opacity",0);
- var size = endAngle - startAngle;
- return size > labelThreshold;
- }
+ // to allow reference to the new center node
+ node = d;
- // When zooming: interpolate the scales.
- function arcTweenZoom(e,i) {
- var xd = d3.interpolate(x.domain(), [node.x, node.x + node.dx]),
- yd = d3.interpolate(y.domain(), [node.y, 1]),
- yr = d3.interpolate(y.range(), [node.y ? 20 : 0, radius]);
+ path.transition()
+ .duration(duration)
+ .attrTween("d", arcTweenZoom)
+ .each('end', function(e) {
+ // partially taken from here: http://bl.ocks.org/metmajer/5480307
+ // check if the animated element's data e lies within the visible angle span given in d
+ if(e.x >= d.x && e.x < (d.x + d.dx) ){
+ if(e.depth >= d.depth){
+ // get a selection of the associated text element
+ var parentNode = d3.select(this.parentNode);
+ var arcText = parentNode.select('text');
- if (i === 0) {
- return function() {return arc(e);}
+ // fade in the text element and recalculate positions
+ arcText.transition().duration(duration)
+ .text( function(e){return labelFormat(e) })
+ .attr("opacity", function(d){
+ if(labelThresholdMatched(d)) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ })
+ .attr("transform", function() {
+ var width = this.getBBox().width;
+ if(e.depth === 0)
+ return "translate(" + (width / 2 * - 1) + ",0)";
+ else if(e.depth === d.depth){
+ return "translate(" + (y(e.y) + 5) + ",0)";
+ }
+ else {
+ var centerAngle = computeCenterAngle(e);
+ var rotation = rotationToAvoidUpsideDown(e);
+ if (rotation === 0) {
+ return 'rotate('+ centerAngle +')translate(' + (y(e.y) + 5) + ',0)';
+ }
+ else {
+ return 'rotate('+ centerAngle +')translate(' + (y(e.y) + width + 5) + ',0)rotate(' + rotation + ')';
+ }
+ }
+ });
+ }
+ }
+ })
}
- else {
- return function (t) {
- x.domain(xd(t));
- y.domain(yd(t)).range(yr(t));
- return arc(e);
- }
- };
- }
- function arcTweenUpdate(d) {
- var ipo = d3.interpolate({x: d.x0, dx: d.dx0, y: d.y0, dy: d.dy0}, d);
+ //============================================================
+ // chart function
+ //------------------------------------------------------------
+ var renderWatch = nv.utils.renderWatch(dispatch);
- return function (t) {
- var b = ipo(t);
+ function chart(selection) {
+ renderWatch.reset();
- d.x0 = b.x;
- d.dx0 = b.dx;
- d.y0 = b.y;
- d.dy0 = b.dy;
+ selection.each(function(data) {
+ container = d3.select(this);
+ availableWidth = nv.utils.availableWidth(width, container, margin);
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+ radius = Math.min(availableWidth, availableHeight) / 2;
- return arc(b);
- };
- }
+ y.range([0, radius]);
- function updatePrevPosition(node) {
- var k = key(node);
- if(! prevPositions[k]) prevPositions[k] = {};
- var pP = prevPositions[k];
- pP.dx = node.dx;
- pP.x = node.x;
- pP.dy = node.dy;
- pP.y = node.y;
- }
+ // Setup containers and skeleton of chart
+ var wrap = container.select('g.nvd3.nv-wrap.nv-sunburst');
+ if( !wrap[0][0] ) {
+ wrap = container.append('g')
+ .attr('class', 'nvd3 nv-wrap nv-sunburst nv-chart-' + id)
+ .attr('transform', 'translate(' + ((availableWidth / 2) + margin.left + margin.right) + ',' + ((availableHeight / 2) + margin.top + margin.bottom) + ')');
+ } else {
+ wrap.attr('transform', 'translate(' + ((availableWidth / 2) + margin.left + margin.right) + ',' + ((availableHeight / 2) + margin.top + margin.bottom) + ')');
+ }
- function storeRetrievePrevPositions(nodes) {
- nodes.forEach(function(n){
- var k = key(n);
- var pP = prevPositions[k];
- //console.log(k,n,pP);
- if( pP ){
- n.dx0 = pP.dx;
- n.x0 = pP.x;
- n.dy0 = pP.dy;
- n.y0 = pP.y;
- }
- else {
- n.dx0 = n.dx;
- n.x0 = n.x;
- n.dy0 = n.dy;
- n.y0 = n.y;
- }
- updatePrevPosition(n);
- });
- }
+ container.on('click', function (d, i) {
+ dispatch.chartClick({
+ data: d,
+ index: i,
+ pos: d3.event,
+ id: id
+ });
+ });
- function zoomClick(d) {
- var labels = container.selectAll('text')
- var path = container.selectAll('path')
+ partition.value(modes[mode] || modes["count"]);
- // fade out all text elements
- labels.transition().attr("opacity",0);
+ //reverse the drawing order so that the labels of inner
+ //arcs are drawn on top of the outer arcs.
+ var nodes = partition.nodes(data[0]).reverse()
- // to allow reference to the new center node
- node = d;
+ storeRetrievePrevPositions(nodes);
+ var cG = wrap.selectAll('.arc-container').data(nodes, key)
- path.transition()
- .duration(duration)
- .attrTween("d", arcTweenZoom)
- .each('end', function(e) {
- // partially taken from here: http://bl.ocks.org/metmajer/5480307
- // check if the animated element's data e lies within the visible angle span given in d
- if(e.x >= d.x && e.x < (d.x + d.dx) ){
- if(e.depth >= d.depth){
- // get a selection of the associated text element
- var parentNode = d3.select(this.parentNode);
- var arcText = parentNode.select('text');
+ //handle new datapoints
+ var cGE = cG.enter()
+ .append("g")
+ .attr("class",'arc-container')
- // fade in the text element and recalculate positions
- arcText.transition().duration(duration)
- .text( function(e){return labelFormat(e) })
+ cGE.append("path")
+ .attr("d", arc)
+ .style("fill", function (d) {
+ if (d.color) {
+ return d.color;
+ }
+ else if (groupColorByParent) {
+ return color((d.children ? d : d.parent).name);
+ }
+ else {
+ return color(d.name);
+ }
+ })
+ .style("stroke", "#FFF")
+ .on("click", function(d,i){
+ zoomClick(d);
+ dispatch.elementClick({
+ data: d,
+ index: i
+ })
+ })
+ .on('mouseover', function(d,i){
+ d3.select(this).classed('hover', true).style('opacity', 0.8);
+ dispatch.elementMouseover({
+ data: d,
+ color: d3.select(this).style("fill"),
+ percent: computeNodePercentage(d)
+ });
+ })
+ .on('mouseout', function(d,i){
+ d3.select(this).classed('hover', false).style('opacity', 1);
+ dispatch.elementMouseout({
+ data: d
+ });
+ })
+ .on('mousemove', function(d,i){
+ dispatch.elementMousemove({
+ data: d
+ });
+ });
+
+ ///Iterating via each and selecting based on the this
+ ///makes it work ... a cG.selectAll('path') doesn't.
+ ///Without iteration the data (in the element) didn't update.
+ cG.each(function(d){
+ d3.select(this).select('path')
+ .transition()
+ .duration(duration)
+ .attrTween('d', arcTweenUpdate);
+ });
+
+ if(showLabels){
+ //remove labels first and add them back
+ cG.selectAll('text').remove();
+
+ //this way labels are on top of newly added arcs
+ cG.append('text')
+ .text( function(e){ return labelFormat(e)})
+ .transition()
+ .duration(duration)
.attr("opacity", function(d){
if(labelThresholdMatched(d)) {
return 1;
}
else {
return 0;
}
})
- .attr("transform", function() {
+ .attr("transform", function(d) {
var width = this.getBBox().width;
- if(e.depth === 0)
- return "translate(" + (width / 2 * - 1) + ",0)";
- else if(e.depth === d.depth){
- return "translate(" + (y(e.y) + 5) + ",0)";
+ if(d.depth === 0){
+ return "rotate(0)translate(" + (width / 2 * -1) + ",0)";
}
else {
- var centerAngle = computeCenterAngle(e);
- var rotation = rotationToAvoidUpsideDown(e);
+ var centerAngle = computeCenterAngle(d);
+ var rotation = rotationToAvoidUpsideDown(d);
if (rotation === 0) {
- return 'rotate('+ centerAngle +')translate(' + (y(e.y) + 5) + ',0)';
+ return 'rotate('+ centerAngle +')translate(' + (y(d.y) + 5) + ',0)';
}
else {
- return 'rotate('+ centerAngle +')translate(' + (y(e.y) + width + 5) + ',0)rotate(' + rotation + ')';
+ return 'rotate('+ centerAngle +')translate(' + (y(d.y) + width + 5) + ',0)rotate(' + rotation + ')';
}
}
});
- }
}
- })
- }
- //============================================================
- // chart function
- //------------------------------------------------------------
- var renderWatch = nv.utils.renderWatch(dispatch);
+ //zoom out to the center when the data is updated.
+ zoomClick(nodes[nodes.length - 1])
- function chart(selection) {
- renderWatch.reset();
- selection.each(function(data) {
- container = d3.select(this);
- availableWidth = nv.utils.availableWidth(width, container, margin);
- availableHeight = nv.utils.availableHeight(height, container, margin);
- radius = Math.min(availableWidth, availableHeight) / 2;
-
- y.range([0, radius]);
-
- // Setup containers and skeleton of chart
- var wrap = container.select('g.nvd3.nv-wrap.nv-sunburst');
- if( !wrap[0][0] ) {
- wrap = container.append('g')
- .attr('class', 'nvd3 nv-wrap nv-sunburst nv-chart-' + id)
- .attr('transform', 'translate(' + ((availableWidth / 2) + margin.left + margin.right) + ',' + ((availableHeight / 2) + margin.top + margin.bottom) + ')');
- } else {
- wrap.attr('transform', 'translate(' + ((availableWidth / 2) + margin.left + margin.right) + ',' + ((availableHeight / 2) + margin.top + margin.bottom) + ')');
- }
-
- container.on('click', function (d, i) {
- dispatch.chartClick({
- data: d,
- index: i,
- pos: d3.event,
- id: id
- });
- });
-
- partition.value(modes[mode] || modes["count"]);
-
- //reverse the drawing order so that the labels of inner
- //arcs are drawn on top of the outer arcs.
- var nodes = partition.nodes(data[0]).reverse()
-
- storeRetrievePrevPositions(nodes);
- var cG = wrap.selectAll('.arc-container').data(nodes, key)
-
- //handle new datapoints
- var cGE = cG.enter()
- .append("g")
- .attr("class",'arc-container')
-
- cGE.append("path")
- .attr("d", arc)
- .style("fill", function (d) {
- if (d.color) {
- return d.color;
- }
- else if (groupColorByParent) {
- return color((d.children ? d : d.parent).name);
- }
- else {
- return color(d.name);
- }
- })
- .style("stroke", "#FFF")
- .on("click", zoomClick)
- .on('mouseover', function(d,i){
- d3.select(this).classed('hover', true).style('opacity', 0.8);
- dispatch.elementMouseover({
- data: d,
- color: d3.select(this).style("fill"),
- percent: computeNodePercentage(d)
- });
- })
- .on('mouseout', function(d,i){
- d3.select(this).classed('hover', false).style('opacity', 1);
- dispatch.elementMouseout({
- data: d
- });
- })
- .on('mousemove', function(d,i){
- dispatch.elementMousemove({
- data: d
- });
- });
-
- ///Iterating via each and selecting based on the this
- ///makes it work ... a cG.selectAll('path') doesn't.
- ///Without iteration the data (in the element) didn't update.
- cG.each(function(d){
- d3.select(this).select('path')
+ //remove unmatched elements ...
+ cG.exit()
.transition()
.duration(duration)
- .attrTween('d', arcTweenUpdate);
+ .attr('opacity',0)
+ .each('end',function(d){
+ var k = key(d);
+ prevPositions[k] = undefined;
+ })
+ .remove();
});
- if(showLabels){
- //remove labels first and add them back
- cG.selectAll('text').remove();
- //this way labels are on top of newly added arcs
- cG.append('text')
- .text( function(e){ return labelFormat(e)})
- .transition()
- .duration(duration)
- .attr("opacity", function(d){
- if(labelThresholdMatched(d)) {
- return 1;
- }
- else {
- return 0;
- }
- })
- .attr("transform", function(d) {
- var width = this.getBBox().width;
- if(d.depth === 0){
- return "rotate(0)translate(" + (width / 2 * -1) + ",0)";
- }
- else {
- var centerAngle = computeCenterAngle(d);
- var rotation = rotationToAvoidUpsideDown(d);
- if (rotation === 0) {
- return 'rotate('+ centerAngle +')translate(' + (y(d.y) + 5) + ',0)';
- }
- else {
- return 'rotate('+ centerAngle +')translate(' + (y(d.y) + width + 5) + ',0)rotate(' + rotation + ')';
- }
- }
- });
- }
+ renderWatch.renderEnd('sunburst immediate');
+ return chart;
+ }
- //zoom out to the center when the data is updated.
- zoomClick(nodes[nodes.length - 1])
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+ chart.dispatch = dispatch;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- //remove unmatched elements ...
- cG.exit()
- .transition()
- .duration(duration)
- .attr('opacity',0)
- .each('end',function(d){
- var k = key(d);
- prevPositions[k] = undefined;
- })
- .remove();
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ mode: {get: function(){return mode;}, set: function(_){mode=_;}},
+ id: {get: function(){return id;}, set: function(_){id=_;}},
+ duration: {get: function(){return duration;}, set: function(_){duration=_;}},
+ groupColorByParent: {get: function(){return groupColorByParent;}, set: function(_){groupColorByParent=!!_;}},
+ showLabels: {get: function(){return showLabels;}, set: function(_){showLabels=!!_}},
+ labelFormat: {get: function(){return labelFormat;}, set: function(_){labelFormat=_}},
+ labelThreshold: {get: function(){return labelThreshold;}, set: function(_){labelThreshold=_}},
+ sort: {get: function(){return sort;}, set: function(_){sort=_}},
+ key: {get: function(){return key;}, set: function(_){key=_}},
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top != undefined ? _.top : margin.top;
+ margin.right = _.right != undefined ? _.right : margin.right;
+ margin.bottom = _.bottom != undefined ? _.bottom : margin.bottom;
+ margin.left = _.left != undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color=nv.utils.getColor(_);
+ }}
});
-
- renderWatch.renderEnd('sunburst immediate');
+ nv.utils.initOptions(chart);
return chart;
- }
+ };
+ nv.models.sunburstChart = function() {
+ "use strict";
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
- chart.dispatch = dispatch;
- chart.options = nv.utils.optionsFunc.bind(chart);
+ var sunburst = nv.models.sunburst();
+ var tooltip = nv.models.tooltip();
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- mode: {get: function(){return mode;}, set: function(_){mode=_;}},
- id: {get: function(){return id;}, set: function(_){id=_;}},
- duration: {get: function(){return duration;}, set: function(_){duration=_;}},
- groupColorByParent: {get: function(){return groupColorByParent;}, set: function(_){groupColorByParent=!!_;}},
- showLabels: {get: function(){return showLabels;}, set: function(_){showLabels=!!_}},
- labelFormat: {get: function(){return labelFormat;}, set: function(_){labelFormat=_}},
- labelThreshold: {get: function(){return labelThreshold;}, set: function(_){labelThreshold=_}},
- sort: {get: function(){return sort;}, set: function(_){sort=_}},
- key: {get: function(){return key;}, set: function(_){key=_}},
- // options that require extra logic in the setter
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top != undefined ? _.top : margin.top;
- margin.right = _.right != undefined ? _.right : margin.right;
- margin.bottom = _.bottom != undefined ? _.bottom : margin.bottom;
- margin.left = _.left != undefined ? _.left : margin.left;
- }},
- color: {get: function(){return color;}, set: function(_){
- color=nv.utils.getColor(_);
- }}
- });
+ var margin = {top: 30, right: 20, bottom: 20, left: 20}
+ , width = null
+ , height = null
+ , color = nv.utils.defaultColor()
+ , showTooltipPercent = false
+ , id = Math.round(Math.random() * 100000)
+ , defaultState = null
+ , noData = null
+ , duration = 250
+ , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd');
- nv.utils.initOptions(chart);
- return chart;
-};
-nv.models.sunburstChart = function() {
- "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
- var sunburst = nv.models.sunburst();
- var tooltip = nv.models.tooltip();
+ var renderWatch = nv.utils.renderWatch(dispatch);
- var margin = {top: 30, right: 20, bottom: 20, left: 20}
- , width = null
- , height = null
- , color = nv.utils.defaultColor()
- , showTooltipPercent = false
- , id = Math.round(Math.random() * 100000)
- , defaultState = null
- , noData = null
- , duration = 250
- , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd');
+ tooltip
+ .duration(0)
+ .headerEnabled(false)
+ .valueFormatter(function(d){return d;});
+ //============================================================
+ // Chart function
+ //------------------------------------------------------------
- //============================================================
- // Private Variables
- //------------------------------------------------------------
+ function chart(selection) {
+ renderWatch.reset();
+ renderWatch.models(sunburst);
- var renderWatch = nv.utils.renderWatch(dispatch);
+ selection.each(function(data) {
+ var container = d3.select(this);
- tooltip
- .duration(0)
- .headerEnabled(false)
- .valueFormatter(function(d){return d;});
+ nv.utils.initSVG(container);
- //============================================================
- // Chart function
- //------------------------------------------------------------
+ var availableWidth = nv.utils.availableWidth(width, container, margin);
+ var availableHeight = nv.utils.availableHeight(height, container, margin);
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(sunburst);
+ chart.update = function() {
+ if (duration === 0) {
+ container.call(chart);
+ } else {
+ container.transition().duration(duration).call(chart);
+ }
+ };
+ chart.container = container;
- selection.each(function(data) {
- var container = d3.select(this);
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.length) {
+ nv.utils.noData(chart, container);
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
- nv.utils.initSVG(container);
+ sunburst.width(availableWidth).height(availableHeight).margin(margin);
+ container.call(sunburst);
+ });
- var availableWidth = nv.utils.availableWidth(width, container, margin);
- var availableHeight = nv.utils.availableHeight(height, container, margin);
+ renderWatch.renderEnd('sunburstChart immediate');
+ return chart;
+ }
- chart.update = function() {
- if (duration === 0) {
- container.call(chart);
- } else {
- container.transition().duration(duration).call(chart);
- }
- };
- chart.container = container;
+ //============================================================
+ // Event Handling/Dispatching (out of chart's scope)
+ //------------------------------------------------------------
- // Display No Data message if there's nothing to show.
- if (!data || !data.length) {
- nv.utils.noData(chart, container);
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
+ sunburst.dispatch.on('elementMouseover.tooltip', function(evt) {
+ evt.series = {
+ key: evt.data.name,
+ value: (evt.data.value || evt.data.size),
+ color: evt.color,
+ percent: evt.percent
+ };
+ if (!showTooltipPercent) {
+ delete evt.percent;
+ delete evt.series.percent;
}
+ tooltip.data(evt).hidden(false);
+ });
- sunburst.width(availableWidth).height(availableHeight).margin(margin);
- container.call(sunburst);
+ sunburst.dispatch.on('elementMouseout.tooltip', function(evt) {
+ tooltip.hidden(true);
});
- renderWatch.renderEnd('sunburstChart immediate');
- return chart;
- }
+ sunburst.dispatch.on('elementMousemove.tooltip', function(evt) {
+ tooltip();
+ });
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
- sunburst.dispatch.on('elementMouseover.tooltip', function(evt) {
- evt.series = {
- key: evt.data.name,
- value: (evt.data.value || evt.data.size),
- color: evt.color,
- percent: evt.percent
- };
- if (!showTooltipPercent) {
- delete evt.percent;
- delete evt.series.percent;
- }
- tooltip.data(evt).hidden(false);
- });
+ // expose chart's sub-components
+ chart.dispatch = dispatch;
+ chart.sunburst = sunburst;
+ chart.tooltip = tooltip;
+ chart.options = nv.utils.optionsFunc.bind(chart);
- sunburst.dispatch.on('elementMouseout.tooltip', function(evt) {
- tooltip.hidden(true);
- });
+ // use Object get/set functionality to map between vars and chart functions
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
+ showTooltipPercent: {get: function(){return showTooltipPercent;}, set: function(_){showTooltipPercent=_;}},
- sunburst.dispatch.on('elementMousemove.tooltip', function(evt) {
- tooltip();
- });
+ // options that require extra logic in the setter
+ color: {get: function(){return color;}, set: function(_){
+ color = _;
+ sunburst.color(color);
+ }},
+ duration: {get: function(){return duration;}, set: function(_){
+ duration = _;
+ renderWatch.reset(duration);
+ sunburst.duration(duration);
+ }},
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ sunburst.margin(margin);
+ }}
+ });
+ nv.utils.inheritOptions(chart, sunburst);
+ nv.utils.initOptions(chart);
+ return chart;
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
+ };
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.sunburst = sunburst;
- chart.tooltip = tooltip;
- chart.options = nv.utils.optionsFunc.bind(chart);
-
- // use Object get/set functionality to map between vars and chart functions
- chart._options = Object.create({}, {
- // simple options, just get/set the necessary values
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
- showTooltipPercent: {get: function(){return showTooltipPercent;}, set: function(_){showTooltipPercent=_;}},
-
- // options that require extra logic in the setter
- color: {get: function(){return color;}, set: function(_){
- color = _;
- sunburst.color(color);
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- sunburst.duration(duration);
- }},
- margin: {get: function(){return margin;}, set: function(_){
- margin.top = _.top !== undefined ? _.top : margin.top;
- margin.right = _.right !== undefined ? _.right : margin.right;
- margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
- margin.left = _.left !== undefined ? _.left : margin.left;
- sunburst.margin(margin);
- }}
- });
- nv.utils.inheritOptions(chart, sunburst);
- nv.utils.initOptions(chart);
- return chart;
-
-};
-
-nv.version = "1.8.4";
+ nv.version = "1.8.5";
})();
+//# sourceMappingURL=nv.d3.js.map
\ No newline at end of file