summaryrefslogtreecommitdiffstats
path: root/src/usr
diff options
context:
space:
mode:
authorJared Dillard <jdillard@netgate.com>2016-06-21 17:54:34 -0500
committerJared Dillard <jdillard@netgate.com>2016-06-21 18:24:25 -0500
commit85c7840f22a9c7e2a9b0cf7b64ff51e896adf9cd (patch)
tree50f0b73d8e1daaf9c43536a24bea766ea586a955 /src/usr
parent55a72d312aae1dce2f3130733a5d9ffbb6cb4691 (diff)
downloadpfsense-85c7840f22a9c7e2a9b0cf7b64ff51e896adf9cd.zip
pfsense-85c7840f22a9c7e2a9b0cf7b64ff51e896adf9cd.tar.gz
update nvd3 files
Diffstat (limited to 'src/usr')
-rw-r--r--src/usr/local/www/vendor/nvd3/nv.d3.css26
-rw-r--r--src/usr/local/www/vendor/nvd3/nv.d3.js1671
2 files changed, 1141 insertions, 556 deletions
diff --git a/src/usr/local/www/vendor/nvd3/nv.d3.css b/src/usr/local/www/vendor/nvd3/nv.d3.css
index 252f472..bca7ab6 100644
--- a/src/usr/local/www/vendor/nvd3/nv.d3.css
+++ b/src/usr/local/www/vendor/nvd3/nv.d3.css
@@ -1,4 +1,4 @@
-/* nvd3 version 1.8.2 (https://github.com/novus/nvd3) 2016-01-24 */
+/* nvd3 version 1.8.3 (https://github.com/novus/nvd3) 2016-04-26 */
.nvd3 .nv-axis {
pointer-events:none;
opacity: 1;
@@ -164,6 +164,18 @@
}
+.nv-force-node {
+ stroke: #fff;
+ stroke-width: 1.5px;
+}
+.nv-force-link {
+ stroke: #999;
+ stroke-opacity: .6;
+}
+.nv-force-node text {
+ stroke-width: 0px
+}
+
.nvd3 .nv-legend .nv-disabled rect {
/*fill-opacity: 0;*/
}
@@ -333,6 +345,16 @@ svg.nvd3-svg {
fill-opacity: .7;
}
+/**********
+* Print
+*/
+
+@media print {
+ .nvd3 text {
+ stroke-width: 0;
+ fill-opacity: 1;
+ }
+}
.nvd3.nv-ohlcBar .nv-ticks .nv-tick {
stroke-width: 1px;
@@ -373,7 +395,7 @@ svg.nvd3-svg {
.nvd3 .nv-parallelCoordinates .hover {
fill-opacity: 1;
- stroke-width: 3px;
+ stroke-width: 3px;
}
diff --git a/src/usr/local/www/vendor/nvd3/nv.d3.js b/src/usr/local/www/vendor/nvd3/nv.d3.js
index 3f7b3c3..149621d 100644
--- a/src/usr/local/www/vendor/nvd3/nv.d3.js
+++ b/src/usr/local/www/vendor/nvd3/nv.d3.js
@@ -1,4 +1,4 @@
-/* nvd3 version 1.8.2 (https://github.com/novus/nvd3) 2016-01-24 */
+/* nvd3 version 1.8.3 (https://github.com/novus/nvd3) 2016-04-26 */
(function(){
// set up main nv object
@@ -13,6 +13,11 @@ nv.charts = {}; //stores all the ready to use charts
nv.logs = {}; //stores some statistics and potential error messages
nv.dom = {}; //DOM manipulation functions
+// Node/CommonJS - require D3
+if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined' && typeof(d3) == 'undefined') {
+ d3 = require('d3');
+}
+
nv.dispatch = d3.dispatch('render_start', 'render_end');
// Function bind polyfill
@@ -157,7 +162,7 @@ if (typeof(window) !== 'undefined') {
*/
nv.dom.write = function(callback) {
if (window.fastdom !== undefined) {
- return fastdom.write(callback);
+ return fastdom.mutate(callback);
}
return callback();
};
@@ -170,10 +175,11 @@ nv.dom.write = function(callback) {
*/
nv.dom.read = function(callback) {
if (window.fastdom !== undefined) {
- return fastdom.read(callback);
+ return fastdom.measure(callback);
}
return callback();
-};/* Utility class to handle creation of an interactive layer.
+};
+/* Utility class to handle creation of an interactive layer.
This places a rectangle on top of the chart. When you mouse move over it, it sends a dispatch
containing the X-coordinate. It can also render a vertical line where the mouse is located.
@@ -258,7 +264,8 @@ nv.interactiveGuideline = function() {
/* If mouseX/Y is outside of the chart's bounds,
trigger a mouseOut event.
*/
- if (mouseX < 0 || mouseY < 0
+ if (d3.event.type === 'mouseout'
+ || mouseX < 0 || mouseY < 0
|| mouseX > availableWidth || mouseY > availableHeight
|| (d3.event.relatedTarget && d3.event.relatedTarget.ownerSVGElement === undefined)
|| mouseOutAnyReason
@@ -642,11 +649,11 @@ nv.models.tooltip = function() {
var dataSeriesExists = function(d) {
if (d && d.series) {
- if (d.series instanceof Array) {
- return !!d.series.length;
+ if (nv.utils.isArray(d.series)) {
+ return true;
}
// if object, it's okay just convert to array of the object
- if (d.series instanceof Object) {
+ if (nv.utils.isObject(d.series)) {
d.series = [d.series];
return true;
}
@@ -754,18 +761,23 @@ nv.models.tooltip = function() {
// Creates new tooltip container, or uses existing one on DOM.
function initTooltip() {
- if (!tooltip) {
+ if (!tooltip || !tooltip.node()) {
var container = chartContainer ? chartContainer : document.body;
-
// Create new tooltip div if it doesn't exist on DOM.
- tooltip = d3.select(container).append("div")
- .attr("class", "nvtooltip " + (classes ? classes : "xy-tooltip"))
- .attr("id", id);
- tooltip.style("top", 0).style("left", 0);
- tooltip.style('opacity', 0);
- tooltip.style('position', 'fixed');
- tooltip.selectAll("div, table, td, tr").classed(nvPointerEventsClass, true);
- tooltip.classed(nvPointerEventsClass, true);
+
+ var data = [1];
+ tooltip = d3.select(container).selectAll('.nvtooltip').data(data);
+
+ tooltip.enter().append('div')
+ .attr("class", "nvtooltip " + (classes ? classes : "xy-tooltip"))
+ .attr("id", id)
+ .style("top", 0).style("left", 0)
+ .style('opacity', 0)
+ .style('position', 'fixed')
+ .selectAll("div, table, td, tr").classed(nvPointerEventsClass, true)
+ .classed(nvPointerEventsClass, true);
+
+ tooltip.exit().remove()
}
}
@@ -884,6 +896,25 @@ nv.utils.windowSize = function() {
return (size);
};
+
+/* handle dumb browser quirks... isinstance breaks if you use frames
+typeof returns 'object' for null, NaN is a number, etc.
+ */
+nv.utils.isArray = Array.isArray;
+nv.utils.isObject = function(a) {
+ return a !== null && typeof a === 'object';
+};
+nv.utils.isFunction = function(a) {
+ return typeof a === 'function';
+};
+nv.utils.isDate = function(a) {
+ return toString.call(a) === '[object Date]';
+};
+nv.utils.isNumber = function(a) {
+ return !isNaN(a) && typeof a === 'number';
+};
+
+
/*
Binds callback function to run when window is resized
*/
@@ -915,8 +946,7 @@ nv.utils.getColor = function(color) {
return nv.utils.defaultColor();
//if passed an array, turn it into a color scale
- // use isArray, instanceof fails if d3 range is created in an iframe
- } else if(Array.isArray(color)) {
+ } else if(nv.utils.isArray(color)) {
var color_scale = d3.scale.ordinal().range(color);
return function(d, i) {
var key = i === undefined ? d : i;
@@ -956,7 +986,7 @@ nv.utils.customTheme = function(dictionary, getKey, defaultColors) {
return function(series, index) {
var key = getKey(series);
- if (typeof dictionary[key] === 'function') {
+ if (nv.utils.isFunction(dictionary[key])) {
return dictionary[key]();
} else if (dictionary[key] !== undefined) {
return dictionary[key];
@@ -1010,12 +1040,10 @@ Most common instance is when the element is in a display:none; container.
Forumla is : text.length * font-size * constant_factor
*/
nv.utils.calcApproxTextWidth = function (svgTextElem) {
- if (typeof svgTextElem.style === 'function'
- && typeof svgTextElem.text === 'function') {
-
+ if (nv.utils.isFunction(svgTextElem.style) && nv.utils.isFunction(svgTextElem.text)) {
var fontSize = parseInt(svgTextElem.style("font-size").replace("px",""), 10);
var textLength = svgTextElem.text().length;
- return textLength * fontSize * 0.5;
+ return nv.utils.NaNtoZero(textLength * fontSize * 0.5);
}
return 0;
};
@@ -1025,7 +1053,7 @@ nv.utils.calcApproxTextWidth = function (svgTextElem) {
Numbers that are undefined, null or NaN, convert them to zeros.
*/
nv.utils.NaNtoZero = function(n) {
- if (typeof n !== 'number'
+ if (!nv.utils.isNumber(n)
|| isNaN(n)
|| n === null
|| n === Infinity
@@ -1143,9 +1171,9 @@ nv.utils.deepExtend = function(dst){
var sources = arguments.length > 1 ? [].slice.call(arguments, 1) : [];
sources.forEach(function(source) {
for (var key in source) {
- var isArray = dst[key] instanceof Array;
- var isObject = typeof dst[key] === 'object';
- var srcObj = typeof source[key] === 'object';
+ var isArray = nv.utils.isArray(dst[key]);
+ var isObject = nv.utils.isObject(dst[key]);
+ var srcObj = nv.utils.isObject(source[key]);
if (isObject && !isArray && srcObj) {
nv.utils.deepExtend(dst[key], source[key]);
@@ -1244,7 +1272,7 @@ chart.options = nv.utils.optionsFunc.bind(chart);
nv.utils.optionsFunc = function(args) {
if (args) {
d3.map(args).forEach((function(key,value) {
- if (typeof this[key] === "function") {
+ if (nv.utils.isFunction(this[key])) {
this[key](value);
}
}).bind(this));
@@ -1560,6 +1588,7 @@ nv.utils.arrayEquals = function (array1, array2) {
, isOrdinal = false
, ticks = null
, axisLabelDistance = 0
+ , fontSize = undefined
, duration = 250
, dispatch = d3.dispatch('renderEnd')
;
@@ -1607,6 +1636,11 @@ nv.utils.arrayEquals = function (array1, array2) {
.data([axisLabelText || null]);
axisLabel.exit().remove();
+ //only skip when fontSize is undefined so it can be cleared with a null or blank string
+ if (fontSize !== undefined) {
+ g.selectAll('g').select("text").style('font-size', fontSize);
+ }
+
var xLabelMargin;
var axisMaxMin;
var w;
@@ -1657,6 +1691,8 @@ nv.utils.arrayEquals = function (array1, array2) {
var xTicks = g.selectAll('g').select("text");
var rotateLabelsRule = '';
if (rotateLabels%360) {
+ //Reset transform on ticks so textHeight can be calculated correctly
+ xTicks.attr('transform', '');
//Calculate the longest xTick width
xTicks.each(function(d,i){
var box = this.getBoundingClientRect();
@@ -1896,6 +1932,7 @@ nv.utils.arrayEquals = function (array1, array2) {
height: {get: function(){return height;}, set: function(_){height=_;}},
ticks: {get: function(){return ticks;}, set: function(_){ticks=_;}},
width: {get: function(){return width;}, set: function(_){width=_;}},
+ fontSize: {get: function(){return fontSize;}, set: function(_){fontSize=_;}},
// options that require extra logic in the setter
margin: {get: function(){return margin;}, set: function(_){
@@ -1929,30 +1966,36 @@ nv.models.boxPlot = function() {
// Public Variables with Default Settings
//------------------------------------------------------------
- var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = 960
- , height = 500
- , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
- , x = d3.scale.ordinal()
- , y = d3.scale.linear()
- , getX = function(d) { return d.x }
- , getY = function(d) { return d.y }
- , color = nv.utils.defaultColor()
- , container = null
- , xDomain
- , yDomain
- , xRange
- , yRange
- , dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd')
- , duration = 250
- , maxBoxWidth = null
- ;
+ var margin = {top: 0, right: 0, bottom: 0, left: 0},
+ width = 960,
+ height = 500,
+ id = Math.floor(Math.random() * 10000), // Create semi-unique ID in case user doesn't select one
+ xScale = d3.scale.ordinal(),
+ yScale = d3.scale.linear(),
+ getX = function(d) { return d.label }, // Default data model selectors.
+ getQ1 = function(d) { return d.values.Q1 },
+ getQ2 = function(d) { return d.values.Q2 },
+ getQ3 = function(d) { return d.values.Q3 },
+ getWl = function(d) { return d.values.whisker_low },
+ getWh = function(d) { return d.values.whisker_high },
+ getColor = function(d) { return d.color },
+ getOlItems = function(d) { return d.values.outliers },
+ getOlValue = function(d, i, j) { return d },
+ getOlLabel = function(d, i, j) { return d },
+ getOlColor = function(d, i, j) { return undefined },
+ color = nv.utils.defaultColor(),
+ container = null,
+ xDomain, xRange,
+ yDomain, yRange,
+ dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove', 'renderEnd'),
+ duration = 250,
+ maxBoxWidth = null;
//============================================================
// Private Variables
//------------------------------------------------------------
- var x0, y0;
+ var xScale0, yScale0;
var renderWatch = nv.utils.renderWatch(dispatch, duration);
function chart(selection) {
@@ -1965,45 +2008,38 @@ nv.models.boxPlot = function() {
nv.utils.initSVG(container);
// Setup Scales
- x .domain(xDomain || data.map(function(d,i) { return getX(d,i); }))
- .rangeBands(xRange || [0, availableWidth], .1);
+ xScale.domain(xDomain || data.map(function(d,i) { return getX(d,i); }))
+ .rangeBands(xRange || [0, availableWidth], 0.1);
// if we know yDomain, no need to calculate
var yData = []
if (!yDomain) {
// (y-range is based on quartiles, whiskers and outliers)
-
- // lower values
- var yMin = d3.min(data.map(function(d) {
- var min_arr = [];
-
- min_arr.push(d.values.Q1);
- if (d.values.hasOwnProperty('whisker_low') && d.values.whisker_low !== null) { min_arr.push(d.values.whisker_low); }
- if (d.values.hasOwnProperty('outliers') && d.values.outliers !== null) { min_arr = min_arr.concat(d.values.outliers); }
-
- return d3.min(min_arr);
- }));
-
- // upper values
- var yMax = d3.max(data.map(function(d) {
- var max_arr = [];
-
- max_arr.push(d.values.Q3);
- if (d.values.hasOwnProperty('whisker_high') && d.values.whisker_high !== null) { max_arr.push(d.values.whisker_high); }
- if (d.values.hasOwnProperty('outliers') && d.values.outliers !== null) { max_arr = max_arr.concat(d.values.outliers); }
-
- return d3.max(max_arr);
- }));
-
+ var values = [], yMin, yMax;
+ data.forEach(function (d, i) {
+ var q1 = getQ1(d), q3 = getQ3(d), wl = getWl(d), wh = getWh(d);
+ var olItems = getOlItems(d);
+ if (olItems) {
+ olItems.forEach(function (e, i) {
+ values.push(getOlValue(e, i, undefined));
+ });
+ }
+ if (wl) { values.push(wl) }
+ if (q1) { values.push(q1) }
+ if (q3) { values.push(q3) }
+ if (wh) { values.push(wh) }
+ });
+ yMin = d3.min(values);
+ yMax = d3.max(values);
yData = [ yMin, yMax ] ;
}
- y.domain(yDomain || yData);
- y.range(yRange || [availableHeight, 0]);
+ yScale.domain(yDomain || yData);
+ yScale.range(yRange || [availableHeight, 0]);
//store old scales if they exist
- x0 = x0 || x;
- y0 = y0 || y.copy().range([y(0),y(0)]);
+ xScale0 = xScale0 || xScale;
+ yScale0 = yScale0 || yScale.copy().range([yScale(0),yScale(0)]);
// Setup containers and skeleton of chart
var wrap = container.selectAll('g.nv-wrap').data([data]);
@@ -2014,15 +2050,15 @@ nv.models.boxPlot = function() {
var boxEnter = boxplots.enter().append('g').style('stroke-opacity', 1e-6).style('fill-opacity', 1e-6);
boxplots
.attr('class', 'nv-boxplot')
- .attr('transform', function(d,i,j) { return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05) + ', 0)'; })
+ .attr('transform', function(d,i,j) { return 'translate(' + (xScale(getX(d,i)) + xScale.rangeBand() * 0.05) + ', 0)'; })
.classed('hover', function(d) { return d.hover });
boxplots
.watchTransition(renderWatch, 'nv-boxplot: boxplots')
.style('stroke-opacity', 1)
- .style('fill-opacity', .75)
+ .style('fill-opacity', 0.75)
.delay(function(d,i) { return i * duration / data.length })
.attr('transform', function(d,i) {
- return 'translate(' + (x(getX(d,i)) + x.rangeBand() * .05) + ', 0)';
+ return 'translate(' + (xScale(getX(d,i)) + xScale.rangeBand() * 0.05) + ', 0)';
});
boxplots.exit().remove();
@@ -2030,97 +2066,62 @@ nv.models.boxPlot = function() {
// conditionally append whisker lines
boxEnter.each(function(d,i) {
- var box = d3.select(this);
-
- ['low', 'high'].forEach(function(key) {
- if (d.values.hasOwnProperty('whisker_' + key) && d.values['whisker_' + key] !== null) {
- box.append('line')
- .style('stroke', (d.color) ? d.color : color(d,i))
- .attr('class', 'nv-boxplot-whisker nv-boxplot-' + key);
-
- box.append('line')
- .style('stroke', (d.color) ? d.color : color(d,i))
- .attr('class', 'nv-boxplot-tick nv-boxplot-' + key);
- }
- });
- });
-
- // outliers
- // TODO: support custom colors here
- var outliers = boxplots.selectAll('.nv-boxplot-outlier').data(function(d) {
- if (d.values.hasOwnProperty('outliers') && d.values.outliers !== null) { return d.values.outliers; }
- else { return []; }
- });
- outliers.enter().append('circle')
- .style('fill', function(d,i,j) { return color(d,j) }).style('stroke', function(d,i,j) { return color(d,j) })
- .on('mouseover', function(d,i,j) {
- d3.select(this).classed('hover', true);
- dispatch.elementMouseover({
- series: { key: d, color: color(d,j) },
- e: d3.event
- });
- })
- .on('mouseout', function(d,i,j) {
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- series: { key: d, color: color(d,j) },
- e: d3.event
- });
- })
- .on('mousemove', function(d,i) {
- dispatch.elementMousemove({e: d3.event});
+ var box = d3.select(this);
+ [getWl, getWh].forEach(function (f) {
+ if (f(d)) {
+ var key = (f === getWl) ? 'low' : 'high';
+ box.append('line')
+ .style('stroke', getColor(d) || color(d,i))
+ .attr('class', 'nv-boxplot-whisker nv-boxplot-' + key);
+ box.append('line')
+ .style('stroke', getColor(d) || color(d,i))
+ .attr('class', 'nv-boxplot-tick nv-boxplot-' + key);
+ }
});
+ });
- outliers.attr('class', 'nv-boxplot-outlier');
- outliers
- .watchTransition(renderWatch, 'nv-boxplot: nv-boxplot-outlier')
- .attr('cx', x.rangeBand() * .45)
- .attr('cy', function(d,i,j) { return y(d); })
- .attr('r', '3');
- outliers.exit().remove();
-
- var box_width = function() { return (maxBoxWidth === null ? x.rangeBand() * .9 : Math.min(75, x.rangeBand() * .9)); };
- var box_left = function() { return x.rangeBand() * .45 - box_width()/2; };
- var box_right = function() { return x.rangeBand() * .45 + box_width()/2; };
+ var box_width = function() { return (maxBoxWidth === null ? xScale.rangeBand() * 0.9 : Math.min(75, xScale.rangeBand() * 0.9)); };
+ var box_left = function() { return xScale.rangeBand() * 0.45 - box_width()/2; };
+ var box_right = function() { return xScale.rangeBand() * 0.45 + box_width()/2; };
// update whisker lines and ticks
- ['low', 'high'].forEach(function(key) {
- var endpoint = (key === 'low') ? 'Q1' : 'Q3';
-
- boxplots.select('line.nv-boxplot-whisker.nv-boxplot-' + key)
- .watchTransition(renderWatch, 'nv-boxplot: boxplots')
- .attr('x1', x.rangeBand() * .45 )
- .attr('y1', function(d,i) { return y(d.values['whisker_' + key]); })
- .attr('x2', x.rangeBand() * .45 )
- .attr('y2', function(d,i) { return y(d.values[endpoint]); });
-
- boxplots.select('line.nv-boxplot-tick.nv-boxplot-' + key)
- .watchTransition(renderWatch, 'nv-boxplot: boxplots')
- .attr('x1', box_left )
- .attr('y1', function(d,i) { return y(d.values['whisker_' + key]); })
- .attr('x2', box_right )
- .attr('y2', function(d,i) { return y(d.values['whisker_' + key]); });
+ [getWl, getWh].forEach(function (f) {
+ var key = (f === getWl) ? 'low' : 'high';
+ var endpoint = (f === getWl) ? getQ1 : getQ3;
+ boxplots.select('line.nv-boxplot-whisker.nv-boxplot-' + key)
+ .watchTransition(renderWatch, 'nv-boxplot: boxplots')
+ .attr('x1', xScale.rangeBand() * 0.45 )
+ .attr('y1', function(d,i) { return yScale(f(d)); })
+ .attr('x2', xScale.rangeBand() * 0.45 )
+ .attr('y2', function(d,i) { return yScale(endpoint(d)); });
+ boxplots.select('line.nv-boxplot-tick.nv-boxplot-' + key)
+ .watchTransition(renderWatch, 'nv-boxplot: boxplots')
+ .attr('x1', box_left )
+ .attr('y1', function(d,i) { return yScale(f(d)); })
+ .attr('x2', box_right )
+ .attr('y2', function(d,i) { return yScale(f(d)); });
});
- ['low', 'high'].forEach(function(key) {
- boxEnter.selectAll('.nv-boxplot-' + key)
- .on('mouseover', function(d,i,j) {
- d3.select(this).classed('hover', true);
- dispatch.elementMouseover({
- series: { key: d.values['whisker_' + key], color: color(d,j) },
- e: d3.event
- });
- })
- .on('mouseout', function(d,i,j) {
- d3.select(this).classed('hover', false);
- dispatch.elementMouseout({
- series: { key: d.values['whisker_' + key], color: color(d,j) },
- e: d3.event
- });
- })
- .on('mousemove', function(d,i) {
- dispatch.elementMousemove({e: d3.event});
- });
+ [getWl, getWh].forEach(function (f) {
+ var key = (f === getWl) ? 'low' : 'high';
+ boxEnter.selectAll('.nv-boxplot-' + key)
+ .on('mouseover', function(d,i,j) {
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ series: { key: f(d), color: getColor(d) || color(d,j) },
+ e: d3.event
+ });
+ })
+ .on('mouseout', function(d,i,j) {
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ series: { key: f(d), color: getColor(d) || color(d,j) },
+ e: d3.event
+ });
+ })
+ .on('mousemove', function(d,i) {
+ dispatch.elementMousemove({e: d3.event});
+ });
});
// boxes
@@ -2130,12 +2131,12 @@ nv.models.boxPlot = function() {
.on('mouseover', function(d,i) {
d3.select(this).classed('hover', true);
dispatch.elementMouseover({
- key: d.label,
- value: d.label,
+ key: getX(d),
+ value: getX(d),
series: [
- { key: 'Q3', value: d.values.Q3, color: d.color || color(d,i) },
- { key: 'Q2', value: d.values.Q2, color: d.color || color(d,i) },
- { key: 'Q1', value: d.values.Q1, color: d.color || color(d,i) }
+ { key: 'Q3', value: getQ3(d), color: getColor(d) || color(d,i) },
+ { key: 'Q2', value: getQ2(d), color: getColor(d) || color(d,i) },
+ { key: 'Q1', value: getQ1(d), color: getColor(d) || color(d,i) }
],
data: d,
index: i,
@@ -2145,12 +2146,12 @@ nv.models.boxPlot = function() {
.on('mouseout', function(d,i) {
d3.select(this).classed('hover', false);
dispatch.elementMouseout({
- key: d.label,
- value: d.label,
+ key: getX(d),
+ value: getX(d),
series: [
- { key: 'Q3', value: d.values.Q3, color: d.color || color(d,i) },
- { key: 'Q2', value: d.values.Q2, color: d.color || color(d,i) },
- { key: 'Q1', value: d.values.Q1, color: d.color || color(d,i) }
+ { key: 'Q3', value: getQ3(d), color: getColor(d) || color(d,i) },
+ { key: 'Q2', value: getQ2(d), color: getColor(d) || color(d,i) },
+ { key: 'Q1', value: getQ1(d), color: getColor(d) || color(d,i) }
],
data: d,
index: i,
@@ -2164,13 +2165,12 @@ nv.models.boxPlot = function() {
// box transitions
boxplots.select('rect.nv-boxplot-box')
.watchTransition(renderWatch, 'nv-boxplot: boxes')
- .attr('y', function(d,i) { return y(d.values.Q3); })
+ .attr('y', function(d,i) { return yScale(getQ3(d)); })
.attr('width', box_width)
.attr('x', box_left )
-
- .attr('height', function(d,i) { return Math.abs(y(d.values.Q3) - y(d.values.Q1)) || 1 })
- .style('fill', function(d,i) { return d.color || color(d,i) })
- .style('stroke', function(d,i) { return d.color || color(d,i) });
+ .attr('height', function(d,i) { return Math.abs(yScale(getQ3(d)) - yScale(getQ1(d))) || 1 })
+ .style('fill', function(d,i) { return getColor(d) || color(d,i) })
+ .style('stroke', function(d,i) { return getColor(d) || color(d,i) });
// median line
boxEnter.append('line').attr('class', 'nv-boxplot-median');
@@ -2178,13 +2178,46 @@ nv.models.boxPlot = function() {
boxplots.select('line.nv-boxplot-median')
.watchTransition(renderWatch, 'nv-boxplot: boxplots line')
.attr('x1', box_left)
- .attr('y1', function(d,i) { return y(d.values.Q2); })
+ .attr('y1', function(d,i) { return yScale(getQ2(d)); })
.attr('x2', box_right)
- .attr('y2', function(d,i) { return y(d.values.Q2); });
+ .attr('y2', function(d,i) { return yScale(getQ2(d)); });
+
+ // outliers
+ var outliers = boxplots.selectAll('.nv-boxplot-outlier').data(function(d) {
+ return getOlItems(d) || [];
+ });
+ outliers.enter().append('circle')
+ .style('fill', function(d,i,j) { return getOlColor(d,i,j) || color(d,j) })
+ .style('stroke', function(d,i,j) { return getOlColor(d,i,j) || color(d,j) })
+ .style('z-index', 9000)
+ .on('mouseover', function(d,i,j) {
+ d3.select(this).classed('hover', true);
+ dispatch.elementMouseover({
+ series: { key: getOlLabel(d,i,j), color: getOlColor(d,i,j) || color(d,j) },
+ e: d3.event
+ });
+ })
+ .on('mouseout', function(d,i,j) {
+ d3.select(this).classed('hover', false);
+ dispatch.elementMouseout({
+ series: { key: getOlLabel(d,i,j), color: getOlColor(d,i,j) || color(d,j) },
+ e: d3.event
+ });
+ })
+ .on('mousemove', function(d,i) {
+ dispatch.elementMousemove({e: d3.event});
+ });
+ outliers.attr('class', 'nv-boxplot-outlier');
+ outliers
+ .watchTransition(renderWatch, 'nv-boxplot: nv-boxplot-outlier')
+ .attr('cx', xScale.rangeBand() * 0.45)
+ .attr('cy', function(d,i,j) { return yScale(getOlValue(d,i,j)); })
+ .attr('r', '3');
+ outliers.exit().remove();
//store old scales for use in transitions on update
- x0 = x.copy();
- y0 = y.copy();
+ xScale0 = xScale.copy();
+ yScale0 = yScale.copy();
});
renderWatch.renderEnd('nv-boxplot immediate');
@@ -2200,20 +2233,37 @@ nv.models.boxPlot = function() {
chart._options = Object.create({}, {
// simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
maxBoxWidth: {get: function(){return maxBoxWidth;}, set: function(_){maxBoxWidth=_;}},
- x: {get: function(){return getX;}, set: function(_){getX=_;}},
- y: {get: function(){return getY;}, set: function(_){getY=_;}},
- xScale: {get: function(){return x;}, set: function(_){x=_;}},
- yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ x: {get: function(){return getX;}, set: function(_){getX=_;}},
+ q1: {get: function(){return getQ1;}, set: function(_){getQ1=_;}},
+ q2: {get: function(){return getQ2;}, set: function(_){getQ2=_;}},
+ q3: {get: function(){return getQ3;}, set: function(_){getQ3=_;}},
+ wl: {get: function(){return getWl;}, set: function(_){getWl=_;}},
+ wh: {get: function(){return getWh;}, set: function(_){getWh=_;}},
+ itemColor: {get: function(){return getColor;}, set: function(_){getColor=_;}},
+ outliers: {get: function(){return getOlItems;}, set: function(_){getOlItems=_;}},
+ outlierValue: {get: function(){return getOlValue;}, set: function(_){getOlValue=_;}},
+ outlierLabel: {get: function(){return getOlLabel;}, set: function(_){getOlLabel=_;}},
+ outlierColor: {get: function(){return getOlColor;}, set: function(_){getOlColor=_;}},
+ xScale: {get: function(){return xScale;}, set: function(_){xScale=_;}},
+ yScale: {get: function(){return yScale;}, set: function(_){yScale=_;}},
xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
id: {get: function(){return id;}, set: function(_){id=_;}},
// rectClass: {get: function(){return rectClass;}, set: function(_){rectClass=_;}},
-
+ y: {
+ get: function() {
+ console.warn('BoxPlot \'y\' chart option is deprecated. Please use model overrides instead.');
+ return {};
+ },
+ set: function(_) {
+ console.warn('BoxPlot \'y\' chart option is deprecated. Please use model overrides instead.');
+ }
+ },
// options that require extra logic in the setter
margin: {get: function(){return margin;}, set: function(_){
margin.top = _.top !== undefined ? _.top : margin.top;
@@ -2241,26 +2291,23 @@ nv.models.boxPlotChart = function() {
// Public Variables with Default Settings
//------------------------------------------------------------
- var boxplot = nv.models.boxPlot()
- , xAxis = nv.models.axis()
- , yAxis = nv.models.axis()
- ;
+ var boxplot = nv.models.boxPlot(),
+ xAxis = nv.models.axis(),
+ yAxis = nv.models.axis();
- var margin = {top: 15, right: 10, bottom: 50, left: 60}
- , width = null
- , height = null
- , color = nv.utils.getColor()
- , showXAxis = true
- , showYAxis = true
- , rightAlignYAxis = false
- , staggerLabels = false
- , tooltip = nv.models.tooltip()
- , x
- , y
- , noData = "No Data Available."
- , dispatch = d3.dispatch('beforeUpdate', 'renderEnd')
- , duration = 250
- ;
+ var margin = {top: 15, right: 10, bottom: 50, left: 60},
+ width = null,
+ height = null,
+ color = nv.utils.getColor(),
+ showXAxis = true,
+ showYAxis = true,
+ rightAlignYAxis = false,
+ staggerLabels = false,
+ tooltip = nv.models.tooltip(),
+ x, y,
+ noData = 'No Data Available.',
+ dispatch = d3.dispatch('beforeUpdate', 'renderEnd'),
+ duration = 250;
xAxis
.orient('bottom')
@@ -2287,13 +2334,10 @@ nv.models.boxPlotChart = function() {
if (showYAxis) renderWatch.models(yAxis);
selection.each(function(data) {
- var container = d3.select(this),
- that = this;
+ var container = d3.select(this), that = this;
nv.utils.initSVG(container);
- var availableWidth = (width || parseInt(container.style('width')) || 960)
- - margin.left - margin.right,
- availableHeight = (height || parseInt(container.style('height')) || 400)
- - margin.top - margin.bottom;
+ var availableWidth = (width || parseInt(container.style('width')) || 960) - margin.left - margin.right;
+ var availableHeight = (height || parseInt(container.style('height')) || 400) - margin.top - margin.bottom;
chart.update = function() {
dispatch.beforeUpdate();
@@ -2301,9 +2345,9 @@ nv.models.boxPlotChart = function() {
};
chart.container = this;
- // Display No Data message if there's nothing to show. (quartiles required at minimum)
- if (!data || !data.length ||
- !data.filter(function(d) { return d.values.hasOwnProperty("Q1") && d.values.hasOwnProperty("Q2") && d.values.hasOwnProperty("Q3"); }).length) {
+ // TODO still need to find a way to validate quartile data presence using boxPlot callbacks.
+ // Display No Data message if there's nothing to show. (quartiles required at minimum).
+ if (!data || !data.length) {
var noDataText = container.selectAll('.nv-noData').data([noData]);
noDataText.enter().append('text')
@@ -2337,25 +2381,21 @@ nv.models.boxPlotChart = function() {
.append('line');
gEnter.append('g').attr('class', 'nv-barsWrap');
-
g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
+ g.select('.nv-y.nv-axis')
+ .attr('transform', 'translate(' + availableWidth + ',0)');
}
// Main Chart Component(s)
- boxplot
- .width(availableWidth)
- .height(availableHeight);
+ boxplot.width(availableWidth).height(availableHeight);
var barsWrap = g.select('.nv-barsWrap')
.datum(data.filter(function(d) { return !d.disabled }))
barsWrap.transition().call(boxplot);
-
defsEnter.append('clipPath')
.attr('id', 'nv-x-label-clip-' + boxplot.id())
.append('rect');
@@ -2379,7 +2419,7 @@ nv.models.boxPlotChart = function() {
if (staggerLabels) {
xTicks
.selectAll('text')
- .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 == 0 ? '5' : '17') + ')' })
+ .attr('transform', function(d,i,j) { return 'translate(0,' + (j % 2 === 0 ? '5' : '17') + ')' })
}
}
@@ -2393,11 +2433,11 @@ nv.models.boxPlotChart = function() {
}
// Zero line
- g.select(".nv-zeroLine line")
- .attr("x1",0)
- .attr("x2",availableWidth)
- .attr("y1", y(0))
- .attr("y2", y(0))
+ g.select('.nv-zeroLine line')
+ .attr('x1',0)
+ .attr('x2',availableWidth)
+ .attr('y1', y(0))
+ .attr('y2', y(0))
;
//============================================================
@@ -2504,8 +2544,19 @@ nv.models.bullet = function() {
, tickFormat = null
, color = nv.utils.getColor(['#1f77b4'])
, dispatch = d3.dispatch('elementMouseover', 'elementMouseout', 'elementMousemove')
+ , defaultRangeLabels = ["Maximum", "Mean", "Minimum"]
+ , legacyRangeClassNames = ["Max", "Avg", "Min"]
;
+ function sortLabels(labels, values){
+ var lz = labels.slice();
+ labels.sort(function(a, b){
+ var iA = lz.indexOf(a);
+ var iB = lz.indexOf(b);
+ return d3.descending(values[iA], values[iB]);
+ });
+ };
+
function chart(selection) {
selection.each(function(d, i) {
var availableWidth = width - margin.left - margin.right,
@@ -2514,13 +2565,23 @@ nv.models.bullet = function() {
container = d3.select(this);
nv.utils.initSVG(container);
- var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
- markerz = markers.call(this, d, i).slice().sort(d3.descending),
- measurez = measures.call(this, d, i).slice().sort(d3.descending),
+ var rangez = ranges.call(this, d, i).slice(),
+ markerz = markers.call(this, d, i).slice(),
+ measurez = measures.call(this, d, i).slice(),
rangeLabelz = rangeLabels.call(this, d, i).slice(),
markerLabelz = markerLabels.call(this, d, i).slice(),
measureLabelz = measureLabels.call(this, d, i).slice();
+ // Sort labels according to their sorted values
+ sortLabels(rangeLabelz, rangez);
+ sortLabels(markerLabelz, markerz);
+ sortLabels(measureLabelz, measurez);
+
+ // sort values descending
+ rangez.sort(d3.descending);
+ markerz.sort(d3.descending);
+ measurez.sort(d3.descending);
+
// Setup Scales
// Compute the new x-scale.
var x1 = d3.scale.linear()
@@ -2545,9 +2606,14 @@ nv.models.bullet = function() {
var gEnter = wrapEnter.append('g');
var g = wrap.select('g');
- gEnter.append('rect').attr('class', 'nv-range nv-rangeMax');
- gEnter.append('rect').attr('class', 'nv-range nv-rangeAvg');
- gEnter.append('rect').attr('class', 'nv-range nv-rangeMin');
+ for(var i=0,il=rangez.length; i<il; i++){
+ var rangeClassNames = 'nv-range nv-range'+i;
+ if(i <= 2){
+ rangeClassNames = rangeClassNames + ' nv-range'+legacyRangeClassNames[i];
+ }
+ gEnter.append('rect').attr('class', rangeClassNames);
+ }
+
gEnter.append('rect').attr('class', 'nv-measure');
wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
@@ -2557,25 +2623,14 @@ nv.models.bullet = function() {
var xp0 = function(d) { return d < 0 ? x0(d) : x0(0) },
xp1 = function(d) { return d < 0 ? x1(d) : x1(0) };
- g.select('rect.nv-rangeMax')
- .attr('height', availableHeight)
- .attr('width', w1(rangeMax > 0 ? rangeMax : rangeMin))
- .attr('x', xp1(rangeMax > 0 ? rangeMax : rangeMin))
- .datum(rangeMax > 0 ? rangeMax : rangeMin)
-
- g.select('rect.nv-rangeAvg')
- .attr('height', availableHeight)
- .attr('width', w1(rangeAvg))
- .attr('x', xp1(rangeAvg))
- .datum(rangeAvg)
-
- g.select('rect.nv-rangeMin')
- .attr('height', availableHeight)
- .attr('width', w1(rangeMax))
- .attr('x', xp1(rangeMax))
- .attr('width', w1(rangeMax > 0 ? rangeMin : rangeMax))
- .attr('x', xp1(rangeMax > 0 ? rangeMin : rangeMax))
- .datum(rangeMax > 0 ? rangeMin : rangeMax)
+ for(var i=0,il=rangez.length; i<il; i++){
+ var range = rangez[i];
+ g.select('rect.nv-range'+i)
+ .attr('height', availableHeight)
+ .attr('width', w1(range))
+ .attr('x', xp1(range))
+ .datum(range)
+ }
g.select('rect.nv-measure')
.style('fill', color)
@@ -2649,7 +2704,7 @@ nv.models.bullet = function() {
wrap.selectAll('.nv-range')
.on('mouseover', function(d,i) {
- var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
+ var label = rangeLabelz[i] || defaultRangeLabels[i];
dispatch.elementMouseover({
value: d,
label: label,
@@ -2664,7 +2719,7 @@ nv.models.bullet = function() {
})
})
.on('mouseout', function(d,i) {
- var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
+ var label = rangeLabelz[i] || defaultRangeLabels[i];
dispatch.elementMouseout({
value: d,
label: label,
@@ -3365,7 +3420,9 @@ nv.models.cumulativeLineChart = function() {
gEnter.append('g').attr('class', 'nv-controlsWrap');
// Legend
- if (showLegend) {
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
legend.width(availableWidth);
g.select('.nv-legendWrap')
@@ -3382,7 +3439,9 @@ nv.models.cumulativeLineChart = function() {
}
// Controls
- if (showControls) {
+ if (!showControls) {
+ g.select('.nv-controlsWrap').selectAll('*').remove();
+ } else {
var controlsData = [
{ key: 'Re-scale y-axis', disabled: !rescaleY }
];
@@ -4158,8 +4217,11 @@ nv.models.discreteBarChart = function() {
gEnter.append('g').attr('class', 'nv-legendWrap');
g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- if (showLegend) {
+
+ // Legend
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
legend.width(availableWidth);
g.select('.nv-legendWrap')
@@ -4180,11 +4242,6 @@ nv.models.discreteBarChart = function() {
.attr("transform", "translate(" + availableWidth + ",0)");
}
- if (rightAlignYAxis) {
- g.select(".nv-y.nv-axis")
- .attr("transform", "translate(" + availableWidth + ",0)");
- }
-
// Main Chart Component(s)
discretebar
.width(availableWidth)
@@ -4494,6 +4551,195 @@ nv.models.distribution = function() {
return chart;
}
+nv.models.forceDirectedGraph = function() {
+ "use strict";
+
+ //============================================================
+ // Public Variables with Default Settings
+ //------------------------------------------------------------
+ var margin = {top: 2, right: 0, bottom: 2, left: 0}
+ , width = 400
+ , height = 32
+ , container = null
+ , dispatch = d3.dispatch('renderEnd')
+ , color = nv.utils.getColor(['#000'])
+ , tooltip = nv.models.tooltip()
+ , noData = null
+ // Force directed graph specific parameters [default values]
+ , linkStrength = 0.1
+ , friction = 0.9
+ , linkDist = 30
+ , charge = -120
+ , gravity = 0.1
+ , theta = 0.8
+ , alpha = 0.1
+ , radius = 5
+ // These functions allow to add extra attributes to ndes and links
+ ,nodeExtras = function(nodes) { /* Do nothing */ }
+ ,linkExtras = function(links) { /* Do nothing */ }
+ ;
+
+
+ //============================================================
+ // Private Variables
+ //------------------------------------------------------------
+
+ var renderWatch = nv.utils.renderWatch(dispatch);
+
+ function chart(selection) {
+ renderWatch.reset();
+
+ selection.each(function(data) {
+ container = d3.select(this);
+ nv.utils.initSVG(container);
+
+ var availableWidth = nv.utils.availableWidth(width, container, margin),
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+
+ container
+ .attr("width", availableWidth)
+ .attr("height", availableHeight);
+
+ // Display No Data message if there's nothing to show.
+ if (!data || !data.links || !data.nodes) {
+ nv.utils.noData(chart, container)
+ return chart;
+ } else {
+ container.selectAll('.nv-noData').remove();
+ }
+ container.selectAll('*').remove();
+
+ // Collect names of all fields in the nodes
+ var nodeFieldSet = new Set();
+ data.nodes.forEach(function(node) {
+ var keys = Object.keys(node);
+ keys.forEach(function(key) {
+ nodeFieldSet.add(key);
+ });
+ });
+
+ var force = d3.layout.force()
+ .nodes(data.nodes)
+ .links(data.links)
+ .size([availableWidth, availableHeight])
+ .linkStrength(linkStrength)
+ .friction(friction)
+ .linkDistance(linkDist)
+ .charge(charge)
+ .gravity(gravity)
+ .theta(theta)
+ .alpha(alpha)
+ .start();
+
+ var link = container.selectAll(".link")
+ .data(data.links)
+ .enter().append("line")
+ .attr("class", "nv-force-link")
+ .style("stroke-width", function(d) { return Math.sqrt(d.value); });
+
+ var node = container.selectAll(".node")
+ .data(data.nodes)
+ .enter()
+ .append("g")
+ .attr("class", "nv-force-node")
+ .call(force.drag);
+
+ node
+ .append("circle")
+ .attr("r", radius)
+ .style("fill", function(d) { return color(d) } )
+ .on("mouseover", function(evt) {
+ container.select('.nv-series-' + evt.seriesIndex + ' .nv-distx-' + evt.pointIndex)
+ .attr('y1', evt.py);
+ container.select('.nv-series-' + evt.seriesIndex + ' .nv-disty-' + evt.pointIndex)
+ .attr('x2', evt.px);
+
+ // Add 'series' object to
+ var nodeColor = color(evt);
+ evt.series = [];
+ nodeFieldSet.forEach(function(field) {
+ evt.series.push({
+ color: nodeColor,
+ key: field,
+ value: evt[field]
+ });
+ });
+ tooltip.data(evt).hidden(false);
+ })
+ .on("mouseout", function(d) {
+ tooltip.hidden(true);
+ });
+
+ tooltip.headerFormatter(function(d) {return "Node";});
+
+ // Apply extra attributes to nodes and links (if any)
+ linkExtras(link);
+ nodeExtras(node);
+
+ force.on("tick", function() {
+ link.attr("x1", function(d) { return d.source.x; })
+ .attr("y1", function(d) { return d.source.y; })
+ .attr("x2", function(d) { return d.target.x; })
+ .attr("y2", function(d) { return d.target.y; });
+
+ node.attr("transform", function(d) {
+ return "translate(" + d.x + ", " + d.y + ")";
+ });
+ });
+ });
+
+ return chart;
+ }
+
+ //============================================================
+ // Expose Public Variables
+ //------------------------------------------------------------
+
+ chart.options = nv.utils.optionsFunc.bind(chart);
+
+ chart._options = Object.create({}, {
+ // simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+
+ // Force directed graph specific parameters
+ linkStrength:{get: function(){return linkStrength;}, set: function(_){linkStrength=_;}},
+ friction: {get: function(){return friction;}, set: function(_){friction=_;}},
+ linkDist: {get: function(){return linkDist;}, set: function(_){linkDist=_;}},
+ charge: {get: function(){return charge;}, set: function(_){charge=_;}},
+ gravity: {get: function(){return gravity;}, set: function(_){gravity=_;}},
+ theta: {get: function(){return theta;}, set: function(_){theta=_;}},
+ alpha: {get: function(){return alpha;}, set: function(_){alpha=_;}},
+ radius: {get: function(){return radius;}, set: function(_){radius=_;}},
+
+ //functor options
+ x: {get: function(){return getX;}, set: function(_){getX=d3.functor(_);}},
+ y: {get: function(){return getY;}, set: function(_){getY=d3.functor(_);}},
+
+ // options that require extra logic in the setter
+ margin: {get: function(){return margin;}, set: function(_){
+ margin.top = _.top !== undefined ? _.top : margin.top;
+ margin.right = _.right !== undefined ? _.right : margin.right;
+ margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom;
+ margin.left = _.left !== undefined ? _.left : margin.left;
+ }},
+ color: {get: function(){return color;}, set: function(_){
+ color = nv.utils.getColor(_);
+ }},
+ noData: {get: function(){return noData;}, set: function(_){noData=_;}},
+ nodeExtras: {get: function(){return nodeExtras;}, set: function(_){
+ nodeExtras = _;
+ }},
+ linkExtras: {get: function(){return linkExtras;}, set: function(_){
+ linkExtras = _;
+ }}
+ });
+
+ chart.dispatch = dispatch;
+ chart.tooltip = tooltip;
+ nv.utils.initOptions(chart);
+ return chart;
+};
nv.models.furiousLegend = function() {
"use strict";
@@ -4675,13 +4921,13 @@ nv.models.furiousLegend = function() {
var seriesWidths = [];
series.each(function(d,i) {
var legendText;
- if (getKey(d).length > maxKeyLength) {
+ if (getKey(d) && (getKey(d).length > maxKeyLength)) {
var trimmedKey = getKey(d).substring(0, maxKeyLength);
legendText = d3.select(this).select('text').text(trimmedKey + "...");
d3.select(this).append("svg:title").text(getKey(d));
} else {
legendText = d3.select(this).select('text');
- }
+ }
var nodeTextLength;
try {
nodeTextLength = legendText.node().getComputedTextLength();
@@ -5180,7 +5426,9 @@ nv.models.historicalBarChart = function(bar_model) {
gEnter.append('g').attr('class', 'nv-interactive');
// Legend
- if (showLegend) {
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
legend.width(availableWidth);
g.select('.nv-legendWrap')
@@ -5534,7 +5782,7 @@ nv.models.legend = function() {
.attr('class','nv-legend-symbol')
.attr('r', 5);
- seriesShape = series.select('circle');
+ seriesShape = series.select('.nv-legend-symbol');
} else if (vers == 'furious') {
seriesEnter.append('rect')
.style('stroke-width', 2)
@@ -5651,13 +5899,13 @@ nv.models.legend = function() {
var seriesWidths = [];
series.each(function(d,i) {
var legendText;
- if (getKey(d).length > maxKeyLength) {
+ if (getKey(d) && (getKey(d).length > maxKeyLength)) {
var trimmedKey = getKey(d).substring(0, maxKeyLength);
legendText = d3.select(this).select('text').text(trimmedKey + "...");
d3.select(this).append("svg:title").text(getKey(d));
} else {
legendText = d3.select(this).select('text');
- }
+ }
var nodeTextLength;
try {
nodeTextLength = legendText.node().getComputedTextLength();
@@ -6104,6 +6352,7 @@ nv.models.lineChart = function() {
, width = null
, height = null
, showLegend = true
+ , legendPosition = 'top'
, showXAxis = true
, showYAxis = true
, rightAlignYAxis = false
@@ -6254,20 +6503,27 @@ nv.models.lineChart = function() {
contextEnter.append('g').attr('class', 'nv-x nv-brush');
// Legend
- if (showLegend) {
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
legend.width(availableWidth);
g.select('.nv-legendWrap')
.datum(data)
.call(legend);
- if ( margin.top != legend.height()) {
- margin.top = legend.height();
- availableHeight1 = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focusHeight : 0);
- }
+ if (legendPosition === 'bottom') {
+ wrap.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + (availableHeight1) +')');
+ } else if (legendPosition === 'top') {
+ if ( margin.top != legend.height()) {
+ margin.top = legend.height();
+ availableHeight1 = nv.utils.availableHeight(height, container, margin) - (focusEnable ? focusHeight : 0);
+ }
- wrap.select('.nv-legendWrap')
- .attr('transform', 'translate(0,' + (-margin.top) +')');
+ wrap.select('.nv-legendWrap')
+ .attr('transform', 'translate(0,' + (-margin.top) +')');
+ }
}
wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
@@ -6491,11 +6747,13 @@ nv.models.lineChart = function() {
allData[indexToHighlight].highlight = true;
}
+ var defaultValueFormatter = function(d,i) {
+ return d == null ? "N/A" : yAxis.tickFormat()(d);
+ };
+
interactiveLayer.tooltip
.chartContainer(chart.container.parentNode)
- .valueFormatter(function(d,i) {
- return d === null ? "N/A" : yAxis.tickFormat()(d);
- })
+ .valueFormatter(interactiveLayer.tooltip.valueFormatter() || defaultValueFormatter)
.data({
value: chart.x()( singlePoint,pointIndex ),
index: pointIndex,
@@ -6669,6 +6927,7 @@ nv.models.lineChart = function() {
width: {get: function(){return width;}, set: function(_){width=_;}},
height: {get: function(){return height;}, set: function(_){height=_;}},
showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
+ legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}},
showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}},
showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}},
focusEnable: {get: function(){return focusEnable;}, set: function(_){focusEnable=_;}},
@@ -6749,8 +7008,7 @@ nv.models.lineWithFocusChart = function() {
return nv.models.lineChart()
.margin({ bottom: 30 })
.focusEnable( true );
-};
-nv.models.linePlusBarChart = function() {
+};nv.models.linePlusBarChart = function() {
"use strict";
//============================================================
@@ -6822,15 +7080,15 @@ nv.models.linePlusBarChart = function() {
//------------------------------------------------------------
var getBarsAxis = function() {
- return switchYAxisOrder
- ? { main: y1Axis, focus: y3Axis }
- : { main: y2Axis, focus: y4Axis }
+ return !switchYAxisOrder
+ ? { main: y2Axis, focus: y4Axis }
+ : { main: y1Axis, focus: y3Axis }
}
var getLinesAxis = function() {
- return switchYAxisOrder
- ? { main: y2Axis, focus: y4Axis }
- : { main: y1Axis, focus: y3Axis }
+ return !switchYAxisOrder
+ ? { main: y1Axis, focus: y3Axis }
+ : { main: y2Axis, focus: y4Axis }
}
var stateGetter = function(data) {
@@ -6900,7 +7158,12 @@ nv.models.linePlusBarChart = function() {
var dataBars = data.filter(function(d) { return !d.disabled && d.bar });
var dataLines = data.filter(function(d) { return !d.bar }); // removed the !d.disabled clause here to fix Issue #240
- x = bars.xScale();
+ if (dataBars.length && !switchYAxisOrder) {
+ x = bars.xScale();
+ } else {
+ x = lines.xScale();
+ }
+
x2 = x2Axis.scale();
// select the scales and series based on the position of the yAxis
@@ -6959,7 +7222,9 @@ nv.models.linePlusBarChart = function() {
// Legend
//------------------------------------------------------------
- if (showLegend) {
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
var legendWidth = legend.align() ? availableWidth / 2 : availableWidth;
var legendXPosition = legend.align() ? legendWidth : 0;
@@ -7234,8 +7499,14 @@ nv.models.linePlusBarChart = function() {
.tickSize(-availableWidth, 0);
y2Axis
.scale(y2)
- ._ticks( nv.utils.calcTicksY(availableHeight1/36, data) )
- .tickSize(dataBars.length ? 0 : -availableWidth, 0); // Show the y2 rules only if y1 has none
+ ._ticks( nv.utils.calcTicksY(availableHeight1/36, data) );
+
+ // Show the y2 rules only if y1 has none
+ if(!switchYAxisOrder) {
+ y2Axis.tickSize(dataBars.length ? 0 : -availableWidth, 0);
+ } else {
+ y2Axis.tickSize(dataLines.length ? 0 : -availableWidth, 0);
+ }
// Calculate opacity of the axis
var barsOpacity = dataBars.length ? 1 : 0;
@@ -7379,11 +7650,20 @@ nv.models.linePlusBarChart = function() {
switchYAxisOrder: {get: function(){return switchYAxisOrder;}, set: function(_){
// Switch the tick format for the yAxis
if(switchYAxisOrder !== _) {
- var tickFormat = y1Axis.tickFormat();
- y1Axis.tickFormat(y2Axis.tickFormat());
- y2Axis.tickFormat(tickFormat);
+ var y1 = y1Axis;
+ y1Axis = y2Axis;
+ y2Axis = y1;
+
+ var y3 = y3Axis;
+ y3Axis = y4Axis;
+ y4Axis = y3;
}
switchYAxisOrder=_;
+
+ y1Axis.orient('left');
+ y2Axis.orient('right');
+ y3Axis.orient('left');
+ y4Axis.orient('right');
}}
});
@@ -7495,7 +7775,7 @@ nv.models.multiBar = function() {
});
// HACK for negative value stacking
- if (stacked) {
+ if (stacked && data.length > 0) {
data[0].values.map(function(d,i) {
var posBase = 0, negBase = 0;
data.map(function(d, idx) {
@@ -7819,7 +8099,8 @@ nv.models.multiBar = function() {
nv.utils.initOptions(chart);
return chart;
-};nv.models.multiBarChart = function() {
+};
+nv.models.multiBarChart = function() {
"use strict";
//============================================================
@@ -7979,7 +8260,9 @@ nv.models.multiBar = function() {
gEnter.append('g').attr('class', 'nv-interactive');
// Legend
- if (showLegend) {
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
legend.width(availableWidth - controlWidth());
g.select('.nv-legendWrap')
@@ -7996,7 +8279,9 @@ nv.models.multiBar = function() {
}
// Controls
- if (showControls) {
+ if (!showControls) {
+ g.select('.nv-controlsWrap').selectAll('*').remove();
+ } else {
var controlsData = [
{ key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() },
{ key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() }
@@ -8480,10 +8765,13 @@ nv.models.multiBarHorizontal = function() {
});
})
.on('click', function(d,i) {
+ var element = this;
dispatch.elementClick({
data: d,
index: i,
- color: d3.select(this).style("fill")
+ color: d3.select(this).style("fill"),
+ event: d3.event,
+ element: element
});
d3.event.stopPropagation();
})
@@ -8807,7 +9095,9 @@ nv.models.multiBarHorizontalChart = function() {
gEnter.append('g').attr('class', 'nv-controlsWrap');
// Legend
- if (showLegend) {
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
legend.width(availableWidth - controlWidth());
g.select('.nv-legendWrap')
@@ -8824,7 +9114,9 @@ nv.models.multiBarHorizontalChart = function() {
}
// Controls
- if (showControls) {
+ if (!showControls) {
+ g.select('.nv-controlsWrap').selectAll('*').remove();
+ } else {
var controlsData = [
{ key: controlLabels.grouped || 'Grouped', disabled: multibar.stacked() },
{ key: controlLabels.stacked || 'Stacked', disabled: !multibar.stacked() }
@@ -9044,7 +9336,7 @@ nv.models.multiChart = function() {
yDomain2,
getX = function(d) { return d.x },
getY = function(d) { return d.y},
- interpolate = 'monotone',
+ interpolate = 'linear',
useVoronoi = true,
interactiveLayer = nv.interactiveGuideline(),
useInteractiveGuideline = false,
@@ -9124,7 +9416,7 @@ nv.models.multiChart = function() {
})
});
- x .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return getX(d) }))
+ x .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x }))
.range([0, availableWidth]);
var wrap = container.selectAll('g.wrap.multiChart').data([data]);
@@ -9150,7 +9442,10 @@ nv.models.multiChart = function() {
return data[i].color || color(d, i);
});
- if (showLegend) {
+ // Legend
+ if (!showLegend) {
+ g.select('.legendWrap').selectAll('*').remove();
+ } else {
var legendWidth = legend.align() ? availableWidth / 2 : availableWidth;
var legendXPosition = legend.align() ? legendWidth : 0;
@@ -9203,10 +9498,12 @@ nv.models.multiChart = function() {
stack1
.width(availableWidth)
.height(availableHeight)
+ .interpolate(interpolate)
.color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'area'}));
stack2
.width(availableWidth)
.height(availableHeight)
+ .interpolate(interpolate)
.color(color_array.filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'area'}));
g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
@@ -9323,6 +9620,9 @@ nv.models.multiChart = function() {
};
tooltip
.duration(0)
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ })
.valueFormatter(function(d, i) {
return yaxis.tickFormat()(d, i);
})
@@ -9340,6 +9640,9 @@ nv.models.multiChart = function() {
};
tooltip
.duration(100)
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ })
.valueFormatter(function(d, i) {
return yaxis.tickFormat()(d, i);
})
@@ -9353,6 +9656,9 @@ nv.models.multiChart = function() {
evt.point['y'] = stack1.y()(evt.point);
tooltip
.duration(0)
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ })
.valueFormatter(function(d, i) {
return yaxis.tickFormat()(d, i);
})
@@ -9371,6 +9677,9 @@ nv.models.multiChart = function() {
};
tooltip
.duration(0)
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ })
.valueFormatter(function(d, i) {
return yaxis.tickFormat()(d, i);
})
@@ -9433,6 +9742,9 @@ nv.models.multiChart = function() {
interactiveLayer.tooltip
.chartContainer(chart.container.parentNode)
+ .headerFormatter(function(d, i) {
+ return xAxis.tickFormat()(d, i);
+ })
.valueFormatter(function(d,i) {
var yAxis = allData[i].yAxis;
return d === null ? "N/A" : yAxis.tickFormat()(d);
@@ -9835,8 +10147,11 @@ nv.models.parallelCoordinates = function() {
var margin = {top: 30, right: 0, bottom: 10, left: 0}
, width = null
, height = null
+ , availableWidth = null
+ , availableHeight = null
, x = d3.scale.ordinal()
, y = {}
+ , undefinedValuesLabel = "undefined values"
, dimensionData = []
, enabledDimensions = []
, dimensionNames = []
@@ -9859,19 +10174,17 @@ nv.models.parallelCoordinates = function() {
// Private Variables
//------------------------------------------------------------
-
var renderWatch = nv.utils.renderWatch(dispatch);
function chart(selection) {
renderWatch.reset();
selection.each(function(data) {
var container = d3.select(this);
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ availableWidth = nv.utils.availableWidth(width, container, margin);
+ availableHeight = nv.utils.availableHeight(height, container, margin);
nv.utils.initSVG(container);
-
//Convert old data to new format (name, values)
if (data[0].values === undefined) {
var newData = [];
@@ -9892,7 +10205,6 @@ nv.models.parallelCoordinates = function() {
dimensionNames = dimensionData.sort(function (a, b) { return a.currentPosition - b.currentPosition; }).map(function (d) { return d.key });
enabledDimensions = dimensionData.filter(function (d) { return !d.disabled; });
-
// Setup Scales
x.rangePoints([0, availableWidth], 1).domain(enabledDimensions.map(function (d) { return d.key; }));
@@ -9900,7 +10212,8 @@ nv.models.parallelCoordinates = function() {
// Extract the list of dimensions and create a scale for each.
var oldDomainMaxValue = {};
var displayMissingValuesline = false;
-
+ var currentTicks = [];
+
dimensionNames.forEach(function(d) {
var extent = d3.extent(dataValues, function (p) { return +p[d]; });
var min = extent[0];
@@ -9943,7 +10256,6 @@ nv.models.parallelCoordinates = function() {
.range([(availableHeight - 12) * 0.9, 0]);
axisWithUndefinedValues = [];
-
y[d].brush = d3.svg.brush().y(y[d]).on('brushstart', brushstart).on('brush', brush).on('brushend', brushend);
});
@@ -9980,8 +10292,8 @@ nv.models.parallelCoordinates = function() {
.attr("y2", function(d) { return d[3]; });
//Add the text "undefined values" under the missing value line
- missingValueslineText = wrap.select('.missingValuesline').selectAll('text').data(["undefined values"]);
- missingValueslineText.append('text').data(["undefined values"]);
+ missingValueslineText = wrap.select('.missingValuesline').selectAll('text').data([undefinedValuesLabel]);
+ missingValueslineText.append('text').data([undefinedValuesLabel]);
missingValueslineText.enter().append('text');
missingValueslineText.exit().remove();
missingValueslineText.attr("y", availableHeight)
@@ -10007,7 +10319,9 @@ nv.models.parallelCoordinates = function() {
d3.select(this).classed('hover', true).style("stroke-width", d.strokeWidth + 2 + "px").style("stroke-opacity", 1);
dispatch.elementMouseover({
label: d.name,
- color: d.color || color(d, i)
+ color: d.color || color(d, i),
+ values: d.values,
+ dimensions: enabledDimensions
});
});
@@ -10035,13 +10349,14 @@ nv.models.parallelCoordinates = function() {
// Add an axis and title.
dimensionsEnter.append('text')
- .attr('class', 'nv-label')
+ .attr('class', 'nv-label')
.style("cursor", "move")
.attr('dy', '-1em')
.attr('text-anchor', 'middle')
.on("mouseover", function(d, i) {
dispatch.elementMouseover({
- label: d.tooltip || d.key
+ label: d.tooltip || d.key,
+ color: d.color
});
})
.on("mouseout", function(d, i) {
@@ -10057,10 +10372,6 @@ nv.models.parallelCoordinates = function() {
dimensionsEnter.append('g').attr('class', 'nv-brushBackground');
dimensions.exit().remove();
dimensions.select('.nv-label').text(function (d) { return d.key });
- dimensions.select('.nv-axis')
- .each(function (d, i) {
- d3.select(this).call(axis.scale(y[d.key]).tickFormat(d3.format(d.format)));
- });
// Add and store a brush for each axis.
restoreBrush(displayBrush);
@@ -10138,27 +10449,30 @@ nv.models.parallelCoordinates = function() {
});
dimensions.select('.nv-brushBackground')
- .each(function (d) {
- d3.select(this).call(y[d.key].brush);
+ .each(function (d) {
+ d3.select(this).call(y[d.key].brush);
- })
- .selectAll('rect')
- .attr('x', -8)
- .attr('width', 16);
+ })
+ .selectAll('rect')
+ .attr('x', -8)
+ .attr('width', 16);
+
+ updateTicks();
}
// Handles a brush event, toggling the display of foreground lines.
function brushstart() {
//If brush aren't visible, show it before brushing again.
if (displayBrush === false) {
+ displayBrush = true;
restoreBrush(true);
}
}
// Handles a brush event, toggling the display of foreground lines.
function brush() {
- actives = dimensionNames.filter(function (p) { return !y[p].brush.empty(); }),
- extents = actives.map(function(p) { return y[p].brush.extent(); });
+ actives = dimensionNames.filter(function (p) { return !y[p].brush.empty(); });
+ extents = actives.map(function(p) { return y[p].brush.extent(); });
filters = []; //erase current filters
actives.forEach(function(d,i) {
@@ -10179,7 +10493,9 @@ nv.models.parallelCoordinates = function() {
if (isActive) active.push(d);
return isActive ? null : 'none';
});
-
+
+ updateTicks();
+
dispatch.brush({
filters: filters,
active: active
@@ -10194,13 +10510,30 @@ nv.models.parallelCoordinates = function() {
f.hasOnlyNaN = true;
});
dispatch.brushEnd(active, hasActiveBrush);
+ }
+ function updateTicks() {
+ dimensions.select('.nv-axis')
+ .each(function (d, i) {
+ var f = filters.filter(function (k) { return k.dimension == d.key; });
+ currentTicks[d.key] = y[d.key].domain();
+
+ //If brush are available, display brush extent
+ if (f.length != 0 && displayBrush)
+ {
+ currentTicks[d.key] = [];
+ if (f[0].extent[1] > y[d.key].domain()[0])
+ currentTicks[d.key] = [f[0].extent[1]];
+ if (f[0].extent[0] >= y[d.key].domain()[0])
+ currentTicks[d.key].push(f[0].extent[0]);
+ }
+
+ d3.select(this).call(axis.scale(y[d.key]).tickFormat(d.format).tickValues(currentTicks[d.key]));
+ });
}
function dragStart(d) {
dragging[d.key] = this.parentNode.__origin__ = x(d.key);
background.attr("visibility", "hidden");
-
}
-
function dragMove(d) {
dragging[d.key] = Math.min(availableWidth, Math.max(0, this.parentNode.__origin__ += d3.event.x));
foreground.attr("d", path);
@@ -10209,7 +10542,6 @@ nv.models.parallelCoordinates = function() {
x.domain(enabledDimensions.map(function (d) { return d.key; }));
dimensions.attr("transform", function(d) { return "translate(" + dimensionPosition(d.key) + ")"; });
}
-
function dragEnd(d, i) {
delete this.parentNode.__origin__;
delete dragging[d.key];
@@ -10222,22 +10554,11 @@ nv.models.parallelCoordinates = function() {
dispatch.dimensionsOrder(enabledDimensions);
}
- function resetBrush() {
- filters = [];
- active = [];
- dispatch.stateChange();
- }
- function resetDrag() {
- dimensionName.map(function (d, i) { return d.currentPosition = d.originalPosition; });
- dispatch.stateChange();
- }
-
function dimensionPosition(d) {
var v = dragging[d];
return v == null ? x(d) : v;
}
});
-
return chart;
}
@@ -10257,7 +10578,8 @@ nv.models.parallelCoordinates = function() {
filters: { get: function () { return filters; }, set: function (_) { filters = _; } },
active: { get: function () { return active; }, set: function (_) { active = _; } },
lineTension: {get: function(){return lineTension;}, set: function(_){lineTension = _;}},
-
+ undefinedValuesLabel : {get: function(){return undefinedValuesLabel;}, set: function(_){undefinedValuesLabel=_;}},
+
// deprecated options
dimensions: {get: function () { return dimensionData.map(function (d){return d.key}); }, set: function (_) {
// deprecated after 1.8.1
@@ -10267,8 +10589,7 @@ nv.models.parallelCoordinates = function() {
} else {
_.forEach(function (k, i) { dimensionData[i].key= k })
}
- }
- },
+ }},
dimensionNames: {get: function () { return dimensionData.map(function (d){return d.key}); }, set: function (_) {
// deprecated after 1.8.1
nv.deprecated('dimensionNames', 'use dimensionData instead');
@@ -10290,7 +10611,6 @@ nv.models.parallelCoordinates = function() {
}
}},
-
// options that require extra logic in the setter
margin: {get: function(){return margin;}, set: function(_){
margin.top = _.top !== undefined ? _.top : margin.top;
@@ -10302,7 +10622,6 @@ nv.models.parallelCoordinates = function() {
color = nv.utils.getColor(_);
}}
});
-
nv.utils.initOptions(chart);
return chart;
};
@@ -10324,10 +10643,10 @@ nv.models.parallelCoordinatesChart = function () {
, color = nv.utils.defaultColor()
, state = nv.utils.state()
, dimensionData = []
- , dimensionNames = []
, displayBrush = true
, defaultState = null
, noData = null
+ , nanValue = "undefined"
, dispatch = d3.dispatch('dimensionsOrder', 'brushEnd', 'stateChange', 'changeState', 'renderEnd')
, controlWidth = function () { return showControls ? 180 : 0 }
;
@@ -10358,6 +10677,20 @@ nv.models.parallelCoordinatesChart = function () {
}
};
+ tooltip.contentGenerator(function(data) {
+ 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>';
+ if(data.series.length !== 0)
+ {
+ str = str + '<tbody><tr><td height ="10px"></td></tr>';
+ data.series.forEach(function(d){
+ 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>';
+ });
+ str = str + '</tbody>';
+ }
+ str = str + '</table>';
+ return str;
+ });
+
//============================================================
// Chart function
//------------------------------------------------------------
@@ -10392,21 +10725,6 @@ nv.models.parallelCoordinatesChart = function () {
d.currentPosition = isNaN(d.currentPosition) ? i : d.currentPosition;
});
- var currentDimensions = dimensionNames.map(function (d) { return d.key; });
- var newDimensions = dimensionData.map(function (d) { return d.key; });
- dimensionData.forEach(function (k, i) {
- var idx = currentDimensions.indexOf(k.key);
- if (idx < 0) {
- dimensionNames.splice(i, 0, k);
- } else {
- var gap = dimensionNames[idx].currentPosition - dimensionNames[idx].originalPosition;
- dimensionNames[idx].originalPosition = k.originalPosition;
- dimensionNames[idx].currentPosition = k.originalPosition + gap;
- }
- });
- //Remove old dimensions
- dimensionNames = dimensionNames.filter(function (d) { return newDimensions.indexOf(d.key) >= 0; });
-
if (!defaultState) {
var key;
defaultState = {};
@@ -10442,12 +10760,14 @@ nv.models.parallelCoordinatesChart = function () {
.attr("height", (availableHeight > 0) ? availableHeight : 0);
// Legend
- if (showLegend) {
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
legend.width(availableWidth)
.color(function (d) { return "rgb(188,190,192)"; });
g.select('.nv-legendWrap')
- .datum(dimensionNames.sort(function (a, b) { return a.originalPosition - b.originalPosition; }))
+ .datum(dimensionData.sort(function (a, b) { return a.originalPosition - b.originalPosition; }))
.call(legend);
if (margin.top != legend.height()) {
@@ -10459,14 +10779,11 @@ nv.models.parallelCoordinatesChart = function () {
}
wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
-
-
// Main Chart Component(s)
parallelCoordinates
.width(availableWidth)
.height(availableHeight)
- .dimensionData(dimensionNames)
+ .dimensionData(dimensionData)
.displayBrush(displayBrush);
var parallelCoordinatesWrap = g.select('.nv-parallelCoordinatesWrap ')
@@ -10498,21 +10815,21 @@ nv.models.parallelCoordinatesChart = function () {
//Update dimensions order and display reset sorting button
parallelCoordinates.dispatch.on('dimensionsOrder', function (e) {
- dimensionNames.sort(function (a, b) { return a.currentPosition - b.currentPosition; });
+ dimensionData.sort(function (a, b) { return a.currentPosition - b.currentPosition; });
var isSorted = false;
- dimensionNames.forEach(function (d, i) {
+ dimensionData.forEach(function (d, i) {
d.currentPosition = i;
if (d.currentPosition !== d.originalPosition)
isSorted = true;
});
- dispatch.dimensionsOrder(dimensionNames, isSorted);
+ dispatch.dimensionsOrder(dimensionData, isSorted);
});
// Update chart from a state object passed to event handler
dispatch.on('changeState', function (e) {
if (typeof e.disabled !== 'undefined') {
- dimensionNames.forEach(function (series, i) {
+ dimensionData.forEach(function (series, i) {
series.disabled = e.disabled[i];
});
state.disabled = e.disabled;
@@ -10530,11 +10847,27 @@ nv.models.parallelCoordinatesChart = function () {
//------------------------------------------------------------
parallelCoordinates.dispatch.on('elementMouseover.tooltip', function (evt) {
- evt['series'] = {
+ var tp = {
key: evt.label,
- color: evt.color
- };
- tooltip.data(evt).hidden(false);
+ color: evt.color,
+ series: []
+ }
+ if(evt.values){
+ Object.keys(evt.values).forEach(function (d) {
+ var dim = evt.dimensions.filter(function (dd) {return dd.key === d;})[0];
+ if(dim){
+ var v;
+ if (isNaN(evt.values[d]) || isNaN(parseFloat(evt.values[d]))) {
+ v = nanValue;
+ } else {
+ v = dim.format(evt.values[d]);
+ }
+ tp.series.push({ idx: dim.currentPosition, key: d, value: v, color: dim.color });
+ }
+ });
+ tp.series.sort(function(a,b) {return a.idx - b.idx});
+ }
+ tooltip.data(tp).hidden(false);
});
parallelCoordinates.dispatch.on('elementMouseout.tooltip', function(evt) {
@@ -10553,7 +10886,6 @@ nv.models.parallelCoordinatesChart = function () {
chart.parallelCoordinates = parallelCoordinates;
chart.legend = legend;
chart.tooltip = tooltip;
-
chart.options = nv.utils.optionsFunc.bind(chart);
chart._options = Object.create({}, {
@@ -10565,7 +10897,8 @@ nv.models.parallelCoordinatesChart = function () {
dimensionData: { get: function () { return dimensionData; }, set: function (_) { dimensionData = _; } },
displayBrush: { get: function () { return displayBrush; }, set: function (_) { displayBrush = _; } },
noData: { get: function () { return noData; }, set: function (_) { noData = _; } },
-
+ nanValue: { get: function () { return nanValue; }, set: function (_) { nanValue = _; } },
+
// options that require extra logic in the setter
margin: {
get: function () { return margin; },
@@ -10587,7 +10920,8 @@ nv.models.parallelCoordinatesChart = function () {
nv.utils.initOptions(chart);
return chart;
- };nv.models.pie = function() {
+ };
+nv.models.pie = function() {
"use strict";
//============================================================
@@ -10649,9 +10983,15 @@ nv.models.parallelCoordinatesChart = function () {
arcsRadiusInner.push(inner);
}
} else {
- arcsRadiusOuter = arcsRadius.map(function (d) { return (d.outer - d.outer / 5) * radius; });
- arcsRadiusInner = arcsRadius.map(function (d) { return (d.inner - d.inner / 5) * radius; });
- donutRatio = d3.min(arcsRadius.map(function (d) { return (d.inner - d.inner / 5); }));
+ if(growOnHover){
+ arcsRadiusOuter = arcsRadius.map(function (d) { return (d.outer - d.outer / 5) * radius; });
+ arcsRadiusInner = arcsRadius.map(function (d) { return (d.inner - d.inner / 5) * radius; });
+ donutRatio = d3.min(arcsRadius.map(function (d) { return (d.inner - d.inner / 5); }));
+ } else {
+ arcsRadiusOuter = arcsRadius.map(function (d) { return d.outer * radius; });
+ arcsRadiusInner = arcsRadius.map(function (d) { return d.inner * radius; });
+ donutRatio = d3.min(arcsRadius.map(function (d) { return d.inner; }));
+ }
}
nv.utils.initSVG(container);
@@ -11119,7 +11459,9 @@ nv.models.pieChart = function() {
gEnter.append('g').attr('class', 'nv-legendWrap');
// Legend
- if (showLegend) {
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
if (legendPosition === "top") {
legend.width( availableWidth ).key(pie.x());
@@ -11219,6 +11561,8 @@ nv.models.pieChart = function() {
// use Object get/set functionality to map between vars and chart functions
chart._options = Object.create({}, {
// simple options, just get/set the necessary values
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
noData: {get: function(){return noData;}, set: function(_){noData=_;}},
showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}},
legendPosition: {get: function(){return legendPosition;}, set: function(_){legendPosition=_;}},
@@ -11288,6 +11632,7 @@ nv.models.scatter = function() {
, useVoronoi = true
, duration = 250
, interactiveUpdateDelay = 300
+ , showLabels = false
;
@@ -11300,8 +11645,34 @@ nv.models.scatter = function() {
, needsUpdate = false // Flag for when the points are visually updating, but the interactive layer is behind, to disable tooltips
, renderWatch = nv.utils.renderWatch(dispatch, duration)
, _sizeRange_def = [16, 256]
+ , _caches
;
+ function getCache(d) {
+ var cache, i;
+ cache = _caches = _caches || {};
+ i = d[0].series;
+ cache = cache[i] = cache[i] || {};
+ i = d[1];
+ cache = cache[i] = cache[i] || {};
+ return cache;
+ }
+
+ function getDiffs(d) {
+ var i, key,
+ point = d[0],
+ cache = getCache(d),
+ diffs = false;
+ for (i = 1; i < arguments.length; i ++) {
+ key = arguments[i];
+ if (cache[key] !== point[key] || !cache.hasOwnProperty(key)) {
+ cache[key] = point[key];
+ diffs = true;
+ }
+ }
+ return diffs;
+ }
+
function chart(selection) {
renderWatch.reset();
selection.each(function(data) {
@@ -11319,6 +11690,7 @@ nv.models.scatter = function() {
});
// Setup Scales
+ var logScale = chart.yScale().name === d3.scale.log().name ? true : false;
// remap and flatten the data for use in calculating the scales' domains
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
d3.merge(
@@ -11337,7 +11709,7 @@ nv.models.scatter = function() {
else
x.range(xRange || [0, availableWidth]);
- if (chart.yScale().name === "o") {
+ if (logScale) {
var min = d3.min(seriesData.map(function(d) { if (d.y !== 0) return d.y; }));
y.clamp(true)
.domain(yDomain || d3.extent(seriesData.map(function(d) {
@@ -11378,6 +11750,8 @@ nv.models.scatter = function() {
y0 = y0 || y;
z0 = z0 || z;
+ var scaleDiff = x(1) !== x0(1) || y(1) !== y0(1) || z(1) !== z0(1);
+
// Setup containers and skeleton of chart
var wrap = container.selectAll('g.nv-wrap.nv-scatter').data([data]);
var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatter nv-chart-' + id);
@@ -11421,8 +11795,8 @@ nv.models.scatter = function() {
var pX = getX(point,pointIndex);
var pY = getY(point,pointIndex);
- return [x(pX)+ Math.random() * 1e-4,
- y(pY)+ Math.random() * 1e-4,
+ return [nv.utils.NaNtoZero(x(pX))+ Math.random() * 1e-4,
+ nv.utils.NaNtoZero(y(pY))+ Math.random() * 1e-4,
groupIndex,
pointIndex, point]; //temp hack to add noise until I think of a better way so there are no duplicates
})
@@ -11621,6 +11995,7 @@ nv.models.scatter = function() {
.attr('class', function(d,i) {
return (d.classed || '') + ' nv-group nv-series-' + i;
})
+ .classed('nv-noninteractive', !interactive)
.classed('hover', function(d) { return d.hover });
groups.watchTransition(renderWatch, 'scatter: groups')
.style('fill', function(d,i) { return color(d, i) })
@@ -11640,6 +12015,9 @@ nv.models.scatter = function() {
})
});
points.enter().append('path')
+ .attr('class', function (d) {
+ return 'nv-point nv-point-' + d[1];
+ })
.style('fill', function (d) { return d.color })
.style('stroke', function (d) { return d.color })
.attr('transform', function(d) {
@@ -11657,25 +12035,66 @@ nv.models.scatter = function() {
return 'translate(' + nv.utils.NaNtoZero(x(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
})
.remove();
- points.each(function(d) {
- d3.select(this)
- .classed('nv-point', true)
- .classed('nv-point-' + d[1], true)
- .classed('nv-noninteractive', !interactive)
- .classed('hover',false)
- ;
- });
- points
+ points.filter(function (d) { return scaleDiff || getDiffs(d, 'x', 'y'); })
.watchTransition(renderWatch, 'scatter points')
.attr('transform', function(d) {
//nv.log(d, getX(d[0],d[1]), x(getX(d[0],d[1])));
return 'translate(' + nv.utils.NaNtoZero(x(getX(d[0],d[1]))) + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
- })
+ });
+ points.filter(function (d) { return scaleDiff || getDiffs(d, 'shape', 'size'); })
+ .watchTransition(renderWatch, 'scatter points')
.attr('d',
nv.utils.symbol()
.type(function(d) { return getShape(d[0]); })
.size(function(d) { return z(getSize(d[0],d[1])) })
);
+
+ // add label a label to scatter chart
+ if(showLabels)
+ {
+ var titles = groups.selectAll('.nv-label')
+ .data(function(d) {
+ return d.values.map(
+ function (point, pointIndex) {
+ return [point, pointIndex]
+ }).filter(
+ function(pointArray, pointIndex) {
+ return pointActive(pointArray[0], pointIndex)
+ })
+ });
+
+ titles.enter().append('text')
+ .style('fill', function (d,i) {
+ return d.color })
+ .style('stroke-opacity', 0)
+ .style('fill-opacity', 1)
+ .attr('transform', function(d) {
+ var dx = nv.utils.NaNtoZero(x0(getX(d[0],d[1]))) + Math.sqrt(z(getSize(d[0],d[1]))/Math.PI) + 2;
+ return 'translate(' + dx + ',' + nv.utils.NaNtoZero(y0(getY(d[0],d[1]))) + ')';
+ })
+ .text(function(d,i){
+ return d[0].label;});
+
+ titles.exit().remove();
+ groups.exit().selectAll('path.nv-label')
+ .watchTransition(renderWatch, 'scatter exit')
+ .attr('transform', function(d) {
+ var dx = nv.utils.NaNtoZero(x(getX(d[0],d[1])))+ Math.sqrt(z(getSize(d[0],d[1]))/Math.PI)+2;
+ return 'translate(' + dx + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')';
+ })
+ .remove();
+ titles.each(function(d) {
+ d3.select(this)
+ .classed('nv-label', true)
+ .classed('nv-label-' + d[1], false)
+ .classed('hover',false);
+ });
+ titles.watchTransition(renderWatch, 'scatter labels')
+ .attr('transform', function(d) {
+ var dx = nv.utils.NaNtoZero(x(getX(d[0],d[1])))+ Math.sqrt(z(getSize(d[0],d[1]))/Math.PI)+2;
+ return 'translate(' + dx + ',' + nv.utils.NaNtoZero(y(getY(d[0],d[1]))) + ')'
+ });
+ }
// Delay updating the invisible interactive layer for smoother animation
if( interactiveUpdateDelay )
@@ -11758,7 +12177,7 @@ nv.models.scatter = function() {
showVoronoi: {get: function(){return showVoronoi;}, set: function(_){showVoronoi=_;}},
id: {get: function(){return id;}, set: function(_){id=_;}},
interactiveUpdateDelay: {get:function(){return interactiveUpdateDelay;}, set: function(_){interactiveUpdateDelay=_;}},
-
+ showLabels: {get: function(){return showLabels;}, set: function(_){ showLabels = _;}},
// simple functor options
x: {get: function(){return getX;}, set: function(_){getX = d3.functor(_);}},
@@ -11826,6 +12245,7 @@ nv.models.scatterChart = function() {
, dispatch = d3.dispatch('stateChange', 'changeState', 'renderEnd')
, noData = null
, duration = 250
+ , showLabels = false
;
scatter.xScale(x).yScale(y);
@@ -11947,7 +12367,9 @@ nv.models.scatterChart = function() {
}
// Legend
- if (showLegend) {
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
var legendWidth = availableWidth;
legend.width(legendWidth);
@@ -11973,7 +12395,8 @@ nv.models.scatterChart = function() {
.color(data.map(function(d,i) {
d.color = d.color || color(d, i);
return d.color;
- }).filter(function(d,i) { return !data[i].disabled }));
+ }).filter(function(d,i) { return !data[i].disabled }))
+ .showLabels(showLabels);
wrap.select('.nv-scatterWrap')
.datum(data.filter(function(d) { return !d.disabled }))
@@ -12151,6 +12574,7 @@ nv.models.scatterChart = function() {
defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}},
noData: {get: function(){return noData;}, set: function(_){noData=_;}},
duration: {get: function(){return duration;}, set: function(_){duration=_;}},
+ showLabels: {get: function(){return showLabels;}, set: function(_){showLabels=_;}},
// options that require extra logic in the setter
margin: {get: function(){return margin;}, set: function(_){
@@ -12197,6 +12621,8 @@ nv.models.sparkline = function() {
, yDomain
, xRange
, yRange
+ , showMinMaxPoints = true
+ , showCurrentPoint = true
, dispatch = d3.dispatch('renderEnd')
;
@@ -12257,7 +12683,7 @@ nv.models.sparkline = function() {
var maxPoint = pointIndex(yValues.lastIndexOf(y.domain()[1])),
minPoint = pointIndex(yValues.indexOf(y.domain()[0])),
currentPoint = pointIndex(yValues.length - 1);
- return [minPoint, maxPoint, currentPoint].filter(function (d) {return d != null;});
+ return [(showMinMaxPoints ? minPoint : null), (showMinMaxPoints ? maxPoint : null), (showCurrentPoint ? currentPoint : null)].filter(function (d) {return d != null;});
});
points.enter().append('circle');
points.exit().remove();
@@ -12283,15 +12709,17 @@ nv.models.sparkline = function() {
chart._options = Object.create({}, {
// simple options, just get/set the necessary values
- width: {get: function(){return width;}, set: function(_){width=_;}},
- height: {get: function(){return height;}, set: function(_){height=_;}},
- xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
- yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
- xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
- yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
- xScale: {get: function(){return x;}, set: function(_){x=_;}},
- yScale: {get: function(){return y;}, set: function(_){y=_;}},
- animate: {get: function(){return animate;}, set: function(_){animate=_;}},
+ width: {get: function(){return width;}, set: function(_){width=_;}},
+ height: {get: function(){return height;}, set: function(_){height=_;}},
+ xDomain: {get: function(){return xDomain;}, set: function(_){xDomain=_;}},
+ yDomain: {get: function(){return yDomain;}, set: function(_){yDomain=_;}},
+ xRange: {get: function(){return xRange;}, set: function(_){xRange=_;}},
+ yRange: {get: function(){return yRange;}, set: function(_){yRange=_;}},
+ xScale: {get: function(){return x;}, set: function(_){x=_;}},
+ yScale: {get: function(){return y;}, set: function(_){y=_;}},
+ animate: {get: function(){return animate;}, set: function(_){animate=_;}},
+ showMinMaxPoints: {get: function(){return showMinMaxPoints;}, set: function(_){showMinMaxPoints=_;}},
+ showCurrentPoint: {get: function(){return showCurrentPoint;}, set: function(_){showCurrentPoint=_;}},
//functor options
x: {get: function(){return getX;}, set: function(_){getX=d3.functor(_);}},
@@ -12646,7 +13074,6 @@ nv.models.stackedArea = function() {
.y(function(d) {
if (d.display !== undefined) { return d.display.y + d.display.y0; }
})
- .forceY([0])
.color(data.map(function(d,i) {
d.color = d.color || color(d, d.seriesIndex);
return d.color;
@@ -13021,7 +13448,9 @@ nv.models.stackedAreaChart = function() {
g.select("rect").attr("width",availableWidth).attr("height",availableHeight);
// Legend
- if (showLegend) {
+ if (!showLegend) {
+ g.select('.nv-legendWrap').selectAll('*').remove();
+ } else {
var legendWidth = (showControls) ? availableWidth - controlWidth : availableWidth;
legend.width(legendWidth);
@@ -13037,7 +13466,9 @@ nv.models.stackedAreaChart = function() {
}
// Controls
- if (showControls) {
+ if (!showControls) {
+ g.select('.nv-controlsWrap').selectAll('*').remove();
+ } else {
var controlsData = [
{
key: controlLabels.stacked || 'Stacked',
@@ -13231,7 +13662,7 @@ nv.models.stackedAreaChart = function() {
key: series.key,
value: tooltipValue,
color: color(series,series.seriesIndex),
- stackedValue: point.display
+ point: point
});
if (showTotalInTooltip && stacked.style() != 'expand') {
@@ -13250,8 +13681,8 @@ nv.models.stackedAreaChart = function() {
//To handle situation where the stacked area chart is negative, we need to use absolute values
//when checking if the mouse Y value is within the stack area.
yValue = Math.abs(yValue);
- var stackedY0 = Math.abs(series.stackedValue.y0);
- var stackedY = Math.abs(series.stackedValue.y);
+ var stackedY0 = Math.abs(series.point.display.y0);
+ var stackedY = Math.abs(series.point.display.y);
if ( yValue >= stackedY0 && yValue <= (stackedY + stackedY0))
{
indexToHighlight = i;
@@ -13423,70 +13854,208 @@ nv.models.sunburst = function() {
//------------------------------------------------------------
var margin = {top: 0, right: 0, bottom: 0, left: 0}
- , width = null
- , height = null
+ , width = 600
+ , height = 600
, mode = "count"
- , modes = {count: function(d) { return 1; }, size: function(d) { return d.size }}
+ , modes = {count: function(d) { return 1; }, value: function(d) { return d.value || d.size }, size: function(d) { return d.value || d.size }}
, id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
, container = null
, color = nv.utils.defaultColor()
+ , showLabels = false
+ , labelFormat = function(d){if(mode === 'count'){return d.name + ' #' + d.value}else{return d.name + ' ' + (d.value || d.size)}}
+ , labelThreshold = 0.02
+ , sort = function(d1, d2){return d1.name > d2.name;}
+ , key = function(d,i){return d.name;}
, groupColorByParent = true
, duration = 500
- , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMousemove', 'elementMouseover', 'elementMouseout', 'renderEnd')
- ;
+ , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMousemove', 'elementMouseover', 'elementMouseout', 'renderEnd');
+
+ //============================================================
+ // aux functions and setup
+ //------------------------------------------------------------
var x = d3.scale.linear().range([0, 2 * Math.PI]);
var y = d3.scale.sqrt();
- var partition = d3.layout.partition()
- .sort(null)
- .value(function(d) { return 1; });
+ var partition = d3.layout.partition().sort(sort);
+
+ var node, availableWidth, availableHeight, radius;
+ var prevPositions = {};
var arc = d3.svg.arc()
- .startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
- .endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
- .innerRadius(function(d) { return Math.max(0, y(d.y)); })
- .outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
+ .startAngle(function(d) {return Math.max(0, Math.min(2 * Math.PI, x(d.x))) })
+ .endAngle(function(d) {return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))) })
+ .innerRadius(function(d) {return Math.max(0, y(d.y)) })
+ .outerRadius(function(d) {return Math.max(0, y(d.y + d.dy)) });
+
+ function rotationToAvoidUpsideDown(d) {
+ var centerAngle = computeCenterAngle(d);
+ if(centerAngle > 90){
+ return 180;
+ }
+ else {
+ return 0;
+ }
+ }
+
+ function computeCenterAngle(d) {
+ var startAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x)));
+ var endAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
+ var centerAngle = (((startAngle + endAngle) / 2) * (180 / Math.PI)) - 90;
+ return centerAngle;
+ }
+
+ function labelThresholdMatched(d) {
+ var startAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x)));
+ var endAngle = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
+
+ var size = endAngle - startAngle;
+ return size > labelThreshold;
+ }
+
+ // When zooming: interpolate the scales.
+ function arcTweenZoom(e,i) {
+ var xd = d3.interpolate(x.domain(), [node.x, node.x + node.dx]),
+ yd = d3.interpolate(y.domain(), [node.y, 1]),
+ yr = d3.interpolate(y.range(), [node.y ? 20 : 0, radius]);
- // Keep track of the current and previous node being displayed as the root.
- var node, prevNode;
- // Keep track of the root node
- var rootNode;
+ if (i === 0) {
+ return function() {return arc(e);}
+ }
+ else {
+ return function (t) {
+ x.domain(xd(t));
+ y.domain(yd(t)).range(yr(t));
+ return arc(e);
+ }
+ };
+ }
+
+ function arcTweenUpdate(d) {
+ var ipo = d3.interpolate({x: d.x0, dx: d.dx0, y: d.y0, dy: d.dy0}, d);
+
+ return function (t) {
+ var b = ipo(t);
+
+ d.x0 = b.x;
+ d.dx0 = b.dx;
+ d.y0 = b.y;
+ d.dy0 = b.dy;
+
+ return arc(b);
+ };
+ }
+
+ function updatePrevPosition(node) {
+ var k = key(node);
+ if(! prevPositions[k]) prevPositions[k] = {};
+ var pP = prevPositions[k];
+ pP.dx = node.dx;
+ pP.x = node.x;
+ pP.dy = node.dy;
+ pP.y = node.y;
+ }
+
+ function storeRetrievePrevPositions(nodes) {
+ nodes.forEach(function(n){
+ var k = key(n);
+ var pP = prevPositions[k];
+ //console.log(k,n,pP);
+ if( pP ){
+ n.dx0 = pP.dx;
+ n.x0 = pP.x;
+ n.dy0 = pP.dy;
+ n.y0 = pP.y;
+ }
+ else {
+ n.dx0 = n.dx;
+ n.x0 = n.x;
+ n.dy0 = n.dy;
+ n.y0 = n.y;
+ }
+ updatePrevPosition(n);
+ });
+ }
+
+ function zoomClick(d) {
+ var labels = container.selectAll('text')
+ var path = container.selectAll('path')
+
+ // fade out all text elements
+ labels.transition().attr("opacity",0);
+
+ // to allow reference to the new center node
+ node = d;
+
+ path.transition()
+ .duration(duration)
+ .attrTween("d", arcTweenZoom)
+ .each('end', function(e) {
+ // partially taken from here: http://bl.ocks.org/metmajer/5480307
+ // check if the animated element's data e lies within the visible angle span given in d
+ if(e.x >= d.x && e.x < (d.x + d.dx) ){
+ if(e.depth >= d.depth){
+ // get a selection of the associated text element
+ var parentNode = d3.select(this.parentNode);
+ var arcText = parentNode.select('text');
+
+ // fade in the text element and recalculate positions
+ arcText.transition().duration(duration)
+ .text( function(e){return labelFormat(e) })
+ .attr("opacity", function(d){
+ if(labelThresholdMatched(d)) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ })
+ .attr("transform", function() {
+ var width = this.getBBox().width;
+ if(e.depth === 0)
+ return "translate(" + (width / 2 * - 1) + ",0)";
+ else if(e.depth === d.depth){
+ return "translate(" + (y(e.y) + 5) + ",0)";
+ }
+ else {
+ var centerAngle = computeCenterAngle(e);
+ var rotation = rotationToAvoidUpsideDown(e);
+ if (rotation === 0) {
+ return 'rotate('+ centerAngle +')translate(' + (y(e.y) + 5) + ',0)';
+ }
+ else {
+ return 'rotate('+ centerAngle +')translate(' + (y(e.y) + width + 5) + ',0)rotate(' + rotation + ')';
+ }
+ }
+ });
+ }
+ }
+ })
+ }
//============================================================
// chart function
//------------------------------------------------------------
-
var renderWatch = nv.utils.renderWatch(dispatch);
function chart(selection) {
renderWatch.reset();
+
selection.each(function(data) {
container = d3.select(this);
- var availableWidth = nv.utils.availableWidth(width, container, margin);
- var availableHeight = nv.utils.availableHeight(height, container, margin);
- var radius = Math.min(availableWidth, availableHeight) / 2;
- var path;
+ availableWidth = nv.utils.availableWidth(width, container, margin);
+ availableHeight = nv.utils.availableHeight(height, container, margin);
+ radius = Math.min(availableWidth, availableHeight) / 2;
- nv.utils.initSVG(container);
+ y.range([0, radius]);
// Setup containers and skeleton of chart
- var wrap = container.selectAll('.nv-wrap.nv-sunburst').data(data);
- var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sunburst nv-chart-' + id);
-
- var g = wrapEnter.selectAll('nv-sunburst');
-
- chart.update = function() {
- if ( duration === 0 ) {
- container.call(chart);
- } else {
- container.transition().duration(duration).call(chart);
- }
- };
- chart.container = this;
-
-
- wrap.attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
+ var wrap = container.select('g.nvd3.nv-wrap.nv-sunburst');
+ if( !wrap[0][0] ) {
+ wrap = container.append('g')
+ .attr('class', 'nvd3 nv-wrap nv-sunburst nv-chart-' + id)
+ .attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
+ }
container.on('click', function (d, i) {
dispatch.chartClick({
@@ -13497,13 +14066,21 @@ nv.models.sunburst = function() {
});
});
- y.range([0, radius]);
-
- node = node || data;
- rootNode = data[0];
partition.value(modes[mode] || modes["count"]);
- path = g.data(partition.nodes).enter()
- .append("path")
+
+ //reverse the drawing order so that the labels of inner
+ //arcs are drawn on top of the outer arcs.
+ var nodes = partition.nodes(data[0]).reverse()
+
+ storeRetrievePrevPositions(nodes);
+ var cG = wrap.selectAll('.arc-container').data(nodes, key)
+
+ //handle new datapoints
+ var cGE = cG.enter()
+ .append("g")
+ .attr("class",'arc-container')
+
+ cGE.append("path")
.attr("d", arc)
.style("fill", function (d) {
if (d.color) {
@@ -13517,22 +14094,7 @@ nv.models.sunburst = function() {
}
})
.style("stroke", "#FFF")
- .on("click", function(d) {
- if (prevNode !== node && node !== d) prevNode = node;
- node = d;
- path.transition()
- .duration(duration)
- .attrTween("d", arcTweenZoom(d));
- })
- .each(stash)
- .on("dblclick", function(d) {
- if (prevNode.parent == d) {
- path.transition()
- .duration(duration)
- .attrTween("d", arcTweenZoom(rootNode));
- }
- })
- .each(stash)
+ .on("click", zoomClick)
.on('mouseover', function(d,i){
d3.select(this).classed('hover', true).style('opacity', 0.8);
dispatch.elementMouseover({
@@ -13552,58 +14114,68 @@ nv.models.sunburst = function() {
});
});
+ ///Iterating via each and selecting based on the this
+ ///makes it work ... a cG.selectAll('path') doesn't.
+ ///Without iteration the data (in the element) didn't update.
+ cG.each(function(d){
+ d3.select(this).select('path')
+ .transition()
+ .duration(duration)
+ .attrTween('d', arcTweenUpdate);
+ });
+ if(showLabels){
+ //remove labels first and add them back
+ cG.selectAll('text').remove();
- // Setup for switching data: stash the old values for transition.
- function stash(d) {
- d.x0 = d.x;
- d.dx0 = d.dx;
+ //this way labels are on top of newly added arcs
+ cG.append('text')
+ .text( function(e){ return labelFormat(e)})
+ .transition()
+ .duration(duration)
+ .attr("opacity", function(d){
+ if(labelThresholdMatched(d)) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ })
+ .attr("transform", function(d) {
+ var width = this.getBBox().width;
+ if(d.depth === 0){
+ return "rotate(0)translate(" + (width / 2 * -1) + ",0)";
+ }
+ else {
+ var centerAngle = computeCenterAngle(d);
+ var rotation = rotationToAvoidUpsideDown(d);
+ if (rotation === 0) {
+ return 'rotate('+ centerAngle +')translate(' + (y(d.y) + 5) + ',0)';
+ }
+ else {
+ return 'rotate('+ centerAngle +')translate(' + (y(d.y) + width + 5) + ',0)rotate(' + rotation + ')';
+ }
+ }
+ });
}
- // When switching data: interpolate the arcs in data space.
- function arcTweenData(a, i) {
- var oi = d3.interpolate({x: a.x0, dx: a.dx0}, a);
-
- function tween(t) {
- var b = oi(t);
- a.x0 = b.x;
- a.dx0 = b.dx;
- return arc(b);
- }
+ //zoom out to the center when the data is updated.
+ zoomClick(nodes[nodes.length - 1])
- if (i == 0) {
- // If we are on the first arc, adjust the x domain to match the root node
- // at the current zoom level. (We only need to do this once.)
- var xd = d3.interpolate(x.domain(), [node.x, node.x + node.dx]);
- return function (t) {
- x.domain(xd(t));
- return tween(t);
- };
- } else {
- return tween;
- }
- }
-
- // When zooming: interpolate the scales.
- function arcTweenZoom(d) {
- var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
- yd = d3.interpolate(y.domain(), [d.y, 1]),
- yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
- return function (d, i) {
- return i
- ? function (t) {
- return arc(d);
- }
- : function (t) {
- x.domain(xd(t));
- y.domain(yd(t)).range(yr(t));
- return arc(d);
- };
- };
- }
+ //remove unmatched elements ...
+ cG.exit()
+ .transition()
+ .duration(duration)
+ .attr('opacity',0)
+ .each('end',function(d){
+ var k = key(d);
+ prevPositions[k] = undefined;
+ })
+ .remove();
});
+
renderWatch.renderEnd('sunburst immediate');
return chart;
}
@@ -13623,7 +14195,11 @@ nv.models.sunburst = function() {
id: {get: function(){return id;}, set: function(_){id=_;}},
duration: {get: function(){return duration;}, set: function(_){duration=_;}},
groupColorByParent: {get: function(){return groupColorByParent;}, set: function(_){groupColorByParent=!!_;}},
-
+ showLabels: {get: function(){return showLabels;}, set: function(_){showLabels=!!_}},
+ labelFormat: {get: function(){return labelFormat;}, set: function(_){labelFormat=_}},
+ labelThreshold: {get: function(){return labelThreshold;}, set: function(_){labelThreshold=_}},
+ sort: {get: function(){return sort;}, set: function(_){sort=_}},
+ key: {get: function(){return key;}, set: function(_){key=_}},
// options that require extra logic in the setter
margin: {get: function(){return margin;}, set: function(_){
margin.top = _.top != undefined ? _.top : margin.top;
@@ -13657,21 +14233,19 @@ nv.models.sunburstChart = function() {
, defaultState = null
, noData = null
, duration = 250
- , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd')
- ;
+ , dispatch = d3.dispatch('stateChange', 'changeState','renderEnd');
- tooltip.duration(0);
//============================================================
// Private Variables
//------------------------------------------------------------
var renderWatch = nv.utils.renderWatch(dispatch);
+
tooltip
+ .duration(0)
.headerEnabled(false)
- .valueFormatter(function(d, i) {
- return d;
- });
+ .valueFormatter(function(d){return d;});
//============================================================
// Chart function
@@ -13683,11 +14257,11 @@ nv.models.sunburstChart = function() {
selection.each(function(data) {
var container = d3.select(this);
+
nv.utils.initSVG(container);
- var that = this;
- var availableWidth = nv.utils.availableWidth(width, container, margin),
- availableHeight = nv.utils.availableHeight(height, container, margin);
+ var availableWidth = nv.utils.availableWidth(width, container, margin);
+ var availableHeight = nv.utils.availableHeight(height, container, margin);
chart.update = function() {
if (duration === 0) {
@@ -13696,7 +14270,7 @@ nv.models.sunburstChart = function() {
container.transition().duration(duration).call(chart);
}
};
- chart.container = this;
+ chart.container = container;
// Display No Data message if there's nothing to show.
if (!data || !data.length) {
@@ -13706,20 +14280,8 @@ nv.models.sunburstChart = function() {
container.selectAll('.nv-noData').remove();
}
- // Setup containers and skeleton of chart
- var wrap = container.selectAll('g.nv-wrap.nv-sunburstChart').data(data);
- var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-sunburstChart').append('g');
- var g = wrap.select('g');
-
- gEnter.append('g').attr('class', 'nv-sunburstWrap');
-
- wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
-
- // Main Chart Component(s)
sunburst.width(availableWidth).height(availableHeight);
- var sunWrap = g.select('.nv-sunburstWrap').datum(data);
- d3.transition(sunWrap).call(sunburst);
-
+ container.call(sunburst);
});
renderWatch.renderEnd('sunburstChart immediate');
@@ -13731,9 +14293,9 @@ nv.models.sunburstChart = function() {
//------------------------------------------------------------
sunburst.dispatch.on('elementMouseover.tooltip', function(evt) {
- evt['series'] = {
+ evt.series = {
key: evt.data.name,
- value: evt.data.size,
+ value: (evt.data.value || evt.data.size),
color: evt.color
};
tooltip.data(evt).hidden(false);
@@ -13783,7 +14345,8 @@ nv.models.sunburstChart = function() {
nv.utils.inheritOptions(chart, sunburst);
nv.utils.initOptions(chart);
return chart;
+
};
-nv.version = "1.8.2";
+nv.version = "1.8.3";
})(); \ No newline at end of file
OpenPOWER on IntegriCloud