novus-nvd3-rails 1.8.4 → 1.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a5f121514df13fbf7e5af57fa40e09c4656fc5d4
4
- data.tar.gz: f11b3b9f8aa1ef76718a486f4cf7650c9227c972
3
+ metadata.gz: 27caa71fa31191352db665a99346297a44beb342
4
+ data.tar.gz: 387b68dda653bac75183ce5054544d0472919f7b
5
5
  SHA512:
6
- metadata.gz: ddd3c9ba4788e2ad49037fda46064a0a66e964f68f1326ed1aab266908a90b35bcfaa7c283d3cf98c1db79a3f527eb7fbf19ada7ea1c776b9b22e88d72ba16ef
7
- data.tar.gz: 0ac7e602cddcd6c0fdc6f10813e18c264332dc2e67006674b55a18f55fd04dc890fd503689f7391849af12b50854350bed0034cadc92c7218d50007f82dce076
6
+ metadata.gz: ac1095ea207a2b638a845fc02ab24303c6f2a583e72455562a8c3d13a81b8ec730e1df403d9d5f21bbebf7f14b3a21e15332698ef9d6b044f8ad58359d03f496
7
+ data.tar.gz: f8312d124b7850009d4454dce38e253f74af85eefb1035f151d6b30e68027896670b7e8cf580297171616a19619a30c23278efeceecf7d7fe098f9a014447577
@@ -1,7 +1,7 @@
1
1
  module Novus
2
2
  module Nvd3
3
3
  module Rails
4
- VERSION = "1.8.4"
4
+ VERSION = "1.8.5"
5
5
  end
6
6
  end
7
7
  end
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.12"
22
22
  spec.add_development_dependency "rake", "~> 10.0"
23
- spec.add_development_dependency 'rails', '4.2.6'
23
+ spec.add_development_dependency 'rails', '>= 4.0', '< 5.1'
24
24
  spec.add_development_dependency 'rspec-rails', '~> 3.0'
25
25
  spec.add_development_dependency 'capybara', '~> 2.6'
26
26
  spec.add_development_dependency 'sqlite3', '~> 1.3'
