Initial OpenECOMP policy/engine commit
[policy/engine.git] / ecomp-sdk-app / src / main / webapp / static / fusion / raptor / d3 / js / models / multiChart.js
1 nv.models.multiChart = function() {
2   "use strict";
3   //============================================================
4   // Public Variables with Default Settings
5   //------------------------------------------------------------
6
7   var margin = {top: 30, right: 20, bottom: 50, left: 60},
8       color = d3.scale.category20().range(),
9       width = null, 
10       height = null,
11       showLegend = true,
12       tooltips = true,
13       tooltip = function(key, x, y, e, graph) {
14         return '<h3>' + key + '</h3>' +
15                '<p>' +  y + ' at ' + x + '</p>'
16       },
17       x,
18       y,
19       yDomain1,
20       yDomain2
21       ; //can be accessed via chart.lines.[x/y]Scale()
22
23   //============================================================
24   // Private Variables
25   //------------------------------------------------------------
26
27   var x = d3.scale.linear(),
28       yScale1 = d3.scale.linear(),
29       yScale2 = d3.scale.linear(),
30
31       lines1 = nv.models.line().yScale(yScale1),
32       lines2 = nv.models.line().yScale(yScale2),
33
34       bars1 = nv.models.multiBar().stacked(false).yScale(yScale1),
35       bars2 = nv.models.multiBar().stacked(false).yScale(yScale2),
36
37       stack1 = nv.models.stackedArea().yScale(yScale1),
38       stack2 = nv.models.stackedArea().yScale(yScale2),
39
40       xAxis = nv.models.axis().scale(x).orient('bottom').tickPadding(5),
41       yAxis1 = nv.models.axis().scale(yScale1).orient('left'),
42       yAxis2 = nv.models.axis().scale(yScale2).orient('right'),
43
44       legend = nv.models.legend().height(30),
45       dispatch = d3.dispatch('tooltipShow', 'tooltipHide');
46
47   var showTooltip = function(e, offsetElement) {
48     var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
49         top = e.pos[1] + ( offsetElement.offsetTop || 0),
50         x = xAxis.tickFormat()(lines1.x()(e.point, e.pointIndex)),
51         y = ((e.series.yAxis == 2) ? yAxis2 : yAxis1).tickFormat()(lines1.y()(e.point, e.pointIndex)),
52         content = tooltip(e.series.key, x, y, e, chart);
53
54     nv.tooltip.show([left, top], content, undefined, undefined, offsetElement.offsetParent);
55   };
56
57   function chart(selection) {
58     selection.each(function(data) {
59       var container = d3.select(this),
60           that = this;
61
62       chart.update = function() { container.transition().call(chart); };
63       chart.container = this;
64
65       var availableWidth = (width  || parseInt(container.style('width')) || 960)
66                              - margin.left - margin.right,
67           availableHeight = (height || parseInt(container.style('height')) || 400)
68                              - margin.top - margin.bottom;
69
70       var dataLines1 = data.filter(function(d) {return !d.disabled && d.type == 'line' && d.yAxis == 1})
71       var dataLines2 = data.filter(function(d) {return !d.disabled && d.type == 'line' && d.yAxis == 2})
72       var dataBars1 = data.filter(function(d) {return !d.disabled && d.type == 'bar' && d.yAxis == 1})
73       var dataBars2 = data.filter(function(d) {return !d.disabled && d.type == 'bar' && d.yAxis == 2})
74       var dataStack1 = data.filter(function(d) {return !d.disabled && d.type == 'area' && d.yAxis == 1})
75       var dataStack2 = data.filter(function(d) {return !d.disabled && d.type == 'area' && d.yAxis == 2})
76
77       var series1 = data.filter(function(d) {return !d.disabled && d.yAxis == 1})
78             .map(function(d) {
79               return d.values.map(function(d,i) {
80                 return { x: d.x, y: d.y }
81               })
82             })
83
84       var series2 = data.filter(function(d) {return !d.disabled && d.yAxis == 2})
85             .map(function(d) {
86               return d.values.map(function(d,i) {
87                 return { x: d.x, y: d.y }
88               })
89             })
90
91       x   .domain(d3.extent(d3.merge(series1.concat(series2)), function(d) { return d.x } ))
92           .range([0, availableWidth]);
93
94       var wrap = container.selectAll('g.wrap.multiChart').data([data]);
95       var gEnter = wrap.enter().append('g').attr('class', 'wrap nvd3 multiChart').append('g');
96
97       gEnter.append('g').attr('class', 'x axis');
98       gEnter.append('g').attr('class', 'y1 axis');
99       gEnter.append('g').attr('class', 'y2 axis');
100       gEnter.append('g').attr('class', 'lines1Wrap');
101       gEnter.append('g').attr('class', 'lines2Wrap');
102       gEnter.append('g').attr('class', 'bars1Wrap');
103       gEnter.append('g').attr('class', 'bars2Wrap');
104       gEnter.append('g').attr('class', 'stack1Wrap');
105       gEnter.append('g').attr('class', 'stack2Wrap');
106       gEnter.append('g').attr('class', 'legendWrap');
107
108       var g = wrap.select('g');
109
110       if (showLegend) {
111         legend.width( availableWidth / 2 );
112
113         g.select('.legendWrap')
114             .datum(data.map(function(series) { 
115               series.originalKey = series.originalKey === undefined ? series.key : series.originalKey;
116               series.key = series.originalKey + (series.yAxis == 1 ? '' : ' (right axis)');
117               return series;
118             }))
119           .call(legend);
120
121         if ( margin.top != legend.height()) {
122           margin.top = legend.height();
123           availableHeight = (height || parseInt(container.style('height')) || 400)
124                              - margin.top - margin.bottom;
125         }
126
127         g.select('.legendWrap')
128             .attr('transform', 'translate(' + ( availableWidth / 2 ) + ',' + (-margin.top) +')');
129       }
130
131
132       lines1
133         .width(availableWidth)
134         .height(availableHeight)
135         .interpolate("monotone")
136         .color(data.map(function(d,i) {
137           return d.color || color[i % color.length];
138         }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'line'}));
139
140       lines2
141         .width(availableWidth)
142         .height(availableHeight)
143         .interpolate("monotone")
144         .color(data.map(function(d,i) {
145           return d.color || color[i % color.length];
146         }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'line'}));
147
148       bars1
149         .width(availableWidth)
150         .height(availableHeight)
151         .color(data.map(function(d,i) {
152           return d.color || color[i % color.length];
153         }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'bar'}));
154
155       bars2
156         .width(availableWidth)
157         .height(availableHeight)
158         .color(data.map(function(d,i) {
159           return d.color || color[i % color.length];
160         }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'bar'}));
161
162       stack1
163         .width(availableWidth)
164         .height(availableHeight)
165         .color(data.map(function(d,i) {
166           return d.color || color[i % color.length];
167         }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 1 && data[i].type == 'area'}));
168
169       stack2
170         .width(availableWidth)
171         .height(availableHeight)
172         .color(data.map(function(d,i) {
173           return d.color || color[i % color.length];
174         }).filter(function(d,i) { return !data[i].disabled && data[i].yAxis == 2 && data[i].type == 'area'}));
175
176       g.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
177
178
179       var lines1Wrap = g.select('.lines1Wrap')
180           .datum(dataLines1)
181       var bars1Wrap = g.select('.bars1Wrap')
182           .datum(dataBars1)
183       var stack1Wrap = g.select('.stack1Wrap')
184           .datum(dataStack1)
185
186       var lines2Wrap = g.select('.lines2Wrap')
187           .datum(dataLines2)
188       var bars2Wrap = g.select('.bars2Wrap')
189           .datum(dataBars2)
190       var stack2Wrap = g.select('.stack2Wrap')
191           .datum(dataStack2)
192
193       var extraValue1 = dataStack1.length ? dataStack1.map(function(a){return a.values}).reduce(function(a,b){
194         return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
195       }).concat([{x:0, y:0}]) : []
196       var extraValue2 = dataStack2.length ? dataStack2.map(function(a){return a.values}).reduce(function(a,b){
197         return a.map(function(aVal,i){return {x: aVal.x, y: aVal.y + b[i].y}})
198       }).concat([{x:0, y:0}]) : []
199
200       yScale1 .domain(yDomain1 || d3.extent(d3.merge(series1).concat(extraValue1), function(d) { return d.y } ))
201               .range([0, availableHeight])
202
203       yScale2 .domain(yDomain2 || d3.extent(d3.merge(series2).concat(extraValue2), function(d) { return d.y } ))
204               .range([0, availableHeight])
205
206       lines1.yDomain(yScale1.domain())
207       bars1.yDomain(yScale1.domain())
208       stack1.yDomain(yScale1.domain())
209
210       lines2.yDomain(yScale2.domain())
211       bars2.yDomain(yScale2.domain())
212       stack2.yDomain(yScale2.domain())
213
214       if(dataStack1.length){d3.transition(stack1Wrap).call(stack1);}
215       if(dataStack2.length){d3.transition(stack2Wrap).call(stack2);}
216
217       if(dataBars1.length){d3.transition(bars1Wrap).call(bars1);}
218       if(dataBars2.length){d3.transition(bars2Wrap).call(bars2);}
219
220       if(dataLines1.length){d3.transition(lines1Wrap).call(lines1);}
221       if(dataLines2.length){d3.transition(lines2Wrap).call(lines2);}
222       
223
224
225       xAxis
226         .ticks( availableWidth / 100 )
227         .tickSize(-availableHeight, 0);
228
229       g.select('.x.axis')
230           .attr('transform', 'translate(0,' + availableHeight + ')');
231       d3.transition(g.select('.x.axis'))
232           .call(xAxis);
233
234       yAxis1
235         .ticks( availableHeight / 36 )
236         .tickSize( -availableWidth, 0);
237
238
239       d3.transition(g.select('.y1.axis'))
240           .call(yAxis1);
241
242       yAxis2
243         .ticks( availableHeight / 36 )
244         .tickSize( -availableWidth, 0);
245
246       d3.transition(g.select('.y2.axis'))
247           .call(yAxis2);
248
249       g.select('.y2.axis')
250           .style('opacity', series2.length ? 1 : 0)
251           .attr('transform', 'translate(' + x.range()[1] + ',0)');
252
253       legend.dispatch.on('stateChange', function(newState) { 
254         chart.update();
255       });
256      
257       dispatch.on('tooltipShow', function(e) {
258         if (tooltips) showTooltip(e, that.parentNode);
259       });
260
261     });
262
263     return chart;
264   }
265
266
267   //============================================================
268   // Event Handling/Dispatching (out of chart's scope)
269   //------------------------------------------------------------
270
271   lines1.dispatch.on('elementMouseover.tooltip', function(e) {
272     e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
273     dispatch.tooltipShow(e);
274   });
275
276   lines1.dispatch.on('elementMouseout.tooltip', function(e) {
277     dispatch.tooltipHide(e);
278   });
279
280   lines2.dispatch.on('elementMouseover.tooltip', function(e) {
281     e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
282     dispatch.tooltipShow(e);
283   });
284
285   lines2.dispatch.on('elementMouseout.tooltip', function(e) {
286     dispatch.tooltipHide(e);
287   });
288
289   bars1.dispatch.on('elementMouseover.tooltip', function(e) {
290     e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
291     dispatch.tooltipShow(e);
292   });
293
294   bars1.dispatch.on('elementMouseout.tooltip', function(e) {
295     dispatch.tooltipHide(e);
296   });
297
298   bars2.dispatch.on('elementMouseover.tooltip', function(e) {
299     e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
300     dispatch.tooltipShow(e);
301   });
302
303   bars2.dispatch.on('elementMouseout.tooltip', function(e) {
304     dispatch.tooltipHide(e);
305   });
306
307   stack1.dispatch.on('tooltipShow', function(e) {
308     //disable tooltips when value ~= 0
309     //// TODO: consider removing points from voronoi that have 0 value instead of this hack
310     if (!Math.round(stack1.y()(e.point) * 100)) {  // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
311       setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
312       return false;
313     }
314
315     e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
316     dispatch.tooltipShow(e);
317   });
318
319   stack1.dispatch.on('tooltipHide', function(e) {
320     dispatch.tooltipHide(e);
321   });
322
323   stack2.dispatch.on('tooltipShow', function(e) {
324     //disable tooltips when value ~= 0
325     //// TODO: consider removing points from voronoi that have 0 value instead of this hack
326     if (!Math.round(stack2.y()(e.point) * 100)) {  // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
327       setTimeout(function() { d3.selectAll('.point.hover').classed('hover', false) }, 0);
328       return false;
329     }
330
331     e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top],
332     dispatch.tooltipShow(e);
333   });
334
335   stack2.dispatch.on('tooltipHide', function(e) {
336     dispatch.tooltipHide(e);
337   });
338
339     lines1.dispatch.on('elementMouseover.tooltip', function(e) {
340     e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
341     dispatch.tooltipShow(e);
342   });
343
344   lines1.dispatch.on('elementMouseout.tooltip', function(e) {
345     dispatch.tooltipHide(e);
346   });
347
348   lines2.dispatch.on('elementMouseover.tooltip', function(e) {
349     e.pos = [e.pos[0] +  margin.left, e.pos[1] + margin.top];
350     dispatch.tooltipShow(e);
351   });
352
353   lines2.dispatch.on('elementMouseout.tooltip', function(e) {
354     dispatch.tooltipHide(e);
355   });
356
357   dispatch.on('tooltipHide', function() {
358     if (tooltips) nv.tooltip.cleanup();
359   });
360
361
362
363   //============================================================
364   // Global getters and setters
365   //------------------------------------------------------------
366
367   chart.dispatch = dispatch;
368   chart.lines1 = lines1;
369   chart.lines2 = lines2;
370   chart.bars1 = bars1;
371   chart.bars2 = bars2;
372   chart.stack1 = stack1;
373   chart.stack2 = stack2;
374   chart.xAxis = xAxis;
375   chart.yAxis1 = yAxis1;
376   chart.yAxis2 = yAxis2;
377   chart.options = nv.utils.optionsFunc.bind(chart);
378
379   chart.x = function(_) {
380     if (!arguments.length) return getX;
381     getX = _;
382     lines1.x(_);
383     bars1.x(_);
384     return chart;
385   };
386
387   chart.y = function(_) {
388     if (!arguments.length) return getY;
389     getY = _;
390     lines1.y(_);
391     bars1.y(_);
392     return chart;
393   };
394
395   chart.yDomain1 = function(_) {
396     if (!arguments.length) return yDomain1;
397     yDomain1 = _;
398     return chart;
399   };
400
401   chart.yDomain2 = function(_) {
402     if (!arguments.length) return yDomain2;
403     yDomain2 = _;
404     return chart;
405   };
406
407   chart.margin = function(_) {
408     if (!arguments.length) return margin;
409     margin = _;
410     return chart;
411   };
412
413   chart.width = function(_) {
414     if (!arguments.length) return width;
415     width = _;
416     return chart;
417   };
418
419   chart.height = function(_) {
420     if (!arguments.length) return height;
421     height = _;
422     return chart;
423   };
424
425   chart.color = function(_) {
426     if (!arguments.length) return color;
427     color = _;
428     legend.color(_);
429     return chart;
430   };
431
432   chart.showLegend = function(_) {
433     if (!arguments.length) return showLegend;
434     showLegend = _;
435     return chart;
436   };
437
438   chart.tooltips = function(_) {
439     if (!arguments.length) return tooltips;
440     tooltips = _;
441     return chart;
442   };
443
444   chart.tooltipContent = function(_) {
445     if (!arguments.length) return tooltip;
446     tooltip = _;
447     return chart;
448   };
449
450   return chart;
451 }
452