Initial OpenECOMP policy/engine commit
[policy/engine.git] / ecomp-sdk-app / src / main / webapp / static / fusion / raptor / d3 / js / models / scatterChart.js
1 nv.models.scatterChart = function() {
2   "use strict";
3   //============================================================
4   // Public Variables with Default Settings
5   //------------------------------------------------------------
6
7   var scatter      = nv.models.scatter()
8     , xAxis        = nv.models.axis()
9     , yAxis        = nv.models.axis()
10     , legend       = nv.models.legend()
11     , controls     = nv.models.legend()
12     , distX        = nv.models.distribution()
13     , distY        = nv.models.distribution()
14     ;
15
16   var margin       = {top: 30, right: 20, bottom: 50, left: 75}
17     , width        = null
18     , height       = null
19     , color        = nv.utils.defaultColor()
20     , x            = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.xScale()
21     , y            = d3.fisheye ? d3.fisheye.scale(d3.scale.linear).distortion(0) : scatter.yScale()
22     , xPadding     = 0
23     , yPadding     = 0
24     , showDistX    = false
25     , showDistY    = false
26     , showLegend   = true
27     , showXAxis    = true
28     , showYAxis    = true
29     , rightAlignYAxis = false
30     , showControls = !!d3.fisheye
31     , fisheye      = 0
32     , pauseFisheye = false
33     , tooltips     = true
34     , tooltipX     = function(key, x, y) { return '<strong>' + x + '</strong>' }
35     , tooltipY     = function(key, x, y) { return '<strong>' + y + '</strong>' }
36     , tooltip      = null
37     , state = {}
38     , defaultState = null
39     , dispatch     = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
40     , noData       = "No Data Available."
41     , transitionDuration = 250
42     ;
43
44   scatter
45     .xScale(x)
46     .yScale(y)
47     ;
48   xAxis
49     .orient('bottom')
50     .tickPadding(10)
51     ;
52   yAxis
53     .orient((rightAlignYAxis) ? 'right' : 'left')
54     .tickPadding(10)
55     ;
56   distX
57     .axis('x')
58     ;
59   distY
60     .axis('y')
61     ;
62
63   controls.updateState(false);
64
65   //============================================================
66
67
68   //============================================================
69   // Private Variables
70   //------------------------------------------------------------
71
72   var x0, y0;
73
74   var showTooltip = function(e, offsetElement) {
75     //TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?)
76
77     var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
78         top = e.pos[1] + ( offsetElement.offsetTop || 0),
79         leftX = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
80         topX = y.range()[0] + margin.top + ( offsetElement.offsetTop || 0),
81         leftY = x.range()[0] + margin.left + ( offsetElement.offsetLeft || 0 ),
82         topY = e.pos[1] + ( offsetElement.offsetTop || 0),
83         xVal = xAxis.tickFormat()(scatter.x()(e.point, e.pointIndex)),
84         yVal = yAxis.tickFormat()(scatter.y()(e.point, e.pointIndex));
85
86       if( tooltipX != null )
87           nv.tooltip.show([leftX, topX], tooltipX(e.series.key, xVal, yVal, e, chart), 'n', 1, offsetElement, 'x-nvtooltip');
88       if( tooltipY != null )
89           nv.tooltip.show([leftY, topY], tooltipY(e.series.key, xVal, yVal, e, chart), 'e', 1, offsetElement, 'y-nvtooltip');
90       if( tooltip != null )
91           nv.tooltip.show([left, top], tooltip(e.series.key, xVal, yVal, e, chart), e.value < 0 ? 'n' : 's', null, offsetElement);
92   };
93
94   var controlsData = [
95     { key: 'Magnify', disabled: true }
96   ];
97
98   //============================================================
99
100
101   function chart(selection) {
102     selection.each(function(data) {
103       var container = d3.select(this),
104           that = this;
105
106       var availableWidth = (width  || parseInt(container.style('width')) || 960)
107                              - margin.left - margin.right,
108           availableHeight = (height || parseInt(container.style('height')) || 400)
109                              - margin.top - margin.bottom;
110
111       chart.update = function() { container.transition().duration(transitionDuration).call(chart); };
112       chart.container = this;
113
114       //set state.disabled
115       state.disabled = data.map(function(d) { return !!d.disabled });
116
117       if (!defaultState) {
118         var key;
119         defaultState = {};
120         for (key in state) {
121           if (state[key] instanceof Array)
122             defaultState[key] = state[key].slice(0);
123           else
124             defaultState[key] = state[key];
125         }
126       }
127
128       //------------------------------------------------------------
129       // Display noData message if there's nothing to show.
130
131       if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
132         var noDataText = container.selectAll('.nv-noData').data([noData]);
133
134         noDataText.enter().append('text')
135           .attr('class', 'nvd3 nv-noData')
136           .attr('dy', '-.7em')
137           .style('text-anchor', 'middle');
138
139         noDataText
140           .attr('x', margin.left + availableWidth / 2)
141           .attr('y', margin.top + availableHeight / 2)
142           .text(function(d) { return d });
143
144         return chart;
145       } else {
146         container.selectAll('.nv-noData').remove();
147       }
148
149       //------------------------------------------------------------
150
151
152       //------------------------------------------------------------
153       // Setup Scales
154
155       x0 = x0 || x;
156       y0 = y0 || y;
157
158       //------------------------------------------------------------
159
160
161       //------------------------------------------------------------
162       // Setup containers and skeleton of chart
163
164       var wrap = container.selectAll('g.nv-wrap.nv-scatterChart').data([data]);
165       var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-scatterChart nv-chart-' + scatter.id());
166       var gEnter = wrapEnter.append('g');
167       var g = wrap.select('g');
168
169       // background for pointer events
170       gEnter.append('rect').attr('class', 'nvd3 nv-background');
171
172       gEnter.append('g').attr('class', 'nv-x nv-axis');
173       gEnter.append('g').attr('class', 'nv-y nv-axis');
174       gEnter.append('g').attr('class', 'nv-scatterWrap');
175       gEnter.append('g').attr('class', 'nv-distWrap');
176       gEnter.append('g').attr('class', 'nv-legendWrap');
177       gEnter.append('g').attr('class', 'nv-controlsWrap');
178
179       //------------------------------------------------------------
180
181
182       //------------------------------------------------------------
183       // Legend
184
185       if (showLegend) {
186         var legendWidth = (showControls) ? availableWidth / 2 : availableWidth;
187         legend.width(legendWidth);
188
189         wrap.select('.nv-legendWrap')
190             .datum(data)
191             .call(legend);
192
193         if ( margin.top != legend.height()) {
194           margin.top = legend.height();
195           availableHeight = (height || parseInt(container.style('height')) || 400)
196                              - margin.top - margin.bottom;
197         }
198
199         wrap.select('.nv-legendWrap')
200             .attr('transform', 'translate(' + (availableWidth - legendWidth) + ',' + (-margin.top) +')');
201       }
202
203       //------------------------------------------------------------
204
205
206       //------------------------------------------------------------
207       // Controls
208
209       if (showControls) {
210         controls.width(180).color(['#444']);
211         g.select('.nv-controlsWrap')
212             .datum(controlsData)
213             .attr('transform', 'translate(0,' + (-margin.top) +')')
214             .call(controls);
215       }
216
217       //------------------------------------------------------------
218
219
220       wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
221
222       if (rightAlignYAxis) {
223           g.select(".nv-y.nv-axis")
224               .attr("transform", "translate(" + availableWidth + ",0)");
225       }
226
227       //------------------------------------------------------------
228       // Main Chart Component(s)
229
230       scatter
231           .width(availableWidth)
232           .height(availableHeight)
233           .color(data.map(function(d,i) {
234             return d.color || color(d, i);
235           }).filter(function(d,i) { return !data[i].disabled }));
236
237       if (xPadding !== 0)
238         scatter.xDomain(null);
239
240       if (yPadding !== 0)
241         scatter.yDomain(null);
242
243       wrap.select('.nv-scatterWrap')
244           .datum(data.filter(function(d) { return !d.disabled }))
245           .call(scatter);
246
247       //Adjust for x and y padding
248       if (xPadding !== 0) {
249         var xRange = x.domain()[1] - x.domain()[0];
250         scatter.xDomain([x.domain()[0] - (xPadding * xRange), x.domain()[1] + (xPadding * xRange)]);
251       }
252
253       if (yPadding !== 0) {
254         var yRange = y.domain()[1] - y.domain()[0];
255         scatter.yDomain([y.domain()[0] - (yPadding * yRange), y.domain()[1] + (yPadding * yRange)]);
256       }
257
258       //Only need to update the scatter again if x/yPadding changed the domain.
259       if (yPadding !== 0 || xPadding !== 0) {
260         wrap.select('.nv-scatterWrap')
261             .datum(data.filter(function(d) { return !d.disabled }))
262             .call(scatter);
263       }
264
265       //------------------------------------------------------------
266
267
268       //------------------------------------------------------------
269       // Setup Axes
270       if (showXAxis) {
271         xAxis
272             .scale(x)
273             .ticks( xAxis.ticks() && xAxis.ticks().length ? xAxis.ticks() : availableWidth / 100 )
274             .tickSize( -availableHeight , 0);
275
276         g.select('.nv-x.nv-axis')
277             .attr('transform', 'translate(0,' + y.range()[0] + ')')
278             .call(xAxis);
279
280       }
281
282       if (showYAxis) {
283         yAxis
284             .scale(y)
285             .ticks( yAxis.ticks() && yAxis.ticks().length ? yAxis.ticks() : availableHeight / 36 )
286             .tickSize( -availableWidth, 0);
287
288         g.select('.nv-y.nv-axis')
289             .call(yAxis);
290       }
291
292
293       if (showDistX) {
294         distX
295             .getData(scatter.x())
296             .scale(x)
297             .width(availableWidth)
298             .color(data.map(function(d,i) {
299               return d.color || color(d, i);
300             }).filter(function(d,i) { return !data[i].disabled }));
301         gEnter.select('.nv-distWrap').append('g')
302             .attr('class', 'nv-distributionX');
303         g.select('.nv-distributionX')
304             .attr('transform', 'translate(0,' + y.range()[0] + ')')
305             .datum(data.filter(function(d) { return !d.disabled }))
306             .call(distX);
307       }
308
309       if (showDistY) {
310         distY
311             .getData(scatter.y())
312             .scale(y)
313             .width(availableHeight)
314             .color(data.map(function(d,i) {
315               return d.color || color(d, i);
316             }).filter(function(d,i) { return !data[i].disabled }));
317         gEnter.select('.nv-distWrap').append('g')
318             .attr('class', 'nv-distributionY');
319         g.select('.nv-distributionY')
320             .attr('transform', 
321               'translate(' + (rightAlignYAxis ? availableWidth : -distY.size() ) + ',0)')
322             .datum(data.filter(function(d) { return !d.disabled }))
323             .call(distY);
324       }
325
326       //------------------------------------------------------------
327
328
329
330
331       if (d3.fisheye) {
332         g.select('.nv-background')
333             .attr('width', availableWidth)
334             .attr('height', availableHeight);
335
336         g.select('.nv-background').on('mousemove', updateFisheye);
337         g.select('.nv-background').on('click', function() { pauseFisheye = !pauseFisheye;});
338         scatter.dispatch.on('elementClick.freezeFisheye', function() {
339           pauseFisheye = !pauseFisheye;
340         });
341       }
342
343
344       function updateFisheye() {
345         if (pauseFisheye) {
346           g.select('.nv-point-paths').style('pointer-events', 'all');
347           return false;
348         }
349
350         g.select('.nv-point-paths').style('pointer-events', 'none' );
351
352         var mouse = d3.mouse(this);
353         x.distortion(fisheye).focus(mouse[0]);
354         y.distortion(fisheye).focus(mouse[1]);
355
356         g.select('.nv-scatterWrap')
357             .call(scatter);
358
359         if (showXAxis)
360           g.select('.nv-x.nv-axis').call(xAxis);
361         
362         if (showYAxis)
363           g.select('.nv-y.nv-axis').call(yAxis);
364         
365         g.select('.nv-distributionX')
366             .datum(data.filter(function(d) { return !d.disabled }))
367             .call(distX);
368         g.select('.nv-distributionY')
369             .datum(data.filter(function(d) { return !d.disabled }))
370             .call(distY);
371       }
372
373
374
375       //============================================================
376       // Event Handling/Dispatching (in chart's scope)
377       //------------------------------------------------------------
378
379       controls.dispatch.on('legendClick', function(d,i) {
380         d.disabled = !d.disabled;
381
382         fisheye = d.disabled ? 0 : 2.5;
383         g.select('.nv-background') .style('pointer-events', d.disabled ? 'none' : 'all');
384         g.select('.nv-point-paths').style('pointer-events', d.disabled ? 'all' : 'none' );
385
386         if (d.disabled) {
387           x.distortion(fisheye).focus(0);
388           y.distortion(fisheye).focus(0);
389
390           g.select('.nv-scatterWrap').call(scatter);
391           g.select('.nv-x.nv-axis').call(xAxis);
392           g.select('.nv-y.nv-axis').call(yAxis);
393         } else {
394           pauseFisheye = false;
395         }
396
397         chart.update();
398       });
399
400       legend.dispatch.on('stateChange', function(newState) {
401         state.disabled = newState.disabled;
402         dispatch.stateChange(state);
403         chart.update();
404       });
405
406       scatter.dispatch.on('elementMouseover.tooltip', function(e) {
407         d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
408             .attr('y1', function(d,i) { return e.pos[1] - availableHeight;});
409         d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
410             .attr('x2', e.pos[0] + distX.size());
411
412         e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
413         dispatch.tooltipShow(e);
414       });
415
416       dispatch.on('tooltipShow', function(e) {
417         if (tooltips) showTooltip(e, that.parentNode);
418       });
419
420       // Update chart from a state object passed to event handler
421       dispatch.on('changeState', function(e) {
422
423         if (typeof e.disabled !== 'undefined') {
424           data.forEach(function(series,i) {
425             series.disabled = e.disabled[i];
426           });
427
428           state.disabled = e.disabled;
429         }
430
431         chart.update();
432       });
433
434       //============================================================
435
436
437       //store old scales for use in transitions on update
438       x0 = x.copy();
439       y0 = y.copy();
440
441
442     });
443
444     return chart;
445   }
446
447
448   //============================================================
449   // Event Handling/Dispatching (out of chart's scope)
450   //------------------------------------------------------------
451
452   scatter.dispatch.on('elementMouseout.tooltip', function(e) {
453     dispatch.tooltipHide(e);
454
455     d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-distx-' + e.pointIndex)
456         .attr('y1', 0);
457     d3.select('.nv-chart-' + scatter.id() + ' .nv-series-' + e.seriesIndex + ' .nv-disty-' + e.pointIndex)
458         .attr('x2', distY.size());
459   });
460   dispatch.on('tooltipHide', function() {
461     if (tooltips) nv.tooltip.cleanup();
462   });
463
464   //============================================================
465
466
467   //============================================================
468   // Expose Public Variables
469   //------------------------------------------------------------
470
471   // expose chart's sub-components
472   chart.dispatch = dispatch;
473   chart.scatter = scatter;
474   chart.legend = legend;
475   chart.controls = controls;
476   chart.xAxis = xAxis;
477   chart.yAxis = yAxis;
478   chart.distX = distX;
479   chart.distY = distY;
480
481   d3.rebind(chart, scatter, 'id', 'interactive', 'pointActive', 'x', 'y', 'shape', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'sizeRange', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius', 'useVoronoi');
482   chart.options = nv.utils.optionsFunc.bind(chart);
483   
484   chart.margin = function(_) {
485     if (!arguments.length) return margin;
486     margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
487     margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
488     margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
489     margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
490     return chart;
491   };
492
493   chart.width = function(_) {
494     if (!arguments.length) return width;
495     width = _;
496     return chart;
497   };
498
499   chart.height = function(_) {
500     if (!arguments.length) return height;
501     height = _;
502     return chart;
503   };
504
505   chart.color = function(_) {
506     if (!arguments.length) return color;
507     color = nv.utils.getColor(_);
508     legend.color(color);
509     distX.color(color);
510     distY.color(color);
511     return chart;
512   };
513
514   chart.showDistX = function(_) {
515     if (!arguments.length) return showDistX;
516     showDistX = _;
517     return chart;
518   };
519
520   chart.showDistY = function(_) {
521     if (!arguments.length) return showDistY;
522     showDistY = _;
523     return chart;
524   };
525
526   chart.showControls = function(_) {
527     if (!arguments.length) return showControls;
528     showControls = _;
529     return chart;
530   };
531
532   chart.showLegend = function(_) {
533     if (!arguments.length) return showLegend;
534     showLegend = _;
535     return chart;
536   };
537
538   chart.showXAxis = function(_) {
539     if (!arguments.length) return showXAxis;
540     showXAxis = _;
541     return chart;
542   };
543
544   chart.showYAxis = function(_) {
545     if (!arguments.length) return showYAxis;
546     showYAxis = _;
547     return chart;
548   };
549
550   chart.rightAlignYAxis = function(_) {
551     if(!arguments.length) return rightAlignYAxis;
552     rightAlignYAxis = _;
553     yAxis.orient( (_) ? 'right' : 'left');
554     return chart;
555   };
556
557
558   chart.fisheye = function(_) {
559     if (!arguments.length) return fisheye;
560     fisheye = _;
561     return chart;
562   };
563
564   chart.xPadding = function(_) {
565     if (!arguments.length) return xPadding;
566     xPadding = _;
567     return chart;
568   };
569
570   chart.yPadding = function(_) {
571     if (!arguments.length) return yPadding;
572     yPadding = _;
573     return chart;
574   };
575
576   chart.tooltips = function(_) {
577     if (!arguments.length) return tooltips;
578     tooltips = _;
579     return chart;
580   };
581
582   chart.tooltipContent = function(_) {
583     if (!arguments.length) return tooltip;
584     tooltip = _;
585     return chart;
586   };
587
588   chart.tooltipXContent = function(_) {
589     if (!arguments.length) return tooltipX;
590     tooltipX = _;
591     return chart;
592   };
593
594   chart.tooltipYContent = function(_) {
595     if (!arguments.length) return tooltipY;
596     tooltipY = _;
597     return chart;
598   };
599
600   chart.state = function(_) {
601     if (!arguments.length) return state;
602     state = _;
603     return chart;
604   };
605
606   chart.defaultState = function(_) {
607     if (!arguments.length) return defaultState;
608     defaultState = _;
609     return chart;
610   };
611   
612   chart.noData = function(_) {
613     if (!arguments.length) return noData;
614     noData = _;
615     return chart;
616   };
617
618   chart.transitionDuration = function(_) {
619     if (!arguments.length) return transitionDuration;
620     transitionDuration = _;
621     return chart;
622   };
623
624   //============================================================
625
626
627   return chart;
628 }