novus-nvd3-rails 1.8.2 → 1.8.3

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: 57a3aaed9b38c413221cca11bdedaf298062321e
4
- data.tar.gz: 7287d08610e30fa6bd9b1643dbcd6d4abc967e9b
3
+ metadata.gz: fd609fdf8f388b5244547d270b2f5cb244e7ad2b
4
+ data.tar.gz: 5a954a6949b7fefe57819aa489330d36e34d693f
5
5
  SHA512:
6
- metadata.gz: f64d277e12ed0feed64d8e88d3a47881d10209065fe43149b3b9fa960b55521926238652bb42be7840f9d23724b9a59ef7480658cf721d4f09bc3f2b33286918
7
- data.tar.gz: 9a16ff71f4b064ce4c105657af8a973d1b0de279a74750c0388449e2b82cfa03d77f664cfd145e2f6397eb64cc8afcc10ab3544ad7e74f512bf1838e9dbed6c0
6
+ metadata.gz: fe70dd05dce10fca74e1996cc6188f9b968755455a8a2472b0f0af81b8a11be7f361b95f18ec8eb03012b0d01c4fba21d3f4e2e65ae7c6afa50ad68c20a9d893
7
+ data.tar.gz: 6c2ecaa4a7eef83d6a32cb422125c449d589af58a079907d415905811be280d549be4e531c0dc10db51c43596ad4c30764e6777c3d4bdd328f4415d963f0d69d
data/README.md CHANGED
@@ -36,3 +36,7 @@ https://nvd3-community.github.io/nvd3
36
36
  3. Commit your changes (`git commit -am 'Add some feature'`)
37
37
  4. Push to the branch (`git push origin my-new-feature`)
38
38
  5. Create new Pull Request
39
+
40
+ ## License
41
+
42
+ Released under the MIT License.
@@ -1,7 +1,7 @@
1
1
  module Novus
2
2
  module Nvd3
3
3
  module Rails
4
- VERSION = "1.8.2"
4
+ VERSION = "1.8.3"
5
5
  end
6
6
  end
7
7
  end
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.7"
22
22
  spec.add_development_dependency "rake", "~> 10.0"
23
- spec.add_development_dependency 'rails', '4.2.5'
23
+ spec.add_development_dependency 'rails', '4.2.6'
24
24
  spec.add_development_dependency 'rspec-rails', '~> 3.0'
25
25
  spec.add_development_dependency 'capybara', '~> 2.6'
26
26
  spec.add_development_dependency 'sqlite3', '~> 1.3'