@@ -1,4 +1,4 @@
1
- /* nvd3 version 1.8.4 (https://github.com/novus/nvd3) 2016-07-03 */
1
+ /* nvd3 version 1.8.5 (https://github.com/novus/nvd3) 2016-12-01 */
2
2
  (function(){
3
3
 
4
4
  // set up main nv object
@@ -779,7 +779,7 @@ nv.models.tooltip = function() {
779
779
  // Create new tooltip div if it doesn't exist on DOM.
780
780
 
781
781
  var data = [1];
782
- tooltip = d3.select(document.body).selectAll('.nvtooltip').data(data);
782
+ tooltip = d3.select(document.body).select('#'+id).data(data);
783
783
 
784
784
  tooltip.enter().append('div')
785
785
  .attr("class", "nvtooltip " + (classes ? classes : "xy-tooltip"))
@@ -2085,7 +2085,7 @@ nv.models.boxPlot = function() {
2085
2085
  boxEnter.each(function(d,i) {
2086
2086
  var box = d3.select(this);
2087
2087
  [getWl, getWh].forEach(function (f) {
2088
- if (f(d)) {
2088
+ if (f(d) !== undefined && f(d) !== null) {
2089
2089
  var key = (f === getWl) ? 'low' : 'high';
2090
2090
  box.append('line')
2091
2091
  .style('stroke', getColor(d) || color(d,i))
@@ -2565,6 +2565,7 @@ nv.models.bullet = function() {
2565
2565
  , dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove')
2566
2566
  , defaultRangeLabels = ["Maximum", "Mean", "Minimum"]
2567
2567
  , legacyRangeClassNames = ["Max", "Avg", "Min"]
2568
+ , duration = 1000
2568
2569
  ;
2569
2570
 
2570
2571
  function sortLabels(labels, values){
@@ -2586,7 +2587,7 @@ nv.models.bullet = function() {
2586
2587
 
2587
2588
  var rangez = ranges.call(this, d, i).slice(),
2588
2589
  markerz = markers.call(this, d, i).slice(),
2589
- markerLinez = markerLines.call(this, d, i).slice().sort(d3.descending),
2590
+ markerLinez = markerLines.call(this, d, i).slice(),
2590
2591
  measurez = measures.call(this, d, i).slice(),
2591
2592
  rangeLabelz = rangeLabels.call(this, d, i).slice(),
2592
2593
  markerLabelz = markerLabels.call(this, d, i).slice(),
@@ -2602,6 +2603,7 @@ nv.models.bullet = function() {
2602
2603
  // sort values descending
2603
2604
  rangez.sort(d3.descending);
2604
2605
  markerz.sort(d3.descending);
2606
+ markerLinez.sort(d3.descending);
2605
2607
  measurez.sort(d3.descending);
2606
2608
 
2607
2609
  // Setup Scales
@@ -2648,20 +2650,18 @@ nv.models.bullet = function() {
2648
2650
  for(var i=0,il=rangez.length; i<il; i++){
2649
2651
  var range = rangez[i];
2650
2652
  g.select('rect.nv-range'+i)
2653
+ .datum(range)
2651
2654
  .attr('height', availableHeight)
2655
+ .transition()
2656
+ .duration(duration)
2652
2657
  .attr('width', w1(range))
2653
2658
  .attr('x', xp1(range))
2654
- .datum(range)
2655
2659
  }
2656
2660
 
2657
2661
  g.select('rect.nv-measure')
2658
2662
  .style('fill', color)
2659
2663
  .attr('height', availableHeight / 3)
2660
2664
  .attr('y', availableHeight / 3)
2661
- .attr('width', measurez < 0 ?
2662
- x1(0) - x1(measurez[0])
2663
- : x1(measurez[0]) - x1(0))
2664
- .attr('x', xp1(measurez))
2665
2665
  .on('mouseover', function() {
2666
2666
  dispatch.elementMouseover({
2667
2667
  value: measurez[0],
@@ -2682,7 +2682,13 @@ nv.models.bullet = function() {
2682
2682
  label: measureLabelz[0] || 'Current',
2683
2683
  color: d3.select(this).style("fill")
2684
2684
  })
2685
- });
2685
+ })
2686
+ .transition()
2687
+ .duration(duration)
2688
+ .attr('width', measurez < 0 ?
2689
+ x1(0) - x1(measurez[0])
2690
+ : x1(measurez[0]) - x1(0))
2691
+ .attr('x', xp1(measurez));
2686
2692
 
2687
2693
  var h3 = availableHeight / 6;
2688
2694
 
@@ -2722,13 +2728,15 @@ nv.models.bullet = function() {
2722
2728
 
2723
2729
  g.selectAll("path.nv-markerTriangle")
2724
2730
  .data(markerData)
2731
+ .transition()
2732
+ .duration(duration)
2725
2733
  .attr('transform', function(d) { return 'translate(' + x1(d.value) + ',' + (availableHeight / 2) + ')' });
2726
2734
 
2727
2735
  var markerLinesData = markerLinez.map( function(marker, index) {
2728
2736
  return {value: marker, label: markerLineLabelz[index]}
2729
2737
  });
2730
2738
  gEnter
2731
- .selectAll("path.nv-markerLine")
2739
+ .selectAll("line.nv-markerLine")
2732
2740
  .data(markerLinesData)
2733
2741
  .enter()
2734
2742
  .append('line')
@@ -2762,9 +2770,12 @@ nv.models.bullet = function() {
2762
2770
  })
2763
2771
  });
2764
2772
 
2765
- g.selectAll("path.nv-markerLines")
2773
+ g.selectAll("line.nv-markerLine")
2766
2774
  .data(markerLinesData)
2767
- .attr('transform', function(d) { return 'translate(' + x1(d.value) + ',' + (availableHeight / 2) + ')' });
2775
+ .transition()
2776
+ .duration(duration)
2777
+ .attr('x1', function(d) { return x1(d.value) })
2778
+ .attr('x2', function(d) { return x1(d.value) });
2768
2779
 
2769
2780
  wrap.selectAll('.nv-range')
2770
2781
  .on('mouseover', function(d,i) {
@@ -2811,6 +2822,7 @@ nv.models.bullet = function() {
2811
2822
  width: {get: function(){return width;}, set: function(_){width=_;}},
2812
2823
  height: {get: function(){return height;}, set: function(_){height=_;}},
2813
2824
  tickFormat: {get: function(){return tickFormat;}, set: function(_){tickFormat=_;}},
2825
+ duration: {get: function(){return duration;}, set: function(_){duration=_;}},
2814
2826
 
2815
2827
  // options that require extra logic in the setter
2816
2828
  margin: {get: function(){return margin;}, set: function(_){
@@ -2856,7 +2868,7 @@ nv.models.bulletChart = function() {
2856
2868
  , width = null
2857
2869
  , height = 55
2858
2870
  , tickFormat = null
2859
- , ticks = null
2871
+ , ticks = null
2860
2872
  , noData = null
2861
2873
  , dispatch = d3.dispatch()
2862
2874
  ;
@@ -2930,7 +2942,7 @@ nv.models.bulletChart = function() {
2930
2942
 
2931
2943
  bullet
2932
2944
  .width(availableWidth)
2933
- .height(availableHeight)
2945
+ .height(availableHeight);
2934
2946
 
2935
2947
  var bulletWrap = g.select('.nv-bulletWrap');
2936
2948
  d3.transition(bulletWrap).call(bullet);
@@ -2962,6 +2974,8 @@ nv.models.bulletChart = function() {
2962
2974
 
2963
2975
  // Transition the updating ticks to the new scale, x1.
2964
2976
  var tickUpdate = d3.transition(tick)
2977
+ .transition()
2978
+ .duration(bullet.duration())
2965
2979
  .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
2966
2980
  .style('opacity', 1);
2967
2981
 
@@ -2974,6 +2988,8 @@ nv.models.bulletChart = function() {
2974
2988
 
2975
2989
  // Transition the exiting ticks to the new scale, x1.
2976
2990
  d3.transition(tick.exit())
2991
+ .transition()
2992
+ .duration(bullet.duration())
2977
2993
  .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
2978
2994
  .style('opacity', 1e-6)
2979
2995
  .remove();
@@ -3289,6 +3305,7 @@ nv.models.cumulativeLineChart = function() {
3289
3305
  ;
3290
3306
 
3291
3307
  var margin = {top: 30, right: 30, bottom: 50, left: 60}
3308
+ , marginTop = null
3292
3309
  , color = nv.utils.defaultColor()
3293
3310
  , width = null
3294
3311
  , height = null
@@ -3493,7 +3510,7 @@ nv.models.cumulativeLineChart = function() {
3493
3510
  .datum(data)
3494
3511
  .call(legend);
3495
3512
 
3496
- if (legend.height() > margin.top) {
3513
+ if (!marginTop && legend.height() !== margin.top) {
3497
3514
  margin.top = legend.height();
3498
3515
  availableHeight = nv.utils.availableHeight(height, container, margin);
3499
3516
  }
@@ -3887,7 +3904,10 @@ nv.models.cumulativeLineChart = function() {
3887
3904
 
3888
3905
  // options that require extra logic in the setter
3889
3906
  margin: {get: function(){return margin;}, set: function(_){
3890
- margin.top = _.top !== undefined ? _.top : margin.top;
3907
+ if (_.top !== undefined) {
3908
+ margin.top = _.top;
3909
+ marginTop = _.top;
3910
+ }
3891
3911
  margin.right = _.right !== undefined ? _.right : margin.right;
3892
3912
  margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
3893
3913
  margin.left = _.left !== undefined ? _.left : margin.left;
@@ -4191,6 +4211,7 @@ nv.models.discreteBarChart = function() {
4191
4211
  ;
4192
4212
 
4193
4213
  var margin = {top: 15, right: 10, bottom: 50, left: 60}
4214
+ , marginTop = null
4194
4215
  , width = null
4195
4216
  , height = null
4196
4217
  , color = nv.utils.getColor()
@@ -4291,7 +4312,7 @@ nv.models.discreteBarChart = function() {
4291
4312
  .datum(data)
4292
4313
  .call(legend);
4293
4314
 
4294
- if (legend.height() > margin.top) {
4315
+ if (!marginTop && legend.height() !== margin.top) {
4295
4316
  margin.top = legend.height();
4296
4317
  availableHeight = nv.utils.availableHeight(height, container, margin);
4297
4318
  }
@@ -4426,7 +4447,10 @@ nv.models.discreteBarChart = function() {
4426
4447
 
4427
4448
  // options that require extra logic in the setter
4428
4449
  margin: {get: function(){return margin;}, set: function(_){
4429
- margin.top = _.top !== undefined ? _.top : margin.top;
4450
+ if (_.top !== undefined) {
4451
+ margin.top = _.top;
4452
+ marginTop = _.top;
4453
+ }
4430
4454
  margin.right = _.right !== undefined ? _.right : margin.right;
4431
4455
  margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
4432
4456
  margin.left = _.left !== undefined ? _.left : margin.left;
@@ -4640,6 +4664,7 @@ nv.models.focus = function(content) {
4640
4664
  , brushExtent = null
4641
4665
  , duration = 250
4642
4666
  , dispatch = d3.dispatch('brush', 'onBrush', 'renderEnd')
4667
+ , syncBrushing = true
4643
4668
  ;
4644
4669
 
4645
4670
  content.interactive(false);
@@ -4715,9 +4740,15 @@ nv.models.focus = function(content) {
4715
4740
  brush
4716
4741
  .x(x)
4717
4742
  .on('brush', function() {
4718
- onBrush();
4743
+ onBrush(syncBrushing);
4719
4744
  });
4720
4745
 
4746
+ brush.on('brushend', function () {
4747
+ if (!syncBrushing) {
4748
+ dispatch.onBrush(brush.empty() ? x.domain() : brush.extent());
4749
+ }
4750
+ });
4751
+
4721
4752
  if (brushExtent) brush.extent(brushExtent);
4722
4753
 
4723
4754
  var brushBG = g.select('.nv-brushBackground').selectAll('g')
@@ -4744,7 +4775,7 @@ nv.models.focus = function(content) {
4744
4775
  .attr('height', availableHeight);
4745
4776
  gBrush.selectAll('.resize').append('path').attr('d', resizePath);
4746
4777
 
4747
- onBrush();
4778
+ onBrush(true);
4748
4779
 
4749
4780
  g.select('.nv-background rect')
4750
4781
  .attr('width', availableWidth)
@@ -4814,24 +4845,17 @@ nv.models.focus = function(content) {
4814
4845
  .attr('width', rightWidth < 0 ? 0 : rightWidth);
4815
4846
  });
4816
4847
  }
4817
-
4818
-
4819
- function onBrush() {
4848
+
4849
+
4850
+ function onBrush(shouldDispatch) {
4820
4851
  brushExtent = brush.empty() ? null : brush.extent();
4821
4852
  var extent = brush.empty() ? x.domain() : brush.extent();
4822
-
4823
- //The brush extent cannot be less than one. If it is, don't update the line chart.
4824
- if (Math.abs(extent[0] - extent[1]) <= 1) {
4825
- return;
4826
- }
4827
-
4828
4853
  dispatch.brush({extent: extent, brush: brush});
4829
-
4830
4854
  updateBrushBG();
4831
- dispatch.onBrush(extent);
4855
+ if (shouldDispatch) {
4856
+ dispatch.onBrush(extent);
4857
+ }
4832
4858
  }
4833
-
4834
-
4835
4859
  });
4836
4860
 
4837
4861
  renderWatch.renderEnd('focus immediate');
@@ -4862,6 +4886,7 @@ nv.models.focus = function(content) {
4862
4886
  showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
4863
4887
  showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
4864
4888
  brushExtent: {get: function(){return brushExtent;}, set: function(_){brushExtent=_;}},
4889
+ syncBrushing: {get: function(){return syncBrushing;}, set: function(_){syncBrushing=_;}},
4865
4890
 
4866
4891
  // options that require extra logic in the setter
4867
4892
  margin: {get: function(){return margin;}, set: function(_){
@@ -4899,7 +4924,7 @@ nv.models.focus = function(content) {
4899
4924
  rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){
4900
4925
  rightAlignYAxis = _;
4901
4926
  yAxis.orient( rightAlignYAxis ? 'right' : 'left');
4902
- }},
4927
+ }}
4903
4928
  });
4904
4929
 
4905
4930
  nv.utils.inheritOptions(chart, content);
@@ -5577,10 +5602,13 @@ nv.models.historicalBar = function() {
5577
5602
  })
5578
5603
  .on('click', function(d,i) {
5579
5604
  if (!interactive) return;
5605
+ var element = this;
5580
5606
  dispatch.elementClick({
5581
5607
  data: d,
5582
5608
  index: i,
5583
- color: d3.select(this).style("fill")
5609
+ color: d3.select(this).style("fill"),
5610
+ event: d3.event,
5611
+ element: element
5584
5612
  });
5585
5613
  d3.event.stopPropagation();
5586
5614
  })
@@ -5694,6 +5722,7 @@ nv.models.historicalBarChart = function(bar_model) {
5694
5722
 
5695
5723
 
5696
5724
  var margin = {top: 30, right: 90, bottom: 50, left: 90}
5725
+ , marginTop = null
5697
5726
  , color = nv.utils.defaultColor()
5698
5727
  , width = null
5699
5728
  , height = null
@@ -5793,7 +5822,7 @@ nv.models.historicalBarChart = function(bar_model) {
5793
5822
  .datum(data)
5794
5823
  .call(legend);
5795
5824
 
5796
- if (legend.height() > margin.top) {
5825
+ if (!marginTop && legend.height() !== margin.top) {
5797
5826
  margin.top = legend.height();
5798
5827
  availableHeight = nv.utils.availableHeight(height, container, margin);
5799
5828
  }
@@ -5996,7 +6025,10 @@ nv.models.historicalBarChart = function(bar_model) {
5996
6025
 
5997
6026
  // options that require extra logic in the setter
5998
6027
  margin: {get: function(){return margin;}, set: function(_){
5999
- margin.top = _.top !== undefined ? _.top : margin.top;
6028
+ if (_.top !== undefined) {
6029
+ margin.top = _.top;
6030
+ marginTop = _.top;
6031
+ }
6000
6032
  margin.right = _.right !== undefined ? _.right : margin.right;
6001
6033
  margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
6002
6034
  margin.left = _.left !== undefined ? _.left : margin.left;
@@ -6111,7 +6143,10 @@ nv.models.legend = function() {
6111
6143
  var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-legend').append('g');
6112
6144
  var g = wrap.select('g');
6113
6145
 
6114
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
6146
+ if (rightAlign)
6147
+ wrap.attr('transform', 'translate(' + (- margin.right) + ',' + margin.top + ')');
6148
+ else
6149
+ wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
6115
6150
 
6116
6151
  var series = g.selectAll('.nv-series')
6117
6152
  .data(function(d) {
@@ -6703,6 +6738,7 @@ nv.models.lineChart = function() {
6703
6738
  ;
6704
6739
 
6705
6740
  var margin = {top: 30, right: 20, bottom: 50, left: 60}
6741
+ , marginTop = null
6706
6742
  , color = nv.utils.defaultColor()
6707
6743
  , width = null
6708
6744
  , height = null
@@ -6850,7 +6886,7 @@ nv.models.lineChart = function() {
6850
6886
  wrap.select('.nv-legendWrap')
6851
6887
  .attr('transform', 'translate(0,' + availableHeight +')');
6852
6888
  } else if (legendPosition === 'top') {
6853
- if (legend.height() > margin.top) {
6889
+ if (!marginTop && legend.height() !== margin.top) {
6854
6890
  margin.top = legend.height();
6855
6891
  availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
6856
6892
  }
@@ -6974,14 +7010,20 @@ nv.models.lineChart = function() {
6974
7010
  .forEach(function(series,i) {
6975
7011
  var extent = focusEnable ? (focus.brush.empty() ? focus.xScale().domain() : focus.brush.extent()) : x.domain();
6976
7012
  var currentValues = series.values.filter(function(d,i) {
6977
- return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
7013
+ // Checks if the x point is between the extents, handling case where extent[0] is greater than extent[1]
7014
+ // (e.g. x domain is manually set to reverse the x-axis)
7015
+ if(extent[0] <= extent[1]) {
7016
+ return lines.x()(d,i) >= extent[0] && lines.x()(d,i) <= extent[1];
7017
+ } else {
7018
+ return lines.x()(d,i) >= extent[1] && lines.x()(d,i) <= extent[0];
7019
+ }
6978
7020
  });
6979
7021
 
6980
7022
  pointIndex = nv.interactiveBisect(currentValues, e.pointXValue, lines.x());
6981
7023
  var point = currentValues[pointIndex];
6982
7024
  var pointYValue = chart.y()(point, pointIndex);
6983
7025
  if (pointYValue !== null) {
6984
- lines.highlightPoint(series.seriesIndex, pointIndex, true);
7026
+ lines.highlightPoint(i, pointIndex, true);
6985
7027
  }
6986
7028
  if (point === undefined) return;
6987
7029
  if (singlePoint === undefined) singlePoint = point;
@@ -7160,7 +7202,10 @@ nv.models.lineChart = function() {
7160
7202
 
7161
7203
  // options that require extra logic in the setter
7162
7204
  focusMargin: {get: function(){return focus.margin}, set: function(_){
7163
- focus.margin.top = _.top !== undefined ? _.top : focus.margin.top;
7205
+ if (_.top !== undefined) {
7206
+ margin.top = _.top;
7207
+ marginTop = _.top;
7208
+ }
7164
7209
  focus.margin.right = _.right !== undefined ? _.right : focus.margin.right;
7165
7210
  focus.margin.bottom = _.bottom !== undefined ? _.bottom : focus.margin.bottom;
7166
7211
  focus.margin.left = _.left !== undefined ? _.left : focus.margin.left;
@@ -7252,6 +7297,7 @@ nv.models.linePlusBarChart = function() {
7252
7297
  ;
7253
7298
 
7254
7299
  var margin = {top: 30, right: 30, bottom: 30, left: 60}
7300
+ , marginTop = null
7255
7301
  , margin2 = {top: 0, right: 30, bottom: 20, left: 60}
7256
7302
  , width = null
7257
7303
  , height = null
@@ -7463,7 +7509,7 @@ nv.models.linePlusBarChart = function() {
7463
7509
  }))
7464
7510
  .call(legend);
7465
7511
 
7466
- if (legend.height() > margin.top) {
7512
+ if (!marginTop && legend.height() !== margin.top) {
7467
7513
  margin.top = legend.height();
7468
7514
  // FIXME: shouldn't this be "- (focusEnabled ? focusHeight : 0)"?
7469
7515
  availableHeight1 = nv.utils.availableHeight(height, container, margin) - focusHeight;
@@ -7837,7 +7883,10 @@ nv.models.linePlusBarChart = function() {
7837
7883
 
7838
7884
  // options that require extra logic in the setter
7839
7885
  margin: {get: function(){return margin;}, set: function(_){
7840
- margin.top = _.top !== undefined ? _.top : margin.top;
7886
+ if (_.top !== undefined) {
7887
+ margin.top = _.top;
7888
+ marginTop = _.top;
7889
+ }
7841
7890
  margin.right = _.right !== undefined ? _.right : margin.right;
7842
7891
  margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
7843
7892
  margin.left = _.left !== undefined ? _.left : margin.left;
@@ -8341,6 +8390,7 @@ nv.models.multiBarChart = function() {
8341
8390
  ;
8342
8391
 
8343
8392
  var margin = {top: 30, right: 20, bottom: 50, left: 60}
8393
+ , marginTop = null
8344
8394
  , width = null
8345
8395
  , height = null
8346
8396
  , color = nv.utils.defaultColor()
@@ -8388,6 +8438,31 @@ nv.models.multiBarChart = function() {
8388
8438
  return xAxis.tickFormat()(d, i);
8389
8439
  });
8390
8440
 
8441
+ interactiveLayer.tooltip
8442
+ .valueFormatter(function(d, i) {
8443
+ return d == null ? "N/A" : yAxis.tickFormat()(d, i);
8444
+ })
8445
+ .headerFormatter(function(d, i) {
8446
+ return xAxis.tickFormat()(d, i);
8447
+ });
8448
+
8449
+ interactiveLayer.tooltip
8450
+ .valueFormatter(function (d, i) {
8451
+ return d == null ? "N/A" : yAxis.tickFormat()(d, i);
8452
+ })
8453
+ .headerFormatter(function (d, i) {
8454
+ return xAxis.tickFormat()(d, i);
8455
+ });
8456
+
8457
+ interactiveLayer.tooltip
8458
+ .duration(0)
8459
+ .valueFormatter(function(d, i) {
8460
+ return yAxis.tickFormat()(d, i);
8461
+ })
8462
+ .headerFormatter(function(d, i) {
8463
+ return xAxis.tickFormat()(d, i);
8464
+ });
8465
+
8391
8466
  controls.updateState(false);
8392
8467
 
8393
8468
  //============================================================
@@ -8493,7 +8568,7 @@ nv.models.multiBarChart = function() {
8493
8568
  .datum(data)
8494
8569
  .call(legend);
8495
8570
 
8496
- if (legend.height() > margin.top) {
8571
+ if (!marginTop && legend.height() !== margin.top) {
8497
8572
  margin.top = legend.height();
8498
8573
  availableHeight = nv.utils.availableHeight(height, container, margin);
8499
8574
  }
@@ -8773,7 +8848,10 @@ nv.models.multiBarChart = function() {
8773
8848
 
8774
8849
  // options that require extra logic in the setter
8775
8850
  margin: {get: function(){return margin;}, set: function(_){
8776
- margin.top = _.top !== undefined ? _.top : margin.top;
8851
+ if (_.top !== undefined) {
8852
+ margin.top = _.top;
8853
+ marginTop = _.top;
8854
+ }
8777
8855
  margin.right = _.right !== undefined ? _.right : margin.right;
8778
8856
  margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
8779
8857
  margin.left = _.left !== undefined ? _.left : margin.left;
@@ -9186,6 +9264,7 @@ nv.models.multiBarHorizontalChart = function() {
9186
9264
  ;
9187
9265
 
9188
9266
  var margin = {top: 30, right: 20, bottom: 50, left: 60}
9267
+ , marginTop = null
9189
9268
  , width = null
9190
9269
  , height = null
9191
9270
  , color = nv.utils.defaultColor()
@@ -9329,7 +9408,7 @@ nv.models.multiBarHorizontalChart = function() {
9329
9408
  .datum(data)
9330
9409
  .call(legend);
9331
9410
 
9332
- if (legend.height() > margin.top) {
9411
+ if (!marginTop && legend.height() !== margin.top) {
9333
9412
  margin.top = legend.height();
9334
9413
  availableHeight = nv.utils.availableHeight(height, container, margin);
9335
9414
  }
@@ -9517,7 +9596,10 @@ nv.models.multiBarHorizontalChart = function() {
9517
9596
 
9518
9597
  // options that require extra logic in the setter
9519
9598
  margin: {get: function(){return margin;}, set: function(_){
9520
- margin.top = _.top !== undefined ? _.top : margin.top;
9599
+ if (_.top !== undefined) {
9600
+ margin.top = _.top;
9601
+ marginTop = _.top;
9602
+ }
9521
9603
  margin.right = _.right !== undefined ? _.right : margin.right;
9522
9604
  margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
9523
9605
  margin.left = _.left !== undefined ? _.left : margin.left;
@@ -9552,6 +9634,7 @@ nv.models.multiChart = function() {
9552
9634
  //------------------------------------------------------------
9553
9635
 
9554
9636
  var margin = {top: 30, right: 20, bottom: 50, left: 60},
9637
+ marginTop = null,
9555
9638
  color = nv.utils.defaultColor(),
9556
9639
  width = null,
9557
9640
  height = null,
@@ -9565,7 +9648,8 @@ nv.models.multiChart = function() {
9565
9648
  useVoronoi = true,
9566
9649
  interactiveLayer = nv.interactiveGuideline(),
9567
9650
  useInteractiveGuideline = false,
9568
- legendRightAxisHint = ' (right axis)'
9651
+ legendRightAxisHint = ' (right axis)',
9652
+ duration = 250
9569
9653
  ;
9570
9654
 
9571
9655
  //============================================================
@@ -9576,21 +9660,21 @@ nv.models.multiChart = function() {
9576
9660
  yScale1 = d3.scale.linear(),
9577
9661
  yScale2 = d3.scale.linear(),
9578
9662
 
9579
- lines1 = nv.models.line().yScale(yScale1),
9580
- lines2 = nv.models.line().yScale(yScale2),
9663
+ lines1 = nv.models.line().yScale(yScale1).duration(duration),
9664
+ lines2 = nv.models.line().yScale(yScale2).duration(duration),
9581
9665
 
9582
- scatters1 = nv.models.scatter().yScale(yScale1),
9583
- scatters2 = nv.models.scatter().yScale(yScale2),
9666
+ scatters1 = nv.models.scatter().yScale(yScale1).duration(duration),
9667
+ scatters2 = nv.models.scatter().yScale(yScale2).duration(duration),
9584
9668
 
9585
- bars1 = nv.models.multiBar().stacked(false).yScale(yScale1),
9586
- bars2 = nv.models.multiBar().stacked(false).yScale(yScale2),
9669
+ bars1 = nv.models.multiBar().stacked(false).yScale(yScale1).duration(duration),
9670
+ bars2 = nv.models.multiBar().stacked(false).yScale(yScale2).duration(duration),
9587
9671
 
9588
- stack1 = nv.models.stackedArea().yScale(yScale1),
9589
- stack2 = nv.models.stackedArea().yScale(yScale2),
9672
+ stack1 = nv.models.stackedArea().yScale(yScale1).duration(duration),
9673
+ stack2 = nv.models.stackedArea().yScale(yScale2).duration(duration),
9590
9674
 
9591
- xAxis = nv.models.axis().scale(x).orient('bottom').tickPadding(5),
9592
- yAxis1 = nv.models.axis().scale(yScale1).orient('left'),
9593
- yAxis2 = nv.models.axis().scale(yScale2).orient('right'),
9675
+ xAxis = nv.models.axis().scale(x).orient('bottom').tickPadding(5).duration(duration),
9676
+ yAxis1 = nv.models.axis().scale(yScale1).orient('left').duration(duration),
9677
+ yAxis2 = nv.models.axis().scale(yScale2).orient('right').duration(duration),
9594
9678
 
9595
9679
  legend = nv.models.legend().height(30),
9596
9680
  tooltip = nv.models.tooltip(),
@@ -9685,7 +9769,7 @@ nv.models.multiChart = function() {
9685
9769
  }))
9686
9770
  .call(legend);
9687
9771
 
9688
- if (legend.height() > margin.top) {
9772
+ if (!marginTop && legend.height() !== margin.top) {
9689
9773
  margin.top = legend.height();
9690
9774
  availableHeight = nv.utils.availableHeight(height, container, margin);
9691
9775
  }
@@ -10071,7 +10155,10 @@ nv.models.multiChart = function() {
10071
10155
 
10072
10156
  // options that require extra logic in the setter
10073
10157
  margin: {get: function(){return margin;}, set: function(_){
10074
- margin.top = _.top !== undefined ? _.top : margin.top;
10158
+ if (_.top !== undefined) {
10159
+ margin.top = _.top;
10160
+ marginTop = _.top;
10161
+ }
10075
10162
  margin.right = _.right !== undefined ? _.right : margin.right;
10076
10163
  margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
10077
10164
  margin.left = _.left !== undefined ? _.left : margin.left;
@@ -10123,6 +10210,13 @@ nv.models.multiChart = function() {
10123
10210
  scatters1.interactive(false);
10124
10211
  scatters2.interactive(false);
10125
10212
  }
10213
+ }},
10214
+
10215
+ duration: {get: function(){return duration;}, set: function(_) {
10216
+ duration = _;
10217
+ [lines1, lines2, stack1, stack2, scatters1, scatters2, xAxis, yAxis1, yAxis2].forEach(function(model){
10218
+ model.duration(duration);
10219
+ });
10126
10220
  }}
10127
10221
  });
10128
10222
 
@@ -10507,6 +10601,7 @@ nv.models.parallelCoordinates = function() {
10507
10601
  //Add missing value line at the bottom of the chart
10508
10602
  var missingValuesline, missingValueslineText;
10509
10603
  var step = x.range()[1] - x.range()[0];
10604
+ step = isNaN(step) ? x.range()[0] : step;
10510
10605
  if (!isNaN(step)) {
10511
10606
  var lineData = [0 + step / 2, availableHeight - 12, availableWidth - step / 2, availableHeight - 12];
10512
10607
  missingValuesline = wrap.select('.missingValuesline').selectAll('line').data([lineData]);
@@ -10863,10 +10958,11 @@ nv.models.parallelCoordinatesChart = function () {
10863
10958
  var dimensionTooltip = nv.models.tooltip();
10864
10959
 
10865
10960
  var margin = { top: 0, right: 0, bottom: 0, left: 0 }
10961
+ , marginTop = null
10866
10962
  , width = null
10867
- , height = null
10963
+ , height = null
10868
10964
  , showLegend = true
10869
- , color = nv.utils.defaultColor()
10965
+ , color = nv.utils.defaultColor()
10870
10966
  , state = nv.utils.state()
10871
10967
  , dimensionData = []
10872
10968
  , displayBrush = true
@@ -10996,7 +11092,7 @@ nv.models.parallelCoordinatesChart = function () {
10996
11092
  .datum(dimensionData.sort(function (a, b) { return a.originalPosition - b.originalPosition; }))
10997
11093
  .call(legend);
10998
11094
 
10999
- if (legend.height() > margin.top) {
11095
+ if (!marginTop && legend.height() !== margin.top) {
11000
11096
  margin.top = legend.height();
11001
11097
  availableHeight = nv.utils.availableHeight(height, container, margin);
11002
11098
  }
@@ -11129,7 +11225,10 @@ nv.models.parallelCoordinatesChart = function () {
11129
11225
  margin: {
11130
11226
  get: function () { return margin; },
11131
11227
  set: function (_) {
11132
- margin.top = _.top !== undefined ? _.top : margin.top;
11228
+ if (_.top !== undefined) {
11229
+ margin.top = _.top;
11230
+ marginTop = _.top;
11231
+ }
11133
11232
  margin.right = _.right !== undefined ? _.right : margin.right;
11134
11233
  margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
11135
11234
  margin.left = _.left !== undefined ? _.left : margin.left;
@@ -11595,6 +11694,7 @@ nv.models.pieChart = function() {
11595
11694
  var tooltip = nv.models.tooltip();
11596
11695
 
11597
11696
  var margin = {top: 30, right: 20, bottom: 20, left: 20}
11697
+ , marginTop = null
11598
11698
  , width = null
11599
11699
  , height = null
11600
11700
  , showTooltipPercent = false
@@ -11703,7 +11803,7 @@ nv.models.pieChart = function() {
11703
11803
  .datum(data)
11704
11804
  .call(legend);
11705
11805
 
11706
- if (legend.height() > margin.top) {
11806
+ if (!marginTop && legend.height() !== margin.top) {
11707
11807
  margin.top = legend.height();
11708
11808
  availableHeight = nv.utils.availableHeight(height, container, margin);
11709
11809
  }
@@ -11820,7 +11920,10 @@ nv.models.pieChart = function() {
11820
11920
  pie.duration(duration);
11821
11921
  }},
11822
11922
  margin: {get: function(){return margin;}, set: function(_){
11823
- margin.top = _.top !== undefined ? _.top : margin.top;
11923
+ if (_.top !== undefined) {
11924
+ margin.top = _.top;
11925
+ marginTop = _.top;
11926
+ }
11824
11927
  margin.right = _.right !== undefined ? _.right : margin.right;
11825
11928
  margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
11826
11929
  margin.left = _.left !== undefined ? _.left : margin.left;
@@ -11830,6 +11933,568 @@ nv.models.pieChart = function() {
11830
11933
  nv.utils.initOptions(chart);
11831
11934
  return chart;
11832
11935
  };
11936
+ nv.models.sankey = function() {
11937
+ 'use strict';
11938
+
11939
+ // Sources:
11940
+ // - https://bost.ocks.org/mike/sankey/
11941
+ // - https://github.com/soxofaan/d3-plugin-captain-sankey
11942
+
11943
+ //============================================================
11944
+ // Public Variables with Default Settings
11945
+ //------------------------------------------------------------
11946
+
11947
+ var sankey = {},
11948
+ nodeWidth = 24,
11949
+ nodePadding = 8,
11950
+ size = [1, 1],
11951
+ nodes = [],
11952
+ links = [],
11953
+ sinksRight = true;
11954
+
11955
+ var layout = function(iterations) {
11956
+ computeNodeLinks();
11957
+ computeNodeValues();
11958
+ computeNodeBreadths();
11959
+ computeNodeDepths(iterations);
11960
+ };
11961
+
11962
+ var relayout = function() {
11963
+ computeLinkDepths();
11964
+ };
11965
+
11966
+ // SVG path data generator, to be used as 'd' attribute on 'path' element selection.
11967
+ var link = function() {
11968
+ var curvature = .5;
11969
+
11970
+ function link(d) {
11971
+
11972
+ var x0 = d.source.x + d.source.dx,
11973
+ x1 = d.target.x,
11974
+ xi = d3.interpolateNumber(x0, x1),
11975
+ x2 = xi(curvature),
11976
+ x3 = xi(1 - curvature),
11977
+ y0 = d.source.y + d.sy + d.dy / 2,
11978
+ y1 = d.target.y + d.ty + d.dy / 2;
11979
+ var linkPath = 'M' + x0 + ',' + y0
11980
+ + 'C' + x2 + ',' + y0
11981
+ + ' ' + x3 + ',' + y1
11982
+ + ' ' + x1 + ',' + y1;
11983
+ return linkPath;
11984
+ }
11985
+
11986
+ link.curvature = function(_) {
11987
+ if (!arguments.length) return curvature;
11988
+ curvature = +_;
11989
+ return link;
11990
+ };
11991
+
11992
+ return link;
11993
+ };
11994
+
11995
+ // Y-position of the middle of a node.
11996
+ var center = function(node) {
11997
+ return node.y + node.dy / 2;
11998
+ };
11999
+
12000
+ //============================================================
12001
+ // Private Variables
12002
+ //------------------------------------------------------------
12003
+
12004
+ // Populate the sourceLinks and targetLinks for each node.
12005
+ // Also, if the source and target are not objects, assume they are indices.
12006
+ function computeNodeLinks() {
12007
+ nodes.forEach(function(node) {
12008
+ // Links that have this node as source.
12009
+ node.sourceLinks = [];
12010
+ // Links that have this node as target.
12011
+ node.targetLinks = [];
12012
+ });
12013
+ links.forEach(function(link) {
12014
+ var source = link.source,
12015
+ target = link.target;
12016
+ if (typeof source === 'number') source = link.source = nodes[link.source];
12017
+ if (typeof target === 'number') target = link.target = nodes[link.target];
12018
+ source.sourceLinks.push(link);
12019
+ target.targetLinks.push(link);
12020
+ });
12021
+ }
12022
+
12023
+ // Compute the value (size) of each node by summing the associated links.
12024
+ function computeNodeValues() {
12025
+ nodes.forEach(function(node) {
12026
+ node.value = Math.max(
12027
+ d3.sum(node.sourceLinks, value),
12028
+ d3.sum(node.targetLinks, value)
12029
+ );
12030
+ });
12031
+ }
12032
+
12033
+ // Iteratively assign the breadth (x-position) for each node.
12034
+ // Nodes are assigned the maximum breadth of incoming neighbors plus one;
12035
+ // nodes with no incoming links are assigned breadth zero, while
12036
+ // nodes with no outgoing links are assigned the maximum breadth.
12037
+ function computeNodeBreadths() {
12038
+ //
12039
+ var remainingNodes = nodes,
12040
+ nextNodes,
12041
+ x = 0;
12042
+
12043
+ // Work from left to right.
12044
+ // Keep updating the breath (x-position) of nodes that are target of recently updated nodes.
12045
+ //
12046
+ while (remainingNodes.length && x < nodes.length) {
12047
+ nextNodes = [];
12048
+ remainingNodes.forEach(function(node) {
12049
+ node.x = x;
12050
+ node.dx = nodeWidth;
12051
+ node.sourceLinks.forEach(function(link) {
12052
+ if (nextNodes.indexOf(link.target) < 0) {
12053
+ nextNodes.push(link.target);
12054
+ }
12055
+ });
12056
+ });
12057
+ remainingNodes = nextNodes;
12058
+ ++x;
12059
+ //
12060
+ }
12061
+
12062
+ // Optionally move pure sinks always to the right.
12063
+ if (sinksRight) {
12064
+ moveSinksRight(x);
12065
+ }
12066
+
12067
+ scaleNodeBreadths((size[0] - nodeWidth) / (x - 1));
12068
+ }
12069
+
12070
+ function moveSourcesRight() {
12071
+ nodes.forEach(function(node) {
12072
+ if (!node.targetLinks.length) {
12073
+ node.x = d3.min(node.sourceLinks, function(d) { return d.target.x; }) - 1;
12074
+ }
12075
+ });
12076
+ }
12077
+
12078
+ function moveSinksRight(x) {
12079
+ nodes.forEach(function(node) {
12080
+ if (!node.sourceLinks.length) {
12081
+ node.x = x - 1;
12082
+ }
12083
+ });
12084
+ }
12085
+
12086
+ function scaleNodeBreadths(kx) {
12087
+ nodes.forEach(function(node) {
12088
+ node.x *= kx;
12089
+ });
12090
+ }
12091
+
12092
+ // Compute the depth (y-position) for each node.
12093
+ function computeNodeDepths(iterations) {
12094
+ // Group nodes by breath.
12095
+ var nodesByBreadth = d3.nest()
12096
+ .key(function(d) { return d.x; })
12097
+ .sortKeys(d3.ascending)
12098
+ .entries(nodes)
12099
+ .map(function(d) { return d.values; });
12100
+
12101
+ //
12102
+ initializeNodeDepth();
12103
+ resolveCollisions();
12104
+ computeLinkDepths();
12105
+ for (var alpha = 1; iterations > 0; --iterations) {
12106
+ relaxRightToLeft(alpha *= .99);
12107
+ resolveCollisions();
12108
+ computeLinkDepths();
12109
+ relaxLeftToRight(alpha);
12110
+ resolveCollisions();
12111
+ computeLinkDepths();
12112
+ }
12113
+
12114
+ function initializeNodeDepth() {
12115
+ // Calculate vertical scaling factor.
12116
+ var ky = d3.min(nodesByBreadth, function(nodes) {
12117
+ return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value);
12118
+ });
12119
+
12120
+ nodesByBreadth.forEach(function(nodes) {
12121
+ nodes.forEach(function(node, i) {
12122
+ node.y = i;
12123
+ node.dy = node.value * ky;
12124
+ });
12125
+ });
12126
+
12127
+ links.forEach(function(link) {
12128
+ link.dy = link.value * ky;
12129
+ });
12130
+ }
12131
+
12132
+ function relaxLeftToRight(alpha) {
12133
+ nodesByBreadth.forEach(function(nodes, breadth) {
12134
+ nodes.forEach(function(node) {
12135
+ if (node.targetLinks.length) {
12136
+ // Value-weighted average of the y-position of source node centers linked to this node.
12137
+ var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value);
12138
+ node.y += (y - center(node)) * alpha;
12139
+ }
12140
+ });
12141
+ });
12142
+
12143
+ function weightedSource(link) {
12144
+ return (link.source.y + link.sy + link.dy / 2) * link.value;
12145
+ }
12146
+ }
12147
+
12148
+ function relaxRightToLeft(alpha) {
12149
+ nodesByBreadth.slice().reverse().forEach(function(nodes) {
12150
+ nodes.forEach(function(node) {
12151
+ if (node.sourceLinks.length) {
12152
+ // Value-weighted average of the y-positions of target nodes linked to this node.
12153
+ var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value);
12154
+ node.y += (y - center(node)) * alpha;
12155
+ }
12156
+ });
12157
+ });
12158
+
12159
+ function weightedTarget(link) {
12160
+ return (link.target.y + link.ty + link.dy / 2) * link.value;
12161
+ }
12162
+ }
12163
+
12164
+ function resolveCollisions() {
12165
+ nodesByBreadth.forEach(function(nodes) {
12166
+ var node,
12167
+ dy,
12168
+ y0 = 0,
12169
+ n = nodes.length,
12170
+ i;
12171
+
12172
+ // Push any overlapping nodes down.
12173
+ nodes.sort(ascendingDepth);
12174
+ for (i = 0; i < n; ++i) {
12175
+ node = nodes[i];
12176
+ dy = y0 - node.y;
12177
+ if (dy > 0) node.y += dy;
12178
+ y0 = node.y + node.dy + nodePadding;
12179
+ }
12180
+
12181
+ // If the bottommost node goes outside the bounds, push it back up.
12182
+ dy = y0 - nodePadding - size[1];
12183
+ if (dy > 0) {
12184
+ y0 = node.y -= dy;
12185
+
12186
+ // Push any overlapping nodes back up.
12187
+ for (i = n - 2; i >= 0; --i) {
12188
+ node = nodes[i];
12189
+ dy = node.y + node.dy + nodePadding - y0;
12190
+ if (dy > 0) node.y -= dy;
12191
+ y0 = node.y;
12192
+ }
12193
+ }
12194
+ });
12195
+ }
12196
+
12197
+ function ascendingDepth(a, b) {
12198
+ return a.y - b.y;
12199
+ }
12200
+ }
12201
+
12202
+ // Compute y-offset of the source endpoint (sy) and target endpoints (ty) of links,
12203
+ // relative to the source/target node's y-position.
12204
+ function computeLinkDepths() {
12205
+ nodes.forEach(function(node) {
12206
+ node.sourceLinks.sort(ascendingTargetDepth);
12207
+ node.targetLinks.sort(ascendingSourceDepth);
12208
+ });
12209
+ nodes.forEach(function(node) {
12210
+ var sy = 0, ty = 0;
12211
+ node.sourceLinks.forEach(function(link) {
12212
+ link.sy = sy;
12213
+ sy += link.dy;
12214
+ });
12215
+ node.targetLinks.forEach(function(link) {
12216
+ link.ty = ty;
12217
+ ty += link.dy;
12218
+ });
12219
+ });
12220
+
12221
+ function ascendingSourceDepth(a, b) {
12222
+ return a.source.y - b.source.y;
12223
+ }
12224
+
12225
+ function ascendingTargetDepth(a, b) {
12226
+ return a.target.y - b.target.y;
12227
+ }
12228
+ }
12229
+
12230
+ // Value property accessor.
12231
+ function value(x) {
12232
+ return x.value;
12233
+ }
12234
+
12235
+ sankey.options = nv.utils.optionsFunc.bind(sankey);
12236
+ sankey._options = Object.create({}, {
12237
+ nodeWidth: {get: function(){return nodeWidth;}, set: function(_){nodeWidth=+_;}},
12238
+ nodePadding: {get: function(){return nodePadding;}, set: function(_){nodePadding=_;}},
12239
+ nodes: {get: function(){return nodes;}, set: function(_){nodes=_;}},
12240
+ links: {get: function(){return links ;}, set: function(_){links=_;}},
12241
+ size: {get: function(){return size;}, set: function(_){size=_;}},
12242
+ sinksRight: {get: function(){return sinksRight;}, set: function(_){sinksRight=_;}},
12243
+
12244
+ layout: {get: function(){layout(32);}, set: function(_){layout(_);}},
12245
+ relayout: {get: function(){relayout();}, set: function(_){}},
12246
+ center: {get: function(){return center();}, set: function(_){
12247
+ if(typeof _ === 'function'){
12248
+ center=_;
12249
+ }
12250
+ }},
12251
+ link: {get: function(){return link();}, set: function(_){
12252
+ if(typeof _ === 'function'){
12253
+ link=_;
12254
+ }
12255
+ return link();
12256
+ }}
12257
+ });
12258
+
12259
+ nv.utils.initOptions(sankey);
12260
+
12261
+ return sankey;
12262
+ };
12263
+ nv.models.sankeyChart = function() {
12264
+ "use strict";
12265
+
12266
+ // Sources:
12267
+ // - https://bost.ocks.org/mike/sankey/
12268
+ // - https://github.com/soxofaan/d3-plugin-captain-sankey
12269
+
12270
+ //============================================================
12271
+ // Public Variables with Default Settings
12272
+ //------------------------------------------------------------
12273
+
12274
+ var margin = {top: 5, right: 0, bottom: 5, left: 0}
12275
+ , sankey = nv.models.sankey()
12276
+ , width = 600
12277
+ , height = 400
12278
+ , nodeWidth = 36
12279
+ , nodePadding = 40
12280
+ , units = 'units'
12281
+ , center = undefined
12282
+ ;
12283
+
12284
+ //============================================================
12285
+ // Private Variables
12286
+ //------------------------------------------------------------
12287
+
12288
+ var formatNumber = d3.format(',.0f'); // zero decimal places
12289
+ var format = function(d) {
12290
+ return formatNumber(d) + ' ' + units;
12291
+ };
12292
+ var color = d3.scale.category20();
12293
+ var linkTitle = function(d){
12294
+ return d.source.name + ' → ' + d.target.name + '\n' + format(d.value);
12295
+ };
12296
+ var nodeFillColor = function(d){
12297
+ return d.color = color(d.name.replace(/ .*/, ''));
12298
+ };
12299
+ var nodeStrokeColor = function(d){
12300
+ return d3.rgb(d.color).darker(2);
12301
+ };
12302
+ var nodeTitle = function(d){
12303
+ return d.name + '\n' + format(d.value);
12304
+ };
12305
+
12306
+ var showError = function(element, message) {
12307
+ element.append('text')
12308
+ .attr('x', 0)
12309
+ .attr('y', 0)
12310
+ .attr('class', 'nvd3-sankey-chart-error')
12311
+ .attr('text-anchor', 'middle')
12312
+ .text(message);
12313
+ };
12314
+
12315
+ function chart(selection) {
12316
+ selection.each(function(data) {
12317
+
12318
+ var testData = {
12319
+ nodes:
12320
+ [
12321
+ {'node': 1, 'name': 'Test 1'},
12322
+ {'node': 2, 'name': 'Test 2'},
12323
+ {'node': 3, 'name': 'Test 3'},
12324
+ {'node': 4, 'name': 'Test 4'},
12325
+ {'node': 5, 'name': 'Test 5'},
12326
+ {'node': 6, 'name': 'Test 6'}
12327
+ ],
12328
+ links:
12329
+ [
12330
+ {'source': 0, 'target': 1, 'value': 2295},
12331
+ {'source': 0, 'target': 5, 'value': 1199},
12332
+ {'source': 1, 'target': 2, 'value': 1119},
12333
+ {'source': 1, 'target': 5, 'value': 1176},
12334
+ {'source': 2, 'target': 3, 'value': 487},
12335
+ {'source': 2, 'target': 5, 'value': 632},
12336
+ {'source': 3, 'target': 4, 'value': 301},
12337
+ {'source': 3, 'target': 5, 'value': 186}
12338
+ ]
12339
+ };
12340
+
12341
+ // Error handling
12342
+ var isDataValid = false;
12343
+ var dataAvailable = false;
12344
+
12345
+ // check if data is valid
12346
+ if(
12347
+ (typeof data['nodes'] === 'object' && data['nodes'].length) >= 0 &&
12348
+ (typeof data['links'] === 'object' && data['links'].length) >= 0
12349
+ ){
12350
+ isDataValid = true;
12351
+ }
12352
+
12353
+ // check if data is available
12354
+ if(
12355
+ data['nodes'] && data['nodes'].length > 0 &&
12356
+ data['links'] && data['links'].length > 0
12357
+ ) {
12358
+ dataAvailable = true;
12359
+ }
12360
+
12361
+ // show error
12362
+ if(!isDataValid) {
12363
+ console.error('NVD3 Sankey chart error:', 'invalid data format for', data);
12364
+ console.info('Valid data format is: ', testData, JSON.stringify(testData));
12365
+ showError(selection, 'Error loading chart, data is invalid');
12366
+ return false;
12367
+ }
12368
+
12369
+ // TODO use nv.utils.noData
12370
+ if(!dataAvailable) {
12371
+ showError(selection, 'No data available');
12372
+ return false;
12373
+ }
12374
+
12375
+ // No errors, continue
12376
+
12377
+ // append the svg canvas to the page
12378
+ var svg = selection.append('svg')
12379
+ .attr('width', width)
12380
+ .attr('height', height)
12381
+ .append('g')
12382
+ .attr('class', 'nvd3 nv-wrap nv-sankeyChart');
12383
+
12384
+ // Set the sankey diagram properties
12385
+ sankey
12386
+ .nodeWidth(nodeWidth)
12387
+ .nodePadding(nodePadding)
12388
+ .size([width, height]);
12389
+
12390
+ var path = sankey.link();
12391
+
12392
+ sankey
12393
+ .nodes(data.nodes)
12394
+ .links(data.links)
12395
+ .layout(32)
12396
+ .center(center);
12397
+
12398
+ // add in the links
12399
+ var link = svg.append('g').selectAll('.link')
12400
+ .data(data.links)
12401
+ .enter().append('path')
12402
+ .attr('class', 'link')
12403
+ .attr('d', path)
12404
+ .style('stroke-width', function(d) { return Math.max(1, d.dy); })
12405
+ .sort(function(a,b) { return b.dy - a.dy; });
12406
+
12407
+ // add the link titles
12408
+ link.append('title')
12409
+ .text(linkTitle);
12410
+
12411
+ // add in the nodes
12412
+ var node = svg.append('g').selectAll('.node')
12413
+ .data(data.nodes)
12414
+ .enter().append('g')
12415
+ .attr('class', 'node')
12416
+ .attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; })
12417
+ .call(
12418
+ d3.behavior
12419
+ .drag()
12420
+ .origin(function(d) { return d; })
12421
+ .on('dragstart', function() {
12422
+ this.parentNode.appendChild(this);
12423
+ })
12424
+ .on('drag', dragmove)
12425
+ );
12426
+
12427
+ // add the rectangles for the nodes
12428
+ node.append('rect')
12429
+ .attr('height', function(d) { return d.dy; })
12430
+ .attr('width', sankey.nodeWidth())
12431
+ .style('fill', nodeFillColor)
12432
+ .style('stroke', nodeStrokeColor)
12433
+ .append('title')
12434
+ .text(nodeTitle);
12435
+
12436
+ // add in the title for the nodes
12437
+ node.append('text')
12438
+ .attr('x', -6)
12439
+ .attr('y', function(d) { return d.dy / 2; })
12440
+ .attr('dy', '.35em')
12441
+ .attr('text-anchor', 'end')
12442
+ .attr('transform', null)
12443
+ .text(function(d) { return d.name; })
12444
+ .filter(function(d) { return d.x < width / 2; })
12445
+ .attr('x', 6 + sankey.nodeWidth())
12446
+ .attr('text-anchor', 'start');
12447
+
12448
+ // the function for moving the nodes
12449
+ function dragmove(d) {
12450
+ d3.select(this).attr('transform',
12451
+ 'translate(' + d.x + ',' + (
12452
+ d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
12453
+ ) + ')');
12454
+ sankey.relayout();
12455
+ link.attr('d', path);
12456
+ }
12457
+ });
12458
+
12459
+ return chart;
12460
+ }
12461
+
12462
+ //============================================================
12463
+ // Expose Public Variables
12464
+ //------------------------------------------------------------
12465
+
12466
+ chart.options = nv.utils.optionsFunc.bind(chart);
12467
+
12468
+ chart._options = Object.create({}, {
12469
+ // simple options, just get/set the necessary values
12470
+ units: {get: function(){return units;}, set: function(_){units=_;}},
12471
+ width: {get: function(){return width;}, set: function(_){width=_;}},
12472
+ height: {get: function(){return height;}, set: function(_){height=_;}},
12473
+ format: {get: function(){return format;}, set: function(_){format=_;}},
12474
+ linkTitle: {get: function(){return linkTitle;}, set: function(_){linkTitle=_;}},
12475
+ nodeWidth: {get: function(){return nodeWidth;}, set: function(_){nodeWidth=_;}},
12476
+ nodePadding: {get: function(){return nodePadding;}, set: function(_){nodePadding=_;}},
12477
+ center: {get: function(){return center}, set: function(_){center=_}},
12478
+
12479
+ // options that require extra logic in the setter
12480
+ margin: {get: function(){return margin;}, set: function(_){
12481
+ margin.top = _.top !== undefined ? _.top : margin.top;
12482
+ margin.right = _.right !== undefined ? _.right : margin.right;
12483
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
12484
+ margin.left = _.left !== undefined ? _.left : margin.left;
12485
+ }},
12486
+ nodeStyle: {get: function(){return {};}, set: function(_){
12487
+ nodeFillColor = _.fillColor !== undefined ? _.fillColor : nodeFillColor;
12488
+ nodeStrokeColor = _.strokeColor !== undefined ? _.strokeColor : nodeStrokeColor;
12489
+ nodeTitle = _.title !== undefined ? _.title : nodeTitle;
12490
+ }}
12491
+
12492
+ });
12493
+
12494
+ nv.utils.initOptions(chart);
12495
+
12496
+ return chart;
12497
+ };
11833
12498
 
11834
12499
  nv.models.scatter = function() {
11835
12500
  "use strict";
@@ -11842,6 +12507,7 @@ nv.models.scatter = function() {
11842
12507
  , width = null
11843
12508
  , height = null
11844
12509
  , color = nv.utils.defaultColor() // chooses color
12510
+ , pointBorderColor = null
11845
12511
  , id = Math.floor(Math.random() * 100000) //Create semi-unique ID incase user doesn't select one
11846
12512
  , container = null
11847
12513
  , x = d3.scale.linear()
@@ -11873,7 +12539,7 @@ nv.models.scatter = function() {
11873
12539
  , useVoronoi = true
11874
12540
  , duration = 250
11875
12541
  , interactiveUpdateDelay = 300
11876
- , showLabels = false
12542
+ , showLabels = false
11877
12543
  ;
11878
12544
 
11879
12545
 
@@ -11882,32 +12548,37 @@ nv.models.scatter = function() {
11882
12548
  //------------------------------------------------------------
11883
12549
 
11884
12550
  var x0, y0, z0 // used to store previous scales
12551
+ , width0
12552
+ , height0
11885
12553
  , timeoutID
11886
12554
  , needsUpdate = false // Flag for when the points are visually updating, but the interactive layer is behind, to disable tooltips
11887
12555
  , renderWatch = nv.utils.renderWatch(dispatch, duration)
11888
12556
  , _sizeRange_def = [16, 256]
11889
- , _caches
12557
+ , _cache = {}
11890
12558
  ;
11891
12559
 
11892
12560
  function getCache(d) {
11893
- var cache, i;
11894
- cache = _caches = _caches || {};
11895
- i = d[0].series;
11896
- cache = cache[i] = cache[i] || {};
11897
- i = d[1];
11898
- cache = cache[i] = cache[i] || {};
11899
- return cache;
12561
+ var key, val;
12562
+ key = d[0].series + ':' + d[1];
12563
+ val = _cache[key] = _cache[key] || {};
12564
+ return val;
12565
+ }
12566
+
12567
+ function delCache(d) {
12568
+ var key, val;
12569
+ key = d[0].series + ':' + d[1];
12570
+ delete _cache[key];
11900
12571
  }
11901
12572
 
11902
12573
  function getDiffs(d) {
11903
- var i, key,
11904
- point = d[0],
12574
+ var i, key, val,
11905
12575
  cache = getCache(d),
11906
12576
  diffs = false;
11907
- for (i = 1; i < arguments.length; i ++) {
12577
+ for (i = 1; i < arguments.length; i += 2) {
11908
12578
  key = arguments[i];
11909
- if (cache[key] !== point[key] || !cache.hasOwnProperty(key)) {
11910
- cache[key] = point[key];
12579
+ val = arguments[i + 1](d[0], d[1]);
12580
+ if (cache[key] !== val || !cache.hasOwnProperty(key)) {
12581
+ cache[key] = val;
11911
12582
  diffs = true;
11912
12583
  }
11913
12584
  }
@@ -11931,7 +12602,7 @@ nv.models.scatter = function() {
11931
12602
  });
11932
12603
 
11933
12604
  // Setup Scales
11934
- var logScale = chart.yScale().name === d3.scale.log().name ? true : false;
12605
+ var logScale = chart.yScale().name === d3.scale.log().name ? true : false;
11935
12606
  // remap and flatten the data for use in calculating the scales' domains
11936
12607
  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
11937
12608
  d3.merge(
@@ -11993,6 +12664,11 @@ nv.models.scatter = function() {
11993
12664
 
11994
12665
  var scaleDiff = x(1) !== x0(1) || y(1) !== y0(1) || z(1) !== z0(1);
11995
12666
 
12667
+ width0 = width0 || width;
12668
+ height0 = height0 || height;
12669
+
12670
+ var sizeDiff = width0 !== width || height0 !== height;
12671
+
11996
12672
  // Setup containers and skeleton of chart
11997
12673
  var wrap = container.selectAll('g.nv-wrap.nv-scatter').data([data]);
11998
12674
  var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatter nv-chart-' + id);
@@ -12009,11 +12685,12 @@ nv.models.scatter = function() {
12009
12685
 
12010
12686
  defsEnter.append('clipPath')
12011
12687
  .attr('id', 'nv-edge-clip-' + id)
12012
- .append('rect');
12013
-
12688
+ .append('rect')
12689
+ .attr('transform', 'translate( -10, -10)');
12690
+
12014
12691
  wrap.select('#nv-edge-clip-' + id + ' rect')
12015
- .attr('width', availableWidth)
12016
- .attr('height', (availableHeight > 0) ? availableHeight : 0);
12692
+ .attr('width', availableWidth + 20)
12693
+ .attr('height', (availableHeight > 0) ? availableHeight + 20 : 0);
12017
12694
 
12018
12695
  g.attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
12019
12696
 
@@ -12111,7 +12788,7 @@ nv.models.scatter = function() {
12111
12788
  .attr('r', clipRadius);
12112
12789
  }
12113
12790
 
12114
- var mouseEventCallback = function(d, mDispatch) {
12791
+ var mouseEventCallback = function(el, d, mDispatch) {
12115
12792
  if (needsUpdate) return 0;
12116
12793
  var series = data[d.series];
12117
12794
  if (series === undefined) return;
@@ -12138,22 +12815,24 @@ nv.models.scatter = function() {
12138
12815
  pos: pos,
12139
12816
  relativePos: [x(getX(point, d.point)) + margin.left, y(getY(point, d.point)) + margin.top],
12140
12817
  seriesIndex: d.series,
12141
- pointIndex: d.point
12818
+ pointIndex: d.point,
12819
+ event: d3.event,
12820
+ element: el
12142
12821
  });
12143
12822
  };
12144
12823
 
12145
12824
  pointPaths
12146
12825
  .on('click', function(d) {
12147
- mouseEventCallback(d, dispatch.elementClick);
12826
+ mouseEventCallback(this, d, dispatch.elementClick);
12148
12827
  })
12149
12828
  .on('dblclick', function(d) {
12150
- mouseEventCallback(d, dispatch.elementDblClick);
12829
+ mouseEventCallback(this, d, dispatch.elementDblClick);
12151
12830
  })
12152
12831
  .on('mouseover', function(d) {
12153
- mouseEventCallback(d, dispatch.elementMouseover);
12832
+ mouseEventCallback(this, d, dispatch.elementMouseover);
12154
12833
  })
12155
12834
  .on('mouseout', function(d, i) {
12156
- mouseEventCallback(d, dispatch.elementMouseout);
12835
+ mouseEventCallback(this, d, dispatch.elementMouseout);
12157
12836
  });
12158
12837
 
12159
12838
  } else {
@@ -12242,7 +12921,7 @@ nv.models.scatter = function() {
12242
12921
  .classed('hover', function(d) { return d.hover });
12243
12922
  groups.watchTransition(renderWatch, 'scatter: groups')
12244
12923
  .style('fill', function(d,i) { return color(d, i) })
12245
- .style('stroke', function(d,i) { return color(d, i) })
12924
+ .style('stroke', function(d,i) { return d.pointBorderColor || pointBorderColor || color(d, i) })
12246
12925
  .style('stroke-opacity', 1)
12247
12926
  .style('fill-opacity', .5);
12248
12927
 
@@ -12271,30 +12950,32 @@ nv.models.scatter = function() {
12271
12950
  .type(function(d) { return getShape(d[0]); })
12272
12951
  .size(function(d) { return z(getSize(d[0],d[1])) })
12273
12952
  );
12274
- points.exit().remove();
12953
+ points.exit().each(delCache).remove();
12275
12954
  groups.exit().selectAll('path.nv-point')
12276
12955
  .watchTransition(renderWatch, 'scatter exit')
12277
12956
  .attr('transform', function(d) {
12278
12957
  return 'translate(' + nv.utils.NaNtoZero(x(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
12279
12958
  })
12280
12959
  .remove();
12281
- points.filter(function (d) { return scaleDiff || getDiffs(d, 'x', 'y'); })
12960
+ // Update points position only if "x" or "y" have changed
12961
+ points.filter(function (d) { return scaleDiff || sizeDiff || getDiffs(d, 'x', getX, 'y', getY); })
12282
12962
  .watchTransition(renderWatch, 'scatter points')
12283
12963
  .attr('transform', function(d) {
12284
12964
  //nv.log(d, getX(d[0],d[1]), x(getX(d[0],d[1])));
12285
12965
  return 'translate(' + nv.utils.NaNtoZero(x(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
12286
12966
  });
12287
- points.filter(function (d) { return scaleDiff || getDiffs(d, 'shape', 'size'); })
12967
+ // Update points appearance only if "shape" or "size" have changed
12968
+ points.filter(function (d) { return scaleDiff || sizeDiff || getDiffs(d, 'shape', getShape, 'size', getSize); })
12288
12969
  .watchTransition(renderWatch, 'scatter points')
12289
12970
  .attr('d',
12290
12971
  nv.utils.symbol()
12291
12972
  .type(function(d) { return getShape(d[0]); })
12292
12973
  .size(function(d) { return z(getSize(d[0],d[1])) })
12293
12974
  );
12294
-
12295
- // add label a label to scatter chart
12975
+
12976
+ // add label a label to scatter chart
12296
12977
  if(showLabels)
12297
- {
12978
+ {
12298
12979
  var titles = groups.selectAll('.nv-label')
12299
12980
  .data(function(d) {
12300
12981
  return d.values.map(
@@ -12307,7 +12988,7 @@ nv.models.scatter = function() {
12307
12988
  });
12308
12989
 
12309
12990
  titles.enter().append('text')
12310
- .style('fill', function (d,i) {
12991
+ .style('fill', function (d,i) {
12311
12992
  return d.color })
12312
12993
  .style('stroke-opacity', 0)
12313
12994
  .style('fill-opacity', 1)
@@ -12355,6 +13036,9 @@ nv.models.scatter = function() {
12355
13036
  y0 = y.copy();
12356
13037
  z0 = z.copy();
12357
13038
 
13039
+ width0 = width;
13040
+ height0 = height;
13041
+
12358
13042
  });
12359
13043
  renderWatch.renderEnd('scatter immediate');
12360
13044
  return chart;
@@ -12421,6 +13105,7 @@ nv.models.scatter = function() {
12421
13105
  id: {get: function(){return id;}, set: function(_){id=_;}},
12422
13106
  interactiveUpdateDelay: {get:function(){return interactiveUpdateDelay;}, set: function(_){interactiveUpdateDelay=_;}},
12423
13107
  showLabels: {get: function(){return showLabels;}, set: function(_){ showLabels = _;}},
13108
+ pointBorderColor: {get: function(){return pointBorderColor;}, set: function(_){pointBorderColor=_;}},
12424
13109
 
12425
13110
  // simple functor options
12426
13111
  x: {get: function(){return getX;}, set: function(_){getX = d3.functor(_);}},
@@ -12471,6 +13156,7 @@ nv.models.scatterChart = function() {
12471
13156
  ;
12472
13157
 
12473
13158
  var margin = {top: 30, right: 20, bottom: 50, left: 75}
13159
+ , marginTop = null
12474
13160
  , width = null
12475
13161
  , height = null
12476
13162
  , container = null
@@ -12620,7 +13306,7 @@ nv.models.scatterChart = function() {
12620
13306
  .datum(data)
12621
13307
  .call(legend);
12622
13308
 
12623
- if (legend.height() > margin.top) {
13309
+ if (!marginTop && legend.height() !== margin.top) {
12624
13310
  margin.top = legend.height();
12625
13311
  availableHeight = nv.utils.availableHeight(height, container, margin);
12626
13312
  }
@@ -12821,7 +13507,10 @@ nv.models.scatterChart = function() {
12821
13507
 
12822
13508
  // options that require extra logic in the setter
12823
13509
  margin: {get: function(){return margin;}, set: function(_){
12824
- margin.top = _.top !== undefined ? _.top : margin.top;
13510
+ if (_.top !== undefined) {
13511
+ margin.top = _.top;
13512
+ marginTop = _.top;
13513
+ }
12825
13514
  margin.right = _.right !== undefined ? _.right : margin.right;
12826
13515
  margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
12827
13516
  margin.left = _.left !== undefined ? _.left : margin.left;
@@ -13550,12 +14239,14 @@ nv.models.stackedAreaChart = function() {
13550
14239
  , focus = nv.models.focus(nv.models.stackedArea())
13551
14240
  ;
13552
14241
 
13553
- var margin = {top: 30, right: 25, bottom: 50, left: 60}
14242
+ var margin = {top: 10, right: 25, bottom: 50, left: 60}
14243
+ , marginTop = null
13554
14244
  , width = null
13555
14245
  , height = null
13556
14246
  , color = nv.utils.defaultColor()
13557
14247
  , showControls = true
13558
14248
  , showLegend = true
14249
+ , legendPosition = 'top'
13559
14250
  , showXAxis = true
13560
14251
  , showYAxis = true
13561
14252
  , rightAlignYAxis = false
@@ -13699,18 +14390,28 @@ nv.models.stackedAreaChart = function() {
13699
14390
  if (!showLegend) {
13700
14391
  g.select('.nv-legendWrap').selectAll('*').remove();
13701
14392
  } else {
13702
- var legendWidth = (showControls) ? availableWidth - controlWidth : availableWidth;
14393
+ var legendWidth = (showControls && legendPosition === 'top') ? availableWidth - controlWidth : availableWidth;
13703
14394
 
13704
14395
  legend.width(legendWidth);
13705
14396
  g.select('.nv-legendWrap').datum(data).call(legend);
13706
14397
 
13707
- if (legend.height() > margin.top) {
13708
- margin.top = legend.height();
13709
- availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
13710
- }
14398
+ if (legendPosition === 'bottom') {
14399
+ // constant from axis.js, plus some margin for better layout
14400
+ var xAxisHeight = (showXAxis ? 12 : 0) + 10;
14401
+ margin.bottom = Math.max(legend.height() + xAxisHeight, margin.bottom);
14402
+ availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
14403
+ var legendTop = availableHeight + xAxisHeight;
14404
+ g.select('.nv-legendWrap')
14405
+ .attr('transform', 'translate(0,' + legendTop +')');
14406
+ } else if (legendPosition === 'top') {
14407
+ if (!marginTop && margin.top != legend.height()) {
14408
+ margin.top = legend.height();
14409
+ availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
14410
+ }
13711
14411
 
13712
- g.select('.nv-legendWrap')
13713
- .attr('transform', 'translate(' + (availableWidth-legendWidth) + ',' + (-margin.top) +')');
14412
+ g.select('.nv-legendWrap')
14413
+ .attr('transform', 'translate(' + (availableWidth-legendWidth) + ',' + (-margin.top) +')');
14414
+ }
13714
14415
  }
13715
14416
 
13716
14417
  // Controls
@@ -13757,9 +14458,11 @@ nv.models.stackedAreaChart = function() {
13757
14458
  .datum(controlsData)
13758
14459
  .call(controls);
13759
14460
 
13760
- if (Math.max(controls.height(), legend.height()) > margin.top) {
13761
- margin.top = Math.max(controls.height(), legend.height());
13762
- availableHeight = nv.utils.availableHeight(height, container, margin);
14461
+ var requiredTop = Math.max(controls.height(), showLegend && (legendPosition === 'top') ? legend.height() : 0);
14462
+
14463
+ if ( margin.top != requiredTop ) {
14464
+ margin.top = requiredTop;
14465
+ availableHeight = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focus.height() : 0);
13763
14466
  }
13764
14467
 
13765
14468
  g.select('.nv-controlsWrap')
@@ -14117,6 +14820,7 @@ nv.models.stackedAreaChart = function() {
14117
14820
  width: {get: function(){return width;}, set: function(_){width=_;}},
14118
14821
  height: {get: function(){return height;}, set: function(_){height=_;}},
14119
14822
  showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
14823
+ legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}},
14120
14824
  showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
14121
14825
  showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
14122
14826
  defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
@@ -14132,7 +14836,10 @@ nv.models.stackedAreaChart = function() {
14132
14836
 
14133
14837
  // options that require extra logic in the setter
14134
14838
  margin: {get: function(){return margin;}, set: function(_){
14135
- margin.top = _.top !== undefined ? _.top : margin.top;
14839
+ if (_.top !== undefined) {
14840
+ margin.top = _.top;
14841
+ marginTop = _.top;
14842
+ }
14136
14843
  margin.right = _.right !== undefined ? _.right : margin.right;
14137
14844
  margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
14138
14845
  margin.left = _.left !== undefined ? _.left : margin.left;
@@ -14444,7 +15151,13 @@ nv.models.sunburst = function() {
14444
15151
  }
14445
15152
  })
14446
15153
  .style("stroke", "#FFF")
14447
- .on("click", zoomClick)
15154
+ .on("click", function(d,i){
15155
+ zoomClick(d);
15156
+ dispatch.elementClick({
15157
+ data: d,
15158
+ index: i
15159
+ })
15160
+ })
14448
15161
  .on('mouseover', function(d,i){
14449
15162
  d3.select(this).classed('hover', true).style('opacity', 0.8);
14450
15163
  dispatch.elementMouseover({
@@ -14707,5 +15420,6 @@ nv.models.sunburstChart = function() {
14707
15420
 
14708
15421
  };
14709
15422
 
14710
- nv.version = "1.8.4";
14711
- })();
15423
+ nv.version = "1.8.5";
15424
+ })();
15425
+ //# sourceMappingURL=nv.d3.js.map