novus-nvd3-rails 1.8.5 → 1.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 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.