novus-nvd3-rails 1.8.5 → 1.8.6

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: 27caa71fa31191352db665a99346297a44beb342
4
- data.tar.gz: 387b68dda653bac75183ce5054544d0472919f7b
3
+ metadata.gz: f5a3b600d4ce921a4c4ef5efac45c6e33915acde
4
+ data.tar.gz: 19c327dad4f61916d35c02602a5ddec5604eb92d
5
5
  SHA512:
6
- metadata.gz: ac1095ea207a2b638a845fc02ab24303c6f2a583e72455562a8c3d13a81b8ec730e1df403d9d5f21bbebf7f14b3a21e15332698ef9d6b044f8ad58359d03f496
7
- data.tar.gz: f8312d124b7850009d4454dce38e253f74af85eefb1035f151d6b30e68027896670b7e8cf580297171616a19619a30c23278efeceecf7d7fe098f9a014447577
6
+ metadata.gz: '07694780ab62c0a622da40d5b0ca3b41f76b6e4f577d87d841529733c871746cca41a7a0ce0a286761150870e93786baf94e950eb2d49b0c0772ae5fcd5a8aa3'
7
+ data.tar.gz: 8684fb0dba933fd725c3f3ec4512e8f54f883eef0c8dc59d4ecdf5b80cb8cce98408c30d195f71018722ec71ca7bcb3d69df21c74bc790cedffdf885b1ff5bce
@@ -1,7 +1,7 @@
1
1
  module Novus
2
2
  module Nvd3
3
3
  module Rails
4
- VERSION = "1.8.5"
4
+ VERSION = "1.8.6"
5
5
  end
6
6
  end
7
7
  end
