nvd3-fork
Version:
FORK! of NVD3, a reusable charting library written in d3.js
301 lines (254 loc) • 12.8 kB
JavaScript
nv.models.parallelCoordinatesChart = function () {
"use strict";
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var parallelCoordinates = nv.models.parallelCoordinates()
var legend = nv.models.legend()
var tooltip = nv.models.tooltip();
var dimensionTooltip = nv.models.tooltip();
var margin = { top: 0, right: 0, bottom: 0, left: 0 }
, marginTop = null
, width = null
, height = null
, showLegend = true
, color = nv.utils.defaultColor()
, state = nv.utils.state()
, dimensionData = []
, displayBrush = true
, defaultState = null
, noData = null
, nanValue = "undefined"
, dispatch = d3.dispatch('dimensionsOrder', 'brushEnd', 'stateChange', 'changeState', 'renderEnd')
, controlWidth = function () { return showControls ? 180 : 0 }
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var renderWatch = nv.utils.renderWatch(dispatch);
var stateGetter = function(data) {
return function() {
return {
active: data.map(function(d) { return !d.disabled })
};
}
};
var stateSetter = function(data) {
return function(state) {
if(state.active !== undefined) {
data.forEach(function(series, i) {
series.disabled = !state.active[i];
});
}
}
};
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
//------------------------------------------------------------
function chart(selection) {
renderWatch.reset();
renderWatch.models(parallelCoordinates);
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);
chart.update = function() { container.call(chart); };
chart.container = this;
state.setter(stateSetter(dimensionData), chart.update)
.getter(stateGetter(dimensionData))
.update();
//set state.disabled
state.disabled = dimensionData.map(function (d) { return !!d.disabled });
//Keep dimensions position in memory
dimensionData = dimensionData.map(function (d) {d.disabled = !!d.disabled; return d});
dimensionData.forEach(function (d, i) {
d.originalPosition = isNaN(d.originalPosition) ? i : d.originalPosition;
d.currentPosition = isNaN(d.currentPosition) ? i : d.currentPosition;
});
if (!defaultState) {
var key;
defaultState = {};
for(key in state) {
if(state[key] instanceof Array)
defaultState[key] = state[key].slice(0);
else
defaultState[key] = state[key];
}
}
// Display No Data message if there's nothing to show.
if(!data || !data.length) {
nv.utils.noData(chart, container);
return chart;
} else {
container.selectAll('.nv-noData').remove();
}
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container.selectAll('g.nv-wrap.nv-parallelCoordinatesChart').data([data]);
var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-parallelCoordinatesChart').append('g');
var g = wrap.select('g');
gEnter.append('g').attr('class', 'nv-parallelCoordinatesWrap');
gEnter.append('g').attr('class', 'nv-legendWrap');
g.select("rect")
.attr("width", availableWidth)
.attr("height", (availableHeight > 0) ? availableHeight : 0);
// Legend
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(dimensionData.sort(function (a, b) { return a.originalPosition - b.originalPosition; }))
.call(legend);
if (!marginTop && legend.height() !== margin.top) {
margin.top = legend.height();
availableHeight = nv.utils.availableHeight(height, container, margin);
}
wrap.select('.nv-legendWrap')
.attr('transform', 'translate( 0 ,' + (-margin.top) + ')');
}
wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// Main Chart Component(s)
parallelCoordinates
.width(availableWidth)
.height(availableHeight)
.dimensionData(dimensionData)
.displayBrush(displayBrush);
var parallelCoordinatesWrap = g.select('.nv-parallelCoordinatesWrap ')
.datum(data);
parallelCoordinatesWrap.transition().call(parallelCoordinates);
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
//Display reset brush button
parallelCoordinates.dispatch.on('brushEnd', function (active, hasActiveBrush) {
if (hasActiveBrush) {
displayBrush = true;
dispatch.brushEnd(active);
} else {
displayBrush = false;
}
});
legend.dispatch.on('stateChange', function(newState) {
for(var key in newState) {
state[key] = newState[key];
}
dispatch.stateChange(state);
chart.update();
});
//Update dimensions order and display reset sorting button
parallelCoordinates.dispatch.on('dimensionsOrder', function (e) {
dimensionData.sort(function (a, b) { return a.currentPosition - b.currentPosition; });
var isSorted = false;
dimensionData.forEach(function (d, i) {
d.currentPosition = i;
if (d.currentPosition !== d.originalPosition)
isSorted = true;
});
dispatch.dimensionsOrder(dimensionData, isSorted);
});
// Update chart from a state object passed to event handler
dispatch.on('changeState', function (e) {
if (typeof e.disabled !== 'undefined') {
dimensionData.forEach(function (series, i) {
series.disabled = e.disabled[i];
});
state.disabled = e.disabled;
}
chart.update();
});
});
renderWatch.renderEnd('parraleleCoordinateChart immediate');
return chart;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
parallelCoordinates.dispatch.on('elementMouseover.tooltip', function (evt) {
var tp = {
key: evt.label,
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) {
tooltip.hidden(true)
});
parallelCoordinates.dispatch.on('elementMousemove.tooltip', function () {
tooltip();
});
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart.dispatch = dispatch;
chart.parallelCoordinates = parallelCoordinates;
chart.legend = legend;
chart.tooltip = tooltip;
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 = _; } },
showLegend: { get: function () { return showLegend; }, set: function (_) { showLegend = _; } },
defaultState: { get: function () { return defaultState; }, set: function (_) { defaultState = _; } },
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; },
set: function (_) {
if (_.top !== undefined) {
margin.top = _.top;
marginTop = _.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(_);
legend.color(color);
parallelCoordinates.color(color);
}}
});
nv.utils.inheritOptions(chart, parallelCoordinates);
nv.utils.initOptions(chart);
return chart;
};