@@ -1,4 +1,4 @@
1
- /* nvd3 version 1.8.2 (https://github.com/novus/nvd3) 2016-01-24 */
1
+ /* nvd3 version 1.8.3 (https://github.com/novus/nvd3) 2016-04-26 */
2
2
  (function(){
3
3
 
4
4
  // set up main nv object
@@ -13,6 +13,11 @@ nv.charts = {}; //stores all the ready to use charts
13
13
  nv.logs = {}; //stores some statistics and potential error messages
14
14
  nv.dom = {}; //DOM manipulation functions
15
15
 
16
+ // Node/CommonJS - require D3
17
+ if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined' && typeof(d3) == 'undefined') {
18
+ d3 = require('d3');
19
+ }
20
+
16
21
  nv.dispatch = d3.dispatch('render_start', 'render_end');
17
22
 
18
23
  // Function bind polyfill
@@ -157,7 +162,7 @@ if (typeof(window) !== 'undefined') {
157
162
  */
158
163
  nv.dom.write = function(callback) {
159
164
  if (window.fastdom !== undefined) {
160
- return fastdom.write(callback);
165
+ return fastdom.mutate(callback);
161
166
  }
162
167
  return callback();
163
168
  };
@@ -170,10 +175,11 @@ nv.dom.write = function(callback) {
170
175
  */
171
176
  nv.dom.read = function(callback) {
172
177
  if (window.fastdom !== undefined) {
173
- return fastdom.read(callback);
178
+ return fastdom.measure(callback);
174
179
  }
175
180
  return callback();
176
- };/* Utility class to handle creation of an interactive layer.
181
+ };
182
+ /* Utility class to handle creation of an interactive layer.
177
183
  This places a rectangle on top of the chart. When you mouse move over it, it sends a dispatch
178
184
  containing the X-coordinate. It can also render a vertical line where the mouse is located.
179
185
 
@@ -258,7 +264,8 @@ nv.interactiveGuideline = function() {
258
264
  /* If mouseX/Y is outside of the chart's bounds,
259
265
  trigger a mouseOut event.
260
266
  */
261
- if (mouseX < 0 || mouseY < 0
267
+ if (d3.event.type === 'mouseout'
268
+ || mouseX < 0 || mouseY < 0
262
269
  || mouseX > availableWidth || mouseY > availableHeight
263
270
  || (d3.event.relatedTarget && d3.event.relatedTarget.ownerSVGElement === undefined)
264
271
  || mouseOutAnyReason
@@ -642,11 +649,11 @@ nv.models.tooltip = function() {
642
649
 
643
650
  var dataSeriesExists = function(d) {
644
651
  if (d && d.series) {
645
- if (d.series instanceof Array) {
646
- return !!d.series.length;
652
+ if (nv.utils.isArray(d.series)) {
653
+ return true;
647
654
  }
648
655
  // if object, it's okay just convert to array of the object
649
- if (d.series instanceof Object) {
656
+ if (nv.utils.isObject(d.series)) {
650
657
  d.series = [d.series];
651
658
  return true;
652
659
  }
@@ -754,18 +761,23 @@ nv.models.tooltip = function() {
754
761
 
755
762
  // Creates new tooltip container, or uses existing one on DOM.
756
763
  function initTooltip() {
757
- if (!tooltip) {
764
+ if (!tooltip || !tooltip.node()) {
758
765
  var container = chartContainer ? chartContainer : document.body;
759
-
760
766
  // Create new tooltip div if it doesn't exist on DOM.
761
- tooltip = d3.select(container).append("div")
762
- .attr("class", "nvtooltip " + (classes ? classes : "xy-tooltip"))
763
- .attr("id", id);
764
- tooltip.style("top", 0).style("left", 0);
765
- tooltip.style('opacity', 0);
766
- tooltip.style('position', 'fixed');
767
- tooltip.selectAll("div, table, td, tr").classed(nvPointerEventsClass, true);
768
- tooltip.classed(nvPointerEventsClass, true);
767
+
768
+ var data = [1];
769
+ tooltip = d3.select(container).selectAll('.nvtooltip').data(data);
770
+
771
+ tooltip.enter().append('div')
772
+ .attr("class", "nvtooltip " + (classes ? classes : "xy-tooltip"))
773
+ .attr("id", id)
774
+ .style("top", 0).style("left", 0)
775
+ .style('opacity', 0)
776
+ .style('position', 'fixed')
777
+ .selectAll("div, table, td, tr").classed(nvPointerEventsClass, true)
778
+ .classed(nvPointerEventsClass, true);
779
+
780
+ tooltip.exit().remove()
769
781
  }
770
782
  }
771
783
 
@@ -884,6 +896,25 @@ nv.utils.windowSize = function() {
884
896
  return (size);
885
897
  };
886
898
 
899
+
900
+ /* handle dumb browser quirks... isinstance breaks if you use frames
901
+ typeof returns 'object' for null, NaN is a number, etc.
902
+ */
903
+ nv.utils.isArray = Array.isArray;
904
+ nv.utils.isObject = function(a) {
905
+ return a !== null && typeof a === 'object';
906
+ };
907
+ nv.utils.isFunction = function(a) {
908
+ return typeof a === 'function';
909
+ };
910
+ nv.utils.isDate = function(a) {
911
+ return toString.call(a) === '[object Date]';
912
+ };
913
+ nv.utils.isNumber = function(a) {
914
+ return !isNaN(a) && typeof a === 'number';
915
+ };
916
+
917
+
887
918
  /*
888
919
  Binds callback function to run when window is resized
889
920
  */
@@ -915,8 +946,7 @@ nv.utils.getColor = function(color) {
915
946
  return nv.utils.defaultColor();
916
947
 
917
948
  //if passed an array, turn it into a color scale
918
- // use isArray, instanceof fails if d3 range is created in an iframe
919
- } else if(Array.isArray(color)) {
949
+ } else if(nv.utils.isArray(color)) {
920
950
  var color_scale = d3.scale.ordinal().range(color);
921
951
  return function(d, i) {
922
952
  var key = i === undefined ? d : i;
@@ -956,7 +986,7 @@ nv.utils.customTheme = function(dictionary, getKey, defaultColors) {
956
986
 
957
987
  return function(series, index) {
958
988
  var key = getKey(series);
959
- if (typeof dictionary[key] === 'function') {
989
+ if (nv.utils.isFunction(dictionary[key])) {
960
990
  return dictionary[key]();
961
991
  } else if (dictionary[key] !== undefined) {
962
992
  return dictionary[key];
@@ -1010,12 +1040,10 @@ Most common instance is when the element is in a display:none; container.
1010
1040
  Forumla is : text.length * font-size * constant_factor
1011
1041
  */
1012
1042
  nv.utils.calcApproxTextWidth = function (svgTextElem) {
1013
- if (typeof svgTextElem.style === 'function'
1014
- && typeof svgTextElem.text === 'function') {
1015
-
1043
+ if (nv.utils.isFunction(svgTextElem.style) && nv.utils.isFunction(svgTextElem.text)) {
1016
1044
  var fontSize = parseInt(svgTextElem.style("font-size").replace("px",""), 10);
1017
1045
  var textLength = svgTextElem.text().length;
1018
- return textLength * fontSize * 0.5;
1046
+ return nv.utils.NaNtoZero(textLength * fontSize * 0.5);
1019
1047
  }
1020
1048
  return 0;
1021
1049
  };
@@ -1025,7 +1053,7 @@ nv.utils.calcApproxTextWidth = function (svgTextElem) {
1025
1053
  Numbers that are undefined, null or NaN, convert them to zeros.
1026
1054
  */
1027
1055
  nv.utils.NaNtoZero = function(n) {
1028
- if (typeof n !== 'number'
1056
+ if (!nv.utils.isNumber(n)
1029
1057
  || isNaN(n)
1030
1058
  || n === null
1031
1059
  || n === Infinity
@@ -1143,9 +1171,9 @@ nv.utils.deepExtend = function(dst){
1143
1171
  var sources = arguments.length > 1 ? [].slice.call(arguments, 1) : [];
1144
1172
  sources.forEach(function(source) {
1145
1173
  for (var key in source) {
1146
- var isArray = dst[key] instanceof Array;
1147
- var isObject = typeof dst[key] === 'object';
1148
- var srcObj = typeof source[key] === 'object';
1174
+ var isArray = nv.utils.isArray(dst[key]);
1175
+ var isObject = nv.utils.isObject(dst[key]);
1176
+ var srcObj = nv.utils.isObject(source[key]);
1149
1177
 
1150
1178
  if (isObject && !isArray && srcObj) {
1151
1179
  nv.utils.deepExtend(dst[key], source[key]);
@@ -1244,7 +1272,7 @@ chart.options = nv.utils.optionsFunc.bind(chart);
1244
1272
  nv.utils.optionsFunc = function(args) {
1245
1273
  if (args) {
1246
1274
  d3.map(args).forEach((function(key,value) {
1247
- if (typeof this[key] === "function") {
1275
+ if (nv.utils.isFunction(this[key])) {
1248
1276
  this[key](value);
1249
1277
  }
1250
1278
  }).bind(this));
@@ -1560,6 +1588,7 @@ nv.utils.arrayEquals = function (array1, array2) {
1560
1588
  , isOrdinal = false
1561
1589
  , ticks = null
1562
1590
  , axisLabelDistance = 0
1591
+ , fontSize = undefined
1563
1592
  , duration = 250
1564
1593
  , dispatch = d3.dispatch('renderEnd')
1565
1594
  ;
@@ -1607,6 +1636,11 @@ nv.utils.arrayEquals = function (array1, array2) {
1607
1636
  .data([axisLabelText || null]);
1608
1637
  axisLabel.exit().remove();
1609
1638
 
1639
+ //only skip when fontSize is undefined so it can be cleared with a null or blank string
1640
+ if (fontSize !== undefined) {
1641
+ g.selectAll('g').select("text").style('font-size', fontSize);
1642
+ }
1643
+
1610
1644
  var xLabelMargin;
1611
1645
  var axisMaxMin;
1612
1646
  var w;
@@ -1657,6 +1691,8 @@ nv.utils.arrayEquals = function (array1, array2) {
1657
1691
  var xTicks = g.selectAll('g').select("text");
1658
1692
  var rotateLabelsRule = '';
1659
1693
  if (rotateLabels%360) {
1694
+ //Reset transform on ticks so textHeight can be calculated correctly
1695
+ xTicks.attr('transform', '');
1660
1696
  //Calculate the longest xTick width
1661
1697
  xTicks.each(function(d,i){
1662
1698
  var box = this.getBoundingClientRect();
@@ -1896,6 +1932,7 @@ nv.utils.arrayEquals = function (array1, array2) {
1896
1932
  height: {get: function(){return height;}, set: function(_){height=_;}},
1897
1933
  ticks: {get: function(){return ticks;}, set: function(_){ticks=_;}},
1898
1934
  width: {get: function(){return width;}, set: function(_){width=_;}},
1935
+ fontSize: {get: function(){return fontSize;}, set: function(_){fontSize=_;}},
1899
1936
 
1900
1937
  // options that require extra logic in the setter
1901
1938
  margin: {get: function(){return margin;}, set: function(_){
@@ -1929,30 +1966,36 @@ nv.models.boxPlot = function() {
1929
1966
  // Public Variables with Default Settings
1930
1967
  //------------------------------------------------------------
1931
1968
 
1932
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
1933
- , width = 960
1934
- , height = 500
1935
- , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
1936
- , x = d3.scale.ordinal()
1937
- , y = d3.scale.linear()
1938
- , getX = function(d) { return d.x }
1939
- , getY = function(d) { return d.y }
1940
- , color = nv.utils.defaultColor()
1941
- , container = null
1942
- , xDomain
1943
- , yDomain
1944
- , xRange
1945
- , yRange
1946
- , dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd')
1947
- , duration = 250
1948
- , maxBoxWidth = null
1949
- ;
1969
+ var margin = {top: 0, right: 0, bottom: 0, left: 0},
1970
+ width = 960,
1971
+ height = 500,
1972
+ id = Math.floor(Math.random() * 10000), // Create semi-unique ID in case user doesn't select one
1973
+ xScale = d3.scale.ordinal(),
1974
+ yScale = d3.scale.linear(),
1975
+ getX = function(d) { return d.label }, // Default data model selectors.
1976
+ getQ1 = function(d) { return d.values.Q1 },
1977
+ getQ2 = function(d) { return d.values.Q2 },
1978
+ getQ3 = function(d) { return d.values.Q3 },
1979
+ getWl = function(d) { return d.values.whisker_low },
1980
+ getWh = function(d) { return d.values.whisker_high },
1981
+ getColor = function(d) { return d.color },
1982
+ getOlItems = function(d) { return d.values.outliers },
1983
+ getOlValue = function(d, i, j) { return d },
1984
+ getOlLabel = function(d, i, j) { return d },
1985
+ getOlColor = function(d, i, j) { return undefined },
1986
+ color = nv.utils.defaultColor(),
1987
+ container = null,
1988
+ xDomain, xRange,
1989
+ yDomain, yRange,
1990
+ dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd'),
1991
+ duration = 250,
1992
+ maxBoxWidth = null;
1950
1993
 
1951
1994
  //============================================================
1952
1995
  // Private Variables
1953
1996
  //------------------------------------------------------------
1954
1997
 
1955
- var x0, y0;
1998
+ var xScale0, yScale0;
1956
1999
  var renderWatch = nv.utils.renderWatch(dispatch, duration);
1957
2000
 
1958
2001
  function chart(selection) {
@@ -1965,45 +2008,38 @@ nv.models.boxPlot = function() {
1965
2008
  nv.utils.initSVG(container);
1966
2009
 
1967
2010
  // Setup Scales
1968
- x .domain(xDomain || data.map(function(d,i) { return getX(d,i); }))
1969
- .rangeBands(xRange || [0, availableWidth], .1);
2011
+ xScale.domain(xDomain || data.map(function(d,i) { return getX(d,i); }))
2012
+ .rangeBands(xRange || [0, availableWidth], 0.1);
1970
2013
 
1971
2014
  // if we know yDomain, no need to calculate
1972
2015
  var yData = []
1973
2016
  if (!yDomain) {
1974
2017
  // (y-range is based on quartiles, whiskers and outliers)
1975
-
1976
- // lower values
1977
- var yMin = d3.min(data.map(function(d) {
1978
- var min_arr = [];
1979
-
1980
- min_arr.push(d.values.Q1);
1981
- if (d.values.hasOwnProperty('whisker_low') && d.values.whisker_low !== null) { min_arr.push(d.values.whisker_low); }
1982
- if (d.values.hasOwnProperty('outliers') && d.values.outliers !== null) { min_arr = min_arr.concat(d.values.outliers); }
1983
-
1984
- return d3.min(min_arr);
1985
- }));
1986
-
1987
- // upper values
1988
- var yMax = d3.max(data.map(function(d) {
1989
- var max_arr = [];
1990
-
1991
- max_arr.push(d.values.Q3);
1992
- if (d.values.hasOwnProperty('whisker_high') && d.values.whisker_high !== null) { max_arr.push(d.values.whisker_high); }
1993
- if (d.values.hasOwnProperty('outliers') && d.values.outliers !== null) { max_arr = max_arr.concat(d.values.outliers); }
1994
-
1995
- return d3.max(max_arr);
1996
- }));
1997
-
2018
+ var values = [], yMin, yMax;
2019
+ data.forEach(function (d, i) {
2020
+ var q1 = getQ1(d), q3 = getQ3(d), wl = getWl(d), wh = getWh(d);
2021
+ var olItems = getOlItems(d);
2022
+ if (olItems) {
2023
+ olItems.forEach(function (e, i) {
2024
+ values.push(getOlValue(e, i, undefined));
2025
+ });
2026
+ }
2027
+ if (wl) { values.push(wl) }
2028
+ if (q1) { values.push(q1) }
2029
+ if (q3) { values.push(q3) }
2030
+ if (wh) { values.push(wh) }
2031
+ });
2032
+ yMin = d3.min(values);
2033
+ yMax = d3.max(values);
1998
2034
  yData = [ yMin, yMax ] ;
1999
2035
  }
2000
2036
 
2001
- y.domain(yDomain || yData);
2002
- y.range(yRange || [availableHeight, 0]);
2037
+ yScale.domain(yDomain || yData);
2038
+ yScale.range(yRange || [availableHeight, 0]);
2003
2039
 
2004
2040
  //store old scales if they exist
2005
- x0 = x0 || x;
2006
- y0 = y0 || y.copy().range([y(0),y(0)]);
2041
+ xScale0 = xScale0 || xScale;
2042
+ yScale0 = yScale0 || yScale.copy().range([yScale(0),yScale(0)]);
2007
2043
 
2008
2044
  // Setup containers and skeleton of chart
2009
2045
  var wrap = container.selectAll('g.nv-wrap').data([data]);
@@ -2014,15 +2050,15 @@ nv.models.boxPlot = function() {
2014
2050
  var boxEnter = boxplots.enter().append('g').style('stroke-opacity', 1e-6).style('fill-opacity', 1e-6);
2015
2051
  boxplots
2016
2052
  .attr('class', 'nv-boxplot')
2017
- .attr('transform', function(d,i,j) { return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05) + ', 0)'; })
2053
+ .attr('transform', function(d,i,j) { return 'translate(' + (xScale(getX(d,i)) + xScale.rangeBand() * 0.05) + ', 0)'; })
2018
2054
  .classed('hover', function(d) { return d.hover });
2019
2055
  boxplots
2020
2056
  .watchTransition(renderWatch, 'nv-boxplot: boxplots')
2021
2057
  .style('stroke-opacity', 1)
2022
- .style('fill-opacity', .75)
2058
+ .style('fill-opacity', 0.75)
2023
2059
  .delay(function(d,i) { return i * duration / data.length })
2024
2060
  .attr('transform', function(d,i) {
2025
- return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05) + ', 0)';
2061
+ return 'translate(' + (xScale(getX(d,i)) + xScale.rangeBand() * 0.05) + ', 0)';
2026
2062
  });
2027
2063
  boxplots.exit().remove();
2028
2064
 
@@ -2030,97 +2066,62 @@ nv.models.boxPlot = function() {
2030
2066
 
2031
2067
  // conditionally append whisker lines
2032
2068
  boxEnter.each(function(d,i) {
2033
- var box = d3.select(this);
2034
-
2035
- ['low', 'high'].forEach(function(key) {
2036
- if (d.values.hasOwnProperty('whisker_' + key) && d.values['whisker_' + key] !== null) {
2037
- box.append('line')
2038
- .style('stroke', (d.color) ? d.color : color(d,i))
2039
- .attr('class', 'nv-boxplot-whisker nv-boxplot-' + key);
2040
-
2041
- box.append('line')
2042
- .style('stroke', (d.color) ? d.color : color(d,i))
2043
- .attr('class', 'nv-boxplot-tick nv-boxplot-' + key);
2044
- }
2045
- });
2046
- });
2047
-
2048
- // outliers
2049
- // TODO: support custom colors here
2050
- var outliers = boxplots.selectAll('.nv-boxplot-outlier').data(function(d) {
2051
- if (d.values.hasOwnProperty('outliers') && d.values.outliers !== null) { return d.values.outliers; }
2052
- else { return []; }
2053
- });
2054
- outliers.enter().append('circle')
2055
- .style('fill', function(d,i,j) { return color(d,j) }).style('stroke', function(d,i,j) { return color(d,j) })
2056
- .on('mouseover', function(d,i,j) {
2057
- d3.select(this).classed('hover', true);
2058
- dispatch.elementMouseover({
2059
- series: { key: d, color: color(d,j) },
2060
- e: d3.event
2061
- });
2062
- })
2063
- .on('mouseout', function(d,i,j) {
2064
- d3.select(this).classed('hover', false);
2065
- dispatch.elementMouseout({
2066
- series: { key: d, color: color(d,j) },
2067
- e: d3.event
2068
- });
2069
- })
2070
- .on('mousemove', function(d,i) {
2071
- dispatch.elementMousemove({e: d3.event});
2069
+ var box = d3.select(this);
2070
+ [getWl, getWh].forEach(function (f) {
2071
+ if (f(d)) {
2072
+ var key = (f === getWl) ? 'low' : 'high';
2073
+ box.append('line')
2074
+ .style('stroke', getColor(d) || color(d,i))
2075
+ .attr('class', 'nv-boxplot-whisker nv-boxplot-' + key);
2076
+ box.append('line')
2077
+ .style('stroke', getColor(d) || color(d,i))
2078
+ .attr('class', 'nv-boxplot-tick nv-boxplot-' + key);
2079
+ }
2072
2080
  });
2081
+ });
2073
2082
 
2074
- outliers.attr('class', 'nv-boxplot-outlier');
2075
- outliers
2076
- .watchTransition(renderWatch, 'nv-boxplot: nv-boxplot-outlier')
2077
- .attr('cx', x.rangeBand() * .45)
2078
- .attr('cy', function(d,i,j) { return y(d); })
2079
- .attr('r', '3');
2080
- outliers.exit().remove();
2081
-
2082
- var box_width = function() { return (maxBoxWidth === null ? x.rangeBand() * .9 : Math.min(75, x.rangeBand() * .9)); };
2083
- var box_left = function() { return x.rangeBand() * .45 - box_width()/2; };
2084
- var box_right = function() { return x.rangeBand() * .45 + box_width()/2; };
2083
+ var box_width = function() { return (maxBoxWidth === null ? xScale.rangeBand() * 0.9 : Math.min(75, xScale.rangeBand() * 0.9)); };
2084
+ var box_left = function() { return xScale.rangeBand() * 0.45 - box_width()/2; };
2085
+ var box_right = function() { return xScale.rangeBand() * 0.45 + box_width()/2; };
2085
2086
 
2086
2087
  // update whisker lines and ticks
2087
- ['low', 'high'].forEach(function(key) {
2088
- var endpoint = (key === 'low') ? 'Q1' : 'Q3';
2089
-
2090
- boxplots.select('line.nv-boxplot-whisker.nv-boxplot-' + key)
2091
- .watchTransition(renderWatch, 'nv-boxplot: boxplots')
2092
- .attr('x1', x.rangeBand() * .45 )
2093
- .attr('y1', function(d,i) { return y(d.values['whisker_' + key]); })
2094
- .attr('x2', x.rangeBand() * .45 )
2095
- .attr('y2', function(d,i) { return y(d.values[endpoint]); });
2096
-
2097
- boxplots.select('line.nv-boxplot-tick.nv-boxplot-' + key)
2098
- .watchTransition(renderWatch, 'nv-boxplot: boxplots')
2099
- .attr('x1', box_left )
2100
- .attr('y1', function(d,i) { return y(d.values['whisker_' + key]); })
2101
- .attr('x2', box_right )
2102
- .attr('y2', function(d,i) { return y(d.values['whisker_' + key]); });
2088
+ [getWl, getWh].forEach(function (f) {
2089
+ var key = (f === getWl) ? 'low' : 'high';
2090
+ var endpoint = (f === getWl) ? getQ1 : getQ3;
2091
+ boxplots.select('line.nv-boxplot-whisker.nv-boxplot-' + key)
2092
+ .watchTransition(renderWatch, 'nv-boxplot: boxplots')
2093
+ .attr('x1', xScale.rangeBand() * 0.45 )
2094
+ .attr('y1', function(d,i) { return yScale(f(d)); })
2095
+ .attr('x2', xScale.rangeBand() * 0.45 )
2096
+ .attr('y2', function(d,i) { return yScale(endpoint(d)); });
2097
+ boxplots.select('line.nv-boxplot-tick.nv-boxplot-' + key)
2098
+ .watchTransition(renderWatch, 'nv-boxplot: boxplots')
2099
+ .attr('x1', box_left )
2100
+ .attr('y1', function(d,i) { return yScale(f(d)); })
2101
+ .attr('x2', box_right )
2102
+ .attr('y2', function(d,i) { return yScale(f(d)); });
2103
2103
  });
2104
2104
 
2105
- ['low', 'high'].forEach(function(key) {
2106
- boxEnter.selectAll('.nv-boxplot-' + key)
2107
- .on('mouseover', function(d,i,j) {
2108
- d3.select(this).classed('hover', true);
2109
- dispatch.elementMouseover({
2110
- series: { key: d.values['whisker_' + key], color: color(d,j) },
2111
- e: d3.event
2112
- });
2113
- })
2114
- .on('mouseout', function(d,i,j) {
2115
- d3.select(this).classed('hover', false);
2116
- dispatch.elementMouseout({
2117
- series: { key: d.values['whisker_' + key], color: color(d,j) },
2118
- e: d3.event
2119
- });
2120
- })
2121
- .on('mousemove', function(d,i) {
2122
- dispatch.elementMousemove({e: d3.event});
2123
- });
2105
+ [getWl, getWh].forEach(function (f) {
2106
+ var key = (f === getWl) ? 'low' : 'high';
2107
+ boxEnter.selectAll('.nv-boxplot-' + key)
2108
+ .on('mouseover', function(d,i,j) {
2109
+ d3.select(this).classed('hover', true);
2110
+ dispatch.elementMouseover({
2111
+ series: { key: f(d), color: getColor(d) || color(d,j) },
2112
+ e: d3.event
2113
+ });
2114
+ })
2115
+ .on('mouseout', function(d,i,j) {
2116
+ d3.select(this).classed('hover', false);
2117
+ dispatch.elementMouseout({
2118
+ series: { key: f(d), color: getColor(d) || color(d,j) },
2119
+ e: d3.event
2120
+ });
2121
+ })
2122
+ .on('mousemove', function(d,i) {
2123
+ dispatch.elementMousemove({e: d3.event});
2124
+ });
2124
2125
  });
2125
2126
 
2126
2127
  // boxes
@@ -2130,12 +2131,12 @@ nv.models.boxPlot = function() {
2130
2131
  .on('mouseover', function(d,i) {
2131
2132
  d3.select(this).classed('hover', true);
2132
2133
  dispatch.elementMouseover({
2133
- key: d.label,
2134
- value: d.label,
2134
+ key: getX(d),
2135
+ value: getX(d),
2135
2136
  series: [
2136
- { key: 'Q3', value: d.values.Q3, color: d.color || color(d,i) },
2137
- { key: 'Q2', value: d.values.Q2, color: d.color || color(d,i) },
2138
- { key: 'Q1', value: d.values.Q1, color: d.color || color(d,i) }
2137
+ { key: 'Q3', value: getQ3(d), color: getColor(d) || color(d,i) },
2138
+ { key: 'Q2', value: getQ2(d), color: getColor(d) || color(d,i) },
2139
+ { key: 'Q1', value: getQ1(d), color: getColor(d) || color(d,i) }
2139
2140
  ],
2140
2141
  data: d,
2141
2142
  index: i,
@@ -2145,12 +2146,12 @@ nv.models.boxPlot = function() {
2145
2146
  .on('mouseout', function(d,i) {
2146
2147
  d3.select(this).classed('hover', false);
2147
2148
  dispatch.elementMouseout({
2148
- key: d.label,
2149
- value: d.label,
2149
+ key: getX(d),
2150
+ value: getX(d),
2150
2151
  series: [
2151
- { key: 'Q3', value: d.values.Q3, color: d.color || color(d,i) },
2152
- { key: 'Q2', value: d.values.Q2, color: d.color || color(d,i) },
2153
- { key: 'Q1', value: d.values.Q1, color: d.color || color(d,i) }
2152
+ { key: 'Q3', value: getQ3(d), color: getColor(d) || color(d,i) },
2153
+ { key: 'Q2', value: getQ2(d), color: getColor(d) || color(d,i) },
2154
+ { key: 'Q1', value: getQ1(d), color: getColor(d) || color(d,i) }
2154
2155
  ],
2155
2156
  data: d,
2156
2157
  index: i,
@@ -2164,13 +2165,12 @@ nv.models.boxPlot = function() {
2164
2165
  // box transitions
2165
2166
  boxplots.select('rect.nv-boxplot-box')
2166
2167
  .watchTransition(renderWatch, 'nv-boxplot: boxes')
2167
- .attr('y', function(d,i) { return y(d.values.Q3); })
2168
+ .attr('y', function(d,i) { return yScale(getQ3(d)); })
2168
2169
  .attr('width', box_width)
2169
2170
  .attr('x', box_left )
2170
-
2171
- .attr('height', function(d,i) { return Math.abs(y(d.values.Q3) - y(d.values.Q1)) || 1 })
2172
- .style('fill', function(d,i) { return d.color || color(d,i) })
2173
- .style('stroke', function(d,i) { return d.color || color(d,i) });
2171
+ .attr('height', function(d,i) { return Math.abs(yScale(getQ3(d)) - yScale(getQ1(d))) || 1 })
2172
+ .style('fill', function(d,i) { return getColor(d) || color(d,i) })
2173
+ .style('stroke', function(d,i) { return getColor(d) || color(d,i) });
2174
2174
 
2175
2175
  // median line
2176
2176
  boxEnter.append('line').attr('class', 'nv-boxplot-median');
@@ -2178,13 +2178,46 @@ nv.models.boxPlot = function() {
2178
2178
  boxplots.select('line.nv-boxplot-median')
2179
2179
  .watchTransition(renderWatch, 'nv-boxplot: boxplots line')
2180
2180
  .attr('x1', box_left)
2181
- .attr('y1', function(d,i) { return y(d.values.Q2); })
2181
+ .attr('y1', function(d,i) { return yScale(getQ2(d)); })
2182
2182
  .attr('x2', box_right)
2183
- .attr('y2', function(d,i) { return y(d.values.Q2); });
2183
+ .attr('y2', function(d,i) { return yScale(getQ2(d)); });
2184
+
2185
+ // outliers
2186
+ var outliers = boxplots.selectAll('.nv-boxplot-outlier').data(function(d) {
2187
+ return getOlItems(d) || [];
2188
+ });
2189
+ outliers.enter().append('circle')
2190
+ .style('fill', function(d,i,j) { return getOlColor(d,i,j) || color(d,j) })
2191
+ .style('stroke', function(d,i,j) { return getOlColor(d,i,j) || color(d,j) })
2192
+ .style('z-index', 9000)
2193
+ .on('mouseover', function(d,i,j) {
2194
+ d3.select(this).classed('hover', true);
2195
+ dispatch.elementMouseover({
2196
+ series: { key: getOlLabel(d,i,j), color: getOlColor(d,i,j) || color(d,j) },
2197
+ e: d3.event
2198
+ });
2199
+ })
2200
+ .on('mouseout', function(d,i,j) {
2201
+ d3.select(this).classed('hover', false);
2202
+ dispatch.elementMouseout({
2203
+ series: { key: getOlLabel(d,i,j), color: getOlColor(d,i,j) || color(d,j) },
2204
+ e: d3.event
2205
+ });
2206
+ })
2207
+ .on('mousemove', function(d,i) {
2208
+ dispatch.elementMousemove({e: d3.event});
2209
+ });
2210
+ outliers.attr('class', 'nv-boxplot-outlier');
2211
+ outliers
2212
+ .watchTransition(renderWatch, 'nv-boxplot: nv-boxplot-outlier')
2213
+ .attr('cx', xScale.rangeBand() * 0.45)
2214
+ .attr('cy', function(d,i,j) { return yScale(getOlValue(d,i,j)); })
2215
+ .attr('r', '3');
2216
+ outliers.exit().remove();
2184
2217
 
2185
2218
  //store old scales for use in transitions on update
2186
- x0 = x.copy();
2187
- y0 = y.copy();
2219
+ xScale0 = xScale.copy();
2220
+ yScale0 = yScale.copy();
2188
2221
  });
2189
2222
 
2190
2223
  renderWatch.renderEnd('nv-boxplot immediate');
@@ -2200,20 +2233,37 @@ nv.models.boxPlot = function() {
2200
2233
 
2201
2234
  chart._options = Object.create({}, {
2202
2235
  // simple options, just get/set the necessary values
2203
- width: {get: function(){return width;}, set: function(_){width=_;}},
2204
- height: {get: function(){return height;}, set: function(_){height=_;}},
2236
+ width: {get: function(){return width;}, set: function(_){width=_;}},
2237
+ height: {get: function(){return height;}, set: function(_){height=_;}},
2205
2238
  maxBoxWidth: {get: function(){return maxBoxWidth;}, set: function(_){maxBoxWidth=_;}},
2206
- x: {get: function(){return getX;}, set: function(_){getX=_;}},
2207
- y: {get: function(){return getY;}, set: function(_){getY=_;}},
2208
- xScale: {get: function(){return x;}, set: function(_){x=_;}},
2209
- yScale: {get: function(){return y;}, set: function(_){y=_;}},
2239
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
2240
+ q1: {get: function(){return getQ1;}, set: function(_){getQ1=_;}},
2241
+ q2: {get: function(){return getQ2;}, set: function(_){getQ2=_;}},
2242
+ q3: {get: function(){return getQ3;}, set: function(_){getQ3=_;}},
2243
+ wl: {get: function(){return getWl;}, set: function(_){getWl=_;}},
2244
+ wh: {get: function(){return getWh;}, set: function(_){getWh=_;}},
2245
+ itemColor: {get: function(){return getColor;}, set: function(_){getColor=_;}},
2246
+ outliers: {get: function(){return getOlItems;}, set: function(_){getOlItems=_;}},
2247
+ outlierValue: {get: function(){return getOlValue;}, set: function(_){getOlValue=_;}},
2248
+ outlierLabel: {get: function(){return getOlLabel;}, set: function(_){getOlLabel=_;}},
2249
+ outlierColor: {get: function(){return getOlColor;}, set: function(_){getOlColor=_;}},
2250
+ xScale: {get: function(){return xScale;}, set: function(_){xScale=_;}},
2251
+ yScale: {get: function(){return yScale;}, set: function(_){yScale=_;}},
2210
2252
  xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
2211
2253
  yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
2212
2254
  xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
2213
2255
  yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
2214
2256
  id: {get: function(){return id;}, set: function(_){id=_;}},
2215
2257
  // rectClass: {get: function(){return rectClass;}, set: function(_){rectClass=_;}},
2216
-
2258
+ y: {
2259
+ get: function() {
2260
+ console.warn('BoxPlot \'y\' chart option is deprecated. Please use model overrides instead.');
2261
+ return {};
2262
+ },
2263
+ set: function(_) {
2264
+ console.warn('BoxPlot \'y\' chart option is deprecated. Please use model overrides instead.');
2265
+ }
2266
+ },
2217
2267
  // options that require extra logic in the setter
2218
2268
  margin: {get: function(){return margin;}, set: function(_){
2219
2269
  margin.top = _.top !== undefined ? _.top : margin.top;
@@ -2241,26 +2291,23 @@ nv.models.boxPlotChart = function() {
2241
2291
  // Public Variables with Default Settings
2242
2292
  //------------------------------------------------------------
2243
2293
 
2244
- var boxplot = nv.models.boxPlot()
2245
- , xAxis = nv.models.axis()
2246
- , yAxis = nv.models.axis()
2247
- ;
2294
+ var boxplot = nv.models.boxPlot(),
2295
+ xAxis = nv.models.axis(),
2296
+ yAxis = nv.models.axis();
2248
2297
 
2249
- var margin = {top: 15, right: 10, bottom: 50, left: 60}
2250
- , width = null
2251
- , height = null
2252
- , color = nv.utils.getColor()
2253
- , showXAxis = true
2254
- , showYAxis = true
2255
- , rightAlignYAxis = false
2256
- , staggerLabels = false
2257
- , tooltip = nv.models.tooltip()
2258
- , x
2259
- , y
2260
- , noData = "No Data Available."
2261
- , dispatch = d3.dispatch('beforeUpdate', 'renderEnd')
2262
- , duration = 250
2263
- ;
2298
+ var margin = {top: 15, right: 10, bottom: 50, left: 60},
2299
+ width = null,
2300
+ height = null,
2301
+ color = nv.utils.getColor(),
2302
+ showXAxis = true,
2303
+ showYAxis = true,
2304
+ rightAlignYAxis = false,
2305
+ staggerLabels = false,
2306
+ tooltip = nv.models.tooltip(),
2307
+ x, y,
2308
+ noData = 'No Data Available.',
2309
+ dispatch = d3.dispatch('beforeUpdate', 'renderEnd'),
2310
+ duration = 250;
2264
2311
 
2265
2312
  xAxis
2266
2313
  .orient('bottom')
@@ -2287,13 +2334,10 @@ nv.models.boxPlotChart = function() {
2287
2334
  if (showYAxis) renderWatch.models(yAxis);
2288
2335
 
2289
2336
  selection.each(function(data) {
2290
- var container = d3.select(this),
2291
- that = this;
2337
+ var container = d3.select(this), that = this;
2292
2338
  nv.utils.initSVG(container);
2293
- var availableWidth = (width || parseInt(container.style('width')) || 960)
2294
- - margin.left - margin.right,
2295
- availableHeight = (height || parseInt(container.style('height')) || 400)
2296
- - margin.top - margin.bottom;
2339
+ var availableWidth = (width || parseInt(container.style('width')) || 960) - margin.left - margin.right;
2340
+ var availableHeight = (height || parseInt(container.style('height')) || 400) - margin.top - margin.bottom;
2297
2341
 
2298
2342
  chart.update = function() {
2299
2343
  dispatch.beforeUpdate();
@@ -2301,9 +2345,9 @@ nv.models.boxPlotChart = function() {
2301
2345
  };
2302
2346
  chart.container = this;
2303
2347
 
2304
- // Display No Data message if there's nothing to show. (quartiles required at minimum)
2305
- if (!data || !data.length ||
2306
- !data.filter(function(d) { return d.values.hasOwnProperty("Q1") && d.values.hasOwnProperty("Q2") && d.values.hasOwnProperty("Q3"); }).length) {
2348
+ // TODO still need to find a way to validate quartile data presence using boxPlot callbacks.
2349
+ // Display No Data message if there's nothing to show. (quartiles required at minimum).
2350
+ if (!data || !data.length) {
2307
2351
  var noDataText = container.selectAll('.nv-noData').data([noData]);
2308
2352
 
2309
2353
  noDataText.enter().append('text')
@@ -2337,25 +2381,21 @@ nv.models.boxPlotChart = function() {
2337
2381
  .append('line');
2338
2382
 
2339
2383
  gEnter.append('g').attr('class', 'nv-barsWrap');
2340
-
2341
2384
  g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
2342
2385
 
2343
2386
  if (rightAlignYAxis) {
2344
- g.select(".nv-y.nv-axis")
2345
- .attr("transform", "translate(" + availableWidth + ",0)");
2387
+ g.select('.nv-y.nv-axis')
2388
+ .attr('transform', 'translate(' + availableWidth + ',0)');
2346
2389
  }
2347
2390
 
2348
2391
  // Main Chart Component(s)
2349
- boxplot
2350
- .width(availableWidth)
2351
- .height(availableHeight);
2392
+ boxplot.width(availableWidth).height(availableHeight);
2352
2393
 
2353
2394
  var barsWrap = g.select('.nv-barsWrap')
2354
2395
  .datum(data.filter(function(d) { return !d.disabled }))
2355
2396
 
2356
2397
  barsWrap.transition().call(boxplot);
2357
2398
 
2358
-
2359
2399
  defsEnter.append('clipPath')
2360
2400
  .attr('id', 'nv-x-label-clip-' + boxplot.id())
2361
2401
  .append('rect');
@@ -2379,7 +2419,7 @@ nv.models.boxPlotChart = function() {
2379
2419
  if (staggerLabels) {
2380
2420
  xTicks
2381
2421
  .selectAll('text')
2382
- .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 == 0 ? '5' : '17') + ')' })
2422
+ .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 === 0 ? '5' : '17') + ')' })
2383
2423
  }
2384
2424
  }
2385
2425
 
@@ -2393,11 +2433,11 @@ nv.models.boxPlotChart = function() {
2393
2433
  }
2394
2434
 
2395
2435
  // Zero line
2396
- g.select(".nv-zeroLine line")
2397
- .attr("x1",0)
2398
- .attr("x2",availableWidth)
2399
- .attr("y1", y(0))
2400
- .attr("y2", y(0))
2436
+ g.select('.nv-zeroLine line')
2437
+ .attr('x1',0)
2438
+ .attr('x2',availableWidth)
2439
+ .attr('y1', y(0))
2440
+ .attr('y2', y(0))
2401
2441
  ;
2402
2442
 
2403
2443
  //============================================================
@@ -2504,8 +2544,19 @@ nv.models.bullet = function() {
2504
2544
  , tickFormat = null
2505
2545
  , color = nv.utils.getColor(['#1f77b4'])
2506
2546
  , dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove')
2547
+ , defaultRangeLabels = ["Maximum", "Mean", "Minimum"]
2548
+ , legacyRangeClassNames = ["Max", "Avg", "Min"]
2507
2549
  ;
2508
2550
 
2551
+ function sortLabels(labels, values){
2552
+ var lz = labels.slice();
2553
+ labels.sort(function(a, b){
2554
+ var iA = lz.indexOf(a);
2555
+ var iB = lz.indexOf(b);
2556
+ return d3.descending(values[iA], values[iB]);
2557
+ });
2558
+ };
2559
+
2509
2560
  function chart(selection) {
2510
2561
  selection.each(function(d, i) {
2511
2562
  var availableWidth = width - margin.left - margin.right,
@@ -2514,13 +2565,23 @@ nv.models.bullet = function() {
2514
2565
  container = d3.select(this);
2515
2566
  nv.utils.initSVG(container);
2516
2567
 
2517
- var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
2518
- markerz = markers.call(this, d, i).slice().sort(d3.descending),
2519
- measurez = measures.call(this, d, i).slice().sort(d3.descending),
2568
+ var rangez = ranges.call(this, d, i).slice(),
2569
+ markerz = markers.call(this, d, i).slice(),
2570
+ measurez = measures.call(this, d, i).slice(),
2520
2571
  rangeLabelz = rangeLabels.call(this, d, i).slice(),
2521
2572
  markerLabelz = markerLabels.call(this, d, i).slice(),
2522
2573
  measureLabelz = measureLabels.call(this, d, i).slice();
2523
2574
 
2575
+ // Sort labels according to their sorted values
2576
+ sortLabels(rangeLabelz, rangez);
2577
+ sortLabels(markerLabelz, markerz);
2578
+ sortLabels(measureLabelz, measurez);
2579
+
2580
+ // sort values descending
2581
+ rangez.sort(d3.descending);
2582
+ markerz.sort(d3.descending);
2583
+ measurez.sort(d3.descending);
2584
+
2524
2585
  // Setup Scales
2525
2586
  // Compute the new x-scale.
2526
2587
  var x1 = d3.scale.linear()
@@ -2545,9 +2606,14 @@ nv.models.bullet = function() {
2545
2606
  var gEnter = wrapEnter.append('g');
2546
2607
  var g = wrap.select('g');
2547
2608
 
2548
- gEnter.append('rect').attr('class', 'nv-range nv-rangeMax');
2549
- gEnter.append('rect').attr('class', 'nv-range nv-rangeAvg');
2550
- gEnter.append('rect').attr('class', 'nv-range nv-rangeMin');
2609
+ for(var i=0,il=rangez.length; i<il; i++){
2610
+ var rangeClassNames = 'nv-range nv-range'+i;
2611
+ if(i <= 2){
2612
+ rangeClassNames = rangeClassNames + ' nv-range'+legacyRangeClassNames[i];
2613
+ }
2614
+ gEnter.append('rect').attr('class', rangeClassNames);
2615
+ }
2616
+
2551
2617
  gEnter.append('rect').attr('class', 'nv-measure');
2552
2618
 
2553
2619
  wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
@@ -2557,25 +2623,14 @@ nv.models.bullet = function() {
2557
2623
  var xp0 = function(d) { return d < 0 ? x0(d) : x0(0) },
2558
2624
  xp1 = function(d) { return d < 0 ? x1(d) : x1(0) };
2559
2625
 
2560
- g.select('rect.nv-rangeMax')
2561
- .attr('height', availableHeight)
2562
- .attr('width', w1(rangeMax > 0 ? rangeMax : rangeMin))
2563
- .attr('x', xp1(rangeMax > 0 ? rangeMax : rangeMin))
2564
- .datum(rangeMax > 0 ? rangeMax : rangeMin)
2565
-
2566
- g.select('rect.nv-rangeAvg')
2567
- .attr('height', availableHeight)
2568
- .attr('width', w1(rangeAvg))
2569
- .attr('x', xp1(rangeAvg))
2570
- .datum(rangeAvg)
2571
-
2572
- g.select('rect.nv-rangeMin')
2573
- .attr('height', availableHeight)
2574
- .attr('width', w1(rangeMax))
2575
- .attr('x', xp1(rangeMax))
2576
- .attr('width', w1(rangeMax > 0 ? rangeMin : rangeMax))
2577
- .attr('x', xp1(rangeMax > 0 ? rangeMin : rangeMax))
2578
- .datum(rangeMax > 0 ? rangeMin : rangeMax)
2626
+ for(var i=0,il=rangez.length; i<il; i++){
2627
+ var range = rangez[i];
2628
+ g.select('rect.nv-range'+i)
2629
+ .attr('height', availableHeight)
2630
+ .attr('width', w1(range))
2631
+ .attr('x', xp1(range))
2632
+ .datum(range)
2633
+ }
2579
2634
 
2580
2635
  g.select('rect.nv-measure')
2581
2636
  .style('fill', color)
@@ -2649,7 +2704,7 @@ nv.models.bullet = function() {
2649
2704
 
2650
2705
  wrap.selectAll('.nv-range')
2651
2706
  .on('mouseover', function(d,i) {
2652
- var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
2707
+ var label = rangeLabelz[i] || defaultRangeLabels[i];
2653
2708
  dispatch.elementMouseover({
2654
2709
  value: d,
2655
2710
  label: label,
@@ -2664,7 +2719,7 @@ nv.models.bullet = function() {
2664
2719
  })
2665
2720
  })
2666
2721
  .on('mouseout', function(d,i) {
2667
- var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
2722
+ var label = rangeLabelz[i] || defaultRangeLabels[i];
2668
2723
  dispatch.elementMouseout({
2669
2724
  value: d,
2670
2725
  label: label,
@@ -3365,7 +3420,9 @@ nv.models.cumulativeLineChart = function() {
3365
3420
  gEnter.append('g').attr('class', 'nv-controlsWrap');
3366
3421
 
3367
3422
  // Legend
3368
- if (showLegend) {
3423
+ if (!showLegend) {
3424
+ g.select('.nv-legendWrap').selectAll('*').remove();
3425
+ } else {
3369
3426
  legend.width(availableWidth);
3370
3427
 
3371
3428
  g.select('.nv-legendWrap')
@@ -3382,7 +3439,9 @@ nv.models.cumulativeLineChart = function() {
3382
3439
  }
3383
3440
 
3384
3441
  // Controls
3385
- if (showControls) {
3442
+ if (!showControls) {
3443
+ g.select('.nv-controlsWrap').selectAll('*').remove();
3444
+ } else {
3386
3445
  var controlsData = [
3387
3446
  { key: 'Re-scale y-axis', disabled: !rescaleY }
3388
3447
  ];
@@ -4158,8 +4217,11 @@ nv.models.discreteBarChart = function() {
4158
4217
  gEnter.append('g').attr('class', 'nv-legendWrap');
4159
4218
 
4160
4219
  g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
4161
-
4162
- if (showLegend) {
4220
+
4221
+ // Legend
4222
+ if (!showLegend) {
4223
+ g.select('.nv-legendWrap').selectAll('*').remove();
4224
+ } else {
4163
4225
  legend.width(availableWidth);
4164
4226
 
4165
4227
  g.select('.nv-legendWrap')
@@ -4180,11 +4242,6 @@ nv.models.discreteBarChart = function() {
4180
4242
  .attr("transform", "translate(" + availableWidth + ",0)");
4181
4243
  }
4182
4244
 
4183
- if (rightAlignYAxis) {
4184
- g.select(".nv-y.nv-axis")
4185
- .attr("transform", "translate(" + availableWidth + ",0)");
4186
- }
4187
-
4188
4245
  // Main Chart Component(s)
4189
4246
  discretebar
4190
4247
  .width(availableWidth)
@@ -4494,6 +4551,195 @@ nv.models.distribution = function() {
4494
4551
 
4495
4552
  return chart;
4496
4553
  }
4554
+ nv.models.forceDirectedGraph = function() {
4555
+ "use strict";
4556
+
4557
+ //============================================================
4558
+ // Public Variables with Default Settings
4559
+ //------------------------------------------------------------
4560
+ var margin = {top: 2, right: 0, bottom: 2, left: 0}
4561
+ , width = 400
4562
+ , height = 32
4563
+ , container = null
4564
+ , dispatch = d3.dispatch('renderEnd')
4565
+ , color = nv.utils.getColor(['#000'])
4566
+ , tooltip = nv.models.tooltip()
4567
+ , noData = null
4568
+ // Force directed graph specific parameters [default values]
4569
+ , linkStrength = 0.1
4570
+ , friction = 0.9
4571
+ , linkDist = 30
4572
+ , charge = -120
4573
+ , gravity = 0.1
4574
+ , theta = 0.8
4575
+ , alpha = 0.1
4576
+ , radius = 5
4577
+ // These functions allow to add extra attributes to ndes and links
4578
+ ,nodeExtras = function(nodes) { /* Do nothing */ }
4579
+ ,linkExtras = function(links) { /* Do nothing */ }
4580
+ ;
4581
+
4582
+
4583
+ //============================================================
4584
+ // Private Variables
4585
+ //------------------------------------------------------------
4586
+
4587
+ var renderWatch = nv.utils.renderWatch(dispatch);
4588
+
4589
+ function chart(selection) {
4590
+ renderWatch.reset();
4591
+
4592
+ selection.each(function(data) {
4593
+ container = d3.select(this);
4594
+ nv.utils.initSVG(container);
4595
+
4596
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
4597
+ availableHeight = nv.utils.availableHeight(height, container, margin);
4598
+
4599
+ container
4600
+ .attr("width", availableWidth)
4601
+ .attr("height", availableHeight);
4602
+
4603
+ // Display No Data message if there's nothing to show.
4604
+ if (!data || !data.links || !data.nodes) {
4605
+ nv.utils.noData(chart, container)
4606
+ return chart;
4607
+ } else {
4608
+ container.selectAll('.nv-noData').remove();
4609
+ }
4610
+ container.selectAll('*').remove();
4611
+
4612
+ // Collect names of all fields in the nodes
4613
+ var nodeFieldSet = new Set();
4614
+ data.nodes.forEach(function(node) {
4615
+ var keys = Object.keys(node);
4616
+ keys.forEach(function(key) {
4617
+ nodeFieldSet.add(key);
4618
+ });
4619
+ });
4620
+
4621
+ var force = d3.layout.force()
4622
+ .nodes(data.nodes)
4623
+ .links(data.links)
4624
+ .size([availableWidth, availableHeight])
4625
+ .linkStrength(linkStrength)
4626
+ .friction(friction)
4627
+ .linkDistance(linkDist)
4628
+ .charge(charge)
4629
+ .gravity(gravity)
4630
+ .theta(theta)
4631
+ .alpha(alpha)
4632
+ .start();
4633
+
4634
+ var link = container.selectAll(".link")
4635
+ .data(data.links)
4636
+ .enter().append("line")
4637
+ .attr("class", "nv-force-link")
4638
+ .style("stroke-width", function(d) { return Math.sqrt(d.value); });
4639
+
4640
+ var node = container.selectAll(".node")
4641
+ .data(data.nodes)
4642
+ .enter()
4643
+ .append("g")
4644
+ .attr("class", "nv-force-node")
4645
+ .call(force.drag);
4646
+
4647
+ node
4648
+ .append("circle")
4649
+ .attr("r", radius)
4650
+ .style("fill", function(d) { return color(d) } )
4651
+ .on("mouseover", function(evt) {
4652
+ container.select('.nv-series-' + evt.seriesIndex + ' .nv-distx-' + evt.pointIndex)
4653
+ .attr('y1', evt.py);
4654
+ container.select('.nv-series-' + evt.seriesIndex + ' .nv-disty-' + evt.pointIndex)
4655
+ .attr('x2', evt.px);
4656
+
4657
+ // Add 'series' object to
4658
+ var nodeColor = color(evt);
4659
+ evt.series = [];
4660
+ nodeFieldSet.forEach(function(field) {
4661
+ evt.series.push({
4662
+ color: nodeColor,
4663
+ key: field,
4664
+ value: evt[field]
4665
+ });
4666
+ });
4667
+ tooltip.data(evt).hidden(false);
4668
+ })
4669
+ .on("mouseout", function(d) {
4670
+ tooltip.hidden(true);
4671
+ });
4672
+
4673
+ tooltip.headerFormatter(function(d) {return "Node";});
4674
+
4675
+ // Apply extra attributes to nodes and links (if any)
4676
+ linkExtras(link);
4677
+ nodeExtras(node);
4678
+
4679
+ force.on("tick", function() {
4680
+ link.attr("x1", function(d) { return d.source.x; })
4681
+ .attr("y1", function(d) { return d.source.y; })
4682
+ .attr("x2", function(d) { return d.target.x; })
4683
+ .attr("y2", function(d) { return d.target.y; });
4684
+
4685
+ node.attr("transform", function(d) {
4686
+ return "translate(" + d.x + ", " + d.y + ")";
4687
+ });
4688
+ });
4689
+ });
4690
+
4691
+ return chart;
4692
+ }
4693
+
4694
+ //============================================================
4695
+ // Expose Public Variables
4696
+ //------------------------------------------------------------
4697
+
4698
+ chart.options = nv.utils.optionsFunc.bind(chart);
4699
+
4700
+ chart._options = Object.create({}, {
4701
+ // simple options, just get/set the necessary values
4702
+ width: {get: function(){return width;}, set: function(_){width=_;}},
4703
+ height: {get: function(){return height;}, set: function(_){height=_;}},
4704
+
4705
+ // Force directed graph specific parameters
4706
+ linkStrength:{get: function(){return linkStrength;}, set: function(_){linkStrength=_;}},
4707
+ friction: {get: function(){return friction;}, set: function(_){friction=_;}},
4708
+ linkDist: {get: function(){return linkDist;}, set: function(_){linkDist=_;}},
4709
+ charge: {get: function(){return charge;}, set: function(_){charge=_;}},
4710
+ gravity: {get: function(){return gravity;}, set: function(_){gravity=_;}},
4711
+ theta: {get: function(){return theta;}, set: function(_){theta=_;}},
4712
+ alpha: {get: function(){return alpha;}, set: function(_){alpha=_;}},
4713
+ radius: {get: function(){return radius;}, set: function(_){radius=_;}},
4714
+
4715
+ //functor options
4716
+ x: {get: function(){return getX;}, set: function(_){getX=d3.functor(_);}},
4717
+ y: {get: function(){return getY;}, set: function(_){getY=d3.functor(_);}},
4718
+
4719
+ // options that require extra logic in the setter
4720
+ margin: {get: function(){return margin;}, set: function(_){
4721
+ margin.top = _.top !== undefined ? _.top : margin.top;
4722
+ margin.right = _.right !== undefined ? _.right : margin.right;
4723
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
4724
+ margin.left = _.left !== undefined ? _.left : margin.left;
4725
+ }},
4726
+ color: {get: function(){return color;}, set: function(_){
4727
+ color = nv.utils.getColor(_);
4728
+ }},
4729
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
4730
+ nodeExtras: {get: function(){return nodeExtras;}, set: function(_){
4731
+ nodeExtras = _;
4732
+ }},
4733
+ linkExtras: {get: function(){return linkExtras;}, set: function(_){
4734
+ linkExtras = _;
4735
+ }}
4736
+ });
4737
+
4738
+ chart.dispatch = dispatch;
4739
+ chart.tooltip = tooltip;
4740
+ nv.utils.initOptions(chart);
4741
+ return chart;
4742
+ };
4497
4743
  nv.models.furiousLegend = function() {
4498
4744
  "use strict";
4499
4745
 
@@ -4675,13 +4921,13 @@ nv.models.furiousLegend = function() {
4675
4921
  var seriesWidths = [];
4676
4922
  series.each(function(d,i) {
4677
4923
  var legendText;
4678
- if (getKey(d).length > maxKeyLength) {
4924
+ if (getKey(d) && (getKey(d).length > maxKeyLength)) {
4679
4925
  var trimmedKey = getKey(d).substring(0, maxKeyLength);
4680
4926
  legendText = d3.select(this).select('text').text(trimmedKey + "...");
4681
4927
  d3.select(this).append("svg:title").text(getKey(d));
4682
4928
  } else {
4683
4929
  legendText = d3.select(this).select('text');
4684
- }
4930
+ }
4685
4931
  var nodeTextLength;
4686
4932
  try {
4687
4933
  nodeTextLength = legendText.node().getComputedTextLength();
@@ -5180,7 +5426,9 @@ nv.models.historicalBarChart = function(bar_model) {
5180
5426
  gEnter.append('g').attr('class', 'nv-interactive');
5181
5427
 
5182
5428
  // Legend
5183
- if (showLegend) {
5429
+ if (!showLegend) {
5430
+ g.select('.nv-legendWrap').selectAll('*').remove();
5431
+ } else {
5184
5432
  legend.width(availableWidth);
5185
5433
 
5186
5434
  g.select('.nv-legendWrap')
@@ -5534,7 +5782,7 @@ nv.models.legend = function() {
5534
5782
  .attr('class','nv-legend-symbol')
5535
5783
  .attr('r', 5);
5536
5784
 
5537
- seriesShape = series.select('circle');
5785
+ seriesShape = series.select('.nv-legend-symbol');
5538
5786
  } else if (vers == 'furious') {
5539
5787
  seriesEnter.append('rect')
5540
5788
  .style('stroke-width', 2)
@@ -5651,13 +5899,13 @@ nv.models.legend = function() {
5651
5899
  var seriesWidths = [];
5652
5900
  series.each(function(d,i) {
5653
5901
  var legendText;
5654
- if (getKey(d).length > maxKeyLength) {
5902
+ if (getKey(d) && (getKey(d).length > maxKeyLength)) {
5655
5903
  var trimmedKey = getKey(d).substring(0, maxKeyLength);
5656
5904
  legendText = d3.select(this).select('text').text(trimmedKey + "...");
5657
5905
  d3.select(this).append("svg:title").text(getKey(d));
5658
5906
  } else {
5659
5907
  legendText = d3.select(this).select('text');
5660
- }
5908
+ }
5661
5909
  var nodeTextLength;
5662
5910
  try {
5663
5911
  nodeTextLength = legendText.node().getComputedTextLength();
@@ -6104,6 +6352,7 @@ nv.models.lineChart = function() {
6104
6352
  , width = null
6105
6353
  , height = null
6106
6354
  , showLegend = true
6355
+ , legendPosition = 'top'
6107
6356
  , showXAxis = true
6108
6357
  , showYAxis = true
6109
6358
  , rightAlignYAxis = false
@@ -6254,20 +6503,27 @@ nv.models.lineChart = function() {
6254
6503
  contextEnter.append('g').attr('class', 'nv-x nv-brush');
6255
6504
 
6256
6505
  // Legend
6257
- if (showLegend) {
6506
+ if (!showLegend) {
6507
+ g.select('.nv-legendWrap').selectAll('*').remove();
6508
+ } else {
6258
6509
  legend.width(availableWidth);
6259
6510
 
6260
6511
  g.select('.nv-legendWrap')
6261
6512
  .datum(data)
6262
6513
  .call(legend);
6263
6514
 
6264
- if ( margin.top != legend.height()) {
6265
- margin.top = legend.height();
6266
- availableHeight1 = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focusHeight : 0);
6267
- }
6515
+ if (legendPosition === 'bottom') {
6516
+ wrap.select('.nv-legendWrap')
6517
+ .attr('transform', 'translate(0,' + (availableHeight1) +')');
6518
+ } else if (legendPosition === 'top') {
6519
+ if ( margin.top != legend.height()) {
6520
+ margin.top = legend.height();
6521
+ availableHeight1 = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focusHeight : 0);
6522
+ }
6268
6523
 
6269
- wrap.select('.nv-legendWrap')
6270
- .attr('transform', 'translate(0,' + (-margin.top) +')');
6524
+ wrap.select('.nv-legendWrap')
6525
+ .attr('transform', 'translate(0,' + (-margin.top) +')');
6526
+ }
6271
6527
  }
6272
6528
 
6273
6529
  wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
@@ -6491,11 +6747,13 @@ nv.models.lineChart = function() {
6491
6747
  allData[indexToHighlight].highlight = true;
6492
6748
  }
6493
6749
 
6750
+ var defaultValueFormatter = function(d,i) {
6751
+ return d == null ? "N/A" : yAxis.tickFormat()(d);
6752
+ };
6753
+
6494
6754
  interactiveLayer.tooltip
6495
6755
  .chartContainer(chart.container.parentNode)
6496
- .valueFormatter(function(d,i) {
6497
- return d === null ? "N/A" : yAxis.tickFormat()(d);
6498
- })
6756
+ .valueFormatter(interactiveLayer.tooltip.valueFormatter() || defaultValueFormatter)
6499
6757
  .data({
6500
6758
  value: chart.x()( singlePoint,pointIndex ),
6501
6759
  index: pointIndex,
@@ -6669,6 +6927,7 @@ nv.models.lineChart = function() {
6669
6927
  width: {get: function(){return width;}, set: function(_){width=_;}},
6670
6928
  height: {get: function(){return height;}, set: function(_){height=_;}},
6671
6929
  showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
6930
+ legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}},
6672
6931
  showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
6673
6932
  showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
6674
6933
  focusEnable: {get: function(){return focusEnable;}, set: function(_){focusEnable=_;}},
@@ -6749,8 +7008,7 @@ nv.models.lineWithFocusChart = function() {
6749
7008
  return nv.models.lineChart()
6750
7009
  .margin({ bottom: 30 })
6751
7010
  .focusEnable( true );
6752
- };
6753
- nv.models.linePlusBarChart = function() {
7011
+ };nv.models.linePlusBarChart = function() {
6754
7012
  "use strict";
6755
7013
 
6756
7014
  //============================================================
@@ -6822,15 +7080,15 @@ nv.models.linePlusBarChart = function() {
6822
7080
  //------------------------------------------------------------
6823
7081
 
6824
7082
  var getBarsAxis = function() {
6825
- return switchYAxisOrder
6826
- ? { main: y1Axis, focus: y3Axis }
6827
- : { main: y2Axis, focus: y4Axis }
7083
+ return !switchYAxisOrder
7084
+ ? { main: y2Axis, focus: y4Axis }
7085
+ : { main: y1Axis, focus: y3Axis }
6828
7086
  }
6829
7087
 
6830
7088
  var getLinesAxis = function() {
6831
- return switchYAxisOrder
6832
- ? { main: y2Axis, focus: y4Axis }
6833
- : { main: y1Axis, focus: y3Axis }
7089
+ return !switchYAxisOrder
7090
+ ? { main: y1Axis, focus: y3Axis }
7091
+ : { main: y2Axis, focus: y4Axis }
6834
7092
  }
6835
7093
 
6836
7094
  var stateGetter = function(data) {
@@ -6900,7 +7158,12 @@ nv.models.linePlusBarChart = function() {
6900
7158
  var dataBars = data.filter(function(d) { return !d.disabled && d.bar });
6901
7159
  var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240
6902
7160
 
6903
- x = bars.xScale();
7161
+ if (dataBars.length && !switchYAxisOrder) {
7162
+ x = bars.xScale();
7163
+ } else {
7164
+ x = lines.xScale();
7165
+ }
7166
+
6904
7167
  x2 = x2Axis.scale();
6905
7168
 
6906
7169
  // select the scales and series based on the position of the yAxis
@@ -6959,7 +7222,9 @@ nv.models.linePlusBarChart = function() {
6959
7222
  // Legend
6960
7223
  //------------------------------------------------------------
6961
7224
 
6962
- if (showLegend) {
7225
+ if (!showLegend) {
7226
+ g.select('.nv-legendWrap').selectAll('*').remove();
7227
+ } else {
6963
7228
  var legendWidth = legend.align() ? availableWidth / 2 : availableWidth;
6964
7229
  var legendXPosition = legend.align() ? legendWidth : 0;
6965
7230
 
@@ -7234,8 +7499,14 @@ nv.models.linePlusBarChart = function() {
7234
7499
  .tickSize(-availableWidth, 0);
7235
7500
  y2Axis
7236
7501
  .scale(y2)
7237
- ._ticks( nv.utils.calcTicksY(availableHeight1/36, data) )
7238
- .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
7502
+ ._ticks( nv.utils.calcTicksY(availableHeight1/36, data) );
7503
+
7504
+ // Show the y2 rules only if y1 has none
7505
+ if(!switchYAxisOrder) {
7506
+ y2Axis.tickSize(dataBars.length ? 0 : -availableWidth, 0);
7507
+ } else {
7508
+ y2Axis.tickSize(dataLines.length ? 0 : -availableWidth, 0);
7509
+ }
7239
7510
 
7240
7511
  // Calculate opacity of the axis
7241
7512
  var barsOpacity = dataBars.length ? 1 : 0;
@@ -7379,11 +7650,20 @@ nv.models.linePlusBarChart = function() {
7379
7650
  switchYAxisOrder: {get: function(){return switchYAxisOrder;}, set: function(_){
7380
7651
  // Switch the tick format for the yAxis
7381
7652
  if(switchYAxisOrder !== _) {
7382
- var tickFormat = y1Axis.tickFormat();
7383
- y1Axis.tickFormat(y2Axis.tickFormat());
7384
- y2Axis.tickFormat(tickFormat);
7653
+ var y1 = y1Axis;
7654
+ y1Axis = y2Axis;
7655
+ y2Axis = y1;
7656
+
7657
+ var y3 = y3Axis;
7658
+ y3Axis = y4Axis;
7659
+ y4Axis = y3;
7385
7660
  }
7386
7661
  switchYAxisOrder=_;
7662
+
7663
+ y1Axis.orient('left');
7664
+ y2Axis.orient('right');
7665
+ y3Axis.orient('left');
7666
+ y4Axis.orient('right');
7387
7667
  }}
7388
7668
  });
7389
7669
 
@@ -7495,7 +7775,7 @@ nv.models.multiBar = function() {
7495
7775
  });
7496
7776
 
7497
7777
  // HACK for negative value stacking
7498
- if (stacked) {
7778
+ if (stacked && data.length > 0) {
7499
7779
  data[0].values.map(function(d,i) {
7500
7780
  var posBase = 0, negBase = 0;
7501
7781
  data.map(function(d, idx) {
@@ -7819,7 +8099,8 @@ nv.models.multiBar = function() {
7819
8099
  nv.utils.initOptions(chart);
7820
8100
 
7821
8101
  return chart;
7822
- };nv.models.multiBarChart = function() {
8102
+ };
8103
+ nv.models.multiBarChart = function() {
7823
8104
  "use strict";
7824
8105
 
7825
8106
  //============================================================
@@ -7979,7 +8260,9 @@ nv.models.multiBar = function() {
7979
8260
  gEnter.append('g').attr('class', 'nv-interactive');
7980
8261
 
7981
8262
  // Legend
7982
- if (showLegend) {
8263
+ if (!showLegend) {
8264
+ g.select('.nv-legendWrap').selectAll('*').remove();
8265
+ } else {
7983
8266
  legend.width(availableWidth - controlWidth());
7984
8267
 
7985
8268
  g.select('.nv-legendWrap')
@@ -7996,7 +8279,9 @@ nv.models.multiBar = function() {
7996
8279
  }
7997
8280
 
7998
8281
  // Controls
7999
- if (showControls) {
8282
+ if (!showControls) {
8283
+ g.select('.nv-controlsWrap').selectAll('*').remove();
8284
+ } else {
8000
8285
  var controlsData = [
8001
8286
  { key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() },
8002
8287
  { key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() }
@@ -8480,10 +8765,13 @@ nv.models.multiBarHorizontal = function() {
8480
8765
  });
8481
8766
  })
8482
8767
  .on('click', function(d,i) {
8768
+ var element = this;
8483
8769
  dispatch.elementClick({
8484
8770
  data: d,
8485
8771
  index: i,
8486
- color: d3.select(this).style("fill")
8772
+ color: d3.select(this).style("fill"),
8773
+ event: d3.event,
8774
+ element: element
8487
8775
  });
8488
8776
  d3.event.stopPropagation();
8489
8777
  })
@@ -8807,7 +9095,9 @@ nv.models.multiBarHorizontalChart = function() {
8807
9095
  gEnter.append('g').attr('class', 'nv-controlsWrap');
8808
9096
 
8809
9097
  // Legend
8810
- if (showLegend) {
9098
+ if (!showLegend) {
9099
+ g.select('.nv-legendWrap').selectAll('*').remove();
9100
+ } else {
8811
9101
  legend.width(availableWidth - controlWidth());
8812
9102
 
8813
9103
  g.select('.nv-legendWrap')
@@ -8824,7 +9114,9 @@ nv.models.multiBarHorizontalChart = function() {
8824
9114
  }
8825
9115
 
8826
9116
  // Controls
8827
- if (showControls) {
9117
+ if (!showControls) {
9118
+ g.select('.nv-controlsWrap').selectAll('*').remove();
9119
+ } else {
8828
9120
  var controlsData = [
8829
9121
  { key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() },
8830
9122
  { key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() }
@@ -9044,7 +9336,7 @@ nv.models.multiChart = function() {
9044
9336
  yDomain2,
9045
9337
  getX = function(d) { return d.x },
9046
9338
  getY = function(d) { return d.y},
9047
- interpolate = 'monotone',
9339
+ interpolate = 'linear',
9048
9340
  useVoronoi = true,
9049
9341
  interactiveLayer = nv.interactiveGuideline(),
9050
9342
  useInteractiveGuideline = false,
@@ -9124,7 +9416,7 @@ nv.models.multiChart = function() {
9124
9416
  })
9125
9417
  });
9126
9418
 
9127
- x .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return getX(d) }))
9419
+ x .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x }))
9128
9420
  .range([0, availableWidth]);
9129
9421
 
9130
9422
  var wrap = container.selectAll('g.wrap.multiChart').data([data]);
@@ -9150,7 +9442,10 @@ nv.models.multiChart = function() {
9150
9442
  return data[i].color || color(d, i);
9151
9443
  });
9152
9444
 
9153
- if (showLegend) {
9445
+ // Legend
9446
+ if (!showLegend) {
9447
+ g.select('.legendWrap').selectAll('*').remove();
9448
+ } else {
9154
9449
  var legendWidth = legend.align() ? availableWidth / 2 : availableWidth;
9155
9450
  var legendXPosition = legend.align() ? legendWidth : 0;
9156
9451
 
@@ -9203,10 +9498,12 @@ nv.models.multiChart = function() {
9203
9498
  stack1
9204
9499
  .width(availableWidth)
9205
9500
  .height(availableHeight)
9501
+ .interpolate(interpolate)
9206
9502
  .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'area'}));
9207
9503
  stack2
9208
9504
  .width(availableWidth)
9209
9505
  .height(availableHeight)
9506
+ .interpolate(interpolate)
9210
9507
  .color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'area'}));
9211
9508
 
9212
9509
  g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
@@ -9323,6 +9620,9 @@ nv.models.multiChart = function() {
9323
9620
  };
9324
9621
  tooltip
9325
9622
  .duration(0)
9623
+ .headerFormatter(function(d, i) {
9624
+ return xAxis.tickFormat()(d, i);
9625
+ })
9326
9626
  .valueFormatter(function(d, i) {
9327
9627
  return yaxis.tickFormat()(d, i);
9328
9628
  })
@@ -9340,6 +9640,9 @@ nv.models.multiChart = function() {
9340
9640
  };
9341
9641
  tooltip
9342
9642
  .duration(100)
9643
+ .headerFormatter(function(d, i) {
9644
+ return xAxis.tickFormat()(d, i);
9645
+ })
9343
9646
  .valueFormatter(function(d, i) {
9344
9647
  return yaxis.tickFormat()(d, i);
9345
9648
  })
@@ -9353,6 +9656,9 @@ nv.models.multiChart = function() {
9353
9656
  evt.point['y'] = stack1.y()(evt.point);
9354
9657
  tooltip
9355
9658
  .duration(0)
9659
+ .headerFormatter(function(d, i) {
9660
+ return xAxis.tickFormat()(d, i);
9661
+ })
9356
9662
  .valueFormatter(function(d, i) {
9357
9663
  return yaxis.tickFormat()(d, i);
9358
9664
  })
@@ -9371,6 +9677,9 @@ nv.models.multiChart = function() {
9371
9677
  };
9372
9678
  tooltip
9373
9679
  .duration(0)
9680
+ .headerFormatter(function(d, i) {
9681
+ return xAxis.tickFormat()(d, i);
9682
+ })
9374
9683
  .valueFormatter(function(d, i) {
9375
9684
  return yaxis.tickFormat()(d, i);
9376
9685
  })
@@ -9433,6 +9742,9 @@ nv.models.multiChart = function() {
9433
9742
 
9434
9743
  interactiveLayer.tooltip
9435
9744
  .chartContainer(chart.container.parentNode)
9745
+ .headerFormatter(function(d, i) {
9746
+ return xAxis.tickFormat()(d, i);
9747
+ })
9436
9748
  .valueFormatter(function(d,i) {
9437
9749
  var yAxis = allData[i].yAxis;
9438
9750
  return d === null ? "N/A" : yAxis.tickFormat()(d);
@@ -9835,8 +10147,11 @@ nv.models.parallelCoordinates = function() {
9835
10147
  var margin = {top: 30, right: 0, bottom: 10, left: 0}
9836
10148
  , width = null
9837
10149
  , height = null
10150
+ , availableWidth = null
10151
+ , availableHeight = null
9838
10152
  , x = d3.scale.ordinal()
9839
10153
  , y = {}
10154
+ , undefinedValuesLabel = "undefined values"
9840
10155
  , dimensionData = []
9841
10156
  , enabledDimensions = []
9842
10157
  , dimensionNames = []
@@ -9859,19 +10174,17 @@ nv.models.parallelCoordinates = function() {
9859
10174
  // Private Variables
9860
10175
  //------------------------------------------------------------
9861
10176
 
9862
-
9863
10177
  var renderWatch = nv.utils.renderWatch(dispatch);
9864
10178
 
9865
10179
  function chart(selection) {
9866
10180
  renderWatch.reset();
9867
10181
  selection.each(function(data) {
9868
10182
  var container = d3.select(this);
9869
- var availableWidth = nv.utils.availableWidth(width, container, margin),
9870
- availableHeight = nv.utils.availableHeight(height, container, margin);
10183
+ availableWidth = nv.utils.availableWidth(width, container, margin);
10184
+ availableHeight = nv.utils.availableHeight(height, container, margin);
9871
10185
 
9872
10186
  nv.utils.initSVG(container);
9873
10187
 
9874
-
9875
10188
  //Convert old data to new format (name, values)
9876
10189
  if (data[0].values === undefined) {
9877
10190
  var newData = [];
@@ -9892,7 +10205,6 @@ nv.models.parallelCoordinates = function() {
9892
10205
  dimensionNames = dimensionData.sort(function (a, b) { return a.currentPosition - b.currentPosition; }).map(function (d) { return d.key });
9893
10206
  enabledDimensions = dimensionData.filter(function (d) { return !d.disabled; });
9894
10207
 
9895
-
9896
10208
  // Setup Scales
9897
10209
  x.rangePoints([0, availableWidth], 1).domain(enabledDimensions.map(function (d) { return d.key; }));
9898
10210
 
@@ -9900,7 +10212,8 @@ nv.models.parallelCoordinates = function() {
9900
10212
  // Extract the list of dimensions and create a scale for each.
9901
10213
  var oldDomainMaxValue = {};
9902
10214
  var displayMissingValuesline = false;
9903
-
10215
+ var currentTicks = [];
10216
+
9904
10217
  dimensionNames.forEach(function(d) {
9905
10218
  var extent = d3.extent(dataValues, function (p) { return +p[d]; });
9906
10219
  var min = extent[0];
@@ -9943,7 +10256,6 @@ nv.models.parallelCoordinates = function() {
9943
10256
  .range([(availableHeight - 12) * 0.9, 0]);
9944
10257
 
9945
10258
  axisWithUndefinedValues = [];
9946
-
9947
10259
  y[d].brush = d3.svg.brush().y(y[d]).on('brushstart', brushstart).on('brush', brush).on('brushend', brushend);
9948
10260
  });
9949
10261
 
@@ -9980,8 +10292,8 @@ nv.models.parallelCoordinates = function() {
9980
10292
  .attr("y2", function(d) { return d[3]; });
9981
10293
 
9982
10294
  //Add the text "undefined values" under the missing value line
9983
- missingValueslineText = wrap.select('.missingValuesline').selectAll('text').data(["undefined values"]);
9984
- missingValueslineText.append('text').data(["undefined values"]);
10295
+ missingValueslineText = wrap.select('.missingValuesline').selectAll('text').data([undefinedValuesLabel]);
10296
+ missingValueslineText.append('text').data([undefinedValuesLabel]);
9985
10297
  missingValueslineText.enter().append('text');
9986
10298
  missingValueslineText.exit().remove();
9987
10299
  missingValueslineText.attr("y", availableHeight)
@@ -10007,7 +10319,9 @@ nv.models.parallelCoordinates = function() {
10007
10319
  d3.select(this).classed('hover', true).style("stroke-width", d.strokeWidth + 2 + "px").style("stroke-opacity", 1);
10008
10320
  dispatch.elementMouseover({
10009
10321
  label: d.name,
10010
- color: d.color || color(d, i)
10322
+ color: d.color || color(d, i),
10323
+ values: d.values,
10324
+ dimensions: enabledDimensions
10011
10325
  });
10012
10326
 
10013
10327
  });
@@ -10035,13 +10349,14 @@ nv.models.parallelCoordinates = function() {
10035
10349
 
10036
10350
  // Add an axis and title.
10037
10351
  dimensionsEnter.append('text')
10038
- .attr('class', 'nv-label')
10352
+ .attr('class', 'nv-label')
10039
10353
  .style("cursor", "move")
10040
10354
  .attr('dy', '-1em')
10041
10355
  .attr('text-anchor', 'middle')
10042
10356
  .on("mouseover", function(d, i) {
10043
10357
  dispatch.elementMouseover({
10044
- label: d.tooltip || d.key
10358
+ label: d.tooltip || d.key,
10359
+ color: d.color
10045
10360
  });
10046
10361
  })
10047
10362
  .on("mouseout", function(d, i) {
@@ -10057,10 +10372,6 @@ nv.models.parallelCoordinates = function() {
10057
10372
  dimensionsEnter.append('g').attr('class', 'nv-brushBackground');
10058
10373
  dimensions.exit().remove();
10059
10374
  dimensions.select('.nv-label').text(function (d) { return d.key });
10060
- dimensions.select('.nv-axis')
10061
- .each(function (d, i) {
10062
- d3.select(this).call(axis.scale(y[d.key]).tickFormat(d3.format(d.format)));
10063
- });
10064
10375
 
10065
10376
  // Add and store a brush for each axis.
10066
10377
  restoreBrush(displayBrush);
@@ -10138,27 +10449,30 @@ nv.models.parallelCoordinates = function() {
10138
10449
  });
10139
10450
 
10140
10451
  dimensions.select('.nv-brushBackground')
10141
- .each(function (d) {
10142
- d3.select(this).call(y[d.key].brush);
10452
+ .each(function (d) {
10453
+ d3.select(this).call(y[d.key].brush);
10143
10454
 
10144
- })
10145
- .selectAll('rect')
10146
- .attr('x', -8)
10147
- .attr('width', 16);
10455
+ })
10456
+ .selectAll('rect')
10457
+ .attr('x', -8)
10458
+ .attr('width', 16);
10459
+
10460
+ updateTicks();
10148
10461
  }
10149
10462
 
10150
10463
  // Handles a brush event, toggling the display of foreground lines.
10151
10464
  function brushstart() {
10152
10465
  //If brush aren't visible, show it before brushing again.
10153
10466
  if (displayBrush === false) {
10467
+ displayBrush = true;
10154
10468
  restoreBrush(true);
10155
10469
  }
10156
10470
  }
10157
10471
 
10158
10472
  // Handles a brush event, toggling the display of foreground lines.
10159
10473
  function brush() {
10160
- actives = dimensionNames.filter(function (p) { return !y[p].brush.empty(); }),
10161
- extents = actives.map(function(p) { return y[p].brush.extent(); });
10474
+ actives = dimensionNames.filter(function (p) { return !y[p].brush.empty(); });
10475
+ extents = actives.map(function(p) { return y[p].brush.extent(); });
10162
10476
 
10163
10477
  filters = []; //erase current filters
10164
10478
  actives.forEach(function(d,i) {
@@ -10179,7 +10493,9 @@ nv.models.parallelCoordinates = function() {
10179
10493
  if (isActive) active.push(d);
10180
10494
  return isActive ? null : 'none';
10181
10495
  });
10182
-
10496
+
10497
+ updateTicks();
10498
+
10183
10499
  dispatch.brush({
10184
10500
  filters: filters,
10185
10501
  active: active
@@ -10194,13 +10510,30 @@ nv.models.parallelCoordinates = function() {
10194
10510
  f.hasOnlyNaN = true;
10195
10511
  });
10196
10512
  dispatch.brushEnd(active, hasActiveBrush);
10513
+ }
10514
+ function updateTicks() {
10515
+ dimensions.select('.nv-axis')
10516
+ .each(function (d, i) {
10517
+ var f = filters.filter(function (k) { return k.dimension == d.key; });
10518
+ currentTicks[d.key] = y[d.key].domain();
10519
+
10520
+ //If brush are available, display brush extent
10521
+ if (f.length != 0 && displayBrush)
10522
+ {
10523
+ currentTicks[d.key] = [];
10524
+ if (f[0].extent[1] > y[d.key].domain()[0])
10525
+ currentTicks[d.key] = [f[0].extent[1]];
10526
+ if (f[0].extent[0] >= y[d.key].domain()[0])
10527
+ currentTicks[d.key].push(f[0].extent[0]);
10528
+ }
10529
+
10530
+ d3.select(this).call(axis.scale(y[d.key]).tickFormat(d.format).tickValues(currentTicks[d.key]));
10531
+ });
10197
10532
  }
10198
10533
  function dragStart(d) {
10199
10534
  dragging[d.key] = this.parentNode.__origin__ = x(d.key);
10200
10535
  background.attr("visibility", "hidden");
10201
-
10202
10536
  }
10203
-
10204
10537
  function dragMove(d) {
10205
10538
  dragging[d.key] = Math.min(availableWidth, Math.max(0, this.parentNode.__origin__ += d3.event.x));
10206
10539
  foreground.attr("d", path);
@@ -10209,7 +10542,6 @@ nv.models.parallelCoordinates = function() {
10209
10542
  x.domain(enabledDimensions.map(function (d) { return d.key; }));
10210
10543
  dimensions.attr("transform", function(d) { return "translate(" + dimensionPosition(d.key) + ")"; });
10211
10544
  }
10212
-
10213
10545
  function dragEnd(d, i) {
10214
10546
  delete this.parentNode.__origin__;
10215
10547
  delete dragging[d.key];
@@ -10222,22 +10554,11 @@ nv.models.parallelCoordinates = function() {
10222
10554
 
10223
10555
  dispatch.dimensionsOrder(enabledDimensions);
10224
10556
  }
10225
- function resetBrush() {
10226
- filters = [];
10227
- active = [];
10228
- dispatch.stateChange();
10229
- }
10230
- function resetDrag() {
10231
- dimensionName.map(function (d, i) { return d.currentPosition = d.originalPosition; });
10232
- dispatch.stateChange();
10233
- }
10234
-
10235
10557
  function dimensionPosition(d) {
10236
10558
  var v = dragging[d];
10237
10559
  return v == null ? x(d) : v;
10238
10560
  }
10239
10561
  });
10240
-
10241
10562
  return chart;
10242
10563
  }
10243
10564
 
@@ -10257,7 +10578,8 @@ nv.models.parallelCoordinates = function() {
10257
10578
  filters: { get: function () { return filters; }, set: function (_) { filters = _; } },
10258
10579
  active: { get: function () { return active; }, set: function (_) { active = _; } },
10259
10580
  lineTension: {get: function(){return lineTension;}, set: function(_){lineTension = _;}},
10260
-
10581
+ undefinedValuesLabel : {get: function(){return undefinedValuesLabel;}, set: function(_){undefinedValuesLabel=_;}},
10582
+
10261
10583
  // deprecated options
10262
10584
  dimensions: {get: function () { return dimensionData.map(function (d){return d.key}); }, set: function (_) {
10263
10585
  // deprecated after 1.8.1
@@ -10267,8 +10589,7 @@ nv.models.parallelCoordinates = function() {
10267
10589
  } else {
10268
10590
  _.forEach(function (k, i) { dimensionData[i].key= k })
10269
10591
  }
10270
- }
10271
- },
10592
+ }},
10272
10593
  dimensionNames: {get: function () { return dimensionData.map(function (d){return d.key}); }, set: function (_) {
10273
10594
  // deprecated after 1.8.1
10274
10595
  nv.deprecated('dimensionNames', 'use dimensionData instead');
@@ -10290,7 +10611,6 @@ nv.models.parallelCoordinates = function() {
10290
10611
  }
10291
10612
 
10292
10613
  }},
10293
-
10294
10614
  // options that require extra logic in the setter
10295
10615
  margin: {get: function(){return margin;}, set: function(_){
10296
10616
  margin.top = _.top !== undefined ? _.top : margin.top;
@@ -10302,7 +10622,6 @@ nv.models.parallelCoordinates = function() {
10302
10622
  color = nv.utils.getColor(_);
10303
10623
  }}
10304
10624
  });
10305
-
10306
10625
  nv.utils.initOptions(chart);
10307
10626
  return chart;
10308
10627
  };
@@ -10324,10 +10643,10 @@ nv.models.parallelCoordinatesChart = function () {
10324
10643
  , color = nv.utils.defaultColor()
10325
10644
  , state = nv.utils.state()
10326
10645
  , dimensionData = []
10327
- , dimensionNames = []
10328
10646
  , displayBrush = true
10329
10647
  , defaultState = null
10330
10648
  , noData = null
10649
+ , nanValue = "undefined"
10331
10650
  , dispatch = d3.dispatch('dimensionsOrder', 'brushEnd', 'stateChange', 'changeState', 'renderEnd')
10332
10651
  , controlWidth = function () { return showControls ? 180 : 0 }
10333
10652
  ;
@@ -10358,6 +10677,20 @@ nv.models.parallelCoordinatesChart = function () {
10358
10677
  }
10359
10678
  };
10360
10679
 
10680
+ tooltip.contentGenerator(function(data) {
10681
+ var str = '<table><thead><tr><td class="legend-color-guide"><div style="background-color:' + data.color + '"></div></td><td><strong>' + data.key + '</strong></td></tr></thead>';
10682
+ if(data.series.length !== 0)
10683
+ {
10684
+ str = str + '<tbody><tr><td height ="10px"></td></tr>';
10685
+ data.series.forEach(function(d){
10686
+ str = str + '<tr><td class="legend-color-guide"><div style="background-color:' + d.color + '"></div></td><td class="key">' + d.key + '</td><td class="value">' + d.value + '</td></tr>';
10687
+ });
10688
+ str = str + '</tbody>';
10689
+ }
10690
+ str = str + '</table>';
10691
+ return str;
10692
+ });
10693
+
10361
10694
  //============================================================
10362
10695
  // Chart function
10363
10696
  //------------------------------------------------------------
@@ -10392,21 +10725,6 @@ nv.models.parallelCoordinatesChart = function () {
10392
10725
  d.currentPosition = isNaN(d.currentPosition) ? i : d.currentPosition;
10393
10726
  });
10394
10727
 
10395
- var currentDimensions = dimensionNames.map(function (d) { return d.key; });
10396
- var newDimensions = dimensionData.map(function (d) { return d.key; });
10397
- dimensionData.forEach(function (k, i) {
10398
- var idx = currentDimensions.indexOf(k.key);
10399
- if (idx < 0) {
10400
- dimensionNames.splice(i, 0, k);
10401
- } else {
10402
- var gap = dimensionNames[idx].currentPosition - dimensionNames[idx].originalPosition;
10403
- dimensionNames[idx].originalPosition = k.originalPosition;
10404
- dimensionNames[idx].currentPosition = k.originalPosition + gap;
10405
- }
10406
- });
10407
- //Remove old dimensions
10408
- dimensionNames = dimensionNames.filter(function (d) { return newDimensions.indexOf(d.key) >= 0; });
10409
-
10410
10728
  if (!defaultState) {
10411
10729
  var key;
10412
10730
  defaultState = {};
@@ -10442,12 +10760,14 @@ nv.models.parallelCoordinatesChart = function () {
10442
10760
  .attr("height", (availableHeight > 0) ? availableHeight : 0);
10443
10761
 
10444
10762
  // Legend
10445
- if (showLegend) {
10763
+ if (!showLegend) {
10764
+ g.select('.nv-legendWrap').selectAll('*').remove();
10765
+ } else {
10446
10766
  legend.width(availableWidth)
10447
10767
  .color(function (d) { return "rgb(188,190,192)"; });
10448
10768
 
10449
10769
  g.select('.nv-legendWrap')
10450
- .datum(dimensionNames.sort(function (a, b) { return a.originalPosition - b.originalPosition; }))
10770
+ .datum(dimensionData.sort(function (a, b) { return a.originalPosition - b.originalPosition; }))
10451
10771
  .call(legend);
10452
10772
 
10453
10773
  if (margin.top != legend.height()) {
@@ -10459,14 +10779,11 @@ nv.models.parallelCoordinatesChart = function () {
10459
10779
  }
10460
10780
  wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
10461
10781
 
10462
-
10463
-
10464
-
10465
10782
  // Main Chart Component(s)
10466
10783
  parallelCoordinates
10467
10784
  .width(availableWidth)
10468
10785
  .height(availableHeight)
10469
- .dimensionData(dimensionNames)
10786
+ .dimensionData(dimensionData)
10470
10787
  .displayBrush(displayBrush);
10471
10788
 
10472
10789
  var parallelCoordinatesWrap = g.select('.nv-parallelCoordinatesWrap ')
@@ -10498,21 +10815,21 @@ nv.models.parallelCoordinatesChart = function () {
10498
10815
 
10499
10816
  //Update dimensions order and display reset sorting button
10500
10817
  parallelCoordinates.dispatch.on('dimensionsOrder', function (e) {
10501
- dimensionNames.sort(function (a, b) { return a.currentPosition - b.currentPosition; });
10818
+ dimensionData.sort(function (a, b) { return a.currentPosition - b.currentPosition; });
10502
10819
  var isSorted = false;
10503
- dimensionNames.forEach(function (d, i) {
10820
+ dimensionData.forEach(function (d, i) {
10504
10821
  d.currentPosition = i;
10505
10822
  if (d.currentPosition !== d.originalPosition)
10506
10823
  isSorted = true;
10507
10824
  });
10508
- dispatch.dimensionsOrder(dimensionNames, isSorted);
10825
+ dispatch.dimensionsOrder(dimensionData, isSorted);
10509
10826
  });
10510
10827
 
10511
10828
  // Update chart from a state object passed to event handler
10512
10829
  dispatch.on('changeState', function (e) {
10513
10830
 
10514
10831
  if (typeof e.disabled !== 'undefined') {
10515
- dimensionNames.forEach(function (series, i) {
10832
+ dimensionData.forEach(function (series, i) {
10516
10833
  series.disabled = e.disabled[i];
10517
10834
  });
10518
10835
  state.disabled = e.disabled;
@@ -10530,11 +10847,27 @@ nv.models.parallelCoordinatesChart = function () {
10530
10847
  //------------------------------------------------------------
10531
10848
 
10532
10849
  parallelCoordinates.dispatch.on('elementMouseover.tooltip', function (evt) {
10533
- evt['series'] = {
10850
+ var tp = {
10534
10851
  key: evt.label,
10535
- color: evt.color
10536
- };
10537
- tooltip.data(evt).hidden(false);
10852
+ color: evt.color,
10853
+ series: []
10854
+ }
10855
+ if(evt.values){
10856
+ Object.keys(evt.values).forEach(function (d) {
10857
+ var dim = evt.dimensions.filter(function (dd) {return dd.key === d;})[0];
10858
+ if(dim){
10859
+ var v;
10860
+ if (isNaN(evt.values[d]) || isNaN(parseFloat(evt.values[d]))) {
10861
+ v = nanValue;
10862
+ } else {
10863
+ v = dim.format(evt.values[d]);
10864
+ }
10865
+ tp.series.push({ idx: dim.currentPosition, key: d, value: v, color: dim.color });
10866
+ }
10867
+ });
10868
+ tp.series.sort(function(a,b) {return a.idx - b.idx});
10869
+ }
10870
+ tooltip.data(tp).hidden(false);
10538
10871
  });
10539
10872
 
10540
10873
  parallelCoordinates.dispatch.on('elementMouseout.tooltip', function(evt) {
@@ -10553,7 +10886,6 @@ nv.models.parallelCoordinatesChart = function () {
10553
10886
  chart.parallelCoordinates = parallelCoordinates;
10554
10887
  chart.legend = legend;
10555
10888
  chart.tooltip = tooltip;
10556
-
10557
10889
  chart.options = nv.utils.optionsFunc.bind(chart);
10558
10890
 
10559
10891
  chart._options = Object.create({}, {
@@ -10565,7 +10897,8 @@ nv.models.parallelCoordinatesChart = function () {
10565
10897
  dimensionData: { get: function () { return dimensionData; }, set: function (_) { dimensionData = _; } },
10566
10898
  displayBrush: { get: function () { return displayBrush; }, set: function (_) { displayBrush = _; } },
10567
10899
  noData: { get: function () { return noData; }, set: function (_) { noData = _; } },
10568
-
10900
+ nanValue: { get: function () { return nanValue; }, set: function (_) { nanValue = _; } },
10901
+
10569
10902
  // options that require extra logic in the setter
10570
10903
  margin: {
10571
10904
  get: function () { return margin; },
@@ -10587,7 +10920,8 @@ nv.models.parallelCoordinatesChart = function () {
10587
10920
  nv.utils.initOptions(chart);
10588
10921
 
10589
10922
  return chart;
10590
- };nv.models.pie = function() {
10923
+ };
10924
+ nv.models.pie = function() {
10591
10925
  "use strict";
10592
10926
 
10593
10927
  //============================================================
@@ -10649,9 +10983,15 @@ nv.models.parallelCoordinatesChart = function () {
10649
10983
  arcsRadiusInner.push(inner);
10650
10984
  }
10651
10985
  } else {
10652
- arcsRadiusOuter = arcsRadius.map(function (d) { return (d.outer - d.outer / 5) * radius; });
10653
- arcsRadiusInner = arcsRadius.map(function (d) { return (d.inner - d.inner / 5) * radius; });
10654
- donutRatio = d3.min(arcsRadius.map(function (d) { return (d.inner - d.inner / 5); }));
10986
+ if(growOnHover){
10987
+ arcsRadiusOuter = arcsRadius.map(function (d) { return (d.outer - d.outer / 5) * radius; });
10988
+ arcsRadiusInner = arcsRadius.map(function (d) { return (d.inner - d.inner / 5) * radius; });
10989
+ donutRatio = d3.min(arcsRadius.map(function (d) { return (d.inner - d.inner / 5); }));
10990
+ } else {
10991
+ arcsRadiusOuter = arcsRadius.map(function (d) { return d.outer * radius; });
10992
+ arcsRadiusInner = arcsRadius.map(function (d) { return d.inner * radius; });
10993
+ donutRatio = d3.min(arcsRadius.map(function (d) { return d.inner; }));
10994
+ }
10655
10995
  }
10656
10996
  nv.utils.initSVG(container);
10657
10997
 
@@ -11119,7 +11459,9 @@ nv.models.pieChart = function() {
11119
11459
  gEnter.append('g').attr('class', 'nv-legendWrap');
11120
11460
 
11121
11461
  // Legend
11122
- if (showLegend) {
11462
+ if (!showLegend) {
11463
+ g.select('.nv-legendWrap').selectAll('*').remove();
11464
+ } else {
11123
11465
  if (legendPosition === "top") {
11124
11466
  legend.width( availableWidth ).key(pie.x());
11125
11467
 
@@ -11219,6 +11561,8 @@ nv.models.pieChart = function() {
11219
11561
  // use Object get/set functionality to map between vars and chart functions
11220
11562
  chart._options = Object.create({}, {
11221
11563
  // simple options, just get/set the necessary values
11564
+ width: {get: function(){return width;}, set: function(_){width=_;}},
11565
+ height: {get: function(){return height;}, set: function(_){height=_;}},
11222
11566
  noData: {get: function(){return noData;}, set: function(_){noData=_;}},
11223
11567
  showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
11224
11568
  legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}},
@@ -11288,6 +11632,7 @@ nv.models.scatter = function() {
11288
11632
  , useVoronoi = true
11289
11633
  , duration = 250
11290
11634
  , interactiveUpdateDelay = 300
11635
+ , showLabels = false
11291
11636
  ;
11292
11637
 
11293
11638
 
@@ -11300,8 +11645,34 @@ nv.models.scatter = function() {
11300
11645
  , needsUpdate = false // Flag for when the points are visually updating, but the interactive layer is behind, to disable tooltips
11301
11646
  , renderWatch = nv.utils.renderWatch(dispatch, duration)
11302
11647
  , _sizeRange_def = [16, 256]
11648
+ , _caches
11303
11649
  ;
11304
11650
 
11651
+ function getCache(d) {
11652
+ var cache, i;
11653
+ cache = _caches = _caches || {};
11654
+ i = d[0].series;
11655
+ cache = cache[i] = cache[i] || {};
11656
+ i = d[1];
11657
+ cache = cache[i] = cache[i] || {};
11658
+ return cache;
11659
+ }
11660
+
11661
+ function getDiffs(d) {
11662
+ var i, key,
11663
+ point = d[0],
11664
+ cache = getCache(d),
11665
+ diffs = false;
11666
+ for (i = 1; i < arguments.length; i ++) {
11667
+ key = arguments[i];
11668
+ if (cache[key] !== point[key] || !cache.hasOwnProperty(key)) {
11669
+ cache[key] = point[key];
11670
+ diffs = true;
11671
+ }
11672
+ }
11673
+ return diffs;
11674
+ }
11675
+
11305
11676
  function chart(selection) {
11306
11677
  renderWatch.reset();
11307
11678
  selection.each(function(data) {
@@ -11319,6 +11690,7 @@ nv.models.scatter = function() {
11319
11690
  });
11320
11691
 
11321
11692
  // Setup Scales
11693
+ var logScale = chart.yScale().name === d3.scale.log().name ? true : false;
11322
11694
  // remap and flatten the data for use in calculating the scales' domains
11323
11695
  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
11324
11696
  d3.merge(
@@ -11337,7 +11709,7 @@ nv.models.scatter = function() {
11337
11709
  else
11338
11710
  x.range(xRange || [0, availableWidth]);
11339
11711
 
11340
- if (chart.yScale().name === "o") {
11712
+ if (logScale) {
11341
11713
  var min = d3.min(seriesData.map(function(d) { if (d.y !== 0) return d.y; }));
11342
11714
  y.clamp(true)
11343
11715
  .domain(yDomain || d3.extent(seriesData.map(function(d) {
@@ -11378,6 +11750,8 @@ nv.models.scatter = function() {
11378
11750
  y0 = y0 || y;
11379
11751
  z0 = z0 || z;
11380
11752
 
11753
+ var scaleDiff = x(1) !== x0(1) || y(1) !== y0(1) || z(1) !== z0(1);
11754
+
11381
11755
  // Setup containers and skeleton of chart
11382
11756
  var wrap = container.selectAll('g.nv-wrap.nv-scatter').data([data]);
11383
11757
  var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatter nv-chart-' + id);
@@ -11421,8 +11795,8 @@ nv.models.scatter = function() {
11421
11795
  var pX = getX(point,pointIndex);
11422
11796
  var pY = getY(point,pointIndex);
11423
11797
 
11424
- return [x(pX)+ Math.random() * 1e-4,
11425
- y(pY)+ Math.random() * 1e-4,
11798
+ return [nv.utils.NaNtoZero(x(pX))+ Math.random() * 1e-4,
11799
+ nv.utils.NaNtoZero(y(pY))+ Math.random() * 1e-4,
11426
11800
  groupIndex,
11427
11801
  pointIndex, point]; //temp hack to add noise until I think of a better way so there are no duplicates
11428
11802
  })
@@ -11621,6 +11995,7 @@ nv.models.scatter = function() {
11621
11995
  .attr('class', function(d,i) {
11622
11996
  return (d.classed || '') + ' nv-group nv-series-' + i;
11623
11997
  })
11998
+ .classed('nv-noninteractive', !interactive)
11624
11999
  .classed('hover', function(d) { return d.hover });
11625
12000
  groups.watchTransition(renderWatch, 'scatter: groups')
11626
12001
  .style('fill', function(d,i) { return color(d, i) })
@@ -11640,6 +12015,9 @@ nv.models.scatter = function() {
11640
12015
  })
11641
12016
  });
11642
12017
  points.enter().append('path')
12018
+ .attr('class', function (d) {
12019
+ return 'nv-point nv-point-' + d[1];
12020
+ })
11643
12021
  .style('fill', function (d) { return d.color })
11644
12022
  .style('stroke', function (d) { return d.color })
11645
12023
  .attr('transform', function(d) {
@@ -11657,25 +12035,66 @@ nv.models.scatter = function() {
11657
12035
  return 'translate(' + nv.utils.NaNtoZero(x(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
11658
12036
  })
11659
12037
  .remove();
11660
- points.each(function(d) {
11661
- d3.select(this)
11662
- .classed('nv-point', true)
11663
- .classed('nv-point-' + d[1], true)
11664
- .classed('nv-noninteractive', !interactive)
11665
- .classed('hover',false)
11666
- ;
11667
- });
11668
- points
12038
+ points.filter(function (d) { return scaleDiff || getDiffs(d, 'x', 'y'); })
11669
12039
  .watchTransition(renderWatch, 'scatter points')
11670
12040
  .attr('transform', function(d) {
11671
12041
  //nv.log(d, getX(d[0],d[1]), x(getX(d[0],d[1])));
11672
12042
  return 'translate(' + nv.utils.NaNtoZero(x(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
11673
- })
12043
+ });
12044
+ points.filter(function (d) { return scaleDiff || getDiffs(d, 'shape', 'size'); })
12045
+ .watchTransition(renderWatch, 'scatter points')
11674
12046
  .attr('d',
11675
12047
  nv.utils.symbol()
11676
12048
  .type(function(d) { return getShape(d[0]); })
11677
12049
  .size(function(d) { return z(getSize(d[0],d[1])) })
11678
12050
  );
12051
+
12052
+ // add label a label to scatter chart
12053
+ if(showLabels)
12054
+ {
12055
+ var titles = groups.selectAll('.nv-label')
12056
+ .data(function(d) {
12057
+ return d.values.map(
12058
+ function (point, pointIndex) {
12059
+ return [point, pointIndex]
12060
+ }).filter(
12061
+ function(pointArray, pointIndex) {
12062
+ return pointActive(pointArray[0], pointIndex)
12063
+ })
12064
+ });
12065
+
12066
+ titles.enter().append('text')
12067
+ .style('fill', function (d,i) {
12068
+ return d.color })
12069
+ .style('stroke-opacity', 0)
12070
+ .style('fill-opacity', 1)
12071
+ .attr('transform', function(d) {
12072
+ var dx = nv.utils.NaNtoZero(x0(getX(d[0],d[1]))) + Math.sqrt(z(getSize(d[0],d[1]))/Math.PI) + 2;
12073
+ return 'translate(' + dx + ',' + nv.utils.NaNtoZero(y0(getY(d[0],d[1]))) + ')';
12074
+ })
12075
+ .text(function(d,i){
12076
+ return d[0].label;});
12077
+
12078
+ titles.exit().remove();
12079
+ groups.exit().selectAll('path.nv-label')
12080
+ .watchTransition(renderWatch, 'scatter exit')
12081
+ .attr('transform', function(d) {
12082
+ var dx = nv.utils.NaNtoZero(x(getX(d[0],d[1])))+ Math.sqrt(z(getSize(d[0],d[1]))/Math.PI)+2;
12083
+ return 'translate(' + dx + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')';
12084
+ })
12085
+ .remove();
12086
+ titles.each(function(d) {
12087
+ d3.select(this)
12088
+ .classed('nv-label', true)
12089
+ .classed('nv-label-' + d[1], false)
12090
+ .classed('hover',false);
12091
+ });
12092
+ titles.watchTransition(renderWatch, 'scatter labels')
12093
+ .attr('transform', function(d) {
12094
+ var dx = nv.utils.NaNtoZero(x(getX(d[0],d[1])))+ Math.sqrt(z(getSize(d[0],d[1]))/Math.PI)+2;
12095
+ return 'translate(' + dx + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
12096
+ });
12097
+ }
11679
12098
 
11680
12099
  // Delay updating the invisible interactive layer for smoother animation
11681
12100
  if( interactiveUpdateDelay )
@@ -11758,7 +12177,7 @@ nv.models.scatter = function() {
11758
12177
  showVoronoi: {get: function(){return showVoronoi;}, set: function(_){showVoronoi=_;}},
11759
12178
  id: {get: function(){return id;}, set: function(_){id=_;}},
11760
12179
  interactiveUpdateDelay: {get:function(){return interactiveUpdateDelay;}, set: function(_){interactiveUpdateDelay=_;}},
11761
-
12180
+ showLabels: {get: function(){return showLabels;}, set: function(_){ showLabels = _;}},
11762
12181
 
11763
12182
  // simple functor options
11764
12183
  x: {get: function(){return getX;}, set: function(_){getX = d3.functor(_);}},
@@ -11826,6 +12245,7 @@ nv.models.scatterChart = function() {
11826
12245
  , dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd')
11827
12246
  , noData = null
11828
12247
  , duration = 250
12248
+ , showLabels = false
11829
12249
  ;
11830
12250
 
11831
12251
  scatter.xScale(x).yScale(y);
@@ -11947,7 +12367,9 @@ nv.models.scatterChart = function() {
11947
12367
  }
11948
12368
 
11949
12369
  // Legend
11950
- if (showLegend) {
12370
+ if (!showLegend) {
12371
+ g.select('.nv-legendWrap').selectAll('*').remove();
12372
+ } else {
11951
12373
  var legendWidth = availableWidth;
11952
12374
  legend.width(legendWidth);
11953
12375
 
@@ -11973,7 +12395,8 @@ nv.models.scatterChart = function() {
11973
12395
  .color(data.map(function(d,i) {
11974
12396
  d.color = d.color || color(d, i);
11975
12397
  return d.color;
11976
- }).filter(function(d,i) { return !data[i].disabled }));
12398
+ }).filter(function(d,i) { return !data[i].disabled }))
12399
+ .showLabels(showLabels);
11977
12400
 
11978
12401
  wrap.select('.nv-scatterWrap')
11979
12402
  .datum(data.filter(function(d) { return !d.disabled }))
@@ -12151,6 +12574,7 @@ nv.models.scatterChart = function() {
12151
12574
  defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
12152
12575
  noData: {get: function(){return noData;}, set: function(_){noData=_;}},
12153
12576
  duration: {get: function(){return duration;}, set: function(_){duration=_;}},
12577
+ showLabels: {get: function(){return showLabels;}, set: function(_){showLabels=_;}},
12154
12578
 
12155
12579
  // options that require extra logic in the setter
12156
12580
  margin: {get: function(){return margin;}, set: function(_){
@@ -12197,6 +12621,8 @@ nv.models.sparkline = function() {
12197
12621
  , yDomain
12198
12622
  , xRange
12199
12623
  , yRange
12624
+ , showMinMaxPoints = true
12625
+ , showCurrentPoint = true
12200
12626
  , dispatch = d3.dispatch('renderEnd')
12201
12627
  ;
12202
12628
 
@@ -12257,7 +12683,7 @@ nv.models.sparkline = function() {
12257
12683
  var maxPoint = pointIndex(yValues.lastIndexOf(y.domain()[1])),
12258
12684
  minPoint = pointIndex(yValues.indexOf(y.domain()[0])),
12259
12685
  currentPoint = pointIndex(yValues.length - 1);
12260
- return [minPoint, maxPoint, currentPoint].filter(function (d) {return d != null;});
12686
+ return [(showMinMaxPoints ? minPoint : null), (showMinMaxPoints ? maxPoint : null), (showCurrentPoint ? currentPoint : null)].filter(function (d) {return d != null;});
12261
12687
  });
12262
12688
  points.enter().append('circle');
12263
12689
  points.exit().remove();
@@ -12283,15 +12709,17 @@ nv.models.sparkline = function() {
12283
12709
 
12284
12710
  chart._options = Object.create({}, {
12285
12711
  // simple options, just get/set the necessary values
12286
- width: {get: function(){return width;}, set: function(_){width=_;}},
12287
- height: {get: function(){return height;}, set: function(_){height=_;}},
12288
- xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
12289
- yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
12290
- xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
12291
- yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
12292
- xScale: {get: function(){return x;}, set: function(_){x=_;}},
12293
- yScale: {get: function(){return y;}, set: function(_){y=_;}},
12294
- animate: {get: function(){return animate;}, set: function(_){animate=_;}},
12712
+ width: {get: function(){return width;}, set: function(_){width=_;}},
12713
+ height: {get: function(){return height;}, set: function(_){height=_;}},
12714
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
12715
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
12716
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
12717
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
12718
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
12719
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
12720
+ animate: {get: function(){return animate;}, set: function(_){animate=_;}},
12721
+ showMinMaxPoints: {get: function(){return showMinMaxPoints;}, set: function(_){showMinMaxPoints=_;}},
12722
+ showCurrentPoint: {get: function(){return showCurrentPoint;}, set: function(_){showCurrentPoint=_;}},
12295
12723
 
12296
12724
  //functor options
12297
12725
  x: {get: function(){return getX;}, set: function(_){getX=d3.functor(_);}},
@@ -12646,7 +13074,6 @@ nv.models.stackedArea = function() {
12646
13074
  .y(function(d) {
12647
13075
  if (d.display !== undefined) { return d.display.y + d.display.y0; }
12648
13076
  })
12649
- .forceY([0])
12650
13077
  .color(data.map(function(d,i) {
12651
13078
  d.color = d.color || color(d, d.seriesIndex);
12652
13079
  return d.color;
@@ -13021,7 +13448,9 @@ nv.models.stackedAreaChart = function() {
13021
13448
  g.select("rect").attr("width",availableWidth).attr("height",availableHeight);
13022
13449
 
13023
13450
  // Legend
13024
- if (showLegend) {
13451
+ if (!showLegend) {
13452
+ g.select('.nv-legendWrap').selectAll('*').remove();
13453
+ } else {
13025
13454
  var legendWidth = (showControls) ? availableWidth - controlWidth : availableWidth;
13026
13455
 
13027
13456
  legend.width(legendWidth);
@@ -13037,7 +13466,9 @@ nv.models.stackedAreaChart = function() {
13037
13466
  }
13038
13467
 
13039
13468
  // Controls
13040
- if (showControls) {
13469
+ if (!showControls) {
13470
+ g.select('.nv-controlsWrap').selectAll('*').remove();
13471
+ } else {
13041
13472
  var controlsData = [
13042
13473
  {
13043
13474
  key: controlLabels.stacked || 'Stacked',
@@ -13231,7 +13662,7 @@ nv.models.stackedAreaChart = function() {
13231
13662
  key: series.key,
13232
13663
  value: tooltipValue,
13233
13664
  color: color(series,series.seriesIndex),
13234
- stackedValue: point.display
13665
+ point: point
13235
13666
  });
13236
13667
 
13237
13668
  if (showTotalInTooltip && stacked.style() != 'expand') {
@@ -13250,8 +13681,8 @@ nv.models.stackedAreaChart = function() {
13250
13681
  //To handle situation where the stacked area chart is negative, we need to use absolute values
13251
13682
  //when checking if the mouse Y value is within the stack area.
13252
13683
  yValue = Math.abs(yValue);
13253
- var stackedY0 = Math.abs(series.stackedValue.y0);
13254
- var stackedY = Math.abs(series.stackedValue.y);
13684
+ var stackedY0 = Math.abs(series.point.display.y0);
13685
+ var stackedY = Math.abs(series.point.display.y);
13255
13686
  if ( yValue >= stackedY0 && yValue <= (stackedY + stackedY0))
13256
13687
  {
13257
13688
  indexToHighlight = i;
@@ -13423,70 +13854,208 @@ nv.models.sunburst = function() {
13423
13854
  //------------------------------------------------------------
13424
13855
 
13425
13856
  var margin = {top: 0, right: 0, bottom: 0, left: 0}
13426
- , width = null
13427
- , height = null
13857
+ , width = 600
13858
+ , height = 600
13428
13859
  , mode = "count"
13429
- , modes = {count: function(d) { return 1; }, size: function(d) { return d.size }}
13860
+ , modes = {count: function(d) { return 1; }, value: function(d) { return d.value || d.size }, size: function(d) { return d.value || d.size }}
13430
13861
  , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
13431
13862
  , container = null
13432
13863
  , color = nv.utils.defaultColor()
13864
+ , showLabels = false
13865
+ , labelFormat = function(d){if(mode === 'count'){return d.name + ' #' + d.value}else{return d.name + ' ' + (d.value || d.size)}}
13866
+ , labelThreshold = 0.02
13867
+ , sort = function(d1, d2){return d1.name > d2.name;}
13868
+ , key = function(d,i){return d.name;}
13433
13869
  , groupColorByParent = true
13434
13870
  , duration = 500
13435
- , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMousemove', 'elementMouseover', 'elementMouseout', 'renderEnd')
13436
- ;
13871
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMousemove', 'elementMouseover', 'elementMouseout', 'renderEnd');
13872
+
13873
+ //============================================================
13874
+ // aux functions and setup
13875
+ //------------------------------------------------------------
13437
13876
 
13438
13877
  var x = d3.scale.linear().range([0, 2 * Math.PI]);
13439
13878
  var y = d3.scale.sqrt();
13440
13879
 
13441
- var partition = d3.layout.partition()
13442
- .sort(null)
13443
- .value(function(d) { return 1; });
13880
+ var partition = d3.layout.partition().sort(sort);
13881
+
13882
+ var node, availableWidth, availableHeight, radius;
13883
+ var prevPositions = {};
13444
13884
 
13445
13885
  var arc = d3.svg.arc()
13446
- .startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
13447
- .endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
13448
- .innerRadius(function(d) { return Math.max(0, y(d.y)); })
13449
- .outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
13886
+ .startAngle(function(d) {return Math.max(0, Math.min(2 * Math.PI, x(d.x))) })
13887
+ .endAngle(function(d) {return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))) })
13888
+ .innerRadius(function(d) {return Math.max(0, y(d.y)) })
13889
+ .outerRadius(function(d) {return Math.max(0, y(d.y + d.dy)) });
13890
+
13891
+ function rotationToAvoidUpsideDown(d) {
13892
+ var centerAngle = computeCenterAngle(d);
13893
+ if(centerAngle > 90){
13894
+ return 180;
13895
+ }
13896
+ else {
13897
+ return 0;
13898
+ }
13899
+ }
13900
+
13901
+ function computeCenterAngle(d) {
13902
+ var startAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x)));
13903
+ var endAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
13904
+ var centerAngle = (((startAngle + endAngle) / 2) * (180 / Math.PI)) - 90;
13905
+ return centerAngle;
13906
+ }
13907
+
13908
+ function labelThresholdMatched(d) {
13909
+ var startAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x)));
13910
+ var endAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
13911
+
13912
+ var size = endAngle - startAngle;
13913
+ return size > labelThreshold;
13914
+ }
13915
+
13916
+ // When zooming: interpolate the scales.
13917
+ function arcTweenZoom(e,i) {
13918
+ var xd = d3.interpolate(x.domain(), [node.x, node.x + node.dx]),
13919
+ yd = d3.interpolate(y.domain(), [node.y, 1]),
13920
+ yr = d3.interpolate(y.range(), [node.y ? 20 : 0, radius]);
13921
+
13922
+ if (i === 0) {
13923
+ return function() {return arc(e);}
13924
+ }
13925
+ else {
13926
+ return function (t) {
13927
+ x.domain(xd(t));
13928
+ y.domain(yd(t)).range(yr(t));
13929
+ return arc(e);
13930
+ }
13931
+ };
13932
+ }
13933
+
13934
+ function arcTweenUpdate(d) {
13935
+ var ipo = d3.interpolate({x: d.x0, dx: d.dx0, y: d.y0, dy: d.dy0}, d);
13936
+
13937
+ return function (t) {
13938
+ var b = ipo(t);
13939
+
13940
+ d.x0 = b.x;
13941
+ d.dx0 = b.dx;
13942
+ d.y0 = b.y;
13943
+ d.dy0 = b.dy;
13944
+
13945
+ return arc(b);
13946
+ };
13947
+ }
13948
+
13949
+ function updatePrevPosition(node) {
13950
+ var k = key(node);
13951
+ if(! prevPositions[k]) prevPositions[k] = {};
13952
+ var pP = prevPositions[k];
13953
+ pP.dx = node.dx;
13954
+ pP.x = node.x;
13955
+ pP.dy = node.dy;
13956
+ pP.y = node.y;
13957
+ }
13958
+
13959
+ function storeRetrievePrevPositions(nodes) {
13960
+ nodes.forEach(function(n){
13961
+ var k = key(n);
13962
+ var pP = prevPositions[k];
13963
+ //console.log(k,n,pP);
13964
+ if( pP ){
13965
+ n.dx0 = pP.dx;
13966
+ n.x0 = pP.x;
13967
+ n.dy0 = pP.dy;
13968
+ n.y0 = pP.y;
13969
+ }
13970
+ else {
13971
+ n.dx0 = n.dx;
13972
+ n.x0 = n.x;
13973
+ n.dy0 = n.dy;
13974
+ n.y0 = n.y;
13975
+ }
13976
+ updatePrevPosition(n);
13977
+ });
13978
+ }
13450
13979
 
13451
- // Keep track of the current and previous node being displayed as the root.
13452
- var node, prevNode;
13453
- // Keep track of the root node
13454
- var rootNode;
13980
+ function zoomClick(d) {
13981
+ var labels = container.selectAll('text')
13982
+ var path = container.selectAll('path')
13983
+
13984
+ // fade out all text elements
13985
+ labels.transition().attr("opacity",0);
13986
+
13987
+ // to allow reference to the new center node
13988
+ node = d;
13989
+
13990
+ path.transition()
13991
+ .duration(duration)
13992
+ .attrTween("d", arcTweenZoom)
13993
+ .each('end', function(e) {
13994
+ // partially taken from here: http://bl.ocks.org/metmajer/5480307
13995
+ // check if the animated element's data e lies within the visible angle span given in d
13996
+ if(e.x >= d.x && e.x < (d.x + d.dx) ){
13997
+ if(e.depth >= d.depth){
13998
+ // get a selection of the associated text element
13999
+ var parentNode = d3.select(this.parentNode);
14000
+ var arcText = parentNode.select('text');
14001
+
14002
+ // fade in the text element and recalculate positions
14003
+ arcText.transition().duration(duration)
14004
+ .text( function(e){return labelFormat(e) })
14005
+ .attr("opacity", function(d){
14006
+ if(labelThresholdMatched(d)) {
14007
+ return 1;
14008
+ }
14009
+ else {
14010
+ return 0;
14011
+ }
14012
+ })
14013
+ .attr("transform", function() {
14014
+ var width = this.getBBox().width;
14015
+ if(e.depth === 0)
14016
+ return "translate(" + (width / 2 * - 1) + ",0)";
14017
+ else if(e.depth === d.depth){
14018
+ return "translate(" + (y(e.y) + 5) + ",0)";
14019
+ }
14020
+ else {
14021
+ var centerAngle = computeCenterAngle(e);
14022
+ var rotation = rotationToAvoidUpsideDown(e);
14023
+ if (rotation === 0) {
14024
+ return 'rotate('+ centerAngle +')translate(' + (y(e.y) + 5) + ',0)';
14025
+ }
14026
+ else {
14027
+ return 'rotate('+ centerAngle +')translate(' + (y(e.y) + width + 5) + ',0)rotate(' + rotation + ')';
14028
+ }
14029
+ }
14030
+ });
14031
+ }
14032
+ }
14033
+ })
14034
+ }
13455
14035
 
13456
14036
  //============================================================
13457
14037
  // chart function
13458
14038
  //------------------------------------------------------------
13459
-
13460
14039
  var renderWatch = nv.utils.renderWatch(dispatch);
13461
14040
 
13462
14041
  function chart(selection) {
13463
14042
  renderWatch.reset();
14043
+
13464
14044
  selection.each(function(data) {
13465
14045
  container = d3.select(this);
13466
- var availableWidth = nv.utils.availableWidth(width, container, margin);
13467
- var availableHeight = nv.utils.availableHeight(height, container, margin);
13468
- var radius = Math.min(availableWidth, availableHeight) / 2;
13469
- var path;
14046
+ availableWidth = nv.utils.availableWidth(width, container, margin);
14047
+ availableHeight = nv.utils.availableHeight(height, container, margin);
14048
+ radius = Math.min(availableWidth, availableHeight) / 2;
13470
14049
 
13471
- nv.utils.initSVG(container);
14050
+ y.range([0, radius]);
13472
14051
 
13473
14052
  // Setup containers and skeleton of chart
13474
- var wrap = container.selectAll('.nv-wrap.nv-sunburst').data(data);
13475
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sunburst nv-chart-' + id);
13476
-
13477
- var g = wrapEnter.selectAll('nv-sunburst');
13478
-
13479
- chart.update = function() {
13480
- if ( duration === 0 ) {
13481
- container.call(chart);
13482
- } else {
13483
- container.transition().duration(duration).call(chart);
13484
- }
13485
- };
13486
- chart.container = this;
13487
-
13488
-
13489
- wrap.attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
14053
+ var wrap = container.select('g.nvd3.nv-wrap.nv-sunburst');
14054
+ if( !wrap[0][0] ) {
14055
+ wrap = container.append('g')
14056
+ .attr('class', 'nvd3 nv-wrap nv-sunburst nv-chart-' + id)
14057
+ .attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
14058
+ }
13490
14059
 
13491
14060
  container.on('click', function (d, i) {
13492
14061
  dispatch.chartClick({
@@ -13497,13 +14066,21 @@ nv.models.sunburst = function() {
13497
14066
  });
13498
14067
  });
13499
14068
 
13500
- y.range([0, radius]);
13501
-
13502
- node = node || data;
13503
- rootNode = data[0];
13504
14069
  partition.value(modes[mode] || modes["count"]);
13505
- path = g.data(partition.nodes).enter()
13506
- .append("path")
14070
+
14071
+ //reverse the drawing order so that the labels of inner
14072
+ //arcs are drawn on top of the outer arcs.
14073
+ var nodes = partition.nodes(data[0]).reverse()
14074
+
14075
+ storeRetrievePrevPositions(nodes);
14076
+ var cG = wrap.selectAll('.arc-container').data(nodes, key)
14077
+
14078
+ //handle new datapoints
14079
+ var cGE = cG.enter()
14080
+ .append("g")
14081
+ .attr("class",'arc-container')
14082
+
14083
+ cGE.append("path")
13507
14084
  .attr("d", arc)
13508
14085
  .style("fill", function (d) {
13509
14086
  if (d.color) {
@@ -13517,22 +14094,7 @@ nv.models.sunburst = function() {
13517
14094
  }
13518
14095
  })
13519
14096
  .style("stroke", "#FFF")
13520
- .on("click", function(d) {
13521
- if (prevNode !== node && node !== d) prevNode = node;
13522
- node = d;
13523
- path.transition()
13524
- .duration(duration)
13525
- .attrTween("d", arcTweenZoom(d));
13526
- })
13527
- .each(stash)
13528
- .on("dblclick", function(d) {
13529
- if (prevNode.parent == d) {
13530
- path.transition()
13531
- .duration(duration)
13532
- .attrTween("d", arcTweenZoom(rootNode));
13533
- }
13534
- })
13535
- .each(stash)
14097
+ .on("click", zoomClick)
13536
14098
  .on('mouseover', function(d,i){
13537
14099
  d3.select(this).classed('hover', true).style('opacity', 0.8);
13538
14100
  dispatch.elementMouseover({
@@ -13552,58 +14114,68 @@ nv.models.sunburst = function() {
13552
14114
  });
13553
14115
  });
13554
14116
 
14117
+ ///Iterating via each and selecting based on the this
14118
+ ///makes it work ... a cG.selectAll('path') doesn't.
14119
+ ///Without iteration the data (in the element) didn't update.
14120
+ cG.each(function(d){
14121
+ d3.select(this).select('path')
14122
+ .transition()
14123
+ .duration(duration)
14124
+ .attrTween('d', arcTweenUpdate);
14125
+ });
13555
14126
 
14127
+ if(showLabels){
14128
+ //remove labels first and add them back
14129
+ cG.selectAll('text').remove();
13556
14130
 
13557
- // Setup for switching data: stash the old values for transition.
13558
- function stash(d) {
13559
- d.x0 = d.x;
13560
- d.dx0 = d.dx;
14131
+ //this way labels are on top of newly added arcs
14132
+ cG.append('text')
14133
+ .text( function(e){ return labelFormat(e)})
14134
+ .transition()
14135
+ .duration(duration)
14136
+ .attr("opacity", function(d){
14137
+ if(labelThresholdMatched(d)) {
14138
+ return 1;
14139
+ }
14140
+ else {
14141
+ return 0;
14142
+ }
14143
+ })
14144
+ .attr("transform", function(d) {
14145
+ var width = this.getBBox().width;
14146
+ if(d.depth === 0){
14147
+ return "rotate(0)translate(" + (width / 2 * -1) + ",0)";
14148
+ }
14149
+ else {
14150
+ var centerAngle = computeCenterAngle(d);
14151
+ var rotation = rotationToAvoidUpsideDown(d);
14152
+ if (rotation === 0) {
14153
+ return 'rotate('+ centerAngle +')translate(' + (y(d.y) + 5) + ',0)';
14154
+ }
14155
+ else {
14156
+ return 'rotate('+ centerAngle +')translate(' + (y(d.y) + width + 5) + ',0)rotate(' + rotation + ')';
14157
+ }
14158
+ }
14159
+ });
13561
14160
  }
13562
14161
 
13563
- // When switching data: interpolate the arcs in data space.
13564
- function arcTweenData(a, i) {
13565
- var oi = d3.interpolate({x: a.x0, dx: a.dx0}, a);
13566
-
13567
- function tween(t) {
13568
- var b = oi(t);
13569
- a.x0 = b.x;
13570
- a.dx0 = b.dx;
13571
- return arc(b);
13572
- }
13573
-
13574
- if (i == 0) {
13575
- // If we are on the first arc, adjust the x domain to match the root node
13576
- // at the current zoom level. (We only need to do this once.)
13577
- var xd = d3.interpolate(x.domain(), [node.x, node.x + node.dx]);
13578
- return function (t) {
13579
- x.domain(xd(t));
13580
- return tween(t);
13581
- };
13582
- } else {
13583
- return tween;
13584
- }
13585
- }
14162
+ //zoom out to the center when the data is updated.
14163
+ zoomClick(nodes[nodes.length - 1])
13586
14164
 
13587
- // When zooming: interpolate the scales.
13588
- function arcTweenZoom(d) {
13589
- var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
13590
- yd = d3.interpolate(y.domain(), [d.y, 1]),
13591
- yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
13592
- return function (d, i) {
13593
- return i
13594
- ? function (t) {
13595
- return arc(d);
13596
- }
13597
- : function (t) {
13598
- x.domain(xd(t));
13599
- y.domain(yd(t)).range(yr(t));
13600
- return arc(d);
13601
- };
13602
- };
13603
- }
13604
14165
 
14166
+ //remove unmatched elements ...
14167
+ cG.exit()
14168
+ .transition()
14169
+ .duration(duration)
14170
+ .attr('opacity',0)
14171
+ .each('end',function(d){
14172
+ var k = key(d);
14173
+ prevPositions[k] = undefined;
14174
+ })
14175
+ .remove();
13605
14176
  });
13606
14177
 
14178
+
13607
14179
  renderWatch.renderEnd('sunburst immediate');
13608
14180
  return chart;
13609
14181
  }
@@ -13623,7 +14195,11 @@ nv.models.sunburst = function() {
13623
14195
  id: {get: function(){return id;}, set: function(_){id=_;}},
13624
14196
  duration: {get: function(){return duration;}, set: function(_){duration=_;}},
13625
14197
  groupColorByParent: {get: function(){return groupColorByParent;}, set: function(_){groupColorByParent=!!_;}},
13626
-
14198
+ showLabels: {get: function(){return showLabels;}, set: function(_){showLabels=!!_}},
14199
+ labelFormat: {get: function(){return labelFormat;}, set: function(_){labelFormat=_}},
14200
+ labelThreshold: {get: function(){return labelThreshold;}, set: function(_){labelThreshold=_}},
14201
+ sort: {get: function(){return sort;}, set: function(_){sort=_}},
14202
+ key: {get: function(){return key;}, set: function(_){key=_}},
13627
14203
  // options that require extra logic in the setter
13628
14204
  margin: {get: function(){return margin;}, set: function(_){
13629
14205
  margin.top = _.top != undefined ? _.top : margin.top;
@@ -13657,21 +14233,19 @@ nv.models.sunburstChart = function() {
13657
14233
  , defaultState = null
13658
14234
  , noData = null
13659
14235
  , duration = 250
13660
- , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd')
13661
- ;
14236
+ , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd');
13662
14237
 
13663
- tooltip.duration(0);
13664
14238
 
13665
14239
  //============================================================
13666
14240
  // Private Variables
13667
14241
  //------------------------------------------------------------
13668
14242
 
13669
14243
  var renderWatch = nv.utils.renderWatch(dispatch);
14244
+
13670
14245
  tooltip
14246
+ .duration(0)
13671
14247
  .headerEnabled(false)
13672
- .valueFormatter(function(d, i) {
13673
- return d;
13674
- });
14248
+ .valueFormatter(function(d){return d;});
13675
14249
 
13676
14250
  //============================================================
13677
14251
  // Chart function
@@ -13683,11 +14257,11 @@ nv.models.sunburstChart = function() {
13683
14257
 
13684
14258
  selection.each(function(data) {
13685
14259
  var container = d3.select(this);
14260
+
13686
14261
  nv.utils.initSVG(container);
13687
14262
 
13688
- var that = this;
13689
- var availableWidth = nv.utils.availableWidth(width, container, margin),
13690
- availableHeight = nv.utils.availableHeight(height, container, margin);
14263
+ var availableWidth = nv.utils.availableWidth(width, container, margin);
14264
+ var availableHeight = nv.utils.availableHeight(height, container, margin);
13691
14265
 
13692
14266
  chart.update = function() {
13693
14267
  if (duration === 0) {
@@ -13696,7 +14270,7 @@ nv.models.sunburstChart = function() {
13696
14270
  container.transition().duration(duration).call(chart);
13697
14271
  }
13698
14272
  };
13699
- chart.container = this;
14273
+ chart.container = container;
13700
14274
 
13701
14275
  // Display No Data message if there's nothing to show.
13702
14276
  if (!data || !data.length) {
@@ -13706,20 +14280,8 @@ nv.models.sunburstChart = function() {
13706
14280
  container.selectAll('.nv-noData').remove();
13707
14281
  }
13708
14282
 
13709
- // Setup containers and skeleton of chart
13710
- var wrap = container.selectAll('g.nv-wrap.nv-sunburstChart').data(data);
13711
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sunburstChart').append('g');
13712
- var g = wrap.select('g');
13713
-
13714
- gEnter.append('g').attr('class', 'nv-sunburstWrap');
13715
-
13716
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
13717
-
13718
- // Main Chart Component(s)
13719
14283
  sunburst.width(availableWidth).height(availableHeight);
13720
- var sunWrap = g.select('.nv-sunburstWrap').datum(data);
13721
- d3.transition(sunWrap).call(sunburst);
13722
-
14284
+ container.call(sunburst);
13723
14285
  });
13724
14286
 
13725
14287
  renderWatch.renderEnd('sunburstChart immediate');
@@ -13731,9 +14293,9 @@ nv.models.sunburstChart = function() {
13731
14293
  //------------------------------------------------------------
13732
14294
 
13733
14295
  sunburst.dispatch.on('elementMouseover.tooltip', function(evt) {
13734
- evt['series'] = {
14296
+ evt.series = {
13735
14297
  key: evt.data.name,
13736
- value: evt.data.size,
14298
+ value: (evt.data.value || evt.data.size),
13737
14299
  color: evt.color
13738
14300
  };
13739
14301
  tooltip.data(evt).hidden(false);
@@ -13783,7 +14345,8 @@ nv.models.sunburstChart = function() {
13783
14345
  nv.utils.inheritOptions(chart, sunburst);
13784
14346
  nv.utils.initOptions(chart);
13785
14347
  return chart;
14348
+
13786
14349
  };
13787
14350
 
13788
- nv.version = "1.8.2";
14351
+ nv.version = "1.8.3";
13789
14352
  })();