From: Ant Zucaro Date: Thu, 4 Dec 2014 03:00:42 +0000 (-0500) Subject: Use latest and greatest NVD3. X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fxonstat.git;a=commitdiff_plain;h=f1590f48b0d4b90d64c9c320ef4c193f49e71965 Use latest and greatest NVD3. --- diff --git a/xonstat/static/js/nv.d3.js b/xonstat/static/js/nv.d3.js index 2ced9ff..cb771d7 100644 --- a/xonstat/static/js/nv.d3.js +++ b/xonstat/static/js/nv.d3.js @@ -1,399 +1,448 @@ +/* nvd3 version 1.6.0(https://github.com/liquidpele/nvd3) 2014-11-23 */ (function(){ +// set up main nv object on window var nv = window.nv || {}; - - -nv.version = '1.1.10b'; -nv.dev = true //set false when in production - window.nv = nv; -nv.tooltip = {}; // For the tooltip system -nv.utils = {}; // Utility subsystem -nv.models = {}; //stores all the possible models/components +// the major global objects under the nv namespace +nv.dev = true; //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'); -// ************************************************************************* -// Development render timers - disabled if dev = false +// Function bind polyfill +// Needed ONLY for phantomJS as it's missing until version 2.0 which is unreleased as of this comment +// https://github.com/ariya/phantomjs/issues/10522 +// http://kangax.github.io/compat-table/es5/#Function.prototype.bind +// phantomJS is used for running the test suite +if (!Function.prototype.bind) { + Function.prototype.bind = function (oThis) { + if (typeof this !== "function") { + // closest thing possible to the ECMAScript 5 internal IsCallable function + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + } -if (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 - }); + 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; + }; } -// ******************************************** -// Public Core NV functions +// 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 && console.log && console.log.apply) - console.log.apply(console, arguments) - else if (nv.dev && 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]; + 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) { - step = step || 1; // number of graphs to generate in each timeout loop + // number of graphs to generate in each timeout loop + step = step || 1; - nv.render.active = true; - nv.dispatch.render_start(); + nv.render.active = true; + nv.dispatch.render_start(); - setTimeout(function() { - var chart, graph; + 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); - } + 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); + nv.render.queue.splice(0, i); - if (nv.render.queue.length) setTimeout(arguments.callee, 0); - else { nv.render.active = false; nv.dispatch.render_end(); } - }, 0); + 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(); -}; - -nv.identity = function(d) { return d; }; - -nv.strip = function(s) { return s.replace(/(\s|&)/g,''); }; + if (typeof arguments[0] === typeof(Function)) { + obj = {generate: arguments[0], callback: arguments[1]}; + } -function daysInMonth(month,year) { - return (new Date(year, month+1, 0)).getDate(); -} + nv.render.queue.push(obj); -function d3_time_range(floor, step, number) { - return function(t0, t1, dt) { - var time = floor(t0), times = []; - if (time < t0) step(time); - if (dt > 1) { - while (time < t1) { - var date = new Date(+time); - if ((number(date) % dt === 0)) times.push(date); - step(time); - } - } else { - while (time < t1) { times.push(new Date(+time)); step(time); } + if (!nv.render.active) { + nv.render(); } - return times; - }; -} +};/* 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"; -d3.time.monthEnd = function(date) { - return new Date(date.getFullYear(), date.getMonth(), 0); -}; + var tooltip = nv.models.tooltip(); + + //Public settings + var width = null; + var height = null; -d3.time.monthEnds = d3_time_range(d3.time.monthEnd, function(date) { - date.setUTCDate(date.getUTCDate() + 1); - date.setDate(daysInMonth(date.getMonth() + 1, date.getFullYear())); - }, function(date) { - return date.getMonth(); - } -); - -/* 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 - , height = null //Please pass in the bounding chart's top and left margins //This is important for calculating the correct mouseX/Y positions. - , margin = {left: 0, top: 0} - , xScale = d3.scale.linear() - , yScale = d3.scale.linear() - , dispatch = d3.dispatch('elementMousemove', 'elementMouseout') - , showGuideLine = true - , svgContainer = null + 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 container. //The mousemove event is attached to this container. - ; + var svgContainer = null; - //Private variables - var isMSIE = navigator.userAgent.indexOf("MSIE") !== -1 //Check user-agent for Microsoft Internet Explorer. - ; + // check if IE by looking for activeX + var isMSIE = "ActiveXObject" in window; - function layer(selection) { - selection.each(function(data) { - var container = d3.select(this); + 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 . 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 container, + it will actually trigger it for all the child elements (like , , 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; + } - var availableWidth = (width || 960), availableHeight = (height || 400); + if (d3.event.target.className.baseVal.match("nv-legend")) { + mouseOutAnyReason = true; + } - var wrap = container.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([data]); - var wrapEnter = wrap.enter() - .append("g").attr("class", " nv-wrap nv-interactiveLineLayer"); + } + if(subtractMargin) { + mouseX -= margin.left; + mouseY -= margin.top; + } - wrapEnter.append("g").attr("class","nv-interactiveGuideLine"); + /* 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 (!svgContainer) { - return; - } + if (isMSIE) { + if (d3.event.relatedTarget + && d3.event.relatedTarget.ownerSVGElement === undefined + && d3.event.relatedTarget.className.match(tooltip.nvPointerEventsClass)) { - 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 . 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 container, - it will actually trigger it for all the child elements (like , , 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 - }); + } + } + dispatch.elementMouseout({ + mouseX: mouseX, + mouseY: mouseY + }); + layer.renderGuideLine(null); //hide the guideline + return; } - svgContainer - .on("mousemove",mouseHandler, true) - .on("mouseout",mouseHandler,true) - ; - - //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; + 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. + 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. + 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 + 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. -*/ + 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 -};/* 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. -*/ + "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() { - var content = null //HTML contents of the tooltip. If null, the content is generated via the data variable. - , data = null /* Tooltip data. If data is given in the proper format, a consistent tooltip is generated. - Format of data: + "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" - } + {key: "Series 1", value: "Value 1", color: "#000"}, + {key: "Series 2", value: "Value 2", color: "#00f"} ] - } - */ - , 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. - , position = {left: null, top: null} //Relative position of the tooltip inside chartContainer. - , enabled = true //True -> tooltips are rendered. False -> don't render tooltips. + 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 - , id = "nvtooltip-" + Math.floor(Math.random() * 100000) - ; + 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"; @@ -411,54 +460,101 @@ window.nv.tooltip.* also has various helper methods. //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 ''; + if (content != null) { + return content; + } - var html = ""; - if (d.series instanceof Array) { - d.series.forEach(function(item, i) { - html += ""; - html += ""; - html += ""; - html += ""; - }); + if (d == null) { + return ''; } - html += "
" + headerFormatter(d.value) + "
" + item.key + ":" + valueFormatter(item.value,i) + "
"; + + 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 += ""; return html; + }; var dataSeriesExists = function(d) { - if (d && d.series && d.series.length > 0) return true; - + 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]; + 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; - } + 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) + if (chartContainer) { body = d3.select(chartContainer); - else + } else { body = d3.select("body"); + } var container = body.select(".nvtooltip"); if (container.node() === null) { @@ -466,10 +562,9 @@ window.nv.tooltip.* also has various helper methods. 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) @@ -477,8 +572,6 @@ window.nv.tooltip.* also has various helper methods. return container.node(); } - - //Draw the tooltip onto the DOM. function nvtooltip() { if (!enabled) return; @@ -489,7 +582,7 @@ window.nv.tooltip.* also has various helper methods. 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(); @@ -497,7 +590,16 @@ window.nv.tooltip.* also has various helper methods. if (svgComp) { var svgBound = svgComp.getBoundingClientRect(); var chartBound = chartContainer.getBoundingClientRect(); - svgOffset.top = Math.abs(svgBound.top - chartBound.top); + 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
with scrollbars, subtract the scroll offsets. @@ -513,7 +615,7 @@ window.nv.tooltip.* also has various helper methods. nv.tooltip.calcTooltipPosition([left,top], gravity, distance, container); return nvtooltip; - }; + } nvtooltip.nvPointerEventsClass = nvPointerEventsClass; @@ -523,6 +625,11 @@ window.nv.tooltip.* also has various helper methods. 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') { @@ -607,14 +714,12 @@ window.nv.tooltip.* also has various helper methods. 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) { + //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'); @@ -629,82 +734,86 @@ window.nv.tooltip.* also has various helper methods. container.style.left = 0; container.style.top = 0; container.style.opacity = 0; - container.innerHTML = content; + // 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
with scrollbars, subtract the scroll offsets. if (parentContainer) { - pos[0] = pos[0] - parentContainer.scrollLeft; - pos[1] = pos[1] - parentContainer.scrollTop; + 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; + }; - //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; + //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); } - return Elem; - }; + } while( Elem = Elem.offsetParent ); + return offsetTop; + }; - //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; + //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.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); - }; + do { + if( !isNaN( Elem.offsetLeft ) ) { + offsetLeft += (Elem.offsetLeft); + } + } while( Elem = Elem.offsetParent ); + return offsetLeft; + }; - var tooltipLeft = function ( Elem ) { - return nv.tooltip.findTotalOffsetLeft(Elem,left); - }; + //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': + switch (gravity) { + case 'e': left = pos[0] - width - dist; top = pos[1] - (height / 2); var tLeft = tooltipLeft(container); @@ -713,7 +822,7 @@ window.nv.tooltip.* also has various helper methods. if (tTop < scrollTop) top = scrollTop - tTop + top; if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height; break; - case 'w': + case 'w': left = pos[0] + dist; top = pos[1] - (height / 2); var tLeft = tooltipLeft(container); @@ -722,7 +831,7 @@ window.nv.tooltip.* also has various helper methods. if (tTop < scrollTop) top = scrollTop + 5; if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height; break; - case 'n': + case 'n': left = pos[0] - (width / 2) - 5; top = pos[1] + dist; var tLeft = tooltipLeft(container); @@ -731,7 +840,7 @@ window.nv.tooltip.* also has various helper methods. if (tLeft + width > windowWidth) left = left - width/2 + 5; if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height; break; - case 's': + case 's': left = pos[0] - (width / 2); top = pos[1] - height - dist; var tLeft = tooltipLeft(container); @@ -740,47 +849,52 @@ window.nv.tooltip.* also has various helper methods. if (tLeft + width > windowWidth) left = left - width/2 + 5; if (scrollTop > tTop) top = scrollTop; break; - case 'none': + 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'; + container.style.left = left+'px'; + container.style.top = top+'px'; + container.style.opacity = 1; + container.style.position = 'absolute'; - return container; + 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'; - } + // 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() { + setTimeout(function() { - while (purging.length) { - var removeMe = purging.pop(); - removeMe.parentNode.removeChild(removeMe); - } - }, 500); + 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}; @@ -795,6 +909,7 @@ nv.utils.windowSize = function() { if (document.compatMode=='CSS1Compat' && document.documentElement && document.documentElement.offsetWidth ) { + size.width = document.documentElement.offsetWidth; size.height = document.documentElement.offsetHeight; } @@ -808,9397 +923,10022 @@ nv.utils.windowSize = function() { }; +/* +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); + } + } +}; -// Easy way to bind multiple functions to window.onresize -// TODO: give a way to remove a function after its bound, other than removing all of them -nv.utils.windowResize = function(fun){ - if (fun === undefined) return; - var oldresize = window.onresize; - - window.onresize = function(e) { - if (typeof oldresize == 'function') oldresize(e); - fun(e); - } -} -// Backwards compatible way to implement more d3-like coloring of graphs. -// If passed an array, wrap it in a function which implements the old default -// behavior +/* +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 +*/ nv.utils.getColor = function(color) { - if (!arguments.length) return nv.utils.defaultColor(); //if you pass in nothing, get default colors back + //if you pass in nothing or not an array, get default colors back + if (!arguments.length || !(color instanceof Array)) { + return nv.utils.defaultColor(); + } else { + return function (d, i) { + return d.color || color[i % color.length]; + }; + } +}; - if( Object.prototype.toString.call( color ) === '[object Array]' ) - return function(d, i) { return d.color || color[i % color.length]; }; - else - return color; - //can't really help it if someone passes rubbish as color -} -// Default color chooser uses the index of an object as before. +/* +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] }; -} + 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, +/* +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) { - getKey = getKey || function(series) { return series.key }; // use default series.key if getKey is undefined - defaultColors = defaultColors || d3.scale.category20().range(); //default color function - - var defIndex = defaultColors.length; //current default color (going in reverse) - - return function(series, index) { - var key = getKey(series); + // 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]; + } + }; +}; - if (!defIndex) defIndex = defaultColors.length; //used all the default colors, start over - if (typeof dictionary[key] !== "undefined") - return (typeof dictionary[key] === "function") ? dictionary[key]() : dictionary[key]; - else - return defaultColors[--defIndex]; // no match in dictionary, use default color - } -} +/* +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(); + }); -// 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) { - d3.selectAll(links).on("click", function() { - history.pushState(this.href, this.textContent, this.href); - load(this.href); - d3.event.preventDefault(); - }); - - function load(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.select(window).on("popstate", function() { + if (d3.event.state) { + load(d3.event.state); + } }); - } +}; - d3.select(window).on("popstate", function() { - if (d3.event.state) load(d3.event.state); - }); -} -/* For situations where we want to approximate the width in pixels for an SVG:text element. +/* +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 (svgTextElem instanceof d3.selection) { + 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. + +/* +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) return 0; + || n === Infinity + || n === -Infinity) { + return 0; + } return n; }; /* -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 -}); +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); +}; -To enable in the chart: -chart.options = nv.utils.optionsFunc.bind(chart); + +/* +Helper object to watch when d3 has rendered something */ -nv.utils.optionsFunc = function(args) { - if (args) { - d3.map(args).forEach((function(key,value) { - if (typeof this[key] === "function") { - this[key](value); - } - }).bind(this)); +nv.utils.renderWatch = function(dispatch, duration) { + if (!(this instanceof nv.utils.renderWatch)) { + return new nv.utils.renderWatch(dispatch, duration); } - return this; -};nv.models.axis = function() { - "use strict"; - //============================================================ - // Public Variables with Default Settings - //------------------------------------------------------------ - var axis = d3.svg.axis() - ; + 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); - var margin = {top: 0, right: 0, bottom: 0, left: 0} - , width = 75 //only used for tickLabel currently - , height = 60 //only used for tickLabel currently - , scale = d3.scale.linear() - , 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 - ; + if (renderStack.indexOf(model) < 0) { + renderStack.push(model); + } + }); + return this; + }; - axis - .scale(scale) - .orient('bottom') - .tickFormat(function(d) { return d }) - ; + 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; - //============================================================ - // Private Variables - //------------------------------------------------------------ + if (renderStack.indexOf(selection) < 0) { + renderStack.push(selection); + } - var scale0; + 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); + } + } - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this); +}; - //------------------------------------------------------------ - // Setup containers and skeleton of chart +/* +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]; + } + } + }); +}; - 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') - //------------------------------------------------------------ +/* +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) { + 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); + for (var i in ops) { + nv.utils.initOption(chart, ops[i]); + } +}; + + +/* +Inherit option getter/setter functions from source to target +d3.rebind makes calling the function on target actually call it on source +*/ +nv.utils.inheritOptions = function(target, source) { + var args = Object.getOwnPropertyNames(source._options); + args.unshift(source); + args.unshift(target); + d3.rebind.apply(this, args); +}; + + +/* +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 margin = {top: 0, right: 0, bottom: 0, left: 0} + , width = 75 //only used for tickLabel currently + , height = 60 //only used for tickLabel currently + , scale = d3.scale.linear() + , 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 + , 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(' + 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(' + 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(' + (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(' + (scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0)) + ',0)' + }); + } + if (staggerLabels) + xTicks + .attr('transform', function(d,i) { return 'translate(0,' + (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,' + 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,' + 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,' + 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,' + 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) + } + }); + g.selectAll('g') // the g's wrapping each tick + .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; + + d3.rebind(chart, axis, 'orient', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat'); + d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands'); //these are also accessible by chart.scale(), but added common ones directly for ease of use + + chart.options = nv.utils.optionsFunc.bind(chart); + + chart.margin = function(_) { + if(!arguments.length) return margin; + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; + return chart; + } + + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; + + chart.ticks = function(_) { + if (!arguments.length) return ticks; + ticks = _; + return chart; + }; + + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; + + chart.axisLabel = function(_) { + if (!arguments.length) return axisLabelText; + axisLabelText = _; + return chart; + } + + chart.showMaxMin = function(_) { + if (!arguments.length) return showMaxMin; + showMaxMin = _; + return chart; + } + + chart.highlightZero = function(_) { + if (!arguments.length) return highlightZero; + highlightZero = _; + return chart; + } + + chart.scale = function(_) { + if (!arguments.length) return scale; + scale = _; + axis.scale(scale); + isOrdinal = typeof scale.rangeBands === 'function'; + d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands'); + return chart; + } + + chart.rotateYLabel = function(_) { + if(!arguments.length) return rotateYLabel; + rotateYLabel = _; + return chart; + } + + chart.rotateLabels = function(_) { + if(!arguments.length) return rotateLabels; + rotateLabels = _; + return chart; + }; + + chart.staggerLabels = function(_) { + if (!arguments.length) return staggerLabels; + staggerLabels = _; + return chart; + }; + + chart.axisLabelDistance = function(_) { + if (!arguments.length) return axisLabelDistance; + axisLabelDistance = _; + return chart; + }; + + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + return chart; + }; + + + //============================================================ + + + return chart; +} + +// Chart design based on the recommendations of Stephen Few. Implementation +// based on the work of Clint Ivy, Jamie Love, and Jason Davies. +// http://projects.instantcognition.com/protovis/bulletchart/ + +nv.models.bullet = function() { + "use strict"; + //============================================================ + // 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) + /* + .attr('x', rangeMin < 0 ? + rangeMax > 0 ? + x1(rangeMin) + : x1(rangeMax) + : x1(0)) + */ + + g.select('rect.nv-rangeAvg') + .attr('height', availableHeight) + .attr('width', w1(rangeAvg)) + .attr('x', xp1(rangeAvg)) + .datum(rangeAvg) + /* + .attr('width', rangeMax <= 0 ? + x1(rangeMax) - x1(rangeAvg) + : x1(rangeAvg) - x1(rangeMin)) + .attr('x', rangeMax <= 0 ? + x1(rangeAvg) + : x1(rangeMin)) + */ + + 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) + /* + .attr('width', rangeMax <= 0 ? + x1(rangeAvg) - x1(rangeMin) + : x1(rangeMax) - x1(rangeAvg)) + .attr('x', rangeMax <= 0 ? + x1(rangeMin) + : x1(rangeAvg)) + */ + + 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 + }) + }); + }); + + // d3.timer.flush(); // Not needed? + + return chart; + } + + + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ + + chart.dispatch = dispatch; + + chart.options = nv.utils.optionsFunc.bind(chart); + + // left, right, top, bottom + chart.orient = function(_) { + if (!arguments.length) return orient; + orient = _; + reverse = orient == 'right' || orient == 'bottom'; + return chart; + }; + + // ranges (bad, satisfactory, good) + chart.ranges = function(_) { + if (!arguments.length) return ranges; + ranges = _; + return chart; + }; + + // markers (previous, goal) + chart.markers = function(_) { + if (!arguments.length) return markers; + markers = _; + return chart; + }; + + // measures (actual, forecast) + chart.measures = function(_) { + if (!arguments.length) return measures; + measures = _; + return chart; + }; + + chart.forceX = function(_) { + if (!arguments.length) return forceX; + forceX = _; + return chart; + }; + + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; + + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; + + chart.margin = function(_) { + if (!arguments.length) return margin; + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; + return chart; + }; + + chart.tickFormat = function(_) { + if (!arguments.length) return tickFormat; + tickFormat = _; + return chart; + }; + + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + return chart; + }; + + //============================================================ + + + return chart; +}; + + + +// Chart design based on the recommendations of Stephen Few. Implementation +// based on the work of Clint Ivy, Jamie Love, and Jason Davies. +// http://projects.instantcognition.com/protovis/bulletchart/ +nv.models.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 '

' + x + '

' + + '

' + y + '

' + } + , 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; + + /* + // Derive width-scales from the x-scales. + var w0 = bulletWidth(x0), + w1 = bulletWidth(x1); + + function bulletWidth(x) { + var x0 = x(0); + return function(d) { + return Math.abs(x(d) - x(0)); + }; + } + + function bulletTranslate(x) { + return function(d) { + return 'translate(' + x(d) + ',0)'; + }; + } + */ + + var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0) + w1 = function(d) { return Math.abs(x1(d) - x1(0)) }; + + + var title = gEnter.select('.nv-titles').append('g') + .attr('text-anchor', 'end') + .attr('transform', 'translate(-6,' + (height - margin.top - margin.bottom) / 2 + ')'); + title.append('text') + .attr('class', 'nv-title') + .text(function(d) { return d.title; }); + + 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.dispatch = dispatch; + chart.bullet = bullet; + + d3.rebind(chart, bullet, 'color'); + chart.options = nv.utils.optionsFunc.bind(chart); - 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); + // left, right, top, bottom + chart.orient = function(x) { + if (!arguments.length) return orient; + orient = x; + reverse = orient == 'right' || orient == 'bottom'; + return chart; + }; + // ranges (bad, satisfactory, good) + chart.ranges = function(x) { + if (!arguments.length) return ranges; + ranges = x; + return chart; + }; - //TODO: consider calculating width/height based on whether or not label is added, for reference in charts using this component + // markers (previous, goal) + chart.markers = function(x) { + if (!arguments.length) return markers; + markers = x; + return chart; + }; + // measures (actual, forecast) + chart.measures = function(x) { + if (!arguments.length) return measures; + measures = x; + return chart; + }; - g.transition().call(axis); + chart.width = function(x) { + if (!arguments.length) return width; + width = x; + return chart; + }; - scale0 = scale0 || axis.scale(); + chart.height = function(x) { + if (!arguments.length) return height; + height = x; + return chart; + }; - var fmt = axis.tickFormat(); - if (fmt == null) { - fmt = scale0.tickFormat(); - } + 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; + }; - 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 = (scale.range().length==2) ? scale.range()[1] : (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(' + scale(d) + ',0)' - }) - .select('text') - .attr('dy', '0em') - .attr('y', -axis.tickPadding()) - .attr('text-anchor', 'middle') - .text(function(d,i) { - var v = fmt(d); - return ('' + v).match('NaN') ? '' : v; - }); - axisMaxMin.transition() - .attr('transform', function(d,i) { - return 'translate(' + scale.range()[i] + ',0)' - }); - } - break; - case 'bottom': - var xLabelMargin = 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.getBBox().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 = (scale.range().length==2) ? scale.range()[1] : (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(' + (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.transition() - .attr('transform', function(d,i) { - //return 'translate(' + scale.range()[i] + ',0)' - //return 'translate(' + scale(d) + ',0)' - return 'translate(' + (scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0)) + ',0)' - }); - } - if (staggerLabels) - xTicks - .attr('transform', function(d,i) { return 'translate(0,' + (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,' + 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.transition() - .attr('transform', function(d,i) { - return 'translate(0,' + 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.getBBox().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) + 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,' + 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; + chart.tickFormat = function(x) { + if (!arguments.length) return tickFormat; + tickFormat = x; + return chart; + }; + + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; + + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; + + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return 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 '

' + key + '

' + + '

' + y + ' at ' + x + '

' + } + , 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]; }); - axisMaxMin.transition() - .attr('transform', function(d,i) { - return 'translate(0,' + 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.getBBox().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.getBBox().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) - } - }); - g.selectAll('g') // the g's wrapping each tick - .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(); + //============================================================ + + 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 - d3.select(this).select('text').remove(); // Don't remove the ZERO line!! - } - }); - } + 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); - //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(d.__data__*100000)/1000000) && (d.__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(); + 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(); + } - return chart; - } + 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); + } - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + //------------------------------------------------------------ + // Display No Data message if there's nothing to show. - // expose chart's sub-components - chart.axis = axis; + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { + var noDataText = container.selectAll('.nv-noData').data([noData]); - d3.rebind(chart, axis, 'orient', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat'); - d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands'); //these are also accessible by chart.scale(), but added common ones directly for ease of use + noDataText.enter().append('text') + .attr('class', 'nvd3 nv-noData') + .attr('dy', '-.7em') + .style('text-anchor', 'middle'); - chart.options = nv.utils.optionsFunc.bind(chart); + noDataText + .attr('x', margin.left + availableWidth / 2) + .attr('y', margin.top + availableHeight / 2) + .text(function(d) { return d }); - 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; - } + return chart; + } else { + container.selectAll('.nv-noData').remove(); + } - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + //------------------------------------------------------------ - chart.ticks = function(_) { - if (!arguments.length) return ticks; - ticks = _; - return chart; - }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + //------------------------------------------------------------ + // Setup Scales - chart.axisLabel = function(_) { - if (!arguments.length) return axisLabelText; - axisLabelText = _; - return chart; - } + x = lines.xScale(); + y = lines.yScale(); - chart.showMaxMin = function(_) { - if (!arguments.length) return showMaxMin; - showMaxMin = _; - return chart; - } - chart.highlightZero = function(_) { - if (!arguments.length) return highlightZero; - highlightZero = _; - return chart; - } - - chart.scale = function(_) { - if (!arguments.length) return scale; - scale = _; - axis.scale(scale); - isOrdinal = typeof scale.rangeBands === 'function'; - d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands'); - return chart; - } + if (!rescaleY) { + var seriesDomains = data + .filter(function(series) { return !series.disabled }) + .map(function(series,i) { + var initialDomain = d3.extent(series.values, lines.y()); - chart.rotateYLabel = function(_) { - if(!arguments.length) return rotateYLabel; - rotateYLabel = _; - return chart; - } + //account for series being disabled when losing 95% or more + if (initialDomain[0] < -.95) initialDomain[0] = -.95; - chart.rotateLabels = function(_) { - if(!arguments.length) return rotateLabels; - rotateLabels = _; - return chart; - } + return [ + (initialDomain[0] - initialDomain[1]) / (1 + initialDomain[1]), + (initialDomain[1] - initialDomain[0]) / (1 + initialDomain[0]) + ]; + }); - chart.staggerLabels = function(_) { - if (!arguments.length) return staggerLabels; - staggerLabels = _; - return chart; - }; + 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); + } - return chart; -} + dx .domain([0, data[0].values.length - 1]) //Assumes all series have same length + .range([0, availableWidth]) + .clamp(true); -// 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 } - , 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') - ; - //============================================================ + var data = indexify(index.i, data); - 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); - - 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) - /* - .attr('x', rangeMin < 0 ? - rangeMax > 0 ? - x1(rangeMin) - : x1(rangeMax) - : x1(0)) - */ - - g.select('rect.nv-rangeAvg') - .attr('height', availableHeight) - .attr('width', w1(rangeAvg)) - .attr('x', xp1(rangeAvg)) - .datum(rangeAvg) - /* - .attr('width', rangeMax <= 0 ? - x1(rangeMax) - x1(rangeAvg) - : x1(rangeAvg) - x1(rangeMin)) - .attr('x', rangeMax <= 0 ? - x1(rangeAvg) - : x1(rangeMin)) - */ - - 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) - /* - .attr('width', rangeMax <= 0 ? - x1(rangeAvg) - x1(rangeMin) - : x1(rangeMax) - x1(rangeAvg)) - .attr('x', rangeMax <= 0 ? - x1(rangeMin) - : x1(rangeAvg)) - */ - - 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 - }) - }) - -/* // THIS IS THE PREVIOUS BULLET IMPLEMENTATION, WILL REMOVE SHORTLY - // Update the range rects. - var range = g.selectAll('rect.nv-range') - .data(rangez); - - range.enter().append('rect') - .attr('class', function(d, i) { return 'nv-range nv-s' + i; }) - .attr('width', w0) - .attr('height', availableHeight) - .attr('x', reverse ? x0 : 0) - .on('mouseover', function(d,i) { - dispatch.elementMouseover({ - value: d, - label: (i <= 0) ? 'Maximum' : (i > 1) ? 'Minimum' : 'Mean', //TODO: make these labels a variable - pos: [x1(d), availableHeight/2] - }) - }) - .on('mouseout', function(d,i) { - dispatch.elementMouseout({ - value: d, - label: (i <= 0) ? 'Minimum' : (i >=1) ? 'Maximum' : 'Mean' //TODO: make these labels a variable - }) - }) - - d3.transition(range) - .attr('x', reverse ? x1 : 0) - .attr('width', w1) - .attr('height', availableHeight); - - - // Update the measure rects. - var measure = g.selectAll('rect.nv-measure') - .data(measurez); - - measure.enter().append('rect') - .attr('class', function(d, i) { return 'nv-measure nv-s' + i; }) - .style('fill', function(d,i) { return color(d,i ) }) - .attr('width', w0) - .attr('height', availableHeight / 3) - .attr('x', reverse ? x0 : 0) - .attr('y', availableHeight / 3) - .on('mouseover', function(d) { - dispatch.elementMouseover({ - value: d, - label: 'Current', //TODO: make these labels a variable - pos: [x1(d), availableHeight/2] - }) - }) - .on('mouseout', function(d) { - dispatch.elementMouseout({ - value: d, - label: 'Current' //TODO: make these labels a variable - }) - }) - - d3.transition(measure) - .attr('width', w1) - .attr('height', availableHeight / 3) - .attr('x', reverse ? x1 : 0) - .attr('y', availableHeight / 3); - - - - // Update the marker lines. - var marker = g.selectAll('path.nv-markerTriangle') - .data(markerz); - - var h3 = availableHeight / 6; - marker.enter().append('path') - .attr('class', 'nv-markerTriangle') - .attr('transform', function(d) { return 'translate(' + x0(d) + ',' + (availableHeight / 2) + ')' }) - .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z') - .on('mouseover', function(d,i) { - dispatch.elementMouseover({ - value: d, - label: 'Previous', - pos: [x1(d), availableHeight/2] - }) - }) - .on('mouseout', function(d,i) { - dispatch.elementMouseout({ - value: d, - label: 'Previous' - }) - }); - - d3.transition(marker) - .attr('transform', function(d) { return 'translate(' + (x1(d) - x1(0)) + ',' + (availableHeight / 2) + ')' }); - - marker.exit().remove(); -*/ - - }); + //------------------------------------------------------------ + // 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'); - // d3.timer.flush(); // Not needed? + 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'); - return chart; - } + //------------------------------------------------------------ + // Legend - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + if (showLegend) { + legend.width(availableWidth); - chart.dispatch = dispatch; + g.select('.nv-legendWrap') + .datum(data) + .call(legend); - chart.options = nv.utils.optionsFunc.bind(chart); + if ( margin.top != legend.height()) { + margin.top = legend.height(); + availableHeight = (height || parseInt(container.style('height')) || 400) + - margin.top - margin.bottom; + } - // left, right, top, bottom - chart.orient = function(_) { - if (!arguments.length) return orient; - orient = _; - reverse = orient == 'right' || orient == 'bottom'; - return chart; - }; + g.select('.nv-legendWrap') + .attr('transform', 'translate(0,' + (-margin.top) +')') + } - // ranges (bad, satisfactory, good) - chart.ranges = function(_) { - if (!arguments.length) return ranges; - ranges = _; - return chart; - }; + //------------------------------------------------------------ - // markers (previous, goal) - chart.markers = function(_) { - if (!arguments.length) return markers; - markers = _; - return chart; - }; - // measures (actual, forecast) - chart.measures = function(_) { - if (!arguments.length) return measures; - measures = _; - return chart; - }; + //------------------------------------------------------------ + // Controls - chart.forceX = function(_) { - if (!arguments.length) return forceX; - forceX = _; - return chart; - }; + if (showControls) { + var controlsData = [ + { key: 'Re-scale y-axis', disabled: !rescaleY } + ]; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + controls + .width(140) + .color(['#444', '#444', '#444']) + .rightAlign(false) + .margin({top: 5, right: 0, bottom: 5, left: 20}) + ; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + g.select('.nv-controlsWrap') + .datum(controlsData) + .attr('transform', 'translate(0,' + (-margin.top) +')') + .call(controls); + } - chart.tickFormat = function(_) { - if (!arguments.length) return tickFormat; - tickFormat = _; - return chart; - }; + //------------------------------------------------------------ - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - return chart; - }; - //============================================================ + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + if (rightAlignYAxis) { + g.select(".nv-y.nv-axis") + .attr("transform", "translate(" + availableWidth + ",0)"); + } - return chart; -}; + // 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.'); + } + //------------------------------------------------------------ + // Main Chart Component(s) + + //------------------------------------------------------------ + //Set up interactive layer + if (useInteractiveGuideline) { + interactiveLayer + .width(availableWidth) + .height(availableHeight) + .margin({left:margin.left,top:margin.top}) + .svgContainer(container) + .xScale(x); + wrap.select(".nv-interactive").call(interactiveLayer); + } -// Chart 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 - //------------------------------------------------------------ + gEnter.select('.nv-background') + .append('rect'); - var bullet = nv.models.bullet() - ; + g.select('.nv-background rect') + .attr('width', availableWidth) + .attr('height', availableHeight); - 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 } - , measures = function(d) { return d.measures } - , width = null - , height = 55 - , tickFormat = null - , tooltips = true - , tooltip = function(key, x, y, e, graph) { - return '

' + x + '

' + - '

' + y + '

' - } - , noData = 'No Data Available.' - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide') - ; + 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; })); - //============================================================ - //============================================================ - // Private Variables - //------------------------------------------------------------ + var linesWrap = g.select('.nv-linesWrap') + .datum(data.filter(function(d) { return !d.disabled && !d.tempDisabled })); - 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); + //d3.transition(linesWrap).call(lines); + linesWrap.call(lines); - nv.tooltip.show([left, top], content, e.value < 0 ? 'e' : 'w', null, offsetElement); - }; + /*Handle average lines [AN-612] ----------------------------*/ - //============================================================ + //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); + }); - function chart(selection) { - selection.each(function(d, i) { - var container = d3.select(this); + var avgLines = g.select(".nv-avgLinesWrap").selectAll("line") + .data(avgLineData, function(d) { return d.key; }); - var availableWidth = (width || parseInt(container.style('width')) || 960) - - margin.left - margin.right, - availableHeight = height - margin.top - margin.bottom, - that = this; + 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); - chart.update = function() { chart(selection) }; - chart.container = this; + avgLines.exit().remove(); - //------------------------------------------------------------ - // Display No Data message if there's nothing to show. + //Create index line ----------------------------------------- - if (!d || !ranges.call(this, d, i)) { - var noDataText = container.selectAll('.nv-noData').data([noData]); + 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); - noDataText.enter().append('text') - .attr('class', 'nvd3 nv-noData') - .attr('dy', '-.7em') - .style('text-anchor', 'middle'); + indexLine + .attr('transform', function(d) { return 'translate(' + dx(d.i) + ',0)' }) + .attr('height', availableHeight); - 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(); - } - //------------------------------------------------------------ + //------------------------------------------------------------ + // 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); + } - 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); + if (showYAxis) { + yAxis + .scale(y) + .ticks( nv.utils.calcTicksY(availableHeight/36, data) ) + .tickSize( -availableWidth, 0); + g.select('.nv-y.nv-axis') + .call(yAxis); + } + //------------------------------------------------------------ - //------------------------------------------------------------ - // 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'); + //============================================================ + // Event Handling/Dispatching (in chart's scope) + //------------------------------------------------------------ - gEnter.append('g').attr('class', 'nv-bulletWrap'); - gEnter.append('g').attr('class', 'nv-titles'); - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + 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)); - // 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]); + // update state and send stateChange with new index + state.index = index.i; + dispatch.stateChange(state); - // Retrieve the old x-scale, if this is an update. - var x0 = this.__chart__ || d3.scale.linear() - .domain([0, Infinity]) - .range(x1.range()); + updateZero(); + }); - // Stash the new scale. - this.__chart__ = x1; + lines.dispatch.on('elementClick', function(e) { + index.i = e.pointIndex; + index.x = dx(index.i); - /* - // Derive width-scales from the x-scales. - var w0 = bulletWidth(x0), - w1 = bulletWidth(x1); + // update state and send stateChange with new index + state.index = index.i; + dispatch.stateChange(state); - function bulletWidth(x) { - var x0 = x(0); - return function(d) { - return Math.abs(x(d) - x(0)); - }; - } + updateZero(); + }); - function bulletTranslate(x) { - return function(d) { - return 'translate(' + x(d) + ',0)'; - }; - } - */ + controls.dispatch.on('legendClick', function(d,i) { + d.disabled = !d.disabled; + rescaleY = !d.disabled; - 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)) }; + 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 title = gEnter.select('.nv-titles').append('g') - .attr('text-anchor', 'end') - .attr('transform', 'translate(-6,' + (height - margin.top - margin.bottom) / 2 + ')'); - title.append('text') - .attr('class', 'nv-title') - .text(function(d) { return d.title; }); + var 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 + } + )(); - title.append('text') - .attr('class', 'nv-subtitle') - .attr('dy', '1em') - .text(function(d) { return d.subtitle; }); + interactiveLayer.renderGuideLine(pointXLocation); + }); + interactiveLayer.dispatch.on("elementMouseout",function(e) { + dispatch.tooltipHide(); + lines.clearHighlights(); + }); - bullet - .width(availableWidth) - .height(availableHeight) + dispatch.on('tooltipShow', function(e) { + if (tooltips) showTooltip(e, that.parentNode); + }); - var bulletWrap = g.select('.nv-bulletWrap'); - d3.transition(bulletWrap).call(bullet); + // 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; + } - // 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); - }); + if (typeof e.index !== 'undefined') { + index.i = e.index; + index.x = dx(index.i); - // 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); + state.index = e.index; - tickEnter.append('line') - .attr('y1', availableHeight) - .attr('y2', availableHeight * 7 / 6); + indexLine + .data([index]); + } - tickEnter.append('text') - .attr('text-anchor', 'middle') - .attr('dy', '1em') - .attr('y', availableHeight * 7 / 6) - .text(format); + if (typeof e.rescaleY !== 'undefined') { + rescaleY = e.rescaleY; + } + chart.update(); + }); - // 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); + renderWatch.renderEnd('cumulativeLineChart immediate'); - // 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(); + return chart; + } - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ - dispatch.on('tooltipShow', function(e) { - e.key = d.title; - if (tooltips) showTooltip(e, that.parentNode); - }); + 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(); }); - d3.timer.flush(); + //============================================================ - return chart; - } + //============================================================ + // 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; + + // DO NOT DELETE. This is currently overridden below + // until deprecated portions are removed. + chart.state = state; + + d3.rebind(chart, lines, 'defined', 'isArea', 'x', 'y', 'xScale','yScale', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi','useVoronoi', 'id'); + + chart.options = nv.utils.optionsFunc.bind(chart); - //============================================================ - // Event Handling/Dispatching (out of chart's scope) - //------------------------------------------------------------ + 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; + }; - bullet.dispatch.on('elementMouseover.tooltip', function(e) { - dispatch.tooltipShow(e); - }); + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - bullet.dispatch.on('elementMouseout.tooltip', function(e) { - dispatch.tooltipHide(e); - }); + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - dispatch.on('tooltipHide', function() { - if (tooltips) nv.tooltip.cleanup(); - }); + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + legend.color(color); + return chart; + }; - //============================================================ + chart.rescaleY = function(_) { + if (!arguments.length) return rescaleY; + rescaleY = _; + return chart; + }; + chart.showControls = function(_) { + if (!arguments.length) return showControls; + showControls = _; + return chart; + }; - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + chart.useInteractiveGuideline = function(_) { + if(!arguments.length) return useInteractiveGuideline; + useInteractiveGuideline = _; + if (_ === true) { + chart.interactive(false); + chart.useVoronoi(false); + } + return chart; + }; - chart.dispatch = dispatch; - chart.bullet = bullet; + chart.showLegend = function(_) { + if (!arguments.length) return showLegend; + showLegend = _; + return chart; + }; - d3.rebind(chart, bullet, 'color'); + chart.showXAxis = function(_) { + if (!arguments.length) return showXAxis; + showXAxis = _; + return chart; + }; - chart.options = nv.utils.optionsFunc.bind(chart); + chart.showYAxis = function(_) { + if (!arguments.length) return showYAxis; + showYAxis = _; + return chart; + }; - // left, right, top, bottom - chart.orient = function(x) { - if (!arguments.length) return orient; - orient = x; - reverse = orient == 'right' || orient == 'bottom'; - return chart; - }; + chart.rightAlignYAxis = function(_) { + if(!arguments.length) return rightAlignYAxis; + rightAlignYAxis = _; + yAxis.orient( (_) ? 'right' : 'left'); + return chart; + }; - // ranges (bad, satisfactory, good) - chart.ranges = function(x) { - if (!arguments.length) return ranges; - ranges = x; - return chart; - }; + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; - // markers (previous, goal) - chart.markers = function(x) { - if (!arguments.length) return markers; - markers = x; - return chart; - }; + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - // measures (actual, forecast) - chart.measures = function(x) { - if (!arguments.length) return measures; - measures = x; - return chart; - }; + // DEPRECATED + chart.state = function(_) { + nv.deprecated('cululativeLineChart.state'); + if (!arguments.length) return state; + state = _; + return chart; + }; + for (var key in state) { + chart.state[key] = state[key]; + } + // END DEPRECATED - chart.width = function(x) { - if (!arguments.length) return width; - width = x; - return chart; - }; + chart.defaultState = function(_) { + if (!arguments.length) return defaultState; + defaultState = _; + return chart; + }; - chart.height = function(x) { - if (!arguments.length) return height; - height = x; - return chart; - }; - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; - chart.tickFormat = function(x) { - if (!arguments.length) return tickFormat; - tickFormat = x; - return chart; - }; + chart.average = function(_) { + if(!arguments.length) return average; + average = _; + return chart; + }; - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; + chart.transitionDuration = function(_) { + nv.deprecated('cumulativeLineChart.transitionDuration'); + return chart.duration(_); + }; - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + chart.duration = function(_) { + if(!arguments.length) return duration; + duration = _; + lines.duration(duration); + xAxis.duration(duration); + yAxis.duration(duration); + renderWatch.reset(duration); + return chart; + }; - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; + chart.noErrorCheck = function(_) { + if (!arguments.length) return noErrorCheck; + noErrorCheck = _; + return chart; + }; - //============================================================ + //============================================================ - return chart; -}; + //============================================================ + // 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) -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() - ; + line.tempDisabled = true; + return line; + } - 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 '

' + key + '

' + - '

' + y + ' at ' + x + '

' - } - , x //can be accessed via chart.xScale() - , y //can be accessed via chart.yScale() - , id = lines.id() - , state = { index: 0, rescaleY: rescaleY } - , defaultState = null - , noData = 'No Data Available.' - , average = function(d) { return d.average } - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') - , transitionDuration = 250 - ; + line.tempDisabled = false; - xAxis - .orient('bottom') - .tickPadding(7) - ; - yAxis - .orient((rightAlignYAxis) ? 'right' : 'left') - ; + line.values = line.values.map(function(point, pointIndex) { + point.display = {'y': (indexifyYGetter(point, pointIndex) - v) / (1 + v) }; + return point; + }); + + return line; + }) + } - //============================================================ - controls.updateState(false); + //============================================================ - //============================================================ - // Private Variables - //------------------------------------------------------------ - var dx = d3.scale.linear() - , index = {i: 0, x: 0} - ; + 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 + ; - 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); - }; -/* - //Moved to see if we can get better behavior to fix issue #315 - 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) { - d.x += d3.event.dx; - d.i = Math.round(dx.invert(d.x)); - - d3.select(this).attr('transform', 'translate(' + dx(d.i) + ',0)'); - chart.update(); - } - - function dragEnd(d,i) { - d3.select(chart.container) - .style('cursor', 'auto'); - chart.update(); - } -*/ + //============================================================ + // Private Variables + //------------------------------------------------------------ - //============================================================ + var x0, y0; + var renderWatch = nv.utils.renderWatch(dispatch, duration); + //============================================================ - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this).classed('nv-chart-' + id, true), - 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; + 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); - chart.update = function() { container.transition().duration(transitionDuration).call(chart) }; - chart.container = this; + //add series index to each data point for reference + data.forEach(function(series, i) { + series.values.forEach(function(point) { + point.series = i; + }); + }); - //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]; - } - } + //------------------------------------------------------------ + // Setup Scales - var indexDrag = d3.behavior.drag() - .on('dragstart', dragStart) - .on('drag', dragMove) - .on('dragend', dragEnd); + // 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); - function dragStart(d,i) { - d3.select(chart.container) - .style('cursor', 'ew-resize'); - } + y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y }).concat(forceY))); - 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'); + // 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]); - // update state and send stateChange with new index - state.index = index.i; - dispatch.stateChange(state); - } + //store old scales if they exist + x0 = x0 || x; + y0 = y0 || y.copy().range([y(0),y(0)]); - //------------------------------------------------------------ - // 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'); + //------------------------------------------------------------ + // Setup containers and skeleton of chart - noDataText - .attr('x', margin.left + availableWidth / 2) - .attr('y', margin.top + availableHeight / 2) - .text(function(d) { return d }); + 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'); - return chart; - } else { - container.selectAll('.nv-noData').remove(); - } + gEnter.append('g').attr('class', 'nv-groups'); - //------------------------------------------------------------ + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + //------------------------------------------------------------ - //------------------------------------------------------------ - // Setup Scales - x = lines.xScale(); - y = lines.yScale(); + //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); - 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; + var bars = groups.selectAll('g.nv-bar') + .data(function(d) { return d.values }); - return [ - (initialDomain[0] - initialDomain[1]) / (1 + initialDomain[1]), - (initialDomain[1] - initialDomain[0]) / (1 + initialDomain[0]) - ]; - }); + bars.exit().remove(); - 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); - } + 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 ) - dx .domain([0, data[0].values.length - 1]) //Assumes all series have same length - .range([0, availableWidth]) - .clamp(true); + 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(); + } - var data = indexify(index.i, data); + 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) + }); - //------------------------------------------------------------ - // 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'); + //store old scales for use in transitions on update + x0 = x.copy(); + y0 = y.copy(); - 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'); + }); + renderWatch.renderEnd('discreteBar immediate'); + return chart; + } - //------------------------------------------------------------ - // Legend - if (showLegend) { - legend.width(availableWidth); + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ - g.select('.nv-legendWrap') - .datum(data) - .call(legend); + chart.dispatch = dispatch; - if ( margin.top != legend.height()) { - margin.top = legend.height(); - availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; - } + chart.options = nv.utils.optionsFunc.bind(chart); - g.select('.nv-legendWrap') - .attr('transform', 'translate(0,' + (-margin.top) +')') - } + chart.x = function(_) { + if (!arguments.length) return getX; + getX = _; + return chart; + }; - //------------------------------------------------------------ + chart.y = function(_) { + if (!arguments.length) return getY; + getY = _; + return chart; + }; + chart.margin = function(_) { + if (!arguments.length) return margin; + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; + return chart; + }; - //------------------------------------------------------------ - // Controls + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - if (showControls) { - var controlsData = [ - { key: 'Re-scale y-axis', disabled: !rescaleY } - ]; - - controls.width(140).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)"); - } - - // 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.'); - } - - //------------------------------------------------------------ - // Main Chart Component(s) - - //------------------------------------------------------------ - //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 })); - - //d3.transition(linesWrap).call(lines); - linesWrap.call(lines); - - /*Handle average lines [AN-612] ----------------------------*/ - - //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) - //Suggest how many ticks based on the chart width and D3 should listen (70 is the optimal number for MM/DD/YY dates) - .ticks( Math.min(data[0].values.length,availableWidth/70) ) - .tickSize(-availableHeight, 0); - - g.select('.nv-x.nv-axis') - .attr('transform', 'translate(0,' + y.range()[0] + ')'); - d3.transition(g.select('.nv-x.nv-axis')) - .call(xAxis); - } - - - if (showYAxis) { - yAxis - .scale(y) - .ticks( availableHeight / 36 ) - .tickSize( -availableWidth, 0); - - d3.transition(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.transitionDuration(); - chart.transitionDuration(0); - chart.update(); - chart.transitionDuration(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) { - state.disabled = newState.disabled; - 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) - }); - }); - - 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 - } - )(); + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - interactiveLayer.renderGuideLine(pointXLocation); + chart.xScale = function(_) { + if (!arguments.length) return x; + x = _; + return chart; + }; - }); + chart.yScale = function(_) { + if (!arguments.length) return y; + y = _; + return chart; + }; - interactiveLayer.dispatch.on("elementMouseout",function(e) { - dispatch.tooltipHide(); - lines.clearHighlights(); - }); + chart.xDomain = function(_) { + if (!arguments.length) return xDomain; + xDomain = _; + return chart; + }; - dispatch.on('tooltipShow', function(e) { - if (tooltips) showTooltip(e, that.parentNode); - }); + chart.yDomain = function(_) { + if (!arguments.length) return yDomain; + yDomain = _; + return chart; + }; + chart.xRange = function(_) { + if (!arguments.length) return xRange; + xRange = _; + return chart; + }; - // Update chart from a state object passed to event handler - dispatch.on('changeState', function(e) { + chart.yRange = function(_) { + if (!arguments.length) return yRange; + yRange = _; + return chart; + }; - if (typeof e.disabled !== 'undefined') { - data.forEach(function(series,i) { - series.disabled = e.disabled[i]; - }); + chart.forceY = function(_) { + if (!arguments.length) return forceY; + forceY = _; + return chart; + }; - state.disabled = e.disabled; - } + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + return chart; + }; + chart.id = function(_) { + if (!arguments.length) return id; + id = _; + return chart; + }; - if (typeof e.index !== 'undefined') { - index.i = e.index; - index.x = dx(index.i); + chart.showValues = function(_) { + if (!arguments.length) return showValues; + showValues = _; + return chart; + }; - state.index = e.index; + chart.valueFormat= function(_) { + if (!arguments.length) return valueFormat; + valueFormat = _; + return chart; + }; - indexLine - .data([index]); - } + chart.rectClass= function(_) { + if (!arguments.length) return rectClass; + rectClass = _; + return chart; + }; + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + return chart; + }; + //============================================================ - if (typeof e.rescaleY !== 'undefined') { - rescaleY = e.rescaleY; - } - chart.update(); - }); + 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 '

' + x + '

' + + '

' + y + '

' + } + , x + , y + , noData = "No Data Available." + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'beforeUpdate','renderEnd') + , duration = 250 + ; - return chart; - } + xAxis + .orient('bottom') + .highlightZero(false) + .showMaxMin(false) + .tickFormat(function(d) { return d }) + ; + yAxis + .orient((rightAlignYAxis) ? 'right' : 'left') + .tickFormat(d3.format(',.1f')) + ; + //============================================================ - //============================================================ - // 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); - }); + //============================================================ + // Private Variables + //------------------------------------------------------------ - lines.dispatch.on('elementMouseout.tooltip', function(e) { - dispatch.tooltipHide(e); - }); + 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); - dispatch.on('tooltipHide', function() { - if (tooltips) nv.tooltip.cleanup(); - }); + nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement); + }; - //============================================================ + var renderWatch = nv.utils.renderWatch(dispatch, duration); + //============================================================ - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + function chart(selection) { + renderWatch.reset(); + renderWatch.models(discretebar); + if (showXAxis) renderWatch.models(xAxis); + if (showYAxis) renderWatch.models(yAxis); - // expose chart's sub-components - chart.dispatch = dispatch; - chart.lines = lines; - chart.legend = legend; - chart.xAxis = xAxis; - chart.yAxis = yAxis; - chart.interactiveLayer = interactiveLayer; + 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; - d3.rebind(chart, lines, 'defined', 'isArea', 'x', 'y', 'xScale','yScale', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi','useVoronoi', 'id'); - chart.options = nv.utils.optionsFunc.bind(chart); + chart.update = function() { + dispatch.beforeUpdate(); + container.transition().duration(duration).call(chart); + }; + chart.container = this; - 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; - }; + //------------------------------------------------------------ + // Display No Data message if there's nothing to show. - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { + var noDataText = container.selectAll('.nv-noData').data([noData]); - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - legend.color(color); - return chart; - }; + noDataText.enter().append('text') + .attr('class', 'nvd3 nv-noData') + .attr('dy', '-.7em') + .style('text-anchor', 'middle'); - chart.rescaleY = function(_) { - if (!arguments.length) return rescaleY; - rescaleY = _ - return rescaleY; - }; + noDataText + .attr('x', margin.left + availableWidth / 2) + .attr('y', margin.top + availableHeight / 2) + .text(function(d) { return d }); - chart.showControls = function(_) { - if (!arguments.length) return showControls; - showControls = _; - return chart; - }; - - chart.useInteractiveGuideline = function(_) { - if(!arguments.length) return useInteractiveGuideline; - useInteractiveGuideline = _; - if (_ === true) { - chart.interactive(false); - chart.useVoronoi(false); - } - return chart; - }; + return chart; + } else { + container.selectAll('.nv-noData').remove(); + } - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + //------------------------------------------------------------ - chart.showXAxis = function(_) { - if (!arguments.length) return showXAxis; - showXAxis = _; - return chart; - }; - chart.showYAxis = function(_) { - if (!arguments.length) return showYAxis; - showYAxis = _; - return chart; - }; + //------------------------------------------------------------ + // Setup Scales - chart.rightAlignYAxis = function(_) { - if(!arguments.length) return rightAlignYAxis; - rightAlignYAxis = _; - yAxis.orient( (_) ? 'right' : 'left'); - return chart; - }; + x = discretebar.xScale(); + y = discretebar.yScale().clamp(true); - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; + //------------------------------------------------------------ - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; - chart.state = function(_) { - if (!arguments.length) return state; - state = _; - return chart; - }; + //------------------------------------------------------------ + // Setup containers and skeleton of chart - chart.defaultState = function(_) { - if (!arguments.length) return defaultState; - defaultState = _; - return 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'); - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; + 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'); - chart.average = function(_) { - if(!arguments.length) return average; - average = _; - return chart; - }; + gEnter.append('g').attr('class', 'nv-barsWrap'); - chart.transitionDuration = function(_) { - if (!arguments.length) return transitionDuration; - transitionDuration = _; - return chart; - }; + g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - //============================================================ + if (rightAlignYAxis) { + g.select(".nv-y.nv-axis") + .attr("transform", "translate(" + availableWidth + ",0)"); + } + //------------------------------------------------------------ - //============================================================ - // Functions - //------------------------------------------------------------ - /* Normalize the data according to an index point. */ - function indexify(idx, data) { - return data.map(function(line, i) { - if (!line.values) { - return line; - } - var v = lines.y()(line.values[idx], idx); + //------------------------------------------------------------ + // Main Chart Component(s) - //TODO: implement check below, and disable series if series loses 100% or more cause divide by 0 issue - if (v < -.95) { - //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; - } + discretebar + .width(availableWidth) + .height(availableHeight); - line.tempDisabled = false; - line.values = line.values.map(function(point, pointIndex) { - point.display = {'y': (lines.y()(point, pointIndex) - v) / (1 + v) }; - return point; - }) + var barsWrap = g.select('.nv-barsWrap') + .datum(data.filter(function(d) { return !d.disabled })) - return line; - }) - } + barsWrap.transition().call(discretebar); - //============================================================ + //------------------------------------------------------------ - 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') - , rectClass = '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 )); - //============================================================ - // Private Variables - //------------------------------------------------------------ - var x0, y0; + //------------------------------------------------------------ + // 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)) + ')'); - function chart(selection) { - selection.each(function(data) { - var availableWidth = width - margin.left - margin.right, - availableHeight = height - margin.top - margin.bottom, - container = d3.select(this); + g.select('.nv-x.nv-axis').call(xAxis); - //add series index to each data point for reference - data = data.map(function(series, i) { - series.values = series.values.map(function(point) { - point.series = i; - return point; - }); - return series; - }); + 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') + ')' }) + } + } - //------------------------------------------------------------ - // Setup Scales + if (showYAxis) { + yAxis + .scale(y) + .ticks( nv.utils.calcTicksY(availableHeight/36, data) ) + .tickSize( -availableWidth, 0); - // 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 } - }) - }); + g.select('.nv-y.nv-axis').call(yAxis); + } - x .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x })) - .rangeBands(xRange || [0, availableWidth], .1); + // Zero line + g.select(".nv-zeroLine line") + .attr("x1",0) + .attr("x2",availableWidth) + .attr("y1", y(0)) + .attr("y2", y(0)) + ; - 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]); + //============================================================ + // Event Handling/Dispatching (in chart's scope) + //------------------------------------------------------------ - //store old scales if they exist - x0 = x0 || x; - y0 = y0 || y.copy().range([y(0),y(0)]); + dispatch.on('tooltipShow', function(e) { + if (tooltips) showTooltip(e, that.parentNode); + }); - //------------------------------------------------------------ + //============================================================ - //------------------------------------------------------------ - // 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'); + renderWatch.renderEnd('discreteBar chart immediate'); + return chart; + } - gEnter.append('g').attr('class', 'nv-groups'); + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + 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(); + }); + //============================================================ - //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() - .transition() - .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 - .transition() - .style('stroke-opacity', 1) - .style('fill-opacity', .75); + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ - var bars = groups.selectAll('g.nv-bar') - .data(function(d) { return d.values }); + // expose chart's sub-components + chart.dispatch = dispatch; + chart.discretebar = discretebar; + chart.xAxis = xAxis; + chart.yAxis = yAxis; - bars.exit().remove(); + d3.rebind(chart, discretebar, 'x', 'y', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'id', 'showValues', 'valueFormat'); + chart.options = nv.utils.optionsFunc.bind(chart); - 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)) }) - .transition() - .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) - .transition() - .attr('width', x.rangeBand() * .9 / data.length); - bars.transition() - //.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)); + 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; + }; - 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) - }); + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - //store old scales for use in transitions on update - x0 = x.copy(); - y0 = y.copy(); + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + discretebar.color(color); + return chart; + }; - }); + chart.showXAxis = function(_) { + if (!arguments.length) return showXAxis; + showXAxis = _; + return chart; + }; - return chart; - } + chart.showYAxis = function(_) { + if (!arguments.length) return showYAxis; + showYAxis = _; + return chart; + }; + chart.rightAlignYAxis = function(_) { + if(!arguments.length) return rightAlignYAxis; + rightAlignYAxis = _; + yAxis.orient( (_) ? 'right' : 'left'); + return chart; + }; - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + chart.staggerLabels = function(_) { + if (!arguments.length) return staggerLabels; + staggerLabels = _; + return chart; + }; - chart.dispatch = dispatch; + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; - chart.options = nv.utils.optionsFunc.bind(chart); + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - chart.x = function(_) { - if (!arguments.length) return getX; - getX = _; - return chart; - }; + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; - chart.y = function(_) { - if (!arguments.length) return getY; - getY = _; - return chart; - }; - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + chart.transitionDuration = function(_) { + nv.deprecated('discreteBar.transitionDuration'); + return chart.duration(_); + }; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + discretebar.duration(duration); + xAxis.duration(duration); + yAxis.duration(duration); + return chart; + }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + //============================================================ - chart.xScale = function(_) { - if (!arguments.length) return x; - x = _; - return chart; - }; - chart.yScale = function(_) { - if (!arguments.length) return y; - y = _; return chart; - }; +} - chart.xDomain = function(_) { - if (!arguments.length) return xDomain; - xDomain = _; - 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') + ; - chart.yDomain = function(_) { - if (!arguments.length) return yDomain; - yDomain = _; - return chart; - }; + //============================================================ - chart.xRange = function(_) { - if (!arguments.length) return xRange; - xRange = _; - return chart; - }; - chart.yRange = function(_) { - if (!arguments.length) return yRange; - yRange = _; - return chart; - }; + //============================================================ + // Private Variables + //------------------------------------------------------------ - chart.forceY = function(_) { - if (!arguments.length) return forceY; - forceY = _; - return chart; - }; + var scale0; + var renderWatch = nv.utils.renderWatch(dispatch, duration); - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - return chart; - }; + //============================================================ - chart.id = function(_) { - if (!arguments.length) return id; - id = _; - return chart; - }; - chart.showValues = function(_) { - if (!arguments.length) return showValues; - showValues = _; - return chart; - }; + 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); - chart.valueFormat= function(_) { - if (!arguments.length) return valueFormat; - valueFormat = _; - return chart; - }; + //------------------------------------------------------------ + // Setup Scales - chart.rectClass= function(_) { - if (!arguments.length) return rectClass; - rectClass = _; - return chart; - }; - //============================================================ + scale0 = scale0 || scale; + //------------------------------------------------------------ - 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() - ; + //------------------------------------------------------------ + // Setup containers and skeleton of chart - 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 '

' + x + '

' + - '

' + y + '

' - } - , x - , y - , noData = "No Data Available." - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'beforeUpdate') - , transitionDuration = 250 - ; + 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'); - xAxis - .orient('bottom') - .highlightZero(false) - .showMaxMin(false) - .tickFormat(function(d) { return d }) - ; - yAxis - .orient((rightAlignYAxis) ? 'right' : 'left') - .tickFormat(d3.format(',.1f')) - ; + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') - //============================================================ + //------------------------------------------------------------ - //============================================================ - // Private Variables - //------------------------------------------------------------ + var distWrap = g.selectAll('g.nv-dist') + .data(function(d) { return d }, function(d) { return d.key }); - 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); + 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) }); - nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement); - }; + 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(); - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this), - that = this; + }); + renderWatch.renderEnd('distribution immediate'); + return chart; + } - var availableWidth = (width || parseInt(container.style('width')) || 960) - - margin.left - margin.right, - availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ + chart.options = nv.utils.optionsFunc.bind(chart); + chart.dispatch = dispatch; - chart.update = function() { - dispatch.beforeUpdate(); - container.transition().duration(transitionDuration).call(chart); - }; - chart.container = this; + 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; + }; - //------------------------------------------------------------ - // Display No Data message if there's nothing to show. + chart.axis = function(_) { + if (!arguments.length) return axis; + axis = _; + return chart; + }; - if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { - var noDataText = container.selectAll('.nv-noData').data([noData]); + chart.size = function(_) { + if (!arguments.length) return size; + size = _; + return chart; + }; - noDataText.enter().append('text') - .attr('class', 'nvd3 nv-noData') - .attr('dy', '-.7em') - .style('text-anchor', 'middle'); + chart.getData = function(_) { + if (!arguments.length) return getData; + getData = d3.functor(_); + return chart; + }; - noDataText - .attr('x', margin.left + availableWidth / 2) - .attr('y', margin.top + availableHeight / 2) - .text(function(d) { return d }); + 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; - } else { - container.selectAll('.nv-noData').remove(); - } + }; - //------------------------------------------------------------ + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + return chart; + }; + //============================================================ - //------------------------------------------------------------ - // Setup Scales + 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 = 960 + , height = 500 + , 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 + ; - x = discretebar.xScale(); - y = discretebar.yScale().clamp(true); + //============================================================ + var renderWatch = nv.utils.renderWatch(dispatch, 0); - //------------------------------------------------------------ + function chart(selection) { + selection.each(function(data) { + renderWatch.reset(); + var availableWidth = width - margin.left - margin.right, + availableHeight = height - margin.top - margin.bottom, + container = d3.select(this); + nv.utils.initSVG(container); + //------------------------------------------------------------ + // Setup Scales - //------------------------------------------------------------ - // Setup containers and skeleton of chart + x .domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) )) - 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'); + 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]); - 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'); + y .domain(yDomain || d3.extent(data[0].values.map(getY).concat(forceY) )) + .range(yRange || [availableHeight, 0]); - g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point - if (rightAlignYAxis) { - g.select(".nv-y.nv-axis") - .attr("transform", "translate(" + availableWidth + ",0)"); - } + 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]); + //------------------------------------------------------------ - //------------------------------------------------------------ - // Main Chart Component(s) - discretebar - .width(availableWidth) - .height(availableHeight); + //------------------------------------------------------------ + // 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'); - var barsWrap = g.select('.nv-barsWrap') - .datum(data.filter(function(d) { return !d.disabled })) + gEnter.append('g').attr('class', 'nv-bars'); - barsWrap.transition().call(discretebar); + 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-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 )); + 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); - //------------------------------------------------------------ - // Setup Axes + g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : ''); - if (showXAxis) { - xAxis - .scale(x) - .ticks( availableWidth / 100 ) - .tickSize(-availableHeight, 0); - g.select('.nv-x.nv-axis') - .attr('transform', 'translate(0,' + (y.range()[0] + ((discretebar.showValues() && y.domain()[0] < 0) ? 16 : 0)) + ')'); - //d3.transition(g.select('.nv-x.nv-axis')) - g.select('.nv-x.nv-axis').transition() - .call(xAxis); + var bars = wrap.select('.nv-bars').selectAll('.nv-bar') + .data(function(d) { return d }, function(d,i) {return getX(d,i)}); - var xTicks = g.select('.nv-x.nv-axis').selectAll('g'); + bars.exit().remove(); - 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( availableHeight / 36 ) - .tickSize( -availableWidth, 0); + var barsEnter = bars.enter().append('rect') + //.attr('class', function(d,i,j) { return (getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive') + ' nv-bar-' + j + '-' + i }) + .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 + }); - g.select('.nv-y.nv-axis').transition() - .call(yAxis); - } + }) + .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 ); - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ + 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)) }); - dispatch.on('tooltipShow', function(e) { - if (tooltips) showTooltip(e, that.parentNode); - }); + }); - //============================================================ + 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 + //------------------------------------------------------------ - return chart; - } + chart.dispatch = dispatch; - //============================================================ - // Event Handling/Dispatching (out of chart's scope) - //------------------------------------------------------------ + chart.options = nv.utils.optionsFunc.bind(chart); - discretebar.dispatch.on('elementMouseover.tooltip', function(e) { - e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; - dispatch.tooltipShow(e); - }); + chart.x = function(_) { + if (!arguments.length) return getX; + getX = _; + return chart; + }; - discretebar.dispatch.on('elementMouseout.tooltip', function(e) { - dispatch.tooltipHide(e); - }); + chart.y = function(_) { + if (!arguments.length) return getY; + getY = _; + return chart; + }; - dispatch.on('tooltipHide', function() { - if (tooltips) nv.tooltip.cleanup(); - }); + 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.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + chart.xScale = function(_) { + if (!arguments.length) return x; + x = _; + return chart; + }; - // expose chart's sub-components - chart.dispatch = dispatch; - chart.discretebar = discretebar; - chart.xAxis = xAxis; - chart.yAxis = yAxis; + chart.yScale = function(_) { + if (!arguments.length) return y; + y = _; + return chart; + }; - d3.rebind(chart, discretebar, 'x', 'y', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'id', 'showValues', 'valueFormat'); + chart.xDomain = function(_) { + if (!arguments.length) return xDomain; + xDomain = _; + return chart; + }; - chart.options = nv.utils.optionsFunc.bind(chart); + chart.yDomain = function(_) { + if (!arguments.length) return yDomain; + yDomain = _; + return chart; + }; - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + chart.xRange = function(_) { + if (!arguments.length) return xRange; + xRange = _; + return chart; + }; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + chart.yRange = function(_) { + if (!arguments.length) return yRange; + yRange = _; + return chart; + }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + chart.forceX = function(_) { + if (!arguments.length) return forceX; + forceX = _; + return chart; + }; - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - discretebar.color(color); - return chart; - }; + chart.forceY = function(_) { + if (!arguments.length) return forceY; + forceY = _; + return chart; + }; - chart.showXAxis = function(_) { - if (!arguments.length) return showXAxis; - showXAxis = _; - return chart; - }; + chart.padData = function(_) { + if (!arguments.length) return padData; + padData = _; + return chart; + }; - chart.showYAxis = function(_) { - if (!arguments.length) return showYAxis; - showYAxis = _; - return chart; - }; + chart.clipEdge = function(_) { + if (!arguments.length) return clipEdge; + clipEdge = _; + return chart; + }; - chart.rightAlignYAxis = function(_) { - if(!arguments.length) return rightAlignYAxis; - rightAlignYAxis = _; - yAxis.orient( (_) ? 'right' : 'left'); - return chart; - }; + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + return chart; + }; - chart.staggerLabels = function(_) { - if (!arguments.length) return staggerLabels; - staggerLabels = _; - return chart; - }; + chart.id = function(_) { + if (!arguments.length) return id; + id = _; + return chart; + }; - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; + chart.interactive = function(_) { + if(!arguments.length) return interactive; + interactive = false; + return chart; + }; - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + //============================================================ - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; - chart.transitionDuration = function(_) { - if (!arguments.length) return transitionDuration; - transitionDuration = _; return chart; - }; +} - //============================================================ +nv.models.historicalBarChart = function() { + "use strict"; + //============================================================ + // Public Variables with Default Settings + //------------------------------------------------------------ + + var bars = nv.models.historicalBar() + , xAxis = nv.models.axis() + , yAxis = nv.models.axis() + , legend = nv.models.legend() + , interactiveLayer = nv.interactiveGuideline() + ; - return chart; -} + 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 '

' + key + '

' + + '

' + y + ' at ' + x + '

' + } + , x + , y + , state = {} + , defaultState = null + , noData = 'No Data Available.' + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd') + , transitionDuration = 250 + ; -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 + xAxis + .orient('bottom') + .tickPadding(7) + ; + yAxis + .orient( (rightAlignYAxis) ? 'right' : 'left') ; - //============================================================ + //============================================================ - //============================================================ - // Private Variables - //------------------------------------------------------------ + //============================================================ + // Private Variables + //------------------------------------------------------------ - var scale0; + 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); - function chart(selection) { - 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.tooltip.show([left, top], content, null, null, offsetElement); + }; + var renderWatch = nv.utils.renderWatch(dispatch, 0); - //------------------------------------------------------------ - // Setup Scales + //============================================================ - scale0 = scale0 || scale; - //------------------------------------------------------------ + 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; - //------------------------------------------------------------ - // 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'); + chart.update = function() { container.transition().duration(transitionDuration).call(chart) }; + chart.container = this; - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') + //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. - var distWrap = g.selectAll('g.nv-dist') - .data(function(d) { return d }, function(d) { return d.key }); + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { + var noDataText = container.selectAll('.nv-noData').data([noData]); - 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) }); + noDataText.enter().append('text') + .attr('class', 'nvd3 nv-noData') + .attr('dy', '-.7em') + .style('text-anchor', 'middle'); - 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)) }) - distWrap.exit().selectAll('line.nv-dist' + axis) - .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); - dist - .transition() - .attr(axis + '1', function(d,i) { return scale(getData(d,i)) }) - .attr(axis + '2', function(d,i) { return scale(getData(d,i)) }) + 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(); + } - scale0 = scale.copy(); + //------------------------------------------------------------ - }); - return chart; - } + //------------------------------------------------------------ + // Setup Scales + x = bars.xScale(); + y = bars.yScale(); - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ - chart.options = nv.utils.optionsFunc.bind(chart); + //------------------------------------------------------------ - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + //------------------------------------------------------------ + // Setup containers and skeleton of chart - chart.axis = function(_) { - if (!arguments.length) return axis; - axis = _; - return 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'); - chart.size = function(_) { - if (!arguments.length) return size; - size = _; - return chart; - }; + 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'); - 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; - }; - //============================================================ + //------------------------------------------------------------ + // Legend + if (showLegend) { + legend.width(availableWidth); - 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 = 960 - , height = 500 - , 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') - , interactive = true - ; + 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) +')') + } - function chart(selection) { - selection.each(function(data) { - var availableWidth = width - margin.left - margin.right, - availableHeight = height - margin.top - margin.bottom, - container = d3.select(this); + //------------------------------------------------------------ + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - //------------------------------------------------------------ - // Setup Scales + if (rightAlignYAxis) { + g.select(".nv-y.nv-axis") + .attr("transform", "translate(" + availableWidth + ",0)"); + } - 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]); + //------------------------------------------------------------ + // Main Chart Component(s) - y .domain(yDomain || d3.extent(data[0].values.map(getY).concat(forceY) )) - .range(yRange || [availableHeight, 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); + } - // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point + 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 })); - 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]); + var barsWrap = g.select('.nv-barsWrap') + .datum(data.filter(function(d) { return !d.disabled })) - //------------------------------------------------------------ + barsWrap.transition().call(bars); + //------------------------------------------------------------ - //------------------------------------------------------------ - // Setup containers and skeleton of chart - var wrap = container.selectAll('g.nv-wrap.nv-historicalBar-' + id).data([data[0].values]); - var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBar-' + id); - var defsEnter = wrapEnter.append('defs'); - var gEnter = wrapEnter.append('g'); - var g = wrap.select('g'); + //------------------------------------------------------------ + // Setup Axes - gEnter.append('g').attr('class', 'nv-bars'); + if (showXAxis) { + xAxis + .scale(x) + .tickSize(-availableHeight, 0); - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + 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); + } + //------------------------------------------------------------ - container - .on('click', function(d,i) { - dispatch.chartClick({ - data: d, - index: i, - pos: d3.event, - id: id - }); - }); + //============================================================ + // Event Handling/Dispatching (in chart's scope) + //------------------------------------------------------------ - defsEnter.append('clipPath') - .attr('id', 'nv-chart-clip-path-' + id) - .append('rect'); + interactiveLayer.dispatch.on('elementMousemove', function(e) { + bars.clearHighlights() - wrap.select('#nv-chart-clip-path-' + id + ' rect') - .attr('width', availableWidth) - .attr('height', availableHeight); + 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) + }); + }); + + 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 + } + )(); - g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : ''); + interactiveLayer.renderGuideLine(pointXLocation); + }); + interactiveLayer.dispatch.on("elementMouseout",function(e) { + dispatch.tooltipHide(); + bars.clearHighlights(); + }); - var bars = wrap.select('.nv-bars').selectAll('.nv-bar') - .data(function(d) { return d }, function(d,i) {return getX(d,i)}); + legend.dispatch.on('legendClick', function(d,i) { + d.disabled = !d.disabled; - bars.exit().remove(); + 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); - var barsEnter = bars.enter().append('rect') - //.attr('class', function(d,i,j) { return (getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive') + ' nv-bar-' + j + '-' + i }) - .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 + selection.transition().call(chart); }); - }) - .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 + legend.dispatch.on('legendDblclick', function(d) { + //Double clicking should always enable current series, and disabled all others. + data.forEach(function(d) { + d.disabled = true; }); - 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 }) - .transition() - .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.transition() - .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)) }); + d.disabled = false; - }); + state.disabled = data.map(function(d) { return !!d.disabled }); + dispatch.stateChange(state); + chart.update(); + }); - 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.x = function(_) { - if (!arguments.length) return getX; - getX = _; - return chart; - }; + dispatch.on('tooltipShow', function(e) { + if (tooltips) showTooltip(e, that.parentNode); + }); - chart.y = function(_) { - if (!arguments.length) return getY; - getY = _; - return chart; - }; - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + dispatch.on('changeState', function(e) { - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + if (typeof e.disabled !== 'undefined') { + data.forEach(function(series,i) { + series.disabled = e.disabled[i]; + }); - chart.xScale = function(_) { - if (!arguments.length) return x; - x = _; - return chart; - }; + state.disabled = e.disabled; + } - chart.yScale = function(_) { - if (!arguments.length) return y; - y = _; - return chart; - }; + chart.update(); + }); - chart.xDomain = function(_) { - if (!arguments.length) return xDomain; - xDomain = _; - return chart; - }; + //============================================================ - chart.yDomain = function(_) { - if (!arguments.length) return yDomain; - yDomain = _; - return chart; - }; + }); - chart.xRange = function(_) { - if (!arguments.length) return xRange; - xRange = _; - return chart; - }; + renderWatch.renderEnd('historicalBarChart immediate'); + return chart; + } - chart.yRange = function(_) { - if (!arguments.length) return yRange; - yRange = _; - return chart; - }; - chart.forceX = function(_) { - if (!arguments.length) return forceX; - forceX = _; - return chart; - }; + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ - chart.forceY = function(_) { - if (!arguments.length) return forceY; - forceY = _; - return chart; - }; + bars.dispatch.on('elementMouseover.tooltip', function(e) { + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; + dispatch.tooltipShow(e); + }); - chart.padData = function(_) { - if (!arguments.length) return padData; - padData = _; - return chart; - }; + bars.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); + }); - chart.clipEdge = function(_) { - if (!arguments.length) return clipEdge; - clipEdge = _; - return chart; - }; + dispatch.on('tooltipHide', function() { + if (tooltips) nv.tooltip.cleanup(); + }); - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - return chart; - }; + //============================================================ - chart.id = function(_) { - if (!arguments.length) return id; - id = _; - return chart; - }; - chart.interactive = function(_) { - if(!arguments.length) return interactive; - interactive = false; - return chart; - }; + //============================================================ + // 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; + d3.rebind(chart, bars, 'defined', 'isArea', 'x', 'y', 'size', 'xScale', 'yScale', + 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id', 'interpolate','highlightPoint','clearHighlights'); - return chart; -} + chart.options = nv.utils.optionsFunc.bind(chart); -nv.models.historicalBarChart = function() { - "use strict"; - //============================================================ - // Public Variables with Default Settings - //------------------------------------------------------------ - - var bars = nv.models.historicalBar() - , xAxis = nv.models.axis() - , yAxis = nv.models.axis() - , legend = nv.models.legend() - ; + 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; + }; - 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 - , tooltips = true - , tooltip = function(key, x, y, e, graph) { - return '

' + key + '

' + - '

' + y + ' at ' + x + '

' - } - , x - , y - , state = {} - , defaultState = null - , noData = 'No Data Available.' - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') - , transitionDuration = 250 - ; + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - xAxis - .orient('bottom') - .tickPadding(7) - ; - yAxis - .orient( (rightAlignYAxis) ? 'right' : 'left') - ; + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + legend.color(color); + return chart; + }; + + chart.showLegend = function(_) { + if (!arguments.length) return showLegend; + showLegend = _; + return chart; + }; + + chart.showXAxis = function(_) { + if (!arguments.length) return showXAxis; + showXAxis = _; + return chart; + }; + + chart.showYAxis = function(_) { + if (!arguments.length) return showYAxis; + showYAxis = _; + return chart; + }; - //============================================================ + chart.rightAlignYAxis = function(_) { + if(!arguments.length) return rightAlignYAxis; + rightAlignYAxis = _; + yAxis.orient( (_) ? 'right' : 'left'); + return chart; + }; + + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - //============================================================ - // Private Variables - //------------------------------------------------------------ + chart.state = function(_) { + if (!arguments.length) return state; + state = _; + return chart; + }; - var showTooltip = function(e, offsetElement) { + chart.defaultState = function(_) { + if (!arguments.length) return defaultState; + defaultState = _; + return chart; + }; - // 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; - } - } + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; - 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); + chart.transitionDuration = function(_) { + if (!arguments.length) return transitionDuration; + transitionDuration = _; + return chart; + }; - nv.tooltip.show([left, top], content, null, null, offsetElement); - }; + chart.useInteractiveGuideline = function(_) { + if(!arguments.length) return useInteractiveGuideline; + useInteractiveGuideline = _; + if (_ === true) { + chart.interactive(false); + } + return chart; + }; - //============================================================ + //============================================================ - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this), - that = this; + 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') + ; - 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; + function chart(selection) { + selection.each(function(data) { + var availableWidth = width - margin.left - margin.right, + container = d3.select(this); + nv.utils.initSVG(container); - //set state.disabled - state.disabled = data.map(function(d) { return !!d.disabled }); + //------------------------------------------------------------ + // Setup containers and skeleton of chart - 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 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'); - //------------------------------------------------------------ - // Display noData message if there's nothing to show. + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - 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 }); + 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('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); + } - return chart; - } else { - container.selectAll('.nv-noData').remove(); - } + 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 - //------------------------------------------------------------ - // Setup Scales - x = bars.xScale(); - y = bars.yScale(); + 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; + }); + } - //------------------------------------------------------------ - // Setup containers and skeleton of chart + var xPositions = []; + for (var i = 0, curX = 0; i < seriesPerRow; i++) { + xPositions[i] = curX; + curX += columnWidths[i]; + } - 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'); + series + .attr('transform', function(d, i) { + return 'translate(' + xPositions[i % seriesPerRow] + ',' + (5 + Math.floor(i / seriesPerRow) * 20) + ')'; + }); - 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'); + //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 { - //------------------------------------------------------------ - // Legend + 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 (showLegend) { - legend.width(availableWidth); + if (width < margin.left + margin.right + xpos + length) { + newxpos = xpos = 5; + ypos += 20; + } - g.select('.nv-legendWrap') - .datum(data) - .call(legend); + newxpos += length; + if (newxpos > maxwidth) maxwidth = newxpos; - if ( margin.top != legend.height()) { - margin.top = legend.height(); - availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; - } + return 'translate(' + xpos + ',' + ypos + ')'; + }); - wrap.select('.nv-legendWrap') - .attr('transform', 'translate(0,' + (-margin.top) +')') - } + //position legend as far right as possible within the total width + g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')'); - //------------------------------------------------------------ + height = margin.top + margin.bottom + ypos + 15; - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + } - if (rightAlignYAxis) { - g.select(".nv-y.nv-axis") - .attr("transform", "translate(" + availableWidth + ",0)"); - } + }); + return chart; + } - //------------------------------------------------------------ - // Main Chart Component(s) - 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 })); + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ + chart.dispatch = dispatch; + chart.options = nv.utils.optionsFunc.bind(chart); - var barsWrap = g.select('.nv-barsWrap') - .datum(data.filter(function(d) { return !d.disabled })) + 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; + }; - barsWrap.transition().call(bars); + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - //------------------------------------------------------------ + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; + chart.key = function(_) { + if (!arguments.length) return getKey; + getKey = _; + return chart; + }; - //------------------------------------------------------------ - // Setup Axes + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + return chart; + }; - if (showXAxis) { - xAxis - .scale(x) - .tickSize(-availableHeight, 0); + chart.align = function(_) { + if (!arguments.length) return align; + align = _; + return chart; + }; - g.select('.nv-x.nv-axis') - .attr('transform', 'translate(0,' + y.range()[0] + ')'); - g.select('.nv-x.nv-axis') - .transition() - .call(xAxis); - } + chart.rightAlign = function(_) { + if (!arguments.length) return rightAlign; + rightAlign = _; + return chart; + }; - if (showYAxis) { - yAxis - .scale(y) - .ticks( availableHeight / 36 ) - .tickSize( -availableWidth, 0); + chart.updateState = function(_) { + if (!arguments.length) return updateState; + updateState = _; + return chart; + }; - g.select('.nv-y.nv-axis') - .transition() - .call(yAxis); - } - //------------------------------------------------------------ + chart.radioButtonMode = function(_) { + if (!arguments.length) return radioButtonMode; + radioButtonMode = _; + return chart; + }; + //============================================================ - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ - legend.dispatch.on('legendClick', function(d,i) { - d.disabled = !d.disabled; + return chart; +} - 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; - }); - } +nv.models.line = function() { + "use strict"; + //============================================================ + // Public Variables with Default Settings + //------------------------------------------------------------ - state.disabled = data.map(function(d) { return !!d.disabled }); - dispatch.stateChange(state); + var scatter = nv.models.scatter() + ; - selection.transition().call(chart); - }); + 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') + ; - 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; + scatter + .size(16) // default size + .sizeDomain([16,256]) //set to speed up calculation, needs to be unset if there is a custom size accessor + ; - 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); - }); + //============================================================ + // Private Variables + //------------------------------------------------------------ - dispatch.on('changeState', function(e) { + var x0, y0 //used to store previous scales + , renderWatch = nv.utils.renderWatch(dispatch, duration) + ; - if (typeof e.disabled !== 'undefined') { - data.forEach(function(series,i) { - series.disabled = e.disabled[i]; - }); + //============================================================ - state.disabled = e.disabled; - } - selection.call(chart); - }); + 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; - return chart; - } + //------------------------------------------------------------ - //============================================================ - // Event Handling/Dispatching (out of chart's scope) - //------------------------------------------------------------ + //------------------------------------------------------------ + // Setup containers and skeleton of chart - bars.dispatch.on('elementMouseover.tooltip', function(e) { - e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; - dispatch.tooltipShow(e); - }); + 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') - bars.dispatch.on('elementMouseout.tooltip', function(e) { - dispatch.tooltipHide(e); - }); + gEnter.append('g').attr('class', 'nv-groups'); + gEnter.append('g').attr('class', 'nv-scatterWrap'); - dispatch.on('tooltipHide', function() { - if (tooltips) nv.tooltip.cleanup(); - }); + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - //============================================================ + //------------------------------------------------------------ - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ - // expose chart's sub-components - chart.dispatch = dispatch; - chart.bars = bars; - chart.legend = legend; - chart.xAxis = xAxis; - chart.yAxis = yAxis; - d3.rebind(chart, bars, 'defined', 'isArea', 'x', 'y', 'size', 'xScale', 'yScale', - 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id', 'interpolate','highlightPoint','clearHighlights', 'interactive'); + scatter + .width(availableWidth) + .height(availableHeight) - chart.options = nv.utils.optionsFunc.bind(chart); + var scatterWrap = wrap.select('.nv-scatterWrap'); + //.datum(data); // Data automatically trickles down from the wrap - 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; - }; + scatterWrap.call(scatter); - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - legend.color(color); - return chart; - }; + defsEnter.append('clipPath') + .attr('id', 'nv-edge-clip-' + scatter.id()) + .append('rect'); - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + wrap.select('#nv-edge-clip-' + scatter.id() + ' rect') + .attr('width', availableWidth) + .attr('height', (availableHeight > 0) ? availableHeight : 0); - chart.showXAxis = function(_) { - if (!arguments.length) return showXAxis; - showXAxis = _; - return chart; - }; + g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : ''); + scatterWrap + .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : ''); - chart.showYAxis = function(_) { - if (!arguments.length) return showYAxis; - showYAxis = _; - return chart; - }; - chart.rightAlignYAxis = function(_) { - if(!arguments.length) return rightAlignYAxis; - rightAlignYAxis = _; - yAxis.orient( (_) ? 'right' : 'left'); - return chart; - }; - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; + 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); - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + groups.exit().remove(); - chart.state = function(_) { - if (!arguments.length) return state; - state = _; - return chart; - }; + 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); - chart.defaultState = function(_) { - if (!arguments.length) return defaultState; - defaultState = _; - return chart; - }; - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; - chart.transitionDuration = function(_) { - if (!arguments.length) return transitionDuration; - transitionDuration = _; - return chart; - }; + 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]) + }); - //============================================================ - return chart; -} -nv.models.indentedTree = function() { - "use strict"; - //============================================================ - // Public Variables with Default Settings - //------------------------------------------------------------ - - var margin = {top: 0, right: 0, bottom: 0, left: 0} //TODO: implement, maybe as margin on the containing div - , width = 960 - , height = 500 - , color = nv.utils.defaultColor() - , id = Math.floor(Math.random() * 10000) - , header = true - , filterZero = false - , noData = "No Data Available." - , childIndent = 20 - , columns = [{key:'key', label: 'Name', type:'text'}] //TODO: consider functions like chart.addColumn, chart.removeColumn, instead of a block like this - , tableClass = null - , iconOpen = 'images/grey-plus.png' //TODO: consider removing this and replacing with a '+' or '-' unless user defines images - , iconClose = 'images/grey-minus.png' - , dispatch = d3.dispatch('elementClick', 'elementDblclick', 'elementMouseover', 'elementMouseout') - , getUrl = function(d) { return d.url } - ; + 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))) }) + ); - var idx = 0; - function chart(selection) { - selection.each(function(data) { - var depth = 1, - container = d3.select(this); - var tree = d3.layout.tree() - .children(function(d) { return d.values }) - .size([height, childIndent]); //Not sure if this is needed now that the result is HTML + //store old scales for use in transitions on update + x0 = x.copy(); + y0 = y.copy(); - chart.update = function() { container.transition().duration(600).call(chart) }; + }); + renderWatch.renderEnd('line immediate'); + return chart; + } - //------------------------------------------------------------ - // Display No Data message if there's nothing to show. - if (!data[0]) data[0] = {key: noData}; + //============================================================ + // 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); }) + d3.rebind(chart, scatter, 'id', 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', + 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'useVoronoi', 'clipRadius', 'padData','highlightPoint','clearHighlights'); - var nodes = tree.nodes(data[0]); + chart.options = nv.utils.optionsFunc.bind(chart); - // nodes.map(function(d) { - // d.id = i++; - // }) + 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; + }; - //------------------------------------------------------------ - // Setup containers and skeleton of chart + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - var wrap = d3.select(this).selectAll('div').data([[nodes]]); - var wrapEnter = wrap.enter().append('div').attr('class', 'nvd3 nv-wrap nv-indentedtree'); - var tableEnter = wrapEnter.append('table'); - var table = wrap.select('table').attr('width', '100%').attr('class', tableClass); + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - //------------------------------------------------------------ + chart.x = function(_) { + if (!arguments.length) return getX; + getX = _; + scatter.x(_); + return chart; + }; + chart.y = function(_) { + if (!arguments.length) return getY; + getY = _; + scatter.y(_); + return chart; + }; - if (header) { - var thead = tableEnter.append('thead'); + chart.clipEdge = function(_) { + if (!arguments.length) return clipEdge; + clipEdge = _; + return chart; + }; - var theadRow1 = thead.append('tr'); + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + scatter.color(color); + return chart; + }; - columns.forEach(function(column) { - theadRow1 - .append('th') - .attr('width', column.width ? column.width : '10%') - .style('text-align', column.type == 'numeric' ? 'right' : 'left') - .append('span') - .text(column.label); - }); - } + chart.interpolate = function(_) { + if (!arguments.length) return interpolate; + interpolate = _; + return chart; + }; + chart.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return chart; + }; - var tbody = table.selectAll('tbody') - .data(function(d) { return d }); - tbody.enter().append('tbody'); + chart.isArea = function(_) { + if (!arguments.length) return isArea; + isArea = d3.functor(_); + return chart; + }; + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + scatter.duration(duration); + return chart; + }; + //============================================================ - //compute max generations - depth = d3.max(nodes, function(node) { return node.depth }); - tree.size([height, depth * childIndent]); //TODO: see if this is necessary at all + return chart; +} +nv.models.lineChart = function() { + "use strict"; - // Update the nodes… - var node = tbody.selectAll('tr') - // .data(function(d) { return d; }, function(d) { return d.id || (d.id == ++i)}); - .data(function(d) { return d.filter(function(d) { return (filterZero && !d.children) ? filterZero(d) : true; } )}, function(d,i) { return d.id || (d.id || ++idx)}); - //.style('display', 'table-row'); //TODO: see if this does anything + //============================================================ + // Public Variables with Default Settings + //------------------------------------------------------------ - node.exit().remove(); + var lines = nv.models.line() + , xAxis = nv.models.axis() + , yAxis = nv.models.axis() + , legend = nv.models.legend() + , interactiveLayer = nv.interactiveGuideline() + ; - node.select('img.nv-treeicon') - .attr('src', icon) - .classed('folded', folded); + 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 '

' + key + '

' + + '

' + y + ' at ' + x + '

' + } + , x + , y + , state = nv.utils.state() + , defaultState = null + , noData = 'No Data Available.' + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd') + , duration = 250 + ; - var nodeEnter = node.enter().append('tr'); + xAxis + .orient('bottom') + .tickPadding(7) + ; + yAxis + .orient((rightAlignYAxis) ? 'right' : 'left') + ; + //============================================================ - columns.forEach(function(column, index) { - var nodeName = nodeEnter.append('td') - .style('padding-left', function(d) { return (index ? 0 : d.depth * childIndent + 12 + (icon(d) ? 0 : 16)) + 'px' }, 'important') //TODO: check why I did the ternary here - .style('text-align', column.type == 'numeric' ? '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); - if (index == 0) { - nodeName.append('img') - .classed('nv-treeicon', true) - .classed('nv-folded', folded) - .attr('src', icon) - .style('width', '14px') - .style('height', '14px') - .style('padding', '0 1px') - .style('display', function(d) { return icon(d) ? 'inline-block' : 'none'; }) - .on('click', click); - } + nv.tooltip.show([left, top], content, null, null, offsetElement); + }; + var renderWatch = nv.utils.renderWatch(dispatch, duration); - nodeName.each(function(d) { - if (!index && getUrl(d)) - d3.select(this) - .append('a') - .attr('href',getUrl) - .attr('class', d3.functor(column.classes)) - .append('span') - else - d3.select(this) - .append('span') - - d3.select(this).select('span') - .attr('class', d3.functor(column.classes) ) - .text(function(d) { return column.format ? column.format(d) : - (d[column.key] || '-') }); - }); - - if (column.showCount) { - nodeName.append('span') - .attr('class', 'nv-childrenCount'); - - node.selectAll('span.nv-childrenCount').text(function(d) { - return ((d.values && d.values.length) || (d._values && d._values.length)) ? //If this is a parent - '(' + ((d.values && (d.values.filter(function(d) { return filterZero ? filterZero(d) : true; }).length)) //If children are in values check its children and filter - || (d._values && d._values.filter(function(d) { return filterZero ? filterZero(d) : true; }).length) //Otherwise, do the same, but with the other name, _values... - || 0) + ')' //This is the catch-all in case there are no children after a filter - : '' //If this is not a parent, just give an empty string - }); + var stateGetter = function(data) { + return function(){ + return { + active: data.map(function(d) { return !d.disabled }) + }; } + }; - // if (column.click) - // nodeName.select('span').on('click', column.click); - - }); + var stateSetter = function(data) { + return function(state) { + if (state.active !== undefined) + data.forEach(function(series,i) { + series.disabled = !state.active[i]; + }); + } + }; - node - .order() - .on('click', function(d) { - dispatch.elementClick({ - row: this, //TODO: decide whether or not this should be consistent with scatter/line events or should be an html link (a href) - data: d, - pos: [d.x, d.y] - }); - }) - .on('dblclick', function(d) { - dispatch.elementDblclick({ - row: this, - data: d, - pos: [d.x, d.y] - }); - }) - .on('mouseover', function(d) { - dispatch.elementMouseover({ - row: this, - data: d, - pos: [d.x, d.y] - }); - }) - .on('mouseout', function(d) { - dispatch.elementMouseout({ - row: this, - data: d, - pos: [d.x, d.y] - }); - }); + //============================================================ + 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; - // Toggle children on click. - function click(d, _, unshift) { - d3.event.stopPropagation(); - if(d3.event.shiftKey && !unshift) { - //If you shift-click, it'll toggle fold all the children, instead of itself - d3.event.shiftKey = false; - d.values && d.values.forEach(function(node){ - if (node.values || node._values) { - click(node, 0, true); + 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]; + } } - }); - return true; - } - if(!hasChildren(d)) { - //download file - //window.location.href = d.url; - return true; - } - if (d.values) { - d._values = d.values; - d.values = null; - } else { - d.values = d._values; - d._values = null; - } - chart.update(); - } + //------------------------------------------------------------ + // Display noData message if there's nothing to show. - function icon(d) { - return (d._values && d._values.length) ? iconOpen : (d.values && d.values.length) ? iconClose : ''; - } + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { + var noDataText = container.selectAll('.nv-noData').data([noData]); - function folded(d) { - return (d._values && d._values.length); - } + noDataText.enter().append('text') + .attr('class', 'nvd3 nv-noData') + .attr('dy', '-.7em') + .style('text-anchor', 'middle'); - function hasChildren(d) { - var values = d.values || d._values; + noDataText + .attr('x', margin.left + availableWidth / 2) + .attr('y', margin.top + availableHeight / 2) + .text(function(d) { return d }); - return (values && values.length); - } + return chart; + } else { + container.selectAll('.nv-noData').remove(); + } + //------------------------------------------------------------ - }); - return chart; - } + //------------------------------------------------------------ + // Setup Scales + x = lines.xScale(); + y = lines.yScale(); - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ - chart.options = nv.utils.optionsFunc.bind(chart); + //------------------------------------------------------------ - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + //------------------------------------------------------------ + // Setup containers and skeleton of chart - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return 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'); - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - scatter.color(color); - return chart; - }; + 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'); - chart.id = function(_) { - if (!arguments.length) return id; - id = _; - return chart; - }; + g.select("rect") + .attr("width",availableWidth) + .attr("height",(availableHeight > 0) ? availableHeight : 0); + //------------------------------------------------------------ + // Legend - chart.header = function(_) { - if (!arguments.length) return header; - header = _; - return chart; - }; + if (showLegend) { + legend.width(availableWidth); - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; + g.select('.nv-legendWrap') + .datum(data) + .call(legend); - chart.filterZero = function(_) { - if (!arguments.length) return filterZero; - filterZero = _; - return chart; - }; + if ( margin.top != legend.height()) { + margin.top = legend.height(); + availableHeight = (height || parseInt(container.style('height')) || 400) + - margin.top - margin.bottom; + } - chart.columns = function(_) { - if (!arguments.length) return columns; - columns = _; - return chart; - }; + wrap.select('.nv-legendWrap') + .attr('transform', 'translate(0,' + (-margin.top) +')') + } - chart.tableClass = function(_) { - if (!arguments.length) return tableClass; - tableClass = _; - return chart; - }; + //------------------------------------------------------------ - chart.iconOpen = function(_){ - if (!arguments.length) return iconOpen; - iconOpen = _; - return chart; - } + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - chart.iconClose = function(_){ - if (!arguments.length) return iconClose; - iconClose = _; - return chart; - } + if (rightAlignYAxis) { + g.select(".nv-y.nv-axis") + .attr("transform", "translate(" + availableWidth + ",0)"); + } - chart.getUrl = function(_){ - if (!arguments.length) return getUrl; - getUrl = _; - return chart; - } - - //============================================================ - - - 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') - ; + //------------------------------------------------------------ + // Main Chart Component(s) - //============================================================ - - - function chart(selection) { - selection.each(function(data) { - var availableWidth = width - margin.left - margin.right, - container = d3.select(this); - - - //------------------------------------------------------------ - // 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 }) - }); + + //------------------------------------------------------------ + //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); } - }); - 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('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(); - } - 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 + 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 })); - while ( legendWidth > availableWidth && seriesPerRow > 1 ) { - columnWidths = []; - seriesPerRow--; + var linesWrap = g.select('.nv-linesWrap') + .datum(data.filter(function(d) { return !d.disabled })) - for (var k = 0; k < seriesWidths.length; k++) { - if (seriesWidths[k] > (columnWidths[k % seriesPerRow] || 0) ) - columnWidths[k % seriesPerRow] = seriesWidths[k]; - } + linesWrap.call(lines); - 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) + ')'; - }); + //------------------------------------------------------------ + // Setup Axes - //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 + ')'); - } + if (showXAxis) { + xAxis + .scale(x) + .ticks( nv.utils.calcTicksX(availableWidth/100, data) ) + .tickSize(-availableHeight, 0); - height = margin.top + margin.bottom + (Math.ceil(seriesWidths.length / seriesPerRow) * 20); + g.select('.nv-x.nv-axis') + .attr('transform', 'translate(0,' + y.range()[0] + ')'); + g.select('.nv-x.nv-axis') + .call(xAxis); + } - } else { + if (showYAxis) { + yAxis + .scale(y) + .ticks( nv.utils.calcTicksY(availableHeight/36, data) ) + .tickSize( -availableWidth, 0); - 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; + g.select('.nv-y.nv-axis') + .call(yAxis); + } + //------------------------------------------------------------ - if (width < margin.left + margin.right + xpos + length) { - newxpos = xpos = 5; - ypos += 20; - } - newxpos += length; - if (newxpos > maxwidth) maxwidth = newxpos; + //============================================================ + // Event Handling/Dispatching (in chart's scope) + //------------------------------------------------------------ - return 'translate(' + xpos + ',' + ypos + ')'; + legend.dispatch.on('stateChange', function(newState) { + for (var key in newState) + state[key] = newState[key]; + dispatch.stateChange(state); + chart.update(); }); - //position legend as far right as possible within the total width - g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')'); + 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; + } - height = margin.top + margin.bottom + ypos + 15; + 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); - }); + }); - return chart; - } + 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); + }); - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + interactiveLayer.dispatch.on("elementMouseout",function(e) { + dispatch.tooltipHide(); + lines.clearHighlights(); + }); - chart.dispatch = dispatch; - chart.options = nv.utils.optionsFunc.bind(chart); + dispatch.on('tooltipShow', function(e) { + if (tooltips) showTooltip(e, that.parentNode); + }); - 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; - }; + dispatch.on('changeState', function(e) { - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) { + data.forEach(function(series,i) { + series.disabled = e.disabled[i]; + }); - chart.key = function(_) { - if (!arguments.length) return getKey; - getKey = _; - return chart; - }; + state.disabled = e.disabled; + } - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - return chart; - }; + chart.update(); + }); - chart.align = function(_) { - if (!arguments.length) return align; - align = _; - return chart; - }; + //============================================================ - chart.rightAlign = function(_) { - if (!arguments.length) return rightAlign; - rightAlign = _; - return chart; - }; + }); - chart.updateState = function(_) { - if (!arguments.length) return updateState; - updateState = _; - return chart; - }; + renderWatch.renderEnd('lineChart immediate'); + return chart; + } - chart.radioButtonMode = function(_) { - if (!arguments.length) return radioButtonMode; - radioButtonMode = _; - 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); + }); - return chart; -} + lines.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); + }); -nv.models.line = function() { - "use strict"; - //============================================================ - // Public Variables with Default Settings - //------------------------------------------------------------ + dispatch.on('tooltipHide', function() { + if (tooltips) nv.tooltip.cleanup(); + }); - 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 - ; - scatter - .size(16) // default size - .sizeDomain([16,256]) //set to speed up calculation, needs to be unset if there is a custom size accessor - ; + //============================================================ + // 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; + // DO NOT DELETE. This is currently overridden below + // until deprecated portions are removed. + chart.state = state; - //============================================================ + d3.rebind(chart, lines, 'defined', 'isArea', 'x', 'y', 'size', 'xScale', 'yScale', 'xDomain', 'yDomain', 'xRange', 'yRange' + , 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'useVoronoi','id', 'interpolate'); + chart.options = nv.utils.optionsFunc.bind(chart); - //============================================================ - // Private Variables - //------------------------------------------------------------ + 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; + }; - var x0, y0 //used to store previous scales - ; + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - //============================================================ + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + legend.color(color); + return chart; + }; - function chart(selection) { - selection.each(function(data) { - var availableWidth = width - margin.left - margin.right, - availableHeight = height - margin.top - margin.bottom, - container = d3.select(this); + chart.showLegend = function(_) { + if (!arguments.length) return showLegend; + showLegend = _; + return chart; + }; - //------------------------------------------------------------ - // Setup Scales + chart.showXAxis = function(_) { + if (!arguments.length) return showXAxis; + showXAxis = _; + return chart; + }; - x = scatter.xScale(); - y = scatter.yScale(); + chart.showYAxis = function(_) { + if (!arguments.length) return showYAxis; + showYAxis = _; + return chart; + }; - x0 = x0 || x; - y0 = y0 || y; + chart.rightAlignYAxis = function(_) { + if(!arguments.length) return rightAlignYAxis; + rightAlignYAxis = _; + yAxis.orient( (_) ? 'right' : 'left'); + return chart; + }; - //------------------------------------------------------------ + chart.useInteractiveGuideline = function(_) { + if(!arguments.length) return useInteractiveGuideline; + useInteractiveGuideline = _; + if (_ === true) { + chart.interactive(false); + chart.useVoronoi(false); + } + return chart; + }; + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; - //------------------------------------------------------------ - // Setup containers and skeleton of chart + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return 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') + // DEPRECATED + chart.state = function(_) { + nv.deprecated('lineChart.state'); + if (!arguments.length) return state; + state = _; + return chart; + }; + for (var key in state) { + chart.state[key] = state[key]; + } + // END DEPRECATED - gEnter.append('g').attr('class', 'nv-groups'); - gEnter.append('g').attr('class', 'nv-scatterWrap'); + chart.defaultState = function(_) { + if (!arguments.length) return defaultState; + defaultState = _; + return chart; + }; - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; - //------------------------------------------------------------ + chart.transitionDuration = function(_) { + nv.deprecated('lineChart.transitionDuration'); + return chart.duration(_); + }; + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + lines.duration(duration); + xAxis.duration(duration); + yAxis.duration(duration); + return chart; + }; + //============================================================ + return chart; +}; - scatter - .width(availableWidth) - .height(availableHeight) +nv.models.linePlusBarChart = function() { + "use strict"; + //============================================================ + // Public Variables with Default Settings + //------------------------------------------------------------ + + var lines = nv.models.line() + , bars = nv.models.historicalBar() + , xAxis = nv.models.axis() + , y1Axis = nv.models.axis() + , y2Axis = nv.models.axis() + , legend = nv.models.legend() + ; - var scatterWrap = wrap.select('.nv-scatterWrap'); - //.datum(data); // Data automatically trickles down from the wrap + var margin = {top: 30, right: 60, bottom: 50, left: 60} + , width = null + , height = null + , getX = function(d) { return d.x } + , getY = function(d) { return d.y } + , color = nv.utils.defaultColor() + , showLegend = true + , tooltips = true + , tooltip = function(key, x, y, e, graph) { + return '

' + key + '

' + + '

' + y + ' at ' + x + '

'; + } + , x + , y1 + , y2 + , state = nv.utils.state() + , defaultState = null + , noData = "No Data Available." + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') + ; - scatterWrap.transition().call(scatter); + bars + .padData(true) + ; + lines + .clipEdge(false) + .padData(true) + ; + xAxis + .orient('bottom') + .tickPadding(7) + .highlightZero(false) + ; + y1Axis + .orient('left') + ; + y2Axis + .orient('right') + ; + //============================================================ - defsEnter.append('clipPath') - .attr('id', 'nv-edge-clip-' + scatter.id()) - .append('rect'); + //============================================================ + // Private Variables + //------------------------------------------------------------ - wrap.select('#nv-edge-clip-' + scatter.id() + ' rect') - .attr('width', availableWidth) - .attr('height', availableHeight); + 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 = (e.series.bar ? y1Axis : y2Axis).tickFormat()(lines.y()(e.point, e.pointIndex)), + content = tooltip(e.series.key, x, y, e, chart); - g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : ''); - scatterWrap - .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : ''); + 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]; + }); + } + }; + //------------------------------------------------------------ - 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() - .transition() - .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 - .transition() - .style('stroke-opacity', 1) - .style('fill-opacity', .5); + 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, + availableHeight = (height || parseInt(container.style('height')) || 400) + - margin.top - margin.bottom; - 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 - .transition() - .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))) }) - ); - groups.exit().selectAll('path.nv-line') - .transition() - .attr('d', - d3.svg.line() - .interpolate(interpolate) - .defined(defined) - .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) }) - .y(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) }) - ); - linePaths - .transition() - .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(); + chart.update = function() { container.transition().call(chart); }; + // chart.container = this; - }); + state + .setter(stateSetter(data), chart.update) + .getter(stateGetter(data)) + .update(); - return chart; - } + // 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]; + } + } - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + //------------------------------------------------------------ + // Display No Data message if there's nothing to show. - chart.dispatch = scatter.dispatch; - chart.scatter = scatter; + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { + var noDataText = container.selectAll('.nv-noData').data([noData]); - d3.rebind(chart, scatter, 'id', 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', - 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'useVoronoi', 'clipRadius', 'padData','highlightPoint','clearHighlights'); + noDataText.enter().append('text') + .attr('class', 'nvd3 nv-noData') + .attr('dy', '-.7em') + .style('text-anchor', 'middle'); - chart.options = nv.utils.optionsFunc.bind(chart); + noDataText + .attr('x', margin.left + availableWidth / 2) + .attr('y', margin.top + availableHeight / 2) + .text(function(d) { return d }); - 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; - }; + return chart; + } else { + container.selectAll('.nv-noData').remove(); + } - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + //------------------------------------------------------------ - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; - chart.x = function(_) { - if (!arguments.length) return getX; - getX = _; - scatter.x(_); - return chart; - }; + //------------------------------------------------------------ + // Setup Scales - chart.y = function(_) { - if (!arguments.length) return getY; - getY = _; - scatter.y(_); - return chart; - }; + 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 - chart.clipEdge = function(_) { - if (!arguments.length) return clipEdge; - clipEdge = _; - return chart; - }; + //x = xAxis.scale(); + x = dataLines.filter(function(d) { return !d.disabled; }).length && dataLines.filter(function(d) { return !d.disabled; })[0].values.length ? lines.xScale() : bars.xScale(); + //x = dataLines.filter(function(d) { return !d.disabled; }).length ? lines.xScale() : bars.xScale(); //old code before change above + y1 = bars.yScale(); + y2 = lines.yScale(); - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - scatter.color(color); - return chart; - }; + //------------------------------------------------------------ - chart.interpolate = function(_) { - if (!arguments.length) return interpolate; - interpolate = _; - return chart; - }; + //------------------------------------------------------------ + // Setup containers and skeleton of chart - chart.defined = function(_) { - if (!arguments.length) return defined; - defined = _; - return chart; - }; + var wrap = d3.select(this).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'); - chart.isArea = function(_) { - if (!arguments.length) return isArea; - isArea = d3.functor(_); - return chart; - }; + gEnter.append('g').attr('class', 'nv-x nv-axis'); + gEnter.append('g').attr('class', 'nv-y1 nv-axis'); + gEnter.append('g').attr('class', 'nv-y2 nv-axis'); + gEnter.append('g').attr('class', 'nv-barsWrap'); + gEnter.append('g').attr('class', 'nv-linesWrap'); + gEnter.append('g').attr('class', 'nv-legendWrap'); - //============================================================ + //------------------------------------------------------------ - return chart; -} + //------------------------------------------------------------ + // Legend -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() - ; + if (showLegend) { + legend.width( availableWidth / 2 ); - 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 '

' + key + '

' + - '

' + y + ' at ' + x + '

' - } - , x - , y - , state = {} - , defaultState = null - , noData = 'No Data Available.' - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') - , transitionDuration = 250 - ; + 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); - xAxis - .orient('bottom') - .tickPadding(7) - ; - yAxis - .orient((rightAlignYAxis) ? 'right' : 'left') - ; + 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 / 2 ) + ',' + (-margin.top) +')'); + } + //------------------------------------------------------------ - //============================================================ - // 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); + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - nv.tooltip.show([left, top], content, null, null, offsetElement); - }; - //============================================================ + //------------------------------------------------------------ + // Main Chart Component(s) - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this), - that = this; + 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 && !data[i].bar })); - var availableWidth = (width || parseInt(container.style('width')) || 960) - - margin.left - margin.right, - availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; + 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 && data[i].bar })); - chart.update = function() { container.transition().duration(transitionDuration).call(chart) }; - chart.container = this; - //set state.disabled - state.disabled = data.map(function(d) { return !!d.disabled }); + var barsWrap = g.select('.nv-barsWrap') + .datum(dataBars.length ? dataBars : [{values:[]}]); + var linesWrap = g.select('.nv-linesWrap') + .datum(dataLines[0] && !dataLines[0].disabled ? dataLines : [{values:[]}] ); + //.datum(!dataLines[0].disabled ? dataLines : [{values:dataLines[0].values.map(function(d) { return [d[0], null] }) }] ); - 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]; - } - } + d3.transition(barsWrap).call(bars); + d3.transition(linesWrap).call(lines); - //------------------------------------------------------------ - // 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'); + //------------------------------------------------------------ + // Setup Axes - noDataText - .attr('x', margin.left + availableWidth / 2) - .attr('y', margin.top + availableHeight / 2) - .text(function(d) { return d }); + xAxis + .scale(x) + .ticks( nv.utils.calcTicksX(availableWidth/100, data) ) + .tickSize(-availableHeight, 0); - return chart; - } else { - container.selectAll('.nv-noData').remove(); - } + g.select('.nv-x.nv-axis') + .attr('transform', 'translate(0,' + y1.range()[0] + ')'); + d3.transition(g.select('.nv-x.nv-axis')) + .call(xAxis); - //------------------------------------------------------------ + y1Axis + .scale(y1) + .ticks( nv.utils.calcTicksY(availableHeight/36, data) ) + .tickSize(-availableWidth, 0); - //------------------------------------------------------------ - // Setup Scales + d3.transition(g.select('.nv-y1.nv-axis')) + .style('opacity', dataBars.length ? 1 : 0) + .call(y1Axis); - x = lines.xScale(); - y = lines.yScale(); - //------------------------------------------------------------ + y2Axis + .scale(y2) + .ticks( nv.utils.calcTicksY(availableHeight/36, data) ) + .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none + g.select('.nv-y2.nv-axis') + .style('opacity', dataLines.length ? 1 : 0) + .attr('transform', 'translate(' + availableWidth + ',0)'); + //.attr('transform', 'translate(' + x.range()[1] + ',0)'); - //------------------------------------------------------------ - // Setup containers and skeleton of chart + d3.transition(g.select('.nv-y2.nv-axis')) + .call(y2Axis); - 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); - //------------------------------------------------------------ - // Legend + //============================================================ + // Event Handling/Dispatching (in chart's scope) + //------------------------------------------------------------ - if (showLegend) { - legend.width(availableWidth); + legend.dispatch.on('stateChange', function(newState) { + for (var key in newState) + state[key] = newState[key]; + dispatch.stateChange(state); + chart.update(); + }); - g.select('.nv-legendWrap') - .datum(data) - .call(legend); + dispatch.on('tooltipShow', function(e) { + if (tooltips) showTooltip(e, that.parentNode); + }); - 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)"); - } - - //------------------------------------------------------------ - // Main Chart Component(s) - - - //------------------------------------------------------------ - //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.transition().call(lines); - - //------------------------------------------------------------ - - - //------------------------------------------------------------ - // Setup Axes - - if (showXAxis) { - xAxis - .scale(x) - .ticks( availableWidth / 100 ) - .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( availableHeight / 36 ) - .tickSize( -availableWidth, 0); - - g.select('.nv-y.nv-axis') - .transition() - .call(yAxis); - } - //------------------------------------------------------------ - - - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ - - legend.dispatch.on('stateChange', function(newState) { - state = newState; - 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) - }); - }); - - 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 - } - )(); + // Update chart from a state object passed to event handler + dispatch.on('changeState', function(e) { - interactiveLayer.renderGuideLine(pointXLocation); + if (typeof e.disabled !== 'undefined') { + data.forEach(function(series,i) { + series.disabled = e.disabled[i]; + }); - }); + state.disabled = e.disabled; + } - interactiveLayer.dispatch.on("elementMouseout",function(e) { - dispatch.tooltipHide(); - lines.clearHighlights(); - }); + 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]; - }); + return chart; + } - state.disabled = e.disabled; - } - chart.update(); - }); + //============================================================ + // 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); }); - return chart; - } + 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); + }); - //============================================================ - // Event Handling/Dispatching (out of chart's scope) - //------------------------------------------------------------ + dispatch.on('tooltipHide', function() { + if (tooltips) nv.tooltip.cleanup(); + }); - 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.bars = bars; + chart.xAxis = xAxis; + chart.y1Axis = y1Axis; + chart.y2Axis = y2Axis; + // DO NOT DELETE. This is currently overridden below + // until deprecated portions are removed. + chart.state = state; - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + d3.rebind(chart, lines, 'defined', 'size', 'clipVoronoi', 'interpolate'); + //TODO: consider rebinding x, y and some other stuff, and simply do soemthign lile bars.x(lines.x()), etc. + //d3.rebind(chart, lines, 'x', 'y', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id'); - // expose chart's sub-components - chart.dispatch = dispatch; - chart.lines = lines; - chart.legend = legend; - chart.xAxis = xAxis; - chart.yAxis = yAxis; - chart.interactiveLayer = interactiveLayer; + chart.options = nv.utils.optionsFunc.bind(chart); - d3.rebind(chart, lines, 'defined', 'isArea', 'x', 'y', 'size', 'xScale', 'yScale', 'xDomain', 'yDomain', 'xRange', 'yRange' - , 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'useVoronoi','id', 'interpolate'); + chart.x = function(_) { + if (!arguments.length) return getX; + getX = _; + lines.x(_); + bars.x(_); + return chart; + }; - chart.options = nv.utils.optionsFunc.bind(chart); + chart.y = function(_) { + if (!arguments.length) return getY; + getY = _; + lines.y(_); + bars.y(_); + return chart; + }; - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + chart.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.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - legend.color(color); - return chart; - }; + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + legend.color(color); + return chart; + }; - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + chart.showLegend = function(_) { + if (!arguments.length) return showLegend; + showLegend = _; + return chart; + }; - chart.showXAxis = function(_) { - if (!arguments.length) return showXAxis; - showXAxis = _; - return chart; - }; + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; - chart.showYAxis = function(_) { - if (!arguments.length) return showYAxis; - showYAxis = _; - return chart; - }; + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - chart.rightAlignYAxis = function(_) { - if(!arguments.length) return rightAlignYAxis; - rightAlignYAxis = _; - yAxis.orient( (_) ? 'right' : 'left'); - return chart; - }; - - chart.useInteractiveGuideline = function(_) { - if(!arguments.length) return useInteractiveGuideline; - useInteractiveGuideline = _; - if (_ === true) { - chart.interactive(false); - chart.useVoronoi(false); + // DEPRECATED + chart.state = function(_) { + nv.deprecated('linePlusBarChart.state'); + if (!arguments.length) return state; + state = _; + return chart; + }; + for (var key in state) { + chart.state[key] = state[key]; } - return chart; - }; + // END DEPRECATED - chart.tooltip = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; - - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; - - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; - - chart.state = function(_) { - if (!arguments.length) return state; - state = _; - return chart; - }; + chart.defaultState = function(_) { + if (!arguments.length) return defaultState; + defaultState = _; + return chart; + }; - chart.defaultState = function(_) { - if (!arguments.length) return defaultState; - defaultState = _; - return chart; - }; + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; + //============================================================ - chart.transitionDuration = function(_) { - if (!arguments.length) return transitionDuration; - transitionDuration = _; return chart; - }; - - //============================================================ - +}; +nv.models.linePlusBarWithFocusChart = 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() + ; - return chart; -} + var margin = {top: 30, right: 30, bottom: 30, left: 60} + , margin2 = {top: 0, right: 30, bottom: 20, left: 60} + , width = null + , height = null + , height2 = 100 + , getX = function(d) { return d.x } + , getY = function(d) { return d.y } + , color = nv.utils.defaultColor() + , showLegend = true + , extent + , brushExtent = null + , tooltips = true + , tooltip = function(key, x, y, e, graph) { + return '

' + key + '

' + + '

' + y + ' at ' + x + '

'; + } + , 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 + ; -nv.models.linePlusBarChart = function() { - "use strict"; - //============================================================ - // Public Variables with Default Settings - //------------------------------------------------------------ - - var lines = nv.models.line() - , bars = nv.models.historicalBar() - , xAxis = nv.models.axis() - , y1Axis = nv.models.axis() - , y2Axis = nv.models.axis() - , legend = nv.models.legend() + lines + .clipEdge(true) ; - - var margin = {top: 30, right: 60, bottom: 50, left: 60} - , width = null - , height = null - , getX = function(d) { return d.x } - , getY = function(d) { return d.y } - , color = nv.utils.defaultColor() - , showLegend = true - , tooltips = true - , tooltip = function(key, x, y, e, graph) { - return '

' + key + '

' + - '

' + y + ' at ' + x + '

'; - } - , x - , y1 - , y2 - , state = {} - , defaultState = null - , noData = "No Data Available." - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') + lines2 + .interactive(false) ; - - bars - .padData(true) + xAxis + .orient('bottom') + .tickPadding(5) ; - lines - .clipEdge(false) - .padData(true) + y1Axis + .orient('left') ; - xAxis - .orient('bottom') - .tickPadding(7) - .highlightZero(false) + y2Axis + .orient('right') ; - y1Axis - .orient('left') + x2Axis + .orient('bottom') + .tickPadding(5) ; - y2Axis - .orient('right') + y3Axis + .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 = (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); - } + y4Axis + .orient('right') ; - //------------------------------------------------------------ - - + //============================================================ - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this), - 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; + //============================================================ + // Private Variables + //------------------------------------------------------------ - chart.update = function() { container.transition().call(chart); }; - // chart.container = this; + 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); - //set state.disabled - state.disabled = data.map(function(d) { return !!d.disabled }); + nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement); + }; - if (!defaultState) { - var key; - defaultState = {}; - for (key in state) { - if (state[key] instanceof Array) - defaultState[key] = state[key].slice(0); - else - defaultState[key] = state[key]; + var stateGetter = function(data) { + return function(){ + return { + active: data.map(function(d) { return !d.disabled }) + }; } - } - - //------------------------------------------------------------ - // 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]); + var stateSetter = function(data) { + return function(state) { + if (state.active !== undefined) + data.forEach(function(series,i) { + series.disabled = !state.active[i]; + }); + } + }; - 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(); - } - //------------------------------------------------------------ + 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; - //------------------------------------------------------------ - // Setup Scales + state + .setter(stateSetter(data), chart.update) + .getter(stateGetter(data)) + .update(); - 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 + // DEPRECATED set state.disableddisabled + state.disabled = data.map(function(d) { return !!d.disabled }); - //x = xAxis.scale(); - x = dataLines.filter(function(d) { return !d.disabled; }).length && dataLines.filter(function(d) { return !d.disabled; })[0].values.length ? lines.xScale() : bars.xScale(); - //x = dataLines.filter(function(d) { return !d.disabled; }).length ? lines.xScale() : bars.xScale(); //old code before change above - y1 = bars.yScale(); - y2 = lines.yScale(); + 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. - //------------------------------------------------------------ - // Setup containers and skeleton of chart + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { + var noDataText = container.selectAll('.nv-noData').data([noData]); - var wrap = d3.select(this).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'); + noDataText.enter().append('text') + .attr('class', 'nvd3 nv-noData') + .attr('dy', '-.7em') + .style('text-anchor', 'middle'); - gEnter.append('g').attr('class', 'nv-x nv-axis'); - gEnter.append('g').attr('class', 'nv-y1 nv-axis'); - gEnter.append('g').attr('class', 'nv-y2 nv-axis'); - gEnter.append('g').attr('class', 'nv-barsWrap'); - gEnter.append('g').attr('class', 'nv-linesWrap'); - gEnter.append('g').attr('class', 'nv-legendWrap'); + 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(); + } + //------------------------------------------------------------ - //------------------------------------------------------------ - // Legend - if (showLegend) { - legend.width( availableWidth / 2 ); + //------------------------------------------------------------ + // Setup Scales - 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); + var dataBars = data.filter(function(d) { return !d.disabled && d.bar }); + var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240 - if ( margin.top != legend.height()) { - margin.top = legend.height(); - availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; - } + 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) } + }) + }); - g.select('.nv-legendWrap') - .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')'); - } + 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]); - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + //------------------------------------------------------------ - //------------------------------------------------------------ - // Main Chart Component(s) + //------------------------------------------------------------ + // Setup containers and skeleton of chart - 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 && !data[i].bar })) + 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'); - 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 && data[i].bar })) + 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-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'); + 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'); - var barsWrap = g.select('.nv-barsWrap') - .datum(dataBars.length ? dataBars : [{values:[]}]) - var linesWrap = g.select('.nv-linesWrap') - .datum(dataLines[0] && !dataLines[0].disabled ? dataLines : [{values:[]}] ); - //.datum(!dataLines[0].disabled ? dataLines : [{values:dataLines[0].values.map(function(d) { return [d[0], null] }) }] ); + //------------------------------------------------------------ - d3.transition(barsWrap).call(bars); - d3.transition(linesWrap).call(lines); - //------------------------------------------------------------ + //------------------------------------------------------------ + // Legend + if (showLegend) { + legend.width( availableWidth / 2 ); - //------------------------------------------------------------ - // Setup Axes + 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); - xAxis - .scale(x) - .ticks( availableWidth / 100 ) - .tickSize(-availableHeight, 0); + if ( margin.top != legend.height()) { + margin.top = legend.height(); + availableHeight1 = (height || parseInt(container.style('height')) || 400) + - margin.top - margin.bottom - height2; + } - g.select('.nv-x.nv-axis') - .attr('transform', 'translate(0,' + y1.range()[0] + ')'); - d3.transition(g.select('.nv-x.nv-axis')) - .call(xAxis); + g.select('.nv-legendWrap') + .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')'); + } + //------------------------------------------------------------ - y1Axis - .scale(y1) - .ticks( availableHeight / 36 ) - .tickSize(-availableWidth, 0); - d3.transition(g.select('.nv-y1.nv-axis')) - .style('opacity', dataBars.length ? 1 : 0) - .call(y1Axis); + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - y2Axis - .scale(y2) - .ticks( availableHeight / 36 ) - .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none + //------------------------------------------------------------ + // Context Components - g.select('.nv-y2.nv-axis') - .style('opacity', dataLines.length ? 1 : 0) - .attr('transform', 'translate(' + availableWidth + ',0)'); - //.attr('transform', 'translate(' + x.range()[1] + ',0)'); + 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 })); - d3.transition(g.select('.nv-y2.nv-axis')) - .call(y2Axis); + 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:[]}]); - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ + g.select('.nv-context') + .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')') - legend.dispatch.on('stateChange', function(newState) { - state = newState; - dispatch.stateChange(state); - chart.update(); - }); + bars2Wrap.transition().call(bars2); + lines2Wrap.transition().call(lines2); - 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]; - }); + //------------------------------------------------------------ + // Setup Brush - state.disabled = e.disabled; - } + brush + .x(x2) + .on('brush', onBrush); - chart.update(); - }); + 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); - return chart; - } + 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 (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); - }); + //------------------------------------------------------------ + // Setup Secondary (Context) Axes - lines.dispatch.on('elementMouseout.tooltip', function(e) { - dispatch.tooltipHide(e); - }); + x2Axis + .ticks( nv.utils.calcTicksX(availableWidth/100, data) ) + .tickSize(-availableHeight2, 0); - bars.dispatch.on('elementMouseover.tooltip', function(e) { - e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; - dispatch.tooltipShow(e); - }); + 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); - bars.dispatch.on('elementMouseout.tooltip', function(e) { - dispatch.tooltipHide(e); - }); - dispatch.on('tooltipHide', function() { - if (tooltips) nv.tooltip.cleanup(); - }); + y3Axis + .scale(y3) + .ticks( availableHeight2 / 36 ) + .tickSize( -availableWidth, 0); - //============================================================ + g.select('.nv-context .nv-y1.nv-axis') + .style('opacity', dataBars.length ? 1 : 0) + .attr('transform', 'translate(0,' + x2.range()[0] + ')'); + g.select('.nv-context .nv-y1.nv-axis').transition() + .call(y3Axis); - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ - // expose chart's sub-components - chart.dispatch = dispatch; - chart.legend = legend; - chart.lines = lines; - chart.bars = bars; - chart.xAxis = xAxis; - chart.y1Axis = y1Axis; - chart.y2Axis = y2Axis; + y4Axis + .scale(y4) + .ticks( availableHeight2 / 36 ) + .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none - d3.rebind(chart, lines, 'defined', 'size', 'clipVoronoi', 'interpolate'); - //TODO: consider rebinding x, y and some other stuff, and simply do soemthign lile bars.x(lines.x()), etc. - //d3.rebind(chart, lines, 'x', 'y', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id'); + g.select('.nv-context .nv-y2.nv-axis') + .style('opacity', dataLines.length ? 1 : 0) + .attr('transform', 'translate(' + x2.range()[1] + ',0)'); - chart.options = nv.utils.optionsFunc.bind(chart); + g.select('.nv-context .nv-y2.nv-axis').transition() + .call(y4Axis); - chart.x = function(_) { - if (!arguments.length) return getX; - getX = _; - lines.x(_); - bars.x(_); - return chart; - }; + //------------------------------------------------------------ - chart.y = function(_) { - if (!arguments.length) return getY; - getY = _; - lines.y(_); - bars.y(_); - return chart; - }; - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + //============================================================ + // Event Handling/Dispatching (in chart's scope) + //------------------------------------------------------------ - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + legend.dispatch.on('stateChange', function(newState) { + for (var key in newState) + state[key] = newState[key]; + dispatch.stateChange(state); + chart.update(); + }); - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + dispatch.on('tooltipShow', function(e) { + if (tooltips) showTooltip(e, that.parentNode); + }); - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - legend.color(color); - return chart; - }; + // 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(); + }); - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + //============================================================ + + + //============================================================ + // 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); + } - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + 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); + }); + } - chart.state = function(_) { - if (!arguments.length) return state; - state = _; - return chart; - }; - chart.defaultState = function(_) { - if (!arguments.length) return defaultState; - defaultState = _; - return chart; - }; + function onBrush() { + brushExtent = brush.empty() ? null : brush.extent(); + extent = brush.empty() ? x2.domain() : brush.extent(); - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; - //============================================================ + dispatch.brush({extent: extent, brush: brush}); + updateBrushBG(); - 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 '

' + key + '

' + - '

' + y + ' at ' + x + '

' - } - , noData = "No Data Available." - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'brush') - , transitionDuration = 250 - ; + //------------------------------------------------------------ + // Prepare Main (Focus) Bars and Lines - lines - .clipEdge(true) - ; - lines2 - .interactive(false) - ; - xAxis - .orient('bottom') - .tickPadding(5) - ; - yAxis - .orient('left') - ; - x2Axis - .orient('bottom') - .tickPadding(5) - ; - y2Axis - .orient('left') - ; - //============================================================ + 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 })); - //============================================================ - // Private Variables - //------------------------------------------------------------ + 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 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); + 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]; + }) + } + }) + ); - nv.tooltip.show([left, top], content, null, null, offsetElement); - }; + 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]; + }) + } + }) + ); - //============================================================ + //------------------------------------------------------------ - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this), - that = this; + //------------------------------------------------------------ + // Update Main (Focus) X Axis - 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; + if (dataBars.length) { + x = bars.xScale(); + } else { + x = lines.xScale(); + } - chart.update = function() { container.transition().duration(transitionDuration).call(chart) }; - chart.container = this; + xAxis + .scale(x) + .ticks( nv.utils.calcTicksX(availableWidth/100, data) ) + .tickSize(-availableHeight1, 0); + xAxis.domain([Math.ceil(extent[0]), Math.floor(extent[1])]); - //------------------------------------------------------------ - // Display No Data message if there's nothing to show. + g.select('.nv-x.nv-axis').transition().duration(transitionDuration) + .call(xAxis); + //------------------------------------------------------------ - 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'); + //------------------------------------------------------------ + // Update Main (Focus) Bars and Lines - noDataText - .attr('x', margin.left + availableWidth / 2) - .attr('y', margin.top + availableHeight1 / 2) - .text(function(d) { return d }); + focusBarsWrap.transition().duration(transitionDuration).call(bars); + focusLinesWrap.transition().duration(transitionDuration).call(lines); - return chart; - } else { - container.selectAll('.nv-noData').remove(); - } + //------------------------------------------------------------ - //------------------------------------------------------------ + //------------------------------------------------------------ + // Setup and Update Main (Focus) Y Axes - //------------------------------------------------------------ - // Setup Scales + g.select('.nv-focus .nv-x.nv-axis') + .attr('transform', 'translate(0,' + y1.range()[0] + ')'); - x = lines.xScale(); - y = lines.yScale(); - x2 = lines2.xScale(); - y2 = lines2.yScale(); - //------------------------------------------------------------ + y1Axis + .scale(y1) + .ticks( nv.utils.calcTicksY(availableHeight1/36, data) ) + .tickSize(-availableWidth, 0); + g.select('.nv-focus .nv-y1.nv-axis') + .style('opacity', dataBars.length ? 1 : 0); - //------------------------------------------------------------ - // 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'); + 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 - gEnter.append('g').attr('class', 'nv-legendWrap'); + g.select('.nv-focus .nv-y2.nv-axis') + .style('opacity', dataLines.length ? 1 : 0) + .attr('transform', 'translate(' + x.range()[1] + ',0)'); - 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'); + 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); + } - 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'); + //============================================================ - //------------------------------------------------------------ + onBrush(); + }); - //------------------------------------------------------------ - // Legend + return chart; + } - if (showLegend) { - legend.width(availableWidth); - g.select('.nv-legendWrap') - .datum(data) - .call(legend); + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ - if ( margin.top != legend.height()) { - margin.top = legend.height(); - availableHeight1 = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom - height2; - } + lines.dispatch.on('elementMouseover.tooltip', function(e) { + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; + dispatch.tooltipShow(e); + }); - g.select('.nv-legendWrap') - .attr('transform', 'translate(0,' + (-margin.top) +')') - } + 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); + }); - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + 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; + + // DO NOT DELETE. This is currently overridden below + // until deprecated portions are removed. + chart.state = state; + + d3.rebind(chart, lines, 'defined', 'size', 'clipVoronoi', 'interpolate'); + //TODO: consider rebinding x, y and some other stuff, and simply do soemthign lile bars.x(lines.x()), etc. + //d3.rebind(chart, lines, 'x', 'y', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id'); + + chart.options = nv.utils.optionsFunc.bind(chart); + + chart.x = function(_) { + if (!arguments.length) return getX; + getX = _; + lines.x(_); + bars.x(_); + return chart; + }; - //------------------------------------------------------------ - // Main Chart Component(s) + chart.y = function(_) { + if (!arguments.length) return getY; + getY = _; + lines.y(_); + bars.y(_); + return chart; + }; - 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; - }) - ); + 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; + }; - 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; - }) - ); + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - g.select('.nv-context') - .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')') + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - var contextLinesWrap = g.select('.nv-context .nv-linesWrap') - .datum(data.filter(function(d) { return !d.disabled })) + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + legend.color(color); + return chart; + }; - d3.transition(contextLinesWrap).call(lines2); + chart.showLegend = function(_) { + if (!arguments.length) return showLegend; + showLegend = _; + return chart; + }; - //------------------------------------------------------------ + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - /* - var focusLinesWrap = g.select('.nv-focus .nv-linesWrap') - .datum(data.filter(function(d) { return !d.disabled })) + // DEPRECATED + chart.state = function(_) { + nv.deprecated('linePlusBarWithFocusChart.state'); + if (!arguments.length) return state; + state = _; + return chart; + }; + for (var key in state) { + chart.state[key] = state[key]; + } + // END DEPRECATED - d3.transition(focusLinesWrap).call(lines); - */ + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; + chart.brushExtent = function(_) { + if (!arguments.length) return brushExtent; + brushExtent = _; + return chart; + }; - //------------------------------------------------------------ - // Setup Main (Focus) Axes - xAxis - .scale(x) - .ticks( availableWidth / 100 ) - .tickSize(-availableHeight1, 0); + //============================================================ - yAxis - .scale(y) - .ticks( availableHeight1 / 36 ) - .tickSize( -availableWidth, 0); - g.select('.nv-focus .nv-x.nv-axis') - .attr('transform', 'translate(0,' + availableHeight1 + ')'); + 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 '

' + key + '

' + + '

' + y + ' at ' + x + '

' + } + , 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') + ; + //============================================================ - //------------------------------------------------------------ - // Setup Brush - brush - .x(x2) - .on('brush', function() { - //When brushing, turn off transitions because chart needs to change immediately. - var oldTransition = chart.transitionDuration(); - chart.transitionDuration(0); - onBrush(); - chart.transitionDuration(oldTransition); - }); + //============================================================ + // Private Variables + //------------------------------------------------------------ - if (brushExtent) brush.extent(brushExtent); + 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); - var brushBG = g.select('.nv-brushBackground').selectAll('g') - .data([brushExtent || brush.extent()]) + nv.tooltip.show([left, top], content, null, null, offsetElement); + }; - var brushBGenter = brushBG.enter() - .append('g'); + var stateGetter = function(data) { + return function(){ + return { + active: data.map(function(d) { return !d.disabled }) + }; + } + }; - brushBGenter.append('rect') - .attr('class', 'left') - .attr('x', 0) - .attr('y', 0) - .attr('height', availableHeight2); + var stateSetter = function(data) { + return function(state) { + if (state.active !== undefined) + data.forEach(function(series,i) { + series.disabled = !state.active[i]; + }); + } + }; - brushBGenter.append('rect') - .attr('class', 'right') - .attr('x', 0) - .attr('y', 0) - .attr('height', availableHeight2); + //============================================================ + + + 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]; + } + } - 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); + //------------------------------------------------------------ + // Display No Data message if there's nothing to show. - onBrush(); + 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 }); - //------------------------------------------------------------ - // Setup Secondary (Context) Axes + return chart; + } else { + container.selectAll('.nv-noData').remove(); + } - x2Axis - .scale(x2) - .ticks( availableWidth / 100 ) - .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); + //------------------------------------------------------------ + // Setup Scales - y2Axis - .scale(y2) - .ticks( availableHeight2 / 36 ) - .tickSize( -availableWidth, 0); + x = lines.xScale(); + y = lines.yScale(); + x2 = lines2.xScale(); + y2 = lines2.yScale(); - 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] + ')'); - //------------------------------------------------------------ + //------------------------------------------------------------ + // 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'); - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ + gEnter.append('g').attr('class', 'nv-legendWrap'); - legend.dispatch.on('stateChange', function(newState) { - chart.update(); - }); + 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'); - dispatch.on('tooltipShow', function(e) { - if (tooltips) showTooltip(e, that.parentNode); - }); + 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'); - //============================================================ + //------------------------------------------------------------ - //============================================================ - // Functions - //------------------------------------------------------------ + //------------------------------------------------------------ + // Legend - // 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); - } + if (showLegend) { + legend.width(availableWidth); + g.select('.nv-legendWrap') + .datum(data) + .call(legend); - 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); - }); - } + 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) +')') + } - 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}); + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - updateBrushBG(); + //------------------------------------------------------------ + // Main Chart Component(s) - // 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, - values: d.values.filter(function(d,i) { - return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1]; + 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; }) - } - }) ); - 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; - } + 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; + }) + ); - //============================================================ - // 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); - }); + g.select('.nv-context') + .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')') - lines.dispatch.on('elementMouseout.tooltip', function(e) { - dispatch.tooltipHide(e); - }); + var contextLinesWrap = g.select('.nv-context .nv-linesWrap') + .datum(data.filter(function(d) { return !d.disabled })) - dispatch.on('tooltipHide', function() { - if (tooltips) nv.tooltip.cleanup(); - }); + d3.transition(contextLinesWrap).call(lines2); - //============================================================ + //------------------------------------------------------------ - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + /* + var focusLinesWrap = g.select('.nv-focus .nv-linesWrap') + .datum(data.filter(function(d) { return !d.disabled })) - // 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; + d3.transition(focusLinesWrap).call(lines); + */ - d3.rebind(chart, lines, 'defined', 'isArea', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id'); - chart.options = nv.utils.optionsFunc.bind(chart); + //------------------------------------------------------------ + // Setup Main (Focus) Axes - chart.x = function(_) { - if (!arguments.length) return lines.x; - lines.x(_); - lines2.x(_); - return chart; - }; + xAxis + .scale(x) + .ticks( nv.utils.calcTicksX(availableWidth/100, data) ) + .tickSize(-availableHeight1, 0); - chart.y = function(_) { - if (!arguments.length) return lines.y; - lines.y(_); - lines2.y(_); - return chart; - }; - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + yAxis + .scale(y) + .ticks( nv.utils.calcTicksY(availableHeight1/36, data) ) + .tickSize( -availableWidth, 0); - chart.margin2 = function(_) { - if (!arguments.length) return margin2; - margin2 = _; - return chart; - }; + g.select('.nv-focus .nv-x.nv-axis') + .attr('transform', 'translate(0,' + availableHeight1 + ')'); - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + //------------------------------------------------------------ - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; - chart.height2 = function(_) { - if (!arguments.length) return height2; - height2 = _; - return chart; - }; + //------------------------------------------------------------ + // Setup Brush - chart.color = function(_) { - if (!arguments.length) return color; - color =nv.utils.getColor(_); - legend.color(color); - return chart; - }; + brush + .x(x2) + .on('brush', function() { + //When brushing, turn off transitions because chart needs to change immediately. + var oldTransition = chart.transitionDuration(); + chart.transitionDuration(0); + onBrush(); + chart.transitionDuration(oldTransition); + }); - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + if (brushExtent) brush.extent(brushExtent); - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; + var brushBG = g.select('.nv-brushBackground').selectAll('g') + .data([brushExtent || brush.extent()]) - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + var brushBGenter = brushBG.enter() + .append('g'); - chart.interpolate = function(_) { - if (!arguments.length) return lines.interpolate(); - lines.interpolate(_); - lines2.interpolate(_); - return chart; - }; + brushBGenter.append('rect') + .attr('class', 'left') + .attr('x', 0) + .attr('y', 0) + .attr('height', availableHeight2); - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; + brushBGenter.append('rect') + .attr('class', 'right') + .attr('x', 0) + .attr('y', 0) + .attr('height', availableHeight2); - // Chart has multiple similar Axes, to prevent code duplication, probably need to link all axis functions manually like below - chart.xTickFormat = function(_) { - if (!arguments.length) return xAxis.tickFormat(); - xAxis.tickFormat(_); - x2Axis.tickFormat(_); - return chart; - }; + 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); - chart.yTickFormat = function(_) { - if (!arguments.length) return yAxis.tickFormat(); - yAxis.tickFormat(_); - y2Axis.tickFormat(_); - return chart; - }; + onBrush(); - chart.brushExtent = function(_) { - if (!arguments.length) return brushExtent; - brushExtent = _; - return chart; - }; + //------------------------------------------------------------ - chart.transitionDuration = function(_) { - if (!arguments.length) return transitionDuration; - transitionDuration = _; - return chart; - }; - //============================================================ + //------------------------------------------------------------ + // Setup Secondary (Context) Axes + x2Axis + .scale(x2) + .ticks( nv.utils.calcTicksX(availableWidth/100, data) ) + .tickSize(-availableHeight2, 0); - return chart; -} + 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); -nv.models.linePlusBarWithFocusChart = 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() - ; + y2Axis + .scale(y2) + .ticks( nv.utils.calcTicksY(availableHeight2/36, data) ) + .tickSize( -availableWidth, 0); - var margin = {top: 30, right: 30, bottom: 30, left: 60} - , margin2 = {top: 0, right: 30, bottom: 20, left: 60} - , width = null - , height = null - , height2 = 100 - , getX = function(d) { return d.x } - , getY = function(d) { return d.y } - , color = nv.utils.defaultColor() - , showLegend = true - , extent - , brushExtent = null - , tooltips = true - , tooltip = function(key, x, y, e, graph) { - return '

' + key + '

' + - '

' + y + ' at ' + x + '

'; - } - , x - , x2 - , y1 - , y2 - , y3 - , y4 - , noData = "No Data Available." - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'brush') - , transitionDuration = 0 - ; + d3.transition(g.select('.nv-context .nv-y.nv-axis')) + .call(y2Axis); - 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') - ; + g.select('.nv-context .nv-x.nv-axis') + .attr('transform', 'translate(0,' + y2.range()[0] + ')'); - //============================================================ + //------------------------------------------------------------ - //============================================================ - // Private Variables - //------------------------------------------------------------ + //============================================================ + // Event Handling/Dispatching (in chart's scope) + //------------------------------------------------------------ - 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); + legend.dispatch.on('stateChange', function(newState) { + for (var key in newState) + state[key] = newState[key]; + dispatch.stateChange(state); + chart.update(); + }); - nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement); - }; + 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 chart(selection) { - selection.each(function(data) { - var container = d3.select(this), - that = this; + 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); + }); + } - 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; + 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; + } - //------------------------------------------------------------ - // Display No Data message if there's nothing to show. + 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); + } - 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(); + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ - 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) } - }) - }); + lines.dispatch.on('elementMouseover.tooltip', function(e) { + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; + dispatch.tooltipShow(e); + }); - 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) } - }) - }); + lines.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); + }); - x .range([0, availableWidth]); + dispatch.on('tooltipHide', function() { + if (tooltips) nv.tooltip.cleanup(); + }); - x2 .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } )) - .range([0, availableWidth]); + //============================================================ - //------------------------------------------------------------ + //============================================================ + // 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; - //------------------------------------------------------------ - // Setup containers and skeleton of chart + // DO NOT DELETE. This is currently overridden below + // until deprecated portions are removed. + chart.state = state; - 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'); + d3.rebind(chart, lines, 'defined', 'isArea', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id'); - gEnter.append('g').attr('class', 'nv-legendWrap'); + chart.options = nv.utils.optionsFunc.bind(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'); + chart.x = function(_) { + if (!arguments.length) return lines.x; + lines.x(_); + lines2.x(_); + return chart; + }; - 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'); + chart.y = function(_) { + if (!arguments.length) return lines.y; + lines.y(_); + lines2.y(_); + return chart; + }; + chart.margin = function(_) { + if (!arguments.length) return margin; + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; + return chart; + }; - //------------------------------------------------------------ + chart.margin2 = function(_) { + if (!arguments.length) return margin2; + margin2 = _; + return chart; + }; + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - //------------------------------------------------------------ - // Legend + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - if (showLegend) { - legend.width( availableWidth / 2 ); + chart.height2 = function(_) { + if (!arguments.length) return height2; + height2 = _; + return chart; + }; - 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); + chart.color = function(_) { + if (!arguments.length) return color; + color =nv.utils.getColor(_); + legend.color(color); + return chart; + }; - if ( margin.top != legend.height()) { - margin.top = legend.height(); - availableHeight1 = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom - height2; - } + chart.showLegend = function(_) { + if (!arguments.length) return showLegend; + showLegend = _; + return chart; + }; - g.select('.nv-legendWrap') - .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')'); - } + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; - //------------------------------------------------------------ + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; + // DEPRECATED + chart.state = function(_) { + nv.deprecated('lineWithFocusChart.state'); + if (!arguments.length) return state; + state = _; + return chart; + }; + for (var key in state) { + chart.state[key] = state[key]; + } + // END DEPRECATED - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + chart.defaultState = function(_) { + if (!arguments.length) return defaultState; + defaultState = _; + return chart; + }; + chart.interpolate = function(_) { + if (!arguments.length) return lines.interpolate(); + lines.interpolate(_); + lines2.interpolate(_); + return chart; + }; - //------------------------------------------------------------ - // Context Components + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; - 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 })); + // Chart has multiple similar Axes, to prevent code duplication, probably need to link all axis functions manually like below + chart.xTickFormat = function(_) { + if (!arguments.length) return xAxis.tickFormat(); + xAxis.tickFormat(_); + x2Axis.tickFormat(_); + return chart; + }; - 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 })); + chart.yTickFormat = function(_) { + if (!arguments.length) return yAxis.tickFormat(); + yAxis.tickFormat(_); + y2Axis.tickFormat(_); + return chart; + }; - var bars2Wrap = g.select('.nv-context .nv-barsWrap') - .datum(dataBars.length ? dataBars : [{values:[]}]); + chart.brushExtent = function(_) { + if (!arguments.length) return brushExtent; + brushExtent = _; + return chart; + }; - var lines2Wrap = g.select('.nv-context .nv-linesWrap') - .datum(!dataLines[0].disabled ? dataLines : [{values:[]}]); + chart.transitionDuration = function(_) { + if (!arguments.length) return transitionDuration; + transitionDuration = _; + return chart; + }; - g.select('.nv-context') - .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')') + //============================================================ - bars2Wrap.transition().call(bars2); - lines2Wrap.transition().call(lines2); - //------------------------------------------------------------ + 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 = 1000 + , xDomain + , yDomain + , xRange + , yRange + , groupSpacing = 0.1 + , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'renderEnd') + ; + //============================================================ - //------------------------------------------------------------ - // Setup Brush - brush - .x(x2) - .on('brush', onBrush); + //============================================================ + // Private Variables + //------------------------------------------------------------ - if (brushExtent) brush.extent(brushExtent); + var x0, y0 //used to store previous scales + , renderWatch = nv.utils.renderWatch(dispatch, duration) + ; - 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); + 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; + } - //------------------------------------------------------------ - // Setup Secondary (Context) Axes - x2Axis - .ticks( availableWidth / 100 ) - .tickSize(-availableHeight2, 0); + 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; + }); + }); - 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); + //------------------------------------------------------------ + // 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; + } + }); + }); - y3Axis - .scale(y3) - .ticks( availableHeight2 / 36 ) - .tickSize( -availableWidth, 0); + //------------------------------------------------------------ + // Setup Scales - g.select('.nv-context .nv-y1.nv-axis') - .style('opacity', dataBars.length ? 1 : 0) - .attr('transform', 'translate(0,' + x2.range()[0] + ')'); + // 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 } + }) + }); - g.select('.nv-context .nv-y1.nv-axis').transition() - .call(y3Axis); + 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 d.y + (stacked ? d.y1 : 0) }).concat(forceY))) + 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]); - y4Axis - .scale(y4) - .ticks( availableHeight2 / 36 ) - .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none + // 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]); - g.select('.nv-context .nv-y2.nv-axis') - .style('opacity', dataLines.length ? 1 : 0) - .attr('transform', 'translate(' + x2.range()[1] + ',0)'); + 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]); - g.select('.nv-context .nv-y2.nv-axis').transition() - .call(y4Axis); - //------------------------------------------------------------ + x0 = x0 || x; + y0 = y0 || y; - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ + //------------------------------------------------------------ - legend.dispatch.on('stateChange', function(newState) { - chart.update(); - }); - dispatch.on('tooltipShow', function(e) { - if (tooltips) showTooltip(e, that.parentNode); - }); + //------------------------------------------------------------ + // 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'); - //============================================================ - // Functions - //------------------------------------------------------------ + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - // 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(); + defsEnter.append('clipPath') + .attr('id', 'nv-edge-clip-' + id) + .append('rect'); + wrap.select('#nv-edge-clip-' + id + ' rect') + .attr('width', availableWidth) + .attr('height', availableHeight); - dispatch.brush({extent: extent, brush: brush}); + g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : ''); - updateBrushBG(); + 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(250, duration)) + .attr('y', function(d) { return stacked ? y0(d.y0) : y0(0) }) + .attr('height', 0) + .remove(); + if (exitTransition.delay) + exitTransition.delay(function(d,i) { + return i * duration / data[0].values.length; + }); + 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); - //------------------------------------------------------------ - // 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 })); + var bars = groups.selectAll('rect.nv-bar') + .data(function(d) { return (hideable && !data.length) ? hideable.values : d.values }); + bars.exit().remove(); - 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 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) }) + .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 + }); }) - ); - - 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]; - }) - } + .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 + }); }) - ); - - //------------------------------------------------------------ - - - //------------------------------------------------------------ - // Update Main (Focus) X Axis - - if (dataBars.length) { - x = bars.xScale(); - } else { - x = lines.xScale(); - } - - xAxis - .scale(x) - .ticks( availableWidth / 100 ) - .tickSize(-availableHeight1, 0); + .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(); }); + } - xAxis.domain([Math.ceil(extent[0]), Math.floor(extent[1])]); + 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; + }); - g.select('.nv-x.nv-axis').transition().duration(transitionDuration) - .call(xAxis); - //------------------------------------------------------------ + //store old scales for use in transitions on update + x0 = x.copy(); + y0 = y.copy(); + }); - //------------------------------------------------------------ - // Update Main (Focus) Bars and Lines + renderWatch.renderEnd('multibar immediate'); - focusBarsWrap.transition().duration(transitionDuration).call(bars); - focusLinesWrap.transition().duration(transitionDuration).call(lines); + return chart; + } - //------------------------------------------------------------ + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ + // As a getter, returns a new instance of d3 dispatch and sets appropriate vars. + // As a setter, sets dispatch. + // Useful when same chart instance is used to render several data models. + // Since dispatch is instance-specific, it cannot be contained inside chart model. - //------------------------------------------------------------ - // Setup and Update Main (Focus) Y Axes + chart.dispatch = dispatch; - g.select('.nv-focus .nv-x.nv-axis') - .attr('transform', 'translate(0,' + y1.range()[0] + ')'); + chart.x = function(_) { + if (!arguments.length) return getX; + getX = _; + return chart; + }; + chart.y = function(_) { + if (!arguments.length) return getY; + getY = _; + return chart; + }; - y1Axis - .scale(y1) - .ticks( availableHeight1 / 36 ) - .tickSize(-availableWidth, 0); + 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; + }; - g.select('.nv-focus .nv-y1.nv-axis') - .style('opacity', dataBars.length ? 1 : 0); + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - y2Axis - .scale(y2) - .ticks( availableHeight1 / 36 ) - .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none + chart.xScale = function(_) { + if (!arguments.length) return x; + x = _; + return chart; + }; - g.select('.nv-focus .nv-y2.nv-axis') - .style('opacity', dataLines.length ? 1 : 0) - .attr('transform', 'translate(' + x.range()[1] + ',0)'); + chart.yScale = function(_) { + if (!arguments.length) return y; + y = _; + return chart; + }; - 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); - } + chart.xDomain = function(_) { + if (!arguments.length) return xDomain; + xDomain = _; + return chart; + }; - //============================================================ + chart.yDomain = function(_) { + if (!arguments.length) return yDomain; + yDomain = _; + return chart; + }; - onBrush(); + chart.xRange = function(_) { + if (!arguments.length) return xRange; + xRange = _; + return chart; + }; - }); + chart.yRange = function(_) { + if (!arguments.length) return yRange; + yRange = _; + return chart; + }; - 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; - - d3.rebind(chart, lines, 'defined', 'size', 'clipVoronoi', 'interpolate'); - //TODO: consider rebinding x, y and some other stuff, and simply do soemthign lile bars.x(lines.x()), etc. - //d3.rebind(chart, lines, 'x', 'y', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id'); - - chart.options = nv.utils.optionsFunc.bind(chart); - - chart.x = function(_) { - if (!arguments.length) return getX; - getX = _; - lines.x(_); - bars.x(_); - return chart; - }; + chart.forceY = function(_) { + if (!arguments.length) return forceY; + forceY = _; + return chart; + }; - chart.y = function(_) { - if (!arguments.length) return getY; - getY = _; - lines.y(_); - bars.y(_); - return chart; - }; - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + chart.stacked = function(_) { + if (!arguments.length) return stacked; + stacked = _; + return chart; + }; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + chart.stackOffset = function(_) { + if (!arguments.length) return stackOffset; + stackOffset = _; + return chart; + }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + chart.clipEdge = function(_) { + if (!arguments.length) return clipEdge; + clipEdge = _; + return chart; + }; - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - legend.color(color); - return chart; - }; + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + return chart; + }; - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + chart.barColor = function(_) { + if (!arguments.length) return barColor; + barColor = nv.utils.getColor(_); + return chart; + }; - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; + chart.disabled = function(_) { + if (!arguments.length) return disabled; + disabled = _; + return chart; + }; - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + chart.id = function(_) { + if (!arguments.length) return id; + id = _; + return chart; + }; - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; + chart.hideable = function(_) { + if (!arguments.length) return hideable; + hideable = _; + return chart; + }; - chart.brushExtent = function(_) { - if (!arguments.length) return brushExtent; - brushExtent = _; - return chart; - }; + chart.groupSpacing = function(_) { + if (!arguments.length) return groupSpacing; + groupSpacing = _; + return chart; + }; + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + return chart; + } - //============================================================ - return chart; -} + //============================================================ + // Deprecated Methods + //------------------------------------------------------------ -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 - , 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 - , delay = 1200 - , xDomain - , yDomain - , xRange - , yRange - , groupSpacing = 0.1 - , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout') - ; + chart.delay = function(_) { + nv.deprecated('multiBar.delay'); + return chart.duration(_); + }; - //============================================================ + chart.options = nv.utils.optionsFunc.bind(chart); + //============================================================ - //============================================================ - // Private Variables - //------------------------------------------------------------ + return chart; +} - var x0, y0 //used to store previous scales - ; +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 '

' + key + '

' + + '

' + y + ' on ' + x + '

' + } + , 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 - function chart(selection) { - selection.each(function(data) { - var availableWidth = width - margin.left - margin.right, - availableHeight = height - margin.top - margin.bottom, - container = d3.select(this); + 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')) + ; - if(hideable && data.length) hideable = [{ - values: data[0].values.map(function(d) { - return { - x: d.x, - y: 0, - series: d.series, - size: 0.01 - };} - )}]; + controls.updateState(false); + //============================================================ - if (stacked) - data = d3.layout.stack() - .offset('zero') - .values(function(d){ return d.values }) - .y(getY) - (!data.length && hideable ? hideable : data); + //============================================================ + // Private Variables + //------------------------------------------------------------ + var renderWatch = nv.utils.renderWatch(dispatch); + var stacked = false; - //add series index to each data point for reference - data = data.map(function(series, i) { - series.values = series.values.map(function(point) { - point.series = i; - return point; - }); - return series; - }); - - - //------------------------------------------------------------ - // 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; - } - }); - }); + 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); - //------------------------------------------------------------ - // Setup Scales + nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement); + }; - // remap and flatten the data for use in calculating the scales' domains - var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate - data.map(function(d) { - return d.values.map(function(d,i) { - return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1 } - }) - }); + var stateGetter = function(data) { + return function(){ + return { + active: data.map(function(d) { return !d.disabled }), + stacked: stacked + }; + } + }; - x .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x })) - .rangeBands(xRange || [0, availableWidth], groupSpacing); + 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]; + }); + } + } - //y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y + (stacked ? d.y1 : 0) }).concat(forceY))) - 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]); + 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; - x0 = x0 || x; - y0 = y0 || y; + 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'); - //------------------------------------------------------------ - // Setup containers and skeleton of chart + noDataText + .attr('x', margin.left + availableWidth / 2) + .attr('y', margin.top + availableHeight / 2) + .text(function(d) { return d }); - 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') + return chart; + } else { + container.selectAll('.nv-noData').remove(); + } - gEnter.append('g').attr('class', 'nv-groups'); + //------------------------------------------------------------ - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - //------------------------------------------------------------ + //------------------------------------------------------------ + // Setup Scales + x = multibar.xScale(); + y = multibar.yScale(); + //------------------------------------------------------------ - 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 + ')' : ''); + //------------------------------------------------------------ + // 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'); - 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() - .transition() - .selectAll('rect.nv-bar') - .delay(function(d,i) { - return i * delay/ data[0].values.length; - }) - .attr('y', function(d) { return stacked ? y0(d.y0) : y0(0) }) - .attr('height', 0) - .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 - .transition() - .style('stroke-opacity', 1) - .style('fill-opacity', .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) }) - .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'}) - .transition() - .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(); }); - } - - - if (stacked) - bars.transition() - .delay(function(d,i) { - - return i * delay / data[0].values.length; - }) - .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 - bars.transition() - .delay(function(d,i) { - return i * delay/ data[0].values.length; - }) - .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; - }); + //------------------------------------------------------------ + //------------------------------------------------------------ + // Legend - //store old scales for use in transitions on update - x0 = x.copy(); - y0 = y.copy(); + if (showLegend) { + legend.width(availableWidth - controlWidth()); - }); + if (multibar.barColor()) + data.forEach(function(series,i) { + series.color = d3.rgb('#ccc').darker(i * 1.5).toString(); + }) - return chart; - } + 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; + } - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + g.select('.nv-legendWrap') + .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')'); + } - chart.dispatch = dispatch; + //------------------------------------------------------------ - chart.options = nv.utils.optionsFunc.bind(chart); - chart.x = function(_) { - if (!arguments.length) return getX; - getX = _; - return chart; - }; + //------------------------------------------------------------ + // Controls - chart.y = function(_) { - if (!arguments.length) return getY; - getY = _; - return chart; - }; - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + if (showControls) { + var controlsData = [ + { key: 'Grouped', disabled: multibar.stacked() }, + { key: 'Stacked', disabled: !multibar.stacked() } + ]; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + controls.width(controlWidth()).color(['#444', '#444', '#444']); + g.select('.nv-controlsWrap') + .datum(controlsData) + .attr('transform', 'translate(0,' + (-margin.top) +')') + .call(controls); + } - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + //------------------------------------------------------------ - chart.xScale = function(_) { - if (!arguments.length) return x; - x = _; - return chart; - }; - chart.yScale = function(_) { - if (!arguments.length) return y; - y = _; - return chart; - }; + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - chart.xDomain = function(_) { - if (!arguments.length) return xDomain; - xDomain = _; - return chart; - }; + if (rightAlignYAxis) { + g.select(".nv-y.nv-axis") + .attr("transform", "translate(" + availableWidth + ",0)"); + } - chart.yDomain = function(_) { - if (!arguments.length) return yDomain; - yDomain = _; - return chart; - }; + //------------------------------------------------------------ + // Main Chart Component(s) - chart.xRange = function(_) { - if (!arguments.length) return xRange; - xRange = _; - return chart; - }; + 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 })) - chart.yRange = function(_) { - if (!arguments.length) return yRange; - yRange = _; - return chart; - }; - chart.forceY = function(_) { - if (!arguments.length) return forceY; - forceY = _; - return chart; - }; + var barsWrap = g.select('.nv-barsWrap') + .datum(data.filter(function(d) { return !d.disabled })) - chart.stacked = function(_) { - if (!arguments.length) return stacked; - stacked = _; - return chart; - }; + barsWrap.call(multibar); - chart.clipEdge = function(_) { - if (!arguments.length) return clipEdge; - clipEdge = _; - return chart; - }; + //------------------------------------------------------------ - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - return chart; - }; - chart.barColor = function(_) { - if (!arguments.length) return barColor; - barColor = nv.utils.getColor(_); - return chart; - }; + //------------------------------------------------------------ + // Setup Axes - chart.disabled = function(_) { - if (!arguments.length) return disabled; - disabled = _; - return chart; - }; + if (showXAxis) { + xAxis + .scale(x) + .ticks( nv.utils.calcTicksX(availableWidth/100, data) ) + .tickSize(-availableHeight, 0); - chart.id = function(_) { - if (!arguments.length) return id; - id = _; - return chart; - }; + g.select('.nv-x.nv-axis') + .attr('transform', 'translate(0,' + y.range()[0] + ')'); + g.select('.nv-x.nv-axis') + .call(xAxis); - chart.hideable = function(_) { - if (!arguments.length) return hideable; - hideable = _; - return chart; - }; + var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g'); - chart.delay = function(_) { - if (!arguments.length) return delay; - delay = _; - return chart; - }; + xTicks + .selectAll('line, text') + .style('opacity', 1) - chart.groupSpacing = function(_) { - if (!arguments.length) return groupSpacing; - groupSpacing = _; - return chart; - }; + 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); + }); + } - return chart; -} + 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); + } -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 '

' + key + '

' + - '

' + y + ' on ' + x + '

' - } - , x //can be accessed via chart.xScale() - , y //can be accessed via chart.yScale() - , state = { stacked: false } - , defaultState = null - , noData = "No Data Available." - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') - , controlWidth = function() { return showControls ? 180 : 0 } - , transitionDuration = 250 - ; + if (showYAxis) { + yAxis + .scale(y) + .ticks( nv.utils.calcTicksY(availableHeight/36, data) ) + .tickSize( -availableWidth, 0); - 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')) - ; + g.select('.nv-y.nv-axis') + .call(yAxis); + } - 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 ? 'n' : 's', null, offsetElement); - }; + //============================================================ + // 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; - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this), - that = this; + switch (d.key) { + case 'Grouped': + multibar.stacked(false); + break; + case 'Stacked': + multibar.stacked(true); + break; + } - var availableWidth = (width || parseInt(container.style('width')) || 960) - - margin.left - margin.right, - availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; + state.stacked = multibar.stacked(); + dispatch.stateChange(state); - chart.update = function() { container.transition().duration(transitionDuration).call(chart) }; - chart.container = this; + chart.update(); + }); - //set state.disabled - state.disabled = data.map(function(d) { return !!d.disabled }); + dispatch.on('tooltipShow', function(e) { + if (tooltips) showTooltip(e, that.parentNode) + }); - 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. + // Update chart from a state object passed to event handler + dispatch.on('changeState', function(e) { - if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { - var noDataText = container.selectAll('.nv-noData').data([noData]); + if (typeof e.disabled !== 'undefined') { + data.forEach(function(series,i) { + series.disabled = e.disabled[i]; + }); - noDataText.enter().append('text') - .attr('class', 'nvd3 nv-noData') - .attr('dy', '-.7em') - .style('text-anchor', 'middle'); + state.disabled = e.disabled; + } - noDataText - .attr('x', margin.left + availableWidth / 2) - .attr('y', margin.top + availableHeight / 2) - .text(function(d) { return d }); + if (typeof e.stacked !== 'undefined') { + multibar.stacked(e.stacked); + state.stacked = e.stacked; + stacked = e.stacked; + } - return chart; - } else { - container.selectAll('.nv-noData').remove(); - } + chart.update(); + }); - //------------------------------------------------------------ + //============================================================ - //------------------------------------------------------------ - // Setup Scales + }); - x = multibar.xScale(); - y = multibar.yScale(); + renderWatch.renderEnd('multibarchart immediate'); - //------------------------------------------------------------ + return chart; + } - //------------------------------------------------------------ - // Setup containers and skeleton of chart + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ - 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'); + multibar.dispatch.on('elementMouseover.tooltip', function(e) { + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; + dispatch.tooltipShow(e); + }); - 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'); + multibar.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); + }); + dispatch.on('tooltipHide', function() { + if (tooltips) nv.tooltip.cleanup(); + }); - //------------------------------------------------------------ + //============================================================ - //------------------------------------------------------------ - // Legend - if (showLegend) { - legend.width(availableWidth - controlWidth()); + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ - if (multibar.barColor()) - data.forEach(function(series,i) { - series.color = d3.rgb('#ccc').darker(i * 1.5).toString(); - }) + // expose chart's sub-components + chart.dispatch = dispatch; + chart.multibar = multibar; + chart.legend = legend; + chart.xAxis = xAxis; + chart.yAxis = yAxis; + // DO NOT DELETE. This is currently overridden below + // until deprecated portions are removed. + chart.state = state; - g.select('.nv-legendWrap') - .datum(data) - .call(legend); + d3.rebind(chart, multibar, 'x', 'y', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'clipEdge', + 'id', 'stacked', 'stackOffset', 'delay', 'barColor','groupSpacing'); - if ( margin.top != legend.height()) { - margin.top = legend.height(); - availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; - } + chart.options = nv.utils.optionsFunc.bind(chart); - g.select('.nv-legendWrap') - .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')'); - } + 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.height = function(_) { - //------------------------------------------------------------ - // Controls + if (!arguments.length) return height; + height = _; + return chart; + }; - if (showControls) { - var controlsData = [ - { key: 'Grouped', disabled: multibar.stacked() }, - { key: 'Stacked', disabled: !multibar.stacked() } - ]; + chart.color = function(_) { - controls.width(controlWidth()).color(['#444', '#444', '#444']); - g.select('.nv-controlsWrap') - .datum(controlsData) - .attr('transform', 'translate(0,' + (-margin.top) +')') - .call(controls); - } + if (!arguments.length) return color; + color = nv.utils.getColor(_); + legend.color(color); + return chart; + }; - //------------------------------------------------------------ + chart.showControls = function(_) { + if (!arguments.length) return showControls; + showControls = _; + return chart; + }; + chart.showLegend = function(_) { + if (!arguments.length) return showLegend; + showLegend = _; + return chart; + }; - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + chart.showXAxis = function(_) { + if (!arguments.length) return showXAxis; + showXAxis = _; + return chart; + }; - if (rightAlignYAxis) { - g.select(".nv-y.nv-axis") - .attr("transform", "translate(" + availableWidth + ",0)"); - } + chart.showYAxis = function(_) { + if (!arguments.length) return showYAxis; + showYAxis = _; + return chart; + }; - //------------------------------------------------------------ - // Main Chart Component(s) + chart.rightAlignYAxis = function(_) { + if(!arguments.length) return rightAlignYAxis; + rightAlignYAxis = _; + yAxis.orient( (_) ? 'right' : 'left'); + return chart; + }; - 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 })) + chart.reduceXTicks= function(_) { + if (!arguments.length) return reduceXTicks; + reduceXTicks = _; + return chart; + }; + chart.rotateLabels = function(_) { + if (!arguments.length) return rotateLabels; + rotateLabels = _; + return chart; + } - var barsWrap = g.select('.nv-barsWrap') - .datum(data.filter(function(d) { return !d.disabled })) + chart.staggerLabels = function(_) { + if (!arguments.length) return staggerLabels; + staggerLabels = _; + return chart; + }; - barsWrap.transition().call(multibar); + chart.tooltip = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - //------------------------------------------------------------ + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - //------------------------------------------------------------ - // Setup Axes + // DEPRECATED + chart.state = function(_) { + nv.deprecated('multiBarChart.state'); + if (!arguments.length) return state; + state = _; + return chart; + }; + for (var key in state) { + chart.state[key] = state[key]; + } + // END DEPRECATED - if (showXAxis) { - xAxis - .scale(x) - .ticks( availableWidth / 100 ) - .tickSize(-availableHeight, 0); + chart.defaultState = function(_) { + if (!arguments.length) return defaultState; + defaultState = _; + return chart; + }; - g.select('.nv-x.nv-axis') - .attr('transform', 'translate(0,' + y.range()[0] + ')'); - g.select('.nv-x.nv-axis').transition() - .call(xAxis); + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; - var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g'); + chart.transitionDuration = function(_) { + nv.deprecated('multiBarChart.transitionDuration'); + return chart.duration(_); + }; - xTicks - .selectAll('line, text') - .style('opacity', 1) + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + multibar.duration(duration); + xAxis.duration(duration); + yAxis.duration(duration); + renderWatch.reset(duration); + return chart; + } - 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); - }); - } + return chart; +} - if (reduceXTicks) - xTicks - .filter(function(d,i) { - return i % Math.ceil(data[0].values.length / (availableWidth / 100)) !== 0; - }) - .selectAll('text, line') - .style('opacity', 0); +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 } + , 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') + ; - 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); - } + //============================================================ + // Private Variables + //------------------------------------------------------------ - if (showYAxis) { - yAxis - .scale(y) - .ticks( availableHeight / 36 ) - .tickSize( -availableWidth, 0); + var x0, y0; //used to store previous scales + var renderWatch = nv.utils.renderWatch(dispatch, duration); - g.select('.nv-y.nv-axis').transition() - .call(yAxis); - } + //============================================================ - //------------------------------------------------------------ + 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); - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ - legend.dispatch.on('stateChange', function(newState) { - state = newState; - dispatch.stateChange(state); - chart.update(); - }); + //add series index to each data point for reference + data.forEach(function(series, i) { + series.values.forEach(function(point) { + point.series = i; + }); + }); - 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(); - }); + //------------------------------------------------------------ + // 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; + } + }); + }); - 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]; - }); + //------------------------------------------------------------ + // Setup Scales - state.disabled = e.disabled; - } + // 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 } + }) + }); - if (typeof e.stacked !== 'undefined') { - multibar.stacked(e.stacked); - state.stacked = e.stacked; - } + x .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x })) + .rangeBands(xRange || [0, availableHeight], .1); - chart.update(); - }); + //y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y + (stacked ? d.y0 : 0) }).concat(forceY))) + 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)]); - }); + //------------------------------------------------------------ - return chart; - } + //------------------------------------------------------------ + // Setup containers and skeleton of chart - //============================================================ - // Event Handling/Dispatching (out of chart's scope) - //------------------------------------------------------------ + 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'); - multibar.dispatch.on('elementMouseover.tooltip', function(e) { - e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; - dispatch.tooltipShow(e); - }); + gEnter.append('g').attr('class', 'nv-groups'); - multibar.dispatch.on('elementMouseout.tooltip', function(e) { - dispatch.tooltipHide(e); - }); - dispatch.on('tooltipHide', function() { - if (tooltips) nv.tooltip.cleanup(); - }); + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - //============================================================ + //------------------------------------------------------------ - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ - // expose chart's sub-components - chart.dispatch = dispatch; - chart.multibar = multibar; - chart.legend = legend; - chart.xAxis = xAxis; - chart.yAxis = yAxis; + 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); - d3.rebind(chart, multibar, 'x', 'y', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'clipEdge', - 'id', 'stacked', 'delay', 'barColor','groupSpacing'); - chart.options = nv.utils.optionsFunc.bind(chart); + var bars = groups.selectAll('g.nv-bar') + .data(function(d) { return d.values }); - 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; - }; + bars.exit().remove(); - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + 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))) + ')' + }); - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - legend.color(color); - return chart; - }; + 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(); + }); - chart.showControls = function(_) { - if (!arguments.length) return showControls; - showControls = _; - return chart; - }; - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + barsEnter.append('text'); - chart.showXAxis = function(_) { - if (!arguments.length) return showXAxis; - showXAxis = _; - return chart; - }; + if (showValues && !stacked) { + bars.select('text') + .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'end' : 'start' }) + .attr('y', x.rangeBand() / (data.length * 2)) + .attr('dy', '.32em') + .text(function(d,i) { return valueFormat(getY(d,i)) }) + bars.watchTransition(renderWatch, 'multibarhorizontal: bars') + .select('text') + .attr('x', function(d,i) { return getY(d,i) < 0 ? -4 : y(getY(d,i)) - y(0) + 4 }) + } else { + bars.selectAll('text').text(''); + } - chart.showYAxis = function(_) { - if (!arguments.length) return showYAxis; - showYAxis = _; - return chart; - }; + if (showBarLabels && !stacked) { + barsEnter.append('text').classed('nv-bar-label',true); + bars.select('text.nv-bar-label') + .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'start' : 'end' }) + .attr('y', x.rangeBand() / (data.length * 2)) + .attr('dy', '.32em') + .text(function(d,i) { return getX(d,i) }); + bars.watchTransition(renderWatch, 'multibarhorizontal: bars') + .select('text.nv-bar-label') + .attr('x', function(d,i) { return getY(d,i) < 0 ? y(0) - y(getY(d,i)) + 4 : -4 }); + } + else { + bars.selectAll('text.nv-bar-label').text(''); + } - chart.rightAlignYAxis = function(_) { - if(!arguments.length) return rightAlignYAxis; - rightAlignYAxis = _; - yAxis.orient( (_) ? 'right' : 'left'); - return chart; - }; + bars + .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'}) - chart.reduceXTicks= function(_) { - if (!arguments.length) return reduceXTicks; - reduceXTicks = _; - return chart; - }; + 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(); }); + } - chart.rotateLabels = function(_) { - if (!arguments.length) return rotateLabels; - rotateLabels = _; - return chart; - } + 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) + }); - chart.staggerLabels = function(_) { - if (!arguments.length) return staggerLabels; - staggerLabels = _; - return chart; - }; - chart.tooltip = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + //store old scales for use in transitions on update + x0 = x.copy(); + y0 = y.copy(); - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; + }); - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + renderWatch.renderEnd('multibarHorizontal immediate'); + return chart; + } - chart.state = function(_) { - if (!arguments.length) return state; - state = _; - return chart; - }; - chart.defaultState = function(_) { - if (!arguments.length) return defaultState; - defaultState = _; - return chart; - }; + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; + chart.dispatch = dispatch; - chart.transitionDuration = function(_) { - if (!arguments.length) return transitionDuration; - transitionDuration = _; - return chart; - }; + chart.options = nv.utils.optionsFunc.bind(chart); - //============================================================ + chart.x = function(_) { + if (!arguments.length) return getX; + getX = _; + return chart; + }; + chart.y = function(_) { + if (!arguments.length) return getY; + getY = _; + return chart; + }; - return chart; -} + chart.margin = function(_) { + if (!arguments.length) return margin; + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; + return chart; + }; -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 } - , 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 - , valuePadding = 60 - , valueFormat = d3.format(',.2f') - , delay = 1200 - , xDomain - , yDomain - , xRange - , yRange - , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout') - ; + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - //============================================================ + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; + chart.xScale = function(_) { + if (!arguments.length) return x; + x = _; + return chart; + }; - //============================================================ - // Private Variables - //------------------------------------------------------------ + chart.yScale = function(_) { + if (!arguments.length) return y; + y = _; + return chart; + }; - var x0, y0 //used to store previous scales - ; + chart.xDomain = function(_) { + if (!arguments.length) return xDomain; + xDomain = _; + return chart; + }; - //============================================================ + chart.yDomain = function(_) { + if (!arguments.length) return yDomain; + yDomain = _; + return chart; + }; + chart.xRange = function(_) { + if (!arguments.length) return xRange; + xRange = _; + return chart; + }; - function chart(selection) { - selection.each(function(data) { - var availableWidth = width - margin.left - margin.right, - availableHeight = height - margin.top - margin.bottom, - container = d3.select(this); + chart.yRange = function(_) { + if (!arguments.length) return yRange; + yRange = _; + return chart; + }; + chart.forceY = function(_) { + if (!arguments.length) return forceY; + forceY = _; + return chart; + }; - if (stacked) - data = d3.layout.stack() - .offset('zero') - .values(function(d){ return d.values }) - .y(getY) - (data); + chart.stacked = function(_) { + if (!arguments.length) return stacked; + stacked = _; + return chart; + }; + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + return chart; + }; - //add series index to each data point for reference - data = data.map(function(series, i) { - series.values = series.values.map(function(point) { - point.series = i; - return point; - }); - return series; - }); - - - - //------------------------------------------------------------ - // 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; - } - }); - }); + chart.barColor = function(_) { + if (!arguments.length) return barColor; + barColor = nv.utils.getColor(_); + return chart; + }; + chart.disabled = function(_) { + if (!arguments.length) return disabled; + disabled = _; + return chart; + }; + chart.id = function(_) { + if (!arguments.length) return id; + id = _; + return chart; + }; - //------------------------------------------------------------ - // Setup Scales + chart.delay = function(_) { + if (!arguments.length) return delay; + delay = _; + return chart; + }; - // 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 } - }) - }); + chart.showValues = function(_) { + if (!arguments.length) return showValues; + showValues = _; + return chart; + }; - x .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x })) - .rangeBands(xRange || [0, availableHeight], .1); + chart.showBarLabels = function(_) { + if (!arguments.length) return showBarLabels; + showBarLabels = _; + return chart; + }; - //y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y + (stacked ? d.y0 : 0) }).concat(forceY))) - 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]); + chart.valueFormat= function(_) { + if (!arguments.length) return valueFormat; + valueFormat = _; + return chart; + }; - x0 = x0 || x; - y0 = y0 || d3.scale.linear().domain(y.domain()).range([y(0),y(0)]); + chart.valuePadding = function(_) { + if (!arguments.length) return valuePadding; + valuePadding = _; + return chart; + }; - //------------------------------------------------------------ + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + return chart; + }; + //============================================================ - //------------------------------------------------------------ - // 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'); + return chart; +} - gEnter.append('g').attr('class', 'nv-groups'); +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) + ; - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + 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 '

' + key + ' - ' + x + '

' + + '

' + y + '

' + } + , 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); + //============================================================ - 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().transition() - .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.transition() - .style('stroke-opacity', 1) - .style('fill-opacity', .75); + //============================================================ + // Private Variables + //------------------------------------------------------------ - var bars = groups.selectAll('g.nv-bar') - .data(function(d) { return d.values }); - bars.exit().remove(); + 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 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))) + ')' - }); + var stateGetter = function(data) { + return function(){ + return { + active: data.map(function(d) { return !d.disabled }), + stacked: stacked + }; + } + }; - barsEnter.append('rect') - .attr('width', 0) - .attr('height', x.rangeBand() / (stacked ? 1 : data.length) ) + 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]; + }); + } + }; - 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(); - }); - - - 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') - .text(function(d,i) { return valueFormat(getY(d,i)) }) - bars.transition() - .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(''); - } - - 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.transition() - .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.transition() - .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) - }); + //============================================================ + 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. - //store old scales for use in transitions on update - x0 = x.copy(); - y0 = y.copy(); + 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'); - return chart; - } + 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(); + } - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + //------------------------------------------------------------ - chart.dispatch = dispatch; - chart.options = nv.utils.optionsFunc.bind(chart); + //------------------------------------------------------------ + // Setup Scales - chart.x = function(_) { - if (!arguments.length) return getX; - getX = _; - return chart; - }; + x = multibar.xScale(); + y = multibar.yScale(); - chart.y = function(_) { - if (!arguments.length) return getY; - getY = _; - return chart; - }; - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + //------------------------------------------------------------ - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + //------------------------------------------------------------ + // Setup containers and skeleton of chart - chart.xScale = function(_) { - if (!arguments.length) return x; - x = _; - return 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'); - chart.yScale = function(_) { - if (!arguments.length) return y; - y = _; - return chart; - }; + 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'); - chart.xDomain = function(_) { - if (!arguments.length) return xDomain; - xDomain = _; - return chart; - }; + //------------------------------------------------------------ - chart.yDomain = function(_) { - if (!arguments.length) return yDomain; - yDomain = _; - return chart; - }; - chart.xRange = function(_) { - if (!arguments.length) return xRange; - xRange = _; - return chart; - }; + //------------------------------------------------------------ + // Legend - chart.yRange = function(_) { - if (!arguments.length) return yRange; - yRange = _; - return chart; - }; + if (showLegend) { + legend.width(availableWidth - controlWidth()); - chart.forceY = function(_) { - if (!arguments.length) return forceY; - forceY = _; - return chart; - }; + if (multibar.barColor()) + data.forEach(function(series,i) { + series.color = d3.rgb('#ccc').darker(i * 1.5).toString(); + }); - chart.stacked = function(_) { - if (!arguments.length) return stacked; - stacked = _; - return chart; - }; + g.select('.nv-legendWrap') + .datum(data) + .call(legend); - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - return chart; - }; + if ( margin.top != legend.height()) { + margin.top = legend.height(); + availableHeight = (height || parseInt(container.style('height')) || 400) + - margin.top - margin.bottom; + } - chart.barColor = function(_) { - if (!arguments.length) return barColor; - barColor = nv.utils.getColor(_); - return chart; - }; + g.select('.nv-legendWrap') + .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')'); + } - chart.disabled = function(_) { - if (!arguments.length) return disabled; - disabled = _; - return chart; - }; + //------------------------------------------------------------ - chart.id = function(_) { - if (!arguments.length) return id; - id = _; - return chart; - }; - chart.delay = function(_) { - if (!arguments.length) return delay; - delay = _; - return chart; - }; + //------------------------------------------------------------ + // Controls - chart.showValues = function(_) { - if (!arguments.length) return showValues; - showValues = _; - return chart; - }; + if (showControls) { + var controlsData = [ + { key: 'Grouped', disabled: multibar.stacked() }, + { key: 'Stacked', disabled: !multibar.stacked() } + ]; - chart.valueFormat= function(_) { - if (!arguments.length) return valueFormat; - valueFormat = _; - return chart; - }; + controls.width(controlWidth()).color(['#444', '#444', '#444']); + g.select('.nv-controlsWrap') + .datum(controlsData) + .attr('transform', 'translate(0,' + (-margin.top) +')') + .call(controls); + } - chart.valuePadding = function(_) { - if (!arguments.length) return valuePadding; - valuePadding = _; - return chart; - }; + //------------------------------------------------------------ - //============================================================ + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - 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) - ; + //------------------------------------------------------------ + // Main Chart Component(s) - var margin = {top: 30, right: 20, bottom: 50, left: 60} - , width = null - , height = null - , color = nv.utils.defaultColor() - , showControls = true - , showLegend = true - , stacked = false - , tooltips = true - , tooltip = function(key, x, y, e, graph) { - return '

' + key + ' - ' + x + '

' + - '

' + y + '

' - } - , x //can be accessed via chart.xScale() - , y //can be accessed via chart.yScale() - , state = { stacked: stacked } - , defaultState = null - , noData = 'No Data Available.' - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') - , controlWidth = function() { return showControls ? 180 : 0 } - , transitionDuration = 250 - ; + 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 })); - multibar - .stacked(stacked) - ; - xAxis - .orient('left') - .tickPadding(5) - .highlightZero(false) - .showMaxMin(false) - .tickFormat(function(d) { return d }) - ; - yAxis - .orient('bottom') - .tickFormat(d3.format(',.1f')) - ; + var barsWrap = g.select('.nv-barsWrap') + .datum(data.filter(function(d) { return !d.disabled })); - controls.updateState(false); - //============================================================ + barsWrap.transition().call(multibar); + //------------------------------------------------------------ - //============================================================ - // 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); + //------------------------------------------------------------ + // Setup Axes - nv.tooltip.show([left, top], content, e.value < 0 ? 'e' : 'w', null, offsetElement); - }; + 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'); - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this), - that = this; + xTicks + .selectAll('line, text'); + } - var availableWidth = (width || parseInt(container.style('width')) || 960) - - margin.left - margin.right, - availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; + if (showYAxis) { + yAxis + .scale(y) + .ticks( nv.utils.calcTicksX(availableWidth/100, data) ) + .tickSize( -availableHeight, 0); - chart.update = function() { container.transition().duration(transitionDuration).call(chart) }; - chart.container = this; + g.select('.nv-y.nv-axis') + .attr('transform', 'translate(0,' + availableHeight + ')'); + g.select('.nv-y.nv-axis').call(yAxis); + } - //set state.disabled - state.disabled = data.map(function(d) { return !!d.disabled }); + // Zero line + g.select(".nv-zeroLine line") + .attr("x1", y(0)) + .attr("x2", y(0)) + .attr("y1", 0) + .attr("y2", -availableHeight) + ; - 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'); + //============================================================ + // Event Handling/Dispatching (in chart's scope) + //------------------------------------------------------------ - noDataText - .attr('x', margin.left + availableWidth / 2) - .attr('y', margin.top + availableHeight / 2) - .text(function(d) { return d }); + legend.dispatch.on('stateChange', function(newState) { + for (var key in newState) + state[key] = newState[key]; + dispatch.stateChange(state); + chart.update(); + }); - return chart; - } else { - container.selectAll('.nv-noData').remove(); - } + 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(); - //------------------------------------------------------------ - // Setup Scales + chart.update(); + }); - x = multibar.xScale(); - y = multibar.yScale(); + 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]; + }); - //------------------------------------------------------------ - // Setup containers and skeleton of chart + state.disabled = e.disabled; + } - 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'); + if (typeof e.stacked !== 'undefined') { + multibar.stacked(e.stacked); + state.stacked = e.stacked; + stacked = e.stacked; + } - 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'); + chart.update(); + }); + //============================================================ - //------------------------------------------------------------ + }); + renderWatch.renderEnd('multibar horizontal chart immediate'); + return chart; + } - //------------------------------------------------------------ - // Legend + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ - if (showLegend) { - legend.width(availableWidth - controlWidth()); + multibar.dispatch.on('elementMouseover.tooltip', function(e) { + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; + dispatch.tooltipShow(e); + }); - if (multibar.barColor()) - data.forEach(function(series,i) { - series.color = d3.rgb('#ccc').darker(i * 1.5).toString(); - }) + multibar.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); + }); + dispatch.on('tooltipHide', function() { + if (tooltips) nv.tooltip.cleanup(); + }); - 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) +')'); - } + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ - //------------------------------------------------------------ + // expose chart's sub-components + chart.dispatch = dispatch; + chart.multibar = multibar; + chart.legend = legend; + chart.xAxis = xAxis; + chart.yAxis = yAxis; + // DO NOT DELETE. This is currently overridden below + // until deprecated portions are removed. + chart.state = state; - //------------------------------------------------------------ - // Controls + d3.rebind(chart, multibar, 'x', 'y', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', + 'clipEdge', 'id', 'delay', 'showValues','showBarLabels', 'valueFormat', 'stacked', 'barColor'); - if (showControls) { - var controlsData = [ - { key: 'Grouped', disabled: multibar.stacked() }, - { key: 'Stacked', disabled: !multibar.stacked() } - ]; + chart.options = nv.utils.optionsFunc.bind(chart); - controls.width(controlWidth()).color(['#444', '#444', '#444']); - g.select('.nv-controlsWrap') - .datum(controlsData) - .attr('transform', 'translate(0,' + (-margin.top) +')') - .call(controls); - } + 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.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + legend.color(color); + return chart; + }; + chart.showControls = function(_) { + if (!arguments.length) return showControls; + showControls = _; + return chart; + }; - //------------------------------------------------------------ - // Main Chart Component(s) + chart.showLegend = function(_) { + if (!arguments.length) return showLegend; + showLegend = _; + return chart; + }; - 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 })) + chart.showXAxis = function(_) { + if (!arguments.length) return showXAxis; + showXAxis = _; + return chart; + }; + chart.showYAxis = function(_) { + if (!arguments.length) return showYAxis; + showYAxis = _; + return chart; + }; - var barsWrap = g.select('.nv-barsWrap') - .datum(data.filter(function(d) { return !d.disabled })) + chart.tooltip = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - barsWrap.transition().call(multibar); + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; - //------------------------------------------------------------ + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; + // DEPRECATED + chart.state = function(_) { + nv.deprecated('multiBarHorizontalChart.state'); + if (!arguments.length) return state; + state = _; + return chart; + }; + for (var key in state) { + chart.state[key] = state[key]; + } + // END DEPRECATED - //------------------------------------------------------------ - // Setup Axes + chart.defaultState = function(_) { + if (!arguments.length) return defaultState; + defaultState = _; + return chart; + }; - xAxis - .scale(x) - .ticks( availableHeight / 24 ) - .tickSize(-availableWidth, 0); + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; - g.select('.nv-x.nv-axis').transition() - .call(xAxis); + chart.transitionDuration = function(_) { + nv.deprecated('multiBarHorizontalChart.transitionDuration'); + return chart.duration(_); + }; - var xTicks = g.select('.nv-x.nv-axis').selectAll('g'); + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + multibar.duration(duration); + xAxis.duration(duration); + yAxis.duration(duration); + return chart; + }; + //============================================================ - xTicks - .selectAll('line, text') - .style('opacity', 1) + 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 '

' + key + '

' + + '

' + y + ' at ' + x + '

' + }, + x, + y, + yDomain1, + yDomain2, + getX = function(d) { return d.x }, + getY = function(d) { return d.y}, + interpolate = 'monotone' + ; + //============================================================ + // Private Variables + //------------------------------------------------------------ - yAxis - .scale(y) - .ticks( availableWidth / 100 ) - .tickSize( -availableHeight, 0); + var x = d3.scale.linear(), + yScale1 = d3.scale.linear(), + yScale2 = d3.scale.linear(), - g.select('.nv-y.nv-axis') - .attr('transform', 'translate(0,' + availableHeight + ')'); - g.select('.nv-y.nv-axis').transition() - .call(yAxis); + 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'), - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ + legend = nv.models.legend().height(30), + dispatch = d3.dispatch('tooltipShow', 'tooltipHide'); - legend.dispatch.on('stateChange', function(newState) { - state = newState; - dispatch.stateChange(state); - chart.update(); - }); + 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); - 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; - } + nv.tooltip.show([left, top], content, undefined, undefined, offsetElement.offsetParent); + }; - state.stacked = multibar.stacked(); - dispatch.stateChange(state); + 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.disabled && d.type == 'line' && d.yAxis == 1}) + var dataLines2 = data.filter(function(d) {return !d.disabled && d.type == 'line' && d.yAxis == 2}) + var dataBars1 = data.filter(function(d) {return !d.disabled && d.type == 'bar' && d.yAxis == 1}) + var dataBars2 = data.filter(function(d) {return !d.disabled && d.type == 'bar' && d.yAxis == 2}) + var dataStack1 = data.filter(function(d) {return !d.disabled && d.type == 'area' && d.yAxis == 1}) + var dataStack2 = data.filter(function(d) {return !d.disabled && d.type == 'area' && d.yAxis == 2}) + + 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 } + }) + }) - chart.update(); - }); + 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 } + }) + }) - dispatch.on('tooltipShow', function(e) { - if (tooltips) showTooltip(e, that.parentNode); - }); + 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'); + + if (showLegend) { + 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; + } - // Update chart from a state object passed to event handler - dispatch.on('changeState', function(e) { + g.select('.legendWrap') + .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')'); + } - if (typeof e.disabled !== 'undefined') { - data.forEach(function(series,i) { - series.disabled = e.disabled[i]; - }); - state.disabled = e.disabled; - } + lines1 + .width(availableWidth) + .height(availableHeight) + .interpolate(interpolate) + .color(data.map(function(d, i) { + return d.color || color[i % color.length]; + }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'line'})); - if (typeof e.stacked !== 'undefined') { - multibar.stacked(e.stacked); - state.stacked = e.stacked; - } + lines2 + .width(availableWidth) + .height(availableHeight) + .interpolate(interpolate) + .color(data.map(function(d,i) { + return d.color || color[i % color.length]; + }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'line'})); + + bars1 + .width(availableWidth) + .height(availableHeight) + .color(data.map(function(d,i) { + return d.color || color[i % color.length]; + }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'bar'})); - selection.call(chart); - }); - //============================================================ + bars2 + .width(availableWidth) + .height(availableHeight) + .color(data.map(function(d,i) { + return d.color || color[i % color.length]; + }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'bar'})); + stack1 + .width(availableWidth) + .height(availableHeight) + .color(data.map(function(d,i) { + return d.color || color[i % color.length]; + }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'area'})); - }); + stack2 + .width(availableWidth) + .height(availableHeight) + .color(data.map(function(d,i) { + return d.color || color[i % color.length]; + }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'area'})); - return chart; - } + g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - //============================================================ - // Event Handling/Dispatching (out of chart's scope) - //------------------------------------------------------------ + var lines1Wrap = g.select('.lines1Wrap') + .datum(dataLines1) + var bars1Wrap = g.select('.bars1Wrap') + .datum(dataBars1) + var stack1Wrap = g.select('.stack1Wrap') + .datum(dataStack1) - multibar.dispatch.on('elementMouseover.tooltip', function(e) { - e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; - dispatch.tooltipShow(e); - }); + var lines2Wrap = g.select('.lines2Wrap') + .datum(dataLines2) + var bars2Wrap = g.select('.bars2Wrap') + .datum(dataBars2) + var stack2Wrap = g.select('.stack2Wrap') + .datum(dataStack2) - multibar.dispatch.on('elementMouseout.tooltip', function(e) { - dispatch.tooltipHide(e); - }); - dispatch.on('tooltipHide', function() { - if (tooltips) nv.tooltip.cleanup(); - }); + 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]) - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + lines1.yDomain(yScale1.domain()) + bars1.yDomain(yScale1.domain()) + stack1.yDomain(yScale1.domain()) - // expose chart's sub-components - chart.dispatch = dispatch; - chart.multibar = multibar; - chart.legend = legend; - chart.xAxis = xAxis; - chart.yAxis = yAxis; + lines2.yDomain(yScale2.domain()) + bars2.yDomain(yScale2.domain()) + stack2.yDomain(yScale2.domain()) - d3.rebind(chart, multibar, 'x', 'y', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'clipEdge', 'id', 'delay', 'showValues', 'valueFormat', 'stacked', 'barColor'); + if(dataStack1.length){d3.transition(stack1Wrap).call(stack1);} + if(dataStack2.length){d3.transition(stack2Wrap).call(stack2);} - chart.options = nv.utils.optionsFunc.bind(chart); + if(dataBars1.length){d3.transition(bars1Wrap).call(bars1);} + if(dataBars2.length){d3.transition(bars2Wrap).call(bars2);} - 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; - }; + if(dataLines1.length){d3.transition(lines1Wrap).call(lines1);} + if(dataLines2.length){d3.transition(lines2Wrap).call(lines2);} - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - legend.color(color); - return chart; - }; + xAxis + .ticks( nv.utils.calcTicksX(availableWidth/100, data) ) + .tickSize(-availableHeight, 0); - chart.showControls = function(_) { - if (!arguments.length) return showControls; - showControls = _; - return chart; - }; + g.select('.x.axis') + .attr('transform', 'translate(0,' + availableHeight + ')'); + d3.transition(g.select('.x.axis')) + .call(xAxis); - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + yAxis1 + .ticks( nv.utils.calcTicksY(availableHeight/36, data) ) + .tickSize( -availableWidth, 0); - chart.tooltip = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; + d3.transition(g.select('.y1.axis')) + .call(yAxis1); - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + yAxis2 + .ticks( nv.utils.calcTicksY(availableHeight/36, data) ) + .tickSize( -availableWidth, 0); - chart.state = function(_) { - if (!arguments.length) return state; - state = _; - return chart; - }; + d3.transition(g.select('.y2.axis')) + .call(yAxis2); - chart.defaultState = function(_) { - if (!arguments.length) return defaultState; - defaultState = _; - return chart; - }; + g.select('.y2.axis') + .style('opacity', series2.length ? 1 : 0) + .attr('transform', 'translate(' + x.range()[1] + ',0)'); - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; + legend.dispatch.on('stateChange', function(newState) { + chart.update(); + }); - chart.transitionDuration = function(_) { - if (!arguments.length) return transitionDuration; - transitionDuration = _; - return chart; - }; - //============================================================ + dispatch.on('tooltipShow', function(e) { + if (tooltips) showTooltip(e, that.parentNode); + }); + }); - return chart; -} -nv.models.multiChart = function() { - "use strict"; - //============================================================ - // Public Variables with Default Settings - //------------------------------------------------------------ - - var margin = {top: 30, right: 20, bottom: 50, left: 60}, - color = d3.scale.category20().range(), - width = null, - height = null, - showLegend = true, - tooltips = true, - tooltip = function(key, x, y, e, graph) { - return '

' + key + '

' + - '

' + y + ' at ' + x + '

' - }, - x, - y, - yDomain1, - yDomain2 - ; //can be accessed via chart.lines.[x/y]Scale() - - //============================================================ - // 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; - - 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.disabled && d.type == 'line' && d.yAxis == 1}) - var dataLines2 = data.filter(function(d) {return !d.disabled && d.type == 'line' && d.yAxis == 2}) - var dataBars1 = data.filter(function(d) {return !d.disabled && d.type == 'bar' && d.yAxis == 1}) - var dataBars2 = data.filter(function(d) {return !d.disabled && d.type == 'bar' && d.yAxis == 2}) - var dataStack1 = data.filter(function(d) {return !d.disabled && d.type == 'area' && d.yAxis == 1}) - var dataStack2 = data.filter(function(d) {return !d.disabled && d.type == 'area' && d.yAxis == 2}) - - 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'); - - if (showLegend) { - 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; - } + return chart; + } - g.select('.legendWrap') - .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')'); - } - - - lines1 - .width(availableWidth) - .height(availableHeight) - .interpolate("monotone") - .color(data.map(function(d,i) { - return d.color || color[i % color.length]; - }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'line'})); - - lines2 - .width(availableWidth) - .height(availableHeight) - .interpolate("monotone") - .color(data.map(function(d,i) { - return d.color || color[i % color.length]; - }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'line'})); - - bars1 - .width(availableWidth) - .height(availableHeight) - .color(data.map(function(d,i) { - return d.color || color[i % color.length]; - }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'bar'})); - - bars2 - .width(availableWidth) - .height(availableHeight) - .color(data.map(function(d,i) { - return d.color || color[i % color.length]; - }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'bar'})); - stack1 - .width(availableWidth) - .height(availableHeight) - .color(data.map(function(d,i) { - return d.color || color[i % color.length]; - }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'area'})); + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ - stack2 - .width(availableWidth) - .height(availableHeight) - .color(data.map(function(d,i) { - return d.color || color[i % color.length]; - }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'area'})); + lines1.dispatch.on('elementMouseover.tooltip', function(e) { + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; + dispatch.tooltipShow(e); + }); - g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + 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); + }); - var lines1Wrap = g.select('.lines1Wrap') - .datum(dataLines1) - var bars1Wrap = g.select('.bars1Wrap') - .datum(dataBars1) - var stack1Wrap = g.select('.stack1Wrap') - .datum(dataStack1) + lines2.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); + }); - var lines2Wrap = g.select('.lines2Wrap') - .datum(dataLines2) - var bars2Wrap = g.select('.bars2Wrap') - .datum(dataBars2) - var stack2Wrap = g.select('.stack2Wrap') - .datum(dataStack2) + bars1.dispatch.on('elementMouseover.tooltip', function(e) { + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; + dispatch.tooltipShow(e); + }); - 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}]) : [] + bars1.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); + }); - yScale1 .domain(yDomain1 || d3.extent(d3.merge(series1).concat(extraValue1), function(d) { return d.y } )) - .range([0, availableHeight]) + bars2.dispatch.on('elementMouseover.tooltip', function(e) { + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; + dispatch.tooltipShow(e); + }); - yScale2 .domain(yDomain2 || d3.extent(d3.merge(series2).concat(extraValue2), function(d) { return d.y } )) - .range([0, availableHeight]) + bars2.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); + }); - lines1.yDomain(yScale1.domain()) - bars1.yDomain(yScale1.domain()) - stack1.yDomain(yScale1.domain()) + 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; + } - lines2.yDomain(yScale2.domain()) - bars2.yDomain(yScale2.domain()) - stack2.yDomain(yScale2.domain()) + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top], + dispatch.tooltipShow(e); + }); - if(dataStack1.length){d3.transition(stack1Wrap).call(stack1);} - if(dataStack2.length){d3.transition(stack2Wrap).call(stack2);} + stack1.dispatch.on('tooltipHide', function(e) { + dispatch.tooltipHide(e); + }); - if(dataBars1.length){d3.transition(bars1Wrap).call(bars1);} - if(dataBars2.length){d3.transition(bars2Wrap).call(bars2);} + 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; + } - if(dataLines1.length){d3.transition(lines1Wrap).call(lines1);} - if(dataLines2.length){d3.transition(lines2Wrap).call(lines2);} + 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); + }); - xAxis - .ticks( availableWidth / 100 ) - .tickSize(-availableHeight, 0); - - g.select('.x.axis') - .attr('transform', 'translate(0,' + availableHeight + ')'); - d3.transition(g.select('.x.axis')) - .call(xAxis); - - yAxis1 - .ticks( availableHeight / 36 ) - .tickSize( -availableWidth, 0); - + lines1.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); + }); - d3.transition(g.select('.y1.axis')) - .call(yAxis1); - - yAxis2 - .ticks( availableHeight / 36 ) - .tickSize( -availableWidth, 0); + lines2.dispatch.on('elementMouseover.tooltip', function(e) { + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; + dispatch.tooltipShow(e); + }); - d3.transition(g.select('.y2.axis')) - .call(yAxis2); + lines2.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); + }); - g.select('.y2.axis') - .style('opacity', series2.length ? 1 : 0) - .attr('transform', 'translate(' + x.range()[1] + ',0)'); + dispatch.on('tooltipHide', function() { + if (tooltips) nv.tooltip.cleanup(); + }); - legend.dispatch.on('stateChange', function(newState) { - chart.update(); - }); - dispatch.on('tooltipShow', function(e) { - if (tooltips) showTooltip(e, that.parentNode); - }); - }); + //============================================================ + // 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.x = function(_) { + if (!arguments.length) return getX; + getX = _; + lines1.x(_); + bars1.x(_); + return chart; + }; - 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; - } + chart.y = function(_) { + if (!arguments.length) return getY; + getY = _; + lines1.y(_); + bars1.y(_); + return chart; + }; - e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top], - dispatch.tooltipShow(e); - }); + chart.yDomain1 = function(_) { + if (!arguments.length) return yDomain1; + yDomain1 = _; + return chart; + }; - stack1.dispatch.on('tooltipHide', function(e) { - dispatch.tooltipHide(e); - }); + chart.yDomain2 = function(_) { + if (!arguments.length) return yDomain2; + yDomain2 = _; + return chart; + }; - 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; - } + chart.margin = function(_) { + if (!arguments.length) return margin; + margin = _; + return chart; + }; - e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top], - dispatch.tooltipShow(e); - }); + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - stack2.dispatch.on('tooltipHide', function(e) { - dispatch.tooltipHide(e); - }); + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - 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.x = function(_) { - if (!arguments.length) return getX; - getX = _; - lines1.x(_); - bars1.x(_); - return chart; - }; + chart.color = function(_) { + if (!arguments.length) return color; + color = _; + legend.color(_); + return chart; + }; - chart.y = function(_) { - if (!arguments.length) return getY; - getY = _; - lines1.y(_); - bars1.y(_); - return chart; - }; + chart.showLegend = function(_) { + if (!arguments.length) return showLegend; + showLegend = _; + return chart; + }; - chart.yDomain1 = function(_) { - if (!arguments.length) return yDomain1; - yDomain1 = _; - return chart; - }; + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; - chart.yDomain2 = function(_) { - if (!arguments.length) return yDomain2; - yDomain2 = _; - return chart; - }; + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - chart.margin = function(_) { - if (!arguments.length) return margin; - margin = _; - return chart; - }; + chart.interpolate = function(_) { + if(!arguments.length) { + return interpolate; + } + interpolate = _; + return chart; + }; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; return chart; - }; +}; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; - chart.color = function(_) { - if (!arguments.length) return color; - color = _; - legend.color(_); - return chart; - }; +nv.models.ohlcBar = 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.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() + , xDomain + , yDomain + , xRange + , yRange + , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout') + ; - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + //============================================================ - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; + //============================================================ + // Private Variables + //------------------------------------------------------------ - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + //TODO: store old scales for transitions - return chart; -} + //============================================================ -nv.models.ohlcBar = 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.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() - , xDomain - , yDomain - , xRange - , yRange - , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout') - ; + 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 - //============================================================ - // Private Variables - //------------------------------------------------------------ + x .domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) )); - //TODO: store old scales for transitions + 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]); - function chart(selection) { - selection.each(function(data) { - var availableWidth = width - margin.left - margin.right, - availableHeight = height - margin.top - margin.bottom, - container = d3.select(this); + 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 Scales - x .domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) )); + //------------------------------------------------------------ + // Setup containers and skeleton of chart - 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]); + 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'); - 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]); + gEnter.append('g').attr('class', 'nv-ticks'); - // 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]); + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - 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]); + //------------------------------------------------------------ - //------------------------------------------------------------ + container + .on('click', function(d,i) { + dispatch.chartClick({ + data: d, + index: i, + pos: d3.event, + id: id + }); + }); - //------------------------------------------------------------ - // 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'); + defsEnter.append('clipPath') + .attr('id', 'nv-chart-clip-path-' + id) + .append('rect'); - gEnter.append('g').attr('class', 'nv-ticks'); + wrap.select('#nv-chart-clip-path-' + id + ' rect') + .attr('width', availableWidth) + .attr('height', availableHeight); - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : ''); - //------------------------------------------------------------ - container - .on('click', function(d,i) { - dispatch.chartClick({ - data: d, - index: i, - pos: d3.event, - id: id - }); - }); - - - defsEnter.append('clipPath') - .attr('id', 'nv-chart-clip-path-' + id) - .append('rect'); - - 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)) }) - .on('mouseover', function(d,i) { - 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 - }); + var ticks = wrap.select('.nv-ticks').selectAll('.nv-tick') + .data(function(d) { return d }); - }) - .on('mouseout', function(d,i) { - 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) { - 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) { - 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(); - }); - - 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'; - }) - //.attr('width', (availableWidth / data[0].values.length) * .9 ) - - - //d3.transition(ticks) - //.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)) }); - //.order(); // not sure if this makes any sense for this model + ticks.exit().remove(); - }); - return chart; - } + 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)) }) + .on('mouseover', function(d,i) { + 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) { + 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) { + 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) { + 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(); + }); - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + 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'; + }) + //.attr('width', (availableWidth / data[0].values.length) * .9 ) - chart.dispatch = dispatch; - chart.options = nv.utils.optionsFunc.bind(chart); + //d3.transition(ticks) + //.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)) }); + //.order(); // not sure if this makes any sense for this model - chart.x = function(_) { - if (!arguments.length) return getX; - getX = _; - return chart; - }; + }); - chart.y = function(_) { - if (!arguments.length) return getY; - getY = _; - return chart; - }; + return chart; + } - chart.open = function(_) { - if (!arguments.length) return getOpen; - getOpen = _; - return chart; - }; - chart.close = function(_) { - if (!arguments.length) return getClose; - getClose = _; - return chart; - }; + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ - chart.high = function(_) { - if (!arguments.length) return getHigh; - getHigh = _; - return chart; - }; + chart.dispatch = dispatch; - chart.low = function(_) { - if (!arguments.length) return getLow; - getLow = _; - return chart; - }; - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + chart.options = nv.utils.optionsFunc.bind(chart); - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + chart.x = function(_) { + if (!arguments.length) return getX; + getX = _; + return chart; + }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + chart.y = function(_) { + if (!arguments.length) return getY; + getY = _; + return chart; + }; - chart.xScale = function(_) { - if (!arguments.length) return x; - x = _; - return chart; - }; + chart.open = function(_) { + if (!arguments.length) return getOpen; + getOpen = _; + return chart; + }; - chart.yScale = function(_) { - if (!arguments.length) return y; - y = _; - return chart; - }; + chart.close = function(_) { + if (!arguments.length) return getClose; + getClose = _; + return chart; + }; - chart.xDomain = function(_) { - if (!arguments.length) return xDomain; - xDomain = _; - return chart; - }; + chart.high = function(_) { + if (!arguments.length) return getHigh; + getHigh = _; + return chart; + }; - chart.yDomain = function(_) { - if (!arguments.length) return yDomain; - yDomain = _; - return chart; - }; + chart.low = function(_) { + if (!arguments.length) return getLow; + getLow = _; + return chart; + }; - chart.xRange = function(_) { - if (!arguments.length) return xRange; - xRange = _; - return chart; - }; + chart.margin = function(_) { + if (!arguments.length) return margin; + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; + return chart; + }; - chart.yRange = function(_) { - if (!arguments.length) return yRange; - yRange = _; - return chart; - }; + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - chart.forceX = function(_) { - if (!arguments.length) return forceX; - forceX = _; - return chart; - }; + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - chart.forceY = function(_) { - if (!arguments.length) return forceY; - forceY = _; - return chart; - }; + chart.xScale = function(_) { + if (!arguments.length) return x; + x = _; + return chart; + }; - chart.padData = function(_) { - if (!arguments.length) return padData; - padData = _; - return chart; - }; + chart.yScale = function(_) { + if (!arguments.length) return y; + y = _; + return chart; + }; - chart.clipEdge = function(_) { - if (!arguments.length) return clipEdge; - clipEdge = _; - return chart; - }; + chart.xDomain = function(_) { + if (!arguments.length) return xDomain; + xDomain = _; + return chart; + }; - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - return chart; - }; + chart.yDomain = function(_) { + if (!arguments.length) return yDomain; + yDomain = _; + return chart; + }; - chart.id = function(_) { - if (!arguments.length) return id; - id = _; - return chart; - }; + chart.xRange = function(_) { + if (!arguments.length) return xRange; + xRange = _; + return chart; + }; - //============================================================ + chart.yRange = function(_) { + if (!arguments.length) return yRange; + yRange = _; + return chart; + }; + chart.forceX = function(_) { + if (!arguments.length) return forceX; + forceX = _; + return 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 } - , getDescription = function(d) { return d.description } - , 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') - , showLabels = true - , pieLabelsOutside = true - , donutLabelsOutside = false - , labelType = "key" - , labelThreshold = .02 //if slice percentage is under this, don't show label - , donut = false - , labelSunbeamLayout = false - , startAngle = false - , endAngle = false - , donutRatio = 0.5 - , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout') - ; + chart.forceY = function(_) { + if (!arguments.length) return forceY; + forceY = _; + return chart; + }; - //============================================================ + chart.padData = function(_) { + if (!arguments.length) return padData; + padData = _; + return chart; + }; + chart.clipEdge = function(_) { + if (!arguments.length) return clipEdge; + clipEdge = _; + return chart; + }; - function chart(selection) { - 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); + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + return chart; + }; + chart.id = function(_) { + if (!arguments.length) return id; + id = _; + return chart; + }; - //------------------------------------------------------------ - // Setup containers and skeleton of chart + //============================================================ - //var wrap = container.selectAll('.nv-wrap.nv-pie').data([data]); - 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'); - gEnter.append('g').attr('class', 'nv-pie'); + 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 + , endAngle = false + , donutRatio = 0.5 + , duration = 250 + , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'renderEnd') + ; - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - g.select('.nv-pie').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')'); - //------------------------------------------------------------ + //============================================================ + // 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 + }); + }); - 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); + } - var arc = d3.svg.arc() - .outerRadius(arcRadius); + // 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) }); - if (startAngle) arc.startAngle(startAngle) - if (endAngle) arc.endAngle(endAngle); - if (donut) arc.innerRadius(radius * donutRatio); + // 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'); - // 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) }); + 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 slices = wrap.select('.nv-pie').selectAll('.nv-slice').data(pie); + var pieLabels = wrap.select('.nv-pieLabels').selectAll('.nv-label').data(pie); - slices.exit().remove(); + slices.exit().remove(); + pieLabels.exit().remove(); - var ae = slices.enter().append('g') - .attr('class', 'nv-slice') - .on('mouseover', function(d,i){ + 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 + id: id, + color: d3.select(this).style("fill") }); - }) - .on('mouseout', function(d,i){ + }); + 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), @@ -10206,8 +10946,8 @@ nv.models.pie = function() { index: i, id: id }); - }) - .on('click', function(d,i) { + }); + ae.on('click', function(d,i) { dispatch.elementClick({ label: getX(d.data), value: getY(d.data), @@ -10217,8 +10957,8 @@ nv.models.pie = function() { id: id }); d3.event.stopPropagation(); - }) - .on('dblclick', function(d,i) { + }); + ae.on('dblclick', function(d,i) { dispatch.elementDblClick({ label: getX(d.data), value: getY(d.data), @@ -10228,3907 +10968,4052 @@ nv.models.pie = function() { id: id }); d3.event.stopPropagation(); - }); - - slices - .attr('fill', function(d,i) { return color(d, i); }) - .attr('stroke', function(d,i) { return color(d, i); }); - - var paths = ae.append('path') - .each(function(d) { this._current = d; }); - //.attr('d', arc); - - d3.transition(slices.select('path')) - .attr('d', arc) - .attrTween('d', arcTween); - - if (showLabels) { - // This does the normal label - var labelsArc = d3.svg.arc().innerRadius(0); - - if (pieLabelsOutside){ labelsArc = arc; } - - if (donutLabelsOutside) { labelsArc = d3.svg.arc().outerRadius(arc.outerRadius()); } - - ae.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') - - - }); - - slices.select(".nv-label").transition() - .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) + ')' - } + 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.each(function(d, i) { - var slice = d3.select(this); - - slice - .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": d3.format('%')(percent) - }; - return (d.value && percent > labelThreshold) ? labelTypes[labelType] : ''; + 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 textBox = slice.select('text').node().getBBox(); - slice.select(".nv-label rect") - .attr("width", textBox.width + 10) - .attr("height", textBox.height + 10) - .attr("transform", function() { - return "translate(" + [textBox.x - 5, textBox.y - 5] + ")"; - }); - }); - } + 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)); + }; + } + }); - // 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; + 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=_;}}, + 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 '

' + key + '

' + + '

' + y + '

'; } + , 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); - 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)); - }; + var stateGetter = function(data) { + return function(){ + return { + active: data.map(function(d) { return !d.disabled }) + }; } + }; - function tweenPie(b) { - b.innerRadius = 0; - var i = d3.interpolate({startAngle: 0, endAngle: 0}, b); - return function(t) { - return arc(i(t)); - }; + 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]; + } + } - return chart; - } + // 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 }); - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + return chart; + } else { + container.selectAll('.nv-noData').remove(); + } - chart.dispatch = dispatch; - chart.options = nv.utils.optionsFunc.bind(chart); + // 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'); - 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; - }; + gEnter.append('g').attr('class', 'nv-pieWrap'); + gEnter.append('g').attr('class', 'nv-legendWrap'); - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + // Legend + if (showLegend) { + legend.width( availableWidth ).key(pie.x()); - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + wrap.select('.nv-legendWrap') + .datum(data) + .call(legend); - chart.values = function(_) { - nv.log("pie.values() is no longer supported."); - return chart; - }; + if ( margin.top != legend.height()) { + margin.top = legend.height(); + availableHeight = (height || parseInt(container.style('height')) || 400) + - margin.top - margin.bottom; + } - chart.x = function(_) { - if (!arguments.length) return getX; - getX = _; - return chart; - }; + wrap.select('.nv-legendWrap') + .attr('transform', 'translate(0,' + (-margin.top) +')'); + } + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - chart.y = function(_) { - if (!arguments.length) return getY; - getY = d3.functor(_); - return chart; - }; + // Main Chart Component(s) + pie.width(availableWidth).height(availableHeight); + var pieWrap = g.select('.nv-pieWrap').datum([data]); + d3.transition(pieWrap).call(pie); - chart.description = function(_) { - if (!arguments.length) return getDescription; - getDescription = _; - return chart; - }; + // 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(); + }); - chart.showLabels = function(_) { - if (!arguments.length) return showLabels; - showLabels = _; - return chart; - }; + pie.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); + }); - chart.labelSunbeamLayout = function(_) { - if (!arguments.length) return labelSunbeamLayout; - labelSunbeamLayout = _; - return chart; - }; + // 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(); + }); - chart.donutLabelsOutside = function(_) { - if (!arguments.length) return donutLabelsOutside; - donutLabelsOutside = _; - return chart; - }; + }); - chart.pieLabelsOutside = function(_) { - if (!arguments.length) return pieLabelsOutside; - pieLabelsOutside = _; - return chart; - }; + renderWatch.renderEnd('pieChart immediate'); + return chart; + } - chart.labelType = function(_) { - if (!arguments.length) return labelType; - labelType = _; - labelType = labelType || "key"; - return chart; - }; + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ - chart.donut = function(_) { - if (!arguments.length) return donut; - donut = _; - return chart; - }; + pie.dispatch.on('elementMouseover.tooltip', function(e) { + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; + dispatch.tooltipShow(e); + }); - chart.donutRatio = function(_) { - if (!arguments.length) return donutRatio; - donutRatio = _; - return chart; - }; + dispatch.on('tooltipShow', function(e) { + if (tooltips) showTooltip(e); + }); - chart.startAngle = function(_) { - if (!arguments.length) return startAngle; - startAngle = _; - return chart; - }; + dispatch.on('tooltipHide', function() { + if (tooltips) nv.tooltip.cleanup(); + }); - chart.endAngle = function(_) { - if (!arguments.length) return endAngle; - endAngle = _; + //============================================================ + // 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; - }; +}; - chart.id = function(_) { - if (!arguments.length) return id; - id = _; - return chart; - }; +nv.models.scatter = 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() // 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 + , onlyCircles = true // Set to false to use shapes + , 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 + , pointKey = null + , 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 + ; - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - return chart; - }; + //============================================================ - chart.valueFormat = function(_) { - if (!arguments.length) return valueFormat; - valueFormat = _; - return chart; - }; - chart.labelThreshold = function(_) { - if (!arguments.length) return labelThreshold; - labelThreshold = _; - return chart; - }; - //============================================================ + //============================================================ + // 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) + ; - return chart; -} -nv.models.pieChart = function() { - "use strict"; - //============================================================ - // Public Variables with Default Settings - //------------------------------------------------------------ + //============================================================ - var pie = nv.models.pie() - , 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 '

' + key + '

' + - '

' + y + '

' - } - , state = {} - , defaultState = null - , noData = "No Data Available." - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') - ; + 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 && 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]); - //============================================================ - // Private Variables - //------------------------------------------------------------ + y .domain(yDomain || d3.extent(seriesData.map(function(d) { return d.y }).concat(forceY))) + .range(yRange || [availableHeight, 0]); - var showTooltip = function(e, offsetElement) { - var tooltipLabel = pie.description()(e.point) || 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); + z .domain(sizeDomain || d3.extent(seriesData.map(function(d) { return d.size }).concat(forceSize))) + .range(sizeRange || [16, 256]); - nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement); - }; + // 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]); + } - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this), - that = this; + if ( isNaN(y.domain()[0])) { + y.domain([-1,1]); + } - 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().call(chart); }; - chart.container = this; + x0 = x0 || x; + y0 = y0 || y; + z0 = z0 || z; - //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. + //------------------------------------------------------------ + // Setup containers and skeleton of chart - if (!data || !data.length) { - var noDataText = container.selectAll('.nv-noData').data([noData]); + 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'); - noDataText.enter().append('text') - .attr('class', 'nvd3 nv-noData') - .attr('dy', '-.7em') - .style('text-anchor', 'middle'); + gEnter.append('g').attr('class', 'nv-groups'); + gEnter.append('g').attr('class', 'nv-point-paths'); - noDataText - .attr('x', margin.left + availableWidth / 2) - .attr('y', margin.top + availableHeight / 2) - .text(function(d) { return d }); + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - return chart; - } else { - container.selectAll('.nv-noData').remove(); - } + //------------------------------------------------------------ - //------------------------------------------------------------ + defsEnter.append('clipPath') + .attr('id', 'nv-edge-clip-' + id) + .append('rect'); - //------------------------------------------------------------ - // Setup containers and skeleton of chart + wrap.select('#nv-edge-clip-' + id + ' rect') + .attr('width', availableWidth) + .attr('height', (availableHeight > 0) ? availableHeight : 0); - 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'); + g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : ''); - gEnter.append('g').attr('class', 'nv-pieWrap'); - gEnter.append('g').attr('class', 'nv-legendWrap'); - //------------------------------------------------------------ + function updateInteractiveLayer() { + if (!interactive) return false; - //------------------------------------------------------------ - // Legend + var eventElements; - if (showLegend) { - legend - .width( availableWidth ) - .key(pie.x()); + 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); - wrap.select('.nv-legendWrap') - .datum(data) - .call(legend); + 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! + }) + }) + ); - 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) +')'); - } - //------------------------------------------------------------ + //inject series and point index for reference into voronoi + if (useVoronoi === true) { + if (clipVoronoi) { + var pointClipsEnter = wrap.select('defs').selectAll('.nv-point-clips') + .data([id]) + .enter(); - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + pointClipsEnter.append('clipPath') + .attr('class', 'nv-point-clips') + .attr('id', 'nv-points-clip-' + id); + + var pointClips = wrap.select('#nv-points-clip-' + id).selectAll('circle') + .data(vertices); + pointClips.enter().append('circle') + .attr('r', clipRadius); + pointClips.exit().remove(); + pointClips + .attr('cx', function(d) { return d[0] }) + .attr('cy', function(d) { return d[1] }); + + wrap.select('.nv-point-paths') + .attr('clip-path', 'url(#nv-points-clip-' + id + ')'); + } + + + if(vertices.length) { + // 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]); + } + + 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] + } + }); + + + var pointPaths = wrap.select('.nv-point-paths').selectAll('path') + .data(voronoi); + pointPaths.enter().append('path') + .attr('class', function(d,i) { return 'nv-path-'+i; }); + pointPaths.exit().remove(); + pointPaths + .attr('d', function(d) { + if (!d || !d.data || d.data.length === 0) + return 'M 0 0' + else + return 'M' + d.data.join('L') + 'Z'; + }); + var mouseEventCallback = function(d,mDispatch) { + if (needsUpdate) return 0; + var series = data[d.series]; + if (typeof series === 'undefined') return; - //------------------------------------------------------------ - // Main Chart Component(s) + var point = series.values[d.point]; - pie - .width(availableWidth) - .height(availableHeight); + 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); + }); - var pieWrap = g.select('.nv-pieWrap') - .datum([data]); - d3.transition(pieWrap).call(pie); + } 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; + } - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ + 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); + + + if (onlyCircles) { + var points = groups.selectAll('circle.nv-point') + .data(function(d) { return d.values }, pointKey); + points.enter().append('circle') + .style('fill', function (d,i) { return d.color }) + .style('stroke', function (d,i) { return d.color }) + .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) }) + .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) }) + .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) }); + points.exit().remove(); + groups.exit().selectAll('path.nv-point') + .watchTransition(renderWatch, 'scatter exit') + .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) }) + .attr('cy', function(d,i) { return nv.utils.NaNtoZero(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('cx', function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) }) + .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) }) + .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) }); + + } else { + + 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', + d3.svg.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', + d3.svg.symbol() + .type(getShape) + .size(function(d,i) { return z(getSize(d,i)) }) + ); + } - legend.dispatch.on('stateChange', function(newState) { - state = newState; - dispatch.stateChange(state); - chart.update(); - }); - pie.dispatch.on('elementMouseout.tooltip', function(e) { - dispatch.tooltipHide(e); - }); + // Delay updating the invisible interactive layer for smoother animation + clearTimeout(timeoutID); // stop repeat calls to updateInteractiveLayer + timeoutID = setTimeout(updateInteractiveLayer, 300); + //updateInteractiveLayer(); - // Update chart from a state object passed to event handler - dispatch.on('changeState', function(e) { + //store old scales for use in transitions on update + x0 = x.copy(); + y0 = y.copy(); + z0 = z.copy(); - if (typeof e.disabled !== 'undefined') { - data.forEach(function(series,i) { - series.disabled = e.disabled[i]; - }); + }); + renderWatch.renderEnd('scatter immediate'); + return chart; + } - state.disabled = e.disabled; - } - chart.update(); - }); + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ + chart.clearHighlights = function() { + //Remove the 'hover' class from all highlighted points. + d3.selectAll(".nv-chart-" + id + " .nv-point.hover").classed("hover",false); + }; - //============================================================ + chart.highlightPoint = function(seriesIndex,pointIndex,isHoverOver) { + d3.select(".nv-chart-" + id + " .nv-series-" + seriesIndex + " .nv-point-" + pointIndex) + .classed("hover",isHoverOver); + }; + dispatch.on('elementMouseover.point', function(d) { + if (interactive) chart.highlightPoint(d.seriesIndex,d.pointIndex,true); }); - return chart; - } + dispatch.on('elementMouseout.point', function(d) { + if (interactive) chart.highlightPoint(d.seriesIndex,d.pointIndex,false); + }); - //============================================================ - // 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); - }); + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ - dispatch.on('tooltipHide', function() { - if (tooltips) nv.tooltip.cleanup(); - }); + chart.dispatch = dispatch; + chart.options = nv.utils.optionsFunc.bind(chart); - //============================================================ + chart.x = function(_) { + if (!arguments.length) return getX; + getX = d3.functor(_); + return chart; + }; + chart.y = function(_) { + if (!arguments.length) return getY; + getY = d3.functor(_); + return chart; + }; - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + chart.size = function(_) { + if (!arguments.length) return getSize; + getSize = d3.functor(_); + return chart; + }; - // expose chart's sub-components - chart.legend = legend; - chart.dispatch = dispatch; - chart.pie = pie; + 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; + }; - d3.rebind(chart, pie, 'valueFormat', 'values', 'x', 'y', 'description', 'id', 'showLabels', 'donutLabelsOutside', 'pieLabelsOutside', 'labelType', 'donut', 'donutRatio', 'labelThreshold'); - chart.options = nv.utils.optionsFunc.bind(chart); + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + chart.xScale = function(_) { + if (!arguments.length) return x; + x = _; + return chart; + }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + chart.yScale = function(_) { + if (!arguments.length) return y; + y = _; + return chart; + }; - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - legend.color(color); - pie.color(color); - return chart; - }; + chart.zScale = function(_) { + if (!arguments.length) return z; + z = _; + return chart; + }; - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + chart.xDomain = function(_) { + if (!arguments.length) return xDomain; + xDomain = _; + return chart; + }; - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; + chart.yDomain = function(_) { + if (!arguments.length) return yDomain; + yDomain = _; + return chart; + }; - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + chart.sizeDomain = function(_) { + if (!arguments.length) return sizeDomain; + sizeDomain = _; + return chart; + }; - chart.state = function(_) { - if (!arguments.length) return state; - state = _; - return chart; - }; + chart.xRange = function(_) { + if (!arguments.length) return xRange; + xRange = _; + return chart; + }; - chart.defaultState = function(_) { - if (!arguments.length) return defaultState; - defaultState = _; - return chart; - }; + chart.yRange = function(_) { + if (!arguments.length) return yRange; + yRange = _; + return chart; + }; - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; + chart.sizeRange = function(_) { + if (!arguments.length) return sizeRange; + sizeRange = _; + return chart; + }; - //============================================================ + chart.forceX = function(_) { + if (!arguments.length) return forceX; + forceX = _; + return chart; + }; + chart.forceY = function(_) { + if (!arguments.length) return forceY; + forceY = _; + return chart; + }; - return chart; -} + chart.forceSize = function(_) { + if (!arguments.length) return forceSize; + forceSize = _; + return chart; + }; -nv.models.scatter = 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() // 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 - , onlyCircles = true // Set to false to use shapes - , 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 - , pointKey = null - , 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', 'elementMouseover', 'elementMouseout') - , useVoronoi = true - ; + chart.interactive = function(_) { + if (!arguments.length) return interactive; + interactive = _; + return chart; + }; - //============================================================ + chart.pointKey = function(_) { + if (!arguments.length) return pointKey; + pointKey = _; + return chart; + }; + chart.pointActive = function(_) { + if (!arguments.length) return pointActive; + pointActive = _; + return chart; + }; - //============================================================ - // Private Variables - //------------------------------------------------------------ + chart.padData = function(_) { + if (!arguments.length) return padData; + padData = _; + return chart; + }; - 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 - ; + chart.padDataOuter = function(_) { + if (!arguments.length) return padDataOuter; + padDataOuter = _; + return chart; + }; - //============================================================ + chart.clipEdge = function(_) { + if (!arguments.length) return clipEdge; + clipEdge = _; + return chart; + }; + chart.clipVoronoi= function(_) { + if (!arguments.length) return clipVoronoi; + clipVoronoi = _; + return chart; + }; - function chart(selection) { - selection.each(function(data) { - var availableWidth = width - margin.left - margin.right, - availableHeight = height - margin.top - margin.bottom, - container = d3.select(this); + chart.useVoronoi= function(_) { + if (!arguments.length) return useVoronoi; + useVoronoi = _; + if (useVoronoi === false) { + clipVoronoi = false; + } + return chart; + }; - //add series index to each data point for reference - data = data.map(function(series, i) { - series.values = series.values.map(function(point) { - point.series = i; - return point; - }); - return series; - }); - - //------------------------------------------------------------ - // 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) } - }) - }) - ); + chart.clipRadius = function(_) { + if (!arguments.length) return clipRadius; + clipRadius = _; + return chart; + }; - x .domain(xDomain || d3.extent(seriesData.map(function(d) { return d.x; }).concat(forceX))) + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + return chart; + }; - 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]); + chart.shape = function(_) { + if (!arguments.length) return getShape; + getShape = _; + return chart; + }; - y .domain(yDomain || d3.extent(seriesData.map(function(d) { return d.y }).concat(forceY))) - .range(yRange || [availableHeight, 0]); + chart.onlyCircles = function(_) { + if (!arguments.length) return onlyCircles; + onlyCircles = _; + return chart; + }; - z .domain(sizeDomain || d3.extent(seriesData.map(function(d) { return d.size }).concat(forceSize))) - .range(sizeRange || [16, 256]); + chart.id = function(_) { + if (!arguments.length) return id; + id = _; + return chart; + }; - // 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]); + chart.singlePoint = function(_) { + if (!arguments.length) return singlePoint; + singlePoint = _; + return chart; + }; - if (y.domain()[0] === y.domain()[1]) - y.domain()[0] ? - y.domain([y.domain()[0] - y.domain()[0] * 0.01, y.domain()[1] + y.domain()[1] * 0.01]) - : y.domain([-1,1]); + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + return chart; + }; - if ( isNaN(x.domain()[0])) { - x.domain([-1,1]); - } + //============================================================ - if ( isNaN(y.domain()[0])) { - y.domain([-1,1]); - } + 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() + , controls = nv.models.legend() + , distX = nv.models.distribution() + , distY = nv.models.distribution() + ; - x0 = x0 || x; - y0 = y0 || y; - z0 = z0 || z; + var margin = {top: 30, right: 20, bottom: 50, left: 75} + , width = null + , height = null + , color = nv.utils.defaultColor() + , x = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.xScale() + , y = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.yScale() + , xPadding = 0 + , yPadding = 0 + , showDistX = false + , showDistY = false + , showLegend = true + , showXAxis = true + , showYAxis = true + , rightAlignYAxis = false + , showControls = !!d3.fisheye + , fisheye = 0 + , pauseFisheye = false + , tooltips = true + , tooltipX = function(key, x, y) { return '' + x + '' } + , tooltipY = function(key, x, y) { return '' + y + '' } + , tooltip = null + , state = nv.utils.state() + , defaultState = null + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd') + , noData = "No Data Available." + , transitionDuration = 250 + , 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') + ; + controls.updateState(false); - //------------------------------------------------------------ - // 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'); + //============================================================ + // Private Variables + //------------------------------------------------------------ - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + var x0, y0; + var 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)); - defsEnter.append('clipPath') - .attr('id', 'nv-edge-clip-' + id) - .append('rect'); + 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, chart), e.value < 0 ? 'n' : 's', null, offsetElement); + }; - wrap.select('#nv-edge-clip-' + id + ' rect') - .attr('width', availableWidth) - .attr('height', availableHeight); + var controlsData = [ + { key: 'Magnify', disabled: true } + ]; - g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : ''); + 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 updateInteractiveLayer() { + //============================================================ - if (!interactive) return false; - var eventElements; + 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); - 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); + selection.each(function(data) { + var container = d3.select(this), + that = this; + nv.utils.initSVG(container); - 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! - }) - }) - ); + 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; - //inject series and point index for reference into voronoi - if (useVoronoi === true) { + state + .setter(stateSetter(data), chart.update) + .getter(stateGetter(data)) + .update(); - if (clipVoronoi) { - var pointClipsEnter = wrap.select('defs').selectAll('.nv-point-clips') - .data([id]) - .enter(); + // DEPRECATED set state.disabled + state.disabled = data.map(function(d) { return !!d.disabled }); - pointClipsEnter.append('clipPath') - .attr('class', 'nv-point-clips') - .attr('id', 'nv-points-clip-' + id); + 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 pointClips = wrap.select('#nv-points-clip-' + id).selectAll('circle') - .data(vertices); - pointClips.enter().append('circle') - .attr('r', clipRadius); - pointClips.exit().remove(); - pointClips - .attr('cx', function(d) { return d[0] }) - .attr('cy', function(d) { return d[1] }); + //------------------------------------------------------------ + // Display noData message if there's nothing to show. - wrap.select('.nv-point-paths') - .attr('clip-path', 'url(#nv-points-clip-' + id + ')'); - } + 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'); - if(vertices.length) { - // 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]); - } + noDataText + .attr('x', margin.left + availableWidth / 2) + .attr('y', margin.top + availableHeight / 2) + .text(function(d) { return d }); - var bounds = d3.geom.polygon([ - [-10,-10], - [-10,height + 10], - [width + 10,height + 10], - [width + 10,-10] - ]); + return chart; + } else { + container.selectAll('.nv-noData').remove(); + } - var voronoi = d3.geom.voronoi(vertices).map(function(d, i) { - return { - 'data': bounds.clip(d), - 'series': vertices[i][2], - 'point': vertices[i][3] - } - }); + //------------------------------------------------------------ - var pointPaths = wrap.select('.nv-point-paths').selectAll('path') - .data(voronoi); - pointPaths.enter().append('path') - .attr('class', function(d,i) { return 'nv-path-'+i; }); - pointPaths.exit().remove(); - pointPaths - .attr('d', function(d) { - if (d.data.length === 0) - return 'M 0 0' - else - return 'M' + d.data.join('L') + 'Z'; - }); - - 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 - }); - }; + //------------------------------------------------------------ + // Setup Scales - pointPaths - .on('click', function(d) { - mouseEventCallback(d, dispatch.elementClick); - }) - .on('mouseover', function(d) { - mouseEventCallback(d, dispatch.elementMouseover); - }) - .on('mouseout', function(d, i) { - mouseEventCallback(d, dispatch.elementMouseout); - }); + x0 = x0 || x; + y0 = y0 || y; + //------------------------------------------------------------ - } 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]; + //------------------------------------------------------------ + // Setup containers and skeleton of chart - 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]; + 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'); - 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 - .transition() - .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); - - - if (onlyCircles) { - - var points = groups.selectAll('circle.nv-point') - .data(function(d) { return d.values }, pointKey); - points.enter().append('circle') - .style('fill', function (d,i) { return d.color }) - .style('stroke', function (d,i) { return d.color }) - .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) }) - .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) }) - .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) }); - points.exit().remove(); - groups.exit().selectAll('path.nv-point').transition() - .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) }) - .attr('cy', function(d,i) { return nv.utils.NaNtoZero(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.transition() - .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) }) - .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) }) - .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) }); - - } else { - - 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', - d3.svg.symbol() - .type(getShape) - .size(function(d,i) { return z(getSize(d,i)) }) - ); - points.exit().remove(); - groups.exit().selectAll('path.nv-point') - .transition() - .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.transition() - .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', - d3.svg.symbol() - .type(getShape) - .size(function(d,i) { return z(getSize(d,i)) }) - ); - } + // background for pointer events + gEnter.append('rect').attr('class', 'nvd3 nv-background'); + 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-distWrap'); + gEnter.append('g').attr('class', 'nv-legendWrap'); + gEnter.append('g').attr('class', 'nv-controlsWrap'); - // 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(); - }); + //------------------------------------------------------------ + // Legend - return chart; - } + if (showLegend) { + var legendWidth = (showControls) ? availableWidth / 2 : availableWidth; + legend.width(legendWidth); + wrap.select('.nv-legendWrap') + .datum(data) + .call(legend); - //============================================================ - // Event Handling/Dispatching (out of chart's scope) - //------------------------------------------------------------ - chart.clearHighlights = function() { - //Remove the 'hover' class from all highlighted points. - d3.selectAll(".nv-chart-" + id + " .nv-point.hover").classed("hover",false); - }; + if ( margin.top != legend.height()) { + margin.top = legend.height(); + availableHeight = (height || parseInt(container.style('height')) || 400) + - margin.top - margin.bottom; + } - chart.highlightPoint = function(seriesIndex,pointIndex,isHoverOver) { - d3.select(".nv-chart-" + id + " .nv-series-" + seriesIndex + " .nv-point-" + pointIndex) - .classed("hover",isHoverOver); - }; + wrap.select('.nv-legendWrap') + .attr('transform', 'translate(' + (availableWidth - legendWidth) + ',' + (-margin.top) +')'); + } + //------------------------------------------------------------ - dispatch.on('elementMouseover.point', function(d) { - if (interactive) chart.highlightPoint(d.seriesIndex,d.pointIndex,true); - }); - dispatch.on('elementMouseout.point', function(d) { - if (interactive) chart.highlightPoint(d.seriesIndex,d.pointIndex,false); - }); + //------------------------------------------------------------ + // Controls - //============================================================ + if (showControls) { + controls.width(180).color(['#444']); + g.select('.nv-controlsWrap') + .datum(controlsData) + .attr('transform', 'translate(0,' + (-margin.top) +')') + .call(controls); + } + //------------------------------------------------------------ - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ - chart.dispatch = dispatch; - chart.options = nv.utils.optionsFunc.bind(chart); + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - chart.x = function(_) { - if (!arguments.length) return getX; - getX = d3.functor(_); - return chart; - }; + if (rightAlignYAxis) { + g.select(".nv-y.nv-axis") + .attr("transform", "translate(" + availableWidth + ",0)"); + } - chart.y = function(_) { - if (!arguments.length) return getY; - getY = d3.functor(_); - return chart; - }; + //------------------------------------------------------------ + // 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 })); + + if (xPadding !== 0) + scatter.xDomain(null); + + if (yPadding !== 0) + scatter.yDomain(null); + wrap.select('.nv-scatterWrap') + .datum(data.filter(function(d) { return !d.disabled })) + .call(scatter); + + //Adjust for x and y padding + if (xPadding !== 0) { + var xRange = x.domain()[1] - x.domain()[0]; + scatter.xDomain([x.domain()[0] - (xPadding * xRange), x.domain()[1] + (xPadding * xRange)]); + } - chart.size = function(_) { - if (!arguments.length) return getSize; - getSize = d3.functor(_); - return chart; - }; - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + if (yPadding !== 0) { + var yRange = y.domain()[1] - y.domain()[0]; + scatter.yDomain([y.domain()[0] - (yPadding * yRange), y.domain()[1] + (yPadding * yRange)]); + } - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + //Only need to update the scatter again if x/yPadding changed the domain. + if (yPadding !== 0 || xPadding !== 0) { + wrap.select('.nv-scatterWrap') + .datum(data.filter(function(d) { return !d.disabled })) + .call(scatter); + } - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + //------------------------------------------------------------ - chart.xScale = function(_) { - if (!arguments.length) return x; - x = _; - return chart; - }; - chart.yScale = function(_) { - if (!arguments.length) return y; - y = _; - return chart; - }; + //------------------------------------------------------------ + // Setup Axes + if (showXAxis) { + xAxis + .scale(x) + .ticks( xAxis.ticks() && xAxis.ticks().length ? xAxis.ticks() : nv.utils.calcTicksX(availableWidth/100, data) ) + .tickSize( -availableHeight , 0); - chart.zScale = function(_) { - if (!arguments.length) return z; - z = _; - return chart; - }; + g.select('.nv-x.nv-axis') + .attr('transform', 'translate(0,' + y.range()[0] + ')') + .call(xAxis); - chart.xDomain = function(_) { - if (!arguments.length) return xDomain; - xDomain = _; - return chart; - }; + } - chart.yDomain = function(_) { - if (!arguments.length) return yDomain; - yDomain = _; - return chart; - }; + if (showYAxis) { + yAxis + .scale(y) + .ticks( yAxis.ticks() && yAxis.ticks().length ? yAxis.ticks() : nv.utils.calcTicksY(availableHeight/36, data) ) + .tickSize( -availableWidth, 0); - chart.sizeDomain = function(_) { - if (!arguments.length) return sizeDomain; - sizeDomain = _; - return chart; - }; + g.select('.nv-y.nv-axis') + .call(yAxis); + } - chart.xRange = function(_) { - if (!arguments.length) return xRange; - xRange = _; - return chart; - }; - chart.yRange = function(_) { - if (!arguments.length) return yRange; - yRange = _; - return chart; - }; + 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); + } - chart.sizeRange = function(_) { - if (!arguments.length) return sizeRange; - sizeRange = _; - return chart; - }; + 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); + } - chart.forceX = function(_) { - if (!arguments.length) return forceX; - forceX = _; - return chart; - }; + //------------------------------------------------------------ - chart.forceY = function(_) { - if (!arguments.length) return forceY; - forceY = _; - return chart; - }; - chart.forceSize = function(_) { - if (!arguments.length) return forceSize; - forceSize = _; - return chart; - }; - chart.interactive = function(_) { - if (!arguments.length) return interactive; - interactive = _; - return chart; - }; - chart.pointKey = function(_) { - if (!arguments.length) return pointKey; - pointKey = _; - return chart; - }; + if (d3.fisheye) { + g.select('.nv-background') + .attr('width', availableWidth) + .attr('height', availableHeight); - chart.pointActive = function(_) { - if (!arguments.length) return pointActive; - pointActive = _; - return chart; - }; + g.select('.nv-background').on('mousemove', updateFisheye); + g.select('.nv-background').on('click', function() { pauseFisheye = !pauseFisheye;}); + scatter.dispatch.on('elementClick.freezeFisheye', function() { + pauseFisheye = !pauseFisheye; + }); + } - chart.padData = function(_) { - if (!arguments.length) return padData; - padData = _; - return chart; - }; - chart.padDataOuter = function(_) { - if (!arguments.length) return padDataOuter; - padDataOuter = _; - return chart; - }; + function updateFisheye() { + if (pauseFisheye) { + g.select('.nv-point-paths').style('pointer-events', 'all'); + return false; + } - chart.clipEdge = function(_) { - if (!arguments.length) return clipEdge; - clipEdge = _; - return chart; - }; + g.select('.nv-point-paths').style('pointer-events', 'none' ); - chart.clipVoronoi= function(_) { - if (!arguments.length) return clipVoronoi; - clipVoronoi = _; - return chart; - }; + var mouse = d3.mouse(this); + x.distortion(fisheye).focus(mouse[0]); + y.distortion(fisheye).focus(mouse[1]); - chart.useVoronoi= function(_) { - if (!arguments.length) return useVoronoi; - useVoronoi = _; - if (useVoronoi === false) { - clipVoronoi = false; - } - return chart; - }; + g.select('.nv-scatterWrap') + .call(scatter); - chart.clipRadius = function(_) { - if (!arguments.length) return clipRadius; - clipRadius = _; - return chart; - }; + if (showXAxis) + g.select('.nv-x.nv-axis').call(xAxis); - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - return chart; - }; + if (showYAxis) + g.select('.nv-y.nv-axis').call(yAxis); - chart.shape = function(_) { - if (!arguments.length) return getShape; - getShape = _; - return chart; - }; + g.select('.nv-distributionX') + .datum(data.filter(function(d) { return !d.disabled })) + .call(distX); + g.select('.nv-distributionY') + .datum(data.filter(function(d) { return !d.disabled })) + .call(distY); + } - chart.onlyCircles = function(_) { - if (!arguments.length) return onlyCircles; - onlyCircles = _; - return chart; - }; - chart.id = function(_) { - if (!arguments.length) return id; - id = _; - return chart; - }; - chart.singlePoint = function(_) { - if (!arguments.length) return singlePoint; - singlePoint = _; - return chart; - }; + //============================================================ + // Event Handling/Dispatching (in chart's scope) + //------------------------------------------------------------ - //============================================================ + controls.dispatch.on('legendClick', function(d,i) { + d.disabled = !d.disabled; + fisheye = d.disabled ? 0 : 2.5; + g.select('.nv-background') .style('pointer-events', d.disabled ? 'none' : 'all'); + g.select('.nv-point-paths').style('pointer-events', d.disabled ? 'all' : 'none' ); - 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() - , controls = nv.models.legend() - , distX = nv.models.distribution() - , distY = nv.models.distribution() - ; + if (d.disabled) { + x.distortion(fisheye).focus(0); + y.distortion(fisheye).focus(0); - var margin = {top: 30, right: 20, bottom: 50, left: 75} - , width = null - , height = null - , color = nv.utils.defaultColor() - , x = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.xScale() - , y = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.yScale() - , xPadding = 0 - , yPadding = 0 - , showDistX = false - , showDistY = false - , showLegend = true - , showXAxis = true - , showYAxis = true - , rightAlignYAxis = false - , showControls = !!d3.fisheye - , fisheye = 0 - , pauseFisheye = false - , tooltips = true - , tooltipX = function(key, x, y) { return '' + x + '' } - , tooltipY = function(key, x, y) { return '' + y + '' } - , tooltip = null - , state = {} - , defaultState = null - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') - , noData = "No Data Available." - , transitionDuration = 250 - ; + g.select('.nv-scatterWrap').call(scatter); + g.select('.nv-x.nv-axis').call(xAxis); + g.select('.nv-y.nv-axis').call(yAxis); + } else { + pauseFisheye = false; + } - scatter - .xScale(x) - .yScale(y) - ; - xAxis - .orient('bottom') - .tickPadding(10) - ; - yAxis - .orient((rightAlignYAxis) ? 'right' : 'left') - .tickPadding(10) - ; - distX - .axis('x') - ; - distY - .axis('y') - ; + dispatch.stateChange(state); - controls.updateState(false); + chart.update(); + }); - //============================================================ + 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', function(d,i) { return 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()); - //============================================================ - // Private Variables - //------------------------------------------------------------ + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; + dispatch.tooltipShow(e); + }); - var x0, y0; + dispatch.on('tooltipShow', function(e) { + if (tooltips) showTooltip(e, that.parentNode); + }); - var showTooltip = function(e, offsetElement) { - //TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?) + // Update chart from a state object passed to event handler + dispatch.on('changeState', function(e) { - 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 (typeof e.disabled !== 'undefined') { + data.forEach(function(series,i) { + series.disabled = e.disabled[i]; + }); - 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, chart), e.value < 0 ? 'n' : 's', null, offsetElement); - }; + state.disabled = e.disabled; + } - var controlsData = [ - { key: 'Magnify', disabled: true } - ]; + chart.update(); + }); - //============================================================ + //============================================================ - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this), - that = this; + //store old scales for use in transitions on update + x0 = x.copy(); + y0 = y.copy(); - 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; + }); + renderWatch.renderEnd('scatterChart immediate'); + return chart; + } - //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]; - } - } + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ - //------------------------------------------------------------ - // Display noData message if there's nothing to show. + scatter.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); - if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { - var noDataText = container.selectAll('.nv-noData').data([noData]); + 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(); + }); - noDataText.enter().append('text') - .attr('class', 'nvd3 nv-noData') - .attr('dy', '-.7em') - .style('text-anchor', 'middle'); + //============================================================ + + + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ + + // expose chart's sub-components + chart.dispatch = dispatch; + chart.scatter = scatter; + chart.legend = legend; + chart.controls = controls; + chart.xAxis = xAxis; + chart.yAxis = yAxis; + chart.distX = distX; + chart.distY = distY; + // DO NOT DELETE. This is currently overridden below + // until deprecated portions are removed. + chart.state = state; + + d3.rebind(chart, scatter, 'id', 'interactive', 'pointActive', 'x', 'y', 'shape', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'sizeRange', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius', 'useVoronoi'); + chart.options = nv.utils.optionsFunc.bind(chart); + + chart.margin = function(_) { + if (!arguments.length) return margin; + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; + return chart; + }; - noDataText - .attr('x', margin.left + availableWidth / 2) - .attr('y', margin.top + availableHeight / 2) - .text(function(d) { return d }); + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; + chart.height = function(_) { + if (!arguments.length) return height; + height = _; return chart; - } else { - container.selectAll('.nv-noData').remove(); - } + }; - //------------------------------------------------------------ + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + legend.color(color); + distX.color(color); + distY.color(color); + return chart; + }; + chart.showDistX = function(_) { + if (!arguments.length) return showDistX; + showDistX = _; + return chart; + }; - //------------------------------------------------------------ - // Setup Scales + chart.showDistY = function(_) { + if (!arguments.length) return showDistY; + showDistY = _; + return chart; + }; - x0 = x0 || x; - y0 = y0 || y; + chart.showControls = function(_) { + if (!arguments.length) return showControls; + showControls = _; + return chart; + }; - //------------------------------------------------------------ + chart.showLegend = function(_) { + if (!arguments.length) return showLegend; + showLegend = _; + return chart; + }; + chart.showXAxis = function(_) { + if (!arguments.length) return showXAxis; + showXAxis = _; + return chart; + }; - //------------------------------------------------------------ - // Setup containers and skeleton of chart + chart.showYAxis = function(_) { + if (!arguments.length) return showYAxis; + showYAxis = _; + return 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'); + chart.rightAlignYAxis = function(_) { + if(!arguments.length) return rightAlignYAxis; + rightAlignYAxis = _; + yAxis.orient( (_) ? 'right' : 'left'); + return chart; + }; - // background for pointer events - gEnter.append('rect').attr('class', 'nvd3 nv-background'); - 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-distWrap'); - gEnter.append('g').attr('class', 'nv-legendWrap'); - gEnter.append('g').attr('class', 'nv-controlsWrap'); + chart.fisheye = function(_) { + if (!arguments.length) return fisheye; + fisheye = _; + return chart; + }; - //------------------------------------------------------------ + chart.xPadding = function(_) { + if (!arguments.length) return xPadding; + xPadding = _; + return chart; + }; + chart.yPadding = function(_) { + if (!arguments.length) return yPadding; + yPadding = _; + return chart; + }; - //------------------------------------------------------------ - // Legend + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; - if (showLegend) { - var legendWidth = (showControls) ? availableWidth / 2 : availableWidth; - legend.width(legendWidth); + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - wrap.select('.nv-legendWrap') - .datum(data) - .call(legend); + chart.tooltipXContent = function(_) { + if (!arguments.length) return tooltipX; + tooltipX = _; + return chart; + }; - if ( margin.top != legend.height()) { - margin.top = legend.height(); - availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; - } + chart.tooltipYContent = function(_) { + if (!arguments.length) return tooltipY; + tooltipY = _; + return chart; + }; - wrap.select('.nv-legendWrap') - .attr('transform', 'translate(' + (availableWidth - legendWidth) + ',' + (-margin.top) +')'); - } + // DEPRECATED + chart.state = function(_) { + nv.deprecated('scatterChart.state'); + if (!arguments.length) return state; + state = _; + return chart; + }; + for (var key in state) { + chart.state[key] = state[key]; + } + // END DEPRECATED - //------------------------------------------------------------ + chart.defaultState = function(_) { + if (!arguments.length) return defaultState; + defaultState = _; + return chart; + }; + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + scatter.duration(duration); + xAxis.duration(duration); + yAxis.duration(duration); + distX.duration(duration); + distY.duration(duration); + return chart; + }; + chart.transitionDuration = function(_) { + nv.deprecated('scatterChart.transitionDuration'); + return chart.duration(_); + }; - //------------------------------------------------------------ - // Controls - - if (showControls) { - controls.width(180).color(['#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) - - 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 })); - - if (xPadding !== 0) - scatter.xDomain(null); - - if (yPadding !== 0) - scatter.yDomain(null); - - wrap.select('.nv-scatterWrap') - .datum(data.filter(function(d) { return !d.disabled })) - .call(scatter); - - //Adjust for x and y padding - if (xPadding !== 0) { - var xRange = x.domain()[1] - x.domain()[0]; - scatter.xDomain([x.domain()[0] - (xPadding * xRange), x.domain()[1] + (xPadding * xRange)]); - } - - if (yPadding !== 0) { - var yRange = y.domain()[1] - y.domain()[0]; - scatter.yDomain([y.domain()[0] - (yPadding * yRange), y.domain()[1] + (yPadding * yRange)]); - } - - //Only need to update the scatter again if x/yPadding changed the domain. - if (yPadding !== 0 || xPadding !== 0) { - wrap.select('.nv-scatterWrap') - .datum(data.filter(function(d) { return !d.disabled })) - .call(scatter); - } - - //------------------------------------------------------------ - - - //------------------------------------------------------------ - // Setup Axes - if (showXAxis) { - xAxis - .scale(x) - .ticks( xAxis.ticks() && xAxis.ticks().length ? xAxis.ticks() : availableWidth / 100 ) - .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().length ? yAxis.ticks() : availableHeight / 36 ) - .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); - } - - //------------------------------------------------------------ - - - - - if (d3.fisheye) { - g.select('.nv-background') - .attr('width', availableWidth) - .attr('height', availableHeight); - - g.select('.nv-background').on('mousemove', updateFisheye); - g.select('.nv-background').on('click', function() { pauseFisheye = !pauseFisheye;}); - scatter.dispatch.on('elementClick.freezeFisheye', function() { - pauseFisheye = !pauseFisheye; - }); - } + //============================================================ - function updateFisheye() { - if (pauseFisheye) { - g.select('.nv-point-paths').style('pointer-events', 'all'); - return false; - } + return chart; +}; - g.select('.nv-point-paths').style('pointer-events', 'none' ); +nv.models.scatterPlusLineChart = 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() + , controls = nv.models.legend() + , distX = nv.models.distribution() + , distY = nv.models.distribution() + ; - var mouse = d3.mouse(this); - x.distortion(fisheye).focus(mouse[0]); - y.distortion(fisheye).focus(mouse[1]); + var margin = {top: 30, right: 20, bottom: 50, left: 75} + , width = null + , height = null + , color = nv.utils.defaultColor() + , x = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.xScale() + , y = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.yScale() + , showDistX = false + , showDistY = false + , showLegend = true + , showXAxis = true + , showYAxis = true + , rightAlignYAxis = false + , showControls = !!d3.fisheye + , fisheye = 0 + , pauseFisheye = false + , tooltips = true + , tooltipX = function(key, x, y) { return '' + x + '' } + , tooltipY = function(key, x, y) { return '' + y + '' } + , tooltip = function(key, x, y, date) { return '

' + key + '

' + + '

' + date + '

' } + , state = nv.utils.state() + , defaultState = null + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd') + , noData = "No Data Available." + , duration = 250 + ; - g.select('.nv-scatterWrap') - .call(scatter); + state.fisheye = 0; // DEPRECATED Maintained for backward compatibility - if (showXAxis) - g.select('.nv-x.nv-axis').call(xAxis); + scatter + .xScale(x) + .yScale(y) + ; + xAxis + .orient('bottom') + .tickPadding(10) + ; + yAxis + .orient((rightAlignYAxis) ? 'right' : 'left') + .tickPadding(10) + ; + distX + .axis('x') + ; + distY + .axis('y') + ; - if (showYAxis) - g.select('.nv-y.nv-axis').call(yAxis); + controls.updateState(false); + //============================================================ - g.select('.nv-distributionX') - .datum(data.filter(function(d) { return !d.disabled })) - .call(distX); - g.select('.nv-distributionY') - .datum(data.filter(function(d) { return !d.disabled })) - .call(distY); - } + //============================================================ + // Private Variables + //------------------------------------------------------------ + var x0, y0 + , renderWatch = nv.utils.renderWatch(dispatch, duration); - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ + var showTooltip = function(e, offsetElement) { + //TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?) - controls.dispatch.on('legendClick', function(d,i) { - d.disabled = !d.disabled; + 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)); - fisheye = d.disabled ? 0 : 2.5; - g.select('.nv-background') .style('pointer-events', d.disabled ? 'none' : 'all'); - g.select('.nv-point-paths').style('pointer-events', d.disabled ? 'all' : 'none' ); + 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); + }; - if (d.disabled) { - x.distortion(fisheye).focus(0); - y.distortion(fisheye).focus(0); + var controlsData = [ + { key: 'Magnify', disabled: true } + ]; - g.select('.nv-scatterWrap').call(scatter); - g.select('.nv-x.nv-axis').call(xAxis); - g.select('.nv-y.nv-axis').call(yAxis); - } else { - pauseFisheye = false; + var stateGetter = function(data) { + return function(){ + return { + active: data.map(function(d) { return !d.disabled }), + fisheye: fisheye + }; } + }; - chart.update(); - }); - - legend.dispatch.on('stateChange', function(newState) { - state.disabled = newState.disabled; - 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', function(d,i) { return 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()); + var stateSetter = function(data) { + return function(state) { + if (state.fisheye !== undefined) + fisheye = state.fisheye; + if (state.active !== undefined) + data.forEach(function(series,i) { + series.disabled = !state.active[i]; + }); + } + }; - 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) { + 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); - if (typeof e.disabled !== 'undefined') { - data.forEach(function(series,i) { - series.disabled = e.disabled[i]; - }); + selection.each(function(data) { + var container = d3.select(this), + that = this; + nv.utils.initSVG(container); - state.disabled = e.disabled; - } + 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(); - }); + 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]); - //store old scales for use in transitions on update - x0 = x.copy(); - y0 = y.copy(); + 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; - } - - - //============================================================ - // 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.controls = controls; - chart.xAxis = xAxis; - chart.yAxis = yAxis; - chart.distX = distX; - chart.distY = distY; - - d3.rebind(chart, scatter, 'id', 'interactive', 'pointActive', 'x', 'y', 'shape', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'sizeRange', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius', 'useVoronoi'); - chart.options = nv.utils.optionsFunc.bind(chart); - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + return chart; + } else { + container.selectAll('.nv-noData').remove(); + } - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + //------------------------------------------------------------ - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; - - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - legend.color(color); - distX.color(color); - distY.color(color); - return chart; - }; - chart.showDistX = function(_) { - if (!arguments.length) return showDistX; - showDistX = _; - return chart; - }; + //------------------------------------------------------------ + // Setup Scales - chart.showDistY = function(_) { - if (!arguments.length) return showDistY; - showDistY = _; - return chart; - }; + x = scatter.xScale(); + y = scatter.yScale(); - chart.showControls = function(_) { - if (!arguments.length) return showControls; - showControls = _; - return chart; - }; + x0 = x0 || x; + y0 = y0 || y; - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + //------------------------------------------------------------ - chart.showXAxis = function(_) { - if (!arguments.length) return showXAxis; - showXAxis = _; - return chart; - }; - chart.showYAxis = function(_) { - if (!arguments.length) return showYAxis; - showYAxis = _; - return chart; - }; + //------------------------------------------------------------ + // Setup containers and skeleton of chart - chart.rightAlignYAxis = function(_) { - if(!arguments.length) return rightAlignYAxis; - rightAlignYAxis = _; - yAxis.orient( (_) ? 'right' : 'left'); - return 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"); - chart.fisheye = function(_) { - if (!arguments.length) return fisheye; - fisheye = _; - return chart; - }; + gEnter.append('g').attr('class', 'nv-x nv-axis'); + gEnter.append('g').attr('class', 'nv-y nv-axis'); + gEnter.append('g').attr('class', 'nv-scatterWrap'); + gEnter.append('g').attr('class', 'nv-regressionLinesWrap'); + gEnter.append('g').attr('class', 'nv-distWrap'); + gEnter.append('g').attr('class', 'nv-legendWrap'); + gEnter.append('g').attr('class', 'nv-controlsWrap'); - chart.xPadding = function(_) { - if (!arguments.length) return xPadding; - xPadding = _; - return chart; - }; + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - chart.yPadding = function(_) { - if (!arguments.length) return yPadding; - yPadding = _; - return chart; - }; + if (rightAlignYAxis) { + g.select(".nv-y.nv-axis") + .attr("transform", "translate(" + availableWidth + ",0)"); + } - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; + //------------------------------------------------------------ - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; - chart.tooltipXContent = function(_) { - if (!arguments.length) return tooltipX; - tooltipX = _; - return chart; - }; + //------------------------------------------------------------ + // Legend - chart.tooltipYContent = function(_) { - if (!arguments.length) return tooltipY; - tooltipY = _; - return chart; - }; + if (showLegend) { + legend.width( availableWidth / 2 ); - chart.state = function(_) { - if (!arguments.length) return state; - state = _; - return chart; - }; + wrap.select('.nv-legendWrap') + .datum(data) + .call(legend); - chart.defaultState = function(_) { - if (!arguments.length) return defaultState; - defaultState = _; - return chart; - }; + if ( margin.top != legend.height()) { + margin.top = legend.height(); + availableHeight = (height || parseInt(container.style('height')) || 400) + - margin.top - margin.bottom; + } - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; + wrap.select('.nv-legendWrap') + .attr('transform', 'translate(' + (availableWidth / 2) + ',' + (-margin.top) +')'); + } - chart.transitionDuration = function(_) { - if (!arguments.length) return transitionDuration; - transitionDuration = _; - return chart; - }; + //------------------------------------------------------------ - //============================================================ + //------------------------------------------------------------ + // Controls - return chart; -} + if (showControls) { + controls.width(180).color(['#444']); + g.select('.nv-controlsWrap') + .datum(controlsData) + .attr('transform', 'translate(0,' + (-margin.top) +')') + .call(controls); + } -nv.models.scatterPlusLineChart = 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() - , controls = 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 = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.xScale() - , y = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.yScale() - , showDistX = false - , showDistY = false - , showLegend = true - , showXAxis = true - , showYAxis = true - , rightAlignYAxis = false - , showControls = !!d3.fisheye - , fisheye = 0 - , pauseFisheye = false - , tooltips = true - , tooltipX = function(key, x, y) { return '' + x + '' } - , tooltipY = function(key, x, y) { return '' + y + '' } - , tooltip = function(key, x, y, date) { return '

' + key + '

' - + '

' + date + '

' } - , state = {} - , defaultState = null - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') - , noData = "No Data Available." - , transitionDuration = 250 - ; - scatter - .xScale(x) - .yScale(y) - ; - xAxis - .orient('bottom') - .tickPadding(10) - ; - yAxis - .orient((rightAlignYAxis) ? 'right' : 'left') - .tickPadding(10) - ; - distX - .axis('x') - ; - distY - .axis('y') - ; + //------------------------------------------------------------ + // Main Chart Component(s) - controls.updateState(false); - //============================================================ + 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); - //============================================================ - // Private Variables - //------------------------------------------------------------ + wrap.select('.nv-regressionLinesWrap') + .attr('clip-path', 'url(#nv-edge-clip-' + scatter.id() + ')'); - var x0, y0; + var regWrap = wrap.select('.nv-regressionLinesWrap').selectAll('.nv-regLines') + .data(function(d) {return d }); - var showTooltip = function(e, offsetElement) { - //TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?) + regWrap.enter().append('g').attr('class', 'nv-regLines'); - 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)); + var regLine = regWrap.selectAll('.nv-regLine').data(function(d){return [d]}); + var regLineEnter = regLine.enter() + .append('line').attr('class', 'nv-regLine') + .style('stroke-opacity', 0); - 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); - }; + regLine + .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 + }); - var controlsData = [ - { key: 'Magnify', disabled: true } - ]; + //------------------------------------------------------------ - //============================================================ + //------------------------------------------------------------ + // Setup Axes - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this), - that = this; + if (showXAxis) { + xAxis + .scale(x) + .ticks( xAxis.ticks() ? xAxis.ticks() : nv.utils.calcTicksX(availableWidth/100, data) ) + .tickSize( -availableHeight , 0); - var availableWidth = (width || parseInt(container.style('width')) || 960) - - margin.left - margin.right, - availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; + g.select('.nv-x.nv-axis') + .attr('transform', 'translate(0,' + y.range()[0] + ')') + .call(xAxis); + } - chart.update = function() { container.transition().duration(transitionDuration).call(chart); }; - chart.container = this; + if (showYAxis) { + yAxis + .scale(y) + .ticks( yAxis.ticks() ? yAxis.ticks() : nv.utils.calcTicksY(availableHeight/36, data) ) + .tickSize( -availableWidth, 0); - //set state.disabled - state.disabled = data.map(function(d) { return !!d.disabled }); + g.select('.nv-y.nv-axis') + .call(yAxis); + } - 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 (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 (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { - var noDataText = container.selectAll('.nv-noData').data([noData]); + 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); + } - 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(); - } - //------------------------------------------------------------ + if (d3.fisheye) { + g.select('.nv-background') + .attr('width', availableWidth) + .attr('height', availableHeight) + ; - //------------------------------------------------------------ - // Setup Scales + g.select('.nv-background').on('mousemove', updateFisheye); + g.select('.nv-background').on('click', function() { pauseFisheye = !pauseFisheye;}); + scatter.dispatch.on('elementClick.freezeFisheye', function() { + pauseFisheye = !pauseFisheye; + }); + } - x = scatter.xScale(); - y = scatter.yScale(); + // At this point, everything has been selected and bound... I think - x0 = x0 || x; - y0 = y0 || y; - //------------------------------------------------------------ + function updateFisheye() { + if (pauseFisheye) { + g.select('.nv-point-paths').style('pointer-events', 'all'); + return false; + } + g.select('.nv-point-paths').style('pointer-events', 'none' ); - //------------------------------------------------------------ - // Setup containers and skeleton of chart + var mouse = d3.mouse(this); + x.distortion(fisheye).focus(mouse[0]); + y.distortion(fisheye).focus(mouse[1]); - 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') + g.select('.nv-scatterWrap') + .datum(data.filter(function(d) { return !d.disabled })) + .call(scatter); - // background for pointer events - gEnter.append('rect').attr('class', 'nvd3 nv-background').style("pointer-events","none"); + if (showXAxis) + g.select('.nv-x.nv-axis').call(xAxis); - gEnter.append('g').attr('class', 'nv-x nv-axis'); - gEnter.append('g').attr('class', 'nv-y nv-axis'); - gEnter.append('g').attr('class', 'nv-scatterWrap'); - gEnter.append('g').attr('class', 'nv-regressionLinesWrap'); - gEnter.append('g').attr('class', 'nv-distWrap'); - gEnter.append('g').attr('class', 'nv-legendWrap'); - gEnter.append('g').attr('class', 'nv-controlsWrap'); + if (showYAxis) + g.select('.nv-y.nv-axis').call(yAxis); - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + g.select('.nv-distributionX') + .datum(data.filter(function(d) { return !d.disabled })) + .call(distX); + g.select('.nv-distributionY') + .datum(data.filter(function(d) { return !d.disabled })) + .call(distY); + } - if (rightAlignYAxis) { - g.select(".nv-y.nv-axis") - .attr("transform", "translate(" + availableWidth + ",0)"); - } - //------------------------------------------------------------ + //============================================================ + // Event Handling/Dispatching (in chart's scope) + //------------------------------------------------------------ - //------------------------------------------------------------ - // Legend + controls.dispatch.on('legendClick', function(d,i) { + d.disabled = !d.disabled; - if (showLegend) { - legend.width( availableWidth / 2 ); + fisheye = d.disabled ? 0 : 2.5; + g.select('.nv-background') .style('pointer-events', d.disabled ? 'none' : 'all'); + g.select('.nv-point-paths').style('pointer-events', d.disabled ? 'all' : 'none' ); - wrap.select('.nv-legendWrap') - .datum(data) - .call(legend); + if (d.disabled) { + x.distortion(fisheye).focus(0); + y.distortion(fisheye).focus(0); - if ( margin.top != legend.height()) { - margin.top = legend.height(); - availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; - } + g.select('.nv-scatterWrap').call(scatter); + g.select('.nv-x.nv-axis').call(xAxis); + g.select('.nv-y.nv-axis').call(yAxis); + } else { + pauseFisheye = false; + } - wrap.select('.nv-legendWrap') - .attr('transform', 'translate(' + (availableWidth / 2) + ',' + (-margin.top) +')'); - } - - //------------------------------------------------------------ - - - //------------------------------------------------------------ - // Controls - - if (showControls) { - controls.width(180).color(['#444']); - g.select('.nv-controlsWrap') - .datum(controlsData) - .attr('transform', 'translate(0,' + (-margin.top) +')') - .call(controls); - } - - //------------------------------------------------------------ - - - //------------------------------------------------------------ - // 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]}); - var regLineEnter = regLine.enter() - .append('line').attr('class', 'nv-regLine') - .style('stroke-opacity', 0); - - regLine - .transition() - .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() : availableWidth / 100 ) - .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() : availableHeight / 36 ) - .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); - } - - //------------------------------------------------------------ - - - - - if (d3.fisheye) { - g.select('.nv-background') - .attr('width', availableWidth) - .attr('height', availableHeight) - ; + state.fisheye = fisheye; + dispatch.stateChange(state); - g.select('.nv-background').on('mousemove', updateFisheye); - g.select('.nv-background').on('click', function() { pauseFisheye = !pauseFisheye;}); - scatter.dispatch.on('elementClick.freezeFisheye', function() { - pauseFisheye = !pauseFisheye; - }); - } + chart.update(); + }); + legend.dispatch.on('stateChange', function(newState) { + for (var key in newState) + state[key] = newState[key]; + dispatch.stateChange(state); + chart.update(); + }); - function updateFisheye() { - if (pauseFisheye) { - g.select('.nv-point-paths').style('pointer-events', 'all'); - return false; - } - g.select('.nv-point-paths').style('pointer-events', 'none' ); + 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()); - var mouse = d3.mouse(this); - x.distortion(fisheye).focus(mouse[0]); - y.distortion(fisheye).focus(mouse[1]); + e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top]; + dispatch.tooltipShow(e); + }); - g.select('.nv-scatterWrap') - .datum(data.filter(function(d) { return !d.disabled })) - .call(scatter); + dispatch.on('tooltipShow', function(e) { + if (tooltips) showTooltip(e, that.parentNode); + }); - if (showXAxis) - g.select('.nv-x.nv-axis').call(xAxis); + // Update chart from a state object passed to event handler + dispatch.on('changeState', function(e) { - if (showYAxis) - g.select('.nv-y.nv-axis').call(yAxis); + if (typeof e.disabled !== 'undefined') { + data.forEach(function(series,i) { + series.disabled = e.disabled[i]; + }); - g.select('.nv-distributionX') - .datum(data.filter(function(d) { return !d.disabled })) - .call(distX); - g.select('.nv-distributionY') - .datum(data.filter(function(d) { return !d.disabled })) - .call(distY); - } + state.disabled = e.disabled; + } + if (typeof e.fisheye !== 'undefined') { + state.fisheye = e.fisheye; + fisheye = e.fisheye; + } + chart.update(); + }); - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ + //============================================================ - controls.dispatch.on('legendClick', function(d,i) { - d.disabled = !d.disabled; - fisheye = d.disabled ? 0 : 2.5; - g.select('.nv-background') .style('pointer-events', d.disabled ? 'none' : 'all'); - g.select('.nv-point-paths').style('pointer-events', d.disabled ? 'all' : 'none' ); + //store old scales for use in transitions on update + x0 = x.copy(); + y0 = y.copy(); - if (d.disabled) { - x.distortion(fisheye).focus(0); - y.distortion(fisheye).focus(0); - g.select('.nv-scatterWrap').call(scatter); - g.select('.nv-x.nv-axis').call(xAxis); - g.select('.nv-y.nv-axis').call(yAxis); - } else { - pauseFisheye = false; - } + }); - chart.update(); - }); + renderWatch.renderEnd('scatter with line immediate'); + return chart; + } - legend.dispatch.on('stateChange', function(newState) { - state = newState; - dispatch.stateChange(state); - chart.update(); - }); + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ + scatter.dispatch.on('elementMouseout.tooltip', function(e) { + dispatch.tooltipHide(e); - 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); + .attr('y1', 0); d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex) - .attr('x2', e.pos[0] + distX.size()); + .attr('x2', distY.size()); + }); + dispatch.on('tooltipHide', function() { + if (tooltips) nv.tooltip.cleanup(); + }); - 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) { + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ - if (typeof e.disabled !== 'undefined') { - data.forEach(function(series,i) { - series.disabled = e.disabled[i]; - }); + // expose chart's sub-components + chart.dispatch = dispatch; + chart.scatter = scatter; + chart.legend = legend; + chart.controls = controls; + chart.xAxis = xAxis; + chart.yAxis = yAxis; + chart.distX = distX; + chart.distY = distY; - state.disabled = e.disabled; - } + // DO NOT DELETE. This is currently overridden below + // until deprecated portions are removed. + chart.state = state; - chart.update(); - }); + d3.rebind(chart, scatter, 'id', 'interactive', 'pointActive', 'x', 'y', 'shape', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'sizeRange', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius', 'useVoronoi'); - //============================================================ + chart.options = nv.utils.optionsFunc.bind(chart); + chart.margin = function(_) { + if (!arguments.length) return margin; + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; + return chart; + }; - //store old scales for use in transitions on update - x0 = x.copy(); - y0 = y.copy(); + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - }); + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + legend.color(color); + distX.color(color); + distY.color(color); + return chart; + }; - return chart; - } + chart.showDistX = function(_) { + if (!arguments.length) return showDistX; + showDistX = _; + return chart; + }; + chart.showDistY = function(_) { + if (!arguments.length) return showDistY; + showDistY = _; + return chart; + }; - //============================================================ - // Event Handling/Dispatching (out of chart's scope) - //------------------------------------------------------------ + chart.showControls = function(_) { + if (!arguments.length) return showControls; + showControls = _; + return chart; + }; - scatter.dispatch.on('elementMouseout.tooltip', function(e) { - dispatch.tooltipHide(e); + chart.showLegend = function(_) { + if (!arguments.length) return showLegend; + showLegend = _; + return chart; + }; - 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(); - }); + chart.showXAxis = function(_) { + if (!arguments.length) return showXAxis; + showXAxis = _; + return chart; + }; - //============================================================ + chart.showYAxis = function(_) { + if (!arguments.length) return showYAxis; + showYAxis = _; + return chart; + }; + chart.rightAlignYAxis = function(_) { + if(!arguments.length) return rightAlignYAxis; + rightAlignYAxis = _; + yAxis.orient( (_) ? 'right' : 'left'); + return chart; + }; - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + chart.fisheye = function(_) { + if (!arguments.length) return fisheye; + fisheye = _; + return chart; + }; - // expose chart's sub-components - chart.dispatch = dispatch; - chart.scatter = scatter; - chart.legend = legend; - chart.controls = controls; - chart.xAxis = xAxis; - chart.yAxis = yAxis; - chart.distX = distX; - chart.distY = distY; + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; - d3.rebind(chart, scatter, 'id', 'interactive', 'pointActive', 'x', 'y', 'shape', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'sizeRange', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius', 'useVoronoi'); + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - chart.options = nv.utils.optionsFunc.bind(chart); + chart.tooltipXContent = function(_) { + if (!arguments.length) return tooltipX; + tooltipX = _; + return chart; + }; - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + chart.tooltipYContent = function(_) { + if (!arguments.length) return tooltipY; + tooltipY = _; + return chart; + }; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + // DEPRECATED + chart.state = function(_) { + nv.deprecated('scatterPlusLineChart.state'); + if (!arguments.length) return state; + state = _; + return chart; + }; + for (var key in state) { + chart.state[key] = state[key]; + } + // END DEPRECATED - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; - - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - legend.color(color); - distX.color(color); - distY.color(color); - return chart; - }; + chart.defaultState = function(_) { + if (!arguments.length) return defaultState; + defaultState = _; + return chart; + }; - chart.showDistX = function(_) { - if (!arguments.length) return showDistX; - showDistX = _; - return chart; - }; + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; - chart.showDistY = function(_) { - if (!arguments.length) return showDistY; - showDistY = _; - return chart; - }; + chart.transitionDuration = function(_) { + nv.deprecated('scatterPlusLineChart.transitionDuration'); + return chart.duration(_); + }; - chart.showControls = function(_) { - if (!arguments.length) return showControls; - showControls = _; - return chart; - }; + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + return chart; + }; - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + //============================================================ - chart.showXAxis = function(_) { - if (!arguments.length) return showXAxis; - showXAxis = _; - return chart; - }; - chart.showYAxis = function(_) { - if (!arguments.length) return showYAxis; - showYAxis = _; return chart; - }; +}; - chart.rightAlignYAxis = function(_) { - if(!arguments.length) return rightAlignYAxis; - rightAlignYAxis = _; - yAxis.orient( (_) ? 'right' : 'left'); - 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 + ; - chart.fisheye = function(_) { - if (!arguments.length) return fisheye; - fisheye = _; - return chart; - }; + //============================================================ - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + 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); - chart.tooltipXContent = function(_) { - if (!arguments.length) return tooltipX; - tooltipX = _; - return chart; - }; + //------------------------------------------------------------ + // Setup Scales - chart.tooltipYContent = function(_) { - if (!arguments.length) return tooltipY; - tooltipY = _; - return chart; - }; + x .domain(xDomain || d3.extent(data, getX )) + .range(xRange || [0, availableWidth]); - chart.state = function(_) { - if (!arguments.length) return state; - state = _; - return chart; - }; + y .domain(yDomain || d3.extent(data, getY )) + .range(yRange || [availableHeight, 0]); - chart.defaultState = function(_) { - if (!arguments.length) return defaultState; - defaultState = _; - return chart; - }; + //------------------------------------------------------------ - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; - chart.transitionDuration = function(_) { - if (!arguments.length) return transitionDuration; - transitionDuration = _; - return chart; - }; + //------------------------------------------------------------ + // 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 + ')') - 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); - - - //------------------------------------------------------------ - // 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' - }); - }); + 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)) }) + ); - return chart; - } + // 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' + }); + }); - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ - chart.options = nv.utils.optionsFunc.bind(chart); + return chart; + } - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ + chart.options = nv.utils.optionsFunc.bind(chart); - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + chart.margin = function(_) { + if (!arguments.length) return margin; + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; + return chart; + }; - chart.x = function(_) { - if (!arguments.length) return getX; - getX = d3.functor(_); - return chart; - }; + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - chart.y = function(_) { - if (!arguments.length) return getY; - getY = d3.functor(_); - return chart; - }; + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - chart.xScale = function(_) { - if (!arguments.length) return x; - x = _; - return chart; - }; + chart.x = function(_) { + if (!arguments.length) return getX; + getX = d3.functor(_); + return chart; + }; - chart.yScale = function(_) { - if (!arguments.length) return y; - y = _; - return chart; - }; + chart.y = function(_) { + if (!arguments.length) return getY; + getY = d3.functor(_); + return chart; + }; - chart.xDomain = function(_) { - if (!arguments.length) return xDomain; - xDomain = _; - return chart; - }; + chart.xScale = function(_) { + if (!arguments.length) return x; + x = _; + return chart; + }; - chart.yDomain = function(_) { - if (!arguments.length) return yDomain; - yDomain = _; - return chart; - }; + chart.yScale = function(_) { + if (!arguments.length) return y; + y = _; + return chart; + }; - chart.xRange = function(_) { - if (!arguments.length) return xRange; - xRange = _; - return chart; - }; + chart.xDomain = function(_) { + if (!arguments.length) return xDomain; + xDomain = _; + return chart; + }; - chart.yRange = function(_) { - if (!arguments.length) return yRange; - yRange = _; - return chart; - }; + chart.yDomain = function(_) { + if (!arguments.length) return yDomain; + yDomain = _; + return chart; + }; - chart.animate = function(_) { - if (!arguments.length) return animate; - animate = _; - return chart; - }; + chart.xRange = function(_) { + if (!arguments.length) return xRange; + xRange = _; + return chart; + }; - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - return chart; - }; + chart.yRange = function(_) { + if (!arguments.length) return yRange; + yRange = _; + return chart; + }; + + chart.animate = function(_) { + if (!arguments.length) return animate; + animate = _; + return chart; + }; + + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + return chart; + }; - //============================================================ + //============================================================ - return 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." - ; + "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); + 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; + 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; + chart.update = function() { chart(selection) }; + chart.container = this; - //------------------------------------------------------------ - // Display No Data message if there's nothing to show. + //------------------------------------------------------------ + // Display No Data message if there's nothing to show. - if (!data || !data.length) { - var noDataText = container.selectAll('.nv-noData').data([noData]); + if (!data || !data.length) { + var noDataText = container.selectAll('.nv-noData').data([noData]); - noDataText.enter().append('text') - .attr('class', 'nvd3 nv-noData') - .attr('dy', '-.7em') - .style('text-anchor', 'middle'); + noDataText.enter().append('text') + .attr('class', 'nvd3 nv-noData') + .attr('dy', '-.7em') + .style('text-anchor', 'middle'); - noDataText - .attr('x', margin.left + availableWidth / 2) - .attr('y', margin.top + availableHeight / 2) - .text(function(d) { return d }); + noDataText + .attr('x', margin.left + availableWidth / 2) + .attr('y', margin.top + availableHeight / 2) + .text(function(d) { return d }); - return chart; - } else { - container.selectAll('.nv-noData').remove(); - } + return chart; + } else { + container.selectAll('.nv-noData').remove(); + } - var currentValue = sparkline.y()(data[data.length-1], data.length-1); + var currentValue = sparkline.y()(data[data.length-1], data.length-1); - //------------------------------------------------------------ + //------------------------------------------------------------ - //------------------------------------------------------------ - // Setup Scales + //------------------------------------------------------------ + // Setup Scales - x = sparkline.xScale(); - y = sparkline.yScale(); + x = sparkline.xScale(); + y = sparkline.yScale(); - //------------------------------------------------------------ + //------------------------------------------------------------ - //------------------------------------------------------------ - // Setup containers and skeleton of chart + //------------------------------------------------------------ + // 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'); + var wrap = container.selectAll('g.nv-wrap.nv-sparklineplus').data([data]); + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparklineplus'); + var gEnter = wrapEnter.append('g'); + var g = wrap.select('g'); - gEnter.append('g').attr('class', 'nv-sparklineWrap'); - gEnter.append('g').attr('class', 'nv-valueWrap'); - gEnter.append('g').attr('class', 'nv-hoverArea'); + gEnter.append('g').attr('class', 'nv-sparklineWrap'); + gEnter.append('g').attr('class', 'nv-valueWrap'); + gEnter.append('g').attr('class', 'nv-hoverArea'); - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - //------------------------------------------------------------ + //------------------------------------------------------------ - //------------------------------------------------------------ - // Main Chart Component(s) + //------------------------------------------------------------ + // Main Chart Component(s) - var sparklineWrap = g.select('.nv-sparklineWrap'); + var sparklineWrap = g.select('.nv-sparklineWrap'); - sparkline - .width(availableWidth) - .height(availableHeight); + sparkline + .width(availableWidth) + .height(availableHeight); - sparklineWrap - .call(sparkline); + sparklineWrap + .call(sparkline); - //------------------------------------------------------------ + //------------------------------------------------------------ - var valueWrap = g.select('.nv-valueWrap'); + var valueWrap = g.select('.nv-valueWrap'); - var value = valueWrap.selectAll('.nv-currentValue') - .data([currentValue]); + var value = valueWrap.selectAll('.nv-currentValue') + .data([currentValue]); - value.enter().append('text').attr('class', 'nv-currentValue') - .attr('dx', rightAlignValue ? -8 : 8) - .attr('dy', '.9em') - .style('text-anchor', rightAlignValue ? 'end' : 'start'); + value.enter().append('text').attr('class', 'nv-currentValue') + .attr('dx', rightAlignValue ? -8 : 8) + .attr('dy', '.9em') + .style('text-anchor', rightAlignValue ? 'end' : 'start'); - value - .attr('x', availableWidth + (rightAlignValue ? margin.right : 0)) - .attr('y', alignValue ? function(d) { return y(d) } : 0) - .style('fill', sparkline.color()(data[data.length-1], data.length-1)) - .text(yTickFormat(currentValue)); + value + .attr('x', availableWidth + (rightAlignValue ? margin.right : 0)) + .attr('y', alignValue ? function(d) { return y(d) } : 0) + .style('fill', sparkline.color()(data[data.length-1], data.length-1)) + .text(yTickFormat(currentValue)); - gEnter.select('.nv-hoverArea').append('rect') - .on('mousemove', sparklineHover) - .on('click', function() { paused = !paused }) - .on('mouseout', function() { index = []; updateValueLine(); }); - //.on('mouseout', function() { index = null; updateValueLine(); }); + gEnter.select('.nv-hoverArea').append('rect') + .on('mousemove', sparklineHover) + .on('click', function() { paused = !paused }) + .on('mouseout', function() { index = []; updateValueLine(); }); + //.on('mouseout', function() { index = null; updateValueLine(); }); - g.select('.nv-hoverArea rect') - .attr('transform', function(d) { return 'translate(' + -margin.left + ',' + -margin.top + ')' }) - .attr('width', availableWidth + margin.left + margin.right) - .attr('height', availableHeight + margin.top); + g.select('.nv-hoverArea rect') + .attr('transform', function(d) { return 'translate(' + -margin.left + ',' + -margin.top + ')' }) + .attr('width', availableWidth + margin.left + margin.right) + .attr('height', availableHeight + margin.top); - function updateValueLine() { //index is currently global (within the chart), may or may not keep it that way - if (paused) return; + function updateValueLine() { //index is currently global (within the chart), may or may not keep it that way + if (paused) return; - var hoverValue = g.selectAll('.nv-hoverValue').data(index) + var hoverValue = g.selectAll('.nv-hoverValue').data(index) - var hoverEnter = hoverValue.enter() - .append('g').attr('class', 'nv-hoverValue') - .style('stroke-opacity', 0) - .style('fill-opacity', 0); + var hoverEnter = hoverValue.enter() + .append('g').attr('class', 'nv-hoverValue') + .style('stroke-opacity', 0) + .style('fill-opacity', 0); - hoverValue.exit() - .transition().duration(250) - .style('stroke-opacity', 0) - .style('fill-opacity', 0) - .remove(); + hoverValue.exit() + .transition().duration(250) + .style('stroke-opacity', 0) + .style('fill-opacity', 0) + .remove(); - hoverValue - .attr('transform', function(d) { return 'translate(' + x(sparkline.x()(data[d],d)) + ',0)' }) - .transition().duration(250) - .style('stroke-opacity', 1) - .style('fill-opacity', 1); + hoverValue + .attr('transform', function(d) { return 'translate(' + x(sparkline.x()(data[d],d)) + ',0)' }) + .transition().duration(250) + .style('stroke-opacity', 1) + .style('fill-opacity', 1); - if (!index.length) return; + if (!index.length) return; - hoverEnter.append('line') - .attr('x1', 0) - .attr('y1', -margin.top) - .attr('x2', 0) - .attr('y2', availableHeight); + hoverEnter.append('line') + .attr('x1', 0) + .attr('y1', -margin.top) + .attr('x2', 0) + .attr('y2', availableHeight); - hoverEnter.append('text').attr('class', 'nv-xValue') - .attr('x', -6) - .attr('y', -margin.top) - .attr('text-anchor', 'end') - .attr('dy', '.9em') + hoverEnter.append('text').attr('class', 'nv-xValue') + .attr('x', -6) + .attr('y', -margin.top) + .attr('text-anchor', 'end') + .attr('dy', '.9em') - g.select('.nv-hoverValue .nv-xValue') - .text(xTickFormat(sparkline.x()(data[index[0]], index[0]))); + g.select('.nv-hoverValue .nv-xValue') + .text(xTickFormat(sparkline.x()(data[index[0]], index[0]))); - hoverEnter.append('text').attr('class', 'nv-yValue') - .attr('x', 6) - .attr('y', -margin.top) - .attr('text-anchor', 'start') - .attr('dy', '.9em') + hoverEnter.append('text').attr('class', 'nv-yValue') + .attr('x', 6) + .attr('y', -margin.top) + .attr('text-anchor', 'start') + .attr('dy', '.9em') - g.select('.nv-hoverValue .nv-yValue') - .text(yTickFormat(sparkline.y()(data[index[0]], index[0]))); + g.select('.nv-hoverValue .nv-yValue') + .text(yTickFormat(sparkline.y()(data[index[0]], index[0]))); - } + } - function sparklineHover() { - if (paused) return; + function sparklineHover() { + if (paused) return; - var pos = d3.mouse(this)[0] - margin.left; + var pos = d3.mouse(this)[0] - margin.left; - function getClosestIndex(data, x) { - var distance = Math.abs(sparkline.x()(data[0], 0) - x); - var closestIndex = 0; - for (var i = 0; i < data.length; i++){ - if (Math.abs(sparkline.x()(data[i], i) - x) < distance) { - distance = Math.abs(sparkline.x()(data[i], i) - x); - closestIndex = i; - } - } - return closestIndex; - } + 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)))]; + index = [getClosestIndex(data, Math.round(x.invert(pos)))]; - updateValueLine(); - } + updateValueLine(); + } - }); + }); - return chart; - } + return chart; + } - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ - // expose chart's sub-components - chart.sparkline = sparkline; + // expose chart's sub-components + chart.sparkline = sparkline; - d3.rebind(chart, sparkline, 'x', 'y', 'xScale', 'yScale', 'color'); + d3.rebind(chart, sparkline, 'x', 'y', 'xScale', 'yScale', 'color'); - chart.options = nv.utils.optionsFunc.bind(chart); + chart.options = nv.utils.optionsFunc.bind(chart); - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + chart.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.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - chart.xTickFormat = function(_) { - if (!arguments.length) return xTickFormat; - xTickFormat = _; - return chart; - }; + chart.xTickFormat = function(_) { + if (!arguments.length) return xTickFormat; + xTickFormat = _; + return chart; + }; - chart.yTickFormat = function(_) { - if (!arguments.length) return yTickFormat; - yTickFormat = _; - return chart; - }; + chart.yTickFormat = function(_) { + if (!arguments.length) return yTickFormat; + yTickFormat = _; + return chart; + }; - chart.showValue = function(_) { - if (!arguments.length) return showValue; - showValue = _; - return chart; - }; + chart.showValue = function(_) { + if (!arguments.length) return showValue; + showValue = _; + return chart; + }; - chart.alignValue = function(_) { - if (!arguments.length) return alignValue; - alignValue = _; - return chart; - }; + chart.alignValue = function(_) { + if (!arguments.length) return alignValue; + alignValue = _; + return chart; + }; - chart.rightAlignValue = function(_) { - if (!arguments.length) return rightAlignValue; - rightAlignValue = _; - return chart; - }; + chart.rightAlignValue = function(_) { + if (!arguments.length) return rightAlignValue; + rightAlignValue = _; + return chart; + }; - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; - //============================================================ + //============================================================ - return 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() - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'areaClick', 'areaMouseover', 'areaMouseout') - ; + "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 - .size(2.2) // default size - .sizeDomain([2.2,2.2]) // all the same size by default + // scatter is interactive by default, but this chart isn't so must disable + scatter.interactive(false); + + scatter + .size(2.2) // default size + .sizeDomain([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) - ************************************/ - - //============================================================ - - - function chart(selection) { - selection.each(function(data) { - var availableWidth = width - margin.left - margin.right, - availableHeight = height - margin.top - margin.bottom, - container = d3.select(this); - - //------------------------------------------------------------ - // Setup Scales - - x = scatter.xScale(); - y = scatter.yScale(); - - //------------------------------------------------------------ - - - // Injecting point index into each point because d3.layout.stack().out does not give index - data = data.map(function(aseries, i) { - aseries.seriesIndex = i; - aseries.values = aseries.values.map(function(d, j) { - d.index = j; - d.seriesIndex = i; - return d; - }) - return aseries; - }); - - 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) { + /************************************ + * 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 + y: yHeight, + y0: y0 }; }) - (dataFiltered); + (dataFiltered); - //------------------------------------------------------------ - // Setup containers and skeleton of chart + //------------------------------------------------------------ + // 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'); + var wrap = container.selectAll('g.nv-wrap.nv-stackedarea').data([data]); + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedarea'); + var defsEnter = wrapEnter.append('defs'); + var gEnter = wrapEnter.append('g'); + var g = wrap.select('g'); - gEnter.append('g').attr('class', 'nv-areaWrap'); - gEnter.append('g').attr('class', 'nv-scatterWrap'); + gEnter.append('g').attr('class', 'nv-areaWrap'); + gEnter.append('g').attr('class', 'nv-scatterWrap'); - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - //------------------------------------------------------------ + //------------------------------------------------------------ - 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); - })); + 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); + var scatterWrap = g.select('.nv-scatterWrap') + .datum(data); - scatterWrap.call(scatter); + scatterWrap.call(scatter); - defsEnter.append('clipPath') - .attr('id', 'nv-edge-clip-' + id) - .append('rect'); + defsEnter.append('clipPath') + .attr('id', 'nv-edge-clip-' + id) + .append('rect'); - wrap.select('#nv-edge-clip-' + id + ' rect') - .attr('width', availableWidth) - .attr('height', availableHeight); + wrap.select('#nv-edge-clip-' + id + ' rect') + .attr('width', availableWidth) + .attr('height', availableHeight); - g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : ''); + g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : ''); - var area = d3.svg.area() - .x(function(d,i) { return x(getX(d,i)) }) - .y0(function(d) { - return y(d.display.y0) - }) - .y1(function(d) { - return y(d.display.y + d.display.y0) - }) - .interpolate(interpolate); + var area = d3.svg.area() + .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 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 }); + 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: i - }); - }) - .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: i + 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); }); - }) - .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: i + scatter.dispatch.on('elementMouseout.area', function(e) { + g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', false); }); - }) - path.exit().transition() - .attr('d', function(d,i) { return zeroArea(d.values,i) }) - .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.transition() - .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; + }; + }); - return chart; - } + renderWatch.renderEnd('stackedArea immediate'); + return chart; + } - //============================================================ - // Event Handling/Dispatching (out of chart's scope) - //------------------------------------------------------------ + //============================================================ + // 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) { + 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.tooltipShow(e); + }); + scatter.dispatch.on('elementMouseout.tooltip', function(e) { dispatch.tooltipHide(e); - }); + }); + + //============================================================ + + //============================================================ + // Global getters and setters + //------------------------------------------------------------ - //============================================================ + chart.dispatch = dispatch; + chart.scatter = scatter; + d3.rebind(chart, scatter, 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', + 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'useVoronoi','clipRadius','highlightPoint','clearHighlights'); - //============================================================ - // Global getters and setters - //------------------------------------------------------------ + chart.options = nv.utils.optionsFunc.bind(chart); - chart.dispatch = dispatch; - chart.scatter = scatter; + chart.x = function(_) { + if (!arguments.length) return getX; + getX = d3.functor(_); + return chart; + }; - d3.rebind(chart, scatter, 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', - 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'useVoronoi','clipRadius','highlightPoint','clearHighlights'); + chart.y = function(_) { + if (!arguments.length) return getY; + getY = d3.functor(_); + return chart; + } - chart.options = nv.utils.optionsFunc.bind(chart); + chart.margin = function(_) { + if (!arguments.length) return margin; + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; + return chart; + }; - chart.x = function(_) { - if (!arguments.length) return getX; - getX = d3.functor(_); - return chart; - }; + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - chart.y = function(_) { - if (!arguments.length) return getY; - getY = d3.functor(_); - return chart; - } - - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + chart.clipEdge = function(_) { + if (!arguments.length) return clipEdge; + clipEdge = _; + return chart; + }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + return chart; + }; - chart.clipEdge = function(_) { - if (!arguments.length) return clipEdge; - clipEdge = _; - return chart; - }; + chart.offset = function(_) { + if (!arguments.length) return offset; + offset = _; + return chart; + }; - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - return chart; - }; + chart.order = function(_) { + if (!arguments.length) return order; + order = _; + return chart; + }; - chart.offset = function(_) { - if (!arguments.length) return offset; - offset = _; - return chart; - }; + //shortcut for offset + order + chart.style = function(_) { + if (!arguments.length) return style; + style = _; - chart.order = function(_) { - if (!arguments.length) return order; - order = _; - return chart; - }; - - //shortcut for offset + order - chart.style = function(_) { - if (!arguments.length) return style; - 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; - } + 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; + } - return chart; - }; + return chart; + }; + + chart.interpolate = function(_) { + if (!arguments.length) return interpolate; + interpolate = _; + return chart; + }; - chart.interpolate = function(_) { - if (!arguments.length) return interpolate; - interpolate = _; - return chart; - }; - //============================================================ + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + scatter.duration(duration); + return chart; + }; + //============================================================ - return 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() - ; + "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() // a function that takes in d, i and returns color - , showControls = true - , showLegend = true - , showXAxis = true - , showYAxis = true - , rightAlignYAxis = false - , useInteractiveGuideline = false - , tooltips = true - , tooltip = function(key, x, y, e, graph) { - return '

' + key + '

' + - '

' + y + ' on ' + x + '

' - } - , x //can be accessed via chart.xScale() - , y //can be accessed via chart.yScale() - , yAxisTickFormat = d3.format(',.2f') - , state = { style: stacked.style() } - , defaultState = null - , noData = 'No Data Available.' - , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') - , controlWidth = 250 - , cData = ['Stacked','Stream','Expanded'] - , transitionDuration = 250 - ; + var margin = {top: 30, right: 25, bottom: 50, left: 60} + , width = null + , height = null + , color = nv.utils.defaultColor() // a function that takes in d, i and returns color + , showControls = true + , showLegend = true + , showXAxis = true + , showYAxis = true + , rightAlignYAxis = false + , useInteractiveGuideline = false + , tooltips = true + , tooltip = function(key, x, y, e, graph) { + return '

' + key + '

' + + '

' + y + ' on ' + x + '

' + } + , 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) + xAxis + .orient('bottom') + .tickPadding(7) ; - yAxis - .orient((rightAlignYAxis) ? 'right' : 'left') + yAxis + .orient((rightAlignYAxis) ? 'right' : 'left') ; - controls.updateState(false); - //============================================================ + controls.updateState(false); + //============================================================ - //============================================================ - // Private Variables - //------------------------------------------------------------ + //============================================================ + // 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); + 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); + }; - 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) { - selection.each(function(data) { - var container = d3.select(this), - 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; + function chart(selection) { + renderWatch.reset(); + renderWatch.models(stacked); + if (showXAxis) renderWatch.models(xAxis); + if (showYAxis) renderWatch.models(yAxis); - chart.update = function() { container.transition().duration(transitionDuration).call(chart); }; - chart.container = this; + selection.each(function(data) { + var container = d3.select(this), + that = this; + nv.utils.initSVG(container); - //set state.disabled - state.disabled = data.map(function(d) { return !!d.disabled }); + var availableWidth = (width || parseInt(container.style('width')) || 960) + - margin.left - margin.right, + availableHeight = (height || parseInt(container.style('height')) || 400) + - margin.top - margin.bottom; - 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]; - } - } + chart.update = function() { container.transition().duration(duration).call(chart); }; + chart.container = this; - //------------------------------------------------------------ - // Display No Data message if there's nothing to show. + state + .setter(stateSetter(data), chart.update) + .getter(stateGetter(data)) + .update(); - if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { - var noDataText = container.selectAll('.nv-noData').data([noData]); + // DEPRECATED set state.disabled + state.disabled = data.map(function(d) { return !!d.disabled }); - noDataText.enter().append('text') - .attr('class', 'nvd3 nv-noData') - .attr('dy', '-.7em') - .style('text-anchor', 'middle'); + 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]; + } + } - noDataText - .attr('x', margin.left + availableWidth / 2) - .attr('y', margin.top + availableHeight / 2) - .text(function(d) { return d }); + //------------------------------------------------------------ + // Display No Data message if there's nothing to show. - return chart; - } else { - container.selectAll('.nv-noData').remove(); - } + 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 }); - //------------------------------------------------------------ - // Setup Scales + return chart; + } else { + container.selectAll('.nv-noData').remove(); + } - x = stacked.xScale(); - y = stacked.yScale(); + //------------------------------------------------------------ - //------------------------------------------------------------ + //------------------------------------------------------------ + // Setup Scales - //------------------------------------------------------------ - // Setup containers and skeleton of chart + x = stacked.xScale(); + y = stacked.yScale(); - 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 + //------------------------------------------------------------ + // Setup containers and skeleton of chart - if (showLegend) { - var legendWidth = (showControls) ? availableWidth - controlWidth : availableWidth; - legend - .width(legendWidth); + 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'); - g.select('.nv-legendWrap') - .datum(data) - .call(legend); + 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'); - if ( margin.top != legend.height()) { - margin.top = legend.height(); - availableHeight = (height || parseInt(container.style('height')) || 400) - - margin.top - margin.bottom; - } + g.select("rect").attr("width",availableWidth).attr("height",availableHeight); + //------------------------------------------------------------ + // Legend - g.select('.nv-legendWrap') - .attr('transform', 'translate(' + (availableWidth-legendWidth) + ',' + (-margin.top) +')'); - } + 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: 'Stacked', disabled: stacked.offset() != 'zero' }, - { key: 'Stream', disabled: stacked.offset() != 'wiggle' }, - { key: 'Expanded', disabled: stacked.offset() != 'expand' } - ]; - controlWidth = (cData.length/3) * 260; + //------------------------------------------------------------ + // Controls - controlsData = controlsData.filter(function(d) { - return cData.indexOf(d.key) > -1; - }) + 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' + } + ]; - controls - .width( controlWidth ) - .color(['#444', '#444', '#444']); + controlWidth = (cData.length/3) * 260; - g.select('.nv-controlsWrap') - .datum(controlsData) - .call(controls); + controlsData = controlsData.filter(function(d) { + return cData.indexOf(d.metaKey) !== -1; + }); + controls + .width( controlWidth ) + .color(['#444', '#444', '#444']); - 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') + .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) +')'); - } + g.select('.nv-controlsWrap') + .attr('transform', 'translate(0,' + (-margin.top) +')'); + } - //------------------------------------------------------------ + //------------------------------------------------------------ - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - if (rightAlignYAxis) { - g.select(".nv-y.nv-axis") - .attr("transform", "translate(" + availableWidth + ",0)"); - } + if (rightAlignYAxis) { + g.select(".nv-y.nv-axis") + .attr("transform", "translate(" + availableWidth + ",0)"); + } - //------------------------------------------------------------ - // Main Chart Component(s) + //------------------------------------------------------------ + // Main Chart Component(s) + + //------------------------------------------------------------ + //Set up interactive layer + if (useInteractiveGuideline) { + interactiveLayer + .width(availableWidth) + .height(availableHeight) + .margin({left: margin.left, top: margin.top}) + .svgContainer(container) + .xScale(x); + wrap.select(".nv-interactive").call(interactiveLayer); + } - //------------------------------------------------------------ - //Set up interactive layer - if (useInteractiveGuideline) { - interactiveLayer - .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); - stacked - .width(availableWidth) - .height(availableHeight) + var stackedWrap = g.select('.nv-stackedWrap') + .datum(data); - var stackedWrap = g.select('.nv-stackedWrap') - .datum(data); + stackedWrap.transition().call(stacked); - stackedWrap.transition().call(stacked); + //------------------------------------------------------------ - //------------------------------------------------------------ + //------------------------------------------------------------ + // Setup Axes - //------------------------------------------------------------ - // Setup Axes + if (showXAxis) { + xAxis + .scale(x) + .ticks( nv.utils.calcTicksX(availableWidth/100, data) ) + .tickSize( -availableHeight, 0); - if (showXAxis) { - xAxis - .scale(x) - .ticks( availableWidth / 100 ) - .tickSize( -availableHeight, 0); + g.select('.nv-x.nv-axis') + .attr('transform', 'translate(0,' + availableHeight + ')'); - g.select('.nv-x.nv-axis') - .attr('transform', 'translate(0,' + availableHeight + ')'); + g.select('.nv-x.nv-axis') + .transition().duration(0) + .call(xAxis); + } - 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); + } - if (showYAxis) { - yAxis - .scale(y) - .ticks(stacked.offset() == 'wiggle' ? 0 : availableHeight / 36) - .tickSize(-availableWidth, 0) - .setTickFormat(stacked.offset() == 'expand' ? 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); + }); - //============================================================ - // Event Handling/Dispatching (in chart's scope) - //------------------------------------------------------------ + state.disabled = data.map(function(d) { return !!d.disabled }); + dispatch.stateChange(state); - stacked.dispatch.on('areaClick.toggle', function(e) { - if (data.filter(function(d) { return !d.disabled }).length === 1) - data = data.map(function(d) { - d.disabled = false; - return d - }); - else - data = data.map(function(d,i) { - d.disabled = (i != e.seriesIndex); - return d - }); + 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; - state.disabled = data.map(function(d) { return !!d.disabled }); - dispatch.stateChange(state); + controlsData = controlsData.map(function(s) { + s.disabled = true; + return s; + }); + d.disabled = false; - chart.update(); - }); + stacked.style(d.style); - legend.dispatch.on('stateChange', function(newState) { - state.disabled = newState.disabled; - dispatch.stateChange(state); - chart.update(); - }); - controls.dispatch.on('legendClick', function(d,i) { - if (!d.disabled) return; + state.style = stacked.style(); + dispatch.stateChange(state); - controlsData = controlsData.map(function(s) { - s.disabled = true; - return s; - }); - d.disabled = false; - - switch (d.key) { - case 'Stacked': - stacked.style('stack'); - break; - case 'Stream': - stacked.style('stream'); - break; - case 'Expanded': - stacked.style('expand'); - break; - } + 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; + } - 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)); - allData.push({ - key: series.key, - value: chart.y()(point, pointIndex), - color: color(series,series.seriesIndex) - }); - }); - - 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( - { + 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.renderGuideLine(pointXLocation); - }); + }); - interactiveLayer.dispatch.on("elementMouseout",function(e) { - dispatch.tooltipHide(); - stacked.clearHighlights(); - }); + interactiveLayer.dispatch.on("elementMouseout",function(e) { + dispatch.tooltipHide(); + stacked.clearHighlights(); + }); - dispatch.on('tooltipShow', function(e) { - if (tooltips) showTooltip(e, that.parentNode); - }); + 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) { + // 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]; - }); + if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) { + data.forEach(function(series,i) { + series.disabled = e.disabled[i]; + }); - state.disabled = e.disabled; - } + state.disabled = e.disabled; + } - if (typeof e.style !== 'undefined') { - stacked.style(e.style); - } + if (typeof e.style !== 'undefined') { + stacked.style(e.style); + style = e.style; + } - chart.update(); - }); + chart.update(); + }); + + }); + + renderWatch.renderEnd('stacked Area chart immediate'); + return chart; + } + + + //============================================================ + // Event Handling/Dispatching (out of chart's scope) + //------------------------------------------------------------ + stacked.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(stacked.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); }); + stacked.dispatch.on('tooltipHide', function(e) { + dispatch.tooltipHide(e); + }); - return chart; - } + dispatch.on('tooltipHide', function() { + if (tooltips) nv.tooltip.cleanup(); + }); + //============================================================ - //============================================================ - // Event Handling/Dispatching (out of chart's scope) - //------------------------------------------------------------ - stacked.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(stacked.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; - } - */ + //============================================================ + // Expose Public Variables + //------------------------------------------------------------ - e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top], - dispatch.tooltipShow(e); - }); + // 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; - stacked.dispatch.on('tooltipHide', function(e) { - dispatch.tooltipHide(e); - }); + // DO NOT DELETE. This is currently overridden below + // until deprecated portions are removed. + chart.state = state; - dispatch.on('tooltipHide', function() { - if (tooltips) nv.tooltip.cleanup(); - }); + d3.rebind(chart, stacked, 'x', 'y', 'size', 'xScale', 'yScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'interactive', 'useVoronoi', 'offset', 'order', 'style', 'clipEdge', 'forceX', 'forceY', 'forceSize', 'interpolate'); - //============================================================ + chart.options = nv.utils.optionsFunc.bind(chart); + chart.margin = function(_) { + if (!arguments.length) return margin; + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; + return chart; + }; - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ + chart.width = function(_) { + if (!arguments.length) return width; + width = _; + return chart; + }; - // 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; + chart.height = function(_) { + if (!arguments.length) return height; + height = _; + return chart; + }; - d3.rebind(chart, stacked, 'x', 'y', 'size', 'xScale', 'yScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'interactive', 'useVoronoi', 'offset', 'order', 'style', 'clipEdge', 'forceX', 'forceY', 'forceSize', 'interpolate'); + chart.color = function(_) { + if (!arguments.length) return color; + color = nv.utils.getColor(_); + legend.color(color); + stacked.color(color); + return chart; + }; - chart.options = nv.utils.optionsFunc.bind(chart); + chart.showControls = function(_) { + if (!arguments.length) return showControls; + showControls = _; + return chart; + }; - chart.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.right = typeof _.right != 'undefined' ? _.right : margin.right; - margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return chart; - }; + chart.showLegend = function(_) { + if (!arguments.length) return showLegend; + showLegend = _; + return chart; + }; - chart.width = function(_) { - if (!arguments.length) return width; - width = _; - return chart; - }; + chart.showXAxis = function(_) { + if (!arguments.length) return showXAxis; + showXAxis = _; + return chart; + }; - chart.height = function(_) { - if (!arguments.length) return height; - height = _; - return chart; - }; + chart.showYAxis = function(_) { + if (!arguments.length) return showYAxis; + showYAxis = _; + return chart; + }; - chart.color = function(_) { - if (!arguments.length) return color; - color = nv.utils.getColor(_); - legend.color(color); - stacked.color(color); - return chart; - }; + chart.rightAlignYAxis = function(_) { + if(!arguments.length) return rightAlignYAxis; + rightAlignYAxis = _; + yAxis.orient( (_) ? 'right' : 'left'); + return chart; + }; - chart.showControls = function(_) { - if (!arguments.length) return showControls; - showControls = _; - return chart; - }; + chart.useInteractiveGuideline = function(_) { + if(!arguments.length) return useInteractiveGuideline; + useInteractiveGuideline = _; + if (_ === true) { + chart.interactive(false); + chart.useVoronoi(false); + } + return chart; + }; - chart.showLegend = function(_) { - if (!arguments.length) return showLegend; - showLegend = _; - return chart; - }; + chart.tooltip = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - chart.showXAxis = function(_) { - if (!arguments.length) return showXAxis; - showXAxis = _; - return chart; - }; + chart.tooltips = function(_) { + if (!arguments.length) return tooltips; + tooltips = _; + return chart; + }; - chart.showYAxis = function(_) { - if (!arguments.length) return showYAxis; - showYAxis = _; - return chart; - }; + chart.tooltipContent = function(_) { + if (!arguments.length) return tooltip; + tooltip = _; + return chart; + }; - chart.rightAlignYAxis = function(_) { - if(!arguments.length) return rightAlignYAxis; - rightAlignYAxis = _; - yAxis.orient( (_) ? 'right' : 'left'); - return chart; - }; - - chart.useInteractiveGuideline = function(_) { - if(!arguments.length) return useInteractiveGuideline; - useInteractiveGuideline = _; - if (_ === true) { - chart.interactive(false); - chart.useVoronoi(false); + // DEPRECATED + chart.state = function(_) { + nv.deprecated('stackedAreaChart.state'); + if (!arguments.length) return state; + state = _; + return chart; + }; + for (var key in state) { + chart.state[key] = state[key]; } - return chart; - }; - - chart.tooltip = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + // END DEPRECATED - chart.tooltips = function(_) { - if (!arguments.length) return tooltips; - tooltips = _; - return chart; - }; + chart.defaultState = function(_) { + if (!arguments.length) return defaultState; + defaultState = _; + return chart; + }; - chart.tooltipContent = function(_) { - if (!arguments.length) return tooltip; - tooltip = _; - return chart; - }; + chart.noData = function(_) { + if (!arguments.length) return noData; + noData = _; + return chart; + }; - chart.state = function(_) { - if (!arguments.length) return state; - state = _; - return chart; - }; + chart.transitionDuration = function(_) { + nv.deprecated('lineChart.transitionDuration'); + return chart.duration(_); + }; - chart.defaultState = function(_) { - if (!arguments.length) return defaultState; - defaultState = _; - return chart; - }; + chart.controlsData = function(_) { + if (!arguments.length) return cData; + cData = _; + return chart; + }; - chart.noData = function(_) { - if (!arguments.length) return noData; - noData = _; - return chart; - }; + chart.controlLabels = function(_) { + if (!arguments.length) return controlLabels; + if (typeof _ !== 'object') return controlLabels; + controlLabels = _; + return chart; + }; - chart.transitionDuration = function(_) { - if (!arguments.length) return transitionDuration; - transitionDuration = _; - return chart; - }; + yAxis.setTickFormat = yAxis.tickFormat; - chart.controlsData = function(_) { - if (!arguments.length) return cData; - cData = _; - return chart; - }; + yAxis.tickFormat = function(_) { + if (!arguments.length) return yAxisTickFormat; + yAxisTickFormat = _; + return yAxis; + }; - yAxis.setTickFormat = yAxis.tickFormat; + chart.duration = function(_) { + if (!arguments.length) return duration; + duration = _; + renderWatch.reset(duration); + stacked.duration(duration); + xAxis.duration(duration); + yAxis.duration(duration); + return chart; + }; - yAxis.tickFormat = function(_) { - if (!arguments.length) return yAxisTickFormat; - yAxisTickFormat = _; - return yAxis; - }; + //============================================================ - //============================================================ + return chart; +}; - return chart; -} -})(); +nv.version = "1.6.0"; +})(); \ No newline at end of file diff --git a/xonstat/static/js/nv.d3.min.js b/xonstat/static/js/nv.d3.min.js index cc56976..33c93ab 100644 --- a/xonstat/static/js/nv.d3.min.js +++ b/xonstat/static/js/nv.d3.min.js @@ -1 +1,8 @@ -(function(){var c=window.nv||{};c.version="1.1.10b";c.dev=true;window.nv=c;c.tooltip={};c.utils={};c.models={};c.charts={};c.graphs=[];c.logs={};c.dispatch=d3.dispatch("render_start","render_end");if(c.dev){c.dispatch.on("render_start",function(f){c.logs.startTime=+new Date()});c.dispatch.on("render_end",function(f){c.logs.endTime=+new Date();c.logs.totalTime=c.logs.endTime-c.logs.startTime;c.log("total",c.logs.totalTime)})}c.log=function(){if(c.dev&&console.log&&console.log.apply){console.log.apply(console,arguments)}else{if(c.dev&&typeof console.log=="function"&&Function.prototype.bind){var e=Function.prototype.bind.call(console.log,console);e.apply(console,arguments)}}return arguments[arguments.length-1]};c.render=function d(e){e=e||1;c.render.active=true;c.dispatch.render_start();setTimeout(function(){var g,h;for(var f=0;f1){while(ls||y>v||(d3.event.relatedTarget&&d3.event.relatedTarget.ownerSVGElement===undefined)||C){if(l){if(d3.event.relatedTarget&&d3.event.relatedTarget.ownerSVGElement===undefined&&d3.event.relatedTarget.className.match(o.nvPointerEventsClass)){return}}j.elementMouseout({mouseX:z,mouseY:y});h.renderGuideLine(null);return}var x=m.invert(z);j.elementMousemove({mouseX:z,mouseY:y,pointXValue:x})}i.on("mousemove",u,true).on("mouseout",u,true);h.renderGuideLine=function(y){if(!k){return}var z=r.select(".nv-interactiveGuideLine").selectAll("line").data((y!=null)?[c.utils.NaNtoZero(y)]:[],String);z.enter().append("line").attr("class","nv-guideline").attr("x1",function(x){return x}).attr("x2",function(x){return x}).attr("y1",v).attr("y2",0);z.exit().remove()}})}h.dispatch=j;h.tooltip=o;h.margin=function(p){if(!arguments.length){return g}g.top=typeof p.top!="undefined"?p.top:g.top;g.left=typeof p.left!="undefined"?p.left:g.left;return h};h.width=function(p){if(!arguments.length){return e}e=p;return h};h.height=function(p){if(!arguments.length){return n}n=p;return h};h.xScale=function(p){if(!arguments.length){return m}m=p;return h};h.showGuideLine=function(p){if(!arguments.length){return k}k=p;return h};h.svgContainer=function(p){if(!arguments.length){return i}i=p;return h};return h};c.interactiveBisect=function(g,f,k){if(!g instanceof Array){return null}if(typeof k!=="function"){k=function(n,m){return n.x}}var l=d3.bisector(k).left;var h=d3.max([0,l(g,f)-1]);var j=k(g[h],h);if(typeof j==="undefined"){j=h}if(j===f){return h}var e=d3.min([h+1,g.length-1]);var i=k(g[e],e);if(typeof i==="undefined"){i=e}if(Math.abs(i-f)>=Math.abs(j-f)){return h}else{return e}};(function(){window.nv.tooltip={};window.nv.models.tooltip=function(){var r=null,w=null,i="w",h=50,o=25,f=null,t=null,s=null,v={left:null,top:null},l=true,p="nvtooltip-"+Math.floor(Math.random()*100000);var k="nv-pointer-events-none";var e=function(y,x){return y};var q=function(x){return x};var g=function(y){if(r!=null){return r}if(y==null){return""}var x="";if(y.series instanceof Array){y.series.forEach(function(A,z){x+="";x+="";x+="";x+=""})}x+="
"+q(y.value)+"
"+A.key+":"+e(A.value,z)+"
";return x};var j=function(x){if(x&&x.series&&x.series.length>0){return true}return false};function m(){if(s){var x=d3.select(s);if(x.node().tagName!=="svg"){x=x.select("svg")}var z=(x.node())?x.attr("viewBox"):null;if(z){z=z.split(" ");var y=parseInt(x.style("width"))/z[2];v.left=v.left*y;v.top=v.top*y}}}function u(z){var x;if(s){x=d3.select(s)}else{x=d3.select("body")}var y=x.select(".nvtooltip");if(y.node()===null){y=x.append("div").attr("class","nvtooltip "+(t?t:"xy-tooltip")).attr("id",p)}y.node().innerHTML=z;y.style("top",0).style("left",0).style("opacity",0);y.selectAll("div, table, td, tr").classed(k,true);y.classed(k,true);return y.node()}function n(){if(!l){return}if(!j(w)){return}m();var D=v.left;var C=(f!=null)?f:v.top;var x=u(g(w));if(s){var A=s.getElementsByTagName("svg")[0];var z=(A)?A.getBoundingClientRect():s.getBoundingClientRect();var E={left:0,top:0};if(A){var y=A.getBoundingClientRect();var B=s.getBoundingClientRect();E.top=Math.abs(y.top-B.top);E.left=Math.abs(y.left-B.left)}D+=s.offsetLeft+E.left-2*s.scrollLeft;C+=s.offsetTop+E.top-2*s.scrollTop}if(o&&o>0){C=Math.floor(C/o)*o}c.tooltip.calcTooltipPosition([D,C],i,h,x);return n}n.nvPointerEventsClass=k;n.content=function(x){if(!arguments.length){return r}r=x;return n};n.contentGenerator=function(x){if(!arguments.length){return g}if(typeof x==="function"){g=x}return n};n.data=function(x){if(!arguments.length){return w}w=x;return n};n.gravity=function(x){if(!arguments.length){return i}i=x;return n};n.distance=function(x){if(!arguments.length){return h}h=x;return n};n.snapDistance=function(x){if(!arguments.length){return o}o=x;return n};n.classes=function(x){if(!arguments.length){return t}t=x;return n};n.chartContainer=function(x){if(!arguments.length){return s}s=x;return n};n.position=function(x){if(!arguments.length){return v}v.left=(typeof x.left!=="undefined")?x.left:v.left;v.top=(typeof x.top!=="undefined")?x.top:v.top;return n};n.fixedTop=function(x){if(!arguments.length){return f}f=x;return n};n.enabled=function(x){if(!arguments.length){return l}l=x;return n};n.valueFormatter=function(x){if(!arguments.length){return e}if(typeof x==="function"){e=x}return n};n.headerFormatter=function(x){if(!arguments.length){return q}if(typeof x==="function"){q=x}return n};n.id=function(){return p};return n};c.tooltip.show=function(l,h,k,j,i,g){var f=document.createElement("div");f.className="nvtooltip "+(g?g:"xy-tooltip");var e=i;if(!i||i.tagName.match(/g|svg/i)){e=document.getElementsByTagName("body")[0]}f.style.left=0;f.style.top=0;f.style.opacity=0;f.innerHTML=h;e.appendChild(f);if(i){l[0]=l[0]-i.scrollLeft;l[1]=l[1]-i.scrollTop}c.tooltip.calcTooltipPosition(l,k,j,f)};c.tooltip.findFirstNonSVGParent=function(e){while(e.tagName.match(/^g|svg$/i)!==null){e=e.parentNode}return e};c.tooltip.findTotalOffsetTop=function(e,g){var f=g;do{if(!isNaN(e.offsetTop)){f+=(e.offsetTop)}}while(e=e.offsetParent);return f};c.tooltip.findTotalOffsetLeft=function(f,e){var g=e;do{if(!isNaN(f.offsetLeft)){g+=(f.offsetLeft)}}while(f=f.offsetParent);return g};c.tooltip.calcTooltipPosition=function(q,t,o,f){var s=parseInt(f.offsetHeight),g=parseInt(f.offsetWidth),i=c.utils.windowSize().width,e=c.utils.windowSize().height,h=window.pageYOffset,k=window.pageXOffset,j,p;e=window.innerWidth>=document.body.scrollWidth?e:e-16;i=window.innerHeight>=document.body.scrollHeight?i:i-16;t=t||"s";o=o||20;var l=function(u){return c.tooltip.findTotalOffsetTop(u,p)};var r=function(u){return c.tooltip.findTotalOffsetLeft(u,j)};switch(t){case"e":j=q[0]-g-o;p=q[1]-(s/2);var n=r(f);var m=l(f);if(nk?q[0]+o:k-n+j}if(mh+e){p=h+e-m+p-s}break;case"w":j=q[0]+o;p=q[1]-(s/2);var n=r(f);var m=l(f);if(n+g>i){j=q[0]-g-o}if(mh+e){p=h+e-m+p-s}break;case"n":j=q[0]-(g/2)-5;p=q[1]+o;var n=r(f);var m=l(f);if(ni){j=j-g/2+5}if(m+s>h+e){p=h+e-m+p-s}break;case"s":j=q[0]-(g/2);p=q[1]-s-o;var n=r(f);var m=l(f);if(ni){j=j-g/2+5}if(h>m){p=h}break;case"none":j=q[0];p=q[1]-o;var n=r(f);var m=l(f);break}f.style.left=j+"px";f.style.top=p+"px";f.style.opacity=1;f.style.position="absolute";return f};c.tooltip.cleanup=function(){var e=document.getElementsByClassName("nvtooltip");var f=[];while(e.length){f.push(e[0]);e[0].style.transitionDelay="0 !important";e[0].style.opacity=0;e[0].className="nvtooltip-pending-removal"}setTimeout(function(){while(f.length){var g=f.pop();g.parentNode.removeChild(g)}},500)}})();c.utils.windowSize=function(){var e={width:640,height:480};if(document.body&&document.body.offsetWidth){e.width=document.body.offsetWidth;e.height=document.body.offsetHeight}if(document.compatMode=="CSS1Compat"&&document.documentElement&&document.documentElement.offsetWidth){e.width=document.documentElement.offsetWidth;e.height=document.documentElement.offsetHeight}if(window.innerWidth&&window.innerHeight){e.width=window.innerWidth;e.height=window.innerHeight}return(e)};c.utils.windowResize=function(e){if(e===undefined){return}var f=window.onresize;window.onresize=function(g){if(typeof f=="function"){f(g)}e(g)}};c.utils.getColor=function(e){if(!arguments.length){return c.utils.defaultColor()}if(Object.prototype.toString.call(e)==="[object Array]"){return function(g,f){return g.color||e[f%e.length]}}else{return e}};c.utils.defaultColor=function(){var e=d3.scale.category20().range();return function(g,f){return g.color||e[f%e.length]}};c.utils.customTheme=function(h,f,g){f=f||function(i){return i.key};g=g||d3.scale.category20().range();var e=g.length;return function(k,i){var j=f(k);if(!e){e=g.length}if(typeof h[j]!=="undefined"){return(typeof h[j]==="function")?h[j]():h[j]}else{return g[--e]}}};c.utils.pjax=function(e,f){d3.selectAll(e).on("click",function(){history.pushState(this.href,this.textContent,this.href);g(this.href);d3.event.preventDefault()});function g(h){d3.html(h,function(i){var j=d3.select(f).node();j.parentNode.replaceChild(d3.select(i).select(f).node(),j);c.utils.pjax(e,f)})}d3.select(window).on("popstate",function(){if(d3.event.state){g(d3.event.state)}})};c.utils.calcApproxTextWidth=function(e){if(e instanceof d3.selection){var g=parseInt(e.style("font-size").replace("px",""));var f=e.text().length;return f*g*0.5}return 0};c.utils.NaNtoZero=function(e){if(typeof e!=="number"||isNaN(e)||e===null||e===Infinity){return 0}return e};c.utils.optionsFunc=function(e){if(e){d3.map(e).forEach((function(f,g){if(typeof this[f]==="function"){this[f](g)}}).bind(this))}return this};c.models.axis=function(){var j=d3.svg.axis();var m={top:0,right:0,bottom:0,left:0},g=75,s=60,l=d3.scale.linear(),n=null,i=true,k=true,f=0,h=true,o=false,e=false,r=null;j.scale(l).orient("bottom").tickFormat(function(t){return t});var p;function q(t){t.each(function(F){var v=d3.select(this);var y=v.selectAll("g.nv-wrap.nv-axis").data([F]);var B=y.enter().append("g").attr("class","nvd3 nv-wrap nv-axis");var u=B.append("g");var H=y.select("g");if(r!==null){j.ticks(r)}else{if(j.orient()=="top"||j.orient()=="bottom"){j.ticks(Math.abs(l.range()[1]-l.range()[0])/100)}}H.transition().call(j);p=p||j.scale();var A=j.tickFormat();if(A==null){A=p.tickFormat()}var z=H.selectAll("text.nv-axislabel").data([n||null]);z.exit().remove();switch(j.orient()){case"top":z.enter().append("text").attr("class","nv-axislabel");var J=(l.range().length==2)?l.range()[1]:(l.range()[l.range().length-1]+(l.range()[1]-l.range()[0]));z.attr("text-anchor","middle").attr("y",0).attr("x",J/2);if(i){var G=y.selectAll("g.nv-axisMaxMin").data(l.domain());G.enter().append("g").attr("class","nv-axisMaxMin").append("text");G.exit().remove();G.attr("transform",function(K,w){return"translate("+l(K)+",0)"}).select("text").attr("dy","0em").attr("y",-j.tickPadding()).attr("text-anchor","middle").text(function(L,K){var w=A(L);return(""+w).match("NaN")?"":w});G.transition().attr("transform",function(K,w){return"translate("+l.range()[w]+",0)"})}break;case"bottom":var E=36;var x=30;var C=H.selectAll("g").select("text");if(f%360){C.each(function(L,w){var K=this.getBBox().width;if(K>x){x=K}});var I=Math.abs(Math.sin(f*Math.PI/180));var E=(I?I*x:x)+30;C.attr("transform",function(L,K,w){return"rotate("+f+" 0,0)"}).style("text-anchor",f%360>0?"start":"end")}z.enter().append("text").attr("class","nv-axislabel");var J=(l.range().length==2)?l.range()[1]:(l.range()[l.range().length-1]+(l.range()[1]-l.range()[0]));z.attr("text-anchor","middle").attr("y",E).attr("x",J/2);if(i){var G=y.selectAll("g.nv-axisMaxMin").data([l.domain()[0],l.domain()[l.domain().length-1]]);G.enter().append("g").attr("class","nv-axisMaxMin").append("text");G.exit().remove();G.attr("transform",function(K,w){return"translate("+(l(K)+(e?l.rangeBand()/2:0))+",0)"}).select("text").attr("dy",".71em").attr("y",j.tickPadding()).attr("transform",function(L,K,w){return"rotate("+f+" 0,0)"}).style("text-anchor",f?(f%360>0?"start":"end"):"middle").text(function(L,K){var w=A(L);return(""+w).match("NaN")?"":w});G.transition().attr("transform",function(K,w){return"translate("+(l(K)+(e?l.rangeBand()/2:0))+",0)"})}if(o){C.attr("transform",function(K,w){return"translate(0,"+(w%2==0?"0":"12")+")"})}break;case"right":z.enter().append("text").attr("class","nv-axislabel");z.style("text-anchor",h?"middle":"begin").attr("transform",h?"rotate(90)":"").attr("y",h?(-Math.max(m.right,g)+12):-10).attr("x",h?(l.range()[0]/2):j.tickPadding());if(i){var G=y.selectAll("g.nv-axisMaxMin").data(l.domain());G.enter().append("g").attr("class","nv-axisMaxMin").append("text").style("opacity",0);G.exit().remove();G.attr("transform",function(K,w){return"translate(0,"+l(K)+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",j.tickPadding()).style("text-anchor","start").text(function(L,K){var w=A(L);return(""+w).match("NaN")?"":w});G.transition().attr("transform",function(K,w){return"translate(0,"+l.range()[w]+")"}).select("text").style("opacity",1)}break;case"left":z.enter().append("text").attr("class","nv-axislabel");z.style("text-anchor",h?"middle":"end").attr("transform",h?"rotate(-90)":"").attr("y",h?(-Math.max(m.left,g)+12):-10).attr("x",h?(-l.range()[0]/2):-j.tickPadding());if(i){var G=y.selectAll("g.nv-axisMaxMin").data(l.domain());G.enter().append("g").attr("class","nv-axisMaxMin").append("text").style("opacity",0);G.exit().remove();G.attr("transform",function(K,w){return"translate(0,"+p(K)+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",-j.tickPadding()).attr("text-anchor","end").text(function(L,K){var w=A(L);return(""+w).match("NaN")?"":w});G.transition().attr("transform",function(K,w){return"translate(0,"+l.range()[w]+")"}).select("text").style("opacity",1)}break}z.text(function(w){return w});if(i&&(j.orient()==="left"||j.orient()==="right")){H.selectAll("g").each(function(K,w){d3.select(this).select("text").attr("opacity",1);if(l(K)l.range()[0]-10){if(K>1e-10||K<-1e-10){d3.select(this).attr("opacity",0)}d3.select(this).select("text").attr("opacity",0)}});if(l.domain()[0]==l.domain()[1]&&l.domain()[0]==0){y.selectAll("g.nv-axisMaxMin").style("opacity",function(K,w){return !w?1:0})}}if(i&&(j.orient()==="top"||j.orient()==="bottom")){var D=[];y.selectAll("g.nv-axisMaxMin").each(function(L,w){try{if(w){D.push(l(L)-this.getBBox().width-4)}else{D.push(l(L)+this.getBBox().width+4)}}catch(K){if(w){D.push(l(L)-4)}else{D.push(l(L)+4)}}});H.selectAll("g").each(function(K,w){if(l(K)D[1]){if(K>1e-10||K<-1e-10){d3.select(this).remove()}else{d3.select(this).select("text").remove()}}})}if(k){H.selectAll(".tick").filter(function(w){return !parseFloat(Math.round(w.__data__*100000)/1000000)&&(w.__data__!==undefined)}).classed("zero",true)}p=l.copy()});return q}q.axis=j;d3.rebind(q,j,"orient","tickValues","tickSubdivide","tickSize","tickPadding","tickFormat");d3.rebind(q,l,"domain","range","rangeBand","rangeBands");q.options=c.utils.optionsFunc.bind(q);q.margin=function(t){if(!arguments.length){return m}m.top=typeof t.top!="undefined"?t.top:m.top;m.right=typeof t.right!="undefined"?t.right:m.right;m.bottom=typeof t.bottom!="undefined"?t.bottom:m.bottom;m.left=typeof t.left!="undefined"?t.left:m.left;return q};q.width=function(t){if(!arguments.length){return g}g=t;return q};q.ticks=function(t){if(!arguments.length){return r}r=t;return q};q.height=function(t){if(!arguments.length){return s}s=t;return q};q.axisLabel=function(t){if(!arguments.length){return n}n=t;return q};q.showMaxMin=function(t){if(!arguments.length){return i}i=t;return q};q.highlightZero=function(t){if(!arguments.length){return k}k=t;return q};q.scale=function(t){if(!arguments.length){return l}l=t;j.scale(l);e=typeof l.rangeBands==="function";d3.rebind(q,l,"domain","range","rangeBand","rangeBands");return q};q.rotateYLabel=function(t){if(!arguments.length){return h}h=t;return q};q.rotateLabels=function(t){if(!arguments.length){return f}f=t;return q};q.staggerLabels=function(t){if(!arguments.length){return o}o=t;return q};return q};c.models.bullet=function(){var k={top:0,right:0,bottom:0,left:0},m="left",q=false,f=function(u){return u.ranges},l=function(u){return u.markers},e=function(u){return u.measures},o=function(u){return u.rangeLabels?u.rangeLabels:[]},s=function(u){return u.markerLabels?u.markerLabels:[]},h=function(u){return u.measureLabels?u.measureLabels:[]},n=[0],g=380,t=30,i=null,j=c.utils.getColor(["#1f77b4"]),p=d3.dispatch("elementMouseover","elementMouseout");function r(u){u.each(function(S,N){var v=g-k.left-k.right,w=t-k.top-k.bottom,K=d3.select(this);var T=f.call(this,S,N).slice().sort(d3.descending),L=l.call(this,S,N).slice().sort(d3.descending),I=e.call(this,S,N).slice().sort(d3.descending),J=o.call(this,S,N).slice(),A=s.call(this,S,N).slice(),Q=h.call(this,S,N).slice();var P=d3.scale.linear().domain(d3.extent(d3.merge([n,T]))).range(q?[v,0]:[0,v]);var R=this.__chart__||d3.scale.linear().domain([0,Infinity]).range(P.range());this.__chart__=P;var y=d3.min(T),B=d3.max(T),x=T[1];var D=K.selectAll("g.nv-wrap.nv-bullet").data([S]);var z=D.enter().append("g").attr("class","nvd3 nv-wrap nv-bullet");var M=z.append("g");var O=D.select("g");M.append("rect").attr("class","nv-range nv-rangeMax");M.append("rect").attr("class","nv-range nv-rangeAvg");M.append("rect").attr("class","nv-range nv-rangeMin");M.append("rect").attr("class","nv-measure");M.append("path").attr("class","nv-markerTriangle");D.attr("transform","translate("+k.left+","+k.top+")");var H=function(U){return Math.abs(R(U)-R(0))},F=function(U){return Math.abs(P(U)-P(0))};var G=function(U){return U<0?R(U):R(0)},E=function(U){return U<0?P(U):P(0)};O.select("rect.nv-rangeMax").attr("height",w).attr("width",F(B>0?B:y)).attr("x",E(B>0?B:y)).datum(B>0?B:y);O.select("rect.nv-rangeAvg").attr("height",w).attr("width",F(x)).attr("x",E(x)).datum(x);O.select("rect.nv-rangeMin").attr("height",w).attr("width",F(B)).attr("x",E(B)).attr("width",F(B>0?y:B)).attr("x",E(B>0?y:B)).datum(B>0?y:B);O.select("rect.nv-measure").style("fill",j).attr("height",w/3).attr("y",w/3).attr("width",I<0?P(0)-P(I[0]):P(I[0])-P(0)).attr("x",E(I)).on("mouseover",function(){p.elementMouseover({value:I[0],label:Q[0]||"Current",pos:[P(I[0]),w/2]})}).on("mouseout",function(){p.elementMouseout({value:I[0],label:Q[0]||"Current"})});var C=w/6;if(L[0]){O.selectAll("path.nv-markerTriangle").attr("transform",function(U){return"translate("+P(L[0])+","+(w/2)+")"}).attr("d","M0,"+C+"L"+C+","+(-C)+" "+(-C)+","+(-C)+"Z").on("mouseover",function(){p.elementMouseover({value:L[0],label:A[0]||"Previous",pos:[P(L[0]),w/2]})}).on("mouseout",function(){p.elementMouseout({value:L[0],label:A[0]||"Previous"})})}else{O.selectAll("path.nv-markerTriangle").remove()}D.selectAll(".nv-range").on("mouseover",function(W,V){var U=J[V]||(!V?"Maximum":V==1?"Mean":"Minimum");p.elementMouseover({value:W,label:U,pos:[P(W),w/2]})}).on("mouseout",function(W,V){var U=J[V]||(!V?"Maximum":V==1?"Mean":"Minimum");p.elementMouseout({value:W,label:U})})});return r}r.dispatch=p;r.options=c.utils.optionsFunc.bind(r);r.orient=function(u){if(!arguments.length){return m}m=u;q=m=="right"||m=="bottom";return r};r.ranges=function(u){if(!arguments.length){return f}f=u;return r};r.markers=function(u){if(!arguments.length){return l}l=u;return r};r.measures=function(u){if(!arguments.length){return e}e=u;return r};r.forceX=function(u){if(!arguments.length){return n}n=u;return r};r.width=function(u){if(!arguments.length){return g}g=u;return r};r.height=function(u){if(!arguments.length){return t}t=u;return r};r.margin=function(u){if(!arguments.length){return k}k.top=typeof u.top!="undefined"?u.top:k.top;k.right=typeof u.right!="undefined"?u.right:k.right;k.bottom=typeof u.bottom!="undefined"?u.bottom:k.bottom;k.left=typeof u.left!="undefined"?u.left:k.left;return r};r.tickFormat=function(u){if(!arguments.length){return i}i=u;return r};r.color=function(u){if(!arguments.length){return j}j=c.utils.getColor(u);return r};return r};c.models.bulletChart=function(){var e=c.models.bullet();var l="left",n=false,j={top:5,right:40,bottom:20,left:120},g=function(u){return u.ranges},k=function(u){return u.markers},f=function(u){return u.measures},h=null,s=55,i=null,p=true,t=function(v,u,A,z,w){return"

"+u+"

"+A+"

"},r="No Data Available.",m=d3.dispatch("tooltipShow","tooltipHide");var q=function(y,u){var x=y.pos[0]+(u.offsetLeft||0)+j.left,w=y.pos[1]+(u.offsetTop||0)+j.top,v=t(y.key,y.label,y.value,y,o);c.tooltip.show([x,w],v,y.value<0?"e":"w",null,u)};function o(u){u.each(function(P,J){var F=d3.select(this);var v=(h||parseInt(F.style("width"))||960)-j.left-j.right,w=s-j.top-j.bottom,z=this;o.update=function(){o(u)};o.container=this;if(!P||!g.call(this,P,J)){var B=F.selectAll(".nv-noData").data([r]);B.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");B.attr("x",j.left+v/2).attr("y",18+j.top+w/2).text(function(T){return T});return o}else{F.selectAll(".nv-noData").remove()}var S=g.call(this,P,J).slice().sort(d3.descending),H=k.call(this,P,J).slice().sort(d3.descending),D=f.call(this,P,J).slice().sort(d3.descending);var A=F.selectAll("g.nv-wrap.nv-bulletChart").data([P]);var x=A.enter().append("g").attr("class","nvd3 nv-wrap nv-bulletChart");var I=x.append("g");var M=A.select("g");I.append("g").attr("class","nv-bulletWrap");I.append("g").attr("class","nv-titles");A.attr("transform","translate("+j.left+","+j.top+")");var N=d3.scale.linear().domain([0,Math.max(S[0],H[0],D[0])]).range(n?[v,0]:[0,v]);var O=this.__chart__||d3.scale.linear().domain([0,Infinity]).range(N.range());this.__chart__=N;var E=function(T){return Math.abs(O(T)-O(0))},C=function(T){return Math.abs(N(T)-N(0))};var R=I.select(".nv-titles").append("g").attr("text-anchor","end").attr("transform","translate(-6,"+(s-j.top-j.bottom)/2+")");R.append("text").attr("class","nv-title").text(function(T){return T.title});R.append("text").attr("class","nv-subtitle").attr("dy","1em").text(function(T){return T.subtitle});e.width(v).height(w);var Q=M.select(".nv-bulletWrap");d3.transition(Q).call(e);var L=i||N.tickFormat(v/100);var K=M.selectAll("g.nv-tick").data(N.ticks(v/50),function(T){return this.textContent||L(T)});var G=K.enter().append("g").attr("class","nv-tick").attr("transform",function(T){return"translate("+O(T)+",0)"}).style("opacity",0.000001);G.append("line").attr("y1",w).attr("y2",w*7/6);G.append("text").attr("text-anchor","middle").attr("dy","1em").attr("y",w*7/6).text(L);var y=d3.transition(K).attr("transform",function(T){return"translate("+N(T)+",0)"}).style("opacity",1);y.select("line").attr("y1",w).attr("y2",w*7/6);y.select("text").attr("y",w*7/6);d3.transition(K.exit()).attr("transform",function(T){return"translate("+N(T)+",0)"}).style("opacity",0.000001).remove();m.on("tooltipShow",function(T){T.key=P.title;if(p){q(T,z.parentNode)}})});d3.timer.flush();return o}e.dispatch.on("elementMouseover.tooltip",function(u){m.tooltipShow(u)});e.dispatch.on("elementMouseout.tooltip",function(u){m.tooltipHide(u)});m.on("tooltipHide",function(){if(p){c.tooltip.cleanup()}});o.dispatch=m;o.bullet=e;d3.rebind(o,e,"color");o.options=c.utils.optionsFunc.bind(o);o.orient=function(u){if(!arguments.length){return l}l=u;n=l=="right"||l=="bottom";return o};o.ranges=function(u){if(!arguments.length){return g}g=u;return o};o.markers=function(u){if(!arguments.length){return k}k=u;return o};o.measures=function(u){if(!arguments.length){return f}f=u;return o};o.width=function(u){if(!arguments.length){return h}h=u;return o};o.height=function(u){if(!arguments.length){return s}s=u;return o};o.margin=function(u){if(!arguments.length){return j}j.top=typeof u.top!="undefined"?u.top:j.top;j.right=typeof u.right!="undefined"?u.right:j.right;j.bottom=typeof u.bottom!="undefined"?u.bottom:j.bottom;j.left=typeof u.left!="undefined"?u.left:j.left;return o};o.tickFormat=function(u){if(!arguments.length){return i}i=u;return o};o.tooltips=function(u){if(!arguments.length){return p}p=u;return o};o.tooltipContent=function(u){if(!arguments.length){return t}t=u;return o};o.noData=function(u){if(!arguments.length){return r}r=u;return o};return o};c.models.cumulativeLineChart=function(){var f=c.models.line(),r=c.models.axis(),e=c.models.axis(),h=c.models.legend(),A=c.models.legend(),F=c.interactiveGuideline();var C={top:30,right:30,bottom:50,left:60},H=c.utils.defaultColor(),G=null,D=null,I=true,J=true,v=true,q=false,g=true,K=true,u=false,p=true,m=function(O,N,R,Q,P){return"

"+O+"

"+R+" at "+N+"

"},t,s,E=f.id(),l={index:0,rescaleY:p},n=null,k="No Data Available.",i=function(x){return x.average},M=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState"),z=250;r.orient("bottom").tickPadding(7);e.orient((q)?"right":"left");A.updateState(false);var w=d3.scale.linear(),o={i:0,x:0};var j=function(S,O){var R=S.pos[0]+(O.offsetLeft||0),Q=S.pos[1]+(O.offsetTop||0),N=r.tickFormat()(f.x()(S.point,S.pointIndex)),T=e.tickFormat()(f.y()(S.point,S.pointIndex)),P=m(S.series.key,N,T,S,B);c.tooltip.show([R,Q],P,null,null,O)};function B(x){x.each(function(aj){var W=d3.select(this).classed("nv-chart-"+E,true),T=this;var N=(G||parseInt(W.style("width"))||960)-C.left-C.right,P=(D||parseInt(W.style("height"))||400)-C.top-C.bottom;B.update=function(){W.transition().duration(z).call(B)};B.container=this;l.disabled=aj.map(function(al){return !!al.disabled});if(!n){var ak;n={};for(ak in l){if(l[ak] instanceof Array){n[ak]=l[ak].slice(0)}else{n[ak]=l[ak]}}}var ai=d3.behavior.drag().on("dragstart",ag).on("drag",ah).on("dragend",ab);function ag(am,al){d3.select(B.container).style("cursor","ew-resize")}function ah(am,al){o.x=d3.event.x;o.i=Math.round(w.invert(o.x));Z()}function ab(am,al){d3.select(B.container).style("cursor","auto");l.index=o.i;M.stateChange(l)}if(!aj||!aj.length||!aj.filter(function(al){return al.values.length}).length){var V=W.selectAll(".nv-noData").data([k]);V.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");V.attr("x",C.left+N/2).attr("y",C.top+P/2).text(function(al){return al});return B}else{W.selectAll(".nv-noData").remove()}t=f.xScale();s=f.yScale();if(!p){var O=aj.filter(function(al){return !al.disabled}).map(function(an,am){var al=d3.extent(an.values,f.y());if(al[0]<-0.95){al[0]=-0.95}return[(al[0]-al[1])/(1+al[1]),(al[1]-al[0])/(1+al[0])]});var Q=[d3.min(O,function(al){return al[0]}),d3.max(O,function(al){return al[1]})];f.yDomain(Q)}else{f.yDomain(null)}w.domain([0,aj[0].values.length-1]).range([0,N]).clamp(true);var aj=L(o.i,aj);var ae=(u)?"none":"all";var U=W.selectAll("g.nv-wrap.nv-cumulativeLine").data([aj]);var Y=U.enter().append("g").attr("class","nvd3 nv-wrap nv-cumulativeLine").append("g");var af=U.select("g");Y.append("g").attr("class","nv-interactive");Y.append("g").attr("class","nv-x nv-axis").style("pointer-events","none");Y.append("g").attr("class","nv-y nv-axis");Y.append("g").attr("class","nv-background");Y.append("g").attr("class","nv-linesWrap").style("pointer-events",ae);Y.append("g").attr("class","nv-avgLinesWrap").style("pointer-events","none");Y.append("g").attr("class","nv-legendWrap");Y.append("g").attr("class","nv-controlsWrap");if(I){h.width(N);af.select(".nv-legendWrap").datum(aj).call(h);if(C.top!=h.height()){C.top=h.height();P=(D||parseInt(W.style("height"))||400)-C.top-C.bottom}af.select(".nv-legendWrap").attr("transform","translate(0,"+(-C.top)+")")}if(K){var S=[{key:"Re-scale y-axis",disabled:!p}];A.width(140).color(["#444","#444","#444"]);af.select(".nv-controlsWrap").datum(S).attr("transform","translate(0,"+(-C.top)+")").call(A)}U.attr("transform","translate("+C.left+","+C.top+")");if(q){af.select(".nv-y.nv-axis").attr("transform","translate("+N+",0)")}var y=aj.filter(function(al){return al.tempDisabled});U.select(".tempDisabled").remove();if(y.length){U.append("text").attr("class","tempDisabled").attr("x",N/2).attr("y","-.71em").style("text-anchor","end").text(y.map(function(al){return al.key}).join(", ")+" values cannot be calculated for this time period.")}if(u){F.width(N).height(P).margin({left:C.left,top:C.top}).svgContainer(W).xScale(t);U.select(".nv-interactive").call(F)}Y.select(".nv-background").append("rect");af.select(".nv-background rect").attr("width",N).attr("height",P);f.y(function(al){return al.display.y}).width(N).height(P).color(aj.map(function(am,al){return am.color||H(am,al)}).filter(function(am,al){return !aj[al].disabled&&!aj[al].tempDisabled}));var aa=af.select(".nv-linesWrap").datum(aj.filter(function(al){return !al.disabled&&!al.tempDisabled}));aa.call(f);aj.forEach(function(am,al){am.seriesIndex=al});var ad=aj.filter(function(al){return !al.disabled&&!!i(al)});var ac=af.select(".nv-avgLinesWrap").selectAll("line").data(ad,function(al){return al.key});var R=function(am){var al=s(i(am));if(al<0){return 0}if(al>P){return P}return al};ac.enter().append("line").style("stroke-width",2).style("stroke-dasharray","10,10").style("stroke",function(am,al){return f.color()(am,am.seriesIndex)}).attr("x1",0).attr("x2",N).attr("y1",R).attr("y2",R);ac.style("stroke-opacity",function(am){var al=s(i(am));if(al<0||al>P){return 0}return 1}).attr("x1",0).attr("x2",N).attr("y1",R).attr("y2",R);ac.exit().remove();var X=aa.selectAll(".nv-indexLine").data([o]);X.enter().append("rect").attr("class","nv-indexLine").attr("width",3).attr("x",-2).attr("fill","red").attr("fill-opacity",0.5).style("pointer-events","all").call(ai);X.attr("transform",function(al){return"translate("+w(al.i)+",0)"}).attr("height",P);if(J){r.scale(t).ticks(Math.min(aj[0].values.length,N/70)).tickSize(-P,0);af.select(".nv-x.nv-axis").attr("transform","translate(0,"+s.range()[0]+")");d3.transition(af.select(".nv-x.nv-axis")).call(r)}if(v){e.scale(s).ticks(P/36).tickSize(-N,0);d3.transition(af.select(".nv-y.nv-axis")).call(e)}function Z(){X.data([o]);var al=B.transitionDuration();B.transitionDuration(0);B.update();B.transitionDuration(al)}af.select(".nv-background rect").on("click",function(){o.x=d3.mouse(this)[0];o.i=Math.round(w.invert(o.x));l.index=o.i;M.stateChange(l);Z()});f.dispatch.on("elementClick",function(al){o.i=al.pointIndex;o.x=w(o.i);l.index=o.i;M.stateChange(l);Z()});A.dispatch.on("legendClick",function(am,al){am.disabled=!am.disabled;p=!am.disabled;l.rescaleY=p;M.stateChange(l);B.update()});h.dispatch.on("stateChange",function(al){l.disabled=al.disabled;M.stateChange(l);B.update()});F.dispatch.on("elementMousemove",function(aq){f.clearHighlights();var ap,ao,al,an=[];aj.filter(function(at,ar){at.seriesIndex=ar;return !at.disabled}).forEach(function(au,at){ao=c.interactiveBisect(au.values,aq.pointXValue,B.x());f.highlightPoint(at,ao,true);var ar=au.values[ao];if(typeof ar==="undefined"){return}if(typeof ap==="undefined"){ap=ar}if(typeof al==="undefined"){al=B.xScale()(B.x()(ar,ao))}an.push({key:au.key,value:B.y()(ar,ao),color:H(au,au.seriesIndex)})});var am=r.tickFormat()(B.x()(ap,ao));F.tooltip.position({left:al+C.left,top:aq.mouseY+C.top}).chartContainer(T.parentNode).enabled(g).valueFormatter(function(at,ar){return e.tickFormat()(at)}).data({value:am,series:an})();F.renderGuideLine(al)});F.dispatch.on("elementMouseout",function(al){M.tooltipHide();f.clearHighlights()});M.on("tooltipShow",function(al){if(g){j(al,T.parentNode)}});M.on("changeState",function(al){if(typeof al.disabled!=="undefined"){aj.forEach(function(an,am){an.disabled=al.disabled[am]});l.disabled=al.disabled}if(typeof al.index!=="undefined"){o.i=al.index;o.x=w(o.i);l.index=al.index;X.data([o])}if(typeof al.rescaleY!=="undefined"){p=al.rescaleY}B.update()})});return B}f.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+C.left,x.pos[1]+C.top];M.tooltipShow(x)});f.dispatch.on("elementMouseout.tooltip",function(x){M.tooltipHide(x)});M.on("tooltipHide",function(){if(g){c.tooltip.cleanup()}});B.dispatch=M;B.lines=f;B.legend=h;B.xAxis=r;B.yAxis=e;B.interactiveLayer=F;d3.rebind(B,f,"defined","isArea","x","y","xScale","yScale","size","xDomain","yDomain","xRange","yRange","forceX","forceY","interactive","clipEdge","clipVoronoi","useVoronoi","id");B.options=c.utils.optionsFunc.bind(B);B.margin=function(x){if(!arguments.length){return C}C.top=typeof x.top!="undefined"?x.top:C.top;C.right=typeof x.right!="undefined"?x.right:C.right;C.bottom=typeof x.bottom!="undefined"?x.bottom:C.bottom;C.left=typeof x.left!="undefined"?x.left:C.left;return B};B.width=function(x){if(!arguments.length){return G}G=x;return B};B.height=function(x){if(!arguments.length){return D}D=x;return B};B.color=function(x){if(!arguments.length){return H}H=c.utils.getColor(x);h.color(H);return B};B.rescaleY=function(x){if(!arguments.length){return p}p=x;return p};B.showControls=function(x){if(!arguments.length){return K}K=x;return B};B.useInteractiveGuideline=function(x){if(!arguments.length){return u}u=x;if(x===true){B.interactive(false);B.useVoronoi(false)}return B};B.showLegend=function(x){if(!arguments.length){return I}I=x;return B};B.showXAxis=function(x){if(!arguments.length){return J}J=x;return B};B.showYAxis=function(x){if(!arguments.length){return v}v=x;return B};B.rightAlignYAxis=function(x){if(!arguments.length){return q}q=x;e.orient((x)?"right":"left");return B};B.tooltips=function(x){if(!arguments.length){return g}g=x;return B};B.tooltipContent=function(x){if(!arguments.length){return m}m=x;return B};B.state=function(x){if(!arguments.length){return l}l=x;return B};B.defaultState=function(x){if(!arguments.length){return n}n=x;return B};B.noData=function(x){if(!arguments.length){return k}k=x;return B};B.average=function(x){if(!arguments.length){return i}i=x;return B};B.transitionDuration=function(x){if(!arguments.length){return z}z=x;return B};function L(x,y){return y.map(function(N,P){if(!N.values){return N}var O=f.y()(N.values[x],x);if(O<-0.95){N.tempDisabled=true;return N}N.tempDisabled=false;N.values=N.values.map(function(Q,R){Q.display={y:(f.y()(Q,R)-O)/(1+O)};return Q});return N})}return B};c.models.discreteBar=function(){var k={top:0,right:0,bottom:0,left:0},o=960,m=500,n=Math.floor(Math.random()*10000),i=d3.scale.ordinal(),h=d3.scale.linear(),v=function(x){return x.x},t=function(x){return x.y},f=[0],s=c.utils.defaultColor(),w=false,g=d3.format(",.2f"),q,p,l,r,A=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout"),z="discreteBar";var u,e;function j(x){x.each(function(I){var L=o-k.left-k.right,E=m-k.top-k.bottom,B=d3.select(this);I=I.map(function(N,M){N.values=N.values.map(function(O){O.series=M;return O});return N});var D=(q&&p)?[]:I.map(function(M){return M.values.map(function(O,N){return{x:v(O,N),y:t(O,N),y0:O.y0}})});i.domain(q||d3.merge(D).map(function(M){return M.x})).rangeBands(l||[0,L],0.1);h.domain(p||d3.extent(d3.merge(D).map(function(M){return M.y}).concat(f)));if(w){h.range(r||[E-(h.domain()[0]<0?12:0),h.domain()[1]>0?12:0])}else{h.range(r||[E,0])}u=u||i;e=e||h.copy().range([h(0),h(0)]);var C=B.selectAll("g.nv-wrap.nv-discretebar").data([I]);var G=C.enter().append("g").attr("class","nvd3 nv-wrap nv-discretebar");var y=G.append("g");var J=C.select("g");y.append("g").attr("class","nv-groups");C.attr("transform","translate("+k.left+","+k.top+")");var F=C.select(".nv-groups").selectAll(".nv-group").data(function(M){return M},function(M){return M.key});F.enter().append("g").style("stroke-opacity",0.000001).style("fill-opacity",0.000001);F.exit().transition().style("stroke-opacity",0.000001).style("fill-opacity",0.000001).remove();F.attr("class",function(N,M){return"nv-group nv-series-"+M}).classed("hover",function(M){return M.hover});F.transition().style("stroke-opacity",1).style("fill-opacity",0.75);var K=F.selectAll("g.nv-bar").data(function(M){return M.values});K.exit().remove();var H=K.enter().append("g").attr("transform",function(O,N,M){return"translate("+(i(v(O,N))+i.rangeBand()*0.05)+", "+h(0)+")"}).on("mouseover",function(N,M){d3.select(this).classed("hover",true);A.elementMouseover({value:t(N,M),point:N,series:I[N.series],pos:[i(v(N,M))+(i.rangeBand()*(N.series+0.5)/I.length),h(t(N,M))],pointIndex:M,seriesIndex:N.series,e:d3.event})}).on("mouseout",function(N,M){d3.select(this).classed("hover",false);A.elementMouseout({value:t(N,M),point:N,series:I[N.series],pointIndex:M,seriesIndex:N.series,e:d3.event})}).on("click",function(N,M){A.elementClick({value:t(N,M),point:N,series:I[N.series],pos:[i(v(N,M))+(i.rangeBand()*(N.series+0.5)/I.length),h(t(N,M))],pointIndex:M,seriesIndex:N.series,e:d3.event});d3.event.stopPropagation()}).on("dblclick",function(N,M){A.elementDblClick({value:t(N,M),point:N,series:I[N.series],pos:[i(v(N,M))+(i.rangeBand()*(N.series+0.5)/I.length),h(t(N,M))],pointIndex:M,seriesIndex:N.series,e:d3.event});d3.event.stopPropagation()});H.append("rect").attr("height",0).attr("width",i.rangeBand()*0.9/I.length);if(w){H.append("text").attr("text-anchor","middle");K.select("text").text(function(N,M){return g(t(N,M))}).transition().attr("x",i.rangeBand()*0.9/2).attr("y",function(N,M){return t(N,M)<0?h(t(N,M))-h(0)+12:-4})}else{K.selectAll("text").remove()}K.attr("class",function(N,M){return t(N,M)<0?"nv-bar negative":"nv-bar positive"}).style("fill",function(N,M){return N.color||s(N,M)}).style("stroke",function(N,M){return N.color||s(N,M)}).select("rect").attr("class",z).transition().attr("width",i.rangeBand()*0.9/I.length);K.transition().attr("transform",function(P,M){var O=i(v(P,M))+i.rangeBand()*0.05,N=t(P,M)<0?h(0):h(0)-h(t(P,M))<1?h(0)-1:h(t(P,M));return"translate("+O+", "+N+")"}).select("rect").attr("height",function(N,M){return Math.max(Math.abs(h(t(N,M))-h((p&&p[0])||0))||1)});u=i.copy();e=h.copy()});return j}j.dispatch=A;j.options=c.utils.optionsFunc.bind(j);j.x=function(x){if(!arguments.length){return v}v=x;return j};j.y=function(x){if(!arguments.length){return t}t=x;return j};j.margin=function(x){if(!arguments.length){return k}k.top=typeof x.top!="undefined"?x.top:k.top;k.right=typeof x.right!="undefined"?x.right:k.right;k.bottom=typeof x.bottom!="undefined"?x.bottom:k.bottom;k.left=typeof x.left!="undefined"?x.left:k.left;return j};j.width=function(x){if(!arguments.length){return o}o=x;return j};j.height=function(x){if(!arguments.length){return m}m=x;return j};j.xScale=function(x){if(!arguments.length){return i}i=x;return j};j.yScale=function(x){if(!arguments.length){return h}h=x;return j};j.xDomain=function(x){if(!arguments.length){return q}q=x;return j};j.yDomain=function(x){if(!arguments.length){return p}p=x;return j};j.xRange=function(x){if(!arguments.length){return l}l=x;return j};j.yRange=function(x){if(!arguments.length){return r}r=x;return j};j.forceY=function(x){if(!arguments.length){return f}f=x;return j};j.color=function(x){if(!arguments.length){return s}s=c.utils.getColor(x);return j};j.id=function(x){if(!arguments.length){return n}n=x;return j};j.showValues=function(x){if(!arguments.length){return w}w=x;return j};j.valueFormat=function(x){if(!arguments.length){return g}g=x;return j};j.rectClass=function(x){if(!arguments.length){return z}z=x;return j};return j};c.models.discreteBarChart=function(){var w=c.models.discreteBar(),l=c.models.axis(),e=c.models.axis();var r={top:15,right:10,bottom:50,left:60},t=null,s=null,u=c.utils.getColor(),v=true,o=true,k=false,g=false,f=true,j=function(B,A,E,D,C){return"

"+A+"

"+E+"

"},n,m,i="No Data Available.",z=d3.dispatch("tooltipShow","tooltipHide","beforeUpdate"),p=250;l.orient("bottom").highlightZero(false).showMaxMin(false).tickFormat(function(x){return x});e.orient((k)?"right":"left").tickFormat(d3.format(",.1f"));var h=function(F,B){var E=F.pos[0]+(B.offsetLeft||0),D=F.pos[1]+(B.offsetTop||0),A=l.tickFormat()(w.x()(F.point,F.pointIndex)),G=e.tickFormat()(w.y()(F.point,F.pointIndex)),C=j(F.series.key,A,G,F,q);c.tooltip.show([E,D],C,F.value<0?"n":"s",null,B)};function q(x){x.each(function(G){var A=d3.select(this),J=this;var K=(t||parseInt(A.style("width"))||960)-r.left-r.right,E=(s||parseInt(A.style("height"))||400)-r.top-r.bottom;q.update=function(){z.beforeUpdate();A.transition().duration(p).call(q)};q.container=this;if(!G||!G.length||!G.filter(function(L){return L.values.length}).length){var B=A.selectAll(".nv-noData").data([i]);B.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");B.attr("x",r.left+K/2).attr("y",r.top+E/2).text(function(L){return L});return q}else{A.selectAll(".nv-noData").remove()}n=w.xScale();m=w.yScale().clamp(true);var C=A.selectAll("g.nv-wrap.nv-discreteBarWithAxes").data([G]);var y=C.enter().append("g").attr("class","nvd3 nv-wrap nv-discreteBarWithAxes").append("g");var D=y.append("defs");var H=C.select("g");y.append("g").attr("class","nv-x nv-axis");y.append("g").attr("class","nv-y nv-axis");y.append("g").attr("class","nv-barsWrap");H.attr("transform","translate("+r.left+","+r.top+")");if(k){H.select(".nv-y.nv-axis").attr("transform","translate("+K+",0)")}w.width(K).height(E);var I=H.select(".nv-barsWrap").datum(G.filter(function(L){return !L.disabled}));I.transition().call(w);D.append("clipPath").attr("id","nv-x-label-clip-"+w.id()).append("rect");H.select("#nv-x-label-clip-"+w.id()+" rect").attr("width",n.rangeBand()*(g?2:1)).attr("height",16).attr("x",-n.rangeBand()/(g?1:2));if(v){l.scale(n).ticks(K/100).tickSize(-E,0);H.select(".nv-x.nv-axis").attr("transform","translate(0,"+(m.range()[0]+((w.showValues()&&m.domain()[0]<0)?16:0))+")");H.select(".nv-x.nv-axis").transition().call(l);var F=H.select(".nv-x.nv-axis").selectAll("g");if(g){F.selectAll("text").attr("transform",function(N,M,L){return"translate(0,"+(L%2==0?"5":"17")+")"})}}if(o){e.scale(m).ticks(E/36).tickSize(-K,0);H.select(".nv-y.nv-axis").transition().call(e)}z.on("tooltipShow",function(L){if(f){h(L,J.parentNode)}})});return q}w.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+r.left,x.pos[1]+r.top];z.tooltipShow(x)});w.dispatch.on("elementMouseout.tooltip",function(x){z.tooltipHide(x)});z.on("tooltipHide",function(){if(f){c.tooltip.cleanup()}});q.dispatch=z;q.discretebar=w;q.xAxis=l;q.yAxis=e;d3.rebind(q,w,"x","y","xDomain","yDomain","xRange","yRange","forceX","forceY","id","showValues","valueFormat");q.options=c.utils.optionsFunc.bind(q);q.margin=function(x){if(!arguments.length){return r}r.top=typeof x.top!="undefined"?x.top:r.top;r.right=typeof x.right!="undefined"?x.right:r.right;r.bottom=typeof x.bottom!="undefined"?x.bottom:r.bottom;r.left=typeof x.left!="undefined"?x.left:r.left;return q};q.width=function(x){if(!arguments.length){return t}t=x;return q};q.height=function(x){if(!arguments.length){return s}s=x;return q};q.color=function(x){if(!arguments.length){return u}u=c.utils.getColor(x);w.color(u);return q};q.showXAxis=function(x){if(!arguments.length){return v}v=x;return q};q.showYAxis=function(x){if(!arguments.length){return o}o=x;return q};q.rightAlignYAxis=function(x){if(!arguments.length){return k}k=x;e.orient((x)?"right":"left");return q};q.staggerLabels=function(x){if(!arguments.length){return g}g=x;return q};q.tooltips=function(x){if(!arguments.length){return f}f=x;return q};q.tooltipContent=function(x){if(!arguments.length){return j}j=x;return q};q.noData=function(x){if(!arguments.length){return i}i=x;return q};q.transitionDuration=function(x){if(!arguments.length){return p}p=x;return q};return q};c.models.distribution=function(){var h={top:0,right:0,bottom:0,left:0},e=400,n=8,f="x",k=function(o){return o[f]},i=c.utils.defaultColor(),g=d3.scale.linear(),j;var l;function m(o){o.each(function(t){var u=e-(f==="x"?h.left+h.right:h.top+h.bottom),x=f=="x"?"y":"x",q=d3.select(this);l=l||g;var r=q.selectAll("g.nv-distribution").data([t]);var s=r.enter().append("g").attr("class","nvd3 nv-distribution");var p=s.append("g");var v=r.select("g");r.attr("transform","translate("+h.left+","+h.top+")");var y=v.selectAll("g.nv-dist").data(function(z){return z},function(z){return z.key});y.enter().append("g");y.attr("class",function(A,z){return"nv-dist nv-series-"+z}).style("stroke",function(A,z){return i(A,z)});var w=y.selectAll("line.nv-dist"+f).data(function(z){return z.values});w.enter().append("line").attr(f+"1",function(A,z){return l(k(A,z))}).attr(f+"2",function(A,z){return l(k(A,z))});y.exit().selectAll("line.nv-dist"+f).transition().attr(f+"1",function(A,z){return g(k(A,z))}).attr(f+"2",function(A,z){return g(k(A,z))}).style("stroke-opacity",0).remove();w.attr("class",function(A,z){return"nv-dist"+f+" nv-dist"+f+"-"+z}).attr(x+"1",0).attr(x+"2",n);w.transition().attr(f+"1",function(A,z){return g(k(A,z))}).attr(f+"2",function(A,z){return g(k(A,z))});l=g.copy()});return m}m.options=c.utils.optionsFunc.bind(m);m.margin=function(o){if(!arguments.length){return h}h.top=typeof o.top!="undefined"?o.top:h.top;h.right=typeof o.right!="undefined"?o.right:h.right;h.bottom=typeof o.bottom!="undefined"?o.bottom:h.bottom;h.left=typeof o.left!="undefined"?o.left:h.left;return m};m.width=function(o){if(!arguments.length){return e}e=o;return m};m.axis=function(o){if(!arguments.length){return f}f=o;return m};m.size=function(o){if(!arguments.length){return n}n=o;return m};m.getData=function(o){if(!arguments.length){return k}k=d3.functor(o);return m};m.scale=function(o){if(!arguments.length){return g}g=o;return m};m.color=function(o){if(!arguments.length){return i}i=c.utils.getColor(o);return m};return m};c.models.historicalBar=function(){var k={top:0,right:0,bottom:0,left:0},o=960,m=500,n=Math.floor(Math.random()*10000),h=d3.scale.linear(),g=d3.scale.linear(),u=function(x){return x.x},t=function(x){return x.y},f=[],e=[0],i=false,w=true,s=c.utils.defaultColor(),q,p,l,r,z=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout"),v=true;function j(x){x.each(function(G){var J=o-k.left-k.right,C=m-k.top-k.bottom,A=d3.select(this);h.domain(q||d3.extent(G[0].values.map(u).concat(f)));if(i){h.range(l||[J*0.5/G[0].values.length,J*(G[0].values.length-0.5)/G[0].values.length])}else{h.range(l||[0,J])}g.domain(p||d3.extent(G[0].values.map(t).concat(e))).range(r||[C,0]);if(h.domain()[0]===h.domain()[1]){h.domain()[0]?h.domain([h.domain()[0]-h.domain()[0]*0.01,h.domain()[1]+h.domain()[1]*0.01]):h.domain([-1,1])}if(g.domain()[0]===g.domain()[1]){g.domain()[0]?g.domain([g.domain()[0]+g.domain()[0]*0.01,g.domain()[1]-g.domain()[1]*0.01]):g.domain([-1,1])}var B=A.selectAll("g.nv-wrap.nv-historicalBar-"+n).data([G[0].values]);var E=B.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBar-"+n);var D=E.append("defs");var y=E.append("g");var H=B.select("g");y.append("g").attr("class","nv-bars");B.attr("transform","translate("+k.left+","+k.top+")");A.on("click",function(L,K){z.chartClick({data:L,index:K,pos:d3.event,id:n})});D.append("clipPath").attr("id","nv-chart-clip-path-"+n).append("rect");B.select("#nv-chart-clip-path-"+n+" rect").attr("width",J).attr("height",C);H.attr("clip-path",w?"url(#nv-chart-clip-path-"+n+")":"");var I=B.select(".nv-bars").selectAll(".nv-bar").data(function(K){return K},function(L,K){return u(L,K)});I.exit().remove();var F=I.enter().append("rect").attr("x",0).attr("y",function(L,K){return c.utils.NaNtoZero(g(Math.max(0,t(L,K))))}).attr("height",function(L,K){return c.utils.NaNtoZero(Math.abs(g(t(L,K))-g(0)))}).attr("transform",function(L,K){return"translate("+(h(u(L,K))-J/G[0].values.length*0.45)+",0)"}).on("mouseover",function(L,K){if(!v){return}d3.select(this).classed("hover",true);z.elementMouseover({point:L,series:G[0],pos:[h(u(L,K)),g(t(L,K))],pointIndex:K,seriesIndex:0,e:d3.event})}).on("mouseout",function(L,K){if(!v){return}d3.select(this).classed("hover",false);z.elementMouseout({point:L,series:G[0],pointIndex:K,seriesIndex:0,e:d3.event})}).on("click",function(L,K){if(!v){return}z.elementClick({value:t(L,K),data:L,index:K,pos:[h(u(L,K)),g(t(L,K))],e:d3.event,id:n});d3.event.stopPropagation()}).on("dblclick",function(L,K){if(!v){return}z.elementDblClick({value:t(L,K),data:L,index:K,pos:[h(u(L,K)),g(t(L,K))],e:d3.event,id:n});d3.event.stopPropagation()});I.attr("fill",function(L,K){return s(L,K)}).attr("class",function(M,L,K){return(t(M,L)<0?"nv-bar negative":"nv-bar positive")+" nv-bar-"+K+"-"+L}).transition().attr("transform",function(L,K){return"translate("+(h(u(L,K))-J/G[0].values.length*0.45)+",0)"}).attr("width",(J/G[0].values.length)*0.9);I.transition().attr("y",function(M,K){var L=t(M,K)<0?g(0):g(0)-g(t(M,K))<1?g(0)-1:g(t(M,K));return c.utils.NaNtoZero(L)}).attr("height",function(L,K){return c.utils.NaNtoZero(Math.max(Math.abs(g(t(L,K))-g(0)),1))})});return j}j.highlightPoint=function(y,x){d3.select(".nv-historicalBar-"+n).select(".nv-bars .nv-bar-0-"+y).classed("hover",x)};j.clearHighlights=function(){d3.select(".nv-historicalBar-"+n).select(".nv-bars .nv-bar.hover").classed("hover",false)};j.dispatch=z;j.options=c.utils.optionsFunc.bind(j);j.x=function(x){if(!arguments.length){return u}u=x;return j};j.y=function(x){if(!arguments.length){return t}t=x;return j};j.margin=function(x){if(!arguments.length){return k}k.top=typeof x.top!="undefined"?x.top:k.top;k.right=typeof x.right!="undefined"?x.right:k.right;k.bottom=typeof x.bottom!="undefined"?x.bottom:k.bottom;k.left=typeof x.left!="undefined"?x.left:k.left;return j};j.width=function(x){if(!arguments.length){return o}o=x;return j};j.height=function(x){if(!arguments.length){return m}m=x;return j};j.xScale=function(x){if(!arguments.length){return h}h=x;return j};j.yScale=function(x){if(!arguments.length){return g}g=x;return j};j.xDomain=function(x){if(!arguments.length){return q}q=x;return j};j.yDomain=function(x){if(!arguments.length){return p}p=x;return j};j.xRange=function(x){if(!arguments.length){return l}l=x;return j};j.yRange=function(x){if(!arguments.length){return r}r=x;return j};j.forceX=function(x){if(!arguments.length){return f}f=x;return j};j.forceY=function(x){if(!arguments.length){return e}e=x;return j};j.padData=function(x){if(!arguments.length){return i}i=x;return j};j.clipEdge=function(x){if(!arguments.length){return w}w=x;return j};j.color=function(x){if(!arguments.length){return s}s=c.utils.getColor(x);return j};j.id=function(x){if(!arguments.length){return n}n=x;return j};j.interactive=function(x){if(!arguments.length){return v}v=false;return j};return j};c.models.historicalBarChart=function(){var v=c.models.historicalBar(),n=c.models.axis(),e=c.models.axis(),g=c.models.legend();var t={top:30,right:90,bottom:50,left:90},z=c.utils.defaultColor(),w=null,u=null,A=false,B=true,q=true,m=false,f=true,j=function(E,D,H,G,F){return"

"+E+"

"+H+" at "+D+"

"},p,o,k={},l=null,i="No Data Available.",C=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState"),r=250;n.orient("bottom").tickPadding(7);e.orient((m)?"right":"left");var h=function(G,L){if(L){var E=d3.select(L).select("svg");var M=(E.node())?E.attr("viewBox"):null;if(M){M=M.split(" ");var H=parseInt(E.style("width"))/M[2];G.pos[0]=G.pos[0]*H;G.pos[1]=G.pos[1]*H}}var D=G.pos[0]+(L.offsetLeft||0),J=G.pos[1]+(L.offsetTop||0),K=n.tickFormat()(v.x()(G.point,G.pointIndex)),I=e.tickFormat()(v.y()(G.point,G.pointIndex)),F=j(G.series.key,K,I,G,s);c.tooltip.show([D,J],F,null,null,L)};function s(x){x.each(function(H){var D=d3.select(this),K=this;var M=(w||parseInt(D.style("width"))||960)-t.left-t.right,G=(u||parseInt(D.style("height"))||400)-t.top-t.bottom;s.update=function(){D.transition().duration(r).call(s)};s.container=this;k.disabled=H.map(function(N){return !!N.disabled});if(!l){var L;l={};for(L in k){if(k[L] instanceof Array){l[L]=k[L].slice(0)}else{l[L]=k[L]}}}if(!H||!H.length||!H.filter(function(N){return N.values.length}).length){var E=D.selectAll(".nv-noData").data([i]);E.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");E.attr("x",t.left+M/2).attr("y",t.top+G/2).text(function(N){return N});return s}else{D.selectAll(".nv-noData").remove()}p=v.xScale();o=v.yScale();var F=D.selectAll("g.nv-wrap.nv-historicalBarChart").data([H]);var y=F.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBarChart").append("g");var I=F.select("g");y.append("g").attr("class","nv-x nv-axis");y.append("g").attr("class","nv-y nv-axis");y.append("g").attr("class","nv-barsWrap");y.append("g").attr("class","nv-legendWrap");if(A){g.width(M);I.select(".nv-legendWrap").datum(H).call(g);if(t.top!=g.height()){t.top=g.height();G=(u||parseInt(D.style("height"))||400)-t.top-t.bottom}F.select(".nv-legendWrap").attr("transform","translate(0,"+(-t.top)+")")}F.attr("transform","translate("+t.left+","+t.top+")");if(m){I.select(".nv-y.nv-axis").attr("transform","translate("+M+",0)")}v.width(M).height(G).color(H.map(function(O,N){return O.color||z(O,N)}).filter(function(O,N){return !H[N].disabled}));var J=I.select(".nv-barsWrap").datum(H.filter(function(N){return !N.disabled}));J.transition().call(v);if(B){n.scale(p).tickSize(-G,0);I.select(".nv-x.nv-axis").attr("transform","translate(0,"+o.range()[0]+")");I.select(".nv-x.nv-axis").transition().call(n)}if(q){e.scale(o).ticks(G/36).tickSize(-M,0);I.select(".nv-y.nv-axis").transition().call(e)}g.dispatch.on("legendClick",function(O,N){O.disabled=!O.disabled;if(!H.filter(function(P){return !P.disabled}).length){H.map(function(P){P.disabled=false;F.selectAll(".nv-series").classed("disabled",false);return P})}k.disabled=H.map(function(P){return !!P.disabled});C.stateChange(k);x.transition().call(s)});g.dispatch.on("legendDblclick",function(N){H.forEach(function(O){O.disabled=true});N.disabled=false;k.disabled=H.map(function(O){return !!O.disabled});C.stateChange(k);s.update()});C.on("tooltipShow",function(N){if(f){h(N,K.parentNode)}});C.on("changeState",function(N){if(typeof N.disabled!=="undefined"){H.forEach(function(P,O){P.disabled=N.disabled[O]});k.disabled=N.disabled}x.call(s)})});return s}v.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+t.left,x.pos[1]+t.top];C.tooltipShow(x)});v.dispatch.on("elementMouseout.tooltip",function(x){C.tooltipHide(x)});C.on("tooltipHide",function(){if(f){c.tooltip.cleanup()}});s.dispatch=C;s.bars=v;s.legend=g;s.xAxis=n;s.yAxis=e;d3.rebind(s,v,"defined","isArea","x","y","size","xScale","yScale","xDomain","yDomain","xRange","yRange","forceX","forceY","interactive","clipEdge","clipVoronoi","id","interpolate","highlightPoint","clearHighlights","interactive");s.options=c.utils.optionsFunc.bind(s);s.margin=function(x){if(!arguments.length){return t}t.top=typeof x.top!="undefined"?x.top:t.top;t.right=typeof x.right!="undefined"?x.right:t.right;t.bottom=typeof x.bottom!="undefined"?x.bottom:t.bottom;t.left=typeof x.left!="undefined"?x.left:t.left;return s};s.width=function(x){if(!arguments.length){return w}w=x;return s};s.height=function(x){if(!arguments.length){return u}u=x;return s};s.color=function(x){if(!arguments.length){return z}z=c.utils.getColor(x);g.color(z);return s};s.showLegend=function(x){if(!arguments.length){return A}A=x;return s};s.showXAxis=function(x){if(!arguments.length){return B}B=x;return s};s.showYAxis=function(x){if(!arguments.length){return q}q=x;return s};s.rightAlignYAxis=function(x){if(!arguments.length){return m}m=x;e.orient((x)?"right":"left");return s};s.tooltips=function(x){if(!arguments.length){return f}f=x;return s};s.tooltipContent=function(x){if(!arguments.length){return j}j=x;return s};s.state=function(x){if(!arguments.length){return k}k=x;return s};s.defaultState=function(x){if(!arguments.length){return l}l=x;return s};s.noData=function(x){if(!arguments.length){return i}i=x;return s};s.transitionDuration=function(x){if(!arguments.length){return r}r=x;return s};return s};c.models.indentedTree=function(){var i={top:0,right:0,bottom:0,left:0},g=960,t=500,j=c.utils.defaultColor(),f=Math.floor(Math.random()*10000),l=true,u=false,s="No Data Available.",m=20,h=[{key:"key",label:"Name",type:"text"}],q=null,e="images/grey-plus.png",k="images/grey-minus.png",n=d3.dispatch("elementClick","elementDblclick","elementMouseover","elementMouseout"),p=function(v){return v.url};var r=0;function o(v){v.each(function(L){var M=1,A=d3.select(this);var B=d3.layout.tree().children(function(O){return O.values}).size([t,m]);o.update=function(){A.transition().duration(600).call(o)};if(!L[0]){L[0]={key:s}}var D=B.nodes(L[0]);var z=d3.select(this).selectAll("div").data([[D]]);var x=z.enter().append("div").attr("class","nvd3 nv-wrap nv-indentedtree");var N=x.append("table");var I=z.select("table").attr("width","100%").attr("class",q);if(l){var H=N.append("thead");var K=H.append("tr");h.forEach(function(O){K.append("th").attr("width",O.width?O.width:"10%").style("text-align",O.type=="numeric"?"right":"left").append("span").text(O.label)})}var w=I.selectAll("tbody").data(function(O){return O});w.enter().append("tbody");M=d3.max(D,function(O){return O.depth});B.size([t,M*m]);var F=w.selectAll("tr").data(function(O){return O.filter(function(P){return(u&&!P.children)?u(P):true})},function(P,O){return P.id||(P.id||++r)});F.exit().remove();F.select("img.nv-treeicon").attr("src",J).classed("folded",C);var y=F.enter().append("tr");h.forEach(function(P,O){var Q=y.append("td").style("padding-left",function(R){return(O?0:R.depth*m+12+(J(R)?0:16))+"px"},"important").style("text-align",P.type=="numeric"?"right":"left");if(O==0){Q.append("img").classed("nv-treeicon",true).classed("nv-folded",C).attr("src",J).style("width","14px").style("height","14px").style("padding","0 1px").style("display",function(R){return J(R)?"inline-block":"none"}).on("click",E)}Q.each(function(R){if(!O&&p(R)){d3.select(this).append("a").attr("href",p).attr("class",d3.functor(P.classes)).append("span")}else{d3.select(this).append("span")}d3.select(this).select("span").attr("class",d3.functor(P.classes)).text(function(S){return P.format?P.format(S):(S[P.key]||"-")})});if(P.showCount){Q.append("span").attr("class","nv-childrenCount");F.selectAll("span.nv-childrenCount").text(function(R){return((R.values&&R.values.length)||(R._values&&R._values.length))?"("+((R.values&&(R.values.filter(function(S){return u?u(S):true}).length))||(R._values&&R._values.filter(function(S){return u?u(S):true}).length)||0)+")":""})}});F.order().on("click",function(O){n.elementClick({row:this,data:O,pos:[O.x,O.y]})}).on("dblclick",function(O){n.elementDblclick({row:this,data:O,pos:[O.x,O.y]})}).on("mouseover",function(O){n.elementMouseover({row:this,data:O,pos:[O.x,O.y]})}).on("mouseout",function(O){n.elementMouseout({row:this,data:O,pos:[O.x,O.y]})});function E(Q,P,O){d3.event.stopPropagation();if(d3.event.shiftKey&&!O){d3.event.shiftKey=false;Q.values&&Q.values.forEach(function(R){if(R.values||R._values){E(R,0,true)}});return true}if(!G(Q)){return true}if(Q.values){Q._values=Q.values;Q.values=null}else{Q.values=Q._values;Q._values=null}o.update()}function J(O){return(O._values&&O._values.length)?e:(O.values&&O.values.length)?k:""}function C(O){return(O._values&&O._values.length)}function G(P){var O=P.values||P._values;return(O&&O.length)}});return o}o.options=c.utils.optionsFunc.bind(o);o.margin=function(v){if(!arguments.length){return i}i.top=typeof v.top!="undefined"?v.top:i.top;i.right=typeof v.right!="undefined"?v.right:i.right;i.bottom=typeof v.bottom!="undefined"?v.bottom:i.bottom;i.left=typeof v.left!="undefined"?v.left:i.left;return o};o.width=function(v){if(!arguments.length){return g}g=v;return o};o.height=function(v){if(!arguments.length){return t}t=v;return o};o.color=function(v){if(!arguments.length){return j}j=c.utils.getColor(v);scatter.color(j);return o};o.id=function(v){if(!arguments.length){return f}f=v;return o};o.header=function(v){if(!arguments.length){return l}l=v;return o};o.noData=function(v){if(!arguments.length){return s}s=v;return o};o.filterZero=function(v){if(!arguments.length){return u}u=v;return o};o.columns=function(v){if(!arguments.length){return h}h=v;return o};o.tableClass=function(v){if(!arguments.length){return q}q=v;return o};o.iconOpen=function(v){if(!arguments.length){return e}e=v;return o};o.iconClose=function(v){if(!arguments.length){return k}k=v;return o};o.getUrl=function(v){if(!arguments.length){return p}p=v;return o};return o};c.models.legend=function(){var h={top:5,right:0,bottom:5,left:0},f=400,n=20,j=function(p){return p.key},i=c.utils.defaultColor(),l=true,g=true,o=true,e=false,k=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange");function m(p){p.each(function(J){var q=f-h.left-h.right,B=d3.select(this);var z=B.selectAll("g.nv-legend").data([J]);var F=z.enter().append("g").attr("class","nvd3 nv-legend").append("g");var I=z.select("g");z.attr("transform","translate("+h.left+","+h.top+")");var A=I.selectAll(".nv-series").data(function(K){return K});var u=A.enter().append("g").attr("class","nv-series").on("mouseover",function(L,K){k.legendMouseover(L,K)}).on("mouseout",function(L,K){k.legendMouseout(L,K)}).on("click",function(L,K){k.legendClick(L,K);if(o){if(e){J.forEach(function(M){M.disabled=true});L.disabled=false}else{L.disabled=!L.disabled;if(J.every(function(M){return M.disabled})){J.forEach(function(M){M.disabled=false})}}k.stateChange({disabled:J.map(function(M){return !!M.disabled})})}}).on("dblclick",function(L,K){k.legendDblclick(L,K);if(o){J.forEach(function(M){M.disabled=true});L.disabled=false;k.stateChange({disabled:J.map(function(M){return !!M.disabled})})}});u.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5);u.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");A.classed("disabled",function(K){return K.disabled});A.exit().remove();A.select("circle").style("fill",function(L,K){return L.color||i(L,K)}).style("stroke",function(L,K){return L.color||i(L,K)});A.select("text").text(j);if(l){var t=[];A.each(function(O,M){var L=d3.select(this).select("text");var K;try{K=L.node().getComputedTextLength()}catch(N){K=c.utils.calcApproxTextWidth(L)}t.push(K+28)});var x=0;var E=0;var C=[];while(Eq&&x>1){C=[];x--;for(var G=0;G(C[G%x]||0)){C[G%x]=t[G]}}E=C.reduce(function(L,M,K,N){return L+M})}var r=[];for(var H=0,y=0;HD){D=w}return"translate("+s+","+v+")"});I.attr("transform","translate("+(f-h.right-D)+","+h.top+")");n=h.top+h.bottom+v+15}});return m}m.dispatch=k;m.options=c.utils.optionsFunc.bind(m);m.margin=function(p){if(!arguments.length){return h}h.top=typeof p.top!="undefined"?p.top:h.top;h.right=typeof p.right!="undefined"?p.right:h.right;h.bottom=typeof p.bottom!="undefined"?p.bottom:h.bottom;h.left=typeof p.left!="undefined"?p.left:h.left;return m};m.width=function(p){if(!arguments.length){return f}f=p;return m};m.height=function(p){if(!arguments.length){return n}n=p;return m};m.key=function(p){if(!arguments.length){return j}j=p;return m};m.color=function(p){if(!arguments.length){return i}i=c.utils.getColor(p);return m};m.align=function(p){if(!arguments.length){return l}l=p;return m};m.rightAlign=function(p){if(!arguments.length){return g}g=p;return m};m.updateState=function(p){if(!arguments.length){return o}o=p;return m};m.radioButtonMode=function(p){if(!arguments.length){return e}e=p;return m};return m};c.models.line=function(){var e=c.models.scatter();var i={top:0,right:0,bottom:0,left:0},f=960,t=500,j=c.utils.defaultColor(),s=function(u){return u.x},r=function(u){return u.y},h=function(v,u){return !isNaN(r(v,u))&&r(v,u)!==null},m=function(u){return u.area},q=false,o,n,k="linear";e.size(16).sizeDomain([16,256]);var g,p;function l(u){u.each(function(E){var H=f-i.left-i.right,z=t-i.top-i.bottom,w=d3.select(this);o=e.xScale();n=e.yScale();g=g||o;p=p||n;var x=w.selectAll("g.nv-wrap.nv-line").data([E]);var C=x.enter().append("g").attr("class","nvd3 nv-wrap nv-line");var A=C.append("defs");var v=C.append("g");var F=x.select("g");v.append("g").attr("class","nv-groups");v.append("g").attr("class","nv-scatterWrap");x.attr("transform","translate("+i.left+","+i.top+")");e.width(H).height(z);var y=x.select(".nv-scatterWrap");y.transition().call(e);A.append("clipPath").attr("id","nv-edge-clip-"+e.id()).append("rect");x.select("#nv-edge-clip-"+e.id()+" rect").attr("width",H).attr("height",z);F.attr("clip-path",q?"url(#nv-edge-clip-"+e.id()+")":"");y.attr("clip-path",q?"url(#nv-edge-clip-"+e.id()+")":"");var B=x.select(".nv-groups").selectAll(".nv-group").data(function(I){return I},function(I){return I.key});B.enter().append("g").style("stroke-opacity",0.000001).style("fill-opacity",0.000001);B.exit().transition().style("stroke-opacity",0.000001).style("fill-opacity",0.000001).remove();B.attr("class",function(J,I){return"nv-group nv-series-"+I}).classed("hover",function(I){return I.hover}).style("fill",function(J,I){return j(J,I)}).style("stroke",function(J,I){return j(J,I)});B.transition().style("stroke-opacity",1).style("fill-opacity",0.5);var G=B.selectAll("path.nv-area").data(function(I){return m(I)?[I]:[]});G.enter().append("path").attr("class","nv-area").attr("d",function(I){return d3.svg.area().interpolate(k).defined(h).x(function(K,J){return c.utils.NaNtoZero(g(s(K,J)))}).y0(function(K,J){return c.utils.NaNtoZero(p(r(K,J)))}).y1(function(K,J){return p(n.domain()[0]<=0?n.domain()[1]>=0?0:n.domain()[1]:n.domain()[0])}).apply(this,[I.values])});B.exit().selectAll("path.nv-area").remove();G.transition().attr("d",function(I){return d3.svg.area().interpolate(k).defined(h).x(function(K,J){return c.utils.NaNtoZero(o(s(K,J)))}).y0(function(K,J){return c.utils.NaNtoZero(n(r(K,J)))}).y1(function(K,J){return n(n.domain()[0]<=0?n.domain()[1]>=0?0:n.domain()[1]:n.domain()[0])}).apply(this,[I.values])});var D=B.selectAll("path.nv-line").data(function(I){return[I.values]});D.enter().append("path").attr("class","nv-line").attr("d",d3.svg.line().interpolate(k).defined(h).x(function(J,I){return c.utils.NaNtoZero(g(s(J,I)))}).y(function(J,I){return c.utils.NaNtoZero(p(r(J,I)))}));B.exit().selectAll("path.nv-line").transition().attr("d",d3.svg.line().interpolate(k).defined(h).x(function(J,I){return c.utils.NaNtoZero(o(s(J,I)))}).y(function(J,I){return c.utils.NaNtoZero(n(r(J,I)))}));D.transition().attr("d",d3.svg.line().interpolate(k).defined(h).x(function(J,I){return c.utils.NaNtoZero(o(s(J,I)))}).y(function(J,I){return c.utils.NaNtoZero(n(r(J,I)))}));g=o.copy();p=n.copy()});return l}l.dispatch=e.dispatch;l.scatter=e;d3.rebind(l,e,"id","interactive","size","xScale","yScale","zScale","xDomain","yDomain","xRange","yRange","sizeDomain","forceX","forceY","forceSize","clipVoronoi","useVoronoi","clipRadius","padData","highlightPoint","clearHighlights");l.options=c.utils.optionsFunc.bind(l);l.margin=function(u){if(!arguments.length){return i}i.top=typeof u.top!="undefined"?u.top:i.top;i.right=typeof u.right!="undefined"?u.right:i.right;i.bottom=typeof u.bottom!="undefined"?u.bottom:i.bottom;i.left=typeof u.left!="undefined"?u.left:i.left;return l};l.width=function(u){if(!arguments.length){return f}f=u;return l};l.height=function(u){if(!arguments.length){return t}t=u;return l};l.x=function(u){if(!arguments.length){return s}s=u;e.x(u);return l};l.y=function(u){if(!arguments.length){return r}r=u;e.y(u);return l};l.clipEdge=function(u){if(!arguments.length){return q}q=u;return l};l.color=function(u){if(!arguments.length){return j}j=c.utils.getColor(u);e.color(j);return l};l.interpolate=function(u){if(!arguments.length){return k}k=u;return l};l.defined=function(u){if(!arguments.length){return h}h=u;return l};l.isArea=function(u){if(!arguments.length){return m}m=d3.functor(u);return l};return l};c.models.lineChart=function(){var f=c.models.line(),o=c.models.axis(),e=c.models.axis(),h=c.models.legend(),z=c.interactiveGuideline();var v={top:30,right:20,bottom:50,left:60},B=c.utils.defaultColor(),A=null,w=null,C=true,D=true,s=true,n=false,r=false,g=true,l=function(G,F,J,I,H){return"

"+G+"

"+J+" at "+F+"

"},q,p,k={},m=null,j="No Data Available.",E=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState"),t=250;o.orient("bottom").tickPadding(7);e.orient((n)?"right":"left");var i=function(K,G){var J=K.pos[0]+(G.offsetLeft||0),I=K.pos[1]+(G.offsetTop||0),F=o.tickFormat()(f.x()(K.point,K.pointIndex)),L=e.tickFormat()(f.y()(K.point,K.pointIndex)),H=l(K.series.key,F,L,K,u);c.tooltip.show([J,I],H,null,null,G)};function u(x){x.each(function(K){var G=d3.select(this),M=this;var O=(A||parseInt(G.style("width"))||960)-v.left-v.right,J=(w||parseInt(G.style("height"))||400)-v.top-v.bottom;u.update=function(){G.transition().duration(t).call(u)};u.container=this;k.disabled=K.map(function(P){return !!P.disabled});if(!m){var N;m={};for(N in k){if(k[N] instanceof Array){m[N]=k[N].slice(0)}else{m[N]=k[N]}}}if(!K||!K.length||!K.filter(function(P){return P.values.length}).length){var H=G.selectAll(".nv-noData").data([j]);H.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");H.attr("x",v.left+O/2).attr("y",v.top+J/2).text(function(P){return P});return u}else{G.selectAll(".nv-noData").remove()}q=f.xScale();p=f.yScale();var I=G.selectAll("g.nv-wrap.nv-lineChart").data([K]);var y=I.enter().append("g").attr("class","nvd3 nv-wrap nv-lineChart").append("g");var L=I.select("g");y.append("rect").style("opacity",0);y.append("g").attr("class","nv-x nv-axis");y.append("g").attr("class","nv-y nv-axis");y.append("g").attr("class","nv-linesWrap");y.append("g").attr("class","nv-legendWrap");y.append("g").attr("class","nv-interactive");L.select("rect").attr("width",O).attr("height",J);if(C){h.width(O);L.select(".nv-legendWrap").datum(K).call(h);if(v.top!=h.height()){v.top=h.height();J=(w||parseInt(G.style("height"))||400)-v.top-v.bottom}I.select(".nv-legendWrap").attr("transform","translate(0,"+(-v.top)+")")}I.attr("transform","translate("+v.left+","+v.top+")");if(n){L.select(".nv-y.nv-axis").attr("transform","translate("+O+",0)")}if(r){z.width(O).height(J).margin({left:v.left,top:v.top}).svgContainer(G).xScale(q);I.select(".nv-interactive").call(z)}f.width(O).height(J).color(K.map(function(Q,P){return Q.color||B(Q,P)}).filter(function(Q,P){return !K[P].disabled}));var F=L.select(".nv-linesWrap").datum(K.filter(function(P){return !P.disabled}));F.transition().call(f);if(D){o.scale(q).ticks(O/100).tickSize(-J,0);L.select(".nv-x.nv-axis").attr("transform","translate(0,"+p.range()[0]+")");L.select(".nv-x.nv-axis").transition().call(o)}if(s){e.scale(p).ticks(J/36).tickSize(-O,0);L.select(".nv-y.nv-axis").transition().call(e)}h.dispatch.on("stateChange",function(P){k=P;E.stateChange(k);u.update()});z.dispatch.on("elementMousemove",function(U){f.clearHighlights();var T,S,P,R=[];K.filter(function(W,V){W.seriesIndex=V;return !W.disabled}).forEach(function(X,W){S=c.interactiveBisect(X.values,U.pointXValue,u.x());f.highlightPoint(W,S,true);var V=X.values[S];if(typeof V==="undefined"){return}if(typeof T==="undefined"){T=V}if(typeof P==="undefined"){P=u.xScale()(u.x()(V,S))}R.push({key:X.key,value:u.y()(V,S),color:B(X,X.seriesIndex)})});var Q=o.tickFormat()(u.x()(T,S));z.tooltip.position({left:P+v.left,top:U.mouseY+v.top}).chartContainer(M.parentNode).enabled(g).valueFormatter(function(W,V){return e.tickFormat()(W)}).data({value:Q,series:R})();z.renderGuideLine(P)});z.dispatch.on("elementMouseout",function(P){E.tooltipHide();f.clearHighlights()});E.on("tooltipShow",function(P){if(g){i(P,M.parentNode)}});E.on("changeState",function(P){if(typeof P.disabled!=="undefined"){K.forEach(function(R,Q){R.disabled=P.disabled[Q]});k.disabled=P.disabled}u.update()})});return u}f.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+v.left,x.pos[1]+v.top];E.tooltipShow(x)});f.dispatch.on("elementMouseout.tooltip",function(x){E.tooltipHide(x)});E.on("tooltipHide",function(){if(g){c.tooltip.cleanup()}});u.dispatch=E;u.lines=f;u.legend=h;u.xAxis=o;u.yAxis=e;u.interactiveLayer=z;d3.rebind(u,f,"defined","isArea","x","y","size","xScale","yScale","xDomain","yDomain","xRange","yRange","forceX","forceY","interactive","clipEdge","clipVoronoi","useVoronoi","id","interpolate");u.options=c.utils.optionsFunc.bind(u);u.margin=function(x){if(!arguments.length){return v}v.top=typeof x.top!="undefined"?x.top:v.top;v.right=typeof x.right!="undefined"?x.right:v.right;v.bottom=typeof x.bottom!="undefined"?x.bottom:v.bottom;v.left=typeof x.left!="undefined"?x.left:v.left;return u};u.width=function(x){if(!arguments.length){return A}A=x;return u};u.height=function(x){if(!arguments.length){return w}w=x;return u};u.color=function(x){if(!arguments.length){return B}B=c.utils.getColor(x);h.color(B);return u};u.showLegend=function(x){if(!arguments.length){return C}C=x;return u};u.showXAxis=function(x){if(!arguments.length){return D}D=x;return u};u.showYAxis=function(x){if(!arguments.length){return s}s=x;return u};u.rightAlignYAxis=function(x){if(!arguments.length){return n}n=x;e.orient((x)?"right":"left");return u};u.useInteractiveGuideline=function(x){if(!arguments.length){return r}r=x;if(x===true){u.interactive(false);u.useVoronoi(false)}return u};u.tooltip=function(x){if(!arguments.length){return l}l=x;return u};u.tooltips=function(x){if(!arguments.length){return g}g=x;return u};u.tooltipContent=function(x){if(!arguments.length){return l}l=x;return u};u.state=function(x){if(!arguments.length){return k}k=x;return u};u.defaultState=function(x){if(!arguments.length){return m}m=x;return u};u.noData=function(x){if(!arguments.length){return j}j=x;return u};u.transitionDuration=function(x){if(!arguments.length){return t}t=x;return u};return u};c.models.linePlusBarChart=function(){var e=c.models.line(),v=c.models.historicalBar(),p=c.models.axis(),r=c.models.axis(),h=c.models.axis(),g=c.models.legend();var t={top:30,right:60,bottom:50,left:60},w=null,u=null,A=function(x){return x.x},z=function(x){return x.y},y=c.utils.defaultColor(),B=true,f=true,n=function(E,D,H,G,F){return"

"+E+"

"+H+" at "+D+"

"},q,j,i,m={},o=null,l="No Data Available.",C=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState");v.padData(true);e.clipEdge(false).padData(true);p.orient("bottom").tickPadding(7).highlightZero(false);r.orient("left");h.orient("right");var k=function(I,E){var H=I.pos[0]+(E.offsetLeft||0),G=I.pos[1]+(E.offsetTop||0),D=p.tickFormat()(e.x()(I.point,I.pointIndex)),J=(I.series.bar?r:h).tickFormat()(e.y()(I.point,I.pointIndex)),F=n(I.series.key,D,J,I,s);c.tooltip.show([H,G],F,I.value<0?"n":"s",null,E)};function s(x){x.each(function(K){var F=d3.select(this),N=this;var P=(w||parseInt(F.style("width"))||960)-t.left-t.right,I=(u||parseInt(F.style("height"))||400)-t.top-t.bottom;s.update=function(){F.transition().call(s)};m.disabled=K.map(function(R){return !!R.disabled});if(!o){var O;o={};for(O in m){if(m[O] instanceof Array){o[O]=m[O].slice(0)}else{o[O]=m[O]}}}if(!K||!K.length||!K.filter(function(R){return R.values.length}).length){var G=F.selectAll(".nv-noData").data([l]);G.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");G.attr("x",t.left+P/2).attr("y",t.top+I/2).text(function(R){return R});return s}else{F.selectAll(".nv-noData").remove()}var Q=K.filter(function(R){return !R.disabled&&R.bar});var J=K.filter(function(R){return !R.bar});q=J.filter(function(R){return !R.disabled}).length&&J.filter(function(R){return !R.disabled})[0].values.length?e.xScale():v.xScale();j=v.yScale();i=e.yScale();var H=d3.select(this).selectAll("g.nv-wrap.nv-linePlusBar").data([K]);var D=H.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g");var L=H.select("g");D.append("g").attr("class","nv-x nv-axis");D.append("g").attr("class","nv-y1 nv-axis");D.append("g").attr("class","nv-y2 nv-axis");D.append("g").attr("class","nv-barsWrap");D.append("g").attr("class","nv-linesWrap");D.append("g").attr("class","nv-legendWrap");if(B){g.width(P/2);L.select(".nv-legendWrap").datum(K.map(function(R){R.originalKey=R.originalKey===undefined?R.key:R.originalKey;R.key=R.originalKey+(R.bar?" (left axis)":" (right axis)");return R})).call(g);if(t.top!=g.height()){t.top=g.height();I=(u||parseInt(F.style("height"))||400)-t.top-t.bottom}L.select(".nv-legendWrap").attr("transform","translate("+(P/2)+","+(-t.top)+")")}H.attr("transform","translate("+t.left+","+t.top+")");e.width(P).height(I).color(K.map(function(S,R){return S.color||y(S,R)}).filter(function(S,R){return !K[R].disabled&&!K[R].bar}));v.width(P).height(I).color(K.map(function(S,R){return S.color||y(S,R)}).filter(function(S,R){return !K[R].disabled&&K[R].bar}));var M=L.select(".nv-barsWrap").datum(Q.length?Q:[{values:[]}]);var E=L.select(".nv-linesWrap").datum(J[0]&&!J[0].disabled?J:[{values:[]}]);d3.transition(M).call(v);d3.transition(E).call(e);p.scale(q).ticks(P/100).tickSize(-I,0);L.select(".nv-x.nv-axis").attr("transform","translate(0,"+j.range()[0]+")");d3.transition(L.select(".nv-x.nv-axis")).call(p);r.scale(j).ticks(I/36).tickSize(-P,0);d3.transition(L.select(".nv-y1.nv-axis")).style("opacity",Q.length?1:0).call(r);h.scale(i).ticks(I/36).tickSize(Q.length?0:-P,0);L.select(".nv-y2.nv-axis").style("opacity",J.length?1:0).attr("transform","translate("+P+",0)");d3.transition(L.select(".nv-y2.nv-axis")).call(h);g.dispatch.on("stateChange",function(R){m=R;C.stateChange(m);s.update()});C.on("tooltipShow",function(R){if(f){k(R,N.parentNode)}});C.on("changeState",function(R){if(typeof R.disabled!=="undefined"){K.forEach(function(T,S){T.disabled=R.disabled[S]});m.disabled=R.disabled}s.update()})});return s}e.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+t.left,x.pos[1]+t.top];C.tooltipShow(x)});e.dispatch.on("elementMouseout.tooltip",function(x){C.tooltipHide(x)});v.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+t.left,x.pos[1]+t.top];C.tooltipShow(x)});v.dispatch.on("elementMouseout.tooltip",function(x){C.tooltipHide(x)});C.on("tooltipHide",function(){if(f){c.tooltip.cleanup()}});s.dispatch=C;s.legend=g;s.lines=e;s.bars=v;s.xAxis=p;s.y1Axis=r;s.y2Axis=h;d3.rebind(s,e,"defined","size","clipVoronoi","interpolate");s.options=c.utils.optionsFunc.bind(s);s.x=function(x){if(!arguments.length){return A}A=x;e.x(x);v.x(x);return s};s.y=function(x){if(!arguments.length){return z}z=x;e.y(x);v.y(x);return s};s.margin=function(x){if(!arguments.length){return t}t.top=typeof x.top!="undefined"?x.top:t.top;t.right=typeof x.right!="undefined"?x.right:t.right;t.bottom=typeof x.bottom!="undefined"?x.bottom:t.bottom;t.left=typeof x.left!="undefined"?x.left:t.left;return s};s.width=function(x){if(!arguments.length){return w}w=x;return s};s.height=function(x){if(!arguments.length){return u}u=x;return s};s.color=function(x){if(!arguments.length){return y}y=c.utils.getColor(x);g.color(y);return s};s.showLegend=function(x){if(!arguments.length){return B}B=x;return s};s.tooltips=function(x){if(!arguments.length){return f}f=x;return s};s.tooltipContent=function(x){if(!arguments.length){return n}n=x;return s};s.state=function(x){if(!arguments.length){return m}m=x;return s};s.defaultState=function(x){if(!arguments.length){return o}o=x;return s};s.noData=function(x){if(!arguments.length){return l}l=x;return s};return s};c.models.lineWithFocusChart=function(){var g=c.models.line(),p=c.models.line(),q=c.models.axis(),e=c.models.axis(),G=c.models.axis(),j=c.models.axis(),h=c.models.legend(),i=d3.svg.brush();var z={top:30,right:30,bottom:30,left:60},t={top:0,right:30,bottom:20,left:60},C=c.utils.defaultColor(),B=null,A=null,n=100,s,r,D,k,E=true,v=null,f=true,o=function(I,H,L,K,J){return"

"+I+"

"+L+" at "+H+"

"},m="No Data Available.",F=d3.dispatch("tooltipShow","tooltipHide","brush"),u=250;g.clipEdge(true);p.interactive(false);q.orient("bottom").tickPadding(5);e.orient("left");G.orient("bottom").tickPadding(5);j.orient("left");var l=function(M,I){var L=M.pos[0]+(I.offsetLeft||0),K=M.pos[1]+(I.offsetTop||0),H=q.tickFormat()(g.x()(M.point,M.pointIndex)),N=e.tickFormat()(g.y()(M.point,M.pointIndex)),J=o(M.series.key,H,N,M,w);c.tooltip.show([L,K],J,null,null,I)};function w(x){x.each(function(Y){var P=d3.select(this),L=this;var y=(B||parseInt(P.style("width"))||960)-z.left-z.right,M=(A||parseInt(P.style("height"))||400)-z.top-z.bottom-n,K=n-t.top-t.bottom;w.update=function(){P.transition().duration(u).call(w)};w.container=this;if(!Y||!Y.length||!Y.filter(function(Z){return Z.values.length}).length){var O=P.selectAll(".nv-noData").data([m]);O.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");O.attr("x",z.left+y/2).attr("y",z.top+M/2).text(function(Z){return Z});return w}else{P.selectAll(".nv-noData").remove()}s=g.xScale();r=g.yScale();D=p.xScale();k=p.yScale();var N=P.selectAll("g.nv-wrap.nv-lineWithFocusChart").data([Y]);var S=N.enter().append("g").attr("class","nvd3 nv-wrap nv-lineWithFocusChart").append("g");var U=N.select("g");S.append("g").attr("class","nv-legendWrap");var J=S.append("g").attr("class","nv-focus");J.append("g").attr("class","nv-x nv-axis");J.append("g").attr("class","nv-y nv-axis");J.append("g").attr("class","nv-linesWrap");var H=S.append("g").attr("class","nv-context");H.append("g").attr("class","nv-x nv-axis");H.append("g").attr("class","nv-y nv-axis");H.append("g").attr("class","nv-linesWrap");H.append("g").attr("class","nv-brushBackground");H.append("g").attr("class","nv-x nv-brush");if(E){h.width(y);U.select(".nv-legendWrap").datum(Y).call(h);if(z.top!=h.height()){z.top=h.height();M=(A||parseInt(P.style("height"))||400)-z.top-z.bottom-n}U.select(".nv-legendWrap").attr("transform","translate(0,"+(-z.top)+")")}N.attr("transform","translate("+z.left+","+z.top+")");g.width(y).height(M).color(Y.map(function(aa,Z){return aa.color||C(aa,Z)}).filter(function(aa,Z){return !Y[Z].disabled}));p.defined(g.defined()).width(y).height(K).color(Y.map(function(aa,Z){return aa.color||C(aa,Z)}).filter(function(aa,Z){return !Y[Z].disabled}));U.select(".nv-context").attr("transform","translate(0,"+(M+z.bottom+t.top)+")");var I=U.select(".nv-context .nv-linesWrap").datum(Y.filter(function(Z){return !Z.disabled}));d3.transition(I).call(p);q.scale(s).ticks(y/100).tickSize(-M,0);e.scale(r).ticks(M/36).tickSize(-y,0);U.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+M+")");i.x(D).on("brush",function(){var Z=w.transitionDuration();w.transitionDuration(0);R();w.transitionDuration(Z)});if(v){i.extent(v)}var W=U.select(".nv-brushBackground").selectAll("g").data([v||i.extent()]);var V=W.enter().append("g");V.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",K);V.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",K);var X=U.select(".nv-x.nv-brush").call(i);X.selectAll("rect").attr("height",K);X.selectAll(".resize").append("path").attr("d",Q);R();G.scale(D).ticks(y/100).tickSize(-K,0);U.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+k.range()[0]+")");d3.transition(U.select(".nv-context .nv-x.nv-axis")).call(G);j.scale(k).ticks(K/36).tickSize(-y,0);d3.transition(U.select(".nv-context .nv-y.nv-axis")).call(j);U.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+k.range()[0]+")");h.dispatch.on("stateChange",function(Z){w.update()});F.on("tooltipShow",function(Z){if(f){l(Z,L.parentNode)}});function Q(ab){var aa=+(ab=="e"),Z=aa?1:-1,ac=K/3;return"M"+(0.5*Z)+","+ac+"A6,6 0 0 "+aa+" "+(6.5*Z)+","+(ac+6)+"V"+(2*ac-6)+"A6,6 0 0 "+aa+" "+(0.5*Z)+","+(2*ac)+"ZM"+(2.5*Z)+","+(ac+8)+"V"+(2*ac-8)+"M"+(4.5*Z)+","+(ac+8)+"V"+(2*ac-8)}function T(){if(!i.empty()){i.extent(v)}W.data([i.empty()?D.domain():v]).each(function(ac,Z){var aa=D(ac[0])-s.range()[0],ab=s.range()[1]-D(ac[1]);d3.select(this).select(".left").attr("width",aa<0?0:aa);d3.select(this).select(".right").attr("x",D(ac[1])).attr("width",ab<0?0:ab)})}function R(){v=i.empty()?null:i.extent();var aa=i.empty()?D.domain():i.extent();if(Math.abs(aa[0]-aa[1])<=1){return}F.brush({extent:aa,brush:i});T();var Z=U.select(".nv-focus .nv-linesWrap").datum(Y.filter(function(ab){return !ab.disabled}).map(function(ac,ab){return{key:ac.key,values:ac.values.filter(function(ae,ad){return g.x()(ae,ad)>=aa[0]&&g.x()(ae,ad)<=aa[1]})}}));Z.transition().duration(u).call(g);U.select(".nv-focus .nv-x.nv-axis").transition().duration(u).call(q);U.select(".nv-focus .nv-y.nv-axis").transition().duration(u).call(e)}});return w}g.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+z.left,x.pos[1]+z.top];F.tooltipShow(x)});g.dispatch.on("elementMouseout.tooltip",function(x){F.tooltipHide(x)});F.on("tooltipHide",function(){if(f){c.tooltip.cleanup()}});w.dispatch=F;w.legend=h;w.lines=g;w.lines2=p;w.xAxis=q;w.yAxis=e;w.x2Axis=G;w.y2Axis=j;d3.rebind(w,g,"defined","isArea","size","xDomain","yDomain","xRange","yRange","forceX","forceY","interactive","clipEdge","clipVoronoi","id");w.options=c.utils.optionsFunc.bind(w);w.x=function(x){if(!arguments.length){return g.x}g.x(x);p.x(x);return w};w.y=function(x){if(!arguments.length){return g.y}g.y(x);p.y(x);return w};w.margin=function(x){if(!arguments.length){return z}z.top=typeof x.top!="undefined"?x.top:z.top;z.right=typeof x.right!="undefined"?x.right:z.right;z.bottom=typeof x.bottom!="undefined"?x.bottom:z.bottom;z.left=typeof x.left!="undefined"?x.left:z.left;return w};w.margin2=function(x){if(!arguments.length){return t}t=x;return w};w.width=function(x){if(!arguments.length){return B}B=x;return w};w.height=function(x){if(!arguments.length){return A}A=x;return w};w.height2=function(x){if(!arguments.length){return n}n=x;return w};w.color=function(x){if(!arguments.length){return C}C=c.utils.getColor(x);h.color(C);return w};w.showLegend=function(x){if(!arguments.length){return E}E=x;return w};w.tooltips=function(x){if(!arguments.length){return f}f=x;return w};w.tooltipContent=function(x){if(!arguments.length){return o}o=x;return w};w.interpolate=function(x){if(!arguments.length){return g.interpolate()}g.interpolate(x);p.interpolate(x);return w};w.noData=function(x){if(!arguments.length){return m}m=x;return w};w.xTickFormat=function(x){if(!arguments.length){return q.tickFormat()}q.tickFormat(x);G.tickFormat(x);return w};w.yTickFormat=function(x){if(!arguments.length){return e.tickFormat()}e.tickFormat(x);j.tickFormat(x);return w};w.brushExtent=function(x){if(!arguments.length){return v}v=x;return w};w.transitionDuration=function(x){if(!arguments.length){return u}u=x;return w};return w};c.models.linePlusBarWithFocusChart=function(){var v=c.models.line(),o=c.models.line(),u=c.models.historicalBar(),A=c.models.historicalBar(),p=c.models.axis(),t=c.models.axis(),m=c.models.axis(),g=c.models.axis(),E=c.models.axis(),r=c.models.axis(),z=c.models.legend(),G=d3.svg.brush();var i={top:30,right:30,bottom:30,left:60},H={top:0,right:30,bottom:20,left:60},e=null,f=null,q=100,F=function(x){return x.x},D=function(x){return x.y},s=c.utils.defaultColor(),n=true,h,j=null,C=true,w=function(Q,P,T,S,R){return"

"+Q+"

"+T+" at "+P+"

"},y,l,O,M,L,K,k="No Data Available.",B=d3.dispatch("tooltipShow","tooltipHide","brush"),I=0;v.clipEdge(true);o.interactive(false);p.orient("bottom").tickPadding(5);m.orient("left");g.orient("right");t.orient("bottom").tickPadding(5);E.orient("left");r.orient("right");var N=function(U,Q){if(h){U.pointIndex+=Math.ceil(h[0])}var T=U.pos[0]+(Q.offsetLeft||0),S=U.pos[1]+(Q.offsetTop||0),P=p.tickFormat()(v.x()(U.point,U.pointIndex)),V=(U.series.bar?m:g).tickFormat()(v.y()(U.point,U.pointIndex)),R=w(U.series.key,P,V,U,J);c.tooltip.show([T,S],R,U.value<0?"n":"s",null,Q)};function J(x){x.each(function(am){var aa=d3.select(this),U=this;var P=(e||parseInt(aa.style("width"))||960)-i.left-i.right,V=(f||parseInt(aa.style("height"))||400)-i.top-i.bottom-q,T=q-H.top-H.bottom;J.update=function(){aa.transition().duration(I).call(J)};J.container=this;if(!am||!am.length||!am.filter(function(an){return an.values.length}).length){var Z=aa.selectAll(".nv-noData").data([k]);Z.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");Z.attr("x",i.left+P/2).attr("y",i.top+V/2).text(function(an){return an});return J}else{aa.selectAll(".nv-noData").remove()}var Q=am.filter(function(an){return !an.disabled&&an.bar});var ag=am.filter(function(an){return !an.bar});y=u.xScale();l=t.scale();O=u.yScale();M=v.yScale();L=A.yScale();K=o.yScale();var X=am.filter(function(an){return !an.disabled&&an.bar}).map(function(an){return an.values.map(function(ap,ao){return{x:F(ap,ao),y:D(ap,ao)}})});var W=am.filter(function(an){return !an.disabled&&!an.bar}).map(function(an){return an.values.map(function(ap,ao){return{x:F(ap,ao),y:D(ap,ao)}})});y.range([0,P]);l.domain(d3.extent(d3.merge(X.concat(W)),function(an){return an.x})).range([0,P]);var Y=aa.selectAll("g.nv-wrap.nv-linePlusBar").data([am]);var ad=Y.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g");var ah=Y.select("g");ad.append("g").attr("class","nv-legendWrap");var S=ad.append("g").attr("class","nv-focus");S.append("g").attr("class","nv-x nv-axis");S.append("g").attr("class","nv-y1 nv-axis");S.append("g").attr("class","nv-y2 nv-axis");S.append("g").attr("class","nv-barsWrap");S.append("g").attr("class","nv-linesWrap");var R=ad.append("g").attr("class","nv-context");R.append("g").attr("class","nv-x nv-axis");R.append("g").attr("class","nv-y1 nv-axis");R.append("g").attr("class","nv-y2 nv-axis");R.append("g").attr("class","nv-barsWrap");R.append("g").attr("class","nv-linesWrap");R.append("g").attr("class","nv-brushBackground");R.append("g").attr("class","nv-x nv-brush");if(n){z.width(P/2);ah.select(".nv-legendWrap").datum(am.map(function(an){an.originalKey=an.originalKey===undefined?an.key:an.originalKey;an.key=an.originalKey+(an.bar?" (left axis)":" (right axis)");return an})).call(z);if(i.top!=z.height()){i.top=z.height();V=(f||parseInt(aa.style("height"))||400)-i.top-i.bottom-q}ah.select(".nv-legendWrap").attr("transform","translate("+(P/2)+","+(-i.top)+")")}Y.attr("transform","translate("+i.left+","+i.top+")");A.width(P).height(T).color(am.map(function(ao,an){return ao.color||s(ao,an)}).filter(function(ao,an){return !am[an].disabled&&am[an].bar}));o.width(P).height(T).color(am.map(function(ao,an){return ao.color||s(ao,an)}).filter(function(ao,an){return !am[an].disabled&&!am[an].bar}));var ai=ah.select(".nv-context .nv-barsWrap").datum(Q.length?Q:[{values:[]}]);var af=ah.select(".nv-context .nv-linesWrap").datum(!ag[0].disabled?ag:[{values:[]}]);ah.select(".nv-context").attr("transform","translate(0,"+(V+i.bottom+H.top)+")");ai.transition().call(A);af.transition().call(o);G.x(l).on("brush",ac);if(j){G.extent(j)}var al=ah.select(".nv-brushBackground").selectAll("g").data([j||G.extent()]);var aj=al.enter().append("g");aj.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",T);aj.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",T);var ak=ah.select(".nv-x.nv-brush").call(G);ak.selectAll("rect").attr("height",T);ak.selectAll(".resize").append("path").attr("d",ab);t.ticks(P/100).tickSize(-T,0);ah.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+L.range()[0]+")");ah.select(".nv-context .nv-x.nv-axis").transition().call(t);E.scale(L).ticks(T/36).tickSize(-P,0);ah.select(".nv-context .nv-y1.nv-axis").style("opacity",Q.length?1:0).attr("transform","translate(0,"+l.range()[0]+")");ah.select(".nv-context .nv-y1.nv-axis").transition().call(E);r.scale(K).ticks(T/36).tickSize(Q.length?0:-P,0);ah.select(".nv-context .nv-y2.nv-axis").style("opacity",ag.length?1:0).attr("transform","translate("+l.range()[1]+",0)");ah.select(".nv-context .nv-y2.nv-axis").transition().call(r);z.dispatch.on("stateChange",function(an){J.update()});B.on("tooltipShow",function(an){if(C){N(an,U.parentNode)}});function ab(ap){var ao=+(ap=="e"),an=ao?1:-1,aq=T/3;return"M"+(0.5*an)+","+aq+"A6,6 0 0 "+ao+" "+(6.5*an)+","+(aq+6)+"V"+(2*aq-6)+"A6,6 0 0 "+ao+" "+(0.5*an)+","+(2*aq)+"ZM"+(2.5*an)+","+(aq+8)+"V"+(2*aq-8)+"M"+(4.5*an)+","+(aq+8)+"V"+(2*aq-8)}function ae(){if(!G.empty()){G.extent(j)}al.data([G.empty()?l.domain():j]).each(function(aq,an){var ao=l(aq[0])-l.range()[0],ap=l.range()[1]-l(aq[1]);d3.select(this).select(".left").attr("width",ao<0?0:ao);d3.select(this).select(".right").attr("x",l(aq[1])).attr("width",ap<0?0:ap)})}function ac(){j=G.empty()?null:G.extent();h=G.empty()?l.domain():G.extent();B.brush({extent:h,brush:G});ae();u.width(P).height(V).color(am.map(function(aq,ap){return aq.color||s(aq,ap)}).filter(function(aq,ap){return !am[ap].disabled&&am[ap].bar}));v.width(P).height(V).color(am.map(function(aq,ap){return aq.color||s(aq,ap)}).filter(function(aq,ap){return !am[ap].disabled&&!am[ap].bar}));var ao=ah.select(".nv-focus .nv-barsWrap").datum(!Q.length?[{values:[]}]:Q.map(function(aq,ap){return{key:aq.key,values:aq.values.filter(function(at,ar){return u.x()(at,ar)>=h[0]&&u.x()(at,ar)<=h[1]})}}));var an=ah.select(".nv-focus .nv-linesWrap").datum(ag[0].disabled?[{values:[]}]:ag.map(function(aq,ap){return{key:aq.key,values:aq.values.filter(function(at,ar){return v.x()(at,ar)>=h[0]&&v.x()(at,ar)<=h[1]})}}));if(Q.length){y=u.xScale()}else{y=v.xScale()}p.scale(y).ticks(P/100).tickSize(-V,0);p.domain([Math.ceil(h[0]),Math.floor(h[1])]);ah.select(".nv-x.nv-axis").transition().duration(I).call(p);ao.transition().duration(I).call(u);an.transition().duration(I).call(v);ah.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+O.range()[0]+")");m.scale(O).ticks(V/36).tickSize(-P,0);ah.select(".nv-focus .nv-y1.nv-axis").style("opacity",Q.length?1:0);g.scale(M).ticks(V/36).tickSize(Q.length?0:-P,0);ah.select(".nv-focus .nv-y2.nv-axis").style("opacity",ag.length?1:0).attr("transform","translate("+y.range()[1]+",0)");ah.select(".nv-focus .nv-y1.nv-axis").transition().duration(I).call(m);ah.select(".nv-focus .nv-y2.nv-axis").transition().duration(I).call(g)}ac()});return J}v.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+i.left,x.pos[1]+i.top];B.tooltipShow(x)});v.dispatch.on("elementMouseout.tooltip",function(x){B.tooltipHide(x)});u.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+i.left,x.pos[1]+i.top];B.tooltipShow(x)});u.dispatch.on("elementMouseout.tooltip",function(x){B.tooltipHide(x)});B.on("tooltipHide",function(){if(C){c.tooltip.cleanup()}});J.dispatch=B;J.legend=z;J.lines=v;J.lines2=o;J.bars=u;J.bars2=A;J.xAxis=p;J.x2Axis=t;J.y1Axis=m;J.y2Axis=g;J.y3Axis=E;J.y4Axis=r;d3.rebind(J,v,"defined","size","clipVoronoi","interpolate");J.options=c.utils.optionsFunc.bind(J);J.x=function(x){if(!arguments.length){return F}F=x;v.x(x);u.x(x);return J};J.y=function(x){if(!arguments.length){return D}D=x;v.y(x);u.y(x);return J};J.margin=function(x){if(!arguments.length){return i}i.top=typeof x.top!="undefined"?x.top:i.top;i.right=typeof x.right!="undefined"?x.right:i.right;i.bottom=typeof x.bottom!="undefined"?x.bottom:i.bottom;i.left=typeof x.left!="undefined"?x.left:i.left;return J};J.width=function(x){if(!arguments.length){return e}e=x;return J};J.height=function(x){if(!arguments.length){return f}f=x;return J};J.color=function(x){if(!arguments.length){return s}s=c.utils.getColor(x);z.color(s);return J};J.showLegend=function(x){if(!arguments.length){return n}n=x;return J};J.tooltips=function(x){if(!arguments.length){return C}C=x;return J};J.tooltipContent=function(x){if(!arguments.length){return w}w=x;return J};J.noData=function(x){if(!arguments.length){return k}k=x;return J};J.brushExtent=function(x){if(!arguments.length){return j}j=x;return J};return J};c.models.multiBar=function(){var l={top:0,right:0,bottom:0,left:0},q=960,m=500,i=d3.scale.ordinal(),h=d3.scale.linear(),n=Math.floor(Math.random()*10000),w=function(x){return x.x},u=function(x){return x.y},f=[0],A=true,z=false,t=c.utils.defaultColor(),C=false,o=null,g,B=1200,s,p,k,r,E=0.1,D=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout");var v,e;function j(x){x.each(function(N){var Q=q-l.left-l.right,I=m-l.top-l.bottom,F=d3.select(this);if(C&&N.length){C=[{values:N[0].values.map(function(R){return{x:R.x,y:0,series:R.series,size:0.01}})}]}if(z){N=d3.layout.stack().offset("zero").values(function(R){return R.values}).y(u)(!N.length&&C?C:N)}N=N.map(function(S,R){S.values=S.values.map(function(T){T.series=R;return T});return S});if(z){N[0].values.map(function(T,R){var U=0,S=0;N.map(function(W){var V=W.values[R];V.size=Math.abs(V.y);if(V.y<0){V.y1=S;S=S-V.size}else{V.y1=V.size+U;U=U+V.size}})})}var H=(s&&p)?[]:N.map(function(R){return R.values.map(function(T,S){return{x:w(T,S),y:u(T,S),y0:T.y0,y1:T.y1}})});i.domain(s||d3.merge(H).map(function(R){return R.x})).rangeBands(k||[0,Q],E);h.domain(p||d3.extent(d3.merge(H).map(function(R){return z?(R.y>0?R.y1:R.y1+R.y):R.y}).concat(f))).range(r||[I,0]);if(i.domain()[0]===i.domain()[1]){i.domain()[0]?i.domain([i.domain()[0]-i.domain()[0]*0.01,i.domain()[1]+i.domain()[1]*0.01]):i.domain([-1,1])}if(h.domain()[0]===h.domain()[1]){h.domain()[0]?h.domain([h.domain()[0]+h.domain()[0]*0.01,h.domain()[1]-h.domain()[1]*0.01]):h.domain([-1,1])}v=v||i;e=e||h;var G=F.selectAll("g.nv-wrap.nv-multibar").data([N]);var L=G.enter().append("g").attr("class","nvd3 nv-wrap nv-multibar");var J=L.append("defs");var y=L.append("g");var O=G.select("g");y.append("g").attr("class","nv-groups");G.attr("transform","translate("+l.left+","+l.top+")");J.append("clipPath").attr("id","nv-edge-clip-"+n).append("rect");G.select("#nv-edge-clip-"+n+" rect").attr("width",Q).attr("height",I);O.attr("clip-path",A?"url(#nv-edge-clip-"+n+")":"");var K=G.select(".nv-groups").selectAll(".nv-group").data(function(R){return R},function(S,R){return R});K.enter().append("g").style("stroke-opacity",0.000001).style("fill-opacity",0.000001);K.exit().transition().selectAll("rect.nv-bar").delay(function(S,R){return R*B/N[0].values.length}).attr("y",function(R){return z?e(R.y0):e(0)}).attr("height",0).remove();K.attr("class",function(S,R){return"nv-group nv-series-"+R}).classed("hover",function(R){return R.hover}).style("fill",function(S,R){return t(S,R)}).style("stroke",function(S,R){return t(S,R)});K.transition().style("stroke-opacity",1).style("fill-opacity",0.75);var P=K.selectAll("rect.nv-bar").data(function(R){return(C&&!N.length)?C.values:R.values});P.exit().remove();var M=P.enter().append("rect").attr("class",function(S,R){return u(S,R)<0?"nv-bar negative":"nv-bar positive"}).attr("x",function(T,S,R){return z?0:(R*i.rangeBand()/N.length)}).attr("y",function(R){return e(z?R.y0:0)}).attr("height",0).attr("width",i.rangeBand()/(z?1:N.length)).attr("transform",function(S,R){return"translate("+i(w(S,R))+",0)"});P.style("fill",function(T,S,R){return t(T,R,S)}).style("stroke",function(T,S,R){return t(T,R,S)}).on("mouseover",function(S,R){d3.select(this).classed("hover",true);D.elementMouseover({value:u(S,R),point:S,series:N[S.series],pos:[i(w(S,R))+(i.rangeBand()*(z?N.length/2:S.series+0.5)/N.length),h(u(S,R)+(z?S.y0:0))],pointIndex:R,seriesIndex:S.series,e:d3.event})}).on("mouseout",function(S,R){d3.select(this).classed("hover",false);D.elementMouseout({value:u(S,R),point:S,series:N[S.series],pointIndex:R,seriesIndex:S.series,e:d3.event})}).on("click",function(S,R){D.elementClick({value:u(S,R),point:S,series:N[S.series],pos:[i(w(S,R))+(i.rangeBand()*(z?N.length/2:S.series+0.5)/N.length),h(u(S,R)+(z?S.y0:0))],pointIndex:R,seriesIndex:S.series,e:d3.event});d3.event.stopPropagation()}).on("dblclick",function(S,R){D.elementDblClick({value:u(S,R),point:S,series:N[S.series],pos:[i(w(S,R))+(i.rangeBand()*(z?N.length/2:S.series+0.5)/N.length),h(u(S,R)+(z?S.y0:0))],pointIndex:R,seriesIndex:S.series,e:d3.event});d3.event.stopPropagation()});P.attr("class",function(S,R){return u(S,R)<0?"nv-bar negative":"nv-bar positive"}).transition().attr("transform",function(S,R){return"translate("+i(w(S,R))+",0)"});if(o){if(!g){g=N.map(function(){return true})}P.style("fill",function(T,S,R){return d3.rgb(o(T,S)).darker(g.map(function(V,U){return U}).filter(function(V,U){return !g[U]})[R]).toString()}).style("stroke",function(T,S,R){return d3.rgb(o(T,S)).darker(g.map(function(V,U){return U}).filter(function(V,U){return !g[U]})[R]).toString()})}if(z){P.transition().delay(function(S,R){return R*B/N[0].values.length}).attr("y",function(S,R){return h((z?S.y1:0))}).attr("height",function(S,R){return Math.max(Math.abs(h(S.y+(z?S.y0:0))-h((z?S.y0:0))),1)}).attr("x",function(S,R){return z?0:(S.series*i.rangeBand()/N.length)}).attr("width",i.rangeBand()/(z?1:N.length))}else{P.transition().delay(function(S,R){return R*B/N[0].values.length}).attr("x",function(S,R){return S.series*i.rangeBand()/N.length}).attr("width",i.rangeBand()/N.length).attr("y",function(S,R){return u(S,R)<0?h(0):h(0)-h(u(S,R))<1?h(0)-1:h(u(S,R))||0}).attr("height",function(S,R){return Math.max(Math.abs(h(u(S,R))-h(0)),1)||0})}v=i.copy();e=h.copy()});return j}j.dispatch=D;j.options=c.utils.optionsFunc.bind(j);j.x=function(x){if(!arguments.length){return w}w=x;return j};j.y=function(x){if(!arguments.length){return u}u=x;return j};j.margin=function(x){if(!arguments.length){return l}l.top=typeof x.top!="undefined"?x.top:l.top;l.right=typeof x.right!="undefined"?x.right:l.right;l.bottom=typeof x.bottom!="undefined"?x.bottom:l.bottom;l.left=typeof x.left!="undefined"?x.left:l.left;return j};j.width=function(x){if(!arguments.length){return q}q=x;return j};j.height=function(x){if(!arguments.length){return m}m=x;return j};j.xScale=function(x){if(!arguments.length){return i}i=x;return j};j.yScale=function(x){if(!arguments.length){return h}h=x;return j};j.xDomain=function(x){if(!arguments.length){return s}s=x;return j};j.yDomain=function(x){if(!arguments.length){return p}p=x;return j};j.xRange=function(x){if(!arguments.length){return k}k=x;return j};j.yRange=function(x){if(!arguments.length){return r}r=x;return j};j.forceY=function(x){if(!arguments.length){return f}f=x;return j};j.stacked=function(x){if(!arguments.length){return z}z=x;return j};j.clipEdge=function(x){if(!arguments.length){return A}A=x;return j};j.color=function(x){if(!arguments.length){return t}t=c.utils.getColor(x);return j};j.barColor=function(x){if(!arguments.length){return o}o=c.utils.getColor(x);return j};j.disabled=function(x){if(!arguments.length){return g}g=x;return j};j.id=function(x){if(!arguments.length){return n}n=x;return j};j.hideable=function(x){if(!arguments.length){return C}C=x;return j};j.delay=function(x){if(!arguments.length){return B}B=x;return j};j.groupSpacing=function(x){if(!arguments.length){return E}E=x;return j};return j};c.models.multiBarChart=function(){var j=c.models.multiBar(),r=c.models.axis(),f=c.models.axis(),i=c.models.legend(),z=c.models.legend();var B={top:30,right:20,bottom:50,left:60},D=null,C=null,E=c.utils.defaultColor(),H=true,F=true,G=true,u=true,q=false,w=true,h=false,k=0,g=true,o=function(K,J,N,M,L){return"

"+K+"

"+N+" on "+J+"

"},t,s,n={stacked:false},p=null,m="No Data Available.",I=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState"),e=function(){return H?180:0},v=250;j.stacked(false);r.orient("bottom").tickPadding(7).highlightZero(true).showMaxMin(false).tickFormat(function(x){return x});f.orient((q)?"right":"left").tickFormat(d3.format(",.1f"));z.updateState(false);var l=function(O,K){var N=O.pos[0]+(K.offsetLeft||0),M=O.pos[1]+(K.offsetTop||0),J=r.tickFormat()(j.x()(O.point,O.pointIndex)),P=f.tickFormat()(j.y()(O.point,O.pointIndex)),L=o(O.series.key,J,P,O,A);c.tooltip.show([N,M],L,O.value<0?"n":"s",null,K)};function A(x){x.each(function(R){var K=d3.select(this),U=this;var Y=(D||parseInt(K.style("width"))||960)-B.left-B.right,N=(C||parseInt(K.style("height"))||400)-B.top-B.bottom;A.update=function(){K.transition().duration(v).call(A)};A.container=this;n.disabled=R.map(function(Z){return !!Z.disabled});if(!p){var X;p={};for(X in n){if(n[X] instanceof Array){p[X]=n[X].slice(0)}else{p[X]=n[X]}}}if(!R||!R.length||!R.filter(function(Z){return Z.values.length}).length){var L=K.selectAll(".nv-noData").data([m]);L.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");L.attr("x",B.left+Y/2).attr("y",B.top+N/2).text(function(Z){return Z});return A}else{K.selectAll(".nv-noData").remove()}t=j.xScale();s=j.yScale();var M=K.selectAll("g.nv-wrap.nv-multiBarWithLegend").data([R]);var y=M.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarWithLegend").append("g");var S=M.select("g");y.append("g").attr("class","nv-x nv-axis");y.append("g").attr("class","nv-y nv-axis");y.append("g").attr("class","nv-barsWrap");y.append("g").attr("class","nv-legendWrap");y.append("g").attr("class","nv-controlsWrap");if(F){i.width(Y-e());if(j.barColor()){R.forEach(function(aa,Z){aa.color=d3.rgb("#ccc").darker(Z*1.5).toString()})}S.select(".nv-legendWrap").datum(R).call(i);if(B.top!=i.height()){B.top=i.height();N=(C||parseInt(K.style("height"))||400)-B.top-B.bottom}S.select(".nv-legendWrap").attr("transform","translate("+e()+","+(-B.top)+")")}if(H){var V=[{key:"Grouped",disabled:j.stacked()},{key:"Stacked",disabled:!j.stacked()}];z.width(e()).color(["#444","#444","#444"]);S.select(".nv-controlsWrap").datum(V).attr("transform","translate(0,"+(-B.top)+")").call(z)}M.attr("transform","translate("+B.left+","+B.top+")");if(q){S.select(".nv-y.nv-axis").attr("transform","translate("+Y+",0)")}j.disabled(R.map(function(Z){return Z.disabled})).width(Y).height(N).color(R.map(function(aa,Z){return aa.color||E(aa,Z)}).filter(function(aa,Z){return !R[Z].disabled}));var T=S.select(".nv-barsWrap").datum(R.filter(function(Z){return !Z.disabled}));T.transition().call(j);if(G){r.scale(t).ticks(Y/100).tickSize(-N,0);S.select(".nv-x.nv-axis").attr("transform","translate(0,"+s.range()[0]+")");S.select(".nv-x.nv-axis").transition().call(r);var Q=S.select(".nv-x.nv-axis > g").selectAll("g");Q.selectAll("line, text").style("opacity",1);if(h){var W=function(Z,aa){return"translate("+Z+","+aa+")"};var J=5,P=17;Q.selectAll("text").attr("transform",function(ab,aa,Z){return W(0,(Z%2==0?J:P))});var O=d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;S.selectAll(".nv-x.nv-axis .nv-axisMaxMin text").attr("transform",function(aa,Z){return W(0,(Z===0||O%2!==0)?P:J)})}if(w){Q.filter(function(aa,Z){return Z%Math.ceil(R[0].values.length/(Y/100))!==0}).selectAll("text, line").style("opacity",0)}if(k){Q.selectAll(".tick text").attr("transform","rotate("+k+" 0,0)").style("text-anchor",k>0?"start":"end")}S.select(".nv-x.nv-axis").selectAll("g.nv-axisMaxMin text").style("opacity",1)}if(u){f.scale(s).ticks(N/36).tickSize(-Y,0);S.select(".nv-y.nv-axis").transition().call(f)}i.dispatch.on("stateChange",function(Z){n=Z;I.stateChange(n);A.update()});z.dispatch.on("legendClick",function(aa,Z){if(!aa.disabled){return}V=V.map(function(ab){ab.disabled=true;return ab});aa.disabled=false;switch(aa.key){case"Grouped":j.stacked(false);break;case"Stacked":j.stacked(true);break}n.stacked=j.stacked();I.stateChange(n);A.update()});I.on("tooltipShow",function(Z){if(g){l(Z,U.parentNode)}});I.on("changeState",function(Z){if(typeof Z.disabled!=="undefined"){R.forEach(function(ab,aa){ab.disabled=Z.disabled[aa]});n.disabled=Z.disabled}if(typeof Z.stacked!=="undefined"){j.stacked(Z.stacked);n.stacked=Z.stacked}A.update()})});return A}j.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+B.left,x.pos[1]+B.top];I.tooltipShow(x)});j.dispatch.on("elementMouseout.tooltip",function(x){I.tooltipHide(x)});I.on("tooltipHide",function(){if(g){c.tooltip.cleanup()}});A.dispatch=I;A.multibar=j;A.legend=i;A.xAxis=r;A.yAxis=f;d3.rebind(A,j,"x","y","xDomain","yDomain","xRange","yRange","forceX","forceY","clipEdge","id","stacked","delay","barColor","groupSpacing");A.options=c.utils.optionsFunc.bind(A);A.margin=function(x){if(!arguments.length){return B}B.top=typeof x.top!="undefined"?x.top:B.top;B.right=typeof x.right!="undefined"?x.right:B.right;B.bottom=typeof x.bottom!="undefined"?x.bottom:B.bottom;B.left=typeof x.left!="undefined"?x.left:B.left;return A};A.width=function(x){if(!arguments.length){return D}D=x;return A};A.height=function(x){if(!arguments.length){return C}C=x;return A};A.color=function(x){if(!arguments.length){return E}E=c.utils.getColor(x);i.color(E);return A};A.showControls=function(x){if(!arguments.length){return H}H=x;return A};A.showLegend=function(x){if(!arguments.length){return F}F=x;return A};A.showXAxis=function(x){if(!arguments.length){return G}G=x;return A};A.showYAxis=function(x){if(!arguments.length){return u}u=x;return A};A.rightAlignYAxis=function(x){if(!arguments.length){return q}q=x;f.orient((x)?"right":"left");return A};A.reduceXTicks=function(x){if(!arguments.length){return w}w=x;return A};A.rotateLabels=function(x){if(!arguments.length){return k}k=x;return A};A.staggerLabels=function(x){if(!arguments.length){return h}h=x;return A};A.tooltip=function(x){if(!arguments.length){return o}o=x;return A};A.tooltips=function(x){if(!arguments.length){return g}g=x;return A};A.tooltipContent=function(x){if(!arguments.length){return o}o=x;return A};A.state=function(x){if(!arguments.length){return n}n=x;return A};A.defaultState=function(x){if(!arguments.length){return p}p=x;return A};A.noData=function(x){if(!arguments.length){return m}m=x;return A};A.transitionDuration=function(x){if(!arguments.length){return v}v=x;return A};return A};c.models.multiBarHorizontal=function(){var n={top:0,right:0,bottom:0,left:0},s=960,o=500,p=Math.floor(Math.random()*10000),j=d3.scale.ordinal(),i=d3.scale.linear(),A=function(x){return x.x},w=function(x){return x.y},f=[0],v=c.utils.defaultColor(),q=null,g,C=false,B=false,k=60,h=d3.format(",.2f"),D=1200,u,r,m,t,E=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout");var z,e;function l(x){x.each(function(N){var Q=s-n.left-n.right,I=o-n.top-n.bottom,F=d3.select(this);if(C){N=d3.layout.stack().offset("zero").values(function(R){return R.values}).y(w)(N)}N=N.map(function(S,R){S.values=S.values.map(function(T){T.series=R;return T});return S});if(C){N[0].values.map(function(T,R){var U=0,S=0;N.map(function(W){var V=W.values[R];V.size=Math.abs(V.y);if(V.y<0){V.y1=S-V.size;S=S-V.size}else{V.y1=U;U=U+V.size}})})}var H=(u&&r)?[]:N.map(function(R){return R.values.map(function(T,S){return{x:A(T,S),y:w(T,S),y0:T.y0,y1:T.y1}})});j.domain(u||d3.merge(H).map(function(R){return R.x})).rangeBands(m||[0,I],0.1);i.domain(r||d3.extent(d3.merge(H).map(function(R){return C?(R.y>0?R.y1+R.y:R.y1):R.y}).concat(f)));if(B&&!C){i.range(t||[(i.domain()[0]<0?k:0),Q-(i.domain()[1]>0?k:0)])}else{i.range(t||[0,Q])}z=z||j;e=e||d3.scale.linear().domain(i.domain()).range([i(0),i(0)]);var G=d3.select(this).selectAll("g.nv-wrap.nv-multibarHorizontal").data([N]);var L=G.enter().append("g").attr("class","nvd3 nv-wrap nv-multibarHorizontal");var J=L.append("defs");var y=L.append("g");var O=G.select("g");y.append("g").attr("class","nv-groups");G.attr("transform","translate("+n.left+","+n.top+")");var K=G.select(".nv-groups").selectAll(".nv-group").data(function(R){return R},function(S,R){return R});K.enter().append("g").style("stroke-opacity",0.000001).style("fill-opacity",0.000001);K.exit().transition().style("stroke-opacity",0.000001).style("fill-opacity",0.000001).remove();K.attr("class",function(S,R){return"nv-group nv-series-"+R}).classed("hover",function(R){return R.hover}).style("fill",function(S,R){return v(S,R)}).style("stroke",function(S,R){return v(S,R)});K.transition().style("stroke-opacity",1).style("fill-opacity",0.75);var P=K.selectAll("g.nv-bar").data(function(R){return R.values});P.exit().remove();var M=P.enter().append("g").attr("transform",function(T,S,R){return"translate("+e(C?T.y0:0)+","+(C?0:(R*j.rangeBand()/N.length)+j(A(T,S)))+")"});M.append("rect").attr("width",0).attr("height",j.rangeBand()/(C?1:N.length));P.on("mouseover",function(S,R){d3.select(this).classed("hover",true);E.elementMouseover({value:w(S,R),point:S,series:N[S.series],pos:[i(w(S,R)+(C?S.y0:0)),j(A(S,R))+(j.rangeBand()*(C?N.length/2:S.series+0.5)/N.length)],pointIndex:R,seriesIndex:S.series,e:d3.event})}).on("mouseout",function(S,R){d3.select(this).classed("hover",false);E.elementMouseout({value:w(S,R),point:S,series:N[S.series],pointIndex:R,seriesIndex:S.series,e:d3.event})}).on("click",function(S,R){E.elementClick({value:w(S,R),point:S,series:N[S.series],pos:[j(A(S,R))+(j.rangeBand()*(C?N.length/2:S.series+0.5)/N.length),i(w(S,R)+(C?S.y0:0))],pointIndex:R,seriesIndex:S.series,e:d3.event});d3.event.stopPropagation()}).on("dblclick",function(S,R){E.elementDblClick({value:w(S,R),point:S,series:N[S.series],pos:[j(A(S,R))+(j.rangeBand()*(C?N.length/2:S.series+0.5)/N.length),i(w(S,R)+(C?S.y0:0))],pointIndex:R,seriesIndex:S.series,e:d3.event});d3.event.stopPropagation()});M.append("text");if(B&&!C){P.select("text").attr("text-anchor",function(S,R){return w(S,R)<0?"end":"start"}).attr("y",j.rangeBand()/(N.length*2)).attr("dy",".32em").text(function(S,R){return h(w(S,R))});P.transition().select("text").attr("x",function(S,R){return w(S,R)<0?-4:i(w(S,R))-i(0)+4})}else{P.selectAll("text").text("")}P.attr("class",function(S,R){return w(S,R)<0?"nv-bar negative":"nv-bar positive"});if(q){if(!g){g=N.map(function(){return true})}P.style("fill",function(T,S,R){return d3.rgb(q(T,S)).darker(g.map(function(V,U){return U}).filter(function(V,U){return !g[U]})[R]).toString()}).style("stroke",function(T,S,R){return d3.rgb(q(T,S)).darker(g.map(function(V,U){return U}).filter(function(V,U){return !g[U]})[R]).toString()})}if(C){P.transition().attr("transform",function(S,R){return"translate("+i(S.y1)+","+j(A(S,R))+")"}).select("rect").attr("width",function(S,R){return Math.abs(i(w(S,R)+S.y0)-i(S.y0))}).attr("height",j.rangeBand())}else{P.transition().attr("transform",function(S,R){return"translate("+(w(S,R)<0?i(w(S,R)):i(0))+","+(S.series*j.rangeBand()/N.length+j(A(S,R)))+")"}).select("rect").attr("height",j.rangeBand()/N.length).attr("width",function(S,R){return Math.max(Math.abs(i(w(S,R))-i(0)),1)})}z=j.copy();e=i.copy()});return l}l.dispatch=E;l.options=c.utils.optionsFunc.bind(l);l.x=function(x){if(!arguments.length){return A}A=x;return l};l.y=function(x){if(!arguments.length){return w}w=x;return l};l.margin=function(x){if(!arguments.length){return n}n.top=typeof x.top!="undefined"?x.top:n.top;n.right=typeof x.right!="undefined"?x.right:n.right;n.bottom=typeof x.bottom!="undefined"?x.bottom:n.bottom;n.left=typeof x.left!="undefined"?x.left:n.left;return l};l.width=function(x){if(!arguments.length){return s}s=x;return l};l.height=function(x){if(!arguments.length){return o}o=x;return l};l.xScale=function(x){if(!arguments.length){return j}j=x;return l};l.yScale=function(x){if(!arguments.length){return i}i=x;return l};l.xDomain=function(x){if(!arguments.length){return u}u=x;return l};l.yDomain=function(x){if(!arguments.length){return r}r=x;return l};l.xRange=function(x){if(!arguments.length){return m}m=x;return l};l.yRange=function(x){if(!arguments.length){return t}t=x;return l};l.forceY=function(x){if(!arguments.length){return f}f=x;return l};l.stacked=function(x){if(!arguments.length){return C}C=x;return l};l.color=function(x){if(!arguments.length){return v}v=c.utils.getColor(x);return l};l.barColor=function(x){if(!arguments.length){return q}q=c.utils.getColor(x);return l};l.disabled=function(x){if(!arguments.length){return g}g=x;return l};l.id=function(x){if(!arguments.length){return p}p=x;return l};l.delay=function(x){if(!arguments.length){return D}D=x;return l};l.showValues=function(x){if(!arguments.length){return B}B=x;return l};l.valueFormat=function(x){if(!arguments.length){return h}h=x;return l};l.valuePadding=function(x){if(!arguments.length){return k}k=x;return l};return l};c.models.multiBarHorizontalChart=function(){var i=c.models.multiBarHorizontal(),o=c.models.axis(),f=c.models.axis(),h=c.models.legend().height(30),s=c.models.legend().height(30);var u={top:30,right:20,bottom:50,left:60},w=null,v=null,z=c.utils.defaultColor(),C=true,A=true,B=false,g=true,l=function(F,E,I,H,G){return"

"+F+" - "+E+"

"+I+"

"},q,p,m={stacked:B},n=null,k="No Data Available.",D=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState"),e=function(){return C?180:0},r=250;i.stacked(B);o.orient("left").tickPadding(5).highlightZero(false).showMaxMin(false).tickFormat(function(x){return x});f.orient("bottom").tickFormat(d3.format(",.1f"));s.updateState(false);var j=function(J,F){var I=J.pos[0]+(F.offsetLeft||0),H=J.pos[1]+(F.offsetTop||0),E=o.tickFormat()(i.x()(J.point,J.pointIndex)),K=f.tickFormat()(i.y()(J.point,J.pointIndex)),G=l(J.series.key,E,K,J,t);c.tooltip.show([I,H],G,J.value<0?"e":"w",null,F)};function t(x){x.each(function(J){var E=d3.select(this),M=this;var P=(w||parseInt(E.style("width"))||960)-u.left-u.right,H=(v||parseInt(E.style("height"))||400)-u.top-u.bottom;t.update=function(){E.transition().duration(r).call(t)};t.container=this;m.disabled=J.map(function(Q){return !!Q.disabled});if(!n){var O;n={};for(O in m){if(m[O] instanceof Array){n[O]=m[O].slice(0)}else{n[O]=m[O]}}}if(!J||!J.length||!J.filter(function(Q){return Q.values.length}).length){var F=E.selectAll(".nv-noData").data([k]);F.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");F.attr("x",u.left+P/2).attr("y",u.top+H/2).text(function(Q){return Q});return t}else{E.selectAll(".nv-noData").remove()}q=i.xScale();p=i.yScale();var G=E.selectAll("g.nv-wrap.nv-multiBarHorizontalChart").data([J]);var y=G.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarHorizontalChart").append("g");var K=G.select("g");y.append("g").attr("class","nv-x nv-axis");y.append("g").attr("class","nv-y nv-axis");y.append("g").attr("class","nv-barsWrap");y.append("g").attr("class","nv-legendWrap");y.append("g").attr("class","nv-controlsWrap");if(A){h.width(P-e());if(i.barColor()){J.forEach(function(R,Q){R.color=d3.rgb("#ccc").darker(Q*1.5).toString()})}K.select(".nv-legendWrap").datum(J).call(h);if(u.top!=h.height()){u.top=h.height();H=(v||parseInt(E.style("height"))||400)-u.top-u.bottom}K.select(".nv-legendWrap").attr("transform","translate("+e()+","+(-u.top)+")")}if(C){var N=[{key:"Grouped",disabled:i.stacked()},{key:"Stacked",disabled:!i.stacked()}];s.width(e()).color(["#444","#444","#444"]);K.select(".nv-controlsWrap").datum(N).attr("transform","translate(0,"+(-u.top)+")").call(s)}G.attr("transform","translate("+u.left+","+u.top+")");i.disabled(J.map(function(Q){return Q.disabled})).width(P).height(H).color(J.map(function(R,Q){return R.color||z(R,Q)}).filter(function(R,Q){return !J[Q].disabled}));var L=K.select(".nv-barsWrap").datum(J.filter(function(Q){return !Q.disabled}));L.transition().call(i);o.scale(q).ticks(H/24).tickSize(-P,0);K.select(".nv-x.nv-axis").transition().call(o);var I=K.select(".nv-x.nv-axis").selectAll("g");I.selectAll("line, text").style("opacity",1);f.scale(p).ticks(P/100).tickSize(-H,0);K.select(".nv-y.nv-axis").attr("transform","translate(0,"+H+")");K.select(".nv-y.nv-axis").transition().call(f);h.dispatch.on("stateChange",function(Q){m=Q;D.stateChange(m);t.update()});s.dispatch.on("legendClick",function(R,Q){if(!R.disabled){return}N=N.map(function(S){S.disabled=true;return S});R.disabled=false;switch(R.key){case"Grouped":i.stacked(false);break;case"Stacked":i.stacked(true);break}m.stacked=i.stacked();D.stateChange(m);t.update()});D.on("tooltipShow",function(Q){if(g){j(Q,M.parentNode)}});D.on("changeState",function(Q){if(typeof Q.disabled!=="undefined"){J.forEach(function(S,R){S.disabled=Q.disabled[R]});m.disabled=Q.disabled}if(typeof Q.stacked!=="undefined"){i.stacked(Q.stacked);m.stacked=Q.stacked}x.call(t)})});return t}i.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+u.left,x.pos[1]+u.top];D.tooltipShow(x)});i.dispatch.on("elementMouseout.tooltip",function(x){D.tooltipHide(x)});D.on("tooltipHide",function(){if(g){c.tooltip.cleanup()}});t.dispatch=D;t.multibar=i;t.legend=h;t.xAxis=o;t.yAxis=f;d3.rebind(t,i,"x","y","xDomain","yDomain","xRange","yRange","forceX","forceY","clipEdge","id","delay","showValues","valueFormat","stacked","barColor");t.options=c.utils.optionsFunc.bind(t);t.margin=function(x){if(!arguments.length){return u}u.top=typeof x.top!="undefined"?x.top:u.top;u.right=typeof x.right!="undefined"?x.right:u.right;u.bottom=typeof x.bottom!="undefined"?x.bottom:u.bottom;u.left=typeof x.left!="undefined"?x.left:u.left;return t};t.width=function(x){if(!arguments.length){return w}w=x;return t};t.height=function(x){if(!arguments.length){return v}v=x;return t};t.color=function(x){if(!arguments.length){return z}z=c.utils.getColor(x);h.color(z);return t};t.showControls=function(x){if(!arguments.length){return C}C=x;return t};t.showLegend=function(x){if(!arguments.length){return A}A=x;return t};t.tooltip=function(x){if(!arguments.length){return l}l=x;return t};t.tooltips=function(x){if(!arguments.length){return g}g=x;return t};t.tooltipContent=function(x){if(!arguments.length){return l}l=x;return t};t.state=function(x){if(!arguments.length){return m}m=x;return t};t.defaultState=function(x){if(!arguments.length){return n}n=x;return t};t.noData=function(x){if(!arguments.length){return k}k=x;return t};t.transitionDuration=function(x){if(!arguments.length){return r}r=x;return t};return t};c.models.multiChart=function(){var w={top:30,right:20,bottom:50,left:60},B=d3.scale.category20().range(),A=null,z=null,C=true,h=true,l=function(H,G,K,J,I){return"

"+H+"

"+K+" at "+G+"

"},u,t,p,o;var u=d3.scale.linear(),E=d3.scale.linear(),D=d3.scale.linear(),r=c.models.line().yScale(E),q=c.models.line().yScale(D),j=c.models.multiBar().stacked(false).yScale(E),g=c.models.multiBar().stacked(false).yScale(D),n=c.models.stackedArea().yScale(E),m=c.models.stackedArea().yScale(D),s=c.models.axis().scale(u).orient("bottom").tickPadding(5),f=c.models.axis().scale(E).orient("left"),e=c.models.axis().scale(D).orient("right"),i=c.models.legend().height(30),F=d3.dispatch("tooltipShow","tooltipHide");var k=function(L,H){var K=L.pos[0]+(H.offsetLeft||0),J=L.pos[1]+(H.offsetTop||0),G=s.tickFormat()(r.x()(L.point,L.pointIndex)),M=((L.series.yAxis==2)?e:f).tickFormat()(r.y()(L.point,L.pointIndex)),I=l(L.series.key,G,M,L,v);c.tooltip.show([K,J],I,undefined,undefined,H.offsetParent)};function v(x){x.each(function(ac){var R=d3.select(this),J=this;v.update=function(){R.transition().call(v)};v.container=this;var y=(A||parseInt(R.style("width"))||960)-w.left-w.right,H=(z||parseInt(R.style("height"))||400)-w.top-w.bottom;var U=ac.filter(function(ad){return !ad.disabled&&ad.type=="line"&&ad.yAxis==1});var T=ac.filter(function(ad){return !ad.disabled&&ad.type=="line"&&ad.yAxis==2});var Y=ac.filter(function(ad){return !ad.disabled&&ad.type=="bar"&&ad.yAxis==1});var X=ac.filter(function(ad){return !ad.disabled&&ad.type=="bar"&&ad.yAxis==2});var Q=ac.filter(function(ad){return !ad.disabled&&ad.type=="area"&&ad.yAxis==1});var O=ac.filter(function(ad){return !ad.disabled&&ad.type=="area"&&ad.yAxis==2});var L=ac.filter(function(ad){return !ad.disabled&&ad.yAxis==1}).map(function(ad){return ad.values.map(function(af,ae){return{x:af.x,y:af.y}})});var K=ac.filter(function(ad){return !ad.disabled&&ad.yAxis==2}).map(function(ad){return ad.values.map(function(af,ae){return{x:af.x,y:af.y}})});u.domain(d3.extent(d3.merge(L.concat(K)),function(ad){return ad.x})).range([0,y]);var M=R.selectAll("g.wrap.multiChart").data([ac]);var V=M.enter().append("g").attr("class","wrap nvd3 multiChart").append("g");V.append("g").attr("class","x axis");V.append("g").attr("class","y1 axis");V.append("g").attr("class","y2 axis");V.append("g").attr("class","lines1Wrap");V.append("g").attr("class","lines2Wrap");V.append("g").attr("class","bars1Wrap");V.append("g").attr("class","bars2Wrap");V.append("g").attr("class","stack1Wrap");V.append("g").attr("class","stack2Wrap");V.append("g").attr("class","legendWrap");var Z=M.select("g");if(C){i.width(y/2);Z.select(".legendWrap").datum(ac.map(function(ad){ad.originalKey=ad.originalKey===undefined?ad.key:ad.originalKey;ad.key=ad.originalKey+(ad.yAxis==1?"":" (right axis)");return ad})).call(i);if(w.top!=i.height()){w.top=i.height();H=(z||parseInt(R.style("height"))||400)-w.top-w.bottom}Z.select(".legendWrap").attr("transform","translate("+(y/2)+","+(-w.top)+")")}r.width(y).height(H).interpolate("monotone").color(ac.map(function(ae,ad){return ae.color||B[ad%B.length]}).filter(function(ae,ad){return !ac[ad].disabled&&ac[ad].yAxis==1&&ac[ad].type=="line"}));q.width(y).height(H).interpolate("monotone").color(ac.map(function(ae,ad){return ae.color||B[ad%B.length]}).filter(function(ae,ad){return !ac[ad].disabled&&ac[ad].yAxis==2&&ac[ad].type=="line"}));j.width(y).height(H).color(ac.map(function(ae,ad){return ae.color||B[ad%B.length]}).filter(function(ae,ad){return !ac[ad].disabled&&ac[ad].yAxis==1&&ac[ad].type=="bar"}));g.width(y).height(H).color(ac.map(function(ae,ad){return ae.color||B[ad%B.length]}).filter(function(ae,ad){return !ac[ad].disabled&&ac[ad].yAxis==2&&ac[ad].type=="bar"}));n.width(y).height(H).color(ac.map(function(ae,ad){return ae.color||B[ad%B.length]}).filter(function(ae,ad){return !ac[ad].disabled&&ac[ad].yAxis==1&&ac[ad].type=="area"}));m.width(y).height(H).color(ac.map(function(ae,ad){return ae.color||B[ad%B.length]}).filter(function(ae,ad){return !ac[ad].disabled&&ac[ad].yAxis==2&&ac[ad].type=="area"}));Z.attr("transform","translate("+w.left+","+w.top+")");var G=Z.select(".lines1Wrap").datum(U);var I=Z.select(".bars1Wrap").datum(Y);var ab=Z.select(".stack1Wrap").datum(Q);var W=Z.select(".lines2Wrap").datum(T);var aa=Z.select(".bars2Wrap").datum(X);var S=Z.select(".stack2Wrap").datum(O);var P=Q.length?Q.map(function(ad){return ad.values}).reduce(function(ae,ad){return ae.map(function(af,ag){return{x:af.x,y:af.y+ad[ag].y}})}).concat([{x:0,y:0}]):[];var N=O.length?O.map(function(ad){return ad.values}).reduce(function(ae,ad){return ae.map(function(af,ag){return{x:af.x,y:af.y+ad[ag].y}})}).concat([{x:0,y:0}]):[];E.domain(p||d3.extent(d3.merge(L).concat(P),function(ad){return ad.y})).range([0,H]);D.domain(o||d3.extent(d3.merge(K).concat(N),function(ad){return ad.y})).range([0,H]);r.yDomain(E.domain());j.yDomain(E.domain());n.yDomain(E.domain());q.yDomain(D.domain());g.yDomain(D.domain());m.yDomain(D.domain());if(Q.length){d3.transition(ab).call(n)}if(O.length){d3.transition(S).call(m)}if(Y.length){d3.transition(I).call(j)}if(X.length){d3.transition(aa).call(g)}if(U.length){d3.transition(G).call(r)}if(T.length){d3.transition(W).call(q)}s.ticks(y/100).tickSize(-H,0);Z.select(".x.axis").attr("transform","translate(0,"+H+")");d3.transition(Z.select(".x.axis")).call(s);f.ticks(H/36).tickSize(-y,0);d3.transition(Z.select(".y1.axis")).call(f);e.ticks(H/36).tickSize(-y,0);d3.transition(Z.select(".y2.axis")).call(e);Z.select(".y2.axis").style("opacity",K.length?1:0).attr("transform","translate("+u.range()[1]+",0)");i.dispatch.on("stateChange",function(ad){v.update()});F.on("tooltipShow",function(ad){if(h){k(ad,J.parentNode)}})});return v}r.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+w.left,x.pos[1]+w.top];F.tooltipShow(x)});r.dispatch.on("elementMouseout.tooltip",function(x){F.tooltipHide(x)});q.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+w.left,x.pos[1]+w.top];F.tooltipShow(x)});q.dispatch.on("elementMouseout.tooltip",function(x){F.tooltipHide(x)});j.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+w.left,x.pos[1]+w.top];F.tooltipShow(x)});j.dispatch.on("elementMouseout.tooltip",function(x){F.tooltipHide(x)});g.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+w.left,x.pos[1]+w.top];F.tooltipShow(x)});g.dispatch.on("elementMouseout.tooltip",function(x){F.tooltipHide(x)});n.dispatch.on("tooltipShow",function(x){if(!Math.round(n.y()(x.point)*100)){setTimeout(function(){d3.selectAll(".point.hover").classed("hover",false)},0);return false}x.pos=[x.pos[0]+w.left,x.pos[1]+w.top],F.tooltipShow(x)});n.dispatch.on("tooltipHide",function(x){F.tooltipHide(x)});m.dispatch.on("tooltipShow",function(x){if(!Math.round(m.y()(x.point)*100)){setTimeout(function(){d3.selectAll(".point.hover").classed("hover",false)},0);return false}x.pos=[x.pos[0]+w.left,x.pos[1]+w.top],F.tooltipShow(x)});m.dispatch.on("tooltipHide",function(x){F.tooltipHide(x)});r.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+w.left,x.pos[1]+w.top];F.tooltipShow(x)});r.dispatch.on("elementMouseout.tooltip",function(x){F.tooltipHide(x)});q.dispatch.on("elementMouseover.tooltip",function(x){x.pos=[x.pos[0]+w.left,x.pos[1]+w.top];F.tooltipShow(x)});q.dispatch.on("elementMouseout.tooltip",function(x){F.tooltipHide(x)});F.on("tooltipHide",function(){if(h){c.tooltip.cleanup()}});v.dispatch=F;v.lines1=r;v.lines2=q;v.bars1=j;v.bars2=g;v.stack1=n;v.stack2=m;v.xAxis=s;v.yAxis1=f;v.yAxis2=e;v.options=c.utils.optionsFunc.bind(v);v.x=function(x){if(!arguments.length){return getX}getX=x;r.x(x);j.x(x);return v};v.y=function(x){if(!arguments.length){return getY}getY=x;r.y(x);j.y(x);return v};v.yDomain1=function(x){if(!arguments.length){return p}p=x;return v};v.yDomain2=function(x){if(!arguments.length){return o}o=x;return v};v.margin=function(x){if(!arguments.length){return w}w=x;return v};v.width=function(x){if(!arguments.length){return A}A=x;return v};v.height=function(x){if(!arguments.length){return z}z=x;return v};v.color=function(x){if(!arguments.length){return B}B=x;i.color(x);return v};v.showLegend=function(x){if(!arguments.length){return C}C=x;return v};v.tooltips=function(x){if(!arguments.length){return h}h=x;return v};v.tooltipContent=function(x){if(!arguments.length){return l}l=x;return v};return v};c.models.ohlcBar=function(){var o={top:0,right:0,bottom:0,left:0},s=960,p=500,q=Math.floor(Math.random()*10000),j=d3.scale.linear(),i=d3.scale.linear(),z=function(x){return x.x},w=function(x){return x.y},f=function(x){return x.open},e=function(x){return x.close},B=function(x){return x.high},k=function(x){return x.low},h=[],g=[],l=false,A=true,v=c.utils.defaultColor(),u,r,n,t,C=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout");function m(x){x.each(function(J){var M=s-o.left-o.right,G=p-o.top-o.bottom,D=d3.select(this);j.domain(u||d3.extent(J[0].values.map(z).concat(h)));if(l){j.range(n||[M*0.5/J[0].values.length,M*(J[0].values.length-0.5)/J[0].values.length])}else{j.range(n||[0,M])}i.domain(r||[d3.min(J[0].values.map(k).concat(g)),d3.max(J[0].values.map(B).concat(g))]).range(t||[G,0]);if(j.domain()[0]===j.domain()[1]||i.domain()[0]===i.domain()[1]){singlePoint=true}if(j.domain()[0]===j.domain()[1]){j.domain()[0]?j.domain([j.domain()[0]-j.domain()[0]*0.01,j.domain()[1]+j.domain()[1]*0.01]):j.domain([-1,1])}if(i.domain()[0]===i.domain()[1]){i.domain()[0]?i.domain([i.domain()[0]+i.domain()[0]*0.01,i.domain()[1]-i.domain()[1]*0.01]):i.domain([-1,1])}var E=d3.select(this).selectAll("g.nv-wrap.nv-ohlcBar").data([J[0].values]);var I=E.enter().append("g").attr("class","nvd3 nv-wrap nv-ohlcBar");var H=I.append("defs");var y=I.append("g");var K=E.select("g");y.append("g").attr("class","nv-ticks");E.attr("transform","translate("+o.left+","+o.top+")");D.on("click",function(O,N){C.chartClick({data:O,index:N,pos:d3.event,id:q})});H.append("clipPath").attr("id","nv-chart-clip-path-"+q).append("rect");E.select("#nv-chart-clip-path-"+q+" rect").attr("width",M).attr("height",G);K.attr("clip-path",A?"url(#nv-chart-clip-path-"+q+")":"");var L=E.select(".nv-ticks").selectAll(".nv-tick").data(function(N){return N});L.exit().remove();var F=L.enter().append("path").attr("class",function(P,O,N){return(f(P,O)>e(P,O)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+N+"-"+O}).attr("d",function(P,O){var N=(M/J[0].values.length)*0.9;return"m0,0l0,"+(i(f(P,O))-i(B(P,O)))+"l"+(-N/2)+",0l"+(N/2)+",0l0,"+(i(k(P,O))-i(f(P,O)))+"l0,"+(i(e(P,O))-i(k(P,O)))+"l"+(N/2)+",0l"+(-N/2)+",0z"}).attr("transform",function(O,N){return"translate("+j(z(O,N))+","+i(B(O,N))+")"}).on("mouseover",function(O,N){d3.select(this).classed("hover",true);C.elementMouseover({point:O,series:J[0],pos:[j(z(O,N)),i(w(O,N))],pointIndex:N,seriesIndex:0,e:d3.event})}).on("mouseout",function(O,N){d3.select(this).classed("hover",false);C.elementMouseout({point:O,series:J[0],pointIndex:N,seriesIndex:0,e:d3.event})}).on("click",function(O,N){C.elementClick({value:w(O,N),data:O,index:N,pos:[j(z(O,N)),i(w(O,N))],e:d3.event,id:q});d3.event.stopPropagation()}).on("dblclick",function(O,N){C.elementDblClick({value:w(O,N),data:O,index:N,pos:[j(z(O,N)),i(w(O,N))],e:d3.event,id:q});d3.event.stopPropagation()});L.attr("class",function(P,O,N){return(f(P,O)>e(P,O)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+N+"-"+O});d3.transition(L).attr("transform",function(O,N){return"translate("+j(z(O,N))+","+i(B(O,N))+")"}).attr("d",function(P,O){var N=(M/J[0].values.length)*0.9;return"m0,0l0,"+(i(f(P,O))-i(B(P,O)))+"l"+(-N/2)+",0l"+(N/2)+",0l0,"+(i(k(P,O))-i(f(P,O)))+"l0,"+(i(e(P,O))-i(k(P,O)))+"l"+(N/2)+",0l"+(-N/2)+",0z"})});return m}m.dispatch=C;m.options=c.utils.optionsFunc.bind(m);m.x=function(x){if(!arguments.length){return z}z=x;return m};m.y=function(x){if(!arguments.length){return w}w=x;return m};m.open=function(x){if(!arguments.length){return f}f=x;return m};m.close=function(x){if(!arguments.length){return e}e=x;return m};m.high=function(x){if(!arguments.length){return B}B=x;return m};m.low=function(x){if(!arguments.length){return k}k=x;return m};m.margin=function(x){if(!arguments.length){return o}o.top=typeof x.top!="undefined"?x.top:o.top;o.right=typeof x.right!="undefined"?x.right:o.right;o.bottom=typeof x.bottom!="undefined"?x.bottom:o.bottom;o.left=typeof x.left!="undefined"?x.left:o.left;return m};m.width=function(x){if(!arguments.length){return s}s=x;return m};m.height=function(x){if(!arguments.length){return p}p=x;return m};m.xScale=function(x){if(!arguments.length){return j}j=x;return m};m.yScale=function(x){if(!arguments.length){return i}i=x;return m};m.xDomain=function(x){if(!arguments.length){return u}u=x;return m};m.yDomain=function(x){if(!arguments.length){return r}r=x;return m};m.xRange=function(x){if(!arguments.length){return n}n=x;return m};m.yRange=function(x){if(!arguments.length){return t}t=x;return m};m.forceX=function(x){if(!arguments.length){return h}h=x;return m};m.forceY=function(x){if(!arguments.length){return g}g=x;return m};m.padData=function(x){if(!arguments.length){return l}l=x;return m};m.clipEdge=function(x){if(!arguments.length){return A}A=x;return m};m.color=function(x){if(!arguments.length){return v}v=c.utils.getColor(x);return m};m.id=function(x){if(!arguments.length){return q}q=x;return m};return m};c.models.pie=function(){var l={top:0,right:0,bottom:0,left:0},q=500,m=500,w=function(z){return z.x},v=function(z){return z.y},h=function(z){return z.description},o=Math.floor(Math.random()*10000),s=c.utils.defaultColor(),f=d3.format(",.2f"),n=true,g=true,r=false,y="key",t=0.02,i=false,e=false,u=false,p=false,j=0.5,x=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout");function k(z){z.each(function(S){var A=q-l.left-l.right,D=m-l.top-l.bottom,E=Math.min(A,D)/2,H=E-(E/5),K=d3.select(this);var J=K.selectAll(".nv-wrap.nv-pie").data(S);var F=J.enter().append("g").attr("class","nvd3 nv-wrap nv-pie nv-chart-"+o);var O=F.append("g");var P=J.select("g");O.append("g").attr("class","nv-pie");J.attr("transform","translate("+l.left+","+l.top+")");P.select(".nv-pie").attr("transform","translate("+A/2+","+D/2+")");K.on("click",function(U,T){x.chartClick({data:U,index:T,pos:d3.event,id:o})});var I=d3.svg.arc().outerRadius(H);if(u){I.startAngle(u)}if(p){I.endAngle(p)}if(i){I.innerRadius(E*j)}var L=d3.layout.pie().sort(null).value(function(T){return T.disabled?0:v(T)});var B=J.select(".nv-pie").selectAll(".nv-slice").data(L);B.exit().remove();var R=B.enter().append("g").attr("class","nv-slice").on("mouseover",function(U,T){d3.select(this).classed("hover",true);x.elementMouseover({label:w(U.data),value:v(U.data),point:U.data,pointIndex:T,pos:[d3.event.pageX,d3.event.pageY],id:o})}).on("mouseout",function(U,T){d3.select(this).classed("hover",false);x.elementMouseout({label:w(U.data),value:v(U.data),point:U.data,index:T,id:o})}).on("click",function(U,T){x.elementClick({label:w(U.data),value:v(U.data),point:U.data,index:T,pos:d3.event,id:o});d3.event.stopPropagation()}).on("dblclick",function(U,T){x.elementDblClick({label:w(U.data),value:v(U.data),point:U.data,index:T,pos:d3.event,id:o});d3.event.stopPropagation()});B.attr("fill",function(U,T){return s(U,T)}).attr("stroke",function(U,T){return s(U,T)});var N=R.append("path").each(function(T){this._current=T});d3.transition(B.select("path")).attr("d",I).attrTween("d",C);if(n){var G=d3.svg.arc().innerRadius(0);if(g){G=I}if(r){G=d3.svg.arc().outerRadius(I.outerRadius())}R.append("g").classed("nv-label",true).each(function(V,T){var U=d3.select(this);U.attr("transform",function(X){if(e){X.outerRadius=H+10;X.innerRadius=H+15;var W=(X.startAngle+X.endAngle)/2*(180/Math.PI);if((X.startAngle+X.endAngle)/2t)?aa[y]:""});var U=V.select("text").node().getBBox();V.select(".nv-label rect").attr("width",U.width+10).attr("height",U.height+10).attr("transform",function(){return"translate("+[U.x-5,U.y-5]+")"})})}function Q(U){var T=(U.startAngle+U.endAngle)*90/Math.PI-90;return T>90?T-180:T}function C(T){T.endAngle=isNaN(T.endAngle)?0:T.endAngle;T.startAngle=isNaN(T.startAngle)?0:T.startAngle;if(!i){T.innerRadius=0}var U=d3.interpolate(this._current,T);this._current=U(0);return function(V){return I(U(V))}}function M(T){T.innerRadius=0;var U=d3.interpolate({startAngle:0,endAngle:0},T);return function(V){return I(U(V))}}});return k}k.dispatch=x;k.options=c.utils.optionsFunc.bind(k);k.margin=function(z){if(!arguments.length){return l}l.top=typeof z.top!="undefined"?z.top:l.top;l.right=typeof z.right!="undefined"?z.right:l.right;l.bottom=typeof z.bottom!="undefined"?z.bottom:l.bottom;l.left=typeof z.left!="undefined"?z.left:l.left;return k};k.width=function(z){if(!arguments.length){return q}q=z;return k};k.height=function(z){if(!arguments.length){return m}m=z;return k};k.values=function(z){c.log("pie.values() is no longer supported.");return k};k.x=function(z){if(!arguments.length){return w}w=z;return k};k.y=function(z){if(!arguments.length){return v}v=d3.functor(z);return k};k.description=function(z){if(!arguments.length){return h}h=z;return k};k.showLabels=function(z){if(!arguments.length){return n}n=z;return k};k.labelSunbeamLayout=function(z){if(!arguments.length){return e}e=z;return k};k.donutLabelsOutside=function(z){if(!arguments.length){return r}r=z;return k};k.pieLabelsOutside=function(z){if(!arguments.length){return g}g=z;return k};k.labelType=function(z){if(!arguments.length){return y}y=z;y=y||"key";return k};k.donut=function(z){if(!arguments.length){return i}i=z;return k};k.donutRatio=function(z){if(!arguments.length){return j}j=z;return k};k.startAngle=function(z){if(!arguments.length){return u}u=z;return k};k.endAngle=function(z){if(!arguments.length){return p}p=z;return k};k.id=function(z){if(!arguments.length){return o}o=z;return k};k.color=function(z){if(!arguments.length){return s}s=c.utils.getColor(z);return k};k.valueFormat=function(z){if(!arguments.length){return f}f=z;return k};k.labelThreshold=function(z){if(!arguments.length){return t}t=z;return k};return k};c.models.pieChart=function(){var i=c.models.pie(),m=c.models.legend();var g={top:30,right:20,bottom:20,left:20},f=null,q=null,p=true,h=c.utils.defaultColor(),l=true,s=function(t,w,v,u){return"

"+t+"

"+w+"

"},e={},r=null,o="No Data Available.",j=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState");var n=function(z,t){var u=i.description()(z.point)||i.x()(z.point);var x=z.pos[0]+((t&&t.offsetLeft)||0),w=z.pos[1]+((t&&t.offsetTop)||0),A=i.valueFormat()(i.y()(z.point)),v=s(u,A,z,k);c.tooltip.show([x,w],v,z.value<0?"n":"s",null,t)};function k(t){t.each(function(z){var v=d3.select(this),B=this;var E=(f||parseInt(v.style("width"))||960)-g.left-g.right,y=(q||parseInt(v.style("height"))||400)-g.top-g.bottom;k.update=function(){v.transition().call(k)};k.container=this;e.disabled=z.map(function(F){return !!F.disabled});if(!r){var D;r={};for(D in e){if(e[D] instanceof Array){r[D]=e[D].slice(0)}else{r[D]=e[D]}}}if(!z||!z.length){var w=v.selectAll(".nv-noData").data([o]);w.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");w.attr("x",g.left+E/2).attr("y",g.top+y/2).text(function(F){return F});return k}else{v.selectAll(".nv-noData").remove()}var x=v.selectAll("g.nv-wrap.nv-pieChart").data([z]);var u=x.enter().append("g").attr("class","nvd3 nv-wrap nv-pieChart").append("g");var A=x.select("g");u.append("g").attr("class","nv-pieWrap");u.append("g").attr("class","nv-legendWrap");if(p){m.width(E).key(i.x());x.select(".nv-legendWrap").datum(z).call(m);if(g.top!=m.height()){g.top=m.height();y=(q||parseInt(v.style("height"))||400)-g.top-g.bottom}x.select(".nv-legendWrap").attr("transform","translate(0,"+(-g.top)+")")}x.attr("transform","translate("+g.left+","+g.top+")");i.width(E).height(y);var C=A.select(".nv-pieWrap").datum([z]);d3.transition(C).call(i);m.dispatch.on("stateChange",function(F){e=F;j.stateChange(e);k.update()});i.dispatch.on("elementMouseout.tooltip",function(F){j.tooltipHide(F)});j.on("changeState",function(F){if(typeof F.disabled!=="undefined"){z.forEach(function(H,G){H.disabled=F.disabled[G]});e.disabled=F.disabled}k.update()})});return k}i.dispatch.on("elementMouseover.tooltip",function(t){t.pos=[t.pos[0]+g.left,t.pos[1]+g.top];j.tooltipShow(t)});j.on("tooltipShow",function(t){if(l){n(t)}});j.on("tooltipHide",function(){if(l){c.tooltip.cleanup()}});k.legend=m;k.dispatch=j;k.pie=i;d3.rebind(k,i,"valueFormat","values","x","y","description","id","showLabels","donutLabelsOutside","pieLabelsOutside","labelType","donut","donutRatio","labelThreshold");k.options=c.utils.optionsFunc.bind(k);k.margin=function(t){if(!arguments.length){return g}g.top=typeof t.top!="undefined"?t.top:g.top;g.right=typeof t.right!="undefined"?t.right:g.right;g.bottom=typeof t.bottom!="undefined"?t.bottom:g.bottom;g.left=typeof t.left!="undefined"?t.left:g.left;return k};k.width=function(t){if(!arguments.length){return f}f=t;return k};k.height=function(t){if(!arguments.length){return q}q=t;return k};k.color=function(t){if(!arguments.length){return h}h=c.utils.getColor(t);m.color(h);i.color(h);return k};k.showLegend=function(t){if(!arguments.length){return p}p=t;return k};k.tooltips=function(t){if(!arguments.length){return l}l=t;return k};k.tooltipContent=function(t){if(!arguments.length){return s}s=t;return k};k.state=function(t){if(!arguments.length){return e}e=t;return k};k.defaultState=function(t){if(!arguments.length){return r}r=t;return k};k.noData=function(t){if(!arguments.length){return o}o=t;return k};return k};c.models.scatter=function(){var k={top:0,right:0,bottom:0,left:0},e=960,g=500,s=c.utils.defaultColor(),A=Math.floor(Math.random()*100000),G=d3.scale.linear(),E=d3.scale.linear(),C=d3.scale.linear(),L=function(x){return x.x},K=function(x){return x.y},v=function(x){return x.size||1},o=function(x){return x.shape||"circle"},P=true,D=[],B=[],O=[],j=true,M=null,Q=function(x){return !x.notActive},I=false,m=0.1,u=false,S=true,q=function(){return 25},i=null,h=null,N=null,r=null,l=null,t=null,H=false,J=d3.dispatch("elementClick","elementMouseover","elementMouseout"),T=true;var p,f,w,F,n=false;function R(x){x.each(function(aa){var ae=e-k.left-k.right,W=g-k.top-k.bottom,z=d3.select(this);aa=aa.map(function(ag,af){ag.values=ag.values.map(function(ah){ah.series=af;return ah});return ag});var V=(i&&h&&l)?[]:d3.merge(aa.map(function(af){return af.values.map(function(ah,ag){return{x:L(ah,ag),y:K(ah,ag),size:v(ah,ag)}})}));G.domain(i||d3.extent(V.map(function(af){return af.x}).concat(D)));if(I&&aa[0]){G.range(N||[(ae*m+ae)/(2*aa[0].values.length),ae-ae*(1+m)/(2*aa[0].values.length)])}else{G.range(N||[0,ae])}E.domain(h||d3.extent(V.map(function(af){return af.y}).concat(B))).range(r||[W,0]);C.domain(l||d3.extent(V.map(function(af){return af.size}).concat(O))).range(t||[16,256]);if(G.domain()[0]===G.domain()[1]||E.domain()[0]===E.domain()[1]){H=true}if(G.domain()[0]===G.domain()[1]){G.domain()[0]?G.domain([G.domain()[0]-G.domain()[0]*0.01,G.domain()[1]+G.domain()[1]*0.01]):G.domain([-1,1])}if(E.domain()[0]===E.domain()[1]){E.domain()[0]?E.domain([E.domain()[0]-E.domain()[0]*0.01,E.domain()[1]+E.domain()[1]*0.01]):E.domain([-1,1])}if(isNaN(G.domain()[0])){G.domain([-1,1])}if(isNaN(E.domain()[0])){E.domain([-1,1])}p=p||G;f=f||E;w=w||C;var U=z.selectAll("g.nv-wrap.nv-scatter").data([aa]);var Z=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatter nv-chart-"+A+(H?" nv-single-point":""));var X=Z.append("defs");var y=Z.append("g");var ab=U.select("g");y.append("g").attr("class","nv-groups");y.append("g").attr("class","nv-point-paths");U.attr("transform","translate("+k.left+","+k.top+")");X.append("clipPath").attr("id","nv-edge-clip-"+A).append("rect");U.select("#nv-edge-clip-"+A+" rect").attr("width",ae).attr("height",W);ab.attr("clip-path",u?"url(#nv-edge-clip-"+A+")":"");function ac(){if(!j){return false}var ai;var ag=d3.merge(aa.map(function(ao,an){return ao.values.map(function(ap,at){var ar=L(ap,at);var aq=K(ap,at);return[G(ar)+Math.random()*1e-7,E(aq)+Math.random()*1e-7,an,at,ap]}).filter(function(ap,aq){return Q(ap[4],aq)})}));if(T===true){if(S){var al=U.select("defs").selectAll(".nv-point-clips").data([A]).enter();al.append("clipPath").attr("class","nv-point-clips").attr("id","nv-points-clip-"+A);var aj=U.select("#nv-points-clip-"+A).selectAll("circle").data(ag);aj.enter().append("circle").attr("r",q);aj.exit().remove();aj.attr("cx",function(an){return an[0]}).attr("cy",function(an){return an[1]});U.select(".nv-point-paths").attr("clip-path","url(#nv-points-clip-"+A+")")}if(ag.length){ag.push([G.range()[0]-20,E.range()[0]-20,null,null]);ag.push([G.range()[1]+20,E.range()[1]+20,null,null]);ag.push([G.range()[0]-20,E.range()[0]+20,null,null]);ag.push([G.range()[1]+20,E.range()[1]-20,null,null])}var ak=d3.geom.polygon([[-10,-10],[-10,g+10],[e+10,g+10],[e+10,-10]]);var am=d3.geom.voronoi(ag).map(function(ao,an){return{data:ak.clip(ao),series:ag[an][2],point:ag[an][3]}});var af=U.select(".nv-point-paths").selectAll("path").data(am);af.enter().append("path").attr("class",function(ao,an){return"nv-path-"+an});af.exit().remove();af.attr("d",function(an){if(an.data.length===0){return"M 0 0"}else{return"M"+an.data.join("L")+"Z"}});var ah=function(aq,ap){if(n){return 0}var ao=aa[aq.series];if(typeof ao==="undefined"){return}var an=ao.values[aq.point];ap({point:an,series:ao,pos:[G(L(an,aq.point))+k.left,E(K(an,aq.point))+k.top],seriesIndex:aq.series,pointIndex:aq.point})};af.on("click",function(an){ah(an,J.elementClick)}).on("mouseover",function(an){ah(an,J.elementMouseover)}).on("mouseout",function(ao,an){ah(ao,J.elementMouseout)})}else{U.select(".nv-groups").selectAll(".nv-group").selectAll(".nv-point").on("click",function(aq,ap){if(n||!aa[aq.series]){return 0}var ao=aa[aq.series],an=ao.values[ap];J.elementClick({point:an,series:ao,pos:[G(L(an,ap))+k.left,E(K(an,ap))+k.top],seriesIndex:aq.series,pointIndex:ap})}).on("mouseover",function(aq,ap){if(n||!aa[aq.series]){return 0}var ao=aa[aq.series],an=ao.values[ap];J.elementMouseover({point:an,series:ao,pos:[G(L(an,ap))+k.left,E(K(an,ap))+k.top],seriesIndex:aq.series,pointIndex:ap})}).on("mouseout",function(aq,ap){if(n||!aa[aq.series]){return 0}var ao=aa[aq.series],an=ao.values[ap];J.elementMouseout({point:an,series:ao,seriesIndex:aq.series,pointIndex:ap})})}n=false}n=true;var Y=U.select(".nv-groups").selectAll(".nv-group").data(function(af){return af},function(af){return af.key});Y.enter().append("g").style("stroke-opacity",0.000001).style("fill-opacity",0.000001);Y.exit().remove();Y.attr("class",function(ag,af){return"nv-group nv-series-"+af}).classed("hover",function(af){return af.hover});Y.transition().style("fill",function(ag,af){return s(ag,af)}).style("stroke",function(ag,af){return s(ag,af)}).style("stroke-opacity",1).style("fill-opacity",0.5);if(P){var ad=Y.selectAll("circle.nv-point").data(function(af){return af.values},M);ad.enter().append("circle").style("fill",function(ag,af){return ag.color}).style("stroke",function(ag,af){return ag.color}).attr("cx",function(ag,af){return c.utils.NaNtoZero(p(L(ag,af)))}).attr("cy",function(ag,af){return c.utils.NaNtoZero(f(K(ag,af)))}).attr("r",function(ag,af){return Math.sqrt(C(v(ag,af))/Math.PI)});ad.exit().remove();Y.exit().selectAll("path.nv-point").transition().attr("cx",function(ag,af){return c.utils.NaNtoZero(G(L(ag,af)))}).attr("cy",function(ag,af){return c.utils.NaNtoZero(E(K(ag,af)))}).remove();ad.each(function(ag,af){d3.select(this).classed("nv-point",true).classed("nv-point-"+af,true).classed("hover",false)});ad.transition().attr("cx",function(ag,af){return c.utils.NaNtoZero(G(L(ag,af)))}).attr("cy",function(ag,af){return c.utils.NaNtoZero(E(K(ag,af)))}).attr("r",function(ag,af){return Math.sqrt(C(v(ag,af))/Math.PI)})}else{var ad=Y.selectAll("path.nv-point").data(function(af){return af.values});ad.enter().append("path").style("fill",function(ag,af){return ag.color}).style("stroke",function(ag,af){return ag.color}).attr("transform",function(ag,af){return"translate("+p(L(ag,af))+","+f(K(ag,af))+")"}).attr("d",d3.svg.symbol().type(o).size(function(ag,af){return C(v(ag,af))}));ad.exit().remove();Y.exit().selectAll("path.nv-point").transition().attr("transform",function(ag,af){return"translate("+G(L(ag,af))+","+E(K(ag,af))+")"}).remove();ad.each(function(ag,af){d3.select(this).classed("nv-point",true).classed("nv-point-"+af,true).classed("hover",false)});ad.transition().attr("transform",function(ag,af){return"translate("+G(L(ag,af))+","+E(K(ag,af))+")"}).attr("d",d3.svg.symbol().type(o).size(function(ag,af){return C(v(ag,af))}))}clearTimeout(F);F=setTimeout(ac,300);p=G.copy();f=E.copy();w=C.copy()});return R}R.clearHighlights=function(){d3.selectAll(".nv-chart-"+A+" .nv-point.hover").classed("hover",false)};R.highlightPoint=function(y,z,x){d3.select(".nv-chart-"+A+" .nv-series-"+y+" .nv-point-"+z).classed("hover",x)};J.on("elementMouseover.point",function(x){if(j){R.highlightPoint(x.seriesIndex,x.pointIndex,true)}});J.on("elementMouseout.point",function(x){if(j){R.highlightPoint(x.seriesIndex,x.pointIndex,false)}});R.dispatch=J;R.options=c.utils.optionsFunc.bind(R);R.x=function(x){if(!arguments.length){return L}L=d3.functor(x);return R};R.y=function(x){if(!arguments.length){return K}K=d3.functor(x);return R};R.size=function(x){if(!arguments.length){return v}v=d3.functor(x);return R};R.margin=function(x){if(!arguments.length){return k}k.top=typeof x.top!="undefined"?x.top:k.top;k.right=typeof x.right!="undefined"?x.right:k.right;k.bottom=typeof x.bottom!="undefined"?x.bottom:k.bottom;k.left=typeof x.left!="undefined"?x.left:k.left;return R};R.width=function(x){if(!arguments.length){return e}e=x;return R};R.height=function(x){if(!arguments.length){return g}g=x;return R};R.xScale=function(x){if(!arguments.length){return G}G=x;return R};R.yScale=function(x){if(!arguments.length){return E}E=x;return R};R.zScale=function(x){if(!arguments.length){return C}C=x;return R};R.xDomain=function(x){if(!arguments.length){return i}i=x;return R};R.yDomain=function(x){if(!arguments.length){return h}h=x;return R};R.sizeDomain=function(x){if(!arguments.length){return l}l=x;return R};R.xRange=function(x){if(!arguments.length){return N}N=x;return R};R.yRange=function(x){if(!arguments.length){return r}r=x;return R};R.sizeRange=function(x){if(!arguments.length){return t}t=x;return R};R.forceX=function(x){if(!arguments.length){return D}D=x;return R};R.forceY=function(x){if(!arguments.length){return B}B=x;return R};R.forceSize=function(x){if(!arguments.length){return O}O=x;return R};R.interactive=function(x){if(!arguments.length){return j}j=x;return R};R.pointKey=function(x){if(!arguments.length){return M}M=x;return R};R.pointActive=function(x){if(!arguments.length){return Q}Q=x;return R};R.padData=function(x){if(!arguments.length){return I}I=x;return R};R.padDataOuter=function(x){if(!arguments.length){return m}m=x;return R};R.clipEdge=function(x){if(!arguments.length){return u}u=x;return R};R.clipVoronoi=function(x){if(!arguments.length){return S}S=x;return R};R.useVoronoi=function(x){if(!arguments.length){return T}T=x;if(T===false){S=false}return R};R.clipRadius=function(x){if(!arguments.length){return q}q=x;return R};R.color=function(x){if(!arguments.length){return s}s=c.utils.getColor(x);return R};R.shape=function(x){if(!arguments.length){return o}o=x;return R};R.onlyCircles=function(x){if(!arguments.length){return P}P=x;return R};R.id=function(x){if(!arguments.length){return A}A=x;return R};R.singlePoint=function(x){if(!arguments.length){return H}H=x;return R};return R};c.models.scatterChart=function(){var M=c.models.scatter(),z=c.models.axis(),o=c.models.axis(),I=c.models.legend(),A=c.models.legend(),E=c.models.distribution(),D=c.models.distribution();var k={top:30,right:20,bottom:50,left:75},e=null,g=null,C=c.utils.defaultColor(),H=d3.fisheye?d3.fisheye.scale(d3.scale.linear).distortion(0):M.xScale(),G=d3.fisheye?d3.fisheye.scale(d3.scale.linear).distortion(0):M.yScale(),m=0,s=0,r=false,p=false,v=true,i=true,L=true,O=false,l=!!d3.fisheye,w=0,q=false,K=true,j=function(T,S,U){return""+S+""},h=function(T,S,U){return""+U+""},F=null,R={},B=null,J=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState"),n="No Data Available.",N=250;M.xScale(H).yScale(G);z.orient("bottom").tickPadding(10);o.orient((O)?"right":"left").tickPadding(10);E.axis("x");D.axis("y");A.updateState(false);var t,f;var Q=function(V,Y){var T=V.pos[0]+(Y.offsetLeft||0),X=V.pos[1]+(Y.offsetTop||0),U=V.pos[0]+(Y.offsetLeft||0),y=G.range()[0]+k.top+(Y.offsetTop||0),S=H.range()[0]+k.left+(Y.offsetLeft||0),x=V.pos[1]+(Y.offsetTop||0),Z=z.tickFormat()(M.x()(V.point,V.pointIndex)),W=o.tickFormat()(M.y()(V.point,V.pointIndex));if(j!=null){c.tooltip.show([U,y],j(V.series.key,Z,W,V,P),"n",1,Y,"x-nvtooltip")}if(h!=null){c.tooltip.show([S,x],h(V.series.key,Z,W,V,P),"e",1,Y,"y-nvtooltip")}if(F!=null){c.tooltip.show([T,X],F(V.series.key,Z,W,V,P),V.value<0?"n":"s",null,Y)}};var u=[{key:"Magnify",disabled:true}];function P(x){x.each(function(aa){var U=d3.select(this),ac=this;var af=(e||parseInt(U.style("width"))||960)-k.left-k.right,X=(g||parseInt(U.style("height"))||400)-k.top-k.bottom;P.update=function(){U.transition().duration(N).call(P)};P.container=this;R.disabled=aa.map(function(ag){return !!ag.disabled});if(!B){var ad;B={};for(ad in R){if(R[ad] instanceof Array){B[ad]=R[ad].slice(0)}else{B[ad]=R[ad]}}}if(!aa||!aa.length||!aa.filter(function(ag){return ag.values.length}).length){var V=U.selectAll(".nv-noData").data([n]);V.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");V.attr("x",k.left+af/2).attr("y",k.top+X/2).text(function(ag){return ag});return P}else{U.selectAll(".nv-noData").remove()}t=t||H;f=f||G;var W=U.selectAll("g.nv-wrap.nv-scatterChart").data([aa]);var Y=W.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+M.id());var S=Y.append("g");var ab=W.select("g");S.append("rect").attr("class","nvd3 nv-background");S.append("g").attr("class","nv-x nv-axis");S.append("g").attr("class","nv-y nv-axis");S.append("g").attr("class","nv-scatterWrap");S.append("g").attr("class","nv-distWrap");S.append("g").attr("class","nv-legendWrap");S.append("g").attr("class","nv-controlsWrap");if(v){var ae=(l)?af/2:af;I.width(ae);W.select(".nv-legendWrap").datum(aa).call(I);if(k.top!=I.height()){k.top=I.height();X=(g||parseInt(U.style("height"))||400)-k.top-k.bottom}W.select(".nv-legendWrap").attr("transform","translate("+(af-ae)+","+(-k.top)+")")}if(l){A.width(180).color(["#444"]);ab.select(".nv-controlsWrap").datum(u).attr("transform","translate(0,"+(-k.top)+")").call(A)}W.attr("transform","translate("+k.left+","+k.top+")");if(O){ab.select(".nv-y.nv-axis").attr("transform","translate("+af+",0)")}M.width(af).height(X).color(aa.map(function(ah,ag){return ah.color||C(ah,ag)}).filter(function(ah,ag){return !aa[ag].disabled}));if(m!==0){M.xDomain(null)}if(s!==0){M.yDomain(null)}W.select(".nv-scatterWrap").datum(aa.filter(function(ag){return !ag.disabled})).call(M);if(m!==0){var y=H.domain()[1]-H.domain()[0];M.xDomain([H.domain()[0]-(m*y),H.domain()[1]+(m*y)])}if(s!==0){var Z=G.domain()[1]-G.domain()[0];M.yDomain([G.domain()[0]-(s*Z),G.domain()[1]+(s*Z)])}if(s!==0||m!==0){W.select(".nv-scatterWrap").datum(aa.filter(function(ag){return !ag.disabled})).call(M)}if(i){z.scale(H).ticks(z.ticks()&&z.ticks().length?z.ticks():af/100).tickSize(-X,0);ab.select(".nv-x.nv-axis").attr("transform","translate(0,"+G.range()[0]+")").call(z)}if(L){o.scale(G).ticks(o.ticks()&&o.ticks().length?o.ticks():X/36).tickSize(-af,0);ab.select(".nv-y.nv-axis").call(o)}if(r){E.getData(M.x()).scale(H).width(af).color(aa.map(function(ah,ag){return ah.color||C(ah,ag)}).filter(function(ah,ag){return !aa[ag].disabled}));S.select(".nv-distWrap").append("g").attr("class","nv-distributionX");ab.select(".nv-distributionX").attr("transform","translate(0,"+G.range()[0]+")").datum(aa.filter(function(ag){return !ag.disabled})).call(E)}if(p){D.getData(M.y()).scale(G).width(X).color(aa.map(function(ah,ag){return ah.color||C(ah,ag)}).filter(function(ah,ag){return !aa[ag].disabled}));S.select(".nv-distWrap").append("g").attr("class","nv-distributionY");ab.select(".nv-distributionY").attr("transform","translate("+(O?af:-D.size())+",0)").datum(aa.filter(function(ag){return !ag.disabled})).call(D)}if(d3.fisheye){ab.select(".nv-background").attr("width",af).attr("height",X);ab.select(".nv-background").on("mousemove",T);ab.select(".nv-background").on("click",function(){q=!q});M.dispatch.on("elementClick.freezeFisheye",function(){q=!q})}function T(){if(q){ab.select(".nv-point-paths").style("pointer-events","all");return false}ab.select(".nv-point-paths").style("pointer-events","none");var ag=d3.mouse(this);H.distortion(w).focus(ag[0]);G.distortion(w).focus(ag[1]);ab.select(".nv-scatterWrap").call(M);if(i){ab.select(".nv-x.nv-axis").call(z)}if(L){ab.select(".nv-y.nv-axis").call(o)}ab.select(".nv-distributionX").datum(aa.filter(function(ah){return !ah.disabled})).call(E);ab.select(".nv-distributionY").datum(aa.filter(function(ah){return !ah.disabled})).call(D)}A.dispatch.on("legendClick",function(ah,ag){ah.disabled=!ah.disabled;w=ah.disabled?0:2.5;ab.select(".nv-background").style("pointer-events",ah.disabled?"none":"all");ab.select(".nv-point-paths").style("pointer-events",ah.disabled?"all":"none");if(ah.disabled){H.distortion(w).focus(0);G.distortion(w).focus(0);ab.select(".nv-scatterWrap").call(M);ab.select(".nv-x.nv-axis").call(z);ab.select(".nv-y.nv-axis").call(o)}else{q=false}P.update()});I.dispatch.on("stateChange",function(ag){R.disabled=ag.disabled;J.stateChange(R);P.update()});M.dispatch.on("elementMouseover.tooltip",function(ag){d3.select(".nv-chart-"+M.id()+" .nv-series-"+ag.seriesIndex+" .nv-distx-"+ag.pointIndex).attr("y1",function(ai,ah){return ag.pos[1]-X});d3.select(".nv-chart-"+M.id()+" .nv-series-"+ag.seriesIndex+" .nv-disty-"+ag.pointIndex).attr("x2",ag.pos[0]+E.size());ag.pos=[ag.pos[0]+k.left,ag.pos[1]+k.top];J.tooltipShow(ag)});J.on("tooltipShow",function(ag){if(K){Q(ag,ac.parentNode)}});J.on("changeState",function(ag){if(typeof ag.disabled!=="undefined"){aa.forEach(function(ai,ah){ai.disabled=ag.disabled[ah]});R.disabled=ag.disabled}P.update()});t=H.copy();f=G.copy()});return P}M.dispatch.on("elementMouseout.tooltip",function(x){J.tooltipHide(x);d3.select(".nv-chart-"+M.id()+" .nv-series-"+x.seriesIndex+" .nv-distx-"+x.pointIndex).attr("y1",0);d3.select(".nv-chart-"+M.id()+" .nv-series-"+x.seriesIndex+" .nv-disty-"+x.pointIndex).attr("x2",D.size())});J.on("tooltipHide",function(){if(K){c.tooltip.cleanup()}});P.dispatch=J;P.scatter=M;P.legend=I;P.controls=A;P.xAxis=z;P.yAxis=o;P.distX=E;P.distY=D;d3.rebind(P,M,"id","interactive","pointActive","x","y","shape","size","xScale","yScale","zScale","xDomain","yDomain","xRange","yRange","sizeDomain","sizeRange","forceX","forceY","forceSize","clipVoronoi","clipRadius","useVoronoi");P.options=c.utils.optionsFunc.bind(P);P.margin=function(x){if(!arguments.length){return k}k.top=typeof x.top!="undefined"?x.top:k.top;k.right=typeof x.right!="undefined"?x.right:k.right;k.bottom=typeof x.bottom!="undefined"?x.bottom:k.bottom;k.left=typeof x.left!="undefined"?x.left:k.left;return P};P.width=function(x){if(!arguments.length){return e}e=x;return P};P.height=function(x){if(!arguments.length){return g}g=x;return P};P.color=function(x){if(!arguments.length){return C}C=c.utils.getColor(x);I.color(C);E.color(C);D.color(C);return P};P.showDistX=function(x){if(!arguments.length){return r}r=x;return P};P.showDistY=function(x){if(!arguments.length){return p}p=x;return P};P.showControls=function(x){if(!arguments.length){return l}l=x;return P};P.showLegend=function(x){if(!arguments.length){return v}v=x;return P};P.showXAxis=function(x){if(!arguments.length){return i}i=x;return P};P.showYAxis=function(x){if(!arguments.length){return L}L=x;return P};P.rightAlignYAxis=function(x){if(!arguments.length){return O}O=x;o.orient((x)?"right":"left");return P};P.fisheye=function(x){if(!arguments.length){return w}w=x;return P};P.xPadding=function(x){if(!arguments.length){return m}m=x;return P};P.yPadding=function(x){if(!arguments.length){return s}s=x;return P};P.tooltips=function(x){if(!arguments.length){return K}K=x;return P};P.tooltipContent=function(x){if(!arguments.length){return F}F=x;return P};P.tooltipXContent=function(x){if(!arguments.length){return j}j=x;return P};P.tooltipYContent=function(x){if(!arguments.length){return h}h=x;return P};P.state=function(x){if(!arguments.length){return R}R=x;return P};P.defaultState=function(x){if(!arguments.length){return B}B=x;return P};P.noData=function(x){if(!arguments.length){return n}n=x;return P};P.transitionDuration=function(x){if(!arguments.length){return N}N=x;return P};return P};c.models.scatterPlusLineChart=function(){var K=c.models.scatter(),v=c.models.axis(),n=c.models.axis(),G=c.models.legend(),w=c.models.legend(),C=c.models.distribution(),B=c.models.distribution();var k={top:30,right:20,bottom:50,left:75},e=null,g=null,A=c.utils.defaultColor(),F=d3.fisheye?d3.fisheye.scale(d3.scale.linear).distortion(0):K.xScale(),E=d3.fisheye?d3.fisheye.scale(d3.scale.linear).distortion(0):K.yScale(),q=false,o=false,t=true,i=true,J=true,M=false,l=!!d3.fisheye,u=0,p=false,I=true,j=function(R,Q,S){return""+Q+""},h=function(R,Q,S){return""+S+""},D=function(S,Q,T,R){return"

"+S+"

"+R+"

"},O={},z=null,H=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState"),m="No Data Available.",L=250;K.xScale(F).yScale(E);v.orient("bottom").tickPadding(10);n.orient((M)?"right":"left").tickPadding(10);C.axis("x");B.axis("y");w.updateState(false);var r,f;var P=function(T,W){var R=T.pos[0]+(W.offsetLeft||0),V=T.pos[1]+(W.offsetTop||0),S=T.pos[0]+(W.offsetLeft||0),y=E.range()[0]+k.top+(W.offsetTop||0),Q=F.range()[0]+k.left+(W.offsetLeft||0),x=T.pos[1]+(W.offsetTop||0),X=v.tickFormat()(K.x()(T.point,T.pointIndex)),U=n.tickFormat()(K.y()(T.point,T.pointIndex));if(j!=null){c.tooltip.show([S,y],j(T.series.key,X,U,T,N),"n",1,W,"x-nvtooltip")}if(h!=null){c.tooltip.show([Q,x],h(T.series.key,X,U,T,N),"e",1,W,"y-nvtooltip")}if(D!=null){c.tooltip.show([R,V],D(T.series.key,X,U,T.point.tooltip,T,N),T.value<0?"n":"s",null,W)}};var s=[{key:"Magnify",disabled:true}];function N(x){x.each(function(Y){var R=d3.select(this),aa=this;var ad=(e||parseInt(R.style("width"))||960)-k.left-k.right,U=(g||parseInt(R.style("height"))||400)-k.top-k.bottom;N.update=function(){R.transition().duration(L).call(N)};N.container=this;O.disabled=Y.map(function(ae){return !!ae.disabled});if(!z){var ac;z={};for(ac in O){if(O[ac] instanceof Array){z[ac]=O[ac].slice(0)}else{z[ac]=O[ac]}}}if(!Y||!Y.length||!Y.filter(function(ae){return ae.values.length}).length){var S=R.selectAll(".nv-noData").data([m]);S.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");S.attr("x",k.left+ad/2).attr("y",k.top+U/2).text(function(ae){return ae});return N}else{R.selectAll(".nv-noData").remove()}F=K.xScale();E=K.yScale();r=r||F;f=f||E;var T=R.selectAll("g.nv-wrap.nv-scatterChart").data([Y]);var W=T.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+K.id());var y=W.append("g");var Z=T.select("g");y.append("rect").attr("class","nvd3 nv-background").style("pointer-events","none");y.append("g").attr("class","nv-x nv-axis");y.append("g").attr("class","nv-y nv-axis");y.append("g").attr("class","nv-scatterWrap");y.append("g").attr("class","nv-regressionLinesWrap");y.append("g").attr("class","nv-distWrap");y.append("g").attr("class","nv-legendWrap");y.append("g").attr("class","nv-controlsWrap");T.attr("transform","translate("+k.left+","+k.top+")");if(M){Z.select(".nv-y.nv-axis").attr("transform","translate("+ad+",0)")}if(t){G.width(ad/2);T.select(".nv-legendWrap").datum(Y).call(G);if(k.top!=G.height()){k.top=G.height();U=(g||parseInt(R.style("height"))||400)-k.top-k.bottom}T.select(".nv-legendWrap").attr("transform","translate("+(ad/2)+","+(-k.top)+")")}if(l){w.width(180).color(["#444"]);Z.select(".nv-controlsWrap").datum(s).attr("transform","translate(0,"+(-k.top)+")").call(w)}K.width(ad).height(U).color(Y.map(function(af,ae){return af.color||A(af,ae)}).filter(function(af,ae){return !Y[ae].disabled}));T.select(".nv-scatterWrap").datum(Y.filter(function(ae){return !ae.disabled})).call(K);T.select(".nv-regressionLinesWrap").attr("clip-path","url(#nv-edge-clip-"+K.id()+")");var X=T.select(".nv-regressionLinesWrap").selectAll(".nv-regLines").data(function(ae){return ae});X.enter().append("g").attr("class","nv-regLines");var V=X.selectAll(".nv-regLine").data(function(ae){return[ae]});var ab=V.enter().append("line").attr("class","nv-regLine").style("stroke-opacity",0);V.transition().attr("x1",F.range()[0]).attr("x2",F.range()[1]).attr("y1",function(af,ae){return E(F.domain()[0]*af.slope+af.intercept)}).attr("y2",function(af,ae){return E(F.domain()[1]*af.slope+af.intercept)}).style("stroke",function(ag,af,ae){return A(ag,ae)}).style("stroke-opacity",function(af,ae){return(af.disabled||typeof af.slope==="undefined"||typeof af.intercept==="undefined")?0:1});if(i){v.scale(F).ticks(v.ticks()?v.ticks():ad/100).tickSize(-U,0);Z.select(".nv-x.nv-axis").attr("transform","translate(0,"+E.range()[0]+")").call(v)}if(J){n.scale(E).ticks(n.ticks()?n.ticks():U/36).tickSize(-ad,0);Z.select(".nv-y.nv-axis").call(n)}if(q){C.getData(K.x()).scale(F).width(ad).color(Y.map(function(af,ae){return af.color||A(af,ae)}).filter(function(af,ae){return !Y[ae].disabled}));y.select(".nv-distWrap").append("g").attr("class","nv-distributionX");Z.select(".nv-distributionX").attr("transform","translate(0,"+E.range()[0]+")").datum(Y.filter(function(ae){return !ae.disabled})).call(C)}if(o){B.getData(K.y()).scale(E).width(U).color(Y.map(function(af,ae){return af.color||A(af,ae)}).filter(function(af,ae){return !Y[ae].disabled}));y.select(".nv-distWrap").append("g").attr("class","nv-distributionY");Z.select(".nv-distributionY").attr("transform","translate("+(M?ad:-B.size())+",0)").datum(Y.filter(function(ae){return !ae.disabled})).call(B)}if(d3.fisheye){Z.select(".nv-background").attr("width",ad).attr("height",U);Z.select(".nv-background").on("mousemove",Q);Z.select(".nv-background").on("click",function(){p=!p});K.dispatch.on("elementClick.freezeFisheye",function(){p=!p})}function Q(){if(p){Z.select(".nv-point-paths").style("pointer-events","all");return false}Z.select(".nv-point-paths").style("pointer-events","none");var ae=d3.mouse(this);F.distortion(u).focus(ae[0]);E.distortion(u).focus(ae[1]);Z.select(".nv-scatterWrap").datum(Y.filter(function(af){return !af.disabled})).call(K);if(i){Z.select(".nv-x.nv-axis").call(v)}if(J){Z.select(".nv-y.nv-axis").call(n)}Z.select(".nv-distributionX").datum(Y.filter(function(af){return !af.disabled})).call(C);Z.select(".nv-distributionY").datum(Y.filter(function(af){return !af.disabled})).call(B)}w.dispatch.on("legendClick",function(af,ae){af.disabled=!af.disabled;u=af.disabled?0:2.5;Z.select(".nv-background").style("pointer-events",af.disabled?"none":"all");Z.select(".nv-point-paths").style("pointer-events",af.disabled?"all":"none");if(af.disabled){F.distortion(u).focus(0);E.distortion(u).focus(0);Z.select(".nv-scatterWrap").call(K);Z.select(".nv-x.nv-axis").call(v);Z.select(".nv-y.nv-axis").call(n)}else{p=false}N.update()});G.dispatch.on("stateChange",function(ae){O=ae;H.stateChange(O);N.update()});K.dispatch.on("elementMouseover.tooltip",function(ae){d3.select(".nv-chart-"+K.id()+" .nv-series-"+ae.seriesIndex+" .nv-distx-"+ae.pointIndex).attr("y1",ae.pos[1]-U);d3.select(".nv-chart-"+K.id()+" .nv-series-"+ae.seriesIndex+" .nv-disty-"+ae.pointIndex).attr("x2",ae.pos[0]+C.size());ae.pos=[ae.pos[0]+k.left,ae.pos[1]+k.top];H.tooltipShow(ae)});H.on("tooltipShow",function(ae){if(I){P(ae,aa.parentNode)}});H.on("changeState",function(ae){if(typeof ae.disabled!=="undefined"){Y.forEach(function(ag,af){ag.disabled=ae.disabled[af]});O.disabled=ae.disabled}N.update()});r=F.copy();f=E.copy()});return N}K.dispatch.on("elementMouseout.tooltip",function(x){H.tooltipHide(x);d3.select(".nv-chart-"+K.id()+" .nv-series-"+x.seriesIndex+" .nv-distx-"+x.pointIndex).attr("y1",0);d3.select(".nv-chart-"+K.id()+" .nv-series-"+x.seriesIndex+" .nv-disty-"+x.pointIndex).attr("x2",B.size())});H.on("tooltipHide",function(){if(I){c.tooltip.cleanup()}});N.dispatch=H;N.scatter=K;N.legend=G;N.controls=w;N.xAxis=v;N.yAxis=n;N.distX=C;N.distY=B;d3.rebind(N,K,"id","interactive","pointActive","x","y","shape","size","xScale","yScale","zScale","xDomain","yDomain","xRange","yRange","sizeDomain","sizeRange","forceX","forceY","forceSize","clipVoronoi","clipRadius","useVoronoi");N.options=c.utils.optionsFunc.bind(N);N.margin=function(x){if(!arguments.length){return k}k.top=typeof x.top!="undefined"?x.top:k.top;k.right=typeof x.right!="undefined"?x.right:k.right;k.bottom=typeof x.bottom!="undefined"?x.bottom:k.bottom;k.left=typeof x.left!="undefined"?x.left:k.left;return N};N.width=function(x){if(!arguments.length){return e}e=x;return N};N.height=function(x){if(!arguments.length){return g}g=x;return N};N.color=function(x){if(!arguments.length){return A}A=c.utils.getColor(x);G.color(A);C.color(A);B.color(A);return N};N.showDistX=function(x){if(!arguments.length){return q}q=x;return N};N.showDistY=function(x){if(!arguments.length){return o}o=x;return N};N.showControls=function(x){if(!arguments.length){return l}l=x;return N};N.showLegend=function(x){if(!arguments.length){return t}t=x;return N};N.showXAxis=function(x){if(!arguments.length){return i}i=x;return N};N.showYAxis=function(x){if(!arguments.length){return J}J=x;return N};N.rightAlignYAxis=function(x){if(!arguments.length){return M}M=x;n.orient((x)?"right":"left");return N};N.fisheye=function(x){if(!arguments.length){return u}u=x;return N};N.tooltips=function(x){if(!arguments.length){return I}I=x;return N};N.tooltipContent=function(x){if(!arguments.length){return D}D=x;return N};N.tooltipXContent=function(x){if(!arguments.length){return j}j=x;return N};N.tooltipYContent=function(x){if(!arguments.length){return h}h=x;return N};N.state=function(x){if(!arguments.length){return O}O=x;return N};N.defaultState=function(x){if(!arguments.length){return z}z=x;return N};N.noData=function(x){if(!arguments.length){return m}m=x;return N};N.transitionDuration=function(x){if(!arguments.length){return L}L=x;return N};return N};c.models.sparkline=function(){var k={top:2,right:0,bottom:2,left:0},g=400,r=32,h=true,o=d3.scale.linear(),n=d3.scale.linear(),q=function(s){return s.x},p=function(s){return s.y},j=c.utils.getColor(["#000"]),l,f,e,i;function m(s){s.each(function(y){var B=g-k.left-k.right,w=r-k.top-k.bottom,u=d3.select(this);o.domain(l||d3.extent(y,q)).range(e||[0,B]);n.domain(f||d3.extent(y,p)).range(i||[w,0]);var v=u.selectAll("g.nv-wrap.nv-sparkline").data([y]);var x=v.enter().append("g").attr("class","nvd3 nv-wrap nv-sparkline");var t=x.append("g");var z=v.select("g");v.attr("transform","translate("+k.left+","+k.top+")");var C=v.selectAll("path").data(function(D){return[D]});C.enter().append("path");C.exit().remove();C.style("stroke",function(E,D){return E.color||j(E,D)}).attr("d",d3.svg.line().x(function(E,D){return o(q(E,D))}).y(function(E,D){return n(p(E,D))}));var A=v.selectAll("circle.nv-point").data(function(H){var F=H.map(function(K,J){return p(K,J)});function G(K){if(K!=-1){var J=H[K];J.pointIndex=K;return J}else{return null}}var E=G(F.lastIndexOf(n.domain()[1])),D=G(F.indexOf(n.domain()[0])),I=G(F.length-1);return[D,E,I].filter(function(J){return J!=null})});A.enter().append("circle");A.exit().remove();A.attr("cx",function(E,D){return o(q(E,E.pointIndex))}).attr("cy",function(E,D){return n(p(E,E.pointIndex))}).attr("r",2).attr("class",function(E,D){return q(E,E.pointIndex)==o.domain()[1]?"nv-point nv-currentValue":p(E,E.pointIndex)==n.domain()[0]?"nv-point nv-minValue":"nv-point nv-maxValue"})});return m}m.options=c.utils.optionsFunc.bind(m);m.margin=function(s){if(!arguments.length){return k}k.top=typeof s.top!="undefined"?s.top:k.top;k.right=typeof s.right!="undefined"?s.right:k.right;k.bottom=typeof s.bottom!="undefined"?s.bottom:k.bottom;k.left=typeof s.left!="undefined"?s.left:k.left;return m};m.width=function(s){if(!arguments.length){return g}g=s;return m};m.height=function(s){if(!arguments.length){return r}r=s;return m};m.x=function(s){if(!arguments.length){return q}q=d3.functor(s);return m};m.y=function(s){if(!arguments.length){return p}p=d3.functor(s);return m};m.xScale=function(s){if(!arguments.length){return o}o=s;return m};m.yScale=function(s){if(!arguments.length){return n}n=s;return m};m.xDomain=function(s){if(!arguments.length){return l}l=s;return m};m.yDomain=function(s){if(!arguments.length){return f}f=s;return m};m.xRange=function(s){if(!arguments.length){return e}e=s;return m};m.yRange=function(s){if(!arguments.length){return i}i=s;return m};m.animate=function(s){if(!arguments.length){return h}h=s;return m};m.color=function(s){if(!arguments.length){return j}j=c.utils.getColor(s);return m};return m};c.models.sparklinePlus=function(){var p=c.models.sparkline();var f={top:15,right:100,bottom:10,left:50},e=null,r=null,m,l,j=[],q=false,o=d3.format(",r"),i=d3.format(",.2f"),h=true,s=true,g=false,n="No Data Available.";function k(t){t.each(function(B){var v=d3.select(this);var I=(e||parseInt(v.style("width"))||960)-f.left-f.right,y=(r||parseInt(v.style("height"))||400)-f.top-f.bottom;k.update=function(){k(t)};k.container=this;if(!B||!B.length){var w=v.selectAll(".nv-noData").data([n]);w.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");w.attr("x",f.left+I/2).attr("y",f.top+y/2).text(function(J){return J});return k}else{v.selectAll(".nv-noData").remove()}var H=p.y()(B[B.length-1],B.length-1);m=p.xScale();l=p.yScale();var x=v.selectAll("g.nv-wrap.nv-sparklineplus").data([B]);var z=x.enter().append("g").attr("class","nvd3 nv-wrap nv-sparklineplus");var u=z.append("g");var C=x.select("g");u.append("g").attr("class","nv-sparklineWrap");u.append("g").attr("class","nv-valueWrap");u.append("g").attr("class","nv-hoverArea");x.attr("transform","translate("+f.left+","+f.top+")");var D=C.select(".nv-sparklineWrap");p.width(I).height(y);D.call(p);var F=C.select(".nv-valueWrap");var G=F.selectAll(".nv-currentValue").data([H]);G.enter().append("text").attr("class","nv-currentValue").attr("dx",g?-8:8).attr("dy",".9em").style("text-anchor",g?"end":"start");G.attr("x",I+(g?f.right:0)).attr("y",s?function(J){return l(J)}:0).style("fill",p.color()(B[B.length-1],B.length-1)).text(i(H));u.select(".nv-hoverArea").append("rect").on("mousemove",A).on("click",function(){q=!q}).on("mouseout",function(){j=[];E()});C.select(".nv-hoverArea rect").attr("transform",function(J){return"translate("+-f.left+","+-f.top+")"}).attr("width",I+f.left+f.right).attr("height",y+f.top);function E(){if(q){return}var K=C.selectAll(".nv-hoverValue").data(j);var J=K.enter().append("g").attr("class","nv-hoverValue").style("stroke-opacity",0).style("fill-opacity",0);K.exit().transition().duration(250).style("stroke-opacity",0).style("fill-opacity",0).remove();K.attr("transform",function(L){return"translate("+m(p.x()(B[L],L))+",0)"}).transition().duration(250).style("stroke-opacity",1).style("fill-opacity",1);if(!j.length){return}J.append("line").attr("x1",0).attr("y1",-f.top).attr("x2",0).attr("y2",y);J.append("text").attr("class","nv-xValue").attr("x",-6).attr("y",-f.top).attr("text-anchor","end").attr("dy",".9em");C.select(".nv-hoverValue .nv-xValue").text(o(p.x()(B[j[0]],j[0])));J.append("text").attr("class","nv-yValue").attr("x",6).attr("y",-f.top).attr("text-anchor","start").attr("dy",".9em");C.select(".nv-hoverValue .nv-yValue").text(i(p.y()(B[j[0]],j[0])))}function A(){if(q){return}var K=d3.mouse(this)[0]-f.left;function J(O,L){var P=Math.abs(p.x()(O[0],0)-L);var N=0;for(var M=0;M"+L+"

"+O+" on "+K+"

"},s,r,k=d3.format(",.2f"),l={style:H.style()},n=null,j="No Data Available.",J=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState"),e=250,o=["Stacked","Stream","Expanded"],v=250;q.orient("bottom").tickPadding(7);f.orient((p)?"right":"left");w.updateState(false);var i=function(P,L){var O=P.pos[0]+(L.offsetLeft||0),N=P.pos[1]+(L.offsetTop||0),K=q.tickFormat()(H.x()(P.point,P.pointIndex)),Q=f.tickFormat()(H.y()(P.point,P.pointIndex)),M=m(P.series.key,K,Q,P,z);c.tooltip.show([O,N],M,P.value<0?"n":"s",null,L)};function z(x){x.each(function(O){var K=d3.select(this),R=this;var V=(D||parseInt(K.style("width"))||960)-A.left-A.right,N=(B||parseInt(K.style("height"))||400)-A.top-A.bottom;z.update=function(){K.transition().duration(v).call(z)};z.container=this;l.disabled=O.map(function(W){return !!W.disabled});if(!n){var T;n={};for(T in l){if(l[T] instanceof Array){n[T]=l[T].slice(0)}else{n[T]=l[T]}}}if(!O||!O.length||!O.filter(function(W){return W.values.length}).length){var L=K.selectAll(".nv-noData").data([j]);L.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle");L.attr("x",A.left+V/2).attr("y",A.top+N/2).text(function(W){return W});return z}else{K.selectAll(".nv-noData").remove()}s=H.xScale();r=H.yScale();var M=K.selectAll("g.nv-wrap.nv-stackedAreaChart").data([O]);var y=M.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedAreaChart").append("g");var P=M.select("g");y.append("rect").style("opacity",0);y.append("g").attr("class","nv-x nv-axis");y.append("g").attr("class","nv-y nv-axis");y.append("g").attr("class","nv-stackedWrap");y.append("g").attr("class","nv-legendWrap");y.append("g").attr("class","nv-controlsWrap");y.append("g").attr("class","nv-interactive");P.select("rect").attr("width",V).attr("height",N);if(F){var U=(I)?V-e:V;h.width(U);P.select(".nv-legendWrap").datum(O).call(h);if(A.top!=h.height()){A.top=h.height();N=(B||parseInt(K.style("height"))||400)-A.top-A.bottom}P.select(".nv-legendWrap").attr("transform","translate("+(V-U)+","+(-A.top)+")")}if(I){var S=[{key:"Stacked",disabled:H.offset()!="zero"},{key:"Stream",disabled:H.offset()!="wiggle"},{key:"Expanded",disabled:H.offset()!="expand"}];e=(o.length/3)*260;S=S.filter(function(W){return o.indexOf(W.key)>-1});w.width(e).color(["#444","#444","#444"]);P.select(".nv-controlsWrap").datum(S).call(w);if(A.top!=Math.max(w.height(),h.height())){A.top=Math.max(w.height(),h.height());N=(B||parseInt(K.style("height"))||400)-A.top-A.bottom}P.select(".nv-controlsWrap").attr("transform","translate(0,"+(-A.top)+")")}M.attr("transform","translate("+A.left+","+A.top+")");if(p){P.select(".nv-y.nv-axis").attr("transform","translate("+V+",0)")}if(t){C.width(V).height(N).margin({left:A.left,top:A.top}).svgContainer(K).xScale(s);M.select(".nv-interactive").call(C)}H.width(V).height(N);var Q=P.select(".nv-stackedWrap").datum(O);Q.transition().call(H);if(G){q.scale(s).ticks(V/100).tickSize(-N,0);P.select(".nv-x.nv-axis").attr("transform","translate(0,"+N+")");P.select(".nv-x.nv-axis").transition().duration(0).call(q)}if(u){f.scale(r).ticks(H.offset()=="wiggle"?0:N/36).tickSize(-V,0).setTickFormat(H.offset()=="expand"?d3.format("%"):k);P.select(".nv-y.nv-axis").transition().duration(0).call(f)}H.dispatch.on("areaClick.toggle",function(W){if(O.filter(function(X){return !X.disabled}).length===1){O=O.map(function(X){X.disabled=false;return X})}else{O=O.map(function(Y,X){Y.disabled=(X!=W.seriesIndex);return Y})}l.disabled=O.map(function(X){return !!X.disabled});J.stateChange(l);z.update()});h.dispatch.on("stateChange",function(W){l.disabled=W.disabled;J.stateChange(l);z.update()});w.dispatch.on("legendClick",function(X,W){if(!X.disabled){return}S=S.map(function(Y){Y.disabled=true;return Y});X.disabled=false;switch(X.key){case"Stacked":H.style("stack");break;case"Stream":H.style("stream");break;case"Expanded":H.style("expand");break}l.style=H.style();J.stateChange(l);z.update()});C.dispatch.on("elementMousemove",function(ab){H.clearHighlights();var aa,Z,W,Y=[];O.filter(function(ad,ac){ad.seriesIndex=ac;return !ad.disabled}).forEach(function(ae,ad){Z=c.interactiveBisect(ae.values,ab.pointXValue,z.x());H.highlightPoint(ad,Z,true);var ac=ae.values[Z];if(typeof ac==="undefined"){return}if(typeof aa==="undefined"){aa=ac}if(typeof W==="undefined"){W=z.xScale()(z.x()(ac,Z))}Y.push({key:ae.key,value:z.y()(ac,Z),color:E(ae,ae.seriesIndex)})});var X=q.tickFormat()(z.x()(aa,Z));C.tooltip.position({left:W+A.left,top:ab.mouseY+A.top}).chartContainer(R.parentNode).enabled(g).valueFormatter(function(ad,ac){return f.tickFormat()(ad)}).data({value:X,series:Y})();C.renderGuideLine(W)});C.dispatch.on("elementMouseout",function(W){J.tooltipHide();H.clearHighlights()});J.on("tooltipShow",function(W){if(g){i(W,R.parentNode)}});J.on("changeState",function(W){if(typeof W.disabled!=="undefined"){O.forEach(function(Y,X){Y.disabled=W.disabled[X]});l.disabled=W.disabled}if(typeof W.style!=="undefined"){H.style(W.style)}z.update()})});return z}H.dispatch.on("tooltipShow",function(x){x.pos=[x.pos[0]+A.left,x.pos[1]+A.top],J.tooltipShow(x)});H.dispatch.on("tooltipHide",function(x){J.tooltipHide(x)});J.on("tooltipHide",function(){if(g){c.tooltip.cleanup()}});z.dispatch=J;z.stacked=H;z.legend=h;z.controls=w;z.xAxis=q;z.yAxis=f;z.interactiveLayer=C;d3.rebind(z,H,"x","y","size","xScale","yScale","xDomain","yDomain","xRange","yRange","sizeDomain","interactive","useVoronoi","offset","order","style","clipEdge","forceX","forceY","forceSize","interpolate");z.options=c.utils.optionsFunc.bind(z);z.margin=function(x){if(!arguments.length){return A}A.top=typeof x.top!="undefined"?x.top:A.top;A.right=typeof x.right!="undefined"?x.right:A.right;A.bottom=typeof x.bottom!="undefined"?x.bottom:A.bottom;A.left=typeof x.left!="undefined"?x.left:A.left;return z};z.width=function(x){if(!arguments.length){return D}D=x;return z};z.height=function(x){if(!arguments.length){return B}B=x;return z};z.color=function(x){if(!arguments.length){return E}E=c.utils.getColor(x);h.color(E);H.color(E);return z};z.showControls=function(x){if(!arguments.length){return I}I=x;return z};z.showLegend=function(x){if(!arguments.length){return F}F=x;return z};z.showXAxis=function(x){if(!arguments.length){return G}G=x;return z};z.showYAxis=function(x){if(!arguments.length){return u}u=x;return z};z.rightAlignYAxis=function(x){if(!arguments.length){return p}p=x;f.orient((x)?"right":"left");return z};z.useInteractiveGuideline=function(x){if(!arguments.length){return t}t=x;if(x===true){z.interactive(false);z.useVoronoi(false)}return z};z.tooltip=function(x){if(!arguments.length){return m}m=x;return z};z.tooltips=function(x){if(!arguments.length){return g}g=x;return z};z.tooltipContent=function(x){if(!arguments.length){return m}m=x;return z};z.state=function(x){if(!arguments.length){return l}l=x;return z};z.defaultState=function(x){if(!arguments.length){return n}n=x;return z};z.noData=function(x){if(!arguments.length){return j}j=x;return z};z.transitionDuration=function(x){if(!arguments.length){return v}v=x;return z};z.controlsData=function(x){if(!arguments.length){return o}o=x;return z};f.setTickFormat=f.tickFormat;f.tickFormat=function(x){if(!arguments.length){return k}k=x;return f};return z}})(); \ No newline at end of file +/* nvd3 version 1.6.0 (https://github.com/liquidpele/nvd3) 2014-11-23 */ +!function(){var a=window.nv||{};window.nv=a,a.dev=!0,a.tooltip=a.tooltip||{},a.utils=a.utils||{},a.models=a.models||{},a.charts={},a.graphs=[],a.logs={},a.dispatch=d3.dispatch("render_start","render_end"),Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d&&a?this:a,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),a.dev&&(a.dispatch.on("render_start",function(){a.logs.startTime=+new Date}),a.dispatch.on("render_end",function(){a.logs.endTime=+new Date,a.logs.totalTime=a.logs.endTime-a.logs.startTime,a.log("total",a.logs.totalTime)})),a.log=function(){if(a.dev&&window.console&&console.log&&console.log.apply)console.log.apply(console,arguments);else if(a.dev&&window.console&&"function"==typeof console.log&&Function.prototype.bind){var b=Function.prototype.bind.call(console.log,console);b.apply(console,arguments)}return arguments[arguments.length-1]},a.deprecated=function(b){a.dev&&console&&console.warn&&console.warn("`"+b+"` has been deprecated.")},a.render=function(b){b=b||1,a.render.active=!0,a.dispatch.render_start(),setTimeout(function(){for(var c,d,e=0;b>e&&(d=a.render.queue[e]);e++)c=d.generate(),typeof d.callback==typeof Function&&d.callback(c),a.graphs.push(c);a.render.queue.splice(0,e),a.render.queue.length?setTimeout(arguments.callee,0):(a.dispatch.render_end(),a.render.active=!1)},0)},a.render.active=!1,a.render.queue=[],a.addGraph=function(b){typeof arguments[0]==typeof Function&&(b={generate:arguments[0],callback:arguments[1]}),a.render.queue.push(b),a.render.active||a.render()},a.interactiveGuideline=function(){"use strict";function b(l){l.each(function(l){function m(){var a=d3.mouse(this),d=a[0],e=a[1],i=!0,j=!1;if(k&&(d=d3.event.offsetX,e=d3.event.offsetY,"svg"!==d3.event.target.tagName&&(i=!1),d3.event.target.className.baseVal.match("nv-legend")&&(j=!0)),i&&(d-=f.left,e-=f.top),0>d||0>e||d>o||e>p||d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement||j){if(k&&d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement&&d3.event.relatedTarget.className.match(c.nvPointerEventsClass))return;return h.elementMouseout({mouseX:d,mouseY:e}),void b.renderGuideLine(null)}var l=g.invert(d);h.elementMousemove({mouseX:d,mouseY:e,pointXValue:l}),"dblclick"===d3.event.type&&h.elementDblclick({mouseX:d,mouseY:e,pointXValue:l}),"click"===d3.event.type&&h.elementClick({mouseX:d,mouseY:e,pointXValue:l})}var n=d3.select(this),o=d||960,p=e||400,q=n.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([l]),r=q.enter().append("g").attr("class"," nv-wrap nv-interactiveLineLayer");r.append("g").attr("class","nv-interactiveGuideLine"),j&&(j.on("mousemove",m,!0).on("mouseout",m,!0).on("dblclick",m).on("click",m),b.renderGuideLine=function(b){if(i){var c=q.select(".nv-interactiveGuideLine").selectAll("line").data(null!=b?[a.utils.NaNtoZero(b)]:[],String);c.enter().append("line").attr("class","nv-guideline").attr("x1",function(a){return a}).attr("x2",function(a){return a}).attr("y1",p).attr("y2",0),c.exit().remove()}})})}var c=a.models.tooltip(),d=null,e=null,f={left:0,top:0},g=d3.scale.linear(),h=(d3.scale.linear(),d3.dispatch("elementMousemove","elementMouseout","elementClick","elementDblclick")),i=!0,j=null,k="ActiveXObject"in window;return b.dispatch=h,b.tooltip=c,b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.width=function(a){return arguments.length?(d=a,b):d},b.height=function(a){return arguments.length?(e=a,b):e},b.xScale=function(a){return arguments.length?(g=a,b):g},b.showGuideLine=function(a){return arguments.length?(i=a,b):i},b.svgContainer=function(a){return arguments.length?(j=a,b):j},b},a.interactiveBisect=function(a,b,c){"use strict";if(!(a instanceof Array))return null;"function"!=typeof c&&(c=function(a){return a.x});var d=d3.bisector(c).left,e=d3.max([0,d(a,b)-1]),f=c(a[e],e);if("undefined"==typeof f&&(f=e),f===b)return e;var g=d3.min([e+1,a.length-1]),h=c(a[g],g);return"undefined"==typeof h&&(h=g),Math.abs(h-b)>=Math.abs(f-b)?e:g},a.nearestValueIndex=function(a,b,c){"use strict";var d=1/0,e=null;return a.forEach(function(a,f){var g=Math.abs(b-a);d>=g&&c>g&&(d=g,e=f)}),e},function(){"use strict";window.nv.tooltip={},window.nv.models.tooltip=function(){function b(){if(l){var a=d3.select(l);"svg"!==a.node().tagName&&(a=a.select("svg"));var b=a.node()?a.attr("viewBox"):null;if(b){b=b.split(" ");var c=parseInt(a.style("width"))/b[2];n.left=n.left*c,n.top=n.top*c}}}function c(a){var b;b=d3.select(l?l:"body");var c=b.select(".nvtooltip");return null===c.node()&&(c=b.append("div").attr("class","nvtooltip "+(k?k:"xy-tooltip")).attr("id",p)),c.node().innerHTML=a,c.style("top",0).style("left",0).style("opacity",0),c.selectAll("div, table, td, tr").classed(q,!0),c.classed(q,!0),c.node()}function d(){if(o&&u(f)){b();var e=n.left,k=null!=j?j:n.top,p=c(t(f));if(m=p,l){var q=l.getElementsByTagName("svg")[0],r=(q?q.getBoundingClientRect():l.getBoundingClientRect(),{left:0,top:0});if(q){var s=q.getBoundingClientRect(),v=l.getBoundingClientRect(),w=s.top;if(0>w){var x=l.getBoundingClientRect();w=Math.abs(w)>x.height?0:w}r.top=Math.abs(w-v.top),r.left=Math.abs(s.left-v.left)}e+=l.offsetLeft+r.left-2*l.scrollLeft,k+=l.offsetTop+r.top-2*l.scrollTop}return i&&i>0&&(k=Math.floor(k/i)*i),a.tooltip.calcTooltipPosition([e,k],g,h,p),d}}var e=null,f=null,g="w",h=50,i=25,j=null,k=null,l=null,m=null,n={left:null,top:null},o=!0,p="nvtooltip-"+Math.floor(1e5*Math.random()),q="nv-pointer-events-none",r=function(a){return a},s=function(a){return a},t=function(a){if(null!=e)return e;if(null==a)return"";var b=d3.select(document.createElement("table")),c=b.selectAll("thead").data([a]).enter().append("thead");c.append("tr").append("td").attr("colspan",3).append("strong").classed("x-value",!0).html(s(a.value));var d=b.selectAll("tbody").data([a]).enter().append("tbody"),f=d.selectAll("tr").data(function(a){return a.series}).enter().append("tr").classed("highlight",function(a){return a.highlight});f.append("td").classed("legend-color-guide",!0).append("div").style("background-color",function(a){return a.color}),f.append("td").classed("key",!0).html(function(a){return a.key}),f.append("td").classed("value",!0).html(function(a,b){return r(a.value,b)}),f.selectAll("td").each(function(a){if(a.highlight){var b=d3.scale.linear().domain([0,1]).range(["#fff",a.color]),c=.6;d3.select(this).style("border-bottom-color",b(c)).style("border-top-color",b(c))}});var g=b.node().outerHTML;return void 0!==a.footer&&(g+=""),g},u=function(a){return a&&a.series&&a.series.length>0?!0:!1};return d.nvPointerEventsClass=q,d.content=function(a){return arguments.length?(e=a,d):e},d.tooltipElem=function(){return m},d.contentGenerator=function(a){return arguments.length?("function"==typeof a&&(t=a),d):t},d.data=function(a){return arguments.length?(f=a,d):f},d.gravity=function(a){return arguments.length?(g=a,d):g},d.distance=function(a){return arguments.length?(h=a,d):h},d.snapDistance=function(a){return arguments.length?(i=a,d):i},d.classes=function(a){return arguments.length?(k=a,d):k},d.chartContainer=function(a){return arguments.length?(l=a,d):l},d.position=function(a){return arguments.length?(n.left="undefined"!=typeof a.left?a.left:n.left,n.top="undefined"!=typeof a.top?a.top:n.top,d):n},d.fixedTop=function(a){return arguments.length?(j=a,d):j},d.enabled=function(a){return arguments.length?(o=a,d):o},d.valueFormatter=function(a){return arguments.length?("function"==typeof a&&(r=a),d):r},d.headerFormatter=function(a){return arguments.length?("function"==typeof a&&(s=a),d):s},d.id=function(){return p},d},a.tooltip.show=function(b,c,d,e,f,g){var h=document.createElement("div");h.className="nvtooltip "+(g?g:"xy-tooltip");var i=f;(!f||f.tagName.match(/g|svg/i))&&(i=document.getElementsByTagName("body")[0]),h.style.left=0,h.style.top=0,h.style.opacity=0,"string"!=typeof c?h.appendChild(c):h.innerHTML=c,i.appendChild(h),f&&(b[0]=b[0]-f.scrollLeft,b[1]=b[1]-f.scrollTop),a.tooltip.calcTooltipPosition(b,d,e,h)},a.tooltip.findFirstNonSVGParent=function(a){for(;null!==a.tagName.match(/^g|svg$/i);)a=a.parentNode;return a},a.tooltip.findTotalOffsetTop=function(a,b){var c=b;do isNaN(a.offsetTop)||(c+=a.offsetTop);while(a=a.offsetParent);return c},a.tooltip.findTotalOffsetLeft=function(a,b){var c=b;do isNaN(a.offsetLeft)||(c+=a.offsetLeft);while(a=a.offsetParent);return c},a.tooltip.calcTooltipPosition=function(b,c,d,e){var f,g,h=parseInt(e.offsetHeight),i=parseInt(e.offsetWidth),j=a.utils.windowSize().width,k=a.utils.windowSize().height,l=window.pageYOffset,m=window.pageXOffset;k=window.innerWidth>=document.body.scrollWidth?k:k-16,j=window.innerHeight>=document.body.scrollHeight?j:j-16,c=c||"s",d=d||20;var n=function(b){return a.tooltip.findTotalOffsetTop(b,g)},o=function(b){return a.tooltip.findTotalOffsetLeft(b,f)};switch(c){case"e":f=b[0]-i-d,g=b[1]-h/2;var p=o(e),q=n(e);m>p&&(f=b[0]+d>m?b[0]+d:m-p+f),l>q&&(g=l-q+g),q+h>l+k&&(g=l+k-q+g-h);break;case"w":f=b[0]+d,g=b[1]-h/2;var p=o(e),q=n(e);p+i>j&&(f=b[0]-i-d),l>q&&(g=l+5),q+h>l+k&&(g=l+k-q+g-h);break;case"n":f=b[0]-i/2-5,g=b[1]+d;var p=o(e),q=n(e);m>p&&(f=m+5),p+i>j&&(f=f-i/2+5),q+h>l+k&&(g=l+k-q+g-h);break;case"s":f=b[0]-i/2,g=b[1]-h-d;var p=o(e),q=n(e);m>p&&(f=m+5),p+i>j&&(f=f-i/2+5),l>q&&(g=l);break;case"none":f=b[0],g=b[1]-d;var p=o(e),q=n(e)}return e.style.left=f+"px",e.style.top=g+"px",e.style.opacity=1,e.style.position="absolute",e},a.tooltip.cleanup=function(){for(var a=document.getElementsByClassName("nvtooltip"),b=[];a.length;)b.push(a[0]),a[0].style.transitionDelay="0 !important",a[0].style.opacity=0,a[0].className="nvtooltip-pending-removal";setTimeout(function(){for(;b.length;){var a=b.pop();a.parentNode.removeChild(a)}},500)}}(),a.utils.windowSize=function(){var a={width:640,height:480};return document.body&&document.body.offsetWidth&&(a.width=document.body.offsetWidth,a.height=document.body.offsetHeight),"CSS1Compat"==document.compatMode&&document.documentElement&&document.documentElement.offsetWidth&&(a.width=document.documentElement.offsetWidth,a.height=document.documentElement.offsetHeight),window.innerWidth&&window.innerHeight&&(a.width=window.innerWidth,a.height=window.innerHeight),a},a.utils.windowResize=function(b){return window.addEventListener?window.addEventListener("resize",b):a.log("ERROR: Failed to bind to window.resize with: ",b),{callback:b,clear:function(){window.removeEventListener("resize",b)}}},a.utils.getColor=function(b){return arguments.length&&b instanceof Array?function(a,c){return a.color||b[c%b.length]}:a.utils.defaultColor()},a.utils.defaultColor=function(){var a=d3.scale.category20().range();return function(b,c){return b.color||a[c%a.length]}},a.utils.customTheme=function(a,b,c){b=b||function(a){return a.key},c=c||d3.scale.category20().range();var d=c.length;return function(e){var f=b(e);return"function"==typeof a[f]?a[f]():void 0!==a[f]?a[f]:(d||(d=c.length),d-=1,c[d])}},a.utils.pjax=function(b,c){var d=function(d){d3.html(d,function(d){var e=d3.select(c).node();e.parentNode.replaceChild(d3.select(d).select(c).node(),e),a.utils.pjax(b,c)})};d3.selectAll(b).on("click",function(){history.pushState(this.href,this.textContent,this.href),d(this.href),d3.event.preventDefault()}),d3.select(window).on("popstate",function(){d3.event.state&&d(d3.event.state)})},a.utils.calcApproxTextWidth=function(a){if("function"==typeof a.style&&"function"==typeof a.text){var b=parseInt(a.style("font-size").replace("px","")),c=a.text().length;return c*b*.5}return 0},a.utils.NaNtoZero=function(a){return"number"!=typeof a||isNaN(a)||null===a||1/0===a||a===-1/0?0:a},d3.selection.prototype.watchTransition=function(a){var b=[this].concat([].slice.call(arguments,1));return a.transition.apply(a,b)},a.utils.renderWatch=function(b,c){if(!(this instanceof a.utils.renderWatch))return new a.utils.renderWatch(b,c);var d=void 0!==c?c:250,e=[],f=this;this.models=function(a){return a=[].slice.call(arguments,0),a.forEach(function(a){a.__rendered=!1,function(a){a.dispatch.on("renderEnd",function(){a.__rendered=!0,f.renderEnd("model")})}(a),e.indexOf(a)<0&&e.push(a)}),this},this.reset=function(a){void 0!==a&&(d=a),e=[]},this.transition=function(a,b,c){if(b=arguments.length>1?[].slice.call(arguments,1):[],c=b.length>1?b.pop():void 0!==d?d:250,a.__rendered=!1,e.indexOf(a)<0&&e.push(a),0===c)return a.__rendered=!0,a.delay=function(){return this},a.duration=function(){return this},a;a.__rendered=0===a.length?!0:a.every(function(a){return!a.length})?!0:!1;var g=0;return a.transition().duration(c).each(function(){++g}).each("end",function(){0===--g&&(a.__rendered=!0,f.renderEnd.apply(this,b))})},this.renderEnd=function(){e.every(function(a){return a.__rendered})&&(e.forEach(function(a){a.__rendered=!1}),b.renderEnd.apply(this,arguments))}},a.utils.deepExtend=function(b){var c=arguments.length>1?[].slice.call(arguments,1):[];c.forEach(function(c){for(key in c){var d=b[key]instanceof Array,e="object"==typeof b[key],f="object"==typeof c[key];e&&!d&&f?a.utils.deepExtend(b[key],c[key]):b[key]=c[key]}})},a.utils.state=function(){if(!(this instanceof a.utils.state))return new a.utils.state;var b={},c=function(){},d=function(){return{}},e=null,f=null;this.dispatch=d3.dispatch("change","set"),this.dispatch.on("set",function(a){c(a,!0)}),this.getter=function(a){return d=a,this},this.setter=function(a,b){return b||(b=function(){}),c=function(c,d){a(c),d&&b()},this},this.init=function(b){e=e||{},a.utils.deepExtend(e,b)};var g=function(){var a=d();if(JSON.stringify(a)===JSON.stringify(b))return!1;for(var c in a)void 0===b[c]&&(b[c]={}),b[c]=a[c],f=!0;return!0};this.update=function(){e&&(c(e,!1),e=null),g.call(this)&&this.dispatch.change(b)}},a.utils.optionsFunc=function(b){return a.deprecated("nv.utils.optionsFunc"),b&&d3.map(b).forEach(function(a,b){"function"==typeof this[a]&&this[a](b)}.bind(this)),this},a.utils.calcTicksX=function(b,c){var d=1,e=0;for(e;ed?f:d}return a.log("Requested number of ticks: ",b),a.log("Calculated max values to be: ",d),b=b>d?b=d-1:b,b=1>b?1:b,b=Math.floor(b),a.log("Calculating tick count as: ",b),b},a.utils.calcTicksY=function(b,c){return a.utils.calcTicksX(b,c)},a.utils.initOption=function(a,b){a[b]=function(c){return arguments.length?(a._options[b]=c,a):a._options[b]}},a.utils.initOptions=function(b){var c=Object.getOwnPropertyNames(b._options);for(var d in c)a.utils.initOption(b,c[d])},a.utils.inheritOptions=function(a,b){var c=Object.getOwnPropertyNames(b._options);c.unshift(b),c.unshift(a),d3.rebind.apply(this,c)},a.utils.initSVG=function(a){a.classed({"nvd3-svg":!0})},a.models.axis=function(){"use strict";function b(f){return t.reset(),f.each(function(b){var f=d3.select(this);a.utils.initSVG(f);var q=f.selectAll("g.nv-wrap.nv-axis").data([b]),r=q.enter().append("g").attr("class","nvd3 nv-wrap nv-axis"),u=(r.append("g"),q.select("g"));null!==o?c.ticks(o):("top"==c.orient()||"bottom"==c.orient())&&c.ticks(Math.abs(g.range()[1]-g.range()[0])/100),u.watchTransition(t,"axis").call(c),s=s||c.scale();var v=c.tickFormat();null==v&&(v=s.tickFormat());var w=u.selectAll("text.nv-axislabel").data([h||null]);switch(w.exit().remove(),c.orient()){case"top":w.enter().append("text").attr("class","nv-axislabel");var x;if(x=g.range().length<2?0:2===g.range().length?g.range()[1]:g.range()[g.range().length-1]+(g.range()[1]-g.range()[0]),w.attr("text-anchor","middle").attr("y",0).attr("x",x/2),i){var y=q.selectAll("g.nv-axisMaxMin").data(g.domain());y.enter().append("g").attr("class","nv-axisMaxMin").append("text"),y.exit().remove(),y.attr("transform",function(a){return"translate("+g(a)+",0)"}).select("text").attr("dy","-0.5em").attr("y",-c.tickPadding()).attr("text-anchor","middle").text(function(a){var b=v(a);return(""+b).match("NaN")?"":b}),y.watchTransition(t,"min-max top").attr("transform",function(a,b){return"translate("+g.range()[b]+",0)"})}break;case"bottom":var z=p+36,A=30,B=u.selectAll("g").select("text");if(k%360){B.each(function(){var a=this.getBoundingClientRect().width;a>A&&(A=a)});var C=Math.abs(Math.sin(k*Math.PI/180)),z=(C?C*A:A)+30;B.attr("transform",function(){return"rotate("+k+" 0,0)"}).style("text-anchor",k%360>0?"start":"end")}w.enter().append("text").attr("class","nv-axislabel");var x;if(x=g.range().length<2?0:2===g.range().length?g.range()[1]:g.range()[g.range().length-1]+(g.range()[1]-g.range()[0]),w.attr("text-anchor","middle").attr("y",z).attr("x",x/2),i){var y=q.selectAll("g.nv-axisMaxMin").data([g.domain()[0],g.domain()[g.domain().length-1]]);y.enter().append("g").attr("class","nv-axisMaxMin").append("text"),y.exit().remove(),y.attr("transform",function(a){return"translate("+(g(a)+(n?g.rangeBand()/2:0))+",0)"}).select("text").attr("dy",".71em").attr("y",c.tickPadding()).attr("transform",function(){return"rotate("+k+" 0,0)"}).style("text-anchor",k?k%360>0?"start":"end":"middle").text(function(a){var b=v(a);return(""+b).match("NaN")?"":b}),y.watchTransition(t,"min-max bottom").attr("transform",function(a){return"translate("+(g(a)+(n?g.rangeBand()/2:0))+",0)"})}m&&B.attr("transform",function(a,b){return"translate(0,"+(b%2==0?"0":"12")+")"});break;case"right":if(w.enter().append("text").attr("class","nv-axislabel"),w.style("text-anchor",l?"middle":"begin").attr("transform",l?"rotate(90)":"").attr("y",l?-Math.max(d.right,e)+12:-10).attr("x",l?g.range()[0]/2:c.tickPadding()),i){var y=q.selectAll("g.nv-axisMaxMin").data(g.domain());y.enter().append("g").attr("class","nv-axisMaxMin").append("text").style("opacity",0),y.exit().remove(),y.attr("transform",function(a){return"translate(0,"+g(a)+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",c.tickPadding()).style("text-anchor","start").text(function(a){var b=v(a);return(""+b).match("NaN")?"":b}),y.watchTransition(t,"min-max right").attr("transform",function(a,b){return"translate(0,"+g.range()[b]+")"}).select("text").style("opacity",1)}break;case"left":if(w.enter().append("text").attr("class","nv-axislabel"),w.style("text-anchor",l?"middle":"end").attr("transform",l?"rotate(-90)":"").attr("y",l?-Math.max(d.left,e)+25-(p||0):-10).attr("x",l?-g.range()[0]/2:-c.tickPadding()),i){var y=q.selectAll("g.nv-axisMaxMin").data(g.domain());y.enter().append("g").attr("class","nv-axisMaxMin").append("text").style("opacity",0),y.exit().remove(),y.attr("transform",function(a){return"translate(0,"+s(a)+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",-c.tickPadding()).attr("text-anchor","end").text(function(a){var b=v(a);return(""+b).match("NaN")?"":b}),y.watchTransition(t,"min-max right").attr("transform",function(a,b){return"translate(0,"+g.range()[b]+")"}).select("text").style("opacity",1)}}if(w.text(function(a){return a}),!i||"left"!==c.orient()&&"right"!==c.orient()||(u.selectAll("g").each(function(a){d3.select(this).select("text").attr("opacity",1),(g(a)g.range()[0]-10)&&((a>1e-10||-1e-10>a)&&d3.select(this).attr("opacity",0),d3.select(this).select("text").attr("opacity",0))}),g.domain()[0]==g.domain()[1]&&0==g.domain()[0]&&q.selectAll("g.nv-axisMaxMin").style("opacity",function(a,b){return b?0:1})),i&&("top"===c.orient()||"bottom"===c.orient())){var D=[];q.selectAll("g.nv-axisMaxMin").each(function(a,b){try{D.push(b?g(a)-this.getBoundingClientRect().width-4:g(a)+this.getBoundingClientRect().width+4)}catch(c){D.push(b?g(a)-4:g(a)+4)}}),u.selectAll("g").each(function(a){(g(a)D[1])&&(a>1e-10||-1e-10>a?d3.select(this).remove():d3.select(this).select("text").remove())})}j&&u.selectAll(".tick").filter(function(){return!parseFloat(Math.round(1e5*this.__data__)/1e6)&&void 0!==this.__data__}).classed("zero",!0),s=g.copy()}),t.renderEnd("axis immediate"),b}var c=d3.svg.axis(),d={top:0,right:0,bottom:0,left:0},e=75,f=60,g=d3.scale.linear(),h=null,i=!0,j=!0,k=0,l=!0,m=!1,n=!1,o=null,p=0,q=250,r=d3.dispatch("renderEnd");c.scale(g).orient("bottom").tickFormat(function(a){return a});var s,t=a.utils.renderWatch(r,q);return b.axis=c,b.dispatch=r,d3.rebind(b,c,"orient","tickValues","tickSubdivide","tickSize","tickPadding","tickFormat"),d3.rebind(b,g,"domain","range","rangeBand","rangeBands"),b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(d.top="undefined"!=typeof a.top?a.top:d.top,d.right="undefined"!=typeof a.right?a.right:d.right,d.bottom="undefined"!=typeof a.bottom?a.bottom:d.bottom,d.left="undefined"!=typeof a.left?a.left:d.left,b):d},b.width=function(a){return arguments.length?(e=a,b):e},b.ticks=function(a){return arguments.length?(o=a,b):o},b.height=function(a){return arguments.length?(f=a,b):f},b.axisLabel=function(a){return arguments.length?(h=a,b):h},b.showMaxMin=function(a){return arguments.length?(i=a,b):i},b.highlightZero=function(a){return arguments.length?(j=a,b):j},b.scale=function(a){return arguments.length?(g=a,c.scale(g),n="function"==typeof g.rangeBands,d3.rebind(b,g,"domain","range","rangeBand","rangeBands"),b):g},b.rotateYLabel=function(a){return arguments.length?(l=a,b):l},b.rotateLabels=function(a){return arguments.length?(k=a,b):k},b.staggerLabels=function(a){return arguments.length?(m=a,b):m},b.axisLabelDistance=function(a){return arguments.length?(p=a,b):p},b.duration=function(a){return arguments.length?(q=a,t.reset(q),b):q},b},a.models.bullet=function(){"use strict";function b(d){return d.each(function(b,d){var o=m-c.left-c.right,r=n-c.top-c.bottom,s=d3.select(this);a.utils.initSVG(s);{var t=f.call(this,b,d).slice().sort(d3.descending),u=g.call(this,b,d).slice().sort(d3.descending),v=h.call(this,b,d).slice().sort(d3.descending),w=i.call(this,b,d).slice(),x=j.call(this,b,d).slice(),y=k.call(this,b,d).slice(),z=d3.scale.linear().domain(d3.extent(d3.merge([l,t]))).range(e?[o,0]:[0,o]);this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range())}this.__chart__=z;var A=d3.min(t),B=d3.max(t),C=t[1],D=s.selectAll("g.nv-wrap.nv-bullet").data([b]),E=D.enter().append("g").attr("class","nvd3 nv-wrap nv-bullet"),F=E.append("g"),G=D.select("g");F.append("rect").attr("class","nv-range nv-rangeMax"),F.append("rect").attr("class","nv-range nv-rangeAvg"),F.append("rect").attr("class","nv-range nv-rangeMin"),F.append("rect").attr("class","nv-measure"),F.append("path").attr("class","nv-markerTriangle"),D.attr("transform","translate("+c.left+","+c.top+")");var H=function(a){return Math.abs(z(a)-z(0))},I=function(a){return z(0>a?a:0)};G.select("rect.nv-rangeMax").attr("height",r).attr("width",H(B>0?B:A)).attr("x",I(B>0?B:A)).datum(B>0?B:A),G.select("rect.nv-rangeAvg").attr("height",r).attr("width",H(C)).attr("x",I(C)).datum(C),G.select("rect.nv-rangeMin").attr("height",r).attr("width",H(B)).attr("x",I(B)).attr("width",H(B>0?A:B)).attr("x",I(B>0?A:B)).datum(B>0?A:B),G.select("rect.nv-measure").style("fill",p).attr("height",r/3).attr("y",r/3).attr("width",0>v?z(0)-z(v[0]):z(v[0])-z(0)).attr("x",I(v)).on("mouseover",function(){q.elementMouseover({value:v[0],label:y[0]||"Current",pos:[z(v[0]),r/2]})}).on("mouseout",function(){q.elementMouseout({value:v[0],label:y[0]||"Current"})});var J=r/6;u[0]?G.selectAll("path.nv-markerTriangle").attr("transform",function(){return"translate("+z(u[0])+","+r/2+")"}).attr("d","M0,"+J+"L"+J+","+-J+" "+-J+","+-J+"Z").on("mouseover",function(){q.elementMouseover({value:u[0],label:x[0]||"Previous",pos:[z(u[0]),r/2]})}).on("mouseout",function(){q.elementMouseout({value:u[0],label:x[0]||"Previous"})}):G.selectAll("path.nv-markerTriangle").remove(),D.selectAll(".nv-range").on("mouseover",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");q.elementMouseover({value:a,label:c,pos:[z(a),r/2]})}).on("mouseout",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");q.elementMouseout({value:a,label:c})})}),b}var c={top:0,right:0,bottom:0,left:0},d="left",e=!1,f=function(a){return a.ranges},g=function(a){return a.markers?a.markers:[0]},h=function(a){return a.measures},i=function(a){return a.rangeLabels?a.rangeLabels:[]},j=function(a){return a.markerLabels?a.markerLabels:[]},k=function(a){return a.measureLabels?a.measureLabels:[]},l=[0],m=380,n=30,o=null,p=a.utils.getColor(["#1f77b4"]),q=d3.dispatch("elementMouseover","elementMouseout");return b.dispatch=q,b.options=a.utils.optionsFunc.bind(b),b.orient=function(a){return arguments.length?(d=a,e="right"==d||"bottom"==d,b):d},b.ranges=function(a){return arguments.length?(f=a,b):f},b.markers=function(a){return arguments.length?(g=a,b):g},b.measures=function(a){return arguments.length?(h=a,b):h},b.forceX=function(a){return arguments.length?(l=a,b):l},b.width=function(a){return arguments.length?(m=a,b):m},b.height=function(a){return arguments.length?(n=a,b):n},b.margin=function(a){return arguments.length?(c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left,b):c},b.tickFormat=function(a){return arguments.length?(o=a,b):o},b.color=function(c){return arguments.length?(p=a.utils.getColor(c),b):p},b},a.models.bulletChart=function(){"use strict";function b(d){return d.each(function(n,r){var s=d3.select(this);a.utils.initSVG(s);var t=(j||parseInt(s.style("width"))||960)-f.left-f.right,u=k-f.top-f.bottom,v=this;if(b.update=function(){b(d)},b.container=this,!n||!g.call(this,n,r)){var w=s.selectAll(".nv-noData").data([o]);return w.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),w.attr("x",f.left+t/2).attr("y",18+f.top+u/2).text(function(a){return a}),b}s.selectAll(".nv-noData").remove();var x=g.call(this,n,r).slice().sort(d3.descending),y=h.call(this,n,r).slice().sort(d3.descending),z=i.call(this,n,r).slice().sort(d3.descending),A=s.selectAll("g.nv-wrap.nv-bulletChart").data([n]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-bulletChart"),C=B.append("g"),D=A.select("g");C.append("g").attr("class","nv-bulletWrap"),C.append("g").attr("class","nv-titles"),A.attr("transform","translate("+f.left+","+f.top+")");var E=d3.scale.linear().domain([0,Math.max(x[0],y[0],z[0])]).range(e?[t,0]:[0,t]),F=this.__chart__||d3.scale.linear().domain([0,1/0]).range(E.range());this.__chart__=E;var G=C.select(".nv-titles").append("g").attr("text-anchor","end").attr("transform","translate(-6,"+(k-f.top-f.bottom)/2+")");G.append("text").attr("class","nv-title").text(function(a){return a.title}),G.append("text").attr("class","nv-subtitle").attr("dy","1em").text(function(a){return a.subtitle}),c.width(t).height(u);var H=D.select(".nv-bulletWrap");d3.transition(H).call(c);var I=l||E.tickFormat(t/100),J=D.selectAll("g.nv-tick").data(E.ticks(t/50),function(a){return this.textContent||I(a)}),K=J.enter().append("g").attr("class","nv-tick").attr("transform",function(a){return"translate("+F(a)+",0)"}).style("opacity",1e-6);K.append("line").attr("y1",u).attr("y2",7*u/6),K.append("text").attr("text-anchor","middle").attr("dy","1em").attr("y",7*u/6).text(I);var L=d3.transition(J).attr("transform",function(a){return"translate("+E(a)+",0)"}).style("opacity",1);L.select("line").attr("y1",u).attr("y2",7*u/6),L.select("text").attr("y",7*u/6),d3.transition(J.exit()).attr("transform",function(a){return"translate("+E(a)+",0)"}).style("opacity",1e-6).remove(),p.on("tooltipShow",function(a){a.key=n.title,m&&q(a,v.parentNode)})}),d3.timer.flush(),b}var c=a.models.bullet(),d="left",e=!1,f={top:5,right:40,bottom:20,left:120},g=function(a){return a.ranges},h=function(a){return a.markers?a.markers:[0]},i=function(a){return a.measures},j=null,k=55,l=null,m=!0,n=function(a,b,c){return"

"+b+"

"+c+"

"},o="No Data Available.",p=d3.dispatch("tooltipShow","tooltipHide"),q=function(c,d){var e=c.pos[0]+(d.offsetLeft||0)+f.left,g=c.pos[1]+(d.offsetTop||0)+f.top,h=n(c.key,c.label,c.value,c,b);a.tooltip.show([e,g],h,c.value<0?"e":"w",null,d)};return c.dispatch.on("elementMouseover.tooltip",function(a){p.tooltipShow(a)}),c.dispatch.on("elementMouseout.tooltip",function(a){p.tooltipHide(a)}),p.on("tooltipHide",function(){m&&a.tooltip.cleanup()}),b.dispatch=p,b.bullet=c,d3.rebind(b,c,"color"),b.options=a.utils.optionsFunc.bind(b),b.orient=function(a){return arguments.length?(d=a,e="right"==d||"bottom"==d,b):d},b.ranges=function(a){return arguments.length?(g=a,b):g},b.markers=function(a){return arguments.length?(h=a,b):h},b.measures=function(a){return arguments.length?(i=a,b):i},b.width=function(a){return arguments.length?(j=a,b):j},b.height=function(a){return arguments.length?(k=a,b):k},b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.right="undefined"!=typeof a.right?a.right:f.right,f.bottom="undefined"!=typeof a.bottom?a.bottom:f.bottom,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.tickFormat=function(a){return arguments.length?(l=a,b):l},b.tooltips=function(a){return arguments.length?(m=a,b):m},b.tooltipContent=function(a){return arguments.length?(n=a,b):n},b.noData=function(a){return arguments.length?(o=a,b):o},b},a.models.cumulativeLineChart=function(){"use strict";function b(x){return I.reset(),I.models(f),q&&I.models(g),r&&I.models(h),x.each(function(x){function F(){d3.select(b.container).style("cursor","ew-resize")}function I(){H.x=d3.event.x,H.i=Math.round(G.invert(H.x)),N()}function M(){d3.select(b.container).style("cursor","auto"),z.index=H.i,D.stateChange(z)}function N(){fb.data([H]);var a=b.duration();b.duration(0),b.update(),b.duration(a)}var O=d3.select(this);a.utils.initSVG(O),O.classed("nv-chart-"+y,!0);var P=this,Q=(n||parseInt(O.style("width"))||960)-l.left-l.right,R=(o||parseInt(O.style("height"))||400)-l.top-l.bottom;if(b.update=function(){0===E?O.call(b):O.transition().duration(E).call(b)},b.container=this,z.setter(L(x),b.update).getter(K(x)).update(),z.disabled=x.map(function(a){return!!a.disabled}),!A){var S;A={};for(S in z)A[S]=z[S]instanceof Array?z[S].slice(0):z[S]}var T=d3.behavior.drag().on("dragstart",F).on("drag",I).on("dragend",M);if(!(x&&x.length&&x.filter(function(a){return a.values.length}).length)){var U=O.selectAll(".nv-noData").data([B]);return U.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),U.attr("x",l.left+Q/2).attr("y",l.top+R/2).text(function(a){return a}),b}if(O.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale(),w)f.yDomain(null);else{var V=x.filter(function(a){return!a.disabled}).map(function(a){var b=d3.extent(a.values,f.y());return b[0]<-.95&&(b[0]=-.95),[(b[0]-b[1])/(1+b[1]),(b[1]-b[0])/(1+b[0])]}),W=[d3.min(V,function(a){return a[0]}),d3.max(V,function(a){return a[1]})];f.yDomain(W)}G.domain([0,x[0].values.length-1]).range([0,Q]).clamp(!0);var x=c(H.i,x),X=v?"none":"all",Y=O.selectAll("g.nv-wrap.nv-cumulativeLine").data([x]),Z=Y.enter().append("g").attr("class","nvd3 nv-wrap nv-cumulativeLine").append("g"),$=Y.select("g");if(Z.append("g").attr("class","nv-interactive"),Z.append("g").attr("class","nv-x nv-axis").style("pointer-events","none"),Z.append("g").attr("class","nv-y nv-axis"),Z.append("g").attr("class","nv-background"),Z.append("g").attr("class","nv-linesWrap").style("pointer-events",X),Z.append("g").attr("class","nv-avgLinesWrap").style("pointer-events","none"),Z.append("g").attr("class","nv-legendWrap"),Z.append("g").attr("class","nv-controlsWrap"),p&&(i.width(Q),$.select(".nv-legendWrap").datum(x).call(i),l.top!=i.height()&&(l.top=i.height(),R=(o||parseInt(O.style("height"))||400)-l.top-l.bottom),$.select(".nv-legendWrap").attr("transform","translate(0,"+-l.top+")")),u){var _=[{key:"Re-scale y-axis",disabled:!w}];j.width(140).color(["#444","#444","#444"]).rightAlign(!1).margin({top:5,right:0,bottom:5,left:20}),$.select(".nv-controlsWrap").datum(_).attr("transform","translate(0,"+-l.top+")").call(j) +}Y.attr("transform","translate("+l.left+","+l.top+")"),s&&$.select(".nv-y.nv-axis").attr("transform","translate("+Q+",0)");var ab=x.filter(function(a){return a.tempDisabled});Y.select(".tempDisabled").remove(),ab.length&&Y.append("text").attr("class","tempDisabled").attr("x",Q/2).attr("y","-.71em").style("text-anchor","end").text(ab.map(function(a){return a.key}).join(", ")+" values cannot be calculated for this time period."),v&&(k.width(Q).height(R).margin({left:l.left,top:l.top}).svgContainer(O).xScale(d),Y.select(".nv-interactive").call(k)),Z.select(".nv-background").append("rect"),$.select(".nv-background rect").attr("width",Q).attr("height",R),f.y(function(a){return a.display.y}).width(Q).height(R).color(x.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!x[b].disabled&&!x[b].tempDisabled}));var bb=$.select(".nv-linesWrap").datum(x.filter(function(a){return!a.disabled&&!a.tempDisabled}));bb.call(f),x.forEach(function(a,b){a.seriesIndex=b});var cb=x.filter(function(a){return!a.disabled&&!!C(a)}),db=$.select(".nv-avgLinesWrap").selectAll("line").data(cb,function(a){return a.key}),eb=function(a){var b=e(C(a));return 0>b?0:b>R?R:b};db.enter().append("line").style("stroke-width",2).style("stroke-dasharray","10,10").style("stroke",function(a){return f.color()(a,a.seriesIndex)}).attr("x1",0).attr("x2",Q).attr("y1",eb).attr("y2",eb),db.style("stroke-opacity",function(a){var b=e(C(a));return 0>b||b>R?0:1}).attr("x1",0).attr("x2",Q).attr("y1",eb).attr("y2",eb),db.exit().remove();var fb=bb.selectAll(".nv-indexLine").data([H]);fb.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(T),fb.attr("transform",function(a){return"translate("+G(a.i)+",0)"}).attr("height",R),q&&(g.scale(d).ticks(a.utils.calcTicksX(Q/70,x)).tickSize(-R,0),$.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),$.select(".nv-x.nv-axis").call(g)),r&&(h.scale(e).ticks(a.utils.calcTicksY(R/36,x)).tickSize(-Q,0),$.select(".nv-y.nv-axis").call(h)),$.select(".nv-background rect").on("click",function(){H.x=d3.mouse(this)[0],H.i=Math.round(G.invert(H.x)),z.index=H.i,D.stateChange(z),N()}),f.dispatch.on("elementClick",function(a){H.i=a.pointIndex,H.x=G(H.i),z.index=H.i,D.stateChange(z),N()}),j.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,w=!a.disabled,z.rescaleY=w,D.stateChange(z),b.update()}),i.dispatch.on("stateChange",function(a){for(var c in a)z[c]=a[c];D.stateChange(z),b.update()}),k.dispatch.on("elementMousemove",function(c){f.clearHighlights();var d,e,i,j=[];if(x.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g,h){e=a.interactiveBisect(g.values,c.pointXValue,b.x()),f.highlightPoint(h,e,!0);var k=g.values[e];"undefined"!=typeof k&&("undefined"==typeof d&&(d=k),"undefined"==typeof i&&(i=b.xScale()(b.x()(k,e))),j.push({key:g.key,value:b.y()(k,e),color:m(g,g.seriesIndex)}))}),j.length>2){var n=b.yScale().invert(c.mouseY),o=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),p=.03*o,q=a.nearestValueIndex(j.map(function(a){return a.value}),n,p);null!==q&&(j[q].highlight=!0)}var r=g.tickFormat()(b.x()(d,e),e);k.tooltip.position({left:i+l.left,top:c.mouseY+l.top}).chartContainer(P.parentNode).enabled(t).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:r,series:j})(),k.renderGuideLine(i)}),k.dispatch.on("elementMouseout",function(){D.tooltipHide(),f.clearHighlights()}),D.on("tooltipShow",function(a){t&&J(a,P.parentNode)}),D.on("changeState",function(a){"undefined"!=typeof a.disabled&&(x.forEach(function(b,c){b.disabled=a.disabled[c]}),z.disabled=a.disabled),"undefined"!=typeof a.index&&(H.i=a.index,H.x=G(H.i),z.index=a.index,fb.data([H])),"undefined"!=typeof a.rescaleY&&(w=a.rescaleY),b.update()})}),I.renderEnd("cumulativeLineChart immediate"),b}function c(a,b){return N||(N=f.y()),b.map(function(b){if(!b.values)return b;var c=b.values[a];if(null==c)return b;var d=N(c,a);return-.95>d&&!F?(b.tempDisabled=!0,b):(b.tempDisabled=!1,b.values=b.values.map(function(a,b){return a.display={y:(N(a,b)-d)/(1+d)},a}),b)})}var d,e,f=a.models.line(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.models.legend(),k=a.interactiveGuideline(),l={top:30,right:30,bottom:50,left:60},m=a.utils.defaultColor(),n=null,o=null,p=!0,q=!0,r=!0,s=!1,t=!0,u=!0,v=!1,w=!0,x=function(a,b,c){return"

"+a+"

"+c+" at "+b+"

"},y=f.id(),z=a.utils.state(),A=null,B="No Data Available.",C=function(a){return a.average},D=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),E=250,F=!1;z.index=0,z.rescaleY=w,g.orient("bottom").tickPadding(7),h.orient(s?"right":"left"),j.updateState(!1);var G=d3.scale.linear(),H={i:0,x:0},I=a.utils.renderWatch(D,E),J=function(c,d){var e=c.pos[0]+(d.offsetLeft||0),i=c.pos[1]+(d.offsetTop||0),j=g.tickFormat()(f.x()(c.point,c.pointIndex)),k=h.tickFormat()(f.y()(c.point,c.pointIndex)),l=x(c.series.key,j,k,c,b);a.tooltip.show([e,i],l,null,null,d)},K=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),index:H.i,rescaleY:w}}},L=function(a){return function(b){void 0!==b.index&&(H.i=b.index),void 0!==b.rescaleY&&(w=b.rescaleY),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};f.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+l.left,a.pos[1]+l.top],D.tooltipShow(a)}),f.dispatch.on("elementMouseout.tooltip",function(a){D.tooltipHide(a)}),D.on("tooltipHide",function(){t&&a.tooltip.cleanup()}),b.dispatch=D,b.lines=f,b.legend=i,b.xAxis=g,b.yAxis=h,b.interactiveLayer=k,b.state=z,d3.rebind(b,f,"defined","isArea","x","y","xScale","yScale","size","xDomain","yDomain","xRange","yRange","forceX","forceY","interactive","clipEdge","clipVoronoi","useVoronoi","id"),b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(l.top="undefined"!=typeof a.top?a.top:l.top,l.right="undefined"!=typeof a.right?a.right:l.right,l.bottom="undefined"!=typeof a.bottom?a.bottom:l.bottom,l.left="undefined"!=typeof a.left?a.left:l.left,b):l},b.width=function(a){return arguments.length?(n=a,b):n},b.height=function(a){return arguments.length?(o=a,b):o},b.color=function(c){return arguments.length?(m=a.utils.getColor(c),i.color(m),b):m},b.rescaleY=function(a){return arguments.length?(w=a,b):w},b.showControls=function(a){return arguments.length?(u=a,b):u},b.useInteractiveGuideline=function(a){return arguments.length?(v=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1)),b):v},b.showLegend=function(a){return arguments.length?(p=a,b):p},b.showXAxis=function(a){return arguments.length?(q=a,b):q},b.showYAxis=function(a){return arguments.length?(r=a,b):r},b.rightAlignYAxis=function(a){return arguments.length?(s=a,h.orient(a?"right":"left"),b):s},b.tooltips=function(a){return arguments.length?(t=a,b):t},b.tooltipContent=function(a){return arguments.length?(x=a,b):x},b.state=function(c){return a.deprecated("cululativeLineChart.state"),arguments.length?(z=c,b):z};for(var M in z)b.state[M]=z[M];b.defaultState=function(a){return arguments.length?(A=a,b):A},b.noData=function(a){return arguments.length?(B=a,b):B},b.average=function(a){return arguments.length?(C=a,b):C},b.transitionDuration=function(c){return a.deprecated("cumulativeLineChart.transitionDuration"),b.duration(c)},b.duration=function(a){return arguments.length?(E=a,f.duration(E),g.duration(E),h.duration(E),I.reset(E),b):E},b.noErrorCheck=function(a){return arguments.length?(F=a,b):F};var N=null;return b},a.models.discreteBar=function(){"use strict";function b(l){return x.reset(),l.each(function(b){var l=j-i.left-i.right,w=k-i.top-i.bottom,y=d3.select(this);a.utils.initSVG(y),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var z=c&&d?[]:b.map(function(a){return a.values.map(function(a,b){return{x:o(a,b),y:p(a,b),y0:a.y0}})});m.domain(c||d3.merge(z).map(function(a){return a.x})).rangeBands(e||[0,l],.1),n.domain(d||d3.extent(d3.merge(z).map(function(a){return a.y}).concat(q))),n.range(s?f||[w-(n.domain()[0]<0?12:0),n.domain()[1]>0?12:0]:f||[w,0]),g=g||m,h=h||n.copy().range([n(0),n(0)]);{var A=y.selectAll("g.nv-wrap.nv-discretebar").data([b]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-discretebar"),C=B.append("g");A.select("g")}C.append("g").attr("class","nv-groups"),A.attr("transform","translate("+i.left+","+i.top+")");var D=A.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});D.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),D.exit().watchTransition(x,"discreteBar: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),D.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),D.watchTransition(x,"discreteBar: groups").style("stroke-opacity",1).style("fill-opacity",.75);var E=D.selectAll("g.nv-bar").data(function(a){return a.values});E.exit().remove();var F=E.enter().append("g").attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", "+n(0)+")"}).on("mouseover",function(a,c){d3.select(this).classed("hover",!0),u.elementMouseover({value:p(a,c),point:a,series:b[a.series],pos:[m(o(a,c))+m.rangeBand()*(a.series+.5)/b.length,n(p(a,c))],pointIndex:c,seriesIndex:a.series,e:d3.event})}).on("mouseout",function(a,c){d3.select(this).classed("hover",!1),u.elementMouseout({value:p(a,c),point:a,series:b[a.series],pointIndex:c,seriesIndex:a.series,e:d3.event})}).on("click",function(a,c){u.elementClick({value:p(a,c),point:a,series:b[a.series],pos:[m(o(a,c))+m.rangeBand()*(a.series+.5)/b.length,n(p(a,c))],pointIndex:c,seriesIndex:a.series,e:d3.event}),d3.event.stopPropagation()}).on("dblclick",function(a,c){u.elementDblClick({value:p(a,c),point:a,series:b[a.series],pos:[m(o(a,c))+m.rangeBand()*(a.series+.5)/b.length,n(p(a,c))],pointIndex:c,seriesIndex:a.series,e:d3.event}),d3.event.stopPropagation()});F.append("rect").attr("height",0).attr("width",.9*m.rangeBand()/b.length),s?(F.append("text").attr("text-anchor","middle"),E.select("text").text(function(a,b){return t(p(a,b))}).watchTransition(x,"discreteBar: bars text").attr("x",.9*m.rangeBand()/2).attr("y",function(a,b){return p(a,b)<0?n(p(a,b))-n(0)+12:-4})):E.selectAll("text").remove(),E.attr("class",function(a,b){return p(a,b)<0?"nv-bar negative":"nv-bar positive"}).style("fill",function(a,b){return a.color||r(a,b)}).style("stroke",function(a,b){return a.color||r(a,b)}).select("rect").attr("class",v).watchTransition(x,"discreteBar: bars rect").attr("width",.9*m.rangeBand()/b.length),E.watchTransition(x,"discreteBar: bars").attr("transform",function(a,b){var c=m(o(a,b))+.05*m.rangeBand(),d=p(a,b)<0?n(0):n(0)-n(p(a,b))<1?n(0)-1:n(p(a,b));return"translate("+c+", "+d+")"}).select("rect").attr("height",function(a,b){return Math.max(Math.abs(n(p(a,b))-n(d&&d[0]||0))||1)}),g=m.copy(),h=n.copy()}),x.renderEnd("discreteBar immediate"),b}var c,d,e,f,g,h,i={top:0,right:0,bottom:0,left:0},j=960,k=500,l=Math.floor(1e4*Math.random()),m=d3.scale.ordinal(),n=d3.scale.linear(),o=function(a){return a.x},p=function(a){return a.y},q=[0],r=a.utils.defaultColor(),s=!1,t=d3.format(",.2f"),u=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),v="discreteBar",w=250,x=a.utils.renderWatch(u,w);return b.dispatch=u,b.options=a.utils.optionsFunc.bind(b),b.x=function(a){return arguments.length?(o=a,b):o},b.y=function(a){return arguments.length?(p=a,b):p},b.margin=function(a){return arguments.length?(i.top="undefined"!=typeof a.top?a.top:i.top,i.right="undefined"!=typeof a.right?a.right:i.right,i.bottom="undefined"!=typeof a.bottom?a.bottom:i.bottom,i.left="undefined"!=typeof a.left?a.left:i.left,b):i},b.width=function(a){return arguments.length?(j=a,b):j},b.height=function(a){return arguments.length?(k=a,b):k},b.xScale=function(a){return arguments.length?(m=a,b):m},b.yScale=function(a){return arguments.length?(n=a,b):n},b.xDomain=function(a){return arguments.length?(c=a,b):c},b.yDomain=function(a){return arguments.length?(d=a,b):d},b.xRange=function(a){return arguments.length?(e=a,b):e},b.yRange=function(a){return arguments.length?(f=a,b):f},b.forceY=function(a){return arguments.length?(q=a,b):q},b.color=function(c){return arguments.length?(r=a.utils.getColor(c),b):r},b.id=function(a){return arguments.length?(l=a,b):l},b.showValues=function(a){return arguments.length?(s=a,b):s},b.valueFormat=function(a){return arguments.length?(t=a,b):t},b.rectClass=function(a){return arguments.length?(v=a,b):v},b.duration=function(a){return arguments.length?(w=a,x.reset(w),b):w},b},a.models.discreteBarChart=function(){"use strict";function b(k){return v.reset(),v.models(e),l&&v.models(f),m&&v.models(g),k.each(function(k){var q=d3.select(this),v=this;a.utils.initSVG(q);var w=(i||parseInt(q.style("width"))||960)-h.left-h.right,x=(j||parseInt(q.style("height"))||400)-h.top-h.bottom;if(b.update=function(){s.beforeUpdate(),q.transition().duration(t).call(b)},b.container=this,!(k&&k.length&&k.filter(function(a){return a.values.length}).length)){var y=q.selectAll(".nv-noData").data([r]);return y.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),y.attr("x",h.left+w/2).attr("y",h.top+x/2).text(function(a){return a}),b}q.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var z=q.selectAll("g.nv-wrap.nv-discreteBarWithAxes").data([k]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-discreteBarWithAxes").append("g"),B=A.append("defs"),C=z.select("g");A.append("g").attr("class","nv-x nv-axis"),A.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),A.append("g").attr("class","nv-barsWrap"),C.attr("transform","translate("+h.left+","+h.top+")"),n&&C.select(".nv-y.nv-axis").attr("transform","translate("+w+",0)"),e.width(w).height(x);var D=C.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));if(D.transition().call(e),B.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),C.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(o?2:1)).attr("height",16).attr("x",-c.rangeBand()/(o?1:2)),l){f.scale(c).ticks(a.utils.calcTicksX(w/100,k)).tickSize(-x,0),C.select(".nv-x.nv-axis").attr("transform","translate(0,"+(d.range()[0]+(e.showValues()&&d.domain()[0]<0?16:0))+")"),C.select(".nv-x.nv-axis").call(f);var E=C.select(".nv-x.nv-axis").selectAll("g");o&&E.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}m&&(g.scale(d).ticks(a.utils.calcTicksY(x/36,k)).tickSize(-w,0),C.select(".nv-y.nv-axis").call(g)),C.select(".nv-zeroLine line").attr("x1",0).attr("x2",w).attr("y1",d(0)).attr("y2",d(0)),s.on("tooltipShow",function(a){p&&u(a,v.parentNode)})}),v.renderEnd("discreteBar chart immediate"),b}var c,d,e=a.models.discreteBar(),f=a.models.axis(),g=a.models.axis(),h={top:15,right:10,bottom:50,left:60},i=null,j=null,k=a.utils.getColor(),l=!0,m=!0,n=!1,o=!1,p=!0,q=function(a,b,c){return"

"+b+"

"+c+"

"},r="No Data Available.",s=d3.dispatch("tooltipShow","tooltipHide","beforeUpdate","renderEnd"),t=250;f.orient("bottom").highlightZero(!1).showMaxMin(!1).tickFormat(function(a){return a}),g.orient(n?"right":"left").tickFormat(d3.format(",.1f"));var u=function(c,d){var h=c.pos[0]+(d.offsetLeft||0),i=c.pos[1]+(d.offsetTop||0),j=f.tickFormat()(e.x()(c.point,c.pointIndex)),k=g.tickFormat()(e.y()(c.point,c.pointIndex)),l=q(c.series.key,j,k,c,b);a.tooltip.show([h,i],l,c.value<0?"n":"s",null,d)},v=a.utils.renderWatch(s,t);return e.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+h.left,a.pos[1]+h.top],s.tooltipShow(a)}),e.dispatch.on("elementMouseout.tooltip",function(a){s.tooltipHide(a)}),s.on("tooltipHide",function(){p&&a.tooltip.cleanup()}),b.dispatch=s,b.discretebar=e,b.xAxis=f,b.yAxis=g,d3.rebind(b,e,"x","y","xDomain","yDomain","xRange","yRange","forceX","forceY","id","showValues","valueFormat"),b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(h.top="undefined"!=typeof a.top?a.top:h.top,h.right="undefined"!=typeof a.right?a.right:h.right,h.bottom="undefined"!=typeof a.bottom?a.bottom:h.bottom,h.left="undefined"!=typeof a.left?a.left:h.left,b):h},b.width=function(a){return arguments.length?(i=a,b):i},b.height=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(k=a.utils.getColor(c),e.color(k),b):k},b.showXAxis=function(a){return arguments.length?(l=a,b):l},b.showYAxis=function(a){return arguments.length?(m=a,b):m},b.rightAlignYAxis=function(a){return arguments.length?(n=a,g.orient(a?"right":"left"),b):n},b.staggerLabels=function(a){return arguments.length?(o=a,b):o},b.tooltips=function(a){return arguments.length?(p=a,b):p},b.tooltipContent=function(a){return arguments.length?(q=a,b):q},b.noData=function(a){return arguments.length?(r=a,b):r},b.transitionDuration=function(c){return a.deprecated("discreteBar.transitionDuration"),b.duration(c)},b.duration=function(a){return arguments.length?(t=a,v.reset(t),e.duration(t),f.duration(t),g.duration(t),b):t},b},a.models.distribution=function(){"use strict";function b(k){return m.reset(),k.each(function(b){var k=(e-("x"===g?d.left+d.right:d.top+d.bottom),"x"==g?"y":"x"),l=d3.select(this);a.utils.initSVG(l),c=c||j;var n=l.selectAll("g.nv-distribution").data([b]),o=n.enter().append("g").attr("class","nvd3 nv-distribution"),p=(o.append("g"),n.select("g"));n.attr("transform","translate("+d.left+","+d.top+")");var q=p.selectAll("g.nv-dist").data(function(a){return a},function(a){return a.key});q.enter().append("g"),q.attr("class",function(a,b){return"nv-dist nv-series-"+b}).style("stroke",function(a,b){return i(a,b)});var r=q.selectAll("line.nv-dist"+g).data(function(a){return a.values});r.enter().append("line").attr(g+"1",function(a,b){return c(h(a,b))}).attr(g+"2",function(a,b){return c(h(a,b))}),m.transition(q.exit().selectAll("line.nv-dist"+g),"dist exit").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}).style("stroke-opacity",0).remove(),r.attr("class",function(a,b){return"nv-dist"+g+" nv-dist"+g+"-"+b}).attr(k+"1",0).attr(k+"2",f),m.transition(r,"dist").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}),c=j.copy()}),m.renderEnd("distribution immediate"),b}var c,d={top:0,right:0,bottom:0,left:0},e=400,f=8,g="x",h=function(a){return a[g]},i=a.utils.defaultColor(),j=d3.scale.linear(),k=250,l=d3.dispatch("renderEnd"),m=a.utils.renderWatch(l,k);return b.options=a.utils.optionsFunc.bind(b),b.dispatch=l,b.margin=function(a){return arguments.length?(d.top="undefined"!=typeof a.top?a.top:d.top,d.right="undefined"!=typeof a.right?a.right:d.right,d.bottom="undefined"!=typeof a.bottom?a.bottom:d.bottom,d.left="undefined"!=typeof a.left?a.left:d.left,b):d},b.width=function(a){return arguments.length?(e=a,b):e},b.axis=function(a){return arguments.length?(g=a,b):g},b.size=function(a){return arguments.length?(f=a,b):f},b.getData=function(a){return arguments.length?(h=d3.functor(a),b):h},b.scale=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(i=a.utils.getColor(c),b):i},b.duration=function(a){return arguments.length?(k=a,m.reset(k),b):k},b},a.models.historicalBar=function(){"use strict";function b(w){return w.each(function(b){v.reset();var w=h-g.left-g.right,x=i-g.top-g.bottom,y=d3.select(this);a.utils.initSVG(y),k.domain(c||d3.extent(b[0].values.map(m).concat(o))),k.range(q?e||[.5*w/b[0].values.length,w*(b[0].values.length-.5)/b[0].values.length]:e||[0,w]),l.domain(d||d3.extent(b[0].values.map(n).concat(p))).range(f||[x,0]),k.domain()[0]===k.domain()[1]&&k.domain(k.domain()[0]?[k.domain()[0]-.01*k.domain()[0],k.domain()[1]+.01*k.domain()[1]]:[-1,1]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]+.01*l.domain()[0],l.domain()[1]-.01*l.domain()[1]]:[-1,1]);var z=y.selectAll("g.nv-wrap.nv-historicalBar-"+j).data([b[0].values]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBar-"+j),B=A.append("defs"),C=A.append("g"),D=z.select("g");C.append("g").attr("class","nv-bars"),z.attr("transform","translate("+g.left+","+g.top+")"),y.on("click",function(a,b){t.chartClick({data:a,index:b,pos:d3.event,id:j})}),B.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),z.select("#nv-chart-clip-path-"+j+" rect").attr("width",w).attr("height",x),D.attr("clip-path",r?"url(#nv-chart-clip-path-"+j+")":"");var E=z.select(".nv-bars").selectAll(".nv-bar").data(function(a){return a},function(a,b){return m(a,b)});E.exit().remove();E.enter().append("rect").attr("x",0).attr("y",function(b,c){return a.utils.NaNtoZero(l(Math.max(0,n(b,c))))}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.abs(l(n(b,c))-l(0)))}).attr("transform",function(a,c){return"translate("+(k(m(a,c))-w/b[0].values.length*.45)+",0)"}).on("mouseover",function(a,c){u&&(d3.select(this).classed("hover",!0),t.elementMouseover({point:a,series:b[0],pos:[k(m(a,c)),l(n(a,c))],pointIndex:c,seriesIndex:0,e:d3.event}))}).on("mouseout",function(a,c){u&&(d3.select(this).classed("hover",!1),t.elementMouseout({point:a,series:b[0],pointIndex:c,seriesIndex:0,e:d3.event}))}).on("click",function(a,b){u&&(t.elementClick({value:n(a,b),data:a,index:b,pos:[k(m(a,b)),l(n(a,b))],e:d3.event,id:j}),d3.event.stopPropagation())}).on("dblclick",function(a,b){u&&(t.elementDblClick({value:n(a,b),data:a,index:b,pos:[k(m(a,b)),l(n(a,b))],e:d3.event,id:j}),d3.event.stopPropagation())});E.attr("fill",function(a,b){return s(a,b)}).attr("class",function(a,b,c){return(n(a,b)<0?"nv-bar negative":"nv-bar positive")+" nv-bar-"+c+"-"+b}).watchTransition(v,"bars").attr("transform",function(a,c){return"translate("+(k(m(a,c))-w/b[0].values.length*.45)+",0)"}).attr("width",w/b[0].values.length*.9),E.watchTransition(v,"bars").attr("y",function(b,c){var d=n(b,c)<0?l(0):l(0)-l(n(b,c))<1?l(0)-1:l(n(b,c));return a.utils.NaNtoZero(d)}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.max(Math.abs(l(n(b,c))-l(0)),1))})}),v.renderEnd("historicalBar immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=960,i=500,j=Math.floor(1e4*Math.random()),k=d3.scale.linear(),l=d3.scale.linear(),m=function(a){return a.x},n=function(a){return a.y},o=[],p=[0],q=!1,r=!0,s=a.utils.defaultColor(),t=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),u=!0,v=a.utils.renderWatch(t,0);return b.highlightPoint=function(a,b){d3.select(".nv-historicalBar-"+j).select(".nv-bars .nv-bar-0-"+a).classed("hover",b)},b.clearHighlights=function(){d3.select(".nv-historicalBar-"+j).select(".nv-bars .nv-bar.hover").classed("hover",!1)},b.dispatch=t,b.options=a.utils.optionsFunc.bind(b),b.x=function(a){return arguments.length?(m=a,b):m},b.y=function(a){return arguments.length?(n=a,b):n},b.margin=function(a){return arguments.length?(g.top="undefined"!=typeof a.top?a.top:g.top,g.right="undefined"!=typeof a.right?a.right:g.right,g.bottom="undefined"!=typeof a.bottom?a.bottom:g.bottom,g.left="undefined"!=typeof a.left?a.left:g.left,b):g},b.width=function(a){return arguments.length?(h=a,b):h},b.height=function(a){return arguments.length?(i=a,b):i},b.xScale=function(a){return arguments.length?(k=a,b):k},b.yScale=function(a){return arguments.length?(l=a,b):l},b.xDomain=function(a){return arguments.length?(c=a,b):c},b.yDomain=function(a){return arguments.length?(d=a,b):d},b.xRange=function(a){return arguments.length?(e=a,b):e},b.yRange=function(a){return arguments.length?(f=a,b):f},b.forceX=function(a){return arguments.length?(o=a,b):o},b.forceY=function(a){return arguments.length?(p=a,b):p},b.padData=function(a){return arguments.length?(q=a,b):q},b.clipEdge=function(a){return arguments.length?(r=a,b):r},b.color=function(c){return arguments.length?(s=a.utils.getColor(c),b):s},b.id=function(a){return arguments.length?(j=a,b):j},b.interactive=function(){return arguments.length?(u=!1,b):u},b},a.models.historicalBarChart=function(){"use strict";function b(t){return t.each(function(B){A.reset(),A.models(e),o&&A.models(f),p&&A.models(g);var C=d3.select(this),D=this;a.utils.initSVG(C);var E=(l||parseInt(C.style("width"))||960)-j.left-j.right,F=(m||parseInt(C.style("height"))||400)-j.top-j.bottom;if(b.update=function(){C.transition().duration(y).call(b)},b.container=this,u.disabled=B.map(function(a){return!!a.disabled}),!v){var G;v={};for(G in u)v[G]=u[G]instanceof Array?u[G].slice(0):u[G]}if(!(B&&B.length&&B.filter(function(a){return a.values.length}).length)){var H=C.selectAll(".nv-noData").data([w]);return H.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),H.attr("x",j.left+E/2).attr("y",j.top+F/2).text(function(a){return a}),b}C.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var I=C.selectAll("g.nv-wrap.nv-historicalBarChart").data([B]),J=I.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBarChart").append("g"),K=I.select("g");J.append("g").attr("class","nv-x nv-axis"),J.append("g").attr("class","nv-y nv-axis"),J.append("g").attr("class","nv-barsWrap"),J.append("g").attr("class","nv-legendWrap"),J.append("g").attr("class","nv-interactive"),n&&(h.width(E),K.select(".nv-legendWrap").datum(B).call(h),j.top!=h.height()&&(j.top=h.height(),F=(m||parseInt(C.style("height"))||400)-j.top-j.bottom),I.select(".nv-legendWrap").attr("transform","translate(0,"+-j.top+")")),I.attr("transform","translate("+j.left+","+j.top+")"),q&&K.select(".nv-y.nv-axis").attr("transform","translate("+E+",0)"),r&&(i.width(E).height(F).margin({left:j.left,top:j.top}).svgContainer(C).xScale(c),I.select(".nv-interactive").call(i)),e.width(E).height(F).color(B.map(function(a,b){return a.color||k(a,b)}).filter(function(a,b){return!B[b].disabled}));var L=K.select(".nv-barsWrap").datum(B.filter(function(a){return!a.disabled}));L.transition().call(e),o&&(f.scale(c).tickSize(-F,0),K.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),K.select(".nv-x.nv-axis").transition().call(f)),p&&(g.scale(d).ticks(a.utils.calcTicksY(F/36,B)).tickSize(-E,0),K.select(".nv-y.nv-axis").transition().call(g)),i.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,l,m=[];B.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f){h=a.interactiveBisect(f.values,c.pointXValue,b.x()),e.highlightPoint(h,!0);var g=f.values[h];"undefined"!=typeof g&&("undefined"==typeof d&&(d=g),"undefined"==typeof l&&(l=b.xScale()(b.x()(g,h))),m.push({key:f.key,value:b.y()(g,h),color:k(f,f.seriesIndex)}))});var n=f.tickFormat()(b.x()(d,h));i.tooltip.position({left:l+j.left,top:c.mouseY+j.top}).chartContainer(D.parentNode).enabled(s).valueFormatter(function(a){return g.tickFormat()(a)}).data({value:n,series:m})(),i.renderGuideLine(l)}),i.dispatch.on("elementMouseout",function(){x.tooltipHide(),e.clearHighlights()}),h.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,B.filter(function(a){return!a.disabled}).length||B.map(function(a){return a.disabled=!1,I.selectAll(".nv-series").classed("disabled",!1),a}),u.disabled=B.map(function(a){return!!a.disabled}),x.stateChange(u),t.transition().call(b)}),h.dispatch.on("legendDblclick",function(a){B.forEach(function(a){a.disabled=!0}),a.disabled=!1,u.disabled=B.map(function(a){return!!a.disabled}),x.stateChange(u),b.update()}),x.on("tooltipShow",function(a){s&&z(a,D.parentNode)}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(B.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),b.update()})}),A.renderEnd("historicalBarChart immediate"),b}var c,d,e=a.models.historicalBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.interactiveGuideline(),j={top:30,right:90,bottom:50,left:90},k=a.utils.defaultColor(),l=null,m=null,n=!1,o=!0,p=!0,q=!1,r=!1,s=!0,t=function(a,b,c){return"

"+a+"

"+c+" at "+b+"

"},u={},v=null,w="No Data Available.",x=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),y=250;f.orient("bottom").tickPadding(7),g.orient(q?"right":"left");var z=function(c,d){if(d){var h=d3.select(d).select("svg"),i=h.node()?h.attr("viewBox"):null;if(i){i=i.split(" ");var j=parseInt(h.style("width"))/i[2];c.pos[0]=c.pos[0]*j,c.pos[1]=c.pos[1]*j}}var k=c.pos[0]+(d.offsetLeft||0),l=c.pos[1]+(d.offsetTop||0),m=f.tickFormat()(e.x()(c.point,c.pointIndex)),n=g.tickFormat()(e.y()(c.point,c.pointIndex)),o=t(c.series.key,m,n,c,b);a.tooltip.show([k,l],o,null,null,d)},A=a.utils.renderWatch(x,0);return e.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+j.left,a.pos[1]+j.top],x.tooltipShow(a)}),e.dispatch.on("elementMouseout.tooltip",function(a){x.tooltipHide(a)}),x.on("tooltipHide",function(){s&&a.tooltip.cleanup()}),b.dispatch=x,b.bars=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.interactiveLayer=i,d3.rebind(b,e,"defined","isArea","x","y","size","xScale","yScale","xDomain","yDomain","xRange","yRange","forceX","forceY","interactive","clipEdge","clipVoronoi","id","interpolate","highlightPoint","clearHighlights"),b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(j.top="undefined"!=typeof a.top?a.top:j.top,j.right="undefined"!=typeof a.right?a.right:j.right,j.bottom="undefined"!=typeof a.bottom?a.bottom:j.bottom,j.left="undefined"!=typeof a.left?a.left:j.left,b):j},b.width=function(a){return arguments.length?(l=a,b):l},b.height=function(a){return arguments.length?(m=a,b):m},b.color=function(c){return arguments.length?(k=a.utils.getColor(c),h.color(k),b):k},b.showLegend=function(a){return arguments.length?(n=a,b):n},b.showXAxis=function(a){return arguments.length?(o=a,b):o},b.showYAxis=function(a){return arguments.length?(p=a,b):p},b.rightAlignYAxis=function(a){return arguments.length?(q=a,g.orient(a?"right":"left"),b):q},b.tooltips=function(a){return arguments.length?(s=a,b):s},b.tooltipContent=function(a){return arguments.length?(t=a,b):t},b.state=function(a){return arguments.length?(u=a,b):u},b.defaultState=function(a){return arguments.length?(v=a,b):v},b.noData=function(a){return arguments.length?(w=a,b):w},b.transitionDuration=function(a){return arguments.length?(y=a,b):y},b.useInteractiveGuideline=function(a){return arguments.length?(r=a,a===!0&&b.interactive(!1),b):r},b},a.models.legend=function(){"use strict";function b(m){return m.each(function(b){var m=d-c.left-c.right,n=d3.select(this);a.utils.initSVG(n);var o=n.selectAll("g.nv-legend").data([b]),p=(o.enter().append("g").attr("class","nvd3 nv-legend").append("g"),o.select("g"));o.attr("transform","translate("+c.left+","+c.top+")");var q=p.selectAll(".nv-series").data(function(a){return a}),r=q.enter().append("g").attr("class","nv-series").on("mouseover",function(a,b){l.legendMouseover(a,b)}).on("mouseout",function(a,b){l.legendMouseout(a,b)}).on("click",function(a,c){l.legendClick(a,c),j&&(k?(b.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,b.every(function(a){return a.disabled})&&b.forEach(function(a){a.disabled=!1})),l.stateChange({disabled:b.map(function(a){return!!a.disabled})}))}).on("dblclick",function(a,c){l.legendDblclick(a,c),j&&(b.forEach(function(a){a.disabled=!0}),a.disabled=!1,l.stateChange({disabled:b.map(function(a){return!!a.disabled})}))});if(r.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),r.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8"),q.classed("disabled",function(a){return a.disabled}),q.exit().remove(),q.select("circle").style("fill",function(a,b){return a.color||g(a,b)}).style("stroke",function(a,b){return a.color||g(a,b)}),q.select("text").text(f),h){var s=[];q.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}s.push(b+28)});for(var t=0,u=0,v=[];m>u&&tm&&t>1;){v=[],t--;for(var w=0;w(v[w%t]||0)&&(v[w%t]=s[w]);u=v.reduce(function(a,b){return a+b})}for(var x=[],y=0,z=0;t>y;y++)x[y]=z,z+=v[y];q.attr("transform",function(a,b){return"translate("+x[b%t]+","+(5+20*Math.floor(b/t))+")"}),i?p.attr("transform","translate("+(d-c.right-u)+","+c.top+")"):p.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+20*Math.ceil(s.length/t)}else{var A,B=5,C=5,D=0;q.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+28;return A=C,dD&&(D=C),"translate("+A+","+B+")"}),p.attr("transform","translate("+(d-c.right-D)+","+c.top+")"),e=c.top+c.bottom+B+15}}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.defaultColor(),h=!0,i=!0,j=!0,k=!1,l=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange");return b.dispatch=l,b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left,b):c},b.width=function(a){return arguments.length?(d=a,b):d},b.height=function(a){return arguments.length?(e=a,b):e},b.key=function(a){return arguments.length?(f=a,b):f},b.color=function(c){return arguments.length?(g=a.utils.getColor(c),b):g},b.align=function(a){return arguments.length?(h=a,b):h},b.rightAlign=function(a){return arguments.length?(i=a,b):i},b.updateState=function(a){return arguments.length?(j=a,b):j},b.radioButtonMode=function(a){return arguments.length?(k=a,b):k},b},a.models.line=function(){"use strict";function b(p){return t.reset(),t.models(e),p.each(function(b){var p=g-f.left-f.right,q=h-f.top-f.bottom,u=d3.select(this);a.utils.initSVG(u),c=e.xScale(),d=e.yScale(),r=r||c,s=s||d;var v=u.selectAll("g.nv-wrap.nv-line").data([b]),w=v.enter().append("g").attr("class","nvd3 nv-wrap nv-line"),x=w.append("defs"),y=w.append("g"),z=v.select("g");y.append("g").attr("class","nv-groups"),y.append("g").attr("class","nv-scatterWrap"),v.attr("transform","translate("+f.left+","+f.top+")"),e.width(p).height(q);var A=v.select(".nv-scatterWrap");A.call(e),x.append("clipPath").attr("id","nv-edge-clip-"+e.id()).append("rect"),v.select("#nv-edge-clip-"+e.id()+" rect").attr("width",p).attr("height",q>0?q:0),z.attr("clip-path",n?"url(#nv-edge-clip-"+e.id()+")":""),A.attr("clip-path",n?"url(#nv-edge-clip-"+e.id()+")":"");var B=v.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});B.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),B.exit().remove(),B.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return i(a,b)}).style("stroke",function(a,b){return i(a,b)}),B.watchTransition(t,"line: groups").style("stroke-opacity",1).style("fill-opacity",.5);var C=B.selectAll("path.nv-area").data(function(a){return m(a)?[a]:[]});C.enter().append("path").attr("class","nv-area").attr("d",function(b){return d3.svg.area().interpolate(o).defined(l).x(function(b,c){return a.utils.NaNtoZero(r(j(b,c)))}).y0(function(b,c){return a.utils.NaNtoZero(s(k(b,c)))}).y1(function(){return s(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])}),B.exit().selectAll("path.nv-area").remove(),C.watchTransition(t,"line: areaPaths").attr("d",function(b){return d3.svg.area().interpolate(o).defined(l).x(function(b,d){return a.utils.NaNtoZero(c(j(b,d)))}).y0(function(b,c){return a.utils.NaNtoZero(d(k(b,c)))}).y1(function(){return d(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])});var D=B.selectAll("path.nv-line").data(function(a){return[a.values]});D.enter().append("path").attr("class","nv-line").attr("d",d3.svg.line().interpolate(o).defined(l).x(function(b,c){return a.utils.NaNtoZero(r(j(b,c)))}).y(function(b,c){return a.utils.NaNtoZero(s(k(b,c)))})),D.watchTransition(t,"line: linePaths").attr("d",d3.svg.line().interpolate(o).defined(l).x(function(b,d){return a.utils.NaNtoZero(c(j(b,d)))}).y(function(b,c){return a.utils.NaNtoZero(d(k(b,c)))})),r=c.copy(),s=d.copy()}),t.renderEnd("line immediate"),b}var c,d,e=a.models.scatter(),f={top:0,right:0,bottom:0,left:0},g=960,h=500,i=a.utils.defaultColor(),j=function(a){return a.x},k=function(a){return a.y},l=function(a,b){return!isNaN(k(a,b))&&null!==k(a,b)},m=function(a){return a.area},n=!1,o="linear",p=250,q=d3.dispatch("elementClick","elementMouseover","elementMouseout","renderEnd");e.size(16).sizeDomain([16,256]);var r,s,t=a.utils.renderWatch(q,p);return b.dispatch=q,b.scatter=e,e.dispatch.on("elementClick",function(){q.elementClick.apply(this,arguments)}),e.dispatch.on("elementMouseover",function(){q.elementMouseover.apply(this,arguments)}),e.dispatch.on("elementMouseout",function(){q.elementMouseout.apply(this,arguments)}),d3.rebind(b,e,"id","interactive","size","xScale","yScale","zScale","xDomain","yDomain","xRange","yRange","sizeDomain","forceX","forceY","forceSize","clipVoronoi","useVoronoi","clipRadius","padData","highlightPoint","clearHighlights"),b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.right="undefined"!=typeof a.right?a.right:f.right,f.bottom="undefined"!=typeof a.bottom?a.bottom:f.bottom,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.width=function(a){return arguments.length?(g=a,b):g},b.height=function(a){return arguments.length?(h=a,b):h},b.x=function(a){return arguments.length?(j=a,e.x(a),b):j},b.y=function(a){return arguments.length?(k=a,e.y(a),b):k},b.clipEdge=function(a){return arguments.length?(n=a,b):n},b.color=function(c){return arguments.length?(i=a.utils.getColor(c),e.color(i),b):i},b.interpolate=function(a){return arguments.length?(o=a,b):o},b.defined=function(a){return arguments.length?(l=a,b):l},b.isArea=function(a){return arguments.length?(m=d3.functor(a),b):m},b.duration=function(a){return arguments.length?(p=a,t.reset(p),e.duration(p),b):p},b},a.models.lineChart=function(){"use strict";function b(t){return A.reset(),A.models(e),o&&A.models(f),p&&A.models(g),t.each(function(t){var A=d3.select(this),D=this;a.utils.initSVG(A);var E=(l||parseInt(A.style("width"))||960)-j.left-j.right,F=(m||parseInt(A.style("height"))||400)-j.top-j.bottom;if(b.update=function(){0===y?A.call(b):A.transition().duration(y).call(b)},b.container=this,u.setter(C(t),b.update).getter(B(t)).update(),u.disabled=t.map(function(a){return!!a.disabled}),!v){var G;v={};for(G in u)v[G]=u[G]instanceof Array?u[G].slice(0):u[G]}if(!(t&&t.length&&t.filter(function(a){return a.values.length}).length)){var H=A.selectAll(".nv-noData").data([w]);return H.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),H.attr("x",j.left+E/2).attr("y",j.top+F/2).text(function(a){return a}),b}A.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var I=A.selectAll("g.nv-wrap.nv-lineChart").data([t]),J=I.enter().append("g").attr("class","nvd3 nv-wrap nv-lineChart").append("g"),K=I.select("g");J.append("rect").style("opacity",0),J.append("g").attr("class","nv-x nv-axis"),J.append("g").attr("class","nv-y nv-axis"),J.append("g").attr("class","nv-linesWrap"),J.append("g").attr("class","nv-legendWrap"),J.append("g").attr("class","nv-interactive"),K.select("rect").attr("width",E).attr("height",F>0?F:0),n&&(h.width(E),K.select(".nv-legendWrap").datum(t).call(h),j.top!=h.height()&&(j.top=h.height(),F=(m||parseInt(A.style("height"))||400)-j.top-j.bottom),I.select(".nv-legendWrap").attr("transform","translate(0,"+-j.top+")")),I.attr("transform","translate("+j.left+","+j.top+")"),q&&K.select(".nv-y.nv-axis").attr("transform","translate("+E+",0)"),r&&(i.width(E).height(F).margin({left:j.left,top:j.top}).svgContainer(A).xScale(c),I.select(".nv-interactive").call(i)),e.width(E).height(F).color(t.map(function(a,b){return a.color||k(a,b)}).filter(function(a,b){return!t[b].disabled}));var L=K.select(".nv-linesWrap").datum(t.filter(function(a){return!a.disabled}));L.call(e),o&&(f.scale(c).ticks(a.utils.calcTicksX(E/100,t)).tickSize(-F,0),K.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),K.select(".nv-x.nv-axis").call(f)),p&&(g.scale(d).ticks(a.utils.calcTicksY(F/36,t)).tickSize(-E,0),K.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),i.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,l,m=[];if(t.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,g){h=a.interactiveBisect(f.values,c.pointXValue,b.x()),e.highlightPoint(g,h,!0);var i=f.values[h];"undefined"!=typeof i&&("undefined"==typeof d&&(d=i),"undefined"==typeof l&&(l=b.xScale()(b.x()(i,h))),m.push({key:f.key,value:b.y()(i,h),color:k(f,f.seriesIndex)}))}),m.length>2){var n=b.yScale().invert(c.mouseY),o=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),p=.03*o,q=a.nearestValueIndex(m.map(function(a){return a.value}),n,p);null!==q&&(m[q].highlight=!0)}var r=f.tickFormat()(b.x()(d,h));i.tooltip.position({left:l+j.left,top:c.mouseY+j.top}).chartContainer(D.parentNode).enabled(s).valueFormatter(function(a){return g.tickFormat()(a)}).data({value:r,series:m})(),i.renderGuideLine(l)}),i.dispatch.on("elementClick",function(c){var d,f=[];t.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(e){var g=a.interactiveBisect(e.values,c.pointXValue,b.x()),h=e.values[g];if("undefined"!=typeof h){"undefined"==typeof d&&(d=b.xScale()(b.x()(h,g)));var i=b.yScale()(b.y()(h,g));f.push({point:h,pointIndex:g,pos:[d,i],seriesIndex:e.seriesIndex,series:e})}}),e.dispatch.elementClick(f)}),i.dispatch.on("elementMouseout",function(){x.tooltipHide(),e.clearHighlights()}),x.on("tooltipShow",function(a){s&&z(a,D.parentNode)}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&t.length===a.disabled.length&&(t.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),b.update()})}),A.renderEnd("lineChart immediate"),b}var c,d,e=a.models.line(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.interactiveGuideline(),j={top:30,right:20,bottom:50,left:60},k=a.utils.defaultColor(),l=null,m=null,n=!0,o=!0,p=!0,q=!1,r=!1,s=!0,t=function(a,b,c){return"

"+a+"

"+c+" at "+b+"

"},u=a.utils.state(),v=null,w="No Data Available.",x=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),y=250;f.orient("bottom").tickPadding(7),g.orient(q?"right":"left");var z=function(c,d){var h=c.pos[0]+(d.offsetLeft||0),i=c.pos[1]+(d.offsetTop||0),j=f.tickFormat()(e.x()(c.point,c.pointIndex)),k=g.tickFormat()(e.y()(c.point,c.pointIndex)),l=t(c.series.key,j,k,c,b);a.tooltip.show([h,i],l,null,null,d)},A=a.utils.renderWatch(x,y),B=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},C=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};e.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+j.left,a.pos[1]+j.top],x.tooltipShow(a)}),e.dispatch.on("elementMouseout.tooltip",function(a){x.tooltipHide(a)}),x.on("tooltipHide",function(){s&&a.tooltip.cleanup()}),b.dispatch=x,b.lines=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.interactiveLayer=i,b.state=u,d3.rebind(b,e,"defined","isArea","x","y","size","xScale","yScale","xDomain","yDomain","xRange","yRange","forceX","forceY","interactive","clipEdge","clipVoronoi","useVoronoi","id","interpolate"),b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(j.top="undefined"!=typeof a.top?a.top:j.top,j.right="undefined"!=typeof a.right?a.right:j.right,j.bottom="undefined"!=typeof a.bottom?a.bottom:j.bottom,j.left="undefined"!=typeof a.left?a.left:j.left,b):j},b.width=function(a){return arguments.length?(l=a,b):l},b.height=function(a){return arguments.length?(m=a,b):m},b.color=function(c){return arguments.length?(k=a.utils.getColor(c),h.color(k),b):k},b.showLegend=function(a){return arguments.length?(n=a,b):n},b.showXAxis=function(a){return arguments.length?(o=a,b):o},b.showYAxis=function(a){return arguments.length?(p=a,b):p},b.rightAlignYAxis=function(a){return arguments.length?(q=a,g.orient(a?"right":"left"),b):q},b.useInteractiveGuideline=function(a){return arguments.length?(r=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1)),b):r},b.tooltips=function(a){return arguments.length?(s=a,b):s},b.tooltipContent=function(a){return arguments.length?(t=a,b):t},b.state=function(c){return a.deprecated("lineChart.state"),arguments.length?(u=c,b):u};for(var D in u)b.state[D]=u[D];return b.defaultState=function(a){return arguments.length?(v=a,b):v},b.noData=function(a){return arguments.length?(w=a,b):w},b.transitionDuration=function(c){return a.deprecated("lineChart.transitionDuration"),b.duration(c)},b.duration=function(a){return arguments.length?(y=a,A.reset(y),e.duration(y),f.duration(y),g.duration(y),b):y},b},a.models.linePlusBarChart=function(){"use strict";function b(o){return o.each(function(o){var p=d3.select(this),t=this;a.utils.initSVG(p);var B=(m||parseInt(p.style("width"))||960)-l.left-l.right,C=(n||parseInt(p.style("height"))||400)-l.top-l.bottom;if(b.update=function(){p.transition().call(b)},u.setter(A(o),b.update).getter(z(o)).update(),u.disabled=o.map(function(a){return!!a.disabled}),!v){var D;v={};for(D in u)v[D]=u[D]instanceof Array?u[D].slice(0):u[D]}if(!(o&&o.length&&o.filter(function(a){return a.values.length}).length)){var E=p.selectAll(".nv-noData").data([w]);return E.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),E.attr("x",l.left+B/2).attr("y",l.top+C/2).text(function(a){return a}),b}p.selectAll(".nv-noData").remove();var F=o.filter(function(a){return!a.disabled&&a.bar}),G=o.filter(function(a){return!a.bar});c=G.filter(function(a){return!a.disabled}).length&&G.filter(function(a){return!a.disabled})[0].values.length?f.xScale():g.xScale(),d=g.yScale(),e=f.yScale();var H=d3.select(this).selectAll("g.nv-wrap.nv-linePlusBar").data([o]),I=H.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g"),J=H.select("g");I.append("g").attr("class","nv-x nv-axis"),I.append("g").attr("class","nv-y1 nv-axis"),I.append("g").attr("class","nv-y2 nv-axis"),I.append("g").attr("class","nv-barsWrap"),I.append("g").attr("class","nv-linesWrap"),I.append("g").attr("class","nv-legendWrap"),r&&(k.width(B/2),J.select(".nv-legendWrap").datum(o.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(a.bar?" (left axis)":" (right axis)"),a})).call(k),l.top!=k.height()&&(l.top=k.height(),C=(n||parseInt(p.style("height"))||400)-l.top-l.bottom),J.select(".nv-legendWrap").attr("transform","translate("+B/2+","+-l.top+")")),H.attr("transform","translate("+l.left+","+l.top+")"),f.width(B).height(C).color(o.map(function(a,b){return a.color||q(a,b)}).filter(function(a,b){return!o[b].disabled&&!o[b].bar})),g.width(B).height(C).color(o.map(function(a,b){return a.color||q(a,b)}).filter(function(a,b){return!o[b].disabled&&o[b].bar}));var K=J.select(".nv-barsWrap").datum(F.length?F:[{values:[]}]),L=J.select(".nv-linesWrap").datum(G[0]&&!G[0].disabled?G:[{values:[]}]);d3.transition(K).call(g),d3.transition(L).call(f),h.scale(c).ticks(a.utils.calcTicksX(B/100,o)).tickSize(-C,0),J.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),d3.transition(J.select(".nv-x.nv-axis")).call(h),i.scale(d).ticks(a.utils.calcTicksY(C/36,o)).tickSize(-B,0),d3.transition(J.select(".nv-y1.nv-axis")).style("opacity",F.length?1:0).call(i),j.scale(e).ticks(a.utils.calcTicksY(C/36,o)).tickSize(F.length?0:-B,0),J.select(".nv-y2.nv-axis").style("opacity",G.length?1:0).attr("transform","translate("+B+",0)"),d3.transition(J.select(".nv-y2.nv-axis")).call(j),k.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),x.on("tooltipShow",function(a){s&&y(a,t.parentNode)}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(o.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),b.update()})}),b}var c,d,e,f=a.models.line(),g=a.models.historicalBar(),h=a.models.axis(),i=a.models.axis(),j=a.models.axis(),k=a.models.legend(),l={top:30,right:60,bottom:50,left:60},m=null,n=null,o=function(a){return a.x},p=function(a){return a.y},q=a.utils.defaultColor(),r=!0,s=!0,t=function(a,b,c){return"

"+a+"

"+c+" at "+b+"

"},u=a.utils.state(),v=null,w="No Data Available.",x=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState");g.padData(!0),f.clipEdge(!1).padData(!0),h.orient("bottom").tickPadding(7).highlightZero(!1),i.orient("left"),j.orient("right");var y=function(c,d){var e=c.pos[0]+(d.offsetLeft||0),g=c.pos[1]+(d.offsetTop||0),k=h.tickFormat()(f.x()(c.point,c.pointIndex)),l=(c.series.bar?i:j).tickFormat()(f.y()(c.point,c.pointIndex)),m=t(c.series.key,k,l,c,b);a.tooltip.show([e,g],m,c.value<0?"n":"s",null,d)},z=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},A=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};f.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+l.left,a.pos[1]+l.top],x.tooltipShow(a)}),f.dispatch.on("elementMouseout.tooltip",function(a){x.tooltipHide(a)}),g.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+l.left,a.pos[1]+l.top],x.tooltipShow(a)}),g.dispatch.on("elementMouseout.tooltip",function(a){x.tooltipHide(a)}),x.on("tooltipHide",function(){s&&a.tooltip.cleanup()}),b.dispatch=x,b.legend=k,b.lines=f,b.bars=g,b.xAxis=h,b.y1Axis=i,b.y2Axis=j,b.state=u,d3.rebind(b,f,"defined","size","clipVoronoi","interpolate"),b.options=a.utils.optionsFunc.bind(b),b.x=function(a){return arguments.length?(o=a,f.x(a),g.x(a),b):o},b.y=function(a){return arguments.length?(p=a,f.y(a),g.y(a),b):p},b.margin=function(a){return arguments.length?(l.top="undefined"!=typeof a.top?a.top:l.top,l.right="undefined"!=typeof a.right?a.right:l.right,l.bottom="undefined"!=typeof a.bottom?a.bottom:l.bottom,l.left="undefined"!=typeof a.left?a.left:l.left,b):l},b.width=function(a){return arguments.length?(m=a,b):m},b.height=function(a){return arguments.length?(n=a,b):n},b.color=function(c){return arguments.length?(q=a.utils.getColor(c),k.color(q),b):q},b.showLegend=function(a){return arguments.length?(r=a,b):r},b.tooltips=function(a){return arguments.length?(s=a,b):s},b.tooltipContent=function(a){return arguments.length?(t=a,b):t},b.state=function(c){return a.deprecated("linePlusBarChart.state"),arguments.length?(u=c,b):u};for(var B in u)b.state[B]=u[B];return b.defaultState=function(a){return arguments.length?(v=a,b):v},b.noData=function(a){return arguments.length?(w=a,b):w},b},a.models.linePlusBarWithFocusChart=function(){"use strict";function b(G){return G.each(function(G){function P(a){var b=+("e"==a),c=b?1:-1,d=W/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function Q(){u.empty()||u.extent(E),ib.data([u.empty()?e.domain():E]).each(function(a){var b=e(a[0])-e.range()[0],c=e.range()[1]-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>c?0:c)})}function R(){E=u.empty()?null:u.extent(),c=u.empty()?e.domain():u.extent(),I.brush({extent:c,brush:u}),Q(),l.width(U).height(V).color(G.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!G[b].disabled&&G[b].bar})),j.width(U).height(V).color(G.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!G[b].disabled&&!G[b].bar}));var b=db.select(".nv-focus .nv-barsWrap").datum(Z.length?Z.map(function(a){return{key:a.key,values:a.values.filter(function(a,b){return l.x()(a,b)>=c[0]&&l.x()(a,b)<=c[1]})}}):[{values:[]}]),h=db.select(".nv-focus .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$.map(function(a){return{key:a.key,values:a.values.filter(function(a,b){return j.x()(a,b)>=c[0]&&j.x()(a,b)<=c[1]})}}));d=Z.length?l.xScale():j.xScale(),n.scale(d).ticks(a.utils.calcTicksX(U/100,G)).tickSize(-V,0),n.domain([Math.ceil(c[0]),Math.floor(c[1])]),db.select(".nv-x.nv-axis").transition().duration(J).call(n),b.transition().duration(J).call(l),h.transition().duration(J).call(j),db.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),p.scale(f).ticks(a.utils.calcTicksY(V/36,G)).tickSize(-U,0),db.select(".nv-focus .nv-y1.nv-axis").style("opacity",Z.length?1:0),q.scale(g).ticks(a.utils.calcTicksY(V/36,G)).tickSize(Z.length?0:-U,0),db.select(".nv-focus .nv-y2.nv-axis").style("opacity",$.length?1:0).attr("transform","translate("+d.range()[1]+",0)"),db.select(".nv-focus .nv-y1.nv-axis").transition().duration(J).call(p),db.select(".nv-focus .nv-y2.nv-axis").transition().duration(J).call(q)}var S=d3.select(this),T=this;a.utils.initSVG(S);var U=(x||parseInt(S.style("width"))||960)-v.left-v.right,V=(y||parseInt(S.style("height"))||400)-v.top-v.bottom-z,W=z-w.top-w.bottom;if(b.update=function(){S.transition().duration(J).call(b)},b.container=this,K.setter(O(G),b.update).getter(N(G)).update(),K.disabled=G.map(function(a){return!!a.disabled}),!L){var X;L={};for(X in K)L[X]=K[X]instanceof Array?K[X].slice(0):K[X]}if(!(G&&G.length&&G.filter(function(a){return a.values.length}).length)){var Y=S.selectAll(".nv-noData").data([H]);return Y.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),Y.attr("x",v.left+U/2).attr("y",v.top+V/2).text(function(a){return a}),b}S.selectAll(".nv-noData").remove();var Z=G.filter(function(a){return!a.disabled&&a.bar}),$=G.filter(function(a){return!a.bar});d=l.xScale(),e=o.scale(),f=l.yScale(),g=j.yScale(),h=m.yScale(),i=k.yScale();var _=G.filter(function(a){return!a.disabled&&a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})}),ab=G.filter(function(a){return!a.disabled&&!a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})});d.range([0,U]),e.domain(d3.extent(d3.merge(_.concat(ab)),function(a){return a.x})).range([0,U]);var bb=S.selectAll("g.nv-wrap.nv-linePlusBar").data([G]),cb=bb.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g"),db=bb.select("g");cb.append("g").attr("class","nv-legendWrap");var eb=cb.append("g").attr("class","nv-focus");eb.append("g").attr("class","nv-x nv-axis"),eb.append("g").attr("class","nv-y1 nv-axis"),eb.append("g").attr("class","nv-y2 nv-axis"),eb.append("g").attr("class","nv-barsWrap"),eb.append("g").attr("class","nv-linesWrap");var fb=cb.append("g").attr("class","nv-context");fb.append("g").attr("class","nv-x nv-axis"),fb.append("g").attr("class","nv-y1 nv-axis"),fb.append("g").attr("class","nv-y2 nv-axis"),fb.append("g").attr("class","nv-barsWrap"),fb.append("g").attr("class","nv-linesWrap"),fb.append("g").attr("class","nv-brushBackground"),fb.append("g").attr("class","nv-x nv-brush"),D&&(t.width(U/2),db.select(".nv-legendWrap").datum(G.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(a.bar?" (left axis)":" (right axis)"),a})).call(t),v.top!=t.height()&&(v.top=t.height(),V=(y||parseInt(S.style("height"))||400)-v.top-v.bottom-z),db.select(".nv-legendWrap").attr("transform","translate("+U/2+","+-v.top+")")),bb.attr("transform","translate("+v.left+","+v.top+")"),m.width(U).height(W).color(G.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!G[b].disabled&&G[b].bar})),k.width(U).height(W).color(G.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!G[b].disabled&&!G[b].bar}));var gb=db.select(".nv-context .nv-barsWrap").datum(Z.length?Z:[{values:[]}]),hb=db.select(".nv-context .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$);db.select(".nv-context").attr("transform","translate(0,"+(V+v.bottom+w.top)+")"),gb.transition().call(m),hb.transition().call(k),u.x(e).on("brush",R),E&&u.extent(E);var ib=db.select(".nv-brushBackground").selectAll("g").data([E||u.extent()]),jb=ib.enter().append("g");jb.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",W),jb.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",W);var kb=db.select(".nv-x.nv-brush").call(u);kb.selectAll("rect").attr("height",W),kb.selectAll(".resize").append("path").attr("d",P),o.ticks(a.utils.calcTicksX(U/100,G)).tickSize(-W,0),db.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+h.range()[0]+")"),db.select(".nv-context .nv-x.nv-axis").transition().call(o),r.scale(h).ticks(W/36).tickSize(-U,0),db.select(".nv-context .nv-y1.nv-axis").style("opacity",Z.length?1:0).attr("transform","translate(0,"+e.range()[0]+")"),db.select(".nv-context .nv-y1.nv-axis").transition().call(r),s.scale(i).ticks(W/36).tickSize(Z.length?0:-U,0),db.select(".nv-context .nv-y2.nv-axis").style("opacity",$.length?1:0).attr("transform","translate("+e.range()[1]+",0)"),db.select(".nv-context .nv-y2.nv-axis").transition().call(s),t.dispatch.on("stateChange",function(a){for(var c in a)K[c]=a[c];I.stateChange(K),b.update()}),I.on("tooltipShow",function(a){F&&M(a,T.parentNode)}),I.on("changeState",function(a){"undefined"!=typeof a.disabled&&(G.forEach(function(b,c){b.disabled=a.disabled[c]}),K.disabled=a.disabled),b.update()}),R()}),b}var c,d,e,f,g,h,i,j=a.models.line(),k=a.models.line(),l=a.models.historicalBar(),m=a.models.historicalBar(),n=a.models.axis(),o=a.models.axis(),p=a.models.axis(),q=a.models.axis(),r=a.models.axis(),s=a.models.axis(),t=a.models.legend(),u=d3.svg.brush(),v={top:30,right:30,bottom:30,left:60},w={top:0,right:30,bottom:20,left:60},x=null,y=null,z=100,A=function(a){return a.x},B=function(a){return a.y},C=a.utils.defaultColor(),D=!0,E=null,F=!0,G=function(a,b,c){return"

"+a+"

"+c+" at "+b+"

"},H="No Data Available.",I=d3.dispatch("tooltipShow","tooltipHide","brush","stateChange","changeState"),J=0,K=a.utils.state(),L=null;j.clipEdge(!0),k.interactive(!1),n.orient("bottom").tickPadding(5),p.orient("left"),q.orient("right"),o.orient("bottom").tickPadding(5),r.orient("left"),s.orient("right");var M=function(d,e){c&&(d.pointIndex+=Math.ceil(c[0]));var f=d.pos[0]+(e.offsetLeft||0),g=d.pos[1]+(e.offsetTop||0),h=n.tickFormat()(j.x()(d.point,d.pointIndex)),i=(d.series.bar?p:q).tickFormat()(j.y()(d.point,d.pointIndex)),k=G(d.series.key,h,i,d,b);a.tooltip.show([f,g],k,d.value<0?"n":"s",null,e)},N=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},O=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};j.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+v.left,a.pos[1]+v.top],I.tooltipShow(a)}),j.dispatch.on("elementMouseout.tooltip",function(a){I.tooltipHide(a)}),l.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+v.left,a.pos[1]+v.top],I.tooltipShow(a)}),l.dispatch.on("elementMouseout.tooltip",function(a){I.tooltipHide(a)}),I.on("tooltipHide",function(){F&&a.tooltip.cleanup()}),b.dispatch=I,b.legend=t,b.lines=j,b.lines2=k,b.bars=l,b.bars2=m,b.xAxis=n,b.x2Axis=o,b.y1Axis=p,b.y2Axis=q,b.y3Axis=r,b.y4Axis=s,b.state=K,d3.rebind(b,j,"defined","size","clipVoronoi","interpolate"),b.options=a.utils.optionsFunc.bind(b),b.x=function(a){return arguments.length?(A=a,j.x(a),l.x(a),b):A},b.y=function(a){return arguments.length?(B=a,j.y(a),l.y(a),b):B},b.margin=function(a){return arguments.length?(v.top="undefined"!=typeof a.top?a.top:v.top,v.right="undefined"!=typeof a.right?a.right:v.right,v.bottom="undefined"!=typeof a.bottom?a.bottom:v.bottom,v.left="undefined"!=typeof a.left?a.left:v.left,b):v},b.width=function(a){return arguments.length?(x=a,b):x},b.height=function(a){return arguments.length?(y=a,b):y},b.color=function(c){return arguments.length?(C=a.utils.getColor(c),t.color(C),b):C},b.showLegend=function(a){return arguments.length?(D=a,b):D},b.tooltips=function(a){return arguments.length?(F=a,b):F},b.tooltipContent=function(a){return arguments.length?(G=a,b):G},b.state=function(c){return a.deprecated("linePlusBarWithFocusChart.state"),arguments.length?(K=c,b):K};for(var P in K)b.state[P]=K[P];return b.noData=function(a){return arguments.length?(H=a,b):H},b.brushExtent=function(a){return arguments.length?(E=a,b):E},b},a.models.lineWithFocusChart=function(){"use strict";function b(x){return x.each(function(x){function G(a){var b=+("e"==a),c=b?1:-1,d=N/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function H(){n.empty()||n.extent(v),W.data([n.empty()?e.domain():v]).each(function(a){var b=e(a[0])-c.range()[0],d=c.range()[1]-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>d?0:d)})}function I(){v=n.empty()?null:n.extent();var a=n.empty()?e.domain():n.extent();if(!(Math.abs(a[0]-a[1])<=1)){z.brush({extent:a,brush:n}),H();var b=S.select(".nv-focus .nv-linesWrap").datum(x.filter(function(a){return!a.disabled}).map(function(b){return{key:b.key,area:b.area,values:b.values.filter(function(b,c){return g.x()(b,c)>=a[0]&&g.x()(b,c)<=a[1]})}}));b.transition().duration(A).call(g),S.select(".nv-focus .nv-x.nv-axis").transition().duration(A).call(i),S.select(".nv-focus .nv-y.nv-axis").transition().duration(A).call(j)}}var J=d3.select(this),K=this;a.utils.initSVG(J);var L=(r||parseInt(J.style("width"))||960)-o.left-o.right,M=(s||parseInt(J.style("height"))||400)-o.top-o.bottom-t,N=t-p.top-p.bottom;if(b.update=function(){J.transition().duration(A).call(b)},b.container=this,B.setter(F(x),b.update).getter(E(x)).update(),B.disabled=x.map(function(a){return!!a.disabled}),!C){var O;C={};for(O in B)C[O]=B[O]instanceof Array?B[O].slice(0):B[O]}if(!(x&&x.length&&x.filter(function(a){return a.values.length}).length)){var P=J.selectAll(".nv-noData").data([y]);return P.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),P.attr("x",o.left+L/2).attr("y",o.top+M/2).text(function(a){return a}),b}J.selectAll(".nv-noData").remove(),c=g.xScale(),d=g.yScale(),e=h.xScale(),f=h.yScale();var Q=J.selectAll("g.nv-wrap.nv-lineWithFocusChart").data([x]),R=Q.enter().append("g").attr("class","nvd3 nv-wrap nv-lineWithFocusChart").append("g"),S=Q.select("g");R.append("g").attr("class","nv-legendWrap");var T=R.append("g").attr("class","nv-focus");T.append("g").attr("class","nv-x nv-axis"),T.append("g").attr("class","nv-y nv-axis"),T.append("g").attr("class","nv-linesWrap");var U=R.append("g").attr("class","nv-context");U.append("g").attr("class","nv-x nv-axis"),U.append("g").attr("class","nv-y nv-axis"),U.append("g").attr("class","nv-linesWrap"),U.append("g").attr("class","nv-brushBackground"),U.append("g").attr("class","nv-x nv-brush"),u&&(m.width(L),S.select(".nv-legendWrap").datum(x).call(m),o.top!=m.height()&&(o.top=m.height(),M=(s||parseInt(J.style("height"))||400)-o.top-o.bottom-t),S.select(".nv-legendWrap").attr("transform","translate(0,"+-o.top+")")),Q.attr("transform","translate("+o.left+","+o.top+")"),g.width(L).height(M).color(x.map(function(a,b){return a.color||q(a,b)}).filter(function(a,b){return!x[b].disabled})),h.defined(g.defined()).width(L).height(N).color(x.map(function(a,b){return a.color||q(a,b)}).filter(function(a,b){return!x[b].disabled})),S.select(".nv-context").attr("transform","translate(0,"+(M+o.bottom+p.top)+")");var V=S.select(".nv-context .nv-linesWrap").datum(x.filter(function(a){return!a.disabled}));d3.transition(V).call(h),i.scale(c).ticks(a.utils.calcTicksX(L/100,x)).tickSize(-M,0),j.scale(d).ticks(a.utils.calcTicksY(M/36,x)).tickSize(-L,0),S.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+M+")"),n.x(e).on("brush",function(){var a=b.transitionDuration(); +b.transitionDuration(0),I(),b.transitionDuration(a)}),v&&n.extent(v);var W=S.select(".nv-brushBackground").selectAll("g").data([v||n.extent()]),X=W.enter().append("g");X.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",N),X.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",N);var Y=S.select(".nv-x.nv-brush").call(n);Y.selectAll("rect").attr("height",N),Y.selectAll(".resize").append("path").attr("d",G),I(),k.scale(e).ticks(a.utils.calcTicksX(L/100,x)).tickSize(-N,0),S.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),d3.transition(S.select(".nv-context .nv-x.nv-axis")).call(k),l.scale(f).ticks(a.utils.calcTicksY(N/36,x)).tickSize(-L,0),d3.transition(S.select(".nv-context .nv-y.nv-axis")).call(l),S.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),m.dispatch.on("stateChange",function(a){for(var c in a)B[c]=a[c];z.stateChange(B),b.update()}),z.on("tooltipShow",function(a){w&&D(a,K.parentNode)}),z.on("changeState",function(a){"undefined"!=typeof a.disabled&&x.forEach(function(b,c){b.disabled=a.disabled[c]}),b.update()})}),b}var c,d,e,f,g=a.models.line(),h=a.models.line(),i=a.models.axis(),j=a.models.axis(),k=a.models.axis(),l=a.models.axis(),m=a.models.legend(),n=d3.svg.brush(),o={top:30,right:30,bottom:30,left:60},p={top:0,right:30,bottom:20,left:60},q=a.utils.defaultColor(),r=null,s=null,t=100,u=!0,v=null,w=!0,x=function(a,b,c){return"

"+a+"

"+c+" at "+b+"

"},y="No Data Available.",z=d3.dispatch("tooltipShow","tooltipHide","brush","stateChange","changeState"),A=250,B=a.utils.state(),C=null;g.clipEdge(!0),h.interactive(!1),i.orient("bottom").tickPadding(5),j.orient("left"),k.orient("bottom").tickPadding(5),l.orient("left");var D=function(c,d){var e=c.pos[0]+(d.offsetLeft||0),f=c.pos[1]+(d.offsetTop||0),h=i.tickFormat()(g.x()(c.point,c.pointIndex)),k=j.tickFormat()(g.y()(c.point,c.pointIndex)),l=x(c.series.key,h,k,c,b);a.tooltip.show([e,f],l,null,null,d)},E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};g.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+o.left,a.pos[1]+o.top],z.tooltipShow(a)}),g.dispatch.on("elementMouseout.tooltip",function(a){z.tooltipHide(a)}),z.on("tooltipHide",function(){w&&a.tooltip.cleanup()}),b.dispatch=z,b.legend=m,b.lines=g,b.lines2=h,b.xAxis=i,b.yAxis=j,b.x2Axis=k,b.y2Axis=l,b.state=B,d3.rebind(b,g,"defined","isArea","size","xDomain","yDomain","xRange","yRange","forceX","forceY","interactive","clipEdge","clipVoronoi","id"),b.options=a.utils.optionsFunc.bind(b),b.x=function(a){return arguments.length?(g.x(a),h.x(a),b):g.x},b.y=function(a){return arguments.length?(g.y(a),h.y(a),b):g.y},b.margin=function(a){return arguments.length?(o.top="undefined"!=typeof a.top?a.top:o.top,o.right="undefined"!=typeof a.right?a.right:o.right,o.bottom="undefined"!=typeof a.bottom?a.bottom:o.bottom,o.left="undefined"!=typeof a.left?a.left:o.left,b):o},b.margin2=function(a){return arguments.length?(p=a,b):p},b.width=function(a){return arguments.length?(r=a,b):r},b.height=function(a){return arguments.length?(s=a,b):s},b.height2=function(a){return arguments.length?(t=a,b):t},b.color=function(c){return arguments.length?(q=a.utils.getColor(c),m.color(q),b):q},b.showLegend=function(a){return arguments.length?(u=a,b):u},b.tooltips=function(a){return arguments.length?(w=a,b):w},b.tooltipContent=function(a){return arguments.length?(x=a,b):x},b.state=function(c){return a.deprecated("lineWithFocusChart.state"),arguments.length?(B=c,b):B};for(var G in B)b.state[G]=B[G];return b.defaultState=function(a){return arguments.length?(C=a,b):C},b.interpolate=function(a){return arguments.length?(g.interpolate(a),h.interpolate(a),b):g.interpolate()},b.noData=function(a){return arguments.length?(y=a,b):y},b.xTickFormat=function(a){return arguments.length?(i.tickFormat(a),k.tickFormat(a),b):i.tickFormat()},b.yTickFormat=function(a){return arguments.length?(j.tickFormat(a),l.tickFormat(a),b):j.tickFormat()},b.brushExtent=function(a){return arguments.length?(v=a,b):v},b.transitionDuration=function(a){return arguments.length?(A=a,b):A},b},a.models.multiBar=function(){"use strict";function b(C){return B.reset(),C.each(function(b){var C=k-j.left-j.right,D=l-j.top-j.bottom,E=d3.select(this);a.utils.initSVG(E);w&&b.length&&(w=[{values:b[0].values.map(function(a){return{x:a.x,y:0,series:a.series,size:.01}})}]),t&&(b=d3.layout.stack().offset(u).values(function(a){return a.values}).y(q)(!b.length&&w?w:b)),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})}),t&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a){var b=a.values[c];b.size=Math.abs(b.y),b.y<0?(b.y1=e,e-=b.size):(b.y1=b.size+d,d+=b.size)})});var F=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),y0:a.y0,y1:a.y1}})});m.domain(d||d3.merge(F).map(function(a){return a.x})).rangeBands(f||[0,C],z),n.domain(e||d3.extent(d3.merge(F).map(function(a){return t?a.y>0?a.y1:a.y1+a.y:a.y}).concat(r))).range(g||[D,0]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]+.01*n.domain()[0],n.domain()[1]-.01*n.domain()[1]]:[-1,1]),h=h||m,i=i||n;var G=E.selectAll("g.nv-wrap.nv-multibar").data([b]),H=G.enter().append("g").attr("class","nvd3 nv-wrap nv-multibar"),I=H.append("defs"),J=H.append("g"),K=G.select("g");J.append("g").attr("class","nv-groups"),G.attr("transform","translate("+j.left+","+j.top+")"),I.append("clipPath").attr("id","nv-edge-clip-"+o).append("rect"),G.select("#nv-edge-clip-"+o+" rect").attr("width",C).attr("height",D),K.attr("clip-path",s?"url(#nv-edge-clip-"+o+")":"");var L=G.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});L.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);var M=B.transition(L.exit().selectAll("rect.nv-bar"),"multibarExit",Math.min(250,y)).attr("y",function(a){return i(t?a.y0:0)}).attr("height",0).remove();M.delay&&M.delay(function(a,c){return c*y/b[0].values.length}),L.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return v(a,b)}).style("stroke",function(a,b){return v(a,b)}),L.style("stroke-opacity",1).style("fill-opacity",.75);var N=L.selectAll("rect.nv-bar").data(function(a){return w&&!b.length?w.values:a.values});N.exit().remove();N.enter().append("rect").attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("x",function(a,c,d){return t?0:d*m.rangeBand()/b.length}).attr("y",function(a){return i(t?a.y0:0)}).attr("height",0).attr("width",m.rangeBand()/(t?1:b.length)).attr("transform",function(a,b){return"translate("+m(p(a,b))+",0)"});N.style("fill",function(a,b,c){return v(a,c,b)}).style("stroke",function(a,b,c){return v(a,c,b)}).on("mouseover",function(a,c){d3.select(this).classed("hover",!0),A.elementMouseover({value:q(a,c),point:a,series:b[a.series],pos:[m(p(a,c))+m.rangeBand()*(t?b.length/2:a.series+.5)/b.length,n(q(a,c)+(t?a.y0:0))],pointIndex:c,seriesIndex:a.series,e:d3.event})}).on("mouseout",function(a,c){d3.select(this).classed("hover",!1),A.elementMouseout({value:q(a,c),point:a,series:b[a.series],pointIndex:c,seriesIndex:a.series,e:d3.event})}).on("click",function(a,c){A.elementClick({value:q(a,c),point:a,series:b[a.series],pos:[m(p(a,c))+m.rangeBand()*(t?b.length/2:a.series+.5)/b.length,n(q(a,c)+(t?a.y0:0))],pointIndex:c,seriesIndex:a.series,e:d3.event}),d3.event.stopPropagation()}).on("dblclick",function(a,c){A.elementDblClick({value:q(a,c),point:a,series:b[a.series],pos:[m(p(a,c))+m.rangeBand()*(t?b.length/2:a.series+.5)/b.length,n(q(a,c)+(t?a.y0:0))],pointIndex:c,seriesIndex:a.series,e:d3.event}),d3.event.stopPropagation()}),N.attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("transform",function(a,b){return"translate("+m(p(a,b))+",0)"}),x&&(c||(c=b.map(function(){return!0})),N.style("fill",function(a,b,d){return d3.rgb(x(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(x(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}));var O=N.watchTransition(B,"multibar",Math.min(250,y)).delay(function(a,c){return c*y/b[0].values.length});t?O.attr("y",function(a){return n(t?a.y1:0)}).attr("height",function(a){return Math.max(Math.abs(n(a.y+(t?a.y0:0))-n(t?a.y0:0)),1)}).attr("x",function(a){return t?0:a.series*m.rangeBand()/b.length}).attr("width",m.rangeBand()/(t?1:b.length)):O.attr("x",function(a){return a.series*m.rangeBand()/b.length}).attr("width",m.rangeBand()/b.length).attr("y",function(a,b){return q(a,b)<0?n(0):n(0)-n(q(a,b))<1?n(0)-1:n(q(a,b))||0}).attr("height",function(a,b){return Math.max(Math.abs(n(q(a,b))-n(0)),1)||0}),h=m.copy(),i=n.copy()}),B.renderEnd("multibar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=d3.scale.ordinal(),n=d3.scale.linear(),o=Math.floor(1e4*Math.random()),p=function(a){return a.x},q=function(a){return a.y},r=[0],s=!0,t=!1,u="zero",v=a.utils.defaultColor(),w=!1,x=null,y=1e3,z=.1,A=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),B=a.utils.renderWatch(A,y);return b.dispatch=A,b.x=function(a){return arguments.length?(p=a,b):p},b.y=function(a){return arguments.length?(q=a,b):q},b.margin=function(a){return arguments.length?(j.top="undefined"!=typeof a.top?a.top:j.top,j.right="undefined"!=typeof a.right?a.right:j.right,j.bottom="undefined"!=typeof a.bottom?a.bottom:j.bottom,j.left="undefined"!=typeof a.left?a.left:j.left,b):j},b.width=function(a){return arguments.length?(k=a,b):k},b.height=function(a){return arguments.length?(l=a,b):l},b.xScale=function(a){return arguments.length?(m=a,b):m},b.yScale=function(a){return arguments.length?(n=a,b):n},b.xDomain=function(a){return arguments.length?(d=a,b):d},b.yDomain=function(a){return arguments.length?(e=a,b):e},b.xRange=function(a){return arguments.length?(f=a,b):f},b.yRange=function(a){return arguments.length?(g=a,b):g},b.forceY=function(a){return arguments.length?(r=a,b):r},b.stacked=function(a){return arguments.length?(t=a,b):t},b.stackOffset=function(a){return arguments.length?(u=a,b):u},b.clipEdge=function(a){return arguments.length?(s=a,b):s},b.color=function(c){return arguments.length?(v=a.utils.getColor(c),b):v},b.barColor=function(c){return arguments.length?(x=a.utils.getColor(c),b):x},b.disabled=function(a){return arguments.length?(c=a,b):c},b.id=function(a){return arguments.length?(o=a,b):o},b.hideable=function(a){return arguments.length?(w=a,b):w},b.groupSpacing=function(a){return arguments.length?(z=a,b):z},b.duration=function(a){return arguments.length?(y=a,B.reset(y),b):y},b.delay=function(c){return a.deprecated("multiBar.delay"),b.duration(c)},b.options=a.utils.optionsFunc.bind(b),b},a.models.multiBarChart=function(){"use strict";function b(w){return D.reset(),D.models(e),p&&D.models(f),q&&D.models(g),w.each(function(w){var D=d3.select(this),I=this;a.utils.initSVG(D);var J=(k||parseInt(D.style("width"))||960)-j.left-j.right,K=(l||parseInt(D.style("height"))||400)-j.top-j.bottom;if(b.update=function(){0===C?D.call(b):D.transition().duration(C).call(b)},b.container=this,x.setter(H(w),b.update).getter(G(w)).update(),x.disabled=w.map(function(a){return!!a.disabled}),!y){var L;y={};for(L in x)y[L]=x[L]instanceof Array?x[L].slice(0):x[L]}if(!(w&&w.length&&w.filter(function(a){return a.values.length}).length)){var M=D.selectAll(".nv-noData").data([z]);return M.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),M.attr("x",j.left+J/2).attr("y",j.top+K/2).text(function(a){return a}),b}D.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var N=D.selectAll("g.nv-wrap.nv-multiBarWithLegend").data([w]),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarWithLegend").append("g"),P=N.select("g");if(O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y nv-axis"),O.append("g").attr("class","nv-barsWrap"),O.append("g").attr("class","nv-legendWrap"),O.append("g").attr("class","nv-controlsWrap"),o&&(h.width(J-B()),e.barColor()&&w.forEach(function(a,b){a.color=d3.rgb("#ccc").darker(1.5*b).toString()}),P.select(".nv-legendWrap").datum(w).call(h),j.top!=h.height()&&(j.top=h.height(),K=(l||parseInt(D.style("height"))||400)-j.top-j.bottom),P.select(".nv-legendWrap").attr("transform","translate("+B()+","+-j.top+")")),n){var Q=[{key:"Grouped",disabled:e.stacked()},{key:"Stacked",disabled:!e.stacked()}];i.width(B()).color(["#444","#444","#444"]),P.select(".nv-controlsWrap").datum(Q).attr("transform","translate(0,"+-j.top+")").call(i)}N.attr("transform","translate("+j.left+","+j.top+")"),r&&P.select(".nv-y.nv-axis").attr("transform","translate("+J+",0)"),e.disabled(w.map(function(a){return a.disabled})).width(J).height(K).color(w.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!w[b].disabled}));var R=P.select(".nv-barsWrap").datum(w.filter(function(a){return!a.disabled}));if(R.call(e),p){f.scale(c).ticks(a.utils.calcTicksX(J/100,w)).tickSize(-K,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),P.select(".nv-x.nv-axis").call(f);var S=P.select(".nv-x.nv-axis > g").selectAll("g");if(S.selectAll("line, text").style("opacity",1),t){var T=function(a,b){return"translate("+a+","+b+")"},U=5,V=17;S.selectAll("text").attr("transform",function(a,b,c){return T(0,c%2==0?U:V)});var W=d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;P.selectAll(".nv-x.nv-axis .nv-axisMaxMin text").attr("transform",function(a,b){return T(0,0===b||W%2!==0?V:U)})}s&&S.filter(function(a,b){return b%Math.ceil(w[0].values.length/(J/100))!==0}).selectAll("text, line").style("opacity",0),u&&S.selectAll(".tick text").attr("transform","rotate("+u+" 0,0)").style("text-anchor",u>0?"start":"end"),P.select(".nv-x.nv-axis").selectAll("g.nv-axisMaxMin text").style("opacity",1)}q&&(g.scale(d).ticks(a.utils.calcTicksY(K/36,w)).tickSize(-J,0),P.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)x[c]=a[c];A.stateChange(x),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(Q=Q.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":e.stacked(!1);break;case"Stacked":e.stacked(!0)}x.stacked=e.stacked(),A.stateChange(x),b.update()}}),A.on("tooltipShow",function(a){v&&F(a,I.parentNode)}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&(w.forEach(function(b,c){b.disabled=a.disabled[c]}),x.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),x.stacked=a.stacked,E=a.stacked),b.update()})}),D.renderEnd("multibarchart immediate"),b}var c,d,e=a.models.multiBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j={top:30,right:20,bottom:50,left:60},k=null,l=null,m=a.utils.defaultColor(),n=!0,o=!0,p=!0,q=!0,r=!1,s=!0,t=!1,u=0,v=!0,w=function(a,b,c){return"

"+a+"

"+c+" on "+b+"

"},x=a.utils.state(),y=null,z="No Data Available.",A=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),B=function(){return n?180:0},C=250;x.stacked=!1,e.stacked(!1),f.orient("bottom").tickPadding(7).highlightZero(!0).showMaxMin(!1).tickFormat(function(a){return a}),g.orient(r?"right":"left").tickFormat(d3.format(",.1f")),i.updateState(!1);var D=a.utils.renderWatch(A),E=!1,F=function(c,d){var h=c.pos[0]+(d.offsetLeft||0),i=c.pos[1]+(d.offsetTop||0),j=f.tickFormat()(e.x()(c.point,c.pointIndex)),k=g.tickFormat()(e.y()(c.point,c.pointIndex)),l=w(c.series.key,j,k,c,b);a.tooltip.show([h,i],l,c.value<0?"n":"s",null,d)},G=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:E}}},H=function(a){return function(b){void 0!==b.stacked&&(E=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};e.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+j.left,a.pos[1]+j.top],A.tooltipShow(a)}),e.dispatch.on("elementMouseout.tooltip",function(a){A.tooltipHide(a)}),A.on("tooltipHide",function(){v&&a.tooltip.cleanup()}),b.dispatch=A,b.multibar=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.state=x,d3.rebind(b,e,"x","y","xDomain","yDomain","xRange","yRange","forceX","forceY","clipEdge","id","stacked","stackOffset","delay","barColor","groupSpacing"),b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(j.top="undefined"!=typeof a.top?a.top:j.top,j.right="undefined"!=typeof a.right?a.right:j.right,j.bottom="undefined"!=typeof a.bottom?a.bottom:j.bottom,j.left="undefined"!=typeof a.left?a.left:j.left,b):j},b.width=function(a){return arguments.length?(k=a,b):k},b.height=function(a){return arguments.length?(l=a,b):l},b.color=function(c){return arguments.length?(m=a.utils.getColor(c),h.color(m),b):m},b.showControls=function(a){return arguments.length?(n=a,b):n},b.showLegend=function(a){return arguments.length?(o=a,b):o},b.showXAxis=function(a){return arguments.length?(p=a,b):p},b.showYAxis=function(a){return arguments.length?(q=a,b):q},b.rightAlignYAxis=function(a){return arguments.length?(r=a,g.orient(a?"right":"left"),b):r},b.reduceXTicks=function(a){return arguments.length?(s=a,b):s},b.rotateLabels=function(a){return arguments.length?(u=a,b):u},b.staggerLabels=function(a){return arguments.length?(t=a,b):t},b.tooltip=function(a){return arguments.length?(w=a,b):w},b.tooltips=function(a){return arguments.length?(v=a,b):v},b.tooltipContent=function(a){return arguments.length?(w=a,b):w},b.state=function(c){return a.deprecated("multiBarChart.state"),arguments.length?(x=c,b):x};for(var I in x)b.state[I]=x[I];return b.defaultState=function(a){return arguments.length?(y=a,b):y},b.noData=function(a){return arguments.length?(z=a,b):z},b.transitionDuration=function(c){return a.deprecated("multiBarChart.transitionDuration"),b.duration(c)},b.duration=function(a){return arguments.length?(C=a,e.duration(C),f.duration(C),g.duration(C),D.reset(C),b):C},b},a.models.multiBarHorizontal=function(){"use strict";function b(m){return C.reset(),m.each(function(b){var m=k-j.left-j.right,z=l-j.top-j.bottom,A=d3.select(this);a.utils.initSVG(A),u&&(b=d3.layout.stack().offset("zero").values(function(a){return a.values}).y(q)(b)),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})}),u&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a){var b=a.values[c];b.size=Math.abs(b.y),b.y<0?(b.y1=e-b.size,e-=b.size):(b.y1=d,d+=b.size)})});var D=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),y0:a.y0,y1:a.y1}})});n.domain(d||d3.merge(D).map(function(a){return a.x})).rangeBands(f||[0,z],.1),o.domain(e||d3.extent(d3.merge(D).map(function(a){return u?a.y>0?a.y1+a.y:a.y1:a.y}).concat(r))),o.range(v&&!u?g||[o.domain()[0]<0?x:0,m-(o.domain()[1]>0?x:0)]:g||[0,m]),h=h||n,i=i||d3.scale.linear().domain(o.domain()).range([o(0),o(0)]);{var E=d3.select(this).selectAll("g.nv-wrap.nv-multibarHorizontal").data([b]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-multibarHorizontal"),G=(F.append("defs"),F.append("g"));E.select("g")}G.append("g").attr("class","nv-groups"),E.attr("transform","translate("+j.left+","+j.top+")");var H=E.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});H.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),H.exit().watchTransition(C,"multibarhorizontal: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),H.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return s(a,b)}).style("stroke",function(a,b){return s(a,b)}),H.watchTransition(C,"multibarhorizontal: groups").style("stroke-opacity",1).style("fill-opacity",.75);var I=H.selectAll("g.nv-bar").data(function(a){return a.values});I.exit().remove();var J=I.enter().append("g").attr("transform",function(a,c,d){return"translate("+i(u?a.y0:0)+","+(u?0:d*n.rangeBand()/b.length+n(p(a,c)))+")"});J.append("rect").attr("width",0).attr("height",n.rangeBand()/(u?1:b.length)),I.on("mouseover",function(a,c){d3.select(this).classed("hover",!0),B.elementMouseover({value:q(a,c),point:a,series:b[a.series],pos:[o(q(a,c)+(u?a.y0:0)),n(p(a,c))+n.rangeBand()*(u?b.length/2:a.series+.5)/b.length],pointIndex:c,seriesIndex:a.series,e:d3.event})}).on("mouseout",function(a,c){d3.select(this).classed("hover",!1),B.elementMouseout({value:q(a,c),point:a,series:b[a.series],pointIndex:c,seriesIndex:a.series,e:d3.event})}).on("click",function(a,c){B.elementClick({value:q(a,c),point:a,series:b[a.series],pos:[n(p(a,c))+n.rangeBand()*(u?b.length/2:a.series+.5)/b.length,o(q(a,c)+(u?a.y0:0))],pointIndex:c,seriesIndex:a.series,e:d3.event}),d3.event.stopPropagation()}).on("dblclick",function(a,c){B.elementDblClick({value:q(a,c),point:a,series:b[a.series],pos:[n(p(a,c))+n.rangeBand()*(u?b.length/2:a.series+.5)/b.length,o(q(a,c)+(u?a.y0:0))],pointIndex:c,seriesIndex:a.series,e:d3.event}),d3.event.stopPropagation()}),J.append("text"),v&&!u?(I.select("text").attr("text-anchor",function(a,b){return q(a,b)<0?"end":"start"}).attr("y",n.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){return y(q(a,b))}),I.watchTransition(C,"multibarhorizontal: bars").select("text").attr("x",function(a,b){return q(a,b)<0?-4:o(q(a,b))-o(0)+4})):I.selectAll("text").text(""),w&&!u?(J.append("text").classed("nv-bar-label",!0),I.select("text.nv-bar-label").attr("text-anchor",function(a,b){return q(a,b)<0?"start":"end"}).attr("y",n.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){return p(a,b)}),I.watchTransition(C,"multibarhorizontal: bars").select("text.nv-bar-label").attr("x",function(a,b){return q(a,b)<0?o(0)-o(q(a,b))+4:-4})):I.selectAll("text.nv-bar-label").text(""),I.attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}),t&&(c||(c=b.map(function(){return!0})),I.style("fill",function(a,b,d){return d3.rgb(t(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(t(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()})),u?I.watchTransition(C,"multibarhorizontal: bars").attr("transform",function(a,b){return"translate("+o(a.y1)+","+n(p(a,b))+")"}).select("rect").attr("width",function(a,b){return Math.abs(o(q(a,b)+a.y0)-o(a.y0))}).attr("height",n.rangeBand()):I.watchTransition(C,"multibarhorizontal: bars").attr("transform",function(a,c){return"translate("+o(q(a,c)<0?q(a,c):0)+","+(a.series*n.rangeBand()/b.length+n(p(a,c)))+")"}).select("rect").attr("height",n.rangeBand()/b.length).attr("width",function(a,b){return Math.max(Math.abs(o(q(a,b))-o(0)),1)}),h=n.copy(),i=o.copy()}),C.renderEnd("multibarHorizontal immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=d3.scale.ordinal(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=[0],s=a.utils.defaultColor(),t=null,u=!1,v=!1,w=!1,x=60,y=d3.format(",.2f"),z=1200,A=250,B=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),C=a.utils.renderWatch(B,A);return b.dispatch=B,b.options=a.utils.optionsFunc.bind(b),b.x=function(a){return arguments.length?(p=a,b):p},b.y=function(a){return arguments.length?(q=a,b):q},b.margin=function(a){return arguments.length?(j.top="undefined"!=typeof a.top?a.top:j.top,j.right="undefined"!=typeof a.right?a.right:j.right,j.bottom="undefined"!=typeof a.bottom?a.bottom:j.bottom,j.left="undefined"!=typeof a.left?a.left:j.left,b):j},b.width=function(a){return arguments.length?(k=a,b):k},b.height=function(a){return arguments.length?(l=a,b):l},b.xScale=function(a){return arguments.length?(n=a,b):n},b.yScale=function(a){return arguments.length?(o=a,b):o},b.xDomain=function(a){return arguments.length?(d=a,b):d},b.yDomain=function(a){return arguments.length?(e=a,b):e},b.xRange=function(a){return arguments.length?(f=a,b):f},b.yRange=function(a){return arguments.length?(g=a,b):g},b.forceY=function(a){return arguments.length?(r=a,b):r},b.stacked=function(a){return arguments.length?(u=a,b):u},b.color=function(c){return arguments.length?(s=a.utils.getColor(c),b):s},b.barColor=function(c){return arguments.length?(t=a.utils.getColor(c),b):t},b.disabled=function(a){return arguments.length?(c=a,b):c},b.id=function(a){return arguments.length?(m=a,b):m},b.delay=function(a){return arguments.length?(z=a,b):z},b.showValues=function(a){return arguments.length?(v=a,b):v},b.showBarLabels=function(a){return arguments.length?(w=a,b):w},b.valueFormat=function(a){return arguments.length?(y=a,b):y},b.valuePadding=function(a){return arguments.length?(x=a,b):x},b.duration=function(a){return arguments.length?(A=a,C.reset(A),b):A},b},a.models.multiBarHorizontalChart=function(){"use strict";function b(t){return D.reset(),D.models(e),p&&D.models(f),q&&D.models(g),t.each(function(t){var D=d3.select(this),E=this;a.utils.initSVG(D);var F=(k||parseInt(D.style("width"))||960)-j.left-j.right,G=(l||parseInt(D.style("height"))||400)-j.top-j.bottom;if(b.update=function(){D.transition().duration(z).call(b)},b.container=this,r=e.stacked(),u.setter(C(t),b.update).getter(B(t)).update(),u.disabled=t.map(function(a){return!!a.disabled}),!v){var H;v={};for(H in u)v[H]=u[H]instanceof Array?u[H].slice(0):u[H]}if(!(t&&t.length&&t.filter(function(a){return a.values.length}).length)){var I=D.selectAll(".nv-noData").data([w]);return I.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),I.attr("x",j.left+F/2).attr("y",j.top+G/2).text(function(a){return a}),b}D.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var J=D.selectAll("g.nv-wrap.nv-multiBarHorizontalChart").data([t]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarHorizontalChart").append("g"),L=J.select("g");if(K.append("g").attr("class","nv-x nv-axis"),K.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),K.append("g").attr("class","nv-barsWrap"),K.append("g").attr("class","nv-legendWrap"),K.append("g").attr("class","nv-controlsWrap"),o&&(h.width(F-y()),e.barColor()&&t.forEach(function(a,b){a.color=d3.rgb("#ccc").darker(1.5*b).toString()}),L.select(".nv-legendWrap").datum(t).call(h),j.top!=h.height()&&(j.top=h.height(),G=(l||parseInt(D.style("height"))||400)-j.top-j.bottom),L.select(".nv-legendWrap").attr("transform","translate("+y()+","+-j.top+")")),n){var M=[{key:"Grouped",disabled:e.stacked()},{key:"Stacked",disabled:!e.stacked()}];i.width(y()).color(["#444","#444","#444"]),L.select(".nv-controlsWrap").datum(M).attr("transform","translate(0,"+-j.top+")").call(i)}J.attr("transform","translate("+j.left+","+j.top+")"),e.disabled(t.map(function(a){return a.disabled})).width(F).height(G).color(t.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!t[b].disabled}));var N=L.select(".nv-barsWrap").datum(t.filter(function(a){return!a.disabled}));if(N.transition().call(e),p){f.scale(c).ticks(a.utils.calcTicksY(G/24,t)).tickSize(-F,0),L.select(".nv-x.nv-axis").call(f);var O=L.select(".nv-x.nv-axis").selectAll("g");O.selectAll("line, text")}q&&(g.scale(d).ticks(a.utils.calcTicksX(F/100,t)).tickSize(-G,0),L.select(".nv-y.nv-axis").attr("transform","translate(0,"+G+")"),L.select(".nv-y.nv-axis").call(g)),L.select(".nv-zeroLine line").attr("x1",d(0)).attr("x2",d(0)).attr("y1",0).attr("y2",-G),h.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(M=M.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":e.stacked(!1);break;case"Stacked":e.stacked(!0)}u.stacked=e.stacked(),x.stateChange(u),r=e.stacked(),b.update()}}),x.on("tooltipShow",function(a){s&&A(a,E.parentNode)}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(t.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),u.stacked=a.stacked,r=a.stacked),b.update()})}),D.renderEnd("multibar horizontal chart immediate"),b}var c,d,e=a.models.multiBarHorizontal(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend().height(30),i=a.models.legend().height(30),j={top:30,right:20,bottom:50,left:60},k=null,l=null,m=a.utils.defaultColor(),n=!0,o=!0,p=!0,q=!0,r=!1,s=!0,t=function(a,b,c){return"

"+a+" - "+b+"

"+c+"

"},u=a.utils.state(),v=null,w="No Data Available.",x=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),y=function(){return n?180:0},z=250;u.stacked=!1,e.stacked(r),f.orient("left").tickPadding(5).highlightZero(!1).showMaxMin(!1).tickFormat(function(a){return a}),g.orient("bottom").tickFormat(d3.format(",.1f")),i.updateState(!1);var A=function(c,d){var h=c.pos[0]+(d.offsetLeft||0),i=c.pos[1]+(d.offsetTop||0),j=f.tickFormat()(e.x()(c.point,c.pointIndex)),k=g.tickFormat()(e.y()(c.point,c.pointIndex)),l=t(c.series.key,j,k,c,b);a.tooltip.show([h,i],l,c.value<0?"e":"w",null,d)},B=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:r}}},C=function(a){return function(b){void 0!==b.stacked&&(r=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},D=a.utils.renderWatch(x,z);e.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+j.left,a.pos[1]+j.top],x.tooltipShow(a)}),e.dispatch.on("elementMouseout.tooltip",function(a){x.tooltipHide(a)}),x.on("tooltipHide",function(){s&&a.tooltip.cleanup()}),b.dispatch=x,b.multibar=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.state=u,d3.rebind(b,e,"x","y","xDomain","yDomain","xRange","yRange","forceX","forceY","clipEdge","id","delay","showValues","showBarLabels","valueFormat","stacked","barColor"),b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(j.top="undefined"!=typeof a.top?a.top:j.top,j.right="undefined"!=typeof a.right?a.right:j.right,j.bottom="undefined"!=typeof a.bottom?a.bottom:j.bottom,j.left="undefined"!=typeof a.left?a.left:j.left,b):j},b.width=function(a){return arguments.length?(k=a,b):k},b.height=function(a){return arguments.length?(l=a,b):l},b.color=function(c){return arguments.length?(m=a.utils.getColor(c),h.color(m),b):m},b.showControls=function(a){return arguments.length?(n=a,b):n},b.showLegend=function(a){return arguments.length?(o=a,b):o},b.showXAxis=function(a){return arguments.length?(p=a,b):p},b.showYAxis=function(a){return arguments.length?(q=a,b):q},b.tooltip=function(a){return arguments.length?(t=a,b):t},b.tooltips=function(a){return arguments.length?(s=a,b):s},b.tooltipContent=function(a){return arguments.length?(t=a,b):t},b.state=function(c){return a.deprecated("multiBarHorizontalChart.state"),arguments.length?(u=c,b):u};for(var E in u)b.state[E]=u[E];return b.defaultState=function(a){return arguments.length?(v=a,b):v},b.noData=function(a){return arguments.length?(w=a,b):w},b.transitionDuration=function(c){return a.deprecated("multiBarHorizontalChart.transitionDuration"),b.duration(c)},b.duration=function(a){return arguments.length?(z=a,D.reset(z),e.duration(z),f.duration(z),g.duration(z),b):z},b},a.models.multiChart=function(){"use strict";function b(l){return l.each(function(l){var m=d3.select(this),n=this;a.utils.initSVG(m),b.update=function(){m.transition().call(b)},b.container=this;var D=(h||parseInt(m.style("width"))||960)-f.left-f.right,E=(i||parseInt(m.style("height"))||400)-f.top-f.bottom,F=l.filter(function(a){return!a.disabled&&"line"==a.type&&1==a.yAxis}),G=l.filter(function(a){return!a.disabled&&"line"==a.type&&2==a.yAxis +}),H=l.filter(function(a){return!a.disabled&&"bar"==a.type&&1==a.yAxis}),I=l.filter(function(a){return!a.disabled&&"bar"==a.type&&2==a.yAxis}),J=l.filter(function(a){return!a.disabled&&"area"==a.type&&1==a.yAxis}),K=l.filter(function(a){return!a.disabled&&"area"==a.type&&2==a.yAxis}),L=l.filter(function(a){return!a.disabled&&1==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})}),M=l.filter(function(a){return!a.disabled&&2==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})});c.domain(d3.extent(d3.merge(L.concat(M)),function(a){return a.x})).range([0,D]);var N=m.selectAll("g.wrap.multiChart").data([l]),O=N.enter().append("g").attr("class","wrap nvd3 multiChart").append("g");O.append("g").attr("class","x axis"),O.append("g").attr("class","y1 axis"),O.append("g").attr("class","y2 axis"),O.append("g").attr("class","lines1Wrap"),O.append("g").attr("class","lines2Wrap"),O.append("g").attr("class","bars1Wrap"),O.append("g").attr("class","bars2Wrap"),O.append("g").attr("class","stack1Wrap"),O.append("g").attr("class","stack2Wrap"),O.append("g").attr("class","legendWrap");var P=N.select("g");j&&(A.width(D/2),P.select(".legendWrap").datum(l.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(1==a.yAxis?"":" (right axis)"),a})).call(A),f.top!=A.height()&&(f.top=A.height(),E=(i||parseInt(m.style("height"))||400)-f.top-f.bottom),P.select(".legendWrap").attr("transform","translate("+D/2+","+-f.top+")")),r.width(D).height(E).interpolate(o).color(l.map(function(a,b){return a.color||g[b%g.length]}).filter(function(a,b){return!l[b].disabled&&1==l[b].yAxis&&"line"==l[b].type})),s.width(D).height(E).interpolate(o).color(l.map(function(a,b){return a.color||g[b%g.length]}).filter(function(a,b){return!l[b].disabled&&2==l[b].yAxis&&"line"==l[b].type})),t.width(D).height(E).color(l.map(function(a,b){return a.color||g[b%g.length]}).filter(function(a,b){return!l[b].disabled&&1==l[b].yAxis&&"bar"==l[b].type})),u.width(D).height(E).color(l.map(function(a,b){return a.color||g[b%g.length]}).filter(function(a,b){return!l[b].disabled&&2==l[b].yAxis&&"bar"==l[b].type})),v.width(D).height(E).color(l.map(function(a,b){return a.color||g[b%g.length]}).filter(function(a,b){return!l[b].disabled&&1==l[b].yAxis&&"area"==l[b].type})),w.width(D).height(E).color(l.map(function(a,b){return a.color||g[b%g.length]}).filter(function(a,b){return!l[b].disabled&&2==l[b].yAxis&&"area"==l[b].type})),P.attr("transform","translate("+f.left+","+f.top+")");var Q=P.select(".lines1Wrap").datum(F),R=P.select(".bars1Wrap").datum(H),S=P.select(".stack1Wrap").datum(J),T=P.select(".lines2Wrap").datum(G),U=P.select(".bars2Wrap").datum(I),V=P.select(".stack2Wrap").datum(K),W=J.length?J.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[],X=K.length?K.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[];p.domain(d||d3.extent(d3.merge(L).concat(W),function(a){return a.y})).range([0,E]),q.domain(e||d3.extent(d3.merge(M).concat(X),function(a){return a.y})).range([0,E]),r.yDomain(p.domain()),t.yDomain(p.domain()),v.yDomain(p.domain()),s.yDomain(q.domain()),u.yDomain(q.domain()),w.yDomain(q.domain()),J.length&&d3.transition(S).call(v),K.length&&d3.transition(V).call(w),H.length&&d3.transition(R).call(t),I.length&&d3.transition(U).call(u),F.length&&d3.transition(Q).call(r),G.length&&d3.transition(T).call(s),x.ticks(a.utils.calcTicksX(D/100,l)).tickSize(-E,0),P.select(".x.axis").attr("transform","translate(0,"+E+")"),d3.transition(P.select(".x.axis")).call(x),y.ticks(a.utils.calcTicksY(E/36,l)).tickSize(-D,0),d3.transition(P.select(".y1.axis")).call(y),z.ticks(a.utils.calcTicksY(E/36,l)).tickSize(-D,0),d3.transition(P.select(".y2.axis")).call(z),P.select(".y2.axis").style("opacity",M.length?1:0).attr("transform","translate("+c.range()[1]+",0)"),A.dispatch.on("stateChange",function(){b.update()}),B.on("tooltipShow",function(a){k&&C(a,n.parentNode)})}),b}var c,d,e,f={top:30,right:20,bottom:50,left:60},g=a.utils.defaultColor(),h=null,i=null,j=!0,k=!0,l=function(a,b,c){return"

"+a+"

"+c+" at "+b+"

"},m=function(a){return a.x},n=function(a){return a.y},o="monotone",c=d3.scale.linear(),p=d3.scale.linear(),q=d3.scale.linear(),r=a.models.line().yScale(p),s=a.models.line().yScale(q),t=a.models.multiBar().stacked(!1).yScale(p),u=a.models.multiBar().stacked(!1).yScale(q),v=a.models.stackedArea().yScale(p),w=a.models.stackedArea().yScale(q),x=a.models.axis().scale(c).orient("bottom").tickPadding(5),y=a.models.axis().scale(p).orient("left"),z=a.models.axis().scale(q).orient("right"),A=a.models.legend().height(30),B=d3.dispatch("tooltipShow","tooltipHide"),C=function(c,d){var e=c.pos[0]+(d.offsetLeft||0),f=c.pos[1]+(d.offsetTop||0),g=x.tickFormat()(r.x()(c.point,c.pointIndex)),h=(2==c.series.yAxis?z:y).tickFormat()(r.y()(c.point,c.pointIndex)),i=l(c.series.key,g,h,c,b);a.tooltip.show([e,f],i,void 0,void 0,d.offsetParent)};return r.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+f.left,a.pos[1]+f.top],B.tooltipShow(a)}),r.dispatch.on("elementMouseout.tooltip",function(a){B.tooltipHide(a)}),s.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+f.left,a.pos[1]+f.top],B.tooltipShow(a)}),s.dispatch.on("elementMouseout.tooltip",function(a){B.tooltipHide(a)}),t.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+f.left,a.pos[1]+f.top],B.tooltipShow(a)}),t.dispatch.on("elementMouseout.tooltip",function(a){B.tooltipHide(a)}),u.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+f.left,a.pos[1]+f.top],B.tooltipShow(a)}),u.dispatch.on("elementMouseout.tooltip",function(a){B.tooltipHide(a)}),v.dispatch.on("tooltipShow",function(a){return Math.round(100*v.y()(a.point))?(a.pos=[a.pos[0]+f.left,a.pos[1]+f.top],void B.tooltipShow(a)):(setTimeout(function(){d3.selectAll(".point.hover").classed("hover",!1)},0),!1)}),v.dispatch.on("tooltipHide",function(a){B.tooltipHide(a)}),w.dispatch.on("tooltipShow",function(a){return Math.round(100*w.y()(a.point))?(a.pos=[a.pos[0]+f.left,a.pos[1]+f.top],void B.tooltipShow(a)):(setTimeout(function(){d3.selectAll(".point.hover").classed("hover",!1)},0),!1)}),w.dispatch.on("tooltipHide",function(a){B.tooltipHide(a)}),r.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+f.left,a.pos[1]+f.top],B.tooltipShow(a)}),r.dispatch.on("elementMouseout.tooltip",function(a){B.tooltipHide(a)}),s.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+f.left,a.pos[1]+f.top],B.tooltipShow(a)}),s.dispatch.on("elementMouseout.tooltip",function(a){B.tooltipHide(a)}),B.on("tooltipHide",function(){k&&a.tooltip.cleanup()}),b.dispatch=B,b.lines1=r,b.lines2=s,b.bars1=t,b.bars2=u,b.stack1=v,b.stack2=w,b.xAxis=x,b.yAxis1=y,b.yAxis2=z,b.options=a.utils.optionsFunc.bind(b),b.x=function(a){return arguments.length?(m=a,r.x(a),t.x(a),b):m},b.y=function(a){return arguments.length?(n=a,r.y(a),t.y(a),b):n},b.yDomain1=function(a){return arguments.length?(d=a,b):d},b.yDomain2=function(a){return arguments.length?(e=a,b):e},b.margin=function(a){return arguments.length?(f=a,b):f},b.width=function(a){return arguments.length?(h=a,b):h},b.height=function(a){return arguments.length?(i=a,b):i},b.color=function(a){return arguments.length?(g=a,A.color(a),b):g},b.showLegend=function(a){return arguments.length?(j=a,b):j},b.tooltips=function(a){return arguments.length?(k=a,b):k},b.tooltipContent=function(a){return arguments.length?(l=a,b):l},b.interpolate=function(a){return arguments.length?(o=a,b):o},b},a.models.ohlcBar=function(){"use strict";function b(w){return w.each(function(b){var w=h-g.left-g.right,y=i-g.top-g.bottom,z=d3.select(this);a.utils.initSVG(z),k.domain(c||d3.extent(b[0].values.map(m).concat(s))),k.range(u?e||[.5*w/b[0].values.length,w*(b[0].values.length-.5)/b[0].values.length]:e||[0,w]),l.domain(d||[d3.min(b[0].values.map(r).concat(t)),d3.max(b[0].values.map(q).concat(t))]).range(f||[y,0]),k.domain()[0]===k.domain()[1]&&k.domain(k.domain()[0]?[k.domain()[0]-.01*k.domain()[0],k.domain()[1]+.01*k.domain()[1]]:[-1,1]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]+.01*l.domain()[0],l.domain()[1]-.01*l.domain()[1]]:[-1,1]);var A=d3.select(this).selectAll("g.nv-wrap.nv-ohlcBar").data([b[0].values]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-ohlcBar"),C=B.append("defs"),D=B.append("g"),E=A.select("g");D.append("g").attr("class","nv-ticks"),A.attr("transform","translate("+g.left+","+g.top+")"),z.on("click",function(a,b){x.chartClick({data:a,index:b,pos:d3.event,id:j})}),C.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),A.select("#nv-chart-clip-path-"+j+" rect").attr("width",w).attr("height",y),E.attr("clip-path",v?"url(#nv-chart-clip-path-"+j+")":"");var F=A.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});F.exit().remove();F.enter().append("path").attr("class",function(a,b,c){return(o(a,b)>p(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}).attr("d",function(a,c){var d=w/b[0].values.length*.9;return"m0,0l0,"+(l(o(a,c))-l(q(a,c)))+"l"+-d/2+",0l"+d/2+",0l0,"+(l(r(a,c))-l(o(a,c)))+"l0,"+(l(p(a,c))-l(r(a,c)))+"l"+d/2+",0l"+-d/2+",0z"}).attr("transform",function(a,b){return"translate("+k(m(a,b))+","+l(q(a,b))+")"}).on("mouseover",function(a,c){d3.select(this).classed("hover",!0),x.elementMouseover({point:a,series:b[0],pos:[k(m(a,c)),l(n(a,c))],pointIndex:c,seriesIndex:0,e:d3.event})}).on("mouseout",function(a,c){d3.select(this).classed("hover",!1),x.elementMouseout({point:a,series:b[0],pointIndex:c,seriesIndex:0,e:d3.event})}).on("click",function(a,b){x.elementClick({value:n(a,b),data:a,index:b,pos:[k(m(a,b)),l(n(a,b))],e:d3.event,id:j}),d3.event.stopPropagation()}).on("dblclick",function(a,b){x.elementDblClick({value:n(a,b),data:a,index:b,pos:[k(m(a,b)),l(n(a,b))],e:d3.event,id:j}),d3.event.stopPropagation()});F.attr("class",function(a,b,c){return(o(a,b)>p(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}),d3.transition(F).attr("transform",function(a,b){return"translate("+k(m(a,b))+","+l(q(a,b))+")"}).attr("d",function(a,c){var d=w/b[0].values.length*.9;return"m0,0l0,"+(l(o(a,c))-l(q(a,c)))+"l"+-d/2+",0l"+d/2+",0l0,"+(l(r(a,c))-l(o(a,c)))+"l0,"+(l(p(a,c))-l(r(a,c)))+"l"+d/2+",0l"+-d/2+",0z"})}),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=960,i=500,j=Math.floor(1e4*Math.random()),k=d3.scale.linear(),l=d3.scale.linear(),m=function(a){return a.x},n=function(a){return a.y},o=function(a){return a.open},p=function(a){return a.close},q=function(a){return a.high},r=function(a){return a.low},s=[],t=[],u=!1,v=!0,w=a.utils.defaultColor(),x=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout");return b.dispatch=x,b.options=a.utils.optionsFunc.bind(b),b.x=function(a){return arguments.length?(m=a,b):m},b.y=function(a){return arguments.length?(n=a,b):n},b.open=function(a){return arguments.length?(o=a,b):o},b.close=function(a){return arguments.length?(p=a,b):p},b.high=function(a){return arguments.length?(q=a,b):q},b.low=function(a){return arguments.length?(r=a,b):r},b.margin=function(a){return arguments.length?(g.top="undefined"!=typeof a.top?a.top:g.top,g.right="undefined"!=typeof a.right?a.right:g.right,g.bottom="undefined"!=typeof a.bottom?a.bottom:g.bottom,g.left="undefined"!=typeof a.left?a.left:g.left,b):g},b.width=function(a){return arguments.length?(h=a,b):h},b.height=function(a){return arguments.length?(i=a,b):i},b.xScale=function(a){return arguments.length?(k=a,b):k},b.yScale=function(a){return arguments.length?(l=a,b):l},b.xDomain=function(a){return arguments.length?(c=a,b):c},b.yDomain=function(a){return arguments.length?(d=a,b):d},b.xRange=function(a){return arguments.length?(e=a,b):e},b.yRange=function(a){return arguments.length?(f=a,b):f},b.forceX=function(a){return arguments.length?(s=a,b):s},b.forceY=function(a){return arguments.length?(t=a,b):t},b.padData=function(a){return arguments.length?(u=a,b):u},b.clipEdge=function(a){return arguments.length?(v=a,b):v},b.color=function(c){return arguments.length?(w=a.utils.getColor(c),b):w},b.id=function(a){return arguments.length?(j=a,b):j},b},a.models.pie=function(){"use strict";function b(k){return A.reset(),k.each(function(b){function k(a){a.endAngle=isNaN(a.endAngle)?0:a.endAngle,a.startAngle=isNaN(a.startAngle)?0:a.startAngle,r||(a.innerRadius=0);var b=d3.interpolate(this._current,a);return this._current=b(0),function(a){return L(b(a))}}var B=e-c.left-c.right,C=f-c.top-c.bottom,D=Math.min(B,C)/2,E=D-D/5,F=d3.select(this);a.utils.initSVG(F);var G=F.selectAll(".nv-wrap.nv-pie").data(b),H=G.enter().append("g").attr("class","nvd3 nv-wrap nv-pie nv-chart-"+i),I=H.append("g"),J=G.select("g"),K=I.append("g").attr("class","nv-pie");I.append("g").attr("class","nv-pieLabels"),G.attr("transform","translate("+c.left+","+c.top+")"),J.select(".nv-pie").attr("transform","translate("+B/2+","+C/2+")"),J.select(".nv-pieLabels").attr("transform","translate("+B/2+","+C/2+")"),F.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:i})});var L=d3.svg.arc().outerRadius(E),M=d3.svg.arc().outerRadius(E+5);w&&(L.startAngle(w),M.startAngle(w)),x&&(L.endAngle(x),M.endAngle(x)),r&&(L.innerRadius(D*y),M.innerRadius(D*y));var N=d3.layout.pie().sort(null).value(function(a){return a.disabled?0:h(a)});if(r&&s){var O=K.append("g").attr("class","nv-pie");O.append("text").style("text-anchor","middle").attr("class","nv-pie-title").text(function(){return s}).attr("dy","0.35em").attr("transform",function(){return"translate(0, "+u+")"})}var P=G.select(".nv-pie").selectAll(".nv-slice").data(N),Q=G.select(".nv-pieLabels").selectAll(".nv-label").data(N);P.exit().remove(),Q.exit().remove();var R=P.enter().append("g");R.attr("class","nv-slice"),R.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),t&&d3.select(this).select("path").transition().duration(70).attr("d",M),z.elementMouseover({label:g(a.data),value:h(a.data),point:a.data,pointIndex:b,pos:[d3.event.pageX,d3.event.pageY],id:i,color:d3.select(this).style("fill")})}),R.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),t&&d3.select(this).select("path").transition().duration(50).attr("d",L),z.elementMouseout({label:g(a.data),value:h(a.data),point:a.data,index:b,id:i})}),R.on("click",function(a,b){z.elementClick({label:g(a.data),value:h(a.data),point:a.data,index:b,pos:d3.event,id:i}),d3.event.stopPropagation()}),R.on("dblclick",function(a,b){z.elementDblClick({label:g(a.data),value:h(a.data),point:a.data,index:b,pos:d3.event,id:i}),d3.event.stopPropagation()}),P.attr("fill",function(a,b){return j(a,b)}),P.attr("stroke",function(a,b){return j(a,b)});R.append("path").each(function(a){this._current=a});if(P.select("path").transition().attr("d",L).attrTween("d",k),m){var S=d3.svg.arc().innerRadius(0);if(n)var S=L;o&&(S=d3.svg.arc().outerRadius(L.outerRadius())),Q.enter().append("g").classed("nv-label",!0).each(function(a){var b=d3.select(this);b.attr("transform",function(a){if(v){a.outerRadius=E+10,a.innerRadius=E+15;var b=(a.startAngle+a.endAngle)/2*(180/Math.PI);return(a.startAngle+a.endAngle)/2q?c[p]:""})}}),A.renderEnd("pie immediate"),b}var c={top:0,right:0,bottom:0,left:0},e=500,f=500,g=function(a){return a.x},h=function(a){return a.y},i=Math.floor(1e4*Math.random()),j=a.utils.defaultColor(),k=d3.format(",.2f"),l=d3.format("%"),m=!0,n=!0,o=!1,p="key",q=.02,r=!1,s=!1,t=!0,u=0,v=!1,w=!1,x=!1,y=.5,z=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),A=a.utils.renderWatch(z);return b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return e},set:function(a){e=a}},height:{get:function(){return f},set:function(a){f=a}},showLabels:{get:function(){return m},set:function(a){m=a}},title:{get:function(){return s},set:function(a){s=a}},titleOffset:{get:function(){return u},set:function(a){u=a}},labelThreshold:{get:function(){return q},set:function(a){q=a}},labelFormat:{get:function(){return l},set:function(a){l=a}},valueFormat:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return g},set:function(a){g=a}},id:{get:function(){return i},set:function(a){i=a}},endAngle:{get:function(){return x},set:function(a){x=a}},startAngle:{get:function(){return w},set:function(a){w=a}},donutRatio:{get:function(){return y},set:function(a){y=a}},pieLabelsOutside:{get:function(){return n},set:function(a){n=a}},donutLabelsOutside:{get:function(){return o},set:function(a){o=a}},labelSunbeamLayout:{get:function(){return v},set:function(a){v=a}},donut:{get:function(){return r},set:function(a){r=a}},growOnHover:{get:function(){return t},set:function(a){t=a}},margin:{get:function(){return c},set:function(a){c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left}},y:{get:function(){return h},set:function(a){h=d3.functor(a)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},labelType:{get:function(){return p},set:function(a){p=a||"key"}}}),a.utils.initOptions(b),b},a.models.pieChart=function(){"use strict";function b(i){return r.reset(),r.models(c),i.each(function(i){var j=d3.select(this);a.utils.initSVG(j);var k=(f||parseInt(j.style("width"),10)||960)-e.left-e.right,o=(g||parseInt(j.style("height"),10)||400)-e.top-e.bottom;if(b.update=function(){j.transition().call(b)},b.container=this,l.setter(t(i),b.update).getter(s(i)).update(),l.disabled=i.map(function(a){return!!a.disabled}),!m){var q;m={};for(q in l)m[q]=l[q]instanceof Array?l[q].slice(0):l[q]}if(!i||!i.length){var r=j.selectAll(".nv-noData").data([n]);return r.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),r.attr("x",e.left+k/2).attr("y",e.top+o/2).text(function(a){return a}),b}j.selectAll(".nv-noData").remove();var u=j.selectAll("g.nv-wrap.nv-pieChart").data([i]),v=u.enter().append("g").attr("class","nvd3 nv-wrap nv-pieChart").append("g"),w=u.select("g");v.append("g").attr("class","nv-pieWrap"),v.append("g").attr("class","nv-legendWrap"),h&&(d.width(k).key(c.x()),u.select(".nv-legendWrap").datum(i).call(d),e.top!=d.height()&&(e.top=d.height(),o=(g||parseInt(j.style("height"))||400)-e.top-e.bottom),u.select(".nv-legendWrap").attr("transform","translate(0,"+-e.top+")")),u.attr("transform","translate("+e.left+","+e.top+")"),c.width(k).height(o);var x=w.select(".nv-pieWrap").datum([i]);d3.transition(x).call(c),d.dispatch.on("stateChange",function(a){for(var c in a)l[c]=a[c];p.stateChange(l),b.update()}),c.dispatch.on("elementMouseout.tooltip",function(a){p.tooltipHide(a)}),p.on("changeState",function(a){"undefined"!=typeof a.disabled&&(i.forEach(function(b,c){b.disabled=a.disabled[c]}),l.disabled=a.disabled),b.update()})}),r.renderEnd("pieChart immediate"),b}var c=a.models.pie(),d=a.models.legend(),e={top:30,right:20,bottom:20,left:20},f=null,g=null,h=!0,i=a.utils.defaultColor(),j=!0,k=function(a,b,c){return'

'+a+"

"+b+"

"},l=a.utils.state(),m=null,n="No Data Available.",o=250,p=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),q=function(d,e){var f=c.x()(d.point),g=d.pos[0]+(e&&e.offsetLeft||0),h=d.pos[1]+(e&&e.offsetTop||0),i=c.valueFormat()(c.y()(d.point)),j=k(f,i,d,b);a.tooltip.show([g,h],j,d.value<0?"n":"s",null,e)},r=a.utils.renderWatch(p),s=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},t=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return c.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+e.left,a.pos[1]+e.top],p.tooltipShow(a)}),p.on("tooltipShow",function(a){j&&q(a)}),p.on("tooltipHide",function(){j&&a.tooltip.cleanup()}),b.legend=d,b.dispatch=p,b.pie=c,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return n},set:function(a){n=a}},tooltipContent:{get:function(){return k},set:function(a){k=a}},tooltips:{get:function(){return j},set:function(a){j=a}},showLegend:{get:function(){return h},set:function(a){h=a}},defaultState:{get:function(){return m},set:function(a){m=a}},color:{get:function(){return i},set:function(a){i=a,d.color(i),c.color(i)}},duration:{get:function(){return o},set:function(a){o=a,r.reset(o)}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.scatter=function(){"use strict";function b(N){return P.reset(),N.each(function(b){function N(){if(!w)return!1;var a=d3.merge(b.map(function(a,b){return a.values.map(function(a,c){var d=o(a,c),e=p(a,c);return[l(d)+1e-7*Math.random(),m(e)+1e-7*Math.random(),b,c,a]}).filter(function(a,b){return y(a[4],b)})}));if(M===!0){if(C){var c=U.select("defs").selectAll(".nv-point-clips").data([k]).enter();c.append("clipPath").attr("class","nv-point-clips").attr("id","nv-points-clip-"+k);var d=U.select("#nv-points-clip-"+k).selectAll("circle").data(a);d.enter().append("circle").attr("r",D),d.exit().remove(),d.attr("cx",function(a){return a[0]}).attr("cy",function(a){return a[1]}),U.select(".nv-point-paths").attr("clip-path","url(#nv-points-clip-"+k+")")}a.length&&(a.push([l.range()[0]-20,m.range()[0]-20,null,null]),a.push([l.range()[1]+20,m.range()[1]+20,null,null]),a.push([l.range()[0]-20,m.range()[0]+20,null,null]),a.push([l.range()[1]+20,m.range()[1]-20,null,null]));var e=d3.geom.polygon([[-10,-10],[-10,i+10],[h+10,i+10],[h+10,-10]]),f=d3.geom.voronoi(a).map(function(b,c){return{data:e.clip(b),series:a[c][2],point:a[c][3]}}),j=U.select(".nv-point-paths").selectAll("path").data(f);j.enter().append("path").attr("class",function(a,b){return"nv-path-"+b}),j.exit().remove(),j.attr("d",function(a){return a&&a.data&&0!==a.data.length?"M"+a.data.join("L")+"Z":"M 0 0"});var n=function(a,c){if(O)return 0;var d=b[a.series];if("undefined"!=typeof d){var e=d.values[a.point];c({point:e,series:d,pos:[l(o(e,a.point))+g.left,m(p(e,a.point))+g.top],seriesIndex:a.series,pointIndex:a.point})}};j.on("click",function(a){n(a,L.elementClick)}).on("dblclick",function(a){n(a,L.elementDblClick)}).on("mouseover",function(a){n(a,L.elementMouseover)}).on("mouseout",function(a){n(a,L.elementMouseout)})}else U.select(".nv-groups").selectAll(".nv-group").selectAll(".nv-point").on("click",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementClick({point:e,series:d,pos:[l(o(e,c))+g.left,m(p(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("mouseover",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseover({point:e,series:d,pos:[l(o(e,c))+g.left,m(p(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("mouseout",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseout({point:e,series:d,seriesIndex:a.series,pointIndex:c})});O=!1}var Q=h-g.left-g.right,R=i-g.top-g.bottom,S=d3.select(this);a.utils.initSVG(S),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var T=E&&F&&I?[]:d3.merge(b.map(function(a){return a.values.map(function(a,b){return{x:o(a,b),y:p(a,b),size:q(a,b)}})}));l.domain(E||d3.extent(T.map(function(a){return a.x}).concat(t))),l.range(z&&b[0]?G||[(Q*A+Q)/(2*b[0].values.length),Q-Q*(1+A)/(2*b[0].values.length)]:G||[0,Q]),m.domain(F||d3.extent(T.map(function(a){return a.y}).concat(u))).range(H||[R,0]),n.domain(I||d3.extent(T.map(function(a){return a.size}).concat(v))).range(J||[16,256]),(l.domain()[0]===l.domain()[1]||m.domain()[0]===m.domain()[1])&&(K=!0),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),isNaN(l.domain()[0])&&l.domain([-1,1]),isNaN(m.domain()[0])&&m.domain([-1,1]),c=c||l,d=d||m,e=e||n;var U=S.selectAll("g.nv-wrap.nv-scatter").data([b]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatter nv-chart-"+k+(K?" nv-single-point":"")),W=V.append("defs"),X=V.append("g"),Y=U.select("g");X.append("g").attr("class","nv-groups"),X.append("g").attr("class","nv-point-paths"),U.attr("transform","translate("+g.left+","+g.top+")"),W.append("clipPath").attr("id","nv-edge-clip-"+k).append("rect"),U.select("#nv-edge-clip-"+k+" rect").attr("width",Q).attr("height",R>0?R:0),Y.attr("clip-path",B?"url(#nv-edge-clip-"+k+")":""),O=!0;var Z=U.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});if(Z.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),Z.exit().remove(),Z.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),Z.watchTransition(P,"scatter: groups").style("fill",function(a,b){return j(a,b)}).style("stroke",function(a,b){return j(a,b)}).style("stroke-opacity",1).style("fill-opacity",.5),s){var $=Z.selectAll("circle.nv-point").data(function(a){return a.values},x);$.enter().append("circle").style("fill",function(a){return a.color}).style("stroke",function(a){return a.color}).attr("cx",function(b,d){return a.utils.NaNtoZero(c(o(b,d)))}).attr("cy",function(b,c){return a.utils.NaNtoZero(d(p(b,c)))}).attr("r",function(a,b){return Math.sqrt(n(q(a,b))/Math.PI)}),$.exit().remove(),Z.exit().selectAll("path.nv-point").watchTransition(P,"scatter exit").attr("cx",function(b,c){return a.utils.NaNtoZero(l(o(b,c)))}).attr("cy",function(b,c){return a.utils.NaNtoZero(m(p(b,c)))}).remove(),$.each(function(a,b){d3.select(this).classed("nv-point",!0).classed("nv-point-"+b,!0).classed("hover",!1)}),$.watchTransition(P,"scatter points").attr("cx",function(b,c){return a.utils.NaNtoZero(l(o(b,c)))}).attr("cy",function(b,c){return a.utils.NaNtoZero(m(p(b,c)))}).attr("r",function(a,b){return Math.sqrt(n(q(a,b))/Math.PI)})}else{var $=Z.selectAll("path.nv-point").data(function(a){return a.values});$.enter().append("path").style("fill",function(a){return a.color}).style("stroke",function(a){return a.color}).attr("transform",function(a,b){return"translate("+c(o(a,b))+","+d(p(a,b))+")"}).attr("d",d3.svg.symbol().type(r).size(function(a,b){return n(q(a,b))})),$.exit().remove(),Z.exit().selectAll("path.nv-point").watchTransition(P,"scatter exit").attr("transform",function(a,b){return"translate("+l(o(a,b))+","+m(p(a,b))+")"}).remove(),$.each(function(a,b){d3.select(this).classed("nv-point",!0).classed("nv-point-"+b,!0).classed("hover",!1)}),$.watchTransition(P,"scatter points").attr("transform",function(a,b){return"translate("+l(o(a,b))+","+m(p(a,b))+")"}).attr("d",d3.svg.symbol().type(r).size(function(a,b){return n(q(a,b))}))}clearTimeout(f),f=setTimeout(N,300),c=l.copy(),d=m.copy(),e=n.copy()}),P.renderEnd("scatter immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=960,i=500,j=a.utils.defaultColor(),k=Math.floor(1e5*Math.random()),l=d3.scale.linear(),m=d3.scale.linear(),n=d3.scale.linear(),o=function(a){return a.x},p=function(a){return a.y},q=function(a){return a.size||1},r=function(a){return a.shape||"circle"},s=!0,t=[],u=[],v=[],w=!0,x=null,y=function(a){return!a.notActive},z=!1,A=.1,B=!1,C=!0,D=function(){return 25},E=null,F=null,G=null,H=null,I=null,J=null,K=!1,L=d3.dispatch("elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),M=!0,N=250,O=!1,P=a.utils.renderWatch(L,N);return b.clearHighlights=function(){d3.selectAll(".nv-chart-"+k+" .nv-point.hover").classed("hover",!1)},b.highlightPoint=function(a,b,c){d3.select(".nv-chart-"+k+" .nv-series-"+a+" .nv-point-"+b).classed("hover",c)},L.on("elementMouseover.point",function(a){w&&b.highlightPoint(a.seriesIndex,a.pointIndex,!0)}),L.on("elementMouseout.point",function(a){w&&b.highlightPoint(a.seriesIndex,a.pointIndex,!1)}),b.dispatch=L,b.options=a.utils.optionsFunc.bind(b),b.x=function(a){return arguments.length?(o=d3.functor(a),b):o},b.y=function(a){return arguments.length?(p=d3.functor(a),b):p},b.size=function(a){return arguments.length?(q=d3.functor(a),b):q},b.margin=function(a){return arguments.length?(g.top="undefined"!=typeof a.top?a.top:g.top,g.right="undefined"!=typeof a.right?a.right:g.right,g.bottom="undefined"!=typeof a.bottom?a.bottom:g.bottom,g.left="undefined"!=typeof a.left?a.left:g.left,b):g},b.width=function(a){return arguments.length?(h=a,b):h},b.height=function(a){return arguments.length?(i=a,b):i},b.xScale=function(a){return arguments.length?(l=a,b):l},b.yScale=function(a){return arguments.length?(m=a,b):m},b.zScale=function(a){return arguments.length?(n=a,b):n},b.xDomain=function(a){return arguments.length?(E=a,b):E},b.yDomain=function(a){return arguments.length?(F=a,b):F},b.sizeDomain=function(a){return arguments.length?(I=a,b):I},b.xRange=function(a){return arguments.length?(G=a,b):G},b.yRange=function(a){return arguments.length?(H=a,b):H},b.sizeRange=function(a){return arguments.length?(J=a,b):J},b.forceX=function(a){return arguments.length?(t=a,b):t},b.forceY=function(a){return arguments.length?(u=a,b):u},b.forceSize=function(a){return arguments.length?(v=a,b):v},b.interactive=function(a){return arguments.length?(w=a,b):w},b.pointKey=function(a){return arguments.length?(x=a,b):x},b.pointActive=function(a){return arguments.length?(y=a,b):y},b.padData=function(a){return arguments.length?(z=a,b):z},b.padDataOuter=function(a){return arguments.length?(A=a,b):A},b.clipEdge=function(a){return arguments.length?(B=a,b):B},b.clipVoronoi=function(a){return arguments.length?(C=a,b):C},b.useVoronoi=function(a){return arguments.length?(M=a,M===!1&&(C=!1),b):M},b.clipRadius=function(a){return arguments.length?(D=a,b):D},b.color=function(c){return arguments.length?(j=a.utils.getColor(c),b):j},b.shape=function(a){return arguments.length?(r=a,b):r},b.onlyCircles=function(a){return arguments.length?(s=a,b):s},b.id=function(a){return arguments.length?(k=a,b):k},b.singlePoint=function(a){return arguments.length?(K=a,b):K},b.duration=function(a){return arguments.length?(N=a,P.reset(N),b):N},b},a.models.scatterChart=function(){"use strict";function b(B){return L.reset(),L.models(c),u&&L.models(d),v&&L.models(e),r&&L.models(h),s&&L.models(i),B.each(function(B){function C(){if(z)return X.select(".nv-point-paths").style("pointer-events","all"),!1;X.select(".nv-point-paths").style("pointer-events","none");var a=d3.mouse(this);n.distortion(y).focus(a[0]),o.distortion(y).focus(a[1]),X.select(".nv-scatterWrap").call(c),u&&X.select(".nv-x.nv-axis").call(d),v&&X.select(".nv-y.nv-axis").call(e),X.select(".nv-distributionX").datum(B.filter(function(a){return!a.disabled})).call(h),X.select(".nv-distributionY").datum(B.filter(function(a){return!a.disabled})).call(i)}var D=d3.select(this),L=this; +a.utils.initSVG(D);var Q=(k||parseInt(D.style("width"))||960)-j.left-j.right,R=(l||parseInt(D.style("height"))||400)-j.top-j.bottom;if(b.update=function(){0===I?D.call(b):D.transition().duration(I).call(b)},b.container=this,E.setter(P(B),b.update).getter(O(B)).update(),E.disabled=B.map(function(a){return!!a.disabled}),!F){var S;F={};for(S in E)F[S]=E[S]instanceof Array?E[S].slice(0):E[S]}if(!(B&&B.length&&B.filter(function(a){return a.values.length}).length)){var T=D.selectAll(".nv-noData").data([H]);return T.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),T.attr("x",j.left+Q/2).attr("y",j.top+R/2).text(function(a){return a}),b}D.selectAll(".nv-noData").remove(),J=J||n,K=K||o;var U=D.selectAll("g.nv-wrap.nv-scatterChart").data([B]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+c.id()),W=V.append("g"),X=U.select("g");if(W.append("rect").attr("class","nvd3 nv-background"),W.append("g").attr("class","nv-x nv-axis"),W.append("g").attr("class","nv-y nv-axis"),W.append("g").attr("class","nv-scatterWrap"),W.append("g").attr("class","nv-distWrap"),W.append("g").attr("class","nv-legendWrap"),W.append("g").attr("class","nv-controlsWrap"),t){var Y=x?Q/2:Q;f.width(Y),U.select(".nv-legendWrap").datum(B).call(f),j.top!=f.height()&&(j.top=f.height(),R=(l||parseInt(D.style("height"))||400)-j.top-j.bottom),U.select(".nv-legendWrap").attr("transform","translate("+(Q-Y)+","+-j.top+")")}if(x&&(g.width(180).color(["#444"]),X.select(".nv-controlsWrap").datum(N).attr("transform","translate(0,"+-j.top+")").call(g)),U.attr("transform","translate("+j.left+","+j.top+")"),w&&X.select(".nv-y.nv-axis").attr("transform","translate("+Q+",0)"),c.width(Q).height(R).color(B.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!B[b].disabled})),0!==p&&c.xDomain(null),0!==q&&c.yDomain(null),U.select(".nv-scatterWrap").datum(B.filter(function(a){return!a.disabled})).call(c),0!==p){var Z=n.domain()[1]-n.domain()[0];c.xDomain([n.domain()[0]-p*Z,n.domain()[1]+p*Z])}if(0!==q){var $=o.domain()[1]-o.domain()[0];c.yDomain([o.domain()[0]-q*$,o.domain()[1]+q*$])}(0!==q||0!==p)&&U.select(".nv-scatterWrap").datum(B.filter(function(a){return!a.disabled})).call(c),u&&(d.scale(n).ticks(d.ticks()&&d.ticks().length?d.ticks():a.utils.calcTicksX(Q/100,B)).tickSize(-R,0),X.select(".nv-x.nv-axis").attr("transform","translate(0,"+o.range()[0]+")").call(d)),v&&(e.scale(o).ticks(e.ticks()&&e.ticks().length?e.ticks():a.utils.calcTicksY(R/36,B)).tickSize(-Q,0),X.select(".nv-y.nv-axis").call(e)),r&&(h.getData(c.x()).scale(n).width(Q).color(B.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!B[b].disabled})),W.select(".nv-distWrap").append("g").attr("class","nv-distributionX"),X.select(".nv-distributionX").attr("transform","translate(0,"+o.range()[0]+")").datum(B.filter(function(a){return!a.disabled})).call(h)),s&&(i.getData(c.y()).scale(o).width(R).color(B.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!B[b].disabled})),W.select(".nv-distWrap").append("g").attr("class","nv-distributionY"),X.select(".nv-distributionY").attr("transform","translate("+(w?Q:-i.size())+",0)").datum(B.filter(function(a){return!a.disabled})).call(i)),d3.fisheye&&(X.select(".nv-background").attr("width",Q).attr("height",R),X.select(".nv-background").on("mousemove",C),X.select(".nv-background").on("click",function(){z=!z}),c.dispatch.on("elementClick.freezeFisheye",function(){z=!z})),g.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,y=a.disabled?0:2.5,X.select(".nv-background").style("pointer-events",a.disabled?"none":"all"),X.select(".nv-point-paths").style("pointer-events",a.disabled?"all":"none"),a.disabled?(n.distortion(y).focus(0),o.distortion(y).focus(0),X.select(".nv-scatterWrap").call(c),X.select(".nv-x.nv-axis").call(d),X.select(".nv-y.nv-axis").call(e)):z=!1,G.stateChange(E),b.update()}),f.dispatch.on("stateChange",function(a){for(var c in a)E[c]=a[c];G.stateChange(E),b.update()}),c.dispatch.on("elementMouseover.tooltip",function(a){d3.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",function(){return a.pos[1]-R}),d3.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",a.pos[0]+h.size()),a.pos=[a.pos[0]+j.left,a.pos[1]+j.top],G.tooltipShow(a)}),G.on("tooltipShow",function(a){A&&M(a,L.parentNode)}),G.on("changeState",function(a){"undefined"!=typeof a.disabled&&(B.forEach(function(b,c){b.disabled=a.disabled[c]}),E.disabled=a.disabled),b.update()}),J=n.copy(),K=o.copy()}),L.renderEnd("scatterChart immediate"),b}var c=a.models.scatter(),d=a.models.axis(),e=a.models.axis(),f=a.models.legend(),g=a.models.legend(),h=a.models.distribution(),i=a.models.distribution(),j={top:30,right:20,bottom:50,left:75},k=null,l=null,m=a.utils.defaultColor(),n=d3.fisheye?d3.fisheye.scale(d3.scale.linear).distortion(0):c.xScale(),o=d3.fisheye?d3.fisheye.scale(d3.scale.linear).distortion(0):c.yScale(),p=0,q=0,r=!1,s=!1,t=!0,u=!0,v=!0,w=!1,x=!!d3.fisheye,y=0,z=!1,A=!0,B=function(a,b){return""+b+""},C=function(a,b,c){return""+c+""},D=null,E=a.utils.state(),F=null,G=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),H="No Data Available.",I=250;c.xScale(n).yScale(o),d.orient("bottom").tickPadding(10),e.orient(w?"right":"left").tickPadding(10),h.axis("x"),i.axis("y"),g.updateState(!1);var J,K,L=a.utils.renderWatch(G,I),M=function(f,g){var h=f.pos[0]+(g.offsetLeft||0),i=f.pos[1]+(g.offsetTop||0),k=f.pos[0]+(g.offsetLeft||0),l=o.range()[0]+j.top+(g.offsetTop||0),m=n.range()[0]+j.left+(g.offsetLeft||0),p=f.pos[1]+(g.offsetTop||0),q=d.tickFormat()(c.x()(f.point,f.pointIndex)),r=e.tickFormat()(c.y()(f.point,f.pointIndex));null!=B&&a.tooltip.show([k,l],B(f.series.key,q,r,f,b),"n",1,g,"x-nvtooltip"),null!=C&&a.tooltip.show([m,p],C(f.series.key,q,r,f,b),"e",1,g,"y-nvtooltip"),null!=D&&a.tooltip.show([h,i],D(f.series.key,q,r,f,b),f.value<0?"n":"s",null,g)},N=[{key:"Magnify",disabled:!0}],O=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},P=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};c.dispatch.on("elementMouseout.tooltip",function(a){G.tooltipHide(a),d3.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",0),d3.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",i.size())}),G.on("tooltipHide",function(){A&&a.tooltip.cleanup()}),b.dispatch=G,b.scatter=c,b.legend=f,b.controls=g,b.xAxis=d,b.yAxis=e,b.distX=h,b.distY=i,b.state=E,d3.rebind(b,c,"id","interactive","pointActive","x","y","shape","size","xScale","yScale","zScale","xDomain","yDomain","xRange","yRange","sizeDomain","sizeRange","forceX","forceY","forceSize","clipVoronoi","clipRadius","useVoronoi"),b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(j.top="undefined"!=typeof a.top?a.top:j.top,j.right="undefined"!=typeof a.right?a.right:j.right,j.bottom="undefined"!=typeof a.bottom?a.bottom:j.bottom,j.left="undefined"!=typeof a.left?a.left:j.left,b):j},b.width=function(a){return arguments.length?(k=a,b):k},b.height=function(a){return arguments.length?(l=a,b):l},b.color=function(c){return arguments.length?(m=a.utils.getColor(c),f.color(m),h.color(m),i.color(m),b):m},b.showDistX=function(a){return arguments.length?(r=a,b):r},b.showDistY=function(a){return arguments.length?(s=a,b):s},b.showControls=function(a){return arguments.length?(x=a,b):x},b.showLegend=function(a){return arguments.length?(t=a,b):t},b.showXAxis=function(a){return arguments.length?(u=a,b):u},b.showYAxis=function(a){return arguments.length?(v=a,b):v},b.rightAlignYAxis=function(a){return arguments.length?(w=a,e.orient(a?"right":"left"),b):w},b.fisheye=function(a){return arguments.length?(y=a,b):y},b.xPadding=function(a){return arguments.length?(p=a,b):p},b.yPadding=function(a){return arguments.length?(q=a,b):q},b.tooltips=function(a){return arguments.length?(A=a,b):A},b.tooltipContent=function(a){return arguments.length?(D=a,b):D},b.tooltipXContent=function(a){return arguments.length?(B=a,b):B},b.tooltipYContent=function(a){return arguments.length?(C=a,b):C},b.state=function(c){return a.deprecated("scatterChart.state"),arguments.length?(E=c,b):E};for(var Q in E)b.state[Q]=E[Q];return b.defaultState=function(a){return arguments.length?(F=a,b):F},b.noData=function(a){return arguments.length?(H=a,b):H},b.duration=function(a){return arguments.length?(I=a,L.reset(I),c.duration(I),d.duration(I),e.duration(I),h.duration(I),i.duration(I),b):I},b.transitionDuration=function(c){return a.deprecated("scatterChart.transitionDuration"),b.duration(c)},b},a.models.scatterPlusLineChart=function(){"use strict";function b(z){return J.reset(),J.models(c),s&&J.models(d),t&&J.models(e),p&&J.models(h),q&&J.models(i),z.each(function(z){function A(){if(x)return W.select(".nv-point-paths").style("pointer-events","all"),!1;W.select(".nv-point-paths").style("pointer-events","none");var a=d3.mouse(this);n.distortion(w).focus(a[0]),o.distortion(w).focus(a[1]),W.select(".nv-scatterWrap").datum(z.filter(function(a){return!a.disabled})).call(c),s&&W.select(".nv-x.nv-axis").call(d),t&&W.select(".nv-y.nv-axis").call(e),W.select(".nv-distributionX").datum(z.filter(function(a){return!a.disabled})).call(h),W.select(".nv-distributionY").datum(z.filter(function(a){return!a.disabled})).call(i)}var B=d3.select(this),O=this;a.utils.initSVG(B);var P=(k||parseInt(B.style("width"))||960)-j.left-j.right,Q=(l||parseInt(B.style("height"))||400)-j.top-j.bottom;if(b.update=function(){0===G?B.call(b):B.transition().duration(G).call(b)},b.container=this,C.setter(N(z),b.update).getter(M(z)).update(),C.disabled=z.map(function(a){return!!a.disabled}),!D){var R;D={};for(R in C)D[R]=C[R]instanceof Array?C[R].slice(0):C[R]}if(!(z&&z.length&&z.filter(function(a){return a.values.length}).length)){var S=B.selectAll(".nv-noData").data([F]);return S.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),S.attr("x",j.left+P/2).attr("y",j.top+Q/2).text(function(a){return a}),J.renderEnd("scatter immediate"),b}B.selectAll(".nv-noData").remove(),n=c.xScale(),o=c.yScale(),H=H||n,I=I||o;var T=B.selectAll("g.nv-wrap.nv-scatterChart").data([z]),U=T.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+c.id()),V=U.append("g"),W=T.select("g");V.append("rect").attr("class","nvd3 nv-background").style("pointer-events","none"),V.append("g").attr("class","nv-x nv-axis"),V.append("g").attr("class","nv-y nv-axis"),V.append("g").attr("class","nv-scatterWrap"),V.append("g").attr("class","nv-regressionLinesWrap"),V.append("g").attr("class","nv-distWrap"),V.append("g").attr("class","nv-legendWrap"),V.append("g").attr("class","nv-controlsWrap"),T.attr("transform","translate("+j.left+","+j.top+")"),u&&W.select(".nv-y.nv-axis").attr("transform","translate("+P+",0)"),r&&(f.width(P/2),T.select(".nv-legendWrap").datum(z).call(f),j.top!=f.height()&&(j.top=f.height(),Q=(l||parseInt(B.style("height"))||400)-j.top-j.bottom),T.select(".nv-legendWrap").attr("transform","translate("+P/2+","+-j.top+")")),v&&(g.width(180).color(["#444"]),W.select(".nv-controlsWrap").datum(L).attr("transform","translate(0,"+-j.top+")").call(g)),c.width(P).height(Q).color(z.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!z[b].disabled})),T.select(".nv-scatterWrap").datum(z.filter(function(a){return!a.disabled})).call(c),T.select(".nv-regressionLinesWrap").attr("clip-path","url(#nv-edge-clip-"+c.id()+")");var X=T.select(".nv-regressionLinesWrap").selectAll(".nv-regLines").data(function(a){return a});X.enter().append("g").attr("class","nv-regLines");{var Y=X.selectAll(".nv-regLine").data(function(a){return[a]});Y.enter().append("line").attr("class","nv-regLine").style("stroke-opacity",0)}Y.watchTransition(J,"scatterPlusLineChart: regline").attr("x1",n.range()[0]).attr("x2",n.range()[1]).attr("y1",function(a){return o(n.domain()[0]*a.slope+a.intercept)}).attr("y2",function(a){return o(n.domain()[1]*a.slope+a.intercept)}).style("stroke",function(a,b,c){return m(a,c)}).style("stroke-opacity",function(a){return a.disabled||"undefined"==typeof a.slope||"undefined"==typeof a.intercept?0:1}),s&&(d.scale(n).ticks(d.ticks()?d.ticks():a.utils.calcTicksX(P/100,z)).tickSize(-Q,0),W.select(".nv-x.nv-axis").attr("transform","translate(0,"+o.range()[0]+")").call(d)),t&&(e.scale(o).ticks(e.ticks()?e.ticks():a.utils.calcTicksY(Q/36,z)).tickSize(-P,0),W.select(".nv-y.nv-axis").call(e)),p&&(h.getData(c.x()).scale(n).width(P).color(z.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!z[b].disabled})),V.select(".nv-distWrap").append("g").attr("class","nv-distributionX"),W.select(".nv-distributionX").attr("transform","translate(0,"+o.range()[0]+")").datum(z.filter(function(a){return!a.disabled})).call(h)),q&&(i.getData(c.y()).scale(o).width(Q).color(z.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!z[b].disabled})),V.select(".nv-distWrap").append("g").attr("class","nv-distributionY"),W.select(".nv-distributionY").attr("transform","translate("+(u?P:-i.size())+",0)").datum(z.filter(function(a){return!a.disabled})).call(i)),d3.fisheye&&(W.select(".nv-background").attr("width",P).attr("height",Q),W.select(".nv-background").on("mousemove",A),W.select(".nv-background").on("click",function(){x=!x}),c.dispatch.on("elementClick.freezeFisheye",function(){x=!x})),g.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,w=a.disabled?0:2.5,W.select(".nv-background").style("pointer-events",a.disabled?"none":"all"),W.select(".nv-point-paths").style("pointer-events",a.disabled?"all":"none"),a.disabled?(n.distortion(w).focus(0),o.distortion(w).focus(0),W.select(".nv-scatterWrap").call(c),W.select(".nv-x.nv-axis").call(d),W.select(".nv-y.nv-axis").call(e)):x=!1,C.fisheye=w,E.stateChange(C),b.update()}),f.dispatch.on("stateChange",function(a){for(var c in a)C[c]=a[c];E.stateChange(C),b.update()}),c.dispatch.on("elementMouseover.tooltip",function(a){d3.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",a.pos[1]-Q),d3.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",a.pos[0]+h.size()),a.pos=[a.pos[0]+j.left,a.pos[1]+j.top],E.tooltipShow(a)}),E.on("tooltipShow",function(a){y&&K(a,O.parentNode)}),E.on("changeState",function(a){"undefined"!=typeof a.disabled&&(z.forEach(function(b,c){b.disabled=a.disabled[c]}),C.disabled=a.disabled),"undefined"!=typeof a.fisheye&&(C.fisheye=a.fisheye,w=a.fisheye),b.update()}),H=n.copy(),I=o.copy()}),J.renderEnd("scatter with line immediate"),b}var c=a.models.scatter(),d=a.models.axis(),e=a.models.axis(),f=a.models.legend(),g=a.models.legend(),h=a.models.distribution(),i=a.models.distribution(),j={top:30,right:20,bottom:50,left:75},k=null,l=null,m=a.utils.defaultColor(),n=d3.fisheye?d3.fisheye.scale(d3.scale.linear).distortion(0):c.xScale(),o=d3.fisheye?d3.fisheye.scale(d3.scale.linear).distortion(0):c.yScale(),p=!1,q=!1,r=!0,s=!0,t=!0,u=!1,v=!!d3.fisheye,w=0,x=!1,y=!0,z=function(a,b){return""+b+""},A=function(a,b,c){return""+c+""},B=function(a,b,c,d){return"

"+a+"

"+d+"

"},C=a.utils.state(),D=null,E=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),F="No Data Available.",G=250;C.fisheye=0,c.xScale(n).yScale(o),d.orient("bottom").tickPadding(10),e.orient(u?"right":"left").tickPadding(10),h.axis("x"),i.axis("y"),g.updateState(!1);var H,I,J=a.utils.renderWatch(E,G),K=function(f,g){var h=f.pos[0]+(g.offsetLeft||0),i=f.pos[1]+(g.offsetTop||0),k=f.pos[0]+(g.offsetLeft||0),l=o.range()[0]+j.top+(g.offsetTop||0),m=n.range()[0]+j.left+(g.offsetLeft||0),p=f.pos[1]+(g.offsetTop||0),q=d.tickFormat()(c.x()(f.point,f.pointIndex)),r=e.tickFormat()(c.y()(f.point,f.pointIndex));null!=z&&a.tooltip.show([k,l],z(f.series.key,q,r,f,b),"n",1,g,"x-nvtooltip"),null!=A&&a.tooltip.show([m,p],A(f.series.key,q,r,f,b),"e",1,g,"y-nvtooltip"),null!=B&&a.tooltip.show([h,i],B(f.series.key,q,r,f.point.tooltip,f,b),f.value<0?"n":"s",null,g)},L=[{key:"Magnify",disabled:!0}],M=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),fisheye:w}}},N=function(a){return function(b){void 0!==b.fisheye&&(w=b.fisheye),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};c.dispatch.on("elementMouseout.tooltip",function(a){E.tooltipHide(a),d3.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",0),d3.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",i.size())}),E.on("tooltipHide",function(){y&&a.tooltip.cleanup()}),b.dispatch=E,b.scatter=c,b.legend=f,b.controls=g,b.xAxis=d,b.yAxis=e,b.distX=h,b.distY=i,b.state=C,d3.rebind(b,c,"id","interactive","pointActive","x","y","shape","size","xScale","yScale","zScale","xDomain","yDomain","xRange","yRange","sizeDomain","sizeRange","forceX","forceY","forceSize","clipVoronoi","clipRadius","useVoronoi"),b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(j.top="undefined"!=typeof a.top?a.top:j.top,j.right="undefined"!=typeof a.right?a.right:j.right,j.bottom="undefined"!=typeof a.bottom?a.bottom:j.bottom,j.left="undefined"!=typeof a.left?a.left:j.left,b):j},b.width=function(a){return arguments.length?(k=a,b):k},b.height=function(a){return arguments.length?(l=a,b):l},b.color=function(c){return arguments.length?(m=a.utils.getColor(c),f.color(m),h.color(m),i.color(m),b):m},b.showDistX=function(a){return arguments.length?(p=a,b):p},b.showDistY=function(a){return arguments.length?(q=a,b):q},b.showControls=function(a){return arguments.length?(v=a,b):v},b.showLegend=function(a){return arguments.length?(r=a,b):r},b.showXAxis=function(a){return arguments.length?(s=a,b):s},b.showYAxis=function(a){return arguments.length?(t=a,b):t},b.rightAlignYAxis=function(a){return arguments.length?(u=a,e.orient(a?"right":"left"),b):u},b.fisheye=function(a){return arguments.length?(w=a,b):w},b.tooltips=function(a){return arguments.length?(y=a,b):y},b.tooltipContent=function(a){return arguments.length?(B=a,b):B},b.tooltipXContent=function(a){return arguments.length?(z=a,b):z},b.tooltipYContent=function(a){return arguments.length?(A=a,b):A},b.state=function(c){return a.deprecated("scatterPlusLineChart.state"),arguments.length?(C=c,b):C};for(var O in C)b.state[O]=C[O];return b.defaultState=function(a){return arguments.length?(D=a,b):D},b.noData=function(a){return arguments.length?(F=a,b):F},b.transitionDuration=function(c){return a.deprecated("scatterPlusLineChart.transitionDuration"),b.duration(c)},b.duration=function(a){return arguments.length?(G=a,b):G},b},a.models.sparkline=function(){"use strict";function b(j){return j.each(function(b){var j=h-g.left-g.right,p=i-g.top-g.bottom,q=d3.select(this);a.utils.initSVG(q),k.domain(c||d3.extent(b,m)).range(e||[0,j]),l.domain(d||d3.extent(b,n)).range(f||[p,0]);{var r=q.selectAll("g.nv-wrap.nv-sparkline").data([b]),s=r.enter().append("g").attr("class","nvd3 nv-wrap nv-sparkline");s.append("g"),r.select("g")}r.attr("transform","translate("+g.left+","+g.top+")");var t=r.selectAll("path").data(function(a){return[a]});t.enter().append("path"),t.exit().remove(),t.style("stroke",function(a,b){return a.color||o(a,b)}).attr("d",d3.svg.line().x(function(a,b){return k(m(a,b))}).y(function(a,b){return l(n(a,b))}));var u=r.selectAll("circle.nv-point").data(function(a){function b(b){if(-1!=b){var c=a[b];return c.pointIndex=b,c}return null}var c=a.map(function(a,b){return n(a,b)}),d=b(c.lastIndexOf(l.domain()[1])),e=b(c.indexOf(l.domain()[0])),f=b(c.length-1);return[e,d,f].filter(function(a){return null!=a})});u.enter().append("circle"),u.exit().remove(),u.attr("cx",function(a){return k(m(a,a.pointIndex))}).attr("cy",function(a){return l(n(a,a.pointIndex))}).attr("r",2).attr("class",function(a){return m(a,a.pointIndex)==k.domain()[1]?"nv-point nv-currentValue":n(a,a.pointIndex)==l.domain()[0]?"nv-point nv-minValue":"nv-point nv-maxValue"})}),b}var c,d,e,f,g={top:2,right:0,bottom:2,left:0},h=400,i=32,j=!0,k=d3.scale.linear(),l=d3.scale.linear(),m=function(a){return a.x},n=function(a){return a.y},o=a.utils.getColor(["#000"]);return b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(g.top="undefined"!=typeof a.top?a.top:g.top,g.right="undefined"!=typeof a.right?a.right:g.right,g.bottom="undefined"!=typeof a.bottom?a.bottom:g.bottom,g.left="undefined"!=typeof a.left?a.left:g.left,b):g},b.width=function(a){return arguments.length?(h=a,b):h},b.height=function(a){return arguments.length?(i=a,b):i},b.x=function(a){return arguments.length?(m=d3.functor(a),b):m},b.y=function(a){return arguments.length?(n=d3.functor(a),b):n},b.xScale=function(a){return arguments.length?(k=a,b):k},b.yScale=function(a){return arguments.length?(l=a,b):l},b.xDomain=function(a){return arguments.length?(c=a,b):c},b.yDomain=function(a){return arguments.length?(d=a,b):d},b.xRange=function(a){return arguments.length?(e=a,b):e},b.yRange=function(a){return arguments.length?(f=a,b):f},b.animate=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(o=a.utils.getColor(c),b):o},b},a.models.sparklinePlus=function(){"use strict";function b(m){return m.each(function(q){function r(){if(!j){var a=B.selectAll(".nv-hoverValue").data(i),b=a.enter().append("g").attr("class","nv-hoverValue").style("stroke-opacity",0).style("fill-opacity",0);a.exit().transition().duration(250).style("stroke-opacity",0).style("fill-opacity",0).remove(),a.attr("transform",function(a){return"translate("+c(e.x()(q[a],a))+",0)"}).transition().duration(250).style("stroke-opacity",1).style("fill-opacity",1),i.length&&(b.append("line").attr("x1",0).attr("y1",-f.top).attr("x2",0).attr("y2",v),b.append("text").attr("class","nv-xValue").attr("x",-6).attr("y",-f.top).attr("text-anchor","end").attr("dy",".9em"),B.select(".nv-hoverValue .nv-xValue").text(k(e.x()(q[i[0]],i[0]))),b.append("text").attr("class","nv-yValue").attr("x",6).attr("y",-f.top).attr("text-anchor","start").attr("dy",".9em"),B.select(".nv-hoverValue .nv-yValue").text(l(e.y()(q[i[0]],i[0]))))}}function s(){function a(a,b){for(var c=Math.abs(e.x()(a[0],0)-b),d=0,f=0;fc;++c){for(b=0,d=0;bb;b++)a[b][c][1]/=d;else for(b=0;e>b;b++)a[b][c][1]=g}for(c=0;f>c;++c)h[c]=0;return h}}),t.renderEnd("stackedArea immediate"),b}var c,d,e={top:0,right:0,bottom:0,left:0},f=960,g=500,h=a.utils.defaultColor(),i=Math.floor(1e5*Math.random()),j=function(a){return a.x},k=function(a){return a.y},l="stack",m="zero",n="default",o="linear",p=!1,q=a.models.scatter(),r=250,s=d3.dispatch("tooltipShow","tooltipHide","areaClick","areaMouseover","areaMouseout","renderEnd");q.interactive(!1),q.size(2.2).sizeDomain([2.2,2.2]);var t=a.utils.renderWatch(s,r);return q.dispatch.on("elementClick.area",function(a){s.areaClick(a)}),q.dispatch.on("elementMouseover.tooltip",function(a){a.pos=[a.pos[0]+e.left,a.pos[1]+e.top],s.tooltipShow(a)}),q.dispatch.on("elementMouseout.tooltip",function(a){s.tooltipHide(a)}),b.dispatch=s,b.scatter=q,d3.rebind(b,q,"interactive","size","xScale","yScale","zScale","xDomain","yDomain","xRange","yRange","sizeDomain","forceX","forceY","forceSize","clipVoronoi","useVoronoi","clipRadius","highlightPoint","clearHighlights"),b.options=a.utils.optionsFunc.bind(b),b.x=function(a){return arguments.length?(j=d3.functor(a),b):j},b.y=function(a){return arguments.length?(k=d3.functor(a),b):k},b.margin=function(a){return arguments.length?(e.top="undefined"!=typeof a.top?a.top:e.top,e.right="undefined"!=typeof a.right?a.right:e.right,e.bottom="undefined"!=typeof a.bottom?a.bottom:e.bottom,e.left="undefined"!=typeof a.left?a.left:e.left,b):e},b.width=function(a){return arguments.length?(f=a,b):f},b.height=function(a){return arguments.length?(g=a,b):g},b.clipEdge=function(a){return arguments.length?(p=a,b):p},b.color=function(c){return arguments.length?(h=a.utils.getColor(c),b):h},b.offset=function(a){return arguments.length?(m=a,b):m},b.order=function(a){return arguments.length?(n=a,b):n},b.style=function(a){if(!arguments.length)return l;switch(l=a){case"stack":b.offset("zero"),b.order("default");break;case"stream":b.offset("wiggle"),b.order("inside-out");break;case"stream-center":b.offset("silhouette"),b.order("inside-out");break;case"expand":b.offset("expand"),b.order("default");break;case"stack_percent":b.offset(b.d3_stackedOffset_stackPercent),b.order("default")}return b},b.interpolate=function(a){return arguments.length?(o=a,b):o},b.duration=function(a){return arguments.length?(r=a,t.reset(r),q.duration(r),b):r},b},a.models.stackedAreaChart=function(){"use strict";function b(v){return F.reset(),F.models(e),q&&F.models(f),r&&F.models(g),v.each(function(v){var F=d3.select(this),K=this;a.utils.initSVG(F);var L=(l||parseInt(F.style("width"))||960)-k.left-k.right,M=(m||parseInt(F.style("height"))||400)-k.top-k.bottom;if(b.update=function(){F.transition().duration(E).call(b)},b.container=this,x.setter(J(v),b.update).getter(I(v)).update(),x.disabled=v.map(function(a){return!!a.disabled}),!y){var N;y={};for(N in x)y[N]=x[N]instanceof Array?x[N].slice(0):x[N]}if(!(v&&v.length&&v.filter(function(a){return a.values.length}).length)){var O=F.selectAll(".nv-noData").data([z]);return O.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),O.attr("x",k.left+L/2).attr("y",k.top+M/2).text(function(a){return a}),b}F.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var P=F.selectAll("g.nv-wrap.nv-stackedAreaChart").data([v]),Q=P.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedAreaChart").append("g"),R=P.select("g");if(Q.append("rect").style("opacity",0),Q.append("g").attr("class","nv-x nv-axis"),Q.append("g").attr("class","nv-y nv-axis"),Q.append("g").attr("class","nv-stackedWrap"),Q.append("g").attr("class","nv-legendWrap"),Q.append("g").attr("class","nv-controlsWrap"),Q.append("g").attr("class","nv-interactive"),R.select("rect").attr("width",L).attr("height",M),p){var S=o?L-B:L;h.width(S),R.select(".nv-legendWrap").datum(v).call(h),k.top!=h.height()&&(k.top=h.height(),M=(m||parseInt(F.style("height"))||400)-k.top-k.bottom),R.select(".nv-legendWrap").attr("transform","translate("+(L-S)+","+-k.top+")") +}if(o){var T=[{key:D.stacked||"Stacked",metaKey:"Stacked",disabled:"stack"!=e.style(),style:"stack"},{key:D.stream||"Stream",metaKey:"Stream",disabled:"stream"!=e.style(),style:"stream"},{key:D.expanded||"Expanded",metaKey:"Expanded",disabled:"expand"!=e.style(),style:"expand"},{key:D.stack_percent||"Stack %",metaKey:"Stack_Percent",disabled:"stack_percent"!=e.style(),style:"stack_percent"}];B=C.length/3*260,T=T.filter(function(a){return-1!==C.indexOf(a.metaKey)}),i.width(B).color(["#444","#444","#444"]),R.select(".nv-controlsWrap").datum(T).call(i),k.top!=Math.max(i.height(),h.height())&&(k.top=Math.max(i.height(),h.height()),M=(m||parseInt(F.style("height"))||400)-k.top-k.bottom),R.select(".nv-controlsWrap").attr("transform","translate(0,"+-k.top+")")}P.attr("transform","translate("+k.left+","+k.top+")"),s&&R.select(".nv-y.nv-axis").attr("transform","translate("+L+",0)"),t&&(j.width(L).height(M).margin({left:k.left,top:k.top}).svgContainer(F).xScale(c),P.select(".nv-interactive").call(j)),e.width(L).height(M);var U=R.select(".nv-stackedWrap").datum(v);U.transition().call(e),q&&(f.scale(c).ticks(a.utils.calcTicksX(L/100,v)).tickSize(-M,0),R.select(".nv-x.nv-axis").attr("transform","translate(0,"+M+")"),R.select(".nv-x.nv-axis").transition().duration(0).call(f)),r&&(g.scale(d).ticks("wiggle"==e.offset()?0:a.utils.calcTicksY(M/36,v)).tickSize(-L,0).setTickFormat("expand"==e.style()||"stack_percent"==e.style()?d3.format("%"):w),R.select(".nv-y.nv-axis").transition().duration(0).call(g)),e.dispatch.on("areaClick.toggle",function(a){v.forEach(1===v.filter(function(a){return!a.disabled}).length?function(a){a.disabled=!1}:function(b,c){b.disabled=c!=a.seriesIndex}),x.disabled=v.map(function(a){return!!a.disabled}),A.stateChange(x),b.update()}),h.dispatch.on("stateChange",function(a){for(var c in a)x[c]=a[c];A.stateChange(x),b.update()}),i.dispatch.on("legendClick",function(a){a.disabled&&(T=T.map(function(a){return a.disabled=!0,a}),a.disabled=!1,e.style(a.style),x.style=e.style(),A.stateChange(x),b.update())}),j.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,i,l=[];if(v.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,g){h=a.interactiveBisect(f.values,c.pointXValue,b.x()),e.highlightPoint(g,h,!0);var j=f.values[h];if("undefined"!=typeof j){"undefined"==typeof d&&(d=j),"undefined"==typeof i&&(i=b.xScale()(b.x()(j,h)));var k="expand"==e.style()?j.display.y:b.y()(j,h);l.push({key:f.key,value:k,color:n(f,f.seriesIndex),stackedValue:j.display})}}),l.reverse(),l.length>2){var m=b.yScale().invert(c.mouseY),o=null;l.forEach(function(a,b){m=Math.abs(m);var c=Math.abs(a.stackedValue.y0),d=Math.abs(a.stackedValue.y);return m>=c&&d+c>=m?void(o=b):void 0}),null!=o&&(l[o].highlight=!0)}var p=f.tickFormat()(b.x()(d,h)),q="expand"==e.style()?function(a){return d3.format(".1%")(a)}:function(a){return g.tickFormat()(a)};j.tooltip.position({left:i+k.left,top:c.mouseY+k.top}).chartContainer(K.parentNode).enabled(u).valueFormatter(q).data({value:p,series:l})(),j.renderGuideLine(i)}),j.dispatch.on("elementMouseout",function(){A.tooltipHide(),e.clearHighlights()}),A.on("tooltipShow",function(a){u&&H(a,K.parentNode)}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&v.length===a.disabled.length&&(v.forEach(function(b,c){b.disabled=a.disabled[c]}),x.disabled=a.disabled),"undefined"!=typeof a.style&&(e.style(a.style),G=a.style),b.update()})}),F.renderEnd("stacked Area chart immediate"),b}var c,d,e=a.models.stackedArea(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.interactiveGuideline(),k={top:30,right:25,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p=!0,q=!0,r=!0,s=!1,t=!1,u=!0,v=function(a,b,c){return"

"+a+"

"+c+" on "+b+"

"},w=d3.format(",.2f"),x=a.utils.state(),y=null,z="No Data Available.",A=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),B=250,C=["Stacked","Stream","Expanded"],D={},E=250;x.style=e.style(),f.orient("bottom").tickPadding(7),g.orient(s?"right":"left"),i.updateState(!1);var F=a.utils.renderWatch(A),G=e.style(),H=function(c,d){var h=c.pos[0]+(d.offsetLeft||0),i=c.pos[1]+(d.offsetTop||0),j=f.tickFormat()(e.x()(c.point,c.pointIndex)),k=g.tickFormat()(e.y()(c.point,c.pointIndex)),l=v(c.series.key,j,k,c,b);a.tooltip.show([h,i],l,c.value<0?"n":"s",null,d)},I=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),style:e.style()}}},J=function(a){return function(b){void 0!==b.style&&(G=b.style),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};e.dispatch.on("tooltipShow",function(a){a.pos=[a.pos[0]+k.left,a.pos[1]+k.top],A.tooltipShow(a)}),e.dispatch.on("tooltipHide",function(a){A.tooltipHide(a)}),A.on("tooltipHide",function(){u&&a.tooltip.cleanup()}),b.dispatch=A,b.stacked=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.interactiveLayer=j,b.state=x,d3.rebind(b,e,"x","y","size","xScale","yScale","xDomain","yDomain","xRange","yRange","sizeDomain","interactive","useVoronoi","offset","order","style","clipEdge","forceX","forceY","forceSize","interpolate"),b.options=a.utils.optionsFunc.bind(b),b.margin=function(a){return arguments.length?(k.top="undefined"!=typeof a.top?a.top:k.top,k.right="undefined"!=typeof a.right?a.right:k.right,k.bottom="undefined"!=typeof a.bottom?a.bottom:k.bottom,k.left="undefined"!=typeof a.left?a.left:k.left,b):k},b.width=function(a){return arguments.length?(l=a,b):l},b.height=function(a){return arguments.length?(m=a,b):m},b.color=function(c){return arguments.length?(n=a.utils.getColor(c),h.color(n),e.color(n),b):n},b.showControls=function(a){return arguments.length?(o=a,b):o},b.showLegend=function(a){return arguments.length?(p=a,b):p},b.showXAxis=function(a){return arguments.length?(q=a,b):q},b.showYAxis=function(a){return arguments.length?(r=a,b):r},b.rightAlignYAxis=function(a){return arguments.length?(s=a,g.orient(a?"right":"left"),b):s},b.useInteractiveGuideline=function(a){return arguments.length?(t=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1)),b):t},b.tooltip=function(a){return arguments.length?(v=a,b):v},b.tooltips=function(a){return arguments.length?(u=a,b):u},b.tooltipContent=function(a){return arguments.length?(v=a,b):v},b.state=function(c){return a.deprecated("stackedAreaChart.state"),arguments.length?(x=c,b):x};for(var K in x)b.state[K]=x[K];return b.defaultState=function(a){return arguments.length?(y=a,b):y},b.noData=function(a){return arguments.length?(z=a,b):z},b.transitionDuration=function(c){return a.deprecated("lineChart.transitionDuration"),b.duration(c)},b.controlsData=function(a){return arguments.length?(C=a,b):C},b.controlLabels=function(a){return arguments.length?"object"!=typeof a?D:(D=a,b):D},g.setTickFormat=g.tickFormat,g.tickFormat=function(a){return arguments.length?(w=a,g):w},b.duration=function(a){return arguments.length?(E=a,F.reset(E),e.duration(E),f.duration(E),g.duration(E),b):E},b},a.version="1.6.0"}(); \ No newline at end of file diff --git a/xonstat/static/js/weaponCharts.js b/xonstat/static/js/weaponCharts.js index dcd743e..c2a7049 100644 --- a/xonstat/static/js/weaponCharts.js +++ b/xonstat/static/js/weaponCharts.js @@ -1,13 +1,7 @@ -var weapons = ["laser", "shotgun", "uzi", "grenadelauncher", "minelayer", "electro", - "crylink", "nex", "hagar", "rocketlauncher", "porto", "minstanex", "hook", "hlac", - "seeker", "rifle", "tuba", "fireball"]; - var weaponColors = ["#ff5933", "#b2b2b2", "#66e559", "#ff2600", "#bfbf00", "#597fff", "#d83fff", "#00e5ff", "#d87f59", "#ffbf33", "#7fff7f", "#a5a5ff", "#a5ffd8", "#ffa533", "#ff5959", "#d87f3f", "#d87f3f", "#33ff33"]; -var colorScale = d3.scale.ordinal().domain(weapons).range(weaponColors); - var drawDamageChart = function(data) { // the chart should fill the "damageChart" div var width = document.getElementById("damageChart").offsetWidth; @@ -26,30 +20,23 @@ var drawDamageChart = function(data) { width -= margin.left - margin.right; - // colors - keyColor = function(d, i) {return colorScale(d.key)}; - - var chart; nv.addGraph(function() { chart = nv.models.stackedAreaChart() .margin(margin) .width(width) .height(height) + .color(weaponColors) .x(function(d) { return games[d.game_id] }) .y(function(d) { return d.actual }) + .useInteractiveGuideline(true) + .controlsData(['Stacked','Expanded']) + .tooltips(true) .tooltip(function(key, x, y, e, graph) { - return '

' + key + '

' + '

' + y + ' damage in game #' + x + '

' - }) - .color(keyColor); - - chart.xAxis - .axisLabel("Game ID") - .showMaxMin(false) - .ticks(5) - .tickFormat(function(d) { return data.games[d]; }); + return '

' + key + '

' + '

' + y + ' damage in game #' + x + '

'; + }); - chart.yAxis - .tickFormat(d3.format(',02d')); + chart.xAxis.tickFormat(function(d) { return ''; }); + chart.yAxis.tickFormat(d3.format(',02d')); d3.select('#damageChartSVG') .datum(transformedData) @@ -91,34 +78,21 @@ var drawAccuracyChart = function(data) { width -= margin.left - margin.right; - // colors - keyColor = function(d, i) {return colorScale(d.key)}; - - var chart; nv.addGraph(function() { chart = nv.models.lineChart() .margin(margin) .width(width) .height(height) + .color(weaponColors) .forceY([0,1]) .x(function(d) { return games[d.game_id] }) - .y(function(d) { - if(d.fired > 0) { - return d.hit/d.fired; - } else { - return 0; - } - }) - .tooltip(function(key, x, y, e, graph) { + .y(function(d) { return d.fired > 0 ? d.hit/d.fired : 0; }) + .tooltips(true) + .tooltipContent(function(key, x, y, e, graph) { return '

' + key + '

' + '

' + y + ' accuracy in game #' + x + '
' + data.averages[key] + '% average over ' + findNumGames(key) + ' games

'; - }) - .color(keyColor); - - chart.xAxis - .axisLabel("Game ID") - .showMaxMin(false) - .ticks(5) - .tickFormat(function(d) { return data.games[d]; }); + }); + + chart.xAxis.tickFormat(function(d) { return ''; }); var yScale = d3.scale.linear().domain([0,1]).range([0,height]); chart.yAxis