Mercurial > hg > index.fcgi > www > www-1
diff gtc/nv.d3.js @ 89:18f8c214169f
add gtc
author | paulo |
---|---|
date | Sun, 19 Feb 2017 19:45:31 -0800 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gtc/nv.d3.js Sun Feb 19 19:45:31 2017 -0800 1.3 @@ -0,0 +1,13298 @@ 1.4 +/* nvd3 version 1.8.1 (https://github.com/novus/nvd3) 2015-06-15 */ 1.5 +(function(){ 1.6 + 1.7 +// set up main nv object 1.8 +var nv = {}; 1.9 + 1.10 +// the major global objects under the nv namespace 1.11 +nv.dev = false; //set false when in production 1.12 +nv.tooltip = nv.tooltip || {}; // For the tooltip system 1.13 +nv.utils = nv.utils || {}; // Utility subsystem 1.14 +nv.models = nv.models || {}; //stores all the possible models/components 1.15 +nv.charts = {}; //stores all the ready to use charts 1.16 +nv.logs = {}; //stores some statistics and potential error messages 1.17 +nv.dom = {}; //DOM manipulation functions 1.18 + 1.19 +nv.dispatch = d3.dispatch('render_start', 'render_end'); 1.20 + 1.21 +// Function bind polyfill 1.22 +// Needed ONLY for phantomJS as it's missing until version 2.0 which is unreleased as of this comment 1.23 +// https://github.com/ariya/phantomjs/issues/10522 1.24 +// http://kangax.github.io/compat-table/es5/#Function.prototype.bind 1.25 +// phantomJS is used for running the test suite 1.26 +if (!Function.prototype.bind) { 1.27 + Function.prototype.bind = function (oThis) { 1.28 + if (typeof this !== "function") { 1.29 + // closest thing possible to the ECMAScript 5 internal IsCallable function 1.30 + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); 1.31 + } 1.32 + 1.33 + var aArgs = Array.prototype.slice.call(arguments, 1), 1.34 + fToBind = this, 1.35 + fNOP = function () {}, 1.36 + fBound = function () { 1.37 + return fToBind.apply(this instanceof fNOP && oThis 1.38 + ? this 1.39 + : oThis, 1.40 + aArgs.concat(Array.prototype.slice.call(arguments))); 1.41 + }; 1.42 + 1.43 + fNOP.prototype = this.prototype; 1.44 + fBound.prototype = new fNOP(); 1.45 + return fBound; 1.46 + }; 1.47 +} 1.48 + 1.49 +// Development render timers - disabled if dev = false 1.50 +if (nv.dev) { 1.51 + nv.dispatch.on('render_start', function(e) { 1.52 + nv.logs.startTime = +new Date(); 1.53 + }); 1.54 + 1.55 + nv.dispatch.on('render_end', function(e) { 1.56 + nv.logs.endTime = +new Date(); 1.57 + nv.logs.totalTime = nv.logs.endTime - nv.logs.startTime; 1.58 + nv.log('total', nv.logs.totalTime); // used for development, to keep track of graph generation times 1.59 + }); 1.60 +} 1.61 + 1.62 +// Logs all arguments, and returns the last so you can test things in place 1.63 +// Note: in IE8 console.log is an object not a function, and if modernizr is used 1.64 +// then calling Function.prototype.bind with with anything other than a function 1.65 +// causes a TypeError to be thrown. 1.66 +nv.log = function() { 1.67 + if (nv.dev && window.console && console.log && console.log.apply) 1.68 + console.log.apply(console, arguments); 1.69 + else if (nv.dev && window.console && typeof console.log == "function" && Function.prototype.bind) { 1.70 + var log = Function.prototype.bind.call(console.log, console); 1.71 + log.apply(console, arguments); 1.72 + } 1.73 + return arguments[arguments.length - 1]; 1.74 +}; 1.75 + 1.76 +// print console warning, should be used by deprecated functions 1.77 +nv.deprecated = function(name, info) { 1.78 + if (console && console.warn) { 1.79 + console.warn('nvd3 warning: `' + name + '` has been deprecated. ', info || ''); 1.80 + } 1.81 +}; 1.82 + 1.83 +// The nv.render function is used to queue up chart rendering 1.84 +// in non-blocking async functions. 1.85 +// When all queued charts are done rendering, nv.dispatch.render_end is invoked. 1.86 +nv.render = function render(step) { 1.87 + // number of graphs to generate in each timeout loop 1.88 + step = step || 1; 1.89 + 1.90 + nv.render.active = true; 1.91 + nv.dispatch.render_start(); 1.92 + 1.93 + var renderLoop = function() { 1.94 + var chart, graph; 1.95 + 1.96 + for (var i = 0; i < step && (graph = nv.render.queue[i]); i++) { 1.97 + chart = graph.generate(); 1.98 + if (typeof graph.callback == typeof(Function)) graph.callback(chart); 1.99 + } 1.100 + 1.101 + nv.render.queue.splice(0, i); 1.102 + 1.103 + if (nv.render.queue.length) { 1.104 + setTimeout(renderLoop); 1.105 + } 1.106 + else { 1.107 + nv.dispatch.render_end(); 1.108 + nv.render.active = false; 1.109 + } 1.110 + }; 1.111 + 1.112 + setTimeout(renderLoop); 1.113 +}; 1.114 + 1.115 +nv.render.active = false; 1.116 +nv.render.queue = []; 1.117 + 1.118 +/* 1.119 +Adds a chart to the async rendering queue. This method can take arguments in two forms: 1.120 +nv.addGraph({ 1.121 + generate: <Function> 1.122 + callback: <Function> 1.123 +}) 1.124 + 1.125 +or 1.126 + 1.127 +nv.addGraph(<generate Function>, <callback Function>) 1.128 + 1.129 +The generate function should contain code that creates the NVD3 model, sets options 1.130 +on it, adds data to an SVG element, and invokes the chart model. The generate function 1.131 +should return the chart model. See examples/lineChart.html for a usage example. 1.132 + 1.133 +The callback function is optional, and it is called when the generate function completes. 1.134 +*/ 1.135 +nv.addGraph = function(obj) { 1.136 + if (typeof arguments[0] === typeof(Function)) { 1.137 + obj = {generate: arguments[0], callback: arguments[1]}; 1.138 + } 1.139 + 1.140 + nv.render.queue.push(obj); 1.141 + 1.142 + if (!nv.render.active) { 1.143 + nv.render(); 1.144 + } 1.145 +}; 1.146 + 1.147 +// Node/CommonJS exports 1.148 +if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined') { 1.149 + module.exports = nv; 1.150 +} 1.151 + 1.152 +if (typeof(window) !== 'undefined') { 1.153 + window.nv = nv; 1.154 +} 1.155 +/* Facade for queueing DOM write operations 1.156 + * with Fastdom (https://github.com/wilsonpage/fastdom) 1.157 + * if available. 1.158 + * This could easily be extended to support alternate 1.159 + * implementations in the future. 1.160 + */ 1.161 +nv.dom.write = function(callback) { 1.162 + if (window.fastdom !== undefined) { 1.163 + return fastdom.write(callback); 1.164 + } 1.165 + return callback(); 1.166 +}; 1.167 + 1.168 +/* Facade for queueing DOM read operations 1.169 + * with Fastdom (https://github.com/wilsonpage/fastdom) 1.170 + * if available. 1.171 + * This could easily be extended to support alternate 1.172 + * implementations in the future. 1.173 + */ 1.174 +nv.dom.read = function(callback) { 1.175 + if (window.fastdom !== undefined) { 1.176 + return fastdom.read(callback); 1.177 + } 1.178 + return callback(); 1.179 +};/* Utility class to handle creation of an interactive layer. 1.180 + This places a rectangle on top of the chart. When you mouse move over it, it sends a dispatch 1.181 + containing the X-coordinate. It can also render a vertical line where the mouse is located. 1.182 + 1.183 + dispatch.elementMousemove is the important event to latch onto. It is fired whenever the mouse moves over 1.184 + the rectangle. The dispatch is given one object which contains the mouseX/Y location. 1.185 + It also has 'pointXValue', which is the conversion of mouseX to the x-axis scale. 1.186 + */ 1.187 +nv.interactiveGuideline = function() { 1.188 + "use strict"; 1.189 + 1.190 + var tooltip = nv.models.tooltip(); 1.191 + tooltip.duration(0).hideDelay(0)._isInteractiveLayer(true).hidden(false); 1.192 + 1.193 + //Public settings 1.194 + var width = null; 1.195 + var height = null; 1.196 + 1.197 + //Please pass in the bounding chart's top and left margins 1.198 + //This is important for calculating the correct mouseX/Y positions. 1.199 + var margin = {left: 0, top: 0} 1.200 + , xScale = d3.scale.linear() 1.201 + , dispatch = d3.dispatch('elementMousemove', 'elementMouseout', 'elementClick', 'elementDblclick') 1.202 + , showGuideLine = true; 1.203 + //Must pass in the bounding chart's <svg> container. 1.204 + //The mousemove event is attached to this container. 1.205 + var svgContainer = null; 1.206 + 1.207 + // check if IE by looking for activeX 1.208 + var isMSIE = "ActiveXObject" in window; 1.209 + 1.210 + 1.211 + function layer(selection) { 1.212 + selection.each(function(data) { 1.213 + var container = d3.select(this); 1.214 + var availableWidth = (width || 960), availableHeight = (height || 400); 1.215 + var wrap = container.selectAll("g.nv-wrap.nv-interactiveLineLayer") 1.216 + .data([data]); 1.217 + var wrapEnter = wrap.enter() 1.218 + .append("g").attr("class", " nv-wrap nv-interactiveLineLayer"); 1.219 + wrapEnter.append("g").attr("class","nv-interactiveGuideLine"); 1.220 + 1.221 + if (!svgContainer) { 1.222 + return; 1.223 + } 1.224 + 1.225 + function mouseHandler() { 1.226 + var d3mouse = d3.mouse(this); 1.227 + var mouseX = d3mouse[0]; 1.228 + var mouseY = d3mouse[1]; 1.229 + var subtractMargin = true; 1.230 + var mouseOutAnyReason = false; 1.231 + if (isMSIE) { 1.232 + /* 1.233 + D3.js (or maybe SVG.getScreenCTM) has a nasty bug in Internet Explorer 10. 1.234 + d3.mouse() returns incorrect X,Y mouse coordinates when mouse moving 1.235 + over a rect in IE 10. 1.236 + However, d3.event.offsetX/Y also returns the mouse coordinates 1.237 + relative to the triggering <rect>. So we use offsetX/Y on IE. 1.238 + */ 1.239 + mouseX = d3.event.offsetX; 1.240 + mouseY = d3.event.offsetY; 1.241 + 1.242 + /* 1.243 + On IE, if you attach a mouse event listener to the <svg> container, 1.244 + it will actually trigger it for all the child elements (like <path>, <circle>, etc). 1.245 + When this happens on IE, the offsetX/Y is set to where ever the child element 1.246 + is located. 1.247 + As a result, we do NOT need to subtract margins to figure out the mouse X/Y 1.248 + position under this scenario. Removing the line below *will* cause 1.249 + the interactive layer to not work right on IE. 1.250 + */ 1.251 + if(d3.event.target.tagName !== "svg") { 1.252 + subtractMargin = false; 1.253 + } 1.254 + 1.255 + if (d3.event.target.className.baseVal.match("nv-legend")) { 1.256 + mouseOutAnyReason = true; 1.257 + } 1.258 + 1.259 + } 1.260 + 1.261 + if(subtractMargin) { 1.262 + mouseX -= margin.left; 1.263 + mouseY -= margin.top; 1.264 + } 1.265 + 1.266 + /* If mouseX/Y is outside of the chart's bounds, 1.267 + trigger a mouseOut event. 1.268 + */ 1.269 + if (mouseX < 0 || mouseY < 0 1.270 + || mouseX > availableWidth || mouseY > availableHeight 1.271 + || (d3.event.relatedTarget && d3.event.relatedTarget.ownerSVGElement === undefined) 1.272 + || mouseOutAnyReason 1.273 + ) { 1.274 + 1.275 + if (isMSIE) { 1.276 + if (d3.event.relatedTarget 1.277 + && d3.event.relatedTarget.ownerSVGElement === undefined 1.278 + && (d3.event.relatedTarget.className === undefined 1.279 + || d3.event.relatedTarget.className.match(tooltip.nvPointerEventsClass))) { 1.280 + 1.281 + return; 1.282 + } 1.283 + } 1.284 + dispatch.elementMouseout({ 1.285 + mouseX: mouseX, 1.286 + mouseY: mouseY 1.287 + }); 1.288 + layer.renderGuideLine(null); //hide the guideline 1.289 + tooltip.hidden(true); 1.290 + return; 1.291 + } else { 1.292 + tooltip.hidden(false); 1.293 + } 1.294 + 1.295 + var pointXValue = xScale.invert(mouseX); 1.296 + dispatch.elementMousemove({ 1.297 + mouseX: mouseX, 1.298 + mouseY: mouseY, 1.299 + pointXValue: pointXValue 1.300 + }); 1.301 + 1.302 + //If user double clicks the layer, fire a elementDblclick 1.303 + if (d3.event.type === "dblclick") { 1.304 + dispatch.elementDblclick({ 1.305 + mouseX: mouseX, 1.306 + mouseY: mouseY, 1.307 + pointXValue: pointXValue 1.308 + }); 1.309 + } 1.310 + 1.311 + // if user single clicks the layer, fire elementClick 1.312 + if (d3.event.type === 'click') { 1.313 + dispatch.elementClick({ 1.314 + mouseX: mouseX, 1.315 + mouseY: mouseY, 1.316 + pointXValue: pointXValue 1.317 + }); 1.318 + } 1.319 + } 1.320 + 1.321 + svgContainer 1.322 + .on("touchmove",mouseHandler) 1.323 + .on("mousemove",mouseHandler, true) 1.324 + .on("mouseout" ,mouseHandler,true) 1.325 + .on("dblclick" ,mouseHandler) 1.326 + .on("click", mouseHandler) 1.327 + ; 1.328 + 1.329 + layer.guideLine = null; 1.330 + //Draws a vertical guideline at the given X postion. 1.331 + layer.renderGuideLine = function(x) { 1.332 + if (!showGuideLine) return; 1.333 + if (layer.guideLine && layer.guideLine.attr("x1") === x) return; 1.334 + nv.dom.write(function() { 1.335 + var line = wrap.select(".nv-interactiveGuideLine") 1.336 + .selectAll("line") 1.337 + .data((x != null) ? [nv.utils.NaNtoZero(x)] : [], String); 1.338 + line.enter() 1.339 + .append("line") 1.340 + .attr("class", "nv-guideline") 1.341 + .attr("x1", function(d) { return d;}) 1.342 + .attr("x2", function(d) { return d;}) 1.343 + .attr("y1", availableHeight) 1.344 + .attr("y2",0); 1.345 + line.exit().remove(); 1.346 + }); 1.347 + } 1.348 + }); 1.349 + } 1.350 + 1.351 + layer.dispatch = dispatch; 1.352 + layer.tooltip = tooltip; 1.353 + 1.354 + layer.margin = function(_) { 1.355 + if (!arguments.length) return margin; 1.356 + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; 1.357 + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; 1.358 + return layer; 1.359 + }; 1.360 + 1.361 + layer.width = function(_) { 1.362 + if (!arguments.length) return width; 1.363 + width = _; 1.364 + return layer; 1.365 + }; 1.366 + 1.367 + layer.height = function(_) { 1.368 + if (!arguments.length) return height; 1.369 + height = _; 1.370 + return layer; 1.371 + }; 1.372 + 1.373 + layer.xScale = function(_) { 1.374 + if (!arguments.length) return xScale; 1.375 + xScale = _; 1.376 + return layer; 1.377 + }; 1.378 + 1.379 + layer.showGuideLine = function(_) { 1.380 + if (!arguments.length) return showGuideLine; 1.381 + showGuideLine = _; 1.382 + return layer; 1.383 + }; 1.384 + 1.385 + layer.svgContainer = function(_) { 1.386 + if (!arguments.length) return svgContainer; 1.387 + svgContainer = _; 1.388 + return layer; 1.389 + }; 1.390 + 1.391 + return layer; 1.392 +}; 1.393 + 1.394 +/* Utility class that uses d3.bisect to find the index in a given array, where a search value can be inserted. 1.395 + This is different from normal bisectLeft; this function finds the nearest index to insert the search value. 1.396 + 1.397 + For instance, lets say your array is [1,2,3,5,10,30], and you search for 28. 1.398 + Normal d3.bisectLeft will return 4, because 28 is inserted after the number 10. But interactiveBisect will return 5 1.399 + because 28 is closer to 30 than 10. 1.400 + 1.401 + Unit tests can be found in: interactiveBisectTest.html 1.402 + 1.403 + Has the following known issues: 1.404 + * Will not work if the data points move backwards (ie, 10,9,8,7, etc) or if the data points are in random order. 1.405 + * Won't work if there are duplicate x coordinate values. 1.406 + */ 1.407 +nv.interactiveBisect = function (values, searchVal, xAccessor) { 1.408 + "use strict"; 1.409 + if (! (values instanceof Array)) { 1.410 + return null; 1.411 + } 1.412 + var _xAccessor; 1.413 + if (typeof xAccessor !== 'function') { 1.414 + _xAccessor = function(d) { 1.415 + return d.x; 1.416 + } 1.417 + } else { 1.418 + _xAccessor = xAccessor; 1.419 + } 1.420 + var _cmp = function(d, v) { 1.421 + // Accessors are no longer passed the index of the element along with 1.422 + // the element itself when invoked by d3.bisector. 1.423 + // 1.424 + // Starting at D3 v3.4.4, d3.bisector() started inspecting the 1.425 + // function passed to determine if it should consider it an accessor 1.426 + // or a comparator. This meant that accessors that take two arguments 1.427 + // (expecting an index as the second parameter) are treated as 1.428 + // comparators where the second argument is the search value against 1.429 + // which the first argument is compared. 1.430 + return _xAccessor(d) - v; 1.431 + }; 1.432 + 1.433 + var bisect = d3.bisector(_cmp).left; 1.434 + var index = d3.max([0, bisect(values,searchVal) - 1]); 1.435 + var currentValue = _xAccessor(values[index]); 1.436 + 1.437 + if (typeof currentValue === 'undefined') { 1.438 + currentValue = index; 1.439 + } 1.440 + 1.441 + if (currentValue === searchVal) { 1.442 + return index; //found exact match 1.443 + } 1.444 + 1.445 + var nextIndex = d3.min([index+1, values.length - 1]); 1.446 + var nextValue = _xAccessor(values[nextIndex]); 1.447 + 1.448 + if (typeof nextValue === 'undefined') { 1.449 + nextValue = nextIndex; 1.450 + } 1.451 + 1.452 + if (Math.abs(nextValue - searchVal) >= Math.abs(currentValue - searchVal)) { 1.453 + return index; 1.454 + } else { 1.455 + return nextIndex 1.456 + } 1.457 +}; 1.458 + 1.459 +/* 1.460 + Returns the index in the array "values" that is closest to searchVal. 1.461 + Only returns an index if searchVal is within some "threshold". 1.462 + Otherwise, returns null. 1.463 + */ 1.464 +nv.nearestValueIndex = function (values, searchVal, threshold) { 1.465 + "use strict"; 1.466 + var yDistMax = Infinity, indexToHighlight = null; 1.467 + values.forEach(function(d,i) { 1.468 + var delta = Math.abs(searchVal - d); 1.469 + if ( d != null && delta <= yDistMax && delta < threshold) { 1.470 + yDistMax = delta; 1.471 + indexToHighlight = i; 1.472 + } 1.473 + }); 1.474 + return indexToHighlight; 1.475 +}; 1.476 +/* Tooltip rendering model for nvd3 charts. 1.477 + window.nv.models.tooltip is the updated,new way to render tooltips. 1.478 + 1.479 + window.nv.tooltip.show is the old tooltip code. 1.480 + window.nv.tooltip.* also has various helper methods. 1.481 + */ 1.482 +(function() { 1.483 + "use strict"; 1.484 + 1.485 + /* Model which can be instantiated to handle tooltip rendering. 1.486 + Example usage: 1.487 + var tip = nv.models.tooltip().gravity('w').distance(23) 1.488 + .data(myDataObject); 1.489 + 1.490 + tip(); //just invoke the returned function to render tooltip. 1.491 + */ 1.492 + nv.models.tooltip = function() { 1.493 + 1.494 + /* 1.495 + Tooltip data. If data is given in the proper format, a consistent tooltip is generated. 1.496 + Example Format of data: 1.497 + { 1.498 + key: "Date", 1.499 + value: "August 2009", 1.500 + series: [ 1.501 + {key: "Series 1", value: "Value 1", color: "#000"}, 1.502 + {key: "Series 2", value: "Value 2", color: "#00f"} 1.503 + ] 1.504 + } 1.505 + */ 1.506 + var data = null; 1.507 + var gravity = 'w' //Can be 'n','s','e','w'. Determines how tooltip is positioned. 1.508 + , distance = 25 //Distance to offset tooltip from the mouse location. 1.509 + , snapDistance = 0 //Tolerance allowed before tooltip is moved from its current position (creates 'snapping' effect) 1.510 + , fixedTop = null //If not null, this fixes the top position of the tooltip. 1.511 + , classes = null //Attaches additional CSS classes to the tooltip DIV that is created. 1.512 + , chartContainer = null //Parent dom element of the SVG that holds the chart. 1.513 + , hidden = true // start off hidden, toggle with hide/show functions below 1.514 + , hideDelay = 400 // delay before the tooltip hides after calling hide() 1.515 + , tooltip = null // d3 select of tooltipElem below 1.516 + , tooltipElem = null //actual DOM element representing the tooltip. 1.517 + , position = {left: null, top: null} //Relative position of the tooltip inside chartContainer. 1.518 + , offset = {left: 0, top: 0} //Offset of tooltip against the pointer 1.519 + , enabled = true //True -> tooltips are rendered. False -> don't render tooltips. 1.520 + , duration = 100 // duration for tooltip movement 1.521 + , headerEnabled = true 1.522 + ; 1.523 + 1.524 + // set to true by interactive layer to adjust tooltip positions 1.525 + // eventually we should probably fix interactive layer to get the position better. 1.526 + // for now this is needed if you want to set chartContainer for normal tooltips, else it "fixes" it to broken 1.527 + var isInteractiveLayer = false; 1.528 + 1.529 + //Generates a unique id when you create a new tooltip() object 1.530 + var id = "nvtooltip-" + Math.floor(Math.random() * 100000); 1.531 + 1.532 + //CSS class to specify whether element should not have mouse events. 1.533 + var nvPointerEventsClass = "nv-pointer-events-none"; 1.534 + 1.535 + //Format function for the tooltip values column 1.536 + var valueFormatter = function(d,i) { 1.537 + return d; 1.538 + }; 1.539 + 1.540 + //Format function for the tooltip header value. 1.541 + var headerFormatter = function(d) { 1.542 + return d; 1.543 + }; 1.544 + 1.545 + var keyFormatter = function(d, i) { 1.546 + return d; 1.547 + }; 1.548 + 1.549 + //By default, the tooltip model renders a beautiful table inside a DIV. 1.550 + //You can override this function if a custom tooltip is desired. 1.551 + var contentGenerator = function(d) { 1.552 + if (d === null) { 1.553 + return ''; 1.554 + } 1.555 + 1.556 + var table = d3.select(document.createElement("table")); 1.557 + if (headerEnabled) { 1.558 + var theadEnter = table.selectAll("thead") 1.559 + .data([d]) 1.560 + .enter().append("thead"); 1.561 + 1.562 + theadEnter.append("tr") 1.563 + .append("td") 1.564 + .attr("colspan", 3) 1.565 + .append("strong") 1.566 + .classed("x-value", true) 1.567 + .html(headerFormatter(d.value)); 1.568 + } 1.569 + 1.570 + var tbodyEnter = table.selectAll("tbody") 1.571 + .data([d]) 1.572 + .enter().append("tbody"); 1.573 + 1.574 + var trowEnter = tbodyEnter.selectAll("tr") 1.575 + .data(function(p) { return p.series}) 1.576 + .enter() 1.577 + .append("tr") 1.578 + .classed("highlight", function(p) { return p.highlight}); 1.579 + 1.580 + trowEnter.append("td") 1.581 + .classed("legend-color-guide",true) 1.582 + .append("div") 1.583 + .style("background-color", function(p) { return p.color}); 1.584 + 1.585 + trowEnter.append("td") 1.586 + .classed("key",true) 1.587 + .html(function(p, i) {return keyFormatter(p.key, i)}); 1.588 + 1.589 + trowEnter.append("td") 1.590 + .classed("value",true) 1.591 + .html(function(p, i) { return valueFormatter(p.value, i) }); 1.592 + 1.593 + 1.594 + trowEnter.selectAll("td").each(function(p) { 1.595 + if (p.highlight) { 1.596 + var opacityScale = d3.scale.linear().domain([0,1]).range(["#fff",p.color]); 1.597 + var opacity = 0.6; 1.598 + d3.select(this) 1.599 + .style("border-bottom-color", opacityScale(opacity)) 1.600 + .style("border-top-color", opacityScale(opacity)) 1.601 + ; 1.602 + } 1.603 + }); 1.604 + 1.605 + var html = table.node().outerHTML; 1.606 + if (d.footer !== undefined) 1.607 + html += "<div class='footer'>" + d.footer + "</div>"; 1.608 + return html; 1.609 + 1.610 + }; 1.611 + 1.612 + var dataSeriesExists = function(d) { 1.613 + if (d && d.series) { 1.614 + if (d.series instanceof Array) { 1.615 + return !!d.series.length; 1.616 + } 1.617 + // if object, it's okay just convert to array of the object 1.618 + if (d.series instanceof Object) { 1.619 + d.series = [d.series]; 1.620 + return true; 1.621 + } 1.622 + } 1.623 + return false; 1.624 + }; 1.625 + 1.626 + var calcTooltipPosition = function(pos) { 1.627 + if (!tooltipElem) return; 1.628 + 1.629 + nv.dom.read(function() { 1.630 + var height = parseInt(tooltipElem.offsetHeight, 10), 1.631 + width = parseInt(tooltipElem.offsetWidth, 10), 1.632 + windowWidth = nv.utils.windowSize().width, 1.633 + windowHeight = nv.utils.windowSize().height, 1.634 + scrollTop = window.pageYOffset, 1.635 + scrollLeft = window.pageXOffset, 1.636 + left, top; 1.637 + 1.638 + windowHeight = window.innerWidth >= document.body.scrollWidth ? windowHeight : windowHeight - 16; 1.639 + windowWidth = window.innerHeight >= document.body.scrollHeight ? windowWidth : windowWidth - 16; 1.640 + 1.641 + 1.642 + //Helper functions to find the total offsets of a given DOM element. 1.643 + //Looks up the entire ancestry of an element, up to the first relatively positioned element. 1.644 + var tooltipTop = function ( Elem ) { 1.645 + var offsetTop = top; 1.646 + do { 1.647 + if( !isNaN( Elem.offsetTop ) ) { 1.648 + offsetTop += (Elem.offsetTop); 1.649 + } 1.650 + Elem = Elem.offsetParent; 1.651 + } while( Elem ); 1.652 + return offsetTop; 1.653 + }; 1.654 + var tooltipLeft = function ( Elem ) { 1.655 + var offsetLeft = left; 1.656 + do { 1.657 + if( !isNaN( Elem.offsetLeft ) ) { 1.658 + offsetLeft += (Elem.offsetLeft); 1.659 + } 1.660 + Elem = Elem.offsetParent; 1.661 + } while( Elem ); 1.662 + return offsetLeft; 1.663 + }; 1.664 + 1.665 + // calculate position based on gravity 1.666 + var tLeft, tTop; 1.667 + switch (gravity) { 1.668 + case 'e': 1.669 + left = pos[0] - width - distance; 1.670 + top = pos[1] - (height / 2); 1.671 + tLeft = tooltipLeft(tooltipElem); 1.672 + tTop = tooltipTop(tooltipElem); 1.673 + if (tLeft < scrollLeft) left = pos[0] + distance > scrollLeft ? pos[0] + distance : scrollLeft - tLeft + left; 1.674 + if (tTop < scrollTop) top = scrollTop - tTop + top; 1.675 + if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height; 1.676 + break; 1.677 + case 'w': 1.678 + left = pos[0] + distance; 1.679 + top = pos[1] - (height / 2); 1.680 + tLeft = tooltipLeft(tooltipElem); 1.681 + tTop = tooltipTop(tooltipElem); 1.682 + if (tLeft + width > windowWidth) left = pos[0] - width - distance; 1.683 + if (tTop < scrollTop) top = scrollTop + 5; 1.684 + if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height; 1.685 + break; 1.686 + case 'n': 1.687 + left = pos[0] - (width / 2) - 5; 1.688 + top = pos[1] + distance; 1.689 + tLeft = tooltipLeft(tooltipElem); 1.690 + tTop = tooltipTop(tooltipElem); 1.691 + if (tLeft < scrollLeft) left = scrollLeft + 5; 1.692 + if (tLeft + width > windowWidth) left = left - width/2 + 5; 1.693 + if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height; 1.694 + break; 1.695 + case 's': 1.696 + left = pos[0] - (width / 2); 1.697 + top = pos[1] - height - distance; 1.698 + tLeft = tooltipLeft(tooltipElem); 1.699 + tTop = tooltipTop(tooltipElem); 1.700 + if (tLeft < scrollLeft) left = scrollLeft + 5; 1.701 + if (tLeft + width > windowWidth) left = left - width/2 + 5; 1.702 + if (scrollTop > tTop) top = scrollTop; 1.703 + break; 1.704 + case 'none': 1.705 + left = pos[0]; 1.706 + top = pos[1] - distance; 1.707 + tLeft = tooltipLeft(tooltipElem); 1.708 + tTop = tooltipTop(tooltipElem); 1.709 + break; 1.710 + } 1.711 + 1.712 + // adjust tooltip offsets 1.713 + left -= offset.left; 1.714 + top -= offset.top; 1.715 + 1.716 + // using tooltip.style('transform') returns values un-usable for tween 1.717 + var box = tooltipElem.getBoundingClientRect(); 1.718 + var scrollTop = window.pageYOffset || document.documentElement.scrollTop; 1.719 + var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft; 1.720 + var old_translate = 'translate(' + (box.left + scrollLeft) + 'px, ' + (box.top + scrollTop) + 'px)'; 1.721 + var new_translate = 'translate(' + left + 'px, ' + top + 'px)'; 1.722 + var translateInterpolator = d3.interpolateString(old_translate, new_translate); 1.723 + 1.724 + var is_hidden = tooltip.style('opacity') < 0.1; 1.725 + 1.726 + // delay hiding a bit to avoid flickering 1.727 + if (hidden) { 1.728 + tooltip 1.729 + .transition() 1.730 + .delay(hideDelay) 1.731 + .duration(0) 1.732 + .style('opacity', 0); 1.733 + } else { 1.734 + tooltip 1.735 + .interrupt() // cancel running transitions 1.736 + .transition() 1.737 + .duration(is_hidden ? 0 : duration) 1.738 + // using tween since some versions of d3 can't auto-tween a translate on a div 1.739 + .styleTween('transform', function (d) { 1.740 + return translateInterpolator; 1.741 + }, 'important') 1.742 + // Safari has its own `-webkit-transform` and does not support `transform` 1.743 + // transform tooltip without transition only in Safari 1.744 + .style('-webkit-transform', new_translate) 1.745 + .style('opacity', 1); 1.746 + } 1.747 + 1.748 + 1.749 + 1.750 + }); 1.751 + }; 1.752 + 1.753 + //In situations where the chart is in a 'viewBox', re-position the tooltip based on how far chart is zoomed. 1.754 + function convertViewBoxRatio() { 1.755 + if (chartContainer) { 1.756 + var svg = d3.select(chartContainer); 1.757 + if (svg.node().tagName !== "svg") { 1.758 + svg = svg.select("svg"); 1.759 + } 1.760 + var viewBox = (svg.node()) ? svg.attr('viewBox') : null; 1.761 + if (viewBox) { 1.762 + viewBox = viewBox.split(' '); 1.763 + var ratio = parseInt(svg.style('width'), 10) / viewBox[2]; 1.764 + 1.765 + position.left = position.left * ratio; 1.766 + position.top = position.top * ratio; 1.767 + } 1.768 + } 1.769 + } 1.770 + 1.771 + //Creates new tooltip container, or uses existing one on DOM. 1.772 + function initTooltip() { 1.773 + if (!tooltip) { 1.774 + var body; 1.775 + if (chartContainer) { 1.776 + body = chartContainer; 1.777 + } else { 1.778 + body = document.body; 1.779 + } 1.780 + //Create new tooltip div if it doesn't exist on DOM. 1.781 + tooltip = d3.select(body).append("div") 1.782 + .attr("class", "nvtooltip " + (classes ? classes : "xy-tooltip")) 1.783 + .attr("id", id); 1.784 + tooltip.style("top", 0).style("left", 0); 1.785 + tooltip.style('opacity', 0); 1.786 + tooltip.selectAll("div, table, td, tr").classed(nvPointerEventsClass, true); 1.787 + tooltip.classed(nvPointerEventsClass, true); 1.788 + tooltipElem = tooltip.node(); 1.789 + } 1.790 + } 1.791 + 1.792 + //Draw the tooltip onto the DOM. 1.793 + function nvtooltip() { 1.794 + if (!enabled) return; 1.795 + if (!dataSeriesExists(data)) return; 1.796 + 1.797 + convertViewBoxRatio(); 1.798 + 1.799 + var left = position.left; 1.800 + var top = (fixedTop !== null) ? fixedTop : position.top; 1.801 + 1.802 + nv.dom.write(function () { 1.803 + initTooltip(); 1.804 + // generate data and set it into tooltip 1.805 + // Bonus - If you override contentGenerator and return falsey you can use something like 1.806 + // React or Knockout to bind the data for your tooltip 1.807 + var newContent = contentGenerator(data); 1.808 + if (newContent) { 1.809 + tooltipElem.innerHTML = newContent; 1.810 + } 1.811 + 1.812 + if (chartContainer && isInteractiveLayer) { 1.813 + nv.dom.read(function() { 1.814 + var svgComp = chartContainer.getElementsByTagName("svg")[0]; 1.815 + var svgOffset = {left:0,top:0}; 1.816 + if (svgComp) { 1.817 + var svgBound = svgComp.getBoundingClientRect(); 1.818 + var chartBound = chartContainer.getBoundingClientRect(); 1.819 + var svgBoundTop = svgBound.top; 1.820 + 1.821 + //Defensive code. Sometimes, svgBoundTop can be a really negative 1.822 + // number, like -134254. That's a bug. 1.823 + // If such a number is found, use zero instead. FireFox bug only 1.824 + if (svgBoundTop < 0) { 1.825 + var containerBound = chartContainer.getBoundingClientRect(); 1.826 + svgBoundTop = (Math.abs(svgBoundTop) > containerBound.height) ? 0 : svgBoundTop; 1.827 + } 1.828 + svgOffset.top = Math.abs(svgBoundTop - chartBound.top); 1.829 + svgOffset.left = Math.abs(svgBound.left - chartBound.left); 1.830 + } 1.831 + //If the parent container is an overflow <div> with scrollbars, subtract the scroll offsets. 1.832 + //You need to also add any offset between the <svg> element and its containing <div> 1.833 + //Finally, add any offset of the containing <div> on the whole page. 1.834 + left += chartContainer.offsetLeft + svgOffset.left - 2*chartContainer.scrollLeft; 1.835 + top += chartContainer.offsetTop + svgOffset.top - 2*chartContainer.scrollTop; 1.836 + 1.837 + if (snapDistance && snapDistance > 0) { 1.838 + top = Math.floor(top/snapDistance) * snapDistance; 1.839 + } 1.840 + calcTooltipPosition([left,top]); 1.841 + }); 1.842 + } else { 1.843 + calcTooltipPosition([left,top]); 1.844 + } 1.845 + }); 1.846 + 1.847 + return nvtooltip; 1.848 + } 1.849 + 1.850 + nvtooltip.nvPointerEventsClass = nvPointerEventsClass; 1.851 + nvtooltip.options = nv.utils.optionsFunc.bind(nvtooltip); 1.852 + 1.853 + nvtooltip._options = Object.create({}, { 1.854 + // simple read/write options 1.855 + duration: {get: function(){return duration;}, set: function(_){duration=_;}}, 1.856 + gravity: {get: function(){return gravity;}, set: function(_){gravity=_;}}, 1.857 + distance: {get: function(){return distance;}, set: function(_){distance=_;}}, 1.858 + snapDistance: {get: function(){return snapDistance;}, set: function(_){snapDistance=_;}}, 1.859 + classes: {get: function(){return classes;}, set: function(_){classes=_;}}, 1.860 + chartContainer: {get: function(){return chartContainer;}, set: function(_){chartContainer=_;}}, 1.861 + fixedTop: {get: function(){return fixedTop;}, set: function(_){fixedTop=_;}}, 1.862 + enabled: {get: function(){return enabled;}, set: function(_){enabled=_;}}, 1.863 + hideDelay: {get: function(){return hideDelay;}, set: function(_){hideDelay=_;}}, 1.864 + contentGenerator: {get: function(){return contentGenerator;}, set: function(_){contentGenerator=_;}}, 1.865 + valueFormatter: {get: function(){return valueFormatter;}, set: function(_){valueFormatter=_;}}, 1.866 + headerFormatter: {get: function(){return headerFormatter;}, set: function(_){headerFormatter=_;}}, 1.867 + keyFormatter: {get: function(){return keyFormatter;}, set: function(_){keyFormatter=_;}}, 1.868 + headerEnabled: {get: function(){return headerEnabled;}, set: function(_){headerEnabled=_;}}, 1.869 + 1.870 + // internal use only, set by interactive layer to adjust position. 1.871 + _isInteractiveLayer: {get: function(){return isInteractiveLayer;}, set: function(_){isInteractiveLayer=!!_;}}, 1.872 + 1.873 + // options with extra logic 1.874 + position: {get: function(){return position;}, set: function(_){ 1.875 + position.left = _.left !== undefined ? _.left : position.left; 1.876 + position.top = _.top !== undefined ? _.top : position.top; 1.877 + }}, 1.878 + offset: {get: function(){return offset;}, set: function(_){ 1.879 + offset.left = _.left !== undefined ? _.left : offset.left; 1.880 + offset.top = _.top !== undefined ? _.top : offset.top; 1.881 + }}, 1.882 + hidden: {get: function(){return hidden;}, set: function(_){ 1.883 + if (hidden != _) { 1.884 + hidden = !!_; 1.885 + nvtooltip(); 1.886 + } 1.887 + }}, 1.888 + data: {get: function(){return data;}, set: function(_){ 1.889 + // if showing a single data point, adjust data format with that 1.890 + if (_.point) { 1.891 + _.value = _.point.x; 1.892 + _.series = _.series || {}; 1.893 + _.series.value = _.point.y; 1.894 + _.series.color = _.point.color || _.series.color; 1.895 + } 1.896 + data = _; 1.897 + }}, 1.898 + 1.899 + // read only properties 1.900 + tooltipElem: {get: function(){return tooltipElem;}, set: function(_){}}, 1.901 + id: {get: function(){return id;}, set: function(_){}} 1.902 + }); 1.903 + 1.904 + nv.utils.initOptions(nvtooltip); 1.905 + return nvtooltip; 1.906 + }; 1.907 + 1.908 +})(); 1.909 + 1.910 + 1.911 +/* 1.912 +Gets the browser window size 1.913 + 1.914 +Returns object with height and width properties 1.915 + */ 1.916 +nv.utils.windowSize = function() { 1.917 + // Sane defaults 1.918 + var size = {width: 640, height: 480}; 1.919 + 1.920 + // Most recent browsers use 1.921 + if (window.innerWidth && window.innerHeight) { 1.922 + size.width = window.innerWidth; 1.923 + size.height = window.innerHeight; 1.924 + return (size); 1.925 + } 1.926 + 1.927 + // IE can use depending on mode it is in 1.928 + if (document.compatMode=='CSS1Compat' && 1.929 + document.documentElement && 1.930 + document.documentElement.offsetWidth ) { 1.931 + 1.932 + size.width = document.documentElement.offsetWidth; 1.933 + size.height = document.documentElement.offsetHeight; 1.934 + return (size); 1.935 + } 1.936 + 1.937 + // Earlier IE uses Doc.body 1.938 + if (document.body && document.body.offsetWidth) { 1.939 + size.width = document.body.offsetWidth; 1.940 + size.height = document.body.offsetHeight; 1.941 + return (size); 1.942 + } 1.943 + 1.944 + return (size); 1.945 +}; 1.946 + 1.947 +/* 1.948 +Binds callback function to run when window is resized 1.949 + */ 1.950 +nv.utils.windowResize = function(handler) { 1.951 + if (window.addEventListener) { 1.952 + window.addEventListener('resize', handler); 1.953 + } else { 1.954 + nv.log("ERROR: Failed to bind to window.resize with: ", handler); 1.955 + } 1.956 + // return object with clear function to remove the single added callback. 1.957 + return { 1.958 + callback: handler, 1.959 + clear: function() { 1.960 + window.removeEventListener('resize', handler); 1.961 + } 1.962 + } 1.963 +}; 1.964 + 1.965 + 1.966 +/* 1.967 +Backwards compatible way to implement more d3-like coloring of graphs. 1.968 +Can take in nothing, an array, or a function/scale 1.969 +To use a normal scale, get the range and pass that because we must be able 1.970 +to take two arguments and use the index to keep backward compatibility 1.971 +*/ 1.972 +nv.utils.getColor = function(color) { 1.973 + //if you pass in nothing, get default colors back 1.974 + if (color === undefined) { 1.975 + return nv.utils.defaultColor(); 1.976 + 1.977 + //if passed an array, turn it into a color scale 1.978 + // use isArray, instanceof fails if d3 range is created in an iframe 1.979 + } else if(Array.isArray(color)) { 1.980 + var color_scale = d3.scale.ordinal().range(color); 1.981 + return function(d, i) { 1.982 + var key = i === undefined ? d : i; 1.983 + return d.color || color_scale(key); 1.984 + }; 1.985 + 1.986 + //if passed a function or scale, return it, or whatever it may be 1.987 + //external libs, such as angularjs-nvd3-directives use this 1.988 + } else { 1.989 + //can't really help it if someone passes rubbish as color 1.990 + return color; 1.991 + } 1.992 +}; 1.993 + 1.994 + 1.995 +/* 1.996 +Default color chooser uses a color scale of 20 colors from D3 1.997 + https://github.com/mbostock/d3/wiki/Ordinal-Scales#categorical-colors 1.998 + */ 1.999 +nv.utils.defaultColor = function() { 1.1000 + // get range of the scale so we'll turn it into our own function. 1.1001 + return nv.utils.getColor(d3.scale.category20().range()); 1.1002 +}; 1.1003 + 1.1004 + 1.1005 +/* 1.1006 +Returns a color function that takes the result of 'getKey' for each series and 1.1007 +looks for a corresponding color from the dictionary 1.1008 +*/ 1.1009 +nv.utils.customTheme = function(dictionary, getKey, defaultColors) { 1.1010 + // use default series.key if getKey is undefined 1.1011 + getKey = getKey || function(series) { return series.key }; 1.1012 + defaultColors = defaultColors || d3.scale.category20().range(); 1.1013 + 1.1014 + // start at end of default color list and walk back to index 0 1.1015 + var defIndex = defaultColors.length; 1.1016 + 1.1017 + return function(series, index) { 1.1018 + var key = getKey(series); 1.1019 + if (typeof dictionary[key] === 'function') { 1.1020 + return dictionary[key](); 1.1021 + } else if (dictionary[key] !== undefined) { 1.1022 + return dictionary[key]; 1.1023 + } else { 1.1024 + // no match in dictionary, use a default color 1.1025 + if (!defIndex) { 1.1026 + // used all the default colors, start over 1.1027 + defIndex = defaultColors.length; 1.1028 + } 1.1029 + defIndex = defIndex - 1; 1.1030 + return defaultColors[defIndex]; 1.1031 + } 1.1032 + }; 1.1033 +}; 1.1034 + 1.1035 + 1.1036 +/* 1.1037 +From the PJAX example on d3js.org, while this is not really directly needed 1.1038 +it's a very cool method for doing pjax, I may expand upon it a little bit, 1.1039 +open to suggestions on anything that may be useful 1.1040 +*/ 1.1041 +nv.utils.pjax = function(links, content) { 1.1042 + 1.1043 + var load = function(href) { 1.1044 + d3.html(href, function(fragment) { 1.1045 + var target = d3.select(content).node(); 1.1046 + target.parentNode.replaceChild( 1.1047 + d3.select(fragment).select(content).node(), 1.1048 + target); 1.1049 + nv.utils.pjax(links, content); 1.1050 + }); 1.1051 + }; 1.1052 + 1.1053 + d3.selectAll(links).on("click", function() { 1.1054 + history.pushState(this.href, this.textContent, this.href); 1.1055 + load(this.href); 1.1056 + d3.event.preventDefault(); 1.1057 + }); 1.1058 + 1.1059 + d3.select(window).on("popstate", function() { 1.1060 + if (d3.event.state) { 1.1061 + load(d3.event.state); 1.1062 + } 1.1063 + }); 1.1064 +}; 1.1065 + 1.1066 + 1.1067 +/* 1.1068 +For when we want to approximate the width in pixels for an SVG:text element. 1.1069 +Most common instance is when the element is in a display:none; container. 1.1070 +Forumla is : text.length * font-size * constant_factor 1.1071 +*/ 1.1072 +nv.utils.calcApproxTextWidth = function (svgTextElem) { 1.1073 + if (typeof svgTextElem.style === 'function' 1.1074 + && typeof svgTextElem.text === 'function') { 1.1075 + 1.1076 + var fontSize = parseInt(svgTextElem.style("font-size").replace("px",""), 10); 1.1077 + var textLength = svgTextElem.text().length; 1.1078 + return textLength * fontSize * 0.5; 1.1079 + } 1.1080 + return 0; 1.1081 +}; 1.1082 + 1.1083 + 1.1084 +/* 1.1085 +Numbers that are undefined, null or NaN, convert them to zeros. 1.1086 +*/ 1.1087 +nv.utils.NaNtoZero = function(n) { 1.1088 + if (typeof n !== 'number' 1.1089 + || isNaN(n) 1.1090 + || n === null 1.1091 + || n === Infinity 1.1092 + || n === -Infinity) { 1.1093 + 1.1094 + return 0; 1.1095 + } 1.1096 + return n; 1.1097 +}; 1.1098 + 1.1099 +/* 1.1100 +Add a way to watch for d3 transition ends to d3 1.1101 +*/ 1.1102 +d3.selection.prototype.watchTransition = function(renderWatch){ 1.1103 + var args = [this].concat([].slice.call(arguments, 1)); 1.1104 + return renderWatch.transition.apply(renderWatch, args); 1.1105 +}; 1.1106 + 1.1107 + 1.1108 +/* 1.1109 +Helper object to watch when d3 has rendered something 1.1110 +*/ 1.1111 +nv.utils.renderWatch = function(dispatch, duration) { 1.1112 + if (!(this instanceof nv.utils.renderWatch)) { 1.1113 + return new nv.utils.renderWatch(dispatch, duration); 1.1114 + } 1.1115 + 1.1116 + var _duration = duration !== undefined ? duration : 250; 1.1117 + var renderStack = []; 1.1118 + var self = this; 1.1119 + 1.1120 + this.models = function(models) { 1.1121 + models = [].slice.call(arguments, 0); 1.1122 + models.forEach(function(model){ 1.1123 + model.__rendered = false; 1.1124 + (function(m){ 1.1125 + m.dispatch.on('renderEnd', function(arg){ 1.1126 + m.__rendered = true; 1.1127 + self.renderEnd('model'); 1.1128 + }); 1.1129 + })(model); 1.1130 + 1.1131 + if (renderStack.indexOf(model) < 0) { 1.1132 + renderStack.push(model); 1.1133 + } 1.1134 + }); 1.1135 + return this; 1.1136 + }; 1.1137 + 1.1138 + this.reset = function(duration) { 1.1139 + if (duration !== undefined) { 1.1140 + _duration = duration; 1.1141 + } 1.1142 + renderStack = []; 1.1143 + }; 1.1144 + 1.1145 + this.transition = function(selection, args, duration) { 1.1146 + args = arguments.length > 1 ? [].slice.call(arguments, 1) : []; 1.1147 + 1.1148 + if (args.length > 1) { 1.1149 + duration = args.pop(); 1.1150 + } else { 1.1151 + duration = _duration !== undefined ? _duration : 250; 1.1152 + } 1.1153 + selection.__rendered = false; 1.1154 + 1.1155 + if (renderStack.indexOf(selection) < 0) { 1.1156 + renderStack.push(selection); 1.1157 + } 1.1158 + 1.1159 + if (duration === 0) { 1.1160 + selection.__rendered = true; 1.1161 + selection.delay = function() { return this; }; 1.1162 + selection.duration = function() { return this; }; 1.1163 + return selection; 1.1164 + } else { 1.1165 + if (selection.length === 0) { 1.1166 + selection.__rendered = true; 1.1167 + } else if (selection.every( function(d){ return !d.length; } )) { 1.1168 + selection.__rendered = true; 1.1169 + } else { 1.1170 + selection.__rendered = false; 1.1171 + } 1.1172 + 1.1173 + var n = 0; 1.1174 + return selection 1.1175 + .transition() 1.1176 + .duration(duration) 1.1177 + .each(function(){ ++n; }) 1.1178 + .each('end', function(d, i) { 1.1179 + if (--n === 0) { 1.1180 + selection.__rendered = true; 1.1181 + self.renderEnd.apply(this, args); 1.1182 + } 1.1183 + }); 1.1184 + } 1.1185 + }; 1.1186 + 1.1187 + this.renderEnd = function() { 1.1188 + if (renderStack.every( function(d){ return d.__rendered; } )) { 1.1189 + renderStack.forEach( function(d){ d.__rendered = false; }); 1.1190 + dispatch.renderEnd.apply(this, arguments); 1.1191 + } 1.1192 + } 1.1193 + 1.1194 +}; 1.1195 + 1.1196 + 1.1197 +/* 1.1198 +Takes multiple objects and combines them into the first one (dst) 1.1199 +example: nv.utils.deepExtend({a: 1}, {a: 2, b: 3}, {c: 4}); 1.1200 +gives: {a: 2, b: 3, c: 4} 1.1201 +*/ 1.1202 +nv.utils.deepExtend = function(dst){ 1.1203 + var sources = arguments.length > 1 ? [].slice.call(arguments, 1) : []; 1.1204 + sources.forEach(function(source) { 1.1205 + for (var key in source) { 1.1206 + var isArray = dst[key] instanceof Array; 1.1207 + var isObject = typeof dst[key] === 'object'; 1.1208 + var srcObj = typeof source[key] === 'object'; 1.1209 + 1.1210 + if (isObject && !isArray && srcObj) { 1.1211 + nv.utils.deepExtend(dst[key], source[key]); 1.1212 + } else { 1.1213 + dst[key] = source[key]; 1.1214 + } 1.1215 + } 1.1216 + }); 1.1217 +}; 1.1218 + 1.1219 + 1.1220 +/* 1.1221 +state utility object, used to track d3 states in the models 1.1222 +*/ 1.1223 +nv.utils.state = function(){ 1.1224 + if (!(this instanceof nv.utils.state)) { 1.1225 + return new nv.utils.state(); 1.1226 + } 1.1227 + var state = {}; 1.1228 + var _self = this; 1.1229 + var _setState = function(){}; 1.1230 + var _getState = function(){ return {}; }; 1.1231 + var init = null; 1.1232 + var changed = null; 1.1233 + 1.1234 + this.dispatch = d3.dispatch('change', 'set'); 1.1235 + 1.1236 + this.dispatch.on('set', function(state){ 1.1237 + _setState(state, true); 1.1238 + }); 1.1239 + 1.1240 + this.getter = function(fn){ 1.1241 + _getState = fn; 1.1242 + return this; 1.1243 + }; 1.1244 + 1.1245 + this.setter = function(fn, callback) { 1.1246 + if (!callback) { 1.1247 + callback = function(){}; 1.1248 + } 1.1249 + _setState = function(state, update){ 1.1250 + fn(state); 1.1251 + if (update) { 1.1252 + callback(); 1.1253 + } 1.1254 + }; 1.1255 + return this; 1.1256 + }; 1.1257 + 1.1258 + this.init = function(state){ 1.1259 + init = init || {}; 1.1260 + nv.utils.deepExtend(init, state); 1.1261 + }; 1.1262 + 1.1263 + var _set = function(){ 1.1264 + var settings = _getState(); 1.1265 + 1.1266 + if (JSON.stringify(settings) === JSON.stringify(state)) { 1.1267 + return false; 1.1268 + } 1.1269 + 1.1270 + for (var key in settings) { 1.1271 + if (state[key] === undefined) { 1.1272 + state[key] = {}; 1.1273 + } 1.1274 + state[key] = settings[key]; 1.1275 + changed = true; 1.1276 + } 1.1277 + return true; 1.1278 + }; 1.1279 + 1.1280 + this.update = function(){ 1.1281 + if (init) { 1.1282 + _setState(init, false); 1.1283 + init = null; 1.1284 + } 1.1285 + if (_set.call(this)) { 1.1286 + this.dispatch.change(state); 1.1287 + } 1.1288 + }; 1.1289 + 1.1290 +}; 1.1291 + 1.1292 + 1.1293 +/* 1.1294 +Snippet of code you can insert into each nv.models.* to give you the ability to 1.1295 +do things like: 1.1296 +chart.options({ 1.1297 + showXAxis: true, 1.1298 + tooltips: true 1.1299 +}); 1.1300 + 1.1301 +To enable in the chart: 1.1302 +chart.options = nv.utils.optionsFunc.bind(chart); 1.1303 +*/ 1.1304 +nv.utils.optionsFunc = function(args) { 1.1305 + if (args) { 1.1306 + d3.map(args).forEach((function(key,value) { 1.1307 + if (typeof this[key] === "function") { 1.1308 + this[key](value); 1.1309 + } 1.1310 + }).bind(this)); 1.1311 + } 1.1312 + return this; 1.1313 +}; 1.1314 + 1.1315 + 1.1316 +/* 1.1317 +numTicks: requested number of ticks 1.1318 +data: the chart data 1.1319 + 1.1320 +returns the number of ticks to actually use on X axis, based on chart data 1.1321 +to avoid duplicate ticks with the same value 1.1322 +*/ 1.1323 +nv.utils.calcTicksX = function(numTicks, data) { 1.1324 + // find max number of values from all data streams 1.1325 + var numValues = 1; 1.1326 + var i = 0; 1.1327 + for (i; i < data.length; i += 1) { 1.1328 + var stream_len = data[i] && data[i].values ? data[i].values.length : 0; 1.1329 + numValues = stream_len > numValues ? stream_len : numValues; 1.1330 + } 1.1331 + nv.log("Requested number of ticks: ", numTicks); 1.1332 + nv.log("Calculated max values to be: ", numValues); 1.1333 + // make sure we don't have more ticks than values to avoid duplicates 1.1334 + numTicks = numTicks > numValues ? numTicks = numValues - 1 : numTicks; 1.1335 + // make sure we have at least one tick 1.1336 + numTicks = numTicks < 1 ? 1 : numTicks; 1.1337 + // make sure it's an integer 1.1338 + numTicks = Math.floor(numTicks); 1.1339 + nv.log("Calculating tick count as: ", numTicks); 1.1340 + return numTicks; 1.1341 +}; 1.1342 + 1.1343 + 1.1344 +/* 1.1345 +returns number of ticks to actually use on Y axis, based on chart data 1.1346 +*/ 1.1347 +nv.utils.calcTicksY = function(numTicks, data) { 1.1348 + // currently uses the same logic but we can adjust here if needed later 1.1349 + return nv.utils.calcTicksX(numTicks, data); 1.1350 +}; 1.1351 + 1.1352 + 1.1353 +/* 1.1354 +Add a particular option from an options object onto chart 1.1355 +Options exposed on a chart are a getter/setter function that returns chart 1.1356 +on set to mimic typical d3 option chaining, e.g. svg.option1('a').option2('b'); 1.1357 + 1.1358 +option objects should be generated via Object.create() to provide 1.1359 +the option of manipulating data via get/set functions. 1.1360 +*/ 1.1361 +nv.utils.initOption = function(chart, name) { 1.1362 + // if it's a call option, just call it directly, otherwise do get/set 1.1363 + if (chart._calls && chart._calls[name]) { 1.1364 + chart[name] = chart._calls[name]; 1.1365 + } else { 1.1366 + chart[name] = function (_) { 1.1367 + if (!arguments.length) return chart._options[name]; 1.1368 + chart._overrides[name] = true; 1.1369 + chart._options[name] = _; 1.1370 + return chart; 1.1371 + }; 1.1372 + // calling the option as _option will ignore if set by option already 1.1373 + // so nvd3 can set options internally but the stop if set manually 1.1374 + chart['_' + name] = function(_) { 1.1375 + if (!arguments.length) return chart._options[name]; 1.1376 + if (!chart._overrides[name]) { 1.1377 + chart._options[name] = _; 1.1378 + } 1.1379 + return chart; 1.1380 + } 1.1381 + } 1.1382 +}; 1.1383 + 1.1384 + 1.1385 +/* 1.1386 +Add all options in an options object to the chart 1.1387 +*/ 1.1388 +nv.utils.initOptions = function(chart) { 1.1389 + chart._overrides = chart._overrides || {}; 1.1390 + var ops = Object.getOwnPropertyNames(chart._options || {}); 1.1391 + var calls = Object.getOwnPropertyNames(chart._calls || {}); 1.1392 + ops = ops.concat(calls); 1.1393 + for (var i in ops) { 1.1394 + nv.utils.initOption(chart, ops[i]); 1.1395 + } 1.1396 +}; 1.1397 + 1.1398 + 1.1399 +/* 1.1400 +Inherit options from a D3 object 1.1401 +d3.rebind makes calling the function on target actually call it on source 1.1402 +Also use _d3options so we can track what we inherit for documentation and chained inheritance 1.1403 +*/ 1.1404 +nv.utils.inheritOptionsD3 = function(target, d3_source, oplist) { 1.1405 + target._d3options = oplist.concat(target._d3options || []); 1.1406 + oplist.unshift(d3_source); 1.1407 + oplist.unshift(target); 1.1408 + d3.rebind.apply(this, oplist); 1.1409 +}; 1.1410 + 1.1411 + 1.1412 +/* 1.1413 +Remove duplicates from an array 1.1414 +*/ 1.1415 +nv.utils.arrayUnique = function(a) { 1.1416 + return a.sort().filter(function(item, pos) { 1.1417 + return !pos || item != a[pos - 1]; 1.1418 + }); 1.1419 +}; 1.1420 + 1.1421 + 1.1422 +/* 1.1423 +Keeps a list of custom symbols to draw from in addition to d3.svg.symbol 1.1424 +Necessary since d3 doesn't let you extend its list -_- 1.1425 +Add new symbols by doing nv.utils.symbols.set('name', function(size){...}); 1.1426 +*/ 1.1427 +nv.utils.symbolMap = d3.map(); 1.1428 + 1.1429 + 1.1430 +/* 1.1431 +Replaces d3.svg.symbol so that we can look both there and our own map 1.1432 + */ 1.1433 +nv.utils.symbol = function() { 1.1434 + var type, 1.1435 + size = 64; 1.1436 + function symbol(d,i) { 1.1437 + var t = type.call(this,d,i); 1.1438 + var s = size.call(this,d,i); 1.1439 + if (d3.svg.symbolTypes.indexOf(t) !== -1) { 1.1440 + return d3.svg.symbol().type(t).size(s)(); 1.1441 + } else { 1.1442 + return nv.utils.symbolMap.get(t)(s); 1.1443 + } 1.1444 + } 1.1445 + symbol.type = function(_) { 1.1446 + if (!arguments.length) return type; 1.1447 + type = d3.functor(_); 1.1448 + return symbol; 1.1449 + }; 1.1450 + symbol.size = function(_) { 1.1451 + if (!arguments.length) return size; 1.1452 + size = d3.functor(_); 1.1453 + return symbol; 1.1454 + }; 1.1455 + return symbol; 1.1456 +}; 1.1457 + 1.1458 + 1.1459 +/* 1.1460 +Inherit option getter/setter functions from source to target 1.1461 +d3.rebind makes calling the function on target actually call it on source 1.1462 +Also track via _inherited and _d3options so we can track what we inherit 1.1463 +for documentation generation purposes and chained inheritance 1.1464 +*/ 1.1465 +nv.utils.inheritOptions = function(target, source) { 1.1466 + // inherit all the things 1.1467 + var ops = Object.getOwnPropertyNames(source._options || {}); 1.1468 + var calls = Object.getOwnPropertyNames(source._calls || {}); 1.1469 + var inherited = source._inherited || []; 1.1470 + var d3ops = source._d3options || []; 1.1471 + var args = ops.concat(calls).concat(inherited).concat(d3ops); 1.1472 + args.unshift(source); 1.1473 + args.unshift(target); 1.1474 + d3.rebind.apply(this, args); 1.1475 + // pass along the lists to keep track of them, don't allow duplicates 1.1476 + target._inherited = nv.utils.arrayUnique(ops.concat(calls).concat(inherited).concat(ops).concat(target._inherited || [])); 1.1477 + target._d3options = nv.utils.arrayUnique(d3ops.concat(target._d3options || [])); 1.1478 +}; 1.1479 + 1.1480 + 1.1481 +/* 1.1482 +Runs common initialize code on the svg before the chart builds 1.1483 +*/ 1.1484 +nv.utils.initSVG = function(svg) { 1.1485 + svg.classed({'nvd3-svg':true}); 1.1486 +}; 1.1487 + 1.1488 + 1.1489 +/* 1.1490 +Sanitize and provide default for the container height. 1.1491 +*/ 1.1492 +nv.utils.sanitizeHeight = function(height, container) { 1.1493 + return (height || parseInt(container.style('height'), 10) || 400); 1.1494 +}; 1.1495 + 1.1496 + 1.1497 +/* 1.1498 +Sanitize and provide default for the container width. 1.1499 +*/ 1.1500 +nv.utils.sanitizeWidth = function(width, container) { 1.1501 + return (width || parseInt(container.style('width'), 10) || 960); 1.1502 +}; 1.1503 + 1.1504 + 1.1505 +/* 1.1506 +Calculate the available height for a chart. 1.1507 +*/ 1.1508 +nv.utils.availableHeight = function(height, container, margin) { 1.1509 + return nv.utils.sanitizeHeight(height, container) - margin.top - margin.bottom; 1.1510 +}; 1.1511 + 1.1512 +/* 1.1513 +Calculate the available width for a chart. 1.1514 +*/ 1.1515 +nv.utils.availableWidth = function(width, container, margin) { 1.1516 + return nv.utils.sanitizeWidth(width, container) - margin.left - margin.right; 1.1517 +}; 1.1518 + 1.1519 +/* 1.1520 +Clear any rendered chart components and display a chart's 'noData' message 1.1521 +*/ 1.1522 +nv.utils.noData = function(chart, container) { 1.1523 + var opt = chart.options(), 1.1524 + margin = opt.margin(), 1.1525 + noData = opt.noData(), 1.1526 + data = (noData == null) ? ["No Data Available."] : [noData], 1.1527 + height = nv.utils.availableHeight(opt.height(), container, margin), 1.1528 + width = nv.utils.availableWidth(opt.width(), container, margin), 1.1529 + x = margin.left + width/2, 1.1530 + y = margin.top + height/2; 1.1531 + 1.1532 + //Remove any previously created chart components 1.1533 + container.selectAll('g').remove(); 1.1534 + 1.1535 + var noDataText = container.selectAll('.nv-noData').data(data); 1.1536 + 1.1537 + noDataText.enter().append('text') 1.1538 + .attr('class', 'nvd3 nv-noData') 1.1539 + .attr('dy', '-.7em') 1.1540 + .style('text-anchor', 'middle'); 1.1541 + 1.1542 + noDataText 1.1543 + .attr('x', x) 1.1544 + .attr('y', y) 1.1545 + .text(function(t){ return t; }); 1.1546 +}; 1.1547 + 1.1548 +nv.models.axis = function() { 1.1549 + "use strict"; 1.1550 + 1.1551 + //============================================================ 1.1552 + // Public Variables with Default Settings 1.1553 + //------------------------------------------------------------ 1.1554 + 1.1555 + var axis = d3.svg.axis(); 1.1556 + var scale = d3.scale.linear(); 1.1557 + 1.1558 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.1559 + , width = 75 //only used for tickLabel currently 1.1560 + , height = 60 //only used for tickLabel currently 1.1561 + , axisLabelText = null 1.1562 + , showMaxMin = true //TODO: showMaxMin should be disabled on all ordinal scaled axes 1.1563 + , rotateLabels = 0 1.1564 + , rotateYLabel = true 1.1565 + , staggerLabels = false 1.1566 + , isOrdinal = false 1.1567 + , ticks = null 1.1568 + , axisLabelDistance = 0 1.1569 + , duration = 250 1.1570 + , dispatch = d3.dispatch('renderEnd') 1.1571 + ; 1.1572 + axis 1.1573 + .scale(scale) 1.1574 + .orient('bottom') 1.1575 + .tickFormat(function(d) { return d }) 1.1576 + ; 1.1577 + 1.1578 + //============================================================ 1.1579 + // Private Variables 1.1580 + //------------------------------------------------------------ 1.1581 + 1.1582 + var scale0; 1.1583 + var renderWatch = nv.utils.renderWatch(dispatch, duration); 1.1584 + 1.1585 + function chart(selection) { 1.1586 + renderWatch.reset(); 1.1587 + selection.each(function(data) { 1.1588 + var container = d3.select(this); 1.1589 + nv.utils.initSVG(container); 1.1590 + 1.1591 + // Setup containers and skeleton of chart 1.1592 + var wrap = container.selectAll('g.nv-wrap.nv-axis').data([data]); 1.1593 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-axis'); 1.1594 + var gEnter = wrapEnter.append('g'); 1.1595 + var g = wrap.select('g'); 1.1596 + 1.1597 + if (ticks !== null) 1.1598 + axis.ticks(ticks); 1.1599 + else if (axis.orient() == 'top' || axis.orient() == 'bottom') 1.1600 + axis.ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 100); 1.1601 + 1.1602 + //TODO: consider calculating width/height based on whether or not label is added, for reference in charts using this component 1.1603 + g.watchTransition(renderWatch, 'axis').call(axis); 1.1604 + 1.1605 + scale0 = scale0 || axis.scale(); 1.1606 + 1.1607 + var fmt = axis.tickFormat(); 1.1608 + if (fmt == null) { 1.1609 + fmt = scale0.tickFormat(); 1.1610 + } 1.1611 + 1.1612 + var axisLabel = g.selectAll('text.nv-axislabel') 1.1613 + .data([axisLabelText || null]); 1.1614 + axisLabel.exit().remove(); 1.1615 + 1.1616 + var xLabelMargin; 1.1617 + var axisMaxMin; 1.1618 + var w; 1.1619 + switch (axis.orient()) { 1.1620 + case 'top': 1.1621 + axisLabel.enter().append('text').attr('class', 'nv-axislabel'); 1.1622 + if (scale.range().length < 2) { 1.1623 + w = 0; 1.1624 + } else if (scale.range().length === 2) { 1.1625 + w = scale.range()[1]; 1.1626 + } else { 1.1627 + w = scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]); 1.1628 + } 1.1629 + axisLabel 1.1630 + .attr('text-anchor', 'middle') 1.1631 + .attr('y', 0) 1.1632 + .attr('x', w/2); 1.1633 + if (showMaxMin) { 1.1634 + axisMaxMin = wrap.selectAll('g.nv-axisMaxMin') 1.1635 + .data(scale.domain()); 1.1636 + axisMaxMin.enter().append('g').attr('class',function(d,i){ 1.1637 + return ['nv-axisMaxMin','nv-axisMaxMin-x',(i == 0 ? 'nv-axisMin-x':'nv-axisMax-x')].join(' ') 1.1638 + }).append('text'); 1.1639 + axisMaxMin.exit().remove(); 1.1640 + axisMaxMin 1.1641 + .attr('transform', function(d,i) { 1.1642 + return 'translate(' + nv.utils.NaNtoZero(scale(d)) + ',0)' 1.1643 + }) 1.1644 + .select('text') 1.1645 + .attr('dy', '-0.5em') 1.1646 + .attr('y', -axis.tickPadding()) 1.1647 + .attr('text-anchor', 'middle') 1.1648 + .text(function(d,i) { 1.1649 + var v = fmt(d); 1.1650 + return ('' + v).match('NaN') ? '' : v; 1.1651 + }); 1.1652 + axisMaxMin.watchTransition(renderWatch, 'min-max top') 1.1653 + .attr('transform', function(d,i) { 1.1654 + return 'translate(' + nv.utils.NaNtoZero(scale.range()[i]) + ',0)' 1.1655 + }); 1.1656 + } 1.1657 + break; 1.1658 + case 'bottom': 1.1659 + xLabelMargin = axisLabelDistance + 36; 1.1660 + var maxTextWidth = 30; 1.1661 + var textHeight = 0; 1.1662 + var xTicks = g.selectAll('g').select("text"); 1.1663 + var rotateLabelsRule = ''; 1.1664 + if (rotateLabels%360) { 1.1665 + //Calculate the longest xTick width 1.1666 + xTicks.each(function(d,i){ 1.1667 + var box = this.getBoundingClientRect(); 1.1668 + var width = box.width; 1.1669 + textHeight = box.height; 1.1670 + if(width > maxTextWidth) maxTextWidth = width; 1.1671 + }); 1.1672 + rotateLabelsRule = 'rotate(' + rotateLabels + ' 0,' + (textHeight/2 + axis.tickPadding()) + ')'; 1.1673 + //Convert to radians before calculating sin. Add 30 to margin for healthy padding. 1.1674 + var sin = Math.abs(Math.sin(rotateLabels*Math.PI/180)); 1.1675 + xLabelMargin = (sin ? sin*maxTextWidth : maxTextWidth)+30; 1.1676 + //Rotate all xTicks 1.1677 + xTicks 1.1678 + .attr('transform', rotateLabelsRule) 1.1679 + .style('text-anchor', rotateLabels%360 > 0 ? 'start' : 'end'); 1.1680 + } 1.1681 + axisLabel.enter().append('text').attr('class', 'nv-axislabel'); 1.1682 + if (scale.range().length < 2) { 1.1683 + w = 0; 1.1684 + } else if (scale.range().length === 2) { 1.1685 + w = scale.range()[1]; 1.1686 + } else { 1.1687 + w = scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]); 1.1688 + } 1.1689 + axisLabel 1.1690 + .attr('text-anchor', 'middle') 1.1691 + .attr('y', xLabelMargin) 1.1692 + .attr('x', w/2); 1.1693 + if (showMaxMin) { 1.1694 + //if (showMaxMin && !isOrdinal) { 1.1695 + axisMaxMin = wrap.selectAll('g.nv-axisMaxMin') 1.1696 + //.data(scale.domain()) 1.1697 + .data([scale.domain()[0], scale.domain()[scale.domain().length - 1]]); 1.1698 + axisMaxMin.enter().append('g').attr('class',function(d,i){ 1.1699 + return ['nv-axisMaxMin','nv-axisMaxMin-x',(i == 0 ? 'nv-axisMin-x':'nv-axisMax-x')].join(' ') 1.1700 + }).append('text'); 1.1701 + axisMaxMin.exit().remove(); 1.1702 + axisMaxMin 1.1703 + .attr('transform', function(d,i) { 1.1704 + return 'translate(' + nv.utils.NaNtoZero((scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0))) + ',0)' 1.1705 + }) 1.1706 + .select('text') 1.1707 + .attr('dy', '.71em') 1.1708 + .attr('y', axis.tickPadding()) 1.1709 + .attr('transform', rotateLabelsRule) 1.1710 + .style('text-anchor', rotateLabels ? (rotateLabels%360 > 0 ? 'start' : 'end') : 'middle') 1.1711 + .text(function(d,i) { 1.1712 + var v = fmt(d); 1.1713 + return ('' + v).match('NaN') ? '' : v; 1.1714 + }); 1.1715 + axisMaxMin.watchTransition(renderWatch, 'min-max bottom') 1.1716 + .attr('transform', function(d,i) { 1.1717 + return 'translate(' + nv.utils.NaNtoZero((scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0))) + ',0)' 1.1718 + }); 1.1719 + } 1.1720 + if (staggerLabels) 1.1721 + xTicks 1.1722 + .attr('transform', function(d,i) { 1.1723 + return 'translate(0,' + (i % 2 == 0 ? '0' : '12') + ')' 1.1724 + }); 1.1725 + 1.1726 + break; 1.1727 + case 'right': 1.1728 + axisLabel.enter().append('text').attr('class', 'nv-axislabel'); 1.1729 + axisLabel 1.1730 + .style('text-anchor', rotateYLabel ? 'middle' : 'begin') 1.1731 + .attr('transform', rotateYLabel ? 'rotate(90)' : '') 1.1732 + .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 1.1733 + .attr('x', rotateYLabel ? (d3.max(scale.range()) / 2) : axis.tickPadding()); 1.1734 + if (showMaxMin) { 1.1735 + axisMaxMin = wrap.selectAll('g.nv-axisMaxMin') 1.1736 + .data(scale.domain()); 1.1737 + axisMaxMin.enter().append('g').attr('class',function(d,i){ 1.1738 + return ['nv-axisMaxMin','nv-axisMaxMin-y',(i == 0 ? 'nv-axisMin-y':'nv-axisMax-y')].join(' ') 1.1739 + }).append('text') 1.1740 + .style('opacity', 0); 1.1741 + axisMaxMin.exit().remove(); 1.1742 + axisMaxMin 1.1743 + .attr('transform', function(d,i) { 1.1744 + return 'translate(0,' + nv.utils.NaNtoZero(scale(d)) + ')' 1.1745 + }) 1.1746 + .select('text') 1.1747 + .attr('dy', '.32em') 1.1748 + .attr('y', 0) 1.1749 + .attr('x', axis.tickPadding()) 1.1750 + .style('text-anchor', 'start') 1.1751 + .text(function(d, i) { 1.1752 + var v = fmt(d); 1.1753 + return ('' + v).match('NaN') ? '' : v; 1.1754 + }); 1.1755 + axisMaxMin.watchTransition(renderWatch, 'min-max right') 1.1756 + .attr('transform', function(d,i) { 1.1757 + return 'translate(0,' + nv.utils.NaNtoZero(scale.range()[i]) + ')' 1.1758 + }) 1.1759 + .select('text') 1.1760 + .style('opacity', 1); 1.1761 + } 1.1762 + break; 1.1763 + case 'left': 1.1764 + /* 1.1765 + //For dynamically placing the label. Can be used with dynamically-sized chart axis margins 1.1766 + var yTicks = g.selectAll('g').select("text"); 1.1767 + yTicks.each(function(d,i){ 1.1768 + var labelPadding = this.getBoundingClientRect().width + axis.tickPadding() + 16; 1.1769 + if(labelPadding > width) width = labelPadding; 1.1770 + }); 1.1771 + */ 1.1772 + axisLabel.enter().append('text').attr('class', 'nv-axislabel'); 1.1773 + axisLabel 1.1774 + .style('text-anchor', rotateYLabel ? 'middle' : 'end') 1.1775 + .attr('transform', rotateYLabel ? 'rotate(-90)' : '') 1.1776 + .attr('y', rotateYLabel ? (-Math.max(margin.left, width) + 25 - (axisLabelDistance || 0)) : -10) 1.1777 + .attr('x', rotateYLabel ? (-d3.max(scale.range()) / 2) : -axis.tickPadding()); 1.1778 + if (showMaxMin) { 1.1779 + axisMaxMin = wrap.selectAll('g.nv-axisMaxMin') 1.1780 + .data(scale.domain()); 1.1781 + axisMaxMin.enter().append('g').attr('class',function(d,i){ 1.1782 + return ['nv-axisMaxMin','nv-axisMaxMin-y',(i == 0 ? 'nv-axisMin-y':'nv-axisMax-y')].join(' ') 1.1783 + }).append('text') 1.1784 + .style('opacity', 0); 1.1785 + axisMaxMin.exit().remove(); 1.1786 + axisMaxMin 1.1787 + .attr('transform', function(d,i) { 1.1788 + return 'translate(0,' + nv.utils.NaNtoZero(scale0(d)) + ')' 1.1789 + }) 1.1790 + .select('text') 1.1791 + .attr('dy', '.32em') 1.1792 + .attr('y', 0) 1.1793 + .attr('x', -axis.tickPadding()) 1.1794 + .attr('text-anchor', 'end') 1.1795 + .text(function(d,i) { 1.1796 + var v = fmt(d); 1.1797 + return ('' + v).match('NaN') ? '' : v; 1.1798 + }); 1.1799 + axisMaxMin.watchTransition(renderWatch, 'min-max right') 1.1800 + .attr('transform', function(d,i) { 1.1801 + return 'translate(0,' + nv.utils.NaNtoZero(scale.range()[i]) + ')' 1.1802 + }) 1.1803 + .select('text') 1.1804 + .style('opacity', 1); 1.1805 + } 1.1806 + break; 1.1807 + } 1.1808 + axisLabel.text(function(d) { return d }); 1.1809 + 1.1810 + if (showMaxMin && (axis.orient() === 'left' || axis.orient() === 'right')) { 1.1811 + //check if max and min overlap other values, if so, hide the values that overlap 1.1812 + g.selectAll('g') // the g's wrapping each tick 1.1813 + .each(function(d,i) { 1.1814 + d3.select(this).select('text').attr('opacity', 1); 1.1815 + 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! 1.1816 + if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL 1.1817 + d3.select(this).attr('opacity', 0); 1.1818 + 1.1819 + d3.select(this).select('text').attr('opacity', 0); // Don't remove the ZERO line!! 1.1820 + } 1.1821 + }); 1.1822 + 1.1823 + //if Max and Min = 0 only show min, Issue #281 1.1824 + if (scale.domain()[0] == scale.domain()[1] && scale.domain()[0] == 0) { 1.1825 + wrap.selectAll('g.nv-axisMaxMin').style('opacity', function (d, i) { 1.1826 + return !i ? 1 : 0 1.1827 + }); 1.1828 + } 1.1829 + } 1.1830 + 1.1831 + if (showMaxMin && (axis.orient() === 'top' || axis.orient() === 'bottom')) { 1.1832 + var maxMinRange = []; 1.1833 + wrap.selectAll('g.nv-axisMaxMin') 1.1834 + .each(function(d,i) { 1.1835 + try { 1.1836 + if (i) // i== 1, max position 1.1837 + 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) 1.1838 + else // i==0, min position 1.1839 + maxMinRange.push(scale(d) + this.getBoundingClientRect().width + 4) 1.1840 + }catch (err) { 1.1841 + if (i) // i== 1, max position 1.1842 + 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) 1.1843 + else // i==0, min position 1.1844 + maxMinRange.push(scale(d) + 4); 1.1845 + } 1.1846 + }); 1.1847 + // the g's wrapping each tick 1.1848 + g.selectAll('g').each(function(d, i) { 1.1849 + if (scale(d) < maxMinRange[0] || scale(d) > maxMinRange[1]) { 1.1850 + if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL 1.1851 + d3.select(this).remove(); 1.1852 + else 1.1853 + d3.select(this).select('text').remove(); // Don't remove the ZERO line!! 1.1854 + } 1.1855 + }); 1.1856 + } 1.1857 + 1.1858 + //Highlight zero tick line 1.1859 + g.selectAll('.tick') 1.1860 + .filter(function (d) { 1.1861 + /* 1.1862 + The filter needs to return only ticks at or near zero. 1.1863 + Numbers like 0.00001 need to count as zero as well, 1.1864 + and the arithmetic trick below solves that. 1.1865 + */ 1.1866 + return !parseFloat(Math.round(d * 100000) / 1000000) && (d !== undefined) 1.1867 + }) 1.1868 + .classed('zero', true); 1.1869 + 1.1870 + //store old scales for use in transitions on update 1.1871 + scale0 = scale.copy(); 1.1872 + 1.1873 + }); 1.1874 + 1.1875 + renderWatch.renderEnd('axis immediate'); 1.1876 + return chart; 1.1877 + } 1.1878 + 1.1879 + //============================================================ 1.1880 + // Expose Public Variables 1.1881 + //------------------------------------------------------------ 1.1882 + 1.1883 + // expose chart's sub-components 1.1884 + chart.axis = axis; 1.1885 + chart.dispatch = dispatch; 1.1886 + 1.1887 + chart.options = nv.utils.optionsFunc.bind(chart); 1.1888 + chart._options = Object.create({}, { 1.1889 + // simple options, just get/set the necessary values 1.1890 + axisLabelDistance: {get: function(){return axisLabelDistance;}, set: function(_){axisLabelDistance=_;}}, 1.1891 + staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}}, 1.1892 + rotateLabels: {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}}, 1.1893 + rotateYLabel: {get: function(){return rotateYLabel;}, set: function(_){rotateYLabel=_;}}, 1.1894 + showMaxMin: {get: function(){return showMaxMin;}, set: function(_){showMaxMin=_;}}, 1.1895 + axisLabel: {get: function(){return axisLabelText;}, set: function(_){axisLabelText=_;}}, 1.1896 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.1897 + ticks: {get: function(){return ticks;}, set: function(_){ticks=_;}}, 1.1898 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.1899 + 1.1900 + // options that require extra logic in the setter 1.1901 + margin: {get: function(){return margin;}, set: function(_){ 1.1902 + margin.top = _.top !== undefined ? _.top : margin.top; 1.1903 + margin.right = _.right !== undefined ? _.right : margin.right; 1.1904 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.1905 + margin.left = _.left !== undefined ? _.left : margin.left; 1.1906 + }}, 1.1907 + duration: {get: function(){return duration;}, set: function(_){ 1.1908 + duration=_; 1.1909 + renderWatch.reset(duration); 1.1910 + }}, 1.1911 + scale: {get: function(){return scale;}, set: function(_){ 1.1912 + scale = _; 1.1913 + axis.scale(scale); 1.1914 + isOrdinal = typeof scale.rangeBands === 'function'; 1.1915 + nv.utils.inheritOptionsD3(chart, scale, ['domain', 'range', 'rangeBand', 'rangeBands']); 1.1916 + }} 1.1917 + }); 1.1918 + 1.1919 + nv.utils.initOptions(chart); 1.1920 + nv.utils.inheritOptionsD3(chart, axis, ['orient', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat']); 1.1921 + nv.utils.inheritOptionsD3(chart, scale, ['domain', 'range', 'rangeBand', 'rangeBands']); 1.1922 + 1.1923 + return chart; 1.1924 +}; 1.1925 +nv.models.boxPlot = function() { 1.1926 + "use strict"; 1.1927 + 1.1928 + //============================================================ 1.1929 + // Public Variables with Default Settings 1.1930 + //------------------------------------------------------------ 1.1931 + 1.1932 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.1933 + , width = 960 1.1934 + , height = 500 1.1935 + , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one 1.1936 + , x = d3.scale.ordinal() 1.1937 + , y = d3.scale.linear() 1.1938 + , getX = function(d) { return d.x } 1.1939 + , getY = function(d) { return d.y } 1.1940 + , color = nv.utils.defaultColor() 1.1941 + , container = null 1.1942 + , xDomain 1.1943 + , yDomain 1.1944 + , xRange 1.1945 + , yRange 1.1946 + , dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd') 1.1947 + , duration = 250 1.1948 + , maxBoxWidth = null 1.1949 + ; 1.1950 + 1.1951 + //============================================================ 1.1952 + // Private Variables 1.1953 + //------------------------------------------------------------ 1.1954 + 1.1955 + var x0, y0; 1.1956 + var renderWatch = nv.utils.renderWatch(dispatch, duration); 1.1957 + 1.1958 + function chart(selection) { 1.1959 + renderWatch.reset(); 1.1960 + selection.each(function(data) { 1.1961 + var availableWidth = width - margin.left - margin.right, 1.1962 + availableHeight = height - margin.top - margin.bottom; 1.1963 + 1.1964 + container = d3.select(this); 1.1965 + nv.utils.initSVG(container); 1.1966 + 1.1967 + // Setup Scales 1.1968 + x .domain(xDomain || data.map(function(d,i) { return getX(d,i); })) 1.1969 + .rangeBands(xRange || [0, availableWidth], .1); 1.1970 + 1.1971 + // if we know yDomain, no need to calculate 1.1972 + var yData = [] 1.1973 + if (!yDomain) { 1.1974 + // (y-range is based on quartiles, whiskers and outliers) 1.1975 + 1.1976 + // lower values 1.1977 + var yMin = d3.min(data.map(function(d) { 1.1978 + var min_arr = []; 1.1979 + 1.1980 + min_arr.push(d.values.Q1); 1.1981 + if (d.values.hasOwnProperty('whisker_low') && d.values.whisker_low !== null) { min_arr.push(d.values.whisker_low); } 1.1982 + if (d.values.hasOwnProperty('outliers') && d.values.outliers !== null) { min_arr = min_arr.concat(d.values.outliers); } 1.1983 + 1.1984 + return d3.min(min_arr); 1.1985 + })); 1.1986 + 1.1987 + // upper values 1.1988 + var yMax = d3.max(data.map(function(d) { 1.1989 + var max_arr = []; 1.1990 + 1.1991 + max_arr.push(d.values.Q3); 1.1992 + if (d.values.hasOwnProperty('whisker_high') && d.values.whisker_high !== null) { max_arr.push(d.values.whisker_high); } 1.1993 + if (d.values.hasOwnProperty('outliers') && d.values.outliers !== null) { max_arr = max_arr.concat(d.values.outliers); } 1.1994 + 1.1995 + return d3.max(max_arr); 1.1996 + })); 1.1997 + 1.1998 + yData = [ yMin, yMax ] ; 1.1999 + } 1.2000 + 1.2001 + y.domain(yDomain || yData); 1.2002 + y.range(yRange || [availableHeight, 0]); 1.2003 + 1.2004 + //store old scales if they exist 1.2005 + x0 = x0 || x; 1.2006 + y0 = y0 || y.copy().range([y(0),y(0)]); 1.2007 + 1.2008 + // Setup containers and skeleton of chart 1.2009 + var wrap = container.selectAll('g.nv-wrap').data([data]); 1.2010 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap'); 1.2011 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.2012 + 1.2013 + var boxplots = wrap.selectAll('.nv-boxplot').data(function(d) { return d }); 1.2014 + var boxEnter = boxplots.enter().append('g').style('stroke-opacity', 1e-6).style('fill-opacity', 1e-6); 1.2015 + boxplots 1.2016 + .attr('class', 'nv-boxplot') 1.2017 + .attr('transform', function(d,i,j) { return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05) + ', 0)'; }) 1.2018 + .classed('hover', function(d) { return d.hover }); 1.2019 + boxplots 1.2020 + .watchTransition(renderWatch, 'nv-boxplot: boxplots') 1.2021 + .style('stroke-opacity', 1) 1.2022 + .style('fill-opacity', .75) 1.2023 + .delay(function(d,i) { return i * duration / data.length }) 1.2024 + .attr('transform', function(d,i) { 1.2025 + return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05) + ', 0)'; 1.2026 + }); 1.2027 + boxplots.exit().remove(); 1.2028 + 1.2029 + // ----- add the SVG elements for each boxPlot ----- 1.2030 + 1.2031 + // conditionally append whisker lines 1.2032 + boxEnter.each(function(d,i) { 1.2033 + var box = d3.select(this); 1.2034 + 1.2035 + ['low', 'high'].forEach(function(key) { 1.2036 + if (d.values.hasOwnProperty('whisker_' + key) && d.values['whisker_' + key] !== null) { 1.2037 + box.append('line') 1.2038 + .style('stroke', (d.color) ? d.color : color(d,i)) 1.2039 + .attr('class', 'nv-boxplot-whisker nv-boxplot-' + key); 1.2040 + 1.2041 + box.append('line') 1.2042 + .style('stroke', (d.color) ? d.color : color(d,i)) 1.2043 + .attr('class', 'nv-boxplot-tick nv-boxplot-' + key); 1.2044 + } 1.2045 + }); 1.2046 + }); 1.2047 + 1.2048 + // outliers 1.2049 + // TODO: support custom colors here 1.2050 + var outliers = boxplots.selectAll('.nv-boxplot-outlier').data(function(d) { 1.2051 + if (d.values.hasOwnProperty('outliers') && d.values.outliers !== null) { return d.values.outliers; } 1.2052 + else { return []; } 1.2053 + }); 1.2054 + outliers.enter().append('circle') 1.2055 + .style('fill', function(d,i,j) { return color(d,j) }).style('stroke', function(d,i,j) { return color(d,j) }) 1.2056 + .on('mouseover', function(d,i,j) { 1.2057 + d3.select(this).classed('hover', true); 1.2058 + dispatch.elementMouseover({ 1.2059 + series: { key: d, color: color(d,j) }, 1.2060 + e: d3.event 1.2061 + }); 1.2062 + }) 1.2063 + .on('mouseout', function(d,i,j) { 1.2064 + d3.select(this).classed('hover', false); 1.2065 + dispatch.elementMouseout({ 1.2066 + series: { key: d, color: color(d,j) }, 1.2067 + e: d3.event 1.2068 + }); 1.2069 + }) 1.2070 + .on('mousemove', function(d,i) { 1.2071 + dispatch.elementMousemove({e: d3.event}); 1.2072 + }); 1.2073 + 1.2074 + outliers.attr('class', 'nv-boxplot-outlier'); 1.2075 + outliers 1.2076 + .watchTransition(renderWatch, 'nv-boxplot: nv-boxplot-outlier') 1.2077 + .attr('cx', x.rangeBand() * .45) 1.2078 + .attr('cy', function(d,i,j) { return y(d); }) 1.2079 + .attr('r', '3'); 1.2080 + outliers.exit().remove(); 1.2081 + 1.2082 + var box_width = function() { return (maxBoxWidth === null ? x.rangeBand() * .9 : Math.min(75, x.rangeBand() * .9)); }; 1.2083 + var box_left = function() { return x.rangeBand() * .45 - box_width()/2; }; 1.2084 + var box_right = function() { return x.rangeBand() * .45 + box_width()/2; }; 1.2085 + 1.2086 + // update whisker lines and ticks 1.2087 + ['low', 'high'].forEach(function(key) { 1.2088 + var endpoint = (key === 'low') ? 'Q1' : 'Q3'; 1.2089 + 1.2090 + boxplots.select('line.nv-boxplot-whisker.nv-boxplot-' + key) 1.2091 + .watchTransition(renderWatch, 'nv-boxplot: boxplots') 1.2092 + .attr('x1', x.rangeBand() * .45 ) 1.2093 + .attr('y1', function(d,i) { return y(d.values['whisker_' + key]); }) 1.2094 + .attr('x2', x.rangeBand() * .45 ) 1.2095 + .attr('y2', function(d,i) { return y(d.values[endpoint]); }); 1.2096 + 1.2097 + boxplots.select('line.nv-boxplot-tick.nv-boxplot-' + key) 1.2098 + .watchTransition(renderWatch, 'nv-boxplot: boxplots') 1.2099 + .attr('x1', box_left ) 1.2100 + .attr('y1', function(d,i) { return y(d.values['whisker_' + key]); }) 1.2101 + .attr('x2', box_right ) 1.2102 + .attr('y2', function(d,i) { return y(d.values['whisker_' + key]); }); 1.2103 + }); 1.2104 + 1.2105 + ['low', 'high'].forEach(function(key) { 1.2106 + boxEnter.selectAll('.nv-boxplot-' + key) 1.2107 + .on('mouseover', function(d,i,j) { 1.2108 + d3.select(this).classed('hover', true); 1.2109 + dispatch.elementMouseover({ 1.2110 + series: { key: d.values['whisker_' + key], color: color(d,j) }, 1.2111 + e: d3.event 1.2112 + }); 1.2113 + }) 1.2114 + .on('mouseout', function(d,i,j) { 1.2115 + d3.select(this).classed('hover', false); 1.2116 + dispatch.elementMouseout({ 1.2117 + series: { key: d.values['whisker_' + key], color: color(d,j) }, 1.2118 + e: d3.event 1.2119 + }); 1.2120 + }) 1.2121 + .on('mousemove', function(d,i) { 1.2122 + dispatch.elementMousemove({e: d3.event}); 1.2123 + }); 1.2124 + }); 1.2125 + 1.2126 + // boxes 1.2127 + boxEnter.append('rect') 1.2128 + .attr('class', 'nv-boxplot-box') 1.2129 + // tooltip events 1.2130 + .on('mouseover', function(d,i) { 1.2131 + d3.select(this).classed('hover', true); 1.2132 + dispatch.elementMouseover({ 1.2133 + key: d.label, 1.2134 + value: d.label, 1.2135 + series: [ 1.2136 + { key: 'Q3', value: d.values.Q3, color: d.color || color(d,i) }, 1.2137 + { key: 'Q2', value: d.values.Q2, color: d.color || color(d,i) }, 1.2138 + { key: 'Q1', value: d.values.Q1, color: d.color || color(d,i) } 1.2139 + ], 1.2140 + data: d, 1.2141 + index: i, 1.2142 + e: d3.event 1.2143 + }); 1.2144 + }) 1.2145 + .on('mouseout', function(d,i) { 1.2146 + d3.select(this).classed('hover', false); 1.2147 + dispatch.elementMouseout({ 1.2148 + key: d.label, 1.2149 + value: d.label, 1.2150 + series: [ 1.2151 + { key: 'Q3', value: d.values.Q3, color: d.color || color(d,i) }, 1.2152 + { key: 'Q2', value: d.values.Q2, color: d.color || color(d,i) }, 1.2153 + { key: 'Q1', value: d.values.Q1, color: d.color || color(d,i) } 1.2154 + ], 1.2155 + data: d, 1.2156 + index: i, 1.2157 + e: d3.event 1.2158 + }); 1.2159 + }) 1.2160 + .on('mousemove', function(d,i) { 1.2161 + dispatch.elementMousemove({e: d3.event}); 1.2162 + }); 1.2163 + 1.2164 + // box transitions 1.2165 + boxplots.select('rect.nv-boxplot-box') 1.2166 + .watchTransition(renderWatch, 'nv-boxplot: boxes') 1.2167 + .attr('y', function(d,i) { return y(d.values.Q3); }) 1.2168 + .attr('width', box_width) 1.2169 + .attr('x', box_left ) 1.2170 + 1.2171 + .attr('height', function(d,i) { return Math.abs(y(d.values.Q3) - y(d.values.Q1)) || 1 }) 1.2172 + .style('fill', function(d,i) { return d.color || color(d,i) }) 1.2173 + .style('stroke', function(d,i) { return d.color || color(d,i) }); 1.2174 + 1.2175 + // median line 1.2176 + boxEnter.append('line').attr('class', 'nv-boxplot-median'); 1.2177 + 1.2178 + boxplots.select('line.nv-boxplot-median') 1.2179 + .watchTransition(renderWatch, 'nv-boxplot: boxplots line') 1.2180 + .attr('x1', box_left) 1.2181 + .attr('y1', function(d,i) { return y(d.values.Q2); }) 1.2182 + .attr('x2', box_right) 1.2183 + .attr('y2', function(d,i) { return y(d.values.Q2); }); 1.2184 + 1.2185 + //store old scales for use in transitions on update 1.2186 + x0 = x.copy(); 1.2187 + y0 = y.copy(); 1.2188 + }); 1.2189 + 1.2190 + renderWatch.renderEnd('nv-boxplot immediate'); 1.2191 + return chart; 1.2192 + } 1.2193 + 1.2194 + //============================================================ 1.2195 + // Expose Public Variables 1.2196 + //------------------------------------------------------------ 1.2197 + 1.2198 + chart.dispatch = dispatch; 1.2199 + chart.options = nv.utils.optionsFunc.bind(chart); 1.2200 + 1.2201 + chart._options = Object.create({}, { 1.2202 + // simple options, just get/set the necessary values 1.2203 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.2204 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.2205 + maxBoxWidth: {get: function(){return maxBoxWidth;}, set: function(_){maxBoxWidth=_;}}, 1.2206 + x: {get: function(){return getX;}, set: function(_){getX=_;}}, 1.2207 + y: {get: function(){return getY;}, set: function(_){getY=_;}}, 1.2208 + xScale: {get: function(){return x;}, set: function(_){x=_;}}, 1.2209 + yScale: {get: function(){return y;}, set: function(_){y=_;}}, 1.2210 + xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}}, 1.2211 + yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}}, 1.2212 + xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}}, 1.2213 + yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}}, 1.2214 + id: {get: function(){return id;}, set: function(_){id=_;}}, 1.2215 + // rectClass: {get: function(){return rectClass;}, set: function(_){rectClass=_;}}, 1.2216 + 1.2217 + // options that require extra logic in the setter 1.2218 + margin: {get: function(){return margin;}, set: function(_){ 1.2219 + margin.top = _.top !== undefined ? _.top : margin.top; 1.2220 + margin.right = _.right !== undefined ? _.right : margin.right; 1.2221 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.2222 + margin.left = _.left !== undefined ? _.left : margin.left; 1.2223 + }}, 1.2224 + color: {get: function(){return color;}, set: function(_){ 1.2225 + color = nv.utils.getColor(_); 1.2226 + }}, 1.2227 + duration: {get: function(){return duration;}, set: function(_){ 1.2228 + duration = _; 1.2229 + renderWatch.reset(duration); 1.2230 + }} 1.2231 + }); 1.2232 + 1.2233 + nv.utils.initOptions(chart); 1.2234 + 1.2235 + return chart; 1.2236 +}; 1.2237 +nv.models.boxPlotChart = function() { 1.2238 + "use strict"; 1.2239 + 1.2240 + //============================================================ 1.2241 + // Public Variables with Default Settings 1.2242 + //------------------------------------------------------------ 1.2243 + 1.2244 + var boxplot = nv.models.boxPlot() 1.2245 + , xAxis = nv.models.axis() 1.2246 + , yAxis = nv.models.axis() 1.2247 + ; 1.2248 + 1.2249 + var margin = {top: 15, right: 10, bottom: 50, left: 60} 1.2250 + , width = null 1.2251 + , height = null 1.2252 + , color = nv.utils.getColor() 1.2253 + , showXAxis = true 1.2254 + , showYAxis = true 1.2255 + , rightAlignYAxis = false 1.2256 + , staggerLabels = false 1.2257 + , tooltip = nv.models.tooltip() 1.2258 + , x 1.2259 + , y 1.2260 + , noData = "No Data Available." 1.2261 + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'beforeUpdate', 'renderEnd') 1.2262 + , duration = 250 1.2263 + ; 1.2264 + 1.2265 + xAxis 1.2266 + .orient('bottom') 1.2267 + .showMaxMin(false) 1.2268 + .tickFormat(function(d) { return d }) 1.2269 + ; 1.2270 + yAxis 1.2271 + .orient((rightAlignYAxis) ? 'right' : 'left') 1.2272 + .tickFormat(d3.format(',.1f')) 1.2273 + ; 1.2274 + 1.2275 + tooltip.duration(0); 1.2276 + 1.2277 + //============================================================ 1.2278 + // Private Variables 1.2279 + //------------------------------------------------------------ 1.2280 + 1.2281 + var renderWatch = nv.utils.renderWatch(dispatch, duration); 1.2282 + 1.2283 + function chart(selection) { 1.2284 + renderWatch.reset(); 1.2285 + renderWatch.models(boxplot); 1.2286 + if (showXAxis) renderWatch.models(xAxis); 1.2287 + if (showYAxis) renderWatch.models(yAxis); 1.2288 + 1.2289 + selection.each(function(data) { 1.2290 + var container = d3.select(this), 1.2291 + that = this; 1.2292 + nv.utils.initSVG(container); 1.2293 + var availableWidth = (width || parseInt(container.style('width')) || 960) 1.2294 + - margin.left - margin.right, 1.2295 + availableHeight = (height || parseInt(container.style('height')) || 400) 1.2296 + - margin.top - margin.bottom; 1.2297 + 1.2298 + chart.update = function() { 1.2299 + dispatch.beforeUpdate(); 1.2300 + container.transition().duration(duration).call(chart); 1.2301 + }; 1.2302 + chart.container = this; 1.2303 + 1.2304 + // Display No Data message if there's nothing to show. (quartiles required at minimum) 1.2305 + if (!data || !data.length || 1.2306 + !data.filter(function(d) { return d.values.hasOwnProperty("Q1") && d.values.hasOwnProperty("Q2") && d.values.hasOwnProperty("Q3"); }).length) { 1.2307 + var noDataText = container.selectAll('.nv-noData').data([noData]); 1.2308 + 1.2309 + noDataText.enter().append('text') 1.2310 + .attr('class', 'nvd3 nv-noData') 1.2311 + .attr('dy', '-.7em') 1.2312 + .style('text-anchor', 'middle'); 1.2313 + 1.2314 + noDataText 1.2315 + .attr('x', margin.left + availableWidth / 2) 1.2316 + .attr('y', margin.top + availableHeight / 2) 1.2317 + .text(function(d) { return d }); 1.2318 + 1.2319 + return chart; 1.2320 + } else { 1.2321 + container.selectAll('.nv-noData').remove(); 1.2322 + } 1.2323 + 1.2324 + // Setup Scales 1.2325 + x = boxplot.xScale(); 1.2326 + y = boxplot.yScale().clamp(true); 1.2327 + 1.2328 + // Setup containers and skeleton of chart 1.2329 + var wrap = container.selectAll('g.nv-wrap.nv-boxPlotWithAxes').data([data]); 1.2330 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-boxPlotWithAxes').append('g'); 1.2331 + var defsEnter = gEnter.append('defs'); 1.2332 + var g = wrap.select('g'); 1.2333 + 1.2334 + gEnter.append('g').attr('class', 'nv-x nv-axis'); 1.2335 + gEnter.append('g').attr('class', 'nv-y nv-axis') 1.2336 + .append('g').attr('class', 'nv-zeroLine') 1.2337 + .append('line'); 1.2338 + 1.2339 + gEnter.append('g').attr('class', 'nv-barsWrap'); 1.2340 + 1.2341 + g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.2342 + 1.2343 + if (rightAlignYAxis) { 1.2344 + g.select(".nv-y.nv-axis") 1.2345 + .attr("transform", "translate(" + availableWidth + ",0)"); 1.2346 + } 1.2347 + 1.2348 + // Main Chart Component(s) 1.2349 + boxplot 1.2350 + .width(availableWidth) 1.2351 + .height(availableHeight); 1.2352 + 1.2353 + var barsWrap = g.select('.nv-barsWrap') 1.2354 + .datum(data.filter(function(d) { return !d.disabled })) 1.2355 + 1.2356 + barsWrap.transition().call(boxplot); 1.2357 + 1.2358 + 1.2359 + defsEnter.append('clipPath') 1.2360 + .attr('id', 'nv-x-label-clip-' + boxplot.id()) 1.2361 + .append('rect'); 1.2362 + 1.2363 + g.select('#nv-x-label-clip-' + boxplot.id() + ' rect') 1.2364 + .attr('width', x.rangeBand() * (staggerLabels ? 2 : 1)) 1.2365 + .attr('height', 16) 1.2366 + .attr('x', -x.rangeBand() / (staggerLabels ? 1 : 2 )); 1.2367 + 1.2368 + // Setup Axes 1.2369 + if (showXAxis) { 1.2370 + xAxis 1.2371 + .scale(x) 1.2372 + .ticks( nv.utils.calcTicksX(availableWidth/100, data) ) 1.2373 + .tickSize(-availableHeight, 0); 1.2374 + 1.2375 + g.select('.nv-x.nv-axis').attr('transform', 'translate(0,' + y.range()[0] + ')'); 1.2376 + g.select('.nv-x.nv-axis').call(xAxis); 1.2377 + 1.2378 + var xTicks = g.select('.nv-x.nv-axis').selectAll('g'); 1.2379 + if (staggerLabels) { 1.2380 + xTicks 1.2381 + .selectAll('text') 1.2382 + .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 == 0 ? '5' : '17') + ')' }) 1.2383 + } 1.2384 + } 1.2385 + 1.2386 + if (showYAxis) { 1.2387 + yAxis 1.2388 + .scale(y) 1.2389 + .ticks( Math.floor(availableHeight/36) ) // can't use nv.utils.calcTicksY with Object data 1.2390 + .tickSize( -availableWidth, 0); 1.2391 + 1.2392 + g.select('.nv-y.nv-axis').call(yAxis); 1.2393 + } 1.2394 + 1.2395 + // Zero line 1.2396 + g.select(".nv-zeroLine line") 1.2397 + .attr("x1",0) 1.2398 + .attr("x2",availableWidth) 1.2399 + .attr("y1", y(0)) 1.2400 + .attr("y2", y(0)) 1.2401 + ; 1.2402 + 1.2403 + //============================================================ 1.2404 + // Event Handling/Dispatching (in chart's scope) 1.2405 + //------------------------------------------------------------ 1.2406 + }); 1.2407 + 1.2408 + renderWatch.renderEnd('nv-boxplot chart immediate'); 1.2409 + return chart; 1.2410 + } 1.2411 + 1.2412 + //============================================================ 1.2413 + // Event Handling/Dispatching (out of chart's scope) 1.2414 + //------------------------------------------------------------ 1.2415 + 1.2416 + boxplot.dispatch.on('elementMouseover.tooltip', function(evt) { 1.2417 + tooltip.data(evt).hidden(false); 1.2418 + }); 1.2419 + 1.2420 + boxplot.dispatch.on('elementMouseout.tooltip', function(evt) { 1.2421 + tooltip.data(evt).hidden(true); 1.2422 + }); 1.2423 + 1.2424 + boxplot.dispatch.on('elementMousemove.tooltip', function(evt) { 1.2425 + tooltip.position({top: d3.event.pageY, left: d3.event.pageX})(); 1.2426 + }); 1.2427 + 1.2428 + //============================================================ 1.2429 + // Expose Public Variables 1.2430 + //------------------------------------------------------------ 1.2431 + 1.2432 + chart.dispatch = dispatch; 1.2433 + chart.boxplot = boxplot; 1.2434 + chart.xAxis = xAxis; 1.2435 + chart.yAxis = yAxis; 1.2436 + chart.tooltip = tooltip; 1.2437 + 1.2438 + chart.options = nv.utils.optionsFunc.bind(chart); 1.2439 + 1.2440 + chart._options = Object.create({}, { 1.2441 + // simple options, just get/set the necessary values 1.2442 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.2443 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.2444 + staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}}, 1.2445 + showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}}, 1.2446 + showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}}, 1.2447 + tooltips: {get: function(){return tooltips;}, set: function(_){tooltips=_;}}, 1.2448 + tooltipContent: {get: function(){return tooltip;}, set: function(_){tooltip=_;}}, 1.2449 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.2450 + 1.2451 + // options that require extra logic in the setter 1.2452 + margin: {get: function(){return margin;}, set: function(_){ 1.2453 + margin.top = _.top !== undefined ? _.top : margin.top; 1.2454 + margin.right = _.right !== undefined ? _.right : margin.right; 1.2455 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.2456 + margin.left = _.left !== undefined ? _.left : margin.left; 1.2457 + }}, 1.2458 + duration: {get: function(){return duration;}, set: function(_){ 1.2459 + duration = _; 1.2460 + renderWatch.reset(duration); 1.2461 + boxplot.duration(duration); 1.2462 + xAxis.duration(duration); 1.2463 + yAxis.duration(duration); 1.2464 + }}, 1.2465 + color: {get: function(){return color;}, set: function(_){ 1.2466 + color = nv.utils.getColor(_); 1.2467 + boxplot.color(color); 1.2468 + }}, 1.2469 + rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){ 1.2470 + rightAlignYAxis = _; 1.2471 + yAxis.orient( (_) ? 'right' : 'left'); 1.2472 + }} 1.2473 + }); 1.2474 + 1.2475 + nv.utils.inheritOptions(chart, boxplot); 1.2476 + nv.utils.initOptions(chart); 1.2477 + 1.2478 + return chart; 1.2479 +} 1.2480 +// Chart design based on the recommendations of Stephen Few. Implementation 1.2481 +// based on the work of Clint Ivy, Jamie Love, and Jason Davies. 1.2482 +// http://projects.instantcognition.com/protovis/bulletchart/ 1.2483 + 1.2484 +nv.models.bullet = function() { 1.2485 + "use strict"; 1.2486 + 1.2487 + //============================================================ 1.2488 + // Public Variables with Default Settings 1.2489 + //------------------------------------------------------------ 1.2490 + 1.2491 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.2492 + , orient = 'left' // TODO top & bottom 1.2493 + , reverse = false 1.2494 + , ranges = function(d) { return d.ranges } 1.2495 + , markers = function(d) { return d.markers ? d.markers : [0] } 1.2496 + , measures = function(d) { return d.measures } 1.2497 + , rangeLabels = function(d) { return d.rangeLabels ? d.rangeLabels : [] } 1.2498 + , markerLabels = function(d) { return d.markerLabels ? d.markerLabels : [] } 1.2499 + , measureLabels = function(d) { return d.measureLabels ? d.measureLabels : [] } 1.2500 + , forceX = [0] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.) 1.2501 + , width = 380 1.2502 + , height = 30 1.2503 + , container = null 1.2504 + , tickFormat = null 1.2505 + , color = nv.utils.getColor(['#1f77b4']) 1.2506 + , dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove') 1.2507 + ; 1.2508 + 1.2509 + function chart(selection) { 1.2510 + selection.each(function(d, i) { 1.2511 + var availableWidth = width - margin.left - margin.right, 1.2512 + availableHeight = height - margin.top - margin.bottom; 1.2513 + 1.2514 + container = d3.select(this); 1.2515 + nv.utils.initSVG(container); 1.2516 + 1.2517 + var rangez = ranges.call(this, d, i).slice().sort(d3.descending), 1.2518 + markerz = markers.call(this, d, i).slice().sort(d3.descending), 1.2519 + measurez = measures.call(this, d, i).slice().sort(d3.descending), 1.2520 + rangeLabelz = rangeLabels.call(this, d, i).slice(), 1.2521 + markerLabelz = markerLabels.call(this, d, i).slice(), 1.2522 + measureLabelz = measureLabels.call(this, d, i).slice(); 1.2523 + 1.2524 + // Setup Scales 1.2525 + // Compute the new x-scale. 1.2526 + var x1 = d3.scale.linear() 1.2527 + .domain( d3.extent(d3.merge([forceX, rangez])) ) 1.2528 + .range(reverse ? [availableWidth, 0] : [0, availableWidth]); 1.2529 + 1.2530 + // Retrieve the old x-scale, if this is an update. 1.2531 + var x0 = this.__chart__ || d3.scale.linear() 1.2532 + .domain([0, Infinity]) 1.2533 + .range(x1.range()); 1.2534 + 1.2535 + // Stash the new scale. 1.2536 + this.__chart__ = x1; 1.2537 + 1.2538 + var rangeMin = d3.min(rangez), //rangez[2] 1.2539 + rangeMax = d3.max(rangez), //rangez[0] 1.2540 + rangeAvg = rangez[1]; 1.2541 + 1.2542 + // Setup containers and skeleton of chart 1.2543 + var wrap = container.selectAll('g.nv-wrap.nv-bullet').data([d]); 1.2544 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bullet'); 1.2545 + var gEnter = wrapEnter.append('g'); 1.2546 + var g = wrap.select('g'); 1.2547 + 1.2548 + gEnter.append('rect').attr('class', 'nv-range nv-rangeMax'); 1.2549 + gEnter.append('rect').attr('class', 'nv-range nv-rangeAvg'); 1.2550 + gEnter.append('rect').attr('class', 'nv-range nv-rangeMin'); 1.2551 + gEnter.append('rect').attr('class', 'nv-measure'); 1.2552 + 1.2553 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.2554 + 1.2555 + var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0) 1.2556 + w1 = function(d) { return Math.abs(x1(d) - x1(0)) }; 1.2557 + var xp0 = function(d) { return d < 0 ? x0(d) : x0(0) }, 1.2558 + xp1 = function(d) { return d < 0 ? x1(d) : x1(0) }; 1.2559 + 1.2560 + g.select('rect.nv-rangeMax') 1.2561 + .attr('height', availableHeight) 1.2562 + .attr('width', w1(rangeMax > 0 ? rangeMax : rangeMin)) 1.2563 + .attr('x', xp1(rangeMax > 0 ? rangeMax : rangeMin)) 1.2564 + .datum(rangeMax > 0 ? rangeMax : rangeMin) 1.2565 + 1.2566 + g.select('rect.nv-rangeAvg') 1.2567 + .attr('height', availableHeight) 1.2568 + .attr('width', w1(rangeAvg)) 1.2569 + .attr('x', xp1(rangeAvg)) 1.2570 + .datum(rangeAvg) 1.2571 + 1.2572 + g.select('rect.nv-rangeMin') 1.2573 + .attr('height', availableHeight) 1.2574 + .attr('width', w1(rangeMax)) 1.2575 + .attr('x', xp1(rangeMax)) 1.2576 + .attr('width', w1(rangeMax > 0 ? rangeMin : rangeMax)) 1.2577 + .attr('x', xp1(rangeMax > 0 ? rangeMin : rangeMax)) 1.2578 + .datum(rangeMax > 0 ? rangeMin : rangeMax) 1.2579 + 1.2580 + g.select('rect.nv-measure') 1.2581 + .style('fill', color) 1.2582 + .attr('height', availableHeight / 3) 1.2583 + .attr('y', availableHeight / 3) 1.2584 + .attr('width', measurez < 0 ? 1.2585 + x1(0) - x1(measurez[0]) 1.2586 + : x1(measurez[0]) - x1(0)) 1.2587 + .attr('x', xp1(measurez)) 1.2588 + .on('mouseover', function() { 1.2589 + dispatch.elementMouseover({ 1.2590 + value: measurez[0], 1.2591 + label: measureLabelz[0] || 'Current', 1.2592 + color: d3.select(this).style("fill") 1.2593 + }) 1.2594 + }) 1.2595 + .on('mousemove', function() { 1.2596 + dispatch.elementMousemove({ 1.2597 + value: measurez[0], 1.2598 + label: measureLabelz[0] || 'Current', 1.2599 + color: d3.select(this).style("fill") 1.2600 + }) 1.2601 + }) 1.2602 + .on('mouseout', function() { 1.2603 + dispatch.elementMouseout({ 1.2604 + value: measurez[0], 1.2605 + label: measureLabelz[0] || 'Current', 1.2606 + color: d3.select(this).style("fill") 1.2607 + }) 1.2608 + }); 1.2609 + 1.2610 + var h3 = availableHeight / 6; 1.2611 + 1.2612 + var markerData = markerz.map( function(marker, index) { 1.2613 + return {value: marker, label: markerLabelz[index]} 1.2614 + }); 1.2615 + gEnter 1.2616 + .selectAll("path.nv-markerTriangle") 1.2617 + .data(markerData) 1.2618 + .enter() 1.2619 + .append('path') 1.2620 + .attr('class', 'nv-markerTriangle') 1.2621 + .attr('transform', function(d) { return 'translate(' + x1(d.value) + ',' + (availableHeight / 2) + ')' }) 1.2622 + .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z') 1.2623 + .on('mouseover', function(d) { 1.2624 + dispatch.elementMouseover({ 1.2625 + value: d.value, 1.2626 + label: d.label || 'Previous', 1.2627 + color: d3.select(this).style("fill"), 1.2628 + pos: [x1(d.value), availableHeight/2] 1.2629 + }) 1.2630 + 1.2631 + }) 1.2632 + .on('mousemove', function(d) { 1.2633 + dispatch.elementMousemove({ 1.2634 + value: d.value, 1.2635 + label: d.label || 'Previous', 1.2636 + color: d3.select(this).style("fill") 1.2637 + }) 1.2638 + }) 1.2639 + .on('mouseout', function(d, i) { 1.2640 + dispatch.elementMouseout({ 1.2641 + value: d.value, 1.2642 + label: d.label || 'Previous', 1.2643 + color: d3.select(this).style("fill") 1.2644 + }) 1.2645 + }); 1.2646 + 1.2647 + wrap.selectAll('.nv-range') 1.2648 + .on('mouseover', function(d,i) { 1.2649 + var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum"); 1.2650 + dispatch.elementMouseover({ 1.2651 + value: d, 1.2652 + label: label, 1.2653 + color: d3.select(this).style("fill") 1.2654 + }) 1.2655 + }) 1.2656 + .on('mousemove', function() { 1.2657 + dispatch.elementMousemove({ 1.2658 + value: measurez[0], 1.2659 + label: measureLabelz[0] || 'Previous', 1.2660 + color: d3.select(this).style("fill") 1.2661 + }) 1.2662 + }) 1.2663 + .on('mouseout', function(d,i) { 1.2664 + var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum"); 1.2665 + dispatch.elementMouseout({ 1.2666 + value: d, 1.2667 + label: label, 1.2668 + color: d3.select(this).style("fill") 1.2669 + }) 1.2670 + }); 1.2671 + }); 1.2672 + 1.2673 + return chart; 1.2674 + } 1.2675 + 1.2676 + //============================================================ 1.2677 + // Expose Public Variables 1.2678 + //------------------------------------------------------------ 1.2679 + 1.2680 + chart.dispatch = dispatch; 1.2681 + chart.options = nv.utils.optionsFunc.bind(chart); 1.2682 + 1.2683 + chart._options = Object.create({}, { 1.2684 + // simple options, just get/set the necessary values 1.2685 + ranges: {get: function(){return ranges;}, set: function(_){ranges=_;}}, // ranges (bad, satisfactory, good) 1.2686 + markers: {get: function(){return markers;}, set: function(_){markers=_;}}, // markers (previous, goal) 1.2687 + measures: {get: function(){return measures;}, set: function(_){measures=_;}}, // measures (actual, forecast) 1.2688 + forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}}, 1.2689 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.2690 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.2691 + tickFormat: {get: function(){return tickFormat;}, set: function(_){tickFormat=_;}}, 1.2692 + 1.2693 + // options that require extra logic in the setter 1.2694 + margin: {get: function(){return margin;}, set: function(_){ 1.2695 + margin.top = _.top !== undefined ? _.top : margin.top; 1.2696 + margin.right = _.right !== undefined ? _.right : margin.right; 1.2697 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.2698 + margin.left = _.left !== undefined ? _.left : margin.left; 1.2699 + }}, 1.2700 + orient: {get: function(){return orient;}, set: function(_){ // left, right, top, bottom 1.2701 + orient = _; 1.2702 + reverse = orient == 'right' || orient == 'bottom'; 1.2703 + }}, 1.2704 + color: {get: function(){return color;}, set: function(_){ 1.2705 + color = nv.utils.getColor(_); 1.2706 + }} 1.2707 + }); 1.2708 + 1.2709 + nv.utils.initOptions(chart); 1.2710 + return chart; 1.2711 +}; 1.2712 + 1.2713 + 1.2714 + 1.2715 +// Chart design based on the recommendations of Stephen Few. Implementation 1.2716 +// based on the work of Clint Ivy, Jamie Love, and Jason Davies. 1.2717 +// http://projects.instantcognition.com/protovis/bulletchart/ 1.2718 +nv.models.bulletChart = function() { 1.2719 + "use strict"; 1.2720 + 1.2721 + //============================================================ 1.2722 + // Public Variables with Default Settings 1.2723 + //------------------------------------------------------------ 1.2724 + 1.2725 + var bullet = nv.models.bullet(); 1.2726 + var tooltip = nv.models.tooltip(); 1.2727 + 1.2728 + var orient = 'left' // TODO top & bottom 1.2729 + , reverse = false 1.2730 + , margin = {top: 5, right: 40, bottom: 20, left: 120} 1.2731 + , ranges = function(d) { return d.ranges } 1.2732 + , markers = function(d) { return d.markers ? d.markers : [0] } 1.2733 + , measures = function(d) { return d.measures } 1.2734 + , width = null 1.2735 + , height = 55 1.2736 + , tickFormat = null 1.2737 + , ticks = null 1.2738 + , noData = null 1.2739 + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide') 1.2740 + ; 1.2741 + 1.2742 + tooltip.duration(0).headerEnabled(false); 1.2743 + 1.2744 + function chart(selection) { 1.2745 + selection.each(function(d, i) { 1.2746 + var container = d3.select(this); 1.2747 + nv.utils.initSVG(container); 1.2748 + 1.2749 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.2750 + availableHeight = height - margin.top - margin.bottom, 1.2751 + that = this; 1.2752 + 1.2753 + chart.update = function() { chart(selection) }; 1.2754 + chart.container = this; 1.2755 + 1.2756 + // Display No Data message if there's nothing to show. 1.2757 + if (!d || !ranges.call(this, d, i)) { 1.2758 + nv.utils.noData(chart, container) 1.2759 + return chart; 1.2760 + } else { 1.2761 + container.selectAll('.nv-noData').remove(); 1.2762 + } 1.2763 + 1.2764 + var rangez = ranges.call(this, d, i).slice().sort(d3.descending), 1.2765 + markerz = markers.call(this, d, i).slice().sort(d3.descending), 1.2766 + measurez = measures.call(this, d, i).slice().sort(d3.descending); 1.2767 + 1.2768 + // Setup containers and skeleton of chart 1.2769 + var wrap = container.selectAll('g.nv-wrap.nv-bulletChart').data([d]); 1.2770 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bulletChart'); 1.2771 + var gEnter = wrapEnter.append('g'); 1.2772 + var g = wrap.select('g'); 1.2773 + 1.2774 + gEnter.append('g').attr('class', 'nv-bulletWrap'); 1.2775 + gEnter.append('g').attr('class', 'nv-titles'); 1.2776 + 1.2777 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.2778 + 1.2779 + // Compute the new x-scale. 1.2780 + var x1 = d3.scale.linear() 1.2781 + .domain([0, Math.max(rangez[0], markerz[0], measurez[0])]) // TODO: need to allow forceX and forceY, and xDomain, yDomain 1.2782 + .range(reverse ? [availableWidth, 0] : [0, availableWidth]); 1.2783 + 1.2784 + // Retrieve the old x-scale, if this is an update. 1.2785 + var x0 = this.__chart__ || d3.scale.linear() 1.2786 + .domain([0, Infinity]) 1.2787 + .range(x1.range()); 1.2788 + 1.2789 + // Stash the new scale. 1.2790 + this.__chart__ = x1; 1.2791 + 1.2792 + var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0) 1.2793 + w1 = function(d) { return Math.abs(x1(d) - x1(0)) }; 1.2794 + 1.2795 + var title = gEnter.select('.nv-titles').append('g') 1.2796 + .attr('text-anchor', 'end') 1.2797 + .attr('transform', 'translate(-6,' + (height - margin.top - margin.bottom) / 2 + ')'); 1.2798 + title.append('text') 1.2799 + .attr('class', 'nv-title') 1.2800 + .text(function(d) { return d.title; }); 1.2801 + 1.2802 + title.append('text') 1.2803 + .attr('class', 'nv-subtitle') 1.2804 + .attr('dy', '1em') 1.2805 + .text(function(d) { return d.subtitle; }); 1.2806 + 1.2807 + bullet 1.2808 + .width(availableWidth) 1.2809 + .height(availableHeight) 1.2810 + 1.2811 + var bulletWrap = g.select('.nv-bulletWrap'); 1.2812 + d3.transition(bulletWrap).call(bullet); 1.2813 + 1.2814 + // Compute the tick format. 1.2815 + var format = tickFormat || x1.tickFormat( availableWidth / 100 ); 1.2816 + 1.2817 + // Update the tick groups. 1.2818 + var tick = g.selectAll('g.nv-tick') 1.2819 + .data(x1.ticks( ticks ? ticks : (availableWidth / 50) ), function(d) { 1.2820 + return this.textContent || format(d); 1.2821 + }); 1.2822 + 1.2823 + // Initialize the ticks with the old scale, x0. 1.2824 + var tickEnter = tick.enter().append('g') 1.2825 + .attr('class', 'nv-tick') 1.2826 + .attr('transform', function(d) { return 'translate(' + x0(d) + ',0)' }) 1.2827 + .style('opacity', 1e-6); 1.2828 + 1.2829 + tickEnter.append('line') 1.2830 + .attr('y1', availableHeight) 1.2831 + .attr('y2', availableHeight * 7 / 6); 1.2832 + 1.2833 + tickEnter.append('text') 1.2834 + .attr('text-anchor', 'middle') 1.2835 + .attr('dy', '1em') 1.2836 + .attr('y', availableHeight * 7 / 6) 1.2837 + .text(format); 1.2838 + 1.2839 + // Transition the updating ticks to the new scale, x1. 1.2840 + var tickUpdate = d3.transition(tick) 1.2841 + .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' }) 1.2842 + .style('opacity', 1); 1.2843 + 1.2844 + tickUpdate.select('line') 1.2845 + .attr('y1', availableHeight) 1.2846 + .attr('y2', availableHeight * 7 / 6); 1.2847 + 1.2848 + tickUpdate.select('text') 1.2849 + .attr('y', availableHeight * 7 / 6); 1.2850 + 1.2851 + // Transition the exiting ticks to the new scale, x1. 1.2852 + d3.transition(tick.exit()) 1.2853 + .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' }) 1.2854 + .style('opacity', 1e-6) 1.2855 + .remove(); 1.2856 + }); 1.2857 + 1.2858 + d3.timer.flush(); 1.2859 + return chart; 1.2860 + } 1.2861 + 1.2862 + //============================================================ 1.2863 + // Event Handling/Dispatching (out of chart's scope) 1.2864 + //------------------------------------------------------------ 1.2865 + 1.2866 + bullet.dispatch.on('elementMouseover.tooltip', function(evt) { 1.2867 + evt['series'] = { 1.2868 + key: evt.label, 1.2869 + value: evt.value, 1.2870 + color: evt.color 1.2871 + }; 1.2872 + tooltip.data(evt).hidden(false); 1.2873 + }); 1.2874 + 1.2875 + bullet.dispatch.on('elementMouseout.tooltip', function(evt) { 1.2876 + tooltip.hidden(true); 1.2877 + }); 1.2878 + 1.2879 + bullet.dispatch.on('elementMousemove.tooltip', function(evt) { 1.2880 + tooltip.position({top: d3.event.pageY, left: d3.event.pageX})(); 1.2881 + }); 1.2882 + 1.2883 + //============================================================ 1.2884 + // Expose Public Variables 1.2885 + //------------------------------------------------------------ 1.2886 + 1.2887 + chart.bullet = bullet; 1.2888 + chart.dispatch = dispatch; 1.2889 + chart.tooltip = tooltip; 1.2890 + 1.2891 + chart.options = nv.utils.optionsFunc.bind(chart); 1.2892 + 1.2893 + chart._options = Object.create({}, { 1.2894 + // simple options, just get/set the necessary values 1.2895 + ranges: {get: function(){return ranges;}, set: function(_){ranges=_;}}, // ranges (bad, satisfactory, good) 1.2896 + markers: {get: function(){return markers;}, set: function(_){markers=_;}}, // markers (previous, goal) 1.2897 + measures: {get: function(){return measures;}, set: function(_){measures=_;}}, // measures (actual, forecast) 1.2898 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.2899 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.2900 + tickFormat: {get: function(){return tickFormat;}, set: function(_){tickFormat=_;}}, 1.2901 + ticks: {get: function(){return ticks;}, set: function(_){ticks=_;}}, 1.2902 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.2903 + 1.2904 + // deprecated options 1.2905 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.2906 + // deprecated after 1.7.1 1.2907 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.2908 + tooltip.enabled(!!_); 1.2909 + }}, 1.2910 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.2911 + // deprecated after 1.7.1 1.2912 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.2913 + tooltip.contentGenerator(_); 1.2914 + }}, 1.2915 + 1.2916 + // options that require extra logic in the setter 1.2917 + margin: {get: function(){return margin;}, set: function(_){ 1.2918 + margin.top = _.top !== undefined ? _.top : margin.top; 1.2919 + margin.right = _.right !== undefined ? _.right : margin.right; 1.2920 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.2921 + margin.left = _.left !== undefined ? _.left : margin.left; 1.2922 + }}, 1.2923 + orient: {get: function(){return orient;}, set: function(_){ // left, right, top, bottom 1.2924 + orient = _; 1.2925 + reverse = orient == 'right' || orient == 'bottom'; 1.2926 + }} 1.2927 + }); 1.2928 + 1.2929 + nv.utils.inheritOptions(chart, bullet); 1.2930 + nv.utils.initOptions(chart); 1.2931 + 1.2932 + return chart; 1.2933 +}; 1.2934 + 1.2935 + 1.2936 + 1.2937 +nv.models.candlestickBar = function() { 1.2938 + "use strict"; 1.2939 + 1.2940 + //============================================================ 1.2941 + // Public Variables with Default Settings 1.2942 + //------------------------------------------------------------ 1.2943 + 1.2944 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.2945 + , width = null 1.2946 + , height = null 1.2947 + , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one 1.2948 + , container 1.2949 + , x = d3.scale.linear() 1.2950 + , y = d3.scale.linear() 1.2951 + , getX = function(d) { return d.x } 1.2952 + , getY = function(d) { return d.y } 1.2953 + , getOpen = function(d) { return d.open } 1.2954 + , getClose = function(d) { return d.close } 1.2955 + , getHigh = function(d) { return d.high } 1.2956 + , getLow = function(d) { return d.low } 1.2957 + , forceX = [] 1.2958 + , forceY = [] 1.2959 + , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart 1.2960 + , clipEdge = true 1.2961 + , color = nv.utils.defaultColor() 1.2962 + , interactive = false 1.2963 + , xDomain 1.2964 + , yDomain 1.2965 + , xRange 1.2966 + , yRange 1.2967 + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd', 'chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove') 1.2968 + ; 1.2969 + 1.2970 + //============================================================ 1.2971 + // Private Variables 1.2972 + //------------------------------------------------------------ 1.2973 + 1.2974 + function chart(selection) { 1.2975 + selection.each(function(data) { 1.2976 + container = d3.select(this); 1.2977 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.2978 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.2979 + 1.2980 + nv.utils.initSVG(container); 1.2981 + 1.2982 + // Width of the candlestick bars. 1.2983 + var barWidth = (availableWidth / data[0].values.length) * .45; 1.2984 + 1.2985 + // Setup Scales 1.2986 + x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) )); 1.2987 + 1.2988 + if (padData) 1.2989 + x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]); 1.2990 + else 1.2991 + x.range(xRange || [5 + barWidth / 2, availableWidth - barWidth / 2 - 5]); 1.2992 + 1.2993 + y.domain(yDomain || [ 1.2994 + d3.min(data[0].values.map(getLow).concat(forceY)), 1.2995 + d3.max(data[0].values.map(getHigh).concat(forceY)) 1.2996 + ] 1.2997 + ).range(yRange || [availableHeight, 0]); 1.2998 + 1.2999 + // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point 1.3000 + if (x.domain()[0] === x.domain()[1]) 1.3001 + x.domain()[0] ? 1.3002 + x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01]) 1.3003 + : x.domain([-1,1]); 1.3004 + 1.3005 + if (y.domain()[0] === y.domain()[1]) 1.3006 + y.domain()[0] ? 1.3007 + y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01]) 1.3008 + : y.domain([-1,1]); 1.3009 + 1.3010 + // Setup containers and skeleton of chart 1.3011 + var wrap = d3.select(this).selectAll('g.nv-wrap.nv-candlestickBar').data([data[0].values]); 1.3012 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-candlestickBar'); 1.3013 + var defsEnter = wrapEnter.append('defs'); 1.3014 + var gEnter = wrapEnter.append('g'); 1.3015 + var g = wrap.select('g'); 1.3016 + 1.3017 + gEnter.append('g').attr('class', 'nv-ticks'); 1.3018 + 1.3019 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.3020 + 1.3021 + container 1.3022 + .on('click', function(d,i) { 1.3023 + dispatch.chartClick({ 1.3024 + data: d, 1.3025 + index: i, 1.3026 + pos: d3.event, 1.3027 + id: id 1.3028 + }); 1.3029 + }); 1.3030 + 1.3031 + defsEnter.append('clipPath') 1.3032 + .attr('id', 'nv-chart-clip-path-' + id) 1.3033 + .append('rect'); 1.3034 + 1.3035 + wrap.select('#nv-chart-clip-path-' + id + ' rect') 1.3036 + .attr('width', availableWidth) 1.3037 + .attr('height', availableHeight); 1.3038 + 1.3039 + g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : ''); 1.3040 + 1.3041 + var ticks = wrap.select('.nv-ticks').selectAll('.nv-tick') 1.3042 + .data(function(d) { return d }); 1.3043 + ticks.exit().remove(); 1.3044 + 1.3045 + // The colors are currently controlled by CSS. 1.3046 + var tickGroups = ticks.enter().append('g') 1.3047 + .attr('class', function(d, i, j) { return (getOpen(d, i) > getClose(d, i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i}); 1.3048 + 1.3049 + var lines = tickGroups.append('line') 1.3050 + .attr('class', 'nv-candlestick-lines') 1.3051 + .attr('transform', function(d, i) { return 'translate(' + x(getX(d, i)) + ',0)'; }) 1.3052 + .attr('x1', 0) 1.3053 + .attr('y1', function(d, i) { return y(getHigh(d, i)); }) 1.3054 + .attr('x2', 0) 1.3055 + .attr('y2', function(d, i) { return y(getLow(d, i)); }); 1.3056 + 1.3057 + var rects = tickGroups.append('rect') 1.3058 + .attr('class', 'nv-candlestick-rects nv-bars') 1.3059 + .attr('transform', function(d, i) { 1.3060 + return 'translate(' + (x(getX(d, i)) - barWidth/2) + ',' 1.3061 + + (y(getY(d, i)) - (getOpen(d, i) > getClose(d, i) ? (y(getClose(d, i)) - y(getOpen(d, i))) : 0)) 1.3062 + + ')'; 1.3063 + }) 1.3064 + .attr('x', 0) 1.3065 + .attr('y', 0) 1.3066 + .attr('width', barWidth) 1.3067 + .attr('height', function(d, i) { 1.3068 + var open = getOpen(d, i); 1.3069 + var close = getClose(d, i); 1.3070 + return open > close ? y(close) - y(open) : y(open) - y(close); 1.3071 + }); 1.3072 + 1.3073 + container.selectAll('.nv-candlestick-lines').transition() 1.3074 + .attr('transform', function(d, i) { return 'translate(' + x(getX(d, i)) + ',0)'; }) 1.3075 + .attr('x1', 0) 1.3076 + .attr('y1', function(d, i) { return y(getHigh(d, i)); }) 1.3077 + .attr('x2', 0) 1.3078 + .attr('y2', function(d, i) { return y(getLow(d, i)); }); 1.3079 + 1.3080 + container.selectAll('.nv-candlestick-rects').transition() 1.3081 + .attr('transform', function(d, i) { 1.3082 + return 'translate(' + (x(getX(d, i)) - barWidth/2) + ',' 1.3083 + + (y(getY(d, i)) - (getOpen(d, i) > getClose(d, i) ? (y(getClose(d, i)) - y(getOpen(d, i))) : 0)) 1.3084 + + ')'; 1.3085 + }) 1.3086 + .attr('x', 0) 1.3087 + .attr('y', 0) 1.3088 + .attr('width', barWidth) 1.3089 + .attr('height', function(d, i) { 1.3090 + var open = getOpen(d, i); 1.3091 + var close = getClose(d, i); 1.3092 + return open > close ? y(close) - y(open) : y(open) - y(close); 1.3093 + }); 1.3094 + }); 1.3095 + 1.3096 + return chart; 1.3097 + } 1.3098 + 1.3099 + 1.3100 + //Create methods to allow outside functions to highlight a specific bar. 1.3101 + chart.highlightPoint = function(pointIndex, isHoverOver) { 1.3102 + chart.clearHighlights(); 1.3103 + container.select(".nv-candlestickBar .nv-tick-0-" + pointIndex) 1.3104 + .classed("hover", isHoverOver) 1.3105 + ; 1.3106 + }; 1.3107 + 1.3108 + chart.clearHighlights = function() { 1.3109 + container.select(".nv-candlestickBar .nv-tick.hover") 1.3110 + .classed("hover", false) 1.3111 + ; 1.3112 + }; 1.3113 + 1.3114 + //============================================================ 1.3115 + // Expose Public Variables 1.3116 + //------------------------------------------------------------ 1.3117 + 1.3118 + chart.dispatch = dispatch; 1.3119 + chart.options = nv.utils.optionsFunc.bind(chart); 1.3120 + 1.3121 + chart._options = Object.create({}, { 1.3122 + // simple options, just get/set the necessary values 1.3123 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.3124 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.3125 + xScale: {get: function(){return x;}, set: function(_){x=_;}}, 1.3126 + yScale: {get: function(){return y;}, set: function(_){y=_;}}, 1.3127 + xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}}, 1.3128 + yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}}, 1.3129 + xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}}, 1.3130 + yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}}, 1.3131 + forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}}, 1.3132 + forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}}, 1.3133 + padData: {get: function(){return padData;}, set: function(_){padData=_;}}, 1.3134 + clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}}, 1.3135 + id: {get: function(){return id;}, set: function(_){id=_;}}, 1.3136 + interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}}, 1.3137 + 1.3138 + x: {get: function(){return getX;}, set: function(_){getX=_;}}, 1.3139 + y: {get: function(){return getY;}, set: function(_){getY=_;}}, 1.3140 + open: {get: function(){return getOpen();}, set: function(_){getOpen=_;}}, 1.3141 + close: {get: function(){return getClose();}, set: function(_){getClose=_;}}, 1.3142 + high: {get: function(){return getHigh;}, set: function(_){getHigh=_;}}, 1.3143 + low: {get: function(){return getLow;}, set: function(_){getLow=_;}}, 1.3144 + 1.3145 + // options that require extra logic in the setter 1.3146 + margin: {get: function(){return margin;}, set: function(_){ 1.3147 + margin.top = _.top != undefined ? _.top : margin.top; 1.3148 + margin.right = _.right != undefined ? _.right : margin.right; 1.3149 + margin.bottom = _.bottom != undefined ? _.bottom : margin.bottom; 1.3150 + margin.left = _.left != undefined ? _.left : margin.left; 1.3151 + }}, 1.3152 + color: {get: function(){return color;}, set: function(_){ 1.3153 + color = nv.utils.getColor(_); 1.3154 + }} 1.3155 + }); 1.3156 + 1.3157 + nv.utils.initOptions(chart); 1.3158 + return chart; 1.3159 +}; 1.3160 + 1.3161 +nv.models.cumulativeLineChart = function() { 1.3162 + "use strict"; 1.3163 + 1.3164 + //============================================================ 1.3165 + // Public Variables with Default Settings 1.3166 + //------------------------------------------------------------ 1.3167 + 1.3168 + var lines = nv.models.line() 1.3169 + , xAxis = nv.models.axis() 1.3170 + , yAxis = nv.models.axis() 1.3171 + , legend = nv.models.legend() 1.3172 + , controls = nv.models.legend() 1.3173 + , interactiveLayer = nv.interactiveGuideline() 1.3174 + , tooltip = nv.models.tooltip() 1.3175 + ; 1.3176 + 1.3177 + var margin = {top: 30, right: 30, bottom: 50, left: 60} 1.3178 + , color = nv.utils.defaultColor() 1.3179 + , width = null 1.3180 + , height = null 1.3181 + , showLegend = true 1.3182 + , showXAxis = true 1.3183 + , showYAxis = true 1.3184 + , rightAlignYAxis = false 1.3185 + , showControls = true 1.3186 + , useInteractiveGuideline = false 1.3187 + , rescaleY = true 1.3188 + , x //can be accessed via chart.xScale() 1.3189 + , y //can be accessed via chart.yScale() 1.3190 + , id = lines.id() 1.3191 + , state = nv.utils.state() 1.3192 + , defaultState = null 1.3193 + , noData = null 1.3194 + , average = function(d) { return d.average } 1.3195 + , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd') 1.3196 + , transitionDuration = 250 1.3197 + , duration = 250 1.3198 + , noErrorCheck = false //if set to TRUE, will bypass an error check in the indexify function. 1.3199 + ; 1.3200 + 1.3201 + state.index = 0; 1.3202 + state.rescaleY = rescaleY; 1.3203 + 1.3204 + xAxis.orient('bottom').tickPadding(7); 1.3205 + yAxis.orient((rightAlignYAxis) ? 'right' : 'left'); 1.3206 + 1.3207 + tooltip.valueFormatter(function(d, i) { 1.3208 + return yAxis.tickFormat()(d, i); 1.3209 + }).headerFormatter(function(d, i) { 1.3210 + return xAxis.tickFormat()(d, i); 1.3211 + }); 1.3212 + 1.3213 + controls.updateState(false); 1.3214 + 1.3215 + //============================================================ 1.3216 + // Private Variables 1.3217 + //------------------------------------------------------------ 1.3218 + 1.3219 + var dx = d3.scale.linear() 1.3220 + , index = {i: 0, x: 0} 1.3221 + , renderWatch = nv.utils.renderWatch(dispatch, duration) 1.3222 + ; 1.3223 + 1.3224 + var stateGetter = function(data) { 1.3225 + return function(){ 1.3226 + return { 1.3227 + active: data.map(function(d) { return !d.disabled }), 1.3228 + index: index.i, 1.3229 + rescaleY: rescaleY 1.3230 + }; 1.3231 + } 1.3232 + }; 1.3233 + 1.3234 + var stateSetter = function(data) { 1.3235 + return function(state) { 1.3236 + if (state.index !== undefined) 1.3237 + index.i = state.index; 1.3238 + if (state.rescaleY !== undefined) 1.3239 + rescaleY = state.rescaleY; 1.3240 + if (state.active !== undefined) 1.3241 + data.forEach(function(series,i) { 1.3242 + series.disabled = !state.active[i]; 1.3243 + }); 1.3244 + } 1.3245 + }; 1.3246 + 1.3247 + function chart(selection) { 1.3248 + renderWatch.reset(); 1.3249 + renderWatch.models(lines); 1.3250 + if (showXAxis) renderWatch.models(xAxis); 1.3251 + if (showYAxis) renderWatch.models(yAxis); 1.3252 + selection.each(function(data) { 1.3253 + var container = d3.select(this); 1.3254 + nv.utils.initSVG(container); 1.3255 + container.classed('nv-chart-' + id, true); 1.3256 + var that = this; 1.3257 + 1.3258 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.3259 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.3260 + 1.3261 + chart.update = function() { 1.3262 + if (duration === 0) 1.3263 + container.call(chart); 1.3264 + else 1.3265 + container.transition().duration(duration).call(chart) 1.3266 + }; 1.3267 + chart.container = this; 1.3268 + 1.3269 + state 1.3270 + .setter(stateSetter(data), chart.update) 1.3271 + .getter(stateGetter(data)) 1.3272 + .update(); 1.3273 + 1.3274 + // DEPRECATED set state.disableddisabled 1.3275 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.3276 + 1.3277 + if (!defaultState) { 1.3278 + var key; 1.3279 + defaultState = {}; 1.3280 + for (key in state) { 1.3281 + if (state[key] instanceof Array) 1.3282 + defaultState[key] = state[key].slice(0); 1.3283 + else 1.3284 + defaultState[key] = state[key]; 1.3285 + } 1.3286 + } 1.3287 + 1.3288 + var indexDrag = d3.behavior.drag() 1.3289 + .on('dragstart', dragStart) 1.3290 + .on('drag', dragMove) 1.3291 + .on('dragend', dragEnd); 1.3292 + 1.3293 + 1.3294 + function dragStart(d,i) { 1.3295 + d3.select(chart.container) 1.3296 + .style('cursor', 'ew-resize'); 1.3297 + } 1.3298 + 1.3299 + function dragMove(d,i) { 1.3300 + index.x = d3.event.x; 1.3301 + index.i = Math.round(dx.invert(index.x)); 1.3302 + updateZero(); 1.3303 + } 1.3304 + 1.3305 + function dragEnd(d,i) { 1.3306 + d3.select(chart.container) 1.3307 + .style('cursor', 'auto'); 1.3308 + 1.3309 + // update state and send stateChange with new index 1.3310 + state.index = index.i; 1.3311 + dispatch.stateChange(state); 1.3312 + } 1.3313 + 1.3314 + // Display No Data message if there's nothing to show. 1.3315 + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { 1.3316 + nv.utils.noData(chart, container) 1.3317 + return chart; 1.3318 + } else { 1.3319 + container.selectAll('.nv-noData').remove(); 1.3320 + } 1.3321 + 1.3322 + // Setup Scales 1.3323 + x = lines.xScale(); 1.3324 + y = lines.yScale(); 1.3325 + 1.3326 + if (!rescaleY) { 1.3327 + var seriesDomains = data 1.3328 + .filter(function(series) { return !series.disabled }) 1.3329 + .map(function(series,i) { 1.3330 + var initialDomain = d3.extent(series.values, lines.y()); 1.3331 + 1.3332 + //account for series being disabled when losing 95% or more 1.3333 + if (initialDomain[0] < -.95) initialDomain[0] = -.95; 1.3334 + 1.3335 + return [ 1.3336 + (initialDomain[0] - initialDomain[1]) / (1 + initialDomain[1]), 1.3337 + (initialDomain[1] - initialDomain[0]) / (1 + initialDomain[0]) 1.3338 + ]; 1.3339 + }); 1.3340 + 1.3341 + var completeDomain = [ 1.3342 + d3.min(seriesDomains, function(d) { return d[0] }), 1.3343 + d3.max(seriesDomains, function(d) { return d[1] }) 1.3344 + ]; 1.3345 + 1.3346 + lines.yDomain(completeDomain); 1.3347 + } else { 1.3348 + lines.yDomain(null); 1.3349 + } 1.3350 + 1.3351 + dx.domain([0, data[0].values.length - 1]) //Assumes all series have same length 1.3352 + .range([0, availableWidth]) 1.3353 + .clamp(true); 1.3354 + 1.3355 + var data = indexify(index.i, data); 1.3356 + 1.3357 + // Setup containers and skeleton of chart 1.3358 + var interactivePointerEvents = (useInteractiveGuideline) ? "none" : "all"; 1.3359 + var wrap = container.selectAll('g.nv-wrap.nv-cumulativeLine').data([data]); 1.3360 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-cumulativeLine').append('g'); 1.3361 + var g = wrap.select('g'); 1.3362 + 1.3363 + gEnter.append('g').attr('class', 'nv-interactive'); 1.3364 + gEnter.append('g').attr('class', 'nv-x nv-axis').style("pointer-events","none"); 1.3365 + gEnter.append('g').attr('class', 'nv-y nv-axis'); 1.3366 + gEnter.append('g').attr('class', 'nv-background'); 1.3367 + gEnter.append('g').attr('class', 'nv-linesWrap').style("pointer-events",interactivePointerEvents); 1.3368 + gEnter.append('g').attr('class', 'nv-avgLinesWrap').style("pointer-events","none"); 1.3369 + gEnter.append('g').attr('class', 'nv-legendWrap'); 1.3370 + gEnter.append('g').attr('class', 'nv-controlsWrap'); 1.3371 + 1.3372 + // Legend 1.3373 + if (showLegend) { 1.3374 + legend.width(availableWidth); 1.3375 + 1.3376 + g.select('.nv-legendWrap') 1.3377 + .datum(data) 1.3378 + .call(legend); 1.3379 + 1.3380 + if ( margin.top != legend.height()) { 1.3381 + margin.top = legend.height(); 1.3382 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.3383 + } 1.3384 + 1.3385 + g.select('.nv-legendWrap') 1.3386 + .attr('transform', 'translate(0,' + (-margin.top) +')') 1.3387 + } 1.3388 + 1.3389 + // Controls 1.3390 + if (showControls) { 1.3391 + var controlsData = [ 1.3392 + { key: 'Re-scale y-axis', disabled: !rescaleY } 1.3393 + ]; 1.3394 + 1.3395 + controls 1.3396 + .width(140) 1.3397 + .color(['#444', '#444', '#444']) 1.3398 + .rightAlign(false) 1.3399 + .margin({top: 5, right: 0, bottom: 5, left: 20}) 1.3400 + ; 1.3401 + 1.3402 + g.select('.nv-controlsWrap') 1.3403 + .datum(controlsData) 1.3404 + .attr('transform', 'translate(0,' + (-margin.top) +')') 1.3405 + .call(controls); 1.3406 + } 1.3407 + 1.3408 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.3409 + 1.3410 + if (rightAlignYAxis) { 1.3411 + g.select(".nv-y.nv-axis") 1.3412 + .attr("transform", "translate(" + availableWidth + ",0)"); 1.3413 + } 1.3414 + 1.3415 + // Show error if series goes below 100% 1.3416 + var tempDisabled = data.filter(function(d) { return d.tempDisabled }); 1.3417 + 1.3418 + wrap.select('.tempDisabled').remove(); //clean-up and prevent duplicates 1.3419 + if (tempDisabled.length) { 1.3420 + wrap.append('text').attr('class', 'tempDisabled') 1.3421 + .attr('x', availableWidth / 2) 1.3422 + .attr('y', '-.71em') 1.3423 + .style('text-anchor', 'end') 1.3424 + .text(tempDisabled.map(function(d) { return d.key }).join(', ') + ' values cannot be calculated for this time period.'); 1.3425 + } 1.3426 + 1.3427 + //Set up interactive layer 1.3428 + if (useInteractiveGuideline) { 1.3429 + interactiveLayer 1.3430 + .width(availableWidth) 1.3431 + .height(availableHeight) 1.3432 + .margin({left:margin.left,top:margin.top}) 1.3433 + .svgContainer(container) 1.3434 + .xScale(x); 1.3435 + wrap.select(".nv-interactive").call(interactiveLayer); 1.3436 + } 1.3437 + 1.3438 + gEnter.select('.nv-background') 1.3439 + .append('rect'); 1.3440 + 1.3441 + g.select('.nv-background rect') 1.3442 + .attr('width', availableWidth) 1.3443 + .attr('height', availableHeight); 1.3444 + 1.3445 + lines 1.3446 + //.x(function(d) { return d.x }) 1.3447 + .y(function(d) { return d.display.y }) 1.3448 + .width(availableWidth) 1.3449 + .height(availableHeight) 1.3450 + .color(data.map(function(d,i) { 1.3451 + return d.color || color(d, i); 1.3452 + }).filter(function(d,i) { return !data[i].disabled && !data[i].tempDisabled; })); 1.3453 + 1.3454 + var linesWrap = g.select('.nv-linesWrap') 1.3455 + .datum(data.filter(function(d) { return !d.disabled && !d.tempDisabled })); 1.3456 + 1.3457 + linesWrap.call(lines); 1.3458 + 1.3459 + //Store a series index number in the data array. 1.3460 + data.forEach(function(d,i) { 1.3461 + d.seriesIndex = i; 1.3462 + }); 1.3463 + 1.3464 + var avgLineData = data.filter(function(d) { 1.3465 + return !d.disabled && !!average(d); 1.3466 + }); 1.3467 + 1.3468 + var avgLines = g.select(".nv-avgLinesWrap").selectAll("line") 1.3469 + .data(avgLineData, function(d) { return d.key; }); 1.3470 + 1.3471 + var getAvgLineY = function(d) { 1.3472 + //If average lines go off the svg element, clamp them to the svg bounds. 1.3473 + var yVal = y(average(d)); 1.3474 + if (yVal < 0) return 0; 1.3475 + if (yVal > availableHeight) return availableHeight; 1.3476 + return yVal; 1.3477 + }; 1.3478 + 1.3479 + avgLines.enter() 1.3480 + .append('line') 1.3481 + .style('stroke-width',2) 1.3482 + .style('stroke-dasharray','10,10') 1.3483 + .style('stroke',function (d,i) { 1.3484 + return lines.color()(d,d.seriesIndex); 1.3485 + }) 1.3486 + .attr('x1',0) 1.3487 + .attr('x2',availableWidth) 1.3488 + .attr('y1', getAvgLineY) 1.3489 + .attr('y2', getAvgLineY); 1.3490 + 1.3491 + avgLines 1.3492 + .style('stroke-opacity',function(d){ 1.3493 + //If average lines go offscreen, make them transparent 1.3494 + var yVal = y(average(d)); 1.3495 + if (yVal < 0 || yVal > availableHeight) return 0; 1.3496 + return 1; 1.3497 + }) 1.3498 + .attr('x1',0) 1.3499 + .attr('x2',availableWidth) 1.3500 + .attr('y1', getAvgLineY) 1.3501 + .attr('y2', getAvgLineY); 1.3502 + 1.3503 + avgLines.exit().remove(); 1.3504 + 1.3505 + //Create index line 1.3506 + var indexLine = linesWrap.selectAll('.nv-indexLine') 1.3507 + .data([index]); 1.3508 + indexLine.enter().append('rect').attr('class', 'nv-indexLine') 1.3509 + .attr('width', 3) 1.3510 + .attr('x', -2) 1.3511 + .attr('fill', 'red') 1.3512 + .attr('fill-opacity', .5) 1.3513 + .style("pointer-events","all") 1.3514 + .call(indexDrag); 1.3515 + 1.3516 + indexLine 1.3517 + .attr('transform', function(d) { return 'translate(' + dx(d.i) + ',0)' }) 1.3518 + .attr('height', availableHeight); 1.3519 + 1.3520 + // Setup Axes 1.3521 + if (showXAxis) { 1.3522 + xAxis 1.3523 + .scale(x) 1.3524 + ._ticks( nv.utils.calcTicksX(availableWidth/70, data) ) 1.3525 + .tickSize(-availableHeight, 0); 1.3526 + 1.3527 + g.select('.nv-x.nv-axis') 1.3528 + .attr('transform', 'translate(0,' + y.range()[0] + ')'); 1.3529 + g.select('.nv-x.nv-axis') 1.3530 + .call(xAxis); 1.3531 + } 1.3532 + 1.3533 + if (showYAxis) { 1.3534 + yAxis 1.3535 + .scale(y) 1.3536 + ._ticks( nv.utils.calcTicksY(availableHeight/36, data) ) 1.3537 + .tickSize( -availableWidth, 0); 1.3538 + 1.3539 + g.select('.nv-y.nv-axis') 1.3540 + .call(yAxis); 1.3541 + } 1.3542 + 1.3543 + //============================================================ 1.3544 + // Event Handling/Dispatching (in chart's scope) 1.3545 + //------------------------------------------------------------ 1.3546 + 1.3547 + function updateZero() { 1.3548 + indexLine 1.3549 + .data([index]); 1.3550 + 1.3551 + //When dragging the index line, turn off line transitions. 1.3552 + // Then turn them back on when done dragging. 1.3553 + var oldDuration = chart.duration(); 1.3554 + chart.duration(0); 1.3555 + chart.update(); 1.3556 + chart.duration(oldDuration); 1.3557 + } 1.3558 + 1.3559 + g.select('.nv-background rect') 1.3560 + .on('click', function() { 1.3561 + index.x = d3.mouse(this)[0]; 1.3562 + index.i = Math.round(dx.invert(index.x)); 1.3563 + 1.3564 + // update state and send stateChange with new index 1.3565 + state.index = index.i; 1.3566 + dispatch.stateChange(state); 1.3567 + 1.3568 + updateZero(); 1.3569 + }); 1.3570 + 1.3571 + lines.dispatch.on('elementClick', function(e) { 1.3572 + index.i = e.pointIndex; 1.3573 + index.x = dx(index.i); 1.3574 + 1.3575 + // update state and send stateChange with new index 1.3576 + state.index = index.i; 1.3577 + dispatch.stateChange(state); 1.3578 + 1.3579 + updateZero(); 1.3580 + }); 1.3581 + 1.3582 + controls.dispatch.on('legendClick', function(d,i) { 1.3583 + d.disabled = !d.disabled; 1.3584 + rescaleY = !d.disabled; 1.3585 + 1.3586 + state.rescaleY = rescaleY; 1.3587 + dispatch.stateChange(state); 1.3588 + chart.update(); 1.3589 + }); 1.3590 + 1.3591 + legend.dispatch.on('stateChange', function(newState) { 1.3592 + for (var key in newState) 1.3593 + state[key] = newState[key]; 1.3594 + dispatch.stateChange(state); 1.3595 + chart.update(); 1.3596 + }); 1.3597 + 1.3598 + interactiveLayer.dispatch.on('elementMousemove', function(e) { 1.3599 + lines.clearHighlights(); 1.3600 + var singlePoint, pointIndex, pointXLocation, allData = []; 1.3601 + 1.3602 + data 1.3603 + .filter(function(series, i) { 1.3604 + series.seriesIndex = i; 1.3605 + return !series.disabled; 1.3606 + }) 1.3607 + .forEach(function(series,i) { 1.3608 + pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x()); 1.3609 + lines.highlightPoint(i, pointIndex, true); 1.3610 + var point = series.values[pointIndex]; 1.3611 + if (typeof point === 'undefined') return; 1.3612 + if (typeof singlePoint === 'undefined') singlePoint = point; 1.3613 + if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex)); 1.3614 + allData.push({ 1.3615 + key: series.key, 1.3616 + value: chart.y()(point, pointIndex), 1.3617 + color: color(series,series.seriesIndex) 1.3618 + }); 1.3619 + }); 1.3620 + 1.3621 + //Highlight the tooltip entry based on which point the mouse is closest to. 1.3622 + if (allData.length > 2) { 1.3623 + var yValue = chart.yScale().invert(e.mouseY); 1.3624 + var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]); 1.3625 + var threshold = 0.03 * domainExtent; 1.3626 + var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value}),yValue,threshold); 1.3627 + if (indexToHighlight !== null) 1.3628 + allData[indexToHighlight].highlight = true; 1.3629 + } 1.3630 + 1.3631 + var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex), pointIndex); 1.3632 + interactiveLayer.tooltip 1.3633 + .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top}) 1.3634 + .chartContainer(that.parentNode) 1.3635 + .valueFormatter(function(d,i) { 1.3636 + return yAxis.tickFormat()(d); 1.3637 + }) 1.3638 + .data( 1.3639 + { 1.3640 + value: xValue, 1.3641 + series: allData 1.3642 + } 1.3643 + )(); 1.3644 + 1.3645 + interactiveLayer.renderGuideLine(pointXLocation); 1.3646 + }); 1.3647 + 1.3648 + interactiveLayer.dispatch.on("elementMouseout",function(e) { 1.3649 + lines.clearHighlights(); 1.3650 + }); 1.3651 + 1.3652 + // Update chart from a state object passed to event handler 1.3653 + dispatch.on('changeState', function(e) { 1.3654 + if (typeof e.disabled !== 'undefined') { 1.3655 + data.forEach(function(series,i) { 1.3656 + series.disabled = e.disabled[i]; 1.3657 + }); 1.3658 + 1.3659 + state.disabled = e.disabled; 1.3660 + } 1.3661 + 1.3662 + if (typeof e.index !== 'undefined') { 1.3663 + index.i = e.index; 1.3664 + index.x = dx(index.i); 1.3665 + 1.3666 + state.index = e.index; 1.3667 + 1.3668 + indexLine 1.3669 + .data([index]); 1.3670 + } 1.3671 + 1.3672 + if (typeof e.rescaleY !== 'undefined') { 1.3673 + rescaleY = e.rescaleY; 1.3674 + } 1.3675 + 1.3676 + chart.update(); 1.3677 + }); 1.3678 + 1.3679 + }); 1.3680 + 1.3681 + renderWatch.renderEnd('cumulativeLineChart immediate'); 1.3682 + 1.3683 + return chart; 1.3684 + } 1.3685 + 1.3686 + //============================================================ 1.3687 + // Event Handling/Dispatching (out of chart's scope) 1.3688 + //------------------------------------------------------------ 1.3689 + 1.3690 + lines.dispatch.on('elementMouseover.tooltip', function(evt) { 1.3691 + var point = { 1.3692 + x: chart.x()(evt.point), 1.3693 + y: chart.y()(evt.point), 1.3694 + color: evt.point.color 1.3695 + }; 1.3696 + evt.point = point; 1.3697 + tooltip.data(evt).position(evt.pos).hidden(false); 1.3698 + }); 1.3699 + 1.3700 + lines.dispatch.on('elementMouseout.tooltip', function(evt) { 1.3701 + tooltip.hidden(true) 1.3702 + }); 1.3703 + 1.3704 + //============================================================ 1.3705 + // Functions 1.3706 + //------------------------------------------------------------ 1.3707 + 1.3708 + var indexifyYGetter = null; 1.3709 + /* Normalize the data according to an index point. */ 1.3710 + function indexify(idx, data) { 1.3711 + if (!indexifyYGetter) indexifyYGetter = lines.y(); 1.3712 + return data.map(function(line, i) { 1.3713 + if (!line.values) { 1.3714 + return line; 1.3715 + } 1.3716 + var indexValue = line.values[idx]; 1.3717 + if (indexValue == null) { 1.3718 + return line; 1.3719 + } 1.3720 + var v = indexifyYGetter(indexValue, idx); 1.3721 + 1.3722 + //TODO: implement check below, and disable series if series loses 100% or more cause divide by 0 issue 1.3723 + if (v < -.95 && !noErrorCheck) { 1.3724 + //if a series loses more than 100%, calculations fail.. anything close can cause major distortion (but is mathematically correct till it hits 100) 1.3725 + 1.3726 + line.tempDisabled = true; 1.3727 + return line; 1.3728 + } 1.3729 + 1.3730 + line.tempDisabled = false; 1.3731 + 1.3732 + line.values = line.values.map(function(point, pointIndex) { 1.3733 + point.display = {'y': (indexifyYGetter(point, pointIndex) - v) / (1 + v) }; 1.3734 + return point; 1.3735 + }); 1.3736 + 1.3737 + return line; 1.3738 + }) 1.3739 + } 1.3740 + 1.3741 + //============================================================ 1.3742 + // Expose Public Variables 1.3743 + //------------------------------------------------------------ 1.3744 + 1.3745 + // expose chart's sub-components 1.3746 + chart.dispatch = dispatch; 1.3747 + chart.lines = lines; 1.3748 + chart.legend = legend; 1.3749 + chart.controls = controls; 1.3750 + chart.xAxis = xAxis; 1.3751 + chart.yAxis = yAxis; 1.3752 + chart.interactiveLayer = interactiveLayer; 1.3753 + chart.state = state; 1.3754 + chart.tooltip = tooltip; 1.3755 + 1.3756 + chart.options = nv.utils.optionsFunc.bind(chart); 1.3757 + 1.3758 + chart._options = Object.create({}, { 1.3759 + // simple options, just get/set the necessary values 1.3760 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.3761 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.3762 + rescaleY: {get: function(){return rescaleY;}, set: function(_){rescaleY=_;}}, 1.3763 + showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}}, 1.3764 + showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, 1.3765 + average: {get: function(){return average;}, set: function(_){average=_;}}, 1.3766 + defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}}, 1.3767 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.3768 + showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}}, 1.3769 + showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}}, 1.3770 + noErrorCheck: {get: function(){return noErrorCheck;}, set: function(_){noErrorCheck=_;}}, 1.3771 + 1.3772 + // deprecated options 1.3773 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.3774 + // deprecated after 1.7.1 1.3775 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.3776 + tooltip.enabled(!!_); 1.3777 + }}, 1.3778 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.3779 + // deprecated after 1.7.1 1.3780 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.3781 + tooltip.contentGenerator(_); 1.3782 + }}, 1.3783 + 1.3784 + // options that require extra logic in the setter 1.3785 + margin: {get: function(){return margin;}, set: function(_){ 1.3786 + margin.top = _.top !== undefined ? _.top : margin.top; 1.3787 + margin.right = _.right !== undefined ? _.right : margin.right; 1.3788 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.3789 + margin.left = _.left !== undefined ? _.left : margin.left; 1.3790 + }}, 1.3791 + color: {get: function(){return color;}, set: function(_){ 1.3792 + color = nv.utils.getColor(_); 1.3793 + legend.color(color); 1.3794 + }}, 1.3795 + useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){ 1.3796 + useInteractiveGuideline = _; 1.3797 + if (_ === true) { 1.3798 + chart.interactive(false); 1.3799 + chart.useVoronoi(false); 1.3800 + } 1.3801 + }}, 1.3802 + rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){ 1.3803 + rightAlignYAxis = _; 1.3804 + yAxis.orient( (_) ? 'right' : 'left'); 1.3805 + }}, 1.3806 + duration: {get: function(){return duration;}, set: function(_){ 1.3807 + duration = _; 1.3808 + lines.duration(duration); 1.3809 + xAxis.duration(duration); 1.3810 + yAxis.duration(duration); 1.3811 + renderWatch.reset(duration); 1.3812 + }} 1.3813 + }); 1.3814 + 1.3815 + nv.utils.inheritOptions(chart, lines); 1.3816 + nv.utils.initOptions(chart); 1.3817 + 1.3818 + return chart; 1.3819 +}; 1.3820 +//TODO: consider deprecating by adding necessary features to multiBar model 1.3821 +nv.models.discreteBar = function() { 1.3822 + "use strict"; 1.3823 + 1.3824 + //============================================================ 1.3825 + // Public Variables with Default Settings 1.3826 + //------------------------------------------------------------ 1.3827 + 1.3828 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.3829 + , width = 960 1.3830 + , height = 500 1.3831 + , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one 1.3832 + , container 1.3833 + , x = d3.scale.ordinal() 1.3834 + , y = d3.scale.linear() 1.3835 + , getX = function(d) { return d.x } 1.3836 + , getY = function(d) { return d.y } 1.3837 + , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove 1.3838 + , color = nv.utils.defaultColor() 1.3839 + , showValues = false 1.3840 + , valueFormat = d3.format(',.2f') 1.3841 + , xDomain 1.3842 + , yDomain 1.3843 + , xRange 1.3844 + , yRange 1.3845 + , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd') 1.3846 + , rectClass = 'discreteBar' 1.3847 + , duration = 250 1.3848 + ; 1.3849 + 1.3850 + //============================================================ 1.3851 + // Private Variables 1.3852 + //------------------------------------------------------------ 1.3853 + 1.3854 + var x0, y0; 1.3855 + var renderWatch = nv.utils.renderWatch(dispatch, duration); 1.3856 + 1.3857 + function chart(selection) { 1.3858 + renderWatch.reset(); 1.3859 + selection.each(function(data) { 1.3860 + var availableWidth = width - margin.left - margin.right, 1.3861 + availableHeight = height - margin.top - margin.bottom; 1.3862 + 1.3863 + container = d3.select(this); 1.3864 + nv.utils.initSVG(container); 1.3865 + 1.3866 + //add series index to each data point for reference 1.3867 + data.forEach(function(series, i) { 1.3868 + series.values.forEach(function(point) { 1.3869 + point.series = i; 1.3870 + }); 1.3871 + }); 1.3872 + 1.3873 + // Setup Scales 1.3874 + // remap and flatten the data for use in calculating the scales' domains 1.3875 + var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate 1.3876 + data.map(function(d) { 1.3877 + return d.values.map(function(d,i) { 1.3878 + return { x: getX(d,i), y: getY(d,i), y0: d.y0 } 1.3879 + }) 1.3880 + }); 1.3881 + 1.3882 + x .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x })) 1.3883 + .rangeBands(xRange || [0, availableWidth], .1); 1.3884 + y .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y }).concat(forceY))); 1.3885 + 1.3886 + // If showValues, pad the Y axis range to account for label height 1.3887 + if (showValues) y.range(yRange || [availableHeight - (y.domain()[0] < 0 ? 12 : 0), y.domain()[1] > 0 ? 12 : 0]); 1.3888 + else y.range(yRange || [availableHeight, 0]); 1.3889 + 1.3890 + //store old scales if they exist 1.3891 + x0 = x0 || x; 1.3892 + y0 = y0 || y.copy().range([y(0),y(0)]); 1.3893 + 1.3894 + // Setup containers and skeleton of chart 1.3895 + var wrap = container.selectAll('g.nv-wrap.nv-discretebar').data([data]); 1.3896 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-discretebar'); 1.3897 + var gEnter = wrapEnter.append('g'); 1.3898 + var g = wrap.select('g'); 1.3899 + 1.3900 + gEnter.append('g').attr('class', 'nv-groups'); 1.3901 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.3902 + 1.3903 + //TODO: by definition, the discrete bar should not have multiple groups, will modify/remove later 1.3904 + var groups = wrap.select('.nv-groups').selectAll('.nv-group') 1.3905 + .data(function(d) { return d }, function(d) { return d.key }); 1.3906 + groups.enter().append('g') 1.3907 + .style('stroke-opacity', 1e-6) 1.3908 + .style('fill-opacity', 1e-6); 1.3909 + groups.exit() 1.3910 + .watchTransition(renderWatch, 'discreteBar: exit groups') 1.3911 + .style('stroke-opacity', 1e-6) 1.3912 + .style('fill-opacity', 1e-6) 1.3913 + .remove(); 1.3914 + groups 1.3915 + .attr('class', function(d,i) { return 'nv-group nv-series-' + i }) 1.3916 + .classed('hover', function(d) { return d.hover }); 1.3917 + groups 1.3918 + .watchTransition(renderWatch, 'discreteBar: groups') 1.3919 + .style('stroke-opacity', 1) 1.3920 + .style('fill-opacity', .75); 1.3921 + 1.3922 + var bars = groups.selectAll('g.nv-bar') 1.3923 + .data(function(d) { return d.values }); 1.3924 + bars.exit().remove(); 1.3925 + 1.3926 + var barsEnter = bars.enter().append('g') 1.3927 + .attr('transform', function(d,i,j) { 1.3928 + return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05 ) + ', ' + y(0) + ')' 1.3929 + }) 1.3930 + .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here 1.3931 + d3.select(this).classed('hover', true); 1.3932 + dispatch.elementMouseover({ 1.3933 + data: d, 1.3934 + index: i, 1.3935 + color: d3.select(this).style("fill") 1.3936 + }); 1.3937 + }) 1.3938 + .on('mouseout', function(d,i) { 1.3939 + d3.select(this).classed('hover', false); 1.3940 + dispatch.elementMouseout({ 1.3941 + data: d, 1.3942 + index: i, 1.3943 + color: d3.select(this).style("fill") 1.3944 + }); 1.3945 + }) 1.3946 + .on('mousemove', function(d,i) { 1.3947 + dispatch.elementMousemove({ 1.3948 + data: d, 1.3949 + index: i, 1.3950 + color: d3.select(this).style("fill") 1.3951 + }); 1.3952 + }) 1.3953 + .on('click', function(d,i) { 1.3954 + dispatch.elementClick({ 1.3955 + data: d, 1.3956 + index: i, 1.3957 + color: d3.select(this).style("fill") 1.3958 + }); 1.3959 + d3.event.stopPropagation(); 1.3960 + }) 1.3961 + .on('dblclick', function(d,i) { 1.3962 + dispatch.elementDblClick({ 1.3963 + data: d, 1.3964 + index: i, 1.3965 + color: d3.select(this).style("fill") 1.3966 + }); 1.3967 + d3.event.stopPropagation(); 1.3968 + }); 1.3969 + 1.3970 + barsEnter.append('rect') 1.3971 + .attr('height', 0) 1.3972 + .attr('width', x.rangeBand() * .9 / data.length ) 1.3973 + 1.3974 + if (showValues) { 1.3975 + barsEnter.append('text') 1.3976 + .attr('text-anchor', 'middle') 1.3977 + ; 1.3978 + 1.3979 + bars.select('text') 1.3980 + .text(function(d,i) { return valueFormat(getY(d,i)) }) 1.3981 + .watchTransition(renderWatch, 'discreteBar: bars text') 1.3982 + .attr('x', x.rangeBand() * .9 / 2) 1.3983 + .attr('y', function(d,i) { return getY(d,i) < 0 ? y(getY(d,i)) - y(0) + 12 : -4 }) 1.3984 + 1.3985 + ; 1.3986 + } else { 1.3987 + bars.selectAll('text').remove(); 1.3988 + } 1.3989 + 1.3990 + bars 1.3991 + .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive' }) 1.3992 + .style('fill', function(d,i) { return d.color || color(d,i) }) 1.3993 + .style('stroke', function(d,i) { return d.color || color(d,i) }) 1.3994 + .select('rect') 1.3995 + .attr('class', rectClass) 1.3996 + .watchTransition(renderWatch, 'discreteBar: bars rect') 1.3997 + .attr('width', x.rangeBand() * .9 / data.length); 1.3998 + bars.watchTransition(renderWatch, 'discreteBar: bars') 1.3999 + //.delay(function(d,i) { return i * 1200 / data[0].values.length }) 1.4000 + .attr('transform', function(d,i) { 1.4001 + var left = x(getX(d,i)) + x.rangeBand() * .05, 1.4002 + top = getY(d,i) < 0 ? 1.4003 + y(0) : 1.4004 + y(0) - y(getY(d,i)) < 1 ? 1.4005 + y(0) - 1 : //make 1 px positive bars show up above y=0 1.4006 + y(getY(d,i)); 1.4007 + 1.4008 + return 'translate(' + left + ', ' + top + ')' 1.4009 + }) 1.4010 + .select('rect') 1.4011 + .attr('height', function(d,i) { 1.4012 + return Math.max(Math.abs(y(getY(d,i)) - y((yDomain && yDomain[0]) || 0)) || 1) 1.4013 + }); 1.4014 + 1.4015 + 1.4016 + //store old scales for use in transitions on update 1.4017 + x0 = x.copy(); 1.4018 + y0 = y.copy(); 1.4019 + 1.4020 + }); 1.4021 + 1.4022 + renderWatch.renderEnd('discreteBar immediate'); 1.4023 + return chart; 1.4024 + } 1.4025 + 1.4026 + //============================================================ 1.4027 + // Expose Public Variables 1.4028 + //------------------------------------------------------------ 1.4029 + 1.4030 + chart.dispatch = dispatch; 1.4031 + chart.options = nv.utils.optionsFunc.bind(chart); 1.4032 + 1.4033 + chart._options = Object.create({}, { 1.4034 + // simple options, just get/set the necessary values 1.4035 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.4036 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.4037 + forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}}, 1.4038 + showValues: {get: function(){return showValues;}, set: function(_){showValues=_;}}, 1.4039 + x: {get: function(){return getX;}, set: function(_){getX=_;}}, 1.4040 + y: {get: function(){return getY;}, set: function(_){getY=_;}}, 1.4041 + xScale: {get: function(){return x;}, set: function(_){x=_;}}, 1.4042 + yScale: {get: function(){return y;}, set: function(_){y=_;}}, 1.4043 + xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}}, 1.4044 + yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}}, 1.4045 + xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}}, 1.4046 + yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}}, 1.4047 + valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}}, 1.4048 + id: {get: function(){return id;}, set: function(_){id=_;}}, 1.4049 + rectClass: {get: function(){return rectClass;}, set: function(_){rectClass=_;}}, 1.4050 + 1.4051 + // options that require extra logic in the setter 1.4052 + margin: {get: function(){return margin;}, set: function(_){ 1.4053 + margin.top = _.top !== undefined ? _.top : margin.top; 1.4054 + margin.right = _.right !== undefined ? _.right : margin.right; 1.4055 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.4056 + margin.left = _.left !== undefined ? _.left : margin.left; 1.4057 + }}, 1.4058 + color: {get: function(){return color;}, set: function(_){ 1.4059 + color = nv.utils.getColor(_); 1.4060 + }}, 1.4061 + duration: {get: function(){return duration;}, set: function(_){ 1.4062 + duration = _; 1.4063 + renderWatch.reset(duration); 1.4064 + }} 1.4065 + }); 1.4066 + 1.4067 + nv.utils.initOptions(chart); 1.4068 + 1.4069 + return chart; 1.4070 +}; 1.4071 + 1.4072 +nv.models.discreteBarChart = function() { 1.4073 + "use strict"; 1.4074 + 1.4075 + //============================================================ 1.4076 + // Public Variables with Default Settings 1.4077 + //------------------------------------------------------------ 1.4078 + 1.4079 + var discretebar = nv.models.discreteBar() 1.4080 + , xAxis = nv.models.axis() 1.4081 + , yAxis = nv.models.axis() 1.4082 + , tooltip = nv.models.tooltip() 1.4083 + ; 1.4084 + 1.4085 + var margin = {top: 15, right: 10, bottom: 50, left: 60} 1.4086 + , width = null 1.4087 + , height = null 1.4088 + , color = nv.utils.getColor() 1.4089 + , showXAxis = true 1.4090 + , showYAxis = true 1.4091 + , rightAlignYAxis = false 1.4092 + , staggerLabels = false 1.4093 + , x 1.4094 + , y 1.4095 + , noData = null 1.4096 + , dispatch = d3.dispatch('beforeUpdate','renderEnd') 1.4097 + , duration = 250 1.4098 + ; 1.4099 + 1.4100 + xAxis 1.4101 + .orient('bottom') 1.4102 + .showMaxMin(false) 1.4103 + .tickFormat(function(d) { return d }) 1.4104 + ; 1.4105 + yAxis 1.4106 + .orient((rightAlignYAxis) ? 'right' : 'left') 1.4107 + .tickFormat(d3.format(',.1f')) 1.4108 + ; 1.4109 + 1.4110 + tooltip 1.4111 + .duration(0) 1.4112 + .headerEnabled(false) 1.4113 + .valueFormatter(function(d, i) { 1.4114 + return yAxis.tickFormat()(d, i); 1.4115 + }) 1.4116 + .keyFormatter(function(d, i) { 1.4117 + return xAxis.tickFormat()(d, i); 1.4118 + }); 1.4119 + 1.4120 + //============================================================ 1.4121 + // Private Variables 1.4122 + //------------------------------------------------------------ 1.4123 + 1.4124 + var renderWatch = nv.utils.renderWatch(dispatch, duration); 1.4125 + 1.4126 + function chart(selection) { 1.4127 + renderWatch.reset(); 1.4128 + renderWatch.models(discretebar); 1.4129 + if (showXAxis) renderWatch.models(xAxis); 1.4130 + if (showYAxis) renderWatch.models(yAxis); 1.4131 + 1.4132 + selection.each(function(data) { 1.4133 + var container = d3.select(this), 1.4134 + that = this; 1.4135 + nv.utils.initSVG(container); 1.4136 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.4137 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.4138 + 1.4139 + chart.update = function() { 1.4140 + dispatch.beforeUpdate(); 1.4141 + container.transition().duration(duration).call(chart); 1.4142 + }; 1.4143 + chart.container = this; 1.4144 + 1.4145 + // Display No Data message if there's nothing to show. 1.4146 + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { 1.4147 + nv.utils.noData(chart, container); 1.4148 + return chart; 1.4149 + } else { 1.4150 + container.selectAll('.nv-noData').remove(); 1.4151 + } 1.4152 + 1.4153 + // Setup Scales 1.4154 + x = discretebar.xScale(); 1.4155 + y = discretebar.yScale().clamp(true); 1.4156 + 1.4157 + // Setup containers and skeleton of chart 1.4158 + var wrap = container.selectAll('g.nv-wrap.nv-discreteBarWithAxes').data([data]); 1.4159 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-discreteBarWithAxes').append('g'); 1.4160 + var defsEnter = gEnter.append('defs'); 1.4161 + var g = wrap.select('g'); 1.4162 + 1.4163 + gEnter.append('g').attr('class', 'nv-x nv-axis'); 1.4164 + gEnter.append('g').attr('class', 'nv-y nv-axis') 1.4165 + .append('g').attr('class', 'nv-zeroLine') 1.4166 + .append('line'); 1.4167 + 1.4168 + gEnter.append('g').attr('class', 'nv-barsWrap'); 1.4169 + 1.4170 + g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.4171 + 1.4172 + if (rightAlignYAxis) { 1.4173 + g.select(".nv-y.nv-axis") 1.4174 + .attr("transform", "translate(" + availableWidth + ",0)"); 1.4175 + } 1.4176 + 1.4177 + // Main Chart Component(s) 1.4178 + discretebar 1.4179 + .width(availableWidth) 1.4180 + .height(availableHeight); 1.4181 + 1.4182 + var barsWrap = g.select('.nv-barsWrap') 1.4183 + .datum(data.filter(function(d) { return !d.disabled })); 1.4184 + 1.4185 + barsWrap.transition().call(discretebar); 1.4186 + 1.4187 + 1.4188 + defsEnter.append('clipPath') 1.4189 + .attr('id', 'nv-x-label-clip-' + discretebar.id()) 1.4190 + .append('rect'); 1.4191 + 1.4192 + g.select('#nv-x-label-clip-' + discretebar.id() + ' rect') 1.4193 + .attr('width', x.rangeBand() * (staggerLabels ? 2 : 1)) 1.4194 + .attr('height', 16) 1.4195 + .attr('x', -x.rangeBand() / (staggerLabels ? 1 : 2 )); 1.4196 + 1.4197 + // Setup Axes 1.4198 + if (showXAxis) { 1.4199 + xAxis 1.4200 + .scale(x) 1.4201 + ._ticks( nv.utils.calcTicksX(availableWidth/100, data) ) 1.4202 + .tickSize(-availableHeight, 0); 1.4203 + 1.4204 + g.select('.nv-x.nv-axis') 1.4205 + .attr('transform', 'translate(0,' + (y.range()[0] + ((discretebar.showValues() && y.domain()[0] < 0) ? 16 : 0)) + ')'); 1.4206 + g.select('.nv-x.nv-axis').call(xAxis); 1.4207 + 1.4208 + var xTicks = g.select('.nv-x.nv-axis').selectAll('g'); 1.4209 + if (staggerLabels) { 1.4210 + xTicks 1.4211 + .selectAll('text') 1.4212 + .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 == 0 ? '5' : '17') + ')' }) 1.4213 + } 1.4214 + } 1.4215 + 1.4216 + if (showYAxis) { 1.4217 + yAxis 1.4218 + .scale(y) 1.4219 + ._ticks( nv.utils.calcTicksY(availableHeight/36, data) ) 1.4220 + .tickSize( -availableWidth, 0); 1.4221 + 1.4222 + g.select('.nv-y.nv-axis').call(yAxis); 1.4223 + } 1.4224 + 1.4225 + // Zero line 1.4226 + g.select(".nv-zeroLine line") 1.4227 + .attr("x1",0) 1.4228 + .attr("x2",availableWidth) 1.4229 + .attr("y1", y(0)) 1.4230 + .attr("y2", y(0)) 1.4231 + ; 1.4232 + }); 1.4233 + 1.4234 + renderWatch.renderEnd('discreteBar chart immediate'); 1.4235 + return chart; 1.4236 + } 1.4237 + 1.4238 + //============================================================ 1.4239 + // Event Handling/Dispatching (out of chart's scope) 1.4240 + //------------------------------------------------------------ 1.4241 + 1.4242 + discretebar.dispatch.on('elementMouseover.tooltip', function(evt) { 1.4243 + evt['series'] = { 1.4244 + key: chart.x()(evt.data), 1.4245 + value: chart.y()(evt.data), 1.4246 + color: evt.color 1.4247 + }; 1.4248 + tooltip.data(evt).hidden(false); 1.4249 + }); 1.4250 + 1.4251 + discretebar.dispatch.on('elementMouseout.tooltip', function(evt) { 1.4252 + tooltip.hidden(true); 1.4253 + }); 1.4254 + 1.4255 + discretebar.dispatch.on('elementMousemove.tooltip', function(evt) { 1.4256 + tooltip.position({top: d3.event.pageY, left: d3.event.pageX})(); 1.4257 + }); 1.4258 + 1.4259 + //============================================================ 1.4260 + // Expose Public Variables 1.4261 + //------------------------------------------------------------ 1.4262 + 1.4263 + chart.dispatch = dispatch; 1.4264 + chart.discretebar = discretebar; 1.4265 + chart.xAxis = xAxis; 1.4266 + chart.yAxis = yAxis; 1.4267 + chart.tooltip = tooltip; 1.4268 + 1.4269 + chart.options = nv.utils.optionsFunc.bind(chart); 1.4270 + 1.4271 + chart._options = Object.create({}, { 1.4272 + // simple options, just get/set the necessary values 1.4273 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.4274 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.4275 + staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}}, 1.4276 + showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}}, 1.4277 + showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}}, 1.4278 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.4279 + 1.4280 + // deprecated options 1.4281 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.4282 + // deprecated after 1.7.1 1.4283 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.4284 + tooltip.enabled(!!_); 1.4285 + }}, 1.4286 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.4287 + // deprecated after 1.7.1 1.4288 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.4289 + tooltip.contentGenerator(_); 1.4290 + }}, 1.4291 + 1.4292 + // options that require extra logic in the setter 1.4293 + margin: {get: function(){return margin;}, set: function(_){ 1.4294 + margin.top = _.top !== undefined ? _.top : margin.top; 1.4295 + margin.right = _.right !== undefined ? _.right : margin.right; 1.4296 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.4297 + margin.left = _.left !== undefined ? _.left : margin.left; 1.4298 + }}, 1.4299 + duration: {get: function(){return duration;}, set: function(_){ 1.4300 + duration = _; 1.4301 + renderWatch.reset(duration); 1.4302 + discretebar.duration(duration); 1.4303 + xAxis.duration(duration); 1.4304 + yAxis.duration(duration); 1.4305 + }}, 1.4306 + color: {get: function(){return color;}, set: function(_){ 1.4307 + color = nv.utils.getColor(_); 1.4308 + discretebar.color(color); 1.4309 + }}, 1.4310 + rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){ 1.4311 + rightAlignYAxis = _; 1.4312 + yAxis.orient( (_) ? 'right' : 'left'); 1.4313 + }} 1.4314 + }); 1.4315 + 1.4316 + nv.utils.inheritOptions(chart, discretebar); 1.4317 + nv.utils.initOptions(chart); 1.4318 + 1.4319 + return chart; 1.4320 +} 1.4321 + 1.4322 +nv.models.distribution = function() { 1.4323 + "use strict"; 1.4324 + //============================================================ 1.4325 + // Public Variables with Default Settings 1.4326 + //------------------------------------------------------------ 1.4327 + 1.4328 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.4329 + , width = 400 //technically width or height depending on x or y.... 1.4330 + , size = 8 1.4331 + , axis = 'x' // 'x' or 'y'... horizontal or vertical 1.4332 + , getData = function(d) { return d[axis] } // defaults d.x or d.y 1.4333 + , color = nv.utils.defaultColor() 1.4334 + , scale = d3.scale.linear() 1.4335 + , domain 1.4336 + , duration = 250 1.4337 + , dispatch = d3.dispatch('renderEnd') 1.4338 + ; 1.4339 + 1.4340 + //============================================================ 1.4341 + 1.4342 + 1.4343 + //============================================================ 1.4344 + // Private Variables 1.4345 + //------------------------------------------------------------ 1.4346 + 1.4347 + var scale0; 1.4348 + var renderWatch = nv.utils.renderWatch(dispatch, duration); 1.4349 + 1.4350 + //============================================================ 1.4351 + 1.4352 + 1.4353 + function chart(selection) { 1.4354 + renderWatch.reset(); 1.4355 + selection.each(function(data) { 1.4356 + var availableLength = width - (axis === 'x' ? margin.left + margin.right : margin.top + margin.bottom), 1.4357 + naxis = axis == 'x' ? 'y' : 'x', 1.4358 + container = d3.select(this); 1.4359 + nv.utils.initSVG(container); 1.4360 + 1.4361 + //------------------------------------------------------------ 1.4362 + // Setup Scales 1.4363 + 1.4364 + scale0 = scale0 || scale; 1.4365 + 1.4366 + //------------------------------------------------------------ 1.4367 + 1.4368 + 1.4369 + //------------------------------------------------------------ 1.4370 + // Setup containers and skeleton of chart 1.4371 + 1.4372 + var wrap = container.selectAll('g.nv-distribution').data([data]); 1.4373 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-distribution'); 1.4374 + var gEnter = wrapEnter.append('g'); 1.4375 + var g = wrap.select('g'); 1.4376 + 1.4377 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') 1.4378 + 1.4379 + //------------------------------------------------------------ 1.4380 + 1.4381 + 1.4382 + var distWrap = g.selectAll('g.nv-dist') 1.4383 + .data(function(d) { return d }, function(d) { return d.key }); 1.4384 + 1.4385 + distWrap.enter().append('g'); 1.4386 + distWrap 1.4387 + .attr('class', function(d,i) { return 'nv-dist nv-series-' + i }) 1.4388 + .style('stroke', function(d,i) { return color(d, i) }); 1.4389 + 1.4390 + var dist = distWrap.selectAll('line.nv-dist' + axis) 1.4391 + .data(function(d) { return d.values }) 1.4392 + dist.enter().append('line') 1.4393 + .attr(axis + '1', function(d,i) { return scale0(getData(d,i)) }) 1.4394 + .attr(axis + '2', function(d,i) { return scale0(getData(d,i)) }) 1.4395 + renderWatch.transition(distWrap.exit().selectAll('line.nv-dist' + axis), 'dist exit') 1.4396 + // .transition() 1.4397 + .attr(axis + '1', function(d,i) { return scale(getData(d,i)) }) 1.4398 + .attr(axis + '2', function(d,i) { return scale(getData(d,i)) }) 1.4399 + .style('stroke-opacity', 0) 1.4400 + .remove(); 1.4401 + dist 1.4402 + .attr('class', function(d,i) { return 'nv-dist' + axis + ' nv-dist' + axis + '-' + i }) 1.4403 + .attr(naxis + '1', 0) 1.4404 + .attr(naxis + '2', size); 1.4405 + renderWatch.transition(dist, 'dist') 1.4406 + // .transition() 1.4407 + .attr(axis + '1', function(d,i) { return scale(getData(d,i)) }) 1.4408 + .attr(axis + '2', function(d,i) { return scale(getData(d,i)) }) 1.4409 + 1.4410 + 1.4411 + scale0 = scale.copy(); 1.4412 + 1.4413 + }); 1.4414 + renderWatch.renderEnd('distribution immediate'); 1.4415 + return chart; 1.4416 + } 1.4417 + 1.4418 + 1.4419 + //============================================================ 1.4420 + // Expose Public Variables 1.4421 + //------------------------------------------------------------ 1.4422 + chart.options = nv.utils.optionsFunc.bind(chart); 1.4423 + chart.dispatch = dispatch; 1.4424 + 1.4425 + chart.margin = function(_) { 1.4426 + if (!arguments.length) return margin; 1.4427 + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; 1.4428 + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; 1.4429 + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; 1.4430 + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; 1.4431 + return chart; 1.4432 + }; 1.4433 + 1.4434 + chart.width = function(_) { 1.4435 + if (!arguments.length) return width; 1.4436 + width = _; 1.4437 + return chart; 1.4438 + }; 1.4439 + 1.4440 + chart.axis = function(_) { 1.4441 + if (!arguments.length) return axis; 1.4442 + axis = _; 1.4443 + return chart; 1.4444 + }; 1.4445 + 1.4446 + chart.size = function(_) { 1.4447 + if (!arguments.length) return size; 1.4448 + size = _; 1.4449 + return chart; 1.4450 + }; 1.4451 + 1.4452 + chart.getData = function(_) { 1.4453 + if (!arguments.length) return getData; 1.4454 + getData = d3.functor(_); 1.4455 + return chart; 1.4456 + }; 1.4457 + 1.4458 + chart.scale = function(_) { 1.4459 + if (!arguments.length) return scale; 1.4460 + scale = _; 1.4461 + return chart; 1.4462 + }; 1.4463 + 1.4464 + chart.color = function(_) { 1.4465 + if (!arguments.length) return color; 1.4466 + color = nv.utils.getColor(_); 1.4467 + return chart; 1.4468 + }; 1.4469 + 1.4470 + chart.duration = function(_) { 1.4471 + if (!arguments.length) return duration; 1.4472 + duration = _; 1.4473 + renderWatch.reset(duration); 1.4474 + return chart; 1.4475 + }; 1.4476 + //============================================================ 1.4477 + 1.4478 + 1.4479 + return chart; 1.4480 +} 1.4481 +nv.models.furiousLegend = function() { 1.4482 + "use strict"; 1.4483 + 1.4484 + //============================================================ 1.4485 + // Public Variables with Default Settings 1.4486 + //------------------------------------------------------------ 1.4487 + 1.4488 + var margin = {top: 5, right: 0, bottom: 5, left: 0} 1.4489 + , width = 400 1.4490 + , height = 20 1.4491 + , getKey = function(d) { return d.key } 1.4492 + , color = nv.utils.getColor() 1.4493 + , align = true 1.4494 + , padding = 28 //define how much space between legend items. - recommend 32 for furious version 1.4495 + , rightAlign = true 1.4496 + , updateState = true //If true, legend will update data.disabled and trigger a 'stateChange' dispatch. 1.4497 + , radioButtonMode = false //If true, clicking legend items will cause it to behave like a radio button. (only one can be selected at a time) 1.4498 + , expanded = false 1.4499 + , dispatch = d3.dispatch('legendClick', 'legendDblclick', 'legendMouseover', 'legendMouseout', 'stateChange') 1.4500 + , vers = 'classic' //Options are "classic" and "furious" 1.4501 + ; 1.4502 + 1.4503 + function chart(selection) { 1.4504 + selection.each(function(data) { 1.4505 + var availableWidth = width - margin.left - margin.right, 1.4506 + container = d3.select(this); 1.4507 + nv.utils.initSVG(container); 1.4508 + 1.4509 + // Setup containers and skeleton of chart 1.4510 + var wrap = container.selectAll('g.nv-legend').data([data]); 1.4511 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-legend').append('g'); 1.4512 + var g = wrap.select('g'); 1.4513 + 1.4514 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.4515 + 1.4516 + var series = g.selectAll('.nv-series') 1.4517 + .data(function(d) { 1.4518 + if(vers != 'furious') return d; 1.4519 + 1.4520 + return d.filter(function(n) { 1.4521 + return expanded ? true : !n.disengaged; 1.4522 + }); 1.4523 + }); 1.4524 + var seriesEnter = series.enter().append('g').attr('class', 'nv-series') 1.4525 + 1.4526 + var seriesShape; 1.4527 + 1.4528 + if(vers == 'classic') { 1.4529 + seriesEnter.append('circle') 1.4530 + .style('stroke-width', 2) 1.4531 + .attr('class','nv-legend-symbol') 1.4532 + .attr('r', 5); 1.4533 + 1.4534 + seriesShape = series.select('circle'); 1.4535 + } else if (vers == 'furious') { 1.4536 + seriesEnter.append('rect') 1.4537 + .style('stroke-width', 2) 1.4538 + .attr('class','nv-legend-symbol') 1.4539 + .attr('rx', 3) 1.4540 + .attr('ry', 3); 1.4541 + 1.4542 + seriesShape = series.select('rect'); 1.4543 + 1.4544 + seriesEnter.append('g') 1.4545 + .attr('class', 'nv-check-box') 1.4546 + .property('innerHTML','<path d="M0.5,5 L22.5,5 L22.5,26.5 L0.5,26.5 L0.5,5 Z" class="nv-box"></path><path d="M5.5,12.8618467 L11.9185089,19.2803556 L31,0.198864511" class="nv-check"></path>') 1.4547 + .attr('transform', 'translate(-10,-8)scale(0.5)'); 1.4548 + 1.4549 + var seriesCheckbox = series.select('.nv-check-box'); 1.4550 + 1.4551 + seriesCheckbox.each(function(d,i) { 1.4552 + d3.select(this).selectAll('path') 1.4553 + .attr('stroke', setTextColor(d,i)); 1.4554 + }); 1.4555 + } 1.4556 + 1.4557 + seriesEnter.append('text') 1.4558 + .attr('text-anchor', 'start') 1.4559 + .attr('class','nv-legend-text') 1.4560 + .attr('dy', '.32em') 1.4561 + .attr('dx', '8'); 1.4562 + 1.4563 + var seriesText = series.select('text.nv-legend-text'); 1.4564 + 1.4565 + series 1.4566 + .on('mouseover', function(d,i) { 1.4567 + dispatch.legendMouseover(d,i); //TODO: Make consistent with other event objects 1.4568 + }) 1.4569 + .on('mouseout', function(d,i) { 1.4570 + dispatch.legendMouseout(d,i); 1.4571 + }) 1.4572 + .on('click', function(d,i) { 1.4573 + dispatch.legendClick(d,i); 1.4574 + // make sure we re-get data in case it was modified 1.4575 + var data = series.data(); 1.4576 + if (updateState) { 1.4577 + if(vers =='classic') { 1.4578 + if (radioButtonMode) { 1.4579 + //Radio button mode: set every series to disabled, 1.4580 + // and enable the clicked series. 1.4581 + data.forEach(function(series) { series.disabled = true}); 1.4582 + d.disabled = false; 1.4583 + } 1.4584 + else { 1.4585 + d.disabled = !d.disabled; 1.4586 + if (data.every(function(series) { return series.disabled})) { 1.4587 + //the default behavior of NVD3 legends is, if every single series 1.4588 + // is disabled, turn all series' back on. 1.4589 + data.forEach(function(series) { series.disabled = false}); 1.4590 + } 1.4591 + } 1.4592 + } else if(vers == 'furious') { 1.4593 + if(expanded) { 1.4594 + d.disengaged = !d.disengaged; 1.4595 + d.userDisabled = d.userDisabled == undefined ? !!d.disabled : d.userDisabled; 1.4596 + d.disabled = d.disengaged || d.userDisabled; 1.4597 + } else if (!expanded) { 1.4598 + d.disabled = !d.disabled; 1.4599 + d.userDisabled = d.disabled; 1.4600 + var engaged = data.filter(function(d) { return !d.disengaged; }); 1.4601 + if (engaged.every(function(series) { return series.userDisabled })) { 1.4602 + //the default behavior of NVD3 legends is, if every single series 1.4603 + // is disabled, turn all series' back on. 1.4604 + data.forEach(function(series) { 1.4605 + series.disabled = series.userDisabled = false; 1.4606 + }); 1.4607 + } 1.4608 + } 1.4609 + } 1.4610 + dispatch.stateChange({ 1.4611 + disabled: data.map(function(d) { return !!d.disabled }), 1.4612 + disengaged: data.map(function(d) { return !!d.disengaged }) 1.4613 + }); 1.4614 + 1.4615 + } 1.4616 + }) 1.4617 + .on('dblclick', function(d,i) { 1.4618 + if(vers == 'furious' && expanded) return; 1.4619 + dispatch.legendDblclick(d,i); 1.4620 + if (updateState) { 1.4621 + // make sure we re-get data in case it was modified 1.4622 + var data = series.data(); 1.4623 + //the default behavior of NVD3 legends, when double clicking one, 1.4624 + // is to set all other series' to false, and make the double clicked series enabled. 1.4625 + data.forEach(function(series) { 1.4626 + series.disabled = true; 1.4627 + if(vers == 'furious') series.userDisabled = series.disabled; 1.4628 + }); 1.4629 + d.disabled = false; 1.4630 + if(vers == 'furious') d.userDisabled = d.disabled; 1.4631 + dispatch.stateChange({ 1.4632 + disabled: data.map(function(d) { return !!d.disabled }) 1.4633 + }); 1.4634 + } 1.4635 + }); 1.4636 + 1.4637 + series.classed('nv-disabled', function(d) { return d.userDisabled }); 1.4638 + series.exit().remove(); 1.4639 + 1.4640 + seriesText 1.4641 + .attr('fill', setTextColor) 1.4642 + .text(getKey); 1.4643 + 1.4644 + //TODO: implement fixed-width and max-width options (max-width is especially useful with the align option) 1.4645 + // NEW ALIGNING CODE, TODO: clean up 1.4646 + 1.4647 + var versPadding; 1.4648 + switch(vers) { 1.4649 + case 'furious' : 1.4650 + versPadding = 23; 1.4651 + break; 1.4652 + case 'classic' : 1.4653 + versPadding = 20; 1.4654 + } 1.4655 + 1.4656 + if (align) { 1.4657 + 1.4658 + var seriesWidths = []; 1.4659 + series.each(function(d,i) { 1.4660 + var legendText = d3.select(this).select('text'); 1.4661 + var nodeTextLength; 1.4662 + try { 1.4663 + nodeTextLength = legendText.node().getComputedTextLength(); 1.4664 + // If the legendText is display:none'd (nodeTextLength == 0), simulate an error so we approximate, instead 1.4665 + if(nodeTextLength <= 0) throw Error(); 1.4666 + } 1.4667 + catch(e) { 1.4668 + nodeTextLength = nv.utils.calcApproxTextWidth(legendText); 1.4669 + } 1.4670 + 1.4671 + seriesWidths.push(nodeTextLength + padding); 1.4672 + }); 1.4673 + 1.4674 + var seriesPerRow = 0; 1.4675 + var legendWidth = 0; 1.4676 + var columnWidths = []; 1.4677 + 1.4678 + while ( legendWidth < availableWidth && seriesPerRow < seriesWidths.length) { 1.4679 + columnWidths[seriesPerRow] = seriesWidths[seriesPerRow]; 1.4680 + legendWidth += seriesWidths[seriesPerRow++]; 1.4681 + } 1.4682 + if (seriesPerRow === 0) seriesPerRow = 1; //minimum of one series per row 1.4683 + 1.4684 + while ( legendWidth > availableWidth && seriesPerRow > 1 ) { 1.4685 + columnWidths = []; 1.4686 + seriesPerRow--; 1.4687 + 1.4688 + for (var k = 0; k < seriesWidths.length; k++) { 1.4689 + if (seriesWidths[k] > (columnWidths[k % seriesPerRow] || 0) ) 1.4690 + columnWidths[k % seriesPerRow] = seriesWidths[k]; 1.4691 + } 1.4692 + 1.4693 + legendWidth = columnWidths.reduce(function(prev, cur, index, array) { 1.4694 + return prev + cur; 1.4695 + }); 1.4696 + } 1.4697 + 1.4698 + var xPositions = []; 1.4699 + for (var i = 0, curX = 0; i < seriesPerRow; i++) { 1.4700 + xPositions[i] = curX; 1.4701 + curX += columnWidths[i]; 1.4702 + } 1.4703 + 1.4704 + series 1.4705 + .attr('transform', function(d, i) { 1.4706 + return 'translate(' + xPositions[i % seriesPerRow] + ',' + (5 + Math.floor(i / seriesPerRow) * versPadding) + ')'; 1.4707 + }); 1.4708 + 1.4709 + //position legend as far right as possible within the total width 1.4710 + if (rightAlign) { 1.4711 + g.attr('transform', 'translate(' + (width - margin.right - legendWidth) + ',' + margin.top + ')'); 1.4712 + } 1.4713 + else { 1.4714 + g.attr('transform', 'translate(0' + ',' + margin.top + ')'); 1.4715 + } 1.4716 + 1.4717 + height = margin.top + margin.bottom + (Math.ceil(seriesWidths.length / seriesPerRow) * versPadding); 1.4718 + 1.4719 + } else { 1.4720 + 1.4721 + var ypos = 5, 1.4722 + newxpos = 5, 1.4723 + maxwidth = 0, 1.4724 + xpos; 1.4725 + series 1.4726 + .attr('transform', function(d, i) { 1.4727 + var length = d3.select(this).select('text').node().getComputedTextLength() + padding; 1.4728 + xpos = newxpos; 1.4729 + 1.4730 + if (width < margin.left + margin.right + xpos + length) { 1.4731 + newxpos = xpos = 5; 1.4732 + ypos += versPadding; 1.4733 + } 1.4734 + 1.4735 + newxpos += length; 1.4736 + if (newxpos > maxwidth) maxwidth = newxpos; 1.4737 + 1.4738 + return 'translate(' + xpos + ',' + ypos + ')'; 1.4739 + }); 1.4740 + 1.4741 + //position legend as far right as possible within the total width 1.4742 + g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')'); 1.4743 + 1.4744 + height = margin.top + margin.bottom + ypos + 15; 1.4745 + } 1.4746 + 1.4747 + if(vers == 'furious') { 1.4748 + // Size rectangles after text is placed 1.4749 + seriesShape 1.4750 + .attr('width', function(d,i) { 1.4751 + return seriesText[0][i].getComputedTextLength() + 27; 1.4752 + }) 1.4753 + .attr('height', 18) 1.4754 + .attr('y', -9) 1.4755 + .attr('x', -15) 1.4756 + } 1.4757 + 1.4758 + seriesShape 1.4759 + .style('fill', setBGColor) 1.4760 + .style('stroke', function(d,i) { return d.color || color(d, i) }); 1.4761 + }); 1.4762 + 1.4763 + function setTextColor(d,i) { 1.4764 + if(vers != 'furious') return '#000'; 1.4765 + if(expanded) { 1.4766 + return d.disengaged ? color(d,i) : '#fff'; 1.4767 + } else if (!expanded) { 1.4768 + return !!d.disabled ? color(d,i) : '#fff'; 1.4769 + } 1.4770 + } 1.4771 + 1.4772 + function setBGColor(d,i) { 1.4773 + if(expanded && vers == 'furious') { 1.4774 + return d.disengaged ? '#fff' : color(d,i); 1.4775 + } else { 1.4776 + return !!d.disabled ? '#fff' : color(d,i); 1.4777 + } 1.4778 + } 1.4779 + 1.4780 + return chart; 1.4781 + } 1.4782 + 1.4783 + //============================================================ 1.4784 + // Expose Public Variables 1.4785 + //------------------------------------------------------------ 1.4786 + 1.4787 + chart.dispatch = dispatch; 1.4788 + chart.options = nv.utils.optionsFunc.bind(chart); 1.4789 + 1.4790 + chart._options = Object.create({}, { 1.4791 + // simple options, just get/set the necessary values 1.4792 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.4793 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.4794 + key: {get: function(){return getKey;}, set: function(_){getKey=_;}}, 1.4795 + align: {get: function(){return align;}, set: function(_){align=_;}}, 1.4796 + rightAlign: {get: function(){return rightAlign;}, set: function(_){rightAlign=_;}}, 1.4797 + padding: {get: function(){return padding;}, set: function(_){padding=_;}}, 1.4798 + updateState: {get: function(){return updateState;}, set: function(_){updateState=_;}}, 1.4799 + radioButtonMode: {get: function(){return radioButtonMode;}, set: function(_){radioButtonMode=_;}}, 1.4800 + expanded: {get: function(){return expanded;}, set: function(_){expanded=_;}}, 1.4801 + vers: {get: function(){return vers;}, set: function(_){vers=_;}}, 1.4802 + 1.4803 + // options that require extra logic in the setter 1.4804 + margin: {get: function(){return margin;}, set: function(_){ 1.4805 + margin.top = _.top !== undefined ? _.top : margin.top; 1.4806 + margin.right = _.right !== undefined ? _.right : margin.right; 1.4807 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.4808 + margin.left = _.left !== undefined ? _.left : margin.left; 1.4809 + }}, 1.4810 + color: {get: function(){return color;}, set: function(_){ 1.4811 + color = nv.utils.getColor(_); 1.4812 + }} 1.4813 + }); 1.4814 + 1.4815 + nv.utils.initOptions(chart); 1.4816 + 1.4817 + return chart; 1.4818 +}; 1.4819 +//TODO: consider deprecating and using multibar with single series for this 1.4820 +nv.models.historicalBar = function() { 1.4821 + "use strict"; 1.4822 + 1.4823 + //============================================================ 1.4824 + // Public Variables with Default Settings 1.4825 + //------------------------------------------------------------ 1.4826 + 1.4827 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.4828 + , width = null 1.4829 + , height = null 1.4830 + , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one 1.4831 + , container = null 1.4832 + , x = d3.scale.linear() 1.4833 + , y = d3.scale.linear() 1.4834 + , getX = function(d) { return d.x } 1.4835 + , getY = function(d) { return d.y } 1.4836 + , forceX = [] 1.4837 + , forceY = [0] 1.4838 + , padData = false 1.4839 + , clipEdge = true 1.4840 + , color = nv.utils.defaultColor() 1.4841 + , xDomain 1.4842 + , yDomain 1.4843 + , xRange 1.4844 + , yRange 1.4845 + , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd') 1.4846 + , interactive = true 1.4847 + ; 1.4848 + 1.4849 + var renderWatch = nv.utils.renderWatch(dispatch, 0); 1.4850 + 1.4851 + function chart(selection) { 1.4852 + selection.each(function(data) { 1.4853 + renderWatch.reset(); 1.4854 + 1.4855 + container = d3.select(this); 1.4856 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.4857 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.4858 + 1.4859 + nv.utils.initSVG(container); 1.4860 + 1.4861 + // Setup Scales 1.4862 + x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) )); 1.4863 + 1.4864 + if (padData) 1.4865 + x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]); 1.4866 + else 1.4867 + x.range(xRange || [0, availableWidth]); 1.4868 + 1.4869 + y.domain(yDomain || d3.extent(data[0].values.map(getY).concat(forceY) )) 1.4870 + .range(yRange || [availableHeight, 0]); 1.4871 + 1.4872 + // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point 1.4873 + if (x.domain()[0] === x.domain()[1]) 1.4874 + x.domain()[0] ? 1.4875 + x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01]) 1.4876 + : x.domain([-1,1]); 1.4877 + 1.4878 + if (y.domain()[0] === y.domain()[1]) 1.4879 + y.domain()[0] ? 1.4880 + y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01]) 1.4881 + : y.domain([-1,1]); 1.4882 + 1.4883 + // Setup containers and skeleton of chart 1.4884 + var wrap = container.selectAll('g.nv-wrap.nv-historicalBar-' + id).data([data[0].values]); 1.4885 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBar-' + id); 1.4886 + var defsEnter = wrapEnter.append('defs'); 1.4887 + var gEnter = wrapEnter.append('g'); 1.4888 + var g = wrap.select('g'); 1.4889 + 1.4890 + gEnter.append('g').attr('class', 'nv-bars'); 1.4891 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.4892 + 1.4893 + container 1.4894 + .on('click', function(d,i) { 1.4895 + dispatch.chartClick({ 1.4896 + data: d, 1.4897 + index: i, 1.4898 + pos: d3.event, 1.4899 + id: id 1.4900 + }); 1.4901 + }); 1.4902 + 1.4903 + defsEnter.append('clipPath') 1.4904 + .attr('id', 'nv-chart-clip-path-' + id) 1.4905 + .append('rect'); 1.4906 + 1.4907 + wrap.select('#nv-chart-clip-path-' + id + ' rect') 1.4908 + .attr('width', availableWidth) 1.4909 + .attr('height', availableHeight); 1.4910 + 1.4911 + g.attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : ''); 1.4912 + 1.4913 + var bars = wrap.select('.nv-bars').selectAll('.nv-bar') 1.4914 + .data(function(d) { return d }, function(d,i) {return getX(d,i)}); 1.4915 + bars.exit().remove(); 1.4916 + 1.4917 + bars.enter().append('rect') 1.4918 + .attr('x', 0 ) 1.4919 + .attr('y', function(d,i) { return nv.utils.NaNtoZero(y(Math.max(0, getY(d,i)))) }) 1.4920 + .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.abs(y(getY(d,i)) - y(0))) }) 1.4921 + .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; }) 1.4922 + .on('mouseover', function(d,i) { 1.4923 + if (!interactive) return; 1.4924 + d3.select(this).classed('hover', true); 1.4925 + dispatch.elementMouseover({ 1.4926 + data: d, 1.4927 + index: i, 1.4928 + color: d3.select(this).style("fill") 1.4929 + }); 1.4930 + 1.4931 + }) 1.4932 + .on('mouseout', function(d,i) { 1.4933 + if (!interactive) return; 1.4934 + d3.select(this).classed('hover', false); 1.4935 + dispatch.elementMouseout({ 1.4936 + data: d, 1.4937 + index: i, 1.4938 + color: d3.select(this).style("fill") 1.4939 + }); 1.4940 + }) 1.4941 + .on('mousemove', function(d,i) { 1.4942 + if (!interactive) return; 1.4943 + dispatch.elementMousemove({ 1.4944 + data: d, 1.4945 + index: i, 1.4946 + color: d3.select(this).style("fill") 1.4947 + }); 1.4948 + }) 1.4949 + .on('click', function(d,i) { 1.4950 + if (!interactive) return; 1.4951 + dispatch.elementClick({ 1.4952 + data: d, 1.4953 + index: i, 1.4954 + color: d3.select(this).style("fill") 1.4955 + }); 1.4956 + d3.event.stopPropagation(); 1.4957 + }) 1.4958 + .on('dblclick', function(d,i) { 1.4959 + if (!interactive) return; 1.4960 + dispatch.elementDblClick({ 1.4961 + data: d, 1.4962 + index: i, 1.4963 + color: d3.select(this).style("fill") 1.4964 + }); 1.4965 + d3.event.stopPropagation(); 1.4966 + }); 1.4967 + 1.4968 + bars 1.4969 + .attr('fill', function(d,i) { return color(d, i); }) 1.4970 + .attr('class', function(d,i,j) { return (getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive') + ' nv-bar-' + j + '-' + i }) 1.4971 + .watchTransition(renderWatch, 'bars') 1.4972 + .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; }) 1.4973 + //TODO: better width calculations that don't assume always uniform data spacing;w 1.4974 + .attr('width', (availableWidth / data[0].values.length) * .9 ); 1.4975 + 1.4976 + bars.watchTransition(renderWatch, 'bars') 1.4977 + .attr('y', function(d,i) { 1.4978 + var rval = getY(d,i) < 0 ? 1.4979 + y(0) : 1.4980 + y(0) - y(getY(d,i)) < 1 ? 1.4981 + y(0) - 1 : 1.4982 + y(getY(d,i)); 1.4983 + return nv.utils.NaNtoZero(rval); 1.4984 + }) 1.4985 + .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.max(Math.abs(y(getY(d,i)) - y(0)),1)) }); 1.4986 + 1.4987 + }); 1.4988 + 1.4989 + renderWatch.renderEnd('historicalBar immediate'); 1.4990 + return chart; 1.4991 + } 1.4992 + 1.4993 + //Create methods to allow outside functions to highlight a specific bar. 1.4994 + chart.highlightPoint = function(pointIndex, isHoverOver) { 1.4995 + container 1.4996 + .select(".nv-bars .nv-bar-0-" + pointIndex) 1.4997 + .classed("hover", isHoverOver) 1.4998 + ; 1.4999 + }; 1.5000 + 1.5001 + chart.clearHighlights = function() { 1.5002 + container 1.5003 + .select(".nv-bars .nv-bar.hover") 1.5004 + .classed("hover", false) 1.5005 + ; 1.5006 + }; 1.5007 + 1.5008 + //============================================================ 1.5009 + // Expose Public Variables 1.5010 + //------------------------------------------------------------ 1.5011 + 1.5012 + chart.dispatch = dispatch; 1.5013 + chart.options = nv.utils.optionsFunc.bind(chart); 1.5014 + 1.5015 + chart._options = Object.create({}, { 1.5016 + // simple options, just get/set the necessary values 1.5017 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.5018 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.5019 + forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}}, 1.5020 + forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}}, 1.5021 + padData: {get: function(){return padData;}, set: function(_){padData=_;}}, 1.5022 + x: {get: function(){return getX;}, set: function(_){getX=_;}}, 1.5023 + y: {get: function(){return getY;}, set: function(_){getY=_;}}, 1.5024 + xScale: {get: function(){return x;}, set: function(_){x=_;}}, 1.5025 + yScale: {get: function(){return y;}, set: function(_){y=_;}}, 1.5026 + xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}}, 1.5027 + yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}}, 1.5028 + xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}}, 1.5029 + yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}}, 1.5030 + clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}}, 1.5031 + id: {get: function(){return id;}, set: function(_){id=_;}}, 1.5032 + interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}}, 1.5033 + 1.5034 + // options that require extra logic in the setter 1.5035 + margin: {get: function(){return margin;}, set: function(_){ 1.5036 + margin.top = _.top !== undefined ? _.top : margin.top; 1.5037 + margin.right = _.right !== undefined ? _.right : margin.right; 1.5038 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.5039 + margin.left = _.left !== undefined ? _.left : margin.left; 1.5040 + }}, 1.5041 + color: {get: function(){return color;}, set: function(_){ 1.5042 + color = nv.utils.getColor(_); 1.5043 + }} 1.5044 + }); 1.5045 + 1.5046 + nv.utils.initOptions(chart); 1.5047 + 1.5048 + return chart; 1.5049 +}; 1.5050 + 1.5051 +nv.models.historicalBarChart = function(bar_model) { 1.5052 + "use strict"; 1.5053 + 1.5054 + //============================================================ 1.5055 + // Public Variables with Default Settings 1.5056 + //------------------------------------------------------------ 1.5057 + 1.5058 + var bars = bar_model || nv.models.historicalBar() 1.5059 + , xAxis = nv.models.axis() 1.5060 + , yAxis = nv.models.axis() 1.5061 + , legend = nv.models.legend() 1.5062 + , interactiveLayer = nv.interactiveGuideline() 1.5063 + , tooltip = nv.models.tooltip() 1.5064 + ; 1.5065 + 1.5066 + 1.5067 + var margin = {top: 30, right: 90, bottom: 50, left: 90} 1.5068 + , color = nv.utils.defaultColor() 1.5069 + , width = null 1.5070 + , height = null 1.5071 + , showLegend = false 1.5072 + , showXAxis = true 1.5073 + , showYAxis = true 1.5074 + , rightAlignYAxis = false 1.5075 + , useInteractiveGuideline = false 1.5076 + , x 1.5077 + , y 1.5078 + , state = {} 1.5079 + , defaultState = null 1.5080 + , noData = null 1.5081 + , dispatch = d3.dispatch('tooltipHide', 'stateChange', 'changeState', 'renderEnd') 1.5082 + , transitionDuration = 250 1.5083 + ; 1.5084 + 1.5085 + xAxis.orient('bottom').tickPadding(7); 1.5086 + yAxis.orient( (rightAlignYAxis) ? 'right' : 'left'); 1.5087 + tooltip 1.5088 + .duration(0) 1.5089 + .headerEnabled(false) 1.5090 + .valueFormatter(function(d, i) { 1.5091 + return yAxis.tickFormat()(d, i); 1.5092 + }) 1.5093 + .headerFormatter(function(d, i) { 1.5094 + return xAxis.tickFormat()(d, i); 1.5095 + }); 1.5096 + 1.5097 + 1.5098 + //============================================================ 1.5099 + // Private Variables 1.5100 + //------------------------------------------------------------ 1.5101 + 1.5102 + var renderWatch = nv.utils.renderWatch(dispatch, 0); 1.5103 + 1.5104 + function chart(selection) { 1.5105 + selection.each(function(data) { 1.5106 + renderWatch.reset(); 1.5107 + renderWatch.models(bars); 1.5108 + if (showXAxis) renderWatch.models(xAxis); 1.5109 + if (showYAxis) renderWatch.models(yAxis); 1.5110 + 1.5111 + var container = d3.select(this), 1.5112 + that = this; 1.5113 + nv.utils.initSVG(container); 1.5114 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.5115 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.5116 + 1.5117 + chart.update = function() { container.transition().duration(transitionDuration).call(chart) }; 1.5118 + chart.container = this; 1.5119 + 1.5120 + //set state.disabled 1.5121 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.5122 + 1.5123 + if (!defaultState) { 1.5124 + var key; 1.5125 + defaultState = {}; 1.5126 + for (key in state) { 1.5127 + if (state[key] instanceof Array) 1.5128 + defaultState[key] = state[key].slice(0); 1.5129 + else 1.5130 + defaultState[key] = state[key]; 1.5131 + } 1.5132 + } 1.5133 + 1.5134 + // Display noData message if there's nothing to show. 1.5135 + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { 1.5136 + nv.utils.noData(chart, container) 1.5137 + return chart; 1.5138 + } else { 1.5139 + container.selectAll('.nv-noData').remove(); 1.5140 + } 1.5141 + 1.5142 + // Setup Scales 1.5143 + x = bars.xScale(); 1.5144 + y = bars.yScale(); 1.5145 + 1.5146 + // Setup containers and skeleton of chart 1.5147 + var wrap = container.selectAll('g.nv-wrap.nv-historicalBarChart').data([data]); 1.5148 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBarChart').append('g'); 1.5149 + var g = wrap.select('g'); 1.5150 + 1.5151 + gEnter.append('g').attr('class', 'nv-x nv-axis'); 1.5152 + gEnter.append('g').attr('class', 'nv-y nv-axis'); 1.5153 + gEnter.append('g').attr('class', 'nv-barsWrap'); 1.5154 + gEnter.append('g').attr('class', 'nv-legendWrap'); 1.5155 + gEnter.append('g').attr('class', 'nv-interactive'); 1.5156 + 1.5157 + // Legend 1.5158 + if (showLegend) { 1.5159 + legend.width(availableWidth); 1.5160 + 1.5161 + g.select('.nv-legendWrap') 1.5162 + .datum(data) 1.5163 + .call(legend); 1.5164 + 1.5165 + if ( margin.top != legend.height()) { 1.5166 + margin.top = legend.height(); 1.5167 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.5168 + } 1.5169 + 1.5170 + wrap.select('.nv-legendWrap') 1.5171 + .attr('transform', 'translate(0,' + (-margin.top) +')') 1.5172 + } 1.5173 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.5174 + 1.5175 + if (rightAlignYAxis) { 1.5176 + g.select(".nv-y.nv-axis") 1.5177 + .attr("transform", "translate(" + availableWidth + ",0)"); 1.5178 + } 1.5179 + 1.5180 + //Set up interactive layer 1.5181 + if (useInteractiveGuideline) { 1.5182 + interactiveLayer 1.5183 + .width(availableWidth) 1.5184 + .height(availableHeight) 1.5185 + .margin({left:margin.left, top:margin.top}) 1.5186 + .svgContainer(container) 1.5187 + .xScale(x); 1.5188 + wrap.select(".nv-interactive").call(interactiveLayer); 1.5189 + } 1.5190 + bars 1.5191 + .width(availableWidth) 1.5192 + .height(availableHeight) 1.5193 + .color(data.map(function(d,i) { 1.5194 + return d.color || color(d, i); 1.5195 + }).filter(function(d,i) { return !data[i].disabled })); 1.5196 + 1.5197 + var barsWrap = g.select('.nv-barsWrap') 1.5198 + .datum(data.filter(function(d) { return !d.disabled })); 1.5199 + barsWrap.transition().call(bars); 1.5200 + 1.5201 + // Setup Axes 1.5202 + if (showXAxis) { 1.5203 + xAxis 1.5204 + .scale(x) 1.5205 + ._ticks( nv.utils.calcTicksX(availableWidth/100, data) ) 1.5206 + .tickSize(-availableHeight, 0); 1.5207 + 1.5208 + g.select('.nv-x.nv-axis') 1.5209 + .attr('transform', 'translate(0,' + y.range()[0] + ')'); 1.5210 + g.select('.nv-x.nv-axis') 1.5211 + .transition() 1.5212 + .call(xAxis); 1.5213 + } 1.5214 + 1.5215 + if (showYAxis) { 1.5216 + yAxis 1.5217 + .scale(y) 1.5218 + ._ticks( nv.utils.calcTicksY(availableHeight/36, data) ) 1.5219 + .tickSize( -availableWidth, 0); 1.5220 + 1.5221 + g.select('.nv-y.nv-axis') 1.5222 + .transition() 1.5223 + .call(yAxis); 1.5224 + } 1.5225 + 1.5226 + //============================================================ 1.5227 + // Event Handling/Dispatching (in chart's scope) 1.5228 + //------------------------------------------------------------ 1.5229 + 1.5230 + interactiveLayer.dispatch.on('elementMousemove', function(e) { 1.5231 + bars.clearHighlights(); 1.5232 + 1.5233 + var singlePoint, pointIndex, pointXLocation, allData = []; 1.5234 + data 1.5235 + .filter(function(series, i) { 1.5236 + series.seriesIndex = i; 1.5237 + return !series.disabled; 1.5238 + }) 1.5239 + .forEach(function(series,i) { 1.5240 + pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x()); 1.5241 + bars.highlightPoint(pointIndex,true); 1.5242 + var point = series.values[pointIndex]; 1.5243 + if (point === undefined) return; 1.5244 + if (singlePoint === undefined) singlePoint = point; 1.5245 + if (pointXLocation === undefined) pointXLocation = chart.xScale()(chart.x()(point,pointIndex)); 1.5246 + allData.push({ 1.5247 + key: series.key, 1.5248 + value: chart.y()(point, pointIndex), 1.5249 + color: color(series,series.seriesIndex), 1.5250 + data: series.values[pointIndex] 1.5251 + }); 1.5252 + }); 1.5253 + 1.5254 + var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex)); 1.5255 + interactiveLayer.tooltip 1.5256 + .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top}) 1.5257 + .chartContainer(that.parentNode) 1.5258 + .valueFormatter(function(d,i) { 1.5259 + return yAxis.tickFormat()(d); 1.5260 + }) 1.5261 + .data({ 1.5262 + value: xValue, 1.5263 + index: pointIndex, 1.5264 + series: allData 1.5265 + })(); 1.5266 + 1.5267 + interactiveLayer.renderGuideLine(pointXLocation); 1.5268 + 1.5269 + }); 1.5270 + 1.5271 + interactiveLayer.dispatch.on("elementMouseout",function(e) { 1.5272 + dispatch.tooltipHide(); 1.5273 + bars.clearHighlights(); 1.5274 + }); 1.5275 + 1.5276 + legend.dispatch.on('legendClick', function(d,i) { 1.5277 + d.disabled = !d.disabled; 1.5278 + 1.5279 + if (!data.filter(function(d) { return !d.disabled }).length) { 1.5280 + data.map(function(d) { 1.5281 + d.disabled = false; 1.5282 + wrap.selectAll('.nv-series').classed('disabled', false); 1.5283 + return d; 1.5284 + }); 1.5285 + } 1.5286 + 1.5287 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.5288 + dispatch.stateChange(state); 1.5289 + 1.5290 + selection.transition().call(chart); 1.5291 + }); 1.5292 + 1.5293 + legend.dispatch.on('legendDblclick', function(d) { 1.5294 + //Double clicking should always enable current series, and disabled all others. 1.5295 + data.forEach(function(d) { 1.5296 + d.disabled = true; 1.5297 + }); 1.5298 + d.disabled = false; 1.5299 + 1.5300 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.5301 + dispatch.stateChange(state); 1.5302 + chart.update(); 1.5303 + }); 1.5304 + 1.5305 + dispatch.on('changeState', function(e) { 1.5306 + if (typeof e.disabled !== 'undefined') { 1.5307 + data.forEach(function(series,i) { 1.5308 + series.disabled = e.disabled[i]; 1.5309 + }); 1.5310 + 1.5311 + state.disabled = e.disabled; 1.5312 + } 1.5313 + 1.5314 + chart.update(); 1.5315 + }); 1.5316 + }); 1.5317 + 1.5318 + renderWatch.renderEnd('historicalBarChart immediate'); 1.5319 + return chart; 1.5320 + } 1.5321 + 1.5322 + //============================================================ 1.5323 + // Event Handling/Dispatching (out of chart's scope) 1.5324 + //------------------------------------------------------------ 1.5325 + 1.5326 + bars.dispatch.on('elementMouseover.tooltip', function(evt) { 1.5327 + evt['series'] = { 1.5328 + key: chart.x()(evt.data), 1.5329 + value: chart.y()(evt.data), 1.5330 + color: evt.color 1.5331 + }; 1.5332 + tooltip.data(evt).hidden(false); 1.5333 + }); 1.5334 + 1.5335 + bars.dispatch.on('elementMouseout.tooltip', function(evt) { 1.5336 + tooltip.hidden(true); 1.5337 + }); 1.5338 + 1.5339 + bars.dispatch.on('elementMousemove.tooltip', function(evt) { 1.5340 + tooltip.position({top: d3.event.pageY, left: d3.event.pageX})(); 1.5341 + }); 1.5342 + 1.5343 + //============================================================ 1.5344 + // Expose Public Variables 1.5345 + //------------------------------------------------------------ 1.5346 + 1.5347 + // expose chart's sub-components 1.5348 + chart.dispatch = dispatch; 1.5349 + chart.bars = bars; 1.5350 + chart.legend = legend; 1.5351 + chart.xAxis = xAxis; 1.5352 + chart.yAxis = yAxis; 1.5353 + chart.interactiveLayer = interactiveLayer; 1.5354 + chart.tooltip = tooltip; 1.5355 + 1.5356 + chart.options = nv.utils.optionsFunc.bind(chart); 1.5357 + 1.5358 + chart._options = Object.create({}, { 1.5359 + // simple options, just get/set the necessary values 1.5360 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.5361 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.5362 + showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, 1.5363 + showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}}, 1.5364 + showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}}, 1.5365 + defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}}, 1.5366 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.5367 + 1.5368 + // deprecated options 1.5369 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.5370 + // deprecated after 1.7.1 1.5371 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.5372 + tooltip.enabled(!!_); 1.5373 + }}, 1.5374 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.5375 + // deprecated after 1.7.1 1.5376 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.5377 + tooltip.contentGenerator(_); 1.5378 + }}, 1.5379 + 1.5380 + // options that require extra logic in the setter 1.5381 + margin: {get: function(){return margin;}, set: function(_){ 1.5382 + margin.top = _.top !== undefined ? _.top : margin.top; 1.5383 + margin.right = _.right !== undefined ? _.right : margin.right; 1.5384 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.5385 + margin.left = _.left !== undefined ? _.left : margin.left; 1.5386 + }}, 1.5387 + color: {get: function(){return color;}, set: function(_){ 1.5388 + color = nv.utils.getColor(_); 1.5389 + legend.color(color); 1.5390 + bars.color(color); 1.5391 + }}, 1.5392 + duration: {get: function(){return transitionDuration;}, set: function(_){ 1.5393 + transitionDuration=_; 1.5394 + renderWatch.reset(transitionDuration); 1.5395 + yAxis.duration(transitionDuration); 1.5396 + xAxis.duration(transitionDuration); 1.5397 + }}, 1.5398 + rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){ 1.5399 + rightAlignYAxis = _; 1.5400 + yAxis.orient( (_) ? 'right' : 'left'); 1.5401 + }}, 1.5402 + useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){ 1.5403 + useInteractiveGuideline = _; 1.5404 + if (_ === true) { 1.5405 + chart.interactive(false); 1.5406 + } 1.5407 + }} 1.5408 + }); 1.5409 + 1.5410 + nv.utils.inheritOptions(chart, bars); 1.5411 + nv.utils.initOptions(chart); 1.5412 + 1.5413 + return chart; 1.5414 +}; 1.5415 + 1.5416 + 1.5417 +// ohlcChart is just a historical chart with ohlc bars and some tweaks 1.5418 +nv.models.ohlcBarChart = function() { 1.5419 + var chart = nv.models.historicalBarChart(nv.models.ohlcBar()); 1.5420 + 1.5421 + // special default tooltip since we show multiple values per x 1.5422 + chart.useInteractiveGuideline(true); 1.5423 + chart.interactiveLayer.tooltip.contentGenerator(function(data) { 1.5424 + // we assume only one series exists for this chart 1.5425 + var d = data.series[0].data; 1.5426 + // match line colors as defined in nv.d3.css 1.5427 + var color = d.open < d.close ? "2ca02c" : "d62728"; 1.5428 + return '' + 1.5429 + '<h3 style="color: #' + color + '">' + data.value + '</h3>' + 1.5430 + '<table>' + 1.5431 + '<tr><td>open:</td><td>' + chart.yAxis.tickFormat()(d.open) + '</td></tr>' + 1.5432 + '<tr><td>close:</td><td>' + chart.yAxis.tickFormat()(d.close) + '</td></tr>' + 1.5433 + '<tr><td>high</td><td>' + chart.yAxis.tickFormat()(d.high) + '</td></tr>' + 1.5434 + '<tr><td>low:</td><td>' + chart.yAxis.tickFormat()(d.low) + '</td></tr>' + 1.5435 + '</table>'; 1.5436 + }); 1.5437 + return chart; 1.5438 +}; 1.5439 + 1.5440 +// candlestickChart is just a historical chart with candlestick bars and some tweaks 1.5441 +nv.models.candlestickBarChart = function() { 1.5442 + var chart = nv.models.historicalBarChart(nv.models.candlestickBar()); 1.5443 + 1.5444 + // special default tooltip since we show multiple values per x 1.5445 + chart.useInteractiveGuideline(true); 1.5446 + chart.interactiveLayer.tooltip.contentGenerator(function(data) { 1.5447 + // we assume only one series exists for this chart 1.5448 + var d = data.series[0].data; 1.5449 + // match line colors as defined in nv.d3.css 1.5450 + var color = d.open < d.close ? "2ca02c" : "d62728"; 1.5451 + return '' + 1.5452 + '<h3 style="color: #' + color + '">' + data.value + '</h3>' + 1.5453 + '<table>' + 1.5454 + '<tr><td>open:</td><td>' + chart.yAxis.tickFormat()(d.open) + '</td></tr>' + 1.5455 + '<tr><td>close:</td><td>' + chart.yAxis.tickFormat()(d.close) + '</td></tr>' + 1.5456 + '<tr><td>high</td><td>' + chart.yAxis.tickFormat()(d.high) + '</td></tr>' + 1.5457 + '<tr><td>low:</td><td>' + chart.yAxis.tickFormat()(d.low) + '</td></tr>' + 1.5458 + '</table>'; 1.5459 + }); 1.5460 + return chart; 1.5461 +}; 1.5462 +nv.models.legend = function() { 1.5463 + "use strict"; 1.5464 + 1.5465 + //============================================================ 1.5466 + // Public Variables with Default Settings 1.5467 + //------------------------------------------------------------ 1.5468 + 1.5469 + var margin = {top: 5, right: 0, bottom: 5, left: 0} 1.5470 + , width = 400 1.5471 + , height = 20 1.5472 + , getKey = function(d) { return d.key } 1.5473 + , color = nv.utils.getColor() 1.5474 + , align = true 1.5475 + , padding = 32 //define how much space between legend items. - recommend 32 for furious version 1.5476 + , rightAlign = true 1.5477 + , updateState = true //If true, legend will update data.disabled and trigger a 'stateChange' dispatch. 1.5478 + , radioButtonMode = false //If true, clicking legend items will cause it to behave like a radio button. (only one can be selected at a time) 1.5479 + , expanded = false 1.5480 + , dispatch = d3.dispatch('legendClick', 'legendDblclick', 'legendMouseover', 'legendMouseout', 'stateChange') 1.5481 + , vers = 'classic' //Options are "classic" and "furious" 1.5482 + ; 1.5483 + 1.5484 + function chart(selection) { 1.5485 + selection.each(function(data) { 1.5486 + var availableWidth = width - margin.left - margin.right, 1.5487 + container = d3.select(this); 1.5488 + nv.utils.initSVG(container); 1.5489 + 1.5490 + // Setup containers and skeleton of chart 1.5491 + var wrap = container.selectAll('g.nv-legend').data([data]); 1.5492 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-legend').append('g'); 1.5493 + var g = wrap.select('g'); 1.5494 + 1.5495 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.5496 + 1.5497 + var series = g.selectAll('.nv-series') 1.5498 + .data(function(d) { 1.5499 + if(vers != 'furious') return d; 1.5500 + 1.5501 + return d.filter(function(n) { 1.5502 + return expanded ? true : !n.disengaged; 1.5503 + }); 1.5504 + }); 1.5505 + 1.5506 + var seriesEnter = series.enter().append('g').attr('class', 'nv-series'); 1.5507 + var seriesShape; 1.5508 + 1.5509 + var versPadding; 1.5510 + switch(vers) { 1.5511 + case 'furious' : 1.5512 + versPadding = 23; 1.5513 + break; 1.5514 + case 'classic' : 1.5515 + versPadding = 20; 1.5516 + } 1.5517 + 1.5518 + if(vers == 'classic') { 1.5519 + seriesEnter.append('circle') 1.5520 + .style('stroke-width', 2) 1.5521 + .attr('class','nv-legend-symbol') 1.5522 + .attr('r', 5); 1.5523 + 1.5524 + seriesShape = series.select('circle'); 1.5525 + } else if (vers == 'furious') { 1.5526 + seriesEnter.append('rect') 1.5527 + .style('stroke-width', 2) 1.5528 + .attr('class','nv-legend-symbol') 1.5529 + .attr('rx', 3) 1.5530 + .attr('ry', 3); 1.5531 + 1.5532 + seriesShape = series.select('.nv-legend-symbol'); 1.5533 + 1.5534 + seriesEnter.append('g') 1.5535 + .attr('class', 'nv-check-box') 1.5536 + .property('innerHTML','<path d="M0.5,5 L22.5,5 L22.5,26.5 L0.5,26.5 L0.5,5 Z" class="nv-box"></path><path d="M5.5,12.8618467 L11.9185089,19.2803556 L31,0.198864511" class="nv-check"></path>') 1.5537 + .attr('transform', 'translate(-10,-8)scale(0.5)'); 1.5538 + 1.5539 + var seriesCheckbox = series.select('.nv-check-box'); 1.5540 + 1.5541 + seriesCheckbox.each(function(d,i) { 1.5542 + d3.select(this).selectAll('path') 1.5543 + .attr('stroke', setTextColor(d,i)); 1.5544 + }); 1.5545 + } 1.5546 + 1.5547 + seriesEnter.append('text') 1.5548 + .attr('text-anchor', 'start') 1.5549 + .attr('class','nv-legend-text') 1.5550 + .attr('dy', '.32em') 1.5551 + .attr('dx', '8'); 1.5552 + 1.5553 + var seriesText = series.select('text.nv-legend-text'); 1.5554 + 1.5555 + series 1.5556 + .on('mouseover', function(d,i) { 1.5557 + dispatch.legendMouseover(d,i); //TODO: Make consistent with other event objects 1.5558 + }) 1.5559 + .on('mouseout', function(d,i) { 1.5560 + dispatch.legendMouseout(d,i); 1.5561 + }) 1.5562 + .on('click', function(d,i) { 1.5563 + dispatch.legendClick(d,i); 1.5564 + // make sure we re-get data in case it was modified 1.5565 + var data = series.data(); 1.5566 + if (updateState) { 1.5567 + if(vers =='classic') { 1.5568 + if (radioButtonMode) { 1.5569 + //Radio button mode: set every series to disabled, 1.5570 + // and enable the clicked series. 1.5571 + data.forEach(function(series) { series.disabled = true}); 1.5572 + d.disabled = false; 1.5573 + } 1.5574 + else { 1.5575 + d.disabled = !d.disabled; 1.5576 + if (data.every(function(series) { return series.disabled})) { 1.5577 + //the default behavior of NVD3 legends is, if every single series 1.5578 + // is disabled, turn all series' back on. 1.5579 + data.forEach(function(series) { series.disabled = false}); 1.5580 + } 1.5581 + } 1.5582 + } else if(vers == 'furious') { 1.5583 + if(expanded) { 1.5584 + d.disengaged = !d.disengaged; 1.5585 + d.userDisabled = d.userDisabled == undefined ? !!d.disabled : d.userDisabled; 1.5586 + d.disabled = d.disengaged || d.userDisabled; 1.5587 + } else if (!expanded) { 1.5588 + d.disabled = !d.disabled; 1.5589 + d.userDisabled = d.disabled; 1.5590 + var engaged = data.filter(function(d) { return !d.disengaged; }); 1.5591 + if (engaged.every(function(series) { return series.userDisabled })) { 1.5592 + //the default behavior of NVD3 legends is, if every single series 1.5593 + // is disabled, turn all series' back on. 1.5594 + data.forEach(function(series) { 1.5595 + series.disabled = series.userDisabled = false; 1.5596 + }); 1.5597 + } 1.5598 + } 1.5599 + } 1.5600 + dispatch.stateChange({ 1.5601 + disabled: data.map(function(d) { return !!d.disabled }), 1.5602 + disengaged: data.map(function(d) { return !!d.disengaged }) 1.5603 + }); 1.5604 + 1.5605 + } 1.5606 + }) 1.5607 + .on('dblclick', function(d,i) { 1.5608 + if(vers == 'furious' && expanded) return; 1.5609 + dispatch.legendDblclick(d,i); 1.5610 + if (updateState) { 1.5611 + // make sure we re-get data in case it was modified 1.5612 + var data = series.data(); 1.5613 + //the default behavior of NVD3 legends, when double clicking one, 1.5614 + // is to set all other series' to false, and make the double clicked series enabled. 1.5615 + data.forEach(function(series) { 1.5616 + series.disabled = true; 1.5617 + if(vers == 'furious') series.userDisabled = series.disabled; 1.5618 + }); 1.5619 + d.disabled = false; 1.5620 + if(vers == 'furious') d.userDisabled = d.disabled; 1.5621 + dispatch.stateChange({ 1.5622 + disabled: data.map(function(d) { return !!d.disabled }) 1.5623 + }); 1.5624 + } 1.5625 + }); 1.5626 + 1.5627 + series.classed('nv-disabled', function(d) { return d.userDisabled }); 1.5628 + series.exit().remove(); 1.5629 + 1.5630 + seriesText 1.5631 + .attr('fill', setTextColor) 1.5632 + .text(getKey); 1.5633 + 1.5634 + //TODO: implement fixed-width and max-width options (max-width is especially useful with the align option) 1.5635 + // NEW ALIGNING CODE, TODO: clean up 1.5636 + var legendWidth = 0; 1.5637 + if (align) { 1.5638 + 1.5639 + var seriesWidths = []; 1.5640 + series.each(function(d,i) { 1.5641 + var legendText = d3.select(this).select('text'); 1.5642 + var nodeTextLength; 1.5643 + try { 1.5644 + nodeTextLength = legendText.node().getComputedTextLength(); 1.5645 + // If the legendText is display:none'd (nodeTextLength == 0), simulate an error so we approximate, instead 1.5646 + if(nodeTextLength <= 0) throw Error(); 1.5647 + } 1.5648 + catch(e) { 1.5649 + nodeTextLength = nv.utils.calcApproxTextWidth(legendText); 1.5650 + } 1.5651 + 1.5652 + seriesWidths.push(nodeTextLength + padding); 1.5653 + }); 1.5654 + 1.5655 + var seriesPerRow = 0; 1.5656 + var columnWidths = []; 1.5657 + legendWidth = 0; 1.5658 + 1.5659 + while ( legendWidth < availableWidth && seriesPerRow < seriesWidths.length) { 1.5660 + columnWidths[seriesPerRow] = seriesWidths[seriesPerRow]; 1.5661 + legendWidth += seriesWidths[seriesPerRow++]; 1.5662 + } 1.5663 + if (seriesPerRow === 0) seriesPerRow = 1; //minimum of one series per row 1.5664 + 1.5665 + while ( legendWidth > availableWidth && seriesPerRow > 1 ) { 1.5666 + columnWidths = []; 1.5667 + seriesPerRow--; 1.5668 + 1.5669 + for (var k = 0; k < seriesWidths.length; k++) { 1.5670 + if (seriesWidths[k] > (columnWidths[k % seriesPerRow] || 0) ) 1.5671 + columnWidths[k % seriesPerRow] = seriesWidths[k]; 1.5672 + } 1.5673 + 1.5674 + legendWidth = columnWidths.reduce(function(prev, cur, index, array) { 1.5675 + return prev + cur; 1.5676 + }); 1.5677 + } 1.5678 + 1.5679 + var xPositions = []; 1.5680 + for (var i = 0, curX = 0; i < seriesPerRow; i++) { 1.5681 + xPositions[i] = curX; 1.5682 + curX += columnWidths[i]; 1.5683 + } 1.5684 + 1.5685 + series 1.5686 + .attr('transform', function(d, i) { 1.5687 + return 'translate(' + xPositions[i % seriesPerRow] + ',' + (5 + Math.floor(i / seriesPerRow) * versPadding) + ')'; 1.5688 + }); 1.5689 + 1.5690 + //position legend as far right as possible within the total width 1.5691 + if (rightAlign) { 1.5692 + g.attr('transform', 'translate(' + (width - margin.right - legendWidth) + ',' + margin.top + ')'); 1.5693 + } 1.5694 + else { 1.5695 + g.attr('transform', 'translate(0' + ',' + margin.top + ')'); 1.5696 + } 1.5697 + 1.5698 + height = margin.top + margin.bottom + (Math.ceil(seriesWidths.length / seriesPerRow) * versPadding); 1.5699 + 1.5700 + } else { 1.5701 + 1.5702 + var ypos = 5, 1.5703 + newxpos = 5, 1.5704 + maxwidth = 0, 1.5705 + xpos; 1.5706 + series 1.5707 + .attr('transform', function(d, i) { 1.5708 + var length = d3.select(this).select('text').node().getComputedTextLength() + padding; 1.5709 + xpos = newxpos; 1.5710 + 1.5711 + if (width < margin.left + margin.right + xpos + length) { 1.5712 + newxpos = xpos = 5; 1.5713 + ypos += versPadding; 1.5714 + } 1.5715 + 1.5716 + newxpos += length; 1.5717 + if (newxpos > maxwidth) maxwidth = newxpos; 1.5718 + 1.5719 + if(legendWidth < xpos + maxwidth) { 1.5720 + legendWidth = xpos + maxwidth; 1.5721 + } 1.5722 + return 'translate(' + xpos + ',' + ypos + ')'; 1.5723 + }); 1.5724 + 1.5725 + //position legend as far right as possible within the total width 1.5726 + g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')'); 1.5727 + 1.5728 + height = margin.top + margin.bottom + ypos + 15; 1.5729 + } 1.5730 + 1.5731 + if(vers == 'furious') { 1.5732 + // Size rectangles after text is placed 1.5733 + seriesShape 1.5734 + .attr('width', function(d,i) { 1.5735 + return seriesText[0][i].getComputedTextLength() + 27; 1.5736 + }) 1.5737 + .attr('height', 18) 1.5738 + .attr('y', -9) 1.5739 + .attr('x', -15); 1.5740 + 1.5741 + // The background for the expanded legend (UI) 1.5742 + gEnter.insert('rect',':first-child') 1.5743 + .attr('class', 'nv-legend-bg') 1.5744 + .attr('fill', '#eee') 1.5745 + // .attr('stroke', '#444') 1.5746 + .attr('opacity',0); 1.5747 + 1.5748 + var seriesBG = g.select('.nv-legend-bg'); 1.5749 + 1.5750 + seriesBG 1.5751 + .transition().duration(300) 1.5752 + .attr('x', -versPadding ) 1.5753 + .attr('width', legendWidth + versPadding - 12) 1.5754 + .attr('height', height + 10) 1.5755 + .attr('y', -margin.top - 10) 1.5756 + .attr('opacity', expanded ? 1 : 0); 1.5757 + 1.5758 + 1.5759 + } 1.5760 + 1.5761 + seriesShape 1.5762 + .style('fill', setBGColor) 1.5763 + .style('fill-opacity', setBGOpacity) 1.5764 + .style('stroke', setBGColor); 1.5765 + }); 1.5766 + 1.5767 + function setTextColor(d,i) { 1.5768 + if(vers != 'furious') return '#000'; 1.5769 + if(expanded) { 1.5770 + return d.disengaged ? '#000' : '#fff'; 1.5771 + } else if (!expanded) { 1.5772 + if(!d.color) d.color = color(d,i); 1.5773 + return !!d.disabled ? d.color : '#fff'; 1.5774 + } 1.5775 + } 1.5776 + 1.5777 + function setBGColor(d,i) { 1.5778 + if(expanded && vers == 'furious') { 1.5779 + return d.disengaged ? '#eee' : d.color || color(d,i); 1.5780 + } else { 1.5781 + return d.color || color(d,i); 1.5782 + } 1.5783 + } 1.5784 + 1.5785 + 1.5786 + function setBGOpacity(d,i) { 1.5787 + if(expanded && vers == 'furious') { 1.5788 + return 1; 1.5789 + } else { 1.5790 + return !!d.disabled ? 0 : 1; 1.5791 + } 1.5792 + } 1.5793 + 1.5794 + return chart; 1.5795 + } 1.5796 + 1.5797 + //============================================================ 1.5798 + // Expose Public Variables 1.5799 + //------------------------------------------------------------ 1.5800 + 1.5801 + chart.dispatch = dispatch; 1.5802 + chart.options = nv.utils.optionsFunc.bind(chart); 1.5803 + 1.5804 + chart._options = Object.create({}, { 1.5805 + // simple options, just get/set the necessary values 1.5806 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.5807 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.5808 + key: {get: function(){return getKey;}, set: function(_){getKey=_;}}, 1.5809 + align: {get: function(){return align;}, set: function(_){align=_;}}, 1.5810 + rightAlign: {get: function(){return rightAlign;}, set: function(_){rightAlign=_;}}, 1.5811 + padding: {get: function(){return padding;}, set: function(_){padding=_;}}, 1.5812 + updateState: {get: function(){return updateState;}, set: function(_){updateState=_;}}, 1.5813 + radioButtonMode: {get: function(){return radioButtonMode;}, set: function(_){radioButtonMode=_;}}, 1.5814 + expanded: {get: function(){return expanded;}, set: function(_){expanded=_;}}, 1.5815 + vers: {get: function(){return vers;}, set: function(_){vers=_;}}, 1.5816 + 1.5817 + // options that require extra logic in the setter 1.5818 + margin: {get: function(){return margin;}, set: function(_){ 1.5819 + margin.top = _.top !== undefined ? _.top : margin.top; 1.5820 + margin.right = _.right !== undefined ? _.right : margin.right; 1.5821 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.5822 + margin.left = _.left !== undefined ? _.left : margin.left; 1.5823 + }}, 1.5824 + color: {get: function(){return color;}, set: function(_){ 1.5825 + color = nv.utils.getColor(_); 1.5826 + }} 1.5827 + }); 1.5828 + 1.5829 + nv.utils.initOptions(chart); 1.5830 + 1.5831 + return chart; 1.5832 +}; 1.5833 + 1.5834 +nv.models.line = function() { 1.5835 + "use strict"; 1.5836 + //============================================================ 1.5837 + // Public Variables with Default Settings 1.5838 + //------------------------------------------------------------ 1.5839 + 1.5840 + var scatter = nv.models.scatter() 1.5841 + ; 1.5842 + 1.5843 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.5844 + , width = 960 1.5845 + , height = 500 1.5846 + , container = null 1.5847 + , strokeWidth = 1.5 1.5848 + , color = nv.utils.defaultColor() // a function that returns a color 1.5849 + , getX = function(d) { return d.x } // accessor to get the x value from a data point 1.5850 + , getY = function(d) { return d.y } // accessor to get the y value from a data point 1.5851 + , 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 1.5852 + , isArea = function(d) { return d.area } // decides if a line is an area or just a line 1.5853 + , clipEdge = false // if true, masks lines within x and y scale 1.5854 + , x //can be accessed via chart.xScale() 1.5855 + , y //can be accessed via chart.yScale() 1.5856 + , interpolate = "linear" // controls the line interpolation 1.5857 + , duration = 250 1.5858 + , dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout', 'renderEnd') 1.5859 + ; 1.5860 + 1.5861 + scatter 1.5862 + .pointSize(16) // default size 1.5863 + .pointDomain([16,256]) //set to speed up calculation, needs to be unset if there is a custom size accessor 1.5864 + ; 1.5865 + 1.5866 + //============================================================ 1.5867 + 1.5868 + 1.5869 + //============================================================ 1.5870 + // Private Variables 1.5871 + //------------------------------------------------------------ 1.5872 + 1.5873 + var x0, y0 //used to store previous scales 1.5874 + , renderWatch = nv.utils.renderWatch(dispatch, duration) 1.5875 + ; 1.5876 + 1.5877 + //============================================================ 1.5878 + 1.5879 + 1.5880 + function chart(selection) { 1.5881 + renderWatch.reset(); 1.5882 + renderWatch.models(scatter); 1.5883 + selection.each(function(data) { 1.5884 + container = d3.select(this); 1.5885 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.5886 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.5887 + nv.utils.initSVG(container); 1.5888 + 1.5889 + // Setup Scales 1.5890 + x = scatter.xScale(); 1.5891 + y = scatter.yScale(); 1.5892 + 1.5893 + x0 = x0 || x; 1.5894 + y0 = y0 || y; 1.5895 + 1.5896 + // Setup containers and skeleton of chart 1.5897 + var wrap = container.selectAll('g.nv-wrap.nv-line').data([data]); 1.5898 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-line'); 1.5899 + var defsEnter = wrapEnter.append('defs'); 1.5900 + var gEnter = wrapEnter.append('g'); 1.5901 + var g = wrap.select('g'); 1.5902 + 1.5903 + gEnter.append('g').attr('class', 'nv-groups'); 1.5904 + gEnter.append('g').attr('class', 'nv-scatterWrap'); 1.5905 + 1.5906 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.5907 + 1.5908 + scatter 1.5909 + .width(availableWidth) 1.5910 + .height(availableHeight); 1.5911 + 1.5912 + var scatterWrap = wrap.select('.nv-scatterWrap'); 1.5913 + scatterWrap.call(scatter); 1.5914 + 1.5915 + defsEnter.append('clipPath') 1.5916 + .attr('id', 'nv-edge-clip-' + scatter.id()) 1.5917 + .append('rect'); 1.5918 + 1.5919 + wrap.select('#nv-edge-clip-' + scatter.id() + ' rect') 1.5920 + .attr('width', availableWidth) 1.5921 + .attr('height', (availableHeight > 0) ? availableHeight : 0); 1.5922 + 1.5923 + g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : ''); 1.5924 + scatterWrap 1.5925 + .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : ''); 1.5926 + 1.5927 + var groups = wrap.select('.nv-groups').selectAll('.nv-group') 1.5928 + .data(function(d) { return d }, function(d) { return d.key }); 1.5929 + groups.enter().append('g') 1.5930 + .style('stroke-opacity', 1e-6) 1.5931 + .style('stroke-width', function(d) { return d.strokeWidth || strokeWidth }) 1.5932 + .style('fill-opacity', 1e-6); 1.5933 + 1.5934 + groups.exit().remove(); 1.5935 + 1.5936 + groups 1.5937 + .attr('class', function(d,i) { 1.5938 + return (d.classed || '') + ' nv-group nv-series-' + i; 1.5939 + }) 1.5940 + .classed('hover', function(d) { return d.hover }) 1.5941 + .style('fill', function(d,i){ return color(d, i) }) 1.5942 + .style('stroke', function(d,i){ return color(d, i)}); 1.5943 + groups.watchTransition(renderWatch, 'line: groups') 1.5944 + .style('stroke-opacity', 1) 1.5945 + .style('fill-opacity', function(d) { return d.fillOpacity || .5}); 1.5946 + 1.5947 + var areaPaths = groups.selectAll('path.nv-area') 1.5948 + .data(function(d) { return isArea(d) ? [d] : [] }); // this is done differently than lines because I need to check if series is an area 1.5949 + areaPaths.enter().append('path') 1.5950 + .attr('class', 'nv-area') 1.5951 + .attr('d', function(d) { 1.5952 + return d3.svg.area() 1.5953 + .interpolate(interpolate) 1.5954 + .defined(defined) 1.5955 + .x(function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) }) 1.5956 + .y0(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) }) 1.5957 + .y1(function(d,i) { return y0( y.domain()[0] <= 0 ? y.domain()[1] >= 0 ? 0 : y.domain()[1] : y.domain()[0] ) }) 1.5958 + //.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this 1.5959 + .apply(this, [d.values]) 1.5960 + }); 1.5961 + groups.exit().selectAll('path.nv-area') 1.5962 + .remove(); 1.5963 + 1.5964 + areaPaths.watchTransition(renderWatch, 'line: areaPaths') 1.5965 + .attr('d', function(d) { 1.5966 + return d3.svg.area() 1.5967 + .interpolate(interpolate) 1.5968 + .defined(defined) 1.5969 + .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) }) 1.5970 + .y0(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) }) 1.5971 + .y1(function(d,i) { return y( y.domain()[0] <= 0 ? y.domain()[1] >= 0 ? 0 : y.domain()[1] : y.domain()[0] ) }) 1.5972 + //.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this 1.5973 + .apply(this, [d.values]) 1.5974 + }); 1.5975 + 1.5976 + var linePaths = groups.selectAll('path.nv-line') 1.5977 + .data(function(d) { return [d.values] }); 1.5978 + 1.5979 + linePaths.enter().append('path') 1.5980 + .attr('class', 'nv-line') 1.5981 + .attr('d', 1.5982 + d3.svg.line() 1.5983 + .interpolate(interpolate) 1.5984 + .defined(defined) 1.5985 + .x(function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) }) 1.5986 + .y(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) }) 1.5987 + ); 1.5988 + 1.5989 + linePaths.watchTransition(renderWatch, 'line: linePaths') 1.5990 + .attr('d', 1.5991 + d3.svg.line() 1.5992 + .interpolate(interpolate) 1.5993 + .defined(defined) 1.5994 + .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) }) 1.5995 + .y(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) }) 1.5996 + ); 1.5997 + 1.5998 + //store old scales for use in transitions on update 1.5999 + x0 = x.copy(); 1.6000 + y0 = y.copy(); 1.6001 + }); 1.6002 + renderWatch.renderEnd('line immediate'); 1.6003 + return chart; 1.6004 + } 1.6005 + 1.6006 + 1.6007 + //============================================================ 1.6008 + // Expose Public Variables 1.6009 + //------------------------------------------------------------ 1.6010 + 1.6011 + chart.dispatch = dispatch; 1.6012 + chart.scatter = scatter; 1.6013 + // Pass through events 1.6014 + scatter.dispatch.on('elementClick', function(){ dispatch.elementClick.apply(this, arguments); }); 1.6015 + scatter.dispatch.on('elementMouseover', function(){ dispatch.elementMouseover.apply(this, arguments); }); 1.6016 + scatter.dispatch.on('elementMouseout', function(){ dispatch.elementMouseout.apply(this, arguments); }); 1.6017 + 1.6018 + chart.options = nv.utils.optionsFunc.bind(chart); 1.6019 + 1.6020 + chart._options = Object.create({}, { 1.6021 + // simple options, just get/set the necessary values 1.6022 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.6023 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.6024 + defined: {get: function(){return defined;}, set: function(_){defined=_;}}, 1.6025 + interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}}, 1.6026 + clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}}, 1.6027 + 1.6028 + // options that require extra logic in the setter 1.6029 + margin: {get: function(){return margin;}, set: function(_){ 1.6030 + margin.top = _.top !== undefined ? _.top : margin.top; 1.6031 + margin.right = _.right !== undefined ? _.right : margin.right; 1.6032 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.6033 + margin.left = _.left !== undefined ? _.left : margin.left; 1.6034 + }}, 1.6035 + duration: {get: function(){return duration;}, set: function(_){ 1.6036 + duration = _; 1.6037 + renderWatch.reset(duration); 1.6038 + scatter.duration(duration); 1.6039 + }}, 1.6040 + isArea: {get: function(){return isArea;}, set: function(_){ 1.6041 + isArea = d3.functor(_); 1.6042 + }}, 1.6043 + x: {get: function(){return getX;}, set: function(_){ 1.6044 + getX = _; 1.6045 + scatter.x(_); 1.6046 + }}, 1.6047 + y: {get: function(){return getY;}, set: function(_){ 1.6048 + getY = _; 1.6049 + scatter.y(_); 1.6050 + }}, 1.6051 + color: {get: function(){return color;}, set: function(_){ 1.6052 + color = nv.utils.getColor(_); 1.6053 + scatter.color(color); 1.6054 + }} 1.6055 + }); 1.6056 + 1.6057 + nv.utils.inheritOptions(chart, scatter); 1.6058 + nv.utils.initOptions(chart); 1.6059 + 1.6060 + return chart; 1.6061 +}; 1.6062 +nv.models.lineChart = function() { 1.6063 + "use strict"; 1.6064 + 1.6065 + //============================================================ 1.6066 + // Public Variables with Default Settings 1.6067 + //------------------------------------------------------------ 1.6068 + 1.6069 + var lines = nv.models.line() 1.6070 + , xAxis = nv.models.axis() 1.6071 + , yAxis = nv.models.axis() 1.6072 + , legend = nv.models.legend() 1.6073 + , interactiveLayer = nv.interactiveGuideline() 1.6074 + , tooltip = nv.models.tooltip() 1.6075 + ; 1.6076 + 1.6077 + var margin = {top: 30, right: 20, bottom: 50, left: 60} 1.6078 + , color = nv.utils.defaultColor() 1.6079 + , width = null 1.6080 + , height = null 1.6081 + , showLegend = true 1.6082 + , showXAxis = true 1.6083 + , showYAxis = true 1.6084 + , rightAlignYAxis = false 1.6085 + , useInteractiveGuideline = false 1.6086 + , x 1.6087 + , y 1.6088 + , state = nv.utils.state() 1.6089 + , defaultState = null 1.6090 + , noData = null 1.6091 + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd') 1.6092 + , duration = 250 1.6093 + ; 1.6094 + 1.6095 + // set options on sub-objects for this chart 1.6096 + xAxis.orient('bottom').tickPadding(7); 1.6097 + yAxis.orient(rightAlignYAxis ? 'right' : 'left'); 1.6098 + tooltip.valueFormatter(function(d, i) { 1.6099 + return yAxis.tickFormat()(d, i); 1.6100 + }).headerFormatter(function(d, i) { 1.6101 + return xAxis.tickFormat()(d, i); 1.6102 + }); 1.6103 + 1.6104 + 1.6105 + //============================================================ 1.6106 + // Private Variables 1.6107 + //------------------------------------------------------------ 1.6108 + 1.6109 + var renderWatch = nv.utils.renderWatch(dispatch, duration); 1.6110 + 1.6111 + var stateGetter = function(data) { 1.6112 + return function(){ 1.6113 + return { 1.6114 + active: data.map(function(d) { return !d.disabled }) 1.6115 + }; 1.6116 + } 1.6117 + }; 1.6118 + 1.6119 + var stateSetter = function(data) { 1.6120 + return function(state) { 1.6121 + if (state.active !== undefined) 1.6122 + data.forEach(function(series,i) { 1.6123 + series.disabled = !state.active[i]; 1.6124 + }); 1.6125 + } 1.6126 + }; 1.6127 + 1.6128 + function chart(selection) { 1.6129 + renderWatch.reset(); 1.6130 + renderWatch.models(lines); 1.6131 + if (showXAxis) renderWatch.models(xAxis); 1.6132 + if (showYAxis) renderWatch.models(yAxis); 1.6133 + 1.6134 + selection.each(function(data) { 1.6135 + var container = d3.select(this), 1.6136 + that = this; 1.6137 + nv.utils.initSVG(container); 1.6138 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.6139 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.6140 + 1.6141 + chart.update = function() { 1.6142 + if (duration === 0) 1.6143 + container.call(chart); 1.6144 + else 1.6145 + container.transition().duration(duration).call(chart) 1.6146 + }; 1.6147 + chart.container = this; 1.6148 + 1.6149 + state 1.6150 + .setter(stateSetter(data), chart.update) 1.6151 + .getter(stateGetter(data)) 1.6152 + .update(); 1.6153 + 1.6154 + // DEPRECATED set state.disableddisabled 1.6155 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.6156 + 1.6157 + if (!defaultState) { 1.6158 + var key; 1.6159 + defaultState = {}; 1.6160 + for (key in state) { 1.6161 + if (state[key] instanceof Array) 1.6162 + defaultState[key] = state[key].slice(0); 1.6163 + else 1.6164 + defaultState[key] = state[key]; 1.6165 + } 1.6166 + } 1.6167 + 1.6168 + // Display noData message if there's nothing to show. 1.6169 + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { 1.6170 + nv.utils.noData(chart, container) 1.6171 + return chart; 1.6172 + } else { 1.6173 + container.selectAll('.nv-noData').remove(); 1.6174 + } 1.6175 + 1.6176 + 1.6177 + // Setup Scales 1.6178 + x = lines.xScale(); 1.6179 + y = lines.yScale(); 1.6180 + 1.6181 + // Setup containers and skeleton of chart 1.6182 + var wrap = container.selectAll('g.nv-wrap.nv-lineChart').data([data]); 1.6183 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineChart').append('g'); 1.6184 + var g = wrap.select('g'); 1.6185 + 1.6186 + gEnter.append("rect").style("opacity",0); 1.6187 + gEnter.append('g').attr('class', 'nv-x nv-axis'); 1.6188 + gEnter.append('g').attr('class', 'nv-y nv-axis'); 1.6189 + gEnter.append('g').attr('class', 'nv-linesWrap'); 1.6190 + gEnter.append('g').attr('class', 'nv-legendWrap'); 1.6191 + gEnter.append('g').attr('class', 'nv-interactive'); 1.6192 + 1.6193 + g.select("rect") 1.6194 + .attr("width",availableWidth) 1.6195 + .attr("height",(availableHeight > 0) ? availableHeight : 0); 1.6196 + 1.6197 + // Legend 1.6198 + if (showLegend) { 1.6199 + legend.width(availableWidth); 1.6200 + 1.6201 + g.select('.nv-legendWrap') 1.6202 + .datum(data) 1.6203 + .call(legend); 1.6204 + 1.6205 + if ( margin.top != legend.height()) { 1.6206 + margin.top = legend.height(); 1.6207 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.6208 + } 1.6209 + 1.6210 + wrap.select('.nv-legendWrap') 1.6211 + .attr('transform', 'translate(0,' + (-margin.top) +')') 1.6212 + } 1.6213 + 1.6214 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.6215 + 1.6216 + if (rightAlignYAxis) { 1.6217 + g.select(".nv-y.nv-axis") 1.6218 + .attr("transform", "translate(" + availableWidth + ",0)"); 1.6219 + } 1.6220 + 1.6221 + //Set up interactive layer 1.6222 + if (useInteractiveGuideline) { 1.6223 + interactiveLayer 1.6224 + .width(availableWidth) 1.6225 + .height(availableHeight) 1.6226 + .margin({left:margin.left, top:margin.top}) 1.6227 + .svgContainer(container) 1.6228 + .xScale(x); 1.6229 + wrap.select(".nv-interactive").call(interactiveLayer); 1.6230 + } 1.6231 + 1.6232 + lines 1.6233 + .width(availableWidth) 1.6234 + .height(availableHeight) 1.6235 + .color(data.map(function(d,i) { 1.6236 + return d.color || color(d, i); 1.6237 + }).filter(function(d,i) { return !data[i].disabled })); 1.6238 + 1.6239 + 1.6240 + var linesWrap = g.select('.nv-linesWrap') 1.6241 + .datum(data.filter(function(d) { return !d.disabled })); 1.6242 + 1.6243 + linesWrap.call(lines); 1.6244 + 1.6245 + // Setup Axes 1.6246 + if (showXAxis) { 1.6247 + xAxis 1.6248 + .scale(x) 1.6249 + ._ticks(nv.utils.calcTicksX(availableWidth/100, data) ) 1.6250 + .tickSize(-availableHeight, 0); 1.6251 + 1.6252 + g.select('.nv-x.nv-axis') 1.6253 + .attr('transform', 'translate(0,' + y.range()[0] + ')'); 1.6254 + g.select('.nv-x.nv-axis') 1.6255 + .call(xAxis); 1.6256 + } 1.6257 + 1.6258 + if (showYAxis) { 1.6259 + yAxis 1.6260 + .scale(y) 1.6261 + ._ticks(nv.utils.calcTicksY(availableHeight/36, data) ) 1.6262 + .tickSize( -availableWidth, 0); 1.6263 + 1.6264 + g.select('.nv-y.nv-axis') 1.6265 + .call(yAxis); 1.6266 + } 1.6267 + 1.6268 + //============================================================ 1.6269 + // Event Handling/Dispatching (in chart's scope) 1.6270 + //------------------------------------------------------------ 1.6271 + 1.6272 + legend.dispatch.on('stateChange', function(newState) { 1.6273 + for (var key in newState) 1.6274 + state[key] = newState[key]; 1.6275 + dispatch.stateChange(state); 1.6276 + chart.update(); 1.6277 + }); 1.6278 + 1.6279 + interactiveLayer.dispatch.on('elementMousemove', function(e) { 1.6280 + lines.clearHighlights(); 1.6281 + var singlePoint, pointIndex, pointXLocation, allData = []; 1.6282 + data 1.6283 + .filter(function(series, i) { 1.6284 + series.seriesIndex = i; 1.6285 + return !series.disabled; 1.6286 + }) 1.6287 + .forEach(function(series,i) { 1.6288 + pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x()); 1.6289 + var point = series.values[pointIndex]; 1.6290 + var pointYValue = chart.y()(point, pointIndex); 1.6291 + if (pointYValue != null) { 1.6292 + lines.highlightPoint(i, pointIndex, true); 1.6293 + } 1.6294 + if (point === undefined) return; 1.6295 + if (singlePoint === undefined) singlePoint = point; 1.6296 + if (pointXLocation === undefined) pointXLocation = chart.xScale()(chart.x()(point,pointIndex)); 1.6297 + allData.push({ 1.6298 + key: series.key, 1.6299 + value: pointYValue, 1.6300 + color: color(series,series.seriesIndex) 1.6301 + }); 1.6302 + }); 1.6303 + //Highlight the tooltip entry based on which point the mouse is closest to. 1.6304 + if (allData.length > 2) { 1.6305 + var yValue = chart.yScale().invert(e.mouseY); 1.6306 + var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]); 1.6307 + var threshold = 0.03 * domainExtent; 1.6308 + var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value}),yValue,threshold); 1.6309 + if (indexToHighlight !== null) 1.6310 + allData[indexToHighlight].highlight = true; 1.6311 + } 1.6312 + 1.6313 + var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex)); 1.6314 + interactiveLayer.tooltip 1.6315 + .position({left: e.mouseX + margin.left, top: e.mouseY + margin.top}) 1.6316 + .chartContainer(that.parentNode) 1.6317 + .valueFormatter(function(d,i) { 1.6318 + return d == null ? "N/A" : yAxis.tickFormat()(d); 1.6319 + }) 1.6320 + .data({ 1.6321 + value: xValue, 1.6322 + index: pointIndex, 1.6323 + series: allData 1.6324 + })(); 1.6325 + 1.6326 + interactiveLayer.renderGuideLine(pointXLocation); 1.6327 + 1.6328 + }); 1.6329 + 1.6330 + interactiveLayer.dispatch.on('elementClick', function(e) { 1.6331 + var pointXLocation, allData = []; 1.6332 + 1.6333 + data.filter(function(series, i) { 1.6334 + series.seriesIndex = i; 1.6335 + return !series.disabled; 1.6336 + }).forEach(function(series) { 1.6337 + var pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x()); 1.6338 + var point = series.values[pointIndex]; 1.6339 + if (typeof point === 'undefined') return; 1.6340 + if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex)); 1.6341 + var yPos = chart.yScale()(chart.y()(point,pointIndex)); 1.6342 + allData.push({ 1.6343 + point: point, 1.6344 + pointIndex: pointIndex, 1.6345 + pos: [pointXLocation, yPos], 1.6346 + seriesIndex: series.seriesIndex, 1.6347 + series: series 1.6348 + }); 1.6349 + }); 1.6350 + 1.6351 + lines.dispatch.elementClick(allData); 1.6352 + }); 1.6353 + 1.6354 + interactiveLayer.dispatch.on("elementMouseout",function(e) { 1.6355 + lines.clearHighlights(); 1.6356 + }); 1.6357 + 1.6358 + dispatch.on('changeState', function(e) { 1.6359 + if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) { 1.6360 + data.forEach(function(series,i) { 1.6361 + series.disabled = e.disabled[i]; 1.6362 + }); 1.6363 + 1.6364 + state.disabled = e.disabled; 1.6365 + } 1.6366 + 1.6367 + chart.update(); 1.6368 + }); 1.6369 + 1.6370 + }); 1.6371 + 1.6372 + renderWatch.renderEnd('lineChart immediate'); 1.6373 + return chart; 1.6374 + } 1.6375 + 1.6376 + //============================================================ 1.6377 + // Event Handling/Dispatching (out of chart's scope) 1.6378 + //------------------------------------------------------------ 1.6379 + 1.6380 + lines.dispatch.on('elementMouseover.tooltip', function(evt) { 1.6381 + tooltip.data(evt).position(evt.pos).hidden(false); 1.6382 + }); 1.6383 + 1.6384 + lines.dispatch.on('elementMouseout.tooltip', function(evt) { 1.6385 + tooltip.hidden(true) 1.6386 + }); 1.6387 + 1.6388 + //============================================================ 1.6389 + // Expose Public Variables 1.6390 + //------------------------------------------------------------ 1.6391 + 1.6392 + // expose chart's sub-components 1.6393 + chart.dispatch = dispatch; 1.6394 + chart.lines = lines; 1.6395 + chart.legend = legend; 1.6396 + chart.xAxis = xAxis; 1.6397 + chart.yAxis = yAxis; 1.6398 + chart.interactiveLayer = interactiveLayer; 1.6399 + chart.tooltip = tooltip; 1.6400 + 1.6401 + chart.dispatch = dispatch; 1.6402 + chart.options = nv.utils.optionsFunc.bind(chart); 1.6403 + 1.6404 + chart._options = Object.create({}, { 1.6405 + // simple options, just get/set the necessary values 1.6406 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.6407 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.6408 + showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, 1.6409 + showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}}, 1.6410 + showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}}, 1.6411 + defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}}, 1.6412 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.6413 + 1.6414 + // deprecated options 1.6415 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.6416 + // deprecated after 1.7.1 1.6417 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.6418 + tooltip.enabled(!!_); 1.6419 + }}, 1.6420 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.6421 + // deprecated after 1.7.1 1.6422 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.6423 + tooltip.contentGenerator(_); 1.6424 + }}, 1.6425 + 1.6426 + // options that require extra logic in the setter 1.6427 + margin: {get: function(){return margin;}, set: function(_){ 1.6428 + margin.top = _.top !== undefined ? _.top : margin.top; 1.6429 + margin.right = _.right !== undefined ? _.right : margin.right; 1.6430 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.6431 + margin.left = _.left !== undefined ? _.left : margin.left; 1.6432 + }}, 1.6433 + duration: {get: function(){return duration;}, set: function(_){ 1.6434 + duration = _; 1.6435 + renderWatch.reset(duration); 1.6436 + lines.duration(duration); 1.6437 + xAxis.duration(duration); 1.6438 + yAxis.duration(duration); 1.6439 + }}, 1.6440 + color: {get: function(){return color;}, set: function(_){ 1.6441 + color = nv.utils.getColor(_); 1.6442 + legend.color(color); 1.6443 + lines.color(color); 1.6444 + }}, 1.6445 + rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){ 1.6446 + rightAlignYAxis = _; 1.6447 + yAxis.orient( rightAlignYAxis ? 'right' : 'left'); 1.6448 + }}, 1.6449 + useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){ 1.6450 + useInteractiveGuideline = _; 1.6451 + if (useInteractiveGuideline) { 1.6452 + lines.interactive(false); 1.6453 + lines.useVoronoi(false); 1.6454 + } 1.6455 + }} 1.6456 + }); 1.6457 + 1.6458 + nv.utils.inheritOptions(chart, lines); 1.6459 + nv.utils.initOptions(chart); 1.6460 + 1.6461 + return chart; 1.6462 +}; 1.6463 +nv.models.linePlusBarChart = function() { 1.6464 + "use strict"; 1.6465 + 1.6466 + //============================================================ 1.6467 + // Public Variables with Default Settings 1.6468 + //------------------------------------------------------------ 1.6469 + 1.6470 + var lines = nv.models.line() 1.6471 + , lines2 = nv.models.line() 1.6472 + , bars = nv.models.historicalBar() 1.6473 + , bars2 = nv.models.historicalBar() 1.6474 + , xAxis = nv.models.axis() 1.6475 + , x2Axis = nv.models.axis() 1.6476 + , y1Axis = nv.models.axis() 1.6477 + , y2Axis = nv.models.axis() 1.6478 + , y3Axis = nv.models.axis() 1.6479 + , y4Axis = nv.models.axis() 1.6480 + , legend = nv.models.legend() 1.6481 + , brush = d3.svg.brush() 1.6482 + , tooltip = nv.models.tooltip() 1.6483 + ; 1.6484 + 1.6485 + var margin = {top: 30, right: 30, bottom: 30, left: 60} 1.6486 + , margin2 = {top: 0, right: 30, bottom: 20, left: 60} 1.6487 + , width = null 1.6488 + , height = null 1.6489 + , getX = function(d) { return d.x } 1.6490 + , getY = function(d) { return d.y } 1.6491 + , color = nv.utils.defaultColor() 1.6492 + , showLegend = true 1.6493 + , focusEnable = true 1.6494 + , focusShowAxisY = false 1.6495 + , focusShowAxisX = true 1.6496 + , focusHeight = 50 1.6497 + , extent 1.6498 + , brushExtent = null 1.6499 + , x 1.6500 + , x2 1.6501 + , y1 1.6502 + , y2 1.6503 + , y3 1.6504 + , y4 1.6505 + , noData = null 1.6506 + , dispatch = d3.dispatch('brush', 'stateChange', 'changeState') 1.6507 + , transitionDuration = 0 1.6508 + , state = nv.utils.state() 1.6509 + , defaultState = null 1.6510 + , legendLeftAxisHint = ' (left axis)' 1.6511 + , legendRightAxisHint = ' (right axis)' 1.6512 + ; 1.6513 + 1.6514 + lines.clipEdge(true); 1.6515 + lines2.interactive(false); 1.6516 + xAxis.orient('bottom').tickPadding(5); 1.6517 + y1Axis.orient('left'); 1.6518 + y2Axis.orient('right'); 1.6519 + x2Axis.orient('bottom').tickPadding(5); 1.6520 + y3Axis.orient('left'); 1.6521 + y4Axis.orient('right'); 1.6522 + 1.6523 + tooltip.headerEnabled(true).headerFormatter(function(d, i) { 1.6524 + return xAxis.tickFormat()(d, i); 1.6525 + }); 1.6526 + 1.6527 + //============================================================ 1.6528 + // Private Variables 1.6529 + //------------------------------------------------------------ 1.6530 + 1.6531 + var stateGetter = function(data) { 1.6532 + return function(){ 1.6533 + return { 1.6534 + active: data.map(function(d) { return !d.disabled }) 1.6535 + }; 1.6536 + } 1.6537 + }; 1.6538 + 1.6539 + var stateSetter = function(data) { 1.6540 + return function(state) { 1.6541 + if (state.active !== undefined) 1.6542 + data.forEach(function(series,i) { 1.6543 + series.disabled = !state.active[i]; 1.6544 + }); 1.6545 + } 1.6546 + }; 1.6547 + 1.6548 + function chart(selection) { 1.6549 + selection.each(function(data) { 1.6550 + var container = d3.select(this), 1.6551 + that = this; 1.6552 + nv.utils.initSVG(container); 1.6553 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.6554 + availableHeight1 = nv.utils.availableHeight(height, container, margin) 1.6555 + - (focusEnable ? focusHeight : 0), 1.6556 + availableHeight2 = focusHeight - margin2.top - margin2.bottom; 1.6557 + 1.6558 + chart.update = function() { container.transition().duration(transitionDuration).call(chart); }; 1.6559 + chart.container = this; 1.6560 + 1.6561 + state 1.6562 + .setter(stateSetter(data), chart.update) 1.6563 + .getter(stateGetter(data)) 1.6564 + .update(); 1.6565 + 1.6566 + // DEPRECATED set state.disableddisabled 1.6567 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.6568 + 1.6569 + if (!defaultState) { 1.6570 + var key; 1.6571 + defaultState = {}; 1.6572 + for (key in state) { 1.6573 + if (state[key] instanceof Array) 1.6574 + defaultState[key] = state[key].slice(0); 1.6575 + else 1.6576 + defaultState[key] = state[key]; 1.6577 + } 1.6578 + } 1.6579 + 1.6580 + // Display No Data message if there's nothing to show. 1.6581 + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { 1.6582 + nv.utils.noData(chart, container) 1.6583 + return chart; 1.6584 + } else { 1.6585 + container.selectAll('.nv-noData').remove(); 1.6586 + } 1.6587 + 1.6588 + // Setup Scales 1.6589 + var dataBars = data.filter(function(d) { return !d.disabled && d.bar }); 1.6590 + var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240 1.6591 + 1.6592 + x = bars.xScale(); 1.6593 + x2 = x2Axis.scale(); 1.6594 + y1 = bars.yScale(); 1.6595 + y2 = lines.yScale(); 1.6596 + y3 = bars2.yScale(); 1.6597 + y4 = lines2.yScale(); 1.6598 + 1.6599 + var series1 = data 1.6600 + .filter(function(d) { return !d.disabled && d.bar }) 1.6601 + .map(function(d) { 1.6602 + return d.values.map(function(d,i) { 1.6603 + return { x: getX(d,i), y: getY(d,i) } 1.6604 + }) 1.6605 + }); 1.6606 + 1.6607 + var series2 = data 1.6608 + .filter(function(d) { return !d.disabled && !d.bar }) 1.6609 + .map(function(d) { 1.6610 + return d.values.map(function(d,i) { 1.6611 + return { x: getX(d,i), y: getY(d,i) } 1.6612 + }) 1.6613 + }); 1.6614 + 1.6615 + x.range([0, availableWidth]); 1.6616 + 1.6617 + x2 .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } )) 1.6618 + .range([0, availableWidth]); 1.6619 + 1.6620 + // Setup containers and skeleton of chart 1.6621 + var wrap = container.selectAll('g.nv-wrap.nv-linePlusBar').data([data]); 1.6622 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-linePlusBar').append('g'); 1.6623 + var g = wrap.select('g'); 1.6624 + 1.6625 + gEnter.append('g').attr('class', 'nv-legendWrap'); 1.6626 + 1.6627 + // this is the main chart 1.6628 + var focusEnter = gEnter.append('g').attr('class', 'nv-focus'); 1.6629 + focusEnter.append('g').attr('class', 'nv-x nv-axis'); 1.6630 + focusEnter.append('g').attr('class', 'nv-y1 nv-axis'); 1.6631 + focusEnter.append('g').attr('class', 'nv-y2 nv-axis'); 1.6632 + focusEnter.append('g').attr('class', 'nv-barsWrap'); 1.6633 + focusEnter.append('g').attr('class', 'nv-linesWrap'); 1.6634 + 1.6635 + // context chart is where you can focus in 1.6636 + var contextEnter = gEnter.append('g').attr('class', 'nv-context'); 1.6637 + contextEnter.append('g').attr('class', 'nv-x nv-axis'); 1.6638 + contextEnter.append('g').attr('class', 'nv-y1 nv-axis'); 1.6639 + contextEnter.append('g').attr('class', 'nv-y2 nv-axis'); 1.6640 + contextEnter.append('g').attr('class', 'nv-barsWrap'); 1.6641 + contextEnter.append('g').attr('class', 'nv-linesWrap'); 1.6642 + contextEnter.append('g').attr('class', 'nv-brushBackground'); 1.6643 + contextEnter.append('g').attr('class', 'nv-x nv-brush'); 1.6644 + 1.6645 + //============================================================ 1.6646 + // Legend 1.6647 + //------------------------------------------------------------ 1.6648 + 1.6649 + if (showLegend) { 1.6650 + var legendWidth = legend.align() ? availableWidth / 2 : availableWidth; 1.6651 + var legendXPosition = legend.align() ? legendWidth : 0; 1.6652 + 1.6653 + legend.width(legendWidth); 1.6654 + 1.6655 + g.select('.nv-legendWrap') 1.6656 + .datum(data.map(function(series) { 1.6657 + series.originalKey = series.originalKey === undefined ? series.key : series.originalKey; 1.6658 + series.key = series.originalKey + (series.bar ? legendLeftAxisHint : legendRightAxisHint); 1.6659 + return series; 1.6660 + })) 1.6661 + .call(legend); 1.6662 + 1.6663 + if ( margin.top != legend.height()) { 1.6664 + margin.top = legend.height(); 1.6665 + // FIXME: shouldn't this be "- (focusEnabled ? focusHeight : 0)"? 1.6666 + availableHeight1 = nv.utils.availableHeight(height, container, margin) - focusHeight; 1.6667 + } 1.6668 + 1.6669 + g.select('.nv-legendWrap') 1.6670 + .attr('transform', 'translate(' + legendXPosition + ',' + (-margin.top) +')'); 1.6671 + } 1.6672 + 1.6673 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.6674 + 1.6675 + //============================================================ 1.6676 + // Context chart (focus chart) components 1.6677 + //------------------------------------------------------------ 1.6678 + 1.6679 + // hide or show the focus context chart 1.6680 + g.select('.nv-context').style('display', focusEnable ? 'initial' : 'none'); 1.6681 + 1.6682 + bars2 1.6683 + .width(availableWidth) 1.6684 + .height(availableHeight2) 1.6685 + .color(data.map(function (d, i) { 1.6686 + return d.color || color(d, i); 1.6687 + }).filter(function (d, i) { 1.6688 + return !data[i].disabled && data[i].bar 1.6689 + })); 1.6690 + lines2 1.6691 + .width(availableWidth) 1.6692 + .height(availableHeight2) 1.6693 + .color(data.map(function (d, i) { 1.6694 + return d.color || color(d, i); 1.6695 + }).filter(function (d, i) { 1.6696 + return !data[i].disabled && !data[i].bar 1.6697 + })); 1.6698 + 1.6699 + var bars2Wrap = g.select('.nv-context .nv-barsWrap') 1.6700 + .datum(dataBars.length ? dataBars : [ 1.6701 + {values: []} 1.6702 + ]); 1.6703 + var lines2Wrap = g.select('.nv-context .nv-linesWrap') 1.6704 + .datum(!dataLines[0].disabled ? dataLines : [ 1.6705 + {values: []} 1.6706 + ]); 1.6707 + 1.6708 + g.select('.nv-context') 1.6709 + .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')'); 1.6710 + 1.6711 + bars2Wrap.transition().call(bars2); 1.6712 + lines2Wrap.transition().call(lines2); 1.6713 + 1.6714 + // context (focus chart) axis controls 1.6715 + if (focusShowAxisX) { 1.6716 + x2Axis 1.6717 + ._ticks( nv.utils.calcTicksX(availableWidth / 100, data)) 1.6718 + .tickSize(-availableHeight2, 0); 1.6719 + g.select('.nv-context .nv-x.nv-axis') 1.6720 + .attr('transform', 'translate(0,' + y3.range()[0] + ')'); 1.6721 + g.select('.nv-context .nv-x.nv-axis').transition() 1.6722 + .call(x2Axis); 1.6723 + } 1.6724 + 1.6725 + if (focusShowAxisY) { 1.6726 + y3Axis 1.6727 + .scale(y3) 1.6728 + ._ticks( availableHeight2 / 36 ) 1.6729 + .tickSize( -availableWidth, 0); 1.6730 + y4Axis 1.6731 + .scale(y4) 1.6732 + ._ticks( availableHeight2 / 36 ) 1.6733 + .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none 1.6734 + 1.6735 + g.select('.nv-context .nv-y3.nv-axis') 1.6736 + .style('opacity', dataBars.length ? 1 : 0) 1.6737 + .attr('transform', 'translate(0,' + x2.range()[0] + ')'); 1.6738 + g.select('.nv-context .nv-y2.nv-axis') 1.6739 + .style('opacity', dataLines.length ? 1 : 0) 1.6740 + .attr('transform', 'translate(' + x2.range()[1] + ',0)'); 1.6741 + 1.6742 + g.select('.nv-context .nv-y1.nv-axis').transition() 1.6743 + .call(y3Axis); 1.6744 + g.select('.nv-context .nv-y2.nv-axis').transition() 1.6745 + .call(y4Axis); 1.6746 + } 1.6747 + 1.6748 + // Setup Brush 1.6749 + brush.x(x2).on('brush', onBrush); 1.6750 + 1.6751 + if (brushExtent) brush.extent(brushExtent); 1.6752 + 1.6753 + var brushBG = g.select('.nv-brushBackground').selectAll('g') 1.6754 + .data([brushExtent || brush.extent()]); 1.6755 + 1.6756 + var brushBGenter = brushBG.enter() 1.6757 + .append('g'); 1.6758 + 1.6759 + brushBGenter.append('rect') 1.6760 + .attr('class', 'left') 1.6761 + .attr('x', 0) 1.6762 + .attr('y', 0) 1.6763 + .attr('height', availableHeight2); 1.6764 + 1.6765 + brushBGenter.append('rect') 1.6766 + .attr('class', 'right') 1.6767 + .attr('x', 0) 1.6768 + .attr('y', 0) 1.6769 + .attr('height', availableHeight2); 1.6770 + 1.6771 + var gBrush = g.select('.nv-x.nv-brush') 1.6772 + .call(brush); 1.6773 + gBrush.selectAll('rect') 1.6774 + //.attr('y', -5) 1.6775 + .attr('height', availableHeight2); 1.6776 + gBrush.selectAll('.resize').append('path').attr('d', resizePath); 1.6777 + 1.6778 + //============================================================ 1.6779 + // Event Handling/Dispatching (in chart's scope) 1.6780 + //------------------------------------------------------------ 1.6781 + 1.6782 + legend.dispatch.on('stateChange', function(newState) { 1.6783 + for (var key in newState) 1.6784 + state[key] = newState[key]; 1.6785 + dispatch.stateChange(state); 1.6786 + chart.update(); 1.6787 + }); 1.6788 + 1.6789 + // Update chart from a state object passed to event handler 1.6790 + dispatch.on('changeState', function(e) { 1.6791 + if (typeof e.disabled !== 'undefined') { 1.6792 + data.forEach(function(series,i) { 1.6793 + series.disabled = e.disabled[i]; 1.6794 + }); 1.6795 + state.disabled = e.disabled; 1.6796 + } 1.6797 + chart.update(); 1.6798 + }); 1.6799 + 1.6800 + //============================================================ 1.6801 + // Functions 1.6802 + //------------------------------------------------------------ 1.6803 + 1.6804 + // Taken from crossfilter (http://square.github.com/crossfilter/) 1.6805 + function resizePath(d) { 1.6806 + var e = +(d == 'e'), 1.6807 + x = e ? 1 : -1, 1.6808 + y = availableHeight2 / 3; 1.6809 + return 'M' + (.5 * x) + ',' + y 1.6810 + + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6) 1.6811 + + 'V' + (2 * y - 6) 1.6812 + + 'A6,6 0 0 ' + e + ' ' + (.5 * x) + ',' + (2 * y) 1.6813 + + 'Z' 1.6814 + + 'M' + (2.5 * x) + ',' + (y + 8) 1.6815 + + 'V' + (2 * y - 8) 1.6816 + + 'M' + (4.5 * x) + ',' + (y + 8) 1.6817 + + 'V' + (2 * y - 8); 1.6818 + } 1.6819 + 1.6820 + 1.6821 + function updateBrushBG() { 1.6822 + if (!brush.empty()) brush.extent(brushExtent); 1.6823 + brushBG 1.6824 + .data([brush.empty() ? x2.domain() : brushExtent]) 1.6825 + .each(function(d,i) { 1.6826 + var leftWidth = x2(d[0]) - x2.range()[0], 1.6827 + rightWidth = x2.range()[1] - x2(d[1]); 1.6828 + d3.select(this).select('.left') 1.6829 + .attr('width', leftWidth < 0 ? 0 : leftWidth); 1.6830 + 1.6831 + d3.select(this).select('.right') 1.6832 + .attr('x', x2(d[1])) 1.6833 + .attr('width', rightWidth < 0 ? 0 : rightWidth); 1.6834 + }); 1.6835 + } 1.6836 + 1.6837 + function onBrush() { 1.6838 + brushExtent = brush.empty() ? null : brush.extent(); 1.6839 + extent = brush.empty() ? x2.domain() : brush.extent(); 1.6840 + dispatch.brush({extent: extent, brush: brush}); 1.6841 + updateBrushBG(); 1.6842 + 1.6843 + // Prepare Main (Focus) Bars and Lines 1.6844 + bars 1.6845 + .width(availableWidth) 1.6846 + .height(availableHeight1) 1.6847 + .color(data.map(function(d,i) { 1.6848 + return d.color || color(d, i); 1.6849 + }).filter(function(d,i) { return !data[i].disabled && data[i].bar })); 1.6850 + 1.6851 + lines 1.6852 + .width(availableWidth) 1.6853 + .height(availableHeight1) 1.6854 + .color(data.map(function(d,i) { 1.6855 + return d.color || color(d, i); 1.6856 + }).filter(function(d,i) { return !data[i].disabled && !data[i].bar })); 1.6857 + 1.6858 + var focusBarsWrap = g.select('.nv-focus .nv-barsWrap') 1.6859 + .datum(!dataBars.length ? [{values:[]}] : 1.6860 + dataBars 1.6861 + .map(function(d,i) { 1.6862 + return { 1.6863 + key: d.key, 1.6864 + values: d.values.filter(function(d,i) { 1.6865 + return bars.x()(d,i) >= extent[0] && bars.x()(d,i) <= extent[1]; 1.6866 + }) 1.6867 + } 1.6868 + }) 1.6869 + ); 1.6870 + 1.6871 + var focusLinesWrap = g.select('.nv-focus .nv-linesWrap') 1.6872 + .datum(dataLines[0].disabled ? [{values:[]}] : 1.6873 + dataLines 1.6874 + .map(function(d,i) { 1.6875 + return { 1.6876 + area: d.area, 1.6877 + fillOpacity: d.fillOpacity, 1.6878 + key: d.key, 1.6879 + values: d.values.filter(function(d,i) { 1.6880 + return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1]; 1.6881 + }) 1.6882 + } 1.6883 + }) 1.6884 + ); 1.6885 + 1.6886 + // Update Main (Focus) X Axis 1.6887 + if (dataBars.length) { 1.6888 + x = bars.xScale(); 1.6889 + } else { 1.6890 + x = lines.xScale(); 1.6891 + } 1.6892 + 1.6893 + xAxis 1.6894 + .scale(x) 1.6895 + ._ticks( nv.utils.calcTicksX(availableWidth/100, data) ) 1.6896 + .tickSize(-availableHeight1, 0); 1.6897 + 1.6898 + xAxis.domain([Math.ceil(extent[0]), Math.floor(extent[1])]); 1.6899 + 1.6900 + g.select('.nv-x.nv-axis').transition().duration(transitionDuration) 1.6901 + .call(xAxis); 1.6902 + 1.6903 + // Update Main (Focus) Bars and Lines 1.6904 + focusBarsWrap.transition().duration(transitionDuration).call(bars); 1.6905 + focusLinesWrap.transition().duration(transitionDuration).call(lines); 1.6906 + 1.6907 + // Setup and Update Main (Focus) Y Axes 1.6908 + g.select('.nv-focus .nv-x.nv-axis') 1.6909 + .attr('transform', 'translate(0,' + y1.range()[0] + ')'); 1.6910 + 1.6911 + y1Axis 1.6912 + .scale(y1) 1.6913 + ._ticks( nv.utils.calcTicksY(availableHeight1/36, data) ) 1.6914 + .tickSize(-availableWidth, 0); 1.6915 + y2Axis 1.6916 + .scale(y2) 1.6917 + ._ticks( nv.utils.calcTicksY(availableHeight1/36, data) ) 1.6918 + .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none 1.6919 + 1.6920 + g.select('.nv-focus .nv-y1.nv-axis') 1.6921 + .style('opacity', dataBars.length ? 1 : 0); 1.6922 + g.select('.nv-focus .nv-y2.nv-axis') 1.6923 + .style('opacity', dataLines.length && !dataLines[0].disabled ? 1 : 0) 1.6924 + .attr('transform', 'translate(' + x.range()[1] + ',0)'); 1.6925 + 1.6926 + g.select('.nv-focus .nv-y1.nv-axis').transition().duration(transitionDuration) 1.6927 + .call(y1Axis); 1.6928 + g.select('.nv-focus .nv-y2.nv-axis').transition().duration(transitionDuration) 1.6929 + .call(y2Axis); 1.6930 + } 1.6931 + 1.6932 + onBrush(); 1.6933 + 1.6934 + }); 1.6935 + 1.6936 + return chart; 1.6937 + } 1.6938 + 1.6939 + //============================================================ 1.6940 + // Event Handling/Dispatching (out of chart's scope) 1.6941 + //------------------------------------------------------------ 1.6942 + 1.6943 + lines.dispatch.on('elementMouseover.tooltip', function(evt) { 1.6944 + tooltip 1.6945 + .duration(100) 1.6946 + .valueFormatter(function(d, i) { 1.6947 + return y2Axis.tickFormat()(d, i); 1.6948 + }) 1.6949 + .data(evt) 1.6950 + .position(evt.pos) 1.6951 + .hidden(false); 1.6952 + }); 1.6953 + 1.6954 + lines.dispatch.on('elementMouseout.tooltip', function(evt) { 1.6955 + tooltip.hidden(true) 1.6956 + }); 1.6957 + 1.6958 + bars.dispatch.on('elementMouseover.tooltip', function(evt) { 1.6959 + evt.value = chart.x()(evt.data); 1.6960 + evt['series'] = { 1.6961 + value: chart.y()(evt.data), 1.6962 + color: evt.color 1.6963 + }; 1.6964 + tooltip 1.6965 + .duration(0) 1.6966 + .valueFormatter(function(d, i) { 1.6967 + return y1Axis.tickFormat()(d, i); 1.6968 + }) 1.6969 + .data(evt) 1.6970 + .hidden(false); 1.6971 + }); 1.6972 + 1.6973 + bars.dispatch.on('elementMouseout.tooltip', function(evt) { 1.6974 + tooltip.hidden(true); 1.6975 + }); 1.6976 + 1.6977 + bars.dispatch.on('elementMousemove.tooltip', function(evt) { 1.6978 + tooltip.position({top: d3.event.pageY, left: d3.event.pageX})(); 1.6979 + }); 1.6980 + 1.6981 + //============================================================ 1.6982 + 1.6983 + 1.6984 + //============================================================ 1.6985 + // Expose Public Variables 1.6986 + //------------------------------------------------------------ 1.6987 + 1.6988 + // expose chart's sub-components 1.6989 + chart.dispatch = dispatch; 1.6990 + chart.legend = legend; 1.6991 + chart.lines = lines; 1.6992 + chart.lines2 = lines2; 1.6993 + chart.bars = bars; 1.6994 + chart.bars2 = bars2; 1.6995 + chart.xAxis = xAxis; 1.6996 + chart.x2Axis = x2Axis; 1.6997 + chart.y1Axis = y1Axis; 1.6998 + chart.y2Axis = y2Axis; 1.6999 + chart.y3Axis = y3Axis; 1.7000 + chart.y4Axis = y4Axis; 1.7001 + chart.tooltip = tooltip; 1.7002 + 1.7003 + chart.options = nv.utils.optionsFunc.bind(chart); 1.7004 + 1.7005 + chart._options = Object.create({}, { 1.7006 + // simple options, just get/set the necessary values 1.7007 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.7008 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.7009 + showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, 1.7010 + brushExtent: {get: function(){return brushExtent;}, set: function(_){brushExtent=_;}}, 1.7011 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.7012 + focusEnable: {get: function(){return focusEnable;}, set: function(_){focusEnable=_;}}, 1.7013 + focusHeight: {get: function(){return focusHeight;}, set: function(_){focusHeight=_;}}, 1.7014 + focusShowAxisX: {get: function(){return focusShowAxisX;}, set: function(_){focusShowAxisX=_;}}, 1.7015 + focusShowAxisY: {get: function(){return focusShowAxisY;}, set: function(_){focusShowAxisY=_;}}, 1.7016 + legendLeftAxisHint: {get: function(){return legendLeftAxisHint;}, set: function(_){legendLeftAxisHint=_;}}, 1.7017 + legendRightAxisHint: {get: function(){return legendRightAxisHint;}, set: function(_){legendRightAxisHint=_;}}, 1.7018 + 1.7019 + // deprecated options 1.7020 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.7021 + // deprecated after 1.7.1 1.7022 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.7023 + tooltip.enabled(!!_); 1.7024 + }}, 1.7025 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.7026 + // deprecated after 1.7.1 1.7027 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.7028 + tooltip.contentGenerator(_); 1.7029 + }}, 1.7030 + 1.7031 + // options that require extra logic in the setter 1.7032 + margin: {get: function(){return margin;}, set: function(_){ 1.7033 + margin.top = _.top !== undefined ? _.top : margin.top; 1.7034 + margin.right = _.right !== undefined ? _.right : margin.right; 1.7035 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.7036 + margin.left = _.left !== undefined ? _.left : margin.left; 1.7037 + }}, 1.7038 + duration: {get: function(){return transitionDuration;}, set: function(_){ 1.7039 + transitionDuration = _; 1.7040 + }}, 1.7041 + color: {get: function(){return color;}, set: function(_){ 1.7042 + color = nv.utils.getColor(_); 1.7043 + legend.color(color); 1.7044 + }}, 1.7045 + x: {get: function(){return getX;}, set: function(_){ 1.7046 + getX = _; 1.7047 + lines.x(_); 1.7048 + lines2.x(_); 1.7049 + bars.x(_); 1.7050 + bars2.x(_); 1.7051 + }}, 1.7052 + y: {get: function(){return getY;}, set: function(_){ 1.7053 + getY = _; 1.7054 + lines.y(_); 1.7055 + lines2.y(_); 1.7056 + bars.y(_); 1.7057 + bars2.y(_); 1.7058 + }} 1.7059 + }); 1.7060 + 1.7061 + nv.utils.inheritOptions(chart, lines); 1.7062 + nv.utils.initOptions(chart); 1.7063 + 1.7064 + return chart; 1.7065 +}; 1.7066 +nv.models.lineWithFocusChart = function() { 1.7067 + "use strict"; 1.7068 + 1.7069 + //============================================================ 1.7070 + // Public Variables with Default Settings 1.7071 + //------------------------------------------------------------ 1.7072 + 1.7073 + var lines = nv.models.line() 1.7074 + , lines2 = nv.models.line() 1.7075 + , xAxis = nv.models.axis() 1.7076 + , yAxis = nv.models.axis() 1.7077 + , x2Axis = nv.models.axis() 1.7078 + , y2Axis = nv.models.axis() 1.7079 + , legend = nv.models.legend() 1.7080 + , brush = d3.svg.brush() 1.7081 + , tooltip = nv.models.tooltip() 1.7082 + , interactiveLayer = nv.interactiveGuideline() 1.7083 + ; 1.7084 + 1.7085 + var margin = {top: 30, right: 30, bottom: 30, left: 60} 1.7086 + , margin2 = {top: 0, right: 30, bottom: 20, left: 60} 1.7087 + , color = nv.utils.defaultColor() 1.7088 + , width = null 1.7089 + , height = null 1.7090 + , height2 = 50 1.7091 + , useInteractiveGuideline = false 1.7092 + , x 1.7093 + , y 1.7094 + , x2 1.7095 + , y2 1.7096 + , showLegend = true 1.7097 + , brushExtent = null 1.7098 + , noData = null 1.7099 + , dispatch = d3.dispatch('brush', 'stateChange', 'changeState') 1.7100 + , transitionDuration = 250 1.7101 + , state = nv.utils.state() 1.7102 + , defaultState = null 1.7103 + ; 1.7104 + 1.7105 + lines.clipEdge(true).duration(0); 1.7106 + lines2.interactive(false); 1.7107 + xAxis.orient('bottom').tickPadding(5); 1.7108 + yAxis.orient('left'); 1.7109 + x2Axis.orient('bottom').tickPadding(5); 1.7110 + y2Axis.orient('left'); 1.7111 + 1.7112 + tooltip.valueFormatter(function(d, i) { 1.7113 + return yAxis.tickFormat()(d, i); 1.7114 + }).headerFormatter(function(d, i) { 1.7115 + return xAxis.tickFormat()(d, i); 1.7116 + }); 1.7117 + 1.7118 + //============================================================ 1.7119 + // Private Variables 1.7120 + //------------------------------------------------------------ 1.7121 + 1.7122 + var stateGetter = function(data) { 1.7123 + return function(){ 1.7124 + return { 1.7125 + active: data.map(function(d) { return !d.disabled }) 1.7126 + }; 1.7127 + } 1.7128 + }; 1.7129 + 1.7130 + var stateSetter = function(data) { 1.7131 + return function(state) { 1.7132 + if (state.active !== undefined) 1.7133 + data.forEach(function(series,i) { 1.7134 + series.disabled = !state.active[i]; 1.7135 + }); 1.7136 + } 1.7137 + }; 1.7138 + 1.7139 + function chart(selection) { 1.7140 + selection.each(function(data) { 1.7141 + var container = d3.select(this), 1.7142 + that = this; 1.7143 + nv.utils.initSVG(container); 1.7144 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.7145 + availableHeight1 = nv.utils.availableHeight(height, container, margin) - height2, 1.7146 + availableHeight2 = height2 - margin2.top - margin2.bottom; 1.7147 + 1.7148 + chart.update = function() { container.transition().duration(transitionDuration).call(chart) }; 1.7149 + chart.container = this; 1.7150 + 1.7151 + state 1.7152 + .setter(stateSetter(data), chart.update) 1.7153 + .getter(stateGetter(data)) 1.7154 + .update(); 1.7155 + 1.7156 + // DEPRECATED set state.disableddisabled 1.7157 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.7158 + 1.7159 + if (!defaultState) { 1.7160 + var key; 1.7161 + defaultState = {}; 1.7162 + for (key in state) { 1.7163 + if (state[key] instanceof Array) 1.7164 + defaultState[key] = state[key].slice(0); 1.7165 + else 1.7166 + defaultState[key] = state[key]; 1.7167 + } 1.7168 + } 1.7169 + 1.7170 + // Display No Data message if there's nothing to show. 1.7171 + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { 1.7172 + nv.utils.noData(chart, container) 1.7173 + return chart; 1.7174 + } else { 1.7175 + container.selectAll('.nv-noData').remove(); 1.7176 + } 1.7177 + 1.7178 + // Setup Scales 1.7179 + x = lines.xScale(); 1.7180 + y = lines.yScale(); 1.7181 + x2 = lines2.xScale(); 1.7182 + y2 = lines2.yScale(); 1.7183 + 1.7184 + // Setup containers and skeleton of chart 1.7185 + var wrap = container.selectAll('g.nv-wrap.nv-lineWithFocusChart').data([data]); 1.7186 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineWithFocusChart').append('g'); 1.7187 + var g = wrap.select('g'); 1.7188 + 1.7189 + gEnter.append('g').attr('class', 'nv-legendWrap'); 1.7190 + 1.7191 + var focusEnter = gEnter.append('g').attr('class', 'nv-focus'); 1.7192 + focusEnter.append('g').attr('class', 'nv-x nv-axis'); 1.7193 + focusEnter.append('g').attr('class', 'nv-y nv-axis'); 1.7194 + focusEnter.append('g').attr('class', 'nv-linesWrap'); 1.7195 + focusEnter.append('g').attr('class', 'nv-interactive'); 1.7196 + 1.7197 + var contextEnter = gEnter.append('g').attr('class', 'nv-context'); 1.7198 + contextEnter.append('g').attr('class', 'nv-x nv-axis'); 1.7199 + contextEnter.append('g').attr('class', 'nv-y nv-axis'); 1.7200 + contextEnter.append('g').attr('class', 'nv-linesWrap'); 1.7201 + contextEnter.append('g').attr('class', 'nv-brushBackground'); 1.7202 + contextEnter.append('g').attr('class', 'nv-x nv-brush'); 1.7203 + 1.7204 + // Legend 1.7205 + if (showLegend) { 1.7206 + legend.width(availableWidth); 1.7207 + 1.7208 + g.select('.nv-legendWrap') 1.7209 + .datum(data) 1.7210 + .call(legend); 1.7211 + 1.7212 + if ( margin.top != legend.height()) { 1.7213 + margin.top = legend.height(); 1.7214 + availableHeight1 = nv.utils.availableHeight(height, container, margin) - height2; 1.7215 + } 1.7216 + 1.7217 + g.select('.nv-legendWrap') 1.7218 + .attr('transform', 'translate(0,' + (-margin.top) +')') 1.7219 + } 1.7220 + 1.7221 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.7222 + 1.7223 + 1.7224 + //Set up interactive layer 1.7225 + if (useInteractiveGuideline) { 1.7226 + interactiveLayer 1.7227 + .width(availableWidth) 1.7228 + .height(availableHeight1) 1.7229 + .margin({left:margin.left, top:margin.top}) 1.7230 + .svgContainer(container) 1.7231 + .xScale(x); 1.7232 + wrap.select(".nv-interactive").call(interactiveLayer); 1.7233 + } 1.7234 + 1.7235 + // Main Chart Component(s) 1.7236 + lines 1.7237 + .width(availableWidth) 1.7238 + .height(availableHeight1) 1.7239 + .color( 1.7240 + data 1.7241 + .map(function(d,i) { 1.7242 + return d.color || color(d, i); 1.7243 + }) 1.7244 + .filter(function(d,i) { 1.7245 + return !data[i].disabled; 1.7246 + }) 1.7247 + ); 1.7248 + 1.7249 + lines2 1.7250 + .defined(lines.defined()) 1.7251 + .width(availableWidth) 1.7252 + .height(availableHeight2) 1.7253 + .color( 1.7254 + data 1.7255 + .map(function(d,i) { 1.7256 + return d.color || color(d, i); 1.7257 + }) 1.7258 + .filter(function(d,i) { 1.7259 + return !data[i].disabled; 1.7260 + }) 1.7261 + ); 1.7262 + 1.7263 + g.select('.nv-context') 1.7264 + .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')') 1.7265 + 1.7266 + var contextLinesWrap = g.select('.nv-context .nv-linesWrap') 1.7267 + .datum(data.filter(function(d) { return !d.disabled })) 1.7268 + 1.7269 + d3.transition(contextLinesWrap).call(lines2); 1.7270 + 1.7271 + // Setup Main (Focus) Axes 1.7272 + xAxis 1.7273 + .scale(x) 1.7274 + ._ticks( nv.utils.calcTicksX(availableWidth/100, data) ) 1.7275 + .tickSize(-availableHeight1, 0); 1.7276 + 1.7277 + yAxis 1.7278 + .scale(y) 1.7279 + ._ticks( nv.utils.calcTicksY(availableHeight1/36, data) ) 1.7280 + .tickSize( -availableWidth, 0); 1.7281 + 1.7282 + g.select('.nv-focus .nv-x.nv-axis') 1.7283 + .attr('transform', 'translate(0,' + availableHeight1 + ')'); 1.7284 + 1.7285 + // Setup Brush 1.7286 + brush 1.7287 + .x(x2) 1.7288 + .on('brush', function() { 1.7289 + onBrush(); 1.7290 + }); 1.7291 + 1.7292 + if (brushExtent) brush.extent(brushExtent); 1.7293 + 1.7294 + var brushBG = g.select('.nv-brushBackground').selectAll('g') 1.7295 + .data([brushExtent || brush.extent()]) 1.7296 + 1.7297 + var brushBGenter = brushBG.enter() 1.7298 + .append('g'); 1.7299 + 1.7300 + brushBGenter.append('rect') 1.7301 + .attr('class', 'left') 1.7302 + .attr('x', 0) 1.7303 + .attr('y', 0) 1.7304 + .attr('height', availableHeight2); 1.7305 + 1.7306 + brushBGenter.append('rect') 1.7307 + .attr('class', 'right') 1.7308 + .attr('x', 0) 1.7309 + .attr('y', 0) 1.7310 + .attr('height', availableHeight2); 1.7311 + 1.7312 + var gBrush = g.select('.nv-x.nv-brush') 1.7313 + .call(brush); 1.7314 + gBrush.selectAll('rect') 1.7315 + .attr('height', availableHeight2); 1.7316 + gBrush.selectAll('.resize').append('path').attr('d', resizePath); 1.7317 + 1.7318 + onBrush(); 1.7319 + 1.7320 + // Setup Secondary (Context) Axes 1.7321 + x2Axis 1.7322 + .scale(x2) 1.7323 + ._ticks( nv.utils.calcTicksX(availableWidth/100, data) ) 1.7324 + .tickSize(-availableHeight2, 0); 1.7325 + 1.7326 + g.select('.nv-context .nv-x.nv-axis') 1.7327 + .attr('transform', 'translate(0,' + y2.range()[0] + ')'); 1.7328 + d3.transition(g.select('.nv-context .nv-x.nv-axis')) 1.7329 + .call(x2Axis); 1.7330 + 1.7331 + y2Axis 1.7332 + .scale(y2) 1.7333 + ._ticks( nv.utils.calcTicksY(availableHeight2/36, data) ) 1.7334 + .tickSize( -availableWidth, 0); 1.7335 + 1.7336 + d3.transition(g.select('.nv-context .nv-y.nv-axis')) 1.7337 + .call(y2Axis); 1.7338 + 1.7339 + g.select('.nv-context .nv-x.nv-axis') 1.7340 + .attr('transform', 'translate(0,' + y2.range()[0] + ')'); 1.7341 + 1.7342 + //============================================================ 1.7343 + // Event Handling/Dispatching (in chart's scope) 1.7344 + //------------------------------------------------------------ 1.7345 + 1.7346 + legend.dispatch.on('stateChange', function(newState) { 1.7347 + for (var key in newState) 1.7348 + state[key] = newState[key]; 1.7349 + dispatch.stateChange(state); 1.7350 + chart.update(); 1.7351 + }); 1.7352 + 1.7353 + interactiveLayer.dispatch.on('elementMousemove', function(e) { 1.7354 + lines.clearHighlights(); 1.7355 + var singlePoint, pointIndex, pointXLocation, allData = []; 1.7356 + data 1.7357 + .filter(function(series, i) { 1.7358 + series.seriesIndex = i; 1.7359 + return !series.disabled; 1.7360 + }) 1.7361 + .forEach(function(series,i) { 1.7362 + var extent = brush.empty() ? x2.domain() : brush.extent(); 1.7363 + var currentValues = series.values.filter(function(d,i) { 1.7364 + return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1]; 1.7365 + }); 1.7366 + 1.7367 + pointIndex = nv.interactiveBisect(currentValues, e.pointXValue, lines.x()); 1.7368 + var point = currentValues[pointIndex]; 1.7369 + var pointYValue = chart.y()(point, pointIndex); 1.7370 + if (pointYValue != null) { 1.7371 + lines.highlightPoint(i, pointIndex, true); 1.7372 + } 1.7373 + if (point === undefined) return; 1.7374 + if (singlePoint === undefined) singlePoint = point; 1.7375 + if (pointXLocation === undefined) pointXLocation = chart.xScale()(chart.x()(point,pointIndex)); 1.7376 + allData.push({ 1.7377 + key: series.key, 1.7378 + value: chart.y()(point, pointIndex), 1.7379 + color: color(series,series.seriesIndex) 1.7380 + }); 1.7381 + }); 1.7382 + //Highlight the tooltip entry based on which point the mouse is closest to. 1.7383 + if (allData.length > 2) { 1.7384 + var yValue = chart.yScale().invert(e.mouseY); 1.7385 + var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]); 1.7386 + var threshold = 0.03 * domainExtent; 1.7387 + var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value}),yValue,threshold); 1.7388 + if (indexToHighlight !== null) 1.7389 + allData[indexToHighlight].highlight = true; 1.7390 + } 1.7391 + 1.7392 + var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex)); 1.7393 + interactiveLayer.tooltip 1.7394 + .position({left: e.mouseX + margin.left, top: e.mouseY + margin.top}) 1.7395 + .chartContainer(that.parentNode) 1.7396 + .valueFormatter(function(d,i) { 1.7397 + return d == null ? "N/A" : yAxis.tickFormat()(d); 1.7398 + }) 1.7399 + .data({ 1.7400 + value: xValue, 1.7401 + index: pointIndex, 1.7402 + series: allData 1.7403 + })(); 1.7404 + 1.7405 + interactiveLayer.renderGuideLine(pointXLocation); 1.7406 + 1.7407 + }); 1.7408 + 1.7409 + interactiveLayer.dispatch.on("elementMouseout",function(e) { 1.7410 + lines.clearHighlights(); 1.7411 + }); 1.7412 + 1.7413 + dispatch.on('changeState', function(e) { 1.7414 + if (typeof e.disabled !== 'undefined') { 1.7415 + data.forEach(function(series,i) { 1.7416 + series.disabled = e.disabled[i]; 1.7417 + }); 1.7418 + } 1.7419 + chart.update(); 1.7420 + }); 1.7421 + 1.7422 + //============================================================ 1.7423 + // Functions 1.7424 + //------------------------------------------------------------ 1.7425 + 1.7426 + // Taken from crossfilter (http://square.github.com/crossfilter/) 1.7427 + function resizePath(d) { 1.7428 + var e = +(d == 'e'), 1.7429 + x = e ? 1 : -1, 1.7430 + y = availableHeight2 / 3; 1.7431 + return 'M' + (.5 * x) + ',' + y 1.7432 + + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6) 1.7433 + + 'V' + (2 * y - 6) 1.7434 + + 'A6,6 0 0 ' + e + ' ' + (.5 * x) + ',' + (2 * y) 1.7435 + + 'Z' 1.7436 + + 'M' + (2.5 * x) + ',' + (y + 8) 1.7437 + + 'V' + (2 * y - 8) 1.7438 + + 'M' + (4.5 * x) + ',' + (y + 8) 1.7439 + + 'V' + (2 * y - 8); 1.7440 + } 1.7441 + 1.7442 + 1.7443 + function updateBrushBG() { 1.7444 + if (!brush.empty()) brush.extent(brushExtent); 1.7445 + brushBG 1.7446 + .data([brush.empty() ? x2.domain() : brushExtent]) 1.7447 + .each(function(d,i) { 1.7448 + var leftWidth = x2(d[0]) - x.range()[0], 1.7449 + rightWidth = availableWidth - x2(d[1]); 1.7450 + d3.select(this).select('.left') 1.7451 + .attr('width', leftWidth < 0 ? 0 : leftWidth); 1.7452 + 1.7453 + d3.select(this).select('.right') 1.7454 + .attr('x', x2(d[1])) 1.7455 + .attr('width', rightWidth < 0 ? 0 : rightWidth); 1.7456 + }); 1.7457 + } 1.7458 + 1.7459 + 1.7460 + function onBrush() { 1.7461 + brushExtent = brush.empty() ? null : brush.extent(); 1.7462 + var extent = brush.empty() ? x2.domain() : brush.extent(); 1.7463 + 1.7464 + //The brush extent cannot be less than one. If it is, don't update the line chart. 1.7465 + if (Math.abs(extent[0] - extent[1]) <= 1) { 1.7466 + return; 1.7467 + } 1.7468 + 1.7469 + dispatch.brush({extent: extent, brush: brush}); 1.7470 + 1.7471 + 1.7472 + updateBrushBG(); 1.7473 + 1.7474 + // Update Main (Focus) 1.7475 + var focusLinesWrap = g.select('.nv-focus .nv-linesWrap') 1.7476 + .datum( 1.7477 + data 1.7478 + .filter(function(d) { return !d.disabled }) 1.7479 + .map(function(d,i) { 1.7480 + return { 1.7481 + key: d.key, 1.7482 + area: d.area, 1.7483 + values: d.values.filter(function(d,i) { 1.7484 + return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1]; 1.7485 + }) 1.7486 + } 1.7487 + }) 1.7488 + ); 1.7489 + focusLinesWrap.transition().duration(transitionDuration).call(lines); 1.7490 + 1.7491 + 1.7492 + // Update Main (Focus) Axes 1.7493 + g.select('.nv-focus .nv-x.nv-axis').transition().duration(transitionDuration) 1.7494 + .call(xAxis); 1.7495 + g.select('.nv-focus .nv-y.nv-axis').transition().duration(transitionDuration) 1.7496 + .call(yAxis); 1.7497 + } 1.7498 + }); 1.7499 + 1.7500 + return chart; 1.7501 + } 1.7502 + 1.7503 + //============================================================ 1.7504 + // Event Handling/Dispatching (out of chart's scope) 1.7505 + //------------------------------------------------------------ 1.7506 + 1.7507 + lines.dispatch.on('elementMouseover.tooltip', function(evt) { 1.7508 + tooltip.data(evt).position(evt.pos).hidden(false); 1.7509 + }); 1.7510 + 1.7511 + lines.dispatch.on('elementMouseout.tooltip', function(evt) { 1.7512 + tooltip.hidden(true) 1.7513 + }); 1.7514 + 1.7515 + //============================================================ 1.7516 + // Expose Public Variables 1.7517 + //------------------------------------------------------------ 1.7518 + 1.7519 + // expose chart's sub-components 1.7520 + chart.dispatch = dispatch; 1.7521 + chart.legend = legend; 1.7522 + chart.lines = lines; 1.7523 + chart.lines2 = lines2; 1.7524 + chart.xAxis = xAxis; 1.7525 + chart.yAxis = yAxis; 1.7526 + chart.x2Axis = x2Axis; 1.7527 + chart.y2Axis = y2Axis; 1.7528 + chart.interactiveLayer = interactiveLayer; 1.7529 + chart.tooltip = tooltip; 1.7530 + 1.7531 + chart.options = nv.utils.optionsFunc.bind(chart); 1.7532 + 1.7533 + chart._options = Object.create({}, { 1.7534 + // simple options, just get/set the necessary values 1.7535 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.7536 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.7537 + focusHeight: {get: function(){return height2;}, set: function(_){height2=_;}}, 1.7538 + showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, 1.7539 + brushExtent: {get: function(){return brushExtent;}, set: function(_){brushExtent=_;}}, 1.7540 + defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}}, 1.7541 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.7542 + 1.7543 + // deprecated options 1.7544 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.7545 + // deprecated after 1.7.1 1.7546 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.7547 + tooltip.enabled(!!_); 1.7548 + }}, 1.7549 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.7550 + // deprecated after 1.7.1 1.7551 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.7552 + tooltip.contentGenerator(_); 1.7553 + }}, 1.7554 + 1.7555 + // options that require extra logic in the setter 1.7556 + margin: {get: function(){return margin;}, set: function(_){ 1.7557 + margin.top = _.top !== undefined ? _.top : margin.top; 1.7558 + margin.right = _.right !== undefined ? _.right : margin.right; 1.7559 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.7560 + margin.left = _.left !== undefined ? _.left : margin.left; 1.7561 + }}, 1.7562 + color: {get: function(){return color;}, set: function(_){ 1.7563 + color = nv.utils.getColor(_); 1.7564 + legend.color(color); 1.7565 + // line color is handled above? 1.7566 + }}, 1.7567 + interpolate: {get: function(){return lines.interpolate();}, set: function(_){ 1.7568 + lines.interpolate(_); 1.7569 + lines2.interpolate(_); 1.7570 + }}, 1.7571 + xTickFormat: {get: function(){return xAxis.tickFormat();}, set: function(_){ 1.7572 + xAxis.tickFormat(_); 1.7573 + x2Axis.tickFormat(_); 1.7574 + }}, 1.7575 + yTickFormat: {get: function(){return yAxis.tickFormat();}, set: function(_){ 1.7576 + yAxis.tickFormat(_); 1.7577 + y2Axis.tickFormat(_); 1.7578 + }}, 1.7579 + duration: {get: function(){return transitionDuration;}, set: function(_){ 1.7580 + transitionDuration=_; 1.7581 + yAxis.duration(transitionDuration); 1.7582 + y2Axis.duration(transitionDuration); 1.7583 + xAxis.duration(transitionDuration); 1.7584 + x2Axis.duration(transitionDuration); 1.7585 + }}, 1.7586 + x: {get: function(){return lines.x();}, set: function(_){ 1.7587 + lines.x(_); 1.7588 + lines2.x(_); 1.7589 + }}, 1.7590 + y: {get: function(){return lines.y();}, set: function(_){ 1.7591 + lines.y(_); 1.7592 + lines2.y(_); 1.7593 + }}, 1.7594 + useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){ 1.7595 + useInteractiveGuideline = _; 1.7596 + if (useInteractiveGuideline) { 1.7597 + lines.interactive(false); 1.7598 + lines.useVoronoi(false); 1.7599 + } 1.7600 + }} 1.7601 + }); 1.7602 + 1.7603 + nv.utils.inheritOptions(chart, lines); 1.7604 + nv.utils.initOptions(chart); 1.7605 + 1.7606 + return chart; 1.7607 +}; 1.7608 + 1.7609 +nv.models.multiBar = function() { 1.7610 + "use strict"; 1.7611 + 1.7612 + //============================================================ 1.7613 + // Public Variables with Default Settings 1.7614 + //------------------------------------------------------------ 1.7615 + 1.7616 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.7617 + , width = 960 1.7618 + , height = 500 1.7619 + , x = d3.scale.ordinal() 1.7620 + , y = d3.scale.linear() 1.7621 + , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one 1.7622 + , container = null 1.7623 + , getX = function(d) { return d.x } 1.7624 + , getY = function(d) { return d.y } 1.7625 + , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove 1.7626 + , clipEdge = true 1.7627 + , stacked = false 1.7628 + , stackOffset = 'zero' // options include 'silhouette', 'wiggle', 'expand', 'zero', or a custom function 1.7629 + , color = nv.utils.defaultColor() 1.7630 + , hideable = false 1.7631 + , barColor = null // adding the ability to set the color for each rather than the whole group 1.7632 + , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled 1.7633 + , duration = 500 1.7634 + , xDomain 1.7635 + , yDomain 1.7636 + , xRange 1.7637 + , yRange 1.7638 + , groupSpacing = 0.1 1.7639 + , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd') 1.7640 + ; 1.7641 + 1.7642 + //============================================================ 1.7643 + // Private Variables 1.7644 + //------------------------------------------------------------ 1.7645 + 1.7646 + var x0, y0 //used to store previous scales 1.7647 + , renderWatch = nv.utils.renderWatch(dispatch, duration) 1.7648 + ; 1.7649 + 1.7650 + var last_datalength = 0; 1.7651 + 1.7652 + function chart(selection) { 1.7653 + renderWatch.reset(); 1.7654 + selection.each(function(data) { 1.7655 + var availableWidth = width - margin.left - margin.right, 1.7656 + availableHeight = height - margin.top - margin.bottom; 1.7657 + 1.7658 + container = d3.select(this); 1.7659 + nv.utils.initSVG(container); 1.7660 + var nonStackableCount = 0; 1.7661 + // This function defines the requirements for render complete 1.7662 + var endFn = function(d, i) { 1.7663 + if (d.series === data.length - 1 && i === data[0].values.length - 1) 1.7664 + return true; 1.7665 + return false; 1.7666 + }; 1.7667 + 1.7668 + if(hideable && data.length) hideable = [{ 1.7669 + values: data[0].values.map(function(d) { 1.7670 + return { 1.7671 + x: d.x, 1.7672 + y: 0, 1.7673 + series: d.series, 1.7674 + size: 0.01 1.7675 + };} 1.7676 + )}]; 1.7677 + 1.7678 + if (stacked) { 1.7679 + var parsed = d3.layout.stack() 1.7680 + .offset(stackOffset) 1.7681 + .values(function(d){ return d.values }) 1.7682 + .y(getY) 1.7683 + (!data.length && hideable ? hideable : data); 1.7684 + 1.7685 + parsed.forEach(function(series, i){ 1.7686 + // if series is non-stackable, use un-parsed data 1.7687 + if (series.nonStackable) { 1.7688 + data[i].nonStackableSeries = nonStackableCount++; 1.7689 + parsed[i] = data[i]; 1.7690 + } else { 1.7691 + // don't stack this seires on top of the nonStackable seriees 1.7692 + if (i > 0 && parsed[i - 1].nonStackable){ 1.7693 + parsed[i].values.map(function(d,j){ 1.7694 + d.y0 -= parsed[i - 1].values[j].y; 1.7695 + d.y1 = d.y0 + d.y; 1.7696 + }); 1.7697 + } 1.7698 + } 1.7699 + }); 1.7700 + data = parsed; 1.7701 + } 1.7702 + //add series index and key to each data point for reference 1.7703 + data.forEach(function(series, i) { 1.7704 + series.values.forEach(function(point) { 1.7705 + point.series = i; 1.7706 + point.key = series.key; 1.7707 + }); 1.7708 + }); 1.7709 + 1.7710 + // HACK for negative value stacking 1.7711 + if (stacked) { 1.7712 + data[0].values.map(function(d,i) { 1.7713 + var posBase = 0, negBase = 0; 1.7714 + data.map(function(d, idx) { 1.7715 + if (!data[idx].nonStackable) { 1.7716 + var f = d.values[i] 1.7717 + f.size = Math.abs(f.y); 1.7718 + if (f.y<0) { 1.7719 + f.y1 = negBase; 1.7720 + negBase = negBase - f.size; 1.7721 + } else 1.7722 + { 1.7723 + f.y1 = f.size + posBase; 1.7724 + posBase = posBase + f.size; 1.7725 + } 1.7726 + } 1.7727 + 1.7728 + }); 1.7729 + }); 1.7730 + } 1.7731 + // Setup Scales 1.7732 + // remap and flatten the data for use in calculating the scales' domains 1.7733 + var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate 1.7734 + data.map(function(d, idx) { 1.7735 + return d.values.map(function(d,i) { 1.7736 + return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1, idx:idx } 1.7737 + }) 1.7738 + }); 1.7739 + 1.7740 + x.domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x })) 1.7741 + .rangeBands(xRange || [0, availableWidth], groupSpacing); 1.7742 + 1.7743 + y.domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { 1.7744 + var domain = d.y; 1.7745 + // increase the domain range if this series is stackable 1.7746 + if (stacked && !data[d.idx].nonStackable) { 1.7747 + if (d.y > 0){ 1.7748 + domain = d.y1 1.7749 + } else { 1.7750 + domain = d.y1 + d.y 1.7751 + } 1.7752 + } 1.7753 + return domain; 1.7754 + }).concat(forceY))) 1.7755 + .range(yRange || [availableHeight, 0]); 1.7756 + 1.7757 + // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point 1.7758 + if (x.domain()[0] === x.domain()[1]) 1.7759 + x.domain()[0] ? 1.7760 + x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01]) 1.7761 + : x.domain([-1,1]); 1.7762 + 1.7763 + if (y.domain()[0] === y.domain()[1]) 1.7764 + y.domain()[0] ? 1.7765 + y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01]) 1.7766 + : y.domain([-1,1]); 1.7767 + 1.7768 + x0 = x0 || x; 1.7769 + y0 = y0 || y; 1.7770 + 1.7771 + // Setup containers and skeleton of chart 1.7772 + var wrap = container.selectAll('g.nv-wrap.nv-multibar').data([data]); 1.7773 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibar'); 1.7774 + var defsEnter = wrapEnter.append('defs'); 1.7775 + var gEnter = wrapEnter.append('g'); 1.7776 + var g = wrap.select('g'); 1.7777 + 1.7778 + gEnter.append('g').attr('class', 'nv-groups'); 1.7779 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.7780 + 1.7781 + defsEnter.append('clipPath') 1.7782 + .attr('id', 'nv-edge-clip-' + id) 1.7783 + .append('rect'); 1.7784 + wrap.select('#nv-edge-clip-' + id + ' rect') 1.7785 + .attr('width', availableWidth) 1.7786 + .attr('height', availableHeight); 1.7787 + 1.7788 + g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : ''); 1.7789 + 1.7790 + var groups = wrap.select('.nv-groups').selectAll('.nv-group') 1.7791 + .data(function(d) { return d }, function(d,i) { return i }); 1.7792 + groups.enter().append('g') 1.7793 + .style('stroke-opacity', 1e-6) 1.7794 + .style('fill-opacity', 1e-6); 1.7795 + 1.7796 + var exitTransition = renderWatch 1.7797 + .transition(groups.exit().selectAll('rect.nv-bar'), 'multibarExit', Math.min(100, duration)) 1.7798 + .attr('y', function(d, i, j) { 1.7799 + var yVal = y0(0) || 0; 1.7800 + if (stacked) { 1.7801 + if (data[d.series] && !data[d.series].nonStackable) { 1.7802 + yVal = y0(d.y0); 1.7803 + } 1.7804 + } 1.7805 + return yVal; 1.7806 + }) 1.7807 + .attr('height', 0) 1.7808 + .remove(); 1.7809 + if (exitTransition.delay) 1.7810 + exitTransition.delay(function(d,i) { 1.7811 + var delay = i * (duration / (last_datalength + 1)) - i; 1.7812 + return delay; 1.7813 + }); 1.7814 + groups 1.7815 + .attr('class', function(d,i) { return 'nv-group nv-series-' + i }) 1.7816 + .classed('hover', function(d) { return d.hover }) 1.7817 + .style('fill', function(d,i){ return color(d, i) }) 1.7818 + .style('stroke', function(d,i){ return color(d, i) }); 1.7819 + groups 1.7820 + .style('stroke-opacity', 1) 1.7821 + .style('fill-opacity', 0.75); 1.7822 + 1.7823 + var bars = groups.selectAll('rect.nv-bar') 1.7824 + .data(function(d) { return (hideable && !data.length) ? hideable.values : d.values }); 1.7825 + bars.exit().remove(); 1.7826 + 1.7827 + var barsEnter = bars.enter().append('rect') 1.7828 + .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'}) 1.7829 + .attr('x', function(d,i,j) { 1.7830 + return stacked && !data[j].nonStackable ? 0 : (j * x.rangeBand() / data.length ) 1.7831 + }) 1.7832 + .attr('y', function(d,i,j) { return y0(stacked && !data[j].nonStackable ? d.y0 : 0) || 0 }) 1.7833 + .attr('height', 0) 1.7834 + .attr('width', function(d,i,j) { return x.rangeBand() / (stacked && !data[j].nonStackable ? 1 : data.length) }) 1.7835 + .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; }) 1.7836 + ; 1.7837 + bars 1.7838 + .style('fill', function(d,i,j){ return color(d, j, i); }) 1.7839 + .style('stroke', function(d,i,j){ return color(d, j, i); }) 1.7840 + .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here 1.7841 + d3.select(this).classed('hover', true); 1.7842 + dispatch.elementMouseover({ 1.7843 + data: d, 1.7844 + index: i, 1.7845 + color: d3.select(this).style("fill") 1.7846 + }); 1.7847 + }) 1.7848 + .on('mouseout', function(d,i) { 1.7849 + d3.select(this).classed('hover', false); 1.7850 + dispatch.elementMouseout({ 1.7851 + data: d, 1.7852 + index: i, 1.7853 + color: d3.select(this).style("fill") 1.7854 + }); 1.7855 + }) 1.7856 + .on('mousemove', function(d,i) { 1.7857 + dispatch.elementMousemove({ 1.7858 + data: d, 1.7859 + index: i, 1.7860 + color: d3.select(this).style("fill") 1.7861 + }); 1.7862 + }) 1.7863 + .on('click', function(d,i) { 1.7864 + dispatch.elementClick({ 1.7865 + data: d, 1.7866 + index: i, 1.7867 + color: d3.select(this).style("fill") 1.7868 + }); 1.7869 + d3.event.stopPropagation(); 1.7870 + }) 1.7871 + .on('dblclick', function(d,i) { 1.7872 + dispatch.elementDblClick({ 1.7873 + data: d, 1.7874 + index: i, 1.7875 + color: d3.select(this).style("fill") 1.7876 + }); 1.7877 + d3.event.stopPropagation(); 1.7878 + }); 1.7879 + bars 1.7880 + .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'}) 1.7881 + .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; }) 1.7882 + 1.7883 + if (barColor) { 1.7884 + if (!disabled) disabled = data.map(function() { return true }); 1.7885 + bars 1.7886 + .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(); }) 1.7887 + .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(); }); 1.7888 + } 1.7889 + 1.7890 + var barSelection = 1.7891 + bars.watchTransition(renderWatch, 'multibar', Math.min(250, duration)) 1.7892 + .delay(function(d,i) { 1.7893 + return i * duration / data[0].values.length; 1.7894 + }); 1.7895 + if (stacked){ 1.7896 + barSelection 1.7897 + .attr('y', function(d,i,j) { 1.7898 + var yVal = 0; 1.7899 + // if stackable, stack it on top of the previous series 1.7900 + if (!data[j].nonStackable) { 1.7901 + yVal = y(d.y1); 1.7902 + } else { 1.7903 + if (getY(d,i) < 0){ 1.7904 + yVal = y(0); 1.7905 + } else { 1.7906 + if (y(0) - y(getY(d,i)) < -1){ 1.7907 + yVal = y(0) - 1; 1.7908 + } else { 1.7909 + yVal = y(getY(d, i)) || 0; 1.7910 + } 1.7911 + } 1.7912 + } 1.7913 + return yVal; 1.7914 + }) 1.7915 + .attr('height', function(d,i,j) { 1.7916 + if (!data[j].nonStackable) { 1.7917 + return Math.max(Math.abs(y(d.y+d.y0) - y(d.y0)), 1); 1.7918 + } else { 1.7919 + return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) || 0; 1.7920 + } 1.7921 + }) 1.7922 + .attr('x', function(d,i,j) { 1.7923 + var width = 0; 1.7924 + if (data[j].nonStackable) { 1.7925 + width = d.series * x.rangeBand() / data.length; 1.7926 + if (data.length !== nonStackableCount){ 1.7927 + width = data[j].nonStackableSeries * x.rangeBand()/(nonStackableCount*2); 1.7928 + } 1.7929 + } 1.7930 + return width; 1.7931 + }) 1.7932 + .attr('width', function(d,i,j){ 1.7933 + if (!data[j].nonStackable) { 1.7934 + return x.rangeBand(); 1.7935 + } else { 1.7936 + // if all series are nonStacable, take the full width 1.7937 + var width = (x.rangeBand() / nonStackableCount); 1.7938 + // otherwise, nonStackable graph will be only taking the half-width 1.7939 + // of the x rangeBand 1.7940 + if (data.length !== nonStackableCount) { 1.7941 + width = x.rangeBand()/(nonStackableCount*2); 1.7942 + } 1.7943 + return width; 1.7944 + } 1.7945 + }); 1.7946 + } 1.7947 + else { 1.7948 + barSelection 1.7949 + .attr('x', function(d,i) { 1.7950 + return d.series * x.rangeBand() / data.length; 1.7951 + }) 1.7952 + .attr('width', x.rangeBand() / data.length) 1.7953 + .attr('y', function(d,i) { 1.7954 + return getY(d,i) < 0 ? 1.7955 + y(0) : 1.7956 + y(0) - y(getY(d,i)) < 1 ? 1.7957 + y(0) - 1 : 1.7958 + y(getY(d,i)) || 0; 1.7959 + }) 1.7960 + .attr('height', function(d,i) { 1.7961 + return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) || 0; 1.7962 + }); 1.7963 + } 1.7964 + 1.7965 + //store old scales for use in transitions on update 1.7966 + x0 = x.copy(); 1.7967 + y0 = y.copy(); 1.7968 + 1.7969 + // keep track of the last data value length for transition calculations 1.7970 + if (data[0] && data[0].values) { 1.7971 + last_datalength = data[0].values.length; 1.7972 + } 1.7973 + 1.7974 + }); 1.7975 + 1.7976 + renderWatch.renderEnd('multibar immediate'); 1.7977 + 1.7978 + return chart; 1.7979 + } 1.7980 + 1.7981 + //============================================================ 1.7982 + // Expose Public Variables 1.7983 + //------------------------------------------------------------ 1.7984 + 1.7985 + chart.dispatch = dispatch; 1.7986 + 1.7987 + chart.options = nv.utils.optionsFunc.bind(chart); 1.7988 + 1.7989 + chart._options = Object.create({}, { 1.7990 + // simple options, just get/set the necessary values 1.7991 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.7992 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.7993 + x: {get: function(){return getX;}, set: function(_){getX=_;}}, 1.7994 + y: {get: function(){return getY;}, set: function(_){getY=_;}}, 1.7995 + xScale: {get: function(){return x;}, set: function(_){x=_;}}, 1.7996 + yScale: {get: function(){return y;}, set: function(_){y=_;}}, 1.7997 + xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}}, 1.7998 + yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}}, 1.7999 + xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}}, 1.8000 + yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}}, 1.8001 + forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}}, 1.8002 + stacked: {get: function(){return stacked;}, set: function(_){stacked=_;}}, 1.8003 + stackOffset: {get: function(){return stackOffset;}, set: function(_){stackOffset=_;}}, 1.8004 + clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}}, 1.8005 + disabled: {get: function(){return disabled;}, set: function(_){disabled=_;}}, 1.8006 + id: {get: function(){return id;}, set: function(_){id=_;}}, 1.8007 + hideable: {get: function(){return hideable;}, set: function(_){hideable=_;}}, 1.8008 + groupSpacing:{get: function(){return groupSpacing;}, set: function(_){groupSpacing=_;}}, 1.8009 + 1.8010 + // options that require extra logic in the setter 1.8011 + margin: {get: function(){return margin;}, set: function(_){ 1.8012 + margin.top = _.top !== undefined ? _.top : margin.top; 1.8013 + margin.right = _.right !== undefined ? _.right : margin.right; 1.8014 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.8015 + margin.left = _.left !== undefined ? _.left : margin.left; 1.8016 + }}, 1.8017 + duration: {get: function(){return duration;}, set: function(_){ 1.8018 + duration = _; 1.8019 + renderWatch.reset(duration); 1.8020 + }}, 1.8021 + color: {get: function(){return color;}, set: function(_){ 1.8022 + color = nv.utils.getColor(_); 1.8023 + }}, 1.8024 + barColor: {get: function(){return barColor;}, set: function(_){ 1.8025 + barColor = _ ? nv.utils.getColor(_) : null; 1.8026 + }} 1.8027 + }); 1.8028 + 1.8029 + nv.utils.initOptions(chart); 1.8030 + 1.8031 + return chart; 1.8032 +}; 1.8033 +nv.models.multiBarChart = function() { 1.8034 + "use strict"; 1.8035 + 1.8036 + //============================================================ 1.8037 + // Public Variables with Default Settings 1.8038 + //------------------------------------------------------------ 1.8039 + 1.8040 + var multibar = nv.models.multiBar() 1.8041 + , xAxis = nv.models.axis() 1.8042 + , yAxis = nv.models.axis() 1.8043 + , legend = nv.models.legend() 1.8044 + , controls = nv.models.legend() 1.8045 + , tooltip = nv.models.tooltip() 1.8046 + ; 1.8047 + 1.8048 + var margin = {top: 30, right: 20, bottom: 50, left: 60} 1.8049 + , width = null 1.8050 + , height = null 1.8051 + , color = nv.utils.defaultColor() 1.8052 + , showControls = true 1.8053 + , controlLabels = {} 1.8054 + , showLegend = true 1.8055 + , showXAxis = true 1.8056 + , showYAxis = true 1.8057 + , rightAlignYAxis = false 1.8058 + , reduceXTicks = true // if false a tick will show for every data point 1.8059 + , staggerLabels = false 1.8060 + , rotateLabels = 0 1.8061 + , x //can be accessed via chart.xScale() 1.8062 + , y //can be accessed via chart.yScale() 1.8063 + , state = nv.utils.state() 1.8064 + , defaultState = null 1.8065 + , noData = null 1.8066 + , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd') 1.8067 + , controlWidth = function() { return showControls ? 180 : 0 } 1.8068 + , duration = 250 1.8069 + ; 1.8070 + 1.8071 + state.stacked = false // DEPRECATED Maintained for backward compatibility 1.8072 + 1.8073 + multibar.stacked(false); 1.8074 + xAxis 1.8075 + .orient('bottom') 1.8076 + .tickPadding(7) 1.8077 + .showMaxMin(false) 1.8078 + .tickFormat(function(d) { return d }) 1.8079 + ; 1.8080 + yAxis 1.8081 + .orient((rightAlignYAxis) ? 'right' : 'left') 1.8082 + .tickFormat(d3.format(',.1f')) 1.8083 + ; 1.8084 + 1.8085 + tooltip 1.8086 + .duration(0) 1.8087 + .valueFormatter(function(d, i) { 1.8088 + return yAxis.tickFormat()(d, i); 1.8089 + }) 1.8090 + .headerFormatter(function(d, i) { 1.8091 + return xAxis.tickFormat()(d, i); 1.8092 + }); 1.8093 + 1.8094 + controls.updateState(false); 1.8095 + 1.8096 + //============================================================ 1.8097 + // Private Variables 1.8098 + //------------------------------------------------------------ 1.8099 + 1.8100 + var renderWatch = nv.utils.renderWatch(dispatch); 1.8101 + var stacked = false; 1.8102 + 1.8103 + var stateGetter = function(data) { 1.8104 + return function(){ 1.8105 + return { 1.8106 + active: data.map(function(d) { return !d.disabled }), 1.8107 + stacked: stacked 1.8108 + }; 1.8109 + } 1.8110 + }; 1.8111 + 1.8112 + var stateSetter = function(data) { 1.8113 + return function(state) { 1.8114 + if (state.stacked !== undefined) 1.8115 + stacked = state.stacked; 1.8116 + if (state.active !== undefined) 1.8117 + data.forEach(function(series,i) { 1.8118 + series.disabled = !state.active[i]; 1.8119 + }); 1.8120 + } 1.8121 + }; 1.8122 + 1.8123 + function chart(selection) { 1.8124 + renderWatch.reset(); 1.8125 + renderWatch.models(multibar); 1.8126 + if (showXAxis) renderWatch.models(xAxis); 1.8127 + if (showYAxis) renderWatch.models(yAxis); 1.8128 + 1.8129 + selection.each(function(data) { 1.8130 + var container = d3.select(this), 1.8131 + that = this; 1.8132 + nv.utils.initSVG(container); 1.8133 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.8134 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.8135 + 1.8136 + chart.update = function() { 1.8137 + if (duration === 0) 1.8138 + container.call(chart); 1.8139 + else 1.8140 + container.transition() 1.8141 + .duration(duration) 1.8142 + .call(chart); 1.8143 + }; 1.8144 + chart.container = this; 1.8145 + 1.8146 + state 1.8147 + .setter(stateSetter(data), chart.update) 1.8148 + .getter(stateGetter(data)) 1.8149 + .update(); 1.8150 + 1.8151 + // DEPRECATED set state.disableddisabled 1.8152 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.8153 + 1.8154 + if (!defaultState) { 1.8155 + var key; 1.8156 + defaultState = {}; 1.8157 + for (key in state) { 1.8158 + if (state[key] instanceof Array) 1.8159 + defaultState[key] = state[key].slice(0); 1.8160 + else 1.8161 + defaultState[key] = state[key]; 1.8162 + } 1.8163 + } 1.8164 + 1.8165 + // Display noData message if there's nothing to show. 1.8166 + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { 1.8167 + nv.utils.noData(chart, container) 1.8168 + return chart; 1.8169 + } else { 1.8170 + container.selectAll('.nv-noData').remove(); 1.8171 + } 1.8172 + 1.8173 + // Setup Scales 1.8174 + x = multibar.xScale(); 1.8175 + y = multibar.yScale(); 1.8176 + 1.8177 + // Setup containers and skeleton of chart 1.8178 + var wrap = container.selectAll('g.nv-wrap.nv-multiBarWithLegend').data([data]); 1.8179 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarWithLegend').append('g'); 1.8180 + var g = wrap.select('g'); 1.8181 + 1.8182 + gEnter.append('g').attr('class', 'nv-x nv-axis'); 1.8183 + gEnter.append('g').attr('class', 'nv-y nv-axis'); 1.8184 + gEnter.append('g').attr('class', 'nv-barsWrap'); 1.8185 + gEnter.append('g').attr('class', 'nv-legendWrap'); 1.8186 + gEnter.append('g').attr('class', 'nv-controlsWrap'); 1.8187 + 1.8188 + // Legend 1.8189 + if (showLegend) { 1.8190 + legend.width(availableWidth - controlWidth()); 1.8191 + 1.8192 + g.select('.nv-legendWrap') 1.8193 + .datum(data) 1.8194 + .call(legend); 1.8195 + 1.8196 + if ( margin.top != legend.height()) { 1.8197 + margin.top = legend.height(); 1.8198 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.8199 + } 1.8200 + 1.8201 + g.select('.nv-legendWrap') 1.8202 + .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')'); 1.8203 + } 1.8204 + 1.8205 + // Controls 1.8206 + if (showControls) { 1.8207 + var controlsData = [ 1.8208 + { key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() }, 1.8209 + { key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() } 1.8210 + ]; 1.8211 + 1.8212 + controls.width(controlWidth()).color(['#444', '#444', '#444']); 1.8213 + g.select('.nv-controlsWrap') 1.8214 + .datum(controlsData) 1.8215 + .attr('transform', 'translate(0,' + (-margin.top) +')') 1.8216 + .call(controls); 1.8217 + } 1.8218 + 1.8219 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.8220 + if (rightAlignYAxis) { 1.8221 + g.select(".nv-y.nv-axis") 1.8222 + .attr("transform", "translate(" + availableWidth + ",0)"); 1.8223 + } 1.8224 + 1.8225 + // Main Chart Component(s) 1.8226 + multibar 1.8227 + .disabled(data.map(function(series) { return series.disabled })) 1.8228 + .width(availableWidth) 1.8229 + .height(availableHeight) 1.8230 + .color(data.map(function(d,i) { 1.8231 + return d.color || color(d, i); 1.8232 + }).filter(function(d,i) { return !data[i].disabled })); 1.8233 + 1.8234 + 1.8235 + var barsWrap = g.select('.nv-barsWrap') 1.8236 + .datum(data.filter(function(d) { return !d.disabled })); 1.8237 + 1.8238 + barsWrap.call(multibar); 1.8239 + 1.8240 + // Setup Axes 1.8241 + if (showXAxis) { 1.8242 + xAxis 1.8243 + .scale(x) 1.8244 + ._ticks( nv.utils.calcTicksX(availableWidth/100, data) ) 1.8245 + .tickSize(-availableHeight, 0); 1.8246 + 1.8247 + g.select('.nv-x.nv-axis') 1.8248 + .attr('transform', 'translate(0,' + y.range()[0] + ')'); 1.8249 + g.select('.nv-x.nv-axis') 1.8250 + .call(xAxis); 1.8251 + 1.8252 + var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g'); 1.8253 + 1.8254 + xTicks 1.8255 + .selectAll('line, text') 1.8256 + .style('opacity', 1) 1.8257 + 1.8258 + if (staggerLabels) { 1.8259 + var getTranslate = function(x,y) { 1.8260 + return "translate(" + x + "," + y + ")"; 1.8261 + }; 1.8262 + 1.8263 + var staggerUp = 5, staggerDown = 17; //pixels to stagger by 1.8264 + // Issue #140 1.8265 + xTicks 1.8266 + .selectAll("text") 1.8267 + .attr('transform', function(d,i,j) { 1.8268 + return getTranslate(0, (j % 2 == 0 ? staggerUp : staggerDown)); 1.8269 + }); 1.8270 + 1.8271 + var totalInBetweenTicks = d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length; 1.8272 + g.selectAll(".nv-x.nv-axis .nv-axisMaxMin text") 1.8273 + .attr("transform", function(d,i) { 1.8274 + return getTranslate(0, (i === 0 || totalInBetweenTicks % 2 !== 0) ? staggerDown : staggerUp); 1.8275 + }); 1.8276 + } 1.8277 + 1.8278 + if (reduceXTicks) 1.8279 + xTicks 1.8280 + .filter(function(d,i) { 1.8281 + return i % Math.ceil(data[0].values.length / (availableWidth / 100)) !== 0; 1.8282 + }) 1.8283 + .selectAll('text, line') 1.8284 + .style('opacity', 0); 1.8285 + 1.8286 + if(rotateLabels) 1.8287 + xTicks 1.8288 + .selectAll('.tick text') 1.8289 + .attr('transform', 'rotate(' + rotateLabels + ' 0,0)') 1.8290 + .style('text-anchor', rotateLabels > 0 ? 'start' : 'end'); 1.8291 + 1.8292 + g.select('.nv-x.nv-axis').selectAll('g.nv-axisMaxMin text') 1.8293 + .style('opacity', 1); 1.8294 + } 1.8295 + 1.8296 + if (showYAxis) { 1.8297 + yAxis 1.8298 + .scale(y) 1.8299 + ._ticks( nv.utils.calcTicksY(availableHeight/36, data) ) 1.8300 + .tickSize( -availableWidth, 0); 1.8301 + 1.8302 + g.select('.nv-y.nv-axis') 1.8303 + .call(yAxis); 1.8304 + } 1.8305 + 1.8306 + //============================================================ 1.8307 + // Event Handling/Dispatching (in chart's scope) 1.8308 + //------------------------------------------------------------ 1.8309 + 1.8310 + legend.dispatch.on('stateChange', function(newState) { 1.8311 + for (var key in newState) 1.8312 + state[key] = newState[key]; 1.8313 + dispatch.stateChange(state); 1.8314 + chart.update(); 1.8315 + }); 1.8316 + 1.8317 + controls.dispatch.on('legendClick', function(d,i) { 1.8318 + if (!d.disabled) return; 1.8319 + controlsData = controlsData.map(function(s) { 1.8320 + s.disabled = true; 1.8321 + return s; 1.8322 + }); 1.8323 + d.disabled = false; 1.8324 + 1.8325 + switch (d.key) { 1.8326 + case 'Grouped': 1.8327 + case controlLabels.grouped: 1.8328 + multibar.stacked(false); 1.8329 + break; 1.8330 + case 'Stacked': 1.8331 + case controlLabels.stacked: 1.8332 + multibar.stacked(true); 1.8333 + break; 1.8334 + } 1.8335 + 1.8336 + state.stacked = multibar.stacked(); 1.8337 + dispatch.stateChange(state); 1.8338 + chart.update(); 1.8339 + }); 1.8340 + 1.8341 + // Update chart from a state object passed to event handler 1.8342 + dispatch.on('changeState', function(e) { 1.8343 + if (typeof e.disabled !== 'undefined') { 1.8344 + data.forEach(function(series,i) { 1.8345 + series.disabled = e.disabled[i]; 1.8346 + }); 1.8347 + state.disabled = e.disabled; 1.8348 + } 1.8349 + if (typeof e.stacked !== 'undefined') { 1.8350 + multibar.stacked(e.stacked); 1.8351 + state.stacked = e.stacked; 1.8352 + stacked = e.stacked; 1.8353 + } 1.8354 + chart.update(); 1.8355 + }); 1.8356 + }); 1.8357 + 1.8358 + renderWatch.renderEnd('multibarchart immediate'); 1.8359 + return chart; 1.8360 + } 1.8361 + 1.8362 + //============================================================ 1.8363 + // Event Handling/Dispatching (out of chart's scope) 1.8364 + //------------------------------------------------------------ 1.8365 + 1.8366 + multibar.dispatch.on('elementMouseover.tooltip', function(evt) { 1.8367 + evt.value = chart.x()(evt.data); 1.8368 + evt['series'] = { 1.8369 + key: evt.data.key, 1.8370 + value: chart.y()(evt.data), 1.8371 + color: evt.color 1.8372 + }; 1.8373 + tooltip.data(evt).hidden(false); 1.8374 + }); 1.8375 + 1.8376 + multibar.dispatch.on('elementMouseout.tooltip', function(evt) { 1.8377 + tooltip.hidden(true); 1.8378 + }); 1.8379 + 1.8380 + multibar.dispatch.on('elementMousemove.tooltip', function(evt) { 1.8381 + tooltip.position({top: d3.event.pageY, left: d3.event.pageX})(); 1.8382 + }); 1.8383 + 1.8384 + //============================================================ 1.8385 + // Expose Public Variables 1.8386 + //------------------------------------------------------------ 1.8387 + 1.8388 + // expose chart's sub-components 1.8389 + chart.dispatch = dispatch; 1.8390 + chart.multibar = multibar; 1.8391 + chart.legend = legend; 1.8392 + chart.controls = controls; 1.8393 + chart.xAxis = xAxis; 1.8394 + chart.yAxis = yAxis; 1.8395 + chart.state = state; 1.8396 + chart.tooltip = tooltip; 1.8397 + 1.8398 + chart.options = nv.utils.optionsFunc.bind(chart); 1.8399 + 1.8400 + chart._options = Object.create({}, { 1.8401 + // simple options, just get/set the necessary values 1.8402 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.8403 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.8404 + showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, 1.8405 + showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}}, 1.8406 + controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}}, 1.8407 + showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}}, 1.8408 + showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}}, 1.8409 + defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}}, 1.8410 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.8411 + reduceXTicks: {get: function(){return reduceXTicks;}, set: function(_){reduceXTicks=_;}}, 1.8412 + rotateLabels: {get: function(){return rotateLabels;}, set: function(_){rotateLabels=_;}}, 1.8413 + staggerLabels: {get: function(){return staggerLabels;}, set: function(_){staggerLabels=_;}}, 1.8414 + 1.8415 + // deprecated options 1.8416 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.8417 + // deprecated after 1.7.1 1.8418 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.8419 + tooltip.enabled(!!_); 1.8420 + }}, 1.8421 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.8422 + // deprecated after 1.7.1 1.8423 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.8424 + tooltip.contentGenerator(_); 1.8425 + }}, 1.8426 + 1.8427 + // options that require extra logic in the setter 1.8428 + margin: {get: function(){return margin;}, set: function(_){ 1.8429 + margin.top = _.top !== undefined ? _.top : margin.top; 1.8430 + margin.right = _.right !== undefined ? _.right : margin.right; 1.8431 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.8432 + margin.left = _.left !== undefined ? _.left : margin.left; 1.8433 + }}, 1.8434 + duration: {get: function(){return duration;}, set: function(_){ 1.8435 + duration = _; 1.8436 + multibar.duration(duration); 1.8437 + xAxis.duration(duration); 1.8438 + yAxis.duration(duration); 1.8439 + renderWatch.reset(duration); 1.8440 + }}, 1.8441 + color: {get: function(){return color;}, set: function(_){ 1.8442 + color = nv.utils.getColor(_); 1.8443 + legend.color(color); 1.8444 + }}, 1.8445 + rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){ 1.8446 + rightAlignYAxis = _; 1.8447 + yAxis.orient( rightAlignYAxis ? 'right' : 'left'); 1.8448 + }}, 1.8449 + barColor: {get: function(){return multibar.barColor;}, set: function(_){ 1.8450 + multibar.barColor(_); 1.8451 + legend.color(function(d,i) {return d3.rgb('#ccc').darker(i * 1.5).toString();}) 1.8452 + }} 1.8453 + }); 1.8454 + 1.8455 + nv.utils.inheritOptions(chart, multibar); 1.8456 + nv.utils.initOptions(chart); 1.8457 + 1.8458 + return chart; 1.8459 +}; 1.8460 + 1.8461 +nv.models.multiBarHorizontal = function() { 1.8462 + "use strict"; 1.8463 + 1.8464 + //============================================================ 1.8465 + // Public Variables with Default Settings 1.8466 + //------------------------------------------------------------ 1.8467 + 1.8468 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.8469 + , width = 960 1.8470 + , height = 500 1.8471 + , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one 1.8472 + , container = null 1.8473 + , x = d3.scale.ordinal() 1.8474 + , y = d3.scale.linear() 1.8475 + , getX = function(d) { return d.x } 1.8476 + , getY = function(d) { return d.y } 1.8477 + , getYerr = function(d) { return d.yErr } 1.8478 + , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove 1.8479 + , color = nv.utils.defaultColor() 1.8480 + , barColor = null // adding the ability to set the color for each rather than the whole group 1.8481 + , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled 1.8482 + , stacked = false 1.8483 + , showValues = false 1.8484 + , showBarLabels = false 1.8485 + , valuePadding = 60 1.8486 + , groupSpacing = 0.1 1.8487 + , valueFormat = d3.format(',.2f') 1.8488 + , delay = 1200 1.8489 + , xDomain 1.8490 + , yDomain 1.8491 + , xRange 1.8492 + , yRange 1.8493 + , duration = 250 1.8494 + , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd') 1.8495 + ; 1.8496 + 1.8497 + //============================================================ 1.8498 + // Private Variables 1.8499 + //------------------------------------------------------------ 1.8500 + 1.8501 + var x0, y0; //used to store previous scales 1.8502 + var renderWatch = nv.utils.renderWatch(dispatch, duration); 1.8503 + 1.8504 + function chart(selection) { 1.8505 + renderWatch.reset(); 1.8506 + selection.each(function(data) { 1.8507 + var availableWidth = width - margin.left - margin.right, 1.8508 + availableHeight = height - margin.top - margin.bottom; 1.8509 + 1.8510 + container = d3.select(this); 1.8511 + nv.utils.initSVG(container); 1.8512 + 1.8513 + if (stacked) 1.8514 + data = d3.layout.stack() 1.8515 + .offset('zero') 1.8516 + .values(function(d){ return d.values }) 1.8517 + .y(getY) 1.8518 + (data); 1.8519 + 1.8520 + //add series index and key to each data point for reference 1.8521 + data.forEach(function(series, i) { 1.8522 + series.values.forEach(function(point) { 1.8523 + point.series = i; 1.8524 + point.key = series.key; 1.8525 + }); 1.8526 + }); 1.8527 + 1.8528 + // HACK for negative value stacking 1.8529 + if (stacked) 1.8530 + data[0].values.map(function(d,i) { 1.8531 + var posBase = 0, negBase = 0; 1.8532 + data.map(function(d) { 1.8533 + var f = d.values[i] 1.8534 + f.size = Math.abs(f.y); 1.8535 + if (f.y<0) { 1.8536 + f.y1 = negBase - f.size; 1.8537 + negBase = negBase - f.size; 1.8538 + } else 1.8539 + { 1.8540 + f.y1 = posBase; 1.8541 + posBase = posBase + f.size; 1.8542 + } 1.8543 + }); 1.8544 + }); 1.8545 + 1.8546 + // Setup Scales 1.8547 + // remap and flatten the data for use in calculating the scales' domains 1.8548 + var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate 1.8549 + data.map(function(d) { 1.8550 + return d.values.map(function(d,i) { 1.8551 + return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1 } 1.8552 + }) 1.8553 + }); 1.8554 + 1.8555 + x.domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x })) 1.8556 + .rangeBands(xRange || [0, availableHeight], groupSpacing); 1.8557 + 1.8558 + 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))) 1.8559 + 1.8560 + if (showValues && !stacked) 1.8561 + y.range(yRange || [(y.domain()[0] < 0 ? valuePadding : 0), availableWidth - (y.domain()[1] > 0 ? valuePadding : 0) ]); 1.8562 + else 1.8563 + y.range(yRange || [0, availableWidth]); 1.8564 + 1.8565 + x0 = x0 || x; 1.8566 + y0 = y0 || d3.scale.linear().domain(y.domain()).range([y(0),y(0)]); 1.8567 + 1.8568 + // Setup containers and skeleton of chart 1.8569 + var wrap = d3.select(this).selectAll('g.nv-wrap.nv-multibarHorizontal').data([data]); 1.8570 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibarHorizontal'); 1.8571 + var defsEnter = wrapEnter.append('defs'); 1.8572 + var gEnter = wrapEnter.append('g'); 1.8573 + var g = wrap.select('g'); 1.8574 + 1.8575 + gEnter.append('g').attr('class', 'nv-groups'); 1.8576 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.8577 + 1.8578 + var groups = wrap.select('.nv-groups').selectAll('.nv-group') 1.8579 + .data(function(d) { return d }, function(d,i) { return i }); 1.8580 + groups.enter().append('g') 1.8581 + .style('stroke-opacity', 1e-6) 1.8582 + .style('fill-opacity', 1e-6); 1.8583 + groups.exit().watchTransition(renderWatch, 'multibarhorizontal: exit groups') 1.8584 + .style('stroke-opacity', 1e-6) 1.8585 + .style('fill-opacity', 1e-6) 1.8586 + .remove(); 1.8587 + groups 1.8588 + .attr('class', function(d,i) { return 'nv-group nv-series-' + i }) 1.8589 + .classed('hover', function(d) { return d.hover }) 1.8590 + .style('fill', function(d,i){ return color(d, i) }) 1.8591 + .style('stroke', function(d,i){ return color(d, i) }); 1.8592 + groups.watchTransition(renderWatch, 'multibarhorizontal: groups') 1.8593 + .style('stroke-opacity', 1) 1.8594 + .style('fill-opacity', .75); 1.8595 + 1.8596 + var bars = groups.selectAll('g.nv-bar') 1.8597 + .data(function(d) { return d.values }); 1.8598 + bars.exit().remove(); 1.8599 + 1.8600 + var barsEnter = bars.enter().append('g') 1.8601 + .attr('transform', function(d,i,j) { 1.8602 + return 'translate(' + y0(stacked ? d.y0 : 0) + ',' + (stacked ? 0 : (j * x.rangeBand() / data.length ) + x(getX(d,i))) + ')' 1.8603 + }); 1.8604 + 1.8605 + barsEnter.append('rect') 1.8606 + .attr('width', 0) 1.8607 + .attr('height', x.rangeBand() / (stacked ? 1 : data.length) ) 1.8608 + 1.8609 + bars 1.8610 + .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here 1.8611 + d3.select(this).classed('hover', true); 1.8612 + dispatch.elementMouseover({ 1.8613 + data: d, 1.8614 + index: i, 1.8615 + color: d3.select(this).style("fill") 1.8616 + }); 1.8617 + }) 1.8618 + .on('mouseout', function(d,i) { 1.8619 + d3.select(this).classed('hover', false); 1.8620 + dispatch.elementMouseout({ 1.8621 + data: d, 1.8622 + index: i, 1.8623 + color: d3.select(this).style("fill") 1.8624 + }); 1.8625 + }) 1.8626 + .on('mouseout', function(d,i) { 1.8627 + dispatch.elementMouseout({ 1.8628 + data: d, 1.8629 + index: i, 1.8630 + color: d3.select(this).style("fill") 1.8631 + }); 1.8632 + }) 1.8633 + .on('mousemove', function(d,i) { 1.8634 + dispatch.elementMousemove({ 1.8635 + data: d, 1.8636 + index: i, 1.8637 + color: d3.select(this).style("fill") 1.8638 + }); 1.8639 + }) 1.8640 + .on('click', function(d,i) { 1.8641 + dispatch.elementClick({ 1.8642 + data: d, 1.8643 + index: i, 1.8644 + color: d3.select(this).style("fill") 1.8645 + }); 1.8646 + d3.event.stopPropagation(); 1.8647 + }) 1.8648 + .on('dblclick', function(d,i) { 1.8649 + dispatch.elementDblClick({ 1.8650 + data: d, 1.8651 + index: i, 1.8652 + color: d3.select(this).style("fill") 1.8653 + }); 1.8654 + d3.event.stopPropagation(); 1.8655 + }); 1.8656 + 1.8657 + if (getYerr(data[0],0)) { 1.8658 + barsEnter.append('polyline'); 1.8659 + 1.8660 + bars.select('polyline') 1.8661 + .attr('fill', 'none') 1.8662 + .attr('points', function(d,i) { 1.8663 + var xerr = getYerr(d,i) 1.8664 + , mid = 0.8 * x.rangeBand() / ((stacked ? 1 : data.length) * 2); 1.8665 + xerr = xerr.length ? xerr : [-Math.abs(xerr), Math.abs(xerr)]; 1.8666 + xerr = xerr.map(function(e) { return y(e) - y(0); }); 1.8667 + var a = [[xerr[0],-mid], [xerr[0],mid], [xerr[0],0], [xerr[1],0], [xerr[1],-mid], [xerr[1],mid]]; 1.8668 + return a.map(function (path) { return path.join(',') }).join(' '); 1.8669 + }) 1.8670 + .attr('transform', function(d,i) { 1.8671 + var mid = x.rangeBand() / ((stacked ? 1 : data.length) * 2); 1.8672 + return 'translate(' + (getY(d,i) < 0 ? 0 : y(getY(d,i)) - y(0)) + ', ' + mid + ')' 1.8673 + }); 1.8674 + } 1.8675 + 1.8676 + barsEnter.append('text'); 1.8677 + 1.8678 + if (showValues && !stacked) { 1.8679 + bars.select('text') 1.8680 + .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'end' : 'start' }) 1.8681 + .attr('y', x.rangeBand() / (data.length * 2)) 1.8682 + .attr('dy', '.32em') 1.8683 + .text(function(d,i) { 1.8684 + var t = valueFormat(getY(d,i)) 1.8685 + , yerr = getYerr(d,i); 1.8686 + if (yerr === undefined) 1.8687 + return t; 1.8688 + if (!yerr.length) 1.8689 + return t + '±' + valueFormat(Math.abs(yerr)); 1.8690 + return t + '+' + valueFormat(Math.abs(yerr[1])) + '-' + valueFormat(Math.abs(yerr[0])); 1.8691 + }); 1.8692 + bars.watchTransition(renderWatch, 'multibarhorizontal: bars') 1.8693 + .select('text') 1.8694 + .attr('x', function(d,i) { return getY(d,i) < 0 ? -4 : y(getY(d,i)) - y(0) + 4 }) 1.8695 + } else { 1.8696 + bars.selectAll('text').text(''); 1.8697 + } 1.8698 + 1.8699 + if (showBarLabels && !stacked) { 1.8700 + barsEnter.append('text').classed('nv-bar-label',true); 1.8701 + bars.select('text.nv-bar-label') 1.8702 + .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'start' : 'end' }) 1.8703 + .attr('y', x.rangeBand() / (data.length * 2)) 1.8704 + .attr('dy', '.32em') 1.8705 + .text(function(d,i) { return getX(d,i) }); 1.8706 + bars.watchTransition(renderWatch, 'multibarhorizontal: bars') 1.8707 + .select('text.nv-bar-label') 1.8708 + .attr('x', function(d,i) { return getY(d,i) < 0 ? y(0) - y(getY(d,i)) + 4 : -4 }); 1.8709 + } 1.8710 + else { 1.8711 + bars.selectAll('text.nv-bar-label').text(''); 1.8712 + } 1.8713 + 1.8714 + bars 1.8715 + .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'}) 1.8716 + 1.8717 + if (barColor) { 1.8718 + if (!disabled) disabled = data.map(function() { return true }); 1.8719 + bars 1.8720 + .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(); }) 1.8721 + .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(); }); 1.8722 + } 1.8723 + 1.8724 + if (stacked) 1.8725 + bars.watchTransition(renderWatch, 'multibarhorizontal: bars') 1.8726 + .attr('transform', function(d,i) { 1.8727 + return 'translate(' + y(d.y1) + ',' + x(getX(d,i)) + ')' 1.8728 + }) 1.8729 + .select('rect') 1.8730 + .attr('width', function(d,i) { 1.8731 + return Math.abs(y(getY(d,i) + d.y0) - y(d.y0)) 1.8732 + }) 1.8733 + .attr('height', x.rangeBand() ); 1.8734 + else 1.8735 + bars.watchTransition(renderWatch, 'multibarhorizontal: bars') 1.8736 + .attr('transform', function(d,i) { 1.8737 + //TODO: stacked must be all positive or all negative, not both? 1.8738 + return 'translate(' + 1.8739 + (getY(d,i) < 0 ? y(getY(d,i)) : y(0)) 1.8740 + + ',' + 1.8741 + (d.series * x.rangeBand() / data.length 1.8742 + + 1.8743 + x(getX(d,i)) ) 1.8744 + + ')' 1.8745 + }) 1.8746 + .select('rect') 1.8747 + .attr('height', x.rangeBand() / data.length ) 1.8748 + .attr('width', function(d,i) { 1.8749 + return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) 1.8750 + }); 1.8751 + 1.8752 + //store old scales for use in transitions on update 1.8753 + x0 = x.copy(); 1.8754 + y0 = y.copy(); 1.8755 + 1.8756 + }); 1.8757 + 1.8758 + renderWatch.renderEnd('multibarHorizontal immediate'); 1.8759 + return chart; 1.8760 + } 1.8761 + 1.8762 + //============================================================ 1.8763 + // Expose Public Variables 1.8764 + //------------------------------------------------------------ 1.8765 + 1.8766 + chart.dispatch = dispatch; 1.8767 + 1.8768 + chart.options = nv.utils.optionsFunc.bind(chart); 1.8769 + 1.8770 + chart._options = Object.create({}, { 1.8771 + // simple options, just get/set the necessary values 1.8772 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.8773 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.8774 + x: {get: function(){return getX;}, set: function(_){getX=_;}}, 1.8775 + y: {get: function(){return getY;}, set: function(_){getY=_;}}, 1.8776 + yErr: {get: function(){return getYerr;}, set: function(_){getYerr=_;}}, 1.8777 + xScale: {get: function(){return x;}, set: function(_){x=_;}}, 1.8778 + yScale: {get: function(){return y;}, set: function(_){y=_;}}, 1.8779 + xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}}, 1.8780 + yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}}, 1.8781 + xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}}, 1.8782 + yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}}, 1.8783 + forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}}, 1.8784 + stacked: {get: function(){return stacked;}, set: function(_){stacked=_;}}, 1.8785 + showValues: {get: function(){return showValues;}, set: function(_){showValues=_;}}, 1.8786 + // this shows the group name, seems pointless? 1.8787 + //showBarLabels: {get: function(){return showBarLabels;}, set: function(_){showBarLabels=_;}}, 1.8788 + disabled: {get: function(){return disabled;}, set: function(_){disabled=_;}}, 1.8789 + id: {get: function(){return id;}, set: function(_){id=_;}}, 1.8790 + valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}}, 1.8791 + valuePadding: {get: function(){return valuePadding;}, set: function(_){valuePadding=_;}}, 1.8792 + groupSpacing:{get: function(){return groupSpacing;}, set: function(_){groupSpacing=_;}}, 1.8793 + 1.8794 + // options that require extra logic in the setter 1.8795 + margin: {get: function(){return margin;}, set: function(_){ 1.8796 + margin.top = _.top !== undefined ? _.top : margin.top; 1.8797 + margin.right = _.right !== undefined ? _.right : margin.right; 1.8798 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.8799 + margin.left = _.left !== undefined ? _.left : margin.left; 1.8800 + }}, 1.8801 + duration: {get: function(){return duration;}, set: function(_){ 1.8802 + duration = _; 1.8803 + renderWatch.reset(duration); 1.8804 + }}, 1.8805 + color: {get: function(){return color;}, set: function(_){ 1.8806 + color = nv.utils.getColor(_); 1.8807 + }}, 1.8808 + barColor: {get: function(){return barColor;}, set: function(_){ 1.8809 + barColor = _ ? nv.utils.getColor(_) : null; 1.8810 + }} 1.8811 + }); 1.8812 + 1.8813 + nv.utils.initOptions(chart); 1.8814 + 1.8815 + return chart; 1.8816 +}; 1.8817 + 1.8818 +nv.models.multiBarHorizontalChart = function() { 1.8819 + "use strict"; 1.8820 + 1.8821 + //============================================================ 1.8822 + // Public Variables with Default Settings 1.8823 + //------------------------------------------------------------ 1.8824 + 1.8825 + var multibar = nv.models.multiBarHorizontal() 1.8826 + , xAxis = nv.models.axis() 1.8827 + , yAxis = nv.models.axis() 1.8828 + , legend = nv.models.legend().height(30) 1.8829 + , controls = nv.models.legend().height(30) 1.8830 + , tooltip = nv.models.tooltip() 1.8831 + ; 1.8832 + 1.8833 + var margin = {top: 30, right: 20, bottom: 50, left: 60} 1.8834 + , width = null 1.8835 + , height = null 1.8836 + , color = nv.utils.defaultColor() 1.8837 + , showControls = true 1.8838 + , controlLabels = {} 1.8839 + , showLegend = true 1.8840 + , showXAxis = true 1.8841 + , showYAxis = true 1.8842 + , stacked = false 1.8843 + , x //can be accessed via chart.xScale() 1.8844 + , y //can be accessed via chart.yScale() 1.8845 + , state = nv.utils.state() 1.8846 + , defaultState = null 1.8847 + , noData = null 1.8848 + , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd') 1.8849 + , controlWidth = function() { return showControls ? 180 : 0 } 1.8850 + , duration = 250 1.8851 + ; 1.8852 + 1.8853 + state.stacked = false; // DEPRECATED Maintained for backward compatibility 1.8854 + 1.8855 + multibar.stacked(stacked); 1.8856 + 1.8857 + xAxis 1.8858 + .orient('left') 1.8859 + .tickPadding(5) 1.8860 + .showMaxMin(false) 1.8861 + .tickFormat(function(d) { return d }) 1.8862 + ; 1.8863 + yAxis 1.8864 + .orient('bottom') 1.8865 + .tickFormat(d3.format(',.1f')) 1.8866 + ; 1.8867 + 1.8868 + tooltip 1.8869 + .duration(0) 1.8870 + .valueFormatter(function(d, i) { 1.8871 + return yAxis.tickFormat()(d, i); 1.8872 + }) 1.8873 + .headerFormatter(function(d, i) { 1.8874 + return xAxis.tickFormat()(d, i); 1.8875 + }); 1.8876 + 1.8877 + controls.updateState(false); 1.8878 + 1.8879 + //============================================================ 1.8880 + // Private Variables 1.8881 + //------------------------------------------------------------ 1.8882 + 1.8883 + var stateGetter = function(data) { 1.8884 + return function(){ 1.8885 + return { 1.8886 + active: data.map(function(d) { return !d.disabled }), 1.8887 + stacked: stacked 1.8888 + }; 1.8889 + } 1.8890 + }; 1.8891 + 1.8892 + var stateSetter = function(data) { 1.8893 + return function(state) { 1.8894 + if (state.stacked !== undefined) 1.8895 + stacked = state.stacked; 1.8896 + if (state.active !== undefined) 1.8897 + data.forEach(function(series,i) { 1.8898 + series.disabled = !state.active[i]; 1.8899 + }); 1.8900 + } 1.8901 + }; 1.8902 + 1.8903 + var renderWatch = nv.utils.renderWatch(dispatch, duration); 1.8904 + 1.8905 + function chart(selection) { 1.8906 + renderWatch.reset(); 1.8907 + renderWatch.models(multibar); 1.8908 + if (showXAxis) renderWatch.models(xAxis); 1.8909 + if (showYAxis) renderWatch.models(yAxis); 1.8910 + 1.8911 + selection.each(function(data) { 1.8912 + var container = d3.select(this), 1.8913 + that = this; 1.8914 + nv.utils.initSVG(container); 1.8915 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.8916 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.8917 + 1.8918 + chart.update = function() { container.transition().duration(duration).call(chart) }; 1.8919 + chart.container = this; 1.8920 + 1.8921 + stacked = multibar.stacked(); 1.8922 + 1.8923 + state 1.8924 + .setter(stateSetter(data), chart.update) 1.8925 + .getter(stateGetter(data)) 1.8926 + .update(); 1.8927 + 1.8928 + // DEPRECATED set state.disableddisabled 1.8929 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.8930 + 1.8931 + if (!defaultState) { 1.8932 + var key; 1.8933 + defaultState = {}; 1.8934 + for (key in state) { 1.8935 + if (state[key] instanceof Array) 1.8936 + defaultState[key] = state[key].slice(0); 1.8937 + else 1.8938 + defaultState[key] = state[key]; 1.8939 + } 1.8940 + } 1.8941 + 1.8942 + // Display No Data message if there's nothing to show. 1.8943 + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { 1.8944 + nv.utils.noData(chart, container) 1.8945 + return chart; 1.8946 + } else { 1.8947 + container.selectAll('.nv-noData').remove(); 1.8948 + } 1.8949 + 1.8950 + // Setup Scales 1.8951 + x = multibar.xScale(); 1.8952 + y = multibar.yScale(); 1.8953 + 1.8954 + // Setup containers and skeleton of chart 1.8955 + var wrap = container.selectAll('g.nv-wrap.nv-multiBarHorizontalChart').data([data]); 1.8956 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarHorizontalChart').append('g'); 1.8957 + var g = wrap.select('g'); 1.8958 + 1.8959 + gEnter.append('g').attr('class', 'nv-x nv-axis'); 1.8960 + gEnter.append('g').attr('class', 'nv-y nv-axis') 1.8961 + .append('g').attr('class', 'nv-zeroLine') 1.8962 + .append('line'); 1.8963 + gEnter.append('g').attr('class', 'nv-barsWrap'); 1.8964 + gEnter.append('g').attr('class', 'nv-legendWrap'); 1.8965 + gEnter.append('g').attr('class', 'nv-controlsWrap'); 1.8966 + 1.8967 + // Legend 1.8968 + if (showLegend) { 1.8969 + legend.width(availableWidth - controlWidth()); 1.8970 + 1.8971 + g.select('.nv-legendWrap') 1.8972 + .datum(data) 1.8973 + .call(legend); 1.8974 + 1.8975 + if ( margin.top != legend.height()) { 1.8976 + margin.top = legend.height(); 1.8977 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.8978 + } 1.8979 + 1.8980 + g.select('.nv-legendWrap') 1.8981 + .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')'); 1.8982 + } 1.8983 + 1.8984 + // Controls 1.8985 + if (showControls) { 1.8986 + var controlsData = [ 1.8987 + { key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() }, 1.8988 + { key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() } 1.8989 + ]; 1.8990 + 1.8991 + controls.width(controlWidth()).color(['#444', '#444', '#444']); 1.8992 + g.select('.nv-controlsWrap') 1.8993 + .datum(controlsData) 1.8994 + .attr('transform', 'translate(0,' + (-margin.top) +')') 1.8995 + .call(controls); 1.8996 + } 1.8997 + 1.8998 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.8999 + 1.9000 + // Main Chart Component(s) 1.9001 + multibar 1.9002 + .disabled(data.map(function(series) { return series.disabled })) 1.9003 + .width(availableWidth) 1.9004 + .height(availableHeight) 1.9005 + .color(data.map(function(d,i) { 1.9006 + return d.color || color(d, i); 1.9007 + }).filter(function(d,i) { return !data[i].disabled })); 1.9008 + 1.9009 + var barsWrap = g.select('.nv-barsWrap') 1.9010 + .datum(data.filter(function(d) { return !d.disabled })); 1.9011 + 1.9012 + barsWrap.transition().call(multibar); 1.9013 + 1.9014 + // Setup Axes 1.9015 + if (showXAxis) { 1.9016 + xAxis 1.9017 + .scale(x) 1.9018 + ._ticks( nv.utils.calcTicksY(availableHeight/24, data) ) 1.9019 + .tickSize(-availableWidth, 0); 1.9020 + 1.9021 + g.select('.nv-x.nv-axis').call(xAxis); 1.9022 + 1.9023 + var xTicks = g.select('.nv-x.nv-axis').selectAll('g'); 1.9024 + 1.9025 + xTicks 1.9026 + .selectAll('line, text'); 1.9027 + } 1.9028 + 1.9029 + if (showYAxis) { 1.9030 + yAxis 1.9031 + .scale(y) 1.9032 + ._ticks( nv.utils.calcTicksX(availableWidth/100, data) ) 1.9033 + .tickSize( -availableHeight, 0); 1.9034 + 1.9035 + g.select('.nv-y.nv-axis') 1.9036 + .attr('transform', 'translate(0,' + availableHeight + ')'); 1.9037 + g.select('.nv-y.nv-axis').call(yAxis); 1.9038 + } 1.9039 + 1.9040 + // Zero line 1.9041 + g.select(".nv-zeroLine line") 1.9042 + .attr("x1", y(0)) 1.9043 + .attr("x2", y(0)) 1.9044 + .attr("y1", 0) 1.9045 + .attr("y2", -availableHeight) 1.9046 + ; 1.9047 + 1.9048 + //============================================================ 1.9049 + // Event Handling/Dispatching (in chart's scope) 1.9050 + //------------------------------------------------------------ 1.9051 + 1.9052 + legend.dispatch.on('stateChange', function(newState) { 1.9053 + for (var key in newState) 1.9054 + state[key] = newState[key]; 1.9055 + dispatch.stateChange(state); 1.9056 + chart.update(); 1.9057 + }); 1.9058 + 1.9059 + controls.dispatch.on('legendClick', function(d,i) { 1.9060 + if (!d.disabled) return; 1.9061 + controlsData = controlsData.map(function(s) { 1.9062 + s.disabled = true; 1.9063 + return s; 1.9064 + }); 1.9065 + d.disabled = false; 1.9066 + 1.9067 + switch (d.key) { 1.9068 + case 'Grouped': 1.9069 + multibar.stacked(false); 1.9070 + break; 1.9071 + case 'Stacked': 1.9072 + multibar.stacked(true); 1.9073 + break; 1.9074 + } 1.9075 + 1.9076 + state.stacked = multibar.stacked(); 1.9077 + dispatch.stateChange(state); 1.9078 + stacked = multibar.stacked(); 1.9079 + 1.9080 + chart.update(); 1.9081 + }); 1.9082 + 1.9083 + // Update chart from a state object passed to event handler 1.9084 + dispatch.on('changeState', function(e) { 1.9085 + 1.9086 + if (typeof e.disabled !== 'undefined') { 1.9087 + data.forEach(function(series,i) { 1.9088 + series.disabled = e.disabled[i]; 1.9089 + }); 1.9090 + 1.9091 + state.disabled = e.disabled; 1.9092 + } 1.9093 + 1.9094 + if (typeof e.stacked !== 'undefined') { 1.9095 + multibar.stacked(e.stacked); 1.9096 + state.stacked = e.stacked; 1.9097 + stacked = e.stacked; 1.9098 + } 1.9099 + 1.9100 + chart.update(); 1.9101 + }); 1.9102 + }); 1.9103 + renderWatch.renderEnd('multibar horizontal chart immediate'); 1.9104 + return chart; 1.9105 + } 1.9106 + 1.9107 + //============================================================ 1.9108 + // Event Handling/Dispatching (out of chart's scope) 1.9109 + //------------------------------------------------------------ 1.9110 + 1.9111 + multibar.dispatch.on('elementMouseover.tooltip', function(evt) { 1.9112 + evt.value = chart.x()(evt.data); 1.9113 + evt['series'] = { 1.9114 + key: evt.data.key, 1.9115 + value: chart.y()(evt.data), 1.9116 + color: evt.color 1.9117 + }; 1.9118 + tooltip.data(evt).hidden(false); 1.9119 + }); 1.9120 + 1.9121 + multibar.dispatch.on('elementMouseout.tooltip', function(evt) { 1.9122 + tooltip.hidden(true); 1.9123 + }); 1.9124 + 1.9125 + multibar.dispatch.on('elementMousemove.tooltip', function(evt) { 1.9126 + tooltip.position({top: d3.event.pageY, left: d3.event.pageX})(); 1.9127 + }); 1.9128 + 1.9129 + //============================================================ 1.9130 + // Expose Public Variables 1.9131 + //------------------------------------------------------------ 1.9132 + 1.9133 + // expose chart's sub-components 1.9134 + chart.dispatch = dispatch; 1.9135 + chart.multibar = multibar; 1.9136 + chart.legend = legend; 1.9137 + chart.controls = controls; 1.9138 + chart.xAxis = xAxis; 1.9139 + chart.yAxis = yAxis; 1.9140 + chart.state = state; 1.9141 + chart.tooltip = tooltip; 1.9142 + 1.9143 + chart.options = nv.utils.optionsFunc.bind(chart); 1.9144 + 1.9145 + chart._options = Object.create({}, { 1.9146 + // simple options, just get/set the necessary values 1.9147 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.9148 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.9149 + showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, 1.9150 + showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}}, 1.9151 + controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}}, 1.9152 + showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}}, 1.9153 + showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}}, 1.9154 + defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}}, 1.9155 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.9156 + 1.9157 + // deprecated options 1.9158 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.9159 + // deprecated after 1.7.1 1.9160 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.9161 + tooltip.enabled(!!_); 1.9162 + }}, 1.9163 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.9164 + // deprecated after 1.7.1 1.9165 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.9166 + tooltip.contentGenerator(_); 1.9167 + }}, 1.9168 + 1.9169 + // options that require extra logic in the setter 1.9170 + margin: {get: function(){return margin;}, set: function(_){ 1.9171 + margin.top = _.top !== undefined ? _.top : margin.top; 1.9172 + margin.right = _.right !== undefined ? _.right : margin.right; 1.9173 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.9174 + margin.left = _.left !== undefined ? _.left : margin.left; 1.9175 + }}, 1.9176 + duration: {get: function(){return duration;}, set: function(_){ 1.9177 + duration = _; 1.9178 + renderWatch.reset(duration); 1.9179 + multibar.duration(duration); 1.9180 + xAxis.duration(duration); 1.9181 + yAxis.duration(duration); 1.9182 + }}, 1.9183 + color: {get: function(){return color;}, set: function(_){ 1.9184 + color = nv.utils.getColor(_); 1.9185 + legend.color(color); 1.9186 + }}, 1.9187 + barColor: {get: function(){return multibar.barColor;}, set: function(_){ 1.9188 + multibar.barColor(_); 1.9189 + legend.color(function(d,i) {return d3.rgb('#ccc').darker(i * 1.5).toString();}) 1.9190 + }} 1.9191 + }); 1.9192 + 1.9193 + nv.utils.inheritOptions(chart, multibar); 1.9194 + nv.utils.initOptions(chart); 1.9195 + 1.9196 + return chart; 1.9197 +}; 1.9198 +nv.models.multiChart = function() { 1.9199 + "use strict"; 1.9200 + 1.9201 + //============================================================ 1.9202 + // Public Variables with Default Settings 1.9203 + //------------------------------------------------------------ 1.9204 + 1.9205 + var margin = {top: 30, right: 20, bottom: 50, left: 60}, 1.9206 + color = nv.utils.defaultColor(), 1.9207 + width = null, 1.9208 + height = null, 1.9209 + showLegend = true, 1.9210 + noData = null, 1.9211 + yDomain1, 1.9212 + yDomain2, 1.9213 + getX = function(d) { return d.x }, 1.9214 + getY = function(d) { return d.y}, 1.9215 + interpolate = 'monotone', 1.9216 + useVoronoi = true 1.9217 + ; 1.9218 + 1.9219 + //============================================================ 1.9220 + // Private Variables 1.9221 + //------------------------------------------------------------ 1.9222 + 1.9223 + var x = d3.scale.linear(), 1.9224 + yScale1 = d3.scale.linear(), 1.9225 + yScale2 = d3.scale.linear(), 1.9226 + 1.9227 + lines1 = nv.models.line().yScale(yScale1), 1.9228 + lines2 = nv.models.line().yScale(yScale2), 1.9229 + 1.9230 + bars1 = nv.models.multiBar().stacked(false).yScale(yScale1), 1.9231 + bars2 = nv.models.multiBar().stacked(false).yScale(yScale2), 1.9232 + 1.9233 + stack1 = nv.models.stackedArea().yScale(yScale1), 1.9234 + stack2 = nv.models.stackedArea().yScale(yScale2), 1.9235 + 1.9236 + xAxis = nv.models.axis().scale(x).orient('bottom').tickPadding(5), 1.9237 + yAxis1 = nv.models.axis().scale(yScale1).orient('left'), 1.9238 + yAxis2 = nv.models.axis().scale(yScale2).orient('right'), 1.9239 + 1.9240 + legend = nv.models.legend().height(30), 1.9241 + tooltip = nv.models.tooltip(), 1.9242 + dispatch = d3.dispatch(); 1.9243 + 1.9244 + function chart(selection) { 1.9245 + selection.each(function(data) { 1.9246 + var container = d3.select(this), 1.9247 + that = this; 1.9248 + nv.utils.initSVG(container); 1.9249 + 1.9250 + chart.update = function() { container.transition().call(chart); }; 1.9251 + chart.container = this; 1.9252 + 1.9253 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.9254 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.9255 + 1.9256 + var dataLines1 = data.filter(function(d) {return d.type == 'line' && d.yAxis == 1}); 1.9257 + var dataLines2 = data.filter(function(d) {return d.type == 'line' && d.yAxis == 2}); 1.9258 + var dataBars1 = data.filter(function(d) {return d.type == 'bar' && d.yAxis == 1}); 1.9259 + var dataBars2 = data.filter(function(d) {return d.type == 'bar' && d.yAxis == 2}); 1.9260 + var dataStack1 = data.filter(function(d) {return d.type == 'area' && d.yAxis == 1}); 1.9261 + var dataStack2 = data.filter(function(d) {return d.type == 'area' && d.yAxis == 2}); 1.9262 + 1.9263 + // Display noData message if there's nothing to show. 1.9264 + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { 1.9265 + nv.utils.noData(chart, container); 1.9266 + return chart; 1.9267 + } else { 1.9268 + container.selectAll('.nv-noData').remove(); 1.9269 + } 1.9270 + 1.9271 + var series1 = data.filter(function(d) {return !d.disabled && d.yAxis == 1}) 1.9272 + .map(function(d) { 1.9273 + return d.values.map(function(d,i) { 1.9274 + return { x: d.x, y: d.y } 1.9275 + }) 1.9276 + }); 1.9277 + 1.9278 + var series2 = data.filter(function(d) {return !d.disabled && d.yAxis == 2}) 1.9279 + .map(function(d) { 1.9280 + return d.values.map(function(d,i) { 1.9281 + return { x: d.x, y: d.y } 1.9282 + }) 1.9283 + }); 1.9284 + 1.9285 + x .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } )) 1.9286 + .range([0, availableWidth]); 1.9287 + 1.9288 + var wrap = container.selectAll('g.wrap.multiChart').data([data]); 1.9289 + var gEnter = wrap.enter().append('g').attr('class', 'wrap nvd3 multiChart').append('g'); 1.9290 + 1.9291 + gEnter.append('g').attr('class', 'nv-x nv-axis'); 1.9292 + gEnter.append('g').attr('class', 'nv-y1 nv-axis'); 1.9293 + gEnter.append('g').attr('class', 'nv-y2 nv-axis'); 1.9294 + gEnter.append('g').attr('class', 'lines1Wrap'); 1.9295 + gEnter.append('g').attr('class', 'lines2Wrap'); 1.9296 + gEnter.append('g').attr('class', 'bars1Wrap'); 1.9297 + gEnter.append('g').attr('class', 'bars2Wrap'); 1.9298 + gEnter.append('g').attr('class', 'stack1Wrap'); 1.9299 + gEnter.append('g').attr('class', 'stack2Wrap'); 1.9300 + gEnter.append('g').attr('class', 'legendWrap'); 1.9301 + 1.9302 + var g = wrap.select('g'); 1.9303 + 1.9304 + var color_array = data.map(function(d,i) { 1.9305 + return data[i].color || color(d, i); 1.9306 + }); 1.9307 + 1.9308 + if (showLegend) { 1.9309 + var legendWidth = legend.align() ? availableWidth / 2 : availableWidth; 1.9310 + var legendXPosition = legend.align() ? legendWidth : 0; 1.9311 + 1.9312 + legend.width(legendWidth); 1.9313 + legend.color(color_array); 1.9314 + 1.9315 + g.select('.legendWrap') 1.9316 + .datum(data.map(function(series) { 1.9317 + series.originalKey = series.originalKey === undefined ? series.key : series.originalKey; 1.9318 + series.key = series.originalKey + (series.yAxis == 1 ? '' : ' (right axis)'); 1.9319 + return series; 1.9320 + })) 1.9321 + .call(legend); 1.9322 + 1.9323 + if ( margin.top != legend.height()) { 1.9324 + margin.top = legend.height(); 1.9325 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.9326 + } 1.9327 + 1.9328 + g.select('.legendWrap') 1.9329 + .attr('transform', 'translate(' + legendXPosition + ',' + (-margin.top) +')'); 1.9330 + } 1.9331 + 1.9332 + lines1 1.9333 + .width(availableWidth) 1.9334 + .height(availableHeight) 1.9335 + .interpolate(interpolate) 1.9336 + .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'line'})); 1.9337 + lines2 1.9338 + .width(availableWidth) 1.9339 + .height(availableHeight) 1.9340 + .interpolate(interpolate) 1.9341 + .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'line'})); 1.9342 + bars1 1.9343 + .width(availableWidth) 1.9344 + .height(availableHeight) 1.9345 + .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'bar'})); 1.9346 + bars2 1.9347 + .width(availableWidth) 1.9348 + .height(availableHeight) 1.9349 + .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'bar'})); 1.9350 + stack1 1.9351 + .width(availableWidth) 1.9352 + .height(availableHeight) 1.9353 + .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'area'})); 1.9354 + stack2 1.9355 + .width(availableWidth) 1.9356 + .height(availableHeight) 1.9357 + .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'area'})); 1.9358 + 1.9359 + g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.9360 + 1.9361 + var lines1Wrap = g.select('.lines1Wrap') 1.9362 + .datum(dataLines1.filter(function(d){return !d.disabled})); 1.9363 + var bars1Wrap = g.select('.bars1Wrap') 1.9364 + .datum(dataBars1.filter(function(d){return !d.disabled})); 1.9365 + var stack1Wrap = g.select('.stack1Wrap') 1.9366 + .datum(dataStack1.filter(function(d){return !d.disabled})); 1.9367 + var lines2Wrap = g.select('.lines2Wrap') 1.9368 + .datum(dataLines2.filter(function(d){return !d.disabled})); 1.9369 + var bars2Wrap = g.select('.bars2Wrap') 1.9370 + .datum(dataBars2.filter(function(d){return !d.disabled})); 1.9371 + var stack2Wrap = g.select('.stack2Wrap') 1.9372 + .datum(dataStack2.filter(function(d){return !d.disabled})); 1.9373 + 1.9374 + var extraValue1 = dataStack1.length ? dataStack1.map(function(a){return a.values}).reduce(function(a,b){ 1.9375 + return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}}) 1.9376 + }).concat([{x:0, y:0}]) : []; 1.9377 + var extraValue2 = dataStack2.length ? dataStack2.map(function(a){return a.values}).reduce(function(a,b){ 1.9378 + return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}}) 1.9379 + }).concat([{x:0, y:0}]) : []; 1.9380 + 1.9381 + yScale1 .domain(yDomain1 || d3.extent(d3.merge(series1).concat(extraValue1), function(d) { return d.y } )) 1.9382 + .range([0, availableHeight]); 1.9383 + 1.9384 + yScale2 .domain(yDomain2 || d3.extent(d3.merge(series2).concat(extraValue2), function(d) { return d.y } )) 1.9385 + .range([0, availableHeight]); 1.9386 + 1.9387 + lines1.yDomain(yScale1.domain()); 1.9388 + bars1.yDomain(yScale1.domain()); 1.9389 + stack1.yDomain(yScale1.domain()); 1.9390 + 1.9391 + lines2.yDomain(yScale2.domain()); 1.9392 + bars2.yDomain(yScale2.domain()); 1.9393 + stack2.yDomain(yScale2.domain()); 1.9394 + 1.9395 + if(dataStack1.length){d3.transition(stack1Wrap).call(stack1);} 1.9396 + if(dataStack2.length){d3.transition(stack2Wrap).call(stack2);} 1.9397 + 1.9398 + if(dataBars1.length){d3.transition(bars1Wrap).call(bars1);} 1.9399 + if(dataBars2.length){d3.transition(bars2Wrap).call(bars2);} 1.9400 + 1.9401 + if(dataLines1.length){d3.transition(lines1Wrap).call(lines1);} 1.9402 + if(dataLines2.length){d3.transition(lines2Wrap).call(lines2);} 1.9403 + 1.9404 + xAxis 1.9405 + ._ticks( nv.utils.calcTicksX(availableWidth/100, data) ) 1.9406 + .tickSize(-availableHeight, 0); 1.9407 + 1.9408 + g.select('.nv-x.nv-axis') 1.9409 + .attr('transform', 'translate(0,' + availableHeight + ')'); 1.9410 + d3.transition(g.select('.nv-x.nv-axis')) 1.9411 + .call(xAxis); 1.9412 + 1.9413 + yAxis1 1.9414 + ._ticks( nv.utils.calcTicksY(availableHeight/36, data) ) 1.9415 + .tickSize( -availableWidth, 0); 1.9416 + 1.9417 + 1.9418 + d3.transition(g.select('.nv-y1.nv-axis')) 1.9419 + .call(yAxis1); 1.9420 + 1.9421 + yAxis2 1.9422 + ._ticks( nv.utils.calcTicksY(availableHeight/36, data) ) 1.9423 + .tickSize( -availableWidth, 0); 1.9424 + 1.9425 + d3.transition(g.select('.nv-y2.nv-axis')) 1.9426 + .call(yAxis2); 1.9427 + 1.9428 + g.select('.nv-y1.nv-axis') 1.9429 + .classed('nv-disabled', series1.length ? false : true) 1.9430 + .attr('transform', 'translate(' + x.range()[0] + ',0)'); 1.9431 + 1.9432 + g.select('.nv-y2.nv-axis') 1.9433 + .classed('nv-disabled', series2.length ? false : true) 1.9434 + .attr('transform', 'translate(' + x.range()[1] + ',0)'); 1.9435 + 1.9436 + legend.dispatch.on('stateChange', function(newState) { 1.9437 + chart.update(); 1.9438 + }); 1.9439 + 1.9440 + //============================================================ 1.9441 + // Event Handling/Dispatching 1.9442 + //------------------------------------------------------------ 1.9443 + 1.9444 + function mouseover_line(evt) { 1.9445 + var yaxis = data[evt.seriesIndex].yAxis === 2 ? yAxis2 : yAxis1; 1.9446 + evt.value = evt.point.x; 1.9447 + evt.series = { 1.9448 + value: evt.point.y, 1.9449 + color: evt.point.color 1.9450 + }; 1.9451 + tooltip 1.9452 + .duration(100) 1.9453 + .valueFormatter(function(d, i) { 1.9454 + return yaxis.tickFormat()(d, i); 1.9455 + }) 1.9456 + .data(evt) 1.9457 + .position(evt.pos) 1.9458 + .hidden(false); 1.9459 + } 1.9460 + 1.9461 + function mouseover_stack(evt) { 1.9462 + var yaxis = data[evt.seriesIndex].yAxis === 2 ? yAxis2 : yAxis1; 1.9463 + evt.point['x'] = stack1.x()(evt.point); 1.9464 + evt.point['y'] = stack1.y()(evt.point); 1.9465 + tooltip 1.9466 + .duration(100) 1.9467 + .valueFormatter(function(d, i) { 1.9468 + return yaxis.tickFormat()(d, i); 1.9469 + }) 1.9470 + .data(evt) 1.9471 + .position(evt.pos) 1.9472 + .hidden(false); 1.9473 + } 1.9474 + 1.9475 + function mouseover_bar(evt) { 1.9476 + var yaxis = data[evt.data.series].yAxis === 2 ? yAxis2 : yAxis1; 1.9477 + 1.9478 + evt.value = bars1.x()(evt.data); 1.9479 + evt['series'] = { 1.9480 + value: bars1.y()(evt.data), 1.9481 + color: evt.color 1.9482 + }; 1.9483 + tooltip 1.9484 + .duration(0) 1.9485 + .valueFormatter(function(d, i) { 1.9486 + return yaxis.tickFormat()(d, i); 1.9487 + }) 1.9488 + .data(evt) 1.9489 + .hidden(false); 1.9490 + } 1.9491 + 1.9492 + lines1.dispatch.on('elementMouseover.tooltip', mouseover_line); 1.9493 + lines2.dispatch.on('elementMouseover.tooltip', mouseover_line); 1.9494 + lines1.dispatch.on('elementMouseout.tooltip', function(evt) { 1.9495 + tooltip.hidden(true) 1.9496 + }); 1.9497 + lines2.dispatch.on('elementMouseout.tooltip', function(evt) { 1.9498 + tooltip.hidden(true) 1.9499 + }); 1.9500 + 1.9501 + stack1.dispatch.on('elementMouseover.tooltip', mouseover_stack); 1.9502 + stack2.dispatch.on('elementMouseover.tooltip', mouseover_stack); 1.9503 + stack1.dispatch.on('elementMouseout.tooltip', function(evt) { 1.9504 + tooltip.hidden(true) 1.9505 + }); 1.9506 + stack2.dispatch.on('elementMouseout.tooltip', function(evt) { 1.9507 + tooltip.hidden(true) 1.9508 + }); 1.9509 + 1.9510 + bars1.dispatch.on('elementMouseover.tooltip', mouseover_bar); 1.9511 + bars2.dispatch.on('elementMouseover.tooltip', mouseover_bar); 1.9512 + 1.9513 + bars1.dispatch.on('elementMouseout.tooltip', function(evt) { 1.9514 + tooltip.hidden(true); 1.9515 + }); 1.9516 + bars2.dispatch.on('elementMouseout.tooltip', function(evt) { 1.9517 + tooltip.hidden(true); 1.9518 + }); 1.9519 + bars1.dispatch.on('elementMousemove.tooltip', function(evt) { 1.9520 + tooltip.position({top: d3.event.pageY, left: d3.event.pageX})(); 1.9521 + }); 1.9522 + bars2.dispatch.on('elementMousemove.tooltip', function(evt) { 1.9523 + tooltip.position({top: d3.event.pageY, left: d3.event.pageX})(); 1.9524 + }); 1.9525 + 1.9526 + }); 1.9527 + 1.9528 + return chart; 1.9529 + } 1.9530 + 1.9531 + //============================================================ 1.9532 + // Global getters and setters 1.9533 + //------------------------------------------------------------ 1.9534 + 1.9535 + chart.dispatch = dispatch; 1.9536 + chart.lines1 = lines1; 1.9537 + chart.lines2 = lines2; 1.9538 + chart.bars1 = bars1; 1.9539 + chart.bars2 = bars2; 1.9540 + chart.stack1 = stack1; 1.9541 + chart.stack2 = stack2; 1.9542 + chart.xAxis = xAxis; 1.9543 + chart.yAxis1 = yAxis1; 1.9544 + chart.yAxis2 = yAxis2; 1.9545 + chart.tooltip = tooltip; 1.9546 + 1.9547 + chart.options = nv.utils.optionsFunc.bind(chart); 1.9548 + 1.9549 + chart._options = Object.create({}, { 1.9550 + // simple options, just get/set the necessary values 1.9551 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.9552 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.9553 + showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, 1.9554 + yDomain1: {get: function(){return yDomain1;}, set: function(_){yDomain1=_;}}, 1.9555 + yDomain2: {get: function(){return yDomain2;}, set: function(_){yDomain2=_;}}, 1.9556 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.9557 + interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}}, 1.9558 + 1.9559 + // deprecated options 1.9560 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.9561 + // deprecated after 1.7.1 1.9562 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.9563 + tooltip.enabled(!!_); 1.9564 + }}, 1.9565 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.9566 + // deprecated after 1.7.1 1.9567 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.9568 + tooltip.contentGenerator(_); 1.9569 + }}, 1.9570 + 1.9571 + // options that require extra logic in the setter 1.9572 + margin: {get: function(){return margin;}, set: function(_){ 1.9573 + margin.top = _.top !== undefined ? _.top : margin.top; 1.9574 + margin.right = _.right !== undefined ? _.right : margin.right; 1.9575 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.9576 + margin.left = _.left !== undefined ? _.left : margin.left; 1.9577 + }}, 1.9578 + color: {get: function(){return color;}, set: function(_){ 1.9579 + color = nv.utils.getColor(_); 1.9580 + }}, 1.9581 + x: {get: function(){return getX;}, set: function(_){ 1.9582 + getX = _; 1.9583 + lines1.x(_); 1.9584 + lines2.x(_); 1.9585 + bars1.x(_); 1.9586 + bars2.x(_); 1.9587 + stack1.x(_); 1.9588 + stack2.x(_); 1.9589 + }}, 1.9590 + y: {get: function(){return getY;}, set: function(_){ 1.9591 + getY = _; 1.9592 + lines1.y(_); 1.9593 + lines2.y(_); 1.9594 + stack1.y(_); 1.9595 + stack2.y(_); 1.9596 + bars1.y(_); 1.9597 + bars2.y(_); 1.9598 + }}, 1.9599 + useVoronoi: {get: function(){return useVoronoi;}, set: function(_){ 1.9600 + useVoronoi=_; 1.9601 + lines1.useVoronoi(_); 1.9602 + lines2.useVoronoi(_); 1.9603 + stack1.useVoronoi(_); 1.9604 + stack2.useVoronoi(_); 1.9605 + }} 1.9606 + }); 1.9607 + 1.9608 + nv.utils.initOptions(chart); 1.9609 + 1.9610 + return chart; 1.9611 +}; 1.9612 + 1.9613 + 1.9614 +nv.models.ohlcBar = function() { 1.9615 + "use strict"; 1.9616 + 1.9617 + //============================================================ 1.9618 + // Public Variables with Default Settings 1.9619 + //------------------------------------------------------------ 1.9620 + 1.9621 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.9622 + , width = null 1.9623 + , height = null 1.9624 + , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one 1.9625 + , container = null 1.9626 + , x = d3.scale.linear() 1.9627 + , y = d3.scale.linear() 1.9628 + , getX = function(d) { return d.x } 1.9629 + , getY = function(d) { return d.y } 1.9630 + , getOpen = function(d) { return d.open } 1.9631 + , getClose = function(d) { return d.close } 1.9632 + , getHigh = function(d) { return d.high } 1.9633 + , getLow = function(d) { return d.low } 1.9634 + , forceX = [] 1.9635 + , forceY = [] 1.9636 + , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart 1.9637 + , clipEdge = true 1.9638 + , color = nv.utils.defaultColor() 1.9639 + , interactive = false 1.9640 + , xDomain 1.9641 + , yDomain 1.9642 + , xRange 1.9643 + , yRange 1.9644 + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd', 'chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove') 1.9645 + ; 1.9646 + 1.9647 + //============================================================ 1.9648 + // Private Variables 1.9649 + //------------------------------------------------------------ 1.9650 + 1.9651 + function chart(selection) { 1.9652 + selection.each(function(data) { 1.9653 + container = d3.select(this); 1.9654 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.9655 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.9656 + 1.9657 + nv.utils.initSVG(container); 1.9658 + 1.9659 + // ohlc bar width. 1.9660 + var w = (availableWidth / data[0].values.length) * .9; 1.9661 + 1.9662 + // Setup Scales 1.9663 + x.domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) )); 1.9664 + 1.9665 + if (padData) 1.9666 + x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]); 1.9667 + else 1.9668 + x.range(xRange || [5 + w/2, availableWidth - w/2 - 5]); 1.9669 + 1.9670 + y.domain(yDomain || [ 1.9671 + d3.min(data[0].values.map(getLow).concat(forceY)), 1.9672 + d3.max(data[0].values.map(getHigh).concat(forceY)) 1.9673 + ] 1.9674 + ).range(yRange || [availableHeight, 0]); 1.9675 + 1.9676 + // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point 1.9677 + if (x.domain()[0] === x.domain()[1]) 1.9678 + x.domain()[0] ? 1.9679 + x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01]) 1.9680 + : x.domain([-1,1]); 1.9681 + 1.9682 + if (y.domain()[0] === y.domain()[1]) 1.9683 + y.domain()[0] ? 1.9684 + y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01]) 1.9685 + : y.domain([-1,1]); 1.9686 + 1.9687 + // Setup containers and skeleton of chart 1.9688 + var wrap = d3.select(this).selectAll('g.nv-wrap.nv-ohlcBar').data([data[0].values]); 1.9689 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-ohlcBar'); 1.9690 + var defsEnter = wrapEnter.append('defs'); 1.9691 + var gEnter = wrapEnter.append('g'); 1.9692 + var g = wrap.select('g'); 1.9693 + 1.9694 + gEnter.append('g').attr('class', 'nv-ticks'); 1.9695 + 1.9696 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.9697 + 1.9698 + container 1.9699 + .on('click', function(d,i) { 1.9700 + dispatch.chartClick({ 1.9701 + data: d, 1.9702 + index: i, 1.9703 + pos: d3.event, 1.9704 + id: id 1.9705 + }); 1.9706 + }); 1.9707 + 1.9708 + defsEnter.append('clipPath') 1.9709 + .attr('id', 'nv-chart-clip-path-' + id) 1.9710 + .append('rect'); 1.9711 + 1.9712 + wrap.select('#nv-chart-clip-path-' + id + ' rect') 1.9713 + .attr('width', availableWidth) 1.9714 + .attr('height', availableHeight); 1.9715 + 1.9716 + g .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : ''); 1.9717 + 1.9718 + var ticks = wrap.select('.nv-ticks').selectAll('.nv-tick') 1.9719 + .data(function(d) { return d }); 1.9720 + ticks.exit().remove(); 1.9721 + 1.9722 + ticks.enter().append('path') 1.9723 + .attr('class', function(d,i,j) { return (getOpen(d,i) > getClose(d,i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i }) 1.9724 + .attr('d', function(d,i) { 1.9725 + return 'm0,0l0,' 1.9726 + + (y(getOpen(d,i)) 1.9727 + - y(getHigh(d,i))) 1.9728 + + 'l' 1.9729 + + (-w/2) 1.9730 + + ',0l' 1.9731 + + (w/2) 1.9732 + + ',0l0,' 1.9733 + + (y(getLow(d,i)) - y(getOpen(d,i))) 1.9734 + + 'l0,' 1.9735 + + (y(getClose(d,i)) 1.9736 + - y(getLow(d,i))) 1.9737 + + 'l' 1.9738 + + (w/2) 1.9739 + + ',0l' 1.9740 + + (-w/2) 1.9741 + + ',0z'; 1.9742 + }) 1.9743 + .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',' + y(getHigh(d,i)) + ')'; }) 1.9744 + .attr('fill', function(d,i) { return color[0]; }) 1.9745 + .attr('stroke', function(d,i) { return color[0]; }) 1.9746 + .attr('x', 0 ) 1.9747 + .attr('y', function(d,i) { return y(Math.max(0, getY(d,i))) }) 1.9748 + .attr('height', function(d,i) { return Math.abs(y(getY(d,i)) - y(0)) }); 1.9749 + 1.9750 + // the bar colors are controlled by CSS currently 1.9751 + ticks.attr('class', function(d,i,j) { 1.9752 + return (getOpen(d,i) > getClose(d,i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i; 1.9753 + }); 1.9754 + 1.9755 + d3.transition(ticks) 1.9756 + .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',' + y(getHigh(d,i)) + ')'; }) 1.9757 + .attr('d', function(d,i) { 1.9758 + var w = (availableWidth / data[0].values.length) * .9; 1.9759 + return 'm0,0l0,' 1.9760 + + (y(getOpen(d,i)) 1.9761 + - y(getHigh(d,i))) 1.9762 + + 'l' 1.9763 + + (-w/2) 1.9764 + + ',0l' 1.9765 + + (w/2) 1.9766 + + ',0l0,' 1.9767 + + (y(getLow(d,i)) 1.9768 + - y(getOpen(d,i))) 1.9769 + + 'l0,' 1.9770 + + (y(getClose(d,i)) 1.9771 + - y(getLow(d,i))) 1.9772 + + 'l' 1.9773 + + (w/2) 1.9774 + + ',0l' 1.9775 + + (-w/2) 1.9776 + + ',0z'; 1.9777 + }); 1.9778 + }); 1.9779 + 1.9780 + return chart; 1.9781 + } 1.9782 + 1.9783 + 1.9784 + //Create methods to allow outside functions to highlight a specific bar. 1.9785 + chart.highlightPoint = function(pointIndex, isHoverOver) { 1.9786 + chart.clearHighlights(); 1.9787 + container.select(".nv-ohlcBar .nv-tick-0-" + pointIndex) 1.9788 + .classed("hover", isHoverOver) 1.9789 + ; 1.9790 + }; 1.9791 + 1.9792 + chart.clearHighlights = function() { 1.9793 + container.select(".nv-ohlcBar .nv-tick.hover") 1.9794 + .classed("hover", false) 1.9795 + ; 1.9796 + }; 1.9797 + 1.9798 + //============================================================ 1.9799 + // Expose Public Variables 1.9800 + //------------------------------------------------------------ 1.9801 + 1.9802 + chart.dispatch = dispatch; 1.9803 + chart.options = nv.utils.optionsFunc.bind(chart); 1.9804 + 1.9805 + chart._options = Object.create({}, { 1.9806 + // simple options, just get/set the necessary values 1.9807 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.9808 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.9809 + xScale: {get: function(){return x;}, set: function(_){x=_;}}, 1.9810 + yScale: {get: function(){return y;}, set: function(_){y=_;}}, 1.9811 + xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}}, 1.9812 + yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}}, 1.9813 + xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}}, 1.9814 + yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}}, 1.9815 + forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}}, 1.9816 + forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}}, 1.9817 + padData: {get: function(){return padData;}, set: function(_){padData=_;}}, 1.9818 + clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}}, 1.9819 + id: {get: function(){return id;}, set: function(_){id=_;}}, 1.9820 + interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}}, 1.9821 + 1.9822 + x: {get: function(){return getX;}, set: function(_){getX=_;}}, 1.9823 + y: {get: function(){return getY;}, set: function(_){getY=_;}}, 1.9824 + open: {get: function(){return getOpen();}, set: function(_){getOpen=_;}}, 1.9825 + close: {get: function(){return getClose();}, set: function(_){getClose=_;}}, 1.9826 + high: {get: function(){return getHigh;}, set: function(_){getHigh=_;}}, 1.9827 + low: {get: function(){return getLow;}, set: function(_){getLow=_;}}, 1.9828 + 1.9829 + // options that require extra logic in the setter 1.9830 + margin: {get: function(){return margin;}, set: function(_){ 1.9831 + margin.top = _.top != undefined ? _.top : margin.top; 1.9832 + margin.right = _.right != undefined ? _.right : margin.right; 1.9833 + margin.bottom = _.bottom != undefined ? _.bottom : margin.bottom; 1.9834 + margin.left = _.left != undefined ? _.left : margin.left; 1.9835 + }}, 1.9836 + color: {get: function(){return color;}, set: function(_){ 1.9837 + color = nv.utils.getColor(_); 1.9838 + }} 1.9839 + }); 1.9840 + 1.9841 + nv.utils.initOptions(chart); 1.9842 + return chart; 1.9843 +}; 1.9844 +// Code adapted from Jason Davies' "Parallel Coordinates" 1.9845 +// http://bl.ocks.org/jasondavies/1341281 1.9846 +nv.models.parallelCoordinates = function() { 1.9847 + "use strict"; 1.9848 + 1.9849 + //============================================================ 1.9850 + // Public Variables with Default Settings 1.9851 + //------------------------------------------------------------ 1.9852 + 1.9853 + var margin = {top: 30, right: 0, bottom: 10, left: 0} 1.9854 + , width = null 1.9855 + , height = null 1.9856 + , x = d3.scale.ordinal() 1.9857 + , y = {} 1.9858 + , dimensionNames = [] 1.9859 + , dimensionFormats = [] 1.9860 + , color = nv.utils.defaultColor() 1.9861 + , filters = [] 1.9862 + , active = [] 1.9863 + , dragging = [] 1.9864 + , lineTension = 1 1.9865 + , dispatch = d3.dispatch('brush', 'elementMouseover', 'elementMouseout') 1.9866 + ; 1.9867 + 1.9868 + //============================================================ 1.9869 + // Private Variables 1.9870 + //------------------------------------------------------------ 1.9871 + 1.9872 + function chart(selection) { 1.9873 + selection.each(function(data) { 1.9874 + var container = d3.select(this); 1.9875 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.9876 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.9877 + 1.9878 + nv.utils.initSVG(container); 1.9879 + 1.9880 + active = data; //set all active before first brush call 1.9881 + 1.9882 + // Setup Scales 1.9883 + x.rangePoints([0, availableWidth], 1).domain(dimensionNames); 1.9884 + 1.9885 + //Set as true if all values on an axis are missing. 1.9886 + var onlyNanValues = {}; 1.9887 + // Extract the list of dimensions and create a scale for each. 1.9888 + dimensionNames.forEach(function(d) { 1.9889 + var extent = d3.extent(data, function(p) { return +p[d]; }); 1.9890 + onlyNanValues[d] = false; 1.9891 + //If there is no values to display on an axis, set the extent to 0 1.9892 + if (extent[0] === undefined) { 1.9893 + onlyNanValues[d] = true; 1.9894 + extent[0] = 0; 1.9895 + extent[1] = 0; 1.9896 + } 1.9897 + //Scale axis if there is only one value 1.9898 + if (extent[0] === extent[1]) { 1.9899 + extent[0] = extent[0] - 1; 1.9900 + extent[1] = extent[1] + 1; 1.9901 + } 1.9902 + //Use 90% of (availableHeight - 12) for the axis range, 12 reprensenting the space necessary to display "undefined values" text. 1.9903 + //The remaining 10% are used to display the missingValue line. 1.9904 + y[d] = d3.scale.linear() 1.9905 + .domain(extent) 1.9906 + .range([(availableHeight - 12) * 0.9, 0]); 1.9907 + 1.9908 + y[d].brush = d3.svg.brush().y(y[d]).on('brush', brush); 1.9909 + 1.9910 + return d != 'name'; 1.9911 + }); 1.9912 + 1.9913 + // Setup containers and skeleton of chart 1.9914 + var wrap = container.selectAll('g.nv-wrap.nv-parallelCoordinates').data([data]); 1.9915 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-parallelCoordinates'); 1.9916 + var gEnter = wrapEnter.append('g'); 1.9917 + var g = wrap.select('g'); 1.9918 + 1.9919 + gEnter.append('g').attr('class', 'nv-parallelCoordinates background'); 1.9920 + gEnter.append('g').attr('class', 'nv-parallelCoordinates foreground'); 1.9921 + gEnter.append('g').attr('class', 'nv-parallelCoordinates missingValuesline'); 1.9922 + 1.9923 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.9924 + 1.9925 + var line = d3.svg.line().interpolate('cardinal').tension(lineTension), 1.9926 + axis = d3.svg.axis().orient('left'), 1.9927 + axisDrag = d3.behavior.drag() 1.9928 + .on('dragstart', dragStart) 1.9929 + .on('drag', dragMove) 1.9930 + .on('dragend', dragEnd); 1.9931 + 1.9932 + //Add missing value line at the bottom of the chart 1.9933 + var missingValuesline, missingValueslineText; 1.9934 + var step = x.range()[1] - x.range()[0]; 1.9935 + var axisWithMissingValues = []; 1.9936 + var lineData = [0 + step / 2, availableHeight - 12, availableWidth - step / 2, availableHeight - 12]; 1.9937 + missingValuesline = wrap.select('.missingValuesline').selectAll('line').data([lineData]); 1.9938 + missingValuesline.enter().append('line'); 1.9939 + missingValuesline.exit().remove(); 1.9940 + missingValuesline.attr("x1", function(d) { return d[0]; }) 1.9941 + .attr("y1", function(d) { return d[1]; }) 1.9942 + .attr("x2", function(d) { return d[2]; }) 1.9943 + .attr("y2", function(d) { return d[3]; }); 1.9944 + 1.9945 + //Add the text "undefined values" under the missing value line 1.9946 + missingValueslineText = wrap.select('.missingValuesline').selectAll('text').data(["undefined values"]); 1.9947 + missingValueslineText.append('text').data(["undefined values"]); 1.9948 + missingValueslineText.enter().append('text'); 1.9949 + missingValueslineText.exit().remove(); 1.9950 + missingValueslineText.attr("y", availableHeight) 1.9951 + //To have the text right align with the missingValues line, substract 92 representing the text size. 1.9952 + .attr("x", availableWidth - 92 - step / 2) 1.9953 + .text(function(d) { return d; }); 1.9954 + 1.9955 + // Add grey background lines for context. 1.9956 + var background = wrap.select('.background').selectAll('path').data(data); 1.9957 + background.enter().append('path'); 1.9958 + background.exit().remove(); 1.9959 + background.attr('d', path); 1.9960 + 1.9961 + // Add blue foreground lines for focus. 1.9962 + var foreground = wrap.select('.foreground').selectAll('path').data(data); 1.9963 + foreground.enter().append('path') 1.9964 + foreground.exit().remove(); 1.9965 + foreground.attr('d', path).attr('stroke', color); 1.9966 + foreground.on("mouseover", function (d, i) { 1.9967 + d3.select(this).classed('hover', true); 1.9968 + dispatch.elementMouseover({ 1.9969 + label: d.name, 1.9970 + data: d.data, 1.9971 + index: i, 1.9972 + pos: [d3.mouse(this.parentNode)[0], d3.mouse(this.parentNode)[1]] 1.9973 + }); 1.9974 + 1.9975 + }); 1.9976 + foreground.on("mouseout", function (d, i) { 1.9977 + d3.select(this).classed('hover', false); 1.9978 + dispatch.elementMouseout({ 1.9979 + label: d.name, 1.9980 + data: d.data, 1.9981 + index: i 1.9982 + }); 1.9983 + }); 1.9984 + 1.9985 + // Add a group element for each dimension. 1.9986 + var dimensions = g.selectAll('.dimension').data(dimensionNames); 1.9987 + var dimensionsEnter = dimensions.enter().append('g').attr('class', 'nv-parallelCoordinates dimension'); 1.9988 + dimensionsEnter.append('g').attr('class', 'nv-parallelCoordinates nv-axis'); 1.9989 + dimensionsEnter.append('g').attr('class', 'nv-parallelCoordinates-brush'); 1.9990 + dimensionsEnter.append('text').attr('class', 'nv-parallelCoordinates nv-label'); 1.9991 + 1.9992 + dimensions.attr('transform', function(d) { return 'translate(' + x(d) + ',0)'; }); 1.9993 + dimensions.exit().remove(); 1.9994 + 1.9995 + // Add an axis and title. 1.9996 + dimensions.select('.nv-label') 1.9997 + .style("cursor", "move") 1.9998 + .attr('dy', '-1em') 1.9999 + .attr('text-anchor', 'middle') 1.10000 + .text(String) 1.10001 + .on("mouseover", function(d, i) { 1.10002 + dispatch.elementMouseover({ 1.10003 + dim: d, 1.10004 + pos: [d3.mouse(this.parentNode.parentNode)[0], d3.mouse(this.parentNode.parentNode)[1]] 1.10005 + }); 1.10006 + }) 1.10007 + .on("mouseout", function(d, i) { 1.10008 + dispatch.elementMouseout({ 1.10009 + dim: d 1.10010 + }); 1.10011 + }) 1.10012 + .call(axisDrag); 1.10013 + 1.10014 + dimensions.select('.nv-axis') 1.10015 + .each(function (d, i) { 1.10016 + d3.select(this).call(axis.scale(y[d]).tickFormat(d3.format(dimensionFormats[i]))); 1.10017 + }); 1.10018 + 1.10019 + dimensions.select('.nv-parallelCoordinates-brush') 1.10020 + .each(function (d) { 1.10021 + d3.select(this).call(y[d].brush); 1.10022 + }) 1.10023 + .selectAll('rect') 1.10024 + .attr('x', -8) 1.10025 + .attr('width', 16); 1.10026 + 1.10027 + // Returns the path for a given data point. 1.10028 + function path(d) { 1.10029 + return line(dimensionNames.map(function (p) { 1.10030 + //If value if missing, put the value on the missing value line 1.10031 + if(isNaN(d[p]) || isNaN(parseFloat(d[p]))) { 1.10032 + var domain = y[p].domain(); 1.10033 + var range = y[p].range(); 1.10034 + var min = domain[0] - (domain[1] - domain[0]) / 9; 1.10035 + 1.10036 + //If it's not already the case, allow brush to select undefined values 1.10037 + if(axisWithMissingValues.indexOf(p) < 0) { 1.10038 + 1.10039 + var newscale = d3.scale.linear().domain([min, domain[1]]).range([availableHeight - 12, range[1]]); 1.10040 + y[p].brush.y(newscale); 1.10041 + axisWithMissingValues.push(p); 1.10042 + } 1.10043 + 1.10044 + return [x(p), y[p](min)]; 1.10045 + } 1.10046 + 1.10047 + //If parallelCoordinate contain missing values show the missing values line otherwise, hide it. 1.10048 + if(axisWithMissingValues.length > 0) { 1.10049 + missingValuesline.style("display", "inline"); 1.10050 + missingValueslineText.style("display", "inline"); 1.10051 + } else { 1.10052 + missingValuesline.style("display", "none"); 1.10053 + missingValueslineText.style("display", "none"); 1.10054 + } 1.10055 + 1.10056 + return [x(p), y[p](d[p])]; 1.10057 + })); 1.10058 + } 1.10059 + 1.10060 + // Handles a brush event, toggling the display of foreground lines. 1.10061 + function brush() { 1.10062 + var actives = dimensionNames.filter(function(p) { return !y[p].brush.empty(); }), 1.10063 + extents = actives.map(function(p) { return y[p].brush.extent(); }); 1.10064 + 1.10065 + filters = []; //erase current filters 1.10066 + actives.forEach(function(d,i) { 1.10067 + filters[i] = { 1.10068 + dimension: d, 1.10069 + extent: extents[i] 1.10070 + } 1.10071 + }); 1.10072 + 1.10073 + active = []; //erase current active list 1.10074 + foreground.style('display', function(d) { 1.10075 + var isActive = actives.every(function(p, i) { 1.10076 + if(isNaN(d[p]) && extents[i][0] == y[p].brush.y().domain()[0]) return true; 1.10077 + return extents[i][0] <= d[p] && d[p] <= extents[i][1]; 1.10078 + }); 1.10079 + if (isActive) active.push(d); 1.10080 + return isActive ? null : 'none'; 1.10081 + }); 1.10082 + 1.10083 + dispatch.brush({ 1.10084 + filters: filters, 1.10085 + active: active 1.10086 + }); 1.10087 + } 1.10088 + 1.10089 + function dragStart(d, i) { 1.10090 + dragging[d] = this.parentNode.__origin__ = x(d); 1.10091 + background.attr("visibility", "hidden"); 1.10092 + 1.10093 + } 1.10094 + 1.10095 + function dragMove(d, i) { 1.10096 + dragging[d] = Math.min(availableWidth, Math.max(0, this.parentNode.__origin__ += d3.event.x)); 1.10097 + foreground.attr("d", path); 1.10098 + dimensionNames.sort(function (a, b) { return position(a) - position(b); }); 1.10099 + x.domain(dimensionNames); 1.10100 + dimensions.attr("transform", function(d) { return "translate(" + position(d) + ")"; }); 1.10101 + } 1.10102 + 1.10103 + function dragEnd(d, i) { 1.10104 + delete this.parentNode.__origin__; 1.10105 + delete dragging[d]; 1.10106 + d3.select(this.parentNode).attr("transform", "translate(" + x(d) + ")"); 1.10107 + foreground 1.10108 + .attr("d", path); 1.10109 + background 1.10110 + .attr("d", path) 1.10111 + .attr("visibility", null); 1.10112 + 1.10113 + } 1.10114 + 1.10115 + function position(d) { 1.10116 + var v = dragging[d]; 1.10117 + return v == null ? x(d) : v; 1.10118 + } 1.10119 + }); 1.10120 + 1.10121 + return chart; 1.10122 + } 1.10123 + 1.10124 + //============================================================ 1.10125 + // Expose Public Variables 1.10126 + //------------------------------------------------------------ 1.10127 + 1.10128 + chart.dispatch = dispatch; 1.10129 + chart.options = nv.utils.optionsFunc.bind(chart); 1.10130 + 1.10131 + chart._options = Object.create({}, { 1.10132 + // simple options, just get/set the necessary values 1.10133 + width: {get: function(){return width;}, set: function(_){width= _;}}, 1.10134 + height: {get: function(){return height;}, set: function(_){height= _;}}, 1.10135 + dimensionNames: {get: function() { return dimensionNames;}, set: function(_){dimensionNames= _;}}, 1.10136 + dimensionFormats : {get: function(){return dimensionFormats;}, set: function (_){dimensionFormats=_;}}, 1.10137 + lineTension: {get: function(){return lineTension;}, set: function(_){lineTension = _;}}, 1.10138 + 1.10139 + // deprecated options 1.10140 + dimensions: {get: function (){return dimensionNames;}, set: function(_){ 1.10141 + // deprecated after 1.8.1 1.10142 + nv.deprecated('dimensions', 'use dimensionNames instead'); 1.10143 + dimensionNames = _; 1.10144 + }}, 1.10145 + 1.10146 + // options that require extra logic in the setter 1.10147 + margin: {get: function(){return margin;}, set: function(_){ 1.10148 + margin.top = _.top !== undefined ? _.top : margin.top; 1.10149 + margin.right = _.right !== undefined ? _.right : margin.right; 1.10150 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.10151 + margin.left = _.left !== undefined ? _.left : margin.left; 1.10152 + }}, 1.10153 + color: {get: function(){return color;}, set: function(_){ 1.10154 + color = nv.utils.getColor(_); 1.10155 + }} 1.10156 + }); 1.10157 + 1.10158 + nv.utils.initOptions(chart); 1.10159 + return chart; 1.10160 +}; 1.10161 +nv.models.pie = function() { 1.10162 + "use strict"; 1.10163 + 1.10164 + //============================================================ 1.10165 + // Public Variables with Default Settings 1.10166 + //------------------------------------------------------------ 1.10167 + 1.10168 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.10169 + , width = 500 1.10170 + , height = 500 1.10171 + , getX = function(d) { return d.x } 1.10172 + , getY = function(d) { return d.y } 1.10173 + , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one 1.10174 + , container = null 1.10175 + , color = nv.utils.defaultColor() 1.10176 + , valueFormat = d3.format(',.2f') 1.10177 + , showLabels = true 1.10178 + , labelsOutside = false 1.10179 + , labelType = "key" 1.10180 + , labelThreshold = .02 //if slice percentage is under this, don't show label 1.10181 + , donut = false 1.10182 + , title = false 1.10183 + , growOnHover = true 1.10184 + , titleOffset = 0 1.10185 + , labelSunbeamLayout = false 1.10186 + , startAngle = false 1.10187 + , padAngle = false 1.10188 + , endAngle = false 1.10189 + , cornerRadius = 0 1.10190 + , donutRatio = 0.5 1.10191 + , arcsRadius = [] 1.10192 + , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd') 1.10193 + ; 1.10194 + 1.10195 + var arcs = []; 1.10196 + var arcsOver = []; 1.10197 + 1.10198 + //============================================================ 1.10199 + // chart function 1.10200 + //------------------------------------------------------------ 1.10201 + 1.10202 + var renderWatch = nv.utils.renderWatch(dispatch); 1.10203 + 1.10204 + function chart(selection) { 1.10205 + renderWatch.reset(); 1.10206 + selection.each(function(data) { 1.10207 + var availableWidth = width - margin.left - margin.right 1.10208 + , availableHeight = height - margin.top - margin.bottom 1.10209 + , radius = Math.min(availableWidth, availableHeight) / 2 1.10210 + , arcsRadiusOuter = [] 1.10211 + , arcsRadiusInner = [] 1.10212 + ; 1.10213 + 1.10214 + container = d3.select(this) 1.10215 + if (arcsRadius.length === 0) { 1.10216 + var outer = radius - radius / 5; 1.10217 + var inner = donutRatio * radius; 1.10218 + for (var i = 0; i < data[0].length; i++) { 1.10219 + arcsRadiusOuter.push(outer); 1.10220 + arcsRadiusInner.push(inner); 1.10221 + } 1.10222 + } else { 1.10223 + arcsRadiusOuter = arcsRadius.map(function (d) { return (d.outer - d.outer / 5) * radius; }); 1.10224 + arcsRadiusInner = arcsRadius.map(function (d) { return (d.inner - d.inner / 5) * radius; }); 1.10225 + donutRatio = d3.min(arcsRadius.map(function (d) { return (d.inner - d.inner / 5); })); 1.10226 + } 1.10227 + nv.utils.initSVG(container); 1.10228 + 1.10229 + // Setup containers and skeleton of chart 1.10230 + var wrap = container.selectAll('.nv-wrap.nv-pie').data(data); 1.10231 + var wrapEnter = wrap.enter().append('g').attr('class','nvd3 nv-wrap nv-pie nv-chart-' + id); 1.10232 + var gEnter = wrapEnter.append('g'); 1.10233 + var g = wrap.select('g'); 1.10234 + var g_pie = gEnter.append('g').attr('class', 'nv-pie'); 1.10235 + gEnter.append('g').attr('class', 'nv-pieLabels'); 1.10236 + 1.10237 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.10238 + g.select('.nv-pie').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')'); 1.10239 + g.select('.nv-pieLabels').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')'); 1.10240 + 1.10241 + // 1.10242 + container.on('click', function(d,i) { 1.10243 + dispatch.chartClick({ 1.10244 + data: d, 1.10245 + index: i, 1.10246 + pos: d3.event, 1.10247 + id: id 1.10248 + }); 1.10249 + }); 1.10250 + 1.10251 + arcs = []; 1.10252 + arcsOver = []; 1.10253 + for (var i = 0; i < data[0].length; i++) { 1.10254 + 1.10255 + var arc = d3.svg.arc().outerRadius(arcsRadiusOuter[i]); 1.10256 + var arcOver = d3.svg.arc().outerRadius(arcsRadiusOuter[i] + 5); 1.10257 + 1.10258 + if (startAngle !== false) { 1.10259 + arc.startAngle(startAngle); 1.10260 + arcOver.startAngle(startAngle); 1.10261 + } 1.10262 + if (endAngle !== false) { 1.10263 + arc.endAngle(endAngle); 1.10264 + arcOver.endAngle(endAngle); 1.10265 + } 1.10266 + if (donut) { 1.10267 + arc.innerRadius(arcsRadiusInner[i]); 1.10268 + arcOver.innerRadius(arcsRadiusInner[i]); 1.10269 + } 1.10270 + 1.10271 + if (arc.cornerRadius && cornerRadius) { 1.10272 + arc.cornerRadius(cornerRadius); 1.10273 + arcOver.cornerRadius(cornerRadius); 1.10274 + } 1.10275 + 1.10276 + arcs.push(arc); 1.10277 + arcsOver.push(arcOver); 1.10278 + } 1.10279 + 1.10280 + // Setup the Pie chart and choose the data element 1.10281 + var pie = d3.layout.pie() 1.10282 + .sort(null) 1.10283 + .value(function(d) { return d.disabled ? 0 : getY(d) }); 1.10284 + 1.10285 + // padAngle added in d3 3.5 1.10286 + if (pie.padAngle && padAngle) { 1.10287 + pie.padAngle(padAngle); 1.10288 + } 1.10289 + 1.10290 + // if title is specified and donut, put it in the middle 1.10291 + if (donut && title) { 1.10292 + g_pie.append("text").attr('class', 'nv-pie-title'); 1.10293 + 1.10294 + wrap.select('.nv-pie-title') 1.10295 + .style("text-anchor", "middle") 1.10296 + .text(function (d) { 1.10297 + return title; 1.10298 + }) 1.10299 + .style("font-size", (Math.min(availableWidth, availableHeight)) * donutRatio * 2 / (title.length + 2) + "px") 1.10300 + .attr("dy", "0.35em") // trick to vertically center text 1.10301 + .attr('transform', function(d, i) { 1.10302 + return 'translate(0, '+ titleOffset + ')'; 1.10303 + }); 1.10304 + } 1.10305 + 1.10306 + var slices = wrap.select('.nv-pie').selectAll('.nv-slice').data(pie); 1.10307 + var pieLabels = wrap.select('.nv-pieLabels').selectAll('.nv-label').data(pie); 1.10308 + 1.10309 + slices.exit().remove(); 1.10310 + pieLabels.exit().remove(); 1.10311 + 1.10312 + var ae = slices.enter().append('g'); 1.10313 + ae.attr('class', 'nv-slice'); 1.10314 + ae.on('mouseover', function(d, i) { 1.10315 + d3.select(this).classed('hover', true); 1.10316 + if (growOnHover) { 1.10317 + d3.select(this).select("path").transition() 1.10318 + .duration(70) 1.10319 + .attr("d", arcsOver[i]); 1.10320 + } 1.10321 + dispatch.elementMouseover({ 1.10322 + data: d.data, 1.10323 + index: i, 1.10324 + color: d3.select(this).style("fill") 1.10325 + }); 1.10326 + }); 1.10327 + ae.on('mouseout', function(d, i) { 1.10328 + d3.select(this).classed('hover', false); 1.10329 + if (growOnHover) { 1.10330 + d3.select(this).select("path").transition() 1.10331 + .duration(50) 1.10332 + .attr("d", arcs[i]); 1.10333 + } 1.10334 + dispatch.elementMouseout({data: d.data, index: i}); 1.10335 + }); 1.10336 + ae.on('mousemove', function(d, i) { 1.10337 + dispatch.elementMousemove({data: d.data, index: i}); 1.10338 + }); 1.10339 + ae.on('click', function(d, i) { 1.10340 + dispatch.elementClick({ 1.10341 + data: d.data, 1.10342 + index: i, 1.10343 + color: d3.select(this).style("fill") 1.10344 + }); 1.10345 + }); 1.10346 + ae.on('dblclick', function(d, i) { 1.10347 + dispatch.elementDblClick({ 1.10348 + data: d.data, 1.10349 + index: i, 1.10350 + color: d3.select(this).style("fill") 1.10351 + }); 1.10352 + }); 1.10353 + 1.10354 + slices.attr('fill', function(d,i) { return color(d.data, i); }); 1.10355 + slices.attr('stroke', function(d,i) { return color(d.data, i); }); 1.10356 + 1.10357 + var paths = ae.append('path').each(function(d) { 1.10358 + this._current = d; 1.10359 + }); 1.10360 + 1.10361 + slices.select('path') 1.10362 + .transition() 1.10363 + .attr('d', function (d, i) { return arcs[i](d); }) 1.10364 + .attrTween('d', arcTween); 1.10365 + 1.10366 + if (showLabels) { 1.10367 + // This does the normal label 1.10368 + var labelsArc = []; 1.10369 + for (var i = 0; i < data[0].length; i++) { 1.10370 + labelsArc.push(arcs[i]); 1.10371 + 1.10372 + if (labelsOutside) { 1.10373 + if (donut) { 1.10374 + labelsArc[i] = d3.svg.arc().outerRadius(arcs[i].outerRadius()); 1.10375 + if (startAngle !== false) labelsArc[i].startAngle(startAngle); 1.10376 + if (endAngle !== false) labelsArc[i].endAngle(endAngle); 1.10377 + } 1.10378 + } else if (!donut) { 1.10379 + labelsArc[i].innerRadius(0); 1.10380 + } 1.10381 + } 1.10382 + 1.10383 + pieLabels.enter().append("g").classed("nv-label",true).each(function(d,i) { 1.10384 + var group = d3.select(this); 1.10385 + 1.10386 + group.attr('transform', function (d, i) { 1.10387 + if (labelSunbeamLayout) { 1.10388 + d.outerRadius = arcsRadiusOuter[i] + 10; // Set Outer Coordinate 1.10389 + d.innerRadius = arcsRadiusOuter[i] + 15; // Set Inner Coordinate 1.10390 + var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI); 1.10391 + if ((d.startAngle + d.endAngle) / 2 < Math.PI) { 1.10392 + rotateAngle -= 90; 1.10393 + } else { 1.10394 + rotateAngle += 90; 1.10395 + } 1.10396 + return 'translate(' + labelsArc[i].centroid(d) + ') rotate(' + rotateAngle + ')'; 1.10397 + } else { 1.10398 + d.outerRadius = radius + 10; // Set Outer Coordinate 1.10399 + d.innerRadius = radius + 15; // Set Inner Coordinate 1.10400 + return 'translate(' + labelsArc[i].centroid(d) + ')' 1.10401 + } 1.10402 + }); 1.10403 + 1.10404 + group.append('rect') 1.10405 + .style('stroke', '#fff') 1.10406 + .style('fill', '#fff') 1.10407 + .attr("rx", 3) 1.10408 + .attr("ry", 3); 1.10409 + 1.10410 + group.append('text') 1.10411 + .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 1.10412 + .style('fill', '#000') 1.10413 + }); 1.10414 + 1.10415 + var labelLocationHash = {}; 1.10416 + var avgHeight = 14; 1.10417 + var avgWidth = 140; 1.10418 + var createHashKey = function(coordinates) { 1.10419 + return Math.floor(coordinates[0]/avgWidth) * avgWidth + ',' + Math.floor(coordinates[1]/avgHeight) * avgHeight; 1.10420 + }; 1.10421 + 1.10422 + pieLabels.watchTransition(renderWatch, 'pie labels').attr('transform', function (d, i) { 1.10423 + if (labelSunbeamLayout) { 1.10424 + d.outerRadius = arcsRadiusOuter[i] + 10; // Set Outer Coordinate 1.10425 + d.innerRadius = arcsRadiusOuter[i] + 15; // Set Inner Coordinate 1.10426 + var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI); 1.10427 + if ((d.startAngle + d.endAngle) / 2 < Math.PI) { 1.10428 + rotateAngle -= 90; 1.10429 + } else { 1.10430 + rotateAngle += 90; 1.10431 + } 1.10432 + return 'translate(' + labelsArc[i].centroid(d) + ') rotate(' + rotateAngle + ')'; 1.10433 + } else { 1.10434 + d.outerRadius = radius + 10; // Set Outer Coordinate 1.10435 + d.innerRadius = radius + 15; // Set Inner Coordinate 1.10436 + 1.10437 + /* 1.10438 + Overlapping pie labels are not good. What this attempts to do is, prevent overlapping. 1.10439 + Each label location is hashed, and if a hash collision occurs, we assume an overlap. 1.10440 + Adjust the label's y-position to remove the overlap. 1.10441 + */ 1.10442 + var center = labelsArc[i].centroid(d); 1.10443 + if (d.value) { 1.10444 + var hashKey = createHashKey(center); 1.10445 + if (labelLocationHash[hashKey]) { 1.10446 + center[1] -= avgHeight; 1.10447 + } 1.10448 + labelLocationHash[createHashKey(center)] = true; 1.10449 + } 1.10450 + return 'translate(' + center + ')' 1.10451 + } 1.10452 + }); 1.10453 + 1.10454 + pieLabels.select(".nv-label text") 1.10455 + .style('text-anchor', function(d,i) { 1.10456 + //center the text on it's origin or begin/end if orthogonal aligned 1.10457 + return labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle'; 1.10458 + }) 1.10459 + .text(function(d, i) { 1.10460 + var percent = (d.endAngle - d.startAngle) / (2 * Math.PI); 1.10461 + var label = ''; 1.10462 + if (!d.value || percent < labelThreshold) return ''; 1.10463 + 1.10464 + if(typeof labelType === 'function') { 1.10465 + label = labelType(d, i, { 1.10466 + 'key': getX(d.data), 1.10467 + 'value': getY(d.data), 1.10468 + 'percent': valueFormat(percent) 1.10469 + }); 1.10470 + } else { 1.10471 + switch (labelType) { 1.10472 + case 'key': 1.10473 + label = getX(d.data); 1.10474 + break; 1.10475 + case 'value': 1.10476 + label = valueFormat(getY(d.data)); 1.10477 + break; 1.10478 + case 'percent': 1.10479 + label = d3.format('%')(percent); 1.10480 + break; 1.10481 + } 1.10482 + } 1.10483 + return label; 1.10484 + }) 1.10485 + ; 1.10486 + } 1.10487 + 1.10488 + 1.10489 + // Computes the angle of an arc, converting from radians to degrees. 1.10490 + function angle(d) { 1.10491 + var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90; 1.10492 + return a > 90 ? a - 180 : a; 1.10493 + } 1.10494 + 1.10495 + function arcTween(a, idx) { 1.10496 + a.endAngle = isNaN(a.endAngle) ? 0 : a.endAngle; 1.10497 + a.startAngle = isNaN(a.startAngle) ? 0 : a.startAngle; 1.10498 + if (!donut) a.innerRadius = 0; 1.10499 + var i = d3.interpolate(this._current, a); 1.10500 + this._current = i(0); 1.10501 + return function (t) { 1.10502 + return arcs[idx](i(t)); 1.10503 + }; 1.10504 + } 1.10505 + }); 1.10506 + 1.10507 + renderWatch.renderEnd('pie immediate'); 1.10508 + return chart; 1.10509 + } 1.10510 + 1.10511 + //============================================================ 1.10512 + // Expose Public Variables 1.10513 + //------------------------------------------------------------ 1.10514 + 1.10515 + chart.dispatch = dispatch; 1.10516 + chart.options = nv.utils.optionsFunc.bind(chart); 1.10517 + 1.10518 + chart._options = Object.create({}, { 1.10519 + // simple options, just get/set the necessary values 1.10520 + arcsRadius: { get: function () { return arcsRadius; }, set: function (_) { arcsRadius = _; } }, 1.10521 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.10522 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.10523 + showLabels: {get: function(){return showLabels;}, set: function(_){showLabels=_;}}, 1.10524 + title: {get: function(){return title;}, set: function(_){title=_;}}, 1.10525 + titleOffset: {get: function(){return titleOffset;}, set: function(_){titleOffset=_;}}, 1.10526 + labelThreshold: {get: function(){return labelThreshold;}, set: function(_){labelThreshold=_;}}, 1.10527 + valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}}, 1.10528 + x: {get: function(){return getX;}, set: function(_){getX=_;}}, 1.10529 + id: {get: function(){return id;}, set: function(_){id=_;}}, 1.10530 + endAngle: {get: function(){return endAngle;}, set: function(_){endAngle=_;}}, 1.10531 + startAngle: {get: function(){return startAngle;}, set: function(_){startAngle=_;}}, 1.10532 + padAngle: {get: function(){return padAngle;}, set: function(_){padAngle=_;}}, 1.10533 + cornerRadius: {get: function(){return cornerRadius;}, set: function(_){cornerRadius=_;}}, 1.10534 + donutRatio: {get: function(){return donutRatio;}, set: function(_){donutRatio=_;}}, 1.10535 + labelsOutside: {get: function(){return labelsOutside;}, set: function(_){labelsOutside=_;}}, 1.10536 + labelSunbeamLayout: {get: function(){return labelSunbeamLayout;}, set: function(_){labelSunbeamLayout=_;}}, 1.10537 + donut: {get: function(){return donut;}, set: function(_){donut=_;}}, 1.10538 + growOnHover: {get: function(){return growOnHover;}, set: function(_){growOnHover=_;}}, 1.10539 + 1.10540 + // depreciated after 1.7.1 1.10541 + pieLabelsOutside: {get: function(){return labelsOutside;}, set: function(_){ 1.10542 + labelsOutside=_; 1.10543 + nv.deprecated('pieLabelsOutside', 'use labelsOutside instead'); 1.10544 + }}, 1.10545 + // depreciated after 1.7.1 1.10546 + donutLabelsOutside: {get: function(){return labelsOutside;}, set: function(_){ 1.10547 + labelsOutside=_; 1.10548 + nv.deprecated('donutLabelsOutside', 'use labelsOutside instead'); 1.10549 + }}, 1.10550 + // deprecated after 1.7.1 1.10551 + labelFormat: {get: function(){ return valueFormat;}, set: function(_) { 1.10552 + valueFormat=_; 1.10553 + nv.deprecated('labelFormat','use valueFormat instead'); 1.10554 + }}, 1.10555 + 1.10556 + // options that require extra logic in the setter 1.10557 + margin: {get: function(){return margin;}, set: function(_){ 1.10558 + margin.top = typeof _.top != 'undefined' ? _.top : margin.top; 1.10559 + margin.right = typeof _.right != 'undefined' ? _.right : margin.right; 1.10560 + margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom; 1.10561 + margin.left = typeof _.left != 'undefined' ? _.left : margin.left; 1.10562 + }}, 1.10563 + y: {get: function(){return getY;}, set: function(_){ 1.10564 + getY=d3.functor(_); 1.10565 + }}, 1.10566 + color: {get: function(){return color;}, set: function(_){ 1.10567 + color=nv.utils.getColor(_); 1.10568 + }}, 1.10569 + labelType: {get: function(){return labelType;}, set: function(_){ 1.10570 + labelType= _ || 'key'; 1.10571 + }} 1.10572 + }); 1.10573 + 1.10574 + nv.utils.initOptions(chart); 1.10575 + return chart; 1.10576 +}; 1.10577 +nv.models.pieChart = function() { 1.10578 + "use strict"; 1.10579 + 1.10580 + //============================================================ 1.10581 + // Public Variables with Default Settings 1.10582 + //------------------------------------------------------------ 1.10583 + 1.10584 + var pie = nv.models.pie(); 1.10585 + var legend = nv.models.legend(); 1.10586 + var tooltip = nv.models.tooltip(); 1.10587 + 1.10588 + var margin = {top: 30, right: 20, bottom: 20, left: 20} 1.10589 + , width = null 1.10590 + , height = null 1.10591 + , showLegend = true 1.10592 + , legendPosition = "top" 1.10593 + , color = nv.utils.defaultColor() 1.10594 + , state = nv.utils.state() 1.10595 + , defaultState = null 1.10596 + , noData = null 1.10597 + , duration = 250 1.10598 + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState','renderEnd') 1.10599 + ; 1.10600 + 1.10601 + tooltip 1.10602 + .headerEnabled(false) 1.10603 + .duration(0) 1.10604 + .valueFormatter(function(d, i) { 1.10605 + return pie.valueFormat()(d, i); 1.10606 + }); 1.10607 + 1.10608 + //============================================================ 1.10609 + // Private Variables 1.10610 + //------------------------------------------------------------ 1.10611 + 1.10612 + var renderWatch = nv.utils.renderWatch(dispatch); 1.10613 + 1.10614 + var stateGetter = function(data) { 1.10615 + return function(){ 1.10616 + return { 1.10617 + active: data.map(function(d) { return !d.disabled }) 1.10618 + }; 1.10619 + } 1.10620 + }; 1.10621 + 1.10622 + var stateSetter = function(data) { 1.10623 + return function(state) { 1.10624 + if (state.active !== undefined) { 1.10625 + data.forEach(function (series, i) { 1.10626 + series.disabled = !state.active[i]; 1.10627 + }); 1.10628 + } 1.10629 + } 1.10630 + }; 1.10631 + 1.10632 + //============================================================ 1.10633 + // Chart function 1.10634 + //------------------------------------------------------------ 1.10635 + 1.10636 + function chart(selection) { 1.10637 + renderWatch.reset(); 1.10638 + renderWatch.models(pie); 1.10639 + 1.10640 + selection.each(function(data) { 1.10641 + var container = d3.select(this); 1.10642 + nv.utils.initSVG(container); 1.10643 + 1.10644 + var that = this; 1.10645 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.10646 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.10647 + 1.10648 + chart.update = function() { container.transition().call(chart); }; 1.10649 + chart.container = this; 1.10650 + 1.10651 + state.setter(stateSetter(data), chart.update) 1.10652 + .getter(stateGetter(data)) 1.10653 + .update(); 1.10654 + 1.10655 + //set state.disabled 1.10656 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.10657 + 1.10658 + if (!defaultState) { 1.10659 + var key; 1.10660 + defaultState = {}; 1.10661 + for (key in state) { 1.10662 + if (state[key] instanceof Array) 1.10663 + defaultState[key] = state[key].slice(0); 1.10664 + else 1.10665 + defaultState[key] = state[key]; 1.10666 + } 1.10667 + } 1.10668 + 1.10669 + // Display No Data message if there's nothing to show. 1.10670 + if (!data || !data.length) { 1.10671 + nv.utils.noData(chart, container); 1.10672 + return chart; 1.10673 + } else { 1.10674 + container.selectAll('.nv-noData').remove(); 1.10675 + } 1.10676 + 1.10677 + // Setup containers and skeleton of chart 1.10678 + var wrap = container.selectAll('g.nv-wrap.nv-pieChart').data([data]); 1.10679 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-pieChart').append('g'); 1.10680 + var g = wrap.select('g'); 1.10681 + 1.10682 + gEnter.append('g').attr('class', 'nv-pieWrap'); 1.10683 + gEnter.append('g').attr('class', 'nv-legendWrap'); 1.10684 + 1.10685 + // Legend 1.10686 + if (showLegend) { 1.10687 + if (legendPosition === "top") { 1.10688 + legend.width( availableWidth ).key(pie.x()); 1.10689 + 1.10690 + wrap.select('.nv-legendWrap') 1.10691 + .datum(data) 1.10692 + .call(legend); 1.10693 + 1.10694 + if ( margin.top != legend.height()) { 1.10695 + margin.top = legend.height(); 1.10696 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.10697 + } 1.10698 + 1.10699 + wrap.select('.nv-legendWrap') 1.10700 + .attr('transform', 'translate(0,' + (-margin.top) +')'); 1.10701 + } else if (legendPosition === "right") { 1.10702 + var legendWidth = nv.models.legend().width(); 1.10703 + if (availableWidth / 2 < legendWidth) { 1.10704 + legendWidth = (availableWidth / 2) 1.10705 + } 1.10706 + legend.height(availableHeight).key(pie.x()); 1.10707 + legend.width(legendWidth); 1.10708 + availableWidth -= legend.width(); 1.10709 + 1.10710 + wrap.select('.nv-legendWrap') 1.10711 + .datum(data) 1.10712 + .call(legend) 1.10713 + .attr('transform', 'translate(' + (availableWidth) +',0)'); 1.10714 + } 1.10715 + } 1.10716 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.10717 + 1.10718 + // Main Chart Component(s) 1.10719 + pie.width(availableWidth).height(availableHeight); 1.10720 + var pieWrap = g.select('.nv-pieWrap').datum([data]); 1.10721 + d3.transition(pieWrap).call(pie); 1.10722 + 1.10723 + //============================================================ 1.10724 + // Event Handling/Dispatching (in chart's scope) 1.10725 + //------------------------------------------------------------ 1.10726 + 1.10727 + legend.dispatch.on('stateChange', function(newState) { 1.10728 + for (var key in newState) { 1.10729 + state[key] = newState[key]; 1.10730 + } 1.10731 + dispatch.stateChange(state); 1.10732 + chart.update(); 1.10733 + }); 1.10734 + 1.10735 + // Update chart from a state object passed to event handler 1.10736 + dispatch.on('changeState', function(e) { 1.10737 + if (typeof e.disabled !== 'undefined') { 1.10738 + data.forEach(function(series,i) { 1.10739 + series.disabled = e.disabled[i]; 1.10740 + }); 1.10741 + state.disabled = e.disabled; 1.10742 + } 1.10743 + chart.update(); 1.10744 + }); 1.10745 + }); 1.10746 + 1.10747 + renderWatch.renderEnd('pieChart immediate'); 1.10748 + return chart; 1.10749 + } 1.10750 + 1.10751 + //============================================================ 1.10752 + // Event Handling/Dispatching (out of chart's scope) 1.10753 + //------------------------------------------------------------ 1.10754 + 1.10755 + pie.dispatch.on('elementMouseover.tooltip', function(evt) { 1.10756 + evt['series'] = { 1.10757 + key: chart.x()(evt.data), 1.10758 + value: chart.y()(evt.data), 1.10759 + color: evt.color 1.10760 + }; 1.10761 + tooltip.data(evt).hidden(false); 1.10762 + }); 1.10763 + 1.10764 + pie.dispatch.on('elementMouseout.tooltip', function(evt) { 1.10765 + tooltip.hidden(true); 1.10766 + }); 1.10767 + 1.10768 + pie.dispatch.on('elementMousemove.tooltip', function(evt) { 1.10769 + tooltip.position({top: d3.event.pageY, left: d3.event.pageX})(); 1.10770 + }); 1.10771 + 1.10772 + //============================================================ 1.10773 + // Expose Public Variables 1.10774 + //------------------------------------------------------------ 1.10775 + 1.10776 + // expose chart's sub-components 1.10777 + chart.legend = legend; 1.10778 + chart.dispatch = dispatch; 1.10779 + chart.pie = pie; 1.10780 + chart.tooltip = tooltip; 1.10781 + chart.options = nv.utils.optionsFunc.bind(chart); 1.10782 + 1.10783 + // use Object get/set functionality to map between vars and chart functions 1.10784 + chart._options = Object.create({}, { 1.10785 + // simple options, just get/set the necessary values 1.10786 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.10787 + showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, 1.10788 + legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}}, 1.10789 + defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}}, 1.10790 + 1.10791 + // deprecated options 1.10792 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.10793 + // deprecated after 1.7.1 1.10794 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.10795 + tooltip.enabled(!!_); 1.10796 + }}, 1.10797 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.10798 + // deprecated after 1.7.1 1.10799 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.10800 + tooltip.contentGenerator(_); 1.10801 + }}, 1.10802 + 1.10803 + // options that require extra logic in the setter 1.10804 + color: {get: function(){return color;}, set: function(_){ 1.10805 + color = _; 1.10806 + legend.color(color); 1.10807 + pie.color(color); 1.10808 + }}, 1.10809 + duration: {get: function(){return duration;}, set: function(_){ 1.10810 + duration = _; 1.10811 + renderWatch.reset(duration); 1.10812 + }}, 1.10813 + margin: {get: function(){return margin;}, set: function(_){ 1.10814 + margin.top = _.top !== undefined ? _.top : margin.top; 1.10815 + margin.right = _.right !== undefined ? _.right : margin.right; 1.10816 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.10817 + margin.left = _.left !== undefined ? _.left : margin.left; 1.10818 + }} 1.10819 + }); 1.10820 + nv.utils.inheritOptions(chart, pie); 1.10821 + nv.utils.initOptions(chart); 1.10822 + return chart; 1.10823 +}; 1.10824 + 1.10825 +nv.models.scatter = function() { 1.10826 + "use strict"; 1.10827 + 1.10828 + //============================================================ 1.10829 + // Public Variables with Default Settings 1.10830 + //------------------------------------------------------------ 1.10831 + 1.10832 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.10833 + , width = null 1.10834 + , height = null 1.10835 + , color = nv.utils.defaultColor() // chooses color 1.10836 + , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't select one 1.10837 + , container = null 1.10838 + , x = d3.scale.linear() 1.10839 + , y = d3.scale.linear() 1.10840 + , z = d3.scale.linear() //linear because d3.svg.shape.size is treated as area 1.10841 + , getX = function(d) { return d.x } // accessor to get the x value 1.10842 + , getY = function(d) { return d.y } // accessor to get the y value 1.10843 + , getSize = function(d) { return d.size || 1} // accessor to get the point size 1.10844 + , getShape = function(d) { return d.shape || 'circle' } // accessor to get point shape 1.10845 + , forceX = [] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.) 1.10846 + , forceY = [] // List of numbers to Force into the Y scale 1.10847 + , forceSize = [] // List of numbers to Force into the Size scale 1.10848 + , interactive = true // If true, plots a voronoi overlay for advanced point intersection 1.10849 + , pointActive = function(d) { return !d.notActive } // any points that return false will be filtered out 1.10850 + , padData = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart 1.10851 + , padDataOuter = .1 //outerPadding to imitate ordinal scale outer padding 1.10852 + , clipEdge = false // if true, masks points within x and y scale 1.10853 + , clipVoronoi = true // if true, masks each point with a circle... can turn off to slightly increase performance 1.10854 + , showVoronoi = false // display the voronoi areas 1.10855 + , clipRadius = function() { return 25 } // function to get the radius for voronoi point clips 1.10856 + , xDomain = null // Override x domain (skips the calculation from data) 1.10857 + , yDomain = null // Override y domain 1.10858 + , xRange = null // Override x range 1.10859 + , yRange = null // Override y range 1.10860 + , sizeDomain = null // Override point size domain 1.10861 + , sizeRange = null 1.10862 + , singlePoint = false 1.10863 + , dispatch = d3.dispatch('elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'renderEnd') 1.10864 + , useVoronoi = true 1.10865 + , duration = 250 1.10866 + ; 1.10867 + 1.10868 + 1.10869 + //============================================================ 1.10870 + // Private Variables 1.10871 + //------------------------------------------------------------ 1.10872 + 1.10873 + var x0, y0, z0 // used to store previous scales 1.10874 + , timeoutID 1.10875 + , needsUpdate = false // Flag for when the points are visually updating, but the interactive layer is behind, to disable tooltips 1.10876 + , renderWatch = nv.utils.renderWatch(dispatch, duration) 1.10877 + , _sizeRange_def = [16, 256] 1.10878 + ; 1.10879 + 1.10880 + function chart(selection) { 1.10881 + renderWatch.reset(); 1.10882 + selection.each(function(data) { 1.10883 + container = d3.select(this); 1.10884 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.10885 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.10886 + 1.10887 + nv.utils.initSVG(container); 1.10888 + 1.10889 + //add series index to each data point for reference 1.10890 + data.forEach(function(series, i) { 1.10891 + series.values.forEach(function(point) { 1.10892 + point.series = i; 1.10893 + }); 1.10894 + }); 1.10895 + 1.10896 + // Setup Scales 1.10897 + // remap and flatten the data for use in calculating the scales' domains 1.10898 + 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 1.10899 + d3.merge( 1.10900 + data.map(function(d) { 1.10901 + return d.values.map(function(d,i) { 1.10902 + return { x: getX(d,i), y: getY(d,i), size: getSize(d,i) } 1.10903 + }) 1.10904 + }) 1.10905 + ); 1.10906 + 1.10907 + x .domain(xDomain || d3.extent(seriesData.map(function(d) { return d.x; }).concat(forceX))) 1.10908 + 1.10909 + if (padData && data[0]) 1.10910 + x.range(xRange || [(availableWidth * padDataOuter + availableWidth) / (2 *data[0].values.length), availableWidth - availableWidth * (1 + padDataOuter) / (2 * data[0].values.length) ]); 1.10911 + //x.range([availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5) / data[0].values.length ]); 1.10912 + else 1.10913 + x.range(xRange || [0, availableWidth]); 1.10914 + 1.10915 + y .domain(yDomain || d3.extent(seriesData.map(function(d) { return d.y }).concat(forceY))) 1.10916 + .range(yRange || [availableHeight, 0]); 1.10917 + 1.10918 + z .domain(sizeDomain || d3.extent(seriesData.map(function(d) { return d.size }).concat(forceSize))) 1.10919 + .range(sizeRange || _sizeRange_def); 1.10920 + 1.10921 + // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point 1.10922 + singlePoint = x.domain()[0] === x.domain()[1] || y.domain()[0] === y.domain()[1]; 1.10923 + 1.10924 + if (x.domain()[0] === x.domain()[1]) 1.10925 + x.domain()[0] ? 1.10926 + x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01]) 1.10927 + : x.domain([-1,1]); 1.10928 + 1.10929 + if (y.domain()[0] === y.domain()[1]) 1.10930 + y.domain()[0] ? 1.10931 + y.domain([y.domain()[0] - y.domain()[0] * 0.01, y.domain()[1] + y.domain()[1] * 0.01]) 1.10932 + : y.domain([-1,1]); 1.10933 + 1.10934 + if ( isNaN(x.domain()[0])) { 1.10935 + x.domain([-1,1]); 1.10936 + } 1.10937 + 1.10938 + if ( isNaN(y.domain()[0])) { 1.10939 + y.domain([-1,1]); 1.10940 + } 1.10941 + 1.10942 + x0 = x0 || x; 1.10943 + y0 = y0 || y; 1.10944 + z0 = z0 || z; 1.10945 + 1.10946 + // Setup containers and skeleton of chart 1.10947 + var wrap = container.selectAll('g.nv-wrap.nv-scatter').data([data]); 1.10948 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatter nv-chart-' + id); 1.10949 + var defsEnter = wrapEnter.append('defs'); 1.10950 + var gEnter = wrapEnter.append('g'); 1.10951 + var g = wrap.select('g'); 1.10952 + 1.10953 + wrap.classed('nv-single-point', singlePoint); 1.10954 + gEnter.append('g').attr('class', 'nv-groups'); 1.10955 + gEnter.append('g').attr('class', 'nv-point-paths'); 1.10956 + wrapEnter.append('g').attr('class', 'nv-point-clips'); 1.10957 + 1.10958 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.10959 + 1.10960 + defsEnter.append('clipPath') 1.10961 + .attr('id', 'nv-edge-clip-' + id) 1.10962 + .append('rect'); 1.10963 + 1.10964 + wrap.select('#nv-edge-clip-' + id + ' rect') 1.10965 + .attr('width', availableWidth) 1.10966 + .attr('height', (availableHeight > 0) ? availableHeight : 0); 1.10967 + 1.10968 + g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : ''); 1.10969 + 1.10970 + function updateInteractiveLayer() { 1.10971 + // Always clear needs-update flag regardless of whether or not 1.10972 + // we will actually do anything (avoids needless invocations). 1.10973 + needsUpdate = false; 1.10974 + 1.10975 + if (!interactive) return false; 1.10976 + 1.10977 + // inject series and point index for reference into voronoi 1.10978 + if (useVoronoi === true) { 1.10979 + var vertices = d3.merge(data.map(function(group, groupIndex) { 1.10980 + return group.values 1.10981 + .map(function(point, pointIndex) { 1.10982 + // *Adding noise to make duplicates very unlikely 1.10983 + // *Injecting series and point index for reference 1.10984 + /* *Adding a 'jitter' to the points, because there's an issue in d3.geom.voronoi. 1.10985 + */ 1.10986 + var pX = getX(point,pointIndex); 1.10987 + var pY = getY(point,pointIndex); 1.10988 + 1.10989 + return [x(pX)+ Math.random() * 1e-4, 1.10990 + y(pY)+ Math.random() * 1e-4, 1.10991 + groupIndex, 1.10992 + pointIndex, point]; //temp hack to add noise until I think of a better way so there are no duplicates 1.10993 + }) 1.10994 + .filter(function(pointArray, pointIndex) { 1.10995 + return pointActive(pointArray[4], pointIndex); // Issue #237.. move filter to after map, so pointIndex is correct! 1.10996 + }) 1.10997 + }) 1.10998 + ); 1.10999 + 1.11000 + if (vertices.length == 0) return false; // No active points, we're done 1.11001 + if (vertices.length < 3) { 1.11002 + // Issue #283 - Adding 2 dummy points to the voronoi b/c voronoi requires min 3 points to work 1.11003 + vertices.push([x.range()[0] - 20, y.range()[0] - 20, null, null]); 1.11004 + vertices.push([x.range()[1] + 20, y.range()[1] + 20, null, null]); 1.11005 + vertices.push([x.range()[0] - 20, y.range()[0] + 20, null, null]); 1.11006 + vertices.push([x.range()[1] + 20, y.range()[1] - 20, null, null]); 1.11007 + } 1.11008 + 1.11009 + // keep voronoi sections from going more than 10 outside of graph 1.11010 + // to avoid overlap with other things like legend etc 1.11011 + var bounds = d3.geom.polygon([ 1.11012 + [-10,-10], 1.11013 + [-10,height + 10], 1.11014 + [width + 10,height + 10], 1.11015 + [width + 10,-10] 1.11016 + ]); 1.11017 + 1.11018 + var voronoi = d3.geom.voronoi(vertices).map(function(d, i) { 1.11019 + return { 1.11020 + 'data': bounds.clip(d), 1.11021 + 'series': vertices[i][2], 1.11022 + 'point': vertices[i][3] 1.11023 + } 1.11024 + }); 1.11025 + 1.11026 + // nuke all voronoi paths on reload and recreate them 1.11027 + wrap.select('.nv-point-paths').selectAll('path').remove(); 1.11028 + var pointPaths = wrap.select('.nv-point-paths').selectAll('path').data(voronoi); 1.11029 + var vPointPaths = pointPaths 1.11030 + .enter().append("svg:path") 1.11031 + .attr("d", function(d) { 1.11032 + if (!d || !d.data || d.data.length === 0) 1.11033 + return 'M 0 0'; 1.11034 + else 1.11035 + return "M" + d.data.join(",") + "Z"; 1.11036 + }) 1.11037 + .attr("id", function(d,i) { 1.11038 + return "nv-path-"+i; }) 1.11039 + .attr("clip-path", function(d,i) { return "url(#nv-clip-"+i+")"; }) 1.11040 + ; 1.11041 + 1.11042 + // good for debugging point hover issues 1.11043 + if (showVoronoi) { 1.11044 + vPointPaths.style("fill", d3.rgb(230, 230, 230)) 1.11045 + .style('fill-opacity', 0.4) 1.11046 + .style('stroke-opacity', 1) 1.11047 + .style("stroke", d3.rgb(200,200,200)); 1.11048 + } 1.11049 + 1.11050 + if (clipVoronoi) { 1.11051 + // voronoi sections are already set to clip, 1.11052 + // just create the circles with the IDs they expect 1.11053 + wrap.select('.nv-point-clips').selectAll('clipPath').remove(); 1.11054 + wrap.select('.nv-point-clips').selectAll("clipPath") 1.11055 + .data(vertices) 1.11056 + .enter().append("svg:clipPath") 1.11057 + .attr("id", function(d, i) { return "nv-clip-"+i;}) 1.11058 + .append("svg:circle") 1.11059 + .attr('cx', function(d) { return d[0]; }) 1.11060 + .attr('cy', function(d) { return d[1]; }) 1.11061 + .attr('r', clipRadius); 1.11062 + } 1.11063 + 1.11064 + var mouseEventCallback = function(d, mDispatch) { 1.11065 + if (needsUpdate) return 0; 1.11066 + var series = data[d.series]; 1.11067 + if (series === undefined) return; 1.11068 + var point = series.values[d.point]; 1.11069 + point['color'] = color(series, d.series); 1.11070 + 1.11071 + // standardize attributes for tooltip. 1.11072 + point['x'] = getX(point); 1.11073 + point['y'] = getY(point); 1.11074 + 1.11075 + // can't just get box of event node since it's actually a voronoi polygon 1.11076 + var box = container.node().getBoundingClientRect(); 1.11077 + var scrollTop = window.pageYOffset || document.documentElement.scrollTop; 1.11078 + var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft; 1.11079 + 1.11080 + var pos = { 1.11081 + left: x(getX(point, d.point)) + box.left + scrollLeft + margin.left + 10, 1.11082 + top: y(getY(point, d.point)) + box.top + scrollTop + margin.top + 10 1.11083 + }; 1.11084 + 1.11085 + mDispatch({ 1.11086 + point: point, 1.11087 + series: series, 1.11088 + pos: pos, 1.11089 + seriesIndex: d.series, 1.11090 + pointIndex: d.point 1.11091 + }); 1.11092 + }; 1.11093 + 1.11094 + pointPaths 1.11095 + .on('click', function(d) { 1.11096 + mouseEventCallback(d, dispatch.elementClick); 1.11097 + }) 1.11098 + .on('dblclick', function(d) { 1.11099 + mouseEventCallback(d, dispatch.elementDblClick); 1.11100 + }) 1.11101 + .on('mouseover', function(d) { 1.11102 + mouseEventCallback(d, dispatch.elementMouseover); 1.11103 + }) 1.11104 + .on('mouseout', function(d, i) { 1.11105 + mouseEventCallback(d, dispatch.elementMouseout); 1.11106 + }); 1.11107 + 1.11108 + } else { 1.11109 + // add event handlers to points instead voronoi paths 1.11110 + wrap.select('.nv-groups').selectAll('.nv-group') 1.11111 + .selectAll('.nv-point') 1.11112 + //.data(dataWithPoints) 1.11113 + //.style('pointer-events', 'auto') // recativate events, disabled by css 1.11114 + .on('click', function(d,i) { 1.11115 + //nv.log('test', d, i); 1.11116 + if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point 1.11117 + var series = data[d.series], 1.11118 + point = series.values[i]; 1.11119 + 1.11120 + dispatch.elementClick({ 1.11121 + point: point, 1.11122 + series: series, 1.11123 + pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top], 1.11124 + seriesIndex: d.series, 1.11125 + pointIndex: i 1.11126 + }); 1.11127 + }) 1.11128 + .on('dblclick', function(d,i) { 1.11129 + if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point 1.11130 + var series = data[d.series], 1.11131 + point = series.values[i]; 1.11132 + 1.11133 + dispatch.elementDblClick({ 1.11134 + point: point, 1.11135 + series: series, 1.11136 + pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top], 1.11137 + seriesIndex: d.series, 1.11138 + pointIndex: i 1.11139 + }); 1.11140 + }) 1.11141 + .on('mouseover', function(d,i) { 1.11142 + if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point 1.11143 + var series = data[d.series], 1.11144 + point = series.values[i]; 1.11145 + 1.11146 + dispatch.elementMouseover({ 1.11147 + point: point, 1.11148 + series: series, 1.11149 + pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top], 1.11150 + seriesIndex: d.series, 1.11151 + pointIndex: i, 1.11152 + color: color(d, i) 1.11153 + }); 1.11154 + }) 1.11155 + .on('mouseout', function(d,i) { 1.11156 + if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point 1.11157 + var series = data[d.series], 1.11158 + point = series.values[i]; 1.11159 + 1.11160 + dispatch.elementMouseout({ 1.11161 + point: point, 1.11162 + series: series, 1.11163 + seriesIndex: d.series, 1.11164 + pointIndex: i, 1.11165 + color: color(d, i) 1.11166 + }); 1.11167 + }); 1.11168 + } 1.11169 + } 1.11170 + 1.11171 + needsUpdate = true; 1.11172 + var groups = wrap.select('.nv-groups').selectAll('.nv-group') 1.11173 + .data(function(d) { return d }, function(d) { return d.key }); 1.11174 + groups.enter().append('g') 1.11175 + .style('stroke-opacity', 1e-6) 1.11176 + .style('fill-opacity', 1e-6); 1.11177 + groups.exit() 1.11178 + .remove(); 1.11179 + groups 1.11180 + .attr('class', function(d,i) { return 'nv-group nv-series-' + i }) 1.11181 + .classed('hover', function(d) { return d.hover }); 1.11182 + groups.watchTransition(renderWatch, 'scatter: groups') 1.11183 + .style('fill', function(d,i) { return color(d, i) }) 1.11184 + .style('stroke', function(d,i) { return color(d, i) }) 1.11185 + .style('stroke-opacity', 1) 1.11186 + .style('fill-opacity', .5); 1.11187 + 1.11188 + // create the points, maintaining their IDs from the original data set 1.11189 + var points = groups.selectAll('path.nv-point') 1.11190 + .data(function(d) { 1.11191 + return d.values.map( 1.11192 + function (point, pointIndex) { 1.11193 + return [point, pointIndex] 1.11194 + }).filter( 1.11195 + function(pointArray, pointIndex) { 1.11196 + return pointActive(pointArray[0], pointIndex) 1.11197 + }) 1.11198 + }); 1.11199 + points.enter().append('path') 1.11200 + .style('fill', function (d) { return d.color }) 1.11201 + .style('stroke', function (d) { return d.color }) 1.11202 + .attr('transform', function(d) { 1.11203 + return 'translate(' + x0(getX(d[0],d[1])) + ',' + y0(getY(d[0],d[1])) + ')' 1.11204 + }) 1.11205 + .attr('d', 1.11206 + nv.utils.symbol() 1.11207 + .type(function(d) { return getShape(d[0]); }) 1.11208 + .size(function(d) { return z(getSize(d[0],d[1])) }) 1.11209 + ); 1.11210 + points.exit().remove(); 1.11211 + groups.exit().selectAll('path.nv-point') 1.11212 + .watchTransition(renderWatch, 'scatter exit') 1.11213 + .attr('transform', function(d) { 1.11214 + return 'translate(' + x(getX(d[0],d[1])) + ',' + y(getY(d[0],d[1])) + ')' 1.11215 + }) 1.11216 + .remove(); 1.11217 + points.each(function(d) { 1.11218 + d3.select(this) 1.11219 + .classed('nv-point', true) 1.11220 + .classed('nv-point-' + d[1], true) 1.11221 + .classed('nv-noninteractive', !interactive) 1.11222 + .classed('hover',false) 1.11223 + ; 1.11224 + }); 1.11225 + points 1.11226 + .watchTransition(renderWatch, 'scatter points') 1.11227 + .attr('transform', function(d) { 1.11228 + //nv.log(d, getX(d[0],d[1]), x(getX(d[0],d[1]))); 1.11229 + return 'translate(' + x(getX(d[0],d[1])) + ',' + y(getY(d[0],d[1])) + ')' 1.11230 + }) 1.11231 + .attr('d', 1.11232 + nv.utils.symbol() 1.11233 + .type(function(d) { return getShape(d[0]); }) 1.11234 + .size(function(d) { return z(getSize(d[0],d[1])) }) 1.11235 + ); 1.11236 + 1.11237 + // Delay updating the invisible interactive layer for smoother animation 1.11238 + clearTimeout(timeoutID); // stop repeat calls to updateInteractiveLayer 1.11239 + timeoutID = setTimeout(updateInteractiveLayer, 300); 1.11240 + //updateInteractiveLayer(); 1.11241 + 1.11242 + //store old scales for use in transitions on update 1.11243 + x0 = x.copy(); 1.11244 + y0 = y.copy(); 1.11245 + z0 = z.copy(); 1.11246 + 1.11247 + }); 1.11248 + renderWatch.renderEnd('scatter immediate'); 1.11249 + return chart; 1.11250 + } 1.11251 + 1.11252 + //============================================================ 1.11253 + // Expose Public Variables 1.11254 + //------------------------------------------------------------ 1.11255 + 1.11256 + chart.dispatch = dispatch; 1.11257 + chart.options = nv.utils.optionsFunc.bind(chart); 1.11258 + 1.11259 + // utility function calls provided by this chart 1.11260 + chart._calls = new function() { 1.11261 + this.clearHighlights = function () { 1.11262 + nv.dom.write(function() { 1.11263 + container.selectAll(".nv-point.hover").classed("hover", false); 1.11264 + }); 1.11265 + return null; 1.11266 + }; 1.11267 + this.highlightPoint = function (seriesIndex, pointIndex, isHoverOver) { 1.11268 + nv.dom.write(function() { 1.11269 + container.select(" .nv-series-" + seriesIndex + " .nv-point-" + pointIndex) 1.11270 + .classed("hover", isHoverOver); 1.11271 + }); 1.11272 + }; 1.11273 + }; 1.11274 + 1.11275 + // trigger calls from events too 1.11276 + dispatch.on('elementMouseover.point', function(d) { 1.11277 + if (interactive) chart._calls.highlightPoint(d.seriesIndex,d.pointIndex,true); 1.11278 + }); 1.11279 + 1.11280 + dispatch.on('elementMouseout.point', function(d) { 1.11281 + if (interactive) chart._calls.highlightPoint(d.seriesIndex,d.pointIndex,false); 1.11282 + }); 1.11283 + 1.11284 + chart._options = Object.create({}, { 1.11285 + // simple options, just get/set the necessary values 1.11286 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.11287 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.11288 + xScale: {get: function(){return x;}, set: function(_){x=_;}}, 1.11289 + yScale: {get: function(){return y;}, set: function(_){y=_;}}, 1.11290 + pointScale: {get: function(){return z;}, set: function(_){z=_;}}, 1.11291 + xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}}, 1.11292 + yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}}, 1.11293 + pointDomain: {get: function(){return sizeDomain;}, set: function(_){sizeDomain=_;}}, 1.11294 + xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}}, 1.11295 + yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}}, 1.11296 + pointRange: {get: function(){return sizeRange;}, set: function(_){sizeRange=_;}}, 1.11297 + forceX: {get: function(){return forceX;}, set: function(_){forceX=_;}}, 1.11298 + forceY: {get: function(){return forceY;}, set: function(_){forceY=_;}}, 1.11299 + forcePoint: {get: function(){return forceSize;}, set: function(_){forceSize=_;}}, 1.11300 + interactive: {get: function(){return interactive;}, set: function(_){interactive=_;}}, 1.11301 + pointActive: {get: function(){return pointActive;}, set: function(_){pointActive=_;}}, 1.11302 + padDataOuter: {get: function(){return padDataOuter;}, set: function(_){padDataOuter=_;}}, 1.11303 + padData: {get: function(){return padData;}, set: function(_){padData=_;}}, 1.11304 + clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}}, 1.11305 + clipVoronoi: {get: function(){return clipVoronoi;}, set: function(_){clipVoronoi=_;}}, 1.11306 + clipRadius: {get: function(){return clipRadius;}, set: function(_){clipRadius=_;}}, 1.11307 + showVoronoi: {get: function(){return showVoronoi;}, set: function(_){showVoronoi=_;}}, 1.11308 + id: {get: function(){return id;}, set: function(_){id=_;}}, 1.11309 + 1.11310 + 1.11311 + // simple functor options 1.11312 + x: {get: function(){return getX;}, set: function(_){getX = d3.functor(_);}}, 1.11313 + y: {get: function(){return getY;}, set: function(_){getY = d3.functor(_);}}, 1.11314 + pointSize: {get: function(){return getSize;}, set: function(_){getSize = d3.functor(_);}}, 1.11315 + pointShape: {get: function(){return getShape;}, set: function(_){getShape = d3.functor(_);}}, 1.11316 + 1.11317 + // options that require extra logic in the setter 1.11318 + margin: {get: function(){return margin;}, set: function(_){ 1.11319 + margin.top = _.top !== undefined ? _.top : margin.top; 1.11320 + margin.right = _.right !== undefined ? _.right : margin.right; 1.11321 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.11322 + margin.left = _.left !== undefined ? _.left : margin.left; 1.11323 + }}, 1.11324 + duration: {get: function(){return duration;}, set: function(_){ 1.11325 + duration = _; 1.11326 + renderWatch.reset(duration); 1.11327 + }}, 1.11328 + color: {get: function(){return color;}, set: function(_){ 1.11329 + color = nv.utils.getColor(_); 1.11330 + }}, 1.11331 + useVoronoi: {get: function(){return useVoronoi;}, set: function(_){ 1.11332 + useVoronoi = _; 1.11333 + if (useVoronoi === false) { 1.11334 + clipVoronoi = false; 1.11335 + } 1.11336 + }} 1.11337 + }); 1.11338 + 1.11339 + nv.utils.initOptions(chart); 1.11340 + return chart; 1.11341 +}; 1.11342 + 1.11343 +nv.models.scatterChart = function() { 1.11344 + "use strict"; 1.11345 + 1.11346 + //============================================================ 1.11347 + // Public Variables with Default Settings 1.11348 + //------------------------------------------------------------ 1.11349 + 1.11350 + var scatter = nv.models.scatter() 1.11351 + , xAxis = nv.models.axis() 1.11352 + , yAxis = nv.models.axis() 1.11353 + , legend = nv.models.legend() 1.11354 + , distX = nv.models.distribution() 1.11355 + , distY = nv.models.distribution() 1.11356 + , tooltip = nv.models.tooltip() 1.11357 + ; 1.11358 + 1.11359 + var margin = {top: 30, right: 20, bottom: 50, left: 75} 1.11360 + , width = null 1.11361 + , height = null 1.11362 + , container = null 1.11363 + , color = nv.utils.defaultColor() 1.11364 + , x = scatter.xScale() 1.11365 + , y = scatter.yScale() 1.11366 + , showDistX = false 1.11367 + , showDistY = false 1.11368 + , showLegend = true 1.11369 + , showXAxis = true 1.11370 + , showYAxis = true 1.11371 + , rightAlignYAxis = false 1.11372 + , state = nv.utils.state() 1.11373 + , defaultState = null 1.11374 + , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd') 1.11375 + , noData = null 1.11376 + , duration = 250 1.11377 + ; 1.11378 + 1.11379 + scatter.xScale(x).yScale(y); 1.11380 + xAxis.orient('bottom').tickPadding(10); 1.11381 + yAxis 1.11382 + .orient((rightAlignYAxis) ? 'right' : 'left') 1.11383 + .tickPadding(10) 1.11384 + ; 1.11385 + distX.axis('x'); 1.11386 + distY.axis('y'); 1.11387 + tooltip 1.11388 + .headerFormatter(function(d, i) { 1.11389 + return xAxis.tickFormat()(d, i); 1.11390 + }) 1.11391 + .valueFormatter(function(d, i) { 1.11392 + return yAxis.tickFormat()(d, i); 1.11393 + }); 1.11394 + 1.11395 + //============================================================ 1.11396 + // Private Variables 1.11397 + //------------------------------------------------------------ 1.11398 + 1.11399 + var x0, y0 1.11400 + , renderWatch = nv.utils.renderWatch(dispatch, duration); 1.11401 + 1.11402 + var stateGetter = function(data) { 1.11403 + return function(){ 1.11404 + return { 1.11405 + active: data.map(function(d) { return !d.disabled }) 1.11406 + }; 1.11407 + } 1.11408 + }; 1.11409 + 1.11410 + var stateSetter = function(data) { 1.11411 + return function(state) { 1.11412 + if (state.active !== undefined) 1.11413 + data.forEach(function(series,i) { 1.11414 + series.disabled = !state.active[i]; 1.11415 + }); 1.11416 + } 1.11417 + }; 1.11418 + 1.11419 + function chart(selection) { 1.11420 + renderWatch.reset(); 1.11421 + renderWatch.models(scatter); 1.11422 + if (showXAxis) renderWatch.models(xAxis); 1.11423 + if (showYAxis) renderWatch.models(yAxis); 1.11424 + if (showDistX) renderWatch.models(distX); 1.11425 + if (showDistY) renderWatch.models(distY); 1.11426 + 1.11427 + selection.each(function(data) { 1.11428 + var that = this; 1.11429 + 1.11430 + container = d3.select(this); 1.11431 + nv.utils.initSVG(container); 1.11432 + 1.11433 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.11434 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.11435 + 1.11436 + chart.update = function() { 1.11437 + if (duration === 0) 1.11438 + container.call(chart); 1.11439 + else 1.11440 + container.transition().duration(duration).call(chart); 1.11441 + }; 1.11442 + chart.container = this; 1.11443 + 1.11444 + state 1.11445 + .setter(stateSetter(data), chart.update) 1.11446 + .getter(stateGetter(data)) 1.11447 + .update(); 1.11448 + 1.11449 + // DEPRECATED set state.disableddisabled 1.11450 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.11451 + 1.11452 + if (!defaultState) { 1.11453 + var key; 1.11454 + defaultState = {}; 1.11455 + for (key in state) { 1.11456 + if (state[key] instanceof Array) 1.11457 + defaultState[key] = state[key].slice(0); 1.11458 + else 1.11459 + defaultState[key] = state[key]; 1.11460 + } 1.11461 + } 1.11462 + 1.11463 + // Display noData message if there's nothing to show. 1.11464 + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { 1.11465 + nv.utils.noData(chart, container); 1.11466 + renderWatch.renderEnd('scatter immediate'); 1.11467 + return chart; 1.11468 + } else { 1.11469 + container.selectAll('.nv-noData').remove(); 1.11470 + } 1.11471 + 1.11472 + // Setup Scales 1.11473 + x = scatter.xScale(); 1.11474 + y = scatter.yScale(); 1.11475 + 1.11476 + // Setup containers and skeleton of chart 1.11477 + var wrap = container.selectAll('g.nv-wrap.nv-scatterChart').data([data]); 1.11478 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatterChart nv-chart-' + scatter.id()); 1.11479 + var gEnter = wrapEnter.append('g'); 1.11480 + var g = wrap.select('g'); 1.11481 + 1.11482 + // background for pointer events 1.11483 + gEnter.append('rect').attr('class', 'nvd3 nv-background').style("pointer-events","none"); 1.11484 + 1.11485 + gEnter.append('g').attr('class', 'nv-x nv-axis'); 1.11486 + gEnter.append('g').attr('class', 'nv-y nv-axis'); 1.11487 + gEnter.append('g').attr('class', 'nv-scatterWrap'); 1.11488 + gEnter.append('g').attr('class', 'nv-regressionLinesWrap'); 1.11489 + gEnter.append('g').attr('class', 'nv-distWrap'); 1.11490 + gEnter.append('g').attr('class', 'nv-legendWrap'); 1.11491 + 1.11492 + if (rightAlignYAxis) { 1.11493 + g.select(".nv-y.nv-axis") 1.11494 + .attr("transform", "translate(" + availableWidth + ",0)"); 1.11495 + } 1.11496 + 1.11497 + // Legend 1.11498 + if (showLegend) { 1.11499 + var legendWidth = availableWidth; 1.11500 + legend.width(legendWidth); 1.11501 + 1.11502 + wrap.select('.nv-legendWrap') 1.11503 + .datum(data) 1.11504 + .call(legend); 1.11505 + 1.11506 + if ( margin.top != legend.height()) { 1.11507 + margin.top = legend.height(); 1.11508 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.11509 + } 1.11510 + 1.11511 + wrap.select('.nv-legendWrap') 1.11512 + .attr('transform', 'translate(0' + ',' + (-margin.top) +')'); 1.11513 + } 1.11514 + 1.11515 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.11516 + 1.11517 + // Main Chart Component(s) 1.11518 + scatter 1.11519 + .width(availableWidth) 1.11520 + .height(availableHeight) 1.11521 + .color(data.map(function(d,i) { 1.11522 + d.color = d.color || color(d, i); 1.11523 + return d.color; 1.11524 + }).filter(function(d,i) { return !data[i].disabled })); 1.11525 + 1.11526 + wrap.select('.nv-scatterWrap') 1.11527 + .datum(data.filter(function(d) { return !d.disabled })) 1.11528 + .call(scatter); 1.11529 + 1.11530 + 1.11531 + wrap.select('.nv-regressionLinesWrap') 1.11532 + .attr('clip-path', 'url(#nv-edge-clip-' + scatter.id() + ')'); 1.11533 + 1.11534 + var regWrap = wrap.select('.nv-regressionLinesWrap').selectAll('.nv-regLines') 1.11535 + .data(function (d) { 1.11536 + return d; 1.11537 + }); 1.11538 + 1.11539 + regWrap.enter().append('g').attr('class', 'nv-regLines'); 1.11540 + 1.11541 + var regLine = regWrap.selectAll('.nv-regLine') 1.11542 + .data(function (d) { 1.11543 + return [d] 1.11544 + }); 1.11545 + 1.11546 + regLine.enter() 1.11547 + .append('line').attr('class', 'nv-regLine') 1.11548 + .style('stroke-opacity', 0); 1.11549 + 1.11550 + // don't add lines unless we have slope and intercept to use 1.11551 + regLine.filter(function(d) { 1.11552 + return d.intercept && d.slope; 1.11553 + }) 1.11554 + .watchTransition(renderWatch, 'scatterPlusLineChart: regline') 1.11555 + .attr('x1', x.range()[0]) 1.11556 + .attr('x2', x.range()[1]) 1.11557 + .attr('y1', function (d, i) { 1.11558 + return y(x.domain()[0] * d.slope + d.intercept) 1.11559 + }) 1.11560 + .attr('y2', function (d, i) { 1.11561 + return y(x.domain()[1] * d.slope + d.intercept) 1.11562 + }) 1.11563 + .style('stroke', function (d, i, j) { 1.11564 + return color(d, j) 1.11565 + }) 1.11566 + .style('stroke-opacity', function (d, i) { 1.11567 + return (d.disabled || typeof d.slope === 'undefined' || typeof d.intercept === 'undefined') ? 0 : 1 1.11568 + }); 1.11569 + 1.11570 + // Setup Axes 1.11571 + if (showXAxis) { 1.11572 + xAxis 1.11573 + .scale(x) 1.11574 + ._ticks( nv.utils.calcTicksX(availableWidth/100, data) ) 1.11575 + .tickSize( -availableHeight , 0); 1.11576 + 1.11577 + g.select('.nv-x.nv-axis') 1.11578 + .attr('transform', 'translate(0,' + y.range()[0] + ')') 1.11579 + .call(xAxis); 1.11580 + } 1.11581 + 1.11582 + if (showYAxis) { 1.11583 + yAxis 1.11584 + .scale(y) 1.11585 + ._ticks( nv.utils.calcTicksY(availableHeight/36, data) ) 1.11586 + .tickSize( -availableWidth, 0); 1.11587 + 1.11588 + g.select('.nv-y.nv-axis') 1.11589 + .call(yAxis); 1.11590 + } 1.11591 + 1.11592 + 1.11593 + if (showDistX) { 1.11594 + distX 1.11595 + .getData(scatter.x()) 1.11596 + .scale(x) 1.11597 + .width(availableWidth) 1.11598 + .color(data.map(function(d,i) { 1.11599 + return d.color || color(d, i); 1.11600 + }).filter(function(d,i) { return !data[i].disabled })); 1.11601 + gEnter.select('.nv-distWrap').append('g') 1.11602 + .attr('class', 'nv-distributionX'); 1.11603 + g.select('.nv-distributionX') 1.11604 + .attr('transform', 'translate(0,' + y.range()[0] + ')') 1.11605 + .datum(data.filter(function(d) { return !d.disabled })) 1.11606 + .call(distX); 1.11607 + } 1.11608 + 1.11609 + if (showDistY) { 1.11610 + distY 1.11611 + .getData(scatter.y()) 1.11612 + .scale(y) 1.11613 + .width(availableHeight) 1.11614 + .color(data.map(function(d,i) { 1.11615 + return d.color || color(d, i); 1.11616 + }).filter(function(d,i) { return !data[i].disabled })); 1.11617 + gEnter.select('.nv-distWrap').append('g') 1.11618 + .attr('class', 'nv-distributionY'); 1.11619 + g.select('.nv-distributionY') 1.11620 + .attr('transform', 'translate(' + (rightAlignYAxis ? availableWidth : -distY.size() ) + ',0)') 1.11621 + .datum(data.filter(function(d) { return !d.disabled })) 1.11622 + .call(distY); 1.11623 + } 1.11624 + 1.11625 + //============================================================ 1.11626 + // Event Handling/Dispatching (in chart's scope) 1.11627 + //------------------------------------------------------------ 1.11628 + 1.11629 + legend.dispatch.on('stateChange', function(newState) { 1.11630 + for (var key in newState) 1.11631 + state[key] = newState[key]; 1.11632 + dispatch.stateChange(state); 1.11633 + chart.update(); 1.11634 + }); 1.11635 + 1.11636 + // Update chart from a state object passed to event handler 1.11637 + dispatch.on('changeState', function(e) { 1.11638 + if (typeof e.disabled !== 'undefined') { 1.11639 + data.forEach(function(series,i) { 1.11640 + series.disabled = e.disabled[i]; 1.11641 + }); 1.11642 + state.disabled = e.disabled; 1.11643 + } 1.11644 + chart.update(); 1.11645 + }); 1.11646 + 1.11647 + // mouseover needs availableHeight so we just keep scatter mouse events inside the chart block 1.11648 + scatter.dispatch.on('elementMouseout.tooltip', function(evt) { 1.11649 + tooltip.hidden(true); 1.11650 + container.select('.nv-chart-' + scatter.id() + ' .nv-series-' + evt.seriesIndex + ' .nv-distx-' + evt.pointIndex) 1.11651 + .attr('y1', 0); 1.11652 + container.select('.nv-chart-' + scatter.id() + ' .nv-series-' + evt.seriesIndex + ' .nv-disty-' + evt.pointIndex) 1.11653 + .attr('x2', distY.size()); 1.11654 + }); 1.11655 + 1.11656 + scatter.dispatch.on('elementMouseover.tooltip', function(evt) { 1.11657 + container.select('.nv-series-' + evt.seriesIndex + ' .nv-distx-' + evt.pointIndex) 1.11658 + .attr('y1', evt.pos.top - availableHeight - margin.top); 1.11659 + container.select('.nv-series-' + evt.seriesIndex + ' .nv-disty-' + evt.pointIndex) 1.11660 + .attr('x2', evt.pos.left + distX.size() - margin.left); 1.11661 + tooltip.position(evt.pos).data(evt).hidden(false); 1.11662 + }); 1.11663 + 1.11664 + //store old scales for use in transitions on update 1.11665 + x0 = x.copy(); 1.11666 + y0 = y.copy(); 1.11667 + 1.11668 + }); 1.11669 + 1.11670 + renderWatch.renderEnd('scatter with line immediate'); 1.11671 + return chart; 1.11672 + } 1.11673 + 1.11674 + //============================================================ 1.11675 + // Expose Public Variables 1.11676 + //------------------------------------------------------------ 1.11677 + 1.11678 + // expose chart's sub-components 1.11679 + chart.dispatch = dispatch; 1.11680 + chart.scatter = scatter; 1.11681 + chart.legend = legend; 1.11682 + chart.xAxis = xAxis; 1.11683 + chart.yAxis = yAxis; 1.11684 + chart.distX = distX; 1.11685 + chart.distY = distY; 1.11686 + chart.tooltip = tooltip; 1.11687 + 1.11688 + chart.options = nv.utils.optionsFunc.bind(chart); 1.11689 + chart._options = Object.create({}, { 1.11690 + // simple options, just get/set the necessary values 1.11691 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.11692 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.11693 + container: {get: function(){return container;}, set: function(_){container=_;}}, 1.11694 + showDistX: {get: function(){return showDistX;}, set: function(_){showDistX=_;}}, 1.11695 + showDistY: {get: function(){return showDistY;}, set: function(_){showDistY=_;}}, 1.11696 + showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, 1.11697 + showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}}, 1.11698 + showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}}, 1.11699 + defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}}, 1.11700 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.11701 + duration: {get: function(){return duration;}, set: function(_){duration=_;}}, 1.11702 + 1.11703 + // deprecated options 1.11704 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.11705 + // deprecated after 1.7.1 1.11706 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.11707 + tooltip.enabled(!!_); 1.11708 + }}, 1.11709 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.11710 + // deprecated after 1.7.1 1.11711 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.11712 + tooltip.contentGenerator(_); 1.11713 + }}, 1.11714 + tooltipXContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.11715 + // deprecated after 1.7.1 1.11716 + nv.deprecated('tooltipContent', 'This option is removed, put values into main tooltip.'); 1.11717 + }}, 1.11718 + tooltipYContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.11719 + // deprecated after 1.7.1 1.11720 + nv.deprecated('tooltipContent', 'This option is removed, put values into main tooltip.'); 1.11721 + }}, 1.11722 + 1.11723 + // options that require extra logic in the setter 1.11724 + margin: {get: function(){return margin;}, set: function(_){ 1.11725 + margin.top = _.top !== undefined ? _.top : margin.top; 1.11726 + margin.right = _.right !== undefined ? _.right : margin.right; 1.11727 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.11728 + margin.left = _.left !== undefined ? _.left : margin.left; 1.11729 + }}, 1.11730 + rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){ 1.11731 + rightAlignYAxis = _; 1.11732 + yAxis.orient( (_) ? 'right' : 'left'); 1.11733 + }}, 1.11734 + color: {get: function(){return color;}, set: function(_){ 1.11735 + color = nv.utils.getColor(_); 1.11736 + legend.color(color); 1.11737 + distX.color(color); 1.11738 + distY.color(color); 1.11739 + }} 1.11740 + }); 1.11741 + 1.11742 + nv.utils.inheritOptions(chart, scatter); 1.11743 + nv.utils.initOptions(chart); 1.11744 + return chart; 1.11745 +}; 1.11746 + 1.11747 +nv.models.sparkline = function() { 1.11748 + "use strict"; 1.11749 + 1.11750 + //============================================================ 1.11751 + // Public Variables with Default Settings 1.11752 + //------------------------------------------------------------ 1.11753 + 1.11754 + var margin = {top: 2, right: 0, bottom: 2, left: 0} 1.11755 + , width = 400 1.11756 + , height = 32 1.11757 + , container = null 1.11758 + , animate = true 1.11759 + , x = d3.scale.linear() 1.11760 + , y = d3.scale.linear() 1.11761 + , getX = function(d) { return d.x } 1.11762 + , getY = function(d) { return d.y } 1.11763 + , color = nv.utils.getColor(['#000']) 1.11764 + , xDomain 1.11765 + , yDomain 1.11766 + , xRange 1.11767 + , yRange 1.11768 + ; 1.11769 + 1.11770 + function chart(selection) { 1.11771 + selection.each(function(data) { 1.11772 + var availableWidth = width - margin.left - margin.right, 1.11773 + availableHeight = height - margin.top - margin.bottom; 1.11774 + 1.11775 + container = d3.select(this); 1.11776 + nv.utils.initSVG(container); 1.11777 + 1.11778 + // Setup Scales 1.11779 + x .domain(xDomain || d3.extent(data, getX )) 1.11780 + .range(xRange || [0, availableWidth]); 1.11781 + 1.11782 + y .domain(yDomain || d3.extent(data, getY )) 1.11783 + .range(yRange || [availableHeight, 0]); 1.11784 + 1.11785 + // Setup containers and skeleton of chart 1.11786 + var wrap = container.selectAll('g.nv-wrap.nv-sparkline').data([data]); 1.11787 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparkline'); 1.11788 + var gEnter = wrapEnter.append('g'); 1.11789 + var g = wrap.select('g'); 1.11790 + 1.11791 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') 1.11792 + 1.11793 + var paths = wrap.selectAll('path') 1.11794 + .data(function(d) { return [d] }); 1.11795 + paths.enter().append('path'); 1.11796 + paths.exit().remove(); 1.11797 + paths 1.11798 + .style('stroke', function(d,i) { return d.color || color(d, i) }) 1.11799 + .attr('d', d3.svg.line() 1.11800 + .x(function(d,i) { return x(getX(d,i)) }) 1.11801 + .y(function(d,i) { return y(getY(d,i)) }) 1.11802 + ); 1.11803 + 1.11804 + // TODO: Add CURRENT data point (Need Min, Mac, Current / Most recent) 1.11805 + var points = wrap.selectAll('circle.nv-point') 1.11806 + .data(function(data) { 1.11807 + var yValues = data.map(function(d, i) { return getY(d,i); }); 1.11808 + function pointIndex(index) { 1.11809 + if (index != -1) { 1.11810 + var result = data[index]; 1.11811 + result.pointIndex = index; 1.11812 + return result; 1.11813 + } else { 1.11814 + return null; 1.11815 + } 1.11816 + } 1.11817 + var maxPoint = pointIndex(yValues.lastIndexOf(y.domain()[1])), 1.11818 + minPoint = pointIndex(yValues.indexOf(y.domain()[0])), 1.11819 + currentPoint = pointIndex(yValues.length - 1); 1.11820 + return [minPoint, maxPoint, currentPoint].filter(function (d) {return d != null;}); 1.11821 + }); 1.11822 + points.enter().append('circle'); 1.11823 + points.exit().remove(); 1.11824 + points 1.11825 + .attr('cx', function(d,i) { return x(getX(d,d.pointIndex)) }) 1.11826 + .attr('cy', function(d,i) { return y(getY(d,d.pointIndex)) }) 1.11827 + .attr('r', 2) 1.11828 + .attr('class', function(d,i) { 1.11829 + return getX(d, d.pointIndex) == x.domain()[1] ? 'nv-point nv-currentValue' : 1.11830 + getY(d, d.pointIndex) == y.domain()[0] ? 'nv-point nv-minValue' : 'nv-point nv-maxValue' 1.11831 + }); 1.11832 + }); 1.11833 + 1.11834 + return chart; 1.11835 + } 1.11836 + 1.11837 + //============================================================ 1.11838 + // Expose Public Variables 1.11839 + //------------------------------------------------------------ 1.11840 + 1.11841 + chart.options = nv.utils.optionsFunc.bind(chart); 1.11842 + 1.11843 + chart._options = Object.create({}, { 1.11844 + // simple options, just get/set the necessary values 1.11845 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.11846 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.11847 + xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}}, 1.11848 + yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}}, 1.11849 + xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}}, 1.11850 + yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}}, 1.11851 + xScale: {get: function(){return x;}, set: function(_){x=_;}}, 1.11852 + yScale: {get: function(){return y;}, set: function(_){y=_;}}, 1.11853 + animate: {get: function(){return animate;}, set: function(_){animate=_;}}, 1.11854 + 1.11855 + //functor options 1.11856 + x: {get: function(){return getX;}, set: function(_){getX=d3.functor(_);}}, 1.11857 + y: {get: function(){return getY;}, set: function(_){getY=d3.functor(_);}}, 1.11858 + 1.11859 + // options that require extra logic in the setter 1.11860 + margin: {get: function(){return margin;}, set: function(_){ 1.11861 + margin.top = _.top !== undefined ? _.top : margin.top; 1.11862 + margin.right = _.right !== undefined ? _.right : margin.right; 1.11863 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.11864 + margin.left = _.left !== undefined ? _.left : margin.left; 1.11865 + }}, 1.11866 + color: {get: function(){return color;}, set: function(_){ 1.11867 + color = nv.utils.getColor(_); 1.11868 + }} 1.11869 + }); 1.11870 + 1.11871 + nv.utils.initOptions(chart); 1.11872 + return chart; 1.11873 +}; 1.11874 + 1.11875 +nv.models.sparklinePlus = function() { 1.11876 + "use strict"; 1.11877 + 1.11878 + //============================================================ 1.11879 + // Public Variables with Default Settings 1.11880 + //------------------------------------------------------------ 1.11881 + 1.11882 + var sparkline = nv.models.sparkline(); 1.11883 + 1.11884 + var margin = {top: 15, right: 100, bottom: 10, left: 50} 1.11885 + , width = null 1.11886 + , height = null 1.11887 + , x 1.11888 + , y 1.11889 + , index = [] 1.11890 + , paused = false 1.11891 + , xTickFormat = d3.format(',r') 1.11892 + , yTickFormat = d3.format(',.2f') 1.11893 + , showLastValue = true 1.11894 + , alignValue = true 1.11895 + , rightAlignValue = false 1.11896 + , noData = null 1.11897 + ; 1.11898 + 1.11899 + function chart(selection) { 1.11900 + selection.each(function(data) { 1.11901 + var container = d3.select(this); 1.11902 + nv.utils.initSVG(container); 1.11903 + 1.11904 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.11905 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.11906 + 1.11907 + chart.update = function() { container.call(chart); }; 1.11908 + chart.container = this; 1.11909 + 1.11910 + // Display No Data message if there's nothing to show. 1.11911 + if (!data || !data.length) { 1.11912 + nv.utils.noData(chart, container) 1.11913 + return chart; 1.11914 + } else { 1.11915 + container.selectAll('.nv-noData').remove(); 1.11916 + } 1.11917 + 1.11918 + var currentValue = sparkline.y()(data[data.length-1], data.length-1); 1.11919 + 1.11920 + // Setup Scales 1.11921 + x = sparkline.xScale(); 1.11922 + y = sparkline.yScale(); 1.11923 + 1.11924 + // Setup containers and skeleton of chart 1.11925 + var wrap = container.selectAll('g.nv-wrap.nv-sparklineplus').data([data]); 1.11926 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparklineplus'); 1.11927 + var gEnter = wrapEnter.append('g'); 1.11928 + var g = wrap.select('g'); 1.11929 + 1.11930 + gEnter.append('g').attr('class', 'nv-sparklineWrap'); 1.11931 + gEnter.append('g').attr('class', 'nv-valueWrap'); 1.11932 + gEnter.append('g').attr('class', 'nv-hoverArea'); 1.11933 + 1.11934 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.11935 + 1.11936 + // Main Chart Component(s) 1.11937 + var sparklineWrap = g.select('.nv-sparklineWrap'); 1.11938 + 1.11939 + sparkline.width(availableWidth).height(availableHeight); 1.11940 + sparklineWrap.call(sparkline); 1.11941 + 1.11942 + if (showLastValue) { 1.11943 + var valueWrap = g.select('.nv-valueWrap'); 1.11944 + var value = valueWrap.selectAll('.nv-currentValue') 1.11945 + .data([currentValue]); 1.11946 + 1.11947 + value.enter().append('text').attr('class', 'nv-currentValue') 1.11948 + .attr('dx', rightAlignValue ? -8 : 8) 1.11949 + .attr('dy', '.9em') 1.11950 + .style('text-anchor', rightAlignValue ? 'end' : 'start'); 1.11951 + 1.11952 + value 1.11953 + .attr('x', availableWidth + (rightAlignValue ? margin.right : 0)) 1.11954 + .attr('y', alignValue ? function (d) { 1.11955 + return y(d) 1.11956 + } : 0) 1.11957 + .style('fill', sparkline.color()(data[data.length - 1], data.length - 1)) 1.11958 + .text(yTickFormat(currentValue)); 1.11959 + } 1.11960 + 1.11961 + gEnter.select('.nv-hoverArea').append('rect') 1.11962 + .on('mousemove', sparklineHover) 1.11963 + .on('click', function() { paused = !paused }) 1.11964 + .on('mouseout', function() { index = []; updateValueLine(); }); 1.11965 + 1.11966 + g.select('.nv-hoverArea rect') 1.11967 + .attr('transform', function(d) { return 'translate(' + -margin.left + ',' + -margin.top + ')' }) 1.11968 + .attr('width', availableWidth + margin.left + margin.right) 1.11969 + .attr('height', availableHeight + margin.top); 1.11970 + 1.11971 + //index is currently global (within the chart), may or may not keep it that way 1.11972 + function updateValueLine() { 1.11973 + if (paused) return; 1.11974 + 1.11975 + var hoverValue = g.selectAll('.nv-hoverValue').data(index); 1.11976 + 1.11977 + var hoverEnter = hoverValue.enter() 1.11978 + .append('g').attr('class', 'nv-hoverValue') 1.11979 + .style('stroke-opacity', 0) 1.11980 + .style('fill-opacity', 0); 1.11981 + 1.11982 + hoverValue.exit() 1.11983 + .transition().duration(250) 1.11984 + .style('stroke-opacity', 0) 1.11985 + .style('fill-opacity', 0) 1.11986 + .remove(); 1.11987 + 1.11988 + hoverValue 1.11989 + .attr('transform', function(d) { return 'translate(' + x(sparkline.x()(data[d],d)) + ',0)' }) 1.11990 + .transition().duration(250) 1.11991 + .style('stroke-opacity', 1) 1.11992 + .style('fill-opacity', 1); 1.11993 + 1.11994 + if (!index.length) return; 1.11995 + 1.11996 + hoverEnter.append('line') 1.11997 + .attr('x1', 0) 1.11998 + .attr('y1', -margin.top) 1.11999 + .attr('x2', 0) 1.12000 + .attr('y2', availableHeight); 1.12001 + 1.12002 + hoverEnter.append('text').attr('class', 'nv-xValue') 1.12003 + .attr('x', -6) 1.12004 + .attr('y', -margin.top) 1.12005 + .attr('text-anchor', 'end') 1.12006 + .attr('dy', '.9em'); 1.12007 + 1.12008 + g.select('.nv-hoverValue .nv-xValue') 1.12009 + .text(xTickFormat(sparkline.x()(data[index[0]], index[0]))); 1.12010 + 1.12011 + hoverEnter.append('text').attr('class', 'nv-yValue') 1.12012 + .attr('x', 6) 1.12013 + .attr('y', -margin.top) 1.12014 + .attr('text-anchor', 'start') 1.12015 + .attr('dy', '.9em'); 1.12016 + 1.12017 + g.select('.nv-hoverValue .nv-yValue') 1.12018 + .text(yTickFormat(sparkline.y()(data[index[0]], index[0]))); 1.12019 + } 1.12020 + 1.12021 + function sparklineHover() { 1.12022 + if (paused) return; 1.12023 + 1.12024 + var pos = d3.mouse(this)[0] - margin.left; 1.12025 + 1.12026 + function getClosestIndex(data, x) { 1.12027 + var distance = Math.abs(sparkline.x()(data[0], 0) - x); 1.12028 + var closestIndex = 0; 1.12029 + for (var i = 0; i < data.length; i++){ 1.12030 + if (Math.abs(sparkline.x()(data[i], i) - x) < distance) { 1.12031 + distance = Math.abs(sparkline.x()(data[i], i) - x); 1.12032 + closestIndex = i; 1.12033 + } 1.12034 + } 1.12035 + return closestIndex; 1.12036 + } 1.12037 + 1.12038 + index = [getClosestIndex(data, Math.round(x.invert(pos)))]; 1.12039 + updateValueLine(); 1.12040 + } 1.12041 + 1.12042 + }); 1.12043 + 1.12044 + return chart; 1.12045 + } 1.12046 + 1.12047 + //============================================================ 1.12048 + // Expose Public Variables 1.12049 + //------------------------------------------------------------ 1.12050 + 1.12051 + // expose chart's sub-components 1.12052 + chart.sparkline = sparkline; 1.12053 + 1.12054 + chart.options = nv.utils.optionsFunc.bind(chart); 1.12055 + 1.12056 + chart._options = Object.create({}, { 1.12057 + // simple options, just get/set the necessary values 1.12058 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.12059 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.12060 + xTickFormat: {get: function(){return xTickFormat;}, set: function(_){xTickFormat=_;}}, 1.12061 + yTickFormat: {get: function(){return yTickFormat;}, set: function(_){yTickFormat=_;}}, 1.12062 + showLastValue: {get: function(){return showLastValue;}, set: function(_){showLastValue=_;}}, 1.12063 + alignValue: {get: function(){return alignValue;}, set: function(_){alignValue=_;}}, 1.12064 + rightAlignValue: {get: function(){return rightAlignValue;}, set: function(_){rightAlignValue=_;}}, 1.12065 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.12066 + 1.12067 + // options that require extra logic in the setter 1.12068 + margin: {get: function(){return margin;}, set: function(_){ 1.12069 + margin.top = _.top !== undefined ? _.top : margin.top; 1.12070 + margin.right = _.right !== undefined ? _.right : margin.right; 1.12071 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.12072 + margin.left = _.left !== undefined ? _.left : margin.left; 1.12073 + }} 1.12074 + }); 1.12075 + 1.12076 + nv.utils.inheritOptions(chart, sparkline); 1.12077 + nv.utils.initOptions(chart); 1.12078 + 1.12079 + return chart; 1.12080 +}; 1.12081 + 1.12082 +nv.models.stackedArea = function() { 1.12083 + "use strict"; 1.12084 + 1.12085 + //============================================================ 1.12086 + // Public Variables with Default Settings 1.12087 + //------------------------------------------------------------ 1.12088 + 1.12089 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.12090 + , width = 960 1.12091 + , height = 500 1.12092 + , color = nv.utils.defaultColor() // a function that computes the color 1.12093 + , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't selet one 1.12094 + , container = null 1.12095 + , getX = function(d) { return d.x } // accessor to get the x value from a data point 1.12096 + , getY = function(d) { return d.y } // accessor to get the y value from a data point 1.12097 + , style = 'stack' 1.12098 + , offset = 'zero' 1.12099 + , order = 'default' 1.12100 + , interpolate = 'linear' // controls the line interpolation 1.12101 + , clipEdge = false // if true, masks lines within x and y scale 1.12102 + , x //can be accessed via chart.xScale() 1.12103 + , y //can be accessed via chart.yScale() 1.12104 + , scatter = nv.models.scatter() 1.12105 + , duration = 250 1.12106 + , dispatch = d3.dispatch('areaClick', 'areaMouseover', 'areaMouseout','renderEnd', 'elementClick', 'elementMouseover', 'elementMouseout') 1.12107 + ; 1.12108 + 1.12109 + scatter 1.12110 + .pointSize(2.2) // default size 1.12111 + .pointDomain([2.2, 2.2]) // all the same size by default 1.12112 + ; 1.12113 + 1.12114 + /************************************ 1.12115 + * offset: 1.12116 + * 'wiggle' (stream) 1.12117 + * 'zero' (stacked) 1.12118 + * 'expand' (normalize to 100%) 1.12119 + * 'silhouette' (simple centered) 1.12120 + * 1.12121 + * order: 1.12122 + * 'inside-out' (stream) 1.12123 + * 'default' (input order) 1.12124 + ************************************/ 1.12125 + 1.12126 + var renderWatch = nv.utils.renderWatch(dispatch, duration); 1.12127 + 1.12128 + function chart(selection) { 1.12129 + renderWatch.reset(); 1.12130 + renderWatch.models(scatter); 1.12131 + selection.each(function(data) { 1.12132 + var availableWidth = width - margin.left - margin.right, 1.12133 + availableHeight = height - margin.top - margin.bottom; 1.12134 + 1.12135 + container = d3.select(this); 1.12136 + nv.utils.initSVG(container); 1.12137 + 1.12138 + // Setup Scales 1.12139 + x = scatter.xScale(); 1.12140 + y = scatter.yScale(); 1.12141 + 1.12142 + var dataRaw = data; 1.12143 + // Injecting point index into each point because d3.layout.stack().out does not give index 1.12144 + data.forEach(function(aseries, i) { 1.12145 + aseries.seriesIndex = i; 1.12146 + aseries.values = aseries.values.map(function(d, j) { 1.12147 + d.index = j; 1.12148 + d.seriesIndex = i; 1.12149 + return d; 1.12150 + }); 1.12151 + }); 1.12152 + 1.12153 + var dataFiltered = data.filter(function(series) { 1.12154 + return !series.disabled; 1.12155 + }); 1.12156 + 1.12157 + data = d3.layout.stack() 1.12158 + .order(order) 1.12159 + .offset(offset) 1.12160 + .values(function(d) { return d.values }) //TODO: make values customizeable in EVERY model in this fashion 1.12161 + .x(getX) 1.12162 + .y(getY) 1.12163 + .out(function(d, y0, y) { 1.12164 + d.display = { 1.12165 + y: y, 1.12166 + y0: y0 1.12167 + }; 1.12168 + }) 1.12169 + (dataFiltered); 1.12170 + 1.12171 + // Setup containers and skeleton of chart 1.12172 + var wrap = container.selectAll('g.nv-wrap.nv-stackedarea').data([data]); 1.12173 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedarea'); 1.12174 + var defsEnter = wrapEnter.append('defs'); 1.12175 + var gEnter = wrapEnter.append('g'); 1.12176 + var g = wrap.select('g'); 1.12177 + 1.12178 + gEnter.append('g').attr('class', 'nv-areaWrap'); 1.12179 + gEnter.append('g').attr('class', 'nv-scatterWrap'); 1.12180 + 1.12181 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.12182 + 1.12183 + // If the user has not specified forceY, make sure 0 is included in the domain 1.12184 + // Otherwise, use user-specified values for forceY 1.12185 + if (scatter.forceY().length == 0) { 1.12186 + scatter.forceY().push(0); 1.12187 + } 1.12188 + 1.12189 + scatter 1.12190 + .width(availableWidth) 1.12191 + .height(availableHeight) 1.12192 + .x(getX) 1.12193 + .y(function(d) { return d.display.y + d.display.y0 }) 1.12194 + .forceY([0]) 1.12195 + .color(data.map(function(d,i) { 1.12196 + return d.color || color(d, d.seriesIndex); 1.12197 + })); 1.12198 + 1.12199 + var scatterWrap = g.select('.nv-scatterWrap') 1.12200 + .datum(data); 1.12201 + 1.12202 + scatterWrap.call(scatter); 1.12203 + 1.12204 + defsEnter.append('clipPath') 1.12205 + .attr('id', 'nv-edge-clip-' + id) 1.12206 + .append('rect'); 1.12207 + 1.12208 + wrap.select('#nv-edge-clip-' + id + ' rect') 1.12209 + .attr('width', availableWidth) 1.12210 + .attr('height', availableHeight); 1.12211 + 1.12212 + g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : ''); 1.12213 + 1.12214 + var area = d3.svg.area() 1.12215 + .x(function(d,i) { return x(getX(d,i)) }) 1.12216 + .y0(function(d) { 1.12217 + return y(d.display.y0) 1.12218 + }) 1.12219 + .y1(function(d) { 1.12220 + return y(d.display.y + d.display.y0) 1.12221 + }) 1.12222 + .interpolate(interpolate); 1.12223 + 1.12224 + var zeroArea = d3.svg.area() 1.12225 + .x(function(d,i) { return x(getX(d,i)) }) 1.12226 + .y0(function(d) { return y(d.display.y0) }) 1.12227 + .y1(function(d) { return y(d.display.y0) }); 1.12228 + 1.12229 + var path = g.select('.nv-areaWrap').selectAll('path.nv-area') 1.12230 + .data(function(d) { return d }); 1.12231 + 1.12232 + path.enter().append('path').attr('class', function(d,i) { return 'nv-area nv-area-' + i }) 1.12233 + .attr('d', function(d,i){ 1.12234 + return zeroArea(d.values, d.seriesIndex); 1.12235 + }) 1.12236 + .on('mouseover', function(d,i) { 1.12237 + d3.select(this).classed('hover', true); 1.12238 + dispatch.areaMouseover({ 1.12239 + point: d, 1.12240 + series: d.key, 1.12241 + pos: [d3.event.pageX, d3.event.pageY], 1.12242 + seriesIndex: d.seriesIndex 1.12243 + }); 1.12244 + }) 1.12245 + .on('mouseout', function(d,i) { 1.12246 + d3.select(this).classed('hover', false); 1.12247 + dispatch.areaMouseout({ 1.12248 + point: d, 1.12249 + series: d.key, 1.12250 + pos: [d3.event.pageX, d3.event.pageY], 1.12251 + seriesIndex: d.seriesIndex 1.12252 + }); 1.12253 + }) 1.12254 + .on('click', function(d,i) { 1.12255 + d3.select(this).classed('hover', false); 1.12256 + dispatch.areaClick({ 1.12257 + point: d, 1.12258 + series: d.key, 1.12259 + pos: [d3.event.pageX, d3.event.pageY], 1.12260 + seriesIndex: d.seriesIndex 1.12261 + }); 1.12262 + }); 1.12263 + 1.12264 + path.exit().remove(); 1.12265 + path.style('fill', function(d,i){ 1.12266 + return d.color || color(d, d.seriesIndex) 1.12267 + }) 1.12268 + .style('stroke', function(d,i){ return d.color || color(d, d.seriesIndex) }); 1.12269 + path.watchTransition(renderWatch,'stackedArea path') 1.12270 + .attr('d', function(d,i) { 1.12271 + return area(d.values,i) 1.12272 + }); 1.12273 + 1.12274 + //============================================================ 1.12275 + // Event Handling/Dispatching (in chart's scope) 1.12276 + //------------------------------------------------------------ 1.12277 + 1.12278 + scatter.dispatch.on('elementMouseover.area', function(e) { 1.12279 + g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', true); 1.12280 + }); 1.12281 + scatter.dispatch.on('elementMouseout.area', function(e) { 1.12282 + g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', false); 1.12283 + }); 1.12284 + 1.12285 + //Special offset functions 1.12286 + chart.d3_stackedOffset_stackPercent = function(stackData) { 1.12287 + var n = stackData.length, //How many series 1.12288 + m = stackData[0].length, //how many points per series 1.12289 + i, 1.12290 + j, 1.12291 + o, 1.12292 + y0 = []; 1.12293 + 1.12294 + for (j = 0; j < m; ++j) { //Looping through all points 1.12295 + for (i = 0, o = 0; i < dataRaw.length; i++) { //looping through all series 1.12296 + o += getY(dataRaw[i].values[j]); //total y value of all series at a certian point in time. 1.12297 + } 1.12298 + 1.12299 + if (o) for (i = 0; i < n; i++) { //(total y value of all series at point in time i) != 0 1.12300 + stackData[i][j][1] /= o; 1.12301 + } else { //(total y value of all series at point in time i) == 0 1.12302 + for (i = 0; i < n; i++) { 1.12303 + stackData[i][j][1] = 0; 1.12304 + } 1.12305 + } 1.12306 + } 1.12307 + for (j = 0; j < m; ++j) y0[j] = 0; 1.12308 + return y0; 1.12309 + }; 1.12310 + 1.12311 + }); 1.12312 + 1.12313 + renderWatch.renderEnd('stackedArea immediate'); 1.12314 + return chart; 1.12315 + } 1.12316 + 1.12317 + //============================================================ 1.12318 + // Global getters and setters 1.12319 + //------------------------------------------------------------ 1.12320 + 1.12321 + chart.dispatch = dispatch; 1.12322 + chart.scatter = scatter; 1.12323 + 1.12324 + scatter.dispatch.on('elementClick', function(){ dispatch.elementClick.apply(this, arguments); }); 1.12325 + scatter.dispatch.on('elementMouseover', function(){ dispatch.elementMouseover.apply(this, arguments); }); 1.12326 + scatter.dispatch.on('elementMouseout', function(){ dispatch.elementMouseout.apply(this, arguments); }); 1.12327 + 1.12328 + chart.interpolate = function(_) { 1.12329 + if (!arguments.length) return interpolate; 1.12330 + interpolate = _; 1.12331 + return chart; 1.12332 + }; 1.12333 + 1.12334 + chart.duration = function(_) { 1.12335 + if (!arguments.length) return duration; 1.12336 + duration = _; 1.12337 + renderWatch.reset(duration); 1.12338 + scatter.duration(duration); 1.12339 + return chart; 1.12340 + }; 1.12341 + 1.12342 + chart.dispatch = dispatch; 1.12343 + chart.scatter = scatter; 1.12344 + chart.options = nv.utils.optionsFunc.bind(chart); 1.12345 + 1.12346 + chart._options = Object.create({}, { 1.12347 + // simple options, just get/set the necessary values 1.12348 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.12349 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.12350 + clipEdge: {get: function(){return clipEdge;}, set: function(_){clipEdge=_;}}, 1.12351 + offset: {get: function(){return offset;}, set: function(_){offset=_;}}, 1.12352 + order: {get: function(){return order;}, set: function(_){order=_;}}, 1.12353 + interpolate: {get: function(){return interpolate;}, set: function(_){interpolate=_;}}, 1.12354 + 1.12355 + // simple functor options 1.12356 + x: {get: function(){return getX;}, set: function(_){getX = d3.functor(_);}}, 1.12357 + y: {get: function(){return getY;}, set: function(_){getY = d3.functor(_);}}, 1.12358 + 1.12359 + // options that require extra logic in the setter 1.12360 + margin: {get: function(){return margin;}, set: function(_){ 1.12361 + margin.top = _.top !== undefined ? _.top : margin.top; 1.12362 + margin.right = _.right !== undefined ? _.right : margin.right; 1.12363 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.12364 + margin.left = _.left !== undefined ? _.left : margin.left; 1.12365 + }}, 1.12366 + color: {get: function(){return color;}, set: function(_){ 1.12367 + color = nv.utils.getColor(_); 1.12368 + }}, 1.12369 + style: {get: function(){return style;}, set: function(_){ 1.12370 + style = _; 1.12371 + switch (style) { 1.12372 + case 'stack': 1.12373 + chart.offset('zero'); 1.12374 + chart.order('default'); 1.12375 + break; 1.12376 + case 'stream': 1.12377 + chart.offset('wiggle'); 1.12378 + chart.order('inside-out'); 1.12379 + break; 1.12380 + case 'stream-center': 1.12381 + chart.offset('silhouette'); 1.12382 + chart.order('inside-out'); 1.12383 + break; 1.12384 + case 'expand': 1.12385 + chart.offset('expand'); 1.12386 + chart.order('default'); 1.12387 + break; 1.12388 + case 'stack_percent': 1.12389 + chart.offset(chart.d3_stackedOffset_stackPercent); 1.12390 + chart.order('default'); 1.12391 + break; 1.12392 + } 1.12393 + }}, 1.12394 + duration: {get: function(){return duration;}, set: function(_){ 1.12395 + duration = _; 1.12396 + renderWatch.reset(duration); 1.12397 + scatter.duration(duration); 1.12398 + }} 1.12399 + }); 1.12400 + 1.12401 + nv.utils.inheritOptions(chart, scatter); 1.12402 + nv.utils.initOptions(chart); 1.12403 + 1.12404 + return chart; 1.12405 +}; 1.12406 + 1.12407 +nv.models.stackedAreaChart = function() { 1.12408 + "use strict"; 1.12409 + 1.12410 + //============================================================ 1.12411 + // Public Variables with Default Settings 1.12412 + //------------------------------------------------------------ 1.12413 + 1.12414 + var stacked = nv.models.stackedArea() 1.12415 + , xAxis = nv.models.axis() 1.12416 + , yAxis = nv.models.axis() 1.12417 + , legend = nv.models.legend() 1.12418 + , controls = nv.models.legend() 1.12419 + , interactiveLayer = nv.interactiveGuideline() 1.12420 + , tooltip = nv.models.tooltip() 1.12421 + ; 1.12422 + 1.12423 + var margin = {top: 30, right: 25, bottom: 50, left: 60} 1.12424 + , width = null 1.12425 + , height = null 1.12426 + , color = nv.utils.defaultColor() 1.12427 + , showControls = true 1.12428 + , showLegend = true 1.12429 + , showXAxis = true 1.12430 + , showYAxis = true 1.12431 + , rightAlignYAxis = false 1.12432 + , useInteractiveGuideline = false 1.12433 + , x //can be accessed via chart.xScale() 1.12434 + , y //can be accessed via chart.yScale() 1.12435 + , state = nv.utils.state() 1.12436 + , defaultState = null 1.12437 + , noData = null 1.12438 + , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd') 1.12439 + , controlWidth = 250 1.12440 + , controlOptions = ['Stacked','Stream','Expanded'] 1.12441 + , controlLabels = {} 1.12442 + , duration = 250 1.12443 + ; 1.12444 + 1.12445 + state.style = stacked.style(); 1.12446 + xAxis.orient('bottom').tickPadding(7); 1.12447 + yAxis.orient((rightAlignYAxis) ? 'right' : 'left'); 1.12448 + 1.12449 + tooltip 1.12450 + .headerFormatter(function(d, i) { 1.12451 + return xAxis.tickFormat()(d, i); 1.12452 + }) 1.12453 + .valueFormatter(function(d, i) { 1.12454 + return yAxis.tickFormat()(d, i); 1.12455 + }); 1.12456 + 1.12457 + interactiveLayer.tooltip 1.12458 + .headerFormatter(function(d, i) { 1.12459 + return xAxis.tickFormat()(d, i); 1.12460 + }) 1.12461 + .valueFormatter(function(d, i) { 1.12462 + return yAxis.tickFormat()(d, i); 1.12463 + }); 1.12464 + 1.12465 + var oldYTickFormat = null, 1.12466 + oldValueFormatter = null; 1.12467 + 1.12468 + controls.updateState(false); 1.12469 + 1.12470 + //============================================================ 1.12471 + // Private Variables 1.12472 + //------------------------------------------------------------ 1.12473 + 1.12474 + var renderWatch = nv.utils.renderWatch(dispatch); 1.12475 + var style = stacked.style(); 1.12476 + 1.12477 + var stateGetter = function(data) { 1.12478 + return function(){ 1.12479 + return { 1.12480 + active: data.map(function(d) { return !d.disabled }), 1.12481 + style: stacked.style() 1.12482 + }; 1.12483 + } 1.12484 + }; 1.12485 + 1.12486 + var stateSetter = function(data) { 1.12487 + return function(state) { 1.12488 + if (state.style !== undefined) 1.12489 + style = state.style; 1.12490 + if (state.active !== undefined) 1.12491 + data.forEach(function(series,i) { 1.12492 + series.disabled = !state.active[i]; 1.12493 + }); 1.12494 + } 1.12495 + }; 1.12496 + 1.12497 + var percentFormatter = d3.format('%'); 1.12498 + 1.12499 + function chart(selection) { 1.12500 + renderWatch.reset(); 1.12501 + renderWatch.models(stacked); 1.12502 + if (showXAxis) renderWatch.models(xAxis); 1.12503 + if (showYAxis) renderWatch.models(yAxis); 1.12504 + 1.12505 + selection.each(function(data) { 1.12506 + var container = d3.select(this), 1.12507 + that = this; 1.12508 + nv.utils.initSVG(container); 1.12509 + 1.12510 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.12511 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.12512 + 1.12513 + chart.update = function() { container.transition().duration(duration).call(chart); }; 1.12514 + chart.container = this; 1.12515 + 1.12516 + state 1.12517 + .setter(stateSetter(data), chart.update) 1.12518 + .getter(stateGetter(data)) 1.12519 + .update(); 1.12520 + 1.12521 + // DEPRECATED set state.disabled 1.12522 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.12523 + 1.12524 + if (!defaultState) { 1.12525 + var key; 1.12526 + defaultState = {}; 1.12527 + for (key in state) { 1.12528 + if (state[key] instanceof Array) 1.12529 + defaultState[key] = state[key].slice(0); 1.12530 + else 1.12531 + defaultState[key] = state[key]; 1.12532 + } 1.12533 + } 1.12534 + 1.12535 + // Display No Data message if there's nothing to show. 1.12536 + if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { 1.12537 + nv.utils.noData(chart, container) 1.12538 + return chart; 1.12539 + } else { 1.12540 + container.selectAll('.nv-noData').remove(); 1.12541 + } 1.12542 + 1.12543 + // Setup Scales 1.12544 + x = stacked.xScale(); 1.12545 + y = stacked.yScale(); 1.12546 + 1.12547 + // Setup containers and skeleton of chart 1.12548 + var wrap = container.selectAll('g.nv-wrap.nv-stackedAreaChart').data([data]); 1.12549 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedAreaChart').append('g'); 1.12550 + var g = wrap.select('g'); 1.12551 + 1.12552 + gEnter.append("rect").style("opacity",0); 1.12553 + gEnter.append('g').attr('class', 'nv-x nv-axis'); 1.12554 + gEnter.append('g').attr('class', 'nv-y nv-axis'); 1.12555 + gEnter.append('g').attr('class', 'nv-stackedWrap'); 1.12556 + gEnter.append('g').attr('class', 'nv-legendWrap'); 1.12557 + gEnter.append('g').attr('class', 'nv-controlsWrap'); 1.12558 + gEnter.append('g').attr('class', 'nv-interactive'); 1.12559 + 1.12560 + g.select("rect").attr("width",availableWidth).attr("height",availableHeight); 1.12561 + 1.12562 + // Legend 1.12563 + if (showLegend) { 1.12564 + var legendWidth = (showControls) ? availableWidth - controlWidth : availableWidth; 1.12565 + 1.12566 + legend.width(legendWidth); 1.12567 + g.select('.nv-legendWrap').datum(data).call(legend); 1.12568 + 1.12569 + if ( margin.top != legend.height()) { 1.12570 + margin.top = legend.height(); 1.12571 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.12572 + } 1.12573 + 1.12574 + g.select('.nv-legendWrap') 1.12575 + .attr('transform', 'translate(' + (availableWidth-legendWidth) + ',' + (-margin.top) +')'); 1.12576 + } 1.12577 + 1.12578 + // Controls 1.12579 + if (showControls) { 1.12580 + var controlsData = [ 1.12581 + { 1.12582 + key: controlLabels.stacked || 'Stacked', 1.12583 + metaKey: 'Stacked', 1.12584 + disabled: stacked.style() != 'stack', 1.12585 + style: 'stack' 1.12586 + }, 1.12587 + { 1.12588 + key: controlLabels.stream || 'Stream', 1.12589 + metaKey: 'Stream', 1.12590 + disabled: stacked.style() != 'stream', 1.12591 + style: 'stream' 1.12592 + }, 1.12593 + { 1.12594 + key: controlLabels.expanded || 'Expanded', 1.12595 + metaKey: 'Expanded', 1.12596 + disabled: stacked.style() != 'expand', 1.12597 + style: 'expand' 1.12598 + }, 1.12599 + { 1.12600 + key: controlLabels.stack_percent || 'Stack %', 1.12601 + metaKey: 'Stack_Percent', 1.12602 + disabled: stacked.style() != 'stack_percent', 1.12603 + style: 'stack_percent' 1.12604 + } 1.12605 + ]; 1.12606 + 1.12607 + controlWidth = (controlOptions.length/3) * 260; 1.12608 + controlsData = controlsData.filter(function(d) { 1.12609 + return controlOptions.indexOf(d.metaKey) !== -1; 1.12610 + }); 1.12611 + 1.12612 + controls 1.12613 + .width( controlWidth ) 1.12614 + .color(['#444', '#444', '#444']); 1.12615 + 1.12616 + g.select('.nv-controlsWrap') 1.12617 + .datum(controlsData) 1.12618 + .call(controls); 1.12619 + 1.12620 + if ( margin.top != Math.max(controls.height(), legend.height()) ) { 1.12621 + margin.top = Math.max(controls.height(), legend.height()); 1.12622 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.12623 + } 1.12624 + 1.12625 + g.select('.nv-controlsWrap') 1.12626 + .attr('transform', 'translate(0,' + (-margin.top) +')'); 1.12627 + } 1.12628 + 1.12629 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.12630 + 1.12631 + if (rightAlignYAxis) { 1.12632 + g.select(".nv-y.nv-axis") 1.12633 + .attr("transform", "translate(" + availableWidth + ",0)"); 1.12634 + } 1.12635 + 1.12636 + //Set up interactive layer 1.12637 + if (useInteractiveGuideline) { 1.12638 + interactiveLayer 1.12639 + .width(availableWidth) 1.12640 + .height(availableHeight) 1.12641 + .margin({left: margin.left, top: margin.top}) 1.12642 + .svgContainer(container) 1.12643 + .xScale(x); 1.12644 + wrap.select(".nv-interactive").call(interactiveLayer); 1.12645 + } 1.12646 + 1.12647 + stacked 1.12648 + .width(availableWidth) 1.12649 + .height(availableHeight); 1.12650 + 1.12651 + var stackedWrap = g.select('.nv-stackedWrap') 1.12652 + .datum(data); 1.12653 + 1.12654 + stackedWrap.transition().call(stacked); 1.12655 + 1.12656 + // Setup Axes 1.12657 + if (showXAxis) { 1.12658 + xAxis.scale(x) 1.12659 + ._ticks( nv.utils.calcTicksX(availableWidth/100, data) ) 1.12660 + .tickSize( -availableHeight, 0); 1.12661 + 1.12662 + g.select('.nv-x.nv-axis') 1.12663 + .attr('transform', 'translate(0,' + availableHeight + ')'); 1.12664 + 1.12665 + g.select('.nv-x.nv-axis') 1.12666 + .transition().duration(0) 1.12667 + .call(xAxis); 1.12668 + } 1.12669 + 1.12670 + if (showYAxis) { 1.12671 + var ticks; 1.12672 + if (stacked.offset() === 'wiggle') { 1.12673 + ticks = 0; 1.12674 + } 1.12675 + else { 1.12676 + ticks = nv.utils.calcTicksY(availableHeight/36, data); 1.12677 + } 1.12678 + yAxis.scale(y) 1.12679 + ._ticks(ticks) 1.12680 + .tickSize(-availableWidth, 0); 1.12681 + 1.12682 + if (stacked.style() === 'expand' || stacked.style() === 'stack_percent') { 1.12683 + var currentFormat = yAxis.tickFormat(); 1.12684 + 1.12685 + if ( !oldYTickFormat || currentFormat !== percentFormatter ) 1.12686 + oldYTickFormat = currentFormat; 1.12687 + 1.12688 + //Forces the yAxis to use percentage in 'expand' mode. 1.12689 + yAxis.tickFormat(percentFormatter); 1.12690 + } 1.12691 + else { 1.12692 + if (oldYTickFormat) { 1.12693 + yAxis.tickFormat(oldYTickFormat); 1.12694 + oldYTickFormat = null; 1.12695 + } 1.12696 + } 1.12697 + 1.12698 + g.select('.nv-y.nv-axis') 1.12699 + .transition().duration(0) 1.12700 + .call(yAxis); 1.12701 + } 1.12702 + 1.12703 + //============================================================ 1.12704 + // Event Handling/Dispatching (in chart's scope) 1.12705 + //------------------------------------------------------------ 1.12706 + 1.12707 + stacked.dispatch.on('areaClick.toggle', function(e) { 1.12708 + if (data.filter(function(d) { return !d.disabled }).length === 1) 1.12709 + data.forEach(function(d) { 1.12710 + d.disabled = false; 1.12711 + }); 1.12712 + else 1.12713 + data.forEach(function(d,i) { 1.12714 + d.disabled = (i != e.seriesIndex); 1.12715 + }); 1.12716 + 1.12717 + state.disabled = data.map(function(d) { return !!d.disabled }); 1.12718 + dispatch.stateChange(state); 1.12719 + 1.12720 + chart.update(); 1.12721 + }); 1.12722 + 1.12723 + legend.dispatch.on('stateChange', function(newState) { 1.12724 + for (var key in newState) 1.12725 + state[key] = newState[key]; 1.12726 + dispatch.stateChange(state); 1.12727 + chart.update(); 1.12728 + }); 1.12729 + 1.12730 + controls.dispatch.on('legendClick', function(d,i) { 1.12731 + if (!d.disabled) return; 1.12732 + 1.12733 + controlsData = controlsData.map(function(s) { 1.12734 + s.disabled = true; 1.12735 + return s; 1.12736 + }); 1.12737 + d.disabled = false; 1.12738 + 1.12739 + stacked.style(d.style); 1.12740 + 1.12741 + 1.12742 + state.style = stacked.style(); 1.12743 + dispatch.stateChange(state); 1.12744 + 1.12745 + chart.update(); 1.12746 + }); 1.12747 + 1.12748 + interactiveLayer.dispatch.on('elementMousemove', function(e) { 1.12749 + stacked.clearHighlights(); 1.12750 + var singlePoint, pointIndex, pointXLocation, allData = []; 1.12751 + data 1.12752 + .filter(function(series, i) { 1.12753 + series.seriesIndex = i; 1.12754 + return !series.disabled; 1.12755 + }) 1.12756 + .forEach(function(series,i) { 1.12757 + pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x()); 1.12758 + var point = series.values[pointIndex]; 1.12759 + var pointYValue = chart.y()(point, pointIndex); 1.12760 + if (pointYValue != null) { 1.12761 + stacked.highlightPoint(i, pointIndex, true); 1.12762 + } 1.12763 + if (typeof point === 'undefined') return; 1.12764 + if (typeof singlePoint === 'undefined') singlePoint = point; 1.12765 + if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex)); 1.12766 + 1.12767 + //If we are in 'expand' mode, use the stacked percent value instead of raw value. 1.12768 + var tooltipValue = (stacked.style() == 'expand') ? point.display.y : chart.y()(point,pointIndex); 1.12769 + allData.push({ 1.12770 + key: series.key, 1.12771 + value: tooltipValue, 1.12772 + color: color(series,series.seriesIndex), 1.12773 + stackedValue: point.display 1.12774 + }); 1.12775 + }); 1.12776 + 1.12777 + allData.reverse(); 1.12778 + 1.12779 + //Highlight the tooltip entry based on which stack the mouse is closest to. 1.12780 + if (allData.length > 2) { 1.12781 + var yValue = chart.yScale().invert(e.mouseY); 1.12782 + var yDistMax = Infinity, indexToHighlight = null; 1.12783 + allData.forEach(function(series,i) { 1.12784 + 1.12785 + //To handle situation where the stacked area chart is negative, we need to use absolute values 1.12786 + //when checking if the mouse Y value is within the stack area. 1.12787 + yValue = Math.abs(yValue); 1.12788 + var stackedY0 = Math.abs(series.stackedValue.y0); 1.12789 + var stackedY = Math.abs(series.stackedValue.y); 1.12790 + if ( yValue >= stackedY0 && yValue <= (stackedY + stackedY0)) 1.12791 + { 1.12792 + indexToHighlight = i; 1.12793 + return; 1.12794 + } 1.12795 + }); 1.12796 + if (indexToHighlight != null) 1.12797 + allData[indexToHighlight].highlight = true; 1.12798 + } 1.12799 + 1.12800 + var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex)); 1.12801 + 1.12802 + var valueFormatter = interactiveLayer.tooltip.valueFormatter(); 1.12803 + // Keeps track of the tooltip valueFormatter if the chart changes to expanded view 1.12804 + if (stacked.style() === 'expand' || stacked.style() === 'stack_percent') { 1.12805 + if ( !oldValueFormatter ) { 1.12806 + oldValueFormatter = valueFormatter; 1.12807 + } 1.12808 + //Forces the tooltip to use percentage in 'expand' mode. 1.12809 + valueFormatter = d3.format(".1%"); 1.12810 + } 1.12811 + else { 1.12812 + if (oldValueFormatter) { 1.12813 + valueFormatter = oldValueFormatter; 1.12814 + oldValueFormatter = null; 1.12815 + } 1.12816 + } 1.12817 + 1.12818 + interactiveLayer.tooltip 1.12819 + .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top}) 1.12820 + .chartContainer(that.parentNode) 1.12821 + .valueFormatter(valueFormatter) 1.12822 + .data( 1.12823 + { 1.12824 + value: xValue, 1.12825 + series: allData 1.12826 + } 1.12827 + )(); 1.12828 + 1.12829 + interactiveLayer.renderGuideLine(pointXLocation); 1.12830 + 1.12831 + }); 1.12832 + 1.12833 + interactiveLayer.dispatch.on("elementMouseout",function(e) { 1.12834 + stacked.clearHighlights(); 1.12835 + }); 1.12836 + 1.12837 + // Update chart from a state object passed to event handler 1.12838 + dispatch.on('changeState', function(e) { 1.12839 + 1.12840 + if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) { 1.12841 + data.forEach(function(series,i) { 1.12842 + series.disabled = e.disabled[i]; 1.12843 + }); 1.12844 + 1.12845 + state.disabled = e.disabled; 1.12846 + } 1.12847 + 1.12848 + if (typeof e.style !== 'undefined') { 1.12849 + stacked.style(e.style); 1.12850 + style = e.style; 1.12851 + } 1.12852 + 1.12853 + chart.update(); 1.12854 + }); 1.12855 + 1.12856 + }); 1.12857 + 1.12858 + renderWatch.renderEnd('stacked Area chart immediate'); 1.12859 + return chart; 1.12860 + } 1.12861 + 1.12862 + //============================================================ 1.12863 + // Event Handling/Dispatching (out of chart's scope) 1.12864 + //------------------------------------------------------------ 1.12865 + 1.12866 + stacked.dispatch.on('elementMouseover.tooltip', function(evt) { 1.12867 + evt.point['x'] = stacked.x()(evt.point); 1.12868 + evt.point['y'] = stacked.y()(evt.point); 1.12869 + tooltip.data(evt).position(evt.pos).hidden(false); 1.12870 + }); 1.12871 + 1.12872 + stacked.dispatch.on('elementMouseout.tooltip', function(evt) { 1.12873 + tooltip.hidden(true) 1.12874 + }); 1.12875 + 1.12876 + //============================================================ 1.12877 + // Expose Public Variables 1.12878 + //------------------------------------------------------------ 1.12879 + 1.12880 + // expose chart's sub-components 1.12881 + chart.dispatch = dispatch; 1.12882 + chart.stacked = stacked; 1.12883 + chart.legend = legend; 1.12884 + chart.controls = controls; 1.12885 + chart.xAxis = xAxis; 1.12886 + chart.yAxis = yAxis; 1.12887 + chart.interactiveLayer = interactiveLayer; 1.12888 + chart.tooltip = tooltip; 1.12889 + 1.12890 + chart.dispatch = dispatch; 1.12891 + chart.options = nv.utils.optionsFunc.bind(chart); 1.12892 + 1.12893 + chart._options = Object.create({}, { 1.12894 + // simple options, just get/set the necessary values 1.12895 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.12896 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.12897 + showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, 1.12898 + showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}}, 1.12899 + showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}}, 1.12900 + defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}}, 1.12901 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.12902 + showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}}, 1.12903 + controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}}, 1.12904 + controlOptions: {get: function(){return controlOptions;}, set: function(_){controlOptions=_;}}, 1.12905 + 1.12906 + // deprecated options 1.12907 + tooltips: {get: function(){return tooltip.enabled();}, set: function(_){ 1.12908 + // deprecated after 1.7.1 1.12909 + nv.deprecated('tooltips', 'use chart.tooltip.enabled() instead'); 1.12910 + tooltip.enabled(!!_); 1.12911 + }}, 1.12912 + tooltipContent: {get: function(){return tooltip.contentGenerator();}, set: function(_){ 1.12913 + // deprecated after 1.7.1 1.12914 + nv.deprecated('tooltipContent', 'use chart.tooltip.contentGenerator() instead'); 1.12915 + tooltip.contentGenerator(_); 1.12916 + }}, 1.12917 + 1.12918 + // options that require extra logic in the setter 1.12919 + margin: {get: function(){return margin;}, set: function(_){ 1.12920 + margin.top = _.top !== undefined ? _.top : margin.top; 1.12921 + margin.right = _.right !== undefined ? _.right : margin.right; 1.12922 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.12923 + margin.left = _.left !== undefined ? _.left : margin.left; 1.12924 + }}, 1.12925 + duration: {get: function(){return duration;}, set: function(_){ 1.12926 + duration = _; 1.12927 + renderWatch.reset(duration); 1.12928 + stacked.duration(duration); 1.12929 + xAxis.duration(duration); 1.12930 + yAxis.duration(duration); 1.12931 + }}, 1.12932 + color: {get: function(){return color;}, set: function(_){ 1.12933 + color = nv.utils.getColor(_); 1.12934 + legend.color(color); 1.12935 + stacked.color(color); 1.12936 + }}, 1.12937 + rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){ 1.12938 + rightAlignYAxis = _; 1.12939 + yAxis.orient( rightAlignYAxis ? 'right' : 'left'); 1.12940 + }}, 1.12941 + useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){ 1.12942 + useInteractiveGuideline = !!_; 1.12943 + chart.interactive(!_); 1.12944 + chart.useVoronoi(!_); 1.12945 + stacked.scatter.interactive(!_); 1.12946 + }} 1.12947 + }); 1.12948 + 1.12949 + nv.utils.inheritOptions(chart, stacked); 1.12950 + nv.utils.initOptions(chart); 1.12951 + 1.12952 + return chart; 1.12953 +}; 1.12954 +// based on http://bl.ocks.org/kerryrodden/477c1bfb081b783f80ad 1.12955 +nv.models.sunburst = function() { 1.12956 + "use strict"; 1.12957 + 1.12958 + //============================================================ 1.12959 + // Public Variables with Default Settings 1.12960 + //------------------------------------------------------------ 1.12961 + 1.12962 + var margin = {top: 0, right: 0, bottom: 0, left: 0} 1.12963 + , width = null 1.12964 + , height = null 1.12965 + , mode = "count" 1.12966 + , modes = {count: function(d) { return 1; }, size: function(d) { return d.size }} 1.12967 + , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one 1.12968 + , container = null 1.12969 + , color = nv.utils.defaultColor() 1.12970 + , duration = 500 1.12971 + , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMousemove', 'elementMouseover', 'elementMouseout', 'renderEnd') 1.12972 + ; 1.12973 + 1.12974 + var x = d3.scale.linear().range([0, 2 * Math.PI]); 1.12975 + var y = d3.scale.sqrt(); 1.12976 + 1.12977 + var partition = d3.layout.partition() 1.12978 + .sort(null) 1.12979 + .value(function(d) { return 1; }); 1.12980 + 1.12981 + var arc = d3.svg.arc() 1.12982 + .startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); }) 1.12983 + .endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); }) 1.12984 + .innerRadius(function(d) { return Math.max(0, y(d.y)); }) 1.12985 + .outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); }); 1.12986 + 1.12987 + // Keep track of the current and previous node being displayed as the root. 1.12988 + var node, prevNode; 1.12989 + // Keep track of the root node 1.12990 + var rootNode; 1.12991 + 1.12992 + //============================================================ 1.12993 + // chart function 1.12994 + //------------------------------------------------------------ 1.12995 + 1.12996 + var renderWatch = nv.utils.renderWatch(dispatch); 1.12997 + 1.12998 + function chart(selection) { 1.12999 + renderWatch.reset(); 1.13000 + selection.each(function(data) { 1.13001 + container = d3.select(this); 1.13002 + var availableWidth = nv.utils.availableWidth(width, container, margin); 1.13003 + var availableHeight = nv.utils.availableHeight(height, container, margin); 1.13004 + var radius = Math.min(availableWidth, availableHeight) / 2; 1.13005 + var path; 1.13006 + 1.13007 + nv.utils.initSVG(container); 1.13008 + 1.13009 + // Setup containers and skeleton of chart 1.13010 + var wrap = container.selectAll('.nv-wrap.nv-sunburst').data(data); 1.13011 + var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sunburst nv-chart-' + id); 1.13012 + 1.13013 + var g = wrapEnter.selectAll('nv-sunburst'); 1.13014 + 1.13015 + wrap.attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')'); 1.13016 + 1.13017 + container.on('click', function (d, i) { 1.13018 + dispatch.chartClick({ 1.13019 + data: d, 1.13020 + index: i, 1.13021 + pos: d3.event, 1.13022 + id: id 1.13023 + }); 1.13024 + }); 1.13025 + 1.13026 + y.range([0, radius]); 1.13027 + 1.13028 + node = node || data; 1.13029 + rootNode = data[0]; 1.13030 + partition.value(modes[mode] || modes["count"]); 1.13031 + path = g.data(partition.nodes).enter() 1.13032 + .append("path") 1.13033 + .attr("d", arc) 1.13034 + .style("fill", function (d) { 1.13035 + return color((d.children ? d : d.parent).name); 1.13036 + }) 1.13037 + .style("stroke", "#FFF") 1.13038 + .on("click", function(d) { 1.13039 + if (prevNode !== node && node !== d) prevNode = node; 1.13040 + node = d; 1.13041 + path.transition() 1.13042 + .duration(duration) 1.13043 + .attrTween("d", arcTweenZoom(d)); 1.13044 + }) 1.13045 + .each(stash) 1.13046 + .on("dblclick", function(d) { 1.13047 + if (prevNode.parent == d) { 1.13048 + path.transition() 1.13049 + .duration(duration) 1.13050 + .attrTween("d", arcTweenZoom(rootNode)); 1.13051 + } 1.13052 + }) 1.13053 + .each(stash) 1.13054 + .on('mouseover', function(d,i){ 1.13055 + d3.select(this).classed('hover', true).style('opacity', 0.8); 1.13056 + dispatch.elementMouseover({ 1.13057 + data: d, 1.13058 + color: d3.select(this).style("fill") 1.13059 + }); 1.13060 + }) 1.13061 + .on('mouseout', function(d,i){ 1.13062 + d3.select(this).classed('hover', false).style('opacity', 1); 1.13063 + dispatch.elementMouseout({ 1.13064 + data: d 1.13065 + }); 1.13066 + }) 1.13067 + .on('mousemove', function(d,i){ 1.13068 + dispatch.elementMousemove({ 1.13069 + data: d 1.13070 + }); 1.13071 + }); 1.13072 + 1.13073 + 1.13074 + 1.13075 + // Setup for switching data: stash the old values for transition. 1.13076 + function stash(d) { 1.13077 + d.x0 = d.x; 1.13078 + d.dx0 = d.dx; 1.13079 + } 1.13080 + 1.13081 + // When switching data: interpolate the arcs in data space. 1.13082 + function arcTweenData(a, i) { 1.13083 + var oi = d3.interpolate({x: a.x0, dx: a.dx0}, a); 1.13084 + 1.13085 + function tween(t) { 1.13086 + var b = oi(t); 1.13087 + a.x0 = b.x; 1.13088 + a.dx0 = b.dx; 1.13089 + return arc(b); 1.13090 + } 1.13091 + 1.13092 + if (i == 0) { 1.13093 + // If we are on the first arc, adjust the x domain to match the root node 1.13094 + // at the current zoom level. (We only need to do this once.) 1.13095 + var xd = d3.interpolate(x.domain(), [node.x, node.x + node.dx]); 1.13096 + return function (t) { 1.13097 + x.domain(xd(t)); 1.13098 + return tween(t); 1.13099 + }; 1.13100 + } else { 1.13101 + return tween; 1.13102 + } 1.13103 + } 1.13104 + 1.13105 + // When zooming: interpolate the scales. 1.13106 + function arcTweenZoom(d) { 1.13107 + var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]), 1.13108 + yd = d3.interpolate(y.domain(), [d.y, 1]), 1.13109 + yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]); 1.13110 + return function (d, i) { 1.13111 + return i 1.13112 + ? function (t) { 1.13113 + return arc(d); 1.13114 + } 1.13115 + : function (t) { 1.13116 + x.domain(xd(t)); 1.13117 + y.domain(yd(t)).range(yr(t)); 1.13118 + return arc(d); 1.13119 + }; 1.13120 + }; 1.13121 + } 1.13122 + 1.13123 + }); 1.13124 + 1.13125 + renderWatch.renderEnd('sunburst immediate'); 1.13126 + return chart; 1.13127 + } 1.13128 + 1.13129 + //============================================================ 1.13130 + // Expose Public Variables 1.13131 + //------------------------------------------------------------ 1.13132 + 1.13133 + chart.dispatch = dispatch; 1.13134 + chart.options = nv.utils.optionsFunc.bind(chart); 1.13135 + 1.13136 + chart._options = Object.create({}, { 1.13137 + // simple options, just get/set the necessary values 1.13138 + width: {get: function(){return width;}, set: function(_){width=_;}}, 1.13139 + height: {get: function(){return height;}, set: function(_){height=_;}}, 1.13140 + mode: {get: function(){return mode;}, set: function(_){mode=_;}}, 1.13141 + id: {get: function(){return id;}, set: function(_){id=_;}}, 1.13142 + duration: {get: function(){return duration;}, set: function(_){duration=_;}}, 1.13143 + 1.13144 + // options that require extra logic in the setter 1.13145 + margin: {get: function(){return margin;}, set: function(_){ 1.13146 + margin.top = _.top != undefined ? _.top : margin.top; 1.13147 + margin.right = _.right != undefined ? _.right : margin.right; 1.13148 + margin.bottom = _.bottom != undefined ? _.bottom : margin.bottom; 1.13149 + margin.left = _.left != undefined ? _.left : margin.left; 1.13150 + }}, 1.13151 + color: {get: function(){return color;}, set: function(_){ 1.13152 + color=nv.utils.getColor(_); 1.13153 + }} 1.13154 + }); 1.13155 + 1.13156 + nv.utils.initOptions(chart); 1.13157 + return chart; 1.13158 +}; 1.13159 +nv.models.sunburstChart = function() { 1.13160 + "use strict"; 1.13161 + 1.13162 + //============================================================ 1.13163 + // Public Variables with Default Settings 1.13164 + //------------------------------------------------------------ 1.13165 + 1.13166 + var sunburst = nv.models.sunburst(); 1.13167 + var tooltip = nv.models.tooltip(); 1.13168 + 1.13169 + var margin = {top: 30, right: 20, bottom: 20, left: 20} 1.13170 + , width = null 1.13171 + , height = null 1.13172 + , color = nv.utils.defaultColor() 1.13173 + , id = Math.round(Math.random() * 100000) 1.13174 + , defaultState = null 1.13175 + , noData = null 1.13176 + , duration = 250 1.13177 + , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState','renderEnd') 1.13178 + ; 1.13179 + 1.13180 + //============================================================ 1.13181 + // Private Variables 1.13182 + //------------------------------------------------------------ 1.13183 + 1.13184 + var renderWatch = nv.utils.renderWatch(dispatch); 1.13185 + tooltip.headerEnabled(false).duration(0).valueFormatter(function(d, i) { 1.13186 + return d; 1.13187 + }); 1.13188 + 1.13189 + //============================================================ 1.13190 + // Chart function 1.13191 + //------------------------------------------------------------ 1.13192 + 1.13193 + function chart(selection) { 1.13194 + renderWatch.reset(); 1.13195 + renderWatch.models(sunburst); 1.13196 + 1.13197 + selection.each(function(data) { 1.13198 + var container = d3.select(this); 1.13199 + nv.utils.initSVG(container); 1.13200 + 1.13201 + var that = this; 1.13202 + var availableWidth = nv.utils.availableWidth(width, container, margin), 1.13203 + availableHeight = nv.utils.availableHeight(height, container, margin); 1.13204 + 1.13205 + chart.update = function() { 1.13206 + if (duration === 0) 1.13207 + container.call(chart); 1.13208 + else 1.13209 + container.transition().duration(duration).call(chart) 1.13210 + }; 1.13211 + chart.container = this; 1.13212 + 1.13213 + // Display No Data message if there's nothing to show. 1.13214 + if (!data || !data.length) { 1.13215 + nv.utils.noData(chart, container); 1.13216 + return chart; 1.13217 + } else { 1.13218 + container.selectAll('.nv-noData').remove(); 1.13219 + } 1.13220 + 1.13221 + // Setup containers and skeleton of chart 1.13222 + var wrap = container.selectAll('g.nv-wrap.nv-sunburstChart').data(data); 1.13223 + var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sunburstChart').append('g'); 1.13224 + var g = wrap.select('g'); 1.13225 + 1.13226 + gEnter.append('g').attr('class', 'nv-sunburstWrap'); 1.13227 + 1.13228 + wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 1.13229 + 1.13230 + // Main Chart Component(s) 1.13231 + sunburst.width(availableWidth).height(availableHeight); 1.13232 + var sunWrap = g.select('.nv-sunburstWrap').datum(data); 1.13233 + d3.transition(sunWrap).call(sunburst); 1.13234 + 1.13235 + }); 1.13236 + 1.13237 + renderWatch.renderEnd('sunburstChart immediate'); 1.13238 + return chart; 1.13239 + } 1.13240 + 1.13241 + //============================================================ 1.13242 + // Event Handling/Dispatching (out of chart's scope) 1.13243 + //------------------------------------------------------------ 1.13244 + 1.13245 + sunburst.dispatch.on('elementMouseover.tooltip', function(evt) { 1.13246 + evt['series'] = { 1.13247 + key: evt.data.name, 1.13248 + value: evt.data.size, 1.13249 + color: evt.color 1.13250 + }; 1.13251 + tooltip.data(evt).hidden(false); 1.13252 + }); 1.13253 + 1.13254 + sunburst.dispatch.on('elementMouseout.tooltip', function(evt) { 1.13255 + tooltip.hidden(true); 1.13256 + }); 1.13257 + 1.13258 + sunburst.dispatch.on('elementMousemove.tooltip', function(evt) { 1.13259 + tooltip.position({top: d3.event.pageY, left: d3.event.pageX})(); 1.13260 + }); 1.13261 + 1.13262 + //============================================================ 1.13263 + // Expose Public Variables 1.13264 + //------------------------------------------------------------ 1.13265 + 1.13266 + // expose chart's sub-components 1.13267 + chart.dispatch = dispatch; 1.13268 + chart.sunburst = sunburst; 1.13269 + chart.tooltip = tooltip; 1.13270 + chart.options = nv.utils.optionsFunc.bind(chart); 1.13271 + 1.13272 + // use Object get/set functionality to map between vars and chart functions 1.13273 + chart._options = Object.create({}, { 1.13274 + // simple options, just get/set the necessary values 1.13275 + noData: {get: function(){return noData;}, set: function(_){noData=_;}}, 1.13276 + defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}}, 1.13277 + 1.13278 + // options that require extra logic in the setter 1.13279 + color: {get: function(){return color;}, set: function(_){ 1.13280 + color = _; 1.13281 + sunburst.color(color); 1.13282 + }}, 1.13283 + duration: {get: function(){return duration;}, set: function(_){ 1.13284 + duration = _; 1.13285 + renderWatch.reset(duration); 1.13286 + sunburst.duration(duration); 1.13287 + }}, 1.13288 + margin: {get: function(){return margin;}, set: function(_){ 1.13289 + margin.top = _.top !== undefined ? _.top : margin.top; 1.13290 + margin.right = _.right !== undefined ? _.right : margin.right; 1.13291 + margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; 1.13292 + margin.left = _.left !== undefined ? _.left : margin.left; 1.13293 + }} 1.13294 + }); 1.13295 + nv.utils.inheritOptions(chart, sunburst); 1.13296 + nv.utils.initOptions(chart); 1.13297 + return chart; 1.13298 +}; 1.13299 + 1.13300 +nv.version = "1.8.1"; 1.13301 +})(); 1.13302 \ No newline at end of file