@@ -1,4 +1,4 @@
1
- /* nvd3 version 1.8.5 (https://github.com/novus/nvd3) 2016-12-01 */
1
+ /* nvd3 version 1.8.6 (https://github.com/novus/nvd3) 2017-08-23 */
2
2
  (function(){
3
3
 
4
4
  // set up main nv object
@@ -221,9 +221,9 @@ nv.interactiveGuideline = function() {
221
221
  }
222
222
 
223
223
  function mouseHandler() {
224
- var d3mouse = d3.mouse(this);
225
- var mouseX = d3mouse[0];
226
- var mouseY = d3mouse[1];
224
+ var mouseX = d3.event.clientX - this.getBoundingClientRect().left;
225
+ var mouseY = d3.event.clientY - this.getBoundingClientRect().top;
226
+
227
227
  var subtractMargin = true;
228
228
  var mouseOutAnyReason = false;
229
229
  if (isMSIE) {
@@ -556,7 +556,10 @@ nv.models.tooltip = function() {
556
556
  ;
557
557
 
558
558
  // Format function for the tooltip values column.
559
- var valueFormatter = function(d, i) {
559
+ // d is value,
560
+ // i is series index
561
+ // p is point containing the value
562
+ var valueFormatter = function(d, i, p) {
560
563
  return d;
561
564
  };
562
565
 
@@ -569,9 +572,10 @@ nv.models.tooltip = function() {
569
572
  return d;
570
573
  };
571
574
 
572
- // By default, the tooltip model renders a beautiful table inside a DIV.
573
- // You can override this function if a custom tooltip is desired.
574
- var contentGenerator = function(d) {
575
+ // By default, the tooltip model renders a beautiful table inside a DIV, returned as HTML
576
+ // You can override this function if a custom tooltip is desired. For instance, you could directly manipulate
577
+ // the DOM by accessing elem and returning false.
578
+ var contentGenerator = function(d, elem) {
575
579
  if (d === null) {
576
580
  return '';
577
581
  }
@@ -612,7 +616,7 @@ nv.models.tooltip = function() {
612
616
 
613
617
  trowEnter.append("td")
614
618
  .classed("value",true)
615
- .html(function(p, i) { return valueFormatter(p.value, i) });
619
+ .html(function(p, i) { return valueFormatter(p.value, i, p) });
616
620
 
617
621
  trowEnter.filter(function (p,i) { return p.percent !== undefined }).append("td")
618
622
  .classed("percent", true)
@@ -802,9 +806,9 @@ nv.models.tooltip = function() {
802
806
  nv.dom.write(function () {
803
807
  initTooltip();
804
808
  // Generate data and set it into tooltip.
805
- // Bonus - If you override contentGenerator and return falsey you can use something like
806
- // React or Knockout to bind the data for your tooltip.
807
- var newContent = contentGenerator(data);
809
+ // Bonus - If you override contentGenerator and return false, you can use something like
810
+ // Angular, React or Knockout to bind the data for your tooltip directly to the DOM.
811
+ var newContent = contentGenerator(data, tooltip.node());
808
812
  if (newContent) {
809
813
  tooltip.node().innerHTML = newContent;
810
814
  }
@@ -1387,6 +1391,8 @@ Also use _d3options so we can track what we inherit for documentation and chaine
1387
1391
  */
1388
1392
  nv.utils.inheritOptionsD3 = function(target, d3_source, oplist) {
1389
1393
  target._d3options = oplist.concat(target._d3options || []);
1394
+ // Find unique d3 options (string) and update d3options
1395
+ target._d3options = (target._d3options || []).filter(function(item, i, ar){ return ar.indexOf(item) === i; });
1390
1396
  oplist.unshift(d3_source);
1391
1397
  oplist.unshift(target);
1392
1398
  d3.rebind.apply(this, oplist);
@@ -1584,6 +1590,27 @@ nv.utils.arrayEquals = function (array1, array2) {
1584
1590
  }
1585
1591
  return true;
1586
1592
  };
1593
+
1594
+ /*
1595
+ Check if a point within an arc
1596
+ */
1597
+ nv.utils.pointIsInArc = function(pt, ptData, d3Arc) {
1598
+ // Center of the arc is assumed to be 0,0
1599
+ // (pt.x, pt.y) are assumed to be relative to the center
1600
+ var r1 = d3Arc.innerRadius()(ptData), // Note: Using the innerRadius
1601
+ r2 = d3Arc.outerRadius()(ptData),
1602
+ theta1 = d3Arc.startAngle()(ptData),
1603
+ theta2 = d3Arc.endAngle()(ptData);
1604
+
1605
+ var dist = pt.x * pt.x + pt.y * pt.y,
1606
+ angle = Math.atan2(pt.x, -pt.y); // Note: different coordinate system.
1607
+
1608
+ angle = (angle < 0) ? (angle + Math.PI * 2) : angle;
1609
+
1610
+ return (r1 * r1 <= dist) && (dist <= r2 * r2) &&
1611
+ (theta1 <= angle) && (angle <= theta2);
1612
+ };
1613
+
1587
1614
  nv.models.axis = function() {
1588
1615
  "use strict";
1589
1616
 
@@ -1608,6 +1635,7 @@ nv.models.axis = function() {
1608
1635
  , fontSize = undefined
1609
1636
  , duration = 250
1610
1637
  , dispatch = d3.dispatch('renderEnd')
1638
+ , tickFormatMaxMin
1611
1639
  ;
1612
1640
  axis
1613
1641
  .scale(scale)
@@ -1692,7 +1720,8 @@ nv.models.axis = function() {
1692
1720
  .attr('y', -axis.tickPadding())
1693
1721
  .attr('text-anchor', 'middle')
1694
1722
  .text(function(d,i) {
1695
- var v = fmt(d);
1723
+ var formatter = tickFormatMaxMin || fmt;
1724
+ var v = formatter(d);
1696
1725
  return ('' + v).match('NaN') ? '' : v;
1697
1726
  });
1698
1727
  axisMaxMin.watchTransition(renderWatch, 'min-max top')
@@ -1709,7 +1738,7 @@ nv.models.axis = function() {
1709
1738
  var rotateLabelsRule = '';
1710
1739
  if (rotateLabels%360) {
1711
1740
  //Reset transform on ticks so textHeight can be calculated correctly
1712
- xTicks.attr('transform', '');
1741
+ xTicks.attr('transform', '');
1713
1742
  //Calculate the longest xTick width
1714
1743
  xTicks.each(function(d,i){
1715
1744
  var box = this.getBoundingClientRect();
@@ -1767,7 +1796,8 @@ nv.models.axis = function() {
1767
1796
  .attr('transform', rotateLabelsRule)
1768
1797
  .style('text-anchor', rotateLabels ? (rotateLabels%360 > 0 ? 'start' : 'end') : 'middle')
1769
1798
  .text(function(d,i) {
1770
- var v = fmt(d);
1799
+ var formatter = tickFormatMaxMin || fmt;
1800
+ var v = formatter(d);
1771
1801
  return ('' + v).match('NaN') ? '' : v;
1772
1802
  });
1773
1803
  axisMaxMin.watchTransition(renderWatch, 'min-max bottom')
@@ -1802,7 +1832,8 @@ nv.models.axis = function() {
1802
1832
  .attr('x', axis.tickPadding())
1803
1833
  .style('text-anchor', 'start')
1804
1834
  .text(function(d, i) {
1805
- var v = fmt(d);
1835
+ var formatter = tickFormatMaxMin || fmt;
1836
+ var v = formatter(d);
1806
1837
  return ('' + v).match('NaN') ? '' : v;
1807
1838
  });
1808
1839
  axisMaxMin.watchTransition(renderWatch, 'min-max right')
@@ -1846,7 +1877,8 @@ nv.models.axis = function() {
1846
1877
  .attr('x', -axis.tickPadding())
1847
1878
  .attr('text-anchor', 'end')
1848
1879
  .text(function(d,i) {
1849
- var v = fmt(d);
1880
+ var formatter = tickFormatMaxMin || fmt;
1881
+ var v = formatter(d);
1850
1882
  return ('' + v).match('NaN') ? '' : v;
1851
1883
  });
1852
1884
  axisMaxMin.watchTransition(renderWatch, 'min-max right')
@@ -1917,9 +1949,9 @@ nv.models.axis = function() {
1917
1949
  and the arithmetic trick below solves that.
1918
1950
  */
1919
1951
  return !parseFloat(Math.round(d * 100000) / 1000000) && (d !== undefined)
1920
- })
1952
+ })
1921
1953
  .classed('zero', true);
1922
-
1954
+
1923
1955
  //store old scales for use in transitions on update
1924
1956
  scale0 = scale.copy();
1925
1957
 
@@ -1950,6 +1982,7 @@ nv.models.axis = function() {
1950
1982
  ticks: {get: function(){return ticks;}, set: function(_){ticks=_;}},
1951
1983
  width: {get: function(){return width;}, set: function(_){width=_;}},
1952
1984
  fontSize: {get: function(){return fontSize;}, set: function(_){fontSize=_;}},
1985
+ tickFormatMaxMin: {get: function(){return tickFormatMaxMin;}, set: function(_){tickFormatMaxMin=_;}},
1953
1986
 
1954
1987
  // options that require extra logic in the setter
1955
1988
  margin: {get: function(){return margin;}, set: function(_){
@@ -3350,6 +3383,7 @@ nv.models.cumulativeLineChart = function() {
3350
3383
  var dx = d3.scale.linear()
3351
3384
  , index = {i: 0, x: 0}
3352
3385
  , renderWatch = nv.utils.renderWatch(dispatch, duration)
3386
+ , currentYDomain
3353
3387
  ;
3354
3388
 
3355
3389
  var stateGetter = function(data) {
@@ -3454,30 +3488,6 @@ nv.models.cumulativeLineChart = function() {
3454
3488
  x = lines.xScale();
3455
3489
  y = lines.yScale();
3456
3490
 
3457
- if (!rescaleY) {
3458
- var seriesDomains = data
3459
- .filter(function(series) { return !series.disabled })
3460
- .map(function(series,i) {
3461
- var initialDomain = d3.extent(series.values, lines.y());
3462
-
3463
- //account for series being disabled when losing 95% or more
3464
- if (initialDomain[0] < -.95) initialDomain[0] = -.95;
3465
-
3466
- return [
3467
- (initialDomain[0] - initialDomain[1]) / (1 + initialDomain[1]),
3468
- (initialDomain[1] - initialDomain[0]) / (1 + initialDomain[0])
3469
- ];
3470
- });
3471
-
3472
- var completeDomain = [
3473
- d3.min(seriesDomains, function(d) { return d[0] }),
3474
- d3.max(seriesDomains, function(d) { return d[1] })
3475
- ];
3476
-
3477
- lines.yDomain(completeDomain);
3478
- } else {
3479
- lines.yDomain(null);
3480
- }
3481
3491
 
3482
3492
  dx.domain([0, data[0].values.length - 1]) //Assumes all series have same length
3483
3493
  .range([0, availableWidth])
@@ -3485,6 +3495,18 @@ nv.models.cumulativeLineChart = function() {
3485
3495
 
3486
3496
  var data = indexify(index.i, data);
3487
3497
 
3498
+ // initialize the starting yDomain for the not-rescale case after indexify (to have calculated point.display)
3499
+ if (typeof(currentYDomain) === "undefined") {
3500
+ currentYDomain = getCurrentYDomain(data);
3501
+ }
3502
+
3503
+ if (!rescaleY) {
3504
+ lines.yDomain(currentYDomain);
3505
+ lines.clipEdge(true);
3506
+ } else {
3507
+ lines.yDomain(null);
3508
+ }
3509
+
3488
3510
  // Setup containers and skeleton of chart
3489
3511
  var interactivePointerEvents = (useInteractiveGuideline) ? "none" : "all";
3490
3512
  var wrap = container.selectAll('g.nv-wrap.nv-cumulativeLine').data([data]);
@@ -3547,7 +3569,7 @@ nv.models.cumulativeLineChart = function() {
3547
3569
  .attr("transform", "translate(" + availableWidth + ",0)");
3548
3570
  }
3549
3571
 
3550
- // Show error if series goes below 100%
3572
+ // Show error if index point value is 0 (division by zero avoided)
3551
3573
  var tempDisabled = data.filter(function(d) { return d.tempDisabled });
3552
3574
 
3553
3575
  wrap.select('.tempDisabled').remove(); //clean-up and prevent duplicates
@@ -3717,8 +3739,10 @@ nv.models.cumulativeLineChart = function() {
3717
3739
  controls.dispatch.on('legendClick', function(d,i) {
3718
3740
  d.disabled = !d.disabled;
3719
3741
  rescaleY = !d.disabled;
3720
-
3721
3742
  state.rescaleY = rescaleY;
3743
+ if (!rescaleY) {
3744
+ currentYDomain = getCurrentYDomain(data); // rescale is turned off, so set the currentYDomain
3745
+ }
3722
3746
  dispatch.stateChange(state);
3723
3747
  chart.update();
3724
3748
  });
@@ -3737,7 +3761,7 @@ nv.models.cumulativeLineChart = function() {
3737
3761
  data
3738
3762
  .filter(function(series, i) {
3739
3763
  series.seriesIndex = i;
3740
- return !series.disabled;
3764
+ return !(series.disabled || series.tempDisabled);
3741
3765
  })
3742
3766
  .forEach(function(series,i) {
3743
3767
  pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
@@ -3852,10 +3876,8 @@ nv.models.cumulativeLineChart = function() {
3852
3876
  }
3853
3877
  var v = indexifyYGetter(indexValue, idx);
3854
3878
 
3855
- //TODO: implement check below, and disable series if series loses 100% or more cause divide by 0 issue
3856
- if (v < -.95 && !noErrorCheck) {
3857
- //if a series loses more than 100%, calculations fail.. anything close can cause major distortion (but is mathematically correct till it hits 100)
3858
-
3879
+ // avoid divide by zero
3880
+ if (Math.abs(v) < 0.00001 && !noErrorCheck) {
3859
3881
  line.tempDisabled = true;
3860
3882
  return line;
3861
3883
  }
@@ -3863,7 +3885,7 @@ nv.models.cumulativeLineChart = function() {
3863
3885
  line.tempDisabled = false;
3864
3886
 
3865
3887
  line.values = line.values.map(function(point, pointIndex) {
3866
- point.display = {'y': (indexifyYGetter(point, pointIndex) - v) / (1 + v) };
3888
+ point.display = {'y': (indexifyYGetter(point, pointIndex) - v) / v };
3867
3889
  return point;
3868
3890
  });
3869
3891
 
@@ -3871,6 +3893,19 @@ nv.models.cumulativeLineChart = function() {
3871
3893
  })
3872
3894
  }
3873
3895
 
3896
+ function getCurrentYDomain(data) {
3897
+ var seriesDomains = data
3898
+ .filter(function(series) { return !(series.disabled || series.tempDisabled)})
3899
+ .map(function(series,i) {
3900
+ return d3.extent(series.values, function (d) { return d.display.y });
3901
+ });
3902
+
3903
+ return [
3904
+ d3.min(seriesDomains, function(d) { return d[0] }),
3905
+ d3.max(seriesDomains, function(d) { return d[1] })
3906
+ ];
3907
+ }
3908
+
3874
3909
  //============================================================
3875
3910
  // Expose Public Variables
3876
3911
  //------------------------------------------------------------
@@ -3892,7 +3927,6 @@ nv.models.cumulativeLineChart = function() {
3892
3927
  // simple options, just get/set the necessary values
3893
3928
  width: {get: function(){return width;}, set: function(_){width=_;}},
3894
3929
  height: {get: function(){return height;}, set: function(_){height=_;}},
3895
- rescaleY: {get: function(){return rescaleY;}, set: function(_){rescaleY=_;}},
3896
3930
  showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
3897
3931
  showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
3898
3932
  average: {get: function(){return average;}, set: function(_){average=_;}},
@@ -3903,6 +3937,10 @@ nv.models.cumulativeLineChart = function() {
3903
3937
  noErrorCheck: {get: function(){return noErrorCheck;}, set: function(_){noErrorCheck=_;}},
3904
3938
 
3905
3939
  // options that require extra logic in the setter
3940
+ rescaleY: {get: function(){return rescaleY;}, set: function(_){
3941
+ rescaleY = _;
3942
+ chart.state.rescaleY = _; // also update state
3943
+ }},
3906
3944
  margin: {get: function(){return margin;}, set: function(_){
3907
3945
  if (_.top !== undefined) {
3908
3946
  margin.top = _.top;
@@ -4958,6 +4996,8 @@ nv.models.forceDirectedGraph = function() {
4958
4996
  // These functions allow to add extra attributes to ndes and links
4959
4997
  ,nodeExtras = function(nodes) { /* Do nothing */ }
4960
4998
  ,linkExtras = function(links) { /* Do nothing */ }
4999
+ , getX=d3.functor(0.0)
5000
+ , getY=d3.functor(0.0)
4961
5001
  ;
4962
5002
 
4963
5003
 
@@ -6126,6 +6166,7 @@ nv.models.legend = function() {
6126
6166
  , padding = 32 //define how much space between legend items. - recommend 32 for furious version
6127
6167
  , rightAlign = true
6128
6168
  , updateState = true //If true, legend will update data.disabled and trigger a 'stateChange' dispatch.
6169
+ , enableDoubleClick = true //If true, legend will enable double click handling
6129
6170
  , radioButtonMode = false //If true, clicking legend items will cause it to behave like a radio button. (only one can be selected at a time)
6130
6171
  , expanded = false
6131
6172
  , dispatch = d3.dispatch('legendClick', 'legendDblclick', 'legendMouseover', 'legendMouseout', 'stateChange')
@@ -6258,22 +6299,26 @@ nv.models.legend = function() {
6258
6299
  }
6259
6300
  })
6260
6301
  .on('dblclick', function(d,i) {
6261
- if(vers == 'furious' && expanded) return;
6262
- dispatch.legendDblclick(d,i);
6263
- if (updateState) {
6264
- // make sure we re-get data in case it was modified
6265
- var data = series.data();
6266
- //the default behavior of NVD3 legends, when double clicking one,
6267
- // is to set all other series' to false, and make the double clicked series enabled.
6268
- data.forEach(function(series) {
6269
- series.disabled = true;
6270
- if(vers == 'furious') series.userDisabled = series.disabled;
6271
- });
6272
- d.disabled = false;
6273
- if(vers == 'furious') d.userDisabled = d.disabled;
6274
- dispatch.stateChange({
6275
- disabled: data.map(function(d) { return !!d.disabled })
6276
- });
6302
+ if (enableDoubleClick) {
6303
+ if (vers == 'furious' && expanded) return;
6304
+ dispatch.legendDblclick(d, i);
6305
+ if (updateState) {
6306
+ // make sure we re-get data in case it was modified
6307
+ var data = series.data();
6308
+ //the default behavior of NVD3 legends, when double clicking one,
6309
+ // is to set all other series' to false, and make the double clicked series enabled.
6310
+ data.forEach(function (series) {
6311
+ series.disabled = true;
6312
+ if (vers == 'furious') series.userDisabled = series.disabled;
6313
+ });
6314
+ d.disabled = false;
6315
+ if (vers == 'furious') d.userDisabled = d.disabled;
6316
+ dispatch.stateChange({
6317
+ disabled: data.map(function (d) {
6318
+ return !!d.disabled
6319
+ })
6320
+ });
6321
+ }
6277
6322
  }
6278
6323
  });
6279
6324
 
@@ -6472,6 +6517,7 @@ nv.models.legend = function() {
6472
6517
  rightAlign: {get: function(){return rightAlign;}, set: function(_){rightAlign=_;}},
6473
6518
  padding: {get: function(){return padding;}, set: function(_){padding=_;}},
6474
6519
  updateState: {get: function(){return updateState;}, set: function(_){updateState=_;}},
6520
+ enableDoubleClick: {get: function(){return enableDoubleClick;}, set: function(_){enableDoubleClick=_;}},
6475
6521
  radioButtonMode:{get: function(){return radioButtonMode;}, set: function(_){radioButtonMode=_;}},
6476
6522
  expanded: {get: function(){return expanded;}, set: function(_){expanded=_;}},
6477
6523
  vers: {get: function(){return vers;}, set: function(_){vers=_;}},
@@ -6754,7 +6800,7 @@ nv.models.lineChart = function() {
6754
6800
  , state = nv.utils.state()
6755
6801
  , defaultState = null
6756
6802
  , noData = null
6757
- , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
6803
+ , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd')
6758
6804
  , duration = 250
6759
6805
  ;
6760
6806
 
@@ -6883,8 +6929,10 @@ nv.models.lineChart = function() {
6883
6929
  .call(legend);
6884
6930
 
6885
6931
  if (legendPosition === 'bottom') {
6886
- wrap.select('.nv-legendWrap')
6887
- .attr('transform', 'translate(0,' + availableHeight +')');
6932
+ margin.bottom = xAxis.height() + legend.height();
6933
+ availableHeight = nv.utils.availableHeight(height, container, margin);
6934
+ g.select('.nv-legendWrap')
6935
+ .attr('transform', 'translate(0,' + (availableHeight + xAxis.height()) +')');
6888
6936
  } else if (legendPosition === 'top') {
6889
6937
  if (!marginTop && legend.height() !== margin.top) {
6890
6938
  margin.top = legend.height();
@@ -6973,18 +7021,18 @@ nv.models.lineChart = function() {
6973
7021
  //============================================================
6974
7022
  // Update Focus
6975
7023
  //============================================================
6976
- if(!focusEnable) {
7024
+ if (!focusEnable && focus.brush.extent() === null) {
6977
7025
  linesWrap.call(lines);
6978
7026
  updateXAxis();
6979
7027
  updateYAxis();
6980
7028
  } else {
6981
7029
  focus.width(availableWidth);
6982
7030
  g.select('.nv-focusWrap')
7031
+ .style('display', focusEnable ? 'initial' : 'none')
6983
7032
  .attr('transform', 'translate(0,' + ( availableHeight + margin.bottom + focus.margin().top) + ')')
6984
- .datum(data.filter(function(d) { return !d.disabled; }))
6985
7033
  .call(focus);
6986
7034
  var extent = focus.brush.empty() ? focus.xDomain() : focus.brush.extent();
6987
- if(extent !== null){
7035
+ if (extent !== null) {
6988
7036
  onBrush(extent);
6989
7037
  }
6990
7038
  }
@@ -7008,7 +7056,7 @@ nv.models.lineChart = function() {
7008
7056
  return !series.disabled && !series.disableTooltip;
7009
7057
  })
7010
7058
  .forEach(function(series,i) {
7011
- var extent = focusEnable ? (focus.brush.empty() ? focus.xScale().domain() : focus.brush.extent()) : x.domain();
7059
+ var extent = focus.brush.extent() !== null ? (focus.brush.empty() ? focus.xScale().domain() : focus.brush.extent()) : x.domain();
7012
7060
  var currentValues = series.values.filter(function(d,i) {
7013
7061
  // Checks if the x point is between the extents, handling case where extent[0] is greater than extent[1]
7014
7062
  // (e.g. x domain is manually set to reverse the x-axis)
@@ -8176,44 +8224,49 @@ nv.models.multiBar = function() {
8176
8224
  bars
8177
8225
  .style('fill', function(d,i,j){ return color(d, j, i); })
8178
8226
  .style('stroke', function(d,i,j){ return color(d, j, i); })
8179
- .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
8227
+ .on('mouseover', function(d,i,j) {
8180
8228
  d3.select(this).classed('hover', true);
8181
8229
  dispatch.elementMouseover({
8182
8230
  data: d,
8183
8231
  index: i,
8232
+ series: data[j],
8184
8233
  color: d3.select(this).style("fill")
8185
8234
  });
8186
8235
  })
8187
- .on('mouseout', function(d,i) {
8236
+ .on('mouseout', function(d,i,j) {
8188
8237
  d3.select(this).classed('hover', false);
8189
8238
  dispatch.elementMouseout({
8190
8239
  data: d,
8191
8240
  index: i,
8241
+ series: data[j],
8192
8242
  color: d3.select(this).style("fill")
8193
8243
  });
8194
8244
  })
8195
- .on('mousemove', function(d,i) {
8245
+ .on('mousemove', function(d,i,j) {
8196
8246
  dispatch.elementMousemove({
8197
8247
  data: d,
8198
8248
  index: i,
8249
+ series: data[j],
8199
8250
  color: d3.select(this).style("fill")
8200
8251
  });
8201
8252
  })
8202
- .on('click', function(d,i) {
8253
+ .on('click', function(d,i,j) {
8203
8254
  var element = this;
8204
8255
  dispatch.elementClick({
8205
8256
  data: d,
8206
8257
  index: i,
8258
+ series: data[j],
8207
8259
  color: d3.select(this).style("fill"),
8208
8260
  event: d3.event,
8209
8261
  element: element
8210
8262
  });
8211
8263
  d3.event.stopPropagation();
8212
8264
  })
8213
- .on('dblclick', function(d,i) {
8265
+ .on('dblclick', function(d,i,j) {
8214
8266
  dispatch.elementDblClick({
8215
8267
  data: d,
8216
8268
  index: i,
8269
+ series: data[j],
8217
8270
  color: d3.select(this).style("fill")
8218
8271
  });
8219
8272
  d3.event.stopPropagation();
@@ -8397,6 +8450,7 @@ nv.models.multiBarChart = function() {
8397
8450
  , showControls = true
8398
8451
  , controlLabels = {}
8399
8452
  , showLegend = true
8453
+ , legendPosition = null
8400
8454
  , showXAxis = true
8401
8455
  , showYAxis = true
8402
8456
  , rightAlignYAxis = false
@@ -8562,19 +8616,32 @@ nv.models.multiBarChart = function() {
8562
8616
  if (!showLegend) {
8563
8617
  g.select('.nv-legendWrap').selectAll('*').remove();
8564
8618
  } else {
8565
- legend.width(availableWidth - controlWidth());
8619
+ if (legendPosition === 'bottom') {
8620
+ legend.width(availableWidth - margin.right);
8566
8621
 
8567
- g.select('.nv-legendWrap')
8568
- .datum(data)
8569
- .call(legend);
8622
+ g.select('.nv-legendWrap')
8623
+ .datum(data)
8624
+ .call(legend);
8570
8625
 
8571
- if (!marginTop && legend.height() !== margin.top) {
8572
- margin.top = legend.height();
8573
- availableHeight = nv.utils.availableHeight(height, container, margin);
8574
- }
8626
+ margin.bottom = xAxis.height() + legend.height();
8627
+ availableHeight = nv.utils.availableHeight(height, container, margin);
8628
+ g.select('.nv-legendWrap')
8629
+ .attr('transform', 'translate(0,' + (availableHeight + xAxis.height()) +')');
8630
+ } else {
8631
+ legend.width(availableWidth - controlWidth());
8575
8632
 
8576
- g.select('.nv-legendWrap')
8577
- .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
8633
+ g.select('.nv-legendWrap')
8634
+ .datum(data)
8635
+ .call(legend);
8636
+
8637
+ if (!marginTop && legend.height() !== margin.top) {
8638
+ margin.top = legend.height();
8639
+ availableHeight = nv.utils.availableHeight(height, container, margin);
8640
+ }
8641
+
8642
+ g.select('.nv-legendWrap')
8643
+ .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
8644
+ }
8578
8645
  }
8579
8646
 
8580
8647
  // Controls
@@ -8835,6 +8902,7 @@ nv.models.multiBarChart = function() {
8835
8902
  width: {get: function(){return width;}, set: function(_){width=_;}},
8836
8903
  height: {get: function(){return height;}, set: function(_){height=_;}},
8837
8904
  showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
8905
+ legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}},
8838
8906
  showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
8839
8907
  controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
8840
8908
  showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
@@ -9095,13 +9163,13 @@ nv.models.multiBarHorizontal = function() {
9095
9163
  var xerr = getYerr(d,i)
9096
9164
  , mid = 0.8 * x.rangeBand() / ((stacked ? 1 : data.length) * 2);
9097
9165
  xerr = xerr.length ? xerr : [-Math.abs(xerr), Math.abs(xerr)];
9098
- xerr = xerr.map(function(e) { return y(e) - y(0); });
9166
+ xerr = xerr.map(function(e) { return y(e + ((getY(d,i) < 0) ? 0 : getY(d,i))) - y(0); });
9099
9167
  var a = [[xerr[0],-mid], [xerr[0],mid], [xerr[0],0], [xerr[1],0], [xerr[1],-mid], [xerr[1],mid]];
9100
9168
  return a.map(function (path) { return path.join(',') }).join(' ');
9101
9169
  })
9102
9170
  .attr('transform', function(d,i) {
9103
9171
  var mid = x.rangeBand() / ((stacked ? 1 : data.length) * 2);
9104
- return 'translate(' + (getY(d,i) < 0 ? 0 : y(getY(d,i)) - y(0)) + ', ' + mid + ')'
9172
+ return 'translate(0, ' + mid + ')';
9105
9173
  });
9106
9174
  }
9107
9175
 
@@ -9269,8 +9337,10 @@ nv.models.multiBarHorizontalChart = function() {
9269
9337
  , height = null
9270
9338
  , color = nv.utils.defaultColor()
9271
9339
  , showControls = true
9340
+ , controlsPosition = 'top'
9272
9341
  , controlLabels = {}
9273
9342
  , showLegend = true
9343
+ , legendPosition = 'top'
9274
9344
  , showXAxis = true
9275
9345
  , showYAxis = true
9276
9346
  , stacked = false
@@ -9407,14 +9477,21 @@ nv.models.multiBarHorizontalChart = function() {
9407
9477
  g.select('.nv-legendWrap')
9408
9478
  .datum(data)
9409
9479
  .call(legend);
9480
+ if (legendPosition === 'bottom') {
9481
+ margin.bottom = xAxis.height() + legend.height();
9482
+ availableHeight = nv.utils.availableHeight(height, container, margin);
9483
+ g.select('.nv-legendWrap')
9484
+ .attr('transform', 'translate(' + controlWidth() + ',' + (availableHeight + xAxis.height()) +')');
9485
+ } else if (legendPosition === 'top') {
9410
9486
 
9411
- if (!marginTop && legend.height() !== margin.top) {
9412
- margin.top = legend.height();
9413
- availableHeight = nv.utils.availableHeight(height, container, margin);
9414
- }
9487
+ if (!marginTop && legend.height() !== margin.top) {
9488
+ margin.top = legend.height();
9489
+ availableHeight = nv.utils.availableHeight(height, container, margin);
9490
+ }
9415
9491
 
9416
- g.select('.nv-legendWrap')
9417
- .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
9492
+ g.select('.nv-legendWrap')
9493
+ .attr('transform', 'translate(' + controlWidth() + ',' + (-margin.top) +')');
9494
+ }
9418
9495
  }
9419
9496
 
9420
9497
  // Controls
@@ -9427,10 +9504,21 @@ nv.models.multiBarHorizontalChart = function() {
9427
9504
  ];
9428
9505
 
9429
9506
  controls.width(controlWidth()).color(['#444', '#444', '#444']);
9430
- g.select('.nv-controlsWrap')
9431
- .datum(controlsData)
9432
- .attr('transform', 'translate(0,' + (-margin.top) +')')
9433
- .call(controls);
9507
+
9508
+ if (controlsPosition === 'bottom') {
9509
+ margin.bottom = xAxis.height() + legend.height();
9510
+ availableHeight = nv.utils.availableHeight(height, container, margin);
9511
+ g.select('.nv-controlsWrap')
9512
+ .datum(controlsData)
9513
+ .attr('transform', 'translate(0,' + (availableHeight + xAxis.height()) +')')
9514
+ .call(controls);
9515
+
9516
+ } else if (controlsPosition === 'top') {
9517
+ g.select('.nv-controlsWrap')
9518
+ .datum(controlsData)
9519
+ .attr('transform', 'translate(0,' + (-margin.top) +')')
9520
+ .call(controls);
9521
+ }
9434
9522
  }
9435
9523
 
9436
9524
  wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
@@ -9587,6 +9675,8 @@ nv.models.multiBarHorizontalChart = function() {
9587
9675
  width: {get: function(){return width;}, set: function(_){width=_;}},
9588
9676
  height: {get: function(){return height;}, set: function(_){height=_;}},
9589
9677
  showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
9678
+ legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}},
9679
+ controlsPosition: {get: function(){return controlsPosition;}, set: function(_){controlsPosition=_;}},
9590
9680
  showControls: {get: function(){return showControls;}, set: function(_){showControls=_;}},
9591
9681
  controlLabels: {get: function(){return controlLabels;}, set: function(_){controlLabels=_;}},
9592
9682
  showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
@@ -9834,17 +9924,36 @@ nv.models.multiChart = function() {
9834
9924
  var stack2Wrap = g.select('.stack2Wrap')
9835
9925
  .datum(dataStack2.filter(function(d){return !d.disabled}));
9836
9926
 
9837
- var extraValue1 = dataStack1.length ? dataStack1.map(function(a){return a.values}).reduce(function(a,b){
9838
- return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
9839
- }).concat([{x:0, y:0}]) : [];
9840
- var extraValue2 = dataStack2.length ? dataStack2.map(function(a){return a.values}).reduce(function(a,b){
9841
- return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
9842
- }).concat([{x:0, y:0}]) : [];
9843
-
9844
- yScale1 .domain(yDomain1 || d3.extent(d3.merge(series1).concat(extraValue1), function(d) { return d.y } ))
9927
+ var extraValue1BarStacked = [];
9928
+ if (bars1.stacked() && dataBars1.length) {
9929
+ var extraValue1BarStacked = dataBars1.filter(function(d){return !d.disabled}).map(function(a){return a.values});
9930
+
9931
+ if (extraValue1BarStacked.length > 0)
9932
+ extraValue1BarStacked = extraValue1BarStacked.reduce(function(a,b){
9933
+ return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
9934
+ });
9935
+ }
9936
+ if (dataBars1.length) {
9937
+ extraValue1BarStacked.push({x:0, y:0});
9938
+ }
9939
+
9940
+ var extraValue2BarStacked = [];
9941
+ if (bars2.stacked() && dataBars2.length) {
9942
+ var extraValue2BarStacked = dataBars2.filter(function(d){return !d.disabled}).map(function(a){return a.values});
9943
+
9944
+ if (extraValue2BarStacked.length > 0)
9945
+ extraValue2BarStacked = extraValue2BarStacked.reduce(function(a,b){
9946
+ return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
9947
+ });
9948
+ }
9949
+ if (dataBars2.length) {
9950
+ extraValue2BarStacked.push({x:0, y:0});
9951
+ }
9952
+
9953
+ yScale1 .domain(yDomain1 || d3.extent(d3.merge(series1).concat(extraValue1BarStacked), function(d) { return d.y } ))
9845
9954
  .range([0, availableHeight]);
9846
9955
 
9847
- yScale2 .domain(yDomain2 || d3.extent(d3.merge(series2).concat(extraValue2), function(d) { return d.y } ))
9956
+ yScale2 .domain(yDomain2 || d3.extent(d3.merge(series2).concat(extraValue2BarStacked), function(d) { return d.y } ))
9848
9957
  .range([0, availableHeight]);
9849
9958
 
9850
9959
  lines1.yDomain(yScale1.domain());
@@ -9920,7 +10029,7 @@ nv.models.multiChart = function() {
9920
10029
  //------------------------------------------------------------
9921
10030
 
9922
10031
  function mouseover_line(evt) {
9923
- var yaxis = data[evt.seriesIndex].yAxis === 2 ? yAxis2 : yAxis1;
10032
+ var yaxis = evt.series.yAxis === 2 ? yAxis2 : yAxis1;
9924
10033
  evt.value = evt.point.x;
9925
10034
  evt.series = {
9926
10035
  value: evt.point.y,
@@ -9940,7 +10049,7 @@ nv.models.multiChart = function() {
9940
10049
  }
9941
10050
 
9942
10051
  function mouseover_scatter(evt) {
9943
- var yaxis = data[evt.seriesIndex].yAxis === 2 ? yAxis2 : yAxis1;
10052
+ var yaxis = evt.series.yAxis === 2 ? yAxis2 : yAxis1;
9944
10053
  evt.value = evt.point.x;
9945
10054
  evt.series = {
9946
10055
  value: evt.point.y,
@@ -9960,7 +10069,7 @@ nv.models.multiChart = function() {
9960
10069
  }
9961
10070
 
9962
10071
  function mouseover_stack(evt) {
9963
- var yaxis = data[evt.seriesIndex].yAxis === 2 ? yAxis2 : yAxis1;
10072
+ var yaxis = evt.series.yAxis === 2 ? yAxis2 : yAxis1;
9964
10073
  evt.point['x'] = stack1.x()(evt.point);
9965
10074
  evt.point['y'] = stack1.y()(evt.point);
9966
10075
  tooltip
@@ -9976,7 +10085,7 @@ nv.models.multiChart = function() {
9976
10085
  }
9977
10086
 
9978
10087
  function mouseover_bar(evt) {
9979
- var yaxis = data[evt.data.series].yAxis === 2 ? yAxis2 : yAxis1;
10088
+ var yaxis = evt.series.yAxis === 2 ? yAxis2 : yAxis1;
9980
10089
 
9981
10090
  evt.value = bars1.x()(evt.data);
9982
10091
  evt['series'] = {
@@ -11266,6 +11375,7 @@ nv.models.pie = function() {
11266
11375
  , labelsOutside = false
11267
11376
  , labelType = "key"
11268
11377
  , labelThreshold = .02 //if slice percentage is under this, don't show label
11378
+ , hideOverlapLabels = false //Hide labels that don't fit in slice
11269
11379
  , donut = false
11270
11380
  , title = false
11271
11381
  , growOnHover = true
@@ -11302,7 +11412,7 @@ nv.models.pie = function() {
11302
11412
 
11303
11413
  container = d3.select(this)
11304
11414
  if (arcsRadius.length === 0) {
11305
- var outer = radius - radius / 5;
11415
+ var outer = radius - radius / 10;
11306
11416
  var inner = donutRatio * radius;
11307
11417
  for (var i = 0; i < data[0].length; i++) {
11308
11418
  arcsRadiusOuter.push(outer);
@@ -11310,9 +11420,9 @@ nv.models.pie = function() {
11310
11420
  }
11311
11421
  } else {
11312
11422
  if(growOnHover){
11313
- arcsRadiusOuter = arcsRadius.map(function (d) { return (d.outer - d.outer / 5) * radius; });
11314
- arcsRadiusInner = arcsRadius.map(function (d) { return (d.inner - d.inner / 5) * radius; });
11315
- donutRatio = d3.min(arcsRadius.map(function (d) { return (d.inner - d.inner / 5); }));
11423
+ arcsRadiusOuter = arcsRadius.map(function (d) { return (d.outer - d.outer / 10) * radius; });
11424
+ arcsRadiusInner = arcsRadius.map(function (d) { return (d.inner - d.inner / 10) * radius; });
11425
+ donutRatio = d3.min(arcsRadius.map(function (d) { return (d.inner - d.inner / 10); }));
11316
11426
  } else {
11317
11427
  arcsRadiusOuter = arcsRadius.map(function (d) { return d.outer * radius; });
11318
11428
  arcsRadiusInner = arcsRadius.map(function (d) { return d.inner * radius; });
@@ -11587,6 +11697,44 @@ nv.models.pie = function() {
11587
11697
  return label;
11588
11698
  })
11589
11699
  ;
11700
+
11701
+ if (hideOverlapLabels) {
11702
+ pieLabels
11703
+ .each(function (d, i) {
11704
+ if (!this.getBBox) return;
11705
+ var bb = this.getBBox(),
11706
+ center = labelsArc[i].centroid(d);
11707
+ var topLeft = {
11708
+ x : center[0] + bb.x,
11709
+ y : center[1] + bb.y
11710
+ };
11711
+
11712
+ var topRight = {
11713
+ x : topLeft.x + bb.width,
11714
+ y : topLeft.y
11715
+ };
11716
+
11717
+ var bottomLeft = {
11718
+ x : topLeft.x,
11719
+ y : topLeft.y + bb.height
11720
+ };
11721
+
11722
+ var bottomRight = {
11723
+ x : topLeft.x + bb.width,
11724
+ y : topLeft.y + bb.height
11725
+ };
11726
+
11727
+ d.visible = nv.utils.pointIsInArc(topLeft, d, arc) &&
11728
+ nv.utils.pointIsInArc(topRight, d, arc) &&
11729
+ nv.utils.pointIsInArc(bottomLeft, d, arc) &&
11730
+ nv.utils.pointIsInArc(bottomRight, d, arc);
11731
+ })
11732
+ .style('display', function (d) {
11733
+ return d.visible ? null : 'none';
11734
+ })
11735
+ ;
11736
+ }
11737
+
11590
11738
  }
11591
11739
 
11592
11740
 
@@ -11628,6 +11776,7 @@ nv.models.pie = function() {
11628
11776
  title: {get: function(){return title;}, set: function(_){title=_;}},
11629
11777
  titleOffset: {get: function(){return titleOffset;}, set: function(_){titleOffset=_;}},
11630
11778
  labelThreshold: {get: function(){return labelThreshold;}, set: function(_){labelThreshold=_;}},
11779
+ hideOverlapLabels: {get: function(){return hideOverlapLabels;}, set: function(_){hideOverlapLabels=_;}},
11631
11780
  valueFormat: {get: function(){return valueFormat;}, set: function(_){valueFormat=_;}},
11632
11781
  x: {get: function(){return getX;}, set: function(_){getX=_;}},
11633
11782
  id: {get: function(){return id;}, set: function(_){id=_;}},
@@ -11823,6 +11972,16 @@ nv.models.pieChart = function() {
11823
11972
  .datum(data)
11824
11973
  .call(legend)
11825
11974
  .attr('transform', 'translate(' + (availableWidth) +',0)');
11975
+ } else if (legendPosition === "bottom") {
11976
+ legend.width( availableWidth ).key(pie.x());
11977
+ wrap.select('.nv-legendWrap')
11978
+ .datum(data)
11979
+ .call(legend);
11980
+
11981
+ margin.bottom = legend.height();
11982
+ availableHeight = nv.utils.availableHeight(height, container, margin);
11983
+ wrap.select('.nv-legendWrap')
11984
+ .attr('transform', 'translate(0,' + availableHeight +')');
11826
11985
  }
11827
11986
  }
11828
11987
  wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
@@ -12548,6 +12707,7 @@ nv.models.scatter = function() {
12548
12707
  //------------------------------------------------------------
12549
12708
 
12550
12709
  var x0, y0, z0 // used to store previous scales
12710
+ , xDom, yDom // used to store previous domains
12551
12711
  , width0
12552
12712
  , height0
12553
12713
  , timeoutID
@@ -12557,6 +12717,31 @@ nv.models.scatter = function() {
12557
12717
  , _cache = {}
12558
12718
  ;
12559
12719
 
12720
+ //============================================================
12721
+ // Diff and Cache Utilities
12722
+ //------------------------------------------------------------
12723
+ // getDiffs is used to filter unchanged points from the update
12724
+ // selection. It implicitly updates it's cache when called and
12725
+ // therefor the diff is based upon the previous invocation NOT
12726
+ // the previous update.
12727
+ //
12728
+ // getDiffs takes a point as its first argument followed by n
12729
+ // key getter pairs (d, [key, get... key, get]) this approach
12730
+ // was chosen for efficiency. (The filter will call it a LOT).
12731
+ //
12732
+ // It is important to call delCache on point exit to prevent a
12733
+ // memory leak. It is also needed to prevent invalid caches if
12734
+ // a new point uses the same series and point id key.
12735
+ //
12736
+ // Argument Performance Concerns:
12737
+ // - Object property lists for key getter pairs would be very
12738
+ // expensive (points * objects for the GC every update).
12739
+ // - ES6 function names for implicit keys would be nice but
12740
+ // they are not guaranteed to be unique.
12741
+ // - function.toString to obtain implicit keys is possible
12742
+ // but long object keys are not free (internal hash).
12743
+ // - Explicit key without objects are the most efficient.
12744
+
12560
12745
  function getCache(d) {
12561
12746
  var key, val;
12562
12747
  key = d[0].series + ':' + d[1];
@@ -12602,7 +12787,7 @@ nv.models.scatter = function() {
12602
12787
  });
12603
12788
 
12604
12789
  // Setup Scales
12605
- var logScale = chart.yScale().name === d3.scale.log().name ? true : false;
12790
+ var logScale = (typeof(chart.yScale().base) === "function"); // Only log scale has a method "base()"
12606
12791
  // remap and flatten the data for use in calculating the scales' domains
12607
12792
  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
12608
12793
  d3.merge(
@@ -12669,6 +12854,16 @@ nv.models.scatter = function() {
12669
12854
 
12670
12855
  var sizeDiff = width0 !== width || height0 !== height;
12671
12856
 
12857
+ // Domain Diffs
12858
+
12859
+ xDom = xDom || [];
12860
+ var domainDiff = xDom[0] !== x.domain()[0] || xDom[1] !== x.domain()[1];
12861
+ xDom = x.domain();
12862
+
12863
+ yDom = yDom || [];
12864
+ domainDiff = domainDiff || yDom[0] !== y.domain()[0] || yDom[1] !== y.domain()[1];
12865
+ yDom = y.domain();
12866
+
12672
12867
  // Setup containers and skeleton of chart
12673
12868
  var wrap = container.selectAll('g.nv-wrap.nv-scatter').data([data]);
12674
12869
  var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatter nv-chart-' + id);
@@ -12687,7 +12882,7 @@ nv.models.scatter = function() {
12687
12882
  .attr('id', 'nv-edge-clip-' + id)
12688
12883
  .append('rect')
12689
12884
  .attr('transform', 'translate( -10, -10)');
12690
-
12885
+
12691
12886
  wrap.select('#nv-edge-clip-' + id + ' rect')
12692
12887
  .attr('width', availableWidth + 20)
12693
12888
  .attr('height', (availableHeight > 0) ? availableHeight + 20 : 0);
@@ -12703,20 +12898,23 @@ nv.models.scatter = function() {
12703
12898
 
12704
12899
  // inject series and point index for reference into voronoi
12705
12900
  if (useVoronoi === true) {
12901
+
12902
+ // nuke all voronoi paths on reload and recreate them
12903
+ wrap.select('.nv-point-paths').selectAll('path').remove();
12904
+
12706
12905
  var vertices = d3.merge(data.map(function(group, groupIndex) {
12707
12906
  return group.values
12708
12907
  .map(function(point, pointIndex) {
12709
12908
  // *Adding noise to make duplicates very unlikely
12710
12909
  // *Injecting series and point index for reference
12711
- /* *Adding a 'jitter' to the points, because there's an issue in d3.geom.voronoi.
12712
- */
12910
+ // *Adding a 'jitter' to the points, because there's an issue in d3.geom.voronoi.
12713
12911
  var pX = getX(point,pointIndex);
12714
12912
  var pY = getY(point,pointIndex);
12715
12913
 
12716
- return [nv.utils.NaNtoZero(x(pX))+ Math.random() * 1e-4,
12717
- nv.utils.NaNtoZero(y(pY))+ Math.random() * 1e-4,
12914
+ return [nv.utils.NaNtoZero(x(pX)) + Math.random() * 1e-4,
12915
+ nv.utils.NaNtoZero(y(pY)) + Math.random() * 1e-4,
12718
12916
  groupIndex,
12719
- pointIndex, point]; //temp hack to add noise until I think of a better way so there are no duplicates
12917
+ pointIndex, point];
12720
12918
  })
12721
12919
  .filter(function(pointArray, pointIndex) {
12722
12920
  return pointActive(pointArray[4], pointIndex); // Issue #237.. move filter to after map, so pointIndex is correct!
@@ -12742,6 +12940,18 @@ nv.models.scatter = function() {
12742
12940
  [width + 10,-10]
12743
12941
  ]);
12744
12942
 
12943
+ // delete duplicates from vertices - essential assumption for d3.geom.voronoi
12944
+ var epsilon = 1e-4; // Uses 1e-4 to determine equivalence.
12945
+ vertices = vertices.sort(function(a,b){return ((a[0] - b[0]) || (a[1] - b[1]))});
12946
+ for (var i = 0; i < vertices.length - 1; ) {
12947
+ if ((Math.abs(vertices[i][0] - vertices[i+1][0]) < epsilon) &&
12948
+ (Math.abs(vertices[i][1] - vertices[i+1][1]) < epsilon)) {
12949
+ vertices.splice(i+1, 1);
12950
+ } else {
12951
+ i++;
12952
+ }
12953
+ }
12954
+
12745
12955
  var voronoi = d3.geom.voronoi(vertices).map(function(d, i) {
12746
12956
  return {
12747
12957
  'data': bounds.clip(d),
@@ -12750,8 +12960,6 @@ nv.models.scatter = function() {
12750
12960
  }
12751
12961
  });
12752
12962
 
12753
- // nuke all voronoi paths on reload and recreate them
12754
- wrap.select('.nv-point-paths').selectAll('path').remove();
12755
12963
  var pointPaths = wrap.select('.nv-point-paths').selectAll('path').data(voronoi);
12756
12964
  var vPointPaths = pointPaths
12757
12965
  .enter().append("svg:path")
@@ -12957,20 +13165,47 @@ nv.models.scatter = function() {
12957
13165
  return 'translate(' + nv.utils.NaNtoZero(x(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
12958
13166
  })
12959
13167
  .remove();
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); })
12962
- .watchTransition(renderWatch, 'scatter points')
12963
- .attr('transform', function(d) {
12964
- //nv.log(d, getX(d[0],d[1]), x(getX(d[0],d[1])));
12965
- return 'translate(' + nv.utils.NaNtoZero(x(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
12966
- });
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); })
12969
- .watchTransition(renderWatch, 'scatter points')
12970
- .attr('d',
12971
- nv.utils.symbol()
12972
- .type(function(d) { return getShape(d[0]); })
12973
- .size(function(d) { return z(getSize(d[0],d[1])) })
13168
+
13169
+ //============================================================
13170
+ // Point Update Optimisation Notes
13171
+ //------------------------------------------------------------
13172
+ // The following update selections are filtered with getDiffs
13173
+ // (defined at the top of this file) this brings a performance
13174
+ // benefit for charts with large data sets that accumulate a
13175
+ // subset of changes or additions over time.
13176
+ //
13177
+ // Uneccesary and expensive DOM calls are avoided by culling
13178
+ // unchanged points from the selection in exchange for the
13179
+ // cheaper overhead of caching and diffing each point first.
13180
+ //
13181
+ // Due to the way D3 and NVD3 work, other global changes need
13182
+ // to be considered in addition to local point properties.
13183
+ // This is a potential source of bugs (if any of the global
13184
+ // changes that possibly affect points are missed).
13185
+
13186
+ // Update Point Positions [x, y]
13187
+ points.filter(function (d) {
13188
+ // getDiffs must always be called to update cache
13189
+ return getDiffs(d, 'x', getX, 'y', getY) ||
13190
+ scaleDiff || sizeDiff || domainDiff;
13191
+ })
13192
+ .watchTransition(renderWatch, 'scatter points')
13193
+ .attr('transform', function (d) {
13194
+ return 'translate(' +
13195
+ nv.utils.NaNtoZero(x(getX(d[0], d[1]))) + ',' +
13196
+ nv.utils.NaNtoZero(y(getY(d[0], d[1]))) + ')'
13197
+ });
13198
+
13199
+ // Update Point Appearance [shape, size]
13200
+ points.filter(function (d) {
13201
+ // getDiffs must always be called to update cache
13202
+ return getDiffs(d, 'shape', getShape, 'size', getSize) ||
13203
+ scaleDiff || sizeDiff || domainDiff;
13204
+ })
13205
+ .watchTransition(renderWatch, 'scatter points')
13206
+ .attr('d', nv.utils.symbol()
13207
+ .type(function (d) { return getShape(d[0]) })
13208
+ .size(function (d) { return z(getSize(d[0], d[1])) })
12974
13209
  );
12975
13210
 
12976
13211
  // add label a label to scatter chart
@@ -14625,7 +14860,7 @@ nv.models.stackedAreaChart = function() {
14625
14860
 
14626
14861
  interactiveLayer.dispatch.on('elementMousemove', function(e) {
14627
14862
  stacked.clearHighlights();
14628
- var singlePoint, pointIndex, pointXLocation, allData = [], valueSum = 0, allNullValues = true;
14863
+ var singlePoint, pointIndex, pointXLocation, allData = [], valueSum = 0, allNullValues = true, atleastOnePoint = false;
14629
14864
  data
14630
14865
  .filter(function(series, i) {
14631
14866
  series.seriesIndex = i;
@@ -14635,7 +14870,13 @@ nv.models.stackedAreaChart = function() {
14635
14870
  pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
14636
14871
  var point = series.values[pointIndex];
14637
14872
  var pointYValue = chart.y()(point, pointIndex);
14638
- if (pointYValue != null) {
14873
+ if (pointYValue != null && pointYValue > 0) {
14874
+ stacked.highlightPoint(i, pointIndex, true);
14875
+ atleastOnePoint = true;
14876
+ }
14877
+
14878
+ // Draw at least one point if all values are zero.
14879
+ if (i === (data.length - 1) && !atleastOnePoint) {
14639
14880
  stacked.highlightPoint(i, pointIndex, true);
14640
14881
  }
14641
14882
  if (typeof point === 'undefined') return;
@@ -14914,7 +15155,13 @@ nv.models.sunburst = function() {
14914
15155
  , labelFormat = function(d){if(mode === 'count'){return d.name + ' #' + d.value}else{return d.name + ' ' + (d.value || d.size)}}
14915
15156
  , labelThreshold = 0.02
14916
15157
  , sort = function(d1, d2){return d1.name > d2.name;}
14917
- , key = function(d,i){return d.name;}
15158
+ , key = function(d,i){
15159
+ if (d.parent !== undefined) {
15160
+ return d.name + '-' + d.parent.name + '-' + i;
15161
+ } else {
15162
+ return d.name;
15163
+ }
15164
+ }
14918
15165
  , groupColorByParent = true
14919
15166
  , duration = 500
14920
15167
  , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMousemove', 'elementMouseover', 'elementMouseout', 'renderEnd');
@@ -15420,6 +15667,6 @@ nv.models.sunburstChart = function() {
15420
15667
 
15421
15668
  };
15422
15669
 
15423
- nv.version = "1.8.5";
15670
+ nv.version = "1.8.6";
15424
15671
  })();
15425
15672
  //# sourceMappingURL=nv.d3.js.map
@@ -1,4 +1,4 @@
1
- /* nvd3 version 1.8.5 (https://github.com/novus/nvd3) 2016-12-01 */
1
+ /* nvd3 version 1.8.6 (https://github.com/novus/nvd3) 2017-08-23 */
2
2
  .nvd3 .nv-axis {
3
3
  pointer-events:none;
4
4
  opacity: 1;
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: novus-nvd3-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.5
4
+ version: 1.8.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - dbackowski
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-02 00:00:00.000000000 Z
11
+ date: 2017-09-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -183,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
183
  version: '0'
184
184
  requirements: []
185
185
  rubyforge_project:
186
- rubygems_version: 2.5.1
186
+ rubygems_version: 2.6.11
187
187
  signing_key:
188
188
  specification_version: 4
189
189
  summary: Nvd3 - reusable chart library for d3.js.