Initial OpenECOMP policy/engine commit
[policy/engine.git] / ecomp-sdk-app / src / main / webapp / static / fusion / raptor / d3 / js / models / multiBarTimeSeries.js
1 nv.models.multiBarTimeSeries = function() {
2   "use strict";
3   //============================================================
4   // Public Variables with Default Settings
5   //------------------------------------------------------------
6
7   var margin = {top: 0, right: 0, bottom: 0, left: 0}
8     , width = 960
9     , height = 500
10     , x = d3.time.scale()
11     , y = d3.scale.linear()
12     , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
13     , getX = function(d) { return d.x }
14     , getY = function(d) { return d.y }
15     , forceY = [0] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
16     , clipEdge = true
17     , stacked = false
18     , color = nv.utils.defaultColor()
19     , delay = 1200
20     , xDomain
21     , yDomain
22     , xRange
23     , yRange
24     , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
25     ;
26
27   //============================================================
28
29
30   //============================================================
31   // Private Variables
32   //------------------------------------------------------------
33
34   var x0, y0 //used to store previous scales
35       ;
36
37   //============================================================
38
39
40   function chart(selection) {
41     selection.each(function(data) {
42       var availableWidth = width - margin.left - margin.right,
43           availableHeight = height - margin.top - margin.bottom,
44           container = d3.select(this);
45
46       if (stacked)
47         data = d3.layout.stack()
48                  .offset('zero')
49                  .values(function(d){ return d.values })
50                  .y(getY)
51                  (data);
52
53
54       //add series index to each data point for reference
55       data.forEach(function(series, i) {
56         series.values.forEach(function(point) {
57           point.series = i;
58         });
59       });
60
61       //------------------------------------------------------------
62       // Setup Scales
63
64       // remap and flatten the data for use in calculating the scales' domains
65       var seriesData = (xDomain && yDomain) ? [] : // if we know xDomain and yDomain, no need to calculate
66             data.map(function(d) {
67               return d.values.map(function(d,i) {
68                 return { x: getX(d,i), y: getY(d,i), y0: d.y0 }
69               })
70             });
71
72       x   .domain(xDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.x })))
73           .range(xRange || [0, availableWidth]);
74
75       y   .domain(yDomain || d3.extent(d3.merge(seriesData).map(function(d) { return d.y + (stacked ? d.y0 : 0) }).concat(forceY)))
76           .range(yRange || [availableHeight, 0]);
77
78
79       // If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
80       if (x.domain()[0] === x.domain()[1])
81         x.domain()[0] ?
82             x.domain([x.domain()[0] - x.domain()[0] * 0.01, x.domain()[1] + x.domain()[1] * 0.01])
83           : x.domain([-1,1]);
84
85       if (y.domain()[0] === y.domain()[1])
86         y.domain()[0] ?
87             y.domain([y.domain()[0] + y.domain()[0] * 0.01, y.domain()[1] - y.domain()[1] * 0.01])
88           : y.domain([-1,1]);
89
90
91       x0 = x0 || x;
92       y0 = y0 || y;
93
94       //------------------------------------------------------------
95
96
97       //------------------------------------------------------------
98       // Setup containers and skeleton of chart
99
100       var wrap = container.selectAll('g.nv-wrap.nv-multibar').data([data]);
101       var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-multibar');
102       var defsEnter = wrapEnter.append('defs');
103       var gEnter = wrapEnter.append('g');
104       var g = wrap.select('g')
105
106       gEnter.append('g').attr('class', 'nv-groups');
107
108       wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
109
110       //------------------------------------------------------------
111
112
113
114       defsEnter.append('clipPath')
115           .attr('id', 'nv-edge-clip-' + id)
116         .append('rect');
117       wrap.select('#nv-edge-clip-' + id + ' rect')
118           .attr('width', availableWidth)
119           .attr('height', availableHeight);
120
121       g   .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
122
123
124
125       var groups = wrap.select('.nv-groups').selectAll('.nv-group')
126           .data(function(d) { return d }, function(d) { return d.key });
127       groups.enter().append('g')
128           .style('stroke-opacity', 1e-6)
129           .style('fill-opacity', 1e-6);
130       d3.transition(groups.exit())
131           //.style('stroke-opacity', 1e-6)
132           //.style('fill-opacity', 1e-6)
133         .selectAll('rect.nv-bar')
134         .delay(function(d,i) { return i * delay/ data[0].values.length })
135           .attr('y', function(d) { return stacked ? y0(d.y0) : y0(0) })
136           .attr('height', 0)
137           .remove();
138       groups
139           .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
140           .classed('hover', function(d) { return d.hover })
141           .style('fill', function(d,i){ return color(d, i) })
142           .style('stroke', function(d,i){ return color(d, i) });
143       d3.transition(groups)
144           .style('stroke-opacity', 1)
145           .style('fill-opacity', .75);
146
147
148       var bars = groups.selectAll('rect.nv-bar')
149           .data(function(d) { return d.values });
150
151       bars.exit().remove();
152
153       var maxElements = 0;
154       for(var ei=0; ei<seriesData.length; ei+=1) {
155           maxElements = Math.max(seriesData[ei].length, maxElements);
156       }
157
158       var bandWidth = (availableWidth / maxElements)-0.1;
159       var barWidth = bandWidth / data.length;
160
161       var barsEnter = bars.enter().append('rect')
162           .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
163           .attr('x', function(d,i,j) {
164               return stacked ? 0 : (i * bandWidth) + ( j * barWidth )
165           })
166           .attr('y', function(d) { return y0(stacked ? d.y0 : 0) })
167           .attr('height', 0)
168           .attr('width', stacked ? bandWidth : barWidth );
169       bars
170           .on('mouseover', function(d,i) { //TODO: figure out why j works above, but not here
171             d3.select(this).classed('hover', true);
172             dispatch.elementMouseover({
173               value: getY(d,i),
174               point: d,
175               series: data[d.series],
176               pos: [x(getX(d,i)) + (barWidth * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))],  // TODO: Figure out why the value appears to be shifted
177               pointIndex: i,
178               seriesIndex: d.series,
179               e: d3.event
180             });
181           })
182           .on('mouseout', function(d,i) {
183             d3.select(this).classed('hover', false);
184             dispatch.elementMouseout({
185               value: getY(d,i),
186               point: d,
187               series: data[d.series],
188               pointIndex: i,
189               seriesIndex: d.series,
190               e: d3.event
191             });
192           })
193           .on('click', function(d,i) {
194             dispatch.elementClick({
195               value: getY(d,i),
196               point: d,
197               series: data[d.series],
198               pos: [x(getX(d,i)) + (barWidth * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))],  // TODO: Figure out why the value appears to be shifted
199               pointIndex: i,
200               seriesIndex: d.series,
201               e: d3.event
202             });
203             d3.event.stopPropagation();
204           })
205           .on('dblclick', function(d,i) {
206             dispatch.elementDblClick({
207               value: getY(d,i),
208               point: d,
209               series: data[d.series],
210               pos: [x(getX(d,i)) + (barWidth * (stacked ? data.length / 2 : d.series + .5) / data.length), y(getY(d,i) + (stacked ? d.y0 : 0))],  // TODO: Figure out why the value appears to be shifted
211               pointIndex: i,
212               seriesIndex: d.series,
213               e: d3.event
214             });
215             d3.event.stopPropagation();
216           });
217       bars
218           .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
219           .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
220       if (stacked)
221         d3.transition(bars)
222             .delay(function(d,i) { return i * delay / data[0].values.length })
223             .attr('y', function(d,i) {
224               return y(getY(d,i) + (stacked ? d.y0 : 0));
225             })
226             .attr('height', function(d,i) {
227               return Math.abs(y(d.y + (stacked ? d.y0 : 0)) - y((stacked ? d.y0 : 0)))
228             })
229             .each('end', function() {
230               d3.transition(d3.select(this))
231                 .attr('x', function(d,i) {
232                   return stacked ? 0 : (i * bandWidth) + ( j * barWidth )
233                 })
234                 .attr('width', stacked ? bandWidth : barWidth );
235             })
236       else
237         d3.transition(bars)
238           .delay(function(d,i) { return i * delay/ data[0].values.length })
239             .attr('x', function(d,i) {
240               return d.series * barWidth
241             })
242             .attr('width', barWidth)
243             .each('end', function() {
244               d3.transition(d3.select(this))
245                 .attr('y', function(d,i) {
246                   return getY(d,i) < 0 ?
247                     y(0) :
248                     y(getY(d,i))
249                 })
250                 .attr('height', function(d,i) {
251                   return Math.abs(y(getY(d,i)) - y(0))
252                 });
253             })
254
255
256       //store old scales for use in transitions on update
257       x0 = x.copy();
258       y0 = y.copy();
259
260     });
261
262     return chart;
263   }
264
265
266   //============================================================
267   // Expose Public Variables
268   //------------------------------------------------------------
269
270   chart.dispatch = dispatch;
271
272   chart.options = nv.utils.optionsFunc.bind(chart);
273
274   chart.x = function(_) {
275     if (!arguments.length) return getX;
276     getX = _;
277     return chart;
278   };
279
280   chart.y = function(_) {
281     if (!arguments.length) return getY;
282     getY = _;
283     return chart;
284   };
285
286   chart.margin = function(_) {
287     if (!arguments.length) return margin;
288     margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
289     margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
290     margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
291     margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
292     return chart;
293   };
294
295   chart.width = function(_) {
296     if (!arguments.length) return width;
297     width = _;
298     return chart;
299   };
300
301   chart.height = function(_) {
302     if (!arguments.length) return height;
303     height = _;
304     return chart;
305   };
306
307   chart.xScale = function(_) {
308     if (!arguments.length) return x;
309     x = _;
310     return chart;
311   };
312
313   chart.yScale = function(_) {
314     if (!arguments.length) return y;
315     y = _;
316     return chart;
317   };
318
319   chart.xDomain = function(_) {
320     if (!arguments.length) return xDomain;
321     xDomain = _;
322     return chart;
323   };
324
325   chart.yDomain = function(_) {
326     if (!arguments.length) return yDomain;
327     yDomain = _;
328     return chart;
329   };
330
331   chart.xRange = function(_) {
332     if (!arguments.length) return xRange;
333     xRange = _;
334     return chart;
335   };
336
337   chart.yRange = function(_) {
338     if (!arguments.length) return yRange;
339     yRange = _;
340     return chart;
341   };
342
343   chart.forceY = function(_) {
344     if (!arguments.length) return forceY;
345     forceY = _;
346     return chart;
347   };
348
349   chart.stacked = function(_) {
350     if (!arguments.length) return stacked;
351     stacked = _;
352     return chart;
353   };
354
355   chart.clipEdge = function(_) {
356     if (!arguments.length) return clipEdge;
357     clipEdge = _;
358     return chart;
359   };
360
361   chart.color = function(_) {
362     if (!arguments.length) return color;
363     color = nv.utils.getColor(_);
364     return chart;
365   };
366
367   chart.id = function(_) {
368     if (!arguments.length) return id;
369     id = _;
370     return chart;
371   };
372
373   chart.delay = function(_) {
374     if (!arguments.length) return delay;
375     delay = _;
376     return chart;
377   };
378
379   //============================================================
380
381
382   return chart;
383 }
384