~fginther/uci-team/simple-tarmac-failure-metrics

« back to all changes in this revision

Viewing changes to nv.d3.js

  • Committer: Evan Dandrea
  • Date: 2014-10-14 14:39:18 UTC
  • Revision ID: evan.dandrea@canonical.com-20141014143918-bivbklacuppim0ry
Initial commit of branch landing data.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
(function(){
 
2
 
 
3
var nv = window.nv || {};
 
4
 
 
5
 
 
6
nv.version = '1.1.15b';
 
7
nv.dev = true //set false when in production
 
8
 
 
9
window.nv = nv;
 
10
 
 
11
nv.tooltip = nv.tooltip || {}; // For the tooltip system
 
12
nv.utils = nv.utils || {}; // Utility subsystem
 
13
nv.models = nv.models || {}; //stores all the possible models/components
 
14
nv.charts = {}; //stores all the ready to use charts
 
15
nv.graphs = []; //stores all the graphs currently on the page
 
16
nv.logs = {}; //stores some statistics and potential error messages
 
17
 
 
18
nv.dispatch = d3.dispatch('render_start', 'render_end');
 
19
 
 
20
// *************************************************************************
 
21
//  Development render timers - disabled if dev = false
 
22
 
 
23
if (nv.dev) {
 
24
  nv.dispatch.on('render_start', function(e) {
 
25
    nv.logs.startTime = +new Date();
 
26
  });
 
27
 
 
28
  nv.dispatch.on('render_end', function(e) {
 
29
    nv.logs.endTime = +new Date();
 
30
    nv.logs.totalTime = nv.logs.endTime - nv.logs.startTime;
 
31
    nv.log('total', nv.logs.totalTime); // used for development, to keep track of graph generation times
 
32
  });
 
33
}
 
34
 
 
35
// ********************************************
 
36
//  Public Core NV functions
 
37
 
 
38
// Logs all arguments, and returns the last so you can test things in place
 
39
// Note: in IE8 console.log is an object not a function, and if modernizr is used
 
40
// then calling Function.prototype.bind with with anything other than a function
 
41
// causes a TypeError to be thrown.
 
42
nv.log = function() {
 
43
  if (nv.dev && console.log && console.log.apply)
 
44
    console.log.apply(console, arguments)
 
45
  else if (nv.dev && typeof console.log == "function" && Function.prototype.bind) {
 
46
    var log = Function.prototype.bind.call(console.log, console);
 
47
    log.apply(console, arguments);
 
48
  }
 
49
  return arguments[arguments.length - 1];
 
50
};
 
51
 
 
52
 
 
53
nv.render = function render(step) {
 
54
  step = step || 1; // number of graphs to generate in each timeout loop
 
55
 
 
56
  nv.render.active = true;
 
57
  nv.dispatch.render_start();
 
58
 
 
59
  setTimeout(function() {
 
60
    var chart, graph;
 
61
 
 
62
    for (var i = 0; i < step && (graph = nv.render.queue[i]); i++) {
 
63
      chart = graph.generate();
 
64
      if (typeof graph.callback == typeof(Function)) graph.callback(chart);
 
65
      nv.graphs.push(chart);
 
66
    }
 
67
 
 
68
    nv.render.queue.splice(0, i);
 
69
 
 
70
    if (nv.render.queue.length) setTimeout(arguments.callee, 0);
 
71
    else {
 
72
      nv.dispatch.render_end();
 
73
      nv.render.active = false;
 
74
    }
 
75
  }, 0);
 
76
};
 
77
 
 
78
nv.render.active = false;
 
79
nv.render.queue = [];
 
80
 
 
81
nv.addGraph = function(obj) {
 
82
  if (typeof arguments[0] === typeof(Function))
 
83
    obj = {generate: arguments[0], callback: arguments[1]};
 
84
 
 
85
  nv.render.queue.push(obj);
 
86
 
 
87
  if (!nv.render.active) nv.render();
 
88
};
 
89
 
 
90
nv.identity = function(d) { return d; };
 
91
 
 
92
nv.strip = function(s) { return s.replace(/(\s|&)/g,''); };
 
93
 
 
94
function daysInMonth(month,year) {
 
95
  return (new Date(year, month+1, 0)).getDate();
 
96
}
 
97
 
 
98
function d3_time_range(floor, step, number) {
 
99
  return function(t0, t1, dt) {
 
100
    var time = floor(t0), times = [];
 
101
    if (time < t0) step(time);
 
102
    if (dt > 1) {
 
103
      while (time < t1) {
 
104
        var date = new Date(+time);
 
105
        if ((number(date) % dt === 0)) times.push(date);
 
106
        step(time);
 
107
      }
 
108
    } else {
 
109
      while (time < t1) { times.push(new Date(+time)); step(time); }
 
110
    }
 
111
    return times;
 
112
  };
 
113
}
 
114
 
 
115
d3.time.monthEnd = function(date) {
 
116
  return new Date(date.getFullYear(), date.getMonth(), 0);
 
117
};
 
118
 
 
119
d3.time.monthEnds = d3_time_range(d3.time.monthEnd, function(date) {
 
120
    date.setUTCDate(date.getUTCDate() + 1);
 
121
    date.setDate(daysInMonth(date.getMonth() + 1, date.getFullYear()));
 
122
  }, function(date) {
 
123
    return date.getMonth();
 
124
  }
 
125
);
 
126
 
 
127
/* Utility class to handle creation of an interactive layer.
 
128
This places a rectangle on top of the chart. When you mouse move over it, it sends a dispatch
 
129
containing the X-coordinate. It can also render a vertical line where the mouse is located.
 
130
 
 
131
dispatch.elementMousemove is the important event to latch onto.  It is fired whenever the mouse moves over
 
132
the rectangle. The dispatch is given one object which contains the mouseX/Y location.
 
133
It also has 'pointXValue', which is the conversion of mouseX to the x-axis scale.
 
134
*/
 
135
nv.interactiveGuideline = function() {
 
136
        "use strict";
 
137
        var tooltip = nv.models.tooltip();
 
138
        //Public settings
 
139
        var width = null
 
140
        , height = null
 
141
    //Please pass in the bounding chart's top and left margins
 
142
    //This is important for calculating the correct mouseX/Y positions.
 
143
        , margin = {left: 0, top: 0}
 
144
        , xScale = d3.scale.linear()
 
145
        , yScale = d3.scale.linear()
 
146
        , dispatch = d3.dispatch('elementMousemove', 'elementMouseout','elementDblclick')
 
147
        , showGuideLine = true
 
148
        , svgContainer = null  
 
149
    //Must pass in the bounding chart's <svg> container.
 
150
    //The mousemove event is attached to this container.
 
151
        ;
 
152
 
 
153
        //Private variables
 
154
        var isMSIE = navigator.userAgent.indexOf("MSIE") !== -1  //Check user-agent for Microsoft Internet Explorer.
 
155
        ;
 
156
 
 
157
 
 
158
        function layer(selection) {
 
159
                selection.each(function(data) {
 
160
                                var container = d3.select(this);
 
161
                                
 
162
                                var availableWidth = (width || 960), availableHeight = (height || 400);
 
163
 
 
164
                                var wrap = container.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([data]);
 
165
                                var wrapEnter = wrap.enter()
 
166
                                                                .append("g").attr("class", " nv-wrap nv-interactiveLineLayer");
 
167
                                                                
 
168
                                
 
169
                                wrapEnter.append("g").attr("class","nv-interactiveGuideLine");
 
170
                                
 
171
                                if (!svgContainer) {
 
172
                                        return;
 
173
                                }
 
174
 
 
175
                function mouseHandler() {
 
176
                      var d3mouse = d3.mouse(this);
 
177
                      var mouseX = d3mouse[0];
 
178
                      var mouseY = d3mouse[1];
 
179
                      var subtractMargin = true;
 
180
                      var mouseOutAnyReason = false;
 
181
                      if (isMSIE) {
 
182
                         /*
 
183
                            D3.js (or maybe SVG.getScreenCTM) has a nasty bug in Internet Explorer 10.
 
184
                            d3.mouse() returns incorrect X,Y mouse coordinates when mouse moving
 
185
                            over a rect in IE 10.
 
186
                            However, d3.event.offsetX/Y also returns the mouse coordinates
 
187
                            relative to the triggering <rect>. So we use offsetX/Y on IE.  
 
188
                         */
 
189
                         mouseX = d3.event.offsetX;
 
190
                         mouseY = d3.event.offsetY;
 
191
 
 
192
                         /*
 
193
                            On IE, if you attach a mouse event listener to the <svg> container,
 
194
                            it will actually trigger it for all the child elements (like <path>, <circle>, etc).
 
195
                            When this happens on IE, the offsetX/Y is set to where ever the child element
 
196
                            is located.
 
197
                            As a result, we do NOT need to subtract margins to figure out the mouse X/Y
 
198
                            position under this scenario. Removing the line below *will* cause 
 
199
                            the interactive layer to not work right on IE.
 
200
                         */
 
201
                         if(d3.event.target.tagName !== "svg")
 
202
                            subtractMargin = false;
 
203
 
 
204
                         if (d3.event.target.className.baseVal.match("nv-legend"))
 
205
                                mouseOutAnyReason = true;
 
206
                          
 
207
                      }
 
208
 
 
209
                      if(subtractMargin) {
 
210
                         mouseX -= margin.left;
 
211
                         mouseY -= margin.top;
 
212
                      }
 
213
 
 
214
                      /* If mouseX/Y is outside of the chart's bounds,
 
215
                      trigger a mouseOut event.
 
216
                      */
 
217
                      if (mouseX < 0 || mouseY < 0 
 
218
                        || mouseX > availableWidth || mouseY > availableHeight
 
219
                        || (d3.event.relatedTarget && d3.event.relatedTarget.ownerSVGElement === undefined)
 
220
                        || mouseOutAnyReason
 
221
                        ) 
 
222
                      {
 
223
                                if (isMSIE) {
 
224
                                        if (d3.event.relatedTarget 
 
225
                                                && d3.event.relatedTarget.ownerSVGElement === undefined
 
226
                                                && d3.event.relatedTarget.className.match(tooltip.nvPointerEventsClass)) {
 
227
                                                return;
 
228
                                        }
 
229
                                }
 
230
                            dispatch.elementMouseout({
 
231
                               mouseX: mouseX,
 
232
                               mouseY: mouseY
 
233
                            });
 
234
                            layer.renderGuideLine(null); //hide the guideline
 
235
                            return;
 
236
                      }
 
237
                      
 
238
                      var pointXValue = xScale.invert(mouseX);
 
239
                      dispatch.elementMousemove({
 
240
                            mouseX: mouseX,
 
241
                            mouseY: mouseY,
 
242
                            pointXValue: pointXValue
 
243
                      });
 
244
 
 
245
                      //If user double clicks the layer, fire a elementDblclick dispatch.
 
246
                      if (d3.event.type === "dblclick") {
 
247
                        dispatch.elementDblclick({
 
248
                            mouseX: mouseX,
 
249
                            mouseY: mouseY,
 
250
                            pointXValue: pointXValue
 
251
                        });
 
252
                      }
 
253
                }
 
254
 
 
255
                                svgContainer
 
256
                                      .on("mousemove",mouseHandler, true)
 
257
                                      .on("mouseout" ,mouseHandler,true)
 
258
                      .on("dblclick" ,mouseHandler)
 
259
                                      ;
 
260
 
 
261
                                 //Draws a vertical guideline at the given X postion.
 
262
                                layer.renderGuideLine = function(x) {
 
263
                                        if (!showGuideLine) return;
 
264
                                        var line = wrap.select(".nv-interactiveGuideLine")
 
265
                                              .selectAll("line")
 
266
                                              .data((x != null) ? [nv.utils.NaNtoZero(x)] : [], String);
 
267
 
 
268
                                        line.enter()
 
269
                                                .append("line")
 
270
                                                .attr("class", "nv-guideline")
 
271
                                                .attr("x1", function(d) { return d;})
 
272
                                                .attr("x2", function(d) { return d;})
 
273
                                                .attr("y1", availableHeight)
 
274
                                                .attr("y2",0)
 
275
                                                ;
 
276
                                        line.exit().remove();
 
277
 
 
278
                                }
 
279
                });
 
280
        }
 
281
 
 
282
        layer.dispatch = dispatch;
 
283
        layer.tooltip = tooltip;
 
284
 
 
285
        layer.margin = function(_) {
 
286
            if (!arguments.length) return margin;
 
287
            margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
288
            margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
289
            return layer;
 
290
    };
 
291
 
 
292
        layer.width = function(_) {
 
293
                if (!arguments.length) return width;
 
294
                width = _;
 
295
                return layer;
 
296
        };
 
297
 
 
298
        layer.height = function(_) {
 
299
                if (!arguments.length) return height;
 
300
                height = _;
 
301
                return layer;
 
302
        };
 
303
 
 
304
        layer.xScale = function(_) {
 
305
                if (!arguments.length) return xScale;
 
306
                xScale = _;
 
307
                return layer;
 
308
        };
 
309
 
 
310
        layer.showGuideLine = function(_) {
 
311
                if (!arguments.length) return showGuideLine;
 
312
                showGuideLine = _;
 
313
                return layer;
 
314
        };
 
315
 
 
316
        layer.svgContainer = function(_) {
 
317
                if (!arguments.length) return svgContainer;
 
318
                svgContainer = _;
 
319
                return layer;
 
320
        };
 
321
 
 
322
 
 
323
        return layer;
 
324
};
 
325
 
 
326
/* Utility class that uses d3.bisect to find the index in a given array, where a search value can be inserted.
 
327
This is different from normal bisectLeft; this function finds the nearest index to insert the search value.
 
328
 
 
329
For instance, lets say your array is [1,2,3,5,10,30], and you search for 28. 
 
330
Normal d3.bisectLeft will return 4, because 28 is inserted after the number 10.  But interactiveBisect will return 5
 
331
because 28 is closer to 30 than 10.
 
332
 
 
333
Unit tests can be found in: interactiveBisectTest.html
 
334
 
 
335
Has the following known issues:
 
336
   * Will not work if the data points move backwards (ie, 10,9,8,7, etc) or if the data points are in random order.
 
337
   * Won't work if there are duplicate x coordinate values.
 
338
*/
 
339
nv.interactiveBisect = function (values, searchVal, xAccessor) {
 
340
          "use strict";
 
341
      if (! values instanceof Array) return null;
 
342
      if (typeof xAccessor !== 'function') xAccessor = function(d,i) { return d.x;}
 
343
 
 
344
      var bisect = d3.bisector(xAccessor).left;
 
345
      var index = d3.max([0, bisect(values,searchVal) - 1]);
 
346
      var currentValue = xAccessor(values[index], index);
 
347
      if (typeof currentValue === 'undefined') currentValue = index;
 
348
 
 
349
      if (currentValue === searchVal) return index;  //found exact match
 
350
 
 
351
      var nextIndex = d3.min([index+1, values.length - 1]);
 
352
      var nextValue = xAccessor(values[nextIndex], nextIndex);
 
353
      if (typeof nextValue === 'undefined') nextValue = nextIndex;
 
354
 
 
355
      if (Math.abs(nextValue - searchVal) >= Math.abs(currentValue - searchVal))
 
356
          return index;
 
357
      else
 
358
          return nextIndex
 
359
};
 
360
 
 
361
/*
 
362
Returns the index in the array "values" that is closest to searchVal.
 
363
Only returns an index if searchVal is within some "threshold".
 
364
Otherwise, returns null.
 
365
*/
 
366
nv.nearestValueIndex = function (values, searchVal, threshold) {
 
367
      "use strict";
 
368
      var yDistMax = Infinity, indexToHighlight = null;
 
369
      values.forEach(function(d,i) {
 
370
         var delta = Math.abs(searchVal - d);
 
371
         if ( delta <= yDistMax && delta < threshold) {
 
372
            yDistMax = delta;
 
373
            indexToHighlight = i;
 
374
         }
 
375
      });
 
376
      return indexToHighlight;
 
377
};/* Tooltip rendering model for nvd3 charts.
 
378
window.nv.models.tooltip is the updated,new way to render tooltips.
 
379
 
 
380
window.nv.tooltip.show is the old tooltip code.
 
381
window.nv.tooltip.* also has various helper methods.
 
382
*/
 
383
(function() {
 
384
  "use strict";
 
385
  window.nv.tooltip = {};
 
386
 
 
387
  /* Model which can be instantiated to handle tooltip rendering.
 
388
    Example usage: 
 
389
    var tip = nv.models.tooltip().gravity('w').distance(23)
 
390
                .data(myDataObject);
 
391
 
 
392
        tip();    //just invoke the returned function to render tooltip.
 
393
  */
 
394
  window.nv.models.tooltip = function() {
 
395
        var content = null    //HTML contents of the tooltip.  If null, the content is generated via the data variable.
 
396
        ,   data = null     /* Tooltip data. If data is given in the proper format, a consistent tooltip is generated.
 
397
        Format of data:
 
398
        {
 
399
            key: "Date",
 
400
            value: "August 2009", 
 
401
            series: [
 
402
                    {
 
403
                        key: "Series 1",
 
404
                        value: "Value 1",
 
405
                        color: "#000"
 
406
                    },
 
407
                    {
 
408
                        key: "Series 2",
 
409
                        value: "Value 2",
 
410
                        color: "#00f"
 
411
                    }
 
412
            ]
 
413
 
 
414
        }
 
415
 
 
416
        */
 
417
        ,   gravity = 'w'   //Can be 'n','s','e','w'. Determines how tooltip is positioned.
 
418
        ,   distance = 50   //Distance to offset tooltip from the mouse location.
 
419
        ,   snapDistance = 25   //Tolerance allowed before tooltip is moved from its current position (creates 'snapping' effect)
 
420
        ,   fixedTop = null //If not null, this fixes the top position of the tooltip.
 
421
        ,   classes = null  //Attaches additional CSS classes to the tooltip DIV that is created.
 
422
        ,   chartContainer = null   //Parent DIV, of the SVG Container that holds the chart.
 
423
        ,   tooltipElem = null  //actual DOM element representing the tooltip.
 
424
        ,   position = {left: null, top: null}      //Relative position of the tooltip inside chartContainer.
 
425
        ,   enabled = true  //True -> tooltips are rendered. False -> don't render tooltips.
 
426
        //Generates a unique id when you create a new tooltip() object
 
427
        ,   id = "nvtooltip-" + Math.floor(Math.random() * 100000)
 
428
        ;
 
429
 
 
430
        //CSS class to specify whether element should not have mouse events.
 
431
        var  nvPointerEventsClass = "nv-pointer-events-none";
 
432
 
 
433
        //Format function for the tooltip values column
 
434
        var valueFormatter = function(d,i) {
 
435
            return d;
 
436
        };
 
437
 
 
438
        //Format function for the tooltip header value.
 
439
        var headerFormatter = function(d) {
 
440
            return d;
 
441
        };
 
442
 
 
443
        //By default, the tooltip model renders a beautiful table inside a DIV.
 
444
        //You can override this function if a custom tooltip is desired.
 
445
        var contentGenerator = function(d) {
 
446
            if (content != null) return content;
 
447
 
 
448
            if (d == null) return '';
 
449
 
 
450
            var table = d3.select(document.createElement("table"));
 
451
            var theadEnter = table.selectAll("thead")
 
452
                .data([d])
 
453
                .enter().append("thead");
 
454
            theadEnter.append("tr")
 
455
                .append("td")
 
456
                .attr("colspan",3)
 
457
                .append("strong")
 
458
                    .classed("x-value",true)
 
459
                    .html(headerFormatter(d.value));
 
460
 
 
461
            var tbodyEnter = table.selectAll("tbody")
 
462
                .data([d])
 
463
                .enter().append("tbody");
 
464
            var trowEnter = tbodyEnter.selectAll("tr")
 
465
                .data(function(p) { return p.series})
 
466
                .enter()
 
467
                .append("tr")
 
468
                .classed("highlight", function(p) { return p.highlight})
 
469
                ;
 
470
 
 
471
            trowEnter.append("td")
 
472
                .classed("legend-color-guide",true)
 
473
                .append("div")
 
474
                    .style("background-color", function(p) { return p.color});
 
475
            trowEnter.append("td")
 
476
                .classed("key",true)
 
477
                .html(function(p) {return p.key});
 
478
            trowEnter.append("td")
 
479
                .classed("value",true)
 
480
                .html(function(p,i) { return valueFormatter(p.value,i) });
 
481
 
 
482
 
 
483
            trowEnter.selectAll("td").each(function(p) {
 
484
                if (p.highlight) {
 
485
                    var opacityScale = d3.scale.linear().domain([0,1]).range(["#fff",p.color]);
 
486
                    var opacity = 0.6;
 
487
                    d3.select(this)
 
488
                        .style("border-bottom-color", opacityScale(opacity))
 
489
                        .style("border-top-color", opacityScale(opacity))
 
490
                        ;
 
491
                }
 
492
            });
 
493
 
 
494
            var html = table.node().outerHTML;
 
495
            if (d.footer !== undefined)
 
496
                html += "<div class='footer'>" + d.footer + "</div>";
 
497
            return html;
 
498
 
 
499
        };
 
500
 
 
501
        var dataSeriesExists = function(d) {
 
502
            if (d && d.series && d.series.length > 0) return true;
 
503
 
 
504
            return false;
 
505
        };
 
506
 
 
507
        //In situations where the chart is in a 'viewBox', re-position the tooltip based on how far chart is zoomed.
 
508
        function convertViewBoxRatio() {
 
509
            if (chartContainer) {
 
510
              var svg = d3.select(chartContainer);
 
511
              if (svg.node().tagName !== "svg") {
 
512
                 svg = svg.select("svg");
 
513
              }
 
514
              var viewBox = (svg.node()) ? svg.attr('viewBox') : null;
 
515
              if (viewBox) {
 
516
                viewBox = viewBox.split(' ');
 
517
                var ratio = parseInt(svg.style('width')) / viewBox[2];
 
518
                
 
519
                position.left = position.left * ratio;
 
520
                position.top  = position.top * ratio;
 
521
              }
 
522
            }
 
523
        }
 
524
 
 
525
        //Creates new tooltip container, or uses existing one on DOM.
 
526
        function getTooltipContainer(newContent) {
 
527
            var body;
 
528
            if (chartContainer)
 
529
                body = d3.select(chartContainer);
 
530
            else
 
531
                body = d3.select("body");
 
532
 
 
533
            var container = body.select(".nvtooltip");
 
534
            if (container.node() === null) {
 
535
                //Create new tooltip div if it doesn't exist on DOM.
 
536
                container = body.append("div")
 
537
                    .attr("class", "nvtooltip " + (classes? classes: "xy-tooltip"))
 
538
                    .attr("id",id)
 
539
                    ;
 
540
            }
 
541
        
 
542
 
 
543
            container.node().innerHTML = newContent;
 
544
            container.style("top",0).style("left",0).style("opacity",0);
 
545
            container.selectAll("div, table, td, tr").classed(nvPointerEventsClass,true)
 
546
            container.classed(nvPointerEventsClass,true);
 
547
            return container.node();
 
548
        }
 
549
 
 
550
        
 
551
 
 
552
        //Draw the tooltip onto the DOM.
 
553
        function nvtooltip() {
 
554
            if (!enabled) return;
 
555
            if (!dataSeriesExists(data)) return;
 
556
 
 
557
            convertViewBoxRatio();
 
558
 
 
559
            var left = position.left;
 
560
            var top = (fixedTop != null) ? fixedTop : position.top;
 
561
            var container = getTooltipContainer(contentGenerator(data));
 
562
            tooltipElem = container;
 
563
            if (chartContainer) {
 
564
                var svgComp = chartContainer.getElementsByTagName("svg")[0];
 
565
                var boundRect = (svgComp) ? svgComp.getBoundingClientRect() : chartContainer.getBoundingClientRect();
 
566
                var svgOffset = {left:0,top:0};
 
567
                if (svgComp) {
 
568
                    var svgBound = svgComp.getBoundingClientRect();
 
569
                    var chartBound = chartContainer.getBoundingClientRect();
 
570
                    var svgBoundTop = svgBound.top;
 
571
                    
 
572
                    //Defensive code. Sometimes, svgBoundTop can be a really negative
 
573
                    //  number, like -134254. That's a bug. 
 
574
                    //  If such a number is found, use zero instead. FireFox bug only
 
575
                    if (svgBoundTop < 0) {
 
576
                        var containerBound = chartContainer.getBoundingClientRect();
 
577
                        svgBoundTop = (Math.abs(svgBoundTop) > containerBound.height) ? 0 : svgBoundTop;
 
578
                    } 
 
579
                    svgOffset.top = Math.abs(svgBoundTop - chartBound.top);
 
580
                    svgOffset.left = Math.abs(svgBound.left - chartBound.left);
 
581
                }
 
582
                //If the parent container is an overflow <div> with scrollbars, subtract the scroll offsets.
 
583
                //You need to also add any offset between the <svg> element and its containing <div>
 
584
                //Finally, add any offset of the containing <div> on the whole page.
 
585
                left += chartContainer.offsetLeft + svgOffset.left - 2*chartContainer.scrollLeft;
 
586
                top += chartContainer.offsetTop + svgOffset.top - 2*chartContainer.scrollTop;
 
587
            }
 
588
 
 
589
            if (snapDistance && snapDistance > 0) {
 
590
                top = Math.floor(top/snapDistance) * snapDistance;
 
591
            }
 
592
 
 
593
            nv.tooltip.calcTooltipPosition([left,top], gravity, distance, container);
 
594
            return nvtooltip;
 
595
        };
 
596
 
 
597
        nvtooltip.nvPointerEventsClass = nvPointerEventsClass;
 
598
        
 
599
        nvtooltip.content = function(_) {
 
600
            if (!arguments.length) return content;
 
601
            content = _;
 
602
            return nvtooltip;
 
603
        };
 
604
 
 
605
        //Returns tooltipElem...not able to set it.
 
606
        nvtooltip.tooltipElem = function() {
 
607
            return tooltipElem;
 
608
        };
 
609
 
 
610
        nvtooltip.contentGenerator = function(_) {
 
611
            if (!arguments.length) return contentGenerator;
 
612
            if (typeof _ === 'function') {
 
613
                contentGenerator = _;
 
614
            }
 
615
            return nvtooltip;
 
616
        };
 
617
 
 
618
        nvtooltip.data = function(_) {
 
619
            if (!arguments.length) return data;
 
620
            data = _;
 
621
            return nvtooltip;
 
622
        };
 
623
 
 
624
        nvtooltip.gravity = function(_) {
 
625
            if (!arguments.length) return gravity;
 
626
            gravity = _;
 
627
            return nvtooltip;
 
628
        };
 
629
 
 
630
        nvtooltip.distance = function(_) {
 
631
            if (!arguments.length) return distance;
 
632
            distance = _;
 
633
            return nvtooltip;
 
634
        };
 
635
 
 
636
        nvtooltip.snapDistance = function(_) {
 
637
            if (!arguments.length) return snapDistance;
 
638
            snapDistance = _;
 
639
            return nvtooltip;
 
640
        };
 
641
 
 
642
        nvtooltip.classes = function(_) {
 
643
            if (!arguments.length) return classes;
 
644
            classes = _;
 
645
            return nvtooltip;
 
646
        };
 
647
 
 
648
        nvtooltip.chartContainer = function(_) {
 
649
            if (!arguments.length) return chartContainer;
 
650
            chartContainer = _;
 
651
            return nvtooltip;
 
652
        };
 
653
 
 
654
        nvtooltip.position = function(_) {
 
655
            if (!arguments.length) return position;
 
656
            position.left = (typeof _.left !== 'undefined') ? _.left : position.left;
 
657
            position.top = (typeof _.top !== 'undefined') ? _.top : position.top;
 
658
            return nvtooltip;
 
659
        };
 
660
 
 
661
        nvtooltip.fixedTop = function(_) {
 
662
            if (!arguments.length) return fixedTop;
 
663
            fixedTop = _;
 
664
            return nvtooltip;
 
665
        };
 
666
 
 
667
        nvtooltip.enabled = function(_) {
 
668
            if (!arguments.length) return enabled;
 
669
            enabled = _;
 
670
            return nvtooltip;
 
671
        };
 
672
 
 
673
        nvtooltip.valueFormatter = function(_) {
 
674
            if (!arguments.length) return valueFormatter;
 
675
            if (typeof _ === 'function') {
 
676
                valueFormatter = _;
 
677
            }
 
678
            return nvtooltip;
 
679
        };
 
680
 
 
681
        nvtooltip.headerFormatter = function(_) {
 
682
            if (!arguments.length) return headerFormatter;
 
683
            if (typeof _ === 'function') {
 
684
                headerFormatter = _;
 
685
            }
 
686
            return nvtooltip;
 
687
        };
 
688
 
 
689
        //id() is a read-only function. You can't use it to set the id.
 
690
        nvtooltip.id = function() {
 
691
            return id;
 
692
        };
 
693
 
 
694
 
 
695
        return nvtooltip;
 
696
  };
 
697
 
 
698
 
 
699
  //Original tooltip.show function. Kept for backward compatibility.
 
700
  // pos = [left,top]
 
701
  nv.tooltip.show = function(pos, content, gravity, dist, parentContainer, classes) {
 
702
      
 
703
        //Create new tooltip div if it doesn't exist on DOM.
 
704
        var   container = document.createElement('div');
 
705
        container.className = 'nvtooltip ' + (classes ? classes : 'xy-tooltip');
 
706
 
 
707
        var body = parentContainer;
 
708
        if ( !parentContainer || parentContainer.tagName.match(/g|svg/i)) {
 
709
            //If the parent element is an SVG element, place tooltip in the <body> element.
 
710
            body = document.getElementsByTagName('body')[0];
 
711
        }
 
712
   
 
713
        container.style.left = 0;
 
714
        container.style.top = 0;
 
715
        container.style.opacity = 0;
 
716
        container.innerHTML = content;
 
717
        body.appendChild(container);
 
718
        
 
719
        //If the parent container is an overflow <div> with scrollbars, subtract the scroll offsets.
 
720
        if (parentContainer) {
 
721
           pos[0] = pos[0] - parentContainer.scrollLeft;
 
722
           pos[1] = pos[1] - parentContainer.scrollTop;
 
723
        }
 
724
        nv.tooltip.calcTooltipPosition(pos, gravity, dist, container);
 
725
  };
 
726
 
 
727
  //Looks up the ancestry of a DOM element, and returns the first NON-svg node.
 
728
  nv.tooltip.findFirstNonSVGParent = function(Elem) {
 
729
            while(Elem.tagName.match(/^g|svg$/i) !== null) {
 
730
                Elem = Elem.parentNode;
 
731
            }
 
732
            return Elem;
 
733
  };
 
734
 
 
735
  //Finds the total offsetTop of a given DOM element.
 
736
  //Looks up the entire ancestry of an element, up to the first relatively positioned element.
 
737
  nv.tooltip.findTotalOffsetTop = function ( Elem, initialTop ) {
 
738
                var offsetTop = initialTop;
 
739
                
 
740
                do {
 
741
                    if( !isNaN( Elem.offsetTop ) ) {
 
742
                        offsetTop += (Elem.offsetTop);
 
743
                    }
 
744
                } while( Elem = Elem.offsetParent );
 
745
                return offsetTop;
 
746
  };
 
747
 
 
748
  //Finds the total offsetLeft of a given DOM element.
 
749
  //Looks up the entire ancestry of an element, up to the first relatively positioned element.
 
750
  nv.tooltip.findTotalOffsetLeft = function ( Elem, initialLeft) {
 
751
                var offsetLeft = initialLeft;
 
752
                
 
753
                do {
 
754
                    if( !isNaN( Elem.offsetLeft ) ) {
 
755
                        offsetLeft += (Elem.offsetLeft);
 
756
                    }
 
757
                } while( Elem = Elem.offsetParent );
 
758
                return offsetLeft;
 
759
  };
 
760
 
 
761
  //Global utility function to render a tooltip on the DOM.
 
762
  //pos = [left,top] coordinates of where to place the tooltip, relative to the SVG chart container.
 
763
  //gravity = how to orient the tooltip
 
764
  //dist = how far away from the mouse to place tooltip
 
765
  //container = tooltip DIV
 
766
  nv.tooltip.calcTooltipPosition = function(pos, gravity, dist, container) {
 
767
 
 
768
            var height = parseInt(container.offsetHeight),
 
769
                width = parseInt(container.offsetWidth),
 
770
                windowWidth = nv.utils.windowSize().width,
 
771
                windowHeight = nv.utils.windowSize().height,
 
772
                scrollTop = window.pageYOffset,
 
773
                scrollLeft = window.pageXOffset,
 
774
                left, top;
 
775
 
 
776
            windowHeight = window.innerWidth >= document.body.scrollWidth ? windowHeight : windowHeight - 16;
 
777
            windowWidth = window.innerHeight >= document.body.scrollHeight ? windowWidth : windowWidth - 16;
 
778
 
 
779
            gravity = gravity || 's';
 
780
            dist = dist || 20;
 
781
 
 
782
            var tooltipTop = function ( Elem ) {
 
783
                return nv.tooltip.findTotalOffsetTop(Elem, top);
 
784
            };
 
785
 
 
786
            var tooltipLeft = function ( Elem ) {
 
787
                return nv.tooltip.findTotalOffsetLeft(Elem,left);
 
788
            };
 
789
 
 
790
            switch (gravity) {
 
791
              case 'e':
 
792
                left = pos[0] - width - dist;
 
793
                top = pos[1] - (height / 2);
 
794
                var tLeft = tooltipLeft(container);
 
795
                var tTop = tooltipTop(container);
 
796
                if (tLeft < scrollLeft) left = pos[0] + dist > scrollLeft ? pos[0] + dist : scrollLeft - tLeft + left;
 
797
                if (tTop < scrollTop) top = scrollTop - tTop + top;
 
798
                if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
 
799
                break;
 
800
              case 'w':
 
801
                left = pos[0] + dist;
 
802
                top = pos[1] - (height / 2);
 
803
                var tLeft = tooltipLeft(container);
 
804
                var tTop = tooltipTop(container);
 
805
                if (tLeft + width > windowWidth) left = pos[0] - width - dist;
 
806
                if (tTop < scrollTop) top = scrollTop + 5;
 
807
                if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
 
808
                break;
 
809
              case 'n':
 
810
                left = pos[0] - (width / 2) - 5;
 
811
                top = pos[1] + dist;
 
812
                var tLeft = tooltipLeft(container);
 
813
                var tTop = tooltipTop(container);
 
814
                if (tLeft < scrollLeft) left = scrollLeft + 5;
 
815
                if (tLeft + width > windowWidth) left = left - width/2 + 5;
 
816
                if (tTop + height > scrollTop + windowHeight) top = scrollTop + windowHeight - tTop + top - height;
 
817
                break;
 
818
              case 's':
 
819
                left = pos[0] - (width / 2);
 
820
                top = pos[1] - height - dist;
 
821
                var tLeft = tooltipLeft(container);
 
822
                var tTop = tooltipTop(container);
 
823
                if (tLeft < scrollLeft) left = scrollLeft + 5;
 
824
                if (tLeft + width > windowWidth) left = left - width/2 + 5;
 
825
                if (scrollTop > tTop) top = scrollTop;
 
826
                break;
 
827
              case 'none':
 
828
                left = pos[0];
 
829
                top = pos[1] - dist;
 
830
                var tLeft = tooltipLeft(container);
 
831
                var tTop = tooltipTop(container);
 
832
                break;
 
833
            }
 
834
 
 
835
 
 
836
            container.style.left = left+'px';
 
837
            container.style.top = top+'px';
 
838
            container.style.opacity = 1;
 
839
            container.style.position = 'absolute'; 
 
840
 
 
841
            return container;
 
842
    };
 
843
 
 
844
    //Global utility function to remove tooltips from the DOM.
 
845
    nv.tooltip.cleanup = function() {
 
846
 
 
847
              // Find the tooltips, mark them for removal by this class (so others cleanups won't find it)
 
848
              var tooltips = document.getElementsByClassName('nvtooltip');
 
849
              var purging = [];
 
850
              while(tooltips.length) {
 
851
                purging.push(tooltips[0]);
 
852
                tooltips[0].style.transitionDelay = '0 !important';
 
853
                tooltips[0].style.opacity = 0;
 
854
                tooltips[0].className = 'nvtooltip-pending-removal';
 
855
              }
 
856
 
 
857
              setTimeout(function() {
 
858
 
 
859
                  while (purging.length) {
 
860
                     var removeMe = purging.pop();
 
861
                      removeMe.parentNode.removeChild(removeMe);
 
862
                  }
 
863
            }, 500);
 
864
    };
 
865
 
 
866
})();
 
867
 
 
868
nv.utils.windowSize = function() {
 
869
    // Sane defaults
 
870
    var size = {width: 640, height: 480};
 
871
 
 
872
    // Earlier IE uses Doc.body
 
873
    if (document.body && document.body.offsetWidth) {
 
874
        size.width = document.body.offsetWidth;
 
875
        size.height = document.body.offsetHeight;
 
876
    }
 
877
 
 
878
    // IE can use depending on mode it is in
 
879
    if (document.compatMode=='CSS1Compat' &&
 
880
        document.documentElement &&
 
881
        document.documentElement.offsetWidth ) {
 
882
        size.width = document.documentElement.offsetWidth;
 
883
        size.height = document.documentElement.offsetHeight;
 
884
    }
 
885
 
 
886
    // Most recent browsers use
 
887
    if (window.innerWidth && window.innerHeight) {
 
888
        size.width = window.innerWidth;
 
889
        size.height = window.innerHeight;
 
890
    }
 
891
    return (size);
 
892
};
 
893
 
 
894
 
 
895
 
 
896
// Easy way to bind multiple functions to window.onresize
 
897
// TODO: give a way to remove a function after its bound, other than removing all of them
 
898
nv.utils.windowResize = function(fun){
 
899
  if (fun === undefined) return;
 
900
  var oldresize = window.onresize;
 
901
 
 
902
  window.onresize = function(e) {
 
903
    if (typeof oldresize == 'function') oldresize(e);
 
904
    fun(e);
 
905
  }
 
906
}
 
907
 
 
908
// Backwards compatible way to implement more d3-like coloring of graphs.
 
909
// If passed an array, wrap it in a function which implements the old default
 
910
// behavior
 
911
nv.utils.getColor = function(color) {
 
912
    if (!arguments.length) return nv.utils.defaultColor(); //if you pass in nothing, get default colors back
 
913
 
 
914
    if( Object.prototype.toString.call( color ) === '[object Array]' )
 
915
        return function(d, i) { return d.color || color[i % color.length]; };
 
916
    else
 
917
        return color;
 
918
        //can't really help it if someone passes rubbish as color
 
919
}
 
920
 
 
921
// Default color chooser uses the index of an object as before.
 
922
nv.utils.defaultColor = function() {
 
923
    var colors = d3.scale.category20().range();
 
924
    return function(d, i) { return d.color || colors[i % colors.length] };
 
925
}
 
926
 
 
927
 
 
928
// Returns a color function that takes the result of 'getKey' for each series and
 
929
// looks for a corresponding color from the dictionary,
 
930
nv.utils.customTheme = function(dictionary, getKey, defaultColors) {
 
931
  getKey = getKey || function(series) { return series.key }; // use default series.key if getKey is undefined
 
932
  defaultColors = defaultColors || d3.scale.category20().range(); //default color function
 
933
 
 
934
  var defIndex = defaultColors.length; //current default color (going in reverse)
 
935
 
 
936
  return function(series, index) {
 
937
    var key = getKey(series);
 
938
 
 
939
    if (!defIndex) defIndex = defaultColors.length; //used all the default colors, start over
 
940
 
 
941
    if (typeof dictionary[key] !== "undefined")
 
942
      return (typeof dictionary[key] === "function") ? dictionary[key]() : dictionary[key];
 
943
    else
 
944
      return defaultColors[--defIndex]; // no match in dictionary, use default color
 
945
  }
 
946
}
 
947
 
 
948
 
 
949
 
 
950
// From the PJAX example on d3js.org, while this is not really directly needed
 
951
// it's a very cool method for doing pjax, I may expand upon it a little bit,
 
952
// open to suggestions on anything that may be useful
 
953
nv.utils.pjax = function(links, content) {
 
954
  d3.selectAll(links).on("click", function() {
 
955
    history.pushState(this.href, this.textContent, this.href);
 
956
    load(this.href);
 
957
    d3.event.preventDefault();
 
958
  });
 
959
 
 
960
  function load(href) {
 
961
    d3.html(href, function(fragment) {
 
962
      var target = d3.select(content).node();
 
963
      target.parentNode.replaceChild(d3.select(fragment).select(content).node(), target);
 
964
      nv.utils.pjax(links, content);
 
965
    });
 
966
  }
 
967
 
 
968
  d3.select(window).on("popstate", function() {
 
969
    if (d3.event.state) load(d3.event.state);
 
970
  });
 
971
}
 
972
 
 
973
/* For situations where we want to approximate the width in pixels for an SVG:text element.
 
974
Most common instance is when the element is in a display:none; container.
 
975
Forumla is : text.length * font-size * constant_factor
 
976
*/
 
977
nv.utils.calcApproxTextWidth = function (svgTextElem) {
 
978
    if (typeof svgTextElem.style === 'function'
 
979
        && typeof svgTextElem.text === 'function') {
 
980
        var fontSize = parseInt(svgTextElem.style("font-size").replace("px",""));
 
981
        var textLength = svgTextElem.text().length;
 
982
 
 
983
        return textLength * fontSize * 0.5;
 
984
    }
 
985
    return 0;
 
986
};
 
987
 
 
988
/* Numbers that are undefined, null or NaN, convert them to zeros.
 
989
*/
 
990
nv.utils.NaNtoZero = function(n) {
 
991
    if (typeof n !== 'number'
 
992
        || isNaN(n)
 
993
        || n === null
 
994
        || n === Infinity) return 0;
 
995
 
 
996
    return n;
 
997
};
 
998
 
 
999
/*
 
1000
Snippet of code you can insert into each nv.models.* to give you the ability to
 
1001
do things like:
 
1002
chart.options({
 
1003
  showXAxis: true,
 
1004
  tooltips: true
 
1005
});
 
1006
 
 
1007
To enable in the chart:
 
1008
chart.options = nv.utils.optionsFunc.bind(chart);
 
1009
*/
 
1010
nv.utils.optionsFunc = function(args) {
 
1011
    if (args) {
 
1012
      d3.map(args).forEach((function(key,value) {
 
1013
        if (typeof this[key] === "function") {
 
1014
           this[key](value);
 
1015
        }
 
1016
      }).bind(this));
 
1017
    }
 
1018
    return this;
 
1019
};nv.models.axis = function() {
 
1020
  "use strict";
 
1021
  //============================================================
 
1022
  // Public Variables with Default Settings
 
1023
  //------------------------------------------------------------
 
1024
 
 
1025
  var axis = d3.svg.axis()
 
1026
    ;
 
1027
 
 
1028
  var margin = {top: 0, right: 0, bottom: 0, left: 0}
 
1029
    , width = 75 //only used for tickLabel currently
 
1030
    , height = 60 //only used for tickLabel currently
 
1031
    , scale = d3.scale.linear()
 
1032
    , axisLabelText = null
 
1033
    , showMaxMin = true //TODO: showMaxMin should be disabled on all ordinal scaled axes
 
1034
    , highlightZero = true
 
1035
    , rotateLabels = 0
 
1036
    , rotateYLabel = true
 
1037
    , staggerLabels = false
 
1038
    , isOrdinal = false
 
1039
    , ticks = null
 
1040
    , axisLabelDistance = 12 //The larger this number is, the closer the axis label is to the axis.
 
1041
    ;
 
1042
 
 
1043
  axis
 
1044
    .scale(scale)
 
1045
    .orient('bottom')
 
1046
    .tickFormat(function(d) { return d })
 
1047
    ;
 
1048
 
 
1049
  //============================================================
 
1050
 
 
1051
 
 
1052
  //============================================================
 
1053
  // Private Variables
 
1054
  //------------------------------------------------------------
 
1055
 
 
1056
  var scale0;
 
1057
 
 
1058
  //============================================================
 
1059
 
 
1060
 
 
1061
  function chart(selection) {
 
1062
    selection.each(function(data) {
 
1063
      var container = d3.select(this);
 
1064
 
 
1065
 
 
1066
      //------------------------------------------------------------
 
1067
      // Setup containers and skeleton of chart
 
1068
 
 
1069
      var wrap = container.selectAll('g.nv-wrap.nv-axis').data([data]);
 
1070
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-axis');
 
1071
      var gEnter = wrapEnter.append('g');
 
1072
      var g = wrap.select('g')
 
1073
 
 
1074
      //------------------------------------------------------------
 
1075
 
 
1076
 
 
1077
      if (ticks !== null)
 
1078
        axis.ticks(ticks);
 
1079
      else if (axis.orient() == 'top' || axis.orient() == 'bottom')
 
1080
        axis.ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 100);
 
1081
 
 
1082
 
 
1083
      //TODO: consider calculating width/height based on whether or not label is added, for reference in charts using this component
 
1084
 
 
1085
 
 
1086
      g.transition().call(axis);
 
1087
 
 
1088
      scale0 = scale0 || axis.scale();
 
1089
 
 
1090
      var fmt = axis.tickFormat();
 
1091
      if (fmt == null) {
 
1092
        fmt = scale0.tickFormat();
 
1093
      }
 
1094
 
 
1095
      var axisLabel = g.selectAll('text.nv-axislabel')
 
1096
          .data([axisLabelText || null]);
 
1097
      axisLabel.exit().remove();
 
1098
      switch (axis.orient()) {
 
1099
        case 'top':
 
1100
          axisLabel.enter().append('text').attr('class', 'nv-axislabel');
 
1101
          var w = (scale.range().length==2) ? scale.range()[1] : (scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]));
 
1102
          axisLabel
 
1103
              .attr('text-anchor', 'middle')
 
1104
              .attr('y', 0)
 
1105
              .attr('x', w/2);
 
1106
          if (showMaxMin) {
 
1107
            var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
 
1108
                           .data(scale.domain());
 
1109
            axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text');
 
1110
            axisMaxMin.exit().remove();
 
1111
            axisMaxMin
 
1112
                .attr('transform', function(d,i) {
 
1113
                  return 'translate(' + scale(d) + ',0)'
 
1114
                })
 
1115
              .select('text')
 
1116
                .attr('dy', '-0.5em')
 
1117
                .attr('y', -axis.tickPadding())
 
1118
                .attr('text-anchor', 'middle')
 
1119
                .text(function(d,i) {
 
1120
                  var v = fmt(d);
 
1121
                  return ('' + v).match('NaN') ? '' : v;
 
1122
                });
 
1123
            axisMaxMin.transition()
 
1124
                .attr('transform', function(d,i) {
 
1125
                  return 'translate(' + scale.range()[i] + ',0)'
 
1126
                });
 
1127
          }
 
1128
          break;
 
1129
        case 'bottom':
 
1130
          var xLabelMargin = 36;
 
1131
          var maxTextWidth = 30;
 
1132
          var xTicks = g.selectAll('g').select("text");
 
1133
          if (rotateLabels%360) {
 
1134
            //Calculate the longest xTick width
 
1135
            xTicks.each(function(d,i){
 
1136
              var width = this.getBBox().width;
 
1137
              if(width > maxTextWidth) maxTextWidth = width;
 
1138
            });
 
1139
            //Convert to radians before calculating sin. Add 30 to margin for healthy padding.
 
1140
            var sin = Math.abs(Math.sin(rotateLabels*Math.PI/180));
 
1141
            var xLabelMargin = (sin ? sin*maxTextWidth : maxTextWidth)+30;
 
1142
            //Rotate all xTicks
 
1143
            xTicks
 
1144
              .attr('transform', function(d,i,j) { return 'rotate(' + rotateLabels + ' 0,0)' })
 
1145
              .style('text-anchor', rotateLabels%360 > 0 ? 'start' : 'end');
 
1146
          }
 
1147
          axisLabel.enter().append('text').attr('class', 'nv-axislabel');
 
1148
          var w = (scale.range().length==2) ? scale.range()[1] : (scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]));
 
1149
          axisLabel
 
1150
              .attr('text-anchor', 'middle')
 
1151
              .attr('y', xLabelMargin)
 
1152
              .attr('x', w/2);
 
1153
          if (showMaxMin) {
 
1154
          //if (showMaxMin && !isOrdinal) {
 
1155
            var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
 
1156
                           //.data(scale.domain())
 
1157
                           .data([scale.domain()[0], scale.domain()[scale.domain().length - 1]]);
 
1158
            axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text');
 
1159
            axisMaxMin.exit().remove();
 
1160
            axisMaxMin
 
1161
                .attr('transform', function(d,i) {
 
1162
                  return 'translate(' + (scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0)) + ',0)'
 
1163
                })
 
1164
              .select('text')
 
1165
                .attr('dy', '.71em')
 
1166
                .attr('y', axis.tickPadding())
 
1167
                .attr('transform', function(d,i,j) { return 'rotate(' + rotateLabels + ' 0,0)' })
 
1168
                .style('text-anchor', rotateLabels ? (rotateLabels%360 > 0 ? 'start' : 'end') : 'middle')
 
1169
                .text(function(d,i) {
 
1170
                  var v = fmt(d);
 
1171
                  return ('' + v).match('NaN') ? '' : v;
 
1172
                });
 
1173
            axisMaxMin.transition()
 
1174
                .attr('transform', function(d,i) {
 
1175
                  //return 'translate(' + scale.range()[i] + ',0)'
 
1176
                  //return 'translate(' + scale(d) + ',0)'
 
1177
                  return 'translate(' + (scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0)) + ',0)'
 
1178
                });
 
1179
          }
 
1180
          if (staggerLabels)
 
1181
            xTicks
 
1182
                .attr('transform', function(d,i) { return 'translate(0,' + (i % 2 == 0 ? '0' : '12') + ')' });
 
1183
 
 
1184
          break;
 
1185
        case 'right':
 
1186
          axisLabel.enter().append('text').attr('class', 'nv-axislabel');
 
1187
          axisLabel
 
1188
              .style('text-anchor', rotateYLabel ? 'middle' : 'begin')
 
1189
              .attr('transform', rotateYLabel ? 'rotate(90)' : '')
 
1190
              .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
 
1191
              .attr('x', rotateYLabel ? (scale.range()[0] / 2) : axis.tickPadding());
 
1192
          if (showMaxMin) {
 
1193
            var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
 
1194
                           .data(scale.domain());
 
1195
            axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text')
 
1196
                .style('opacity', 0);
 
1197
            axisMaxMin.exit().remove();
 
1198
            axisMaxMin
 
1199
                .attr('transform', function(d,i) {
 
1200
                  return 'translate(0,' + scale(d) + ')'
 
1201
                })
 
1202
              .select('text')
 
1203
                .attr('dy', '.32em')
 
1204
                .attr('y', 0)
 
1205
                .attr('x', axis.tickPadding())
 
1206
                .style('text-anchor', 'start')
 
1207
                .text(function(d,i) {
 
1208
                  var v = fmt(d);
 
1209
                  return ('' + v).match('NaN') ? '' : v;
 
1210
                });
 
1211
            axisMaxMin.transition()
 
1212
                .attr('transform', function(d,i) {
 
1213
                  return 'translate(0,' + scale.range()[i] + ')'
 
1214
                })
 
1215
              .select('text')
 
1216
                .style('opacity', 1);
 
1217
          }
 
1218
          break;
 
1219
        case 'left':
 
1220
          /*
 
1221
          //For dynamically placing the label. Can be used with dynamically-sized chart axis margins
 
1222
          var yTicks = g.selectAll('g').select("text");
 
1223
          yTicks.each(function(d,i){
 
1224
            var labelPadding = this.getBBox().width + axis.tickPadding() + 16;
 
1225
            if(labelPadding > width) width = labelPadding;
 
1226
          });
 
1227
          */
 
1228
          axisLabel.enter().append('text').attr('class', 'nv-axislabel');
 
1229
          axisLabel
 
1230
              .style('text-anchor', rotateYLabel ? 'middle' : 'end')
 
1231
              .attr('transform', rotateYLabel ? 'rotate(-90)' : '')
 
1232
              .attr('y', rotateYLabel ? (-Math.max(margin.left,width) + axisLabelDistance) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
 
1233
              .attr('x', rotateYLabel ? (-scale.range()[0] / 2) : -axis.tickPadding());
 
1234
          if (showMaxMin) {
 
1235
            var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
 
1236
                           .data(scale.domain());
 
1237
            axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text')
 
1238
                .style('opacity', 0);
 
1239
            axisMaxMin.exit().remove();
 
1240
            axisMaxMin
 
1241
                .attr('transform', function(d,i) {
 
1242
                  return 'translate(0,' + scale0(d) + ')'
 
1243
                })
 
1244
              .select('text')
 
1245
                .attr('dy', '.32em')
 
1246
                .attr('y', 0)
 
1247
                .attr('x', -axis.tickPadding())
 
1248
                .attr('text-anchor', 'end')
 
1249
                .text(function(d,i) {
 
1250
                  var v = fmt(d);
 
1251
                  return ('' + v).match('NaN') ? '' : v;
 
1252
                });
 
1253
            axisMaxMin.transition()
 
1254
                .attr('transform', function(d,i) {
 
1255
                  return 'translate(0,' + scale.range()[i] + ')'
 
1256
                })
 
1257
              .select('text')
 
1258
                .style('opacity', 1);
 
1259
          }
 
1260
          break;
 
1261
      }
 
1262
      axisLabel
 
1263
          .text(function(d) { return d });
 
1264
 
 
1265
 
 
1266
      if (showMaxMin && (axis.orient() === 'left' || axis.orient() === 'right')) {
 
1267
        //check if max and min overlap other values, if so, hide the values that overlap
 
1268
        g.selectAll('g') // the g's wrapping each tick
 
1269
            .each(function(d,i) {
 
1270
              d3.select(this).select('text').attr('opacity', 1);
 
1271
              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!
 
1272
                if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
 
1273
                  d3.select(this).attr('opacity', 0);
 
1274
 
 
1275
                d3.select(this).select('text').attr('opacity', 0); // Don't remove the ZERO line!!
 
1276
              }
 
1277
            });
 
1278
 
 
1279
        //if Max and Min = 0 only show min, Issue #281
 
1280
        if (scale.domain()[0] == scale.domain()[1] && scale.domain()[0] == 0)
 
1281
          wrap.selectAll('g.nv-axisMaxMin')
 
1282
            .style('opacity', function(d,i) { return !i ? 1 : 0 });
 
1283
 
 
1284
      }
 
1285
 
 
1286
      if (showMaxMin && (axis.orient() === 'top' || axis.orient() === 'bottom')) {
 
1287
        var maxMinRange = [];
 
1288
        wrap.selectAll('g.nv-axisMaxMin')
 
1289
            .each(function(d,i) {
 
1290
              try {
 
1291
                  if (i) // i== 1, max position
 
1292
                      maxMinRange.push(scale(d) - this.getBBox().width - 4)  //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
 
1293
                  else // i==0, min position
 
1294
                      maxMinRange.push(scale(d) + this.getBBox().width + 4)
 
1295
              }catch (err) {
 
1296
                  if (i) // i== 1, max position
 
1297
                      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)
 
1298
                  else // i==0, min position
 
1299
                      maxMinRange.push(scale(d) + 4)
 
1300
              }
 
1301
            });
 
1302
        g.selectAll('g') // the g's wrapping each tick
 
1303
            .each(function(d,i) {
 
1304
              if (scale(d) < maxMinRange[0] || scale(d) > maxMinRange[1]) {
 
1305
                if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
 
1306
                  d3.select(this).remove();
 
1307
                else
 
1308
                  d3.select(this).select('text').remove(); // Don't remove the ZERO line!!
 
1309
              }
 
1310
            });
 
1311
      }
 
1312
 
 
1313
 
 
1314
      //highlight zero line ... Maybe should not be an option and should just be in CSS?
 
1315
      if (highlightZero)
 
1316
        g.selectAll('.tick')
 
1317
          .filter(function(d) { return !parseFloat(Math.round(d.__data__*100000)/1000000) && (d.__data__ !== undefined) }) //this is because sometimes the 0 tick is a very small fraction, TODO: think of cleaner technique
 
1318
            .classed('zero', true);
 
1319
 
 
1320
      //store old scales for use in transitions on update
 
1321
      scale0 = scale.copy();
 
1322
 
 
1323
    });
 
1324
 
 
1325
    return chart;
 
1326
  }
 
1327
 
 
1328
 
 
1329
  //============================================================
 
1330
  // Expose Public Variables
 
1331
  //------------------------------------------------------------
 
1332
 
 
1333
  // expose chart's sub-components
 
1334
  chart.axis = axis;
 
1335
 
 
1336
  d3.rebind(chart, axis, 'orient', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat');
 
1337
  d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands'); //these are also accessible by chart.scale(), but added common ones directly for ease of use
 
1338
 
 
1339
  chart.options = nv.utils.optionsFunc.bind(chart);
 
1340
 
 
1341
  chart.margin = function(_) {
 
1342
    if(!arguments.length) return margin;
 
1343
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
1344
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
1345
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
1346
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
1347
    return chart;
 
1348
  }
 
1349
 
 
1350
  chart.width = function(_) {
 
1351
    if (!arguments.length) return width;
 
1352
    width = _;
 
1353
    return chart;
 
1354
  };
 
1355
 
 
1356
  chart.ticks = function(_) {
 
1357
    if (!arguments.length) return ticks;
 
1358
    ticks = _;
 
1359
    return chart;
 
1360
  };
 
1361
 
 
1362
  chart.height = function(_) {
 
1363
    if (!arguments.length) return height;
 
1364
    height = _;
 
1365
    return chart;
 
1366
  };
 
1367
 
 
1368
  chart.axisLabel = function(_) {
 
1369
    if (!arguments.length) return axisLabelText;
 
1370
    axisLabelText = _;
 
1371
    return chart;
 
1372
  }
 
1373
 
 
1374
  chart.showMaxMin = function(_) {
 
1375
    if (!arguments.length) return showMaxMin;
 
1376
    showMaxMin = _;
 
1377
    return chart;
 
1378
  }
 
1379
 
 
1380
  chart.highlightZero = function(_) {
 
1381
    if (!arguments.length) return highlightZero;
 
1382
    highlightZero = _;
 
1383
    return chart;
 
1384
  }
 
1385
 
 
1386
  chart.scale = function(_) {
 
1387
    if (!arguments.length) return scale;
 
1388
    scale = _;
 
1389
    axis.scale(scale);
 
1390
    isOrdinal = typeof scale.rangeBands === 'function';
 
1391
    d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands');
 
1392
    return chart;
 
1393
  }
 
1394
 
 
1395
  chart.rotateYLabel = function(_) {
 
1396
    if(!arguments.length) return rotateYLabel;
 
1397
    rotateYLabel = _;
 
1398
    return chart;
 
1399
  }
 
1400
 
 
1401
  chart.rotateLabels = function(_) {
 
1402
    if(!arguments.length) return rotateLabels;
 
1403
    rotateLabels = _;
 
1404
    return chart;
 
1405
  }
 
1406
 
 
1407
  chart.staggerLabels = function(_) {
 
1408
    if (!arguments.length) return staggerLabels;
 
1409
    staggerLabels = _;
 
1410
    return chart;
 
1411
  };
 
1412
 
 
1413
  chart.axisLabelDistance = function(_) {
 
1414
    if (!arguments.length) return axisLabelDistance;
 
1415
    axisLabelDistance = _;
 
1416
    return chart;
 
1417
  };
 
1418
 
 
1419
  //============================================================
 
1420
 
 
1421
 
 
1422
  return chart;
 
1423
}
 
1424
//TODO: consider deprecating and using multibar with single series for this
 
1425
nv.models.historicalBar = function() {
 
1426
  "use strict";
 
1427
  //============================================================
 
1428
  // Public Variables with Default Settings
 
1429
  //------------------------------------------------------------
 
1430
 
 
1431
  var margin = {top: 0, right: 0, bottom: 0, left: 0}
 
1432
    , width = 960
 
1433
    , height = 500
 
1434
    , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
 
1435
    , x = d3.scale.linear()
 
1436
    , y = d3.scale.linear()
 
1437
    , getX = function(d) { return d.x }
 
1438
    , getY = function(d) { return d.y }
 
1439
    , forceX = []
 
1440
    , forceY = [0]
 
1441
    , padData = false
 
1442
    , clipEdge = true
 
1443
    , color = nv.utils.defaultColor()
 
1444
    , xDomain
 
1445
    , yDomain
 
1446
    , xRange
 
1447
    , yRange
 
1448
    , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
 
1449
    , interactive = true
 
1450
    ;
 
1451
 
 
1452
  //============================================================
 
1453
 
 
1454
 
 
1455
  function chart(selection) {
 
1456
    selection.each(function(data) {
 
1457
      var availableWidth = width - margin.left - margin.right,
 
1458
          availableHeight = height - margin.top - margin.bottom,
 
1459
          container = d3.select(this);
 
1460
 
 
1461
 
 
1462
      //------------------------------------------------------------
 
1463
      // Setup Scales
 
1464
 
 
1465
      x   .domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ))
 
1466
 
 
1467
      if (padData)
 
1468
        x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5)  / data[0].values.length ]);
 
1469
      else
 
1470
        x.range(xRange || [0, availableWidth]);
 
1471
 
 
1472
      y   .domain(yDomain || d3.extent(data[0].values.map(getY).concat(forceY) ))
 
1473
          .range(yRange || [availableHeight, 0]);
 
1474
 
 
1475
      // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
 
1476
 
 
1477
      if (x.domain()[0] === x.domain()[1])
 
1478
        x.domain()[0] ?
 
1479
            x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
 
1480
          : x.domain([-1,1]);
 
1481
 
 
1482
      if (y.domain()[0] === y.domain()[1])
 
1483
        y.domain()[0] ?
 
1484
            y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
 
1485
          : y.domain([-1,1]);
 
1486
 
 
1487
      //------------------------------------------------------------
 
1488
 
 
1489
 
 
1490
      //------------------------------------------------------------
 
1491
      // Setup containers and skeleton of chart
 
1492
 
 
1493
      var wrap = container.selectAll('g.nv-wrap.nv-historicalBar-' + id).data([data[0].values]);
 
1494
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBar-' + id);
 
1495
      var defsEnter = wrapEnter.append('defs');
 
1496
      var gEnter = wrapEnter.append('g');
 
1497
      var g = wrap.select('g');
 
1498
 
 
1499
      gEnter.append('g').attr('class', 'nv-bars');
 
1500
 
 
1501
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
1502
 
 
1503
      //------------------------------------------------------------
 
1504
 
 
1505
 
 
1506
      container
 
1507
          .on('click', function(d,i) {
 
1508
            dispatch.chartClick({
 
1509
                data: d,
 
1510
                index: i,
 
1511
                pos: d3.event,
 
1512
                id: id
 
1513
            });
 
1514
          });
 
1515
 
 
1516
 
 
1517
      defsEnter.append('clipPath')
 
1518
          .attr('id', 'nv-chart-clip-path-' + id)
 
1519
        .append('rect');
 
1520
 
 
1521
      wrap.select('#nv-chart-clip-path-' + id + ' rect')
 
1522
          .attr('width', availableWidth)
 
1523
          .attr('height', availableHeight);
 
1524
 
 
1525
      g   .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
 
1526
 
 
1527
 
 
1528
 
 
1529
      var bars = wrap.select('.nv-bars').selectAll('.nv-bar')
 
1530
          .data(function(d) { return d }, function(d,i) {return getX(d,i)});
 
1531
 
 
1532
      bars.exit().remove();
 
1533
 
 
1534
 
 
1535
      var barsEnter = bars.enter().append('rect')
 
1536
          //.attr('class', function(d,i,j) { return (getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive') + ' nv-bar-' + j + '-' + i })
 
1537
          .attr('x', 0 )
 
1538
          .attr('y', function(d,i) {  return nv.utils.NaNtoZero(y(Math.max(0, getY(d,i)))) })
 
1539
          .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.abs(y(getY(d,i)) - y(0))) })
 
1540
          .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; }) 
 
1541
          .on('mouseover', function(d,i) {
 
1542
            if (!interactive) return;
 
1543
            d3.select(this).classed('hover', true);
 
1544
            dispatch.elementMouseover({
 
1545
                point: d,
 
1546
                series: data[0],
 
1547
                pos: [x(getX(d,i)), y(getY(d,i))],  // TODO: Figure out why the value appears to be shifted
 
1548
                pointIndex: i,
 
1549
                seriesIndex: 0,
 
1550
                e: d3.event
 
1551
            });
 
1552
 
 
1553
          })
 
1554
          .on('mouseout', function(d,i) {
 
1555
                if (!interactive) return;
 
1556
                d3.select(this).classed('hover', false);
 
1557
                dispatch.elementMouseout({
 
1558
                    point: d,
 
1559
                    series: data[0],
 
1560
                    pointIndex: i,
 
1561
                    seriesIndex: 0,
 
1562
                    e: d3.event
 
1563
                });
 
1564
          })
 
1565
          .on('click', function(d,i) {
 
1566
                if (!interactive) return;
 
1567
                dispatch.elementClick({
 
1568
                    //label: d[label],
 
1569
                    value: getY(d,i),
 
1570
                    data: d,
 
1571
                    index: i,
 
1572
                    pos: [x(getX(d,i)), y(getY(d,i))],
 
1573
                    e: d3.event,
 
1574
                    id: id
 
1575
                });
 
1576
              d3.event.stopPropagation();
 
1577
          })
 
1578
          .on('dblclick', function(d,i) {
 
1579
              if (!interactive) return;
 
1580
              dispatch.elementDblClick({
 
1581
                  //label: d[label],
 
1582
                  value: getY(d,i),
 
1583
                  data: d,
 
1584
                  index: i,
 
1585
                  pos: [x(getX(d,i)), y(getY(d,i))],
 
1586
                  e: d3.event,
 
1587
                  id: id
 
1588
              });
 
1589
              d3.event.stopPropagation();
 
1590
          });
 
1591
 
 
1592
      bars
 
1593
          .attr('fill', function(d,i) { return color(d, i); })
 
1594
          .attr('class', function(d,i,j) { return (getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive') + ' nv-bar-' + j + '-' + i })
 
1595
          .transition()
 
1596
          .attr('transform', function(d,i) { return 'translate(' + (x(getX(d,i)) - availableWidth / data[0].values.length * .45) + ',0)'; }) 
 
1597
           //TODO: better width calculations that don't assume always uniform data spacing;w
 
1598
          .attr('width', (availableWidth / data[0].values.length) * .9 );
 
1599
 
 
1600
 
 
1601
      bars.transition()
 
1602
          .attr('y', function(d,i) {
 
1603
            var rval = getY(d,i) < 0 ?
 
1604
                    y(0) :
 
1605
                    y(0) - y(getY(d,i)) < 1 ?
 
1606
                      y(0) - 1 :
 
1607
                      y(getY(d,i));
 
1608
            return nv.utils.NaNtoZero(rval);
 
1609
          })
 
1610
          .attr('height', function(d,i) { return nv.utils.NaNtoZero(Math.max(Math.abs(y(getY(d,i)) - y(0)),1)) });
 
1611
 
 
1612
    });
 
1613
 
 
1614
    return chart;
 
1615
  }
 
1616
 
 
1617
  //Create methods to allow outside functions to highlight a specific bar.
 
1618
  chart.highlightPoint = function(pointIndex, isHoverOver) {
 
1619
      d3.select(".nv-historicalBar-" + id)
 
1620
        .select(".nv-bars .nv-bar-0-" + pointIndex)
 
1621
              .classed("hover", isHoverOver)
 
1622
               ;
 
1623
  };
 
1624
 
 
1625
  chart.clearHighlights = function() {
 
1626
      d3.select(".nv-historicalBar-" + id)
 
1627
        .select(".nv-bars .nv-bar.hover")
 
1628
              .classed("hover", false)
 
1629
               ;
 
1630
  };
 
1631
  //============================================================
 
1632
  // Expose Public Variables
 
1633
  //------------------------------------------------------------
 
1634
 
 
1635
  chart.dispatch = dispatch;
 
1636
 
 
1637
  chart.options = nv.utils.optionsFunc.bind(chart);
 
1638
  
 
1639
  chart.x = function(_) {
 
1640
    if (!arguments.length) return getX;
 
1641
    getX = _;
 
1642
    return chart;
 
1643
  };
 
1644
 
 
1645
  chart.y = function(_) {
 
1646
    if (!arguments.length) return getY;
 
1647
    getY = _;
 
1648
    return chart;
 
1649
  };
 
1650
 
 
1651
  chart.margin = function(_) {
 
1652
    if (!arguments.length) return margin;
 
1653
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
1654
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
1655
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
1656
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
1657
    return chart;
 
1658
  };
 
1659
 
 
1660
  chart.width = function(_) {
 
1661
    if (!arguments.length) return width;
 
1662
    width = _;
 
1663
    return chart;
 
1664
  };
 
1665
 
 
1666
  chart.height = function(_) {
 
1667
    if (!arguments.length) return height;
 
1668
    height = _;
 
1669
    return chart;
 
1670
  };
 
1671
 
 
1672
  chart.xScale = function(_) {
 
1673
    if (!arguments.length) return x;
 
1674
    x = _;
 
1675
    return chart;
 
1676
  };
 
1677
 
 
1678
  chart.yScale = function(_) {
 
1679
    if (!arguments.length) return y;
 
1680
    y = _;
 
1681
    return chart;
 
1682
  };
 
1683
 
 
1684
  chart.xDomain = function(_) {
 
1685
    if (!arguments.length) return xDomain;
 
1686
    xDomain = _;
 
1687
    return chart;
 
1688
  };
 
1689
 
 
1690
  chart.yDomain = function(_) {
 
1691
    if (!arguments.length) return yDomain;
 
1692
    yDomain = _;
 
1693
    return chart;
 
1694
  };
 
1695
 
 
1696
  chart.xRange = function(_) {
 
1697
    if (!arguments.length) return xRange;
 
1698
    xRange = _;
 
1699
    return chart;
 
1700
  };
 
1701
 
 
1702
  chart.yRange = function(_) {
 
1703
    if (!arguments.length) return yRange;
 
1704
    yRange = _;
 
1705
    return chart;
 
1706
  };
 
1707
 
 
1708
  chart.forceX = function(_) {
 
1709
    if (!arguments.length) return forceX;
 
1710
    forceX = _;
 
1711
    return chart;
 
1712
  };
 
1713
 
 
1714
  chart.forceY = function(_) {
 
1715
    if (!arguments.length) return forceY;
 
1716
    forceY = _;
 
1717
    return chart;
 
1718
  };
 
1719
 
 
1720
  chart.padData = function(_) {
 
1721
    if (!arguments.length) return padData;
 
1722
    padData = _;
 
1723
    return chart;
 
1724
  };
 
1725
 
 
1726
  chart.clipEdge = function(_) {
 
1727
    if (!arguments.length) return clipEdge;
 
1728
    clipEdge = _;
 
1729
    return chart;
 
1730
  };
 
1731
 
 
1732
  chart.color = function(_) {
 
1733
    if (!arguments.length) return color;
 
1734
    color = nv.utils.getColor(_);
 
1735
    return chart;
 
1736
  };
 
1737
 
 
1738
  chart.id = function(_) {
 
1739
    if (!arguments.length) return id;
 
1740
    id = _;
 
1741
    return chart;
 
1742
  };
 
1743
 
 
1744
  chart.interactive = function(_) {
 
1745
    if(!arguments.length) return interactive;
 
1746
    interactive = false;
 
1747
    return chart;
 
1748
  };
 
1749
 
 
1750
  //============================================================
 
1751
 
 
1752
 
 
1753
  return chart;
 
1754
}
 
1755
 
 
1756
// Chart design based on the recommendations of Stephen Few. Implementation
 
1757
// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
 
1758
// http://projects.instantcognition.com/protovis/bulletchart/
 
1759
 
 
1760
nv.models.bullet = function() {
 
1761
  "use strict";
 
1762
  //============================================================
 
1763
  // Public Variables with Default Settings
 
1764
  //------------------------------------------------------------
 
1765
 
 
1766
  var margin = {top: 0, right: 0, bottom: 0, left: 0}
 
1767
    , orient = 'left' // TODO top & bottom
 
1768
    , reverse = false
 
1769
    , ranges = function(d) { return d.ranges }
 
1770
    , markers = function(d) { return d.markers }
 
1771
    , measures = function(d) { return d.measures }
 
1772
    , rangeLabels = function(d) { return d.rangeLabels ? d.rangeLabels : [] }
 
1773
    , markerLabels = function(d) { return d.markerLabels ? d.markerLabels : []  }
 
1774
    , measureLabels = function(d) { return d.measureLabels ? d.measureLabels : []  }
 
1775
    , forceX = [0] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
 
1776
    , width = 380
 
1777
    , height = 30
 
1778
    , tickFormat = null
 
1779
    , color = nv.utils.getColor(['#1f77b4'])
 
1780
    , dispatch = d3.dispatch('elementMouseover', 'elementMouseout')
 
1781
    ;
 
1782
 
 
1783
  //============================================================
 
1784
 
 
1785
 
 
1786
  function chart(selection) {
 
1787
    selection.each(function(d, i) {
 
1788
      var availableWidth = width - margin.left - margin.right,
 
1789
          availableHeight = height - margin.top - margin.bottom,
 
1790
          container = d3.select(this);
 
1791
 
 
1792
      var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
 
1793
          markerz = markers.call(this, d, i).slice().sort(d3.descending),
 
1794
          measurez = measures.call(this, d, i).slice().sort(d3.descending),
 
1795
          rangeLabelz = rangeLabels.call(this, d, i).slice(),
 
1796
          markerLabelz = markerLabels.call(this, d, i).slice(),
 
1797
          measureLabelz = measureLabels.call(this, d, i).slice();
 
1798
 
 
1799
 
 
1800
      //------------------------------------------------------------
 
1801
      // Setup Scales
 
1802
 
 
1803
      // Compute the new x-scale.
 
1804
      var x1 = d3.scale.linear()
 
1805
          .domain( d3.extent(d3.merge([forceX, rangez])) )
 
1806
          .range(reverse ? [availableWidth, 0] : [0, availableWidth]);
 
1807
 
 
1808
      // Retrieve the old x-scale, if this is an update.
 
1809
      var x0 = this.__chart__ || d3.scale.linear()
 
1810
          .domain([0, Infinity])
 
1811
          .range(x1.range());
 
1812
 
 
1813
      // Stash the new scale.
 
1814
      this.__chart__ = x1;
 
1815
 
 
1816
 
 
1817
      var rangeMin = d3.min(rangez), //rangez[2]
 
1818
          rangeMax = d3.max(rangez), //rangez[0]
 
1819
          rangeAvg = rangez[1];
 
1820
 
 
1821
      //------------------------------------------------------------
 
1822
 
 
1823
 
 
1824
      //------------------------------------------------------------
 
1825
      // Setup containers and skeleton of chart
 
1826
 
 
1827
      var wrap = container.selectAll('g.nv-wrap.nv-bullet').data([d]);
 
1828
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bullet');
 
1829
      var gEnter = wrapEnter.append('g');
 
1830
      var g = wrap.select('g');
 
1831
 
 
1832
      gEnter.append('rect').attr('class', 'nv-range nv-rangeMax');
 
1833
      gEnter.append('rect').attr('class', 'nv-range nv-rangeAvg');
 
1834
      gEnter.append('rect').attr('class', 'nv-range nv-rangeMin');
 
1835
      gEnter.append('rect').attr('class', 'nv-measure');
 
1836
      gEnter.append('path').attr('class', 'nv-markerTriangle');
 
1837
 
 
1838
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
1839
 
 
1840
      //------------------------------------------------------------
 
1841
 
 
1842
 
 
1843
 
 
1844
      var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
 
1845
          w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
 
1846
      var xp0 = function(d) { return d < 0 ? x0(d) : x0(0) },
 
1847
          xp1 = function(d) { return d < 0 ? x1(d) : x1(0) };
 
1848
 
 
1849
 
 
1850
      g.select('rect.nv-rangeMax')
 
1851
          .attr('height', availableHeight)
 
1852
          .attr('width', w1(rangeMax > 0 ? rangeMax : rangeMin))
 
1853
          .attr('x', xp1(rangeMax > 0 ? rangeMax : rangeMin))
 
1854
          .datum(rangeMax > 0 ? rangeMax : rangeMin)
 
1855
          /*
 
1856
          .attr('x', rangeMin < 0 ?
 
1857
                         rangeMax > 0 ?
 
1858
                             x1(rangeMin)
 
1859
                           : x1(rangeMax)
 
1860
                       : x1(0))
 
1861
                      */
 
1862
 
 
1863
      g.select('rect.nv-rangeAvg')
 
1864
          .attr('height', availableHeight)
 
1865
          .attr('width', w1(rangeAvg))
 
1866
          .attr('x', xp1(rangeAvg))
 
1867
          .datum(rangeAvg)
 
1868
          /*
 
1869
          .attr('width', rangeMax <= 0 ?
 
1870
                             x1(rangeMax) - x1(rangeAvg)
 
1871
                           : x1(rangeAvg) - x1(rangeMin))
 
1872
          .attr('x', rangeMax <= 0 ?
 
1873
                         x1(rangeAvg)
 
1874
                       : x1(rangeMin))
 
1875
                      */
 
1876
 
 
1877
      g.select('rect.nv-rangeMin')
 
1878
          .attr('height', availableHeight)
 
1879
          .attr('width', w1(rangeMax))
 
1880
          .attr('x', xp1(rangeMax))
 
1881
          .attr('width', w1(rangeMax > 0 ? rangeMin : rangeMax))
 
1882
          .attr('x', xp1(rangeMax > 0 ? rangeMin : rangeMax))
 
1883
          .datum(rangeMax > 0 ? rangeMin : rangeMax)
 
1884
          /*
 
1885
          .attr('width', rangeMax <= 0 ?
 
1886
                             x1(rangeAvg) - x1(rangeMin)
 
1887
                           : x1(rangeMax) - x1(rangeAvg))
 
1888
          .attr('x', rangeMax <= 0 ?
 
1889
                         x1(rangeMin)
 
1890
                       : x1(rangeAvg))
 
1891
                      */
 
1892
 
 
1893
      g.select('rect.nv-measure')
 
1894
          .style('fill', color)
 
1895
          .attr('height', availableHeight / 3)
 
1896
          .attr('y', availableHeight / 3)
 
1897
          .attr('width', measurez < 0 ?
 
1898
                             x1(0) - x1(measurez[0])
 
1899
                           : x1(measurez[0]) - x1(0))
 
1900
          .attr('x', xp1(measurez))
 
1901
          .on('mouseover', function() {
 
1902
              dispatch.elementMouseover({
 
1903
                value: measurez[0],
 
1904
                label: measureLabelz[0] || 'Current',
 
1905
                pos: [x1(measurez[0]), availableHeight/2]
 
1906
              })
 
1907
          })
 
1908
          .on('mouseout', function() {
 
1909
              dispatch.elementMouseout({
 
1910
                value: measurez[0],
 
1911
                label: measureLabelz[0] || 'Current'
 
1912
              })
 
1913
          })
 
1914
 
 
1915
      var h3 =  availableHeight / 6;
 
1916
      if (markerz[0]) {
 
1917
        g.selectAll('path.nv-markerTriangle')
 
1918
            .attr('transform', function(d) { return 'translate(' + x1(markerz[0]) + ',' + (availableHeight / 2) + ')' })
 
1919
            .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z')
 
1920
            .on('mouseover', function() {
 
1921
              dispatch.elementMouseover({
 
1922
                value: markerz[0],
 
1923
                label: markerLabelz[0] || 'Previous',
 
1924
                pos: [x1(markerz[0]), availableHeight/2]
 
1925
              })
 
1926
            })
 
1927
            .on('mouseout', function() {
 
1928
              dispatch.elementMouseout({
 
1929
                value: markerz[0],
 
1930
                label: markerLabelz[0] || 'Previous'
 
1931
              })
 
1932
            });
 
1933
      } else {
 
1934
        g.selectAll('path.nv-markerTriangle').remove();
 
1935
      }
 
1936
 
 
1937
 
 
1938
      wrap.selectAll('.nv-range')
 
1939
          .on('mouseover', function(d,i) {
 
1940
            var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
 
1941
 
 
1942
            dispatch.elementMouseover({
 
1943
              value: d,
 
1944
              label: label,
 
1945
              pos: [x1(d), availableHeight/2]
 
1946
            })
 
1947
          })
 
1948
          .on('mouseout', function(d,i) {
 
1949
            var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
 
1950
 
 
1951
            dispatch.elementMouseout({
 
1952
              value: d,
 
1953
              label: label
 
1954
            })
 
1955
          })
 
1956
 
 
1957
/* // THIS IS THE PREVIOUS BULLET IMPLEMENTATION, WILL REMOVE SHORTLY
 
1958
      // Update the range rects.
 
1959
      var range = g.selectAll('rect.nv-range')
 
1960
          .data(rangez);
 
1961
 
 
1962
      range.enter().append('rect')
 
1963
          .attr('class', function(d, i) { return 'nv-range nv-s' + i; })
 
1964
          .attr('width', w0)
 
1965
          .attr('height', availableHeight)
 
1966
          .attr('x', reverse ? x0 : 0)
 
1967
          .on('mouseover', function(d,i) { 
 
1968
              dispatch.elementMouseover({
 
1969
                value: d,
 
1970
                label: (i <= 0) ? 'Maximum' : (i > 1) ? 'Minimum' : 'Mean', //TODO: make these labels a variable
 
1971
                pos: [x1(d), availableHeight/2]
 
1972
              })
 
1973
          })
 
1974
          .on('mouseout', function(d,i) { 
 
1975
              dispatch.elementMouseout({
 
1976
                value: d,
 
1977
                label: (i <= 0) ? 'Minimum' : (i >=1) ? 'Maximum' : 'Mean' //TODO: make these labels a variable
 
1978
              })
 
1979
          })
 
1980
 
 
1981
      d3.transition(range)
 
1982
          .attr('x', reverse ? x1 : 0)
 
1983
          .attr('width', w1)
 
1984
          .attr('height', availableHeight);
 
1985
 
 
1986
 
 
1987
      // Update the measure rects.
 
1988
      var measure = g.selectAll('rect.nv-measure')
 
1989
          .data(measurez);
 
1990
 
 
1991
      measure.enter().append('rect')
 
1992
          .attr('class', function(d, i) { return 'nv-measure nv-s' + i; })
 
1993
          .style('fill', function(d,i) { return color(d,i ) })
 
1994
          .attr('width', w0)
 
1995
          .attr('height', availableHeight / 3)
 
1996
          .attr('x', reverse ? x0 : 0)
 
1997
          .attr('y', availableHeight / 3)
 
1998
          .on('mouseover', function(d) { 
 
1999
              dispatch.elementMouseover({
 
2000
                value: d,
 
2001
                label: 'Current', //TODO: make these labels a variable
 
2002
                pos: [x1(d), availableHeight/2]
 
2003
              })
 
2004
          })
 
2005
          .on('mouseout', function(d) { 
 
2006
              dispatch.elementMouseout({
 
2007
                value: d,
 
2008
                label: 'Current' //TODO: make these labels a variable
 
2009
              })
 
2010
          })
 
2011
 
 
2012
      d3.transition(measure)
 
2013
          .attr('width', w1)
 
2014
          .attr('height', availableHeight / 3)
 
2015
          .attr('x', reverse ? x1 : 0)
 
2016
          .attr('y', availableHeight / 3);
 
2017
 
 
2018
 
 
2019
 
 
2020
      // Update the marker lines.
 
2021
      var marker = g.selectAll('path.nv-markerTriangle')
 
2022
          .data(markerz);
 
2023
 
 
2024
      var h3 =  availableHeight / 6;
 
2025
      marker.enter().append('path')
 
2026
          .attr('class', 'nv-markerTriangle')
 
2027
          .attr('transform', function(d) { return 'translate(' + x0(d) + ',' + (availableHeight / 2) + ')' })
 
2028
          .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z')
 
2029
          .on('mouseover', function(d,i) {
 
2030
              dispatch.elementMouseover({
 
2031
                value: d,
 
2032
                label: 'Previous',
 
2033
                pos: [x1(d), availableHeight/2]
 
2034
              })
 
2035
          })
 
2036
          .on('mouseout', function(d,i) {
 
2037
              dispatch.elementMouseout({
 
2038
                value: d,
 
2039
                label: 'Previous'
 
2040
              })
 
2041
          });
 
2042
 
 
2043
      d3.transition(marker)
 
2044
          .attr('transform', function(d) { return 'translate(' + (x1(d) - x1(0)) + ',' + (availableHeight / 2) + ')' });
 
2045
 
 
2046
      marker.exit().remove();
 
2047
*/
 
2048
 
 
2049
    });
 
2050
 
 
2051
    // d3.timer.flush();  // Not needed?
 
2052
 
 
2053
    return chart;
 
2054
  }
 
2055
 
 
2056
 
 
2057
  //============================================================
 
2058
  // Expose Public Variables
 
2059
  //------------------------------------------------------------
 
2060
 
 
2061
  chart.dispatch = dispatch;
 
2062
 
 
2063
  chart.options = nv.utils.optionsFunc.bind(chart);
 
2064
  
 
2065
  // left, right, top, bottom
 
2066
  chart.orient = function(_) {
 
2067
    if (!arguments.length) return orient;
 
2068
    orient = _;
 
2069
    reverse = orient == 'right' || orient == 'bottom';
 
2070
    return chart;
 
2071
  };
 
2072
 
 
2073
  // ranges (bad, satisfactory, good)
 
2074
  chart.ranges = function(_) {
 
2075
    if (!arguments.length) return ranges;
 
2076
    ranges = _;
 
2077
    return chart;
 
2078
  };
 
2079
 
 
2080
  // markers (previous, goal)
 
2081
  chart.markers = function(_) {
 
2082
    if (!arguments.length) return markers;
 
2083
    markers = _;
 
2084
    return chart;
 
2085
  };
 
2086
 
 
2087
  // measures (actual, forecast)
 
2088
  chart.measures = function(_) {
 
2089
    if (!arguments.length) return measures;
 
2090
    measures = _;
 
2091
    return chart;
 
2092
  };
 
2093
 
 
2094
  chart.forceX = function(_) {
 
2095
    if (!arguments.length) return forceX;
 
2096
    forceX = _;
 
2097
    return chart;
 
2098
  };
 
2099
 
 
2100
  chart.width = function(_) {
 
2101
    if (!arguments.length) return width;
 
2102
    width = _;
 
2103
    return chart;
 
2104
  };
 
2105
 
 
2106
  chart.height = function(_) {
 
2107
    if (!arguments.length) return height;
 
2108
    height = _;
 
2109
    return chart;
 
2110
  };
 
2111
 
 
2112
  chart.margin = function(_) {
 
2113
    if (!arguments.length) return margin;
 
2114
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
2115
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
2116
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
2117
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
2118
    return chart;
 
2119
  };
 
2120
 
 
2121
  chart.tickFormat = function(_) {
 
2122
    if (!arguments.length) return tickFormat;
 
2123
    tickFormat = _;
 
2124
    return chart;
 
2125
  };
 
2126
 
 
2127
  chart.color = function(_) {
 
2128
    if (!arguments.length) return color;
 
2129
    color = nv.utils.getColor(_);
 
2130
    return chart;
 
2131
  };
 
2132
 
 
2133
  //============================================================
 
2134
 
 
2135
 
 
2136
  return chart;
 
2137
};
 
2138
 
 
2139
 
 
2140
 
 
2141
// Chart design based on the recommendations of Stephen Few. Implementation
 
2142
// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
 
2143
// http://projects.instantcognition.com/protovis/bulletchart/
 
2144
nv.models.bulletChart = function() {
 
2145
  "use strict";
 
2146
  //============================================================
 
2147
  // Public Variables with Default Settings
 
2148
  //------------------------------------------------------------
 
2149
 
 
2150
  var bullet = nv.models.bullet()
 
2151
    ;
 
2152
 
 
2153
  var orient = 'left' // TODO top & bottom
 
2154
    , reverse = false
 
2155
    , margin = {top: 5, right: 40, bottom: 20, left: 120}
 
2156
    , ranges = function(d) { return d.ranges }
 
2157
    , markers = function(d) { return d.markers }
 
2158
    , measures = function(d) { return d.measures }
 
2159
    , width = null
 
2160
    , height = 55
 
2161
    , tickFormat = null
 
2162
    , tooltips = true
 
2163
    , tooltip = function(key, x, y, e, graph) {
 
2164
        return '<h3>' + x + '</h3>' +
 
2165
               '<p>' + y + '</p>'
 
2166
      }
 
2167
    , noData = 'No Data Available.'
 
2168
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
 
2169
    ;
 
2170
 
 
2171
  //============================================================
 
2172
 
 
2173
 
 
2174
  //============================================================
 
2175
  // Private Variables
 
2176
  //------------------------------------------------------------
 
2177
 
 
2178
  var showTooltip = function(e, offsetElement) {
 
2179
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ) + margin.left,
 
2180
        top = e.pos[1] + ( offsetElement.offsetTop || 0) + margin.top,
 
2181
        content = tooltip(e.key, e.label, e.value, e, chart);
 
2182
 
 
2183
    nv.tooltip.show([left, top], content, e.value < 0 ? 'e' : 'w', null, offsetElement);
 
2184
  };
 
2185
 
 
2186
  //============================================================
 
2187
 
 
2188
 
 
2189
  function chart(selection) {
 
2190
    selection.each(function(d, i) {
 
2191
      var container = d3.select(this);
 
2192
 
 
2193
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
2194
                             - margin.left - margin.right,
 
2195
          availableHeight = height - margin.top - margin.bottom,
 
2196
          that = this;
 
2197
 
 
2198
 
 
2199
      chart.update = function() { chart(selection) };
 
2200
      chart.container = this;
 
2201
 
 
2202
      //------------------------------------------------------------
 
2203
      // Display No Data message if there's nothing to show.
 
2204
 
 
2205
      if (!d || !ranges.call(this, d, i)) {
 
2206
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
2207
 
 
2208
        noDataText.enter().append('text')
 
2209
          .attr('class', 'nvd3 nv-noData')
 
2210
          .attr('dy', '-.7em')
 
2211
          .style('text-anchor', 'middle');
 
2212
 
 
2213
        noDataText
 
2214
          .attr('x', margin.left + availableWidth / 2)
 
2215
          .attr('y', 18 + margin.top + availableHeight / 2)
 
2216
          .text(function(d) { return d });
 
2217
 
 
2218
        return chart;
 
2219
      } else {
 
2220
        container.selectAll('.nv-noData').remove();
 
2221
      }
 
2222
 
 
2223
      //------------------------------------------------------------
 
2224
 
 
2225
 
 
2226
 
 
2227
      var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
 
2228
          markerz = markers.call(this, d, i).slice().sort(d3.descending),
 
2229
          measurez = measures.call(this, d, i).slice().sort(d3.descending);
 
2230
 
 
2231
 
 
2232
      //------------------------------------------------------------
 
2233
      // Setup containers and skeleton of chart
 
2234
 
 
2235
      var wrap = container.selectAll('g.nv-wrap.nv-bulletChart').data([d]);
 
2236
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bulletChart');
 
2237
      var gEnter = wrapEnter.append('g');
 
2238
      var g = wrap.select('g');
 
2239
 
 
2240
      gEnter.append('g').attr('class', 'nv-bulletWrap');
 
2241
      gEnter.append('g').attr('class', 'nv-titles');
 
2242
 
 
2243
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
2244
 
 
2245
      //------------------------------------------------------------
 
2246
 
 
2247
 
 
2248
      // Compute the new x-scale.
 
2249
      var x1 = d3.scale.linear()
 
2250
          .domain([0, Math.max(rangez[0], markerz[0], measurez[0])])  // TODO: need to allow forceX and forceY, and xDomain, yDomain
 
2251
          .range(reverse ? [availableWidth, 0] : [0, availableWidth]);
 
2252
 
 
2253
      // Retrieve the old x-scale, if this is an update.
 
2254
      var x0 = this.__chart__ || d3.scale.linear()
 
2255
          .domain([0, Infinity])
 
2256
          .range(x1.range());
 
2257
 
 
2258
      // Stash the new scale.
 
2259
      this.__chart__ = x1;
 
2260
 
 
2261
      /*
 
2262
      // Derive width-scales from the x-scales.
 
2263
      var w0 = bulletWidth(x0),
 
2264
          w1 = bulletWidth(x1);
 
2265
 
 
2266
      function bulletWidth(x) {
 
2267
        var x0 = x(0);
 
2268
        return function(d) {
 
2269
          return Math.abs(x(d) - x(0));
 
2270
        };
 
2271
      }
 
2272
 
 
2273
      function bulletTranslate(x) {
 
2274
        return function(d) {
 
2275
          return 'translate(' + x(d) + ',0)';
 
2276
        };
 
2277
      }
 
2278
      */
 
2279
 
 
2280
      var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
 
2281
          w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
 
2282
 
 
2283
 
 
2284
      var title = gEnter.select('.nv-titles').append('g')
 
2285
          .attr('text-anchor', 'end')
 
2286
          .attr('transform', 'translate(-6,' + (height - margin.top - margin.bottom) / 2 + ')');
 
2287
      title.append('text')
 
2288
          .attr('class', 'nv-title')
 
2289
          .text(function(d) { return d.title; });
 
2290
 
 
2291
      title.append('text')
 
2292
          .attr('class', 'nv-subtitle')
 
2293
          .attr('dy', '1em')
 
2294
          .text(function(d) { return d.subtitle; });
 
2295
 
 
2296
 
 
2297
 
 
2298
      bullet
 
2299
        .width(availableWidth)
 
2300
        .height(availableHeight)
 
2301
 
 
2302
      var bulletWrap = g.select('.nv-bulletWrap');
 
2303
 
 
2304
      d3.transition(bulletWrap).call(bullet);
 
2305
 
 
2306
 
 
2307
 
 
2308
      // Compute the tick format.
 
2309
      var format = tickFormat || x1.tickFormat( availableWidth / 100 );
 
2310
 
 
2311
      // Update the tick groups.
 
2312
      var tick = g.selectAll('g.nv-tick')
 
2313
          .data(x1.ticks( availableWidth / 50 ), function(d) {
 
2314
            return this.textContent || format(d);
 
2315
          });
 
2316
 
 
2317
      // Initialize the ticks with the old scale, x0.
 
2318
      var tickEnter = tick.enter().append('g')
 
2319
          .attr('class', 'nv-tick')
 
2320
          .attr('transform', function(d) { return 'translate(' + x0(d) + ',0)' })
 
2321
          .style('opacity', 1e-6);
 
2322
 
 
2323
      tickEnter.append('line')
 
2324
          .attr('y1', availableHeight)
 
2325
          .attr('y2', availableHeight * 7 / 6);
 
2326
 
 
2327
      tickEnter.append('text')
 
2328
          .attr('text-anchor', 'middle')
 
2329
          .attr('dy', '1em')
 
2330
          .attr('y', availableHeight * 7 / 6)
 
2331
          .text(format);
 
2332
 
 
2333
 
 
2334
      // Transition the updating ticks to the new scale, x1.
 
2335
      var tickUpdate = d3.transition(tick)
 
2336
          .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
 
2337
          .style('opacity', 1);
 
2338
 
 
2339
      tickUpdate.select('line')
 
2340
          .attr('y1', availableHeight)
 
2341
          .attr('y2', availableHeight * 7 / 6);
 
2342
 
 
2343
      tickUpdate.select('text')
 
2344
          .attr('y', availableHeight * 7 / 6);
 
2345
 
 
2346
      // Transition the exiting ticks to the new scale, x1.
 
2347
      d3.transition(tick.exit())
 
2348
          .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
 
2349
          .style('opacity', 1e-6)
 
2350
          .remove();
 
2351
 
 
2352
 
 
2353
      //============================================================
 
2354
      // Event Handling/Dispatching (in chart's scope)
 
2355
      //------------------------------------------------------------
 
2356
 
 
2357
      dispatch.on('tooltipShow', function(e) {
 
2358
        e.key = d.title;
 
2359
        if (tooltips) showTooltip(e, that.parentNode);
 
2360
      });
 
2361
 
 
2362
      //============================================================
 
2363
 
 
2364
    });
 
2365
 
 
2366
    d3.timer.flush();
 
2367
 
 
2368
    return chart;
 
2369
  }
 
2370
 
 
2371
 
 
2372
  //============================================================
 
2373
  // Event Handling/Dispatching (out of chart's scope)
 
2374
  //------------------------------------------------------------
 
2375
 
 
2376
  bullet.dispatch.on('elementMouseover.tooltip', function(e) {
 
2377
    dispatch.tooltipShow(e);
 
2378
  });
 
2379
 
 
2380
  bullet.dispatch.on('elementMouseout.tooltip', function(e) {
 
2381
    dispatch.tooltipHide(e);
 
2382
  });
 
2383
 
 
2384
  dispatch.on('tooltipHide', function() {
 
2385
    if (tooltips) nv.tooltip.cleanup();
 
2386
  });
 
2387
 
 
2388
  //============================================================
 
2389
 
 
2390
 
 
2391
  //============================================================
 
2392
  // Expose Public Variables
 
2393
  //------------------------------------------------------------
 
2394
 
 
2395
  chart.dispatch = dispatch;
 
2396
  chart.bullet = bullet;
 
2397
 
 
2398
  d3.rebind(chart, bullet, 'color');
 
2399
 
 
2400
  chart.options = nv.utils.optionsFunc.bind(chart);
 
2401
  
 
2402
  // left, right, top, bottom
 
2403
  chart.orient = function(x) {
 
2404
    if (!arguments.length) return orient;
 
2405
    orient = x;
 
2406
    reverse = orient == 'right' || orient == 'bottom';
 
2407
    return chart;
 
2408
  };
 
2409
 
 
2410
  // ranges (bad, satisfactory, good)
 
2411
  chart.ranges = function(x) {
 
2412
    if (!arguments.length) return ranges;
 
2413
    ranges = x;
 
2414
    return chart;
 
2415
  };
 
2416
 
 
2417
  // markers (previous, goal)
 
2418
  chart.markers = function(x) {
 
2419
    if (!arguments.length) return markers;
 
2420
    markers = x;
 
2421
    return chart;
 
2422
  };
 
2423
 
 
2424
  // measures (actual, forecast)
 
2425
  chart.measures = function(x) {
 
2426
    if (!arguments.length) return measures;
 
2427
    measures = x;
 
2428
    return chart;
 
2429
  };
 
2430
 
 
2431
  chart.width = function(x) {
 
2432
    if (!arguments.length) return width;
 
2433
    width = x;
 
2434
    return chart;
 
2435
  };
 
2436
 
 
2437
  chart.height = function(x) {
 
2438
    if (!arguments.length) return height;
 
2439
    height = x;
 
2440
    return chart;
 
2441
  };
 
2442
 
 
2443
  chart.margin = function(_) {
 
2444
    if (!arguments.length) return margin;
 
2445
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
2446
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
2447
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
2448
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
2449
    return chart;
 
2450
  };
 
2451
 
 
2452
  chart.tickFormat = function(x) {
 
2453
    if (!arguments.length) return tickFormat;
 
2454
    tickFormat = x;
 
2455
    return chart;
 
2456
  };
 
2457
 
 
2458
  chart.tooltips = function(_) {
 
2459
    if (!arguments.length) return tooltips;
 
2460
    tooltips = _;
 
2461
    return chart;
 
2462
  };
 
2463
 
 
2464
  chart.tooltipContent = function(_) {
 
2465
    if (!arguments.length) return tooltip;
 
2466
    tooltip = _;
 
2467
    return chart;
 
2468
  };
 
2469
 
 
2470
  chart.noData = function(_) {
 
2471
    if (!arguments.length) return noData;
 
2472
    noData = _;
 
2473
    return chart;
 
2474
  };
 
2475
 
 
2476
  //============================================================
 
2477
 
 
2478
 
 
2479
  return chart;
 
2480
};
 
2481
 
 
2482
 
 
2483
 
 
2484
nv.models.cumulativeLineChart = function() {
 
2485
  "use strict";
 
2486
  //============================================================
 
2487
  // Public Variables with Default Settings
 
2488
  //------------------------------------------------------------
 
2489
 
 
2490
  var lines = nv.models.line()
 
2491
    , xAxis = nv.models.axis()
 
2492
    , yAxis = nv.models.axis()
 
2493
    , legend = nv.models.legend()
 
2494
    , controls = nv.models.legend()
 
2495
    , interactiveLayer = nv.interactiveGuideline()
 
2496
    ;
 
2497
 
 
2498
  var margin = {top: 30, right: 30, bottom: 50, left: 60}
 
2499
    , color = nv.utils.defaultColor()
 
2500
    , width = null
 
2501
    , height = null
 
2502
    , showLegend = true
 
2503
    , showXAxis = true
 
2504
    , showYAxis = true
 
2505
    , rightAlignYAxis = false
 
2506
    , tooltips = true
 
2507
    , showControls = true
 
2508
    , useInteractiveGuideline = false
 
2509
    , rescaleY = true
 
2510
    , tooltip = function(key, x, y, e, graph) {
 
2511
        return '<h3>' + key + '</h3>' +
 
2512
               '<p>' +  y + ' at ' + x + '</p>'
 
2513
      }
 
2514
    , x //can be accessed via chart.xScale()
 
2515
    , y //can be accessed via chart.yScale()
 
2516
    , id = lines.id()
 
2517
    , state = { index: 0, rescaleY: rescaleY }
 
2518
    , defaultState = null
 
2519
    , noData = 'No Data Available.'
 
2520
    , average = function(d) { return d.average }
 
2521
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
 
2522
    , transitionDuration = 250
 
2523
    , noErrorCheck = false  //if set to TRUE, will bypass an error check in the indexify function.
 
2524
    ;
 
2525
 
 
2526
  xAxis
 
2527
    .orient('bottom')
 
2528
    .tickPadding(7)
 
2529
    ;
 
2530
  yAxis
 
2531
    .orient((rightAlignYAxis) ? 'right' : 'left')
 
2532
    ;
 
2533
 
 
2534
  //============================================================
 
2535
  controls.updateState(false);
 
2536
 
 
2537
  //============================================================
 
2538
  // Private Variables
 
2539
  //------------------------------------------------------------
 
2540
 
 
2541
   var dx = d3.scale.linear()
 
2542
     , index = {i: 0, x: 0}
 
2543
     ;
 
2544
 
 
2545
  var showTooltip = function(e, offsetElement) {
 
2546
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
2547
        top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
2548
        x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
 
2549
        y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
 
2550
        content = tooltip(e.series.key, x, y, e, chart);
 
2551
 
 
2552
    nv.tooltip.show([left, top], content, null, null, offsetElement);
 
2553
  };
 
2554
 
 
2555
  //============================================================
 
2556
 
 
2557
  function chart(selection) {
 
2558
    selection.each(function(data) {
 
2559
      var container = d3.select(this).classed('nv-chart-' + id, true),
 
2560
          that = this;
 
2561
 
 
2562
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
2563
                             - margin.left - margin.right,
 
2564
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
2565
                             - margin.top - margin.bottom;
 
2566
 
 
2567
 
 
2568
      chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
 
2569
      chart.container = this;
 
2570
 
 
2571
      //set state.disabled
 
2572
      state.disabled = data.map(function(d) { return !!d.disabled });
 
2573
 
 
2574
      if (!defaultState) {
 
2575
        var key;
 
2576
        defaultState = {};
 
2577
        for (key in state) {
 
2578
          if (state[key] instanceof Array)
 
2579
            defaultState[key] = state[key].slice(0);
 
2580
          else
 
2581
            defaultState[key] = state[key];
 
2582
        }
 
2583
      }
 
2584
 
 
2585
      var indexDrag = d3.behavior.drag()
 
2586
                        .on('dragstart', dragStart)
 
2587
                        .on('drag', dragMove)
 
2588
                        .on('dragend', dragEnd);
 
2589
 
 
2590
 
 
2591
      function dragStart(d,i) {
 
2592
        d3.select(chart.container)
 
2593
            .style('cursor', 'ew-resize');
 
2594
      }
 
2595
 
 
2596
      function dragMove(d,i) {
 
2597
        index.x = d3.event.x;
 
2598
        index.i = Math.round(dx.invert(index.x));
 
2599
        updateZero();
 
2600
      }
 
2601
 
 
2602
      function dragEnd(d,i) {
 
2603
        d3.select(chart.container)
 
2604
            .style('cursor', 'auto');
 
2605
 
 
2606
        // update state and send stateChange with new index
 
2607
        state.index = index.i;
 
2608
        dispatch.stateChange(state);
 
2609
      }
 
2610
 
 
2611
      //------------------------------------------------------------
 
2612
      // Display No Data message if there's nothing to show.
 
2613
 
 
2614
      if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
 
2615
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
2616
 
 
2617
        noDataText.enter().append('text')
 
2618
          .attr('class', 'nvd3 nv-noData')
 
2619
          .attr('dy', '-.7em')
 
2620
          .style('text-anchor', 'middle');
 
2621
 
 
2622
        noDataText
 
2623
          .attr('x', margin.left + availableWidth / 2)
 
2624
          .attr('y', margin.top + availableHeight / 2)
 
2625
          .text(function(d) { return d });
 
2626
 
 
2627
        return chart;
 
2628
      } else {
 
2629
        container.selectAll('.nv-noData').remove();
 
2630
      }
 
2631
 
 
2632
      //------------------------------------------------------------
 
2633
 
 
2634
 
 
2635
      //------------------------------------------------------------
 
2636
      // Setup Scales
 
2637
 
 
2638
      x = lines.xScale();
 
2639
      y = lines.yScale();
 
2640
 
 
2641
 
 
2642
      if (!rescaleY) {
 
2643
        var seriesDomains = data
 
2644
          .filter(function(series) { return !series.disabled })
 
2645
          .map(function(series,i) {
 
2646
            var initialDomain = d3.extent(series.values, lines.y());
 
2647
 
 
2648
            //account for series being disabled when losing 95% or more
 
2649
            if (initialDomain[0] < -.95) initialDomain[0] = -.95;
 
2650
 
 
2651
            return [
 
2652
              (initialDomain[0] - initialDomain[1]) / (1 + initialDomain[1]),
 
2653
              (initialDomain[1] - initialDomain[0]) / (1 + initialDomain[0])
 
2654
            ];
 
2655
          });
 
2656
 
 
2657
        var completeDomain = [
 
2658
          d3.min(seriesDomains, function(d) { return d[0] }),
 
2659
          d3.max(seriesDomains, function(d) { return d[1] })
 
2660
        ]
 
2661
 
 
2662
        lines.yDomain(completeDomain);
 
2663
      } else {
 
2664
        lines.yDomain(null);
 
2665
      }
 
2666
 
 
2667
 
 
2668
      dx  .domain([0, data[0].values.length - 1]) //Assumes all series have same length
 
2669
          .range([0, availableWidth])
 
2670
          .clamp(true);
 
2671
 
 
2672
      //------------------------------------------------------------
 
2673
 
 
2674
 
 
2675
      var data = indexify(index.i, data);
 
2676
 
 
2677
 
 
2678
      //------------------------------------------------------------
 
2679
      // Setup containers and skeleton of chart
 
2680
      var interactivePointerEvents = (useInteractiveGuideline) ? "none" : "all";
 
2681
      var wrap = container.selectAll('g.nv-wrap.nv-cumulativeLine').data([data]);
 
2682
      var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-cumulativeLine').append('g');
 
2683
      var g = wrap.select('g');
 
2684
 
 
2685
      gEnter.append('g').attr('class', 'nv-interactive');
 
2686
      gEnter.append('g').attr('class', 'nv-x nv-axis').style("pointer-events","none");
 
2687
      gEnter.append('g').attr('class', 'nv-y nv-axis');
 
2688
      gEnter.append('g').attr('class', 'nv-background');
 
2689
      gEnter.append('g').attr('class', 'nv-linesWrap').style("pointer-events",interactivePointerEvents);
 
2690
      gEnter.append('g').attr('class', 'nv-avgLinesWrap').style("pointer-events","none");
 
2691
      gEnter.append('g').attr('class', 'nv-legendWrap');
 
2692
      gEnter.append('g').attr('class', 'nv-controlsWrap');
 
2693
 
 
2694
 
 
2695
      //------------------------------------------------------------
 
2696
      // Legend
 
2697
 
 
2698
      if (showLegend) {
 
2699
        legend.width(availableWidth);
 
2700
 
 
2701
        g.select('.nv-legendWrap')
 
2702
            .datum(data)
 
2703
            .call(legend);
 
2704
 
 
2705
        if ( margin.top != legend.height()) {
 
2706
          margin.top = legend.height();
 
2707
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
2708
                             - margin.top - margin.bottom;
 
2709
        }
 
2710
 
 
2711
        g.select('.nv-legendWrap')
 
2712
            .attr('transform', 'translate(0,' + (-margin.top) +')')
 
2713
      }
 
2714
 
 
2715
      //------------------------------------------------------------
 
2716
 
 
2717
 
 
2718
      //------------------------------------------------------------
 
2719
      // Controls
 
2720
 
 
2721
      if (showControls) {
 
2722
        var controlsData = [
 
2723
          { key: 'Re-scale y-axis', disabled: !rescaleY }
 
2724
        ];
 
2725
 
 
2726
        controls
 
2727
            .width(140)
 
2728
            .color(['#444', '#444', '#444'])
 
2729
            .rightAlign(false)
 
2730
            .margin({top: 5, right: 0, bottom: 5, left: 20})
 
2731
            ;
 
2732
 
 
2733
        g.select('.nv-controlsWrap')
 
2734
            .datum(controlsData)
 
2735
            .attr('transform', 'translate(0,' + (-margin.top) +')')
 
2736
            .call(controls);
 
2737
      }
 
2738
 
 
2739
      //------------------------------------------------------------
 
2740
 
 
2741
 
 
2742
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
2743
 
 
2744
      if (rightAlignYAxis) {
 
2745
          g.select(".nv-y.nv-axis")
 
2746
              .attr("transform", "translate(" + availableWidth + ",0)");
 
2747
      }
 
2748
 
 
2749
      // Show error if series goes below 100%
 
2750
      var tempDisabled = data.filter(function(d) { return d.tempDisabled });
 
2751
 
 
2752
      wrap.select('.tempDisabled').remove(); //clean-up and prevent duplicates
 
2753
      if (tempDisabled.length) {
 
2754
        wrap.append('text').attr('class', 'tempDisabled')
 
2755
            .attr('x', availableWidth / 2)
 
2756
            .attr('y', '-.71em')
 
2757
            .style('text-anchor', 'end')
 
2758
            .text(tempDisabled.map(function(d) { return d.key }).join(', ') + ' values cannot be calculated for this time period.');
 
2759
      }
 
2760
 
 
2761
      //------------------------------------------------------------
 
2762
      // Main Chart Component(s)
 
2763
 
 
2764
      //------------------------------------------------------------
 
2765
      //Set up interactive layer
 
2766
      if (useInteractiveGuideline) {
 
2767
        interactiveLayer
 
2768
          .width(availableWidth)
 
2769
          .height(availableHeight)
 
2770
          .margin({left:margin.left,top:margin.top})
 
2771
          .svgContainer(container)
 
2772
          .xScale(x);
 
2773
        wrap.select(".nv-interactive").call(interactiveLayer);
 
2774
      }
 
2775
 
 
2776
      gEnter.select('.nv-background')
 
2777
        .append('rect');
 
2778
 
 
2779
      g.select('.nv-background rect')
 
2780
          .attr('width', availableWidth)
 
2781
          .attr('height', availableHeight);
 
2782
 
 
2783
      lines
 
2784
        //.x(function(d) { return d.x })
 
2785
        .y(function(d) { return d.display.y })
 
2786
        .width(availableWidth)
 
2787
        .height(availableHeight)
 
2788
        .color(data.map(function(d,i) {
 
2789
          return d.color || color(d, i);
 
2790
        }).filter(function(d,i) { return !data[i].disabled && !data[i].tempDisabled; }));
 
2791
 
 
2792
 
 
2793
 
 
2794
      var linesWrap = g.select('.nv-linesWrap')
 
2795
          .datum(data.filter(function(d) { return  !d.disabled && !d.tempDisabled }));
 
2796
 
 
2797
      //d3.transition(linesWrap).call(lines);
 
2798
      linesWrap.call(lines);
 
2799
 
 
2800
      /*Handle average lines [AN-612] ----------------------------*/
 
2801
 
 
2802
      //Store a series index number in the data array.
 
2803
      data.forEach(function(d,i) {
 
2804
            d.seriesIndex = i;
 
2805
      });
 
2806
 
 
2807
      var avgLineData = data.filter(function(d) {
 
2808
          return !d.disabled && !!average(d);
 
2809
      });
 
2810
 
 
2811
      var avgLines = g.select(".nv-avgLinesWrap").selectAll("line")
 
2812
              .data(avgLineData, function(d) { return d.key; });
 
2813
 
 
2814
      var getAvgLineY = function(d) {
 
2815
          //If average lines go off the svg element, clamp them to the svg bounds.
 
2816
          var yVal = y(average(d));
 
2817
          if (yVal < 0) return 0;
 
2818
          if (yVal > availableHeight) return availableHeight;
 
2819
          return yVal;
 
2820
      };
 
2821
 
 
2822
      avgLines.enter()
 
2823
              .append('line')
 
2824
              .style('stroke-width',2)
 
2825
              .style('stroke-dasharray','10,10')
 
2826
              .style('stroke',function (d,i) {
 
2827
                  return lines.color()(d,d.seriesIndex);
 
2828
              })
 
2829
              .attr('x1',0)
 
2830
              .attr('x2',availableWidth)
 
2831
              .attr('y1', getAvgLineY)
 
2832
              .attr('y2', getAvgLineY);
 
2833
 
 
2834
      avgLines
 
2835
              .style('stroke-opacity',function(d){
 
2836
                  //If average lines go offscreen, make them transparent
 
2837
                  var yVal = y(average(d));
 
2838
                  if (yVal < 0 || yVal > availableHeight) return 0;
 
2839
                  return 1;
 
2840
              })
 
2841
              .attr('x1',0)
 
2842
              .attr('x2',availableWidth)
 
2843
              .attr('y1', getAvgLineY)
 
2844
              .attr('y2', getAvgLineY);
 
2845
 
 
2846
      avgLines.exit().remove();
 
2847
 
 
2848
      //Create index line -----------------------------------------
 
2849
 
 
2850
      var indexLine = linesWrap.selectAll('.nv-indexLine')
 
2851
          .data([index]);
 
2852
      indexLine.enter().append('rect').attr('class', 'nv-indexLine')
 
2853
          .attr('width', 3)
 
2854
          .attr('x', -2)
 
2855
          .attr('fill', 'red')
 
2856
          .attr('fill-opacity', .5)
 
2857
          .style("pointer-events","all")
 
2858
          .call(indexDrag)
 
2859
 
 
2860
      indexLine
 
2861
          .attr('transform', function(d) { return 'translate(' + dx(d.i) + ',0)' })
 
2862
          .attr('height', availableHeight)
 
2863
 
 
2864
      //------------------------------------------------------------
 
2865
 
 
2866
 
 
2867
      //------------------------------------------------------------
 
2868
      // Setup Axes
 
2869
 
 
2870
      if (showXAxis) {
 
2871
        xAxis
 
2872
          .scale(x)
 
2873
          //Suggest how many ticks based on the chart width and D3 should listen (70 is the optimal number for MM/DD/YY dates)
 
2874
          .ticks( Math.min(data[0].values.length,availableWidth/70) )
 
2875
          .tickSize(-availableHeight, 0);
 
2876
 
 
2877
        g.select('.nv-x.nv-axis')
 
2878
            .attr('transform', 'translate(0,' + y.range()[0] + ')');
 
2879
        d3.transition(g.select('.nv-x.nv-axis'))
 
2880
            .call(xAxis);
 
2881
      }
 
2882
 
 
2883
 
 
2884
      if (showYAxis) {
 
2885
        yAxis
 
2886
          .scale(y)
 
2887
          .ticks( availableHeight / 36 )
 
2888
          .tickSize( -availableWidth, 0);
 
2889
 
 
2890
        d3.transition(g.select('.nv-y.nv-axis'))
 
2891
            .call(yAxis);
 
2892
      }
 
2893
      //------------------------------------------------------------
 
2894
 
 
2895
 
 
2896
      //============================================================
 
2897
      // Event Handling/Dispatching (in chart's scope)
 
2898
      //------------------------------------------------------------
 
2899
 
 
2900
 
 
2901
      function updateZero() {
 
2902
        indexLine
 
2903
          .data([index]);
 
2904
 
 
2905
        //When dragging the index line, turn off line transitions.
 
2906
        // Then turn them back on when done dragging.
 
2907
        var oldDuration = chart.transitionDuration();
 
2908
        chart.transitionDuration(0);
 
2909
        chart.update();
 
2910
        chart.transitionDuration(oldDuration);
 
2911
      }
 
2912
 
 
2913
      g.select('.nv-background rect')
 
2914
          .on('click', function() {
 
2915
            index.x = d3.mouse(this)[0];
 
2916
            index.i = Math.round(dx.invert(index.x));
 
2917
 
 
2918
            // update state and send stateChange with new index
 
2919
            state.index = index.i;
 
2920
            dispatch.stateChange(state);
 
2921
 
 
2922
            updateZero();
 
2923
          });
 
2924
 
 
2925
      lines.dispatch.on('elementClick', function(e) {
 
2926
        index.i = e.pointIndex;
 
2927
        index.x = dx(index.i);
 
2928
 
 
2929
        // update state and send stateChange with new index
 
2930
        state.index = index.i;
 
2931
        dispatch.stateChange(state);
 
2932
 
 
2933
        updateZero();
 
2934
      });
 
2935
 
 
2936
      controls.dispatch.on('legendClick', function(d,i) {
 
2937
        d.disabled = !d.disabled;
 
2938
        rescaleY = !d.disabled;
 
2939
 
 
2940
        state.rescaleY = rescaleY;
 
2941
        dispatch.stateChange(state);
 
2942
        chart.update();
 
2943
      });
 
2944
 
 
2945
 
 
2946
      legend.dispatch.on('stateChange', function(newState) {
 
2947
        state.disabled = newState.disabled;
 
2948
        dispatch.stateChange(state);
 
2949
        chart.update();
 
2950
      });
 
2951
 
 
2952
      interactiveLayer.dispatch.on('elementMousemove', function(e) {
 
2953
          lines.clearHighlights();
 
2954
          var singlePoint, pointIndex, pointXLocation, allData = [];
 
2955
 
 
2956
 
 
2957
          data
 
2958
          .filter(function(series, i) {
 
2959
            series.seriesIndex = i;
 
2960
            return !series.disabled;
 
2961
          })
 
2962
          .forEach(function(series,i) {
 
2963
              pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
 
2964
              lines.highlightPoint(i, pointIndex, true);
 
2965
              var point = series.values[pointIndex];
 
2966
              if (typeof point === 'undefined') return;
 
2967
              if (typeof singlePoint === 'undefined') singlePoint = point;
 
2968
              if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
 
2969
              allData.push({
 
2970
                  key: series.key,
 
2971
                  value: chart.y()(point, pointIndex),
 
2972
                  color: color(series,series.seriesIndex)
 
2973
              });
 
2974
          });
 
2975
 
 
2976
          //Highlight the tooltip entry based on which point the mouse is closest to.
 
2977
          if (allData.length > 2) {
 
2978
            var yValue = chart.yScale().invert(e.mouseY);
 
2979
            var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]);
 
2980
            var threshold = 0.03 * domainExtent;
 
2981
            var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value}),yValue,threshold);
 
2982
            if (indexToHighlight !== null)
 
2983
              allData[indexToHighlight].highlight = true;
 
2984
          }
 
2985
 
 
2986
          var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex), pointIndex);
 
2987
          interactiveLayer.tooltip
 
2988
                  .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
 
2989
                  .chartContainer(that.parentNode)
 
2990
                  .enabled(tooltips)
 
2991
                  .valueFormatter(function(d,i) {
 
2992
                     return yAxis.tickFormat()(d);
 
2993
                  })
 
2994
                  .data(
 
2995
                      {
 
2996
                        value: xValue,
 
2997
                        series: allData
 
2998
                      }
 
2999
                  )();
 
3000
 
 
3001
          interactiveLayer.renderGuideLine(pointXLocation);
 
3002
 
 
3003
      });
 
3004
 
 
3005
      interactiveLayer.dispatch.on("elementMouseout",function(e) {
 
3006
          dispatch.tooltipHide();
 
3007
          lines.clearHighlights();
 
3008
      });
 
3009
 
 
3010
      dispatch.on('tooltipShow', function(e) {
 
3011
        if (tooltips) showTooltip(e, that.parentNode);
 
3012
      });
 
3013
 
 
3014
 
 
3015
      // Update chart from a state object passed to event handler
 
3016
      dispatch.on('changeState', function(e) {
 
3017
 
 
3018
        if (typeof e.disabled !== 'undefined') {
 
3019
          data.forEach(function(series,i) {
 
3020
            series.disabled = e.disabled[i];
 
3021
          });
 
3022
 
 
3023
          state.disabled = e.disabled;
 
3024
        }
 
3025
 
 
3026
 
 
3027
        if (typeof e.index !== 'undefined') {
 
3028
          index.i = e.index;
 
3029
          index.x = dx(index.i);
 
3030
 
 
3031
          state.index = e.index;
 
3032
 
 
3033
          indexLine
 
3034
            .data([index]);
 
3035
        }
 
3036
 
 
3037
 
 
3038
        if (typeof e.rescaleY !== 'undefined') {
 
3039
          rescaleY = e.rescaleY;
 
3040
        }
 
3041
 
 
3042
        chart.update();
 
3043
      });
 
3044
 
 
3045
      //============================================================
 
3046
 
 
3047
    });
 
3048
 
 
3049
    return chart;
 
3050
  }
 
3051
 
 
3052
 
 
3053
  //============================================================
 
3054
  // Event Handling/Dispatching (out of chart's scope)
 
3055
  //------------------------------------------------------------
 
3056
 
 
3057
  lines.dispatch.on('elementMouseover.tooltip', function(e) {
 
3058
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
3059
    dispatch.tooltipShow(e);
 
3060
  });
 
3061
 
 
3062
  lines.dispatch.on('elementMouseout.tooltip', function(e) {
 
3063
    dispatch.tooltipHide(e);
 
3064
  });
 
3065
 
 
3066
  dispatch.on('tooltipHide', function() {
 
3067
    if (tooltips) nv.tooltip.cleanup();
 
3068
  });
 
3069
 
 
3070
  //============================================================
 
3071
 
 
3072
 
 
3073
  //============================================================
 
3074
  // Expose Public Variables
 
3075
  //------------------------------------------------------------
 
3076
 
 
3077
  // expose chart's sub-components
 
3078
  chart.dispatch = dispatch;
 
3079
  chart.lines = lines;
 
3080
  chart.legend = legend;
 
3081
  chart.xAxis = xAxis;
 
3082
  chart.yAxis = yAxis;
 
3083
  chart.interactiveLayer = interactiveLayer;
 
3084
 
 
3085
  d3.rebind(chart, lines, 'defined', 'isArea', 'x', 'y', 'xScale','yScale', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi','useVoronoi',  'id');
 
3086
 
 
3087
  chart.options = nv.utils.optionsFunc.bind(chart);
 
3088
 
 
3089
  chart.margin = function(_) {
 
3090
    if (!arguments.length) return margin;
 
3091
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
3092
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
3093
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
3094
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
3095
    return chart;
 
3096
  };
 
3097
 
 
3098
  chart.width = function(_) {
 
3099
    if (!arguments.length) return width;
 
3100
    width = _;
 
3101
    return chart;
 
3102
  };
 
3103
 
 
3104
  chart.height = function(_) {
 
3105
    if (!arguments.length) return height;
 
3106
    height = _;
 
3107
    return chart;
 
3108
  };
 
3109
 
 
3110
  chart.color = function(_) {
 
3111
    if (!arguments.length) return color;
 
3112
    color = nv.utils.getColor(_);
 
3113
    legend.color(color);
 
3114
    return chart;
 
3115
  };
 
3116
 
 
3117
  chart.rescaleY = function(_) {
 
3118
    if (!arguments.length) return rescaleY;
 
3119
    rescaleY = _;
 
3120
    return chart;
 
3121
  };
 
3122
 
 
3123
  chart.showControls = function(_) {
 
3124
    if (!arguments.length) return showControls;
 
3125
    showControls = _;
 
3126
    return chart;
 
3127
  };
 
3128
 
 
3129
  chart.useInteractiveGuideline = function(_) {
 
3130
    if(!arguments.length) return useInteractiveGuideline;
 
3131
    useInteractiveGuideline = _;
 
3132
    if (_ === true) {
 
3133
       chart.interactive(false);
 
3134
       chart.useVoronoi(false);
 
3135
    }
 
3136
    return chart;
 
3137
  };
 
3138
 
 
3139
  chart.showLegend = function(_) {
 
3140
    if (!arguments.length) return showLegend;
 
3141
    showLegend = _;
 
3142
    return chart;
 
3143
  };
 
3144
 
 
3145
  chart.showXAxis = function(_) {
 
3146
    if (!arguments.length) return showXAxis;
 
3147
    showXAxis = _;
 
3148
    return chart;
 
3149
  };
 
3150
 
 
3151
  chart.showYAxis = function(_) {
 
3152
    if (!arguments.length) return showYAxis;
 
3153
    showYAxis = _;
 
3154
    return chart;
 
3155
  };
 
3156
 
 
3157
  chart.rightAlignYAxis = function(_) {
 
3158
    if(!arguments.length) return rightAlignYAxis;
 
3159
    rightAlignYAxis = _;
 
3160
    yAxis.orient( (_) ? 'right' : 'left');
 
3161
    return chart;
 
3162
  };
 
3163
 
 
3164
  chart.tooltips = function(_) {
 
3165
    if (!arguments.length) return tooltips;
 
3166
    tooltips = _;
 
3167
    return chart;
 
3168
  };
 
3169
 
 
3170
  chart.tooltipContent = function(_) {
 
3171
    if (!arguments.length) return tooltip;
 
3172
    tooltip = _;
 
3173
    return chart;
 
3174
  };
 
3175
 
 
3176
  chart.state = function(_) {
 
3177
    if (!arguments.length) return state;
 
3178
    state = _;
 
3179
    return chart;
 
3180
  };
 
3181
 
 
3182
  chart.defaultState = function(_) {
 
3183
    if (!arguments.length) return defaultState;
 
3184
    defaultState = _;
 
3185
    return chart;
 
3186
  };
 
3187
 
 
3188
  chart.noData = function(_) {
 
3189
    if (!arguments.length) return noData;
 
3190
    noData = _;
 
3191
    return chart;
 
3192
  };
 
3193
 
 
3194
  chart.average = function(_) {
 
3195
     if(!arguments.length) return average;
 
3196
     average = _;
 
3197
     return chart;
 
3198
  };
 
3199
 
 
3200
  chart.transitionDuration = function(_) {
 
3201
    if (!arguments.length) return transitionDuration;
 
3202
    transitionDuration = _;
 
3203
    return chart;
 
3204
  };
 
3205
 
 
3206
  chart.noErrorCheck = function(_) {
 
3207
    if (!arguments.length) return noErrorCheck;
 
3208
    noErrorCheck = _;
 
3209
    return chart;
 
3210
  };
 
3211
 
 
3212
  //============================================================
 
3213
 
 
3214
 
 
3215
  //============================================================
 
3216
  // Functions
 
3217
  //------------------------------------------------------------
 
3218
 
 
3219
  /* Normalize the data according to an index point. */
 
3220
  function indexify(idx, data) {
 
3221
    return data.map(function(line, i) {
 
3222
      if (!line.values) {
 
3223
         return line;
 
3224
      }
 
3225
      var indexValue = line.values[idx];
 
3226
      if (indexValue == null) {
 
3227
        return line;
 
3228
      }
 
3229
      var v = lines.y()(indexValue, idx);
 
3230
 
 
3231
      //TODO: implement check below, and disable series if series loses 100% or more cause divide by 0 issue
 
3232
      if (v < -.95 && !noErrorCheck) {
 
3233
        //if a series loses more than 100%, calculations fail.. anything close can cause major distortion (but is mathematically correct till it hits 100)
 
3234
 
 
3235
        line.tempDisabled = true;
 
3236
        return line;
 
3237
      }
 
3238
 
 
3239
      line.tempDisabled = false;
 
3240
 
 
3241
      line.values = line.values.map(function(point, pointIndex) {
 
3242
        point.display = {'y': (lines.y()(point, pointIndex) - v) / (1 + v) };
 
3243
        return point;
 
3244
      })
 
3245
 
 
3246
      return line;
 
3247
    })
 
3248
  }
 
3249
 
 
3250
  //============================================================
 
3251
 
 
3252
 
 
3253
  return chart;
 
3254
}
 
3255
//TODO: consider deprecating by adding necessary features to multiBar model
 
3256
nv.models.discreteBar = function() {
 
3257
  "use strict";
 
3258
  //============================================================
 
3259
  // Public Variables with Default Settings
 
3260
  //------------------------------------------------------------
 
3261
 
 
3262
  var margin = {top: 0, right: 0, bottom: 0, left: 0}
 
3263
    , width = 960
 
3264
    , height = 500
 
3265
    , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
 
3266
    , x = d3.scale.ordinal()
 
3267
    , y = d3.scale.linear()
 
3268
    , getX = function(d) { return d.x }
 
3269
    , getY = function(d) { return d.y }
 
3270
    , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
 
3271
    , color = nv.utils.defaultColor()
 
3272
    , showValues = false
 
3273
    , valueFormat = d3.format(',.2f')
 
3274
    , xDomain
 
3275
    , yDomain
 
3276
    , xRange
 
3277
    , yRange
 
3278
    , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
 
3279
    , rectClass = 'discreteBar'
 
3280
    ;
 
3281
 
 
3282
  //============================================================
 
3283
 
 
3284
 
 
3285
  //============================================================
 
3286
  // Private Variables
 
3287
  //------------------------------------------------------------
 
3288
 
 
3289
  var x0, y0;
 
3290
 
 
3291
  //============================================================
 
3292
 
 
3293
 
 
3294
  function chart(selection) {
 
3295
    selection.each(function(data) {
 
3296
      var availableWidth = width - margin.left - margin.right,
 
3297
          availableHeight = height - margin.top - margin.bottom,
 
3298
          container = d3.select(this);
 
3299
 
 
3300
 
 
3301
      //add series index to each data point for reference
 
3302
      data.forEach(function(series, i) {
 
3303
        series.values.forEach(function(point) {
 
3304
          point.series = i;
 
3305
        });
 
3306
      });
 
3307
 
 
3308
 
 
3309
      //------------------------------------------------------------
 
3310
      // Setup Scales
 
3311
 
 
3312
      // remap and flatten the data for use in calculating the scales' domains
 
3313
      var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
 
3314
            data.map(function(d) {
 
3315
              return d.values.map(function(d,i) {
 
3316
                return { x: getX(d,i), y: getY(d,i), y0: d.y0 }
 
3317
              })
 
3318
            });
 
3319
 
 
3320
      x   .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
 
3321
          .rangeBands(xRange || [0, availableWidth], .1);
 
3322
 
 
3323
      y   .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y }).concat(forceY)));
 
3324
 
 
3325
 
 
3326
      // If showValues, pad the Y axis range to account for label height
 
3327
      if (showValues) y.range(yRange || [availableHeight - (y.domain()[0] < 0 ? 12 : 0), y.domain()[1] > 0 ? 12 : 0]);
 
3328
      else y.range(yRange || [availableHeight, 0]);
 
3329
 
 
3330
      //store old scales if they exist
 
3331
      x0 = x0 || x;
 
3332
      y0 = y0 || y.copy().range([y(0),y(0)]);
 
3333
 
 
3334
      //------------------------------------------------------------
 
3335
 
 
3336
 
 
3337
      //------------------------------------------------------------
 
3338
      // Setup containers and skeleton of chart
 
3339
 
 
3340
      var wrap = container.selectAll('g.nv-wrap.nv-discretebar').data([data]);
 
3341
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-discretebar');
 
3342
      var gEnter = wrapEnter.append('g');
 
3343
      var g = wrap.select('g');
 
3344
 
 
3345
      gEnter.append('g').attr('class', 'nv-groups');
 
3346
 
 
3347
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
3348
 
 
3349
      //------------------------------------------------------------
 
3350
 
 
3351
 
 
3352
 
 
3353
      //TODO: by definition, the discrete bar should not have multiple groups, will modify/remove later
 
3354
      var groups = wrap.select('.nv-groups').selectAll('.nv-group')
 
3355
          .data(function(d) { return d }, function(d) { return d.key });
 
3356
      groups.enter().append('g')
 
3357
          .style('stroke-opacity', 1e-6)
 
3358
          .style('fill-opacity', 1e-6);
 
3359
      groups.exit()
 
3360
          .transition()
 
3361
          .style('stroke-opacity', 1e-6)
 
3362
          .style('fill-opacity', 1e-6)
 
3363
          .remove();
 
3364
      groups
 
3365
          .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
 
3366
          .classed('hover', function(d) { return d.hover });
 
3367
      groups
 
3368
          .transition()
 
3369
          .style('stroke-opacity', 1)
 
3370
          .style('fill-opacity', .75);
 
3371
 
 
3372
 
 
3373
      var bars = groups.selectAll('g.nv-bar')
 
3374
          .data(function(d) { return d.values });
 
3375
 
 
3376
      bars.exit().remove();
 
3377
 
 
3378
 
 
3379
      var barsEnter = bars.enter().append('g')
 
3380
          .attr('transform', function(d,i,j) {
 
3381
              return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05 ) + ', ' + y(0) + ')'
 
3382
          })
 
3383
          .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
 
3384
            d3.select(this).classed('hover', true);
 
3385
            dispatch.elementMouseover({
 
3386
              value: getY(d,i),
 
3387
              point: d,
 
3388
              series: data[d.series],
 
3389
              pos: [x(getX(d,i)) + (x.rangeBand() * (d.series + .5) / data.length), y(getY(d,i))],  // TODO: Figure out why the value appears to be shifted
 
3390
              pointIndex: i,
 
3391
              seriesIndex: d.series,
 
3392
              e: d3.event
 
3393
            });
 
3394
          })
 
3395
          .on('mouseout', function(d,i) {
 
3396
            d3.select(this).classed('hover', false);
 
3397
            dispatch.elementMouseout({
 
3398
              value: getY(d,i),
 
3399
              point: d,
 
3400
              series: data[d.series],
 
3401
              pointIndex: i,
 
3402
              seriesIndex: d.series,
 
3403
              e: d3.event
 
3404
            });
 
3405
          })
 
3406
          .on('click', function(d,i) {
 
3407
            dispatch.elementClick({
 
3408
              value: getY(d,i),
 
3409
              point: d,
 
3410
              series: data[d.series],
 
3411
              pos: [x(getX(d,i)) + (x.rangeBand() * (d.series + .5) / data.length), y(getY(d,i))],  // TODO: Figure out why the value appears to be shifted
 
3412
              pointIndex: i,
 
3413
              seriesIndex: d.series,
 
3414
              e: d3.event
 
3415
            });
 
3416
            d3.event.stopPropagation();
 
3417
          })
 
3418
          .on('dblclick', function(d,i) {
 
3419
            dispatch.elementDblClick({
 
3420
              value: getY(d,i),
 
3421
              point: d,
 
3422
              series: data[d.series],
 
3423
              pos: [x(getX(d,i)) + (x.rangeBand() * (d.series + .5) / data.length), y(getY(d,i))],  // TODO: Figure out why the value appears to be shifted
 
3424
              pointIndex: i,
 
3425
              seriesIndex: d.series,
 
3426
              e: d3.event
 
3427
            });
 
3428
            d3.event.stopPropagation();
 
3429
          });
 
3430
 
 
3431
      barsEnter.append('rect')
 
3432
          .attr('height', 0)
 
3433
          .attr('width', x.rangeBand() * .9 / data.length )
 
3434
 
 
3435
      if (showValues) {
 
3436
        barsEnter.append('text')
 
3437
          .attr('text-anchor', 'middle')
 
3438
          ;
 
3439
 
 
3440
        bars.select('text')
 
3441
          .text(function(d,i) { return valueFormat(getY(d,i)) })
 
3442
          .transition()
 
3443
          .attr('x', x.rangeBand() * .9 / 2)
 
3444
          .attr('y', function(d,i) { return getY(d,i) < 0 ? y(getY(d,i)) - y(0) + 12 : -4 })
 
3445
 
 
3446
          ;
 
3447
      } else {
 
3448
        bars.selectAll('text').remove();
 
3449
      }
 
3450
 
 
3451
      bars
 
3452
          .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive' })
 
3453
          .style('fill', function(d,i) { return d.color || color(d,i) })
 
3454
          .style('stroke', function(d,i) { return d.color || color(d,i) })
 
3455
        .select('rect')
 
3456
          .attr('class', rectClass)
 
3457
          .transition()
 
3458
          .attr('width', x.rangeBand() * .9 / data.length);
 
3459
      bars.transition()
 
3460
        //.delay(function(d,i) { return i * 1200 / data[0].values.length })
 
3461
          .attr('transform', function(d,i) {
 
3462
            var left = x(getX(d,i)) + x.rangeBand() * .05,
 
3463
                top = getY(d,i) < 0 ?
 
3464
                        y(0) :
 
3465
                        y(0) - y(getY(d,i)) < 1 ?
 
3466
                          y(0) - 1 : //make 1 px positive bars show up above y=0
 
3467
                          y(getY(d,i));
 
3468
 
 
3469
              return 'translate(' + left + ', ' + top + ')'
 
3470
          })
 
3471
        .select('rect')
 
3472
          .attr('height', function(d,i) {
 
3473
            return  Math.max(Math.abs(y(getY(d,i)) - y((yDomain && yDomain[0]) || 0)) || 1)
 
3474
          });
 
3475
 
 
3476
 
 
3477
      //store old scales for use in transitions on update
 
3478
      x0 = x.copy();
 
3479
      y0 = y.copy();
 
3480
 
 
3481
    });
 
3482
 
 
3483
    return chart;
 
3484
  }
 
3485
 
 
3486
 
 
3487
  //============================================================
 
3488
  // Expose Public Variables
 
3489
  //------------------------------------------------------------
 
3490
 
 
3491
  chart.dispatch = dispatch;
 
3492
 
 
3493
  chart.options = nv.utils.optionsFunc.bind(chart);
 
3494
 
 
3495
  chart.x = function(_) {
 
3496
    if (!arguments.length) return getX;
 
3497
    getX = _;
 
3498
    return chart;
 
3499
  };
 
3500
 
 
3501
  chart.y = function(_) {
 
3502
    if (!arguments.length) return getY;
 
3503
    getY = _;
 
3504
    return chart;
 
3505
  };
 
3506
 
 
3507
  chart.margin = function(_) {
 
3508
    if (!arguments.length) return margin;
 
3509
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
3510
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
3511
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
3512
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
3513
    return chart;
 
3514
  };
 
3515
 
 
3516
  chart.width = function(_) {
 
3517
    if (!arguments.length) return width;
 
3518
    width = _;
 
3519
    return chart;
 
3520
  };
 
3521
 
 
3522
  chart.height = function(_) {
 
3523
    if (!arguments.length) return height;
 
3524
    height = _;
 
3525
    return chart;
 
3526
  };
 
3527
 
 
3528
  chart.xScale = function(_) {
 
3529
    if (!arguments.length) return x;
 
3530
    x = _;
 
3531
    return chart;
 
3532
  };
 
3533
 
 
3534
  chart.yScale = function(_) {
 
3535
    if (!arguments.length) return y;
 
3536
    y = _;
 
3537
    return chart;
 
3538
  };
 
3539
 
 
3540
  chart.xDomain = function(_) {
 
3541
    if (!arguments.length) return xDomain;
 
3542
    xDomain = _;
 
3543
    return chart;
 
3544
  };
 
3545
 
 
3546
  chart.yDomain = function(_) {
 
3547
    if (!arguments.length) return yDomain;
 
3548
    yDomain = _;
 
3549
    return chart;
 
3550
  };
 
3551
 
 
3552
  chart.xRange = function(_) {
 
3553
    if (!arguments.length) return xRange;
 
3554
    xRange = _;
 
3555
    return chart;
 
3556
  };
 
3557
 
 
3558
  chart.yRange = function(_) {
 
3559
    if (!arguments.length) return yRange;
 
3560
    yRange = _;
 
3561
    return chart;
 
3562
  };
 
3563
 
 
3564
  chart.forceY = function(_) {
 
3565
    if (!arguments.length) return forceY;
 
3566
    forceY = _;
 
3567
    return chart;
 
3568
  };
 
3569
 
 
3570
  chart.color = function(_) {
 
3571
    if (!arguments.length) return color;
 
3572
    color = nv.utils.getColor(_);
 
3573
    return chart;
 
3574
  };
 
3575
 
 
3576
  chart.id = function(_) {
 
3577
    if (!arguments.length) return id;
 
3578
    id = _;
 
3579
    return chart;
 
3580
  };
 
3581
 
 
3582
  chart.showValues = function(_) {
 
3583
    if (!arguments.length) return showValues;
 
3584
    showValues = _;
 
3585
    return chart;
 
3586
  };
 
3587
 
 
3588
  chart.valueFormat= function(_) {
 
3589
    if (!arguments.length) return valueFormat;
 
3590
    valueFormat = _;
 
3591
    return chart;
 
3592
  };
 
3593
 
 
3594
  chart.rectClass= function(_) {
 
3595
    if (!arguments.length) return rectClass;
 
3596
    rectClass = _;
 
3597
    return chart;
 
3598
  };
 
3599
  //============================================================
 
3600
 
 
3601
 
 
3602
  return chart;
 
3603
}
 
3604
 
 
3605
nv.models.discreteBarChart = function() {
 
3606
  "use strict";
 
3607
  //============================================================
 
3608
  // Public Variables with Default Settings
 
3609
  //------------------------------------------------------------
 
3610
 
 
3611
  var discretebar = nv.models.discreteBar()
 
3612
    , xAxis = nv.models.axis()
 
3613
    , yAxis = nv.models.axis()
 
3614
    ;
 
3615
 
 
3616
  var margin = {top: 15, right: 10, bottom: 50, left: 60}
 
3617
    , width = null
 
3618
    , height = null
 
3619
    , color = nv.utils.getColor()
 
3620
    , showXAxis = true
 
3621
    , showYAxis = true
 
3622
    , rightAlignYAxis = false
 
3623
    , staggerLabels = false
 
3624
    , tooltips = true
 
3625
    , tooltip = function(key, x, y, e, graph) {
 
3626
        return '<h3>' + x + '</h3>' +
 
3627
               '<p>' +  y + '</p>'
 
3628
      }
 
3629
    , x
 
3630
    , y
 
3631
    , noData = "No Data Available."
 
3632
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'beforeUpdate')
 
3633
    , transitionDuration = 250
 
3634
    ;
 
3635
 
 
3636
  xAxis
 
3637
    .orient('bottom')
 
3638
    .highlightZero(false)
 
3639
    .showMaxMin(false)
 
3640
    .tickFormat(function(d) { return d })
 
3641
    ;
 
3642
  yAxis
 
3643
    .orient((rightAlignYAxis) ? 'right' : 'left')
 
3644
    .tickFormat(d3.format(',.1f'))
 
3645
    ;
 
3646
 
 
3647
  //============================================================
 
3648
 
 
3649
 
 
3650
  //============================================================
 
3651
  // Private Variables
 
3652
  //------------------------------------------------------------
 
3653
 
 
3654
  var showTooltip = function(e, offsetElement) {
 
3655
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
3656
        top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
3657
        x = xAxis.tickFormat()(discretebar.x()(e.point, e.pointIndex)),
 
3658
        y = yAxis.tickFormat()(discretebar.y()(e.point, e.pointIndex)),
 
3659
        content = tooltip(e.series.key, x, y, e, chart);
 
3660
 
 
3661
    nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
 
3662
  };
 
3663
 
 
3664
  //============================================================
 
3665
 
 
3666
 
 
3667
  function chart(selection) {
 
3668
    selection.each(function(data) {
 
3669
      var container = d3.select(this),
 
3670
          that = this;
 
3671
 
 
3672
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
3673
                             - margin.left - margin.right,
 
3674
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
3675
                             - margin.top - margin.bottom;
 
3676
 
 
3677
 
 
3678
      chart.update = function() { 
 
3679
        dispatch.beforeUpdate(); 
 
3680
        container.transition().duration(transitionDuration).call(chart); 
 
3681
      };
 
3682
      chart.container = this;
 
3683
 
 
3684
 
 
3685
      //------------------------------------------------------------
 
3686
      // Display No Data message if there's nothing to show.
 
3687
 
 
3688
      if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
 
3689
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
3690
 
 
3691
        noDataText.enter().append('text')
 
3692
          .attr('class', 'nvd3 nv-noData')
 
3693
          .attr('dy', '-.7em')
 
3694
          .style('text-anchor', 'middle');
 
3695
 
 
3696
        noDataText
 
3697
          .attr('x', margin.left + availableWidth / 2)
 
3698
          .attr('y', margin.top + availableHeight / 2)
 
3699
          .text(function(d) { return d });
 
3700
 
 
3701
        return chart;
 
3702
      } else {
 
3703
        container.selectAll('.nv-noData').remove();
 
3704
      }
 
3705
 
 
3706
      //------------------------------------------------------------
 
3707
 
 
3708
 
 
3709
      //------------------------------------------------------------
 
3710
      // Setup Scales
 
3711
 
 
3712
      x = discretebar.xScale();
 
3713
      y = discretebar.yScale().clamp(true);
 
3714
 
 
3715
      //------------------------------------------------------------
 
3716
 
 
3717
 
 
3718
      //------------------------------------------------------------
 
3719
      // Setup containers and skeleton of chart
 
3720
 
 
3721
      var wrap = container.selectAll('g.nv-wrap.nv-discreteBarWithAxes').data([data]);
 
3722
      var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-discreteBarWithAxes').append('g');
 
3723
      var defsEnter = gEnter.append('defs');
 
3724
      var g = wrap.select('g');
 
3725
 
 
3726
      gEnter.append('g').attr('class', 'nv-x nv-axis');
 
3727
      gEnter.append('g').attr('class', 'nv-y nv-axis')
 
3728
            .append('g').attr('class', 'nv-zeroLine')
 
3729
            .append('line');
 
3730
        
 
3731
      gEnter.append('g').attr('class', 'nv-barsWrap');
 
3732
 
 
3733
      g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
3734
 
 
3735
      if (rightAlignYAxis) {
 
3736
          g.select(".nv-y.nv-axis")
 
3737
              .attr("transform", "translate(" + availableWidth + ",0)");
 
3738
      }
 
3739
 
 
3740
      //------------------------------------------------------------
 
3741
 
 
3742
 
 
3743
      //------------------------------------------------------------
 
3744
      // Main Chart Component(s)
 
3745
 
 
3746
      discretebar
 
3747
        .width(availableWidth)
 
3748
        .height(availableHeight);
 
3749
 
 
3750
 
 
3751
      var barsWrap = g.select('.nv-barsWrap')
 
3752
          .datum(data.filter(function(d) { return !d.disabled }))
 
3753
 
 
3754
      barsWrap.transition().call(discretebar);
 
3755
 
 
3756
      //------------------------------------------------------------
 
3757
 
 
3758
 
 
3759
 
 
3760
      defsEnter.append('clipPath')
 
3761
          .attr('id', 'nv-x-label-clip-' + discretebar.id())
 
3762
        .append('rect');
 
3763
 
 
3764
      g.select('#nv-x-label-clip-' + discretebar.id() + ' rect')
 
3765
          .attr('width', x.rangeBand() * (staggerLabels ? 2 : 1))
 
3766
          .attr('height', 16)
 
3767
          .attr('x', -x.rangeBand() / (staggerLabels ? 1 : 2 ));
 
3768
 
 
3769
 
 
3770
      //------------------------------------------------------------
 
3771
      // Setup Axes
 
3772
 
 
3773
      if (showXAxis) {
 
3774
          xAxis
 
3775
            .scale(x)
 
3776
            .ticks( availableWidth / 100 )
 
3777
            .tickSize(-availableHeight, 0);
 
3778
 
 
3779
          g.select('.nv-x.nv-axis')
 
3780
              .attr('transform', 'translate(0,' + (y.range()[0] + ((discretebar.showValues() && y.domain()[0] < 0) ? 16 : 0)) + ')');
 
3781
          //d3.transition(g.select('.nv-x.nv-axis'))
 
3782
          g.select('.nv-x.nv-axis').transition()
 
3783
              .call(xAxis);
 
3784
 
 
3785
 
 
3786
          var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
 
3787
 
 
3788
          if (staggerLabels) {
 
3789
            xTicks
 
3790
                .selectAll('text')
 
3791
                .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 == 0 ? '5' : '17') + ')' })
 
3792
          }
 
3793
      }
 
3794
 
 
3795
      if (showYAxis) {
 
3796
          yAxis
 
3797
            .scale(y)
 
3798
            .ticks( availableHeight / 36 )
 
3799
            .tickSize( -availableWidth, 0);
 
3800
 
 
3801
          g.select('.nv-y.nv-axis').transition()
 
3802
              .call(yAxis);
 
3803
      }
 
3804
 
 
3805
      // Zero line
 
3806
      g.select(".nv-zeroLine line")
 
3807
        .attr("x1",0)
 
3808
        .attr("x2",availableWidth)
 
3809
        .attr("y1", y(0))
 
3810
        .attr("y2", y(0))
 
3811
        ;
 
3812
 
 
3813
      //------------------------------------------------------------
 
3814
 
 
3815
 
 
3816
      //============================================================
 
3817
      // Event Handling/Dispatching (in chart's scope)
 
3818
      //------------------------------------------------------------
 
3819
 
 
3820
      dispatch.on('tooltipShow', function(e) {
 
3821
        if (tooltips) showTooltip(e, that.parentNode);
 
3822
      });
 
3823
 
 
3824
      //============================================================
 
3825
 
 
3826
 
 
3827
    });
 
3828
 
 
3829
    return chart;
 
3830
  }
 
3831
 
 
3832
  //============================================================
 
3833
  // Event Handling/Dispatching (out of chart's scope)
 
3834
  //------------------------------------------------------------
 
3835
 
 
3836
  discretebar.dispatch.on('elementMouseover.tooltip', function(e) {
 
3837
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
3838
    dispatch.tooltipShow(e);
 
3839
  });
 
3840
 
 
3841
  discretebar.dispatch.on('elementMouseout.tooltip', function(e) {
 
3842
    dispatch.tooltipHide(e);
 
3843
  });
 
3844
 
 
3845
  dispatch.on('tooltipHide', function() {
 
3846
    if (tooltips) nv.tooltip.cleanup();
 
3847
  });
 
3848
 
 
3849
  //============================================================
 
3850
 
 
3851
 
 
3852
  //============================================================
 
3853
  // Expose Public Variables
 
3854
  //------------------------------------------------------------
 
3855
 
 
3856
  // expose chart's sub-components
 
3857
  chart.dispatch = dispatch;
 
3858
  chart.discretebar = discretebar;
 
3859
  chart.xAxis = xAxis;
 
3860
  chart.yAxis = yAxis;
 
3861
 
 
3862
  d3.rebind(chart, discretebar, 'x', 'y', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'id', 'showValues', 'valueFormat');
 
3863
 
 
3864
  chart.options = nv.utils.optionsFunc.bind(chart);
 
3865
  
 
3866
  chart.margin = function(_) {
 
3867
    if (!arguments.length) return margin;
 
3868
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
3869
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
3870
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
3871
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
3872
    return chart;
 
3873
  };
 
3874
 
 
3875
  chart.width = function(_) {
 
3876
    if (!arguments.length) return width;
 
3877
    width = _;
 
3878
    return chart;
 
3879
  };
 
3880
 
 
3881
  chart.height = function(_) {
 
3882
    if (!arguments.length) return height;
 
3883
    height = _;
 
3884
    return chart;
 
3885
  };
 
3886
 
 
3887
  chart.color = function(_) {
 
3888
    if (!arguments.length) return color;
 
3889
    color = nv.utils.getColor(_);
 
3890
    discretebar.color(color);
 
3891
    return chart;
 
3892
  };
 
3893
 
 
3894
  chart.showXAxis = function(_) {
 
3895
    if (!arguments.length) return showXAxis;
 
3896
    showXAxis = _;
 
3897
    return chart;
 
3898
  };
 
3899
 
 
3900
  chart.showYAxis = function(_) {
 
3901
    if (!arguments.length) return showYAxis;
 
3902
    showYAxis = _;
 
3903
    return chart;
 
3904
  };
 
3905
 
 
3906
  chart.rightAlignYAxis = function(_) {
 
3907
    if(!arguments.length) return rightAlignYAxis;
 
3908
    rightAlignYAxis = _;
 
3909
    yAxis.orient( (_) ? 'right' : 'left');
 
3910
    return chart;
 
3911
  };
 
3912
 
 
3913
  chart.staggerLabels = function(_) {
 
3914
    if (!arguments.length) return staggerLabels;
 
3915
    staggerLabels = _;
 
3916
    return chart;
 
3917
  };
 
3918
 
 
3919
  chart.tooltips = function(_) {
 
3920
    if (!arguments.length) return tooltips;
 
3921
    tooltips = _;
 
3922
    return chart;
 
3923
  };
 
3924
 
 
3925
  chart.tooltipContent = function(_) {
 
3926
    if (!arguments.length) return tooltip;
 
3927
    tooltip = _;
 
3928
    return chart;
 
3929
  };
 
3930
 
 
3931
  chart.noData = function(_) {
 
3932
    if (!arguments.length) return noData;
 
3933
    noData = _;
 
3934
    return chart;
 
3935
  };
 
3936
 
 
3937
  chart.transitionDuration = function(_) {
 
3938
    if (!arguments.length) return transitionDuration;
 
3939
    transitionDuration = _;
 
3940
    return chart;
 
3941
  };
 
3942
 
 
3943
  //============================================================
 
3944
 
 
3945
 
 
3946
  return chart;
 
3947
}
 
3948
 
 
3949
nv.models.distribution = function() {
 
3950
  "use strict";
 
3951
  //============================================================
 
3952
  // Public Variables with Default Settings
 
3953
  //------------------------------------------------------------
 
3954
 
 
3955
  var margin = {top: 0, right: 0, bottom: 0, left: 0}
 
3956
    , width = 400 //technically width or height depending on x or y....
 
3957
    , size = 8
 
3958
    , axis = 'x' // 'x' or 'y'... horizontal or vertical
 
3959
    , getData = function(d) { return d[axis] }  // defaults d.x or d.y
 
3960
    , color = nv.utils.defaultColor()
 
3961
    , scale = d3.scale.linear()
 
3962
    , domain
 
3963
    ;
 
3964
 
 
3965
  //============================================================
 
3966
 
 
3967
 
 
3968
  //============================================================
 
3969
  // Private Variables
 
3970
  //------------------------------------------------------------
 
3971
 
 
3972
  var scale0;
 
3973
 
 
3974
  //============================================================
 
3975
 
 
3976
 
 
3977
  function chart(selection) {
 
3978
    selection.each(function(data) {
 
3979
      var availableLength = width - (axis === 'x' ? margin.left + margin.right : margin.top + margin.bottom),
 
3980
          naxis = axis == 'x' ? 'y' : 'x',
 
3981
          container = d3.select(this);
 
3982
 
 
3983
 
 
3984
      //------------------------------------------------------------
 
3985
      // Setup Scales
 
3986
 
 
3987
      scale0 = scale0 || scale;
 
3988
 
 
3989
      //------------------------------------------------------------
 
3990
 
 
3991
 
 
3992
      //------------------------------------------------------------
 
3993
      // Setup containers and skeleton of chart
 
3994
 
 
3995
      var wrap = container.selectAll('g.nv-distribution').data([data]);
 
3996
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-distribution');
 
3997
      var gEnter = wrapEnter.append('g');
 
3998
      var g = wrap.select('g');
 
3999
 
 
4000
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
 
4001
 
 
4002
      //------------------------------------------------------------
 
4003
 
 
4004
 
 
4005
      var distWrap = g.selectAll('g.nv-dist')
 
4006
          .data(function(d) { return d }, function(d) { return d.key });
 
4007
 
 
4008
      distWrap.enter().append('g');
 
4009
      distWrap
 
4010
          .attr('class', function(d,i) { return 'nv-dist nv-series-' + i })
 
4011
          .style('stroke', function(d,i) { return color(d, i) });
 
4012
 
 
4013
      var dist = distWrap.selectAll('line.nv-dist' + axis)
 
4014
          .data(function(d) { return d.values })
 
4015
      dist.enter().append('line')
 
4016
          .attr(axis + '1', function(d,i) { return scale0(getData(d,i)) })
 
4017
          .attr(axis + '2', function(d,i) { return scale0(getData(d,i)) })
 
4018
      distWrap.exit().selectAll('line.nv-dist' + axis)
 
4019
          .transition()
 
4020
          .attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
 
4021
          .attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
 
4022
          .style('stroke-opacity', 0)
 
4023
          .remove();
 
4024
      dist
 
4025
          .attr('class', function(d,i) { return 'nv-dist' + axis + ' nv-dist' + axis + '-' + i })
 
4026
          .attr(naxis + '1', 0)
 
4027
          .attr(naxis + '2', size);
 
4028
      dist
 
4029
          .transition()
 
4030
          .attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
 
4031
          .attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
 
4032
 
 
4033
 
 
4034
      scale0 = scale.copy();
 
4035
 
 
4036
    });
 
4037
 
 
4038
    return chart;
 
4039
  }
 
4040
 
 
4041
 
 
4042
  //============================================================
 
4043
  // Expose Public Variables
 
4044
  //------------------------------------------------------------
 
4045
  chart.options = nv.utils.optionsFunc.bind(chart);
 
4046
  
 
4047
  chart.margin = function(_) {
 
4048
    if (!arguments.length) return margin;
 
4049
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
4050
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
4051
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
4052
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
4053
    return chart;
 
4054
  };
 
4055
 
 
4056
  chart.width = function(_) {
 
4057
    if (!arguments.length) return width;
 
4058
    width = _;
 
4059
    return chart;
 
4060
  };
 
4061
 
 
4062
  chart.axis = function(_) {
 
4063
    if (!arguments.length) return axis;
 
4064
    axis = _;
 
4065
    return chart;
 
4066
  };
 
4067
 
 
4068
  chart.size = function(_) {
 
4069
    if (!arguments.length) return size;
 
4070
    size = _;
 
4071
    return chart;
 
4072
  };
 
4073
 
 
4074
  chart.getData = function(_) {
 
4075
    if (!arguments.length) return getData;
 
4076
    getData = d3.functor(_);
 
4077
    return chart;
 
4078
  };
 
4079
 
 
4080
  chart.scale = function(_) {
 
4081
    if (!arguments.length) return scale;
 
4082
    scale = _;
 
4083
    return chart;
 
4084
  };
 
4085
 
 
4086
  chart.color = function(_) {
 
4087
    if (!arguments.length) return color;
 
4088
    color = nv.utils.getColor(_);
 
4089
    return chart;
 
4090
  };
 
4091
  //============================================================
 
4092
 
 
4093
 
 
4094
  return chart;
 
4095
}
 
4096
 
 
4097
nv.models.historicalBarChart = function() {
 
4098
  "use strict";
 
4099
  //============================================================
 
4100
  // Public Variables with Default Settings
 
4101
  //------------------------------------------------------------
 
4102
 
 
4103
  var bars = nv.models.historicalBar()
 
4104
    , xAxis = nv.models.axis()
 
4105
    , yAxis = nv.models.axis()
 
4106
    , legend = nv.models.legend()
 
4107
    ;
 
4108
 
 
4109
 
 
4110
  var margin = {top: 30, right: 90, bottom: 50, left: 90}
 
4111
    , color = nv.utils.defaultColor()
 
4112
    , width = null
 
4113
    , height = null
 
4114
    , showLegend = false
 
4115
    , showXAxis = true
 
4116
    , showYAxis = true
 
4117
    , rightAlignYAxis = false
 
4118
    , tooltips = true
 
4119
    , tooltip = function(key, x, y, e, graph) {
 
4120
        return '<h3>' + key + '</h3>' +
 
4121
               '<p>' +  y + ' at ' + x + '</p>'
 
4122
      }
 
4123
    , x
 
4124
    , y
 
4125
    , state = {}
 
4126
    , defaultState = null
 
4127
    , noData = 'No Data Available.'
 
4128
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
 
4129
    , transitionDuration = 250
 
4130
    ;
 
4131
 
 
4132
  xAxis
 
4133
    .orient('bottom')
 
4134
    .tickPadding(7)
 
4135
    ;
 
4136
  yAxis
 
4137
    .orient( (rightAlignYAxis) ? 'right' : 'left')
 
4138
    ;
 
4139
 
 
4140
  //============================================================
 
4141
 
 
4142
 
 
4143
  //============================================================
 
4144
  // Private Variables
 
4145
  //------------------------------------------------------------
 
4146
 
 
4147
  var showTooltip = function(e, offsetElement) {
 
4148
 
 
4149
    // New addition to calculate position if SVG is scaled with viewBox, may move TODO: consider implementing everywhere else
 
4150
    if (offsetElement) {
 
4151
      var svg = d3.select(offsetElement).select('svg');
 
4152
      var viewBox = (svg.node()) ? svg.attr('viewBox') : null;
 
4153
      if (viewBox) {
 
4154
        viewBox = viewBox.split(' ');
 
4155
        var ratio = parseInt(svg.style('width')) / viewBox[2];
 
4156
        e.pos[0] = e.pos[0] * ratio;
 
4157
        e.pos[1] = e.pos[1] * ratio;
 
4158
      }
 
4159
    }
 
4160
 
 
4161
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
4162
        top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
4163
        x = xAxis.tickFormat()(bars.x()(e.point, e.pointIndex)),
 
4164
        y = yAxis.tickFormat()(bars.y()(e.point, e.pointIndex)),
 
4165
        content = tooltip(e.series.key, x, y, e, chart);
 
4166
 
 
4167
    nv.tooltip.show([left, top], content, null, null, offsetElement);
 
4168
  };
 
4169
 
 
4170
  //============================================================
 
4171
 
 
4172
 
 
4173
  function chart(selection) {
 
4174
    selection.each(function(data) {
 
4175
      var container = d3.select(this),
 
4176
          that = this;
 
4177
 
 
4178
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
4179
                             - margin.left - margin.right,
 
4180
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
4181
                             - margin.top - margin.bottom;
 
4182
 
 
4183
 
 
4184
      chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
 
4185
      chart.container = this;
 
4186
 
 
4187
      //set state.disabled
 
4188
      state.disabled = data.map(function(d) { return !!d.disabled });
 
4189
 
 
4190
      if (!defaultState) {
 
4191
        var key;
 
4192
        defaultState = {};
 
4193
        for (key in state) {
 
4194
          if (state[key] instanceof Array)
 
4195
            defaultState[key] = state[key].slice(0);
 
4196
          else
 
4197
            defaultState[key] = state[key];
 
4198
        }
 
4199
      }
 
4200
 
 
4201
      //------------------------------------------------------------
 
4202
      // Display noData message if there's nothing to show.
 
4203
 
 
4204
      if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
 
4205
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
4206
 
 
4207
        noDataText.enter().append('text')
 
4208
          .attr('class', 'nvd3 nv-noData')
 
4209
          .attr('dy', '-.7em')
 
4210
          .style('text-anchor', 'middle');
 
4211
 
 
4212
        noDataText
 
4213
          .attr('x', margin.left + availableWidth / 2)
 
4214
          .attr('y', margin.top + availableHeight / 2)
 
4215
          .text(function(d) { return d });
 
4216
 
 
4217
        return chart;
 
4218
      } else {
 
4219
        container.selectAll('.nv-noData').remove();
 
4220
      }
 
4221
 
 
4222
      //------------------------------------------------------------
 
4223
 
 
4224
 
 
4225
      //------------------------------------------------------------
 
4226
      // Setup Scales
 
4227
 
 
4228
      x = bars.xScale();
 
4229
      y = bars.yScale();
 
4230
 
 
4231
      //------------------------------------------------------------
 
4232
 
 
4233
 
 
4234
      //------------------------------------------------------------
 
4235
      // Setup containers and skeleton of chart
 
4236
 
 
4237
      var wrap = container.selectAll('g.nv-wrap.nv-historicalBarChart').data([data]);
 
4238
      var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBarChart').append('g');
 
4239
      var g = wrap.select('g');
 
4240
 
 
4241
      gEnter.append('g').attr('class', 'nv-x nv-axis');
 
4242
      gEnter.append('g').attr('class', 'nv-y nv-axis');
 
4243
      gEnter.append('g').attr('class', 'nv-barsWrap');
 
4244
      gEnter.append('g').attr('class', 'nv-legendWrap');
 
4245
 
 
4246
      //------------------------------------------------------------
 
4247
 
 
4248
 
 
4249
      //------------------------------------------------------------
 
4250
      // Legend
 
4251
 
 
4252
      if (showLegend) {
 
4253
        legend.width(availableWidth);
 
4254
 
 
4255
        g.select('.nv-legendWrap')
 
4256
            .datum(data)
 
4257
            .call(legend);
 
4258
 
 
4259
        if ( margin.top != legend.height()) {
 
4260
          margin.top = legend.height();
 
4261
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
4262
                             - margin.top - margin.bottom;
 
4263
        }
 
4264
 
 
4265
        wrap.select('.nv-legendWrap')
 
4266
            .attr('transform', 'translate(0,' + (-margin.top) +')')
 
4267
      }
 
4268
 
 
4269
      //------------------------------------------------------------
 
4270
 
 
4271
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
4272
 
 
4273
      if (rightAlignYAxis) {
 
4274
        g.select(".nv-y.nv-axis")
 
4275
            .attr("transform", "translate(" + availableWidth + ",0)");
 
4276
      }
 
4277
 
 
4278
 
 
4279
      //------------------------------------------------------------
 
4280
      // Main Chart Component(s)
 
4281
 
 
4282
      bars
 
4283
        .width(availableWidth)
 
4284
        .height(availableHeight)
 
4285
        .color(data.map(function(d,i) {
 
4286
          return d.color || color(d, i);
 
4287
        }).filter(function(d,i) { return !data[i].disabled }));
 
4288
 
 
4289
 
 
4290
      var barsWrap = g.select('.nv-barsWrap')
 
4291
          .datum(data.filter(function(d) { return !d.disabled }))
 
4292
 
 
4293
      barsWrap.transition().call(bars);
 
4294
 
 
4295
      //------------------------------------------------------------
 
4296
 
 
4297
 
 
4298
      //------------------------------------------------------------
 
4299
      // Setup Axes
 
4300
 
 
4301
      if (showXAxis) {
 
4302
        xAxis
 
4303
          .scale(x)
 
4304
          .tickSize(-availableHeight, 0);
 
4305
 
 
4306
        g.select('.nv-x.nv-axis')
 
4307
            .attr('transform', 'translate(0,' + y.range()[0] + ')');
 
4308
        g.select('.nv-x.nv-axis')
 
4309
            .transition()
 
4310
            .call(xAxis);
 
4311
      }
 
4312
 
 
4313
      if (showYAxis) {
 
4314
        yAxis
 
4315
          .scale(y)
 
4316
          .ticks( availableHeight / 36 )
 
4317
          .tickSize( -availableWidth, 0);
 
4318
 
 
4319
        g.select('.nv-y.nv-axis')
 
4320
          .transition()
 
4321
            .call(yAxis);
 
4322
      }
 
4323
      //------------------------------------------------------------
 
4324
 
 
4325
 
 
4326
      //============================================================
 
4327
      // Event Handling/Dispatching (in chart's scope)
 
4328
      //------------------------------------------------------------
 
4329
 
 
4330
      legend.dispatch.on('legendClick', function(d,i) {
 
4331
        d.disabled = !d.disabled;
 
4332
 
 
4333
        if (!data.filter(function(d) { return !d.disabled }).length) {
 
4334
          data.map(function(d) {
 
4335
            d.disabled = false;
 
4336
            wrap.selectAll('.nv-series').classed('disabled', false);
 
4337
            return d;
 
4338
          });
 
4339
        }
 
4340
 
 
4341
        state.disabled = data.map(function(d) { return !!d.disabled });
 
4342
        dispatch.stateChange(state);
 
4343
 
 
4344
        selection.transition().call(chart);
 
4345
      });
 
4346
 
 
4347
      legend.dispatch.on('legendDblclick', function(d) {
 
4348
          //Double clicking should always enable current series, and disabled all others.
 
4349
          data.forEach(function(d) {
 
4350
             d.disabled = true;
 
4351
          });
 
4352
          d.disabled = false;
 
4353
 
 
4354
          state.disabled = data.map(function(d) { return !!d.disabled });
 
4355
          dispatch.stateChange(state);
 
4356
          chart.update();
 
4357
      });
 
4358
 
 
4359
      dispatch.on('tooltipShow', function(e) {
 
4360
        if (tooltips) showTooltip(e, that.parentNode);
 
4361
      });
 
4362
 
 
4363
 
 
4364
      dispatch.on('changeState', function(e) {
 
4365
 
 
4366
        if (typeof e.disabled !== 'undefined') {
 
4367
          data.forEach(function(series,i) {
 
4368
            series.disabled = e.disabled[i];
 
4369
          });
 
4370
 
 
4371
          state.disabled = e.disabled;
 
4372
        }
 
4373
 
 
4374
        chart.update();
 
4375
      });
 
4376
 
 
4377
      //============================================================
 
4378
 
 
4379
    });
 
4380
 
 
4381
    return chart;
 
4382
  }
 
4383
 
 
4384
 
 
4385
  //============================================================
 
4386
  // Event Handling/Dispatching (out of chart's scope)
 
4387
  //------------------------------------------------------------
 
4388
 
 
4389
  bars.dispatch.on('elementMouseover.tooltip', function(e) {
 
4390
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
4391
    dispatch.tooltipShow(e);
 
4392
  });
 
4393
 
 
4394
  bars.dispatch.on('elementMouseout.tooltip', function(e) {
 
4395
    dispatch.tooltipHide(e);
 
4396
  });
 
4397
 
 
4398
  dispatch.on('tooltipHide', function() {
 
4399
    if (tooltips) nv.tooltip.cleanup();
 
4400
  });
 
4401
 
 
4402
  //============================================================
 
4403
 
 
4404
 
 
4405
  //============================================================
 
4406
  // Expose Public Variables
 
4407
  //------------------------------------------------------------
 
4408
 
 
4409
  // expose chart's sub-components
 
4410
  chart.dispatch = dispatch;
 
4411
  chart.bars = bars;
 
4412
  chart.legend = legend;
 
4413
  chart.xAxis = xAxis;
 
4414
  chart.yAxis = yAxis;
 
4415
 
 
4416
  d3.rebind(chart, bars, 'defined', 'isArea', 'x', 'y', 'size', 'xScale', 'yScale',
 
4417
    'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id', 'interpolate','highlightPoint','clearHighlights', 'interactive');
 
4418
 
 
4419
  chart.options = nv.utils.optionsFunc.bind(chart);
 
4420
 
 
4421
  chart.margin = function(_) {
 
4422
    if (!arguments.length) return margin;
 
4423
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
4424
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
4425
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
4426
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
4427
    return chart;
 
4428
  };
 
4429
 
 
4430
  chart.width = function(_) {
 
4431
    if (!arguments.length) return width;
 
4432
    width = _;
 
4433
    return chart;
 
4434
  };
 
4435
 
 
4436
  chart.height = function(_) {
 
4437
    if (!arguments.length) return height;
 
4438
    height = _;
 
4439
    return chart;
 
4440
  };
 
4441
 
 
4442
  chart.color = function(_) {
 
4443
    if (!arguments.length) return color;
 
4444
    color = nv.utils.getColor(_);
 
4445
    legend.color(color);
 
4446
    return chart;
 
4447
  };
 
4448
 
 
4449
  chart.showLegend = function(_) {
 
4450
    if (!arguments.length) return showLegend;
 
4451
    showLegend = _;
 
4452
    return chart;
 
4453
  };
 
4454
 
 
4455
  chart.showXAxis = function(_) {
 
4456
    if (!arguments.length) return showXAxis;
 
4457
    showXAxis = _;
 
4458
    return chart;
 
4459
  };
 
4460
 
 
4461
  chart.showYAxis = function(_) {
 
4462
    if (!arguments.length) return showYAxis;
 
4463
    showYAxis = _;
 
4464
    return chart;
 
4465
  };
 
4466
 
 
4467
  chart.rightAlignYAxis = function(_) {
 
4468
    if(!arguments.length) return rightAlignYAxis;
 
4469
    rightAlignYAxis = _;
 
4470
    yAxis.orient( (_) ? 'right' : 'left');
 
4471
    return chart;
 
4472
  };
 
4473
 
 
4474
  chart.tooltips = function(_) {
 
4475
    if (!arguments.length) return tooltips;
 
4476
    tooltips = _;
 
4477
    return chart;
 
4478
  };
 
4479
 
 
4480
  chart.tooltipContent = function(_) {
 
4481
    if (!arguments.length) return tooltip;
 
4482
    tooltip = _;
 
4483
    return chart;
 
4484
  };
 
4485
 
 
4486
  chart.state = function(_) {
 
4487
    if (!arguments.length) return state;
 
4488
    state = _;
 
4489
    return chart;
 
4490
  };
 
4491
 
 
4492
  chart.defaultState = function(_) {
 
4493
    if (!arguments.length) return defaultState;
 
4494
    defaultState = _;
 
4495
    return chart;
 
4496
  };
 
4497
 
 
4498
  chart.noData = function(_) {
 
4499
    if (!arguments.length) return noData;
 
4500
    noData = _;
 
4501
    return chart;
 
4502
  };
 
4503
 
 
4504
  chart.transitionDuration = function(_) {
 
4505
    if (!arguments.length) return transitionDuration;
 
4506
    transitionDuration = _;
 
4507
    return chart;
 
4508
  };
 
4509
 
 
4510
  //============================================================
 
4511
 
 
4512
 
 
4513
  return chart;
 
4514
}
 
4515
nv.models.indentedTree = function() {
 
4516
  "use strict";
 
4517
  //============================================================
 
4518
  // Public Variables with Default Settings
 
4519
  //------------------------------------------------------------
 
4520
 
 
4521
  var margin = {top: 0, right: 0, bottom: 0, left: 0} //TODO: implement, maybe as margin on the containing div
 
4522
    , width = 960
 
4523
    , height = 500
 
4524
    , color = nv.utils.defaultColor()
 
4525
    , id = Math.floor(Math.random() * 10000)
 
4526
    , header = true
 
4527
    , filterZero = false
 
4528
    , noData = "No Data Available."
 
4529
    , childIndent = 20
 
4530
    , columns = [{key:'key', label: 'Name', type:'text'}] //TODO: consider functions like chart.addColumn, chart.removeColumn, instead of a block like this
 
4531
    , tableClass = null
 
4532
    , iconOpen = 'images/grey-plus.png' //TODO: consider removing this and replacing with a '+' or '-' unless user defines images
 
4533
    , iconClose = 'images/grey-minus.png'
 
4534
    , dispatch = d3.dispatch('elementClick', 'elementDblclick', 'elementMouseover', 'elementMouseout')
 
4535
    , getUrl = function(d) { return d.url }
 
4536
    ;
 
4537
 
 
4538
  //============================================================
 
4539
 
 
4540
  var idx = 0;
 
4541
 
 
4542
  function chart(selection) {
 
4543
    selection.each(function(data) {
 
4544
      var depth = 1,
 
4545
          container = d3.select(this);
 
4546
 
 
4547
      var tree = d3.layout.tree()
 
4548
          .children(function(d) { return d.values })
 
4549
          .size([height, childIndent]); //Not sure if this is needed now that the result is HTML
 
4550
 
 
4551
      chart.update = function() { container.transition().duration(600).call(chart) };
 
4552
 
 
4553
 
 
4554
      //------------------------------------------------------------
 
4555
      // Display No Data message if there's nothing to show.
 
4556
      if (!data[0]) data[0] = {key: noData};
 
4557
 
 
4558
      //------------------------------------------------------------
 
4559
 
 
4560
 
 
4561
      var nodes = tree.nodes(data[0]);
 
4562
 
 
4563
      // nodes.map(function(d) {
 
4564
      //   d.id = i++;
 
4565
      // })
 
4566
 
 
4567
      //------------------------------------------------------------
 
4568
      // Setup containers and skeleton of chart
 
4569
 
 
4570
      var wrap = d3.select(this).selectAll('div').data([[nodes]]);
 
4571
      var wrapEnter = wrap.enter().append('div').attr('class', 'nvd3 nv-wrap nv-indentedtree');
 
4572
      var tableEnter = wrapEnter.append('table');
 
4573
      var table = wrap.select('table').attr('width', '100%').attr('class', tableClass);
 
4574
 
 
4575
      //------------------------------------------------------------
 
4576
 
 
4577
 
 
4578
      if (header) {
 
4579
        var thead = tableEnter.append('thead');
 
4580
 
 
4581
        var theadRow1 = thead.append('tr');
 
4582
 
 
4583
        columns.forEach(function(column) {
 
4584
          theadRow1
 
4585
            .append('th')
 
4586
              .attr('width', column.width ? column.width : '10%')
 
4587
              .style('text-align', column.type == 'numeric' ? 'right' : 'left')
 
4588
            .append('span')
 
4589
              .text(column.label);
 
4590
        });
 
4591
      }
 
4592
 
 
4593
 
 
4594
      var tbody = table.selectAll('tbody')
 
4595
                    .data(function(d) { return d });
 
4596
      tbody.enter().append('tbody');
 
4597
 
 
4598
 
 
4599
 
 
4600
      //compute max generations
 
4601
      depth = d3.max(nodes, function(node) { return node.depth });
 
4602
      tree.size([height, depth * childIndent]); //TODO: see if this is necessary at all
 
4603
 
 
4604
 
 
4605
      // Update the nodes…
 
4606
      var node = tbody.selectAll('tr')
 
4607
          // .data(function(d) { return d; }, function(d) { return d.id || (d.id == ++i)});
 
4608
          .data(function(d) { return d.filter(function(d) { return (filterZero && !d.children) ? filterZero(d) :  true; } )}, function(d,i) { return d.id || (d.id || ++idx)});
 
4609
          //.style('display', 'table-row'); //TODO: see if this does anything
 
4610
 
 
4611
      node.exit().remove();
 
4612
 
 
4613
      node.select('img.nv-treeicon')
 
4614
          .attr('src', icon)
 
4615
          .classed('folded', folded);
 
4616
 
 
4617
      var nodeEnter = node.enter().append('tr');
 
4618
 
 
4619
 
 
4620
      columns.forEach(function(column, index) {
 
4621
 
 
4622
        var nodeName = nodeEnter.append('td')
 
4623
            .style('padding-left', function(d) { return (index ? 0 : d.depth * childIndent + 12 + (icon(d) ? 0 : 16)) + 'px' }, 'important') //TODO: check why I did the ternary here
 
4624
            .style('text-align', column.type == 'numeric' ? 'right' : 'left');
 
4625
 
 
4626
 
 
4627
        if (index == 0) {
 
4628
          nodeName.append('img')
 
4629
              .classed('nv-treeicon', true)
 
4630
              .classed('nv-folded', folded)
 
4631
              .attr('src', icon)
 
4632
              .style('width', '14px')
 
4633
              .style('height', '14px')
 
4634
              .style('padding', '0 1px')
 
4635
              .style('display', function(d) { return icon(d) ? 'inline-block' : 'none'; })
 
4636
              .on('click', click);
 
4637
        }
 
4638
 
 
4639
 
 
4640
        nodeName.each(function(d) {
 
4641
          if (!index && getUrl(d))
 
4642
            d3.select(this)
 
4643
              .append('a')
 
4644
              .attr('href',getUrl)
 
4645
              .attr('class', d3.functor(column.classes))
 
4646
              .append('span')
 
4647
          else
 
4648
            d3.select(this)
 
4649
              .append('span')
 
4650
 
 
4651
            d3.select(this).select('span')
 
4652
              .attr('class', d3.functor(column.classes) )
 
4653
              .text(function(d) { return column.format ? (d[column.key] ? column.format(d[column.key]) : '-') :  (d[column.key] || '-'); });
 
4654
          });
 
4655
 
 
4656
        if  (column.showCount) {
 
4657
          nodeName.append('span')
 
4658
              .attr('class', 'nv-childrenCount');
 
4659
 
 
4660
          node.selectAll('span.nv-childrenCount').text(function(d) {
 
4661
                return ((d.values && d.values.length) || (d._values && d._values.length)) ?                                   //If this is a parent
 
4662
                    '(' + ((d.values && (d.values.filter(function(d) { return filterZero ? filterZero(d) :  true; }).length)) //If children are in values check its children and filter
 
4663
                    || (d._values && d._values.filter(function(d) { return filterZero ? filterZero(d) :  true; }).length)     //Otherwise, do the same, but with the other name, _values...
 
4664
                    || 0) + ')'                                                                                               //This is the catch-all in case there are no children after a filter
 
4665
                    : ''                                                                                                     //If this is not a parent, just give an empty string
 
4666
            });
 
4667
        }
 
4668
 
 
4669
        // if (column.click)
 
4670
        //   nodeName.select('span').on('click', column.click);
 
4671
 
 
4672
      });
 
4673
 
 
4674
      node
 
4675
        .order()
 
4676
        .on('click', function(d) { 
 
4677
          dispatch.elementClick({
 
4678
            row: this, //TODO: decide whether or not this should be consistent with scatter/line events or should be an html link (a href)
 
4679
            data: d,
 
4680
            pos: [d.x, d.y]
 
4681
          });
 
4682
        })
 
4683
        .on('dblclick', function(d) { 
 
4684
          dispatch.elementDblclick({
 
4685
            row: this,
 
4686
            data: d,
 
4687
            pos: [d.x, d.y]
 
4688
          });
 
4689
        })
 
4690
        .on('mouseover', function(d) { 
 
4691
          dispatch.elementMouseover({
 
4692
            row: this,
 
4693
            data: d,
 
4694
            pos: [d.x, d.y]
 
4695
          });
 
4696
        })
 
4697
        .on('mouseout', function(d) { 
 
4698
          dispatch.elementMouseout({
 
4699
            row: this,
 
4700
            data: d,
 
4701
            pos: [d.x, d.y]
 
4702
          });
 
4703
        });
 
4704
 
 
4705
 
 
4706
 
 
4707
 
 
4708
      // Toggle children on click.
 
4709
      function click(d, _, unshift) {
 
4710
        d3.event.stopPropagation();
 
4711
 
 
4712
        if(d3.event.shiftKey && !unshift) {
 
4713
          //If you shift-click, it'll toggle fold all the children, instead of itself
 
4714
          d3.event.shiftKey = false;
 
4715
          d.values && d.values.forEach(function(node){
 
4716
            if (node.values || node._values) {
 
4717
              click(node, 0, true);
 
4718
            }
 
4719
          });
 
4720
          return true;
 
4721
        }
 
4722
        if(!hasChildren(d)) {
 
4723
          //download file
 
4724
          //window.location.href = d.url;
 
4725
          return true;
 
4726
        }
 
4727
        if (d.values) {
 
4728
          d._values = d.values;
 
4729
          d.values = null;
 
4730
        } else {
 
4731
          d.values = d._values;
 
4732
          d._values = null;
 
4733
        }
 
4734
        chart.update();
 
4735
      }
 
4736
 
 
4737
 
 
4738
      function icon(d) {
 
4739
        return (d._values && d._values.length) ? iconOpen : (d.values && d.values.length) ? iconClose : '';
 
4740
      }
 
4741
 
 
4742
      function folded(d) {
 
4743
        return (d._values && d._values.length);
 
4744
      }
 
4745
 
 
4746
      function hasChildren(d) {
 
4747
        var values = d.values || d._values;
 
4748
 
 
4749
        return (values && values.length);
 
4750
      }
 
4751
 
 
4752
 
 
4753
    });
 
4754
 
 
4755
    return chart;
 
4756
  }
 
4757
 
 
4758
 
 
4759
  //============================================================
 
4760
  // Expose Public Variables
 
4761
  //------------------------------------------------------------
 
4762
  chart.options = nv.utils.optionsFunc.bind(chart);
 
4763
  
 
4764
  chart.margin = function(_) {
 
4765
    if (!arguments.length) return margin;
 
4766
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
4767
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
4768
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
4769
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
4770
    return chart;
 
4771
  };
 
4772
 
 
4773
  chart.width = function(_) {
 
4774
    if (!arguments.length) return width;
 
4775
    width = _;
 
4776
    return chart;
 
4777
  };
 
4778
 
 
4779
  chart.height = function(_) {
 
4780
    if (!arguments.length) return height;
 
4781
    height = _;
 
4782
    return chart;
 
4783
  };
 
4784
 
 
4785
  chart.color = function(_) {
 
4786
    if (!arguments.length) return color;
 
4787
    color = nv.utils.getColor(_);
 
4788
    scatter.color(color);
 
4789
    return chart;
 
4790
  };
 
4791
 
 
4792
  chart.id = function(_) {
 
4793
    if (!arguments.length) return id;
 
4794
    id = _;
 
4795
    return chart;
 
4796
  };
 
4797
 
 
4798
  chart.header = function(_) {
 
4799
    if (!arguments.length) return header;
 
4800
    header = _;
 
4801
    return chart;
 
4802
  };
 
4803
 
 
4804
  chart.noData = function(_) {
 
4805
    if (!arguments.length) return noData;
 
4806
    noData = _;
 
4807
    return chart;
 
4808
  };
 
4809
 
 
4810
  chart.filterZero = function(_) {
 
4811
    if (!arguments.length) return filterZero;
 
4812
    filterZero = _;
 
4813
    return chart;
 
4814
  };
 
4815
 
 
4816
  chart.columns = function(_) {
 
4817
    if (!arguments.length) return columns;
 
4818
    columns = _;
 
4819
    return chart;
 
4820
  };
 
4821
 
 
4822
  chart.tableClass = function(_) {
 
4823
    if (!arguments.length) return tableClass;
 
4824
    tableClass = _;
 
4825
    return chart;
 
4826
  };
 
4827
 
 
4828
  chart.iconOpen = function(_){
 
4829
     if (!arguments.length) return iconOpen;
 
4830
    iconOpen = _;
 
4831
    return chart;
 
4832
  }
 
4833
 
 
4834
  chart.iconClose = function(_){
 
4835
     if (!arguments.length) return iconClose;
 
4836
    iconClose = _;
 
4837
    return chart;
 
4838
  }
 
4839
 
 
4840
  chart.getUrl = function(_){
 
4841
     if (!arguments.length) return getUrl;
 
4842
    getUrl = _;
 
4843
    return chart;
 
4844
  }
 
4845
 
 
4846
  //============================================================
 
4847
 
 
4848
 
 
4849
  return chart;
 
4850
};nv.models.legend = function() {
 
4851
  "use strict";
 
4852
  //============================================================
 
4853
  // Public Variables with Default Settings
 
4854
  //------------------------------------------------------------
 
4855
 
 
4856
  var margin = {top: 5, right: 0, bottom: 5, left: 0}
 
4857
    , width = 400
 
4858
    , height = 20
 
4859
    , getKey = function(d) { return d.key }
 
4860
    , color = nv.utils.defaultColor()
 
4861
    , align = true
 
4862
    , rightAlign = true
 
4863
    , updateState = true   //If true, legend will update data.disabled and trigger a 'stateChange' dispatch.
 
4864
    , radioButtonMode = false   //If true, clicking legend items will cause it to behave like a radio button. (only one can be selected at a time)
 
4865
    , dispatch = d3.dispatch('legendClick', 'legendDblclick', 'legendMouseover', 'legendMouseout', 'stateChange')
 
4866
    ;
 
4867
 
 
4868
  //============================================================
 
4869
 
 
4870
 
 
4871
  function chart(selection) {
 
4872
    selection.each(function(data) {
 
4873
      var availableWidth = width - margin.left - margin.right,
 
4874
          container = d3.select(this);
 
4875
 
 
4876
 
 
4877
      //------------------------------------------------------------
 
4878
      // Setup containers and skeleton of chart
 
4879
 
 
4880
      var wrap = container.selectAll('g.nv-legend').data([data]);
 
4881
      var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-legend').append('g');
 
4882
      var g = wrap.select('g');
 
4883
 
 
4884
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
4885
 
 
4886
      //------------------------------------------------------------
 
4887
 
 
4888
 
 
4889
      var series = g.selectAll('.nv-series')
 
4890
          .data(function(d) { return d });
 
4891
      var seriesEnter = series.enter().append('g').attr('class', 'nv-series')
 
4892
          .on('mouseover', function(d,i) {
 
4893
            dispatch.legendMouseover(d,i);  //TODO: Make consistent with other event objects
 
4894
          })
 
4895
          .on('mouseout', function(d,i) {
 
4896
            dispatch.legendMouseout(d,i);
 
4897
          })
 
4898
          .on('click', function(d,i) {
 
4899
            dispatch.legendClick(d,i);
 
4900
            if (updateState) {
 
4901
               if (radioButtonMode) {
 
4902
                   //Radio button mode: set every series to disabled,
 
4903
                   //  and enable the clicked series.
 
4904
                   data.forEach(function(series) { series.disabled = true});
 
4905
                   d.disabled = false;
 
4906
               }
 
4907
               else {
 
4908
                   d.disabled = !d.disabled;
 
4909
                   if (data.every(function(series) { return series.disabled})) {
 
4910
                       //the default behavior of NVD3 legends is, if every single series
 
4911
                       // is disabled, turn all series' back on.
 
4912
                       data.forEach(function(series) { series.disabled = false});
 
4913
                   }
 
4914
               }
 
4915
               dispatch.stateChange({
 
4916
                  disabled: data.map(function(d) { return !!d.disabled })
 
4917
               });
 
4918
            }
 
4919
          })
 
4920
          .on('dblclick', function(d,i) {
 
4921
            dispatch.legendDblclick(d,i);
 
4922
            if (updateState) {
 
4923
                //the default behavior of NVD3 legends, when double clicking one,
 
4924
                // is to set all other series' to false, and make the double clicked series enabled.
 
4925
                data.forEach(function(series) {
 
4926
                   series.disabled = true;
 
4927
                });
 
4928
                d.disabled = false;
 
4929
                dispatch.stateChange({
 
4930
                    disabled: data.map(function(d) { return !!d.disabled })
 
4931
                });
 
4932
            }
 
4933
          });
 
4934
      seriesEnter.append('circle')
 
4935
          .style('stroke-width', 2)
 
4936
          .attr('class','nv-legend-symbol')
 
4937
          .attr('r', 5);
 
4938
      seriesEnter.append('text')
 
4939
          .attr('text-anchor', 'start')
 
4940
          .attr('class','nv-legend-text')
 
4941
          .attr('dy', '.32em')
 
4942
          .attr('dx', '8');
 
4943
      series.classed('disabled', function(d) { return d.disabled });
 
4944
      series.exit().remove();
 
4945
      series.select('circle')
 
4946
          .style('fill', function(d,i) { return d.color || color(d,i)})
 
4947
          .style('stroke', function(d,i) { return d.color || color(d, i) });
 
4948
      series.select('text').text(getKey);
 
4949
 
 
4950
 
 
4951
      //TODO: implement fixed-width and max-width options (max-width is especially useful with the align option)
 
4952
 
 
4953
      // NEW ALIGNING CODE, TODO: clean up
 
4954
      if (align) {
 
4955
 
 
4956
        var seriesWidths = [];
 
4957
        series.each(function(d,i) {
 
4958
              var legendText = d3.select(this).select('text');
 
4959
              var nodeTextLength;
 
4960
              try {
 
4961
                nodeTextLength = legendText.getComputedTextLength();
 
4962
                // If the legendText is display:none'd (nodeTextLength == 0), simulate an error so we approximate, instead
 
4963
                if(nodeTextLength <= 0) throw Error();
 
4964
              }
 
4965
              catch(e) {
 
4966
                nodeTextLength = nv.utils.calcApproxTextWidth(legendText);
 
4967
              }
 
4968
 
 
4969
              seriesWidths.push(nodeTextLength + 28); // 28 is ~ the width of the circle plus some padding
 
4970
            });
 
4971
 
 
4972
        var seriesPerRow = 0;
 
4973
        var legendWidth = 0;
 
4974
        var columnWidths = [];
 
4975
 
 
4976
        while ( legendWidth < availableWidth && seriesPerRow < seriesWidths.length) {
 
4977
          columnWidths[seriesPerRow] = seriesWidths[seriesPerRow];
 
4978
          legendWidth += seriesWidths[seriesPerRow++];
 
4979
        }
 
4980
        if (seriesPerRow === 0) seriesPerRow = 1; //minimum of one series per row
 
4981
 
 
4982
 
 
4983
        while ( legendWidth > availableWidth && seriesPerRow > 1 ) {
 
4984
          columnWidths = [];
 
4985
          seriesPerRow--;
 
4986
 
 
4987
          for (var k = 0; k < seriesWidths.length; k++) {
 
4988
            if (seriesWidths[k] > (columnWidths[k % seriesPerRow] || 0) )
 
4989
              columnWidths[k % seriesPerRow] = seriesWidths[k];
 
4990
          }
 
4991
 
 
4992
          legendWidth = columnWidths.reduce(function(prev, cur, index, array) {
 
4993
                          return prev + cur;
 
4994
                        });
 
4995
        }
 
4996
 
 
4997
        var xPositions = [];
 
4998
        for (var i = 0, curX = 0; i < seriesPerRow; i++) {
 
4999
            xPositions[i] = curX;
 
5000
            curX += columnWidths[i];
 
5001
        }
 
5002
 
 
5003
        series
 
5004
            .attr('transform', function(d, i) {
 
5005
              return 'translate(' + xPositions[i % seriesPerRow] + ',' + (5 + Math.floor(i / seriesPerRow) * 20) + ')';
 
5006
            });
 
5007
 
 
5008
        //position legend as far right as possible within the total width
 
5009
        if (rightAlign) {
 
5010
           g.attr('transform', 'translate(' + (width - margin.right - legendWidth) + ',' + margin.top + ')');
 
5011
        }
 
5012
        else {
 
5013
           g.attr('transform', 'translate(0' + ',' + margin.top + ')');
 
5014
        }
 
5015
 
 
5016
        height = margin.top + margin.bottom + (Math.ceil(seriesWidths.length / seriesPerRow) * 20);
 
5017
 
 
5018
      } else {
 
5019
 
 
5020
        var ypos = 5,
 
5021
            newxpos = 5,
 
5022
            maxwidth = 0,
 
5023
            xpos;
 
5024
        series
 
5025
            .attr('transform', function(d, i) {
 
5026
              var length = d3.select(this).select('text').node().getComputedTextLength() + 28;
 
5027
              xpos = newxpos;
 
5028
 
 
5029
              if (width < margin.left + margin.right + xpos + length) {
 
5030
                newxpos = xpos = 5;
 
5031
                ypos += 20;
 
5032
              }
 
5033
 
 
5034
              newxpos += length;
 
5035
              if (newxpos > maxwidth) maxwidth = newxpos;
 
5036
 
 
5037
              return 'translate(' + xpos + ',' + ypos + ')';
 
5038
            });
 
5039
 
 
5040
        //position legend as far right as possible within the total width
 
5041
        g.attr('transform', 'translate(' + (width - margin.right - maxwidth) + ',' + margin.top + ')');
 
5042
 
 
5043
        height = margin.top + margin.bottom + ypos + 15;
 
5044
 
 
5045
      }
 
5046
 
 
5047
    });
 
5048
 
 
5049
    return chart;
 
5050
  }
 
5051
 
 
5052
 
 
5053
  //============================================================
 
5054
  // Expose Public Variables
 
5055
  //------------------------------------------------------------
 
5056
 
 
5057
  chart.dispatch = dispatch;
 
5058
  chart.options = nv.utils.optionsFunc.bind(chart);
 
5059
 
 
5060
  chart.margin = function(_) {
 
5061
    if (!arguments.length) return margin;
 
5062
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
5063
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
5064
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
5065
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
5066
    return chart;
 
5067
  };
 
5068
 
 
5069
  chart.width = function(_) {
 
5070
    if (!arguments.length) return width;
 
5071
    width = _;
 
5072
    return chart;
 
5073
  };
 
5074
 
 
5075
  chart.height = function(_) {
 
5076
    if (!arguments.length) return height;
 
5077
    height = _;
 
5078
    return chart;
 
5079
  };
 
5080
 
 
5081
  chart.key = function(_) {
 
5082
    if (!arguments.length) return getKey;
 
5083
    getKey = _;
 
5084
    return chart;
 
5085
  };
 
5086
 
 
5087
  chart.color = function(_) {
 
5088
    if (!arguments.length) return color;
 
5089
    color = nv.utils.getColor(_);
 
5090
    return chart;
 
5091
  };
 
5092
 
 
5093
  chart.align = function(_) {
 
5094
    if (!arguments.length) return align;
 
5095
    align = _;
 
5096
    return chart;
 
5097
  };
 
5098
 
 
5099
  chart.rightAlign = function(_) {
 
5100
    if (!arguments.length) return rightAlign;
 
5101
    rightAlign = _;
 
5102
    return chart;
 
5103
  };
 
5104
 
 
5105
  chart.updateState = function(_) {
 
5106
    if (!arguments.length) return updateState;
 
5107
    updateState = _;
 
5108
    return chart;
 
5109
  };
 
5110
 
 
5111
  chart.radioButtonMode = function(_) {
 
5112
    if (!arguments.length) return radioButtonMode;
 
5113
    radioButtonMode = _;
 
5114
    return chart;
 
5115
  };
 
5116
 
 
5117
  //============================================================
 
5118
 
 
5119
 
 
5120
  return chart;
 
5121
}
 
5122
 
 
5123
nv.models.line = function() {
 
5124
  "use strict";
 
5125
  //============================================================
 
5126
  // Public Variables with Default Settings
 
5127
  //------------------------------------------------------------
 
5128
 
 
5129
  var  scatter = nv.models.scatter()
 
5130
    ;
 
5131
 
 
5132
  var margin = {top: 0, right: 0, bottom: 0, left: 0}
 
5133
    , width = 960
 
5134
    , height = 500
 
5135
    , color = nv.utils.defaultColor() // a function that returns a color
 
5136
    , getX = function(d) { return d.x } // accessor to get the x value from a data point
 
5137
    , getY = function(d) { return d.y } // accessor to get the y value from a data point
 
5138
    , 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
 
5139
    , isArea = function(d) { return d.area } // decides if a line is an area or just a line
 
5140
    , clipEdge = false // if true, masks lines within x and y scale
 
5141
    , x //can be accessed via chart.xScale()
 
5142
    , y //can be accessed via chart.yScale()
 
5143
    , interpolate = "linear" // controls the line interpolation
 
5144
    ;
 
5145
 
 
5146
  scatter
 
5147
    .size(16) // default size
 
5148
    .sizeDomain([16,256]) //set to speed up calculation, needs to be unset if there is a custom size accessor
 
5149
    ;
 
5150
 
 
5151
  //============================================================
 
5152
 
 
5153
 
 
5154
  //============================================================
 
5155
  // Private Variables
 
5156
  //------------------------------------------------------------
 
5157
 
 
5158
  var x0, y0 //used to store previous scales
 
5159
      ;
 
5160
 
 
5161
  //============================================================
 
5162
 
 
5163
 
 
5164
  function chart(selection) {
 
5165
    selection.each(function(data) {
 
5166
      var availableWidth = width - margin.left - margin.right,
 
5167
          availableHeight = height - margin.top - margin.bottom,
 
5168
          container = d3.select(this);
 
5169
 
 
5170
      //------------------------------------------------------------
 
5171
      // Setup Scales
 
5172
 
 
5173
      x = scatter.xScale();
 
5174
      y = scatter.yScale();
 
5175
 
 
5176
      x0 = x0 || x;
 
5177
      y0 = y0 || y;
 
5178
 
 
5179
      //------------------------------------------------------------
 
5180
 
 
5181
 
 
5182
      //------------------------------------------------------------
 
5183
      // Setup containers and skeleton of chart
 
5184
 
 
5185
      var wrap = container.selectAll('g.nv-wrap.nv-line').data([data]);
 
5186
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-line');
 
5187
      var defsEnter = wrapEnter.append('defs');
 
5188
      var gEnter = wrapEnter.append('g');
 
5189
      var g = wrap.select('g')
 
5190
 
 
5191
      gEnter.append('g').attr('class', 'nv-groups');
 
5192
      gEnter.append('g').attr('class', 'nv-scatterWrap');
 
5193
 
 
5194
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
5195
 
 
5196
      //------------------------------------------------------------
 
5197
 
 
5198
 
 
5199
 
 
5200
 
 
5201
      scatter
 
5202
        .width(availableWidth)
 
5203
        .height(availableHeight)
 
5204
 
 
5205
      var scatterWrap = wrap.select('.nv-scatterWrap');
 
5206
          //.datum(data); // Data automatically trickles down from the wrap
 
5207
 
 
5208
      scatterWrap.transition().call(scatter);
 
5209
 
 
5210
 
 
5211
 
 
5212
      defsEnter.append('clipPath')
 
5213
          .attr('id', 'nv-edge-clip-' + scatter.id())
 
5214
        .append('rect');
 
5215
 
 
5216
      wrap.select('#nv-edge-clip-' + scatter.id() + ' rect')
 
5217
          .attr('width', availableWidth)
 
5218
          .attr('height', (availableHeight > 0) ? availableHeight : 0);
 
5219
 
 
5220
      g   .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : '');
 
5221
      scatterWrap
 
5222
          .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + scatter.id() + ')' : '');
 
5223
 
 
5224
 
 
5225
 
 
5226
 
 
5227
      var groups = wrap.select('.nv-groups').selectAll('.nv-group')
 
5228
          .data(function(d) { return d }, function(d) { return d.key });
 
5229
      groups.enter().append('g')
 
5230
          .style('stroke-opacity', 1e-6)
 
5231
          .style('fill-opacity', 1e-6);
 
5232
 
 
5233
      groups.exit().remove();
 
5234
 
 
5235
      groups
 
5236
          .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
 
5237
          .classed('hover', function(d) { return d.hover })
 
5238
          .style('fill', function(d,i){ return color(d, i) })
 
5239
          .style('stroke', function(d,i){ return color(d, i)});
 
5240
      groups
 
5241
          .transition()
 
5242
          .style('stroke-opacity', 1)
 
5243
          .style('fill-opacity', .5);
 
5244
 
 
5245
 
 
5246
 
 
5247
      var areaPaths = groups.selectAll('path.nv-area')
 
5248
          .data(function(d) { return isArea(d) ? [d] : [] }); // this is done differently than lines because I need to check if series is an area
 
5249
      areaPaths.enter().append('path')
 
5250
          .attr('class', 'nv-area')
 
5251
          .attr('d', function(d) {
 
5252
            return d3.svg.area()
 
5253
                .interpolate(interpolate)
 
5254
                .defined(defined)
 
5255
                .x(function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) })
 
5256
                .y0(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
 
5257
                .y1(function(d,i) { return y0( y.domain()[0] <= 0 ? y.domain()[1] >= 0 ? 0 : y.domain()[1] : y.domain()[0] ) })
 
5258
                //.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
 
5259
                .apply(this, [d.values])
 
5260
          });
 
5261
      groups.exit().selectAll('path.nv-area')
 
5262
           .remove();
 
5263
 
 
5264
      areaPaths
 
5265
          .transition()
 
5266
          .attr('d', function(d) {
 
5267
            return d3.svg.area()
 
5268
                .interpolate(interpolate)
 
5269
                .defined(defined)
 
5270
                .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
 
5271
                .y0(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
 
5272
                .y1(function(d,i) { return y( y.domain()[0] <= 0 ? y.domain()[1] >= 0 ? 0 : y.domain()[1] : y.domain()[0] ) })
 
5273
                //.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
 
5274
                .apply(this, [d.values])
 
5275
          });
 
5276
 
 
5277
 
 
5278
 
 
5279
      var linePaths = groups.selectAll('path.nv-line')
 
5280
          .data(function(d) { return [d.values] });
 
5281
      linePaths.enter().append('path')
 
5282
          .attr('class', 'nv-line')
 
5283
          .attr('d',
 
5284
            d3.svg.line()
 
5285
              .interpolate(interpolate)
 
5286
              .defined(defined)
 
5287
              .x(function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) })
 
5288
              .y(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
 
5289
          );
 
5290
 
 
5291
      linePaths
 
5292
          .transition()
 
5293
          .attr('d',
 
5294
            d3.svg.line()
 
5295
              .interpolate(interpolate)
 
5296
              .defined(defined)
 
5297
              .x(function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
 
5298
              .y(function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
 
5299
          );
 
5300
 
 
5301
 
 
5302
 
 
5303
      //store old scales for use in transitions on update
 
5304
      x0 = x.copy();
 
5305
      y0 = y.copy();
 
5306
 
 
5307
    });
 
5308
 
 
5309
    return chart;
 
5310
  }
 
5311
 
 
5312
 
 
5313
  //============================================================
 
5314
  // Expose Public Variables
 
5315
  //------------------------------------------------------------
 
5316
 
 
5317
  chart.dispatch = scatter.dispatch;
 
5318
  chart.scatter = scatter;
 
5319
 
 
5320
  d3.rebind(chart, scatter, 'id', 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange',
 
5321
    'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'useVoronoi', 'clipRadius', 'padData','highlightPoint','clearHighlights');
 
5322
 
 
5323
  chart.options = nv.utils.optionsFunc.bind(chart);
 
5324
 
 
5325
  chart.margin = function(_) {
 
5326
    if (!arguments.length) return margin;
 
5327
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
5328
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
5329
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
5330
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
5331
    return chart;
 
5332
  };
 
5333
 
 
5334
  chart.width = function(_) {
 
5335
    if (!arguments.length) return width;
 
5336
    width = _;
 
5337
    return chart;
 
5338
  };
 
5339
 
 
5340
  chart.height = function(_) {
 
5341
    if (!arguments.length) return height;
 
5342
    height = _;
 
5343
    return chart;
 
5344
  };
 
5345
 
 
5346
  chart.x = function(_) {
 
5347
    if (!arguments.length) return getX;
 
5348
    getX = _;
 
5349
    scatter.x(_);
 
5350
    return chart;
 
5351
  };
 
5352
 
 
5353
  chart.y = function(_) {
 
5354
    if (!arguments.length) return getY;
 
5355
    getY = _;
 
5356
    scatter.y(_);
 
5357
    return chart;
 
5358
  };
 
5359
 
 
5360
  chart.clipEdge = function(_) {
 
5361
    if (!arguments.length) return clipEdge;
 
5362
    clipEdge = _;
 
5363
    return chart;
 
5364
  };
 
5365
 
 
5366
  chart.color = function(_) {
 
5367
    if (!arguments.length) return color;
 
5368
    color = nv.utils.getColor(_);
 
5369
    scatter.color(color);
 
5370
    return chart;
 
5371
  };
 
5372
 
 
5373
  chart.interpolate = function(_) {
 
5374
    if (!arguments.length) return interpolate;
 
5375
    interpolate = _;
 
5376
    return chart;
 
5377
  };
 
5378
 
 
5379
  chart.defined = function(_) {
 
5380
    if (!arguments.length) return defined;
 
5381
    defined = _;
 
5382
    return chart;
 
5383
  };
 
5384
 
 
5385
  chart.isArea = function(_) {
 
5386
    if (!arguments.length) return isArea;
 
5387
    isArea = d3.functor(_);
 
5388
    return chart;
 
5389
  };
 
5390
 
 
5391
  //============================================================
 
5392
 
 
5393
 
 
5394
  return chart;
 
5395
}
 
5396
 
 
5397
nv.models.lineChart = function() {
 
5398
  "use strict";
 
5399
  //============================================================
 
5400
  // Public Variables with Default Settings
 
5401
  //------------------------------------------------------------
 
5402
 
 
5403
  var lines = nv.models.line()
 
5404
    , xAxis = nv.models.axis()
 
5405
    , yAxis = nv.models.axis()
 
5406
    , legend = nv.models.legend()
 
5407
    , interactiveLayer = nv.interactiveGuideline()
 
5408
    ;
 
5409
 
 
5410
  var margin = {top: 30, right: 20, bottom: 50, left: 60}
 
5411
    , color = nv.utils.defaultColor()
 
5412
    , width = null
 
5413
    , height = null
 
5414
    , showLegend = true
 
5415
    , showXAxis = true
 
5416
    , showYAxis = true
 
5417
    , rightAlignYAxis = false
 
5418
    , useInteractiveGuideline = false
 
5419
    , tooltips = true
 
5420
    , tooltip = function(key, x, y, e, graph) {
 
5421
        return '<h3>' + key + '</h3>' +
 
5422
               '<p>' +  y + ' at ' + x + '</p>'
 
5423
      }
 
5424
    , x
 
5425
    , y
 
5426
    , state = {}
 
5427
    , defaultState = null
 
5428
    , noData = 'No Data Available.'
 
5429
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
 
5430
    , transitionDuration = 250
 
5431
    ;
 
5432
 
 
5433
  xAxis
 
5434
    .orient('bottom')
 
5435
    .tickPadding(7)
 
5436
    ;
 
5437
  yAxis
 
5438
    .orient((rightAlignYAxis) ? 'right' : 'left')
 
5439
    ;
 
5440
 
 
5441
  //============================================================
 
5442
 
 
5443
 
 
5444
  //============================================================
 
5445
  // Private Variables
 
5446
  //------------------------------------------------------------
 
5447
 
 
5448
  var showTooltip = function(e, offsetElement) {
 
5449
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
5450
        top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
5451
        x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
 
5452
        y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
 
5453
        content = tooltip(e.series.key, x, y, e, chart);
 
5454
 
 
5455
    nv.tooltip.show([left, top], content, null, null, offsetElement);
 
5456
  };
 
5457
 
 
5458
  //============================================================
 
5459
 
 
5460
 
 
5461
  function chart(selection) {
 
5462
    selection.each(function(data) {
 
5463
      var container = d3.select(this),
 
5464
          that = this;
 
5465
 
 
5466
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
5467
                             - margin.left - margin.right,
 
5468
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
5469
                             - margin.top - margin.bottom;
 
5470
 
 
5471
 
 
5472
      chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
 
5473
      chart.container = this;
 
5474
 
 
5475
      //set state.disabled
 
5476
      state.disabled = data.map(function(d) { return !!d.disabled });
 
5477
 
 
5478
 
 
5479
      if (!defaultState) {
 
5480
        var key;
 
5481
        defaultState = {};
 
5482
        for (key in state) {
 
5483
          if (state[key] instanceof Array)
 
5484
            defaultState[key] = state[key].slice(0);
 
5485
          else
 
5486
            defaultState[key] = state[key];
 
5487
        }
 
5488
      }
 
5489
 
 
5490
      //------------------------------------------------------------
 
5491
      // Display noData message if there's nothing to show.
 
5492
 
 
5493
      if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
 
5494
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
5495
 
 
5496
        noDataText.enter().append('text')
 
5497
          .attr('class', 'nvd3 nv-noData')
 
5498
          .attr('dy', '-.7em')
 
5499
          .style('text-anchor', 'middle');
 
5500
 
 
5501
        noDataText
 
5502
          .attr('x', margin.left + availableWidth / 2)
 
5503
          .attr('y', margin.top + availableHeight / 2)
 
5504
          .text(function(d) { return d });
 
5505
 
 
5506
        return chart;
 
5507
      } else {
 
5508
        container.selectAll('.nv-noData').remove();
 
5509
      }
 
5510
 
 
5511
      //------------------------------------------------------------
 
5512
 
 
5513
 
 
5514
      //------------------------------------------------------------
 
5515
      // Setup Scales
 
5516
 
 
5517
      x = lines.xScale();
 
5518
      y = lines.yScale();
 
5519
 
 
5520
      //------------------------------------------------------------
 
5521
 
 
5522
 
 
5523
      //------------------------------------------------------------
 
5524
      // Setup containers and skeleton of chart
 
5525
 
 
5526
      var wrap = container.selectAll('g.nv-wrap.nv-lineChart').data([data]);
 
5527
      var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineChart').append('g');
 
5528
      var g = wrap.select('g');
 
5529
 
 
5530
      gEnter.append("rect").style("opacity",0);
 
5531
      gEnter.append('g').attr('class', 'nv-x nv-axis');
 
5532
      gEnter.append('g').attr('class', 'nv-y nv-axis');
 
5533
      gEnter.append('g').attr('class', 'nv-linesWrap');
 
5534
      gEnter.append('g').attr('class', 'nv-legendWrap');
 
5535
      gEnter.append('g').attr('class', 'nv-interactive');
 
5536
 
 
5537
      g.select("rect")
 
5538
        .attr("width",availableWidth)
 
5539
        .attr("height",(availableHeight > 0) ? availableHeight : 0);
 
5540
      //------------------------------------------------------------
 
5541
      // Legend
 
5542
 
 
5543
      if (showLegend) {
 
5544
        legend.width(availableWidth);
 
5545
 
 
5546
        g.select('.nv-legendWrap')
 
5547
            .datum(data)
 
5548
            .call(legend);
 
5549
 
 
5550
        if ( margin.top != legend.height()) {
 
5551
          margin.top = legend.height();
 
5552
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
5553
                             - margin.top - margin.bottom;
 
5554
        }
 
5555
 
 
5556
        wrap.select('.nv-legendWrap')
 
5557
            .attr('transform', 'translate(0,' + (-margin.top) +')')
 
5558
      }
 
5559
 
 
5560
      //------------------------------------------------------------
 
5561
 
 
5562
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
5563
 
 
5564
      if (rightAlignYAxis) {
 
5565
          g.select(".nv-y.nv-axis")
 
5566
              .attr("transform", "translate(" + availableWidth + ",0)");
 
5567
      }
 
5568
 
 
5569
      //------------------------------------------------------------
 
5570
      // Main Chart Component(s)
 
5571
 
 
5572
 
 
5573
      //------------------------------------------------------------
 
5574
      //Set up interactive layer
 
5575
      if (useInteractiveGuideline) {
 
5576
        interactiveLayer
 
5577
           .width(availableWidth)
 
5578
           .height(availableHeight)
 
5579
           .margin({left:margin.left, top:margin.top})
 
5580
           .svgContainer(container)
 
5581
           .xScale(x);
 
5582
        wrap.select(".nv-interactive").call(interactiveLayer);
 
5583
      }
 
5584
 
 
5585
 
 
5586
      lines
 
5587
        .width(availableWidth)
 
5588
        .height(availableHeight)
 
5589
        .color(data.map(function(d,i) {
 
5590
          return d.color || color(d, i);
 
5591
        }).filter(function(d,i) { return !data[i].disabled }));
 
5592
 
 
5593
 
 
5594
      var linesWrap = g.select('.nv-linesWrap')
 
5595
          .datum(data.filter(function(d) { return !d.disabled }))
 
5596
 
 
5597
      linesWrap.transition().call(lines);
 
5598
 
 
5599
      //------------------------------------------------------------
 
5600
 
 
5601
 
 
5602
      //------------------------------------------------------------
 
5603
      // Setup Axes
 
5604
 
 
5605
      if (showXAxis) {
 
5606
        xAxis
 
5607
          .scale(x)
 
5608
          .ticks( availableWidth / 100 )
 
5609
          .tickSize(-availableHeight, 0);
 
5610
 
 
5611
        g.select('.nv-x.nv-axis')
 
5612
            .attr('transform', 'translate(0,' + y.range()[0] + ')');
 
5613
        g.select('.nv-x.nv-axis')
 
5614
            .transition()
 
5615
            .call(xAxis);
 
5616
      }
 
5617
 
 
5618
      if (showYAxis) {
 
5619
        yAxis
 
5620
          .scale(y)
 
5621
          .ticks( availableHeight / 36 )
 
5622
          .tickSize( -availableWidth, 0);
 
5623
 
 
5624
        g.select('.nv-y.nv-axis')
 
5625
            .transition()
 
5626
            .call(yAxis);
 
5627
      }
 
5628
      //------------------------------------------------------------
 
5629
 
 
5630
 
 
5631
      //============================================================
 
5632
      // Event Handling/Dispatching (in chart's scope)
 
5633
      //------------------------------------------------------------
 
5634
 
 
5635
      legend.dispatch.on('stateChange', function(newState) {
 
5636
          state = newState;
 
5637
          dispatch.stateChange(state);
 
5638
          chart.update();
 
5639
      });
 
5640
 
 
5641
      interactiveLayer.dispatch.on('elementMousemove', function(e) {
 
5642
          lines.clearHighlights();
 
5643
          var singlePoint, pointIndex, pointXLocation, allData = [];
 
5644
          data
 
5645
          .filter(function(series, i) {
 
5646
            series.seriesIndex = i;
 
5647
            return !series.disabled;
 
5648
          })
 
5649
          .forEach(function(series,i) {
 
5650
              pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
 
5651
              lines.highlightPoint(i, pointIndex, true);
 
5652
              var point = series.values[pointIndex];
 
5653
              if (typeof point === 'undefined') return;
 
5654
              if (typeof singlePoint === 'undefined') singlePoint = point;
 
5655
              if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
 
5656
              allData.push({
 
5657
                  key: series.key,
 
5658
                  value: chart.y()(point, pointIndex),
 
5659
                  color: color(series,series.seriesIndex)
 
5660
              });
 
5661
          });
 
5662
          //Highlight the tooltip entry based on which point the mouse is closest to.
 
5663
          if (allData.length > 2) {
 
5664
            var yValue = chart.yScale().invert(e.mouseY);
 
5665
            var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]);
 
5666
            var threshold = 0.03 * domainExtent;
 
5667
            var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value}),yValue,threshold);
 
5668
            if (indexToHighlight !== null)
 
5669
              allData[indexToHighlight].highlight = true;
 
5670
          }
 
5671
 
 
5672
          var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
 
5673
          interactiveLayer.tooltip
 
5674
                  .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
 
5675
                  .chartContainer(that.parentNode)
 
5676
                  .enabled(tooltips)
 
5677
                  .valueFormatter(function(d,i) {
 
5678
                     return yAxis.tickFormat()(d);
 
5679
                  })
 
5680
                  .data(
 
5681
                      {
 
5682
                        value: xValue,
 
5683
                        series: allData
 
5684
                      }
 
5685
                  )();
 
5686
 
 
5687
          interactiveLayer.renderGuideLine(pointXLocation);
 
5688
 
 
5689
      });
 
5690
 
 
5691
      interactiveLayer.dispatch.on("elementMouseout",function(e) {
 
5692
          dispatch.tooltipHide();
 
5693
          lines.clearHighlights();
 
5694
      });
 
5695
 
 
5696
      dispatch.on('tooltipShow', function(e) {
 
5697
        if (tooltips) showTooltip(e, that.parentNode);
 
5698
      });
 
5699
 
 
5700
 
 
5701
      dispatch.on('changeState', function(e) {
 
5702
 
 
5703
        if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) {
 
5704
          data.forEach(function(series,i) {
 
5705
            series.disabled = e.disabled[i];
 
5706
          });
 
5707
 
 
5708
          state.disabled = e.disabled;
 
5709
        }
 
5710
 
 
5711
        chart.update();
 
5712
      });
 
5713
 
 
5714
      //============================================================
 
5715
 
 
5716
    });
 
5717
 
 
5718
    return chart;
 
5719
  }
 
5720
 
 
5721
 
 
5722
  //============================================================
 
5723
  // Event Handling/Dispatching (out of chart's scope)
 
5724
  //------------------------------------------------------------
 
5725
 
 
5726
  lines.dispatch.on('elementMouseover.tooltip', function(e) {
 
5727
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
5728
    dispatch.tooltipShow(e);
 
5729
  });
 
5730
 
 
5731
  lines.dispatch.on('elementMouseout.tooltip', function(e) {
 
5732
    dispatch.tooltipHide(e);
 
5733
  });
 
5734
 
 
5735
  dispatch.on('tooltipHide', function() {
 
5736
    if (tooltips) nv.tooltip.cleanup();
 
5737
  });
 
5738
 
 
5739
  //============================================================
 
5740
 
 
5741
 
 
5742
  //============================================================
 
5743
  // Expose Public Variables
 
5744
  //------------------------------------------------------------
 
5745
 
 
5746
  // expose chart's sub-components
 
5747
  chart.dispatch = dispatch;
 
5748
  chart.lines = lines;
 
5749
  chart.legend = legend;
 
5750
  chart.xAxis = xAxis;
 
5751
  chart.yAxis = yAxis;
 
5752
  chart.interactiveLayer = interactiveLayer;
 
5753
 
 
5754
  d3.rebind(chart, lines, 'defined', 'isArea', 'x', 'y', 'size', 'xScale', 'yScale', 'xDomain', 'yDomain', 'xRange', 'yRange'
 
5755
    , 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'useVoronoi','id', 'interpolate');
 
5756
 
 
5757
  chart.options = nv.utils.optionsFunc.bind(chart);
 
5758
 
 
5759
  chart.margin = function(_) {
 
5760
    if (!arguments.length) return margin;
 
5761
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
5762
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
5763
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
5764
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
5765
    return chart;
 
5766
  };
 
5767
 
 
5768
  chart.width = function(_) {
 
5769
    if (!arguments.length) return width;
 
5770
    width = _;
 
5771
    return chart;
 
5772
  };
 
5773
 
 
5774
  chart.height = function(_) {
 
5775
    if (!arguments.length) return height;
 
5776
    height = _;
 
5777
    return chart;
 
5778
  };
 
5779
 
 
5780
  chart.color = function(_) {
 
5781
    if (!arguments.length) return color;
 
5782
    color = nv.utils.getColor(_);
 
5783
    legend.color(color);
 
5784
    return chart;
 
5785
  };
 
5786
 
 
5787
  chart.showLegend = function(_) {
 
5788
    if (!arguments.length) return showLegend;
 
5789
    showLegend = _;
 
5790
    return chart;
 
5791
  };
 
5792
 
 
5793
  chart.showXAxis = function(_) {
 
5794
    if (!arguments.length) return showXAxis;
 
5795
    showXAxis = _;
 
5796
    return chart;
 
5797
  };
 
5798
 
 
5799
  chart.showYAxis = function(_) {
 
5800
    if (!arguments.length) return showYAxis;
 
5801
    showYAxis = _;
 
5802
    return chart;
 
5803
  };
 
5804
 
 
5805
  chart.rightAlignYAxis = function(_) {
 
5806
    if(!arguments.length) return rightAlignYAxis;
 
5807
    rightAlignYAxis = _;
 
5808
    yAxis.orient( (_) ? 'right' : 'left');
 
5809
    return chart;
 
5810
  };
 
5811
 
 
5812
  chart.useInteractiveGuideline = function(_) {
 
5813
    if(!arguments.length) return useInteractiveGuideline;
 
5814
    useInteractiveGuideline = _;
 
5815
    if (_ === true) {
 
5816
       chart.interactive(false);
 
5817
       chart.useVoronoi(false);
 
5818
    }
 
5819
    return chart;
 
5820
  };
 
5821
 
 
5822
  chart.tooltips = function(_) {
 
5823
    if (!arguments.length) return tooltips;
 
5824
    tooltips = _;
 
5825
    return chart;
 
5826
  };
 
5827
 
 
5828
  chart.tooltipContent = function(_) {
 
5829
    if (!arguments.length) return tooltip;
 
5830
    tooltip = _;
 
5831
    return chart;
 
5832
  };
 
5833
 
 
5834
  chart.state = function(_) {
 
5835
    if (!arguments.length) return state;
 
5836
    state = _;
 
5837
    return chart;
 
5838
  };
 
5839
 
 
5840
  chart.defaultState = function(_) {
 
5841
    if (!arguments.length) return defaultState;
 
5842
    defaultState = _;
 
5843
    return chart;
 
5844
  };
 
5845
 
 
5846
  chart.noData = function(_) {
 
5847
    if (!arguments.length) return noData;
 
5848
    noData = _;
 
5849
    return chart;
 
5850
  };
 
5851
 
 
5852
  chart.transitionDuration = function(_) {
 
5853
    if (!arguments.length) return transitionDuration;
 
5854
    transitionDuration = _;
 
5855
    return chart;
 
5856
  };
 
5857
 
 
5858
  //============================================================
 
5859
 
 
5860
 
 
5861
  return chart;
 
5862
}
 
5863
 
 
5864
nv.models.linePlusBarChart = function() {
 
5865
  "use strict";
 
5866
  //============================================================
 
5867
  // Public Variables with Default Settings
 
5868
  //------------------------------------------------------------
 
5869
 
 
5870
  var lines = nv.models.line()
 
5871
    , bars = nv.models.historicalBar()
 
5872
    , xAxis = nv.models.axis()
 
5873
    , y1Axis = nv.models.axis()
 
5874
    , y2Axis = nv.models.axis()
 
5875
    , legend = nv.models.legend()
 
5876
    ;
 
5877
 
 
5878
  var margin = {top: 30, right: 60, bottom: 50, left: 60}
 
5879
    , width = null
 
5880
    , height = null
 
5881
    , getX = function(d) { return d.x }
 
5882
    , getY = function(d) { return d.y }
 
5883
    , color = nv.utils.defaultColor()
 
5884
    , showLegend = true
 
5885
    , tooltips = true
 
5886
    , tooltip = function(key, x, y, e, graph) {
 
5887
        return '<h3>' + key + '</h3>' +
 
5888
               '<p>' +  y + ' at ' + x + '</p>';
 
5889
      }
 
5890
    , x
 
5891
    , y1
 
5892
    , y2
 
5893
    , state = {}
 
5894
    , defaultState = null
 
5895
    , noData = "No Data Available."
 
5896
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
 
5897
    ;
 
5898
 
 
5899
  bars
 
5900
    .padData(true)
 
5901
    ;
 
5902
  lines
 
5903
    .clipEdge(false)
 
5904
    .padData(true)
 
5905
    ;
 
5906
  xAxis
 
5907
    .orient('bottom')
 
5908
    .tickPadding(7)
 
5909
    .highlightZero(false)
 
5910
    ;
 
5911
  y1Axis
 
5912
    .orient('left')
 
5913
    ;
 
5914
  y2Axis
 
5915
    .orient('right')
 
5916
    ;
 
5917
 
 
5918
  //============================================================
 
5919
 
 
5920
 
 
5921
  //============================================================
 
5922
  // Private Variables
 
5923
  //------------------------------------------------------------
 
5924
 
 
5925
  var showTooltip = function(e, offsetElement) {
 
5926
      var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
5927
          top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
5928
          x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
 
5929
          y = (e.series.bar ? y1Axis : y2Axis).tickFormat()(lines.y()(e.point, e.pointIndex)),
 
5930
          content = tooltip(e.series.key, x, y, e, chart);
 
5931
 
 
5932
      nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
 
5933
    }
 
5934
    ;
 
5935
 
 
5936
  //------------------------------------------------------------
 
5937
 
 
5938
 
 
5939
 
 
5940
  function chart(selection) {
 
5941
    selection.each(function(data) {
 
5942
      var container = d3.select(this),
 
5943
          that = this;
 
5944
 
 
5945
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
5946
                             - margin.left - margin.right,
 
5947
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
5948
                             - margin.top - margin.bottom;
 
5949
 
 
5950
      chart.update = function() { container.transition().call(chart); };
 
5951
      // chart.container = this;
 
5952
 
 
5953
      //set state.disabled
 
5954
      state.disabled = data.map(function(d) { return !!d.disabled });
 
5955
 
 
5956
      if (!defaultState) {
 
5957
        var key;
 
5958
        defaultState = {};
 
5959
        for (key in state) {
 
5960
          if (state[key] instanceof Array)
 
5961
            defaultState[key] = state[key].slice(0);
 
5962
          else
 
5963
            defaultState[key] = state[key];
 
5964
        }
 
5965
      }
 
5966
 
 
5967
      //------------------------------------------------------------
 
5968
      // Display No Data message if there's nothing to show.
 
5969
 
 
5970
      if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
 
5971
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
5972
 
 
5973
        noDataText.enter().append('text')
 
5974
          .attr('class', 'nvd3 nv-noData')
 
5975
          .attr('dy', '-.7em')
 
5976
          .style('text-anchor', 'middle');
 
5977
 
 
5978
        noDataText
 
5979
          .attr('x', margin.left + availableWidth / 2)
 
5980
          .attr('y', margin.top + availableHeight / 2)
 
5981
          .text(function(d) { return d });
 
5982
 
 
5983
        return chart;
 
5984
      } else {
 
5985
        container.selectAll('.nv-noData').remove();
 
5986
      }
 
5987
 
 
5988
      //------------------------------------------------------------
 
5989
 
 
5990
 
 
5991
      //------------------------------------------------------------
 
5992
      // Setup Scales
 
5993
 
 
5994
      var dataBars = data.filter(function(d) { return !d.disabled && d.bar });
 
5995
      var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240
 
5996
 
 
5997
      //x = xAxis.scale();
 
5998
       x = dataLines.filter(function(d) { return !d.disabled; }).length && dataLines.filter(function(d) { return !d.disabled; })[0].values.length ? lines.xScale() : bars.xScale();
 
5999
      //x = dataLines.filter(function(d) { return !d.disabled; }).length ? lines.xScale() : bars.xScale(); //old code before change above
 
6000
      y1 = bars.yScale();
 
6001
      y2 = lines.yScale();
 
6002
 
 
6003
      //------------------------------------------------------------
 
6004
 
 
6005
      //------------------------------------------------------------
 
6006
      // Setup containers and skeleton of chart
 
6007
 
 
6008
      var wrap = d3.select(this).selectAll('g.nv-wrap.nv-linePlusBar').data([data]);
 
6009
      var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-linePlusBar').append('g');
 
6010
      var g = wrap.select('g');
 
6011
 
 
6012
      gEnter.append('g').attr('class', 'nv-x nv-axis');
 
6013
      gEnter.append('g').attr('class', 'nv-y1 nv-axis');
 
6014
      gEnter.append('g').attr('class', 'nv-y2 nv-axis');
 
6015
      gEnter.append('g').attr('class', 'nv-barsWrap');
 
6016
      gEnter.append('g').attr('class', 'nv-linesWrap');
 
6017
      gEnter.append('g').attr('class', 'nv-legendWrap');
 
6018
 
 
6019
      //------------------------------------------------------------
 
6020
 
 
6021
 
 
6022
      //------------------------------------------------------------
 
6023
      // Legend
 
6024
 
 
6025
      if (showLegend) {
 
6026
        legend.width( availableWidth / 2 );
 
6027
 
 
6028
        g.select('.nv-legendWrap')
 
6029
            .datum(data.map(function(series) {
 
6030
              series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
 
6031
              series.key = series.originalKey + (series.bar ? ' (left axis)' : ' (right axis)');
 
6032
              return series;
 
6033
            }))
 
6034
          .call(legend);
 
6035
 
 
6036
        if ( margin.top != legend.height()) {
 
6037
          margin.top = legend.height();
 
6038
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
6039
                             - margin.top - margin.bottom;
 
6040
        }
 
6041
 
 
6042
        g.select('.nv-legendWrap')
 
6043
            .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')');
 
6044
      }
 
6045
 
 
6046
      //------------------------------------------------------------
 
6047
 
 
6048
 
 
6049
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
6050
 
 
6051
 
 
6052
      //------------------------------------------------------------
 
6053
      // Main Chart Component(s)
 
6054
 
 
6055
 
 
6056
      lines
 
6057
        .width(availableWidth)
 
6058
        .height(availableHeight)
 
6059
        .color(data.map(function(d,i) {
 
6060
          return d.color || color(d, i);
 
6061
        }).filter(function(d,i) { return !data[i].disabled && !data[i].bar }))
 
6062
 
 
6063
      bars
 
6064
        .width(availableWidth)
 
6065
        .height(availableHeight)
 
6066
        .color(data.map(function(d,i) {
 
6067
          return d.color || color(d, i);
 
6068
        }).filter(function(d,i) { return !data[i].disabled && data[i].bar }))
 
6069
 
 
6070
 
 
6071
 
 
6072
      var barsWrap = g.select('.nv-barsWrap')
 
6073
          .datum(dataBars.length ? dataBars : [{values:[]}])
 
6074
 
 
6075
      var linesWrap = g.select('.nv-linesWrap')
 
6076
          .datum(dataLines[0] && !dataLines[0].disabled ? dataLines : [{values:[]}] );
 
6077
          //.datum(!dataLines[0].disabled ? dataLines : [{values:dataLines[0].values.map(function(d) { return [d[0], null] }) }] );
 
6078
 
 
6079
      d3.transition(barsWrap).call(bars);
 
6080
      d3.transition(linesWrap).call(lines);
 
6081
 
 
6082
      //------------------------------------------------------------
 
6083
 
 
6084
 
 
6085
      //------------------------------------------------------------
 
6086
      // Setup Axes
 
6087
 
 
6088
      xAxis
 
6089
        .scale(x)
 
6090
        .ticks( availableWidth / 100 )
 
6091
        .tickSize(-availableHeight, 0);
 
6092
 
 
6093
      g.select('.nv-x.nv-axis')
 
6094
          .attr('transform', 'translate(0,' + y1.range()[0] + ')');
 
6095
      d3.transition(g.select('.nv-x.nv-axis'))
 
6096
          .call(xAxis);
 
6097
 
 
6098
 
 
6099
      y1Axis
 
6100
        .scale(y1)
 
6101
        .ticks( availableHeight / 36 )
 
6102
        .tickSize(-availableWidth, 0);
 
6103
 
 
6104
      d3.transition(g.select('.nv-y1.nv-axis'))
 
6105
          .style('opacity', dataBars.length ? 1 : 0)
 
6106
          .call(y1Axis);
 
6107
 
 
6108
 
 
6109
      y2Axis
 
6110
        .scale(y2)
 
6111
        .ticks( availableHeight / 36 )
 
6112
        .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
 
6113
 
 
6114
      g.select('.nv-y2.nv-axis')
 
6115
          .style('opacity', dataLines.length ? 1 : 0)
 
6116
          .attr('transform', 'translate(' + availableWidth + ',0)');
 
6117
          //.attr('transform', 'translate(' + x.range()[1] + ',0)');
 
6118
 
 
6119
      d3.transition(g.select('.nv-y2.nv-axis'))
 
6120
          .call(y2Axis);
 
6121
 
 
6122
      //------------------------------------------------------------
 
6123
 
 
6124
 
 
6125
      //============================================================
 
6126
      // Event Handling/Dispatching (in chart's scope)
 
6127
      //------------------------------------------------------------
 
6128
 
 
6129
      legend.dispatch.on('stateChange', function(newState) { 
 
6130
        state = newState;
 
6131
        dispatch.stateChange(state);
 
6132
        chart.update();
 
6133
      });
 
6134
 
 
6135
      dispatch.on('tooltipShow', function(e) {
 
6136
        if (tooltips) showTooltip(e, that.parentNode);
 
6137
      });
 
6138
 
 
6139
 
 
6140
      // Update chart from a state object passed to event handler
 
6141
      dispatch.on('changeState', function(e) {
 
6142
 
 
6143
        if (typeof e.disabled !== 'undefined') {
 
6144
          data.forEach(function(series,i) {
 
6145
            series.disabled = e.disabled[i];
 
6146
          });
 
6147
 
 
6148
          state.disabled = e.disabled;
 
6149
        }
 
6150
 
 
6151
        chart.update();
 
6152
      });
 
6153
 
 
6154
      //============================================================
 
6155
 
 
6156
 
 
6157
    });
 
6158
 
 
6159
    return chart;
 
6160
  }
 
6161
 
 
6162
 
 
6163
  //============================================================
 
6164
  // Event Handling/Dispatching (out of chart's scope)
 
6165
  //------------------------------------------------------------
 
6166
 
 
6167
  lines.dispatch.on('elementMouseover.tooltip', function(e) {
 
6168
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
6169
    dispatch.tooltipShow(e);
 
6170
  });
 
6171
 
 
6172
  lines.dispatch.on('elementMouseout.tooltip', function(e) {
 
6173
    dispatch.tooltipHide(e);
 
6174
  });
 
6175
 
 
6176
  bars.dispatch.on('elementMouseover.tooltip', function(e) {
 
6177
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
6178
    dispatch.tooltipShow(e);
 
6179
  });
 
6180
 
 
6181
  bars.dispatch.on('elementMouseout.tooltip', function(e) {
 
6182
    dispatch.tooltipHide(e);
 
6183
  });
 
6184
 
 
6185
  dispatch.on('tooltipHide', function() {
 
6186
    if (tooltips) nv.tooltip.cleanup();
 
6187
  });
 
6188
 
 
6189
  //============================================================
 
6190
 
 
6191
 
 
6192
  //============================================================
 
6193
  // Expose Public Variables
 
6194
  //------------------------------------------------------------
 
6195
 
 
6196
  // expose chart's sub-components
 
6197
  chart.dispatch = dispatch;
 
6198
  chart.legend = legend;
 
6199
  chart.lines = lines;
 
6200
  chart.bars = bars;
 
6201
  chart.xAxis = xAxis;
 
6202
  chart.y1Axis = y1Axis;
 
6203
  chart.y2Axis = y2Axis;
 
6204
 
 
6205
  d3.rebind(chart, lines, 'defined', 'size', 'clipVoronoi', 'interpolate');
 
6206
  //TODO: consider rebinding x, y and some other stuff, and simply do soemthign lile bars.x(lines.x()), etc.
 
6207
  //d3.rebind(chart, lines, 'x', 'y', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id');
 
6208
 
 
6209
  chart.options = nv.utils.optionsFunc.bind(chart);
 
6210
  
 
6211
  chart.x = function(_) {
 
6212
    if (!arguments.length) return getX;
 
6213
    getX = _;
 
6214
    lines.x(_);
 
6215
    bars.x(_);
 
6216
    return chart;
 
6217
  };
 
6218
 
 
6219
  chart.y = function(_) {
 
6220
    if (!arguments.length) return getY;
 
6221
    getY = _;
 
6222
    lines.y(_);
 
6223
    bars.y(_);
 
6224
    return chart;
 
6225
  };
 
6226
 
 
6227
  chart.margin = function(_) {
 
6228
    if (!arguments.length) return margin;
 
6229
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
6230
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
6231
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
6232
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
6233
    return chart;
 
6234
  };
 
6235
 
 
6236
  chart.width = function(_) {
 
6237
    if (!arguments.length) return width;
 
6238
    width = _;
 
6239
    return chart;
 
6240
  };
 
6241
 
 
6242
  chart.height = function(_) {
 
6243
    if (!arguments.length) return height;
 
6244
    height = _;
 
6245
    return chart;
 
6246
  };
 
6247
 
 
6248
  chart.color = function(_) {
 
6249
    if (!arguments.length) return color;
 
6250
    color = nv.utils.getColor(_);
 
6251
    legend.color(color);
 
6252
    return chart;
 
6253
  };
 
6254
 
 
6255
  chart.showLegend = function(_) {
 
6256
    if (!arguments.length) return showLegend;
 
6257
    showLegend = _;
 
6258
    return chart;
 
6259
  };
 
6260
 
 
6261
  chart.tooltips = function(_) {
 
6262
    if (!arguments.length) return tooltips;
 
6263
    tooltips = _;
 
6264
    return chart;
 
6265
  };
 
6266
 
 
6267
  chart.tooltipContent = function(_) {
 
6268
    if (!arguments.length) return tooltip;
 
6269
    tooltip = _;
 
6270
    return chart;
 
6271
  };
 
6272
 
 
6273
  chart.state = function(_) {
 
6274
    if (!arguments.length) return state;
 
6275
    state = _;
 
6276
    return chart;
 
6277
  };
 
6278
 
 
6279
  chart.defaultState = function(_) {
 
6280
    if (!arguments.length) return defaultState;
 
6281
    defaultState = _;
 
6282
    return chart;
 
6283
  };
 
6284
 
 
6285
  chart.noData = function(_) {
 
6286
    if (!arguments.length) return noData;
 
6287
    noData = _;
 
6288
    return chart;
 
6289
  };
 
6290
 
 
6291
  //============================================================
 
6292
 
 
6293
 
 
6294
  return chart;
 
6295
}
 
6296
nv.models.lineWithFocusChart = function() {
 
6297
  "use strict";
 
6298
  //============================================================
 
6299
  // Public Variables with Default Settings
 
6300
  //------------------------------------------------------------
 
6301
 
 
6302
  var lines = nv.models.line()
 
6303
    , lines2 = nv.models.line()
 
6304
    , xAxis = nv.models.axis()
 
6305
    , yAxis = nv.models.axis()
 
6306
    , x2Axis = nv.models.axis()
 
6307
    , y2Axis = nv.models.axis()
 
6308
    , legend = nv.models.legend()
 
6309
    , brush = d3.svg.brush()
 
6310
    ;
 
6311
 
 
6312
  var margin = {top: 30, right: 30, bottom: 30, left: 60}
 
6313
    , margin2 = {top: 0, right: 30, bottom: 20, left: 60}
 
6314
    , color = nv.utils.defaultColor()
 
6315
    , width = null
 
6316
    , height = null
 
6317
    , height2 = 100
 
6318
    , x
 
6319
    , y
 
6320
    , x2
 
6321
    , y2
 
6322
    , showLegend = true
 
6323
    , brushExtent = null
 
6324
    , tooltips = true
 
6325
    , tooltip = function(key, x, y, e, graph) {
 
6326
        return '<h3>' + key + '</h3>' +
 
6327
               '<p>' +  y + ' at ' + x + '</p>'
 
6328
      }
 
6329
    , noData = "No Data Available."
 
6330
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'brush')
 
6331
    , transitionDuration = 250
 
6332
    ;
 
6333
 
 
6334
  lines
 
6335
    .clipEdge(true)
 
6336
    ;
 
6337
  lines2
 
6338
    .interactive(false)
 
6339
    ;
 
6340
  xAxis
 
6341
    .orient('bottom')
 
6342
    .tickPadding(5)
 
6343
    ;
 
6344
  yAxis
 
6345
    .orient('left')
 
6346
    ;
 
6347
  x2Axis
 
6348
    .orient('bottom')
 
6349
    .tickPadding(5)
 
6350
    ;
 
6351
  y2Axis
 
6352
    .orient('left')
 
6353
    ;
 
6354
  //============================================================
 
6355
 
 
6356
 
 
6357
  //============================================================
 
6358
  // Private Variables
 
6359
  //------------------------------------------------------------
 
6360
 
 
6361
  var showTooltip = function(e, offsetElement) {
 
6362
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
6363
        top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
6364
        x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
 
6365
        y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
 
6366
        content = tooltip(e.series.key, x, y, e, chart);
 
6367
 
 
6368
    nv.tooltip.show([left, top], content, null, null, offsetElement);
 
6369
  };
 
6370
 
 
6371
  //============================================================
 
6372
 
 
6373
 
 
6374
  function chart(selection) {
 
6375
    selection.each(function(data) {
 
6376
      var container = d3.select(this),
 
6377
          that = this;
 
6378
 
 
6379
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
6380
                             - margin.left - margin.right,
 
6381
          availableHeight1 = (height || parseInt(container.style('height')) || 400)
 
6382
                             - margin.top - margin.bottom - height2,
 
6383
          availableHeight2 = height2 - margin2.top - margin2.bottom;
 
6384
 
 
6385
      chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
 
6386
      chart.container = this;
 
6387
 
 
6388
 
 
6389
      //------------------------------------------------------------
 
6390
      // Display No Data message if there's nothing to show.
 
6391
 
 
6392
      if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
 
6393
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
6394
 
 
6395
        noDataText.enter().append('text')
 
6396
          .attr('class', 'nvd3 nv-noData')
 
6397
          .attr('dy', '-.7em')
 
6398
          .style('text-anchor', 'middle');
 
6399
 
 
6400
        noDataText
 
6401
          .attr('x', margin.left + availableWidth / 2)
 
6402
          .attr('y', margin.top + availableHeight1 / 2)
 
6403
          .text(function(d) { return d });
 
6404
 
 
6405
        return chart;
 
6406
      } else {
 
6407
        container.selectAll('.nv-noData').remove();
 
6408
      }
 
6409
 
 
6410
      //------------------------------------------------------------
 
6411
 
 
6412
 
 
6413
      //------------------------------------------------------------
 
6414
      // Setup Scales
 
6415
 
 
6416
      x = lines.xScale();
 
6417
      y = lines.yScale();
 
6418
      x2 = lines2.xScale();
 
6419
      y2 = lines2.yScale();
 
6420
 
 
6421
      //------------------------------------------------------------
 
6422
 
 
6423
 
 
6424
      //------------------------------------------------------------
 
6425
      // Setup containers and skeleton of chart
 
6426
 
 
6427
      var wrap = container.selectAll('g.nv-wrap.nv-lineWithFocusChart').data([data]);
 
6428
      var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineWithFocusChart').append('g');
 
6429
      var g = wrap.select('g');
 
6430
 
 
6431
      gEnter.append('g').attr('class', 'nv-legendWrap');
 
6432
 
 
6433
      var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
 
6434
      focusEnter.append('g').attr('class', 'nv-x nv-axis');
 
6435
      focusEnter.append('g').attr('class', 'nv-y nv-axis');
 
6436
      focusEnter.append('g').attr('class', 'nv-linesWrap');
 
6437
 
 
6438
      var contextEnter = gEnter.append('g').attr('class', 'nv-context');
 
6439
      contextEnter.append('g').attr('class', 'nv-x nv-axis');
 
6440
      contextEnter.append('g').attr('class', 'nv-y nv-axis');
 
6441
      contextEnter.append('g').attr('class', 'nv-linesWrap');
 
6442
      contextEnter.append('g').attr('class', 'nv-brushBackground');
 
6443
      contextEnter.append('g').attr('class', 'nv-x nv-brush');
 
6444
 
 
6445
      //------------------------------------------------------------
 
6446
 
 
6447
 
 
6448
      //------------------------------------------------------------
 
6449
      // Legend
 
6450
 
 
6451
      if (showLegend) {
 
6452
        legend.width(availableWidth);
 
6453
 
 
6454
        g.select('.nv-legendWrap')
 
6455
            .datum(data)
 
6456
            .call(legend);
 
6457
 
 
6458
        if ( margin.top != legend.height()) {
 
6459
          margin.top = legend.height();
 
6460
          availableHeight1 = (height || parseInt(container.style('height')) || 400)
 
6461
                             - margin.top - margin.bottom - height2;
 
6462
        }
 
6463
 
 
6464
        g.select('.nv-legendWrap')
 
6465
            .attr('transform', 'translate(0,' + (-margin.top) +')')
 
6466
      }
 
6467
 
 
6468
      //------------------------------------------------------------
 
6469
 
 
6470
 
 
6471
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
6472
 
 
6473
 
 
6474
      //------------------------------------------------------------
 
6475
      // Main Chart Component(s)
 
6476
 
 
6477
      lines
 
6478
        .width(availableWidth)
 
6479
        .height(availableHeight1)
 
6480
        .color(
 
6481
          data
 
6482
            .map(function(d,i) {
 
6483
              return d.color || color(d, i);
 
6484
            })
 
6485
            .filter(function(d,i) {
 
6486
              return !data[i].disabled;
 
6487
          })
 
6488
        );
 
6489
 
 
6490
      lines2
 
6491
        .defined(lines.defined())
 
6492
        .width(availableWidth)
 
6493
        .height(availableHeight2)
 
6494
        .color(
 
6495
          data
 
6496
            .map(function(d,i) {
 
6497
              return d.color || color(d, i);
 
6498
            })
 
6499
            .filter(function(d,i) {
 
6500
              return !data[i].disabled;
 
6501
          })
 
6502
        );
 
6503
 
 
6504
      g.select('.nv-context')
 
6505
          .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')')
 
6506
 
 
6507
      var contextLinesWrap = g.select('.nv-context .nv-linesWrap')
 
6508
          .datum(data.filter(function(d) { return !d.disabled }))
 
6509
 
 
6510
      d3.transition(contextLinesWrap).call(lines2);
 
6511
 
 
6512
      //------------------------------------------------------------
 
6513
 
 
6514
 
 
6515
      /*
 
6516
      var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
 
6517
          .datum(data.filter(function(d) { return !d.disabled }))
 
6518
 
 
6519
      d3.transition(focusLinesWrap).call(lines);
 
6520
     */
 
6521
 
 
6522
 
 
6523
      //------------------------------------------------------------
 
6524
      // Setup Main (Focus) Axes
 
6525
 
 
6526
      xAxis
 
6527
        .scale(x)
 
6528
        .ticks( availableWidth / 100 )
 
6529
        .tickSize(-availableHeight1, 0);
 
6530
 
 
6531
      yAxis
 
6532
        .scale(y)
 
6533
        .ticks( availableHeight1 / 36 )
 
6534
        .tickSize( -availableWidth, 0);
 
6535
 
 
6536
      g.select('.nv-focus .nv-x.nv-axis')
 
6537
          .attr('transform', 'translate(0,' + availableHeight1 + ')');
 
6538
 
 
6539
      //------------------------------------------------------------
 
6540
 
 
6541
 
 
6542
      //------------------------------------------------------------
 
6543
      // Setup Brush
 
6544
 
 
6545
      brush
 
6546
        .x(x2)
 
6547
        .on('brush', function() {
 
6548
            //When brushing, turn off transitions because chart needs to change immediately.
 
6549
            var oldTransition = chart.transitionDuration();
 
6550
            chart.transitionDuration(0); 
 
6551
            onBrush();
 
6552
            chart.transitionDuration(oldTransition);
 
6553
        });
 
6554
 
 
6555
      if (brushExtent) brush.extent(brushExtent);
 
6556
 
 
6557
      var brushBG = g.select('.nv-brushBackground').selectAll('g')
 
6558
          .data([brushExtent || brush.extent()])
 
6559
 
 
6560
      var brushBGenter = brushBG.enter()
 
6561
          .append('g');
 
6562
 
 
6563
      brushBGenter.append('rect')
 
6564
          .attr('class', 'left')
 
6565
          .attr('x', 0)
 
6566
          .attr('y', 0)
 
6567
          .attr('height', availableHeight2);
 
6568
 
 
6569
      brushBGenter.append('rect')
 
6570
          .attr('class', 'right')
 
6571
          .attr('x', 0)
 
6572
          .attr('y', 0)
 
6573
          .attr('height', availableHeight2);
 
6574
 
 
6575
      var gBrush = g.select('.nv-x.nv-brush')
 
6576
          .call(brush);
 
6577
      gBrush.selectAll('rect')
 
6578
          //.attr('y', -5)
 
6579
          .attr('height', availableHeight2);
 
6580
      gBrush.selectAll('.resize').append('path').attr('d', resizePath);
 
6581
 
 
6582
      onBrush();
 
6583
 
 
6584
      //------------------------------------------------------------
 
6585
 
 
6586
 
 
6587
      //------------------------------------------------------------
 
6588
      // Setup Secondary (Context) Axes
 
6589
 
 
6590
      x2Axis
 
6591
        .scale(x2)
 
6592
        .ticks( availableWidth / 100 )
 
6593
        .tickSize(-availableHeight2, 0);
 
6594
 
 
6595
      g.select('.nv-context .nv-x.nv-axis')
 
6596
          .attr('transform', 'translate(0,' + y2.range()[0] + ')');
 
6597
      d3.transition(g.select('.nv-context .nv-x.nv-axis'))
 
6598
          .call(x2Axis);
 
6599
 
 
6600
 
 
6601
      y2Axis
 
6602
        .scale(y2)
 
6603
        .ticks( availableHeight2 / 36 )
 
6604
        .tickSize( -availableWidth, 0);
 
6605
 
 
6606
      d3.transition(g.select('.nv-context .nv-y.nv-axis'))
 
6607
          .call(y2Axis);
 
6608
 
 
6609
      g.select('.nv-context .nv-x.nv-axis')
 
6610
          .attr('transform', 'translate(0,' + y2.range()[0] + ')');
 
6611
 
 
6612
      //------------------------------------------------------------
 
6613
 
 
6614
 
 
6615
      //============================================================
 
6616
      // Event Handling/Dispatching (in chart's scope)
 
6617
      //------------------------------------------------------------
 
6618
 
 
6619
      legend.dispatch.on('stateChange', function(newState) { 
 
6620
        chart.update();
 
6621
      });
 
6622
 
 
6623
      dispatch.on('tooltipShow', function(e) {
 
6624
        if (tooltips) showTooltip(e, that.parentNode);
 
6625
      });
 
6626
 
 
6627
      //============================================================
 
6628
 
 
6629
 
 
6630
      //============================================================
 
6631
      // Functions
 
6632
      //------------------------------------------------------------
 
6633
 
 
6634
      // Taken from crossfilter (http://square.github.com/crossfilter/)
 
6635
      function resizePath(d) {
 
6636
        var e = +(d == 'e'),
 
6637
            x = e ? 1 : -1,
 
6638
            y = availableHeight2 / 3;
 
6639
        return 'M' + (.5 * x) + ',' + y
 
6640
            + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
 
6641
            + 'V' + (2 * y - 6)
 
6642
            + 'A6,6 0 0 ' + e + ' ' + (.5 * x) + ',' + (2 * y)
 
6643
            + 'Z'
 
6644
            + 'M' + (2.5 * x) + ',' + (y + 8)
 
6645
            + 'V' + (2 * y - 8)
 
6646
            + 'M' + (4.5 * x) + ',' + (y + 8)
 
6647
            + 'V' + (2 * y - 8);
 
6648
      }
 
6649
 
 
6650
 
 
6651
      function updateBrushBG() {
 
6652
        if (!brush.empty()) brush.extent(brushExtent);
 
6653
        brushBG
 
6654
            .data([brush.empty() ? x2.domain() : brushExtent])
 
6655
            .each(function(d,i) {
 
6656
              var leftWidth = x2(d[0]) - x.range()[0],
 
6657
                  rightWidth = x.range()[1] - x2(d[1]);
 
6658
              d3.select(this).select('.left')
 
6659
                .attr('width',  leftWidth < 0 ? 0 : leftWidth);
 
6660
 
 
6661
              d3.select(this).select('.right')
 
6662
                .attr('x', x2(d[1]))
 
6663
                .attr('width', rightWidth < 0 ? 0 : rightWidth);
 
6664
            });
 
6665
      }
 
6666
 
 
6667
 
 
6668
      function onBrush() {
 
6669
        brushExtent = brush.empty() ? null : brush.extent();
 
6670
        var extent = brush.empty() ? x2.domain() : brush.extent();
 
6671
 
 
6672
        //The brush extent cannot be less than one.  If it is, don't update the line chart.
 
6673
        if (Math.abs(extent[0] - extent[1]) <= 1) {
 
6674
          return;
 
6675
        }
 
6676
 
 
6677
        dispatch.brush({extent: extent, brush: brush});
 
6678
 
 
6679
 
 
6680
        updateBrushBG();
 
6681
 
 
6682
        // Update Main (Focus)
 
6683
        var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
 
6684
            .datum(
 
6685
              data
 
6686
                .filter(function(d) { return !d.disabled })
 
6687
                .map(function(d,i) {
 
6688
                  return {
 
6689
                    key: d.key,
 
6690
                    values: d.values.filter(function(d,i) {
 
6691
                      return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
 
6692
                    })
 
6693
                  }
 
6694
                })
 
6695
            );
 
6696
        focusLinesWrap.transition().duration(transitionDuration).call(lines);
 
6697
 
 
6698
 
 
6699
        // Update Main (Focus) Axes
 
6700
        g.select('.nv-focus .nv-x.nv-axis').transition().duration(transitionDuration)
 
6701
            .call(xAxis);
 
6702
        g.select('.nv-focus .nv-y.nv-axis').transition().duration(transitionDuration)
 
6703
            .call(yAxis);
 
6704
      }
 
6705
 
 
6706
      //============================================================
 
6707
 
 
6708
 
 
6709
    });
 
6710
 
 
6711
    return chart;
 
6712
  }
 
6713
 
 
6714
 
 
6715
  //============================================================
 
6716
  // Event Handling/Dispatching (out of chart's scope)
 
6717
  //------------------------------------------------------------
 
6718
 
 
6719
  lines.dispatch.on('elementMouseover.tooltip', function(e) {
 
6720
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
6721
    dispatch.tooltipShow(e);
 
6722
  });
 
6723
 
 
6724
  lines.dispatch.on('elementMouseout.tooltip', function(e) {
 
6725
    dispatch.tooltipHide(e);
 
6726
  });
 
6727
 
 
6728
  dispatch.on('tooltipHide', function() {
 
6729
    if (tooltips) nv.tooltip.cleanup();
 
6730
  });
 
6731
 
 
6732
  //============================================================
 
6733
 
 
6734
 
 
6735
  //============================================================
 
6736
  // Expose Public Variables
 
6737
  //------------------------------------------------------------
 
6738
 
 
6739
  // expose chart's sub-components
 
6740
  chart.dispatch = dispatch;
 
6741
  chart.legend = legend;
 
6742
  chart.lines = lines;
 
6743
  chart.lines2 = lines2;
 
6744
  chart.xAxis = xAxis;
 
6745
  chart.yAxis = yAxis;
 
6746
  chart.x2Axis = x2Axis;
 
6747
  chart.y2Axis = y2Axis;
 
6748
 
 
6749
  d3.rebind(chart, lines, 'defined', 'isArea', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id');
 
6750
 
 
6751
  chart.options = nv.utils.optionsFunc.bind(chart);
 
6752
  
 
6753
  chart.x = function(_) {
 
6754
    if (!arguments.length) return lines.x;
 
6755
    lines.x(_);
 
6756
    lines2.x(_);
 
6757
    return chart;
 
6758
  };
 
6759
 
 
6760
  chart.y = function(_) {
 
6761
    if (!arguments.length) return lines.y;
 
6762
    lines.y(_);
 
6763
    lines2.y(_);
 
6764
    return chart;
 
6765
  };
 
6766
 
 
6767
  chart.margin = function(_) {
 
6768
    if (!arguments.length) return margin;
 
6769
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
6770
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
6771
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
6772
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
6773
    return chart;
 
6774
  };
 
6775
 
 
6776
  chart.margin2 = function(_) {
 
6777
    if (!arguments.length) return margin2;
 
6778
    margin2 = _;
 
6779
    return chart;
 
6780
  };
 
6781
 
 
6782
  chart.width = function(_) {
 
6783
    if (!arguments.length) return width;
 
6784
    width = _;
 
6785
    return chart;
 
6786
  };
 
6787
 
 
6788
  chart.height = function(_) {
 
6789
    if (!arguments.length) return height;
 
6790
    height = _;
 
6791
    return chart;
 
6792
  };
 
6793
 
 
6794
  chart.height2 = function(_) {
 
6795
    if (!arguments.length) return height2;
 
6796
    height2 = _;
 
6797
    return chart;
 
6798
  };
 
6799
 
 
6800
  chart.color = function(_) {
 
6801
    if (!arguments.length) return color;
 
6802
    color =nv.utils.getColor(_);
 
6803
    legend.color(color);
 
6804
    return chart;
 
6805
  };
 
6806
 
 
6807
  chart.showLegend = function(_) {
 
6808
    if (!arguments.length) return showLegend;
 
6809
    showLegend = _;
 
6810
    return chart;
 
6811
  };
 
6812
 
 
6813
  chart.tooltips = function(_) {
 
6814
    if (!arguments.length) return tooltips;
 
6815
    tooltips = _;
 
6816
    return chart;
 
6817
  };
 
6818
 
 
6819
  chart.tooltipContent = function(_) {
 
6820
    if (!arguments.length) return tooltip;
 
6821
    tooltip = _;
 
6822
    return chart;
 
6823
  };
 
6824
 
 
6825
  chart.interpolate = function(_) {
 
6826
    if (!arguments.length) return lines.interpolate();
 
6827
    lines.interpolate(_);
 
6828
    lines2.interpolate(_);
 
6829
    return chart;
 
6830
  };
 
6831
 
 
6832
  chart.noData = function(_) {
 
6833
    if (!arguments.length) return noData;
 
6834
    noData = _;
 
6835
    return chart;
 
6836
  };
 
6837
 
 
6838
  // Chart has multiple similar Axes, to prevent code duplication, probably need to link all axis functions manually like below
 
6839
  chart.xTickFormat = function(_) {
 
6840
    if (!arguments.length) return xAxis.tickFormat();
 
6841
    xAxis.tickFormat(_);
 
6842
    x2Axis.tickFormat(_);
 
6843
    return chart;
 
6844
  };
 
6845
 
 
6846
  chart.yTickFormat = function(_) {
 
6847
    if (!arguments.length) return yAxis.tickFormat();
 
6848
    yAxis.tickFormat(_);
 
6849
    y2Axis.tickFormat(_);
 
6850
    return chart;
 
6851
  };
 
6852
  
 
6853
  chart.brushExtent = function(_) {
 
6854
    if (!arguments.length) return brushExtent;
 
6855
    brushExtent = _;
 
6856
    return chart;
 
6857
  };
 
6858
 
 
6859
  chart.transitionDuration = function(_) {
 
6860
    if (!arguments.length) return transitionDuration;
 
6861
    transitionDuration = _;
 
6862
    return chart;
 
6863
  };
 
6864
 
 
6865
  //============================================================
 
6866
 
 
6867
 
 
6868
  return chart;
 
6869
}
 
6870
 
 
6871
nv.models.linePlusBarWithFocusChart = function() {
 
6872
  "use strict";
 
6873
  //============================================================
 
6874
  // Public Variables with Default Settings
 
6875
  //------------------------------------------------------------
 
6876
 
 
6877
  var lines = nv.models.line()
 
6878
    , lines2 = nv.models.line()
 
6879
    , bars = nv.models.historicalBar()
 
6880
    , bars2 = nv.models.historicalBar()
 
6881
    , xAxis = nv.models.axis()
 
6882
    , x2Axis = nv.models.axis()
 
6883
    , y1Axis = nv.models.axis()
 
6884
    , y2Axis = nv.models.axis()
 
6885
    , y3Axis = nv.models.axis()
 
6886
    , y4Axis = nv.models.axis()
 
6887
    , legend = nv.models.legend()
 
6888
    , brush = d3.svg.brush()
 
6889
    ;
 
6890
 
 
6891
  var margin = {top: 30, right: 30, bottom: 30, left: 60}
 
6892
    , margin2 = {top: 0, right: 30, bottom: 20, left: 60}
 
6893
    , width = null
 
6894
    , height = null
 
6895
    , height2 = 100
 
6896
    , getX = function(d) { return d.x }
 
6897
    , getY = function(d) { return d.y }
 
6898
    , color = nv.utils.defaultColor()
 
6899
    , showLegend = true
 
6900
    , extent
 
6901
    , brushExtent = null
 
6902
    , tooltips = true
 
6903
    , tooltip = function(key, x, y, e, graph) {
 
6904
        return '<h3>' + key + '</h3>' +
 
6905
               '<p>' +  y + ' at ' + x + '</p>';
 
6906
      }
 
6907
    , x
 
6908
    , x2
 
6909
    , y1
 
6910
    , y2
 
6911
    , y3
 
6912
    , y4
 
6913
    , noData = "No Data Available."
 
6914
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'brush')
 
6915
    , transitionDuration = 0
 
6916
    ;
 
6917
 
 
6918
  lines
 
6919
    .clipEdge(true)
 
6920
    ;
 
6921
  lines2
 
6922
    .interactive(false)
 
6923
    ;
 
6924
  xAxis
 
6925
    .orient('bottom')
 
6926
    .tickPadding(5)
 
6927
    ;
 
6928
  y1Axis
 
6929
    .orient('left')
 
6930
    ;
 
6931
  y2Axis
 
6932
    .orient('right')
 
6933
    ;
 
6934
  x2Axis
 
6935
    .orient('bottom')
 
6936
    .tickPadding(5)
 
6937
    ;
 
6938
  y3Axis
 
6939
    .orient('left')
 
6940
    ;
 
6941
  y4Axis
 
6942
    .orient('right')
 
6943
    ;
 
6944
 
 
6945
  //============================================================
 
6946
 
 
6947
 
 
6948
  //============================================================
 
6949
  // Private Variables
 
6950
  //------------------------------------------------------------
 
6951
 
 
6952
  var showTooltip = function(e, offsetElement) {
 
6953
    if (extent) {
 
6954
        e.pointIndex += Math.ceil(extent[0]);
 
6955
    }
 
6956
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
6957
        top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
6958
        x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
 
6959
        y = (e.series.bar ? y1Axis : y2Axis).tickFormat()(lines.y()(e.point, e.pointIndex)),
 
6960
        content = tooltip(e.series.key, x, y, e, chart);
 
6961
 
 
6962
    nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
 
6963
  };
 
6964
 
 
6965
  //------------------------------------------------------------
 
6966
 
 
6967
 
 
6968
 
 
6969
  function chart(selection) {
 
6970
    selection.each(function(data) {
 
6971
      var container = d3.select(this),
 
6972
          that = this;
 
6973
 
 
6974
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
6975
                             - margin.left - margin.right,
 
6976
          availableHeight1 = (height || parseInt(container.style('height')) || 400)
 
6977
                             - margin.top - margin.bottom - height2,
 
6978
          availableHeight2 = height2 - margin2.top - margin2.bottom;
 
6979
 
 
6980
      chart.update = function() { container.transition().duration(transitionDuration).call(chart); };
 
6981
      chart.container = this;
 
6982
 
 
6983
 
 
6984
      //------------------------------------------------------------
 
6985
      // Display No Data message if there's nothing to show.
 
6986
 
 
6987
      if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
 
6988
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
6989
 
 
6990
        noDataText.enter().append('text')
 
6991
          .attr('class', 'nvd3 nv-noData')
 
6992
          .attr('dy', '-.7em')
 
6993
          .style('text-anchor', 'middle');
 
6994
 
 
6995
        noDataText
 
6996
          .attr('x', margin.left + availableWidth / 2)
 
6997
          .attr('y', margin.top + availableHeight1 / 2)
 
6998
          .text(function(d) { return d });
 
6999
 
 
7000
        return chart;
 
7001
      } else {
 
7002
        container.selectAll('.nv-noData').remove();
 
7003
      }
 
7004
 
 
7005
      //------------------------------------------------------------
 
7006
 
 
7007
 
 
7008
      //------------------------------------------------------------
 
7009
      // Setup Scales
 
7010
 
 
7011
      var dataBars = data.filter(function(d) { return !d.disabled && d.bar });
 
7012
      var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240
 
7013
 
 
7014
      x = bars.xScale();
 
7015
      x2 = x2Axis.scale();
 
7016
      y1 = bars.yScale();
 
7017
      y2 = lines.yScale();
 
7018
      y3 = bars2.yScale();
 
7019
      y4 = lines2.yScale();
 
7020
 
 
7021
      var series1 = data
 
7022
        .filter(function(d) { return !d.disabled && d.bar })
 
7023
        .map(function(d) {
 
7024
          return d.values.map(function(d,i) {
 
7025
            return { x: getX(d,i), y: getY(d,i) }
 
7026
          })
 
7027
        });
 
7028
 
 
7029
      var series2 = data
 
7030
        .filter(function(d) { return !d.disabled && !d.bar })
 
7031
        .map(function(d) {
 
7032
          return d.values.map(function(d,i) {
 
7033
            return { x: getX(d,i), y: getY(d,i) }
 
7034
          })
 
7035
        });
 
7036
 
 
7037
      x   .range([0, availableWidth]);
 
7038
      
 
7039
      x2  .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
 
7040
          .range([0, availableWidth]);
 
7041
 
 
7042
 
 
7043
      //------------------------------------------------------------
 
7044
 
 
7045
 
 
7046
      //------------------------------------------------------------
 
7047
      // Setup containers and skeleton of chart
 
7048
 
 
7049
      var wrap = container.selectAll('g.nv-wrap.nv-linePlusBar').data([data]);
 
7050
      var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-linePlusBar').append('g');
 
7051
      var g = wrap.select('g');
 
7052
 
 
7053
      gEnter.append('g').attr('class', 'nv-legendWrap');
 
7054
      
 
7055
      var focusEnter = gEnter.append('g').attr('class', 'nv-focus');
 
7056
      focusEnter.append('g').attr('class', 'nv-x nv-axis');
 
7057
      focusEnter.append('g').attr('class', 'nv-y1 nv-axis');
 
7058
      focusEnter.append('g').attr('class', 'nv-y2 nv-axis');
 
7059
      focusEnter.append('g').attr('class', 'nv-barsWrap');
 
7060
      focusEnter.append('g').attr('class', 'nv-linesWrap');
 
7061
 
 
7062
      var contextEnter = gEnter.append('g').attr('class', 'nv-context');
 
7063
      contextEnter.append('g').attr('class', 'nv-x nv-axis');
 
7064
      contextEnter.append('g').attr('class', 'nv-y1 nv-axis');
 
7065
      contextEnter.append('g').attr('class', 'nv-y2 nv-axis');
 
7066
      contextEnter.append('g').attr('class', 'nv-barsWrap');
 
7067
      contextEnter.append('g').attr('class', 'nv-linesWrap');
 
7068
      contextEnter.append('g').attr('class', 'nv-brushBackground');
 
7069
      contextEnter.append('g').attr('class', 'nv-x nv-brush');
 
7070
 
 
7071
 
 
7072
      //------------------------------------------------------------
 
7073
 
 
7074
 
 
7075
      //------------------------------------------------------------
 
7076
      // Legend
 
7077
 
 
7078
      if (showLegend) {
 
7079
        legend.width( availableWidth / 2 );
 
7080
 
 
7081
        g.select('.nv-legendWrap')
 
7082
            .datum(data.map(function(series) {
 
7083
              series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
 
7084
              series.key = series.originalKey + (series.bar ? ' (left axis)' : ' (right axis)');
 
7085
              return series;
 
7086
            }))
 
7087
          .call(legend);
 
7088
 
 
7089
        if ( margin.top != legend.height()) {
 
7090
          margin.top = legend.height();
 
7091
          availableHeight1 = (height || parseInt(container.style('height')) || 400)
 
7092
                             - margin.top - margin.bottom - height2;
 
7093
        }
 
7094
 
 
7095
        g.select('.nv-legendWrap')
 
7096
            .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')');
 
7097
      }
 
7098
 
 
7099
      //------------------------------------------------------------
 
7100
 
 
7101
 
 
7102
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
7103
 
 
7104
 
 
7105
      //------------------------------------------------------------
 
7106
      // Context Components
 
7107
 
 
7108
      bars2
 
7109
        .width(availableWidth)
 
7110
        .height(availableHeight2)
 
7111
        .color(data.map(function(d,i) {
 
7112
          return d.color || color(d, i);
 
7113
        }).filter(function(d,i) { return !data[i].disabled && data[i].bar }));
 
7114
 
 
7115
      lines2
 
7116
        .width(availableWidth)
 
7117
        .height(availableHeight2)
 
7118
        .color(data.map(function(d,i) {
 
7119
          return d.color || color(d, i);
 
7120
        }).filter(function(d,i) { return !data[i].disabled && !data[i].bar }));
 
7121
        
 
7122
      var bars2Wrap = g.select('.nv-context .nv-barsWrap')
 
7123
          .datum(dataBars.length ? dataBars : [{values:[]}]);
 
7124
 
 
7125
      var lines2Wrap = g.select('.nv-context .nv-linesWrap')
 
7126
          .datum(!dataLines[0].disabled ? dataLines : [{values:[]}]);
 
7127
          
 
7128
      g.select('.nv-context')
 
7129
          .attr('transform', 'translate(0,' + ( availableHeight1 + margin.bottom + margin2.top) + ')')
 
7130
 
 
7131
      bars2Wrap.transition().call(bars2);
 
7132
      lines2Wrap.transition().call(lines2);
 
7133
 
 
7134
      //------------------------------------------------------------
 
7135
 
 
7136
 
 
7137
 
 
7138
      //------------------------------------------------------------
 
7139
      // Setup Brush
 
7140
 
 
7141
      brush
 
7142
        .x(x2)
 
7143
        .on('brush', onBrush);
 
7144
 
 
7145
      if (brushExtent) brush.extent(brushExtent);
 
7146
 
 
7147
      var brushBG = g.select('.nv-brushBackground').selectAll('g')
 
7148
          .data([brushExtent || brush.extent()])
 
7149
 
 
7150
      var brushBGenter = brushBG.enter()
 
7151
          .append('g');
 
7152
 
 
7153
      brushBGenter.append('rect')
 
7154
          .attr('class', 'left')
 
7155
          .attr('x', 0)
 
7156
          .attr('y', 0)
 
7157
          .attr('height', availableHeight2);
 
7158
 
 
7159
      brushBGenter.append('rect')
 
7160
          .attr('class', 'right')
 
7161
          .attr('x', 0)
 
7162
          .attr('y', 0)
 
7163
          .attr('height', availableHeight2);
 
7164
 
 
7165
      var gBrush = g.select('.nv-x.nv-brush')
 
7166
          .call(brush);
 
7167
      gBrush.selectAll('rect')
 
7168
          //.attr('y', -5)
 
7169
          .attr('height', availableHeight2);
 
7170
      gBrush.selectAll('.resize').append('path').attr('d', resizePath);
 
7171
 
 
7172
      //------------------------------------------------------------
 
7173
 
 
7174
      //------------------------------------------------------------
 
7175
      // Setup Secondary (Context) Axes
 
7176
 
 
7177
      x2Axis
 
7178
        .ticks( availableWidth / 100 )
 
7179
        .tickSize(-availableHeight2, 0);
 
7180
 
 
7181
      g.select('.nv-context .nv-x.nv-axis')
 
7182
          .attr('transform', 'translate(0,' + y3.range()[0] + ')');
 
7183
      g.select('.nv-context .nv-x.nv-axis').transition()
 
7184
          .call(x2Axis);
 
7185
 
 
7186
 
 
7187
      y3Axis
 
7188
        .scale(y3)
 
7189
        .ticks( availableHeight2 / 36 )
 
7190
        .tickSize( -availableWidth, 0);
 
7191
 
 
7192
      g.select('.nv-context .nv-y1.nv-axis')
 
7193
          .style('opacity', dataBars.length ? 1 : 0)
 
7194
          .attr('transform', 'translate(0,' + x2.range()[0] + ')');
 
7195
          
 
7196
      g.select('.nv-context .nv-y1.nv-axis').transition()
 
7197
          .call(y3Axis);
 
7198
          
 
7199
 
 
7200
      y4Axis
 
7201
        .scale(y4)
 
7202
        .ticks( availableHeight2 / 36 )
 
7203
        .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
 
7204
 
 
7205
      g.select('.nv-context .nv-y2.nv-axis')
 
7206
          .style('opacity', dataLines.length ? 1 : 0)
 
7207
          .attr('transform', 'translate(' + x2.range()[1] + ',0)');
 
7208
 
 
7209
      g.select('.nv-context .nv-y2.nv-axis').transition()
 
7210
          .call(y4Axis);
 
7211
          
 
7212
      //------------------------------------------------------------
 
7213
 
 
7214
      //============================================================
 
7215
      // Event Handling/Dispatching (in chart's scope)
 
7216
      //------------------------------------------------------------
 
7217
 
 
7218
      legend.dispatch.on('stateChange', function(newState) { 
 
7219
        chart.update();
 
7220
      });
 
7221
 
 
7222
      dispatch.on('tooltipShow', function(e) {
 
7223
        if (tooltips) showTooltip(e, that.parentNode);
 
7224
      });
 
7225
 
 
7226
      //============================================================
 
7227
 
 
7228
 
 
7229
      //============================================================
 
7230
      // Functions
 
7231
      //------------------------------------------------------------
 
7232
 
 
7233
      // Taken from crossfilter (http://square.github.com/crossfilter/)
 
7234
      function resizePath(d) {
 
7235
        var e = +(d == 'e'),
 
7236
            x = e ? 1 : -1,
 
7237
            y = availableHeight2 / 3;
 
7238
        return 'M' + (.5 * x) + ',' + y
 
7239
            + 'A6,6 0 0 ' + e + ' ' + (6.5 * x) + ',' + (y + 6)
 
7240
            + 'V' + (2 * y - 6)
 
7241
            + 'A6,6 0 0 ' + e + ' ' + (.5 * x) + ',' + (2 * y)
 
7242
            + 'Z'
 
7243
            + 'M' + (2.5 * x) + ',' + (y + 8)
 
7244
            + 'V' + (2 * y - 8)
 
7245
            + 'M' + (4.5 * x) + ',' + (y + 8)
 
7246
            + 'V' + (2 * y - 8);
 
7247
      }
 
7248
 
 
7249
 
 
7250
      function updateBrushBG() {
 
7251
        if (!brush.empty()) brush.extent(brushExtent);
 
7252
        brushBG
 
7253
            .data([brush.empty() ? x2.domain() : brushExtent])
 
7254
            .each(function(d,i) {
 
7255
              var leftWidth = x2(d[0]) - x2.range()[0],
 
7256
                  rightWidth = x2.range()[1] - x2(d[1]);
 
7257
              d3.select(this).select('.left')
 
7258
                .attr('width',  leftWidth < 0 ? 0 : leftWidth);
 
7259
 
 
7260
              d3.select(this).select('.right')
 
7261
                .attr('x', x2(d[1]))
 
7262
                .attr('width', rightWidth < 0 ? 0 : rightWidth);
 
7263
            });
 
7264
      }
 
7265
 
 
7266
 
 
7267
      function onBrush() {
 
7268
        brushExtent = brush.empty() ? null : brush.extent();
 
7269
        extent = brush.empty() ? x2.domain() : brush.extent();
 
7270
 
 
7271
 
 
7272
        dispatch.brush({extent: extent, brush: brush});
 
7273
 
 
7274
        updateBrushBG();
 
7275
 
 
7276
 
 
7277
        //------------------------------------------------------------
 
7278
        // Prepare Main (Focus) Bars and Lines
 
7279
        
 
7280
        bars
 
7281
        .width(availableWidth)
 
7282
        .height(availableHeight1)
 
7283
        .color(data.map(function(d,i) {
 
7284
          return d.color || color(d, i);
 
7285
        }).filter(function(d,i) { return !data[i].disabled && data[i].bar }));
 
7286
 
 
7287
 
 
7288
        lines
 
7289
        .width(availableWidth)
 
7290
        .height(availableHeight1)
 
7291
        .color(data.map(function(d,i) {
 
7292
          return d.color || color(d, i);
 
7293
        }).filter(function(d,i) { return !data[i].disabled && !data[i].bar }));
 
7294
 
 
7295
        var focusBarsWrap = g.select('.nv-focus .nv-barsWrap')
 
7296
            .datum(!dataBars.length ? [{values:[]}] :
 
7297
              dataBars
 
7298
                .map(function(d,i) {
 
7299
                  return {
 
7300
                    key: d.key,
 
7301
                    values: d.values.filter(function(d,i) {
 
7302
                      return bars.x()(d,i) >= extent[0] && bars.x()(d,i) <= extent[1];
 
7303
                    })
 
7304
                  }
 
7305
                })
 
7306
            );
 
7307
        
 
7308
        var focusLinesWrap = g.select('.nv-focus .nv-linesWrap')
 
7309
            .datum(dataLines[0].disabled ? [{values:[]}] :
 
7310
              dataLines
 
7311
                .map(function(d,i) {
 
7312
                  return {
 
7313
                    key: d.key,
 
7314
                    values: d.values.filter(function(d,i) {
 
7315
                      return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
 
7316
                    })
 
7317
                  }
 
7318
                })
 
7319
             );
 
7320
                 
 
7321
        //------------------------------------------------------------
 
7322
        
 
7323
        
 
7324
        //------------------------------------------------------------
 
7325
        // Update Main (Focus) X Axis
 
7326
 
 
7327
        if (dataBars.length) {
 
7328
            x = bars.xScale();
 
7329
        } else {
 
7330
            x = lines.xScale();
 
7331
        }
 
7332
        
 
7333
        xAxis
 
7334
        .scale(x)
 
7335
        .ticks( availableWidth / 100 )
 
7336
        .tickSize(-availableHeight1, 0);
 
7337
 
 
7338
        xAxis.domain([Math.ceil(extent[0]), Math.floor(extent[1])]);
 
7339
        
 
7340
        g.select('.nv-x.nv-axis').transition().duration(transitionDuration)
 
7341
          .call(xAxis);
 
7342
        //------------------------------------------------------------
 
7343
        
 
7344
        
 
7345
        //------------------------------------------------------------
 
7346
        // Update Main (Focus) Bars and Lines
 
7347
 
 
7348
        focusBarsWrap.transition().duration(transitionDuration).call(bars);
 
7349
        focusLinesWrap.transition().duration(transitionDuration).call(lines);
 
7350
        
 
7351
        //------------------------------------------------------------
 
7352
        
 
7353
          
 
7354
        //------------------------------------------------------------
 
7355
        // Setup and Update Main (Focus) Y Axes
 
7356
        
 
7357
        g.select('.nv-focus .nv-x.nv-axis')
 
7358
          .attr('transform', 'translate(0,' + y1.range()[0] + ')');
 
7359
 
 
7360
 
 
7361
        y1Axis
 
7362
        .scale(y1)
 
7363
        .ticks( availableHeight1 / 36 )
 
7364
        .tickSize(-availableWidth, 0);
 
7365
 
 
7366
        g.select('.nv-focus .nv-y1.nv-axis')
 
7367
          .style('opacity', dataBars.length ? 1 : 0);
 
7368
 
 
7369
 
 
7370
        y2Axis
 
7371
        .scale(y2)
 
7372
        .ticks( availableHeight1 / 36 )
 
7373
        .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
 
7374
 
 
7375
        g.select('.nv-focus .nv-y2.nv-axis')
 
7376
          .style('opacity', dataLines.length ? 1 : 0)
 
7377
          .attr('transform', 'translate(' + x.range()[1] + ',0)');
 
7378
 
 
7379
        g.select('.nv-focus .nv-y1.nv-axis').transition().duration(transitionDuration)
 
7380
            .call(y1Axis);
 
7381
        g.select('.nv-focus .nv-y2.nv-axis').transition().duration(transitionDuration)
 
7382
            .call(y2Axis);
 
7383
      }
 
7384
 
 
7385
      //============================================================
 
7386
 
 
7387
      onBrush();
 
7388
 
 
7389
    });
 
7390
 
 
7391
    return chart;
 
7392
  }
 
7393
 
 
7394
 
 
7395
  //============================================================
 
7396
  // Event Handling/Dispatching (out of chart's scope)
 
7397
  //------------------------------------------------------------
 
7398
 
 
7399
  lines.dispatch.on('elementMouseover.tooltip', function(e) {
 
7400
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
7401
    dispatch.tooltipShow(e);
 
7402
  });
 
7403
 
 
7404
  lines.dispatch.on('elementMouseout.tooltip', function(e) {
 
7405
    dispatch.tooltipHide(e);
 
7406
  });
 
7407
 
 
7408
  bars.dispatch.on('elementMouseover.tooltip', function(e) {
 
7409
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
7410
    dispatch.tooltipShow(e);
 
7411
  });
 
7412
 
 
7413
  bars.dispatch.on('elementMouseout.tooltip', function(e) {
 
7414
    dispatch.tooltipHide(e);
 
7415
  });
 
7416
 
 
7417
  dispatch.on('tooltipHide', function() {
 
7418
    if (tooltips) nv.tooltip.cleanup();
 
7419
  });
 
7420
 
 
7421
  //============================================================
 
7422
 
 
7423
 
 
7424
  //============================================================
 
7425
  // Expose Public Variables
 
7426
  //------------------------------------------------------------
 
7427
 
 
7428
  // expose chart's sub-components
 
7429
  chart.dispatch = dispatch;
 
7430
  chart.legend = legend;
 
7431
  chart.lines = lines;
 
7432
  chart.lines2 = lines2;
 
7433
  chart.bars = bars;
 
7434
  chart.bars2 = bars2;
 
7435
  chart.xAxis = xAxis;
 
7436
  chart.x2Axis = x2Axis;
 
7437
  chart.y1Axis = y1Axis;
 
7438
  chart.y2Axis = y2Axis;
 
7439
  chart.y3Axis = y3Axis;
 
7440
  chart.y4Axis = y4Axis;
 
7441
 
 
7442
  d3.rebind(chart, lines, 'defined', 'size', 'clipVoronoi', 'interpolate');
 
7443
  //TODO: consider rebinding x, y and some other stuff, and simply do soemthign lile bars.x(lines.x()), etc.
 
7444
  //d3.rebind(chart, lines, 'x', 'y', 'size', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id');
 
7445
 
 
7446
  chart.options = nv.utils.optionsFunc.bind(chart);
 
7447
  
 
7448
  chart.x = function(_) {
 
7449
    if (!arguments.length) return getX;
 
7450
    getX = _;
 
7451
    lines.x(_);
 
7452
    bars.x(_);
 
7453
    return chart;
 
7454
  };
 
7455
 
 
7456
  chart.y = function(_) {
 
7457
    if (!arguments.length) return getY;
 
7458
    getY = _;
 
7459
    lines.y(_);
 
7460
    bars.y(_);
 
7461
    return chart;
 
7462
  };
 
7463
 
 
7464
  chart.margin = function(_) {
 
7465
    if (!arguments.length) return margin;
 
7466
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
7467
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
7468
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
7469
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
7470
    return chart;
 
7471
  };
 
7472
 
 
7473
  chart.width = function(_) {
 
7474
    if (!arguments.length) return width;
 
7475
    width = _;
 
7476
    return chart;
 
7477
  };
 
7478
 
 
7479
  chart.height = function(_) {
 
7480
    if (!arguments.length) return height;
 
7481
    height = _;
 
7482
    return chart;
 
7483
  };
 
7484
 
 
7485
  chart.color = function(_) {
 
7486
    if (!arguments.length) return color;
 
7487
    color = nv.utils.getColor(_);
 
7488
    legend.color(color);
 
7489
    return chart;
 
7490
  };
 
7491
 
 
7492
  chart.showLegend = function(_) {
 
7493
    if (!arguments.length) return showLegend;
 
7494
    showLegend = _;
 
7495
    return chart;
 
7496
  };
 
7497
 
 
7498
  chart.tooltips = function(_) {
 
7499
    if (!arguments.length) return tooltips;
 
7500
    tooltips = _;
 
7501
    return chart;
 
7502
  };
 
7503
 
 
7504
  chart.tooltipContent = function(_) {
 
7505
    if (!arguments.length) return tooltip;
 
7506
    tooltip = _;
 
7507
    return chart;
 
7508
  };
 
7509
 
 
7510
  chart.noData = function(_) {
 
7511
    if (!arguments.length) return noData;
 
7512
    noData = _;
 
7513
    return chart;
 
7514
  };
 
7515
 
 
7516
  chart.brushExtent = function(_) {
 
7517
    if (!arguments.length) return brushExtent;
 
7518
    brushExtent = _;
 
7519
    return chart;
 
7520
  };
 
7521
 
 
7522
 
 
7523
  //============================================================
 
7524
 
 
7525
 
 
7526
  return chart;
 
7527
}
 
7528
 
 
7529
nv.models.multiBar = function() {
 
7530
  "use strict";
 
7531
  //============================================================
 
7532
  // Public Variables with Default Settings
 
7533
  //------------------------------------------------------------
 
7534
 
 
7535
  var margin = {top: 0, right: 0, bottom: 0, left: 0}
 
7536
    , width = 960
 
7537
    , height = 500
 
7538
    , x = d3.scale.ordinal()
 
7539
    , y = d3.scale.linear()
 
7540
    , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
 
7541
    , getX = function(d) { return d.x }
 
7542
    , getY = function(d) { return d.y }
 
7543
    , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
 
7544
    , clipEdge = true
 
7545
    , stacked = false
 
7546
    , stackOffset = 'zero' // options include 'silhouette', 'wiggle', 'expand', 'zero', or a custom function
 
7547
    , color = nv.utils.defaultColor()
 
7548
    , hideable = false
 
7549
    , barColor = null // adding the ability to set the color for each rather than the whole group
 
7550
    , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
 
7551
    , delay = 1200
 
7552
    , xDomain
 
7553
    , yDomain
 
7554
    , xRange
 
7555
    , yRange
 
7556
    , groupSpacing = 0.1
 
7557
    , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
 
7558
    ;
 
7559
 
 
7560
  //============================================================
 
7561
 
 
7562
 
 
7563
  //============================================================
 
7564
  // Private Variables
 
7565
  //------------------------------------------------------------
 
7566
 
 
7567
  var x0, y0 //used to store previous scales
 
7568
      ;
 
7569
 
 
7570
  //============================================================
 
7571
 
 
7572
 
 
7573
  function chart(selection) {
 
7574
    selection.each(function(data) {
 
7575
      var availableWidth = width - margin.left - margin.right,
 
7576
          availableHeight = height - margin.top - margin.bottom,
 
7577
          container = d3.select(this);
 
7578
 
 
7579
      if(hideable && data.length) hideable = [{
 
7580
        values: data[0].values.map(function(d) {
 
7581
        return {
 
7582
          x: d.x,
 
7583
          y: 0,
 
7584
          series: d.series,
 
7585
          size: 0.01
 
7586
        };}
 
7587
      )}];
 
7588
 
 
7589
      if (stacked)
 
7590
        data = d3.layout.stack()
 
7591
                 .offset(stackOffset)
 
7592
                 .values(function(d){ return d.values })
 
7593
                 .y(getY)
 
7594
                 (!data.length && hideable ? hideable : data);
 
7595
 
 
7596
 
 
7597
      //add series index to each data point for reference
 
7598
      data.forEach(function(series, i) {
 
7599
        series.values.forEach(function(point) {
 
7600
          point.series = i;
 
7601
        });
 
7602
      });
 
7603
 
 
7604
 
 
7605
      //------------------------------------------------------------
 
7606
      // HACK for negative value stacking
 
7607
      if (stacked)
 
7608
        data[0].values.map(function(d,i) {
 
7609
          var posBase = 0, negBase = 0;
 
7610
          data.map(function(d) {
 
7611
            var f = d.values[i]
 
7612
            f.size = Math.abs(f.y);
 
7613
            if (f.y<0)  {
 
7614
              f.y1 = negBase;
 
7615
              negBase = negBase - f.size;
 
7616
            } else
 
7617
            {
 
7618
              f.y1 = f.size + posBase;
 
7619
              posBase = posBase + f.size;
 
7620
            }
 
7621
          });
 
7622
        });
 
7623
 
 
7624
      //------------------------------------------------------------
 
7625
      // Setup Scales
 
7626
 
 
7627
      // remap and flatten the data for use in calculating the scales' domains
 
7628
      var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
 
7629
            data.map(function(d) {
 
7630
              return d.values.map(function(d,i) {
 
7631
                return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1 }
 
7632
              })
 
7633
            });
 
7634
 
 
7635
      x   .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
 
7636
          .rangeBands(xRange || [0, availableWidth], groupSpacing);
 
7637
 
 
7638
      //y   .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y + (stacked ? d.y1 : 0) }).concat(forceY)))
 
7639
      y   .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return stacked ? (d.y > 0 ? d.y1 : d.y1 + d.y ) : d.y }).concat(forceY)))
 
7640
          .range(yRange || [availableHeight, 0]);
 
7641
 
 
7642
      // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
 
7643
      if (x.domain()[0] === x.domain()[1])
 
7644
        x.domain()[0] ?
 
7645
            x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
 
7646
          : x.domain([-1,1]);
 
7647
 
 
7648
      if (y.domain()[0] === y.domain()[1])
 
7649
        y.domain()[0] ?
 
7650
            y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
 
7651
          : y.domain([-1,1]);
 
7652
 
 
7653
 
 
7654
      x0 = x0 || x;
 
7655
      y0 = y0 || y;
 
7656
 
 
7657
      //------------------------------------------------------------
 
7658
 
 
7659
 
 
7660
      //------------------------------------------------------------
 
7661
      // Setup containers and skeleton of chart
 
7662
 
 
7663
      var wrap = container.selectAll('g.nv-wrap.nv-multibar').data([data]);
 
7664
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibar');
 
7665
      var defsEnter = wrapEnter.append('defs');
 
7666
      var gEnter = wrapEnter.append('g');
 
7667
      var g = wrap.select('g')
 
7668
 
 
7669
      gEnter.append('g').attr('class', 'nv-groups');
 
7670
 
 
7671
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
7672
 
 
7673
      //------------------------------------------------------------
 
7674
 
 
7675
 
 
7676
 
 
7677
      defsEnter.append('clipPath')
 
7678
          .attr('id', 'nv-edge-clip-' + id)
 
7679
        .append('rect');
 
7680
      wrap.select('#nv-edge-clip-' + id + ' rect')
 
7681
          .attr('width', availableWidth)
 
7682
          .attr('height', availableHeight);
 
7683
 
 
7684
      g   .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
 
7685
 
 
7686
 
 
7687
 
 
7688
      var groups = wrap.select('.nv-groups').selectAll('.nv-group')
 
7689
          .data(function(d) { return d }, function(d,i) { return i });
 
7690
      groups.enter().append('g')
 
7691
          .style('stroke-opacity', 1e-6)
 
7692
          .style('fill-opacity', 1e-6);
 
7693
      groups.exit()
 
7694
        .transition()
 
7695
        .selectAll('rect.nv-bar')
 
7696
        .delay(function(d,i) {
 
7697
             return i * delay/ data[0].values.length;
 
7698
        })
 
7699
          .attr('y', function(d) { return stacked ? y0(d.y0) : y0(0) })
 
7700
          .attr('height', 0)
 
7701
          .remove();
 
7702
      groups
 
7703
          .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
 
7704
          .classed('hover', function(d) { return d.hover })
 
7705
          .style('fill', function(d,i){ return color(d, i) })
 
7706
          .style('stroke', function(d,i){ return color(d, i) });
 
7707
      groups
 
7708
          .transition()
 
7709
          .style('stroke-opacity', 1)
 
7710
          .style('fill-opacity', .75);
 
7711
 
 
7712
 
 
7713
      var bars = groups.selectAll('rect.nv-bar')
 
7714
          .data(function(d) { return (hideable && !data.length) ? hideable.values : d.values });
 
7715
 
 
7716
      bars.exit().remove();
 
7717
 
 
7718
 
 
7719
      var barsEnter = bars.enter().append('rect')
 
7720
          .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
 
7721
          .attr('x', function(d,i,j) {
 
7722
              return stacked ? 0 : (j * x.rangeBand() / data.length )
 
7723
          })
 
7724
          .attr('y', function(d) { return y0(stacked ? d.y0 : 0) })
 
7725
          .attr('height', 0)
 
7726
          .attr('width', x.rangeBand() / (stacked ? 1 : data.length) )
 
7727
          .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
 
7728
          ;
 
7729
      bars
 
7730
          .style('fill', function(d,i,j){ return color(d, j, i);  })
 
7731
          .style('stroke', function(d,i,j){ return color(d, j, i); })
 
7732
          .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
 
7733
            d3.select(this).classed('hover', true);
 
7734
            dispatch.elementMouseover({
 
7735
              value: getY(d,i),
 
7736
              point: d,
 
7737
              series: data[d.series],
 
7738
              pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))],  // TODO: Figure out why the value appears to be shifted
 
7739
              pointIndex: i,
 
7740
              seriesIndex: d.series,
 
7741
              e: d3.event
 
7742
            });
 
7743
          })
 
7744
          .on('mouseout', function(d,i) {
 
7745
            d3.select(this).classed('hover', false);
 
7746
            dispatch.elementMouseout({
 
7747
              value: getY(d,i),
 
7748
              point: d,
 
7749
              series: data[d.series],
 
7750
              pointIndex: i,
 
7751
              seriesIndex: d.series,
 
7752
              e: d3.event
 
7753
            });
 
7754
          })
 
7755
          .on('click', function(d,i) {
 
7756
            dispatch.elementClick({
 
7757
              value: getY(d,i),
 
7758
              point: d,
 
7759
              series: data[d.series],
 
7760
              pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))],  // TODO: Figure out why the value appears to be shifted
 
7761
              pointIndex: i,
 
7762
              seriesIndex: d.series,
 
7763
              e: d3.event
 
7764
            });
 
7765
            d3.event.stopPropagation();
 
7766
          })
 
7767
          .on('dblclick', function(d,i) {
 
7768
            dispatch.elementDblClick({
 
7769
              value: getY(d,i),
 
7770
              point: d,
 
7771
              series: data[d.series],
 
7772
              pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))],  // TODO: Figure out why the value appears to be shifted
 
7773
              pointIndex: i,
 
7774
              seriesIndex: d.series,
 
7775
              e: d3.event
 
7776
            });
 
7777
            d3.event.stopPropagation();
 
7778
          });
 
7779
      bars
 
7780
          .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
 
7781
          .transition()
 
7782
          .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
 
7783
 
 
7784
      if (barColor) {
 
7785
        if (!disabled) disabled = data.map(function() { return true });
 
7786
        bars
 
7787
          .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(); })
 
7788
          .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(); });
 
7789
      }
 
7790
 
 
7791
 
 
7792
      if (stacked)
 
7793
          bars.transition()
 
7794
            .delay(function(d,i) {
 
7795
 
 
7796
                  return i * delay / data[0].values.length;
 
7797
            })
 
7798
            .attr('y', function(d,i) {
 
7799
 
 
7800
              return y((stacked ? d.y1 : 0));
 
7801
            })
 
7802
            .attr('height', function(d,i) {
 
7803
              return Math.max(Math.abs(y(d.y + (stacked ? d.y0 : 0)) - y((stacked ? d.y0 : 0))),1);
 
7804
            })
 
7805
            .attr('x', function(d,i) {
 
7806
                  return stacked ? 0 : (d.series * x.rangeBand() / data.length )
 
7807
            })
 
7808
            .attr('width', x.rangeBand() / (stacked ? 1 : data.length) );
 
7809
      else
 
7810
          bars.transition()
 
7811
            .delay(function(d,i) {
 
7812
                return i * delay/ data[0].values.length;
 
7813
            })
 
7814
            .attr('x', function(d,i) {
 
7815
              return d.series * x.rangeBand() / data.length
 
7816
            })
 
7817
            .attr('width', x.rangeBand() / data.length)
 
7818
            .attr('y', function(d,i) {
 
7819
                return getY(d,i) < 0 ?
 
7820
                        y(0) :
 
7821
                        y(0) - y(getY(d,i)) < 1 ?
 
7822
                          y(0) - 1 :
 
7823
                        y(getY(d,i)) || 0;
 
7824
            })
 
7825
            .attr('height', function(d,i) {
 
7826
                return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) || 0;
 
7827
            });
 
7828
 
 
7829
 
 
7830
 
 
7831
      //store old scales for use in transitions on update
 
7832
      x0 = x.copy();
 
7833
      y0 = y.copy();
 
7834
 
 
7835
    });
 
7836
 
 
7837
    return chart;
 
7838
  }
 
7839
 
 
7840
 
 
7841
  //============================================================
 
7842
  // Expose Public Variables
 
7843
  //------------------------------------------------------------
 
7844
 
 
7845
  chart.dispatch = dispatch;
 
7846
 
 
7847
  chart.options = nv.utils.optionsFunc.bind(chart);
 
7848
 
 
7849
  chart.x = function(_) {
 
7850
    if (!arguments.length) return getX;
 
7851
    getX = _;
 
7852
    return chart;
 
7853
  };
 
7854
 
 
7855
  chart.y = function(_) {
 
7856
    if (!arguments.length) return getY;
 
7857
    getY = _;
 
7858
    return chart;
 
7859
  };
 
7860
 
 
7861
  chart.margin = function(_) {
 
7862
    if (!arguments.length) return margin;
 
7863
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
7864
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
7865
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
7866
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
7867
    return chart;
 
7868
  };
 
7869
 
 
7870
  chart.width = function(_) {
 
7871
    if (!arguments.length) return width;
 
7872
    width = _;
 
7873
    return chart;
 
7874
  };
 
7875
 
 
7876
  chart.height = function(_) {
 
7877
    if (!arguments.length) return height;
 
7878
    height = _;
 
7879
    return chart;
 
7880
  };
 
7881
 
 
7882
  chart.xScale = function(_) {
 
7883
    if (!arguments.length) return x;
 
7884
    x = _;
 
7885
    return chart;
 
7886
  };
 
7887
 
 
7888
  chart.yScale = function(_) {
 
7889
    if (!arguments.length) return y;
 
7890
    y = _;
 
7891
    return chart;
 
7892
  };
 
7893
 
 
7894
  chart.xDomain = function(_) {
 
7895
    if (!arguments.length) return xDomain;
 
7896
    xDomain = _;
 
7897
    return chart;
 
7898
  };
 
7899
 
 
7900
  chart.yDomain = function(_) {
 
7901
    if (!arguments.length) return yDomain;
 
7902
    yDomain = _;
 
7903
    return chart;
 
7904
  };
 
7905
 
 
7906
  chart.xRange = function(_) {
 
7907
    if (!arguments.length) return xRange;
 
7908
    xRange = _;
 
7909
    return chart;
 
7910
  };
 
7911
 
 
7912
  chart.yRange = function(_) {
 
7913
    if (!arguments.length) return yRange;
 
7914
    yRange = _;
 
7915
    return chart;
 
7916
  };
 
7917
 
 
7918
  chart.forceY = function(_) {
 
7919
    if (!arguments.length) return forceY;
 
7920
    forceY = _;
 
7921
    return chart;
 
7922
  };
 
7923
 
 
7924
  chart.stacked = function(_) {
 
7925
    if (!arguments.length) return stacked;
 
7926
    stacked = _;
 
7927
    return chart;
 
7928
  };
 
7929
 
 
7930
  chart.stackOffset = function(_) {
 
7931
    if (!arguments.length) return stackOffset;
 
7932
    stackOffset = _;
 
7933
    return chart;
 
7934
  };
 
7935
 
 
7936
  chart.clipEdge = function(_) {
 
7937
    if (!arguments.length) return clipEdge;
 
7938
    clipEdge = _;
 
7939
    return chart;
 
7940
  };
 
7941
 
 
7942
  chart.color = function(_) {
 
7943
    if (!arguments.length) return color;
 
7944
    color = nv.utils.getColor(_);
 
7945
    return chart;
 
7946
  };
 
7947
 
 
7948
  chart.barColor = function(_) {
 
7949
    if (!arguments.length) return barColor;
 
7950
    barColor = nv.utils.getColor(_);
 
7951
    return chart;
 
7952
  };
 
7953
 
 
7954
  chart.disabled = function(_) {
 
7955
    if (!arguments.length) return disabled;
 
7956
    disabled = _;
 
7957
    return chart;
 
7958
  };
 
7959
 
 
7960
  chart.id = function(_) {
 
7961
    if (!arguments.length) return id;
 
7962
    id = _;
 
7963
    return chart;
 
7964
  };
 
7965
 
 
7966
  chart.hideable = function(_) {
 
7967
    if (!arguments.length) return hideable;
 
7968
    hideable = _;
 
7969
    return chart;
 
7970
  };
 
7971
 
 
7972
  chart.delay = function(_) {
 
7973
    if (!arguments.length) return delay;
 
7974
    delay = _;
 
7975
    return chart;
 
7976
  };
 
7977
 
 
7978
  chart.groupSpacing = function(_) {
 
7979
    if (!arguments.length) return groupSpacing;
 
7980
    groupSpacing = _;
 
7981
    return chart;
 
7982
  };
 
7983
 
 
7984
  //============================================================
 
7985
 
 
7986
 
 
7987
  return chart;
 
7988
}
 
7989
 
 
7990
nv.models.multiBarChart = function() {
 
7991
  "use strict";
 
7992
  //============================================================
 
7993
  // Public Variables with Default Settings
 
7994
  //------------------------------------------------------------
 
7995
 
 
7996
  var multibar = nv.models.multiBar()
 
7997
    , xAxis = nv.models.axis()
 
7998
    , yAxis = nv.models.axis()
 
7999
    , legend = nv.models.legend()
 
8000
    , controls = nv.models.legend()
 
8001
    ;
 
8002
 
 
8003
  var margin = {top: 30, right: 20, bottom: 50, left: 60}
 
8004
    , width = null
 
8005
    , height = null
 
8006
    , color = nv.utils.defaultColor()
 
8007
    , showControls = true
 
8008
    , showLegend = true
 
8009
    , showXAxis = true
 
8010
    , showYAxis = true
 
8011
    , rightAlignYAxis = false
 
8012
    , reduceXTicks = true // if false a tick will show for every data point
 
8013
    , staggerLabels = false
 
8014
    , rotateLabels = 0
 
8015
    , tooltips = true
 
8016
    , tooltip = function(key, x, y, e, graph) {
 
8017
        return '<h3>' + key + '</h3>' +
 
8018
               '<p>' +  y + ' on ' + x + '</p>'
 
8019
      }
 
8020
    , x //can be accessed via chart.xScale()
 
8021
    , y //can be accessed via chart.yScale()
 
8022
    , state = { stacked: false }
 
8023
    , defaultState = null
 
8024
    , noData = "No Data Available."
 
8025
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
 
8026
    , controlWidth = function() { return showControls ? 180 : 0 }
 
8027
    , transitionDuration = 250
 
8028
    ;
 
8029
 
 
8030
  multibar
 
8031
    .stacked(false)
 
8032
    ;
 
8033
  xAxis
 
8034
    .orient('bottom')
 
8035
    .tickPadding(7)
 
8036
    .highlightZero(true)
 
8037
    .showMaxMin(false)
 
8038
    .tickFormat(function(d) { return d })
 
8039
    ;
 
8040
  yAxis
 
8041
    .orient((rightAlignYAxis) ? 'right' : 'left')
 
8042
    .tickFormat(d3.format(',.1f'))
 
8043
    ;
 
8044
 
 
8045
  controls.updateState(false);
 
8046
  //============================================================
 
8047
 
 
8048
 
 
8049
  //============================================================
 
8050
  // Private Variables
 
8051
  //------------------------------------------------------------
 
8052
 
 
8053
  var showTooltip = function(e, offsetElement) {
 
8054
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
8055
        top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
8056
        x = xAxis.tickFormat()(multibar.x()(e.point, e.pointIndex)),
 
8057
        y = yAxis.tickFormat()(multibar.y()(e.point, e.pointIndex)),
 
8058
        content = tooltip(e.series.key, x, y, e, chart);
 
8059
 
 
8060
    nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
 
8061
  };
 
8062
 
 
8063
  //============================================================
 
8064
 
 
8065
 
 
8066
  function chart(selection) {
 
8067
    selection.each(function(data) {
 
8068
      var container = d3.select(this),
 
8069
          that = this;
 
8070
 
 
8071
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
8072
                             - margin.left - margin.right,
 
8073
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
8074
                             - margin.top - margin.bottom;
 
8075
 
 
8076
      chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
 
8077
      chart.container = this;
 
8078
 
 
8079
      //set state.disabled
 
8080
      state.disabled = data.map(function(d) { return !!d.disabled });
 
8081
 
 
8082
      if (!defaultState) {
 
8083
        var key;
 
8084
        defaultState = {};
 
8085
        for (key in state) {
 
8086
          if (state[key] instanceof Array)
 
8087
            defaultState[key] = state[key].slice(0);
 
8088
          else
 
8089
            defaultState[key] = state[key];
 
8090
        }
 
8091
      }
 
8092
      //------------------------------------------------------------
 
8093
      // Display noData message if there's nothing to show.
 
8094
 
 
8095
      if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
 
8096
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
8097
 
 
8098
        noDataText.enter().append('text')
 
8099
          .attr('class', 'nvd3 nv-noData')
 
8100
          .attr('dy', '-.7em')
 
8101
          .style('text-anchor', 'middle');
 
8102
 
 
8103
        noDataText
 
8104
          .attr('x', margin.left + availableWidth / 2)
 
8105
          .attr('y', margin.top + availableHeight / 2)
 
8106
          .text(function(d) { return d });
 
8107
 
 
8108
        return chart;
 
8109
      } else {
 
8110
        container.selectAll('.nv-noData').remove();
 
8111
      }
 
8112
 
 
8113
      //------------------------------------------------------------
 
8114
 
 
8115
 
 
8116
      //------------------------------------------------------------
 
8117
      // Setup Scales
 
8118
 
 
8119
      x = multibar.xScale();
 
8120
      y = multibar.yScale();
 
8121
 
 
8122
      //------------------------------------------------------------
 
8123
 
 
8124
 
 
8125
      //------------------------------------------------------------
 
8126
      // Setup containers and skeleton of chart
 
8127
 
 
8128
      var wrap = container.selectAll('g.nv-wrap.nv-multiBarWithLegend').data([data]);
 
8129
      var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarWithLegend').append('g');
 
8130
      var g = wrap.select('g');
 
8131
 
 
8132
      gEnter.append('g').attr('class', 'nv-x nv-axis');
 
8133
      gEnter.append('g').attr('class', 'nv-y nv-axis');
 
8134
      gEnter.append('g').attr('class', 'nv-barsWrap');
 
8135
      gEnter.append('g').attr('class', 'nv-legendWrap');
 
8136
      gEnter.append('g').attr('class', 'nv-controlsWrap');
 
8137
 
 
8138
      //------------------------------------------------------------
 
8139
 
 
8140
 
 
8141
      //------------------------------------------------------------
 
8142
      // Legend
 
8143
 
 
8144
      if (showLegend) {
 
8145
        legend.width(availableWidth - controlWidth());
 
8146
 
 
8147
        if (multibar.barColor())
 
8148
          data.forEach(function(series,i) {
 
8149
            series.color = d3.rgb('#ccc').darker(i * 1.5).toString();
 
8150
          })
 
8151
 
 
8152
        g.select('.nv-legendWrap')
 
8153
            .datum(data)
 
8154
            .call(legend);
 
8155
 
 
8156
        if ( margin.top != legend.height()) {
 
8157
          margin.top = legend.height();
 
8158
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
8159
                             - margin.top - margin.bottom;
 
8160
        }
 
8161
 
 
8162
        g.select('.nv-legendWrap')
 
8163
            .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
 
8164
      }
 
8165
 
 
8166
      //------------------------------------------------------------
 
8167
 
 
8168
 
 
8169
      //------------------------------------------------------------
 
8170
      // Controls
 
8171
 
 
8172
      if (showControls) {
 
8173
        var controlsData = [
 
8174
          { key: 'Grouped', disabled: multibar.stacked() },
 
8175
          { key: 'Stacked', disabled: !multibar.stacked() }
 
8176
        ];
 
8177
 
 
8178
        controls.width(controlWidth()).color(['#444', '#444', '#444']);
 
8179
        g.select('.nv-controlsWrap')
 
8180
            .datum(controlsData)
 
8181
            .attr('transform', 'translate(0,' + (-margin.top) +')')
 
8182
            .call(controls);
 
8183
      }
 
8184
 
 
8185
      //------------------------------------------------------------
 
8186
 
 
8187
 
 
8188
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
8189
 
 
8190
      if (rightAlignYAxis) {
 
8191
          g.select(".nv-y.nv-axis")
 
8192
              .attr("transform", "translate(" + availableWidth + ",0)");
 
8193
      }
 
8194
 
 
8195
      //------------------------------------------------------------
 
8196
      // Main Chart Component(s)
 
8197
 
 
8198
      multibar
 
8199
        .disabled(data.map(function(series) { return series.disabled }))
 
8200
        .width(availableWidth)
 
8201
        .height(availableHeight)
 
8202
        .color(data.map(function(d,i) {
 
8203
          return d.color || color(d, i);
 
8204
        }).filter(function(d,i) { return !data[i].disabled }))
 
8205
 
 
8206
 
 
8207
      var barsWrap = g.select('.nv-barsWrap')
 
8208
          .datum(data.filter(function(d) { return !d.disabled }))
 
8209
 
 
8210
      barsWrap.transition().call(multibar);
 
8211
 
 
8212
      //------------------------------------------------------------
 
8213
 
 
8214
 
 
8215
      //------------------------------------------------------------
 
8216
      // Setup Axes
 
8217
 
 
8218
      if (showXAxis) {
 
8219
          xAxis
 
8220
            .scale(x)
 
8221
            .ticks( availableWidth / 100 )
 
8222
            .tickSize(-availableHeight, 0);
 
8223
 
 
8224
          g.select('.nv-x.nv-axis')
 
8225
              .attr('transform', 'translate(0,' + y.range()[0] + ')');
 
8226
          g.select('.nv-x.nv-axis').transition()
 
8227
              .call(xAxis);
 
8228
 
 
8229
          var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g');
 
8230
 
 
8231
          xTicks
 
8232
              .selectAll('line, text')
 
8233
              .style('opacity', 1)
 
8234
 
 
8235
          if (staggerLabels) {
 
8236
              var getTranslate = function(x,y) {
 
8237
                  return "translate(" + x + "," + y + ")";
 
8238
              };
 
8239
 
 
8240
              var staggerUp = 5, staggerDown = 17;  //pixels to stagger by
 
8241
              // Issue #140
 
8242
              xTicks
 
8243
                .selectAll("text")
 
8244
                .attr('transform', function(d,i,j) { 
 
8245
                    return  getTranslate(0, (j % 2 == 0 ? staggerUp : staggerDown));
 
8246
                  });
 
8247
 
 
8248
              var totalInBetweenTicks = d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;
 
8249
              g.selectAll(".nv-x.nv-axis .nv-axisMaxMin text")
 
8250
                .attr("transform", function(d,i) {
 
8251
                    return getTranslate(0, (i === 0 || totalInBetweenTicks % 2 !== 0) ? staggerDown : staggerUp);
 
8252
                });
 
8253
          }
 
8254
 
 
8255
          if (reduceXTicks)
 
8256
            xTicks
 
8257
              .filter(function(d,i) {
 
8258
                  return i % Math.ceil(data[0].values.length / (availableWidth / 100)) !== 0;
 
8259
                })
 
8260
              .selectAll('text, line')
 
8261
              .style('opacity', 0);
 
8262
 
 
8263
          if(rotateLabels)
 
8264
            xTicks
 
8265
              .selectAll('.tick text')
 
8266
              .attr('transform', 'rotate(' + rotateLabels + ' 0,0)')
 
8267
              .style('text-anchor', rotateLabels > 0 ? 'start' : 'end');
 
8268
          
 
8269
          g.select('.nv-x.nv-axis').selectAll('g.nv-axisMaxMin text')
 
8270
              .style('opacity', 1);
 
8271
      }
 
8272
 
 
8273
 
 
8274
      if (showYAxis) {      
 
8275
          yAxis
 
8276
            .scale(y)
 
8277
            .ticks( availableHeight / 36 )
 
8278
            .tickSize( -availableWidth, 0);
 
8279
 
 
8280
          g.select('.nv-y.nv-axis').transition()
 
8281
              .call(yAxis);
 
8282
      }
 
8283
 
 
8284
 
 
8285
      //------------------------------------------------------------
 
8286
 
 
8287
 
 
8288
 
 
8289
      //============================================================
 
8290
      // Event Handling/Dispatching (in chart's scope)
 
8291
      //------------------------------------------------------------
 
8292
 
 
8293
      legend.dispatch.on('stateChange', function(newState) { 
 
8294
        state = newState;
 
8295
        dispatch.stateChange(state);
 
8296
        chart.update();
 
8297
      });
 
8298
 
 
8299
      controls.dispatch.on('legendClick', function(d,i) {
 
8300
        if (!d.disabled) return;
 
8301
        controlsData = controlsData.map(function(s) {
 
8302
          s.disabled = true;
 
8303
          return s;
 
8304
        });
 
8305
        d.disabled = false;
 
8306
 
 
8307
        switch (d.key) {
 
8308
          case 'Grouped':
 
8309
            multibar.stacked(false);
 
8310
            break;
 
8311
          case 'Stacked':
 
8312
            multibar.stacked(true);
 
8313
            break;
 
8314
        }
 
8315
 
 
8316
        state.stacked = multibar.stacked();
 
8317
        dispatch.stateChange(state);
 
8318
 
 
8319
        chart.update();
 
8320
      });
 
8321
 
 
8322
      dispatch.on('tooltipShow', function(e) {
 
8323
        if (tooltips) showTooltip(e, that.parentNode)
 
8324
      });
 
8325
 
 
8326
      // Update chart from a state object passed to event handler
 
8327
      dispatch.on('changeState', function(e) {
 
8328
 
 
8329
        if (typeof e.disabled !== 'undefined') {
 
8330
          data.forEach(function(series,i) {
 
8331
            series.disabled = e.disabled[i];
 
8332
          });
 
8333
 
 
8334
          state.disabled = e.disabled;
 
8335
        }
 
8336
 
 
8337
        if (typeof e.stacked !== 'undefined') {
 
8338
          multibar.stacked(e.stacked);
 
8339
          state.stacked = e.stacked;
 
8340
        }
 
8341
 
 
8342
        chart.update();
 
8343
      });
 
8344
 
 
8345
      //============================================================
 
8346
 
 
8347
 
 
8348
    });
 
8349
 
 
8350
    return chart;
 
8351
  }
 
8352
 
 
8353
 
 
8354
  //============================================================
 
8355
  // Event Handling/Dispatching (out of chart's scope)
 
8356
  //------------------------------------------------------------
 
8357
 
 
8358
  multibar.dispatch.on('elementMouseover.tooltip', function(e) {
 
8359
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
8360
    dispatch.tooltipShow(e);
 
8361
  });
 
8362
 
 
8363
  multibar.dispatch.on('elementMouseout.tooltip', function(e) {
 
8364
    dispatch.tooltipHide(e);
 
8365
  });
 
8366
  dispatch.on('tooltipHide', function() {
 
8367
    if (tooltips) nv.tooltip.cleanup();
 
8368
  });
 
8369
 
 
8370
  //============================================================
 
8371
 
 
8372
 
 
8373
  //============================================================
 
8374
  // Expose Public Variables
 
8375
  //------------------------------------------------------------
 
8376
 
 
8377
  // expose chart's sub-components
 
8378
  chart.dispatch = dispatch;
 
8379
  chart.multibar = multibar;
 
8380
  chart.legend = legend;
 
8381
  chart.xAxis = xAxis;
 
8382
  chart.yAxis = yAxis;
 
8383
 
 
8384
  d3.rebind(chart, multibar, 'x', 'y', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'clipEdge',
 
8385
   'id', 'stacked', 'stackOffset', 'delay', 'barColor','groupSpacing');
 
8386
 
 
8387
  chart.options = nv.utils.optionsFunc.bind(chart);
 
8388
  
 
8389
  chart.margin = function(_) {
 
8390
    if (!arguments.length) return margin;
 
8391
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
8392
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
8393
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
8394
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
8395
    return chart;
 
8396
  };
 
8397
 
 
8398
  chart.width = function(_) {
 
8399
    if (!arguments.length) return width;
 
8400
    width = _;
 
8401
    return chart;
 
8402
  };
 
8403
 
 
8404
  chart.height = function(_) {
 
8405
    if (!arguments.length) return height;
 
8406
    height = _;
 
8407
    return chart;
 
8408
  };
 
8409
 
 
8410
  chart.color = function(_) {
 
8411
    if (!arguments.length) return color;
 
8412
    color = nv.utils.getColor(_);
 
8413
    legend.color(color);
 
8414
    return chart;
 
8415
  };
 
8416
 
 
8417
  chart.showControls = function(_) {
 
8418
    if (!arguments.length) return showControls;
 
8419
    showControls = _;
 
8420
    return chart;
 
8421
  };
 
8422
 
 
8423
  chart.showLegend = function(_) {
 
8424
    if (!arguments.length) return showLegend;
 
8425
    showLegend = _;
 
8426
    return chart;
 
8427
  };
 
8428
 
 
8429
  chart.showXAxis = function(_) {
 
8430
    if (!arguments.length) return showXAxis;
 
8431
    showXAxis = _;
 
8432
    return chart;
 
8433
  };
 
8434
 
 
8435
  chart.showYAxis = function(_) {
 
8436
    if (!arguments.length) return showYAxis;
 
8437
    showYAxis = _;
 
8438
    return chart;
 
8439
  };
 
8440
 
 
8441
  chart.rightAlignYAxis = function(_) {
 
8442
    if(!arguments.length) return rightAlignYAxis;
 
8443
    rightAlignYAxis = _;
 
8444
    yAxis.orient( (_) ? 'right' : 'left');
 
8445
    return chart;
 
8446
  };
 
8447
 
 
8448
  chart.reduceXTicks= function(_) {
 
8449
    if (!arguments.length) return reduceXTicks;
 
8450
    reduceXTicks = _;
 
8451
    return chart;
 
8452
  };
 
8453
 
 
8454
  chart.rotateLabels = function(_) {
 
8455
    if (!arguments.length) return rotateLabels;
 
8456
    rotateLabels = _;
 
8457
    return chart;
 
8458
  }
 
8459
 
 
8460
  chart.staggerLabels = function(_) {
 
8461
    if (!arguments.length) return staggerLabels;
 
8462
    staggerLabels = _;
 
8463
    return chart;
 
8464
  };
 
8465
 
 
8466
  chart.tooltip = function(_) {
 
8467
    if (!arguments.length) return tooltip;
 
8468
    tooltip = _;
 
8469
    return chart;
 
8470
  };
 
8471
 
 
8472
  chart.tooltips = function(_) {
 
8473
    if (!arguments.length) return tooltips;
 
8474
    tooltips = _;
 
8475
    return chart;
 
8476
  };
 
8477
 
 
8478
  chart.tooltipContent = function(_) {
 
8479
    if (!arguments.length) return tooltip;
 
8480
    tooltip = _;
 
8481
    return chart;
 
8482
  };
 
8483
 
 
8484
  chart.state = function(_) {
 
8485
    if (!arguments.length) return state;
 
8486
    state = _;
 
8487
    return chart;
 
8488
  };
 
8489
 
 
8490
  chart.defaultState = function(_) {
 
8491
    if (!arguments.length) return defaultState;
 
8492
    defaultState = _;
 
8493
    return chart;
 
8494
  };
 
8495
  
 
8496
  chart.noData = function(_) {
 
8497
    if (!arguments.length) return noData;
 
8498
    noData = _;
 
8499
    return chart;
 
8500
  };
 
8501
 
 
8502
  chart.transitionDuration = function(_) {
 
8503
    if (!arguments.length) return transitionDuration;
 
8504
    transitionDuration = _;
 
8505
    return chart;
 
8506
  };
 
8507
 
 
8508
  //============================================================
 
8509
 
 
8510
 
 
8511
  return chart;
 
8512
}
 
8513
 
 
8514
nv.models.multiBarHorizontal = function() {
 
8515
  "use strict";
 
8516
  //============================================================
 
8517
  // Public Variables with Default Settings
 
8518
  //------------------------------------------------------------
 
8519
 
 
8520
  var margin = {top: 0, right: 0, bottom: 0, left: 0}
 
8521
    , width = 960
 
8522
    , height = 500
 
8523
    , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
 
8524
    , x = d3.scale.ordinal()
 
8525
    , y = d3.scale.linear()
 
8526
    , getX = function(d) { return d.x }
 
8527
    , getY = function(d) { return d.y }
 
8528
    , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
 
8529
    , color = nv.utils.defaultColor()
 
8530
    , barColor = null // adding the ability to set the color for each rather than the whole group
 
8531
    , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
 
8532
    , stacked = false
 
8533
    , showValues = false
 
8534
    , showBarLabels = false
 
8535
    , valuePadding = 60
 
8536
    , valueFormat = d3.format(',.2f')
 
8537
    , delay = 1200
 
8538
    , xDomain
 
8539
    , yDomain
 
8540
    , xRange
 
8541
    , yRange
 
8542
    , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
 
8543
    ;
 
8544
 
 
8545
  //============================================================
 
8546
 
 
8547
 
 
8548
  //============================================================
 
8549
  // Private Variables
 
8550
  //------------------------------------------------------------
 
8551
 
 
8552
  var x0, y0 //used to store previous scales
 
8553
      ;
 
8554
 
 
8555
  //============================================================
 
8556
 
 
8557
 
 
8558
  function chart(selection) {
 
8559
    selection.each(function(data) {
 
8560
      var availableWidth = width - margin.left - margin.right,
 
8561
          availableHeight = height - margin.top - margin.bottom,
 
8562
          container = d3.select(this);
 
8563
 
 
8564
 
 
8565
      if (stacked)
 
8566
        data = d3.layout.stack()
 
8567
                 .offset('zero')
 
8568
                 .values(function(d){ return d.values })
 
8569
                 .y(getY)
 
8570
                 (data);
 
8571
 
 
8572
 
 
8573
      //add series index to each data point for reference
 
8574
      data.forEach(function(series, i) {
 
8575
        series.values.forEach(function(point) {
 
8576
          point.series = i;
 
8577
        });
 
8578
      });
 
8579
 
 
8580
 
 
8581
 
 
8582
      //------------------------------------------------------------
 
8583
      // HACK for negative value stacking
 
8584
      if (stacked)
 
8585
        data[0].values.map(function(d,i) {
 
8586
          var posBase = 0, negBase = 0;
 
8587
          data.map(function(d) {
 
8588
            var f = d.values[i]
 
8589
            f.size = Math.abs(f.y);
 
8590
            if (f.y<0)  {
 
8591
              f.y1 = negBase - f.size;
 
8592
              negBase = negBase - f.size;
 
8593
            } else
 
8594
            {
 
8595
              f.y1 = posBase;
 
8596
              posBase = posBase + f.size;
 
8597
            }
 
8598
          });
 
8599
        });
 
8600
 
 
8601
 
 
8602
 
 
8603
      //------------------------------------------------------------
 
8604
      // Setup Scales
 
8605
 
 
8606
      // remap and flatten the data for use in calculating the scales' domains
 
8607
      var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
 
8608
            data.map(function(d) {
 
8609
              return d.values.map(function(d,i) {
 
8610
                return { x: getX(d,i), y: getY(d,i), y0: d.y0, y1: d.y1 }
 
8611
              })
 
8612
            });
 
8613
 
 
8614
      x   .domain(xDomain || d3.merge(seriesData).map(function(d) { return d.x }))
 
8615
          .rangeBands(xRange || [0, availableHeight], .1);
 
8616
 
 
8617
      //y   .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y + (stacked ? d.y0 : 0) }).concat(forceY)))
 
8618
      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)))
 
8619
 
 
8620
      if (showValues && !stacked)
 
8621
        y.range(yRange || [(y.domain()[0] < 0 ? valuePadding : 0), availableWidth - (y.domain()[1] > 0 ? valuePadding : 0) ]);
 
8622
      else
 
8623
        y.range(yRange || [0, availableWidth]);
 
8624
 
 
8625
      x0 = x0 || x;
 
8626
      y0 = y0 || d3.scale.linear().domain(y.domain()).range([y(0),y(0)]);
 
8627
 
 
8628
      //------------------------------------------------------------
 
8629
 
 
8630
 
 
8631
      //------------------------------------------------------------
 
8632
      // Setup containers and skeleton of chart
 
8633
 
 
8634
      var wrap = d3.select(this).selectAll('g.nv-wrap.nv-multibarHorizontal').data([data]);
 
8635
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibarHorizontal');
 
8636
      var defsEnter = wrapEnter.append('defs');
 
8637
      var gEnter = wrapEnter.append('g');
 
8638
      var g = wrap.select('g');
 
8639
 
 
8640
      gEnter.append('g').attr('class', 'nv-groups');
 
8641
 
 
8642
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
8643
 
 
8644
      //------------------------------------------------------------
 
8645
 
 
8646
 
 
8647
 
 
8648
      var groups = wrap.select('.nv-groups').selectAll('.nv-group')
 
8649
          .data(function(d) { return d }, function(d,i) { return i });
 
8650
      groups.enter().append('g')
 
8651
          .style('stroke-opacity', 1e-6)
 
8652
          .style('fill-opacity', 1e-6);
 
8653
      groups.exit().transition()
 
8654
          .style('stroke-opacity', 1e-6)
 
8655
          .style('fill-opacity', 1e-6)
 
8656
          .remove();
 
8657
      groups
 
8658
          .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
 
8659
          .classed('hover', function(d) { return d.hover })
 
8660
          .style('fill', function(d,i){ return color(d, i) })
 
8661
          .style('stroke', function(d,i){ return color(d, i) });
 
8662
      groups.transition()
 
8663
          .style('stroke-opacity', 1)
 
8664
          .style('fill-opacity', .75);
 
8665
 
 
8666
 
 
8667
      var bars = groups.selectAll('g.nv-bar')
 
8668
          .data(function(d) { return d.values });
 
8669
 
 
8670
      bars.exit().remove();
 
8671
 
 
8672
 
 
8673
      var barsEnter = bars.enter().append('g')
 
8674
          .attr('transform', function(d,i,j) {
 
8675
              return 'translate(' + y0(stacked ? d.y0 : 0) + ',' + (stacked ? 0 : (j * x.rangeBand() / data.length ) + x(getX(d,i))) + ')'
 
8676
          });
 
8677
 
 
8678
      barsEnter.append('rect')
 
8679
          .attr('width', 0)
 
8680
          .attr('height', x.rangeBand() / (stacked ? 1 : data.length) )
 
8681
 
 
8682
      bars
 
8683
          .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
 
8684
            d3.select(this).classed('hover', true);
 
8685
            dispatch.elementMouseover({
 
8686
              value: getY(d,i),
 
8687
              point: d,
 
8688
              series: data[d.series],
 
8689
              pos: [ y(getY(d,i) + (stacked ? d.y0 : 0)), x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length) ],
 
8690
              pointIndex: i,
 
8691
              seriesIndex: d.series,
 
8692
              e: d3.event
 
8693
            });
 
8694
          })
 
8695
          .on('mouseout', function(d,i) {
 
8696
            d3.select(this).classed('hover', false);
 
8697
            dispatch.elementMouseout({
 
8698
              value: getY(d,i),
 
8699
              point: d,
 
8700
              series: data[d.series],
 
8701
              pointIndex: i,
 
8702
              seriesIndex: d.series,
 
8703
              e: d3.event
 
8704
            });
 
8705
          })
 
8706
          .on('click', function(d,i) {
 
8707
            dispatch.elementClick({
 
8708
              value: getY(d,i),
 
8709
              point: d,
 
8710
              series: data[d.series],
 
8711
              pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))],  // TODO: Figure out why the value appears to be shifted
 
8712
              pointIndex: i,
 
8713
              seriesIndex: d.series,
 
8714
              e: d3.event
 
8715
            });
 
8716
            d3.event.stopPropagation();
 
8717
          })
 
8718
          .on('dblclick', function(d,i) {
 
8719
            dispatch.elementDblClick({
 
8720
              value: getY(d,i),
 
8721
              point: d,
 
8722
              series: data[d.series],
 
8723
              pos: [x(getX(d,i)) + (x.rangeBand() * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))],  // TODO: Figure out why the value appears to be shifted
 
8724
              pointIndex: i,
 
8725
              seriesIndex: d.series,
 
8726
              e: d3.event
 
8727
            });
 
8728
            d3.event.stopPropagation();
 
8729
          });
 
8730
 
 
8731
 
 
8732
      barsEnter.append('text');
 
8733
 
 
8734
      if (showValues && !stacked) {
 
8735
        bars.select('text')
 
8736
            .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'end' : 'start' })
 
8737
            .attr('y', x.rangeBand() / (data.length * 2))
 
8738
            .attr('dy', '.32em')
 
8739
            .text(function(d,i) { return valueFormat(getY(d,i)) })
 
8740
        bars.transition()
 
8741
          .select('text')
 
8742
            .attr('x', function(d,i) { return getY(d,i) < 0 ? -4 : y(getY(d,i)) - y(0) + 4 })
 
8743
      } else {
 
8744
        bars.selectAll('text').text('');
 
8745
      }
 
8746
 
 
8747
      if (showBarLabels && !stacked) {
 
8748
        barsEnter.append('text').classed('nv-bar-label',true);
 
8749
        bars.select('text.nv-bar-label')
 
8750
            .attr('text-anchor', function(d,i) { return getY(d,i) < 0 ? 'start' : 'end' })
 
8751
            .attr('y', x.rangeBand() / (data.length * 2))
 
8752
            .attr('dy', '.32em')
 
8753
            .text(function(d,i) { return getX(d,i) });
 
8754
        bars.transition()
 
8755
          .select('text.nv-bar-label')
 
8756
            .attr('x', function(d,i) { return getY(d,i) < 0 ? y(0) - y(getY(d,i)) + 4 : -4 });
 
8757
      }
 
8758
      else {
 
8759
        bars.selectAll('text.nv-bar-label').text('');
 
8760
      }
 
8761
 
 
8762
      bars
 
8763
          .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
 
8764
 
 
8765
      if (barColor) {
 
8766
        if (!disabled) disabled = data.map(function() { return true });
 
8767
        bars
 
8768
          .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(); })
 
8769
          .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(); });
 
8770
      }
 
8771
 
 
8772
      if (stacked)
 
8773
        bars.transition()
 
8774
            .attr('transform', function(d,i) {
 
8775
              return 'translate(' + y(d.y1) + ',' + x(getX(d,i)) + ')'
 
8776
            })
 
8777
          .select('rect')
 
8778
            .attr('width', function(d,i) {
 
8779
              return Math.abs(y(getY(d,i) + d.y0) - y(d.y0))
 
8780
            })
 
8781
            .attr('height', x.rangeBand() );
 
8782
      else
 
8783
        bars.transition()
 
8784
            .attr('transform', function(d,i) {
 
8785
              //TODO: stacked must be all positive or all negative, not both?
 
8786
              return 'translate(' +
 
8787
              (getY(d,i) < 0 ? y(getY(d,i)) : y(0))
 
8788
              + ',' +
 
8789
              (d.series * x.rangeBand() / data.length
 
8790
              +
 
8791
              x(getX(d,i)) )
 
8792
              + ')'
 
8793
            })
 
8794
          .select('rect')
 
8795
            .attr('height', x.rangeBand() / data.length )
 
8796
            .attr('width', function(d,i) {
 
8797
              return Math.max(Math.abs(y(getY(d,i)) - y(0)),1)
 
8798
            });
 
8799
 
 
8800
 
 
8801
      //store old scales for use in transitions on update
 
8802
      x0 = x.copy();
 
8803
      y0 = y.copy();
 
8804
 
 
8805
    });
 
8806
 
 
8807
    return chart;
 
8808
  }
 
8809
 
 
8810
 
 
8811
  //============================================================
 
8812
  // Expose Public Variables
 
8813
  //------------------------------------------------------------
 
8814
 
 
8815
  chart.dispatch = dispatch;
 
8816
 
 
8817
  chart.options = nv.utils.optionsFunc.bind(chart);
 
8818
 
 
8819
  chart.x = function(_) {
 
8820
    if (!arguments.length) return getX;
 
8821
    getX = _;
 
8822
    return chart;
 
8823
  };
 
8824
 
 
8825
  chart.y = function(_) {
 
8826
    if (!arguments.length) return getY;
 
8827
    getY = _;
 
8828
    return chart;
 
8829
  };
 
8830
 
 
8831
  chart.margin = function(_) {
 
8832
    if (!arguments.length) return margin;
 
8833
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
8834
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
8835
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
8836
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
8837
    return chart;
 
8838
  };
 
8839
 
 
8840
  chart.width = function(_) {
 
8841
    if (!arguments.length) return width;
 
8842
    width = _;
 
8843
    return chart;
 
8844
  };
 
8845
 
 
8846
  chart.height = function(_) {
 
8847
    if (!arguments.length) return height;
 
8848
    height = _;
 
8849
    return chart;
 
8850
  };
 
8851
 
 
8852
  chart.xScale = function(_) {
 
8853
    if (!arguments.length) return x;
 
8854
    x = _;
 
8855
    return chart;
 
8856
  };
 
8857
 
 
8858
  chart.yScale = function(_) {
 
8859
    if (!arguments.length) return y;
 
8860
    y = _;
 
8861
    return chart;
 
8862
  };
 
8863
 
 
8864
  chart.xDomain = function(_) {
 
8865
    if (!arguments.length) return xDomain;
 
8866
    xDomain = _;
 
8867
    return chart;
 
8868
  };
 
8869
 
 
8870
  chart.yDomain = function(_) {
 
8871
    if (!arguments.length) return yDomain;
 
8872
    yDomain = _;
 
8873
    return chart;
 
8874
  };
 
8875
 
 
8876
  chart.xRange = function(_) {
 
8877
    if (!arguments.length) return xRange;
 
8878
    xRange = _;
 
8879
    return chart;
 
8880
  };
 
8881
 
 
8882
  chart.yRange = function(_) {
 
8883
    if (!arguments.length) return yRange;
 
8884
    yRange = _;
 
8885
    return chart;
 
8886
  };
 
8887
 
 
8888
  chart.forceY = function(_) {
 
8889
    if (!arguments.length) return forceY;
 
8890
    forceY = _;
 
8891
    return chart;
 
8892
  };
 
8893
 
 
8894
  chart.stacked = function(_) {
 
8895
    if (!arguments.length) return stacked;
 
8896
    stacked = _;
 
8897
    return chart;
 
8898
  };
 
8899
 
 
8900
  chart.color = function(_) {
 
8901
    if (!arguments.length) return color;
 
8902
    color = nv.utils.getColor(_);
 
8903
    return chart;
 
8904
  };
 
8905
 
 
8906
  chart.barColor = function(_) {
 
8907
    if (!arguments.length) return barColor;
 
8908
    barColor = nv.utils.getColor(_);
 
8909
    return chart;
 
8910
  };
 
8911
 
 
8912
  chart.disabled = function(_) {
 
8913
    if (!arguments.length) return disabled;
 
8914
    disabled = _;
 
8915
    return chart;
 
8916
  };
 
8917
 
 
8918
  chart.id = function(_) {
 
8919
    if (!arguments.length) return id;
 
8920
    id = _;
 
8921
    return chart;
 
8922
  };
 
8923
 
 
8924
  chart.delay = function(_) {
 
8925
    if (!arguments.length) return delay;
 
8926
    delay = _;
 
8927
    return chart;
 
8928
  };
 
8929
 
 
8930
  chart.showValues = function(_) {
 
8931
    if (!arguments.length) return showValues;
 
8932
    showValues = _;
 
8933
    return chart;
 
8934
  };
 
8935
 
 
8936
  chart.showBarLabels = function(_) {
 
8937
    if (!arguments.length) return showBarLabels;
 
8938
    showBarLabels = _;
 
8939
    return chart;
 
8940
  };
 
8941
 
 
8942
 
 
8943
  chart.valueFormat= function(_) {
 
8944
    if (!arguments.length) return valueFormat;
 
8945
    valueFormat = _;
 
8946
    return chart;
 
8947
  };
 
8948
 
 
8949
  chart.valuePadding = function(_) {
 
8950
    if (!arguments.length) return valuePadding;
 
8951
    valuePadding = _;
 
8952
    return chart;
 
8953
  };
 
8954
 
 
8955
  //============================================================
 
8956
 
 
8957
 
 
8958
  return chart;
 
8959
}
 
8960
 
 
8961
nv.models.multiBarHorizontalChart = function() {
 
8962
  "use strict";
 
8963
  //============================================================
 
8964
  // Public Variables with Default Settings
 
8965
  //------------------------------------------------------------
 
8966
 
 
8967
  var multibar = nv.models.multiBarHorizontal()
 
8968
    , xAxis = nv.models.axis()
 
8969
    , yAxis = nv.models.axis()
 
8970
    , legend = nv.models.legend().height(30)
 
8971
    , controls = nv.models.legend().height(30)
 
8972
    ;
 
8973
 
 
8974
  var margin = {top: 30, right: 20, bottom: 50, left: 60}
 
8975
    , width = null
 
8976
    , height = null
 
8977
    , color = nv.utils.defaultColor()
 
8978
    , showControls = true
 
8979
    , showLegend = true
 
8980
    , showXAxis = true
 
8981
    , showYAxis = true
 
8982
    , stacked = false
 
8983
    , tooltips = true
 
8984
    , tooltip = function(key, x, y, e, graph) {
 
8985
        return '<h3>' + key + ' - ' + x + '</h3>' +
 
8986
               '<p>' +  y + '</p>'
 
8987
      }
 
8988
    , x //can be accessed via chart.xScale()
 
8989
    , y //can be accessed via chart.yScale()
 
8990
    , state = { stacked: stacked }
 
8991
    , defaultState = null
 
8992
    , noData = 'No Data Available.'
 
8993
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
 
8994
    , controlWidth = function() { return showControls ? 180 : 0 }
 
8995
    , transitionDuration = 250
 
8996
    ;
 
8997
 
 
8998
  multibar
 
8999
    .stacked(stacked)
 
9000
    ;
 
9001
  xAxis
 
9002
    .orient('left')
 
9003
    .tickPadding(5)
 
9004
    .highlightZero(false)
 
9005
    .showMaxMin(false)
 
9006
    .tickFormat(function(d) { return d })
 
9007
    ;
 
9008
  yAxis
 
9009
    .orient('bottom')
 
9010
    .tickFormat(d3.format(',.1f'))
 
9011
    ;
 
9012
 
 
9013
  controls.updateState(false);
 
9014
  //============================================================
 
9015
 
 
9016
 
 
9017
  //============================================================
 
9018
  // Private Variables
 
9019
  //------------------------------------------------------------
 
9020
 
 
9021
  var showTooltip = function(e, offsetElement) {
 
9022
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
9023
        top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
9024
        x = xAxis.tickFormat()(multibar.x()(e.point, e.pointIndex)),
 
9025
        y = yAxis.tickFormat()(multibar.y()(e.point, e.pointIndex)),
 
9026
        content = tooltip(e.series.key, x, y, e, chart);
 
9027
 
 
9028
    nv.tooltip.show([left, top], content, e.value < 0 ? 'e' : 'w', null, offsetElement);
 
9029
  };
 
9030
 
 
9031
  //============================================================
 
9032
 
 
9033
 
 
9034
  function chart(selection) {
 
9035
    selection.each(function(data) {
 
9036
      var container = d3.select(this),
 
9037
          that = this;
 
9038
 
 
9039
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
9040
                             - margin.left - margin.right,
 
9041
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
9042
                             - margin.top - margin.bottom;
 
9043
 
 
9044
      chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
 
9045
      chart.container = this;
 
9046
 
 
9047
      //set state.disabled
 
9048
      state.disabled = data.map(function(d) { return !!d.disabled });
 
9049
 
 
9050
      if (!defaultState) {
 
9051
        var key;
 
9052
        defaultState = {};
 
9053
        for (key in state) {
 
9054
          if (state[key] instanceof Array)
 
9055
            defaultState[key] = state[key].slice(0);
 
9056
          else
 
9057
            defaultState[key] = state[key];
 
9058
        }
 
9059
      }
 
9060
 
 
9061
      //------------------------------------------------------------
 
9062
      // Display No Data message if there's nothing to show.
 
9063
 
 
9064
      if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
 
9065
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
9066
 
 
9067
        noDataText.enter().append('text')
 
9068
          .attr('class', 'nvd3 nv-noData')
 
9069
          .attr('dy', '-.7em')
 
9070
          .style('text-anchor', 'middle');
 
9071
 
 
9072
        noDataText
 
9073
          .attr('x', margin.left + availableWidth / 2)
 
9074
          .attr('y', margin.top + availableHeight / 2)
 
9075
          .text(function(d) { return d });
 
9076
 
 
9077
        return chart;
 
9078
      } else {
 
9079
        container.selectAll('.nv-noData').remove();
 
9080
      }
 
9081
 
 
9082
      //------------------------------------------------------------
 
9083
 
 
9084
 
 
9085
      //------------------------------------------------------------
 
9086
      // Setup Scales
 
9087
 
 
9088
      x = multibar.xScale();
 
9089
      y = multibar.yScale();
 
9090
 
 
9091
      //------------------------------------------------------------
 
9092
 
 
9093
 
 
9094
      //------------------------------------------------------------
 
9095
      // Setup containers and skeleton of chart
 
9096
 
 
9097
      var wrap = container.selectAll('g.nv-wrap.nv-multiBarHorizontalChart').data([data]);
 
9098
      var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multiBarHorizontalChart').append('g');
 
9099
      var g = wrap.select('g');
 
9100
 
 
9101
      gEnter.append('g').attr('class', 'nv-x nv-axis');
 
9102
      gEnter.append('g').attr('class', 'nv-y nv-axis')
 
9103
            .append('g').attr('class', 'nv-zeroLine')
 
9104
            .append('line');
 
9105
      gEnter.append('g').attr('class', 'nv-barsWrap');
 
9106
      gEnter.append('g').attr('class', 'nv-legendWrap');
 
9107
      gEnter.append('g').attr('class', 'nv-controlsWrap');
 
9108
 
 
9109
      //------------------------------------------------------------
 
9110
 
 
9111
 
 
9112
      //------------------------------------------------------------
 
9113
      // Legend
 
9114
 
 
9115
      if (showLegend) {
 
9116
        legend.width(availableWidth - controlWidth());
 
9117
 
 
9118
        if (multibar.barColor())
 
9119
          data.forEach(function(series,i) {
 
9120
            series.color = d3.rgb('#ccc').darker(i * 1.5).toString();
 
9121
          })
 
9122
 
 
9123
        g.select('.nv-legendWrap')
 
9124
            .datum(data)
 
9125
            .call(legend);
 
9126
 
 
9127
        if ( margin.top != legend.height()) {
 
9128
          margin.top = legend.height();
 
9129
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
9130
                             - margin.top - margin.bottom;
 
9131
        }
 
9132
 
 
9133
        g.select('.nv-legendWrap')
 
9134
            .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
 
9135
      }
 
9136
 
 
9137
      //------------------------------------------------------------
 
9138
 
 
9139
 
 
9140
      //------------------------------------------------------------
 
9141
      // Controls
 
9142
 
 
9143
      if (showControls) {
 
9144
        var controlsData = [
 
9145
          { key: 'Grouped', disabled: multibar.stacked() },
 
9146
          { key: 'Stacked', disabled: !multibar.stacked() }
 
9147
        ];
 
9148
 
 
9149
        controls.width(controlWidth()).color(['#444', '#444', '#444']);
 
9150
        g.select('.nv-controlsWrap')
 
9151
            .datum(controlsData)
 
9152
            .attr('transform', 'translate(0,' + (-margin.top) +')')
 
9153
            .call(controls);
 
9154
      }
 
9155
 
 
9156
      //------------------------------------------------------------
 
9157
 
 
9158
 
 
9159
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
9160
 
 
9161
 
 
9162
      //------------------------------------------------------------
 
9163
      // Main Chart Component(s)
 
9164
 
 
9165
      multibar
 
9166
        .disabled(data.map(function(series) { return series.disabled }))
 
9167
        .width(availableWidth)
 
9168
        .height(availableHeight)
 
9169
        .color(data.map(function(d,i) {
 
9170
          return d.color || color(d, i);
 
9171
        }).filter(function(d,i) { return !data[i].disabled }))
 
9172
 
 
9173
 
 
9174
      var barsWrap = g.select('.nv-barsWrap')
 
9175
          .datum(data.filter(function(d) { return !d.disabled }))
 
9176
 
 
9177
      barsWrap.transition().call(multibar);
 
9178
 
 
9179
      //------------------------------------------------------------
 
9180
 
 
9181
 
 
9182
      //------------------------------------------------------------
 
9183
      // Setup Axes
 
9184
 
 
9185
      if (showXAxis) {
 
9186
          xAxis
 
9187
            .scale(x)
 
9188
            .ticks( availableHeight / 24 )
 
9189
            .tickSize(-availableWidth, 0);
 
9190
 
 
9191
          g.select('.nv-x.nv-axis').transition()
 
9192
              .call(xAxis);
 
9193
 
 
9194
          var xTicks = g.select('.nv-x.nv-axis').selectAll('g');
 
9195
 
 
9196
          xTicks
 
9197
              .selectAll('line, text');
 
9198
      }
 
9199
 
 
9200
      if (showYAxis) {
 
9201
          yAxis
 
9202
            .scale(y)
 
9203
            .ticks( availableWidth / 100 )
 
9204
            .tickSize( -availableHeight, 0);
 
9205
 
 
9206
          g.select('.nv-y.nv-axis')
 
9207
              .attr('transform', 'translate(0,' + availableHeight + ')');
 
9208
          g.select('.nv-y.nv-axis').transition()
 
9209
              .call(yAxis);
 
9210
      }
 
9211
 
 
9212
      // Zero line
 
9213
      g.select(".nv-zeroLine line")
 
9214
        .attr("x1", y(0))
 
9215
        .attr("x2", y(0))
 
9216
        .attr("y1", 0)
 
9217
        .attr("y2", -availableHeight)
 
9218
        ;
 
9219
 
 
9220
      //------------------------------------------------------------
 
9221
 
 
9222
 
 
9223
 
 
9224
      //============================================================
 
9225
      // Event Handling/Dispatching (in chart's scope)
 
9226
      //------------------------------------------------------------
 
9227
 
 
9228
      legend.dispatch.on('stateChange', function(newState) {
 
9229
        state = newState;
 
9230
        dispatch.stateChange(state);
 
9231
        chart.update();
 
9232
      });
 
9233
 
 
9234
      controls.dispatch.on('legendClick', function(d,i) {
 
9235
        if (!d.disabled) return;
 
9236
        controlsData = controlsData.map(function(s) {
 
9237
          s.disabled = true;
 
9238
          return s;
 
9239
        });
 
9240
        d.disabled = false;
 
9241
 
 
9242
        switch (d.key) {
 
9243
          case 'Grouped':
 
9244
            multibar.stacked(false);
 
9245
            break;
 
9246
          case 'Stacked':
 
9247
            multibar.stacked(true);
 
9248
            break;
 
9249
        }
 
9250
 
 
9251
        state.stacked = multibar.stacked();
 
9252
        dispatch.stateChange(state);
 
9253
 
 
9254
        chart.update();
 
9255
      });
 
9256
 
 
9257
      dispatch.on('tooltipShow', function(e) {
 
9258
        if (tooltips) showTooltip(e, that.parentNode);
 
9259
      });
 
9260
 
 
9261
      // Update chart from a state object passed to event handler
 
9262
      dispatch.on('changeState', function(e) {
 
9263
 
 
9264
        if (typeof e.disabled !== 'undefined') {
 
9265
          data.forEach(function(series,i) {
 
9266
            series.disabled = e.disabled[i];
 
9267
          });
 
9268
 
 
9269
          state.disabled = e.disabled;
 
9270
        }
 
9271
 
 
9272
        if (typeof e.stacked !== 'undefined') {
 
9273
          multibar.stacked(e.stacked);
 
9274
          state.stacked = e.stacked;
 
9275
        }
 
9276
 
 
9277
        chart.update();
 
9278
      });
 
9279
      //============================================================
 
9280
 
 
9281
 
 
9282
    });
 
9283
 
 
9284
    return chart;
 
9285
  }
 
9286
 
 
9287
 
 
9288
  //============================================================
 
9289
  // Event Handling/Dispatching (out of chart's scope)
 
9290
  //------------------------------------------------------------
 
9291
 
 
9292
  multibar.dispatch.on('elementMouseover.tooltip', function(e) {
 
9293
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
9294
    dispatch.tooltipShow(e);
 
9295
  });
 
9296
 
 
9297
  multibar.dispatch.on('elementMouseout.tooltip', function(e) {
 
9298
    dispatch.tooltipHide(e);
 
9299
  });
 
9300
  dispatch.on('tooltipHide', function() {
 
9301
    if (tooltips) nv.tooltip.cleanup();
 
9302
  });
 
9303
 
 
9304
  //============================================================
 
9305
 
 
9306
 
 
9307
  //============================================================
 
9308
  // Expose Public Variables
 
9309
  //------------------------------------------------------------
 
9310
 
 
9311
  // expose chart's sub-components
 
9312
  chart.dispatch = dispatch;
 
9313
  chart.multibar = multibar;
 
9314
  chart.legend = legend;
 
9315
  chart.xAxis = xAxis;
 
9316
  chart.yAxis = yAxis;
 
9317
 
 
9318
  d3.rebind(chart, multibar, 'x', 'y', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY',
 
9319
    'clipEdge', 'id', 'delay', 'showValues','showBarLabels', 'valueFormat', 'stacked', 'barColor');
 
9320
 
 
9321
  chart.options = nv.utils.optionsFunc.bind(chart);
 
9322
 
 
9323
  chart.margin = function(_) {
 
9324
    if (!arguments.length) return margin;
 
9325
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
9326
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
9327
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
9328
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
9329
    return chart;
 
9330
  };
 
9331
 
 
9332
  chart.width = function(_) {
 
9333
    if (!arguments.length) return width;
 
9334
    width = _;
 
9335
    return chart;
 
9336
  };
 
9337
 
 
9338
  chart.height = function(_) {
 
9339
    if (!arguments.length) return height;
 
9340
    height = _;
 
9341
    return chart;
 
9342
  };
 
9343
 
 
9344
  chart.color = function(_) {
 
9345
    if (!arguments.length) return color;
 
9346
    color = nv.utils.getColor(_);
 
9347
    legend.color(color);
 
9348
    return chart;
 
9349
  };
 
9350
 
 
9351
  chart.showControls = function(_) {
 
9352
    if (!arguments.length) return showControls;
 
9353
    showControls = _;
 
9354
    return chart;
 
9355
  };
 
9356
 
 
9357
  chart.showLegend = function(_) {
 
9358
    if (!arguments.length) return showLegend;
 
9359
    showLegend = _;
 
9360
    return chart;
 
9361
  };
 
9362
 
 
9363
  chart.showXAxis = function(_) {
 
9364
    if (!arguments.length) return showXAxis;
 
9365
    showXAxis = _;
 
9366
    return chart;
 
9367
  };
 
9368
 
 
9369
  chart.showYAxis = function(_) {
 
9370
    if (!arguments.length) return showYAxis;
 
9371
    showYAxis = _;
 
9372
    return chart;
 
9373
  };
 
9374
 
 
9375
  chart.tooltip = function(_) {
 
9376
    if (!arguments.length) return tooltip;
 
9377
    tooltip = _;
 
9378
    return chart;
 
9379
  };
 
9380
 
 
9381
  chart.tooltips = function(_) {
 
9382
    if (!arguments.length) return tooltips;
 
9383
    tooltips = _;
 
9384
    return chart;
 
9385
  };
 
9386
 
 
9387
  chart.tooltipContent = function(_) {
 
9388
    if (!arguments.length) return tooltip;
 
9389
    tooltip = _;
 
9390
    return chart;
 
9391
  };
 
9392
 
 
9393
  chart.state = function(_) {
 
9394
    if (!arguments.length) return state;
 
9395
    state = _;
 
9396
    return chart;
 
9397
  };
 
9398
 
 
9399
  chart.defaultState = function(_) {
 
9400
    if (!arguments.length) return defaultState;
 
9401
    defaultState = _;
 
9402
    return chart;
 
9403
  };
 
9404
 
 
9405
  chart.noData = function(_) {
 
9406
    if (!arguments.length) return noData;
 
9407
    noData = _;
 
9408
    return chart;
 
9409
  };
 
9410
 
 
9411
  chart.transitionDuration = function(_) {
 
9412
    if (!arguments.length) return transitionDuration;
 
9413
    transitionDuration = _;
 
9414
    return chart;
 
9415
  };
 
9416
  //============================================================
 
9417
 
 
9418
 
 
9419
  return chart;
 
9420
}
 
9421
nv.models.multiChart = function() {
 
9422
  "use strict";
 
9423
  //============================================================
 
9424
  // Public Variables with Default Settings
 
9425
  //------------------------------------------------------------
 
9426
 
 
9427
  var margin = {top: 30, right: 20, bottom: 50, left: 60},
 
9428
      color = d3.scale.category20().range(),
 
9429
      width = null, 
 
9430
      height = null,
 
9431
      showLegend = true,
 
9432
      tooltips = true,
 
9433
      tooltip = function(key, x, y, e, graph) {
 
9434
        return '<h3>' + key + '</h3>' +
 
9435
               '<p>' +  y + ' at ' + x + '</p>'
 
9436
      },
 
9437
      x,
 
9438
      y,
 
9439
      yDomain1,
 
9440
      yDomain2
 
9441
      ; //can be accessed via chart.lines.[x/y]Scale()
 
9442
 
 
9443
  //============================================================
 
9444
  // Private Variables
 
9445
  //------------------------------------------------------------
 
9446
 
 
9447
  var x = d3.scale.linear(),
 
9448
      yScale1 = d3.scale.linear(),
 
9449
      yScale2 = d3.scale.linear(),
 
9450
 
 
9451
      lines1 = nv.models.line().yScale(yScale1),
 
9452
      lines2 = nv.models.line().yScale(yScale2),
 
9453
 
 
9454
      bars1 = nv.models.multiBar().stacked(false).yScale(yScale1),
 
9455
      bars2 = nv.models.multiBar().stacked(false).yScale(yScale2),
 
9456
 
 
9457
      stack1 = nv.models.stackedArea().yScale(yScale1),
 
9458
      stack2 = nv.models.stackedArea().yScale(yScale2),
 
9459
 
 
9460
      xAxis = nv.models.axis().scale(x).orient('bottom').tickPadding(5),
 
9461
      yAxis1 = nv.models.axis().scale(yScale1).orient('left'),
 
9462
      yAxis2 = nv.models.axis().scale(yScale2).orient('right'),
 
9463
 
 
9464
      legend = nv.models.legend().height(30),
 
9465
      dispatch = d3.dispatch('tooltipShow', 'tooltipHide');
 
9466
 
 
9467
  var showTooltip = function(e, offsetElement) {
 
9468
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
9469
        top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
9470
        x = xAxis.tickFormat()(lines1.x()(e.point, e.pointIndex)),
 
9471
        y = ((e.series.yAxis == 2) ? yAxis2 : yAxis1).tickFormat()(lines1.y()(e.point, e.pointIndex)),
 
9472
        content = tooltip(e.series.key, x, y, e, chart);
 
9473
 
 
9474
    nv.tooltip.show([left, top], content, undefined, undefined, offsetElement.offsetParent);
 
9475
  };
 
9476
 
 
9477
  function chart(selection) {
 
9478
    selection.each(function(data) {
 
9479
      var container = d3.select(this),
 
9480
          that = this;
 
9481
 
 
9482
      chart.update = function() { container.transition().call(chart); };
 
9483
      chart.container = this;
 
9484
 
 
9485
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
9486
                             - margin.left - margin.right,
 
9487
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
9488
                             - margin.top - margin.bottom;
 
9489
 
 
9490
      var dataLines1 = data.filter(function(d) {return !d.disabled && d.type == 'line' && d.yAxis == 1})
 
9491
      var dataLines2 = data.filter(function(d) {return !d.disabled && d.type == 'line' && d.yAxis == 2})
 
9492
      var dataBars1 = data.filter(function(d) {return !d.disabled && d.type == 'bar' && d.yAxis == 1})
 
9493
      var dataBars2 = data.filter(function(d) {return !d.disabled && d.type == 'bar' && d.yAxis == 2})
 
9494
      var dataStack1 = data.filter(function(d) {return !d.disabled && d.type == 'area' && d.yAxis == 1})
 
9495
      var dataStack2 = data.filter(function(d) {return !d.disabled && d.type == 'area' && d.yAxis == 2})
 
9496
 
 
9497
      var series1 = data.filter(function(d) {return !d.disabled && d.yAxis == 1})
 
9498
            .map(function(d) {
 
9499
              return d.values.map(function(d,i) {
 
9500
                return { x: d.x, y: d.y }
 
9501
              })
 
9502
            })
 
9503
 
 
9504
      var series2 = data.filter(function(d) {return !d.disabled && d.yAxis == 2})
 
9505
            .map(function(d) {
 
9506
              return d.values.map(function(d,i) {
 
9507
                return { x: d.x, y: d.y }
 
9508
              })
 
9509
            })
 
9510
 
 
9511
      x   .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
 
9512
          .range([0, availableWidth]);
 
9513
 
 
9514
      var wrap = container.selectAll('g.wrap.multiChart').data([data]);
 
9515
      var gEnter = wrap.enter().append('g').attr('class', 'wrap nvd3 multiChart').append('g');
 
9516
 
 
9517
      gEnter.append('g').attr('class', 'x axis');
 
9518
      gEnter.append('g').attr('class', 'y1 axis');
 
9519
      gEnter.append('g').attr('class', 'y2 axis');
 
9520
      gEnter.append('g').attr('class', 'lines1Wrap');
 
9521
      gEnter.append('g').attr('class', 'lines2Wrap');
 
9522
      gEnter.append('g').attr('class', 'bars1Wrap');
 
9523
      gEnter.append('g').attr('class', 'bars2Wrap');
 
9524
      gEnter.append('g').attr('class', 'stack1Wrap');
 
9525
      gEnter.append('g').attr('class', 'stack2Wrap');
 
9526
      gEnter.append('g').attr('class', 'legendWrap');
 
9527
 
 
9528
      var g = wrap.select('g');
 
9529
 
 
9530
      if (showLegend) {
 
9531
        legend.width( availableWidth / 2 );
 
9532
 
 
9533
        g.select('.legendWrap')
 
9534
            .datum(data.map(function(series) { 
 
9535
              series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
 
9536
              series.key = series.originalKey + (series.yAxis == 1 ? '' : ' (right axis)');
 
9537
              return series;
 
9538
            }))
 
9539
          .call(legend);
 
9540
 
 
9541
        if ( margin.top != legend.height()) {
 
9542
          margin.top = legend.height();
 
9543
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
9544
                             - margin.top - margin.bottom;
 
9545
        }
 
9546
 
 
9547
        g.select('.legendWrap')
 
9548
            .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')');
 
9549
      }
 
9550
 
 
9551
 
 
9552
      lines1
 
9553
        .width(availableWidth)
 
9554
        .height(availableHeight)
 
9555
        .interpolate("monotone")
 
9556
        .color(data.map(function(d,i) {
 
9557
          return d.color || color[i % color.length];
 
9558
        }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'line'}));
 
9559
 
 
9560
      lines2
 
9561
        .width(availableWidth)
 
9562
        .height(availableHeight)
 
9563
        .interpolate("monotone")
 
9564
        .color(data.map(function(d,i) {
 
9565
          return d.color || color[i % color.length];
 
9566
        }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'line'}));
 
9567
 
 
9568
      bars1
 
9569
        .width(availableWidth)
 
9570
        .height(availableHeight)
 
9571
        .color(data.map(function(d,i) {
 
9572
          return d.color || color[i % color.length];
 
9573
        }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'bar'}));
 
9574
 
 
9575
      bars2
 
9576
        .width(availableWidth)
 
9577
        .height(availableHeight)
 
9578
        .color(data.map(function(d,i) {
 
9579
          return d.color || color[i % color.length];
 
9580
        }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'bar'}));
 
9581
 
 
9582
      stack1
 
9583
        .width(availableWidth)
 
9584
        .height(availableHeight)
 
9585
        .color(data.map(function(d,i) {
 
9586
          return d.color || color[i % color.length];
 
9587
        }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'area'}));
 
9588
 
 
9589
      stack2
 
9590
        .width(availableWidth)
 
9591
        .height(availableHeight)
 
9592
        .color(data.map(function(d,i) {
 
9593
          return d.color || color[i % color.length];
 
9594
        }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'area'}));
 
9595
 
 
9596
      g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
9597
 
 
9598
 
 
9599
      var lines1Wrap = g.select('.lines1Wrap')
 
9600
          .datum(dataLines1)
 
9601
      var bars1Wrap = g.select('.bars1Wrap')
 
9602
          .datum(dataBars1)
 
9603
      var stack1Wrap = g.select('.stack1Wrap')
 
9604
          .datum(dataStack1)
 
9605
 
 
9606
      var lines2Wrap = g.select('.lines2Wrap')
 
9607
          .datum(dataLines2)
 
9608
      var bars2Wrap = g.select('.bars2Wrap')
 
9609
          .datum(dataBars2)
 
9610
      var stack2Wrap = g.select('.stack2Wrap')
 
9611
          .datum(dataStack2)
 
9612
 
 
9613
      var extraValue1 = dataStack1.length ? dataStack1.map(function(a){return a.values}).reduce(function(a,b){
 
9614
        return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
 
9615
      }).concat([{x:0, y:0}]) : []
 
9616
      var extraValue2 = dataStack2.length ? dataStack2.map(function(a){return a.values}).reduce(function(a,b){
 
9617
        return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
 
9618
      }).concat([{x:0, y:0}]) : []
 
9619
 
 
9620
      yScale1 .domain(yDomain1 || d3.extent(d3.merge(series1).concat(extraValue1), function(d) { return d.y } ))
 
9621
              .range([0, availableHeight])
 
9622
 
 
9623
      yScale2 .domain(yDomain2 || d3.extent(d3.merge(series2).concat(extraValue2), function(d) { return d.y } ))
 
9624
              .range([0, availableHeight])
 
9625
 
 
9626
      lines1.yDomain(yScale1.domain())
 
9627
      bars1.yDomain(yScale1.domain())
 
9628
      stack1.yDomain(yScale1.domain())
 
9629
 
 
9630
      lines2.yDomain(yScale2.domain())
 
9631
      bars2.yDomain(yScale2.domain())
 
9632
      stack2.yDomain(yScale2.domain())
 
9633
 
 
9634
      if(dataStack1.length){d3.transition(stack1Wrap).call(stack1);}
 
9635
      if(dataStack2.length){d3.transition(stack2Wrap).call(stack2);}
 
9636
 
 
9637
      if(dataBars1.length){d3.transition(bars1Wrap).call(bars1);}
 
9638
      if(dataBars2.length){d3.transition(bars2Wrap).call(bars2);}
 
9639
 
 
9640
      if(dataLines1.length){d3.transition(lines1Wrap).call(lines1);}
 
9641
      if(dataLines2.length){d3.transition(lines2Wrap).call(lines2);}
 
9642
      
 
9643
 
 
9644
 
 
9645
      xAxis
 
9646
        .ticks( availableWidth / 100 )
 
9647
        .tickSize(-availableHeight, 0);
 
9648
 
 
9649
      g.select('.x.axis')
 
9650
          .attr('transform', 'translate(0,' + availableHeight + ')');
 
9651
      d3.transition(g.select('.x.axis'))
 
9652
          .call(xAxis);
 
9653
 
 
9654
      yAxis1
 
9655
        .ticks( availableHeight / 36 )
 
9656
        .tickSize( -availableWidth, 0);
 
9657
 
 
9658
 
 
9659
      d3.transition(g.select('.y1.axis'))
 
9660
          .call(yAxis1);
 
9661
 
 
9662
      yAxis2
 
9663
        .ticks( availableHeight / 36 )
 
9664
        .tickSize( -availableWidth, 0);
 
9665
 
 
9666
      d3.transition(g.select('.y2.axis'))
 
9667
          .call(yAxis2);
 
9668
 
 
9669
      g.select('.y2.axis')
 
9670
          .style('opacity', series2.length ? 1 : 0)
 
9671
          .attr('transform', 'translate(' + x.range()[1] + ',0)');
 
9672
 
 
9673
      legend.dispatch.on('stateChange', function(newState) { 
 
9674
        chart.update();
 
9675
      });
 
9676
     
 
9677
      dispatch.on('tooltipShow', function(e) {
 
9678
        if (tooltips) showTooltip(e, that.parentNode);
 
9679
      });
 
9680
 
 
9681
    });
 
9682
 
 
9683
    return chart;
 
9684
  }
 
9685
 
 
9686
 
 
9687
  //============================================================
 
9688
  // Event Handling/Dispatching (out of chart's scope)
 
9689
  //------------------------------------------------------------
 
9690
 
 
9691
  lines1.dispatch.on('elementMouseover.tooltip', function(e) {
 
9692
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
9693
    dispatch.tooltipShow(e);
 
9694
  });
 
9695
 
 
9696
  lines1.dispatch.on('elementMouseout.tooltip', function(e) {
 
9697
    dispatch.tooltipHide(e);
 
9698
  });
 
9699
 
 
9700
  lines2.dispatch.on('elementMouseover.tooltip', function(e) {
 
9701
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
9702
    dispatch.tooltipShow(e);
 
9703
  });
 
9704
 
 
9705
  lines2.dispatch.on('elementMouseout.tooltip', function(e) {
 
9706
    dispatch.tooltipHide(e);
 
9707
  });
 
9708
 
 
9709
  bars1.dispatch.on('elementMouseover.tooltip', function(e) {
 
9710
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
9711
    dispatch.tooltipShow(e);
 
9712
  });
 
9713
 
 
9714
  bars1.dispatch.on('elementMouseout.tooltip', function(e) {
 
9715
    dispatch.tooltipHide(e);
 
9716
  });
 
9717
 
 
9718
  bars2.dispatch.on('elementMouseover.tooltip', function(e) {
 
9719
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
9720
    dispatch.tooltipShow(e);
 
9721
  });
 
9722
 
 
9723
  bars2.dispatch.on('elementMouseout.tooltip', function(e) {
 
9724
    dispatch.tooltipHide(e);
 
9725
  });
 
9726
 
 
9727
  stack1.dispatch.on('tooltipShow', function(e) {
 
9728
    //disable tooltips when value ~= 0
 
9729
    //// TODO: consider removing points from voronoi that have 0 value instead of this hack
 
9730
    if (!Math.round(stack1.y()(e.point) * 100)) {  // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
 
9731
      setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
 
9732
      return false;
 
9733
    }
 
9734
 
 
9735
    e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
 
9736
    dispatch.tooltipShow(e);
 
9737
  });
 
9738
 
 
9739
  stack1.dispatch.on('tooltipHide', function(e) {
 
9740
    dispatch.tooltipHide(e);
 
9741
  });
 
9742
 
 
9743
  stack2.dispatch.on('tooltipShow', function(e) {
 
9744
    //disable tooltips when value ~= 0
 
9745
    //// TODO: consider removing points from voronoi that have 0 value instead of this hack
 
9746
    if (!Math.round(stack2.y()(e.point) * 100)) {  // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
 
9747
      setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
 
9748
      return false;
 
9749
    }
 
9750
 
 
9751
    e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
 
9752
    dispatch.tooltipShow(e);
 
9753
  });
 
9754
 
 
9755
  stack2.dispatch.on('tooltipHide', function(e) {
 
9756
    dispatch.tooltipHide(e);
 
9757
  });
 
9758
 
 
9759
    lines1.dispatch.on('elementMouseover.tooltip', function(e) {
 
9760
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
9761
    dispatch.tooltipShow(e);
 
9762
  });
 
9763
 
 
9764
  lines1.dispatch.on('elementMouseout.tooltip', function(e) {
 
9765
    dispatch.tooltipHide(e);
 
9766
  });
 
9767
 
 
9768
  lines2.dispatch.on('elementMouseover.tooltip', function(e) {
 
9769
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
9770
    dispatch.tooltipShow(e);
 
9771
  });
 
9772
 
 
9773
  lines2.dispatch.on('elementMouseout.tooltip', function(e) {
 
9774
    dispatch.tooltipHide(e);
 
9775
  });
 
9776
 
 
9777
  dispatch.on('tooltipHide', function() {
 
9778
    if (tooltips) nv.tooltip.cleanup();
 
9779
  });
 
9780
 
 
9781
 
 
9782
 
 
9783
  //============================================================
 
9784
  // Global getters and setters
 
9785
  //------------------------------------------------------------
 
9786
 
 
9787
  chart.dispatch = dispatch;
 
9788
  chart.lines1 = lines1;
 
9789
  chart.lines2 = lines2;
 
9790
  chart.bars1 = bars1;
 
9791
  chart.bars2 = bars2;
 
9792
  chart.stack1 = stack1;
 
9793
  chart.stack2 = stack2;
 
9794
  chart.xAxis = xAxis;
 
9795
  chart.yAxis1 = yAxis1;
 
9796
  chart.yAxis2 = yAxis2;
 
9797
  chart.options = nv.utils.optionsFunc.bind(chart);
 
9798
 
 
9799
  chart.x = function(_) {
 
9800
    if (!arguments.length) return getX;
 
9801
    getX = _;
 
9802
    lines1.x(_);
 
9803
    bars1.x(_);
 
9804
    return chart;
 
9805
  };
 
9806
 
 
9807
  chart.y = function(_) {
 
9808
    if (!arguments.length) return getY;
 
9809
    getY = _;
 
9810
    lines1.y(_);
 
9811
    bars1.y(_);
 
9812
    return chart;
 
9813
  };
 
9814
 
 
9815
  chart.yDomain1 = function(_) {
 
9816
    if (!arguments.length) return yDomain1;
 
9817
    yDomain1 = _;
 
9818
    return chart;
 
9819
  };
 
9820
 
 
9821
  chart.yDomain2 = function(_) {
 
9822
    if (!arguments.length) return yDomain2;
 
9823
    yDomain2 = _;
 
9824
    return chart;
 
9825
  };
 
9826
 
 
9827
  chart.margin = function(_) {
 
9828
    if (!arguments.length) return margin;
 
9829
    margin = _;
 
9830
    return chart;
 
9831
  };
 
9832
 
 
9833
  chart.width = function(_) {
 
9834
    if (!arguments.length) return width;
 
9835
    width = _;
 
9836
    return chart;
 
9837
  };
 
9838
 
 
9839
  chart.height = function(_) {
 
9840
    if (!arguments.length) return height;
 
9841
    height = _;
 
9842
    return chart;
 
9843
  };
 
9844
 
 
9845
  chart.color = function(_) {
 
9846
    if (!arguments.length) return color;
 
9847
    color = _;
 
9848
    legend.color(_);
 
9849
    return chart;
 
9850
  };
 
9851
 
 
9852
  chart.showLegend = function(_) {
 
9853
    if (!arguments.length) return showLegend;
 
9854
    showLegend = _;
 
9855
    return chart;
 
9856
  };
 
9857
 
 
9858
  chart.tooltips = function(_) {
 
9859
    if (!arguments.length) return tooltips;
 
9860
    tooltips = _;
 
9861
    return chart;
 
9862
  };
 
9863
 
 
9864
  chart.tooltipContent = function(_) {
 
9865
    if (!arguments.length) return tooltip;
 
9866
    tooltip = _;
 
9867
    return chart;
 
9868
  };
 
9869
 
 
9870
  return chart;
 
9871
}
 
9872
 
 
9873
 
 
9874
nv.models.ohlcBar = function() {
 
9875
  "use strict";
 
9876
  //============================================================
 
9877
  // Public Variables with Default Settings
 
9878
  //------------------------------------------------------------
 
9879
 
 
9880
  var margin = {top: 0, right: 0, bottom: 0, left: 0}
 
9881
    , width = 960
 
9882
    , height = 500
 
9883
    , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
 
9884
    , x = d3.scale.linear()
 
9885
    , y = d3.scale.linear()
 
9886
    , getX = function(d) { return d.x }
 
9887
    , getY = function(d) { return d.y }
 
9888
    , getOpen = function(d) { return d.open }
 
9889
    , getClose = function(d) { return d.close }
 
9890
    , getHigh = function(d) { return d.high }
 
9891
    , getLow = function(d) { return d.low }
 
9892
    , forceX = []
 
9893
    , forceY = []
 
9894
    , padData     = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
 
9895
    , clipEdge = true
 
9896
    , color = nv.utils.defaultColor()
 
9897
    , xDomain
 
9898
    , yDomain
 
9899
    , xRange
 
9900
    , yRange
 
9901
    , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
 
9902
    ;
 
9903
 
 
9904
  //============================================================
 
9905
 
 
9906
  //============================================================
 
9907
  // Private Variables
 
9908
  //------------------------------------------------------------
 
9909
 
 
9910
  //TODO: store old scales for transitions
 
9911
 
 
9912
  //============================================================
 
9913
 
 
9914
 
 
9915
  function chart(selection) {
 
9916
    selection.each(function(data) {
 
9917
      var availableWidth = width - margin.left - margin.right,
 
9918
          availableHeight = height - margin.top - margin.bottom,
 
9919
          container = d3.select(this);
 
9920
 
 
9921
 
 
9922
      //------------------------------------------------------------
 
9923
      // Setup Scales
 
9924
 
 
9925
      x   .domain(xDomain || d3.extent(data[0].values.map(getX).concat(forceX) ));
 
9926
 
 
9927
      if (padData)
 
9928
        x.range(xRange || [availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5)  / data[0].values.length ]);
 
9929
      else
 
9930
        x.range(xRange || [0, availableWidth]);
 
9931
 
 
9932
      y   .domain(yDomain || [
 
9933
            d3.min(data[0].values.map(getLow).concat(forceY)),
 
9934
            d3.max(data[0].values.map(getHigh).concat(forceY))
 
9935
          ])
 
9936
          .range(yRange || [availableHeight, 0]);
 
9937
 
 
9938
      // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
 
9939
      if (x.domain()[0] === x.domain()[1])
 
9940
        x.domain()[0] ?
 
9941
            x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
 
9942
          : x.domain([-1,1]);
 
9943
 
 
9944
      if (y.domain()[0] === y.domain()[1])
 
9945
        y.domain()[0] ?
 
9946
            y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
 
9947
          : y.domain([-1,1]);
 
9948
 
 
9949
      //------------------------------------------------------------
 
9950
 
 
9951
 
 
9952
      //------------------------------------------------------------
 
9953
      // Setup containers and skeleton of chart
 
9954
 
 
9955
      var wrap = d3.select(this).selectAll('g.nv-wrap.nv-ohlcBar').data([data[0].values]);
 
9956
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-ohlcBar');
 
9957
      var defsEnter = wrapEnter.append('defs');
 
9958
      var gEnter = wrapEnter.append('g');
 
9959
      var g = wrap.select('g');
 
9960
 
 
9961
      gEnter.append('g').attr('class', 'nv-ticks');
 
9962
 
 
9963
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
9964
 
 
9965
      //------------------------------------------------------------
 
9966
 
 
9967
 
 
9968
      container
 
9969
          .on('click', function(d,i) {
 
9970
            dispatch.chartClick({
 
9971
                data: d,
 
9972
                index: i,
 
9973
                pos: d3.event,
 
9974
                id: id
 
9975
            });
 
9976
          });
 
9977
 
 
9978
 
 
9979
      defsEnter.append('clipPath')
 
9980
          .attr('id', 'nv-chart-clip-path-' + id)
 
9981
        .append('rect');
 
9982
 
 
9983
      wrap.select('#nv-chart-clip-path-' + id + ' rect')
 
9984
          .attr('width', availableWidth)
 
9985
          .attr('height', availableHeight);
 
9986
 
 
9987
      g   .attr('clip-path', clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '');
 
9988
 
 
9989
 
 
9990
 
 
9991
      var ticks = wrap.select('.nv-ticks').selectAll('.nv-tick')
 
9992
          .data(function(d) { return d });
 
9993
 
 
9994
      ticks.exit().remove();
 
9995
 
 
9996
 
 
9997
      var ticksEnter = ticks.enter().append('path')
 
9998
          .attr('class', function(d,i,j) { return (getOpen(d,i) > getClose(d,i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i })
 
9999
          .attr('d', function(d,i) {
 
10000
            var w = (availableWidth / data[0].values.length) * .9;
 
10001
            return 'm0,0l0,'
 
10002
                 + (y(getOpen(d,i))
 
10003
                 - y(getHigh(d,i)))
 
10004
                 + 'l'
 
10005
                 + (-w/2)
 
10006
                 + ',0l'
 
10007
                 + (w/2)
 
10008
                 + ',0l0,'
 
10009
                 + (y(getLow(d,i)) - y(getOpen(d,i)))
 
10010
                 + 'l0,'
 
10011
                 + (y(getClose(d,i))
 
10012
                 - y(getLow(d,i)))
 
10013
                 + 'l'
 
10014
                 + (w/2)
 
10015
                 + ',0l'
 
10016
                 + (-w/2)
 
10017
                 + ',0z';
 
10018
          })
 
10019
          .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',' + y(getHigh(d,i)) + ')'; })
 
10020
          //.attr('fill', function(d,i) { return color[0]; })
 
10021
          //.attr('stroke', function(d,i) { return color[0]; })
 
10022
          //.attr('x', 0 )
 
10023
          //.attr('y', function(d,i) {  return y(Math.max(0, getY(d,i))) })
 
10024
          //.attr('height', function(d,i) { return Math.abs(y(getY(d,i)) - y(0)) })
 
10025
          .on('mouseover', function(d,i) {
 
10026
            d3.select(this).classed('hover', true);
 
10027
            dispatch.elementMouseover({
 
10028
                point: d,
 
10029
                series: data[0],
 
10030
                pos: [x(getX(d,i)), y(getY(d,i))],  // TODO: Figure out why the value appears to be shifted
 
10031
                pointIndex: i,
 
10032
                seriesIndex: 0,
 
10033
                e: d3.event
 
10034
            });
 
10035
 
 
10036
          })
 
10037
          .on('mouseout', function(d,i) {
 
10038
                d3.select(this).classed('hover', false);
 
10039
                dispatch.elementMouseout({
 
10040
                    point: d,
 
10041
                    series: data[0],
 
10042
                    pointIndex: i,
 
10043
                    seriesIndex: 0,
 
10044
                    e: d3.event
 
10045
                });
 
10046
          })
 
10047
          .on('click', function(d,i) {
 
10048
                dispatch.elementClick({
 
10049
                    //label: d[label],
 
10050
                    value: getY(d,i),
 
10051
                    data: d,
 
10052
                    index: i,
 
10053
                    pos: [x(getX(d,i)), y(getY(d,i))],
 
10054
                    e: d3.event,
 
10055
                    id: id
 
10056
                });
 
10057
              d3.event.stopPropagation();
 
10058
          })
 
10059
          .on('dblclick', function(d,i) {
 
10060
              dispatch.elementDblClick({
 
10061
                  //label: d[label],
 
10062
                  value: getY(d,i),
 
10063
                  data: d,
 
10064
                  index: i,
 
10065
                  pos: [x(getX(d,i)), y(getY(d,i))],
 
10066
                  e: d3.event,
 
10067
                  id: id
 
10068
              });
 
10069
              d3.event.stopPropagation();
 
10070
          });
 
10071
 
 
10072
      ticks
 
10073
          .attr('class', function(d,i,j) { return (getOpen(d,i) > getClose(d,i) ? 'nv-tick negative' : 'nv-tick positive') + ' nv-tick-' + j + '-' + i })
 
10074
      d3.transition(ticks)
 
10075
          .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',' + y(getHigh(d,i)) + ')'; })
 
10076
          .attr('d', function(d,i) {
 
10077
            var w = (availableWidth / data[0].values.length) * .9;
 
10078
            return 'm0,0l0,'
 
10079
                 + (y(getOpen(d,i))
 
10080
                 - y(getHigh(d,i)))
 
10081
                 + 'l'
 
10082
                 + (-w/2)
 
10083
                 + ',0l'
 
10084
                 + (w/2)
 
10085
                 + ',0l0,'
 
10086
                 + (y(getLow(d,i))
 
10087
                 - y(getOpen(d,i)))
 
10088
                 + 'l0,'
 
10089
                 + (y(getClose(d,i))
 
10090
                 - y(getLow(d,i)))
 
10091
                 + 'l'
 
10092
                 + (w/2)
 
10093
                 + ',0l'
 
10094
                 + (-w/2)
 
10095
                 + ',0z';
 
10096
          })
 
10097
          //.attr('width', (availableWidth / data[0].values.length) * .9 )
 
10098
 
 
10099
 
 
10100
      //d3.transition(ticks)
 
10101
          //.attr('y', function(d,i) {  return y(Math.max(0, getY(d,i))) })
 
10102
          //.attr('height', function(d,i) { return Math.abs(y(getY(d,i)) - y(0)) });
 
10103
          //.order();  // not sure if this makes any sense for this model
 
10104
 
 
10105
    });
 
10106
 
 
10107
    return chart;
 
10108
  }
 
10109
 
 
10110
 
 
10111
  //============================================================
 
10112
  // Expose Public Variables
 
10113
  //------------------------------------------------------------
 
10114
 
 
10115
  chart.dispatch = dispatch;
 
10116
 
 
10117
  chart.options = nv.utils.optionsFunc.bind(chart);
 
10118
 
 
10119
  chart.x = function(_) {
 
10120
    if (!arguments.length) return getX;
 
10121
    getX = _;
 
10122
    return chart;
 
10123
  };
 
10124
 
 
10125
  chart.y = function(_) {
 
10126
    if (!arguments.length) return getY;
 
10127
    getY = _;
 
10128
    return chart;
 
10129
  };
 
10130
 
 
10131
  chart.open = function(_) {
 
10132
    if (!arguments.length) return getOpen;
 
10133
    getOpen = _;
 
10134
    return chart;
 
10135
  };
 
10136
 
 
10137
  chart.close = function(_) {
 
10138
    if (!arguments.length) return getClose;
 
10139
    getClose = _;
 
10140
    return chart;
 
10141
  };
 
10142
 
 
10143
  chart.high = function(_) {
 
10144
    if (!arguments.length) return getHigh;
 
10145
    getHigh = _;
 
10146
    return chart;
 
10147
  };
 
10148
 
 
10149
  chart.low = function(_) {
 
10150
    if (!arguments.length) return getLow;
 
10151
    getLow = _;
 
10152
    return chart;
 
10153
  };
 
10154
 
 
10155
  chart.margin = function(_) {
 
10156
    if (!arguments.length) return margin;
 
10157
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
10158
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
10159
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
10160
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
10161
    return chart;
 
10162
  };
 
10163
 
 
10164
  chart.width = function(_) {
 
10165
    if (!arguments.length) return width;
 
10166
    width = _;
 
10167
    return chart;
 
10168
  };
 
10169
 
 
10170
  chart.height = function(_) {
 
10171
    if (!arguments.length) return height;
 
10172
    height = _;
 
10173
    return chart;
 
10174
  };
 
10175
 
 
10176
  chart.xScale = function(_) {
 
10177
    if (!arguments.length) return x;
 
10178
    x = _;
 
10179
    return chart;
 
10180
  };
 
10181
 
 
10182
  chart.yScale = function(_) {
 
10183
    if (!arguments.length) return y;
 
10184
    y = _;
 
10185
    return chart;
 
10186
  };
 
10187
 
 
10188
  chart.xDomain = function(_) {
 
10189
    if (!arguments.length) return xDomain;
 
10190
    xDomain = _;
 
10191
    return chart;
 
10192
  };
 
10193
 
 
10194
  chart.yDomain = function(_) {
 
10195
    if (!arguments.length) return yDomain;
 
10196
    yDomain = _;
 
10197
    return chart;
 
10198
  };
 
10199
 
 
10200
  chart.xRange = function(_) {
 
10201
    if (!arguments.length) return xRange;
 
10202
    xRange = _;
 
10203
    return chart;
 
10204
  };
 
10205
 
 
10206
  chart.yRange = function(_) {
 
10207
    if (!arguments.length) return yRange;
 
10208
    yRange = _;
 
10209
    return chart;
 
10210
  };
 
10211
 
 
10212
  chart.forceX = function(_) {
 
10213
    if (!arguments.length) return forceX;
 
10214
    forceX = _;
 
10215
    return chart;
 
10216
  };
 
10217
 
 
10218
  chart.forceY = function(_) {
 
10219
    if (!arguments.length) return forceY;
 
10220
    forceY = _;
 
10221
    return chart;
 
10222
  };
 
10223
 
 
10224
  chart.padData = function(_) {
 
10225
    if (!arguments.length) return padData;
 
10226
    padData = _;
 
10227
    return chart;
 
10228
  };
 
10229
 
 
10230
  chart.clipEdge = function(_) {
 
10231
    if (!arguments.length) return clipEdge;
 
10232
    clipEdge = _;
 
10233
    return chart;
 
10234
  };
 
10235
 
 
10236
  chart.color = function(_) {
 
10237
    if (!arguments.length) return color;
 
10238
    color = nv.utils.getColor(_);
 
10239
    return chart;
 
10240
  };
 
10241
 
 
10242
  chart.id = function(_) {
 
10243
    if (!arguments.length) return id;
 
10244
    id = _;
 
10245
    return chart;
 
10246
  };
 
10247
 
 
10248
  //============================================================
 
10249
 
 
10250
 
 
10251
  return chart;
 
10252
}
 
10253
nv.models.pie = function() {
 
10254
  "use strict";
 
10255
  //============================================================
 
10256
  // Public Variables with Default Settings
 
10257
  //------------------------------------------------------------
 
10258
 
 
10259
  var margin = {top: 0, right: 0, bottom: 0, left: 0}
 
10260
    , width = 500
 
10261
    , height = 500
 
10262
    , getX = function(d) { return d.x }
 
10263
    , getY = function(d) { return d.y }
 
10264
    , getDescription = function(d) { return d.description }
 
10265
    , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
 
10266
    , color = nv.utils.defaultColor()
 
10267
    , valueFormat = d3.format(',.2f')
 
10268
    , showLabels = true
 
10269
    , pieLabelsOutside = true
 
10270
    , donutLabelsOutside = false
 
10271
    , labelType = "key"
 
10272
    , labelThreshold = .02 //if slice percentage is under this, don't show label
 
10273
    , donut = false
 
10274
    , labelSunbeamLayout = false
 
10275
    , startAngle = false
 
10276
    , endAngle = false
 
10277
    , donutRatio = 0.5
 
10278
    , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
 
10279
    ;
 
10280
 
 
10281
  //============================================================
 
10282
 
 
10283
 
 
10284
  function chart(selection) {
 
10285
    selection.each(function(data) {
 
10286
      var availableWidth = width - margin.left - margin.right,
 
10287
          availableHeight = height - margin.top - margin.bottom,
 
10288
          radius = Math.min(availableWidth, availableHeight) / 2,
 
10289
          arcRadius = radius-(radius / 5),
 
10290
          container = d3.select(this);
 
10291
 
 
10292
 
 
10293
      //------------------------------------------------------------
 
10294
      // Setup containers and skeleton of chart
 
10295
 
 
10296
      //var wrap = container.selectAll('.nv-wrap.nv-pie').data([data]);
 
10297
      var wrap = container.selectAll('.nv-wrap.nv-pie').data(data);
 
10298
      var wrapEnter = wrap.enter().append('g').attr('class','nvd3 nv-wrap nv-pie nv-chart-' + id);
 
10299
      var gEnter = wrapEnter.append('g');
 
10300
      var g = wrap.select('g');
 
10301
 
 
10302
      gEnter.append('g').attr('class', 'nv-pie');
 
10303
      gEnter.append('g').attr('class', 'nv-pieLabels');
 
10304
 
 
10305
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
10306
      g.select('.nv-pie').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
 
10307
      g.select('.nv-pieLabels').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
 
10308
 
 
10309
      //------------------------------------------------------------
 
10310
 
 
10311
 
 
10312
      container
 
10313
          .on('click', function(d,i) {
 
10314
              dispatch.chartClick({
 
10315
                  data: d,
 
10316
                  index: i,
 
10317
                  pos: d3.event,
 
10318
                  id: id
 
10319
              });
 
10320
          });
 
10321
 
 
10322
 
 
10323
      var arc = d3.svg.arc()
 
10324
                  .outerRadius(arcRadius);
 
10325
 
 
10326
      if (startAngle) arc.startAngle(startAngle)
 
10327
      if (endAngle) arc.endAngle(endAngle);
 
10328
      if (donut) arc.innerRadius(radius * donutRatio);
 
10329
 
 
10330
      // Setup the Pie chart and choose the data element
 
10331
      var pie = d3.layout.pie()
 
10332
          .sort(null)
 
10333
          .value(function(d) { return d.disabled ? 0 : getY(d) });
 
10334
 
 
10335
      var slices = wrap.select('.nv-pie').selectAll('.nv-slice')
 
10336
          .data(pie);
 
10337
 
 
10338
      var pieLabels = wrap.select('.nv-pieLabels').selectAll('.nv-label')
 
10339
          .data(pie);
 
10340
 
 
10341
      slices.exit().remove();
 
10342
      pieLabels.exit().remove();
 
10343
 
 
10344
      var ae = slices.enter().append('g')
 
10345
              .attr('class', 'nv-slice')
 
10346
              .on('mouseover', function(d,i){
 
10347
                d3.select(this).classed('hover', true);
 
10348
                dispatch.elementMouseover({
 
10349
                    label: getX(d.data),
 
10350
                    value: getY(d.data),
 
10351
                    point: d.data,
 
10352
                    pointIndex: i,
 
10353
                    pos: [d3.event.pageX, d3.event.pageY],
 
10354
                    id: id
 
10355
                });
 
10356
              })
 
10357
              .on('mouseout', function(d,i){
 
10358
                d3.select(this).classed('hover', false);
 
10359
                dispatch.elementMouseout({
 
10360
                    label: getX(d.data),
 
10361
                    value: getY(d.data),
 
10362
                    point: d.data,
 
10363
                    index: i,
 
10364
                    id: id
 
10365
                });
 
10366
              })
 
10367
              .on('click', function(d,i) {
 
10368
                dispatch.elementClick({
 
10369
                    label: getX(d.data),
 
10370
                    value: getY(d.data),
 
10371
                    point: d.data,
 
10372
                    index: i,
 
10373
                    pos: d3.event,
 
10374
                    id: id
 
10375
                });
 
10376
                d3.event.stopPropagation();
 
10377
              })
 
10378
              .on('dblclick', function(d,i) {
 
10379
                dispatch.elementDblClick({
 
10380
                    label: getX(d.data),
 
10381
                    value: getY(d.data),
 
10382
                    point: d.data,
 
10383
                    index: i,
 
10384
                    pos: d3.event,
 
10385
                    id: id
 
10386
                });
 
10387
                d3.event.stopPropagation();
 
10388
              });
 
10389
 
 
10390
        slices
 
10391
            .attr('fill', function(d,i) { return color(d, i); })
 
10392
            .attr('stroke', function(d,i) { return color(d, i); });
 
10393
 
 
10394
        var paths = ae.append('path')
 
10395
            .each(function(d) { this._current = d; });
 
10396
            //.attr('d', arc);
 
10397
 
 
10398
        slices.select('path')
 
10399
          .transition()
 
10400
            .attr('d', arc)
 
10401
            .attrTween('d', arcTween);
 
10402
 
 
10403
        if (showLabels) {
 
10404
          // This does the normal label
 
10405
          var labelsArc = d3.svg.arc().innerRadius(0);
 
10406
 
 
10407
          if (pieLabelsOutside){ labelsArc = arc; }
 
10408
 
 
10409
          if (donutLabelsOutside) { labelsArc = d3.svg.arc().outerRadius(arc.outerRadius()); }
 
10410
 
 
10411
          pieLabels.enter().append("g").classed("nv-label",true)
 
10412
            .each(function(d,i) {
 
10413
                var group = d3.select(this);
 
10414
 
 
10415
                group
 
10416
                  .attr('transform', function(d) {
 
10417
                       if (labelSunbeamLayout) {
 
10418
                         d.outerRadius = arcRadius + 10; // Set Outer Coordinate
 
10419
                         d.innerRadius = arcRadius + 15; // Set Inner Coordinate
 
10420
                         var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
 
10421
                         if ((d.startAngle+d.endAngle)/2 < Math.PI) {
 
10422
                           rotateAngle -= 90;
 
10423
                         } else {
 
10424
                           rotateAngle += 90;
 
10425
                         }
 
10426
                         return 'translate(' + labelsArc.centroid(d) + ') rotate(' + rotateAngle + ')';
 
10427
                       } else {
 
10428
                         d.outerRadius = radius + 10; // Set Outer Coordinate
 
10429
                         d.innerRadius = radius + 15; // Set Inner Coordinate
 
10430
                         return 'translate(' + labelsArc.centroid(d) + ')'
 
10431
                       }
 
10432
                  });
 
10433
 
 
10434
                group.append('rect')
 
10435
                    .style('stroke', '#fff')
 
10436
                    .style('fill', '#fff')
 
10437
                    .attr("rx", 3)
 
10438
                    .attr("ry", 3);
 
10439
 
 
10440
                group.append('text')
 
10441
                    .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
 
10442
                    .style('fill', '#000')
 
10443
 
 
10444
            });
 
10445
 
 
10446
          var labelLocationHash = {};
 
10447
          var avgHeight = 14;
 
10448
          var avgWidth = 140;
 
10449
          var createHashKey = function(coordinates) {
 
10450
 
 
10451
              return Math.floor(coordinates[0]/avgWidth) * avgWidth + ',' + Math.floor(coordinates[1]/avgHeight) * avgHeight;
 
10452
          };
 
10453
          pieLabels.transition()
 
10454
                .attr('transform', function(d) {
 
10455
                  if (labelSunbeamLayout) {
 
10456
                      d.outerRadius = arcRadius + 10; // Set Outer Coordinate
 
10457
                      d.innerRadius = arcRadius + 15; // Set Inner Coordinate
 
10458
                      var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
 
10459
                      if ((d.startAngle+d.endAngle)/2 < Math.PI) {
 
10460
                        rotateAngle -= 90;
 
10461
                      } else {
 
10462
                        rotateAngle += 90;
 
10463
                      }
 
10464
                      return 'translate(' + labelsArc.centroid(d) + ') rotate(' + rotateAngle + ')';
 
10465
                    } else {
 
10466
                      d.outerRadius = radius + 10; // Set Outer Coordinate
 
10467
                      d.innerRadius = radius + 15; // Set Inner Coordinate
 
10468
 
 
10469
                      /*
 
10470
                      Overlapping pie labels are not good. What this attempts to do is, prevent overlapping.
 
10471
                      Each label location is hashed, and if a hash collision occurs, we assume an overlap.
 
10472
                      Adjust the label's y-position to remove the overlap.
 
10473
                      */
 
10474
                      var center = labelsArc.centroid(d);
 
10475
                      var hashKey = createHashKey(center);
 
10476
                      if (labelLocationHash[hashKey]) {
 
10477
                        center[1] -= avgHeight;
 
10478
                      }
 
10479
                      labelLocationHash[createHashKey(center)] = true;
 
10480
                      return 'translate(' + center + ')'
 
10481
                    }
 
10482
                });
 
10483
          pieLabels.select(".nv-label text")
 
10484
                .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
 
10485
                .text(function(d, i) {
 
10486
                  var percent = (d.endAngle - d.startAngle) / (2 * Math.PI);
 
10487
                  var labelTypes = {
 
10488
                    "key" : getX(d.data),
 
10489
                    "value": getY(d.data),
 
10490
                    "percent": d3.format('%')(percent)
 
10491
                  };
 
10492
                  return (d.value && percent > labelThreshold) ? labelTypes[labelType] : '';
 
10493
                });
 
10494
        }
 
10495
 
 
10496
 
 
10497
        // Computes the angle of an arc, converting from radians to degrees.
 
10498
        function angle(d) {
 
10499
          var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
 
10500
          return a > 90 ? a - 180 : a;
 
10501
        }
 
10502
 
 
10503
        function arcTween(a) {
 
10504
          a.endAngle = isNaN(a.endAngle) ? 0 : a.endAngle;
 
10505
          a.startAngle = isNaN(a.startAngle) ? 0 : a.startAngle;
 
10506
          if (!donut) a.innerRadius = 0;
 
10507
          var i = d3.interpolate(this._current, a);
 
10508
          this._current = i(0);
 
10509
          return function(t) {
 
10510
            return arc(i(t));
 
10511
          };
 
10512
        }
 
10513
 
 
10514
        function tweenPie(b) {
 
10515
          b.innerRadius = 0;
 
10516
          var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
 
10517
          return function(t) {
 
10518
              return arc(i(t));
 
10519
          };
 
10520
        }
 
10521
 
 
10522
    });
 
10523
 
 
10524
    return chart;
 
10525
  }
 
10526
 
 
10527
 
 
10528
  //============================================================
 
10529
  // Expose Public Variables
 
10530
  //------------------------------------------------------------
 
10531
 
 
10532
  chart.dispatch = dispatch;
 
10533
  chart.options = nv.utils.optionsFunc.bind(chart);
 
10534
 
 
10535
  chart.margin = function(_) {
 
10536
    if (!arguments.length) return margin;
 
10537
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
10538
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
10539
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
10540
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
10541
    return chart;
 
10542
  };
 
10543
 
 
10544
  chart.width = function(_) {
 
10545
    if (!arguments.length) return width;
 
10546
    width = _;
 
10547
    return chart;
 
10548
  };
 
10549
 
 
10550
  chart.height = function(_) {
 
10551
    if (!arguments.length) return height;
 
10552
    height = _;
 
10553
    return chart;
 
10554
  };
 
10555
 
 
10556
  chart.values = function(_) {
 
10557
    nv.log("pie.values() is no longer supported.");
 
10558
    return chart;
 
10559
  };
 
10560
 
 
10561
  chart.x = function(_) {
 
10562
    if (!arguments.length) return getX;
 
10563
    getX = _;
 
10564
    return chart;
 
10565
  };
 
10566
 
 
10567
  chart.y = function(_) {
 
10568
    if (!arguments.length) return getY;
 
10569
    getY = d3.functor(_);
 
10570
    return chart;
 
10571
  };
 
10572
 
 
10573
  chart.description = function(_) {
 
10574
    if (!arguments.length) return getDescription;
 
10575
    getDescription = _;
 
10576
    return chart;
 
10577
  };
 
10578
 
 
10579
  chart.showLabels = function(_) {
 
10580
    if (!arguments.length) return showLabels;
 
10581
    showLabels = _;
 
10582
    return chart;
 
10583
  };
 
10584
 
 
10585
  chart.labelSunbeamLayout = function(_) {
 
10586
    if (!arguments.length) return labelSunbeamLayout;
 
10587
    labelSunbeamLayout = _;
 
10588
    return chart;
 
10589
  };
 
10590
 
 
10591
  chart.donutLabelsOutside = function(_) {
 
10592
    if (!arguments.length) return donutLabelsOutside;
 
10593
    donutLabelsOutside = _;
 
10594
    return chart;
 
10595
  };
 
10596
 
 
10597
  chart.pieLabelsOutside = function(_) {
 
10598
    if (!arguments.length) return pieLabelsOutside;
 
10599
    pieLabelsOutside = _;
 
10600
    return chart;
 
10601
  };
 
10602
 
 
10603
  chart.labelType = function(_) {
 
10604
    if (!arguments.length) return labelType;
 
10605
    labelType = _;
 
10606
    labelType = labelType || "key";
 
10607
    return chart;
 
10608
  };
 
10609
 
 
10610
  chart.donut = function(_) {
 
10611
    if (!arguments.length) return donut;
 
10612
    donut = _;
 
10613
    return chart;
 
10614
  };
 
10615
 
 
10616
  chart.donutRatio = function(_) {
 
10617
    if (!arguments.length) return donutRatio;
 
10618
    donutRatio = _;
 
10619
    return chart;
 
10620
  };
 
10621
 
 
10622
  chart.startAngle = function(_) {
 
10623
    if (!arguments.length) return startAngle;
 
10624
    startAngle = _;
 
10625
    return chart;
 
10626
  };
 
10627
 
 
10628
  chart.endAngle = function(_) {
 
10629
    if (!arguments.length) return endAngle;
 
10630
    endAngle = _;
 
10631
    return chart;
 
10632
  };
 
10633
 
 
10634
  chart.id = function(_) {
 
10635
    if (!arguments.length) return id;
 
10636
    id = _;
 
10637
    return chart;
 
10638
  };
 
10639
 
 
10640
  chart.color = function(_) {
 
10641
    if (!arguments.length) return color;
 
10642
    color = nv.utils.getColor(_);
 
10643
    return chart;
 
10644
  };
 
10645
 
 
10646
  chart.valueFormat = function(_) {
 
10647
    if (!arguments.length) return valueFormat;
 
10648
    valueFormat = _;
 
10649
    return chart;
 
10650
  };
 
10651
 
 
10652
  chart.labelThreshold = function(_) {
 
10653
    if (!arguments.length) return labelThreshold;
 
10654
    labelThreshold = _;
 
10655
    return chart;
 
10656
  };
 
10657
  //============================================================
 
10658
 
 
10659
 
 
10660
  return chart;
 
10661
}
 
10662
nv.models.pieChart = function() {
 
10663
  "use strict";
 
10664
  //============================================================
 
10665
  // Public Variables with Default Settings
 
10666
  //------------------------------------------------------------
 
10667
 
 
10668
  var pie = nv.models.pie()
 
10669
    , legend = nv.models.legend()
 
10670
    ;
 
10671
 
 
10672
  var margin = {top: 30, right: 20, bottom: 20, left: 20}
 
10673
    , width = null
 
10674
    , height = null
 
10675
    , showLegend = true
 
10676
    , color = nv.utils.defaultColor()
 
10677
    , tooltips = true
 
10678
    , tooltip = function(key, y, e, graph) {
 
10679
        return '<h3>' + key + '</h3>' +
 
10680
               '<p>' +  y + '</p>'
 
10681
      }
 
10682
    , state = {}
 
10683
    , defaultState = null
 
10684
    , noData = "No Data Available."
 
10685
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
 
10686
    ;
 
10687
 
 
10688
  //============================================================
 
10689
 
 
10690
 
 
10691
  //============================================================
 
10692
  // Private Variables
 
10693
  //------------------------------------------------------------
 
10694
 
 
10695
  var showTooltip = function(e, offsetElement) {
 
10696
    var tooltipLabel = pie.description()(e.point) || pie.x()(e.point)
 
10697
    var left = e.pos[0] + ( (offsetElement && offsetElement.offsetLeft) || 0 ),
 
10698
        top = e.pos[1] + ( (offsetElement && offsetElement.offsetTop) || 0),
 
10699
        y = pie.valueFormat()(pie.y()(e.point)),
 
10700
        content = tooltip(tooltipLabel, y, e, chart);
 
10701
 
 
10702
    nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
 
10703
  };
 
10704
 
 
10705
  //============================================================
 
10706
 
 
10707
 
 
10708
  function chart(selection) {
 
10709
    selection.each(function(data) {
 
10710
      var container = d3.select(this),
 
10711
          that = this;
 
10712
 
 
10713
      var availableWidth = (width || parseInt(container.style('width')) || 960)
 
10714
                             - margin.left - margin.right,
 
10715
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
10716
                             - margin.top - margin.bottom;
 
10717
 
 
10718
      chart.update = function() { container.transition().call(chart); };
 
10719
      chart.container = this;
 
10720
 
 
10721
      //set state.disabled
 
10722
      state.disabled = data.map(function(d) { return !!d.disabled });
 
10723
 
 
10724
      if (!defaultState) {
 
10725
        var key;
 
10726
        defaultState = {};
 
10727
        for (key in state) {
 
10728
          if (state[key] instanceof Array)
 
10729
            defaultState[key] = state[key].slice(0);
 
10730
          else
 
10731
            defaultState[key] = state[key];
 
10732
        }
 
10733
      }
 
10734
 
 
10735
      //------------------------------------------------------------
 
10736
      // Display No Data message if there's nothing to show.
 
10737
 
 
10738
      if (!data || !data.length) {
 
10739
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
10740
 
 
10741
        noDataText.enter().append('text')
 
10742
          .attr('class', 'nvd3 nv-noData')
 
10743
          .attr('dy', '-.7em')
 
10744
          .style('text-anchor', 'middle');
 
10745
 
 
10746
        noDataText
 
10747
          .attr('x', margin.left + availableWidth / 2)
 
10748
          .attr('y', margin.top + availableHeight / 2)
 
10749
          .text(function(d) { return d });
 
10750
 
 
10751
        return chart;
 
10752
      } else {
 
10753
        container.selectAll('.nv-noData').remove();
 
10754
      }
 
10755
 
 
10756
      //------------------------------------------------------------
 
10757
 
 
10758
 
 
10759
      //------------------------------------------------------------
 
10760
      // Setup containers and skeleton of chart
 
10761
 
 
10762
      var wrap = container.selectAll('g.nv-wrap.nv-pieChart').data([data]);
 
10763
      var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-pieChart').append('g');
 
10764
      var g = wrap.select('g');
 
10765
 
 
10766
      gEnter.append('g').attr('class', 'nv-pieWrap');
 
10767
      gEnter.append('g').attr('class', 'nv-legendWrap');
 
10768
 
 
10769
      //------------------------------------------------------------
 
10770
 
 
10771
 
 
10772
      //------------------------------------------------------------
 
10773
      // Legend
 
10774
 
 
10775
      if (showLegend) {
 
10776
        legend
 
10777
          .width( availableWidth )
 
10778
          .key(pie.x());
 
10779
 
 
10780
        wrap.select('.nv-legendWrap')
 
10781
            .datum(data)
 
10782
            .call(legend);
 
10783
 
 
10784
        if ( margin.top != legend.height()) {
 
10785
          margin.top = legend.height();
 
10786
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
10787
                             - margin.top - margin.bottom;
 
10788
        }
 
10789
 
 
10790
        wrap.select('.nv-legendWrap')
 
10791
            .attr('transform', 'translate(0,' + (-margin.top) +')');
 
10792
      }
 
10793
 
 
10794
      //------------------------------------------------------------
 
10795
 
 
10796
 
 
10797
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
10798
 
 
10799
 
 
10800
      //------------------------------------------------------------
 
10801
      // Main Chart Component(s)
 
10802
 
 
10803
      pie
 
10804
        .width(availableWidth)
 
10805
        .height(availableHeight);
 
10806
 
 
10807
 
 
10808
      var pieWrap = g.select('.nv-pieWrap')
 
10809
          .datum([data]);
 
10810
 
 
10811
      d3.transition(pieWrap).call(pie);
 
10812
 
 
10813
      //------------------------------------------------------------
 
10814
 
 
10815
 
 
10816
      //============================================================
 
10817
      // Event Handling/Dispatching (in chart's scope)
 
10818
      //------------------------------------------------------------
 
10819
 
 
10820
      legend.dispatch.on('stateChange', function(newState) {
 
10821
        state = newState;
 
10822
        dispatch.stateChange(state);
 
10823
        chart.update();
 
10824
      });
 
10825
 
 
10826
      pie.dispatch.on('elementMouseout.tooltip', function(e) {
 
10827
        dispatch.tooltipHide(e);
 
10828
      });
 
10829
 
 
10830
      // Update chart from a state object passed to event handler
 
10831
      dispatch.on('changeState', function(e) {
 
10832
 
 
10833
        if (typeof e.disabled !== 'undefined') {
 
10834
          data.forEach(function(series,i) {
 
10835
            series.disabled = e.disabled[i];
 
10836
          });
 
10837
 
 
10838
          state.disabled = e.disabled;
 
10839
        }
 
10840
 
 
10841
        chart.update();
 
10842
      });
 
10843
 
 
10844
      //============================================================
 
10845
 
 
10846
 
 
10847
    });
 
10848
 
 
10849
    return chart;
 
10850
  }
 
10851
 
 
10852
  //============================================================
 
10853
  // Event Handling/Dispatching (out of chart's scope)
 
10854
  //------------------------------------------------------------
 
10855
 
 
10856
  pie.dispatch.on('elementMouseover.tooltip', function(e) {
 
10857
    e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
 
10858
    dispatch.tooltipShow(e);
 
10859
  });
 
10860
 
 
10861
  dispatch.on('tooltipShow', function(e) {
 
10862
    if (tooltips) showTooltip(e);
 
10863
  });
 
10864
 
 
10865
  dispatch.on('tooltipHide', function() {
 
10866
    if (tooltips) nv.tooltip.cleanup();
 
10867
  });
 
10868
 
 
10869
  //============================================================
 
10870
 
 
10871
 
 
10872
  //============================================================
 
10873
  // Expose Public Variables
 
10874
  //------------------------------------------------------------
 
10875
 
 
10876
  // expose chart's sub-components
 
10877
  chart.legend = legend;
 
10878
  chart.dispatch = dispatch;
 
10879
  chart.pie = pie;
 
10880
 
 
10881
  d3.rebind(chart, pie, 'valueFormat', 'values', 'x', 'y', 'description', 'id', 'showLabels', 'donutLabelsOutside', 'pieLabelsOutside', 'labelType', 'donut', 'donutRatio', 'labelThreshold');
 
10882
  chart.options = nv.utils.optionsFunc.bind(chart);
 
10883
  
 
10884
  chart.margin = function(_) {
 
10885
    if (!arguments.length) return margin;
 
10886
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
10887
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
10888
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
10889
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
10890
    return chart;
 
10891
  };
 
10892
 
 
10893
  chart.width = function(_) {
 
10894
    if (!arguments.length) return width;
 
10895
    width = _;
 
10896
    return chart;
 
10897
  };
 
10898
 
 
10899
  chart.height = function(_) {
 
10900
    if (!arguments.length) return height;
 
10901
    height = _;
 
10902
    return chart;
 
10903
  };
 
10904
 
 
10905
  chart.color = function(_) {
 
10906
    if (!arguments.length) return color;
 
10907
    color = nv.utils.getColor(_);
 
10908
    legend.color(color);
 
10909
    pie.color(color);
 
10910
    return chart;
 
10911
  };
 
10912
 
 
10913
  chart.showLegend = function(_) {
 
10914
    if (!arguments.length) return showLegend;
 
10915
    showLegend = _;
 
10916
    return chart;
 
10917
  };
 
10918
 
 
10919
  chart.tooltips = function(_) {
 
10920
    if (!arguments.length) return tooltips;
 
10921
    tooltips = _;
 
10922
    return chart;
 
10923
  };
 
10924
 
 
10925
  chart.tooltipContent = function(_) {
 
10926
    if (!arguments.length) return tooltip;
 
10927
    tooltip = _;
 
10928
    return chart;
 
10929
  };
 
10930
 
 
10931
  chart.state = function(_) {
 
10932
    if (!arguments.length) return state;
 
10933
    state = _;
 
10934
    return chart;
 
10935
  };
 
10936
 
 
10937
  chart.defaultState = function(_) {
 
10938
    if (!arguments.length) return defaultState;
 
10939
    defaultState = _;
 
10940
    return chart;
 
10941
  };
 
10942
 
 
10943
  chart.noData = function(_) {
 
10944
    if (!arguments.length) return noData;
 
10945
    noData = _;
 
10946
    return chart;
 
10947
  };
 
10948
 
 
10949
  //============================================================
 
10950
 
 
10951
 
 
10952
  return chart;
 
10953
}
 
10954
 
 
10955
nv.models.scatter = function() {
 
10956
  "use strict";
 
10957
  //============================================================
 
10958
  // Public Variables with Default Settings
 
10959
  //------------------------------------------------------------
 
10960
 
 
10961
  var margin       = {top: 0, right: 0, bottom: 0, left: 0}
 
10962
    , width        = 960
 
10963
    , height       = 500
 
10964
    , color        = nv.utils.defaultColor() // chooses color
 
10965
    , id           = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't select one
 
10966
    , x            = d3.scale.linear()
 
10967
    , y            = d3.scale.linear()
 
10968
    , z            = d3.scale.linear() //linear because d3.svg.shape.size is treated as area
 
10969
    , getX         = function(d) { return d.x } // accessor to get the x value
 
10970
    , getY         = function(d) { return d.y } // accessor to get the y value
 
10971
    , getSize      = function(d) { return d.size || 1} // accessor to get the point size
 
10972
    , getShape     = function(d) { return d.shape || 'circle' } // accessor to get point shape
 
10973
    , onlyCircles  = true // Set to false to use shapes
 
10974
    , forceX       = [] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
 
10975
    , forceY       = [] // List of numbers to Force into the Y scale
 
10976
    , forceSize    = [] // List of numbers to Force into the Size scale
 
10977
    , interactive  = true // If true, plots a voronoi overlay for advanced point intersection
 
10978
    , pointKey     = null
 
10979
    , pointActive  = function(d) { return !d.notActive } // any points that return false will be filtered out
 
10980
    , padData      = false // If true, adds half a data points width to front and back, for lining up a line chart with a bar chart
 
10981
    , padDataOuter = .1 //outerPadding to imitate ordinal scale outer padding
 
10982
    , clipEdge     = false // if true, masks points within x and y scale
 
10983
    , clipVoronoi  = true // if true, masks each point with a circle... can turn off to slightly increase performance
 
10984
    , clipRadius   = function() { return 25 } // function to get the radius for voronoi point clips
 
10985
    , xDomain      = null // Override x domain (skips the calculation from data)
 
10986
    , yDomain      = null // Override y domain
 
10987
    , xRange       = null // Override x range
 
10988
    , yRange       = null // Override y range
 
10989
    , sizeDomain   = null // Override point size domain
 
10990
    , sizeRange    = null
 
10991
    , singlePoint  = false
 
10992
    , dispatch     = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout')
 
10993
    , useVoronoi   = true
 
10994
    ;
 
10995
 
 
10996
  //============================================================
 
10997
 
 
10998
 
 
10999
  //============================================================
 
11000
  // Private Variables
 
11001
  //------------------------------------------------------------
 
11002
 
 
11003
  var x0, y0, z0 // used to store previous scales
 
11004
    , timeoutID
 
11005
    , needsUpdate = false // Flag for when the points are visually updating, but the interactive layer is behind, to disable tooltips
 
11006
    ;
 
11007
 
 
11008
  //============================================================
 
11009
 
 
11010
 
 
11011
  function chart(selection) {
 
11012
    selection.each(function(data) {
 
11013
      var availableWidth = width - margin.left - margin.right,
 
11014
          availableHeight = height - margin.top - margin.bottom,
 
11015
          container = d3.select(this);
 
11016
 
 
11017
      //add series index to each data point for reference
 
11018
      data.forEach(function(series, i) {
 
11019
        series.values.forEach(function(point) {
 
11020
          point.series = i;
 
11021
        });
 
11022
      });
 
11023
 
 
11024
      //------------------------------------------------------------
 
11025
      // Setup Scales
 
11026
 
 
11027
      // remap and flatten the data for use in calculating the scales' domains
 
11028
      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
 
11029
            d3.merge(
 
11030
              data.map(function(d) {
 
11031
                return d.values.map(function(d,i) {
 
11032
                  return { x: getX(d,i), y: getY(d,i), size: getSize(d,i) }
 
11033
                })
 
11034
              })
 
11035
            );
 
11036
 
 
11037
      x   .domain(xDomain || d3.extent(seriesData.map(function(d) { return d.x; }).concat(forceX)))
 
11038
 
 
11039
      if (padData && data[0])
 
11040
        x.range(xRange || [(availableWidth * padDataOuter +  availableWidth) / (2 *data[0].values.length), availableWidth - availableWidth * (1 + padDataOuter) / (2 * data[0].values.length)  ]);
 
11041
        //x.range([availableWidth * .5 / data[0].values.length, availableWidth * (data[0].values.length - .5)  / data[0].values.length ]);
 
11042
      else
 
11043
        x.range(xRange || [0, availableWidth]);
 
11044
 
 
11045
      y   .domain(yDomain || d3.extent(seriesData.map(function(d) { return d.y }).concat(forceY)))
 
11046
          .range(yRange || [availableHeight, 0]);
 
11047
 
 
11048
      z   .domain(sizeDomain || d3.extent(seriesData.map(function(d) { return d.size }).concat(forceSize)))
 
11049
          .range(sizeRange || [16, 256]);
 
11050
 
 
11051
      // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
 
11052
      if (x.domain()[0] === x.domain()[1] || y.domain()[0] === y.domain()[1]) singlePoint = true;
 
11053
      if (x.domain()[0] === x.domain()[1])
 
11054
        x.domain()[0] ?
 
11055
            x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
 
11056
          : x.domain([-1,1]);
 
11057
 
 
11058
      if (y.domain()[0] === y.domain()[1])
 
11059
        y.domain()[0] ?
 
11060
            y.domain([y.domain()[0] - y.domain()[0] * 0.01, y.domain()[1] + y.domain()[1] * 0.01])
 
11061
          : y.domain([-1,1]);
 
11062
 
 
11063
      if ( isNaN(x.domain()[0])) {
 
11064
          x.domain([-1,1]);
 
11065
      }
 
11066
 
 
11067
      if ( isNaN(y.domain()[0])) {
 
11068
          y.domain([-1,1]);
 
11069
      }
 
11070
 
 
11071
 
 
11072
      x0 = x0 || x;
 
11073
      y0 = y0 || y;
 
11074
      z0 = z0 || z;
 
11075
 
 
11076
      //------------------------------------------------------------
 
11077
 
 
11078
 
 
11079
      //------------------------------------------------------------
 
11080
      // Setup containers and skeleton of chart
 
11081
 
 
11082
      var wrap = container.selectAll('g.nv-wrap.nv-scatter').data([data]);
 
11083
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatter nv-chart-' + id + (singlePoint ? ' nv-single-point' : ''));
 
11084
      var defsEnter = wrapEnter.append('defs');
 
11085
      var gEnter = wrapEnter.append('g');
 
11086
      var g = wrap.select('g');
 
11087
 
 
11088
      gEnter.append('g').attr('class', 'nv-groups');
 
11089
      gEnter.append('g').attr('class', 'nv-point-paths');
 
11090
 
 
11091
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
11092
 
 
11093
      //------------------------------------------------------------
 
11094
 
 
11095
 
 
11096
      defsEnter.append('clipPath')
 
11097
          .attr('id', 'nv-edge-clip-' + id)
 
11098
        .append('rect');
 
11099
 
 
11100
      wrap.select('#nv-edge-clip-' + id + ' rect')
 
11101
          .attr('width', availableWidth)
 
11102
          .attr('height', (availableHeight > 0) ? availableHeight : 0);
 
11103
 
 
11104
      g   .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
 
11105
 
 
11106
 
 
11107
      function updateInteractiveLayer() {
 
11108
 
 
11109
        if (!interactive) return false;
 
11110
 
 
11111
        var eventElements;
 
11112
 
 
11113
        var vertices = d3.merge(data.map(function(group, groupIndex) {
 
11114
            return group.values
 
11115
              .map(function(point, pointIndex) {
 
11116
                // *Adding noise to make duplicates very unlikely
 
11117
                // *Injecting series and point index for reference
 
11118
                /* *Adding a 'jitter' to the points, because there's an issue in d3.geom.voronoi.
 
11119
                */
 
11120
                var pX = getX(point,pointIndex);
 
11121
                var pY = getY(point,pointIndex);
 
11122
 
 
11123
                return [x(pX)+ Math.random() * 1e-7,
 
11124
                        y(pY)+ Math.random() * 1e-7,
 
11125
                        groupIndex,
 
11126
                        pointIndex, point]; //temp hack to add noise untill I think of a better way so there are no duplicates
 
11127
              })
 
11128
              .filter(function(pointArray, pointIndex) {
 
11129
                return pointActive(pointArray[4], pointIndex); // Issue #237.. move filter to after map, so pointIndex is correct!
 
11130
              })
 
11131
          })
 
11132
        );
 
11133
 
 
11134
 
 
11135
 
 
11136
        //inject series and point index for reference into voronoi
 
11137
        if (useVoronoi === true) {
 
11138
 
 
11139
          if (clipVoronoi) {
 
11140
            var pointClipsEnter = wrap.select('defs').selectAll('.nv-point-clips')
 
11141
                .data([id])
 
11142
              .enter();
 
11143
 
 
11144
            pointClipsEnter.append('clipPath')
 
11145
                  .attr('class', 'nv-point-clips')
 
11146
                  .attr('id', 'nv-points-clip-' + id);
 
11147
 
 
11148
            var pointClips = wrap.select('#nv-points-clip-' + id).selectAll('circle')
 
11149
                .data(vertices);
 
11150
            pointClips.enter().append('circle')
 
11151
                .attr('r', clipRadius);
 
11152
            pointClips.exit().remove();
 
11153
            pointClips
 
11154
                .attr('cx', function(d) { return d[0] })
 
11155
                .attr('cy', function(d) { return d[1] });
 
11156
 
 
11157
            wrap.select('.nv-point-paths')
 
11158
                .attr('clip-path', 'url(#nv-points-clip-' + id + ')');
 
11159
          }
 
11160
 
 
11161
 
 
11162
          if(vertices.length) {
 
11163
            // Issue #283 - Adding 2 dummy points to the voronoi b/c voronoi requires min 3 points to work
 
11164
            vertices.push([x.range()[0] - 20, y.range()[0] - 20, null, null]);
 
11165
            vertices.push([x.range()[1] + 20, y.range()[1] + 20, null, null]);
 
11166
            vertices.push([x.range()[0] - 20, y.range()[0] + 20, null, null]);
 
11167
            vertices.push([x.range()[1] + 20, y.range()[1] - 20, null, null]);
 
11168
          }
 
11169
 
 
11170
          var bounds = d3.geom.polygon([
 
11171
              [-10,-10],
 
11172
              [-10,height + 10],
 
11173
              [width + 10,height + 10],
 
11174
              [width + 10,-10]
 
11175
          ]);
 
11176
 
 
11177
          var voronoi = d3.geom.voronoi(vertices).map(function(d, i) {
 
11178
              return {
 
11179
                'data': bounds.clip(d),
 
11180
                'series': vertices[i][2],
 
11181
                'point': vertices[i][3]
 
11182
              }
 
11183
            });
 
11184
 
 
11185
 
 
11186
          var pointPaths = wrap.select('.nv-point-paths').selectAll('path')
 
11187
              .data(voronoi);
 
11188
          pointPaths.enter().append('path')
 
11189
              .attr('class', function(d,i) { return 'nv-path-'+i; });
 
11190
          pointPaths.exit().remove();
 
11191
          pointPaths
 
11192
              .attr('d', function(d) {
 
11193
                if (d.data.length === 0)
 
11194
                    return 'M 0 0'
 
11195
                else
 
11196
                    return 'M' + d.data.join('L') + 'Z';
 
11197
              });
 
11198
 
 
11199
          var mouseEventCallback = function(d,mDispatch) {
 
11200
                if (needsUpdate) return 0;
 
11201
                var series = data[d.series];
 
11202
                if (typeof series === 'undefined') return;
 
11203
 
 
11204
                var point  = series.values[d.point];
 
11205
 
 
11206
                mDispatch({
 
11207
                  point: point,
 
11208
                  series: series,
 
11209
                  pos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
 
11210
                  seriesIndex: d.series,
 
11211
                  pointIndex: d.point
 
11212
                });
 
11213
          };
 
11214
 
 
11215
          pointPaths
 
11216
              .on('click', function(d) {
 
11217
                mouseEventCallback(d, dispatch.elementClick);
 
11218
              })
 
11219
              .on('mouseover', function(d) {
 
11220
                mouseEventCallback(d, dispatch.elementMouseover);
 
11221
              })
 
11222
              .on('mouseout', function(d, i) {
 
11223
                mouseEventCallback(d, dispatch.elementMouseout);
 
11224
              });
 
11225
 
 
11226
 
 
11227
        } else {
 
11228
          /*
 
11229
          // bring data in form needed for click handlers
 
11230
          var dataWithPoints = vertices.map(function(d, i) {
 
11231
              return {
 
11232
                'data': d,
 
11233
                'series': vertices[i][2],
 
11234
                'point': vertices[i][3]
 
11235
              }
 
11236
            });
 
11237
           */
 
11238
 
 
11239
          // add event handlers to points instead voronoi paths
 
11240
          wrap.select('.nv-groups').selectAll('.nv-group')
 
11241
            .selectAll('.nv-point')
 
11242
              //.data(dataWithPoints)
 
11243
              //.style('pointer-events', 'auto') // recativate events, disabled by css
 
11244
              .on('click', function(d,i) {
 
11245
                //nv.log('test', d, i);
 
11246
                if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
 
11247
                var series = data[d.series],
 
11248
                    point  = series.values[i];
 
11249
 
 
11250
                dispatch.elementClick({
 
11251
                  point: point,
 
11252
                  series: series,
 
11253
                  pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
 
11254
                  seriesIndex: d.series,
 
11255
                  pointIndex: i
 
11256
                });
 
11257
              })
 
11258
              .on('mouseover', function(d,i) {
 
11259
                if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
 
11260
                var series = data[d.series],
 
11261
                    point  = series.values[i];
 
11262
 
 
11263
                dispatch.elementMouseover({
 
11264
                  point: point,
 
11265
                  series: series,
 
11266
                  pos: [x(getX(point, i)) + margin.left, y(getY(point, i)) + margin.top],
 
11267
                  seriesIndex: d.series,
 
11268
                  pointIndex: i
 
11269
                });
 
11270
              })
 
11271
              .on('mouseout', function(d,i) {
 
11272
                if (needsUpdate || !data[d.series]) return 0; //check if this is a dummy point
 
11273
                var series = data[d.series],
 
11274
                    point  = series.values[i];
 
11275
 
 
11276
                dispatch.elementMouseout({
 
11277
                  point: point,
 
11278
                  series: series,
 
11279
                  seriesIndex: d.series,
 
11280
                  pointIndex: i
 
11281
                });
 
11282
              });
 
11283
          }
 
11284
 
 
11285
          needsUpdate = false;
 
11286
      }
 
11287
 
 
11288
      needsUpdate = true;
 
11289
 
 
11290
      var groups = wrap.select('.nv-groups').selectAll('.nv-group')
 
11291
          .data(function(d) { return d }, function(d) { return d.key });
 
11292
      groups.enter().append('g')
 
11293
          .style('stroke-opacity', 1e-6)
 
11294
          .style('fill-opacity', 1e-6);
 
11295
      groups.exit()
 
11296
          .remove();
 
11297
      groups
 
11298
          .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
 
11299
          .classed('hover', function(d) { return d.hover });
 
11300
      groups
 
11301
          .transition()
 
11302
          .style('fill', function(d,i) { return color(d, i) })
 
11303
          .style('stroke', function(d,i) { return color(d, i) })
 
11304
          .style('stroke-opacity', 1)
 
11305
          .style('fill-opacity', .5);
 
11306
 
 
11307
 
 
11308
      if (onlyCircles) {
 
11309
 
 
11310
        var points = groups.selectAll('circle.nv-point')
 
11311
            .data(function(d) { return d.values }, pointKey);
 
11312
        points.enter().append('circle')
 
11313
            .style('fill', function (d,i) { return d.color })
 
11314
            .style('stroke', function (d,i) { return d.color })
 
11315
            .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x0(getX(d,i))) })
 
11316
            .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
 
11317
            .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) });
 
11318
        points.exit().remove();
 
11319
        groups.exit().selectAll('path.nv-point').transition()
 
11320
            .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
 
11321
            .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
 
11322
            .remove();
 
11323
        points.each(function(d,i) {
 
11324
          d3.select(this)
 
11325
            .classed('nv-point', true)
 
11326
            .classed('nv-point-' + i, true)
 
11327
            .classed('hover',false)
 
11328
            ;
 
11329
        });
 
11330
        points.transition()
 
11331
            .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
 
11332
            .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
 
11333
            .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) });
 
11334
 
 
11335
      } else {
 
11336
 
 
11337
        var points = groups.selectAll('path.nv-point')
 
11338
            .data(function(d) { return d.values });
 
11339
        points.enter().append('path')
 
11340
            .style('fill', function (d,i) { return d.color })
 
11341
            .style('stroke', function (d,i) { return d.color })
 
11342
            .attr('transform', function(d,i) {
 
11343
              return 'translate(' + x0(getX(d,i)) + ',' + y0(getY(d,i)) + ')'
 
11344
            })
 
11345
            .attr('d',
 
11346
              d3.svg.symbol()
 
11347
                .type(getShape)
 
11348
                .size(function(d,i) { return z(getSize(d,i)) })
 
11349
            );
 
11350
        points.exit().remove();
 
11351
        groups.exit().selectAll('path.nv-point')
 
11352
            .transition()
 
11353
            .attr('transform', function(d,i) {
 
11354
              return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
 
11355
            })
 
11356
            .remove();
 
11357
        points.each(function(d,i) {
 
11358
          d3.select(this)
 
11359
            .classed('nv-point', true)
 
11360
            .classed('nv-point-' + i, true)
 
11361
            .classed('hover',false)
 
11362
            ;
 
11363
        });
 
11364
        points.transition()
 
11365
            .attr('transform', function(d,i) {
 
11366
              //nv.log(d,i,getX(d,i), x(getX(d,i)));
 
11367
              return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
 
11368
            })
 
11369
            .attr('d',
 
11370
              d3.svg.symbol()
 
11371
                .type(getShape)
 
11372
                .size(function(d,i) { return z(getSize(d,i)) })
 
11373
            );
 
11374
      }
 
11375
 
 
11376
 
 
11377
      // Delay updating the invisible interactive layer for smoother animation
 
11378
      clearTimeout(timeoutID); // stop repeat calls to updateInteractiveLayer
 
11379
      timeoutID = setTimeout(updateInteractiveLayer, 300);
 
11380
      //updateInteractiveLayer();
 
11381
 
 
11382
      //store old scales for use in transitions on update
 
11383
      x0 = x.copy();
 
11384
      y0 = y.copy();
 
11385
      z0 = z.copy();
 
11386
 
 
11387
    });
 
11388
 
 
11389
    return chart;
 
11390
  }
 
11391
 
 
11392
 
 
11393
  //============================================================
 
11394
  // Event Handling/Dispatching (out of chart's scope)
 
11395
  //------------------------------------------------------------
 
11396
  chart.clearHighlights = function() {
 
11397
      //Remove the 'hover' class from all highlighted points.
 
11398
      d3.selectAll(".nv-chart-" + id + " .nv-point.hover").classed("hover",false);
 
11399
  };
 
11400
 
 
11401
  chart.highlightPoint = function(seriesIndex,pointIndex,isHoverOver) {
 
11402
      d3.select(".nv-chart-" + id + " .nv-series-" + seriesIndex + " .nv-point-" + pointIndex)
 
11403
          .classed("hover",isHoverOver);
 
11404
  };
 
11405
 
 
11406
 
 
11407
  dispatch.on('elementMouseover.point', function(d) {
 
11408
     if (interactive) chart.highlightPoint(d.seriesIndex,d.pointIndex,true);
 
11409
  });
 
11410
 
 
11411
  dispatch.on('elementMouseout.point', function(d) {
 
11412
     if (interactive) chart.highlightPoint(d.seriesIndex,d.pointIndex,false);
 
11413
  });
 
11414
 
 
11415
  //============================================================
 
11416
 
 
11417
 
 
11418
  //============================================================
 
11419
  // Expose Public Variables
 
11420
  //------------------------------------------------------------
 
11421
 
 
11422
  chart.dispatch = dispatch;
 
11423
  chart.options = nv.utils.optionsFunc.bind(chart);
 
11424
 
 
11425
  chart.x = function(_) {
 
11426
    if (!arguments.length) return getX;
 
11427
    getX = d3.functor(_);
 
11428
    return chart;
 
11429
  };
 
11430
 
 
11431
  chart.y = function(_) {
 
11432
    if (!arguments.length) return getY;
 
11433
    getY = d3.functor(_);
 
11434
    return chart;
 
11435
  };
 
11436
 
 
11437
  chart.size = function(_) {
 
11438
    if (!arguments.length) return getSize;
 
11439
    getSize = d3.functor(_);
 
11440
    return chart;
 
11441
  };
 
11442
 
 
11443
  chart.margin = function(_) {
 
11444
    if (!arguments.length) return margin;
 
11445
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
11446
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
11447
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
11448
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
11449
    return chart;
 
11450
  };
 
11451
 
 
11452
  chart.width = function(_) {
 
11453
    if (!arguments.length) return width;
 
11454
    width = _;
 
11455
    return chart;
 
11456
  };
 
11457
 
 
11458
  chart.height = function(_) {
 
11459
    if (!arguments.length) return height;
 
11460
    height = _;
 
11461
    return chart;
 
11462
  };
 
11463
 
 
11464
  chart.xScale = function(_) {
 
11465
    if (!arguments.length) return x;
 
11466
    x = _;
 
11467
    return chart;
 
11468
  };
 
11469
 
 
11470
  chart.yScale = function(_) {
 
11471
    if (!arguments.length) return y;
 
11472
    y = _;
 
11473
    return chart;
 
11474
  };
 
11475
 
 
11476
  chart.zScale = function(_) {
 
11477
    if (!arguments.length) return z;
 
11478
    z = _;
 
11479
    return chart;
 
11480
  };
 
11481
 
 
11482
  chart.xDomain = function(_) {
 
11483
    if (!arguments.length) return xDomain;
 
11484
    xDomain = _;
 
11485
    return chart;
 
11486
  };
 
11487
 
 
11488
  chart.yDomain = function(_) {
 
11489
    if (!arguments.length) return yDomain;
 
11490
    yDomain = _;
 
11491
    return chart;
 
11492
  };
 
11493
 
 
11494
  chart.sizeDomain = function(_) {
 
11495
    if (!arguments.length) return sizeDomain;
 
11496
    sizeDomain = _;
 
11497
    return chart;
 
11498
  };
 
11499
 
 
11500
  chart.xRange = function(_) {
 
11501
    if (!arguments.length) return xRange;
 
11502
    xRange = _;
 
11503
    return chart;
 
11504
  };
 
11505
 
 
11506
  chart.yRange = function(_) {
 
11507
    if (!arguments.length) return yRange;
 
11508
    yRange = _;
 
11509
    return chart;
 
11510
  };
 
11511
 
 
11512
  chart.sizeRange = function(_) {
 
11513
    if (!arguments.length) return sizeRange;
 
11514
    sizeRange = _;
 
11515
    return chart;
 
11516
  };
 
11517
 
 
11518
  chart.forceX = function(_) {
 
11519
    if (!arguments.length) return forceX;
 
11520
    forceX = _;
 
11521
    return chart;
 
11522
  };
 
11523
 
 
11524
  chart.forceY = function(_) {
 
11525
    if (!arguments.length) return forceY;
 
11526
    forceY = _;
 
11527
    return chart;
 
11528
  };
 
11529
 
 
11530
  chart.forceSize = function(_) {
 
11531
    if (!arguments.length) return forceSize;
 
11532
    forceSize = _;
 
11533
    return chart;
 
11534
  };
 
11535
 
 
11536
  chart.interactive = function(_) {
 
11537
    if (!arguments.length) return interactive;
 
11538
    interactive = _;
 
11539
    return chart;
 
11540
  };
 
11541
 
 
11542
  chart.pointKey = function(_) {
 
11543
    if (!arguments.length) return pointKey;
 
11544
    pointKey = _;
 
11545
    return chart;
 
11546
  };
 
11547
 
 
11548
  chart.pointActive = function(_) {
 
11549
    if (!arguments.length) return pointActive;
 
11550
    pointActive = _;
 
11551
    return chart;
 
11552
  };
 
11553
 
 
11554
  chart.padData = function(_) {
 
11555
    if (!arguments.length) return padData;
 
11556
    padData = _;
 
11557
    return chart;
 
11558
  };
 
11559
 
 
11560
  chart.padDataOuter = function(_) {
 
11561
    if (!arguments.length) return padDataOuter;
 
11562
    padDataOuter = _;
 
11563
    return chart;
 
11564
  };
 
11565
 
 
11566
  chart.clipEdge = function(_) {
 
11567
    if (!arguments.length) return clipEdge;
 
11568
    clipEdge = _;
 
11569
    return chart;
 
11570
  };
 
11571
 
 
11572
  chart.clipVoronoi= function(_) {
 
11573
    if (!arguments.length) return clipVoronoi;
 
11574
    clipVoronoi = _;
 
11575
    return chart;
 
11576
  };
 
11577
 
 
11578
  chart.useVoronoi= function(_) {
 
11579
    if (!arguments.length) return useVoronoi;
 
11580
    useVoronoi = _;
 
11581
    if (useVoronoi === false) {
 
11582
        clipVoronoi = false;
 
11583
    }
 
11584
    return chart;
 
11585
  };
 
11586
 
 
11587
  chart.clipRadius = function(_) {
 
11588
    if (!arguments.length) return clipRadius;
 
11589
    clipRadius = _;
 
11590
    return chart;
 
11591
  };
 
11592
 
 
11593
  chart.color = function(_) {
 
11594
    if (!arguments.length) return color;
 
11595
    color = nv.utils.getColor(_);
 
11596
    return chart;
 
11597
  };
 
11598
 
 
11599
  chart.shape = function(_) {
 
11600
    if (!arguments.length) return getShape;
 
11601
    getShape = _;
 
11602
    return chart;
 
11603
  };
 
11604
 
 
11605
  chart.onlyCircles = function(_) {
 
11606
    if (!arguments.length) return onlyCircles;
 
11607
    onlyCircles = _;
 
11608
    return chart;
 
11609
  };
 
11610
 
 
11611
  chart.id = function(_) {
 
11612
    if (!arguments.length) return id;
 
11613
    id = _;
 
11614
    return chart;
 
11615
  };
 
11616
 
 
11617
  chart.singlePoint = function(_) {
 
11618
    if (!arguments.length) return singlePoint;
 
11619
    singlePoint = _;
 
11620
    return chart;
 
11621
  };
 
11622
 
 
11623
  //============================================================
 
11624
 
 
11625
 
 
11626
  return chart;
 
11627
}
 
11628
nv.models.scatterChart = function() {
 
11629
  "use strict";
 
11630
  //============================================================
 
11631
  // Public Variables with Default Settings
 
11632
  //------------------------------------------------------------
 
11633
 
 
11634
  var scatter      = nv.models.scatter()
 
11635
    , xAxis        = nv.models.axis()
 
11636
    , yAxis        = nv.models.axis()
 
11637
    , legend       = nv.models.legend()
 
11638
    , controls     = nv.models.legend()
 
11639
    , distX        = nv.models.distribution()
 
11640
    , distY        = nv.models.distribution()
 
11641
    ;
 
11642
 
 
11643
  var margin       = {top: 30, right: 20, bottom: 50, left: 75}
 
11644
    , width        = null
 
11645
    , height       = null
 
11646
    , color        = nv.utils.defaultColor()
 
11647
    , x            = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.xScale()
 
11648
    , y            = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.yScale()
 
11649
    , xPadding     = 0
 
11650
    , yPadding     = 0
 
11651
    , showDistX    = false
 
11652
    , showDistY    = false
 
11653
    , showLegend   = true
 
11654
    , showXAxis    = true
 
11655
    , showYAxis    = true
 
11656
    , rightAlignYAxis = false
 
11657
    , showControls = !!d3.fisheye
 
11658
    , fisheye      = 0
 
11659
    , pauseFisheye = false
 
11660
    , tooltips     = true
 
11661
    , tooltipX     = function(key, x, y) { return '<strong>' + x + '</strong>' }
 
11662
    , tooltipY     = function(key, x, y) { return '<strong>' + y + '</strong>' }
 
11663
    , tooltip      = null
 
11664
    , state = {}
 
11665
    , defaultState = null
 
11666
    , dispatch     = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
 
11667
    , noData       = "No Data Available."
 
11668
    , transitionDuration = 250
 
11669
    ;
 
11670
 
 
11671
  scatter
 
11672
    .xScale(x)
 
11673
    .yScale(y)
 
11674
    ;
 
11675
  xAxis
 
11676
    .orient('bottom')
 
11677
    .tickPadding(10)
 
11678
    ;
 
11679
  yAxis
 
11680
    .orient((rightAlignYAxis) ? 'right' : 'left')
 
11681
    .tickPadding(10)
 
11682
    ;
 
11683
  distX
 
11684
    .axis('x')
 
11685
    ;
 
11686
  distY
 
11687
    .axis('y')
 
11688
    ;
 
11689
 
 
11690
  controls.updateState(false);
 
11691
 
 
11692
  //============================================================
 
11693
 
 
11694
 
 
11695
  //============================================================
 
11696
  // Private Variables
 
11697
  //------------------------------------------------------------
 
11698
 
 
11699
  var x0, y0;
 
11700
 
 
11701
  var showTooltip = function(e, offsetElement) {
 
11702
    //TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?)
 
11703
 
 
11704
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
11705
        top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
11706
        leftX = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
11707
        topX = y.range()[0] + margin.top + ( offsetElement.offsetTop || 0),
 
11708
        leftY = x.range()[0] + margin.left + ( offsetElement.offsetLeft || 0 ),
 
11709
        topY = e.pos[1] + ( offsetElement.offsetTop || 0),
 
11710
        xVal = xAxis.tickFormat()(scatter.x()(e.point, e.pointIndex)),
 
11711
        yVal = yAxis.tickFormat()(scatter.y()(e.point, e.pointIndex));
 
11712
 
 
11713
      if( tooltipX != null )
 
11714
          nv.tooltip.show([leftX, topX], tooltipX(e.series.key, xVal, yVal, e, chart), 'n', 1, offsetElement, 'x-nvtooltip');
 
11715
      if( tooltipY != null )
 
11716
          nv.tooltip.show([leftY, topY], tooltipY(e.series.key, xVal, yVal, e, chart), 'e', 1, offsetElement, 'y-nvtooltip');
 
11717
      if( tooltip != null )
 
11718
          nv.tooltip.show([left, top], tooltip(e.series.key, xVal, yVal, e, chart), e.value < 0 ? 'n' : 's', null, offsetElement);
 
11719
  };
 
11720
 
 
11721
  var controlsData = [
 
11722
    { key: 'Magnify', disabled: true }
 
11723
  ];
 
11724
 
 
11725
  //============================================================
 
11726
 
 
11727
 
 
11728
  function chart(selection) {
 
11729
    selection.each(function(data) {
 
11730
      var container = d3.select(this),
 
11731
          that = this;
 
11732
 
 
11733
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
11734
                             - margin.left - margin.right,
 
11735
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
11736
                             - margin.top - margin.bottom;
 
11737
 
 
11738
      chart.update = function() { container.transition().duration(transitionDuration).call(chart); };
 
11739
      chart.container = this;
 
11740
 
 
11741
      //set state.disabled
 
11742
      state.disabled = data.map(function(d) { return !!d.disabled });
 
11743
 
 
11744
      if (!defaultState) {
 
11745
        var key;
 
11746
        defaultState = {};
 
11747
        for (key in state) {
 
11748
          if (state[key] instanceof Array)
 
11749
            defaultState[key] = state[key].slice(0);
 
11750
          else
 
11751
            defaultState[key] = state[key];
 
11752
        }
 
11753
      }
 
11754
 
 
11755
      //------------------------------------------------------------
 
11756
      // Display noData message if there's nothing to show.
 
11757
 
 
11758
      if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
 
11759
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
11760
 
 
11761
        noDataText.enter().append('text')
 
11762
          .attr('class', 'nvd3 nv-noData')
 
11763
          .attr('dy', '-.7em')
 
11764
          .style('text-anchor', 'middle');
 
11765
 
 
11766
        noDataText
 
11767
          .attr('x', margin.left + availableWidth / 2)
 
11768
          .attr('y', margin.top + availableHeight / 2)
 
11769
          .text(function(d) { return d });
 
11770
 
 
11771
        return chart;
 
11772
      } else {
 
11773
        container.selectAll('.nv-noData').remove();
 
11774
      }
 
11775
 
 
11776
      //------------------------------------------------------------
 
11777
 
 
11778
 
 
11779
      //------------------------------------------------------------
 
11780
      // Setup Scales
 
11781
 
 
11782
      x0 = x0 || x;
 
11783
      y0 = y0 || y;
 
11784
 
 
11785
      //------------------------------------------------------------
 
11786
 
 
11787
 
 
11788
      //------------------------------------------------------------
 
11789
      // Setup containers and skeleton of chart
 
11790
 
 
11791
      var wrap = container.selectAll('g.nv-wrap.nv-scatterChart').data([data]);
 
11792
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatterChart nv-chart-' + scatter.id());
 
11793
      var gEnter = wrapEnter.append('g');
 
11794
      var g = wrap.select('g');
 
11795
 
 
11796
      // background for pointer events
 
11797
      gEnter.append('rect').attr('class', 'nvd3 nv-background');
 
11798
 
 
11799
      gEnter.append('g').attr('class', 'nv-x nv-axis');
 
11800
      gEnter.append('g').attr('class', 'nv-y nv-axis');
 
11801
      gEnter.append('g').attr('class', 'nv-scatterWrap');
 
11802
      gEnter.append('g').attr('class', 'nv-distWrap');
 
11803
      gEnter.append('g').attr('class', 'nv-legendWrap');
 
11804
      gEnter.append('g').attr('class', 'nv-controlsWrap');
 
11805
 
 
11806
      //------------------------------------------------------------
 
11807
 
 
11808
 
 
11809
      //------------------------------------------------------------
 
11810
      // Legend
 
11811
 
 
11812
      if (showLegend) {
 
11813
        var legendWidth = (showControls) ? availableWidth / 2 : availableWidth;
 
11814
        legend.width(legendWidth);
 
11815
 
 
11816
        wrap.select('.nv-legendWrap')
 
11817
            .datum(data)
 
11818
            .call(legend);
 
11819
 
 
11820
        if ( margin.top != legend.height()) {
 
11821
          margin.top = legend.height();
 
11822
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
11823
                             - margin.top - margin.bottom;
 
11824
        }
 
11825
 
 
11826
        wrap.select('.nv-legendWrap')
 
11827
            .attr('transform', 'translate(' + (availableWidth - legendWidth) + ',' + (-margin.top) +')');
 
11828
      }
 
11829
 
 
11830
      //------------------------------------------------------------
 
11831
 
 
11832
 
 
11833
      //------------------------------------------------------------
 
11834
      // Controls
 
11835
 
 
11836
      if (showControls) {
 
11837
        controls.width(180).color(['#444']);
 
11838
        g.select('.nv-controlsWrap')
 
11839
            .datum(controlsData)
 
11840
            .attr('transform', 'translate(0,' + (-margin.top) +')')
 
11841
            .call(controls);
 
11842
      }
 
11843
 
 
11844
      //------------------------------------------------------------
 
11845
 
 
11846
 
 
11847
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
11848
 
 
11849
      if (rightAlignYAxis) {
 
11850
          g.select(".nv-y.nv-axis")
 
11851
              .attr("transform", "translate(" + availableWidth + ",0)");
 
11852
      }
 
11853
 
 
11854
      //------------------------------------------------------------
 
11855
      // Main Chart Component(s)
 
11856
 
 
11857
      scatter
 
11858
          .width(availableWidth)
 
11859
          .height(availableHeight)
 
11860
          .color(data.map(function(d,i) {
 
11861
            return d.color || color(d, i);
 
11862
          }).filter(function(d,i) { return !data[i].disabled }));
 
11863
 
 
11864
      if (xPadding !== 0)
 
11865
        scatter.xDomain(null);
 
11866
 
 
11867
      if (yPadding !== 0)
 
11868
        scatter.yDomain(null);
 
11869
 
 
11870
      wrap.select('.nv-scatterWrap')
 
11871
          .datum(data.filter(function(d) { return !d.disabled }))
 
11872
          .call(scatter);
 
11873
 
 
11874
      //Adjust for x and y padding
 
11875
      if (xPadding !== 0) {
 
11876
        var xRange = x.domain()[1] - x.domain()[0];
 
11877
        scatter.xDomain([x.domain()[0] - (xPadding * xRange), x.domain()[1] + (xPadding * xRange)]);
 
11878
      }
 
11879
 
 
11880
      if (yPadding !== 0) {
 
11881
        var yRange = y.domain()[1] - y.domain()[0];
 
11882
        scatter.yDomain([y.domain()[0] - (yPadding * yRange), y.domain()[1] + (yPadding * yRange)]);
 
11883
      }
 
11884
 
 
11885
      //Only need to update the scatter again if x/yPadding changed the domain.
 
11886
      if (yPadding !== 0 || xPadding !== 0) {
 
11887
        wrap.select('.nv-scatterWrap')
 
11888
            .datum(data.filter(function(d) { return !d.disabled }))
 
11889
            .call(scatter);
 
11890
      }
 
11891
 
 
11892
      //------------------------------------------------------------
 
11893
 
 
11894
 
 
11895
      //------------------------------------------------------------
 
11896
      // Setup Axes
 
11897
      if (showXAxis) {
 
11898
        xAxis
 
11899
            .scale(x)
 
11900
            .ticks( xAxis.ticks() && xAxis.ticks().length ? xAxis.ticks() : availableWidth / 100 )
 
11901
            .tickSize( -availableHeight , 0);
 
11902
 
 
11903
        g.select('.nv-x.nv-axis')
 
11904
            .attr('transform', 'translate(0,' + y.range()[0] + ')')
 
11905
            .call(xAxis);
 
11906
 
 
11907
      }
 
11908
 
 
11909
      if (showYAxis) {
 
11910
        yAxis
 
11911
            .scale(y)
 
11912
            .ticks( yAxis.ticks() && yAxis.ticks().length ? yAxis.ticks() : availableHeight / 36 )
 
11913
            .tickSize( -availableWidth, 0);
 
11914
 
 
11915
        g.select('.nv-y.nv-axis')
 
11916
            .call(yAxis);
 
11917
      }
 
11918
 
 
11919
 
 
11920
      if (showDistX) {
 
11921
        distX
 
11922
            .getData(scatter.x())
 
11923
            .scale(x)
 
11924
            .width(availableWidth)
 
11925
            .color(data.map(function(d,i) {
 
11926
              return d.color || color(d, i);
 
11927
            }).filter(function(d,i) { return !data[i].disabled }));
 
11928
        gEnter.select('.nv-distWrap').append('g')
 
11929
            .attr('class', 'nv-distributionX');
 
11930
        g.select('.nv-distributionX')
 
11931
            .attr('transform', 'translate(0,' + y.range()[0] + ')')
 
11932
            .datum(data.filter(function(d) { return !d.disabled }))
 
11933
            .call(distX);
 
11934
      }
 
11935
 
 
11936
      if (showDistY) {
 
11937
        distY
 
11938
            .getData(scatter.y())
 
11939
            .scale(y)
 
11940
            .width(availableHeight)
 
11941
            .color(data.map(function(d,i) {
 
11942
              return d.color || color(d, i);
 
11943
            }).filter(function(d,i) { return !data[i].disabled }));
 
11944
        gEnter.select('.nv-distWrap').append('g')
 
11945
            .attr('class', 'nv-distributionY');
 
11946
        g.select('.nv-distributionY')
 
11947
            .attr('transform', 
 
11948
              'translate(' + (rightAlignYAxis ? availableWidth : -distY.size() ) + ',0)')
 
11949
            .datum(data.filter(function(d) { return !d.disabled }))
 
11950
            .call(distY);
 
11951
      }
 
11952
 
 
11953
      //------------------------------------------------------------
 
11954
 
 
11955
 
 
11956
 
 
11957
 
 
11958
      if (d3.fisheye) {
 
11959
        g.select('.nv-background')
 
11960
            .attr('width', availableWidth)
 
11961
            .attr('height', availableHeight);
 
11962
 
 
11963
        g.select('.nv-background').on('mousemove', updateFisheye);
 
11964
        g.select('.nv-background').on('click', function() { pauseFisheye = !pauseFisheye;});
 
11965
        scatter.dispatch.on('elementClick.freezeFisheye', function() {
 
11966
          pauseFisheye = !pauseFisheye;
 
11967
        });
 
11968
      }
 
11969
 
 
11970
 
 
11971
      function updateFisheye() {
 
11972
        if (pauseFisheye) {
 
11973
          g.select('.nv-point-paths').style('pointer-events', 'all');
 
11974
          return false;
 
11975
        }
 
11976
 
 
11977
        g.select('.nv-point-paths').style('pointer-events', 'none' );
 
11978
 
 
11979
        var mouse = d3.mouse(this);
 
11980
        x.distortion(fisheye).focus(mouse[0]);
 
11981
        y.distortion(fisheye).focus(mouse[1]);
 
11982
 
 
11983
        g.select('.nv-scatterWrap')
 
11984
            .call(scatter);
 
11985
 
 
11986
        if (showXAxis)
 
11987
          g.select('.nv-x.nv-axis').call(xAxis);
 
11988
        
 
11989
        if (showYAxis)
 
11990
          g.select('.nv-y.nv-axis').call(yAxis);
 
11991
        
 
11992
        g.select('.nv-distributionX')
 
11993
            .datum(data.filter(function(d) { return !d.disabled }))
 
11994
            .call(distX);
 
11995
        g.select('.nv-distributionY')
 
11996
            .datum(data.filter(function(d) { return !d.disabled }))
 
11997
            .call(distY);
 
11998
      }
 
11999
 
 
12000
 
 
12001
 
 
12002
      //============================================================
 
12003
      // Event Handling/Dispatching (in chart's scope)
 
12004
      //------------------------------------------------------------
 
12005
 
 
12006
      controls.dispatch.on('legendClick', function(d,i) {
 
12007
        d.disabled = !d.disabled;
 
12008
 
 
12009
        fisheye = d.disabled ? 0 : 2.5;
 
12010
        g.select('.nv-background') .style('pointer-events', d.disabled ? 'none' : 'all');
 
12011
        g.select('.nv-point-paths').style('pointer-events', d.disabled ? 'all' : 'none' );
 
12012
 
 
12013
        if (d.disabled) {
 
12014
          x.distortion(fisheye).focus(0);
 
12015
          y.distortion(fisheye).focus(0);
 
12016
 
 
12017
          g.select('.nv-scatterWrap').call(scatter);
 
12018
          g.select('.nv-x.nv-axis').call(xAxis);
 
12019
          g.select('.nv-y.nv-axis').call(yAxis);
 
12020
        } else {
 
12021
          pauseFisheye = false;
 
12022
        }
 
12023
 
 
12024
        chart.update();
 
12025
      });
 
12026
 
 
12027
      legend.dispatch.on('stateChange', function(newState) {
 
12028
        state.disabled = newState.disabled;
 
12029
        dispatch.stateChange(state);
 
12030
        chart.update();
 
12031
      });
 
12032
 
 
12033
      scatter.dispatch.on('elementMouseover.tooltip', function(e) {
 
12034
        d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
 
12035
            .attr('y1', function(d,i) { return e.pos[1] - availableHeight;});
 
12036
        d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
 
12037
            .attr('x2', e.pos[0] + distX.size());
 
12038
 
 
12039
        e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
 
12040
        dispatch.tooltipShow(e);
 
12041
      });
 
12042
 
 
12043
      dispatch.on('tooltipShow', function(e) {
 
12044
        if (tooltips) showTooltip(e, that.parentNode);
 
12045
      });
 
12046
 
 
12047
      // Update chart from a state object passed to event handler
 
12048
      dispatch.on('changeState', function(e) {
 
12049
 
 
12050
        if (typeof e.disabled !== 'undefined') {
 
12051
          data.forEach(function(series,i) {
 
12052
            series.disabled = e.disabled[i];
 
12053
          });
 
12054
 
 
12055
          state.disabled = e.disabled;
 
12056
        }
 
12057
 
 
12058
        chart.update();
 
12059
      });
 
12060
 
 
12061
      //============================================================
 
12062
 
 
12063
 
 
12064
      //store old scales for use in transitions on update
 
12065
      x0 = x.copy();
 
12066
      y0 = y.copy();
 
12067
 
 
12068
 
 
12069
    });
 
12070
 
 
12071
    return chart;
 
12072
  }
 
12073
 
 
12074
 
 
12075
  //============================================================
 
12076
  // Event Handling/Dispatching (out of chart's scope)
 
12077
  //------------------------------------------------------------
 
12078
 
 
12079
  scatter.dispatch.on('elementMouseout.tooltip', function(e) {
 
12080
    dispatch.tooltipHide(e);
 
12081
 
 
12082
    d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
 
12083
        .attr('y1', 0);
 
12084
    d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
 
12085
        .attr('x2', distY.size());
 
12086
  });
 
12087
  dispatch.on('tooltipHide', function() {
 
12088
    if (tooltips) nv.tooltip.cleanup();
 
12089
  });
 
12090
 
 
12091
  //============================================================
 
12092
 
 
12093
 
 
12094
  //============================================================
 
12095
  // Expose Public Variables
 
12096
  //------------------------------------------------------------
 
12097
 
 
12098
  // expose chart's sub-components
 
12099
  chart.dispatch = dispatch;
 
12100
  chart.scatter = scatter;
 
12101
  chart.legend = legend;
 
12102
  chart.controls = controls;
 
12103
  chart.xAxis = xAxis;
 
12104
  chart.yAxis = yAxis;
 
12105
  chart.distX = distX;
 
12106
  chart.distY = distY;
 
12107
 
 
12108
  d3.rebind(chart, scatter, 'id', 'interactive', 'pointActive', 'x', 'y', 'shape', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'sizeRange', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius', 'useVoronoi');
 
12109
  chart.options = nv.utils.optionsFunc.bind(chart);
 
12110
  
 
12111
  chart.margin = function(_) {
 
12112
    if (!arguments.length) return margin;
 
12113
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
12114
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
12115
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
12116
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
12117
    return chart;
 
12118
  };
 
12119
 
 
12120
  chart.width = function(_) {
 
12121
    if (!arguments.length) return width;
 
12122
    width = _;
 
12123
    return chart;
 
12124
  };
 
12125
 
 
12126
  chart.height = function(_) {
 
12127
    if (!arguments.length) return height;
 
12128
    height = _;
 
12129
    return chart;
 
12130
  };
 
12131
 
 
12132
  chart.color = function(_) {
 
12133
    if (!arguments.length) return color;
 
12134
    color = nv.utils.getColor(_);
 
12135
    legend.color(color);
 
12136
    distX.color(color);
 
12137
    distY.color(color);
 
12138
    return chart;
 
12139
  };
 
12140
 
 
12141
  chart.showDistX = function(_) {
 
12142
    if (!arguments.length) return showDistX;
 
12143
    showDistX = _;
 
12144
    return chart;
 
12145
  };
 
12146
 
 
12147
  chart.showDistY = function(_) {
 
12148
    if (!arguments.length) return showDistY;
 
12149
    showDistY = _;
 
12150
    return chart;
 
12151
  };
 
12152
 
 
12153
  chart.showControls = function(_) {
 
12154
    if (!arguments.length) return showControls;
 
12155
    showControls = _;
 
12156
    return chart;
 
12157
  };
 
12158
 
 
12159
  chart.showLegend = function(_) {
 
12160
    if (!arguments.length) return showLegend;
 
12161
    showLegend = _;
 
12162
    return chart;
 
12163
  };
 
12164
 
 
12165
  chart.showXAxis = function(_) {
 
12166
    if (!arguments.length) return showXAxis;
 
12167
    showXAxis = _;
 
12168
    return chart;
 
12169
  };
 
12170
 
 
12171
  chart.showYAxis = function(_) {
 
12172
    if (!arguments.length) return showYAxis;
 
12173
    showYAxis = _;
 
12174
    return chart;
 
12175
  };
 
12176
 
 
12177
  chart.rightAlignYAxis = function(_) {
 
12178
    if(!arguments.length) return rightAlignYAxis;
 
12179
    rightAlignYAxis = _;
 
12180
    yAxis.orient( (_) ? 'right' : 'left');
 
12181
    return chart;
 
12182
  };
 
12183
 
 
12184
 
 
12185
  chart.fisheye = function(_) {
 
12186
    if (!arguments.length) return fisheye;
 
12187
    fisheye = _;
 
12188
    return chart;
 
12189
  };
 
12190
 
 
12191
  chart.xPadding = function(_) {
 
12192
    if (!arguments.length) return xPadding;
 
12193
    xPadding = _;
 
12194
    return chart;
 
12195
  };
 
12196
 
 
12197
  chart.yPadding = function(_) {
 
12198
    if (!arguments.length) return yPadding;
 
12199
    yPadding = _;
 
12200
    return chart;
 
12201
  };
 
12202
 
 
12203
  chart.tooltips = function(_) {
 
12204
    if (!arguments.length) return tooltips;
 
12205
    tooltips = _;
 
12206
    return chart;
 
12207
  };
 
12208
 
 
12209
  chart.tooltipContent = function(_) {
 
12210
    if (!arguments.length) return tooltip;
 
12211
    tooltip = _;
 
12212
    return chart;
 
12213
  };
 
12214
 
 
12215
  chart.tooltipXContent = function(_) {
 
12216
    if (!arguments.length) return tooltipX;
 
12217
    tooltipX = _;
 
12218
    return chart;
 
12219
  };
 
12220
 
 
12221
  chart.tooltipYContent = function(_) {
 
12222
    if (!arguments.length) return tooltipY;
 
12223
    tooltipY = _;
 
12224
    return chart;
 
12225
  };
 
12226
 
 
12227
  chart.state = function(_) {
 
12228
    if (!arguments.length) return state;
 
12229
    state = _;
 
12230
    return chart;
 
12231
  };
 
12232
 
 
12233
  chart.defaultState = function(_) {
 
12234
    if (!arguments.length) return defaultState;
 
12235
    defaultState = _;
 
12236
    return chart;
 
12237
  };
 
12238
  
 
12239
  chart.noData = function(_) {
 
12240
    if (!arguments.length) return noData;
 
12241
    noData = _;
 
12242
    return chart;
 
12243
  };
 
12244
 
 
12245
  chart.transitionDuration = function(_) {
 
12246
    if (!arguments.length) return transitionDuration;
 
12247
    transitionDuration = _;
 
12248
    return chart;
 
12249
  };
 
12250
 
 
12251
  //============================================================
 
12252
 
 
12253
 
 
12254
  return chart;
 
12255
}
 
12256
 
 
12257
nv.models.scatterPlusLineChart = function() {
 
12258
  "use strict";
 
12259
  //============================================================
 
12260
  // Public Variables with Default Settings
 
12261
  //------------------------------------------------------------
 
12262
 
 
12263
  var scatter      = nv.models.scatter()
 
12264
    , xAxis        = nv.models.axis()
 
12265
    , yAxis        = nv.models.axis()
 
12266
    , legend       = nv.models.legend()
 
12267
    , controls     = nv.models.legend()
 
12268
    , distX        = nv.models.distribution()
 
12269
    , distY        = nv.models.distribution()
 
12270
    ;
 
12271
 
 
12272
  var margin       = {top: 30, right: 20, bottom: 50, left: 75}
 
12273
    , width        = null
 
12274
    , height       = null
 
12275
    , color        = nv.utils.defaultColor()
 
12276
    , x            = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.xScale()
 
12277
    , y            = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.yScale()
 
12278
    , showDistX    = false
 
12279
    , showDistY    = false
 
12280
    , showLegend   = true
 
12281
    , showXAxis    = true
 
12282
    , showYAxis    = true
 
12283
    , rightAlignYAxis = false
 
12284
    , showControls = !!d3.fisheye
 
12285
    , fisheye      = 0
 
12286
    , pauseFisheye = false
 
12287
    , tooltips     = true
 
12288
    , tooltipX     = function(key, x, y) { return '<strong>' + x + '</strong>' }
 
12289
    , tooltipY     = function(key, x, y) { return '<strong>' + y + '</strong>' }
 
12290
    , tooltip      = function(key, x, y, date) { return '<h3>' + key + '</h3>' 
 
12291
                                                      + '<p>' + date + '</p>' }
 
12292
    , state = {}
 
12293
    , defaultState = null
 
12294
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
 
12295
    , noData       = "No Data Available."
 
12296
    , transitionDuration = 250
 
12297
    ;
 
12298
 
 
12299
  scatter
 
12300
    .xScale(x)
 
12301
    .yScale(y)
 
12302
    ;
 
12303
  xAxis
 
12304
    .orient('bottom')
 
12305
    .tickPadding(10)
 
12306
    ;
 
12307
  yAxis
 
12308
    .orient((rightAlignYAxis) ? 'right' : 'left')
 
12309
    .tickPadding(10)
 
12310
    ;
 
12311
  distX
 
12312
    .axis('x')
 
12313
    ;
 
12314
  distY
 
12315
    .axis('y')
 
12316
    ;
 
12317
  
 
12318
  controls.updateState(false);
 
12319
  //============================================================
 
12320
 
 
12321
 
 
12322
  //============================================================
 
12323
  // Private Variables
 
12324
  //------------------------------------------------------------
 
12325
 
 
12326
  var x0, y0;
 
12327
 
 
12328
  var showTooltip = function(e, offsetElement) {
 
12329
    //TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?)
 
12330
 
 
12331
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
12332
        top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
12333
        leftX = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
12334
        topX = y.range()[0] + margin.top + ( offsetElement.offsetTop || 0),
 
12335
        leftY = x.range()[0] + margin.left + ( offsetElement.offsetLeft || 0 ),
 
12336
        topY = e.pos[1] + ( offsetElement.offsetTop || 0),
 
12337
        xVal = xAxis.tickFormat()(scatter.x()(e.point, e.pointIndex)),
 
12338
        yVal = yAxis.tickFormat()(scatter.y()(e.point, e.pointIndex));
 
12339
 
 
12340
      if( tooltipX != null )
 
12341
          nv.tooltip.show([leftX, topX], tooltipX(e.series.key, xVal, yVal, e, chart), 'n', 1, offsetElement, 'x-nvtooltip');
 
12342
      if( tooltipY != null )
 
12343
          nv.tooltip.show([leftY, topY], tooltipY(e.series.key, xVal, yVal, e, chart), 'e', 1, offsetElement, 'y-nvtooltip');
 
12344
      if( tooltip != null )
 
12345
          nv.tooltip.show([left, top], tooltip(e.series.key, xVal, yVal, e.point.tooltip, e, chart), e.value < 0 ? 'n' : 's', null, offsetElement);
 
12346
  };
 
12347
 
 
12348
  var controlsData = [
 
12349
    { key: 'Magnify', disabled: true }
 
12350
  ];
 
12351
 
 
12352
  //============================================================
 
12353
 
 
12354
 
 
12355
  function chart(selection) {
 
12356
    selection.each(function(data) {
 
12357
      var container = d3.select(this),
 
12358
          that = this;
 
12359
 
 
12360
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
12361
                             - margin.left - margin.right,
 
12362
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
12363
                             - margin.top - margin.bottom;
 
12364
 
 
12365
      chart.update = function() { container.transition().duration(transitionDuration).call(chart); };
 
12366
      chart.container = this;
 
12367
 
 
12368
      //set state.disabled
 
12369
      state.disabled = data.map(function(d) { return !!d.disabled });
 
12370
 
 
12371
      if (!defaultState) {
 
12372
        var key;
 
12373
        defaultState = {};
 
12374
        for (key in state) {
 
12375
          if (state[key] instanceof Array)
 
12376
            defaultState[key] = state[key].slice(0);
 
12377
          else
 
12378
            defaultState[key] = state[key];
 
12379
        }
 
12380
      }
 
12381
 
 
12382
      //------------------------------------------------------------
 
12383
      // Display noData message if there's nothing to show.
 
12384
 
 
12385
      if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
 
12386
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
12387
 
 
12388
        noDataText.enter().append('text')
 
12389
          .attr('class', 'nvd3 nv-noData')
 
12390
          .attr('dy', '-.7em')
 
12391
          .style('text-anchor', 'middle');
 
12392
 
 
12393
        noDataText
 
12394
          .attr('x', margin.left + availableWidth / 2)
 
12395
          .attr('y', margin.top + availableHeight / 2)
 
12396
          .text(function(d) { return d });
 
12397
 
 
12398
        return chart;
 
12399
      } else {
 
12400
        container.selectAll('.nv-noData').remove();
 
12401
      }
 
12402
 
 
12403
      //------------------------------------------------------------
 
12404
 
 
12405
 
 
12406
      //------------------------------------------------------------
 
12407
      // Setup Scales
 
12408
 
 
12409
      x = scatter.xScale();
 
12410
      y = scatter.yScale();
 
12411
 
 
12412
      x0 = x0 || x;
 
12413
      y0 = y0 || y;
 
12414
 
 
12415
      //------------------------------------------------------------
 
12416
 
 
12417
 
 
12418
      //------------------------------------------------------------
 
12419
      // Setup containers and skeleton of chart
 
12420
 
 
12421
      var wrap = container.selectAll('g.nv-wrap.nv-scatterChart').data([data]);
 
12422
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatterChart nv-chart-' + scatter.id());
 
12423
      var gEnter = wrapEnter.append('g');
 
12424
      var g = wrap.select('g')
 
12425
 
 
12426
      // background for pointer events
 
12427
      gEnter.append('rect').attr('class', 'nvd3 nv-background').style("pointer-events","none");
 
12428
 
 
12429
      gEnter.append('g').attr('class', 'nv-x nv-axis');
 
12430
      gEnter.append('g').attr('class', 'nv-y nv-axis');
 
12431
      gEnter.append('g').attr('class', 'nv-scatterWrap');
 
12432
      gEnter.append('g').attr('class', 'nv-regressionLinesWrap');
 
12433
      gEnter.append('g').attr('class', 'nv-distWrap');
 
12434
      gEnter.append('g').attr('class', 'nv-legendWrap');
 
12435
      gEnter.append('g').attr('class', 'nv-controlsWrap');
 
12436
 
 
12437
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
12438
 
 
12439
      if (rightAlignYAxis) {
 
12440
          g.select(".nv-y.nv-axis")
 
12441
              .attr("transform", "translate(" + availableWidth + ",0)");
 
12442
      }
 
12443
 
 
12444
      //------------------------------------------------------------
 
12445
 
 
12446
 
 
12447
      //------------------------------------------------------------
 
12448
      // Legend
 
12449
 
 
12450
      if (showLegend) {
 
12451
        legend.width( availableWidth / 2 );
 
12452
 
 
12453
        wrap.select('.nv-legendWrap')
 
12454
            .datum(data)
 
12455
            .call(legend);
 
12456
 
 
12457
        if ( margin.top != legend.height()) {
 
12458
          margin.top = legend.height();
 
12459
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
12460
                             - margin.top - margin.bottom;
 
12461
        }
 
12462
 
 
12463
        wrap.select('.nv-legendWrap')
 
12464
            .attr('transform', 'translate(' + (availableWidth / 2) + ',' + (-margin.top) +')');
 
12465
      }
 
12466
 
 
12467
      //------------------------------------------------------------
 
12468
 
 
12469
 
 
12470
      //------------------------------------------------------------
 
12471
      // Controls
 
12472
 
 
12473
      if (showControls) {
 
12474
        controls.width(180).color(['#444']);
 
12475
        g.select('.nv-controlsWrap')
 
12476
            .datum(controlsData)
 
12477
            .attr('transform', 'translate(0,' + (-margin.top) +')')
 
12478
            .call(controls);
 
12479
      }
 
12480
 
 
12481
      //------------------------------------------------------------
 
12482
 
 
12483
 
 
12484
      //------------------------------------------------------------
 
12485
      // Main Chart Component(s)
 
12486
 
 
12487
      scatter
 
12488
          .width(availableWidth)
 
12489
          .height(availableHeight)
 
12490
          .color(data.map(function(d,i) {
 
12491
            return d.color || color(d, i);
 
12492
          }).filter(function(d,i) { return !data[i].disabled }))
 
12493
 
 
12494
      wrap.select('.nv-scatterWrap')
 
12495
          .datum(data.filter(function(d) { return !d.disabled }))
 
12496
          .call(scatter);
 
12497
 
 
12498
      wrap.select('.nv-regressionLinesWrap')
 
12499
          .attr('clip-path', 'url(#nv-edge-clip-' + scatter.id() + ')');
 
12500
 
 
12501
      var regWrap = wrap.select('.nv-regressionLinesWrap').selectAll('.nv-regLines')
 
12502
                      .data(function(d) {return d });
 
12503
      
 
12504
      regWrap.enter().append('g').attr('class', 'nv-regLines');
 
12505
 
 
12506
      var regLine = regWrap.selectAll('.nv-regLine').data(function(d){return [d]});
 
12507
      var regLineEnter = regLine.enter()
 
12508
                       .append('line').attr('class', 'nv-regLine')
 
12509
                       .style('stroke-opacity', 0);
 
12510
 
 
12511
      regLine
 
12512
          .transition()
 
12513
          .attr('x1', x.range()[0])
 
12514
          .attr('x2', x.range()[1])
 
12515
          .attr('y1', function(d,i) {return y(x.domain()[0] * d.slope + d.intercept) })
 
12516
          .attr('y2', function(d,i) { return y(x.domain()[1] * d.slope + d.intercept) })
 
12517
          .style('stroke', function(d,i,j) { return color(d,j) })
 
12518
          .style('stroke-opacity', function(d,i) {
 
12519
            return (d.disabled || typeof d.slope === 'undefined' || typeof d.intercept === 'undefined') ? 0 : 1 
 
12520
          });
 
12521
 
 
12522
      //------------------------------------------------------------
 
12523
 
 
12524
 
 
12525
      //------------------------------------------------------------
 
12526
      // Setup Axes
 
12527
 
 
12528
      if (showXAxis) {
 
12529
        xAxis
 
12530
            .scale(x)
 
12531
            .ticks( xAxis.ticks() ? xAxis.ticks() : availableWidth / 100 )
 
12532
            .tickSize( -availableHeight , 0);
 
12533
 
 
12534
        g.select('.nv-x.nv-axis')
 
12535
            .attr('transform', 'translate(0,' + y.range()[0] + ')')
 
12536
            .call(xAxis);
 
12537
      }
 
12538
 
 
12539
      if (showYAxis) {
 
12540
        yAxis
 
12541
            .scale(y)
 
12542
            .ticks( yAxis.ticks() ? yAxis.ticks() : availableHeight / 36 )
 
12543
            .tickSize( -availableWidth, 0);
 
12544
 
 
12545
        g.select('.nv-y.nv-axis')
 
12546
            .call(yAxis);
 
12547
      }
 
12548
 
 
12549
 
 
12550
      if (showDistX) {
 
12551
        distX
 
12552
            .getData(scatter.x())
 
12553
            .scale(x)
 
12554
            .width(availableWidth)
 
12555
            .color(data.map(function(d,i) {
 
12556
              return d.color || color(d, i);
 
12557
            }).filter(function(d,i) { return !data[i].disabled }));
 
12558
        gEnter.select('.nv-distWrap').append('g')
 
12559
            .attr('class', 'nv-distributionX');
 
12560
        g.select('.nv-distributionX')
 
12561
            .attr('transform', 'translate(0,' + y.range()[0] + ')')
 
12562
            .datum(data.filter(function(d) { return !d.disabled }))
 
12563
            .call(distX);
 
12564
      }
 
12565
 
 
12566
      if (showDistY) {
 
12567
        distY
 
12568
            .getData(scatter.y())
 
12569
            .scale(y)
 
12570
            .width(availableHeight)
 
12571
            .color(data.map(function(d,i) {
 
12572
              return d.color || color(d, i);
 
12573
            }).filter(function(d,i) { return !data[i].disabled }));
 
12574
        gEnter.select('.nv-distWrap').append('g')
 
12575
            .attr('class', 'nv-distributionY');
 
12576
        g.select('.nv-distributionY')
 
12577
            .attr('transform', 'translate(' + (rightAlignYAxis ? availableWidth : -distY.size() ) + ',0)')
 
12578
            .datum(data.filter(function(d) { return !d.disabled }))
 
12579
            .call(distY);
 
12580
      }
 
12581
 
 
12582
      //------------------------------------------------------------
 
12583
 
 
12584
 
 
12585
 
 
12586
 
 
12587
      if (d3.fisheye) {
 
12588
        g.select('.nv-background')
 
12589
            .attr('width', availableWidth)
 
12590
            .attr('height', availableHeight)
 
12591
            ;
 
12592
 
 
12593
        g.select('.nv-background').on('mousemove', updateFisheye);
 
12594
        g.select('.nv-background').on('click', function() { pauseFisheye = !pauseFisheye;});
 
12595
        scatter.dispatch.on('elementClick.freezeFisheye', function() {
 
12596
          pauseFisheye = !pauseFisheye;
 
12597
        });
 
12598
      }
 
12599
 
 
12600
 
 
12601
      function updateFisheye() {
 
12602
        if (pauseFisheye) {
 
12603
          g.select('.nv-point-paths').style('pointer-events', 'all');
 
12604
          return false;
 
12605
        }
 
12606
 
 
12607
        g.select('.nv-point-paths').style('pointer-events', 'none' );
 
12608
 
 
12609
        var mouse = d3.mouse(this);
 
12610
        x.distortion(fisheye).focus(mouse[0]);
 
12611
        y.distortion(fisheye).focus(mouse[1]);
 
12612
 
 
12613
        g.select('.nv-scatterWrap')
 
12614
            .datum(data.filter(function(d) { return !d.disabled }))
 
12615
            .call(scatter);
 
12616
 
 
12617
        if (showXAxis)
 
12618
          g.select('.nv-x.nv-axis').call(xAxis);
 
12619
 
 
12620
        if (showYAxis)
 
12621
          g.select('.nv-y.nv-axis').call(yAxis);
 
12622
        
 
12623
        g.select('.nv-distributionX')
 
12624
            .datum(data.filter(function(d) { return !d.disabled }))
 
12625
            .call(distX);
 
12626
        g.select('.nv-distributionY')
 
12627
            .datum(data.filter(function(d) { return !d.disabled }))
 
12628
            .call(distY);
 
12629
      }
 
12630
 
 
12631
 
 
12632
 
 
12633
      //============================================================
 
12634
      // Event Handling/Dispatching (in chart's scope)
 
12635
      //------------------------------------------------------------
 
12636
 
 
12637
      controls.dispatch.on('legendClick', function(d,i) {
 
12638
        d.disabled = !d.disabled;
 
12639
 
 
12640
        fisheye = d.disabled ? 0 : 2.5;
 
12641
        g.select('.nv-background') .style('pointer-events', d.disabled ? 'none' : 'all');
 
12642
        g.select('.nv-point-paths').style('pointer-events', d.disabled ? 'all' : 'none' );
 
12643
 
 
12644
        if (d.disabled) {
 
12645
          x.distortion(fisheye).focus(0);
 
12646
          y.distortion(fisheye).focus(0);
 
12647
 
 
12648
          g.select('.nv-scatterWrap').call(scatter);
 
12649
          g.select('.nv-x.nv-axis').call(xAxis);
 
12650
          g.select('.nv-y.nv-axis').call(yAxis);
 
12651
        } else {
 
12652
          pauseFisheye = false;
 
12653
        }
 
12654
 
 
12655
        chart.update();
 
12656
      });
 
12657
 
 
12658
      legend.dispatch.on('stateChange', function(newState) { 
 
12659
        state = newState;
 
12660
        dispatch.stateChange(state);
 
12661
        chart.update();
 
12662
      });
 
12663
 
 
12664
 
 
12665
      scatter.dispatch.on('elementMouseover.tooltip', function(e) {
 
12666
        d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
 
12667
            .attr('y1', e.pos[1] - availableHeight);
 
12668
        d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
 
12669
            .attr('x2', e.pos[0] + distX.size());
 
12670
 
 
12671
        e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
 
12672
        dispatch.tooltipShow(e);
 
12673
      });
 
12674
 
 
12675
      dispatch.on('tooltipShow', function(e) {
 
12676
        if (tooltips) showTooltip(e, that.parentNode);
 
12677
      });
 
12678
 
 
12679
      // Update chart from a state object passed to event handler
 
12680
      dispatch.on('changeState', function(e) {
 
12681
 
 
12682
        if (typeof e.disabled !== 'undefined') {
 
12683
          data.forEach(function(series,i) {
 
12684
            series.disabled = e.disabled[i];
 
12685
          });
 
12686
 
 
12687
          state.disabled = e.disabled;
 
12688
        }
 
12689
 
 
12690
        chart.update();
 
12691
      });
 
12692
 
 
12693
      //============================================================
 
12694
 
 
12695
 
 
12696
      //store old scales for use in transitions on update
 
12697
      x0 = x.copy();
 
12698
      y0 = y.copy();
 
12699
 
 
12700
 
 
12701
    });
 
12702
 
 
12703
    return chart;
 
12704
  }
 
12705
 
 
12706
 
 
12707
  //============================================================
 
12708
  // Event Handling/Dispatching (out of chart's scope)
 
12709
  //------------------------------------------------------------
 
12710
 
 
12711
  scatter.dispatch.on('elementMouseout.tooltip', function(e) {
 
12712
    dispatch.tooltipHide(e);
 
12713
 
 
12714
    d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
 
12715
        .attr('y1', 0);
 
12716
    d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
 
12717
        .attr('x2', distY.size());
 
12718
  });
 
12719
  dispatch.on('tooltipHide', function() {
 
12720
    if (tooltips) nv.tooltip.cleanup();
 
12721
  });
 
12722
 
 
12723
  //============================================================
 
12724
 
 
12725
 
 
12726
  //============================================================
 
12727
  // Expose Public Variables
 
12728
  //------------------------------------------------------------
 
12729
 
 
12730
  // expose chart's sub-components
 
12731
  chart.dispatch = dispatch;
 
12732
  chart.scatter = scatter;
 
12733
  chart.legend = legend;
 
12734
  chart.controls = controls;
 
12735
  chart.xAxis = xAxis;
 
12736
  chart.yAxis = yAxis;
 
12737
  chart.distX = distX;
 
12738
  chart.distY = distY;
 
12739
 
 
12740
  d3.rebind(chart, scatter, 'id', 'interactive', 'pointActive', 'x', 'y', 'shape', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'sizeRange', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius', 'useVoronoi');
 
12741
 
 
12742
  chart.options = nv.utils.optionsFunc.bind(chart);
 
12743
  
 
12744
  chart.margin = function(_) {
 
12745
    if (!arguments.length) return margin;
 
12746
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
12747
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
12748
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
12749
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
12750
    return chart;
 
12751
  };
 
12752
 
 
12753
  chart.width = function(_) {
 
12754
    if (!arguments.length) return width;
 
12755
    width = _;
 
12756
    return chart;
 
12757
  };
 
12758
 
 
12759
  chart.height = function(_) {
 
12760
    if (!arguments.length) return height;
 
12761
    height = _;
 
12762
    return chart;
 
12763
  };
 
12764
 
 
12765
  chart.color = function(_) {
 
12766
    if (!arguments.length) return color;
 
12767
    color = nv.utils.getColor(_);
 
12768
    legend.color(color);
 
12769
    distX.color(color);
 
12770
    distY.color(color);
 
12771
    return chart;
 
12772
  };
 
12773
 
 
12774
  chart.showDistX = function(_) {
 
12775
    if (!arguments.length) return showDistX;
 
12776
    showDistX = _;
 
12777
    return chart;
 
12778
  };
 
12779
 
 
12780
  chart.showDistY = function(_) {
 
12781
    if (!arguments.length) return showDistY;
 
12782
    showDistY = _;
 
12783
    return chart;
 
12784
  };
 
12785
 
 
12786
  chart.showControls = function(_) {
 
12787
    if (!arguments.length) return showControls;
 
12788
    showControls = _;
 
12789
    return chart;
 
12790
  };
 
12791
 
 
12792
  chart.showLegend = function(_) {
 
12793
    if (!arguments.length) return showLegend;
 
12794
    showLegend = _;
 
12795
    return chart;
 
12796
  };
 
12797
 
 
12798
  chart.showXAxis = function(_) {
 
12799
    if (!arguments.length) return showXAxis;
 
12800
    showXAxis = _;
 
12801
    return chart;
 
12802
  };
 
12803
 
 
12804
  chart.showYAxis = function(_) {
 
12805
    if (!arguments.length) return showYAxis;
 
12806
    showYAxis = _;
 
12807
    return chart;
 
12808
  };
 
12809
 
 
12810
  chart.rightAlignYAxis = function(_) {
 
12811
    if(!arguments.length) return rightAlignYAxis;
 
12812
    rightAlignYAxis = _;
 
12813
    yAxis.orient( (_) ? 'right' : 'left');
 
12814
    return chart;
 
12815
  };
 
12816
 
 
12817
  chart.fisheye = function(_) {
 
12818
    if (!arguments.length) return fisheye;
 
12819
    fisheye = _;
 
12820
    return chart;
 
12821
  };
 
12822
 
 
12823
  chart.tooltips = function(_) {
 
12824
    if (!arguments.length) return tooltips;
 
12825
    tooltips = _;
 
12826
    return chart;
 
12827
  };
 
12828
 
 
12829
  chart.tooltipContent = function(_) {
 
12830
    if (!arguments.length) return tooltip;
 
12831
    tooltip = _;
 
12832
    return chart;
 
12833
  };
 
12834
 
 
12835
  chart.tooltipXContent = function(_) {
 
12836
    if (!arguments.length) return tooltipX;
 
12837
    tooltipX = _;
 
12838
    return chart;
 
12839
  };
 
12840
 
 
12841
  chart.tooltipYContent = function(_) {
 
12842
    if (!arguments.length) return tooltipY;
 
12843
    tooltipY = _;
 
12844
    return chart;
 
12845
  };
 
12846
 
 
12847
  chart.state = function(_) {
 
12848
    if (!arguments.length) return state;
 
12849
    state = _;
 
12850
    return chart;
 
12851
  };
 
12852
 
 
12853
  chart.defaultState = function(_) {
 
12854
    if (!arguments.length) return defaultState;
 
12855
    defaultState = _;
 
12856
    return chart;
 
12857
  };
 
12858
 
 
12859
  chart.noData = function(_) {
 
12860
    if (!arguments.length) return noData;
 
12861
    noData = _;
 
12862
    return chart;
 
12863
  };
 
12864
 
 
12865
  chart.transitionDuration = function(_) {
 
12866
    if (!arguments.length) return transitionDuration;
 
12867
    transitionDuration = _;
 
12868
    return chart;
 
12869
  };
 
12870
 
 
12871
  //============================================================
 
12872
 
 
12873
 
 
12874
  return chart;
 
12875
}
 
12876
 
 
12877
nv.models.sparkline = function() {
 
12878
  "use strict";
 
12879
  //============================================================
 
12880
  // Public Variables with Default Settings
 
12881
  //------------------------------------------------------------
 
12882
 
 
12883
  var margin = {top: 2, right: 0, bottom: 2, left: 0}
 
12884
    , width = 400
 
12885
    , height = 32
 
12886
    , animate = true
 
12887
    , x = d3.scale.linear()
 
12888
    , y = d3.scale.linear()
 
12889
    , getX = function(d) { return d.x }
 
12890
    , getY = function(d) { return d.y }
 
12891
    , color = nv.utils.getColor(['#000'])
 
12892
    , xDomain
 
12893
    , yDomain
 
12894
    , xRange
 
12895
    , yRange
 
12896
    ;
 
12897
 
 
12898
  //============================================================
 
12899
 
 
12900
 
 
12901
  function chart(selection) {
 
12902
    selection.each(function(data) {
 
12903
      var availableWidth = width - margin.left - margin.right,
 
12904
          availableHeight = height - margin.top - margin.bottom,
 
12905
          container = d3.select(this);
 
12906
 
 
12907
 
 
12908
      //------------------------------------------------------------
 
12909
      // Setup Scales
 
12910
 
 
12911
      x   .domain(xDomain || d3.extent(data, getX ))
 
12912
          .range(xRange || [0, availableWidth]);
 
12913
 
 
12914
      y   .domain(yDomain || d3.extent(data, getY ))
 
12915
          .range(yRange || [availableHeight, 0]);
 
12916
 
 
12917
      //------------------------------------------------------------
 
12918
 
 
12919
 
 
12920
      //------------------------------------------------------------
 
12921
      // Setup containers and skeleton of chart
 
12922
 
 
12923
      var wrap = container.selectAll('g.nv-wrap.nv-sparkline').data([data]);
 
12924
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparkline');
 
12925
      var gEnter = wrapEnter.append('g');
 
12926
      var g = wrap.select('g');
 
12927
 
 
12928
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
 
12929
 
 
12930
      //------------------------------------------------------------
 
12931
 
 
12932
 
 
12933
      var paths = wrap.selectAll('path')
 
12934
          .data(function(d) { return [d] });
 
12935
      paths.enter().append('path');
 
12936
      paths.exit().remove();
 
12937
      paths
 
12938
          .style('stroke', function(d,i) { return d.color || color(d, i) })
 
12939
          .attr('d', d3.svg.line()
 
12940
            .x(function(d,i) { return x(getX(d,i)) })
 
12941
            .y(function(d,i) { return y(getY(d,i)) })
 
12942
          );
 
12943
 
 
12944
 
 
12945
      // TODO: Add CURRENT data point (Need Min, Mac, Current / Most recent)
 
12946
      var points = wrap.selectAll('circle.nv-point')
 
12947
          .data(function(data) {
 
12948
              var yValues = data.map(function(d, i) { return getY(d,i); });
 
12949
              function pointIndex(index) {
 
12950
                  if (index != -1) {
 
12951
                      var result = data[index];
 
12952
                      result.pointIndex = index;
 
12953
                      return result;
 
12954
                  } else {
 
12955
                      return null;
 
12956
                  }
 
12957
              }
 
12958
              var maxPoint = pointIndex(yValues.lastIndexOf(y.domain()[1])),
 
12959
                  minPoint = pointIndex(yValues.indexOf(y.domain()[0])),
 
12960
                  currentPoint = pointIndex(yValues.length - 1);
 
12961
              return [minPoint, maxPoint, currentPoint].filter(function (d) {return d != null;});
 
12962
          });
 
12963
      points.enter().append('circle');
 
12964
      points.exit().remove();
 
12965
      points
 
12966
          .attr('cx', function(d,i) { return x(getX(d,d.pointIndex)) })
 
12967
          .attr('cy', function(d,i) { return y(getY(d,d.pointIndex)) })
 
12968
          .attr('r', 2)
 
12969
          .attr('class', function(d,i) {
 
12970
            return getX(d, d.pointIndex) == x.domain()[1] ? 'nv-point nv-currentValue' :
 
12971
                   getY(d, d.pointIndex) == y.domain()[0] ? 'nv-point nv-minValue' : 'nv-point nv-maxValue'
 
12972
          });
 
12973
    });
 
12974
 
 
12975
    return chart;
 
12976
  }
 
12977
 
 
12978
 
 
12979
  //============================================================
 
12980
  // Expose Public Variables
 
12981
  //------------------------------------------------------------
 
12982
  chart.options = nv.utils.optionsFunc.bind(chart);
 
12983
  
 
12984
  chart.margin = function(_) {
 
12985
    if (!arguments.length) return margin;
 
12986
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
12987
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
12988
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
12989
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
12990
    return chart;
 
12991
  };
 
12992
 
 
12993
  chart.width = function(_) {
 
12994
    if (!arguments.length) return width;
 
12995
    width = _;
 
12996
    return chart;
 
12997
  };
 
12998
 
 
12999
  chart.height = function(_) {
 
13000
    if (!arguments.length) return height;
 
13001
    height = _;
 
13002
    return chart;
 
13003
  };
 
13004
 
 
13005
  chart.x = function(_) {
 
13006
    if (!arguments.length) return getX;
 
13007
    getX = d3.functor(_);
 
13008
    return chart;
 
13009
  };
 
13010
 
 
13011
  chart.y = function(_) {
 
13012
    if (!arguments.length) return getY;
 
13013
    getY = d3.functor(_);
 
13014
    return chart;
 
13015
  };
 
13016
 
 
13017
  chart.xScale = function(_) {
 
13018
    if (!arguments.length) return x;
 
13019
    x = _;
 
13020
    return chart;
 
13021
  };
 
13022
 
 
13023
  chart.yScale = function(_) {
 
13024
    if (!arguments.length) return y;
 
13025
    y = _;
 
13026
    return chart;
 
13027
  };
 
13028
 
 
13029
  chart.xDomain = function(_) {
 
13030
    if (!arguments.length) return xDomain;
 
13031
    xDomain = _;
 
13032
    return chart;
 
13033
  };
 
13034
 
 
13035
  chart.yDomain = function(_) {
 
13036
    if (!arguments.length) return yDomain;
 
13037
    yDomain = _;
 
13038
    return chart;
 
13039
  };
 
13040
 
 
13041
  chart.xRange = function(_) {
 
13042
    if (!arguments.length) return xRange;
 
13043
    xRange = _;
 
13044
    return chart;
 
13045
  };
 
13046
 
 
13047
  chart.yRange = function(_) {
 
13048
    if (!arguments.length) return yRange;
 
13049
    yRange = _;
 
13050
    return chart;
 
13051
  };
 
13052
 
 
13053
  chart.animate = function(_) {
 
13054
    if (!arguments.length) return animate;
 
13055
    animate = _;
 
13056
    return chart;
 
13057
  };
 
13058
 
 
13059
  chart.color = function(_) {
 
13060
    if (!arguments.length) return color;
 
13061
    color = nv.utils.getColor(_);
 
13062
    return chart;
 
13063
  };
 
13064
 
 
13065
  //============================================================
 
13066
 
 
13067
 
 
13068
  return chart;
 
13069
}
 
13070
 
 
13071
nv.models.sparklinePlus = function() {
 
13072
  "use strict";
 
13073
  //============================================================
 
13074
  // Public Variables with Default Settings
 
13075
  //------------------------------------------------------------
 
13076
 
 
13077
  var sparkline = nv.models.sparkline();
 
13078
 
 
13079
  var margin = {top: 15, right: 100, bottom: 10, left: 50}
 
13080
    , width = null
 
13081
    , height = null
 
13082
    , x
 
13083
    , y
 
13084
    , index = []
 
13085
    , paused = false
 
13086
    , xTickFormat = d3.format(',r')
 
13087
    , yTickFormat = d3.format(',.2f')
 
13088
    , showValue = true
 
13089
    , alignValue = true
 
13090
    , rightAlignValue = false
 
13091
    , noData = "No Data Available."
 
13092
    ;
 
13093
 
 
13094
  //============================================================
 
13095
 
 
13096
 
 
13097
  function chart(selection) {
 
13098
    selection.each(function(data) {
 
13099
      var container = d3.select(this);
 
13100
 
 
13101
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
13102
                             - margin.left - margin.right,
 
13103
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
13104
                             - margin.top - margin.bottom;
 
13105
 
 
13106
      
 
13107
 
 
13108
      chart.update = function() { chart(selection) };
 
13109
      chart.container = this;
 
13110
 
 
13111
 
 
13112
      //------------------------------------------------------------
 
13113
      // Display No Data message if there's nothing to show.
 
13114
 
 
13115
      if (!data || !data.length) {
 
13116
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
13117
 
 
13118
        noDataText.enter().append('text')
 
13119
          .attr('class', 'nvd3 nv-noData')
 
13120
          .attr('dy', '-.7em')
 
13121
          .style('text-anchor', 'middle');
 
13122
 
 
13123
        noDataText
 
13124
          .attr('x', margin.left + availableWidth / 2)
 
13125
          .attr('y', margin.top + availableHeight / 2)
 
13126
          .text(function(d) { return d });
 
13127
 
 
13128
        return chart;
 
13129
      } else {
 
13130
        container.selectAll('.nv-noData').remove();
 
13131
      }
 
13132
 
 
13133
      var currentValue = sparkline.y()(data[data.length-1], data.length-1);
 
13134
 
 
13135
      //------------------------------------------------------------
 
13136
 
 
13137
 
 
13138
 
 
13139
      //------------------------------------------------------------
 
13140
      // Setup Scales
 
13141
 
 
13142
      x = sparkline.xScale();
 
13143
      y = sparkline.yScale();
 
13144
 
 
13145
      //------------------------------------------------------------
 
13146
 
 
13147
 
 
13148
      //------------------------------------------------------------
 
13149
      // Setup containers and skeleton of chart
 
13150
 
 
13151
      var wrap = container.selectAll('g.nv-wrap.nv-sparklineplus').data([data]);
 
13152
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sparklineplus');
 
13153
      var gEnter = wrapEnter.append('g');
 
13154
      var g = wrap.select('g');
 
13155
 
 
13156
      gEnter.append('g').attr('class', 'nv-sparklineWrap');
 
13157
      gEnter.append('g').attr('class', 'nv-valueWrap');
 
13158
      gEnter.append('g').attr('class', 'nv-hoverArea');
 
13159
 
 
13160
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
13161
 
 
13162
      //------------------------------------------------------------
 
13163
 
 
13164
 
 
13165
      //------------------------------------------------------------
 
13166
      // Main Chart Component(s)
 
13167
 
 
13168
      var sparklineWrap = g.select('.nv-sparklineWrap');
 
13169
 
 
13170
      sparkline
 
13171
        .width(availableWidth)
 
13172
        .height(availableHeight);
 
13173
 
 
13174
      sparklineWrap
 
13175
          .call(sparkline);
 
13176
 
 
13177
      //------------------------------------------------------------
 
13178
 
 
13179
 
 
13180
      var valueWrap = g.select('.nv-valueWrap');
 
13181
      
 
13182
      var value = valueWrap.selectAll('.nv-currentValue')
 
13183
          .data([currentValue]);
 
13184
 
 
13185
      value.enter().append('text').attr('class', 'nv-currentValue')
 
13186
          .attr('dx', rightAlignValue ? -8 : 8)
 
13187
          .attr('dy', '.9em')
 
13188
          .style('text-anchor', rightAlignValue ? 'end' : 'start');
 
13189
 
 
13190
      value
 
13191
          .attr('x', availableWidth + (rightAlignValue ? margin.right : 0))
 
13192
          .attr('y', alignValue ? function(d) { return y(d) } : 0)
 
13193
          .style('fill', sparkline.color()(data[data.length-1], data.length-1))
 
13194
          .text(yTickFormat(currentValue));
 
13195
 
 
13196
 
 
13197
 
 
13198
      gEnter.select('.nv-hoverArea').append('rect')
 
13199
          .on('mousemove', sparklineHover)
 
13200
          .on('click', function() { paused = !paused })
 
13201
          .on('mouseout', function() { index = []; updateValueLine(); });
 
13202
          //.on('mouseout', function() { index = null; updateValueLine(); });
 
13203
 
 
13204
      g.select('.nv-hoverArea rect')
 
13205
          .attr('transform', function(d) { return 'translate(' + -margin.left + ',' + -margin.top + ')' })
 
13206
          .attr('width', availableWidth + margin.left + margin.right)
 
13207
          .attr('height', availableHeight + margin.top);
 
13208
 
 
13209
 
 
13210
 
 
13211
      function updateValueLine() { //index is currently global (within the chart), may or may not keep it that way
 
13212
        if (paused) return;
 
13213
 
 
13214
        var hoverValue = g.selectAll('.nv-hoverValue').data(index)
 
13215
 
 
13216
        var hoverEnter = hoverValue.enter()
 
13217
          .append('g').attr('class', 'nv-hoverValue')
 
13218
            .style('stroke-opacity', 0)
 
13219
            .style('fill-opacity', 0);
 
13220
 
 
13221
        hoverValue.exit()
 
13222
          .transition().duration(250)
 
13223
            .style('stroke-opacity', 0)
 
13224
            .style('fill-opacity', 0)
 
13225
            .remove();
 
13226
 
 
13227
        hoverValue
 
13228
            .attr('transform', function(d) { return 'translate(' + x(sparkline.x()(data[d],d)) + ',0)' })
 
13229
          .transition().duration(250)
 
13230
            .style('stroke-opacity', 1)
 
13231
            .style('fill-opacity', 1);
 
13232
 
 
13233
        if (!index.length) return;
 
13234
 
 
13235
        hoverEnter.append('line')
 
13236
            .attr('x1', 0)
 
13237
            .attr('y1', -margin.top)
 
13238
            .attr('x2', 0)
 
13239
            .attr('y2', availableHeight);
 
13240
 
 
13241
 
 
13242
        hoverEnter.append('text').attr('class', 'nv-xValue')
 
13243
            .attr('x', -6)
 
13244
            .attr('y', -margin.top)
 
13245
            .attr('text-anchor', 'end')
 
13246
            .attr('dy', '.9em')
 
13247
 
 
13248
 
 
13249
        g.select('.nv-hoverValue .nv-xValue')
 
13250
            .text(xTickFormat(sparkline.x()(data[index[0]], index[0])));
 
13251
 
 
13252
        hoverEnter.append('text').attr('class', 'nv-yValue')
 
13253
            .attr('x', 6)
 
13254
            .attr('y', -margin.top)
 
13255
            .attr('text-anchor', 'start')
 
13256
            .attr('dy', '.9em')
 
13257
 
 
13258
        g.select('.nv-hoverValue .nv-yValue')
 
13259
            .text(yTickFormat(sparkline.y()(data[index[0]], index[0])));
 
13260
 
 
13261
      }
 
13262
 
 
13263
 
 
13264
      function sparklineHover() {
 
13265
        if (paused) return;
 
13266
 
 
13267
        var pos = d3.mouse(this)[0] - margin.left;
 
13268
 
 
13269
        function getClosestIndex(data, x) {
 
13270
          var distance = Math.abs(sparkline.x()(data[0], 0) - x);
 
13271
          var closestIndex = 0;
 
13272
          for (var i = 0; i < data.length; i++){
 
13273
            if (Math.abs(sparkline.x()(data[i], i) - x) < distance) {
 
13274
              distance = Math.abs(sparkline.x()(data[i], i) - x);
 
13275
              closestIndex = i;
 
13276
            }
 
13277
          }
 
13278
          return closestIndex;
 
13279
        }
 
13280
 
 
13281
        index = [getClosestIndex(data, Math.round(x.invert(pos)))];
 
13282
 
 
13283
        updateValueLine();
 
13284
      }
 
13285
 
 
13286
    });
 
13287
 
 
13288
    return chart;
 
13289
  }
 
13290
 
 
13291
 
 
13292
  //============================================================
 
13293
  // Expose Public Variables
 
13294
  //------------------------------------------------------------
 
13295
 
 
13296
  // expose chart's sub-components
 
13297
  chart.sparkline = sparkline;
 
13298
 
 
13299
  d3.rebind(chart, sparkline, 'x', 'y', 'xScale', 'yScale', 'color');
 
13300
 
 
13301
  chart.options = nv.utils.optionsFunc.bind(chart);
 
13302
  
 
13303
  chart.margin = function(_) {
 
13304
    if (!arguments.length) return margin;
 
13305
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
13306
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
13307
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
13308
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
13309
    return chart;
 
13310
  };
 
13311
 
 
13312
  chart.width = function(_) {
 
13313
    if (!arguments.length) return width;
 
13314
    width = _;
 
13315
    return chart;
 
13316
  };
 
13317
 
 
13318
  chart.height = function(_) {
 
13319
    if (!arguments.length) return height;
 
13320
    height = _;
 
13321
    return chart;
 
13322
  };
 
13323
 
 
13324
  chart.xTickFormat = function(_) {
 
13325
    if (!arguments.length) return xTickFormat;
 
13326
    xTickFormat = _;
 
13327
    return chart;
 
13328
  };
 
13329
 
 
13330
  chart.yTickFormat = function(_) {
 
13331
    if (!arguments.length) return yTickFormat;
 
13332
    yTickFormat = _;
 
13333
    return chart;
 
13334
  };
 
13335
 
 
13336
  chart.showValue = function(_) {
 
13337
    if (!arguments.length) return showValue;
 
13338
    showValue = _;
 
13339
    return chart;
 
13340
  };
 
13341
 
 
13342
  chart.alignValue = function(_) {
 
13343
    if (!arguments.length) return alignValue;
 
13344
    alignValue = _;
 
13345
    return chart;
 
13346
  };
 
13347
 
 
13348
  chart.rightAlignValue = function(_) {
 
13349
    if (!arguments.length) return rightAlignValue;
 
13350
    rightAlignValue = _;
 
13351
    return chart;
 
13352
  };
 
13353
 
 
13354
  chart.noData = function(_) {
 
13355
    if (!arguments.length) return noData;
 
13356
    noData = _;
 
13357
    return chart;
 
13358
  };
 
13359
 
 
13360
  //============================================================
 
13361
 
 
13362
 
 
13363
  return chart;
 
13364
}
 
13365
 
 
13366
nv.models.stackedArea = function() {
 
13367
  "use strict";
 
13368
  //============================================================
 
13369
  // Public Variables with Default Settings
 
13370
  //------------------------------------------------------------
 
13371
 
 
13372
  var margin = {top: 0, right: 0, bottom: 0, left: 0}
 
13373
    , width = 960
 
13374
    , height = 500
 
13375
    , color = nv.utils.defaultColor() // a function that computes the color
 
13376
    , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't selet one
 
13377
    , getX = function(d) { return d.x } // accessor to get the x value from a data point
 
13378
    , getY = function(d) { return d.y } // accessor to get the y value from a data point
 
13379
    , style = 'stack'
 
13380
    , offset = 'zero'
 
13381
    , order = 'default'
 
13382
    , interpolate = 'linear'  // controls the line interpolation
 
13383
    , clipEdge = false // if true, masks lines within x and y scale
 
13384
    , x //can be accessed via chart.xScale()
 
13385
    , y //can be accessed via chart.yScale()
 
13386
    , scatter = nv.models.scatter()
 
13387
    , dispatch =  d3.dispatch('tooltipShow', 'tooltipHide', 'areaClick', 'areaMouseover', 'areaMouseout')
 
13388
    ;
 
13389
 
 
13390
  scatter
 
13391
    .size(2.2) // default size
 
13392
    .sizeDomain([2.2,2.2]) // all the same size by default
 
13393
    ;
 
13394
 
 
13395
  /************************************
 
13396
   * offset:
 
13397
   *   'wiggle' (stream)
 
13398
   *   'zero' (stacked)
 
13399
   *   'expand' (normalize to 100%)
 
13400
   *   'silhouette' (simple centered)
 
13401
   *
 
13402
   * order:
 
13403
   *   'inside-out' (stream)
 
13404
   *   'default' (input order)
 
13405
   ************************************/
 
13406
 
 
13407
  //============================================================
 
13408
 
 
13409
 
 
13410
  function chart(selection) {
 
13411
    selection.each(function(data) {
 
13412
      var availableWidth = width - margin.left - margin.right,
 
13413
          availableHeight = height - margin.top - margin.bottom,
 
13414
          container = d3.select(this);
 
13415
 
 
13416
      //------------------------------------------------------------
 
13417
      // Setup Scales
 
13418
 
 
13419
      x = scatter.xScale();
 
13420
      y = scatter.yScale();
 
13421
 
 
13422
      //------------------------------------------------------------
 
13423
 
 
13424
      var dataRaw = data;
 
13425
      // Injecting point index into each point because d3.layout.stack().out does not give index
 
13426
      data.forEach(function(aseries, i) {
 
13427
        aseries.seriesIndex = i;
 
13428
        aseries.values = aseries.values.map(function(d, j) {
 
13429
          d.index = j;
 
13430
          d.seriesIndex = i;
 
13431
          return d;
 
13432
        });
 
13433
      });
 
13434
 
 
13435
      var dataFiltered = data.filter(function(series) {
 
13436
            return !series.disabled;
 
13437
      });
 
13438
 
 
13439
      data = d3.layout.stack()
 
13440
               .order(order)
 
13441
               .offset(offset)
 
13442
               .values(function(d) { return d.values })  //TODO: make values customizeable in EVERY model in this fashion
 
13443
               .x(getX)
 
13444
               .y(getY)
 
13445
               .out(function(d, y0, y) {
 
13446
                    var yHeight = (getY(d) === 0) ? 0 : y;
 
13447
                    d.display = {
 
13448
                      y: yHeight,
 
13449
                     y0: y0
 
13450
                    };
 
13451
                })
 
13452
              (dataFiltered);
 
13453
 
 
13454
 
 
13455
      //------------------------------------------------------------
 
13456
      // Setup containers and skeleton of chart
 
13457
 
 
13458
      var wrap = container.selectAll('g.nv-wrap.nv-stackedarea').data([data]);
 
13459
      var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedarea');
 
13460
      var defsEnter = wrapEnter.append('defs');
 
13461
      var gEnter = wrapEnter.append('g');
 
13462
      var g = wrap.select('g');
 
13463
 
 
13464
      gEnter.append('g').attr('class', 'nv-areaWrap');
 
13465
      gEnter.append('g').attr('class', 'nv-scatterWrap');
 
13466
 
 
13467
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
13468
 
 
13469
      //------------------------------------------------------------
 
13470
 
 
13471
 
 
13472
      scatter
 
13473
        .width(availableWidth)
 
13474
        .height(availableHeight)
 
13475
        .x(getX)
 
13476
        .y(function(d) { return d.display.y + d.display.y0 })
 
13477
        .forceY([0])
 
13478
        .color(data.map(function(d,i) {
 
13479
          return d.color || color(d, d.seriesIndex);
 
13480
        }));
 
13481
 
 
13482
 
 
13483
      var scatterWrap = g.select('.nv-scatterWrap')
 
13484
          .datum(data);
 
13485
 
 
13486
      scatterWrap.call(scatter);
 
13487
 
 
13488
      defsEnter.append('clipPath')
 
13489
          .attr('id', 'nv-edge-clip-' + id)
 
13490
        .append('rect');
 
13491
 
 
13492
      wrap.select('#nv-edge-clip-' + id + ' rect')
 
13493
          .attr('width', availableWidth)
 
13494
          .attr('height', availableHeight);
 
13495
 
 
13496
      g   .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
 
13497
 
 
13498
      var area = d3.svg.area()
 
13499
          .x(function(d,i)  { return x(getX(d,i)) })
 
13500
          .y0(function(d) {
 
13501
              return y(d.display.y0)
 
13502
          })
 
13503
          .y1(function(d) {
 
13504
              return y(d.display.y + d.display.y0)
 
13505
          })
 
13506
          .interpolate(interpolate);
 
13507
 
 
13508
      var zeroArea = d3.svg.area()
 
13509
          .x(function(d,i)  { return x(getX(d,i)) })
 
13510
          .y0(function(d) { return y(d.display.y0) })
 
13511
          .y1(function(d) { return y(d.display.y0) });
 
13512
 
 
13513
 
 
13514
      var path = g.select('.nv-areaWrap').selectAll('path.nv-area')
 
13515
          .data(function(d) { return d });
 
13516
 
 
13517
      path.enter().append('path').attr('class', function(d,i) { return 'nv-area nv-area-' + i })
 
13518
          .attr('d', function(d,i){
 
13519
            return zeroArea(d.values, d.seriesIndex);
 
13520
          })
 
13521
          .on('mouseover', function(d,i) {
 
13522
            d3.select(this).classed('hover', true);
 
13523
            dispatch.areaMouseover({
 
13524
              point: d,
 
13525
              series: d.key,
 
13526
              pos: [d3.event.pageX, d3.event.pageY],
 
13527
              seriesIndex: d.seriesIndex
 
13528
            });
 
13529
          })
 
13530
          .on('mouseout', function(d,i) {
 
13531
            d3.select(this).classed('hover', false);
 
13532
            dispatch.areaMouseout({
 
13533
              point: d,
 
13534
              series: d.key,
 
13535
              pos: [d3.event.pageX, d3.event.pageY],
 
13536
              seriesIndex: d.seriesIndex
 
13537
            });
 
13538
          })
 
13539
          .on('click', function(d,i) {
 
13540
            d3.select(this).classed('hover', false);
 
13541
            dispatch.areaClick({
 
13542
              point: d,
 
13543
              series: d.key,
 
13544
              pos: [d3.event.pageX, d3.event.pageY],
 
13545
              seriesIndex: d.seriesIndex
 
13546
            });
 
13547
          })
 
13548
 
 
13549
      path.exit().remove();
 
13550
 
 
13551
      path
 
13552
          .style('fill', function(d,i){
 
13553
            return d.color || color(d, d.seriesIndex)
 
13554
          })
 
13555
          .style('stroke', function(d,i){ return d.color || color(d, d.seriesIndex) });
 
13556
      path.transition()
 
13557
          .attr('d', function(d,i) {
 
13558
            return area(d.values,i)
 
13559
          });
 
13560
 
 
13561
 
 
13562
 
 
13563
      //============================================================
 
13564
      // Event Handling/Dispatching (in chart's scope)
 
13565
      //------------------------------------------------------------
 
13566
 
 
13567
      scatter.dispatch.on('elementMouseover.area', function(e) {
 
13568
        g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', true);
 
13569
      });
 
13570
      scatter.dispatch.on('elementMouseout.area', function(e) {
 
13571
        g.select('.nv-chart-' + id + ' .nv-area-' + e.seriesIndex).classed('hover', false);
 
13572
      });
 
13573
 
 
13574
      //============================================================
 
13575
      //Special offset functions
 
13576
      chart.d3_stackedOffset_stackPercent = function(stackData) {
 
13577
          var n = stackData.length,    //How many series
 
13578
          m = stackData[0].length,     //how many points per series
 
13579
          k = 1 / n,
 
13580
           i,
 
13581
           j,
 
13582
           o,
 
13583
           y0 = [];
 
13584
 
 
13585
          for (j = 0; j < m; ++j) { //Looping through all points
 
13586
            for (i = 0, o = 0; i < dataRaw.length; i++)  //looping through series'
 
13587
                o += getY(dataRaw[i].values[j])   //total value of all points at a certian point in time.
 
13588
 
 
13589
            if (o) for (i = 0; i < n; i++)
 
13590
               stackData[i][j][1] /= o;
 
13591
            else
 
13592
              for (i = 0; i < n; i++)
 
13593
               stackData[i][j][1] = k;
 
13594
          }
 
13595
          for (j = 0; j < m; ++j) y0[j] = 0;
 
13596
          return y0;
 
13597
      };
 
13598
 
 
13599
    });
 
13600
 
 
13601
 
 
13602
    return chart;
 
13603
  }
 
13604
 
 
13605
 
 
13606
  //============================================================
 
13607
  // Event Handling/Dispatching (out of chart's scope)
 
13608
  //------------------------------------------------------------
 
13609
 
 
13610
  scatter.dispatch.on('elementClick.area', function(e) {
 
13611
    dispatch.areaClick(e);
 
13612
  })
 
13613
  scatter.dispatch.on('elementMouseover.tooltip', function(e) {
 
13614
        e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
 
13615
        dispatch.tooltipShow(e);
 
13616
  });
 
13617
  scatter.dispatch.on('elementMouseout.tooltip', function(e) {
 
13618
        dispatch.tooltipHide(e);
 
13619
  });
 
13620
 
 
13621
  //============================================================
 
13622
 
 
13623
  //============================================================
 
13624
  // Global getters and setters
 
13625
  //------------------------------------------------------------
 
13626
 
 
13627
  chart.dispatch = dispatch;
 
13628
  chart.scatter = scatter;
 
13629
 
 
13630
  d3.rebind(chart, scatter, 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange',
 
13631
    'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'useVoronoi','clipRadius','highlightPoint','clearHighlights');
 
13632
 
 
13633
  chart.options = nv.utils.optionsFunc.bind(chart);
 
13634
 
 
13635
  chart.x = function(_) {
 
13636
    if (!arguments.length) return getX;
 
13637
    getX = d3.functor(_);
 
13638
    return chart;
 
13639
  };
 
13640
 
 
13641
  chart.y = function(_) {
 
13642
    if (!arguments.length) return getY;
 
13643
    getY = d3.functor(_);
 
13644
    return chart;
 
13645
  }
 
13646
 
 
13647
  chart.margin = function(_) {
 
13648
    if (!arguments.length) return margin;
 
13649
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
13650
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
13651
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
13652
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
13653
    return chart;
 
13654
  };
 
13655
 
 
13656
  chart.width = function(_) {
 
13657
    if (!arguments.length) return width;
 
13658
    width = _;
 
13659
    return chart;
 
13660
  };
 
13661
 
 
13662
  chart.height = function(_) {
 
13663
    if (!arguments.length) return height;
 
13664
    height = _;
 
13665
    return chart;
 
13666
  };
 
13667
 
 
13668
  chart.clipEdge = function(_) {
 
13669
    if (!arguments.length) return clipEdge;
 
13670
    clipEdge = _;
 
13671
    return chart;
 
13672
  };
 
13673
 
 
13674
  chart.color = function(_) {
 
13675
    if (!arguments.length) return color;
 
13676
    color = nv.utils.getColor(_);
 
13677
    return chart;
 
13678
  };
 
13679
 
 
13680
  chart.offset = function(_) {
 
13681
    if (!arguments.length) return offset;
 
13682
    offset = _;
 
13683
    return chart;
 
13684
  };
 
13685
 
 
13686
  chart.order = function(_) {
 
13687
    if (!arguments.length) return order;
 
13688
    order = _;
 
13689
    return chart;
 
13690
  };
 
13691
 
 
13692
  //shortcut for offset + order
 
13693
  chart.style = function(_) {
 
13694
    if (!arguments.length) return style;
 
13695
    style = _;
 
13696
 
 
13697
    switch (style) {
 
13698
      case 'stack':
 
13699
        chart.offset('zero');
 
13700
        chart.order('default');
 
13701
        break;
 
13702
      case 'stream':
 
13703
        chart.offset('wiggle');
 
13704
        chart.order('inside-out');
 
13705
        break;
 
13706
      case 'stream-center':
 
13707
          chart.offset('silhouette');
 
13708
          chart.order('inside-out');
 
13709
          break;
 
13710
      case 'expand':
 
13711
        chart.offset('expand');
 
13712
        chart.order('default');
 
13713
        break;
 
13714
      case 'stack_percent':
 
13715
        chart.offset(chart.d3_stackedOffset_stackPercent);
 
13716
        chart.order('default');
 
13717
        break;
 
13718
    }
 
13719
 
 
13720
    return chart;
 
13721
  };
 
13722
 
 
13723
  chart.interpolate = function(_) {
 
13724
            if (!arguments.length) return interpolate;
 
13725
            interpolate = _;
 
13726
            return chart;
 
13727
  };
 
13728
  //============================================================
 
13729
 
 
13730
 
 
13731
  return chart;
 
13732
}
 
13733
 
 
13734
nv.models.stackedAreaChart = function() {
 
13735
  "use strict";
 
13736
  //============================================================
 
13737
  // Public Variables with Default Settings
 
13738
  //------------------------------------------------------------
 
13739
 
 
13740
  var stacked = nv.models.stackedArea()
 
13741
    , xAxis = nv.models.axis()
 
13742
    , yAxis = nv.models.axis()
 
13743
    , legend = nv.models.legend()
 
13744
    , controls = nv.models.legend()
 
13745
    , interactiveLayer = nv.interactiveGuideline()
 
13746
    ;
 
13747
 
 
13748
  var margin = {top: 30, right: 25, bottom: 50, left: 60}
 
13749
    , width = null
 
13750
    , height = null
 
13751
    , color = nv.utils.defaultColor() // a function that takes in d, i and returns color
 
13752
    , showControls = true
 
13753
    , showLegend = true
 
13754
    , showXAxis = true
 
13755
    , showYAxis = true
 
13756
    , rightAlignYAxis = false
 
13757
    , useInteractiveGuideline = false
 
13758
    , tooltips = true
 
13759
    , tooltip = function(key, x, y, e, graph) {
 
13760
        return '<h3>' + key + '</h3>' +
 
13761
               '<p>' +  y + ' on ' + x + '</p>'
 
13762
      }
 
13763
    , x //can be accessed via chart.xScale()
 
13764
    , y //can be accessed via chart.yScale()
 
13765
    , yAxisTickFormat = d3.format(',.2f')
 
13766
    , state = { style: stacked.style() }
 
13767
    , defaultState = null
 
13768
    , noData = 'No Data Available.'
 
13769
    , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
 
13770
    , controlWidth = 250
 
13771
    , cData = ['Stacked','Stream','Expanded']
 
13772
    , controlLabels = {}
 
13773
    , transitionDuration = 250
 
13774
    ;
 
13775
 
 
13776
  xAxis
 
13777
    .orient('bottom')
 
13778
    .tickPadding(7)
 
13779
    ;
 
13780
  yAxis
 
13781
    .orient((rightAlignYAxis) ? 'right' : 'left')
 
13782
    ;
 
13783
 
 
13784
  controls.updateState(false);
 
13785
  //============================================================
 
13786
 
 
13787
 
 
13788
  //============================================================
 
13789
  // Private Variables
 
13790
  //------------------------------------------------------------
 
13791
 
 
13792
  var showTooltip = function(e, offsetElement) {
 
13793
    var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
 
13794
        top = e.pos[1] + ( offsetElement.offsetTop || 0),
 
13795
        x = xAxis.tickFormat()(stacked.x()(e.point, e.pointIndex)),
 
13796
        y = yAxis.tickFormat()(stacked.y()(e.point, e.pointIndex)),
 
13797
        content = tooltip(e.series.key, x, y, e, chart);
 
13798
 
 
13799
    nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's', null, offsetElement);
 
13800
  };
 
13801
 
 
13802
  //============================================================
 
13803
 
 
13804
 
 
13805
  function chart(selection) {
 
13806
    selection.each(function(data) {
 
13807
      var container = d3.select(this),
 
13808
          that = this;
 
13809
 
 
13810
      var availableWidth = (width  || parseInt(container.style('width')) || 960)
 
13811
                             - margin.left - margin.right,
 
13812
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
13813
                             - margin.top - margin.bottom;
 
13814
 
 
13815
      chart.update = function() { container.transition().duration(transitionDuration).call(chart); };
 
13816
      chart.container = this;
 
13817
 
 
13818
      //set state.disabled
 
13819
      state.disabled = data.map(function(d) { return !!d.disabled });
 
13820
 
 
13821
      if (!defaultState) {
 
13822
        var key;
 
13823
        defaultState = {};
 
13824
        for (key in state) {
 
13825
          if (state[key] instanceof Array)
 
13826
            defaultState[key] = state[key].slice(0);
 
13827
          else
 
13828
            defaultState[key] = state[key];
 
13829
        }
 
13830
      }
 
13831
 
 
13832
      //------------------------------------------------------------
 
13833
      // Display No Data message if there's nothing to show.
 
13834
 
 
13835
      if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
 
13836
        var noDataText = container.selectAll('.nv-noData').data([noData]);
 
13837
 
 
13838
        noDataText.enter().append('text')
 
13839
          .attr('class', 'nvd3 nv-noData')
 
13840
          .attr('dy', '-.7em')
 
13841
          .style('text-anchor', 'middle');
 
13842
 
 
13843
        noDataText
 
13844
          .attr('x', margin.left + availableWidth / 2)
 
13845
          .attr('y', margin.top + availableHeight / 2)
 
13846
          .text(function(d) { return d });
 
13847
 
 
13848
        return chart;
 
13849
      } else {
 
13850
        container.selectAll('.nv-noData').remove();
 
13851
      }
 
13852
 
 
13853
      //------------------------------------------------------------
 
13854
 
 
13855
 
 
13856
      //------------------------------------------------------------
 
13857
      // Setup Scales
 
13858
 
 
13859
      x = stacked.xScale();
 
13860
      y = stacked.yScale();
 
13861
 
 
13862
      //------------------------------------------------------------
 
13863
 
 
13864
 
 
13865
      //------------------------------------------------------------
 
13866
      // Setup containers and skeleton of chart
 
13867
 
 
13868
      var wrap = container.selectAll('g.nv-wrap.nv-stackedAreaChart').data([data]);
 
13869
      var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-stackedAreaChart').append('g');
 
13870
      var g = wrap.select('g');
 
13871
 
 
13872
      gEnter.append("rect").style("opacity",0);
 
13873
      gEnter.append('g').attr('class', 'nv-x nv-axis');
 
13874
      gEnter.append('g').attr('class', 'nv-y nv-axis');
 
13875
      gEnter.append('g').attr('class', 'nv-stackedWrap');
 
13876
      gEnter.append('g').attr('class', 'nv-legendWrap');
 
13877
      gEnter.append('g').attr('class', 'nv-controlsWrap');
 
13878
      gEnter.append('g').attr('class', 'nv-interactive');
 
13879
 
 
13880
      g.select("rect").attr("width",availableWidth).attr("height",availableHeight);
 
13881
      //------------------------------------------------------------
 
13882
      // Legend
 
13883
 
 
13884
      if (showLegend) {
 
13885
        var legendWidth = (showControls) ? availableWidth - controlWidth : availableWidth;
 
13886
        legend
 
13887
          .width(legendWidth);
 
13888
 
 
13889
        g.select('.nv-legendWrap')
 
13890
            .datum(data)
 
13891
            .call(legend);
 
13892
 
 
13893
        if ( margin.top != legend.height()) {
 
13894
          margin.top = legend.height();
 
13895
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
13896
                             - margin.top - margin.bottom;
 
13897
        }
 
13898
 
 
13899
        g.select('.nv-legendWrap')
 
13900
            .attr('transform', 'translate(' + (availableWidth-legendWidth) + ',' + (-margin.top) +')');
 
13901
      }
 
13902
 
 
13903
      //------------------------------------------------------------
 
13904
 
 
13905
 
 
13906
      //------------------------------------------------------------
 
13907
      // Controls
 
13908
 
 
13909
      if (showControls) {
 
13910
        var controlsData = [
 
13911
          {
 
13912
            key: controlLabels.stacked || 'Stacked',
 
13913
            metaKey: 'Stacked',
 
13914
            disabled: stacked.style() != 'stack',
 
13915
            style: 'stack'
 
13916
          },
 
13917
          {
 
13918
            key: controlLabels.stream || 'Stream',
 
13919
            metaKey: 'Stream',
 
13920
            disabled: stacked.style() != 'stream',
 
13921
            style: 'stream'
 
13922
          },
 
13923
          {
 
13924
            key: controlLabels.expanded || 'Expanded',
 
13925
            metaKey: 'Expanded',
 
13926
            disabled: stacked.style() != 'expand',
 
13927
            style: 'expand'
 
13928
          },
 
13929
          {
 
13930
            key: controlLabels.stack_percent || 'Stack %',
 
13931
            metaKey: 'Stack_Percent',
 
13932
            disabled: stacked.style() != 'stack_percent',
 
13933
            style: 'stack_percent'
 
13934
          }
 
13935
        ];
 
13936
 
 
13937
        controlWidth = (cData.length/3) * 260;
 
13938
 
 
13939
        controlsData = controlsData.filter(function(d) {
 
13940
          return cData.indexOf(d.metaKey) !== -1;
 
13941
        })
 
13942
 
 
13943
        controls
 
13944
          .width( controlWidth )
 
13945
          .color(['#444', '#444', '#444']);
 
13946
 
 
13947
        g.select('.nv-controlsWrap')
 
13948
            .datum(controlsData)
 
13949
            .call(controls);
 
13950
 
 
13951
 
 
13952
        if ( margin.top != Math.max(controls.height(), legend.height()) ) {
 
13953
          margin.top = Math.max(controls.height(), legend.height());
 
13954
          availableHeight = (height || parseInt(container.style('height')) || 400)
 
13955
                             - margin.top - margin.bottom;
 
13956
        }
 
13957
 
 
13958
 
 
13959
        g.select('.nv-controlsWrap')
 
13960
            .attr('transform', 'translate(0,' + (-margin.top) +')');
 
13961
      }
 
13962
 
 
13963
      //------------------------------------------------------------
 
13964
 
 
13965
 
 
13966
      wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
 
13967
 
 
13968
      if (rightAlignYAxis) {
 
13969
          g.select(".nv-y.nv-axis")
 
13970
              .attr("transform", "translate(" + availableWidth + ",0)");
 
13971
      }
 
13972
 
 
13973
      //------------------------------------------------------------
 
13974
      // Main Chart Component(s)
 
13975
 
 
13976
      //------------------------------------------------------------
 
13977
      //Set up interactive layer
 
13978
      if (useInteractiveGuideline) {
 
13979
        interactiveLayer
 
13980
           .width(availableWidth)
 
13981
           .height(availableHeight)
 
13982
           .margin({left: margin.left, top: margin.top})
 
13983
           .svgContainer(container)
 
13984
           .xScale(x);
 
13985
        wrap.select(".nv-interactive").call(interactiveLayer);
 
13986
      }
 
13987
 
 
13988
      stacked
 
13989
        .width(availableWidth)
 
13990
        .height(availableHeight)
 
13991
 
 
13992
      var stackedWrap = g.select('.nv-stackedWrap')
 
13993
          .datum(data);
 
13994
 
 
13995
      stackedWrap.transition().call(stacked);
 
13996
 
 
13997
      //------------------------------------------------------------
 
13998
 
 
13999
 
 
14000
      //------------------------------------------------------------
 
14001
      // Setup Axes
 
14002
 
 
14003
      if (showXAxis) {
 
14004
        xAxis
 
14005
          .scale(x)
 
14006
          .ticks( availableWidth / 100 )
 
14007
          .tickSize( -availableHeight, 0);
 
14008
 
 
14009
        g.select('.nv-x.nv-axis')
 
14010
            .attr('transform', 'translate(0,' + availableHeight + ')');
 
14011
 
 
14012
        g.select('.nv-x.nv-axis')
 
14013
          .transition().duration(0)
 
14014
            .call(xAxis);
 
14015
      }
 
14016
 
 
14017
      if (showYAxis) {
 
14018
        yAxis
 
14019
          .scale(y)
 
14020
          .ticks(stacked.offset() == 'wiggle' ? 0 : availableHeight / 36)
 
14021
          .tickSize(-availableWidth, 0)
 
14022
          .setTickFormat( (stacked.style() == 'expand' || stacked.style() == 'stack_percent')
 
14023
                ? d3.format('%') : yAxisTickFormat);
 
14024
 
 
14025
        g.select('.nv-y.nv-axis')
 
14026
          .transition().duration(0)
 
14027
            .call(yAxis);
 
14028
      }
 
14029
 
 
14030
      //------------------------------------------------------------
 
14031
 
 
14032
 
 
14033
      //============================================================
 
14034
      // Event Handling/Dispatching (in chart's scope)
 
14035
      //------------------------------------------------------------
 
14036
 
 
14037
      stacked.dispatch.on('areaClick.toggle', function(e) {
 
14038
        if (data.filter(function(d) { return !d.disabled }).length === 1)
 
14039
          data.forEach(function(d) {
 
14040
            d.disabled = false;
 
14041
          });
 
14042
        else
 
14043
          data.forEach(function(d,i) {
 
14044
            d.disabled = (i != e.seriesIndex);
 
14045
          });
 
14046
 
 
14047
        state.disabled = data.map(function(d) { return !!d.disabled });
 
14048
        dispatch.stateChange(state);
 
14049
 
 
14050
        chart.update();
 
14051
      });
 
14052
 
 
14053
      legend.dispatch.on('stateChange', function(newState) {
 
14054
        state.disabled = newState.disabled;
 
14055
        dispatch.stateChange(state);
 
14056
        chart.update();
 
14057
      });
 
14058
 
 
14059
      controls.dispatch.on('legendClick', function(d,i) {
 
14060
        if (!d.disabled) return;
 
14061
 
 
14062
        controlsData = controlsData.map(function(s) {
 
14063
          s.disabled = true;
 
14064
          return s;
 
14065
        });
 
14066
        d.disabled = false;
 
14067
 
 
14068
        stacked.style(d.style);
 
14069
 
 
14070
 
 
14071
        state.style = stacked.style();
 
14072
        dispatch.stateChange(state);
 
14073
 
 
14074
        chart.update();
 
14075
      });
 
14076
 
 
14077
 
 
14078
      interactiveLayer.dispatch.on('elementMousemove', function(e) {
 
14079
          stacked.clearHighlights();
 
14080
          var singlePoint, pointIndex, pointXLocation, allData = [];
 
14081
          data
 
14082
          .filter(function(series, i) {
 
14083
            series.seriesIndex = i;
 
14084
            return !series.disabled;
 
14085
          })
 
14086
          .forEach(function(series,i) {
 
14087
              pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
 
14088
              stacked.highlightPoint(i, pointIndex, true);
 
14089
              var point = series.values[pointIndex];
 
14090
              if (typeof point === 'undefined') return;
 
14091
              if (typeof singlePoint === 'undefined') singlePoint = point;
 
14092
              if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
 
14093
 
 
14094
              //If we are in 'expand' mode, use the stacked percent value instead of raw value.
 
14095
              var tooltipValue = (stacked.style() == 'expand') ? point.display.y : chart.y()(point,pointIndex);
 
14096
              allData.push({
 
14097
                  key: series.key,
 
14098
                  value: tooltipValue,
 
14099
                  color: color(series,series.seriesIndex),
 
14100
                  stackedValue: point.display
 
14101
              });
 
14102
          });
 
14103
 
 
14104
          allData.reverse();
 
14105
 
 
14106
          //Highlight the tooltip entry based on which stack the mouse is closest to.
 
14107
          if (allData.length > 2) {
 
14108
            var yValue = chart.yScale().invert(e.mouseY);
 
14109
            var yDistMax = Infinity, indexToHighlight = null;
 
14110
            allData.forEach(function(series,i) {
 
14111
 
 
14112
               //To handle situation where the stacked area chart is negative, we need to use absolute values
 
14113
               //when checking if the mouse Y value is within the stack area.
 
14114
               yValue = Math.abs(yValue);
 
14115
               var stackedY0 = Math.abs(series.stackedValue.y0);
 
14116
               var stackedY = Math.abs(series.stackedValue.y);
 
14117
               if ( yValue >= stackedY0 && yValue <= (stackedY + stackedY0))
 
14118
               {
 
14119
                  indexToHighlight = i;
 
14120
                  return;
 
14121
               }
 
14122
            });
 
14123
            if (indexToHighlight != null)
 
14124
               allData[indexToHighlight].highlight = true;
 
14125
          }
 
14126
 
 
14127
          var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
 
14128
 
 
14129
          //If we are in 'expand' mode, force the format to be a percentage.
 
14130
          var valueFormatter = (stacked.style() == 'expand') ?
 
14131
               function(d,i) {return d3.format(".1%")(d);} :
 
14132
               function(d,i) {return yAxis.tickFormat()(d); };
 
14133
          interactiveLayer.tooltip
 
14134
                  .position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
 
14135
                  .chartContainer(that.parentNode)
 
14136
                  .enabled(tooltips)
 
14137
                  .valueFormatter(valueFormatter)
 
14138
                  .data(
 
14139
                      {
 
14140
                        value: xValue,
 
14141
                        series: allData
 
14142
                      }
 
14143
                  )();
 
14144
 
 
14145
          interactiveLayer.renderGuideLine(pointXLocation);
 
14146
 
 
14147
      });
 
14148
 
 
14149
      interactiveLayer.dispatch.on("elementMouseout",function(e) {
 
14150
          dispatch.tooltipHide();
 
14151
          stacked.clearHighlights();
 
14152
      });
 
14153
 
 
14154
 
 
14155
      dispatch.on('tooltipShow', function(e) {
 
14156
        if (tooltips) showTooltip(e, that.parentNode);
 
14157
      });
 
14158
 
 
14159
      // Update chart from a state object passed to event handler
 
14160
      dispatch.on('changeState', function(e) {
 
14161
 
 
14162
        if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) {
 
14163
          data.forEach(function(series,i) {
 
14164
            series.disabled = e.disabled[i];
 
14165
          });
 
14166
 
 
14167
          state.disabled = e.disabled;
 
14168
        }
 
14169
 
 
14170
        if (typeof e.style !== 'undefined') {
 
14171
          stacked.style(e.style);
 
14172
        }
 
14173
 
 
14174
        chart.update();
 
14175
      });
 
14176
 
 
14177
    });
 
14178
 
 
14179
 
 
14180
    return chart;
 
14181
  }
 
14182
 
 
14183
 
 
14184
  //============================================================
 
14185
  // Event Handling/Dispatching (out of chart's scope)
 
14186
  //------------------------------------------------------------
 
14187
 
 
14188
  stacked.dispatch.on('tooltipShow', function(e) {
 
14189
    //disable tooltips when value ~= 0
 
14190
    //// TODO: consider removing points from voronoi that have 0 value instead of this hack
 
14191
    /*
 
14192
    if (!Math.round(stacked.y()(e.point) * 100)) {  // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
 
14193
      setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
 
14194
      return false;
 
14195
    }
 
14196
   */
 
14197
 
 
14198
    e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
 
14199
    dispatch.tooltipShow(e);
 
14200
  });
 
14201
 
 
14202
  stacked.dispatch.on('tooltipHide', function(e) {
 
14203
    dispatch.tooltipHide(e);
 
14204
  });
 
14205
 
 
14206
  dispatch.on('tooltipHide', function() {
 
14207
    if (tooltips) nv.tooltip.cleanup();
 
14208
  });
 
14209
 
 
14210
  //============================================================
 
14211
 
 
14212
 
 
14213
  //============================================================
 
14214
  // Expose Public Variables
 
14215
  //------------------------------------------------------------
 
14216
 
 
14217
  // expose chart's sub-components
 
14218
  chart.dispatch = dispatch;
 
14219
  chart.stacked = stacked;
 
14220
  chart.legend = legend;
 
14221
  chart.controls = controls;
 
14222
  chart.xAxis = xAxis;
 
14223
  chart.yAxis = yAxis;
 
14224
  chart.interactiveLayer = interactiveLayer;
 
14225
 
 
14226
  d3.rebind(chart, stacked, 'x', 'y', 'size', 'xScale', 'yScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'interactive', 'useVoronoi', 'offset', 'order', 'style', 'clipEdge', 'forceX', 'forceY', 'forceSize', 'interpolate');
 
14227
 
 
14228
  chart.options = nv.utils.optionsFunc.bind(chart);
 
14229
 
 
14230
  chart.margin = function(_) {
 
14231
    if (!arguments.length) return margin;
 
14232
    margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
 
14233
    margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
 
14234
    margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
 
14235
    margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
 
14236
    return chart;
 
14237
  };
 
14238
 
 
14239
  chart.width = function(_) {
 
14240
    if (!arguments.length) return width;
 
14241
    width = _;
 
14242
    return chart;
 
14243
  };
 
14244
 
 
14245
  chart.height = function(_) {
 
14246
    if (!arguments.length) return height;
 
14247
    height = _;
 
14248
    return chart;
 
14249
  };
 
14250
 
 
14251
  chart.color = function(_) {
 
14252
    if (!arguments.length) return color;
 
14253
    color = nv.utils.getColor(_);
 
14254
    legend.color(color);
 
14255
    stacked.color(color);
 
14256
    return chart;
 
14257
  };
 
14258
 
 
14259
  chart.showControls = function(_) {
 
14260
    if (!arguments.length) return showControls;
 
14261
    showControls = _;
 
14262
    return chart;
 
14263
  };
 
14264
 
 
14265
  chart.showLegend = function(_) {
 
14266
    if (!arguments.length) return showLegend;
 
14267
    showLegend = _;
 
14268
    return chart;
 
14269
  };
 
14270
 
 
14271
  chart.showXAxis = function(_) {
 
14272
    if (!arguments.length) return showXAxis;
 
14273
    showXAxis = _;
 
14274
    return chart;
 
14275
  };
 
14276
 
 
14277
  chart.showYAxis = function(_) {
 
14278
    if (!arguments.length) return showYAxis;
 
14279
    showYAxis = _;
 
14280
    return chart;
 
14281
  };
 
14282
 
 
14283
  chart.rightAlignYAxis = function(_) {
 
14284
    if(!arguments.length) return rightAlignYAxis;
 
14285
    rightAlignYAxis = _;
 
14286
    yAxis.orient( (_) ? 'right' : 'left');
 
14287
    return chart;
 
14288
  };
 
14289
 
 
14290
  chart.useInteractiveGuideline = function(_) {
 
14291
    if(!arguments.length) return useInteractiveGuideline;
 
14292
    useInteractiveGuideline = _;
 
14293
    if (_ === true) {
 
14294
       chart.interactive(false);
 
14295
       chart.useVoronoi(false);
 
14296
    }
 
14297
    return chart;
 
14298
  };
 
14299
 
 
14300
  chart.tooltip = function(_) {
 
14301
    if (!arguments.length) return tooltip;
 
14302
    tooltip = _;
 
14303
    return chart;
 
14304
  };
 
14305
 
 
14306
  chart.tooltips = function(_) {
 
14307
    if (!arguments.length) return tooltips;
 
14308
    tooltips = _;
 
14309
    return chart;
 
14310
  };
 
14311
 
 
14312
  chart.tooltipContent = function(_) {
 
14313
    if (!arguments.length) return tooltip;
 
14314
    tooltip = _;
 
14315
    return chart;
 
14316
  };
 
14317
 
 
14318
  chart.state = function(_) {
 
14319
    if (!arguments.length) return state;
 
14320
    state = _;
 
14321
    return chart;
 
14322
  };
 
14323
 
 
14324
  chart.defaultState = function(_) {
 
14325
    if (!arguments.length) return defaultState;
 
14326
    defaultState = _;
 
14327
    return chart;
 
14328
  };
 
14329
 
 
14330
  chart.noData = function(_) {
 
14331
    if (!arguments.length) return noData;
 
14332
    noData = _;
 
14333
    return chart;
 
14334
  };
 
14335
 
 
14336
  chart.transitionDuration = function(_) {
 
14337
    if (!arguments.length) return transitionDuration;
 
14338
    transitionDuration = _;
 
14339
    return chart;
 
14340
  };
 
14341
 
 
14342
  chart.controlsData = function(_) {
 
14343
    if (!arguments.length) return cData;
 
14344
    cData = _;
 
14345
    return chart;
 
14346
  };
 
14347
 
 
14348
  chart.controlLabels = function(_) {
 
14349
    if (!arguments.length) return controlLabels;
 
14350
    if (typeof _ !== 'object') return controlLabels;
 
14351
    controlLabels = _;
 
14352
    return chart;
 
14353
  };
 
14354
 
 
14355
  yAxis.setTickFormat = yAxis.tickFormat;
 
14356
 
 
14357
  yAxis.tickFormat = function(_) {
 
14358
    if (!arguments.length) return yAxisTickFormat;
 
14359
    yAxisTickFormat = _;
 
14360
    return yAxis;
 
14361
  };
 
14362
 
 
14363
 
 
14364
  //============================================================
 
14365
 
 
14366
  return chart;
 
14367
}
 
14368
})();