novus-nvd3-rails 1.8.4 → 1.8.5

Sign up to get free protection for your applications and to get access to all the features.
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