2 nv.models.historicalBarChart = function() {
4 //============================================================
5 // Public Variables with Default Settings
6 //------------------------------------------------------------
8 var bars = nv.models.historicalBar()
9 , xAxis = nv.models.axis()
10 , yAxis = nv.models.axis()
11 , legend = nv.models.legend()
15 var margin = {top: 30, right: 90, bottom: 50, left: 90}
16 , color = nv.utils.defaultColor()
22 , rightAlignYAxis = false
24 , tooltip = function(key, x, y, e, graph) {
25 return '<h3>' + key + '</h3>' +
26 '<p>' + y + ' at ' + x + '</p>'
32 , noData = 'No Data Available.'
33 , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
34 , transitionDuration = 250
42 .orient( (rightAlignYAxis) ? 'right' : 'left')
45 //============================================================
48 //============================================================
50 //------------------------------------------------------------
52 var showTooltip = function(e, offsetElement) {
54 // New addition to calculate position if SVG is scaled with viewBox, may move TODO: consider implementing everywhere else
56 var svg = d3.select(offsetElement).select('svg');
57 var viewBox = (svg.node()) ? svg.attr('viewBox') : null;
59 viewBox = viewBox.split(' ');
60 var ratio = parseInt(svg.style('width')) / viewBox[2];
61 e.pos[0] = e.pos[0] * ratio;
62 e.pos[1] = e.pos[1] * ratio;
66 var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
67 top = e.pos[1] + ( offsetElement.offsetTop || 0),
68 x = xAxis.tickFormat()(bars.x()(e.point, e.pointIndex)),
69 y = yAxis.tickFormat()(bars.y()(e.point, e.pointIndex)),
70 content = tooltip(e.series.key, x, y, e, chart);
72 nv.tooltip.show([left, top], content, null, null, offsetElement);
75 //============================================================
78 function chart(selection) {
79 selection.each(function(data) {
80 var container = d3.select(this),
83 var availableWidth = (width || parseInt(container.style('width')) || 960)
84 - margin.left - margin.right,
85 availableHeight = (height || parseInt(container.style('height')) || 400)
86 - margin.top - margin.bottom;
89 chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
90 chart.container = this;
93 state.disabled = data.map(function(d) { return !!d.disabled });
99 if (state[key] instanceof Array)
100 defaultState[key] = state[key].slice(0);
102 defaultState[key] = state[key];
106 //------------------------------------------------------------
107 // Display noData message if there's nothing to show.
109 if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
110 var noDataText = container.selectAll('.nv-noData').data([noData]);
112 noDataText.enter().append('text')
113 .attr('class', 'nvd3 nv-noData')
115 .style('text-anchor', 'middle');
118 .attr('x', margin.left + availableWidth / 2)
119 .attr('y', margin.top + availableHeight / 2)
120 .text(function(d) { return d });
124 container.selectAll('.nv-noData').remove();
127 //------------------------------------------------------------
130 //------------------------------------------------------------
136 //------------------------------------------------------------
139 //------------------------------------------------------------
140 // Setup containers and skeleton of chart
142 var wrap = container.selectAll('g.nv-wrap.nv-historicalBarChart').data([data]);
143 var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBarChart').append('g');
144 var g = wrap.select('g');
146 gEnter.append('g').attr('class', 'nv-x nv-axis');
147 gEnter.append('g').attr('class', 'nv-y nv-axis');
148 gEnter.append('g').attr('class', 'nv-barsWrap');
149 gEnter.append('g').attr('class', 'nv-legendWrap');
151 //------------------------------------------------------------
154 //------------------------------------------------------------
158 legend.width(availableWidth);
160 g.select('.nv-legendWrap')
164 if ( margin.top != legend.height()) {
165 margin.top = legend.height();
166 availableHeight = (height || parseInt(container.style('height')) || 400)
167 - margin.top - margin.bottom;
170 wrap.select('.nv-legendWrap')
171 .attr('transform', 'translate(0,' + (-margin.top) +')')
174 //------------------------------------------------------------
176 wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
178 if (rightAlignYAxis) {
179 g.select(".nv-y.nv-axis")
180 .attr("transform", "translate(" + availableWidth + ",0)");
184 //------------------------------------------------------------
185 // Main Chart Component(s)
188 .width(availableWidth)
189 .height(availableHeight)
190 .color(data.map(function(d,i) {
191 return d.color || color(d, i);
192 }).filter(function(d,i) { return !data[i].disabled }));
195 var barsWrap = g.select('.nv-barsWrap')
196 .datum(data.filter(function(d) { return !d.disabled }))
198 barsWrap.transition().call(bars);
200 //------------------------------------------------------------
203 //------------------------------------------------------------
209 .tickSize(-availableHeight, 0);
211 g.select('.nv-x.nv-axis')
212 .attr('transform', 'translate(0,' + y.range()[0] + ')');
213 g.select('.nv-x.nv-axis')
221 .ticks( availableHeight / 36 )
222 .tickSize( -availableWidth, 0);
224 g.select('.nv-y.nv-axis')
228 //------------------------------------------------------------
231 //============================================================
232 // Event Handling/Dispatching (in chart's scope)
233 //------------------------------------------------------------
235 legend.dispatch.on('legendClick', function(d,i) {
236 d.disabled = !d.disabled;
238 if (!data.filter(function(d) { return !d.disabled }).length) {
239 data.map(function(d) {
241 wrap.selectAll('.nv-series').classed('disabled', false);
246 state.disabled = data.map(function(d) { return !!d.disabled });
247 dispatch.stateChange(state);
249 selection.transition().call(chart);
252 legend.dispatch.on('legendDblclick', function(d) {
253 //Double clicking should always enable current series, and disabled all others.
254 data.forEach(function(d) {
259 state.disabled = data.map(function(d) { return !!d.disabled });
260 dispatch.stateChange(state);
264 dispatch.on('tooltipShow', function(e) {
265 if (tooltips) showTooltip(e, that.parentNode);
269 dispatch.on('changeState', function(e) {
271 if (typeof e.disabled !== 'undefined') {
272 data.forEach(function(series,i) {
273 series.disabled = e.disabled[i];
276 state.disabled = e.disabled;
279 selection.call(chart);
282 //============================================================
290 //============================================================
291 // Event Handling/Dispatching (out of chart's scope)
292 //------------------------------------------------------------
294 bars.dispatch.on('elementMouseover.tooltip', function(e) {
295 e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
296 dispatch.tooltipShow(e);
299 bars.dispatch.on('elementMouseout.tooltip', function(e) {
300 dispatch.tooltipHide(e);
303 dispatch.on('tooltipHide', function() {
304 if (tooltips) nv.tooltip.cleanup();
307 //============================================================
310 //============================================================
311 // Expose Public Variables
312 //------------------------------------------------------------
314 // expose chart's sub-components
315 chart.dispatch = dispatch;
317 chart.legend = legend;
321 d3.rebind(chart, bars, 'defined', 'isArea', 'x', 'y', 'size', 'xScale', 'yScale',
322 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id', 'interpolate','highlightPoint','clearHighlights', 'interactive');
324 chart.options = nv.utils.optionsFunc.bind(chart);
326 chart.margin = function(_) {
327 if (!arguments.length) return margin;
328 margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
329 margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
330 margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
331 margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
335 chart.width = function(_) {
336 if (!arguments.length) return width;
341 chart.height = function(_) {
342 if (!arguments.length) return height;
347 chart.color = function(_) {
348 if (!arguments.length) return color;
349 color = nv.utils.getColor(_);
354 chart.showLegend = function(_) {
355 if (!arguments.length) return showLegend;
360 chart.showXAxis = function(_) {
361 if (!arguments.length) return showXAxis;
366 chart.showYAxis = function(_) {
367 if (!arguments.length) return showYAxis;
372 chart.rightAlignYAxis = function(_) {
373 if(!arguments.length) return rightAlignYAxis;
375 yAxis.orient( (_) ? 'right' : 'left');
379 chart.tooltips = function(_) {
380 if (!arguments.length) return tooltips;
385 chart.tooltipContent = function(_) {
386 if (!arguments.length) return tooltip;
391 chart.state = function(_) {
392 if (!arguments.length) return state;
397 chart.defaultState = function(_) {
398 if (!arguments.length) return defaultState;
403 chart.noData = function(_) {
404 if (!arguments.length) return noData;
409 chart.transitionDuration = function(_) {
410 if (!arguments.length) return transitionDuration;
411 transitionDuration = _;
415 //============================================================