+++ /dev/null
-/* nvd3 version 1.7.0(https://github.com/liquidpele/nvd3) 2014-12-13 */
-(function(){
-
-// set up main nv object on window
-var nv = window.nv || {};
-window.nv = 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.graphs = []; //stores all the graphs currently on the page
-nv.logs = {}; //stores some statistics and potential error messages
-
-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");
- }
-
- 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;
- };
-}
-
-// Development render timers - disabled if dev = false
-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
- });
-}
-
-// 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];
-};
-
-// print console warning, should be used by deprecated functions
-nv.deprecated = function(name) {
- if (nv.dev && console && console.warn) {
- console.warn('`' + name + '` has been deprecated.');
- }
-};
-
-// render function is used to queue up chart rendering
-// in non-blocking timeout functions
-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();
-
- setTimeout(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);
- nv.graphs.push(chart);
- }
-
- nv.render.queue.splice(0, i);
-
- if (nv.render.queue.length) setTimeout(arguments.callee, 0);
- else {
- nv.dispatch.render_end();
- nv.render.active = false;
- }
- }, 0);
-};
-
-nv.render.active = false;
-nv.render.queue = [];
-
-// main function to use when adding a new graph, see examples
-nv.addGraph = function(obj) {
- if (typeof arguments[0] === typeof(Function)) {
- obj = {generate: arguments[0], callback: arguments[1]};
- }
-
- nv.render.queue.push(obj);
-
- if (!nv.render.active) {
- nv.render();
- }
-};/* 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";
-
- var tooltip = nv.models.tooltip();
-
- //Public settings
- var width = null;
- var height = null;
-
- //Please pass in the bounding chart's top and left margins
- //This is important for calculating the correct mouseX/Y positions.
- var margin = {left: 0, top: 0}
- , xScale = d3.scale.linear()
- , yScale = d3.scale.linear()
- , dispatch = d3.dispatch('elementMousemove', 'elementMouseout', 'elementClick', 'elementDblclick')
- , showGuideLine = true;
- //Must pass in the bounding chart's <svg> container.
- //The mousemove event is attached to this container.
- var svgContainer = null;
-
- // check if IE by looking for activeX
- var isMSIE = "ActiveXObject" in window;
-
-
- 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;
- }
-
- 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;
- }
-
- if (d3.event.target.className.baseVal.match("nv-legend")) {
- mouseOutAnyReason = true;
- }
-
- }
-
- if(subtractMargin) {
- mouseX -= margin.left;
- mouseY -= margin.top;
- }
-
- /* If mouseX/Y is outside of the chart's bounds,
- trigger a mouseOut event.
- */
- if (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.match(tooltip.nvPointerEventsClass)) {
-
- return;
- }
- }
- dispatch.elementMouseout({
- mouseX: mouseX,
- mouseY: mouseY
- });
- layer.renderGuideLine(null); //hide the guideline
- return;
- }
-
- var 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
- });
- }
-
- // if user single clicks the layer, fire elementClick
- if (d3.event.type === 'click') {
- dispatch.elementClick({
- mouseX: mouseX,
- mouseY: mouseY,
- pointXValue: pointXValue
- });
- }
- }
-
- svgContainer
- .on("mousemove",mouseHandler, true)
- .on("mouseout" ,mouseHandler,true)
- .on("dblclick" ,mouseHandler)
- .on("click", mouseHandler)
- ;
-
- //Draws a vertical guideline at the given X postion.
- layer.renderGuideLine = function(x) {
- if (!showGuideLine) return;
- 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.dispatch = dispatch;
- layer.tooltip = tooltip;
-
- 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.width = function(_) {
- if (!arguments.length) return width;
- width = _;
- return layer;
- };
-
- layer.height = function(_) {
- if (!arguments.length) return height;
- height = _;
- return layer;
- };
-
- layer.xScale = function(_) {
- if (!arguments.length) return xScale;
- xScale = _;
- return layer;
- };
-
- layer.showGuideLine = function(_) {
- if (!arguments.length) return showGuideLine;
- showGuideLine = _;
- return layer;
- };
-
- layer.svgContainer = function(_) {
- if (!arguments.length) return svgContainer;
- svgContainer = _;
- return layer;
- };
-
- 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.
-
- 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.
-
- Unit tests can be found in: interactiveBisectTest.html
-
- 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;
- }
- if (typeof xAccessor !== 'function') {
- xAccessor = function(d,i) {
- return d.x;
- }
- }
-
- var bisect = d3.bisector(xAccessor).left;
- var index = d3.max([0, bisect(values,searchVal) - 1]);
- var currentValue = xAccessor(values[index], index);
-
- if (typeof currentValue === 'undefined') {
- currentValue = index;
- }
-
- if (currentValue === searchVal) {
- return index; //found exact match
- }
-
- var nextIndex = d3.min([index+1, values.length - 1]);
- var nextValue = xAccessor(values[nextIndex], nextIndex);
-
- if (typeof nextValue === 'undefined') {
- nextValue = nextIndex;
- }
-
- if (Math.abs(nextValue - searchVal) >= Math.abs(currentValue - searchVal)) {
- return index;
- } else {
- return 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 ( delta <= yDistMax && delta < threshold) {
- yDistMax = delta;
- indexToHighlight = i;
- }
- });
- return indexToHighlight;
-};
-/* Tooltip rendering model for nvd3 charts.
- window.nv.models.tooltip is the updated,new way to render tooltips.
-
- window.nv.tooltip.show is the old tooltip code.
- window.nv.tooltip.* also has various helper methods.
- */
-(function() {
- "use strict";
- window.nv.tooltip = {};
-
- /* Model which can be instantiated to handle tooltip rendering.
- Example usage:
- var tip = nv.models.tooltip().gravity('w').distance(23)
- .data(myDataObject);
-
- tip(); //just invoke the returned function to render tooltip.
- */
- window.nv.models.tooltip = function() {
- //HTML contents of the tooltip. If null, the content is generated via the data variable.
- var content = null;
-
- /*
- 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 data = null;
-
- var gravity = 'w' //Can be 'n','s','e','w'. Determines how tooltip is positioned.
- ,distance = 50 //Distance to offset tooltip from the mouse location.
- ,snapDistance = 25 //Tolerance allowed before tooltip is moved from its current position (creates 'snapping' effect)
- , fixedTop = null //If not null, this fixes the top position of the tooltip.
- , classes = null //Attaches additional CSS classes to the tooltip DIV that is created.
- , chartContainer = null //Parent DIV, of the SVG Container that holds the chart.
- , tooltipElem = null //actual DOM element representing the tooltip.
- , position = {left: null, top: null} //Relative position of the tooltip inside chartContainer.
- , enabled = true; //True -> tooltips are rendered. False -> don't render tooltips.
-
- //Generates a unique id when you create a new tooltip() object
- var id = "nvtooltip-" + Math.floor(Math.random() * 100000);
-
- //CSS class to specify whether element should not have mouse events.
- var nvPointerEventsClass = "nv-pointer-events-none";
-
- //Format function for the tooltip values column
- var valueFormatter = 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 (content != null) {
- return content;
- }
-
- if (d == null) {
- return '';
- }
-
- var table = d3.select(document.createElement("table"));
- var theadEnter = table.selectAll("thead")
- .data([d])
- .enter().append("thead");
-
- 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")
- .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("key",true)
- .html(function(p) {return p.key});
-
- trowEnter.append("td")
- .classed("value",true)
- .html(function(p,i) { return valueFormatter(p.value,i) });
-
-
- 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 dataSeriesExists = function(d) {
- if (d && d.series && d.series.length > 0) {
- return true;
- }
- return false;
- };
-
- //In situations where the chart is in a 'viewBox', re-position the tooltip based on how far chart is zoomed.
- function convertViewBoxRatio() {
- if (chartContainer) {
- var svg = d3.select(chartContainer);
- if (svg.node().tagName !== "svg") {
- svg = svg.select("svg");
- }
- var viewBox = (svg.node()) ? svg.attr('viewBox') : null;
- if (viewBox) {
- viewBox = viewBox.split(' ');
- var ratio = parseInt(svg.style('width')) / viewBox[2];
-
- position.left = position.left * ratio;
- position.top = position.top * ratio;
- }
- }
- }
-
- //Creates new tooltip container, or uses existing one on DOM.
- function getTooltipContainer(newContent) {
- var body;
- if (chartContainer) {
- body = d3.select(chartContainer);
- } else {
- body = d3.select("body");
- }
-
- var container = body.select(".nvtooltip");
- if (container.node() === null) {
- //Create new tooltip div if it doesn't exist on DOM.
- container = body.append("div")
- .attr("class", "nvtooltip " + (classes? classes: "xy-tooltip"))
- .attr("id",id)
- ;
- }
-
- container.node().innerHTML = newContent;
- container.style("top",0).style("left",0).style("opacity",0);
- container.selectAll("div, table, td, tr").classed(nvPointerEventsClass,true)
- container.classed(nvPointerEventsClass,true);
- return container.node();
- }
-
- //Draw the tooltip onto the DOM.
- function nvtooltip() {
- if (!enabled) return;
- if (!dataSeriesExists(data)) return;
-
- convertViewBoxRatio();
-
- var left = position.left;
- var top = (fixedTop != null) ? fixedTop : position.top;
- var container = getTooltipContainer(contentGenerator(data));
- tooltipElem = container;
- if (chartContainer) {
- var svgComp = chartContainer.getElementsByTagName("svg")[0];
- var boundRect = (svgComp) ? svgComp.getBoundingClientRect() : chartContainer.getBoundingClientRect();
- var svgOffset = {left:0,top:0};
- if (svgComp) {
- var svgBound = svgComp.getBoundingClientRect();
- var chartBound = chartContainer.getBoundingClientRect();
- var svgBoundTop = svgBound.top;
-
- //Defensive code. Sometimes, svgBoundTop can be a really negative
- // number, like -134254. That's a bug.
- // If such a number is found, use zero instead. FireFox bug only
- if (svgBoundTop < 0) {
- var containerBound = chartContainer.getBoundingClientRect();
- svgBoundTop = (Math.abs(svgBoundTop) > containerBound.height) ? 0 : svgBoundTop;
- }
- svgOffset.top = Math.abs(svgBoundTop - chartBound.top);
- svgOffset.left = Math.abs(svgBound.left - chartBound.left);
- }
- //If the parent container is an overflow <div> with scrollbars, subtract the scroll offsets.
- //You need to also add any offset between the <svg> element and its containing <div>
- //Finally, add any offset of the containing <div> on the whole page.
- left += chartContainer.offsetLeft + svgOffset.left - 2*chartContainer.scrollLeft;
- top += chartContainer.offsetTop + svgOffset.top - 2*chartContainer.scrollTop;
- }
-
- if (snapDistance && snapDistance > 0) {
- top = Math.floor(top/snapDistance) * snapDistance;
- }
-
- nv.tooltip.calcTooltipPosition([left,top], gravity, distance, container);
- return nvtooltip;
- }
-
- nvtooltip.nvPointerEventsClass = nvPointerEventsClass;
-
- nvtooltip.content = function(_) {
- if (!arguments.length) return content;
- content = _;
- return nvtooltip;
- };
-
- //Returns tooltipElem...not able to set it.
- nvtooltip.tooltipElem = function() {
- return tooltipElem;
- };
-
- nvtooltip.contentGenerator = function(_) {
- if (!arguments.length) return contentGenerator;
- if (typeof _ === 'function') {
- contentGenerator = _;
- }
- return nvtooltip;
- };
-
- nvtooltip.data = function(_) {
- if (!arguments.length) return data;
- data = _;
- return nvtooltip;
- };
-
- nvtooltip.gravity = function(_) {
- if (!arguments.length) return gravity;
- gravity = _;
- return nvtooltip;
- };
-
- nvtooltip.distance = function(_) {
- if (!arguments.length) return distance;
- distance = _;
- return nvtooltip;
- };
-
- nvtooltip.snapDistance = function(_) {
- if (!arguments.length) return snapDistance;
- snapDistance = _;
- return nvtooltip;
- };
-
- nvtooltip.classes = function(_) {
- if (!arguments.length) return classes;
- classes = _;
- return nvtooltip;
- };
-
- nvtooltip.chartContainer = function(_) {
- if (!arguments.length) return chartContainer;
- chartContainer = _;
- return nvtooltip;
- };
-
- nvtooltip.position = function(_) {
- if (!arguments.length) return position;
- position.left = (typeof _.left !== 'undefined') ? _.left : position.left;
- position.top = (typeof _.top !== 'undefined') ? _.top : position.top;
- return nvtooltip;
- };
-
- nvtooltip.fixedTop = function(_) {
- if (!arguments.length) return fixedTop;
- fixedTop = _;
- return nvtooltip;
- };
-
- nvtooltip.enabled = function(_) {
- if (!arguments.length) return enabled;
- enabled = _;
- return nvtooltip;
- };
-
- nvtooltip.valueFormatter = function(_) {
- if (!arguments.length) return valueFormatter;
- if (typeof _ === 'function') {
- valueFormatter = _;
- }
- return nvtooltip;
- };
-
- nvtooltip.headerFormatter = function(_) {
- if (!arguments.length) return headerFormatter;
- if (typeof _ === 'function') {
- headerFormatter = _;
- }
- return nvtooltip;
- };
-
- //id() is a read-only function. You can't use it to set the id.
- nvtooltip.id = function() {
- return id;
- };
-
- return nvtooltip;
- };
-
- //Original tooltip.show function. Kept for backward compatibility.
- // pos = [left,top]
- nv.tooltip.show = function(pos, content, gravity, dist, parentContainer, classes) {
-
- //Create new tooltip div if it doesn't exist on DOM.
- var container = document.createElement('div');
- container.className = 'nvtooltip ' + (classes ? classes : 'xy-tooltip');
-
- var body = parentContainer;
- if ( !parentContainer || parentContainer.tagName.match(/g|svg/i)) {
- //If the parent element is an SVG element, place tooltip in the <body> element.
- body = document.getElementsByTagName('body')[0];
- }
-
- container.style.left = 0;
- container.style.top = 0;
- container.style.opacity = 0;
- // Content can also be dom element
- if (typeof content !== 'string')
- container.appendChild(content);
- else
- container.innerHTML = content;
- body.appendChild(container);
-
- //If the parent container is an overflow <div> with scrollbars, subtract the scroll offsets.
- if (parentContainer) {
- pos[0] = pos[0] - parentContainer.scrollLeft;
- pos[1] = pos[1] - parentContainer.scrollTop;
- }
- nv.tooltip.calcTooltipPosition(pos, gravity, dist, container);
- };
-
- //Looks up the ancestry of a DOM element, and returns the first NON-svg node.
- nv.tooltip.findFirstNonSVGParent = function(Elem) {
- while(Elem.tagName.match(/^g|svg$/i) !== null) {
- Elem = Elem.parentNode;
- }
- return Elem;
- };
-
- //Finds the total offsetTop of a given DOM element.
- //Looks up the entire ancestry of an element, up to the first relatively positioned element.
- nv.tooltip.findTotalOffsetTop = function ( Elem, initialTop ) {
- var offsetTop = initialTop;
-
- do {
- if( !isNaN( Elem.offsetTop ) ) {
- offsetTop += (Elem.offsetTop);
- }
- } while( Elem = Elem.offsetParent );
- return offsetTop;
- };
-
- //Finds the total offsetLeft of a given DOM element.
- //Looks up the entire ancestry of an element, up to the first relatively positioned element.
- nv.tooltip.findTotalOffsetLeft = function ( Elem, initialLeft) {
- var offsetLeft = initialLeft;
-
- do {
- if( !isNaN( Elem.offsetLeft ) ) {
- offsetLeft += (Elem.offsetLeft);
- }
- } while( Elem = Elem.offsetParent );
- return offsetLeft;
- };
-
- //Global utility function to render a tooltip on the DOM.
- //pos = [left,top] coordinates of where to place the tooltip, relative to the SVG chart container.
- //gravity = how to orient the tooltip
- //dist = how far away from the mouse to place tooltip
- //container = tooltip DIV
- nv.tooltip.calcTooltipPosition = function(pos, gravity, dist, container) {
-
- var height = parseInt(container.offsetHeight),
- width = parseInt(container.offsetWidth),
- windowWidth = nv.utils.windowSize().width,
- windowHeight = nv.utils.windowSize().height,
- scrollTop = window.pageYOffset,
- scrollLeft = window.pageXOffset,
- left, top;
-
- windowHeight = window.innerWidth >= document.body.scrollWidth ? windowHeight : windowHeight - 16;
- windowWidth = window.innerHeight >= document.body.scrollHeight ? windowWidth : windowWidth - 16;
-
- gravity = gravity || 's';
- dist = dist || 20;
-
- var tooltipTop = function ( Elem ) {
- return nv.tooltip.findTotalOffsetTop(Elem, top);
- };
-
- var tooltipLeft = function ( Elem ) {
- return nv.tooltip.findTotalOffsetLeft(Elem,left);
- };
-
- switch (gravity) {
- case 'e':
- left = pos[0] - width - dist;
- top = pos[1] - (height / 2);
- var tLeft = tooltipLeft(container);
- var tTop = tooltipTop(container);
- if (tLeft < scrollLeft) left = pos[0] + dist > scrollLeft ? pos[0] + dist : scrollLeft - tLeft + left;
- if (tTop < scrollTop) top = scrollTop - tTop + top;
- if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
- break;
- case 'w':
- left = pos[0] + dist;
- top = pos[1] - (height / 2);
- var tLeft = tooltipLeft(container);
- var tTop = tooltipTop(container);
- if (tLeft + width > windowWidth) left = pos[0] - width - dist;
- if (tTop < scrollTop) top = scrollTop + 5;
- if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
- break;
- case 'n':
- left = pos[0] - (width / 2) - 5;
- top = pos[1] + dist;
- var tLeft = tooltipLeft(container);
- var tTop = tooltipTop(container);
- if (tLeft < scrollLeft) left = scrollLeft + 5;
- if (tLeft + width > windowWidth) left = left - width/2 + 5;
- if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
- break;
- case 's':
- left = pos[0] - (width / 2);
- top = pos[1] - height - dist;
- var tLeft = tooltipLeft(container);
- var tTop = tooltipTop(container);
- if (tLeft < scrollLeft) left = scrollLeft + 5;
- if (tLeft + width > windowWidth) left = left - width/2 + 5;
- if (scrollTop > tTop) top = scrollTop;
- break;
- case 'none':
- left = pos[0];
- top = pos[1] - dist;
- var tLeft = tooltipLeft(container);
- var tTop = tooltipTop(container);
- break;
- }
-
- container.style.left = left+'px';
- container.style.top = top+'px';
- container.style.opacity = 1;
- container.style.position = 'absolute';
-
- return container;
- };
-
- //Global utility function to remove tooltips from the DOM.
- nv.tooltip.cleanup = function() {
-
- // Find the tooltips, mark them for removal by this class (so others cleanups won't find it)
- var tooltips = document.getElementsByClassName('nvtooltip');
- var purging = [];
- while(tooltips.length) {
- purging.push(tooltips[0]);
- tooltips[0].style.transitionDelay = '0 !important';
- tooltips[0].style.opacity = 0;
- tooltips[0].className = 'nvtooltip-pending-removal';
- }
-
- setTimeout(function() {
-
- while (purging.length) {
- var removeMe = purging.pop();
- removeMe.parentNode.removeChild(removeMe);
- }
- }, 500);
- };
-
-})();
-
-
-/*
-Gets the browser window size
-
-Returns object with height and width properties
- */
-nv.utils.windowSize = function() {
- // Sane defaults
- var size = {width: 640, height: 480};
-
- // Earlier IE uses Doc.body
- if (document.body && document.body.offsetWidth) {
- size.width = document.body.offsetWidth;
- size.height = document.body.offsetHeight;
- }
-
- // 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;
- }
-
- // Most recent browsers use
- if (window.innerWidth && window.innerHeight) {
- size.width = window.innerWidth;
- size.height = window.innerHeight;
- }
- return (size);
-};
-
-
-/*
-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.
-If passed an array, wrap it in a function which implements the old behavior
-Else return what was passed in
-*/
-nv.utils.getColor = function(color) {
- //if you pass in nothing, get default colors back
- if (!arguments.length) {
- return nv.utils.defaultColor();
-
- //if passed an array, wrap it in a function
- } else if(color instanceof Array) {
- return function(d, i) { return d.color || color[i % color.length]; };
-
- //if passed a function, return the function, 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 the index of an object as before.
- */
-nv.utils.defaultColor = function() {
- var colors = d3.scale.category20().range();
- return function(d, i) {
- return d.color || colors[i % colors.length]
- };
-};
-
-
-/*
-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;
-
- return function(series, index) {
- var key = getKey(series);
- if (typeof dictionary[key] === 'function') {
- 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];
- }
- };
-};
-
-
-/*
-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);
- });
- };
-
- 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 (typeof svgTextElem.style === 'function'
- && typeof svgTextElem.text === 'function') {
-
- var fontSize = parseInt(svgTextElem.style("font-size").replace("px",""));
- var textLength = svgTextElem.text().length;
- return textLength * fontSize * 0.5;
- }
- return 0;
-};
-
-
-/*
-Numbers that are undefined, null or NaN, convert them to zeros.
-*/
-nv.utils.NaNtoZero = function(n) {
- if (typeof n !== 'number'
- || isNaN(n)
- || n === null
- || n === Infinity
- || n === -Infinity) {
-
- 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;
-
- 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);
- }
- });
- return this;
- };
-
- this.reset = function(duration) {
- if (duration !== undefined) {
- _duration = duration;
- }
- renderStack = [];
- };
-
- 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 (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) {
- 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);
- }
- });
- }
- };
-
- 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 (key in source) {
- var isArray = dst[key] instanceof Array;
- var isObject = typeof dst[key] === 'object';
- var srcObj = typeof source[key] === 'object';
-
- 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');
-
- 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();
- }
- };
- return this;
- };
-
- this.init = function(state){
- init = init || {};
- nv.utils.deepExtend(init, state);
- };
-
- var _set = function(){
- var settings = _getState();
-
- if (JSON.stringify(settings) === JSON.stringify(state)) {
- return false;
- }
-
- for (var key in settings) {
- if (state[key] === undefined) {
- state[key] = {};
- }
- state[key] = settings[key];
- changed = true;
- }
- return true;
- };
-
- 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
-});
-
-To enable in the chart:
-chart.options = nv.utils.optionsFunc.bind(chart);
-*/
-nv.utils.optionsFunc = function(args) {
- nv.deprecated('nv.utils.optionsFunc');
- if (args) {
- d3.map(args).forEach((function(key,value) {
- if (typeof this[key] === "function") {
- this[key](value);
- }
- }).bind(this));
- }
- return this;
-};
-
-
-/*
-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 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');
-
-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._options[name] = _;
- return chart;
- };
- }
-};
-
-
-/*
-Add all options in an options object to the chart
-*/
-nv.utils.initOptions = function(chart) {
- 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);
-};
-
-
-/*
-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();
-
-
-/*
-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(_);
- 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 || []));
-};
-
-
-/*
-Runs common initialize code on the svg before the chart builds
-*/
-nv.utils.initSVG = function(svg) {
- svg.classed({'nvd3-svg':true});
-};nv.models.axis = function() {
- "use strict";
-
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
-
- 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
- , highlightZero = true
- , rotateLabels = 0
- , rotateYLabel = true
- , staggerLabels = false
- , isOrdinal = false
- , ticks = null
- , axisLabelDistance = 0
- , duration = 250
- , dispatch = d3.dispatch('renderEnd')
- , axisRendered = false
- , maxMinRendered = false
- ;
- axis
- .scale(scale)
- .orient('bottom')
- .tickFormat(function(d) { return d })
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- 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);
-
- // 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);
-
- //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();
-
- var fmt = axis.tickFormat();
- if (fmt == null) {
- fmt = scale0.tickFormat();
- }
-
- var axisLabel = g.selectAll('text.nv-axislabel')
- .data([axisLabelText || null]);
- axisLabel.exit().remove();
-
- switch (axis.orient()) {
- case 'top':
- axisLabel.enter().append('text').attr('class', 'nv-axislabel');
- var w;
- if (scale.range().length < 2) {
- w = 0;
- } else if (scale.range().length === 2) {
- w = scale.range()[1];
- } else {
- 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) {
- var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
- .data(scale.domain());
- axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').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())
- .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':
- var xLabelMargin = axisLabelDistance + 36;
- var maxTextWidth = 30;
- var xTicks = g.selectAll('g').select("text");
- if (rotateLabels%360) {
- //Calculate the longest xTick width
- xTicks.each(function(d,i){
- var width = this.getBoundingClientRect().width;
- if(width > maxTextWidth) maxTextWidth = width;
- });
- //Convert to radians before calculating sin. Add 30 to margin for healthy padding.
- var sin = Math.abs(Math.sin(rotateLabels*Math.PI/180));
- var xLabelMargin = (sin ? sin*maxTextWidth : maxTextWidth)+30;
- //Rotate all xTicks
- xTicks
- .attr('transform', function(d,i,j) { return 'rotate(' + rotateLabels + ' 0,0)' })
- .style('text-anchor', rotateLabels%360 > 0 ? 'start' : 'end');
- }
- axisLabel.enter().append('text').attr('class', 'nv-axislabel');
- var w;
- if (scale.range().length < 2) {
- w = 0;
- } else if (scale.range().length === 2) {
- w = scale.range()[1];
- } else {
- 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) {
- var 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', 'nv-axisMaxMin').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', function(d,i,j) { return 'rotate(' + rotateLabels + ' 0,0)' })
- .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)'
- });
- }
- if (staggerLabels)
- xTicks
- .attr('transform', function(d,i) { return 'translate(0,' + nv.utils.NaNtoZero((i % 2 == 0 ? '0' : '12')) + ')' });
-
- 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) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
- .attr('x', rotateYLabel ? (scale.range()[0] / 2) : axis.tickPadding());
- if (showMaxMin) {
- var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
- .data(scale.domain());
- axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').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 ? (-scale.range()[0] / 2) : -axis.tickPadding());
- if (showMaxMin) {
- var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
- .data(scale.domain());
- axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').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);
-
- 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 (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!!
- }
- });
- }
-
- //highlight zero line ... Maybe should not be an option and should just be in CSS?
- if (highlightZero) {
- g.selectAll('.tick')
- .filter(function (d) {
- return !parseFloat(Math.round(this.__data__ * 100000) / 1000000) && (this.__data__ !== undefined)
- }) //this is because sometimes the 0 tick is a very small fraction, TODO: think of cleaner technique
- .classed('zero', true);
- }
- //store old scales for use in transitions on update
- scale0 = scale.copy();
-
- });
-
- renderWatch.renderEnd('axis immediate');
- return chart;
- }
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
-
- // 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=_;}},
- highlightZero: {get: function(){return highlightZero;}, set: function(_){highlightZero=_;}},
- 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=_;}},
-
- // 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']);
-
- 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";
-
- //============================================================
- // 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 : [0] }
- , measures = function(d) { return d.measures }
- , rangeLabels = function(d) { return d.rangeLabels ? d.rangeLabels : [] }
- , markerLabels = function(d) { return d.markerLabels ? d.markerLabels : [] }
- , 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
- , tickFormat = null
- , color = nv.utils.getColor(['#1f77b4'])
- , dispatch = d3.dispatch('elementMouseover', 'elementMouseout')
- ;
-
- 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);
-
- 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),
- rangeLabelz = rangeLabels.call(this, d, i).slice(),
- markerLabelz = markerLabels.call(this, d, i).slice(),
- measureLabelz = measureLabels.call(this, d, i).slice();
-
- // 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());
-
- // Stash the new scale.
- this.__chart__ = x1;
-
- 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');
-
- gEnter.append('rect').attr('class', 'nv-range nv-rangeMax');
- gEnter.append('rect').attr('class', 'nv-range nv-rangeAvg');
- gEnter.append('rect').attr('class', 'nv-range nv-rangeMin');
- gEnter.append('rect').attr('class', 'nv-measure');
- gEnter.append('path').attr('class', 'nv-markerTriangle');
-
- 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) };
-
- g.select('rect.nv-rangeMax')
- .attr('height', availableHeight)
- .attr('width', w1(rangeMax > 0 ? rangeMax : rangeMin))
- .attr('x', xp1(rangeMax > 0 ? rangeMax : rangeMin))
- .datum(rangeMax > 0 ? rangeMax : rangeMin)
-
- g.select('rect.nv-rangeAvg')
- .attr('height', availableHeight)
- .attr('width', w1(rangeAvg))
- .attr('x', xp1(rangeAvg))
- .datum(rangeAvg)
-
- g.select('rect.nv-rangeMin')
- .attr('height', availableHeight)
- .attr('width', w1(rangeMax))
- .attr('x', xp1(rangeMax))
- .attr('width', w1(rangeMax > 0 ? rangeMin : rangeMax))
- .attr('x', xp1(rangeMax > 0 ? rangeMin : rangeMax))
- .datum(rangeMax > 0 ? rangeMin : rangeMax)
-
- 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',
- pos: [x1(measurez[0]), availableHeight/2]
- })
- })
- .on('mouseout', function() {
- dispatch.elementMouseout({
- value: measurez[0],
- label: measureLabelz[0] || 'Current'
- })
- });
-
- var h3 = availableHeight / 6;
- if (markerz[0]) {
- g.selectAll('path.nv-markerTriangle')
- .attr('transform', function(d) { return 'translate(' + x1(markerz[0]) + ',' + (availableHeight / 2) + ')' })
- .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z')
- .on('mouseover', function() {
- dispatch.elementMouseover({
- value: markerz[0],
- label: markerLabelz[0] || 'Previous',
- pos: [x1(markerz[0]), availableHeight/2]
- })
- })
- .on('mouseout', function() {
- dispatch.elementMouseout({
- value: markerz[0],
- label: markerLabelz[0] || 'Previous'
- })
- });
- } else {
- g.selectAll('path.nv-markerTriangle').remove();
- }
-
- wrap.selectAll('.nv-range')
- .on('mouseover', function(d,i) {
- var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
-
- dispatch.elementMouseover({
- value: d,
- label: label,
- pos: [x1(d), availableHeight/2]
- })
- })
- .on('mouseout', function(d,i) {
- var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
-
- dispatch.elementMouseout({
- value: d,
- label: label
- })
- });
- });
-
- 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
- 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=_;}},
-
- // 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;
-};
-
-
-
-// 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";
-
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
-
- var bullet = nv.models.bullet()
- ;
-
- 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 : [0] }
- , measures = function(d) { return d.measures }
- , width = null
- , height = 55
- , tickFormat = null
- , tooltips = true
- , tooltip = function(key, x, y, e, graph) {
- return '<h3>' + x + '</h3>' +
- '<p>' + y + '</p>'
- }
- , noData = 'No Data Available.'
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var showTooltip = function(e, offsetElement) {
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ) + margin.left,
- top = e.pos[1] + ( offsetElement.offsetTop || 0) + margin.top,
- content = tooltip(e.key, e.label, e.value, e, chart);
-
- nv.tooltip.show([left, top], content, e.value < 0 ? 'e' : 'w', null, offsetElement);
- };
-
- function chart(selection) {
- selection.each(function(d, i) {
- var container = d3.select(this);
- nv.utils.initSVG(container);
-
- var availableWidth = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right,
- availableHeight = height - margin.top - margin.bottom,
- that = 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)) {
- 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
- .attr('x', margin.left + availableWidth / 2)
- .attr('y', 18 + margin.top + availableHeight / 2)
- .text(function(d) { return d });
-
- 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);
-
- // 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');
-
- 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], 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());
-
- // 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 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; });
-
- bullet
- .width(availableWidth)
- .height(availableHeight)
-
- var bulletWrap = g.select('.nv-bulletWrap');
- d3.transition(bulletWrap).call(bullet);
-
- // 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( 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);
-
- 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);
-
- // 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);
-
- tickUpdate.select('line')
- .attr('y1', availableHeight)
- .attr('y2', 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();
-
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
-
- dispatch.on('tooltipShow', function(e) {
- e.key = d.title;
- if (tooltips) showTooltip(e, that.parentNode);
- });
-
- });
-
- d3.timer.flush();
- return chart;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- bullet.dispatch.on('elementMouseover.tooltip', function(e) {
- dispatch.tooltipShow(e);
- });
-
- bullet.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
-
- chart.bullet = bullet;
- 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)
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- tickFormat: {get: function(){return tickFormat;}, set: function(_){tickFormat=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- 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;
- }},
- 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);
-
- return chart;
-};
-
-
-
-nv.models.cumulativeLineChart = function() {
- "use strict";
-
- //============================================================
- // 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()
- ;
-
- 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
- , tooltips = true
- , showControls = true
- , useInteractiveGuideline = false
- , rescaleY = true
- , tooltip = function(key, x, y, e, graph) {
- return '<h3>' + key + '</h3>' +
- '<p>' + y + ' at ' + x + '</p>'
- }
- , x //can be accessed via chart.xScale()
- , y //can be accessed via chart.yScale()
- , id = lines.id()
- , state = nv.utils.state()
- , defaultState = null
- , noData = 'No Data Available.'
- , average = function(d) { return d.average }
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', '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;
-
- xAxis
- .orient('bottom')
- .tickPadding(7)
- ;
- yAxis
- .orient((rightAlignYAxis) ? 'right' : 'left')
- ;
-
- controls.updateState(false);
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var dx = d3.scale.linear()
- , index = {i: 0, x: 0}
- , renderWatch = nv.utils.renderWatch(dispatch, duration)
- ;
-
- var showTooltip = function(e, offsetElement) {
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- top = e.pos[1] + ( offsetElement.offsetTop || 0),
- x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
- y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
- content = tooltip(e.series.key, x, y, e, chart);
-
- nv.tooltip.show([left, top], content, null, null, offsetElement);
- };
-
- 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];
- });
- }
- };
-
- 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 = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right,
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
- 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 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 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');
-
- // 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) {
- 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
- .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();
- }
-
- // 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());
-
- //account for series being disabled when losing 95% or more
- if (initialDomain[0] < -.95) initialDomain[0] = -.95;
-
- 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] })
- ];
-
- 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);
-
- 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');
-
- 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) {
- legend.width(availableWidth);
-
- g.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
-
- if ( margin.top != legend.height()) {
- margin.top = legend.height();
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
- }
-
- g.select('.nv-legendWrap')
- .attr('transform', 'translate(0,' + (-margin.top) +')')
- }
-
- // Controls
- if (showControls) {
- 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})
- ;
-
- g.select('.nv-controlsWrap')
- .datum(controlsData)
- .attr('transform', 'translate(0,' + (-margin.top) +')')
- .call(controls);
- }
-
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- 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 });
-
- 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);
- }
-
- 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)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled && !data[i].tempDisabled; }));
-
- var linesWrap = g.select('.nv-linesWrap')
- .datum(data.filter(function(d) { return !d.disabled && !d.tempDisabled }));
-
- linesWrap.call(lines);
-
- //Store a series index number in the data array.
- data.forEach(function(d,i) {
- d.seriesIndex = i;
- });
-
- var avgLineData = data.filter(function(d) {
- return !d.disabled && !!average(d);
- });
-
- var avgLines = g.select(".nv-avgLinesWrap").selectAll("line")
- .data(avgLineData, function(d) { return d.key; });
-
- 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.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);
-
- 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);
-
- avgLines.exit().remove();
-
- //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);
-
- indexLine
- .attr('transform', function(d) { return 'translate(' + dx(d.i) + ',0)' })
- .attr('height', availableHeight);
-
- // Setup Axes
- if (showXAxis) {
- xAxis
- .scale(x)
- .ticks( nv.utils.calcTicksX(availableWidth/70, 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);
- }
-
- if (showYAxis) {
- yAxis
- .scale(y)
- .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
-
- g.select('.nv-y.nv-axis')
- .call(yAxis);
- }
-
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
-
- function updateZero() {
- indexLine
- .data([index]);
-
- //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);
- }
-
- g.select('.nv-background rect')
- .on('click', function() {
- index.x = d3.mouse(this)[0];
- index.i = Math.round(dx.invert(index.x));
-
- // 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);
-
- // update state and send stateChange with new index
- state.index = index.i;
- dispatch.stateChange(state);
-
- updateZero();
- });
-
- controls.dispatch.on('legendClick', function(d,i) {
- d.disabled = !d.disabled;
- rescaleY = !d.disabled;
-
- state.rescaleY = rescaleY;
- 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;
- })
- .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)
- });
- });
-
- //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 xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex), pointIndex);
- interactiveLayer.tooltip
- .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
- .chartContainer(that.parentNode)
- .enabled(tooltips)
- .valueFormatter(function(d,i) {
- return yAxis.tickFormat()(d);
- })
- .data(
- {
- value: xValue,
- series: allData
- }
- )();
-
- interactiveLayer.renderGuideLine(pointXLocation);
- });
-
- interactiveLayer.dispatch.on("elementMouseout",function(e) {
- dispatch.tooltipHide();
- lines.clearHighlights();
- });
-
- dispatch.on('tooltipShow', function(e) {
- if (tooltips) showTooltip(e, that.parentNode);
- });
-
- // 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.index !== 'undefined') {
- index.i = e.index;
- index.x = dx(index.i);
-
- state.index = e.index;
-
- indexLine
- .data([index]);
- }
-
- if (typeof e.rescaleY !== 'undefined') {
- rescaleY = e.rescaleY;
- }
-
- chart.update();
- });
-
- });
-
- renderWatch.renderEnd('cumulativeLineChart immediate');
-
- return chart;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- lines.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- lines.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
- // Functions
- //------------------------------------------------------------
-
- 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);
-
- //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;
-
- line.values = line.values.map(function(point, pointIndex) {
- point.display = {'y': (indexifyYGetter(point, pointIndex) - v) / (1 + v) };
- return point;
- });
-
- return line;
- })
- }
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
-
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.lines = lines;
- chart.legend = legend;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.interactiveLayer = interactiveLayer;
- chart.state = state;
-
- 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=_;}},
- 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=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
- 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;
-};//TODO: consider deprecating by adding necessary features to multiBar model
-nv.models.discreteBar = function() {
- "use strict";
-
- //============================================================
- // 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
- , 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','renderEnd')
- , rectClass = 'discreteBar'
- , duration = 250
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- 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,
- 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;
- });
- });
-
- // 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)));
-
- // 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)]);
-
- // 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 + ')');
-
- //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 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({
- value: getY(d,i),
- point: d,
- series: data[d.series],
- pos: [x(getX(d,i)) + (x.rangeBand() * (d.series + .5) / data.length), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
- pointIndex: i,
- seriesIndex: d.series,
- e: d3.event
- });
- })
- .on('mouseout', function(d,i) {
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- value: getY(d,i),
- point: d,
- series: data[d.series],
- pointIndex: i,
- seriesIndex: d.series,
- e: d3.event
- });
- })
- .on('click', function(d,i) {
- dispatch.elementClick({
- value: getY(d,i),
- point: d,
- series: data[d.series],
- pos: [x(getX(d,i)) + (x.rangeBand() * (d.series + .5) / data.length), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
- pointIndex: i,
- seriesIndex: d.series,
- e: d3.event
- });
- d3.event.stopPropagation();
- })
- .on('dblclick', function(d,i) {
- dispatch.elementDblClick({
- value: getY(d,i),
- point: d,
- series: data[d.series],
- pos: [x(getX(d,i)) + (x.rangeBand() * (d.series + .5) / data.length), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
- pointIndex: i,
- seriesIndex: d.series,
- e: d3.event
- });
- d3.event.stopPropagation();
- });
-
- barsEnter.append('rect')
- .attr('height', 0)
- .attr('width', x.rangeBand() * .9 / data.length )
-
- 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 })
-
- ;
- } 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')
- //.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) :
- y(0) - y(getY(d,i)) < 1 ?
- 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((yDomain && yDomain[0]) || 0)) || 1)
- });
-
-
- //store old scales for use in transitions on update
- x0 = x.copy();
- y0 = y.copy();
-
- });
-
- renderWatch.renderEnd('discreteBar 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
- 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);
- }}
- });
-
- nv.utils.initOptions(chart);
-
- return chart;
-};
-
-nv.models.discreteBarChart = function() {
- "use strict";
-
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
-
- var discretebar = nv.models.discreteBar()
- , 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
- , tooltips = true
- , tooltip = function(key, x, y, e, graph) {
- return '<h3>' + x + '</h3>' +
- '<p>' + y + '</p>'
- }
- , x
- , y
- , noData = "No Data Available."
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'beforeUpdate','renderEnd')
- , duration = 250
- ;
-
- xAxis
- .orient('bottom')
- .highlightZero(false)
- .showMaxMin(false)
- .tickFormat(function(d) { return d })
- ;
- yAxis
- .orient((rightAlignYAxis) ? 'right' : 'left')
- .tickFormat(d3.format(',.1f'))
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var showTooltip = function(e, offsetElement) {
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- top = e.pos[1] + ( offsetElement.offsetTop || 0),
- x = xAxis.tickFormat()(discretebar.x()(e.point, e.pointIndex)),
- y = yAxis.tickFormat()(discretebar.y()(e.point, e.pointIndex)),
- content = tooltip(e.series.key, x, y, e, chart);
-
- nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
- };
-
- 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);
-
- 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,
- 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;
-
- // Display No Data message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).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
- .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();
- }
-
- // 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');
-
- 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 + ')');
-
- if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
- }
-
- // Main Chart Component(s)
- discretebar
- .width(availableWidth)
- .height(availableHeight);
-
- var barsWrap = g.select('.nv-barsWrap')
- .datum(data.filter(function(d) { return !d.disabled }))
-
- barsWrap.transition().call(discretebar);
-
-
- 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 ));
-
- // 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);
-
- 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( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
-
- 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))
- ;
-
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
-
- dispatch.on('tooltipShow', function(e) {
- if (tooltips) showTooltip(e, that.parentNode);
- });
-
- });
-
- renderWatch.renderEnd('discreteBar chart immediate');
- return chart;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- discretebar.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- discretebar.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
-
- chart.dispatch = dispatch;
- chart.discretebar = discretebar;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
-
- 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=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- 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);
- discretebar.duration(duration);
- xAxis.duration(duration);
- yAxis.duration(duration);
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- discretebar.color(color);
- }},
- rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
- rightAlignYAxis = _;
- yAxis.orient( (_) ? 'right' : 'left');
- }}
- });
-
- nv.utils.inheritOptions(chart, discretebar);
- nv.utils.initOptions(chart);
-
- 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);
-
- //============================================================
-
-
- 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 + ')')
-
- //------------------------------------------------------------
-
-
- 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)) })
-
-
- scale0 = scale.copy();
-
- });
- renderWatch.renderEnd('distribution immediate');
- return chart;
- }
-
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
- chart.options = nv.utils.optionsFunc.bind(chart);
- chart.dispatch = dispatch;
-
- 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;
- };
-
- chart.axis = function(_) {
- if (!arguments.length) return axis;
- axis = _;
- return chart;
- };
-
- chart.size = function(_) {
- if (!arguments.length) return size;
- size = _;
- return chart;
- };
-
- chart.getData = function(_) {
- if (!arguments.length) return getData;
- getData = d3.functor(_);
- 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;
- };
-
- chart.duration = function(_) {
- if (!arguments.length) return duration;
- duration = _;
- renderWatch.reset(duration);
- return chart;
- };
- //============================================================
-
-
- return chart;
-}
-//TODO: consider deprecating and using multibar with single series for this
-nv.models.historicalBar = function() {
- "use strict";
-
- //============================================================
- // 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
- , 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', 'renderEnd')
- , interactive = true
- ;
-
- var renderWatch = nv.utils.renderWatch(dispatch, 0);
-
- function chart(selection) {
- selection.each(function(data) {
- renderWatch.reset();
-
- var container = d3.select(this);
- var availableWidth = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right;
- var availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
- nv.utils.initSVG(container);
-
- // 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]);
-
- 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 (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');
-
- 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
- });
- });
-
- 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);
-
- 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 barsEnter = 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({
- point: d,
- series: data[0],
- pos: [x(getX(d,i)), y(getY(d,i))], // TODO: Figure out why the value appears to be shifted
- pointIndex: i,
- seriesIndex: 0,
- e: d3.event
- });
-
- })
- .on('mouseout', function(d,i) {
- if (!interactive) return;
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- point: d,
- series: data[0],
- pointIndex: i,
- seriesIndex: 0,
- e: d3.event
- });
- })
- .on('click', function(d,i) {
- if (!interactive) return;
- dispatch.elementClick({
- //label: d[label],
- value: getY(d,i),
- data: d,
- index: i,
- pos: [x(getX(d,i)), y(getY(d,i))],
- e: d3.event,
- id: id
- });
- d3.event.stopPropagation();
- })
- .on('dblclick', function(d,i) {
- if (!interactive) return;
- dispatch.elementDblClick({
- //label: d[label],
- value: getY(d,i),
- data: d,
- index: i,
- pos: [x(getX(d,i)), y(getY(d,i))],
- e: d3.event,
- id: id
- });
- 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.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)) });
-
- });
-
- renderWatch.renderEnd('historicalBar immediate');
- return chart;
- }
-
- //Create methods to allow outside functions to highlight a specific bar.
- chart.highlightPoint = function(pointIndex, isHoverOver) {
- d3.select(".nv-historicalBar-" + id)
- .select(".nv-bars .nv-bar-0-" + pointIndex)
- .classed("hover", isHoverOver)
- ;
- };
-
- chart.clearHighlights = function() {
- d3.select(".nv-historicalBar-" + id)
- .select(".nv-bars .nv-bar.hover")
- .classed("hover", false)
- ;
- };
-
- //============================================================
- // 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
- 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(_);
- }}
- });
-
- nv.utils.initOptions(chart);
-
- return chart;
-};
-
-nv.models.historicalBarChart = function(bar_model) {
- "use strict";
-
- //============================================================
- // 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()
- ;
-
-
- 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
- , tooltips = true
- , tooltip = function(key, x, y, e, graph) {
- return '<h3>' + key + '</h3>' +
- '<p>' + y + ' at ' + x + '</p>'
- }
- , x
- , y
- , state = {}
- , defaultState = null
- , noData = 'No Data Available.'
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
- , transitionDuration = 250
- ;
-
- xAxis
- .orient('bottom')
- .tickPadding(7)
- ;
- yAxis
- .orient( (rightAlignYAxis) ? 'right' : 'left')
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var showTooltip = function(e, offsetElement) {
-
- // New addition to calculate position if SVG is scaled with viewBox, may move TODO: consider implementing everywhere else
- if (offsetElement) {
- var svg = d3.select(offsetElement).select('svg');
- var viewBox = (svg.node()) ? svg.attr('viewBox') : null;
- if (viewBox) {
- viewBox = viewBox.split(' ');
- var ratio = parseInt(svg.style('width')) / viewBox[2];
- e.pos[0] = e.pos[0] * ratio;
- e.pos[1] = e.pos[1] * ratio;
- }
- }
-
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- top = e.pos[1] + ( offsetElement.offsetTop || 0),
- x = xAxis.tickFormat()(bars.x()(e.point, e.pointIndex)),
- y = yAxis.tickFormat()(bars.y()(e.point, e.pointIndex)),
- content = tooltip(e.series.key, x, y, e, chart);
-
- nv.tooltip.show([left, top], content, null, null, offsetElement);
- };
- 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);
-
- var container = d3.select(this),
- that = this;
- nv.utils.initSVG(container);
- var availableWidth = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right,
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
-
- chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
- chart.container = this;
-
- //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 noData message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).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
- .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();
- }
-
- // 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');
-
- 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) {
- legend.width(availableWidth);
-
- g.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
-
- if ( margin.top != legend.height()) {
- margin.top = legend.height();
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
- }
-
- 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)");
- }
-
- //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)
- .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);
-
- // Setup Axes
- if (showXAxis) {
- xAxis
- .scale(x)
- .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);
- }
-
- if (showYAxis) {
- yAxis
- .scale(y)
- .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
-
- g.select('.nv-y.nv-axis')
- .transition()
- .call(yAxis);
- }
-
- //============================================================
- // Event Handling/Dispatching (in chart's scope)
- //------------------------------------------------------------
-
- 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 (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),
- data: series.values[pointIndex]
- });
- });
-
- var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
- interactiveLayer.tooltip
- .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
- .chartContainer(that.parentNode)
- .enabled(tooltips)
- .valueFormatter(function(d,i) {
- return yAxis.tickFormat()(d);
- })
- .data(
- {
- value: xValue,
- series: allData
- }
- )();
-
- interactiveLayer.renderGuideLine(pointXLocation);
-
- });
-
- interactiveLayer.dispatch.on("elementMouseout",function(e) {
- dispatch.tooltipHide();
- bars.clearHighlights();
- });
-
- 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;
- });
- }
-
- 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;
- });
- d.disabled = false;
-
- state.disabled = data.map(function(d) { return !!d.disabled });
- dispatch.stateChange(state);
- chart.update();
- });
-
- dispatch.on('tooltipShow', function(e) {
- if (tooltips) showTooltip(e, that.parentNode);
- });
-
- 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;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- bars.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- bars.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
- // 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.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=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
- 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);
- }
- }}
- });
-
- nv.utils.inheritOptions(chart, bars);
- nv.utils.initOptions(chart);
-
- return chart;
-};
-
-
-// ohlcChart is just a historical chart with oclc bars and some tweaks
-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;
-};nv.models.legend = function() {
- "use strict";
-
- //============================================================
- // 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 }
- , color = nv.utils.defaultColor()
- , align = true
- , 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)
- , dispatch = d3.dispatch('legendClick', 'legendDblclick', 'legendMouseover', 'legendMouseout', 'stateChange')
- ;
-
- 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');
-
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- var series = g.selectAll('.nv-series')
- .data(function(d) { return d });
- var seriesEnter = series.enter().append('g').attr('class', 'nv-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);
- if (updateState) {
- 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});
- }
- }
- dispatch.stateChange({
- disabled: data.map(function(d) { return !!d.disabled })
- });
- }
- })
- .on('dblclick', function(d,i) {
- dispatch.legendDblclick(d,i);
- if (updateState) {
- //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;
- });
- d.disabled = false;
- dispatch.stateChange({
- disabled: data.map(function(d) { return !!d.disabled })
- });
- }
- });
- seriesEnter.append('circle')
- .style('stroke-width', 2)
- .attr('class','nv-legend-symbol')
- .attr('r', 5);
- seriesEnter.append('text')
- .attr('text-anchor', 'start')
- .attr('class','nv-legend-text')
- .attr('dy', '.32em')
- .attr('dx', '8');
- series.classed('nv-disabled', function(d) { return d.disabled });
- series.exit().remove();
- series.select('circle')
- .style('fill', function(d,i) { return d.color || color(d,i)})
- .style('stroke', function(d,i) { return d.color || color(d, i) });
- series.select('text').text(getKey);
-
- //TODO: implement fixed-width and max-width options (max-width is especially useful with the align option)
- // NEW ALIGNING CODE, TODO: clean up
- if (align) {
-
- var seriesWidths = [];
- series.each(function(d,i) {
- var 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 + 28); // 28 is ~ the width of the circle plus some padding
- });
-
- 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 > 1 ) {
- columnWidths = [];
- seriesPerRow--;
-
- 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;
- });
- }
-
- 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) * 20) + ')';
- });
-
- //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) * 20);
-
- } else {
-
- var ypos = 5,
- newxpos = 5,
- maxwidth = 0,
- xpos;
- series
- .attr('transform', function(d, i) {
- var length = d3.select(this).select('text').node().getComputedTextLength() + 28;
- xpos = newxpos;
-
- if (width < margin.left + margin.right + xpos + length) {
- newxpos = xpos = 5;
- ypos += 20;
- }
-
- newxpos += length;
- if (newxpos > maxwidth) maxwidth = newxpos;
-
- 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 + ')');
-
- height = margin.top + margin.bottom + ypos + 15;
- }
- });
-
- 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
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- key: {get: function(){return getKey;}, set: function(_){getKey=_;}},
- align: {get: function(){return align;}, set: function(_){align=_;}},
- rightAlign: {get: function(){return rightAlign;}, set: function(_){rightAlign=_;}},
- updateState: {get: function(){return updateState;}, set: function(_){updateState=_;}},
- radioButtonMode: {get: function(){return radioButtonMode;}, set: function(_){radioButtonMode=_;}},
-
- // 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.line = function() {
- "use strict";
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
-
- var scatter = nv.models.scatter()
- ;
-
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 960
- , height = 500
- , 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
- ;
-
- //============================================================
-
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- 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) {
- var availableWidth = width - margin.left - margin.right,
- availableHeight = height - margin.top - margin.bottom,
- container = d3.select(this);
- nv.utils.initSVG(container);
-
- // Setup Scales
- x = scatter.xScale();
- y = scatter.yScale();
-
- 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');
-
- gEnter.append('g').attr('class', 'nv-groups');
- gEnter.append('g').attr('class', 'nv-scatterWrap');
-
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- scatter
- .width(availableWidth)
- .height(availableHeight);
-
- var scatterWrap = wrap.select('.nv-scatterWrap');
- scatterWrap.call(scatter);
-
- 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);
-
- 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('fill-opacity', 1e-6);
-
- groups.exit().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, 'line: groups')
- .style('stroke-opacity', 1)
- .style('fill-opacity', .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();
-
- 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] });
- 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))) })
- );
-
- //store old scales for use in transitions on update
- x0 = x.copy();
- y0 = y.copy();
- });
- renderWatch.renderEnd('line immediate');
- return chart;
- }
-
-
- //============================================================
- // 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.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=_;}},
-
- // 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);
-
- return chart;
-};
-nv.models.lineChart = function() {
- "use strict";
-
- //============================================================
- // 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()
- ;
-
- var margin = {top: 30, right: 20, bottom: 50, left: 60}
- , color = nv.utils.defaultColor()
- , width = null
- , height = null
- , showLegend = true
- , showXAxis = true
- , showYAxis = true
- , rightAlignYAxis = false
- , useInteractiveGuideline = false
- , tooltips = true
- , tooltip = function(key, x, y, e, graph) {
- return '<h3>' + key + '</h3>' +
- '<p>' + y + ' at ' + x + '</p>'
- }
- , x
- , y
- , state = nv.utils.state()
- , defaultState = null
- , noData = 'No Data Available.'
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
- , duration = 250
- ;
-
- xAxis
- .orient('bottom')
- .tickPadding(7)
- ;
- yAxis
- .orient((rightAlignYAxis) ? 'right' : 'left')
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var showTooltip = function(e, offsetElement) {
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- top = e.pos[1] + ( offsetElement.offsetTop || 0),
- x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
- y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
- content = tooltip(e.series.key, x, y, e, chart);
-
- nv.tooltip.show([left, top], content, null, null, offsetElement);
- };
-
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
-
- 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];
- });
- }
- };
-
- 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),
- that = this;
- nv.utils.initSVG(container);
- var availableWidth = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right,
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
-
- 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];
- }
- }
-
- // Display noData message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).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
- .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();
- }
-
-
- // 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');
-
- gEnter.append("rect").style("opacity",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-linesWrap');
- gEnter.append('g').attr('class', 'nv-legendWrap');
- gEnter.append('g').attr('class', 'nv-interactive');
-
- g.select("rect")
- .attr("width",availableWidth)
- .attr("height",(availableHeight > 0) ? availableHeight : 0);
-
- // Legend
- if (showLegend) {
- legend.width(availableWidth);
-
- g.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
-
- if ( margin.top != legend.height()) {
- margin.top = legend.height();
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
- }
-
- 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)");
- }
-
- //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);
- }
-
- 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 }));
-
- linesWrap.call(lines);
-
- // 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);
- }
-
- if (showYAxis) {
- yAxis
- .scale(y)
- .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
-
- g.select('.nv-y.nv-axis')
- .call(yAxis);
- }
-
- //============================================================
- // 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();
- });
-
- interactiveLayer.dispatch.on('elementMousemove', function(e) {
- lines.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());
- 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)
- });
- });
- //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 xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
- interactiveLayer.tooltip
- .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
- .chartContainer(that.parentNode)
- .enabled(tooltips)
- .valueFormatter(function(d,i) {
- return yAxis.tickFormat()(d);
- })
- .data(
- {
- value: xValue,
- series: allData
- }
- )();
-
- interactiveLayer.renderGuideLine(pointXLocation);
-
- });
-
- 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
- });
- });
-
- lines.dispatch.elementClick(allData);
- });
-
- interactiveLayer.dispatch.on("elementMouseout",function(e) {
- dispatch.tooltipHide();
- lines.clearHighlights();
- });
-
- dispatch.on('tooltipShow', function(e) {
- if (tooltips) showTooltip(e, that.parentNode);
- });
-
- 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();
- });
-
- });
-
- renderWatch.renderEnd('lineChart immediate');
- return chart;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- lines.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- lines.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
-
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.lines = lines;
- chart.legend = legend;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.interactiveLayer = interactiveLayer;
-
- 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=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
- 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;
- }},
- duration: {get: function(){return duration;}, set: function(_){
- duration = _;
- renderWatch.reset(duration);
- lines.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);
- }},
- 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);
- }
- }}
- });
-
- nv.utils.inheritOptions(chart, lines);
- nv.utils.initOptions(chart);
-
- return chart;
-};
-nv.models.linePlusBarChart = function() {
- "use strict";
-
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
-
- 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()
- ;
-
- 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
- , tooltips = true
- , tooltip = function(key, x, y, e, graph) {
- return '<h3>' + key + '</h3>' +
- '<p>' + y + ' at ' + x + '</p>';
- }
- , x
- , x2
- , y1
- , y2
- , y3
- , y4
- , noData = "No Data Available."
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'brush', 'stateChange', 'changeState')
- , transitionDuration = 0
- , state = nv.utils.state()
- , defaultState = null
- ;
-
- lines
- .clipEdge(true)
- ;
- lines2
- .interactive(false)
- ;
- xAxis
- .orient('bottom')
- .tickPadding(5)
- ;
- y1Axis
- .orient('left')
- ;
- y2Axis
- .orient('right')
- ;
- x2Axis
- .orient('bottom')
- .tickPadding(5)
- ;
- y3Axis
- .orient('left')
- ;
- y4Axis
- .orient('right')
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var showTooltip = function(e, offsetElement) {
- if (extent) {
- e.pointIndex += Math.ceil(extent[0]);
- }
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- top = e.pos[1] + ( offsetElement.offsetTop || 0),
- x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
- y = (e.series.bar ? y1Axis : y2Axis).tickFormat()(lines.y()(e.point, e.pointIndex)),
- content = tooltip(e.series.key, x, y, e, chart);
-
- nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
- };
-
- 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];
- });
- }
- };
-
- function chart(selection) {
- 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,
- availableHeight1 = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom - (focusEnable ? focusHeight : 0) ,
- availableHeight2 = focusHeight - margin2.top - margin2.bottom;
-
- chart.update = function() { container.transition().duration(transitionDuration).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];
- }
- }
-
- // Display No Data message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).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
- .attr('x', margin.left + availableWidth / 2)
- .attr('y', margin.top + availableHeight1 / 2)
- .text(function(d) { return d });
-
- 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
-
- x = bars.xScale();
- x2 = x2Axis.scale();
- y1 = bars.yScale();
- y2 = lines.yScale();
- y3 = bars2.yScale();
- y4 = lines2.yScale();
-
- var series1 = data
- .filter(function(d) { return !d.disabled && 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 && !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]);
-
- 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');
-
- 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');
-
- // 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
- //------------------------------------------------------------
-
- if (showLegend) {
- legend.width( availableWidth / 2 );
-
- g.select('.nv-legendWrap')
- .datum(data.map(function(series) {
- series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
- series.key = series.originalKey + (series.bar ? ' (left axis)' : ' (right axis)');
- return series;
- }))
- .call(legend);
-
- if ( margin.top != legend.height()) {
- margin.top = legend.height();
- availableHeight1 = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom - focusHeight;
- }
-
- g.select('.nv-legendWrap')
- .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')');
- }
-
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- //============================================================
- // Context chart (focus chart) components
- //------------------------------------------------------------
-
- // 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
- }));
-
- var bars2Wrap = g.select('.nv-context .nv-barsWrap')
- .datum(dataBars.length ? dataBars : [
- {values: []}
- ]);
- var lines2Wrap = g.select('.nv-context .nv-linesWrap')
- .datum(!dataLines[0].disabled ? dataLines : [
- {values: []}
- ]);
-
- g.select('.nv-context')
- .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')');
-
- 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);
- }
-
- 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-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);
-
- if (brushExtent) brush.extent(brushExtent);
-
- var brushBG = g.select('.nv-brushBackground').selectAll('g')
- .data([brushExtent || brush.extent()]);
-
- 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', 'right')
- .attr('x', 0)
- .attr('y', 0)
- .attr('height', availableHeight2);
-
- 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);
-
- //============================================================
- // 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();
- });
-
- dispatch.on('tooltipShow', function(e) {
- if (tooltips) showTooltip(e, that.parentNode);
- });
-
- // 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
- //------------------------------------------------------------
-
- // 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);
-
- 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();
-
- // 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 }));
-
- 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(dataLines[0].disabled ? [{values:[]}] :
- dataLines
- .map(function(d,i) {
- return {
- 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) {
- x = bars.xScale();
- } else {
- x = lines.xScale();
- }
-
- xAxis
- .scale(x)
- .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize(-availableHeight1, 0);
-
- xAxis.domain([Math.ceil(extent[0]), Math.floor(extent[1])]);
-
- 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);
-
- // 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) )
- .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
-
- g.select('.nv-focus .nv-y1.nv-axis')
- .style('opacity', dataBars.length ? 1 : 0);
- g.select('.nv-focus .nv-y2.nv-axis')
- .style('opacity', dataLines.length && !dataLines[0].disabled ? 1 : 0)
- .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);
- }
-
- onBrush();
-
- });
-
- return chart;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- lines.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- lines.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- bars.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- bars.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
-
-
- //============================================================
- // 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.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=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
- 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=_;}},
-
- // 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 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(_);
- }}
- });
-
- nv.utils.inheritOptions(chart, lines);
- nv.utils.initOptions(chart);
-
- return chart;
-};
-nv.models.lineWithFocusChart = function() {
- "use strict";
-
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
-
- var lines = nv.models.line()
- , lines2 = nv.models.line()
- , xAxis = nv.models.axis()
- , yAxis = nv.models.axis()
- , x2Axis = nv.models.axis()
- , y2Axis = nv.models.axis()
- , legend = nv.models.legend()
- , brush = d3.svg.brush()
- ;
-
- var margin = {top: 30, right: 30, bottom: 30, left: 60}
- , margin2 = {top: 0, right: 30, bottom: 20, left: 60}
- , color = nv.utils.defaultColor()
- , width = null
- , height = null
- , height2 = 100
- , x
- , y
- , x2
- , y2
- , showLegend = true
- , brushExtent = null
- , tooltips = true
- , tooltip = function(key, x, y, e, graph) {
- return '<h3>' + key + '</h3>' +
- '<p>' + y + ' at ' + x + '</p>'
- }
- , noData = "No Data Available."
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'brush', 'stateChange', 'changeState')
- , transitionDuration = 250
- , state = nv.utils.state()
- , defaultState = null
- ;
-
- lines
- .clipEdge(true)
- ;
- lines2
- .interactive(false)
- ;
- xAxis
- .orient('bottom')
- .tickPadding(5)
- ;
- yAxis
- .orient('left')
- ;
- x2Axis
- .orient('bottom')
- .tickPadding(5)
- ;
- y2Axis
- .orient('left')
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var showTooltip = function(e, offsetElement) {
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- top = e.pos[1] + ( offsetElement.offsetTop || 0),
- x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
- y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
- content = tooltip(e.series.key, x, y, e, chart);
-
- nv.tooltip.show([left, top], content, null, null, offsetElement);
- };
-
- 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];
- });
- }
- };
-
- function chart(selection) {
- 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,
- availableHeight1 = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom - height2,
- availableHeight2 = height2 - margin2.top - margin2.bottom;
-
- chart.update = function() { container.transition().duration(transitionDuration).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];
- }
- }
-
- // Display No Data message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).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
- .attr('x', margin.left + availableWidth / 2)
- .attr('y', margin.top + availableHeight1 / 2)
- .text(function(d) { return d });
-
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
-
- // Setup Scales
- x = lines.xScale();
- y = lines.yScale();
- x2 = lines2.xScale();
- y2 = lines2.yScale();
-
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-lineWithFocusChart').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineWithFocusChart').append('g');
- var g = wrap.select('g');
-
- gEnter.append('g').attr('class', 'nv-legendWrap');
-
- var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
- 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');
-
- var contextEnter = gEnter.append('g').attr('class', 'nv-context');
- contextEnter.append('g').attr('class', 'nv-x nv-axis');
- contextEnter.append('g').attr('class', 'nv-y nv-axis');
- contextEnter.append('g').attr('class', 'nv-linesWrap');
- contextEnter.append('g').attr('class', 'nv-brushBackground');
- contextEnter.append('g').attr('class', 'nv-x nv-brush');
-
- // Legend
- if (showLegend) {
- legend.width(availableWidth);
-
- g.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
-
- if ( margin.top != legend.height()) {
- margin.top = legend.height();
- availableHeight1 = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom - height2;
- }
-
- g.select('.nv-legendWrap')
- .attr('transform', 'translate(0,' + (-margin.top) +')')
- }
-
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- // Main Chart Component(s)
- 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;
- })
- );
-
- lines2
- .defined(lines.defined())
- .width(availableWidth)
- .height(availableHeight2)
- .color(
- data
- .map(function(d,i) {
- return d.color || color(d, i);
- })
- .filter(function(d,i) {
- return !data[i].disabled;
- })
- );
-
- g.select('.nv-context')
- .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')')
-
- var contextLinesWrap = g.select('.nv-context .nv-linesWrap')
- .datum(data.filter(function(d) { return !d.disabled }))
-
- d3.transition(contextLinesWrap).call(lines2);
-
- // Setup Main (Focus) Axes
- xAxis
- .scale(x)
- .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize(-availableHeight1, 0);
-
- yAxis
- .scale(y)
- .ticks( nv.utils.calcTicksY(availableHeight1/36, data) )
- .tickSize( -availableWidth, 0);
-
- g.select('.nv-focus .nv-x.nv-axis')
- .attr('transform', 'translate(0,' + availableHeight1 + ')');
-
- // Setup Brush
- brush
- .x(x2)
- .on('brush', function() {
- //When brushing, turn off transitions because chart needs to change immediately.
- var oldTransition = chart.duration();
- chart.duration(0);
- onBrush();
- chart.duration(oldTransition);
- });
-
- if (brushExtent) brush.extent(brushExtent);
-
- var brushBG = g.select('.nv-brushBackground').selectAll('g')
- .data([brushExtent || brush.extent()])
-
- 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', 'right')
- .attr('x', 0)
- .attr('y', 0)
- .attr('height', availableHeight2);
-
- 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);
-
- onBrush();
-
- // Setup Secondary (Context) Axes
- x2Axis
- .scale(x2)
- .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize(-availableHeight2, 0);
-
- g.select('.nv-context .nv-x.nv-axis')
- .attr('transform', 'translate(0,' + y2.range()[0] + ')');
- d3.transition(g.select('.nv-context .nv-x.nv-axis'))
- .call(x2Axis);
-
- y2Axis
- .scale(y2)
- .ticks( nv.utils.calcTicksY(availableHeight2/36, data) )
- .tickSize( -availableWidth, 0);
-
- d3.transition(g.select('.nv-context .nv-y.nv-axis'))
- .call(y2Axis);
-
- g.select('.nv-context .nv-x.nv-axis')
- .attr('transform', 'translate(0,' + y2.range()[0] + ')');
-
- //============================================================
- // 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();
- });
-
- dispatch.on('tooltipShow', function(e) {
- if (tooltips) showTooltip(e, that.parentNode);
- });
-
- dispatch.on('changeState', function(e) {
- if (typeof e.disabled !== 'undefined') {
- data.forEach(function(series,i) {
- series.disabled = e.disabled[i];
- });
- }
- chart.update();
- });
-
- //============================================================
- // 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);
- }
-
-
- function updateBrushBG() {
- if (!brush.empty()) brush.extent(brushExtent);
- brushBG
- .data([brush.empty() ? x2.domain() : brushExtent])
- .each(function(d,i) {
- var leftWidth = x2(d[0]) - x.range()[0],
- rightWidth = x.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);
- });
- }
-
-
- function onBrush() {
- brushExtent = brush.empty() ? null : brush.extent();
- var extent = brush.empty() ? x2.domain() : brush.extent();
-
- //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;
- }
-
- dispatch.brush({extent: extent, brush: brush});
-
-
- updateBrushBG();
-
- // 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,
- values: d.values.filter(function(d,i) {
- return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
- })
- }
- })
- );
- focusLinesWrap.transition().duration(transitionDuration).call(lines);
-
-
- // Update Main (Focus) Axes
- g.select('.nv-focus .nv-x.nv-axis').transition().duration(transitionDuration)
- .call(xAxis);
- g.select('.nv-focus .nv-y.nv-axis').transition().duration(transitionDuration)
- .call(yAxis);
- }
- });
-
- return chart;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- lines.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- lines.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
-
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.legend = legend;
- chart.lines = lines;
- chart.lines2 = lines2;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.x2Axis = x2Axis;
- chart.y2Axis = y2Axis;
-
- 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=_;}},
- focusHeight: {get: function(){return height2;}, set: function(_){height2=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- brushExtent: {get: function(){return brushExtent;}, set: function(_){brushExtent=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
- 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);
- // line color is handled above?
- }},
- interpolate: {get: function(){return lines.interpolate();}, set: function(_){
- lines.interpolate(_);
- lines2.interpolate(_);
- }},
- xTickFormat: {get: function(){return xAxis.xTickFormat();}, set: function(_){
- xAxis.xTickFormat(_);
- x2Axis.xTickFormat(_);
- }},
- yTickFormat: {get: function(){return yAxis.yTickFormat();}, set: function(_){
- yAxis.yTickFormat(_);
- y2Axis.yTickFormat(_);
- }},
- duration: {get: function(){return transitionDuration;}, set: function(_){
- transitionDuration=_;
- yAxis.duration(transitionDuration);
- xAxis.duration(transitionDuration);
- }},
- x: {get: function(){return lines.x();}, set: function(_){
- lines.x(_);
- lines2.x(_);
- }},
- y: {get: function(){return lines.y();}, set: function(_){
- lines.y(_);
- lines2.y(_);
- }}
- });
-
- nv.utils.inheritOptions(chart, lines);
- nv.utils.initOptions(chart);
-
- return chart;
-};
-
-nv.models.multiBar = function() {
- "use strict";
-
- //============================================================
- // 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
- , 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
- , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var x0, y0 //used to store previous scales
- , renderWatch = nv.utils.renderWatch(dispatch, duration)
- ;
-
- 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,
- container = d3.select(this);
- nv.utils.initSVG(container);
-
- // 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) {
- return {
- x: d.x,
- y: 0,
- series: d.series,
- size: 0.01
- };}
- )}];
-
- if (stacked)
- data = d3.layout.stack()
- .offset(stackOffset)
- .values(function(d){ return d.values })
- .y(getY)
- (!data.length && hideable ? hideable : data);
-
-
- //add series index to each data point for reference
- data.forEach(function(series, i) {
- series.values.forEach(function(point) {
- point.series = i;
- });
- });
-
- // 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;
- negBase = negBase - f.size;
- } else
- {
- f.y1 = f.size + posBase;
- posBase = posBase + f.size;
- }
- });
- });
-
- // 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 }
- })
- });
-
- x.domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
- .rangeBands(xRange || [0, availableWidth], groupSpacing);
-
- y.domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return stacked ? (d.y > 0 ? d.y1 : d.y1 + d.y ) : d.y }).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]);
-
- x0 = x0 || x;
- y0 = y0 || y;
-
- // 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')
-
- gEnter.append('g').attr('class', 'nv-groups');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- defsEnter.append('clipPath')
- .attr('id', 'nv-edge-clip-' + id)
- .append('rect');
- wrap.select('#nv-edge-clip-' + id + ' rect')
- .attr('width', availableWidth)
- .attr('height', availableHeight);
-
- g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
-
- 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 exitTransition = renderWatch
- .transition(groups.exit().selectAll('rect.nv-bar'), 'multibarExit', Math.min(100, duration))
- .attr('y', function(d) { return (stacked ? y0(d.y0) : y0(0)) || 0 })
- .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', 0.75);
-
- var bars = groups.selectAll('rect.nv-bar')
- .data(function(d) { return (hideable && !data.length) ? hideable.values : d.values });
- bars.exit().remove();
-
- 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 ? 0 : (j * x.rangeBand() / data.length )
- })
- .attr('y', function(d) { return y0(stacked ? d.y0 : 0) || 0 })
- .attr('height', 0)
- .attr('width', x.rangeBand() / (stacked ? 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({
- value: getY(d,i),
- point: d,
- series: data[d.series],
- pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
- pointIndex: i,
- seriesIndex: d.series,
- e: d3.event
- });
- })
- .on('mouseout', function(d,i) {
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- value: getY(d,i),
- point: d,
- series: data[d.series],
- pointIndex: i,
- seriesIndex: d.series,
- e: d3.event
- });
- })
- .on('click', function(d,i) {
- dispatch.elementClick({
- value: getY(d,i),
- point: d,
- series: data[d.series],
- pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
- pointIndex: i,
- seriesIndex: d.series,
- e: d3.event
- });
- d3.event.stopPropagation();
- })
- .on('dblclick', function(d,i) {
- dispatch.elementDblClick({
- value: getY(d,i),
- point: d,
- series: data[d.series],
- pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
- pointIndex: i,
- seriesIndex: d.series,
- e: d3.event
- });
- 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)'; })
-
- 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) {
- return y((stacked ? d.y1 : 0));
- })
- .attr('height', function(d,i) {
- return Math.max(Math.abs(y(d.y + (stacked ? d.y0 : 0)) - y((stacked ? d.y0 : 0))),1);
- })
- .attr('x', function(d,i) {
- return stacked ? 0 : (d.series * x.rangeBand() / data.length )
- })
- .attr('width', x.rangeBand() / (stacked ? 1 : data.length) );
- 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;
- });
-
- //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;
- }
-
- });
-
- renderWatch.renderEnd('multibar 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
- 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=_;}},
-
- // 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(_);
- }}
- });
-
- nv.utils.initOptions(chart);
-
- return chart;
-};
-
-nv.models.multiBarChart = function() {
- "use strict";
-
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
-
- var multibar = nv.models.multiBar()
- , xAxis = nv.models.axis()
- , yAxis = nv.models.axis()
- , legend = nv.models.legend()
- , controls = nv.models.legend()
- ;
-
- var margin = {top: 30, right: 20, bottom: 50, left: 60}
- , width = null
- , height = null
- , color = nv.utils.defaultColor()
- , showControls = true
- , showLegend = true
- , showXAxis = true
- , showYAxis = true
- , rightAlignYAxis = false
- , reduceXTicks = true // if false a tick will show for every data point
- , staggerLabels = false
- , rotateLabels = 0
- , tooltips = true
- , tooltip = function(key, x, y, e, graph) {
- return '<h3>' + key + '</h3>' +
- '<p>' + y + ' on ' + x + '</p>'
- }
- , x //can be accessed via chart.xScale()
- , y //can be accessed via chart.yScale()
- , state = nv.utils.state()
- , defaultState = null
- , noData = "No Data Available."
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
- , controlWidth = function() { return showControls ? 180 : 0 }
- , duration = 250
- ;
-
- state.stacked = false // DEPRECATED Maintained for backward compatibility
-
- multibar
- .stacked(false)
- ;
- xAxis
- .orient('bottom')
- .tickPadding(7)
- .highlightZero(true)
- .showMaxMin(false)
- .tickFormat(function(d) { return d })
- ;
- yAxis
- .orient((rightAlignYAxis) ? 'right' : 'left')
- .tickFormat(d3.format(',.1f'))
- ;
-
- controls.updateState(false);
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var renderWatch = nv.utils.renderWatch(dispatch);
- var stacked = false;
-
- var showTooltip = function(e, offsetElement) {
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- top = e.pos[1] + ( offsetElement.offsetTop || 0),
- x = xAxis.tickFormat()(multibar.x()(e.point, e.pointIndex)),
- y = yAxis.tickFormat()(multibar.y()(e.point, e.pointIndex)),
- content = tooltip(e.series.key, x, y, e, chart);
-
- nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
- };
-
- var stateGetter = function(data) {
- return function(){
- return {
- active: data.map(function(d) { return !d.disabled }),
- stacked: stacked
- };
- }
- };
-
- 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];
- });
- }
- };
-
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(multibar);
- 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,
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
- 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];
- }
- }
-
- // Display noData message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).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
- .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();
- }
-
- // Setup Scales
- x = multibar.xScale();
- y = multibar.yScale();
-
- // 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');
-
- 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');
-
- // Legend
- if (showLegend) {
- legend.width(availableWidth - controlWidth());
-
- if (multibar.barColor())
- data.forEach(function(series,i) {
- series.color = d3.rgb('#ccc').darker(i * 1.5).toString();
- });
-
- g.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
-
- if ( margin.top != legend.height()) {
- margin.top = legend.height();
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
- }
-
- g.select('.nv-legendWrap')
- .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
- }
-
- // Controls
- if (showControls) {
- var controlsData = [
- { key: 'Grouped', disabled: multibar.stacked() },
- { key: 'Stacked', disabled: !multibar.stacked() }
- ];
-
- controls.width(controlWidth()).color(['#444', '#444', '#444']);
- g.select('.nv-controlsWrap')
- .datum(controlsData)
- .attr('transform', 'translate(0,' + (-margin.top) +')')
- .call(controls);
- }
-
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
- if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
- }
-
- // 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 }));
-
-
- var barsWrap = g.select('.nv-barsWrap')
- .datum(data.filter(function(d) { return !d.disabled }));
-
- barsWrap.call(multibar);
-
- // 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);
-
- var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g');
-
- xTicks
- .selectAll('line, text')
- .style('opacity', 1)
-
- 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 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);
- });
- }
-
- if (reduceXTicks)
- xTicks
- .filter(function(d,i) {
- return i % Math.ceil(data[0].values.length / (availableWidth / 100)) !== 0;
- })
- .selectAll('text, line')
- .style('opacity', 0);
-
- if(rotateLabels)
- xTicks
- .selectAll('.tick text')
- .attr('transform', 'rotate(' + rotateLabels + ' 0,0)')
- .style('text-anchor', rotateLabels > 0 ? 'start' : 'end');
-
- g.select('.nv-x.nv-axis').selectAll('g.nv-axisMaxMin text')
- .style('opacity', 1);
- }
-
- if (showYAxis) {
- yAxis
- .scale(y)
- .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
-
- g.select('.nv-y.nv-axis')
- .call(yAxis);
- }
-
- //============================================================
- // 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();
- });
-
- 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':
- multibar.stacked(false);
- break;
- case 'Stacked':
- multibar.stacked(true);
- break;
- }
-
- state.stacked = multibar.stacked();
- dispatch.stateChange(state);
-
- chart.update();
- });
-
- dispatch.on('tooltipShow', function(e) {
- if (tooltips) showTooltip(e, that.parentNode)
- });
-
- // 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();
- });
- });
-
- renderWatch.renderEnd('multibarchart immediate');
- return chart;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- multibar.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- multibar.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
-
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.multibar = multibar;
- chart.legend = legend;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.state = state;
-
- 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=_;}},
- showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
- 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=_;}},
-
- // 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');
- }}
- });
-
- nv.utils.inheritOptions(chart, multibar);
- nv.utils.initOptions(chart);
-
- return chart;
-};
-
-nv.models.multiBarHorizontal = function() {
- "use strict";
-
- //============================================================
- // 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
- , 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
- , valueFormat = d3.format(',.2f')
- , delay = 1200
- , xDomain
- , yDomain
- , xRange
- , yRange
- , duration = 250
- , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout','renderEnd')
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var x0, y0; //used to store previous scales
- 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,
- container = d3.select(this);
- nv.utils.initSVG(container);
-
- if (stacked)
- data = d3.layout.stack()
- .offset('zero')
- .values(function(d){ return d.values })
- .y(getY)
- (data);
-
- //add series index to each data point for reference
- data.forEach(function(series, i) {
- series.values.forEach(function(point) {
- point.series = i;
- });
- });
-
- // 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;
- }
- });
- });
-
- // 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 }
- })
- });
-
- x.domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
- .rangeBands(xRange || [0, availableHeight], .1);
-
- 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)))
-
- 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]);
-
- x0 = x0 || x;
- y0 = y0 || d3.scale.linear().domain(y.domain()).range([y(0),y(0)]);
-
- // 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');
-
- 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);
- 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', .75);
-
- 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))) + ')'
- });
-
- barsEnter.append('rect')
- .attr('width', 0)
- .attr('height', x.rangeBand() / (stacked ? 1 : data.length) )
-
- bars
- .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
- d3.select(this).classed('hover', true);
- dispatch.elementMouseover({
- value: getY(d,i),
- point: d,
- series: data[d.series],
- pos: [ y(getY(d,i) + (stacked ? d.y0 : 0)), x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length) ],
- pointIndex: i,
- seriesIndex: d.series,
- e: d3.event
- });
- })
- .on('mouseout', function(d,i) {
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- value: getY(d,i),
- point: d,
- series: data[d.series],
- pointIndex: i,
- seriesIndex: d.series,
- e: d3.event
- });
- })
- .on('click', function(d,i) {
- dispatch.elementClick({
- value: getY(d,i),
- point: d,
- series: data[d.series],
- pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
- pointIndex: i,
- seriesIndex: d.series,
- e: d3.event
- });
- d3.event.stopPropagation();
- })
- .on('dblclick', function(d,i) {
- dispatch.elementDblClick({
- value: getY(d,i),
- point: d,
- series: data[d.series],
- pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))], // TODO: Figure out why the value appears to be shifted
- pointIndex: i,
- seriesIndex: d.series,
- e: d3.event
- });
- d3.event.stopPropagation();
- });
-
- 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 + ')'
- });
- }
-
- barsEnter.append('text');
-
- 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')
- .html(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('');
- }
-
- 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('');
- }
-
- 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)) + ')'
- })
- .select('rect')
- .attr('width', function(d,i) {
- return Math.abs(y(getY(d,i) + d.y0) - y(d.y0))
- })
- .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)
- });
-
- //store old scales for use in transitions on update
- x0 = x.copy();
- y0 = y.copy();
-
- });
-
- renderWatch.renderEnd('multibarHorizontal 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
- 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=_;}},
-
- // 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 color;}, set: function(_){
- barColor = nv.utils.getColor(_);
- }}
- });
-
- nv.utils.initOptions(chart);
-
- return chart;
-};
-nv.models.multiBarHorizontalChart = function() {
- "use strict";
-
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
-
- 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)
- ;
-
- var margin = {top: 30, right: 20, bottom: 50, left: 60}
- , width = null
- , height = null
- , color = nv.utils.defaultColor()
- , showControls = true
- , showLegend = true
- , showXAxis = true
- , showYAxis = true
- , stacked = false
- , tooltips = true
- , tooltip = function(key, x, y, e, graph) {
- return '<h3>' + key + ' - ' + x + '</h3>' +
- '<p>' + y + '</p>'
- }
- , x //can be accessed via chart.xScale()
- , y //can be accessed via chart.yScale()
- , state = nv.utils.state()
- , defaultState = null
- , noData = 'No Data Available.'
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState','renderEnd')
- , controlWidth = function() { return showControls ? 180 : 0 }
- , duration = 250
- ;
-
- state.stacked = false; // DEPRECATED Maintained for backward compatibility
-
- multibar
- .stacked(stacked)
- ;
- xAxis
- .orient('left')
- .tickPadding(5)
- .highlightZero(false)
- .showMaxMin(false)
- .tickFormat(function(d) { return d })
- ;
- yAxis
- .orient('bottom')
- .tickFormat(d3.format(',.1f'))
- ;
-
- controls.updateState(false);
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var showTooltip = function(e, offsetElement) {
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- top = e.pos[1] + ( offsetElement.offsetTop || 0),
- x = xAxis.tickFormat()(multibar.x()(e.point, e.pointIndex)),
- y = yAxis.tickFormat()(multibar.y()(e.point, e.pointIndex)),
- content = tooltip(e.series.key, x, y, e, chart);
-
- nv.tooltip.show([left, top], content, e.value < 0 ? 'e' : 'w', null, offsetElement);
- };
-
- var stateGetter = function(data) {
- return function(){
- return {
- active: data.map(function(d) { return !d.disabled }),
- stacked: stacked
- };
- }
- };
-
- 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];
- });
- }
- };
-
- var renderWatch = nv.utils.renderWatch(dispatch, duration);
-
- function chart(selection) {
- renderWatch.reset();
- renderWatch.models(multibar);
- 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,
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
- chart.update = function() { container.transition().duration(duration).call(chart) };
- chart.container = this;
-
- stacked = multibar.stacked();
-
- 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];
- }
- }
-
- // Display No Data message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).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
- .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();
- }
-
- // Setup Scales
- x = multibar.xScale();
- y = multibar.yScale();
-
- // 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');
-
- 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) {
- legend.width(availableWidth - controlWidth());
-
- if (multibar.barColor())
- data.forEach(function(series,i) {
- series.color = d3.rgb('#ccc').darker(i * 1.5).toString();
- });
-
- g.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
-
- if ( margin.top != legend.height()) {
- margin.top = legend.height();
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
- }
-
- g.select('.nv-legendWrap')
- .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
- }
-
- // Controls
- if (showControls) {
- var controlsData = [
- { key: 'Grouped', disabled: multibar.stacked() },
- { key: 'Stacked', disabled: !multibar.stacked() }
- ];
-
- controls.width(controlWidth()).color(['#444', '#444', '#444']);
- g.select('.nv-controlsWrap')
- .datum(controlsData)
- .attr('transform', 'translate(0,' + (-margin.top) +')')
- .call(controls);
- }
-
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- // 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 }));
-
- var barsWrap = g.select('.nv-barsWrap')
- .datum(data.filter(function(d) { return !d.disabled }));
-
- barsWrap.transition().call(multibar);
-
- // Setup Axes
- if (showXAxis) {
- xAxis
- .scale(x)
- .ticks( nv.utils.calcTicksY(availableHeight/24, data) )
- .tickSize(-availableWidth, 0);
-
- g.select('.nv-x.nv-axis').call(xAxis);
-
- var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
-
- xTicks
- .selectAll('line, text');
- }
-
- 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);
- }
-
- // Zero line
- g.select(".nv-zeroLine line")
- .attr("x1", y(0))
- .attr("x2", y(0))
- .attr("y1", 0)
- .attr("y2", -availableHeight)
- ;
-
- //============================================================
- // 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();
- });
-
- 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':
- multibar.stacked(false);
- break;
- case 'Stacked':
- multibar.stacked(true);
- break;
- }
-
- state.stacked = multibar.stacked();
- dispatch.stateChange(state);
- stacked = multibar.stacked();
-
- chart.update();
- });
-
- dispatch.on('tooltipShow', function(e) {
- if (tooltips) showTooltip(e, that.parentNode);
- });
-
- // 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();
- });
- });
- renderWatch.renderEnd('multibar horizontal chart immediate');
- return chart;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- multibar.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- multibar.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
-
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.multibar = multibar;
- chart.legend = legend;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.state = state;
-
- 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=_;}},
- showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
- 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;
- }},
- 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);
- }}
- });
-
- nv.utils.inheritOptions(chart, multibar);
- nv.utils.initOptions(chart);
-
- return chart;
-};
-nv.models.multiChart = function() {
- "use strict";
-
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
-
- var margin = {top: 30, right: 20, bottom: 50, left: 60},
- color = nv.utils.defaultColor(),
- width = null,
- height = null,
- showLegend = true,
- tooltips = true,
- tooltip = function(key, x, y, e, graph) {
- return '<h3>' + key + '</h3>' +
- '<p>' + y + ' at ' + x + '</p>'
- },
- x,
- y,
- noData = 'No Data Available.',
- yDomain1,
- yDomain2,
- getX = function(d) { return d.x },
- getY = function(d) { return d.y},
- interpolate = 'monotone'
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var x = d3.scale.linear(),
- yScale1 = d3.scale.linear(),
- yScale2 = d3.scale.linear(),
-
- lines1 = nv.models.line().yScale(yScale1),
- lines2 = nv.models.line().yScale(yScale2),
-
- bars1 = nv.models.multiBar().stacked(false).yScale(yScale1),
- bars2 = nv.models.multiBar().stacked(false).yScale(yScale2),
-
- stack1 = nv.models.stackedArea().yScale(yScale1),
- stack2 = nv.models.stackedArea().yScale(yScale2),
-
- 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'),
-
- legend = nv.models.legend().height(30),
- dispatch = d3.dispatch('tooltipShow', 'tooltipHide');
-
- var showTooltip = function(e, offsetElement) {
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- top = e.pos[1] + ( offsetElement.offsetTop || 0),
- x = xAxis.tickFormat()(lines1.x()(e.point, e.pointIndex)),
- y = ((e.series.yAxis == 2) ? yAxis2 : yAxis1).tickFormat()(lines1.y()(e.point, e.pointIndex)),
- content = tooltip(e.series.key, x, y, e, chart);
-
- nv.tooltip.show([left, top], content, undefined, undefined, offsetElement.offsetParent);
- };
-
- function chart(selection) {
- selection.each(function(data) {
- var container = d3.select(this),
- that = this;
- nv.utils.initSVG(container);
-
- chart.update = function() { container.transition().call(chart); };
- chart.container = this;
-
- var availableWidth = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right,
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
- 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 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});
-
- // Display noData message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).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
- .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();
- }
-
- var series1 = data.filter(function(d) {return !d.disabled && d.yAxis == 1})
- .map(function(d) {
- return d.values.map(function(d,i) {
- return { x: d.x, y: d.y }
- })
- });
-
- var series2 = data.filter(function(d) {return !d.disabled && d.yAxis == 2})
- .map(function(d) {
- return d.values.map(function(d,i) {
- return { x: d.x, y: d.y }
- })
- });
-
- x .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
- .range([0, availableWidth]);
-
- var wrap = container.selectAll('g.wrap.multiChart').data([data]);
- var gEnter = wrap.enter().append('g').attr('class', 'wrap nvd3 multiChart').append('g');
-
- gEnter.append('g').attr('class', 'x axis');
- gEnter.append('g').attr('class', 'y1 axis');
- gEnter.append('g').attr('class', 'y2 axis');
- gEnter.append('g').attr('class', 'lines1Wrap');
- gEnter.append('g').attr('class', 'lines2Wrap');
- gEnter.append('g').attr('class', 'bars1Wrap');
- gEnter.append('g').attr('class', 'bars2Wrap');
- gEnter.append('g').attr('class', 'stack1Wrap');
- gEnter.append('g').attr('class', 'stack2Wrap');
- gEnter.append('g').attr('class', 'legendWrap');
-
- var g = wrap.select('g');
-
- var color_array = data.map(function(d,i) {
- return data[i].color || color(d, i);
- });
-
- if (showLegend) {
- legend.color(color_array);
- legend.width( availableWidth / 2 );
-
- g.select('.legendWrap')
- .datum(data.map(function(series) {
- series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
- series.key = series.originalKey + (series.yAxis == 1 ? '' : ' (right axis)');
- return series;
- }))
- .call(legend);
-
- if ( margin.top != legend.height()) {
- margin.top = legend.height();
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
- }
-
- g.select('.legendWrap')
- .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')');
- }
-
- 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'}));
- 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)
- .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'area'}));
- stack2
- .width(availableWidth)
- .height(availableHeight)
- .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'area'}));
-
- g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- var lines1Wrap = g.select('.lines1Wrap')
- .datum(
- dataLines1.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 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 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])
-
- yScale2 .domain(yDomain2 || d3.extent(d3.merge(series2).concat(extraValue2), function(d) { return d.y } ))
- .range([0, availableHeight])
-
- lines1.yDomain(yScale1.domain())
- bars1.yDomain(yScale1.domain())
- stack1.yDomain(yScale1.domain())
-
- lines2.yDomain(yScale2.domain())
- bars2.yDomain(yScale2.domain())
- stack2.yDomain(yScale2.domain())
-
- if(dataStack1.length){d3.transition(stack1Wrap).call(stack1);}
- if(dataStack2.length){d3.transition(stack2Wrap).call(stack2);}
-
- if(dataBars1.length){d3.transition(bars1Wrap).call(bars1);}
- if(dataBars2.length){d3.transition(bars2Wrap).call(bars2);}
-
- if(dataLines1.length){d3.transition(lines1Wrap).call(lines1);}
- if(dataLines2.length){d3.transition(lines2Wrap).call(lines2);}
-
- xAxis
- .ticks( nv.utils.calcTicksX(availableWidth/100, data) )
- .tickSize(-availableHeight, 0);
-
- g.select('.x.axis')
- .attr('transform', 'translate(0,' + availableHeight + ')');
- d3.transition(g.select('.x.axis'))
- .call(xAxis);
-
- yAxis1
- .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
-
-
- d3.transition(g.select('.y1.axis'))
- .call(yAxis1);
-
- yAxis2
- .ticks( nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
-
- d3.transition(g.select('.y2.axis'))
- .call(yAxis2);
-
- g.select('.y1.axis')
- .classed('nv-disabled', series1.length ? false : true)
- .attr('transform', 'translate(' + x.range()[0] + ',0)');
-
- g.select('.y2.axis')
- .classed('nv-disabled', series2.length ? false : true)
- .attr('transform', 'translate(' + x.range()[1] + ',0)');
-
- legend.dispatch.on('stateChange', function(newState) {
- chart.update();
- });
-
- dispatch.on('tooltipShow', function(e) {
- if (tooltips) showTooltip(e, that.parentNode);
- });
-
- });
-
- return chart;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- lines1.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- lines1.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- lines2.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- lines2.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- bars1.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- bars1.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- bars2.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- bars2.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- stack1.dispatch.on('tooltipShow', function(e) {
- //disable tooltips when value ~= 0
- //// TODO: consider removing points from voronoi that have 0 value instead of this hack
- if (!Math.round(stack1.y()(e.point) * 100)) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
- setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
- return false;
- }
-
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
- dispatch.tooltipShow(e);
- });
-
- stack1.dispatch.on('tooltipHide', function(e) {
- dispatch.tooltipHide(e);
- });
-
- stack2.dispatch.on('tooltipShow', function(e) {
- //disable tooltips when value ~= 0
- //// TODO: consider removing points from voronoi that have 0 value instead of this hack
- if (!Math.round(stack2.y()(e.point) * 100)) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
- setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
- return false;
- }
-
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
- dispatch.tooltipShow(e);
- });
-
- stack2.dispatch.on('tooltipHide', function(e) {
- dispatch.tooltipHide(e);
- });
-
- lines1.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- lines1.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- lines2.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- lines2.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
- // Global getters and setters
- //------------------------------------------------------------
-
- chart.dispatch = dispatch;
- chart.lines1 = lines1;
- chart.lines2 = lines2;
- chart.bars1 = bars1;
- chart.bars2 = bars2;
- chart.stack1 = stack1;
- chart.stack2 = stack2;
- chart.xAxis = xAxis;
- chart.yAxis1 = yAxis1;
- chart.yAxis2 = yAxis2;
-
- 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=_;}},
- yDomain1: {get: function(){return yDomain1;}, set: function(_){yDomain1=_;}},
- yDomain2: {get: function(){return yDomain2;}, set: function(_){yDomain2=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}},
-
- // 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(_);
- bars1.x(_);
- }},
- y: {get: function(){return getY;}, set: function(_){
- getY = _;
- lines1.y(_);
- bars1.y(_);
- }}
- });
-
- nv.utils.initOptions(chart);
-
- return chart;
-};
-
-
-nv.models.ohlcBar = function() {
- "use strict";
-
- //============================================================
- // 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
- , 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('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd', 'chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- function chart(selection) {
- selection.each(function(data) {
- var container = d3.select(this);
- var availableWidth = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right;
- var availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
- nv.utils.initSVG(container);
-
- // 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]);
-
- 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');
-
- 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 + ')' : '');
-
- var ticks = wrap.select('.nv-ticks').selectAll('.nv-tick')
- .data(function(d) { return d });
- ticks.exit().remove();
-
- var ticksEnter = 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) {
- 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)) - 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)) });
-
- // 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))
- - y(getHigh(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';
- });
- });
-
- return chart;
- }
-
-
- //Create methods to allow outside functions to highlight a specific bar.
- chart.highlightPoint = function(pointIndex, isHoverOver) {
- chart.clearHighlights();
- d3.select(".nv-ohlcBar .nv-tick-0-" + pointIndex)
- .classed("hover", isHoverOver)
- ;
- };
-
- chart.clearHighlights = function() {
- d3.select(".nv-ohlcBar .nv-tick.hover")
- .classed("hover", false)
- ;
- };
-
- //============================================================
- // 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
- 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=_;}},
-
- // 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;
-};
-
-// Code adapted from Jason Davies' "Parallel Coordinates"
-// http://bl.ocks.org/jasondavies/1341281
-
-nv.models.parallelCoordinates = function() {
- "use strict";
-
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
-
- var margin = {top: 30, right: 10, bottom: 10, left: 10}
- , width = null
- , height = null
- , x = d3.scale.ordinal()
- , y = {}
- , dimensions = []
- , color = nv.utils.defaultColor()
- , filters = []
- , active = []
- , dispatch = d3.dispatch('brush')
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- function chart(selection) {
- selection.each(function(data) {
- var container = d3.select(this);
- var availableWidth = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right;
- var availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
- nv.utils.initSVG(container);
-
- active = data; //set all active before first brush call
-
- //This is a placeholder until this chart is made resizeable
- chart.update = function() { };
-
- // Setup Scales
- x.rangePoints([0, availableWidth], 1).domain(dimensions);
-
- // Extract the list of dimensions and create a scale for each.
- dimensions.forEach(function(d) {
- y[d] = d3.scale.linear()
- .domain(d3.extent(data, function(p) { return +p[d]; }))
- .range([availableHeight, 0]);
-
- y[d].brush = d3.svg.brush().y(y[d]).on('brush', brush);
-
- return d != 'name';
- });
-
- // 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-parallelCoordinatesWrap');
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- var line = d3.svg.line(),
- axis = d3.svg.axis().orient('left'),
- background,
- foreground;
-
- // Add grey background lines for context.
- background = gEnter.append('g')
- .attr('class', 'background')
- .selectAll('path')
- .data(data)
- .enter().append('path')
- .attr('d', path)
- ;
-
- // Add blue foreground lines for focus.
- foreground = gEnter.append('g')
- .attr('class', 'foreground')
- .selectAll('path')
- .data(data)
- .enter().append('path')
- .attr('d', path)
- .attr('stroke', color)
- ;
-
- // Add a group element for each dimension.
- var dimension = g.selectAll('.dimension')
- .data(dimensions)
- .enter().append('g')
- .attr('class', 'dimension')
- .attr('transform', function(d) { return 'translate(' + x(d) + ',0)'; });
-
- // Add an axis and title.
- dimension.append('g')
- .attr('class', 'axis')
- .each(function(d) { d3.select(this).call(axis.scale(y[d])); })
- .append('text')
- .attr('text-anchor', 'middle')
- .attr('y', -9)
- .text(String);
-
- // Add and store a brush for each axis.
- dimension.append('g')
- .attr('class', 'brush')
- .each(function(d) { d3.select(this).call(y[d].brush); })
- .selectAll('rect')
- .attr('x', -8)
- .attr('width', 16);
-
- // Returns the path for a given data point.
- function path(d) {
- return line(dimensions.map(function(p) { return [x(p), y[p](d[p])]; }));
- }
-
- // Handles a brush event, toggling the display of foreground lines.
- function brush() {
- var actives = dimensions.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]
- }
- });
-
- active = []; //erase current active list
- foreground.style('display', function(d) {
- var isActive = actives.every(function(p, i) {
- return extents[i][0] <= d[p] && d[p] <= extents[i][1];
- });
- if (isActive) active.push(d);
- return isActive ? null : 'none';
- });
-
- dispatch.brush({
- filters: filters,
- active: active
- });
- }
- });
-
- 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
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- dimensions: {get: function(){return dimensions;}, set: function(_){dimensions=_;}},
-
- // 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;
- }},
- color: {get: function(){return color;}, set: function(_){
- color = nv.utils.getColor(_);
- }}
- });
-
- nv.utils.initOptions(chart);
- return chart;
-};
-nv.models.pie = function() {
- "use strict";
-
- //============================================================
- // 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
- , color = nv.utils.defaultColor()
- , valueFormat = d3.format(',.2f')
- , labelFormat = d3.format('%')
- , showLabels = true
- , pieLabelsOutside = true
- , donutLabelsOutside = 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
- , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
- ;
-
-
- //============================================================
- // chart function
- //------------------------------------------------------------
-
- 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
- ,arcRadius = radius-(radius / 5)
- ,container = d3.select(this)
- ;
- 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');
-
- 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
- });
- });
-
-
- var arc = d3.svg.arc().outerRadius(arcRadius);
- var arcOver = d3.svg.arc().outerRadius(arcRadius + 5);
-
- if (startAngle) {
- arc.startAngle(startAngle);
- arcOver.startAngle(startAngle);
- }
- if (endAngle) {
- arc.endAngle(endAngle);
- arcOver.endAngle(endAngle);
- }
- if (donut) {
- arc.innerRadius(radius * donutRatio);
- arcOver.innerRadius(radius * donutRatio);
- }
-
- // 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);
- }
-
- if (arc.cornerRadius && cornerRadius) {
- arc.cornerRadius(cornerRadius);
- arcOver.cornerRadius(cornerRadius);
- }
-
- // if title is specified and donut, put it in the middle
- if (donut && title) {
- var title_g = g_pie.append('g').attr('class', 'nv-pie');
-
- title_g.append("text")
- .style("text-anchor", "middle")
- .attr('class', 'nv-pie-title')
- .text(function (d) {
- return title;
- })
- .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);
-
- 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", arcOver);
- }
- dispatch.elementMouseover({
- label: getX(d.data),
- value: getY(d.data),
- point: d.data,
- pointIndex: i,
- pos: [d3.event.pageX, d3.event.pageY],
- id: id,
- color: d3.select(this).style("fill")
- });
- });
- ae.on('mouseout', function(d,i){
- d3.select(this).classed('hover', false);
- if (growOnHover) {
- d3.select(this).select("path").transition()
- .duration(50)
- .attr("d", arc);
- }
- dispatch.elementMouseout({
- label: getX(d.data),
- value: getY(d.data),
- point: d.data,
- index: i,
- id: id
- });
- });
- ae.on('click', function(d,i) {
- dispatch.elementClick({
- label: getX(d.data),
- value: getY(d.data),
- point: d.data,
- index: i,
- pos: d3.event,
- id: id
- });
- d3.event.stopPropagation();
- });
- ae.on('dblclick', function(d,i) {
- dispatch.elementDblClick({
- label: getX(d.data),
- value: getY(d.data),
- point: d.data,
- index: i,
- pos: d3.event,
- id: id
- });
- d3.event.stopPropagation();
- });
-
- slices.attr('fill', function(d,i) { return color(d, i); })
- slices.attr('stroke', function(d,i) { return color(d, i); });
-
- var paths = ae.append('path').each(function(d) {
- this._current = d;
- });
-
- slices.select('path')
- .transition()
- .attr('d', arc)
- .attrTween('d', arcTween);
-
- if (showLabels) {
- // This does the normal label
- var labelsArc = d3.svg.arc().innerRadius(0);
-
- if (pieLabelsOutside){
- var labelsArc = arc;
- }
-
- if (donutLabelsOutside) {
- labelsArc = d3.svg.arc().outerRadius(arc.outerRadius());
- }
-
- pieLabels.enter().append("g").classed("nv-label",true).each(function(d,i) {
- var group = d3.select(this);
-
- group.attr('transform', function(d) {
- if (labelSunbeamLayout) {
- d.outerRadius = arcRadius + 10; // Set Outer Coordinate
- d.innerRadius = arcRadius + 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.centroid(d) + ') rotate(' + rotateAngle + ')';
- } else {
- d.outerRadius = radius + 10; // Set Outer Coordinate
- d.innerRadius = radius + 15; // Set Inner Coordinate
- return 'translate(' + labelsArc.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;
- };
-
- pieLabels.watchTransition(renderWatch,'pie labels').attr('transform', function(d) {
- if (labelSunbeamLayout) {
- d.outerRadius = arcRadius + 10; // Set Outer Coordinate
- d.innerRadius = arcRadius + 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.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.centroid(d);
- if(d.value){
- var hashKey = createHashKey(center);
- if (labelLocationHash[hashKey]) {
- center[1] -= avgHeight;
- }
- labelLocationHash[createHashKey(center)] = true;
- }
- return 'translate(' + center + ')'
- }
- });
-
- pieLabels.select(".nv-label 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
- .text(function(d, i) {
- var percent = (d.endAngle - d.startAngle) / (2 * Math.PI);
- var labelTypes = {
- "key" : getX(d.data),
- "value": getY(d.data),
- "percent": labelFormat(percent)
- };
- return (d.value && percent > labelThreshold) ? labelTypes[labelType] : '';
- })
- ;
- }
-
-
- // 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) {
- 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 arc(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
- 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=_;}},
- labelFormat: {get: function(){return labelFormat;}, set: function(_){labelFormat=_;}},
- 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=_;}},
- pieLabelsOutside: {get: function(){return pieLabelsOutside;}, set: function(_){pieLabelsOutside=_;}},
- donutLabelsOutside: {get: function(){return donutLabelsOutside;}, set: function(_){donutLabelsOutside=_;}},
- labelSunbeamLayout: {get: function(){return labelSunbeamLayout;}, set: function(_){labelSunbeamLayout=_;}},
- donut: {get: function(){return donut;}, set: function(_){donut=_;}},
- growOnHover: {get: function(){return growOnHover;}, set: function(_){growOnHover=_;}},
-
- // 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;
- }},
- 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 margin = {top: 30, right: 20, bottom: 20, left: 20}
- , width = null
- , height = null
- , showLegend = true
- , color = nv.utils.defaultColor()
- , tooltips = true
- , tooltip = function(key, y, e, graph) {
- return '<h3 style="background-color: '
- + e.color + '">' + key + '</h3>'
- + '<p>' + y + '</p>';
- }
- , state = nv.utils.state()
- , defaultState = null
- , noData = "No Data Available."
- , duration = 250
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState','renderEnd')
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var showTooltip = function(e, offsetElement) {
- var tooltipLabel = pie.x()(e.point);
- var left = e.pos[0] + ( (offsetElement && offsetElement.offsetLeft) || 0 ),
- top = e.pos[1] + ( (offsetElement && offsetElement.offsetTop) || 0),
- y = pie.valueFormat()(pie.y()(e.point)),
- content = tooltip(tooltipLabel, y, e, chart)
- ;
- nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
- };
-
- var renderWatch = nv.utils.renderWatch(dispatch);
-
- 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];
- });
- }
- }
- };
-
- //============================================================
- // 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 = (width || parseInt(container.style('width'), 10) || 960)
- - margin.left - margin.right,
- availableHeight = (height || parseInt(container.style('height'), 10) || 400)
- - margin.top - margin.bottom
- ;
-
- 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) {
- 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
- .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();
- }
-
- // 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) {
- legend.width( availableWidth ).key(pie.x());
-
- wrap.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
-
- if ( margin.top != legend.height()) {
- margin.top = legend.height();
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
- }
-
- wrap.select('.nv-legendWrap')
- .attr('transform', 'translate(0,' + (-margin.top) +')');
- }
- 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();
- });
-
- pie.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- // 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();
- });
-
- });
-
- renderWatch.renderEnd('pieChart immediate');
- return chart;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- pie.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- dispatch.on('tooltipShow', function(e) {
- if (tooltips) showTooltip(e);
- });
-
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
-
- // expose chart's sub-components
- chart.legend = legend;
- chart.dispatch = dispatch;
- chart.pie = pie;
- 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=_;}},
- tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- 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);
- }}
- });
- nv.utils.inheritOptions(chart, pie);
- nv.utils.initOptions(chart);
- return chart;
-};
-
-nv.models.scatter = function() {
- "use strict";
-
- //============================================================
- // 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
- , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't select one
- , 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
- , 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
- ;
-
-
- //============================================================
- // 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)
- ;
-
- function chart(selection) {
- renderWatch.reset();
- selection.each(function(data) {
- var container = d3.select(this);
- var availableWidth = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right;
- var availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
- 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;
- });
- });
-
- // Setup Scales
- // 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)))
-
- 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]);
-
- 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 || [16, 256]);
-
- // 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] || y.domain()[0] === y.domain()[1]) singlePoint = true;
- 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 ( isNaN(x.domain()[0])) {
- x.domain([-1,1]);
- }
-
- if ( isNaN(y.domain()[0])) {
- y.domain([-1,1]);
- }
-
- x0 = x0 || x;
- y0 = y0 || y;
- z0 = z0 || z;
-
- // 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 + (singlePoint ? ' nv-single-point' : ''));
- 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-point-paths');
-
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- defsEnter.append('clipPath')
- .attr('id', 'nv-edge-clip-' + id)
- .append('rect');
-
- wrap.select('#nv-edge-clip-' + id + ' rect')
- .attr('width', availableWidth)
- .attr('height', (availableHeight > 0) ? availableHeight : 0);
-
- g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
-
- function updateInteractiveLayer() {
-
- if (!interactive) return false;
-
- var eventElements;
-
- 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);
-
- return [x(pX)+ Math.random() * 1e-7,
- y(pY)+ Math.random() * 1e-7,
- groupIndex,
- pointIndex, point]; //temp hack to add noise untill 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!
- })
- })
- );
-
- //inject series and point index for reference into voronoi
- if (useVoronoi === true) {
-
- 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]);
- }
-
- // 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]
- ]);
-
- var voronoi = d3.geom.voronoi(vertices).map(function(d, i) {
- return {
- 'data': bounds.clip(d),
- 'series': vertices[i][2],
- 'point': vertices[i][3]
- }
- });
-
- // 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);
- 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-"+i+")"; })
- ;
- // chain these to above to see the voronoi elements (good for debugging)
- //.style("fill", d3.rgb(230, 230, 230))
- //.style('fill-opacity', 0.4)
- //.style('stroke-opacity', 1)
- //.style("stroke", d3.rgb(200,200,200));
-
- if (clipVoronoi) {
- // voronoi sections are already set to clip,
- // just create the circles with the IDs they expect
- var clips = wrap.append("svg:g").attr("id", "nv-point-clips");
- clips.selectAll("clipPath")
- .data(vertices)
- .enter().append("svg:clipPath")
- .attr("id", function(d, i) { return "nv-clip-"+i;})
- .append("svg:circle")
- .attr('cx', function(d) { return d[0]; })
- .attr('cy', function(d) { return d[1]; })
- .attr('r', clipRadius);
- }
-
- var mouseEventCallback = function(d,mDispatch) {
- if (needsUpdate) return 0;
- var series = data[d.series];
- if (typeof series === 'undefined') return;
- var point = series.values[d.point];
-
- mDispatch({
- point: point,
- series: series,
- pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
- seriesIndex: d.series,
- pointIndex: d.point
- });
- };
-
- 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);
- });
-
- } else {
- /*
- // bring data in form needed for click handlers
- var dataWithPoints = vertices.map(function(d, i) {
- return {
- 'data': d,
- 'series': vertices[i][2],
- 'point': vertices[i][3]
- }
- });
- */
-
- // 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];
-
- dispatch.elementClick({
- point: point,
- series: series,
- pos: [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],
- seriesIndex: d.series,
- pointIndex: 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,
- seriesIndex: d.series,
- pointIndex: i
- });
- });
- }
-
- needsUpdate = false;
- }
-
- 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 'nv-group nv-series-' + i })
- .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);
-
- // create the points
- var points = groups.selectAll('path.nv-point')
- .data(function(d) { return d.values });
- points.enter().append('path')
- .style('fill', function (d,i) { return d.color })
- .style('stroke', function (d,i) { return d.color })
- .attr('transform', function(d,i) {
- return 'translate(' + x0(getX(d,i)) + ',' + y0(getY(d,i)) + ')'
- })
- .attr('d',
- nv.utils.symbol()
- .type(getShape)
- .size(function(d,i) { return z(getSize(d,i)) })
- );
- points.exit().remove();
- groups.exit().selectAll('path.nv-point')
- .watchTransition(renderWatch, 'scatter exit')
- .attr('transform', function(d,i) {
- return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
- })
- .remove();
- points.each(function(d,i) {
- d3.select(this)
- .classed('nv-point', true)
- .classed('nv-point-' + i, true)
- .classed('hover',false)
- ;
- });
- points
- .watchTransition(renderWatch, 'scatter points')
- .attr('transform', function(d,i) {
- //nv.log(d,i,getX(d,i), x(getX(d,i)));
- return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
- })
- .attr('d',
- nv.utils.symbol()
- .type(getShape)
- .size(function(d,i) { return z(getSize(d,i)) })
- );
-
- // Delay updating the invisible interactive layer for smoother animation
- clearTimeout(timeoutID); // stop repeat calls to updateInteractiveLayer
- timeoutID = setTimeout(updateInteractiveLayer, 300);
- //updateInteractiveLayer();
-
- //store old scales for use in transitions on update
- x0 = x.copy();
- y0 = y.copy();
- z0 = z.copy();
-
- });
- 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 () {
- d3.selectAll(".nv-chart-" + id + " .nv-point.hover").classed("hover", false);
- return null;
- };
- this.highlightPoint = function (seriesIndex, pointIndex, isHoverOver) {
- d3.select(".nv-chart-" + id + " .nv-series-" + seriesIndex + " .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);
- });
-
- 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=_;}},
- id: {get: function(){return id;}, set: function(_){id=_;}},
-
-
- // 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;
- }
- }}
- });
-
- nv.utils.initOptions(chart);
- return chart;
-};
-
-nv.models.scatterChart = function() {
- "use strict";
-
- //============================================================
- // 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()
- ;
-
- var margin = {top: 30, right: 20, bottom: 50, left: 75}
- , width = null
- , height = null
- , color = nv.utils.defaultColor()
- , x = scatter.xScale()
- , y = scatter.yScale()
- , showDistX = false
- , showDistY = false
- , showLegend = true
- , showXAxis = true
- , showYAxis = true
- , rightAlignYAxis = false
- , tooltips = true
- , tooltipX = function(key, x, y) { return '<strong>' + x + '</strong>' }
- , tooltipY = function(key, x, y) { return '<strong>' + y + '</strong>' }
- , tooltip = function(key, x, y, date) { return '<h3>' + key + '</h3>'
- + '<p>' + date + '</p>' }
- , state = nv.utils.state()
- , defaultState = null
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
- , noData = "No Data Available."
- , duration = 250
- ;
-
- scatter
- .xScale(x)
- .yScale(y)
- ;
- xAxis
- .orient('bottom')
- .tickPadding(10)
- ;
- yAxis
- .orient((rightAlignYAxis) ? 'right' : 'left')
- .tickPadding(10)
- ;
- distX
- .axis('x')
- ;
- distY
- .axis('y')
- ;
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var x0, y0
- , renderWatch = nv.utils.renderWatch(dispatch, duration);
-
- var showTooltip = function(e, offsetElement) {
- //TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- top = e.pos[1] + ( offsetElement.offsetTop || 0),
- leftX = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- topX = y.range()[0] + margin.top + ( offsetElement.offsetTop || 0),
- leftY = x.range()[0] + margin.left + ( offsetElement.offsetLeft || 0 ),
- topY = e.pos[1] + ( offsetElement.offsetTop || 0),
- xVal = xAxis.tickFormat()(scatter.x()(e.point, e.pointIndex)),
- yVal = yAxis.tickFormat()(scatter.y()(e.point, e.pointIndex));
-
- if( tooltipX != null )
- nv.tooltip.show([leftX, topX], tooltipX(e.series.key, xVal, yVal, e, chart), 'n', 1, offsetElement, 'x-nvtooltip');
- if( tooltipY != null )
- nv.tooltip.show([leftY, topY], tooltipY(e.series.key, xVal, yVal, e, chart), 'e', 1, offsetElement, 'y-nvtooltip');
- if( tooltip != null )
- nv.tooltip.show([left, top], tooltip(e.series.key, xVal, yVal, e.point.tooltip, e, chart), e.value < 0 ? 'n' : 's', null, offsetElement);
- };
-
- 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];
- });
- }
- };
-
- 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 container = d3.select(this),
- that = this;
- nv.utils.initSVG(container);
-
- var availableWidth = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right,
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
- 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];
- }
- }
-
- // Display noData message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).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
- .attr('x', margin.left + availableWidth / 2)
- .attr('y', margin.top + availableHeight / 2)
- .text(function(d) { return d });
-
- renderWatch.renderEnd('scatter immediate');
-
- return chart;
- } else {
- container.selectAll('.nv-noData').remove();
- }
-
- // 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');
-
- // 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');
-
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
- }
-
- // Legend
- if (showLegend) {
- legend.width( availableWidth / 2 );
-
- wrap.select('.nv-legendWrap')
- .datum(data)
- .call(legend);
-
- if ( margin.top != legend.height()) {
- margin.top = legend.height();
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
- }
-
- wrap.select('.nv-legendWrap')
- .attr('transform', 'translate(' + (availableWidth / 2) + ',' + (-margin.top) +')');
- }
-
- // Main Chart Component(s)
- scatter
- .width(availableWidth)
- .height(availableHeight)
- .color(data.map(function(d,i) {
- return d.color || color(d, i);
- }).filter(function(d,i) { return !data[i].disabled }));
-
- 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() + ')');
-
- var regWrap = wrap.select('.nv-regressionLinesWrap').selectAll('.nv-regLines')
- .data(function (d) {
- return d;
- });
-
- regWrap.enter().append('g').attr('class', 'nv-regLines');
-
- var regLine = regWrap.selectAll('.nv-regLine')
- .data(function (d) {
- return [d]
- });
-
- 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)
- })
- .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( xAxis.ticks() ? xAxis.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);
- }
-
- if (showYAxis) {
- yAxis
- .scale(y)
- .ticks( yAxis.ticks() ? yAxis.ticks() : nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize( -availableWidth, 0);
-
- g.select('.nv-y.nv-axis')
- .call(yAxis);
- }
-
-
- 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);
- }
-
- //============================================================
- // 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();
- });
-
-
- scatter.dispatch.on('elementMouseover.tooltip', function(e) {
- d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
- .attr('y1', e.pos[1] - availableHeight);
- d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
- .attr('x2', e.pos[0] + distX.size());
-
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- dispatch.on('tooltipShow', function(e) {
- if (tooltips) showTooltip(e, that.parentNode);
- });
-
- // 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();
- });
-
- //store old scales for use in transitions on update
- x0 = x.copy();
- y0 = y.copy();
-
- });
-
- renderWatch.renderEnd('scatter with line immediate');
- return chart;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- scatter.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
-
- d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
- .attr('y1', 0);
- d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
- .attr('x2', distY.size());
- });
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
- // 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._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=_;}},
- 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=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
- tooltipXContent: {get: function(){return tooltipX;}, set: function(_){tooltipX=_;}},
- tooltipYContent: {get: function(){return tooltipY;}, set: function(_){tooltipY=_;}},
- defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
- noData: {get: function(){return noData;}, set: function(_){noData=_;}},
- 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;
- }},
- 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.models.sparkline = function() {
- "use strict";
-
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
-
- var margin = {top: 2, right: 0, bottom: 2, left: 0}
- , width = 400
- , height = 32
- , 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
- ;
-
- function chart(selection) {
- 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);
-
- // Setup Scales
- x .domain(xDomain || d3.extent(data, getX ))
- .range(xRange || [0, availableWidth]);
-
- 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');
-
- 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)) })
- );
-
- // 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 [minPoint, maxPoint, currentPoint].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'
- });
- });
-
- return chart;
- }
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
-
- 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=_;}},
-
- //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(_);
- }}
- });
-
- nv.utils.initOptions(chart);
- return chart;
-};
-
-nv.models.sparklinePlus = function() {
- "use strict";
-
- //============================================================
- // Public Variables with Default Settings
- //------------------------------------------------------------
-
- 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')
- , showValue = true
- , alignValue = true
- , rightAlignValue = false
- , noData = "No Data Available."
- ;
-
- function chart(selection) {
- selection.each(function(data) {
- var container = d3.select(this);
- nv.utils.initSVG(container);
-
- var availableWidth = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right,
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
- chart.update = function() { chart(selection) };
- chart.container = this;
-
- // Display No Data message if there's nothing to show.
- 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
- .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();
- }
-
- var currentValue = sparkline.y()(data[data.length-1], data.length-1);
-
- // 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');
-
- 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 + ')');
-
- // Main Chart Component(s)
- var sparklineWrap = g.select('.nv-sparklineWrap');
-
- sparkline.width(availableWidth).height(availableHeight);
- sparklineWrap.call(sparkline);
-
- 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
- .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(); });
-
- 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;
-
- 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);
-
- 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);
-
- if (!index.length) return;
-
- 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')
-
- 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')
-
- g.select('.nv-hoverValue .nv-yValue')
- .text(yTickFormat(sparkline.y()(data[index[0]], index[0])));
- }
-
- function sparklineHover() {
- if (paused) return;
-
- 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;
- }
- }
- return closestIndex;
- }
-
- index = [getClosestIndex(data, Math.round(x.invert(pos)))];
- updateValueLine();
- }
-
- });
-
- return chart;
- }
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
-
- // expose chart's sub-components
- chart.sparkline = sparkline;
-
- 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=_;}},
- showValue: {get: function(){return showValue;}, set: function(_){showValue=_;}},
- 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;
- }}
- });
-
- nv.utils.inheritOptions(chart, sparkline);
- nv.utils.initOptions(chart);
-
- return chart;
-};
-
-nv.models.stackedArea = function() {
- "use strict";
-
- //============================================================
- // 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
- , 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
- , 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('tooltipShow', 'tooltipHide', 'areaClick', 'areaMouseover', 'areaMouseout','renderEnd')
- ;
-
- // scatter is interactive by default, but this chart isn't so must disable
- scatter.interactive(false);
-
- 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)
- ************************************/
-
- 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,
- container = d3.select(this);
- nv.utils.initSVG(container);
-
- // 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 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) {
- var yHeight = (getY(d) === 0) ? 0 : y;
- d.display = {
- y: yHeight,
- 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');
-
- gEnter.append('g').attr('class', 'nv-areaWrap');
- gEnter.append('g').attr('class', 'nv-scatterWrap');
-
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- scatter
- .width(availableWidth)
- .height(availableHeight)
- .x(getX)
- .y(function(d) { return d.display.y + d.display.y0 })
- .forceY([0])
- .color(data.map(function(d,i) {
- return d.color || color(d, d.seriesIndex);
- }));
-
- var scatterWrap = g.select('.nv-scatterWrap')
- .datum(data);
-
- scatterWrap.call(scatter);
-
- defsEnter.append('clipPath')
- .attr('id', 'nv-edge-clip-' + id)
- .append('rect');
-
- wrap.select('#nv-edge-clip-' + id + ' rect')
- .attr('width', availableWidth)
- .attr('height', availableHeight);
-
- g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
-
- var area = d3.svg.area()
- .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()
- .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 });
-
- 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
- });
- });
-
- 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)
- });
-
- //============================================================
- // 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);
- });
-
- //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
- k = 1 / n,
- 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 series'
- o += getY(dataRaw[i].values[j]); //total value of all points at a certian point in time.
- }
-
- if (o) for (i = 0; i < n; i++) {
- stackData[i][j][1] /= o;
- } else {
- for (i = 0; i < n; i++) {
- stackData[i][j][1] = k;
- }
- }
- }
- for (j = 0; j < m; ++j) y0[j] = 0;
- return y0;
- };
-
- });
-
- renderWatch.renderEnd('stackedArea immediate');
- return chart;
- }
-
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- scatter.dispatch.on('elementClick.area', function(e) {
- dispatch.areaClick(e);
- });
- scatter.dispatch.on('elementMouseover.tooltip', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
- dispatch.tooltipShow(e);
- });
- scatter.dispatch.on('elementMouseout.tooltip', function(e) {
- dispatch.tooltipHide(e);
- });
-
- //============================================================
- // Global getters and setters
- //------------------------------------------------------------
-
- chart.dispatch = dispatch;
- chart.scatter = scatter;
-
- 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.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=_;}},
- 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(_);}},
-
- // 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);
-
- return chart;
-};
-
-nv.models.stackedAreaChart = function() {
- "use strict";
-
- //============================================================
- // 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()
- ;
-
- 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
- , useInteractiveGuideline = false
- , tooltips = true
- , tooltip = function(key, x, y, e, graph) {
- return '<h3>' + key + '</h3>' +
- '<p>' + y + ' on ' + x + '</p>'
- }
- , x //can be accessed via chart.xScale()
- , y //can be accessed via chart.yScale()
- , yAxisTickFormat = d3.format(',.2f')
- , state = nv.utils.state()
- , defaultState = null
- , noData = 'No Data Available.'
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState','renderEnd')
- , controlWidth = 250
- , cData = ['Stacked','Stream','Expanded']
- , controlLabels = {}
- , duration = 250
- ;
-
- state.style = stacked.style();
- xAxis.orient('bottom').tickPadding(7);
- yAxis.orient((rightAlignYAxis) ? 'right' : 'left');
-
- controls.updateState(false);
-
- //============================================================
- // Private Variables
- //------------------------------------------------------------
-
- var renderWatch = nv.utils.renderWatch(dispatch);
- var style = stacked.style();
-
- var showTooltip = function(e, offsetElement) {
- var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
- top = e.pos[1] + ( offsetElement.offsetTop || 0),
- x = xAxis.tickFormat()(stacked.x()(e.point, e.pointIndex)),
- y = yAxis.tickFormat()(stacked.y()(e.point, e.pointIndex)),
- content = tooltip(e.series.key, x, y, e, chart);
-
- nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
- };
-
- 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];
- });
- }
- };
-
- 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);
-
- var availableWidth = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right,
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
-
- chart.update = function() { container.transition().duration(duration).call(chart); };
- chart.container = this;
-
- state
- .setter(stateSetter(data), chart.update)
- .getter(stateGetter(data))
- .update();
-
- // 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];
- }
- }
-
- // Display No Data message if there's nothing to show.
- if (!data || !data.length || !data.filter(function(d) { return d.values.length }).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
- .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();
- }
-
- // 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');
-
- gEnter.append("rect").style("opacity",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-stackedWrap');
- gEnter.append('g').attr('class', 'nv-legendWrap');
- gEnter.append('g').attr('class', 'nv-controlsWrap');
- gEnter.append('g').attr('class', 'nv-interactive');
-
- g.select("rect").attr("width",availableWidth).attr("height",availableHeight);
-
- // Legend
- if (showLegend) {
- var legendWidth = (showControls) ? availableWidth - controlWidth : availableWidth;
-
- legend.width(legendWidth);
- g.select('.nv-legendWrap').datum(data).call(legend);
-
- if ( margin.top != legend.height()) {
- margin.top = legend.height();
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
- }
-
- g.select('.nv-legendWrap')
- .attr('transform', 'translate(' + (availableWidth-legendWidth) + ',' + (-margin.top) +')');
- }
-
- // Controls
- if (showControls) {
- 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 = (cData.length/3) * 260;
- controlsData = controlsData.filter(function(d) {
- return cData.indexOf(d.metaKey) !== -1;
- });
-
- controls
- .width( controlWidth )
- .color(['#444', '#444', '#444']);
-
- g.select('.nv-controlsWrap')
- .datum(controlsData)
- .call(controls);
-
- if ( margin.top != Math.max(controls.height(), legend.height()) ) {
- margin.top = Math.max(controls.height(), legend.height());
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
- }
-
- g.select('.nv-controlsWrap')
- .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)");
- }
-
- //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);
- }
-
- stacked
- .width(availableWidth)
- .height(availableHeight);
-
- var stackedWrap = g.select('.nv-stackedWrap')
- .datum(data);
-
- stackedWrap.transition().call(stacked);
-
- // 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,' + availableHeight + ')');
-
- g.select('.nv-x.nv-axis')
- .transition().duration(0)
- .call(xAxis);
- }
-
- if (showYAxis) {
- yAxis.scale(y)
- .ticks(stacked.offset() == 'wiggle' ? 0 : nv.utils.calcTicksY(availableHeight/36, data) )
- .tickSize(-availableWidth, 0)
- .setTickFormat( (stacked.style() == 'expand' || stacked.style() == 'stack_percent')
- ? d3.format('%') : yAxisTickFormat);
-
- g.select('.nv-y.nv-axis')
- .transition().duration(0)
- .call(yAxis);
- }
-
- //============================================================
- // 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);
- });
-
- state.disabled = data.map(function(d) { return !!d.disabled });
- 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;
-
- 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();
- });
-
- interactiveLayer.dispatch.on('elementMousemove', function(e) {
- stacked.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());
- stacked.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));
-
- //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),
- stackedValue: point.display
- });
- });
-
- allData.reverse();
-
- //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) {
-
- //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.stackedValue.y0);
- var stackedY = Math.abs(series.stackedValue.y);
- if ( yValue >= stackedY0 && yValue <= (stackedY + stackedY0))
- {
- indexToHighlight = i;
- return;
- }
- });
- if (indexToHighlight != null)
- allData[indexToHighlight].highlight = true;
- }
-
- var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
-
- //If we are in 'expand' mode, force the format to be a percentage.
- var valueFormatter = (stacked.style() == 'expand') ?
- function(d,i) {return d3.format(".1%")(d);} :
- function(d,i) {return yAxis.tickFormat()(d); };
- interactiveLayer.tooltip
- .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
- .chartContainer(that.parentNode)
- .enabled(tooltips)
- .valueFormatter(valueFormatter)
- .data(
- {
- value: xValue,
- series: allData
- }
- )();
-
- interactiveLayer.renderGuideLine(pointXLocation);
-
- });
-
- interactiveLayer.dispatch.on("elementMouseout",function(e) {
- dispatch.tooltipHide();
- stacked.clearHighlights();
- });
-
-
- dispatch.on('tooltipShow', function(e) {
- if (tooltips) showTooltip(e, that.parentNode);
- });
-
- // 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;
- }
-
- if (typeof e.style !== 'undefined') {
- stacked.style(e.style);
- style = e.style;
- }
-
- chart.update();
- });
-
- });
-
- renderWatch.renderEnd('stacked Area chart immediate');
- return chart;
- }
-
- //============================================================
- // Event Handling/Dispatching (out of chart's scope)
- //------------------------------------------------------------
-
- stacked.dispatch.on('tooltipShow', function(e) {
- e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
- dispatch.tooltipShow(e);
- });
-
- stacked.dispatch.on('tooltipHide', function(e) {
- dispatch.tooltipHide(e);
- });
-
- dispatch.on('tooltipHide', function() {
- if (tooltips) nv.tooltip.cleanup();
- });
-
- //============================================================
- // Expose Public Variables
- //------------------------------------------------------------
-
- // expose chart's sub-components
- chart.dispatch = dispatch;
- chart.stacked = stacked;
- chart.legend = legend;
- chart.controls = controls;
- chart.xAxis = xAxis;
- chart.yAxis = yAxis;
- chart.interactiveLayer = interactiveLayer;
-
- yAxis.setTickFormat = yAxis.tickFormat;
-
- 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=_;}},
- showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
- showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
- showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
- tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}},
- tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}},
- 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=_;}},
- yAxisTickFormat: {get: function(){return yAxisTickFormat;}, set: function(_){yAxisTickFormat=_;}},
-
- // 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);
- 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);
- }},
- rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
- rightAlignYAxis = _;
- yAxis.orient( rightAlignYAxis ? 'right' : 'left');
- }},
- useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){
- useInteractiveGuideline = !!_;
- if (_) {
- chart.interactive(false);
- chart.useVoronoi(false);
- }
- }}
- });
-
- nv.utils.inheritOptions(chart, stacked);
- nv.utils.initOptions(chart);
-
- return chart;
-};
-
-nv.version = "1.7.0";
-})();
\ No newline at end of file