Return-Path: X-Original-To: apmail-couchdb-commits-archive@www.apache.org Delivered-To: apmail-couchdb-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 4FD91103FB for ; Mon, 24 Nov 2014 10:45:17 +0000 (UTC) Received: (qmail 94294 invoked by uid 500); 24 Nov 2014 10:45:17 -0000 Delivered-To: apmail-couchdb-commits-archive@couchdb.apache.org Received: (qmail 94112 invoked by uid 500); 24 Nov 2014 10:45:17 -0000 Mailing-List: contact commits-help@couchdb.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@couchdb.apache.org Delivered-To: mailing list commits@couchdb.apache.org Received: (qmail 94096 invoked by uid 99); 24 Nov 2014 10:45:17 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 24 Nov 2014 10:45:17 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id D03C5A1636A; Mon, 24 Nov 2014 10:45:16 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: robertkowalski@apache.org To: commits@couchdb.apache.org Date: Mon, 24 Nov 2014 10:45:16 -0000 Message-Id: <245103563a4f4d9e96f0577a71a9e262@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [1/2] fauxton commit: updated refs/heads/master to bff1eb9 Repository: couchdb-fauxton Updated Branches: refs/heads/master 8b8d5e22f -> bff1eb9e7 http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/bff1eb9e/assets/js/libs/nv.d3.js ---------------------------------------------------------------------- diff --git a/assets/js/libs/nv.d3.js b/assets/js/libs/nv.d3.js deleted file mode 100755 index 983b901..0000000 --- a/assets/js/libs/nv.d3.js +++ /dev/null @@ -1,14374 +0,0 @@ -//this is a hacky fix because nv.d3 doesn't support requirejs -define("nv.d3", ["d3"], function (d3) { - var nv; -(function(){ - - nv = window.nv || {}; - - -nv.version = '1.1.15b'; -nv.dev = true //set false when in production - -window.nv = nv; - -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 - -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 - }); -} - -// ******************************************** -// Public Core NV functions - -// 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]; -}; - - -nv.render = function render(step) { - step = step || 1; // number of graphs to generate in each timeout loop - - nv.render.active = true; - nv.dispatch.render_start(); - - setTimeout(function() { - var chart, graph; - - for (var i = 0; i < step && (graph = nv.render.queue[i]); i++) { - chart = graph.generate(); - if (typeof graph.callback == typeof(Function)) graph.callback(chart); - nv.graphs.push(chart); - } - - nv.render.queue.splice(0, i); - - if (nv.render.queue.length) setTimeout(arguments.callee, 0); - else { - nv.dispatch.render_end(); - nv.render.active = false; - } - }, 0); -}; - -nv.render.active = false; -nv.render.queue = []; - -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,''); }; - -function daysInMonth(month,year) { - return (new Date(year, month+1, 0)).getDate(); -} - -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); } - } - return times; - }; -} - -d3.time.monthEnd = function(date) { - return new Date(date.getFullYear(), date.getMonth(), 0); -}; - -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','elementDblclick') - , showGuideLine = true - , svgContainer = null - //Must pass in the bounding chart's container. - //The mousemove event is attached to this container. - ; - - //Private variables - var isMSIE = navigator.userAgent.indexOf("MSIE") !== -1 //Check user-agent for Microsoft Internet Explorer. - ; - - - 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; - - if (d3.event.target.className.baseVal.match("nv-legend")) - mouseOutAnyReason = true; - - } - - if(subtractMargin) { - mouseX -= margin.left; - mouseY -= margin.top; - } - - /* If mouseX/Y is outside of the chart's bounds, - trigger a mouseOut event. - */ - if (mouseX < 0 || mouseY < 0 - || mouseX > availableWidth || mouseY > availableHeight - || (d3.event.relatedTarget && d3.event.relatedTarget.ownerSVGElement === undefined) - || mouseOutAnyReason - ) - { - if (isMSIE) { - if (d3.event.relatedTarget - && d3.event.relatedTarget.ownerSVGElement === undefined - && d3.event.relatedTarget.className.match(tooltip.nvPointerEventsClass)) { - return; - } - } - dispatch.elementMouseout({ - mouseX: mouseX, - mouseY: mouseY - }); - layer.renderGuideLine(null); //hide the guideline - return; - } - - var pointXValue = xScale.invert(mouseX); - dispatch.elementMousemove({ - mouseX: mouseX, - mouseY: mouseY, - pointXValue: pointXValue - }); - - //If user double clicks the layer, fire a elementDblclick dispatch. - if (d3.event.type === "dblclick") { - dispatch.elementDblclick({ - mouseX: mouseX, - mouseY: mouseY, - pointXValue: pointXValue - }); - } - } - - svgContainer - .on("mousemove",mouseHandler, true) - .on("mouseout" ,mouseHandler,true) - .on("dblclick" ,mouseHandler) - ; - - //Draws a vertical guideline at the given X postion. - layer.renderGuideLine = function(x) { - if (!showGuideLine) return; - var line = wrap.select(".nv-interactiveGuideLine") - .selectAll("line") - .data((x != null) ? [nv.utils.NaNtoZero(x)] : [], String); - - line.enter() - .append("line") - .attr("class", "nv-guideline") - .attr("x1", function(d) { return d;}) - .attr("x2", function(d) { return d;}) - .attr("y1", availableHeight) - .attr("y2",0) - ; - line.exit().remove(); - - } - }); - } - - layer.dispatch = dispatch; - layer.tooltip = tooltip; - - layer.margin = function(_) { - if (!arguments.length) return margin; - margin.top = typeof _.top != 'undefined' ? _.top : margin.top; - margin.left = typeof _.left != 'undefined' ? _.left : margin.left; - return layer; - }; - - layer.width = function(_) { - if (!arguments.length) return width; - width = _; - return layer; - }; - - layer.height = function(_) { - if (!arguments.length) return height; - height = _; - return layer; - }; - - layer.xScale = function(_) { - if (!arguments.length) return xScale; - xScale = _; - return layer; - }; - - layer.showGuideLine = function(_) { - if (!arguments.length) return showGuideLine; - showGuideLine = _; - return layer; - }; - - layer.svgContainer = function(_) { - if (!arguments.length) return svgContainer; - svgContainer = _; - return layer; - }; - - - return layer; -}; - -/* Utility class that uses d3.bisect to find the index in a given array, where a search value can be inserted. -This is different from normal bisectLeft; this function finds the nearest index to insert the search value. - -For instance, lets say your array is [1,2,3,5,10,30], and you search for 28. -Normal d3.bisectLeft will return 4, because 28 is inserted after the number 10. But interactiveBisect will return 5 -because 28 is closer to 30 than 10. - -Unit tests can be found in: interactiveBisectTest.html - -Has the following known issues: - * Will not work if the data points move backwards (ie, 10,9,8,7, etc) or if the data points are in random order. - * Won't work if there are duplicate x coordinate values. -*/ -nv.interactiveBisect = function (values, searchVal, xAccessor) { - "use strict"; - if (! values instanceof Array) return null; - if (typeof xAccessor !== 'function') xAccessor = function(d,i) { return d.x;} - - var bisect = d3.bisector(xAccessor).left; - var index = d3.max([0, bisect(values,searchVal) - 1]); - var currentValue = xAccessor(values[index], index); - if (typeof currentValue === 'undefined') currentValue = index; - - if (currentValue === searchVal) return index; //found exact match - - var nextIndex = d3.min([index+1, values.length - 1]); - var nextValue = xAccessor(values[nextIndex], nextIndex); - if (typeof nextValue === 'undefined') nextValue = nextIndex; - - if (Math.abs(nextValue - searchVal) >= Math.abs(currentValue - searchVal)) - return index; - else - return nextIndex -}; - -/* -Returns the index in the array "values" that is closest to searchVal. -Only returns an index if searchVal is within some "threshold". -Otherwise, returns null. -*/ -nv.nearestValueIndex = function (values, searchVal, threshold) { - "use strict"; - var yDistMax = Infinity, indexToHighlight = null; - values.forEach(function(d,i) { - var delta = Math.abs(searchVal - d); - if ( delta <= yDistMax && delta < threshold) { - yDistMax = delta; - indexToHighlight = i; - } - }); - return indexToHighlight; -};/* Tooltip rendering model for nvd3 charts. -window.nv.models.tooltip is the updated,new way to render tooltips. - -window.nv.tooltip.show is the old tooltip code. -window.nv.tooltip.* also has various helper methods. -*/ -(function() { - "use strict"; - window.nv.tooltip = {}; - - /* Model which can be instantiated to handle tooltip rendering. - Example usage: - var tip = nv.models.tooltip().gravity('w').distance(23) - .data(myDataObject); - - tip(); //just invoke the returned function to render tooltip. - */ - window.nv.models.tooltip = function() { - 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: - { - key: "Date", - value: "August 2009", - series: [ - { - 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. - , 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) - ; - - //CSS class to specify whether element should not have mouse events. - var nvPointerEventsClass = "nv-pointer-events-none"; - - //Format function for the tooltip values column - var valueFormatter = function(d,i) { - return d; - }; - - //Format function for the tooltip header value. - var headerFormatter = function(d) { - return d; - }; - - //By default, the tooltip model renders a beautiful table inside a DIV. - //You can override this function if a custom tooltip is desired. - var contentGenerator = function(d) { - if (content != null) return content; - - if (d == null) return ''; - - var table = d3.select(document.createElement("table")); - var theadEnter = table.selectAll("thead") - .data([d]) - .enter().append("thead"); - theadEnter.append("tr") - .append("td") - .attr("colspan",3) - .append("strong") - .classed("x-value",true) - .html(headerFormatter(d.value)); - - var tbodyEnter = table.selectAll("tbody") - .data([d]) - .enter().append("tbody"); - var trowEnter = tbodyEnter.selectAll("tr") - .data(function(p) { return p.series}) - .enter() - .append("tr") - .classed("highlight", function(p) { return p.highlight}) - ; - - trowEnter.append("td") - .classed("legend-color-guide",true) - .append("div") - .style("background-color", function(p) { return p.color}); - trowEnter.append("td") - .classed("key",true) - .html(function(p) {return p.key}); - trowEnter.append("td") - .classed("value",true) - .html(function(p,i) { return valueFormatter(p.value,i) }); - - - trowEnter.selectAll("td").each(function(p) { - if (p.highlight) { - var opacityScale = d3.scale.linear().domain([0,1]).range(["#fff",p.color]); - var opacity = 0.6; - d3.select(this) - .style("border-bottom-color", opacityScale(opacity)) - .style("border-top-color", opacityScale(opacity)) - ; - } - }); - - var html = table.node().outerHTML; - if (d.footer !== undefined) - html += ""; - return html; - - }; - - var dataSeriesExists = function(d) { - if (d && d.series && d.series.length > 0) return true; - - return false; - }; - - //In situations where the chart is in a 'viewBox', re-position the tooltip based on how far chart is zoomed. - function convertViewBoxRatio() { - if (chartContainer) { - var svg = d3.select(chartContainer); - if (svg.node().tagName !== "svg") { - svg = svg.select("svg"); - } - var viewBox = (svg.node()) ? svg.attr('viewBox') : null; - if (viewBox) { - viewBox = viewBox.split(' '); - var ratio = parseInt(svg.style('width')) / viewBox[2]; - - position.left = position.left * ratio; - position.top = position.top * ratio; - } - } - } - - //Creates new tooltip container, or uses existing one on DOM. - function getTooltipContainer(newContent) { - var body; - if (chartContainer) - body = d3.select(chartContainer); - else - body = d3.select("body"); - - var container = body.select(".nvtooltip"); - if (container.node() === null) { - //Create new tooltip div if it doesn't exist on DOM. - container = body.append("div") - .attr("class", "nvtooltip " + (classes? classes: "xy-tooltip")) - .attr("id",id) - ; - } - - - container.node().innerHTML = newContent; - container.style("top",0).style("left",0).style("opacity",0); - container.selectAll("div, table, td, tr").classed(nvPointerEventsClass,true) - container.classed(nvPointerEventsClass,true); - return container.node(); - } - - - - //Draw the tooltip onto the DOM. - function nvtooltip() { - if (!enabled) return; - if (!dataSeriesExists(data)) return; - - convertViewBoxRatio(); - - var left = position.left; - var top = (fixedTop != null) ? fixedTop : position.top; - var container = getTooltipContainer(contentGenerator(data)); - tooltipElem = container; - if (chartContainer) { - var svgComp = chartContainer.getElementsByTagName("svg")[0]; - var boundRect = (svgComp) ? svgComp.getBoundingClientRect() : chartContainer.getBoundingClientRect(); - var svgOffset = {left:0,top:0}; - if (svgComp) { - var svgBound = svgComp.getBoundingClientRect(); - var chartBound = chartContainer.getBoundingClientRect(); - var svgBoundTop = svgBound.top; - - //Defensive code. Sometimes, svgBoundTop can be a really negative - // number, like -134254. That's a bug. - // If such a number is found, use zero instead. FireFox bug only - if (svgBoundTop < 0) { - var containerBound = chartContainer.getBoundingClientRect(); - svgBoundTop = (Math.abs(svgBoundTop) > containerBound.height) ? 0 : svgBoundTop; - } - svgOffset.top = Math.abs(svgBoundTop - chartBound.top); - svgOffset.left = Math.abs(svgBound.left - chartBound.left); - } - //If the parent container is an overflow
with scrollbars, subtract the scroll offsets. - //You need to also add any offset between the element and its containing
- //Finally, add any offset of the containing
on the whole page. - left += chartContainer.offsetLeft + svgOffset.left - 2*chartContainer.scrollLeft; - top += chartContainer.offsetTop + svgOffset.top - 2*chartContainer.scrollTop; - } - - if (snapDistance && snapDistance > 0) { - top = Math.floor(top/snapDistance) * snapDistance; - } - - nv.tooltip.calcTooltipPosition([left,top], gravity, distance, container); - return nvtooltip; - }; - - nvtooltip.nvPointerEventsClass = nvPointerEventsClass; - - nvtooltip.content = function(_) { - if (!arguments.length) return content; - content = _; - return nvtooltip; - }; - - //Returns tooltipElem...not able to set it. - nvtooltip.tooltipElem = function() { - return tooltipElem; - }; - - nvtooltip.contentGenerator = function(_) { - if (!arguments.length) return contentGenerator; - if (typeof _ === 'function') { - contentGenerator = _; - } - return nvtooltip; - }; - - nvtooltip.data = function(_) { - if (!arguments.length) return data; - data = _; - return nvtooltip; - }; - - nvtooltip.gravity = function(_) { - if (!arguments.length) return gravity; - gravity = _; - return nvtooltip; - }; - - nvtooltip.distance = function(_) { - if (!arguments.length) return distance; - distance = _; - return nvtooltip; - }; - - nvtooltip.snapDistance = function(_) { - if (!arguments.length) return snapDistance; - snapDistance = _; - return nvtooltip; - }; - - nvtooltip.classes = function(_) { - if (!arguments.length) return classes; - classes = _; - return nvtooltip; - }; - - nvtooltip.chartContainer = function(_) { - if (!arguments.length) return chartContainer; - chartContainer = _; - return nvtooltip; - }; - - nvtooltip.position = function(_) { - if (!arguments.length) return position; - position.left = (typeof _.left !== 'undefined') ? _.left : position.left; - position.top = (typeof _.top !== 'undefined') ? _.top : position.top; - return nvtooltip; - }; - - nvtooltip.fixedTop = function(_) { - if (!arguments.length) return fixedTop; - fixedTop = _; - return nvtooltip; - }; - - nvtooltip.enabled = function(_) { - if (!arguments.length) return enabled; - enabled = _; - return nvtooltip; - }; - - nvtooltip.valueFormatter = function(_) { - if (!arguments.length) return valueFormatter; - if (typeof _ === 'function') { - valueFormatter = _; - } - return nvtooltip; - }; - - nvtooltip.headerFormatter = function(_) { - if (!arguments.length) return headerFormatter; - if (typeof _ === 'function') { - headerFormatter = _; - } - return nvtooltip; - }; - - //id() is a read-only function. You can't use it to set the id. - nvtooltip.id = function() { - return id; - }; - - - return nvtooltip; - }; - - - //Original tooltip.show function. Kept for backward compatibility. - // pos = [left,top] - nv.tooltip.show = function(pos, content, gravity, dist, parentContainer, classes) { - - //Create new tooltip div if it doesn't exist on DOM. - var container = document.createElement('div'); - container.className = 'nvtooltip ' + (classes ? classes : 'xy-tooltip'); - - var body = parentContainer; - if ( !parentContainer || parentContainer.tagName.match(/g|svg/i)) { - //If the parent element is an SVG element, place tooltip in the element. - body = document.getElementsByTagName('body')[0]; - } - - container.style.left = 0; - container.style.top = 0; - container.style.opacity = 0; - 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; - } - nv.tooltip.calcTooltipPosition(pos, gravity, dist, container); - }; - - //Looks up the ancestry of a DOM element, and returns the first NON-svg node. - nv.tooltip.findFirstNonSVGParent = function(Elem) { - while(Elem.tagName.match(/^g|svg$/i) !== null) { - Elem = Elem.parentNode; - } - return Elem; - }; - - //Finds the total offsetTop of a given DOM element. - //Looks up the entire ancestry of an element, up to the first relatively positioned element. - nv.tooltip.findTotalOffsetTop = function ( Elem, initialTop ) { - var offsetTop = initialTop; - - do { - if( !isNaN( Elem.offsetTop ) ) { - offsetTop += (Elem.offsetTop); - } - } while( Elem = Elem.offsetParent ); - return offsetTop; - }; - - //Finds the total offsetLeft of a given DOM element. - //Looks up the entire ancestry of an element, up to the first relatively positioned element. - nv.tooltip.findTotalOffsetLeft = function ( Elem, initialLeft) { - var offsetLeft = initialLeft; - - do { - if( !isNaN( Elem.offsetLeft ) ) { - offsetLeft += (Elem.offsetLeft); - } - } while( Elem = Elem.offsetParent ); - return offsetLeft; - }; - - //Global utility function to render a tooltip on the DOM. - //pos = [left,top] coordinates of where to place the tooltip, relative to the SVG chart container. - //gravity = how to orient the tooltip - //dist = how far away from the mouse to place tooltip - //container = tooltip DIV - nv.tooltip.calcTooltipPosition = function(pos, gravity, dist, container) { - - var height = parseInt(container.offsetHeight), - width = parseInt(container.offsetWidth), - windowWidth = nv.utils.windowSize().width, - windowHeight = nv.utils.windowSize().height, - scrollTop = window.pageYOffset, - scrollLeft = window.pageXOffset, - left, top; - - windowHeight = window.innerWidth >= document.body.scrollWidth ? windowHeight : windowHeight - 16; - windowWidth = window.innerHeight >= document.body.scrollHeight ? windowWidth : windowWidth - 16; - - gravity = gravity || 's'; - dist = dist || 20; - - var tooltipTop = function ( Elem ) { - return nv.tooltip.findTotalOffsetTop(Elem, top); - }; - - var tooltipLeft = function ( Elem ) { - return nv.tooltip.findTotalOffsetLeft(Elem,left); - }; - - switch (gravity) { - case 'e': - left = pos[0] - width - dist; - top = pos[1] - (height / 2); - var tLeft = tooltipLeft(container); - var tTop = tooltipTop(container); - if (tLeft < scrollLeft) left = pos[0] + dist > scrollLeft ? pos[0] + dist : scrollLeft - tLeft + left; - if (tTop < scrollTop) top = scrollTop - tTop + top; - if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height; - break; - case 'w': - left = pos[0] + dist; - top = pos[1] - (height / 2); - var tLeft = tooltipLeft(container); - var tTop = tooltipTop(container); - if (tLeft + width > windowWidth) left = pos[0] - width - dist; - if (tTop < scrollTop) top = scrollTop + 5; - if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height; - break; - case 'n': - left = pos[0] - (width / 2) - 5; - top = pos[1] + dist; - var tLeft = tooltipLeft(container); - var tTop = tooltipTop(container); - if (tLeft < scrollLeft) left = scrollLeft + 5; - if (tLeft + width > windowWidth) left = left - width/2 + 5; - if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height; - break; - case 's': - left = pos[0] - (width / 2); - top = pos[1] - height - dist; - var tLeft = tooltipLeft(container); - var tTop = tooltipTop(container); - if (tLeft < scrollLeft) left = scrollLeft + 5; - if (tLeft + width > windowWidth) left = left - width/2 + 5; - if (scrollTop > tTop) top = scrollTop; - break; - case 'none': - left = pos[0]; - top = pos[1] - dist; - var tLeft = tooltipLeft(container); - var tTop = tooltipTop(container); - break; - } - - - container.style.left = left+'px'; - container.style.top = top+'px'; - container.style.opacity = 1; - container.style.position = 'absolute'; - - return container; - }; - - //Global utility function to remove tooltips from the DOM. - nv.tooltip.cleanup = function() { - - // Find the tooltips, mark them for removal by this class (so others cleanups won't find it) - var tooltips = document.getElementsByClassName('nvtooltip'); - var purging = []; - while(tooltips.length) { - purging.push(tooltips[0]); - tooltips[0].style.transitionDelay = '0 !important'; - tooltips[0].style.opacity = 0; - tooltips[0].className = 'nvtooltip-pending-removal'; - } - - setTimeout(function() { - - while (purging.length) { - var removeMe = purging.pop(); - removeMe.parentNode.removeChild(removeMe); - } - }, 500); - }; - -})(); - -nv.utils.windowSize = function() { - // Sane defaults - var size = {width: 640, height: 480}; - - // Earlier IE uses Doc.body - if (document.body && document.body.offsetWidth) { - size.width = document.body.offsetWidth; - size.height = document.body.offsetHeight; - } - - // IE can use depending on mode it is in - if (document.compatMode=='CSS1Compat' && - document.documentElement && - document.documentElement.offsetWidth ) { - size.width = document.documentElement.offsetWidth; - size.height = document.documentElement.offsetHeight; - } - - // Most recent browsers use - if (window.innerWidth && window.innerHeight) { - size.width = window.innerWidth; - size.height = window.innerHeight; - } - return (size); -}; - - - -// 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 -nv.utils.getColor = function(color) { - if (!arguments.length) return nv.utils.defaultColor(); //if you pass in nothing, get default colors back - - 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. -nv.utils.defaultColor = function() { - var colors = d3.scale.category20().range(); - return function(d, i) { return d.color || colors[i % colors.length] }; -} - - -// Returns a color function that takes the result of 'getKey' for each series and -// looks for a corresponding color from the dictionary, -nv.utils.customTheme = function(dictionary, getKey, defaultColors) { - 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); - - 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) { - 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); - }); -} - -/* For situations where we want to approximate the width in pixels for an SVG:text element. -Most common instance is when the element is in a display:none; container. -Forumla is : text.length * font-size * constant_factor -*/ -nv.utils.calcApproxTextWidth = function (svgTextElem) { - if (typeof svgTextElem.style === 'function' - && typeof svgTextElem.text === 'function') { - var fontSize = parseInt(svgTextElem.style("font-size").replace("px","")); - var textLength = svgTextElem.text().length; - - return textLength * fontSize * 0.5; - } - return 0; -}; - -/* Numbers that are undefined, null or NaN, convert them to zeros. -*/ -nv.utils.NaNtoZero = function(n) { - if (typeof n !== 'number' - || isNaN(n) - || n === null - || n === Infinity) 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 -}); - -To enable in the chart: -chart.options = nv.utils.optionsFunc.bind(chart); -*/ -nv.utils.optionsFunc = function(args) { - if (args) { - d3.map(args).forEach((function(key,value) { - if (typeof this[key] === "function") { - this[key](value); - } - }).bind(this)); - } - return this; -};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 = 12 //The larger this number is, the closer the axis label is to the axis. - ; - - axis - .scale(scale) - .orient('bottom') - .tickFormat(function(d) { return d }) - ; - - //============================================================ - - - //============================================================ - // Private Variables - //------------------------------------------------------------ - - var scale0; - - //============================================================ - - - function chart(selection) { - selection.each(function(data) { - var container = d3.select(this); - - - //------------------------------------------------------------ - // 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.transition().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 = (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', '-0.5em') - .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) + axisLabelDistance) : -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; - }); - 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(); - 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(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(); - - }); - - return chart; - } - - - //============================================================ - // Expose Public Variables - //------------------------------------------------------------ - - // expose chart's sub-components - chart.axis = axis; - - 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; - }; - - //============================================================ - - - 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 - ; - - //============================================================ - - - 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[0].values.map(getX).concat(forceX) )) - - if (padData) - x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]); - else - x.range(xRange || [0, availableWidth]); - - y .domain(yDomain || d3.extent(data[0].values.map(getY).concat(forceY) )) - .range(yRange || [availableHeight, 0]); - - // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point - - if (x.domain()[0] === x.domain()[1]) - x.domain()[0] ? - x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01]) - : x.domain([-1,1]); - - if (y.domain()[0] === y.domain()[1]) - y.domain()[0] ? - y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01]) - : y.domain([-1,1]); - - //------------------------------------------------------------ - - - //------------------------------------------------------------ - // Setup containers and skeleton of chart - - var wrap = container.selectAll('g.nv-wrap.nv-historicalBar-' + id).data([data[0].values]); - var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBar-' + id); - var defsEnter = wrapEnter.append('defs'); - var gEnter = wrapEnter.append('g'); - var g = wrap.select('g'); - - gEnter.append('g').attr('class', 'nv-bars'); - - wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - - //------------------------------------------------------------ - - - container - .on('click', function(d,i) { - dispatch.chartClick({ - data: d, - index: i, - pos: d3.event, - id: id - }); - }); - - - defsEnter.append('clipPath') - .attr('id', 'nv-chart-clip-path-' + id) - .append('rect'); - - wrap.select('#nv-chart-clip-path-' + id + ' rect') - .attr('width', availableWidth) - .attr('height', availableHeight); - - g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : ''); - - - - var bars = wrap.select('.nv-bars').selectAll('.nv-bar') - .data(function(d) { return d }, function(d,i) {return getX(d,i)}); - - bars.exit().remove(); - - - var barsEnter = bars.enter().append('rect') - //.attr('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 - }); - - }) - .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 }) - .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)) }); - - }); - - 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; - }; - - 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; - }; - - 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; - }; - - 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; - }; - - chart.forceX = function(_) { - if (!arguments.length) return forceX; - forceX = _; - return chart; - }; - - 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; - }; - - 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; - }; - - //============================================================ - - - 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 } - , 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); - - 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(); -*/ - - }); - - // 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 } - , 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); - - 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); - - // 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; - }; - - // 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; - }; - - chart.width = function(x) { - if (!arguments.length) return width; - width = x; - 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.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.