Initial OpenECOMP policy/engine commit
[policy/engine.git] / ecomp-sdk-app / src / main / webapp / static / fusion / raptor / d3 / js / models / bulletChart.js
1
2 // Chart design based on the recommendations of Stephen Few. Implementation
3 // based on the work of Clint Ivy, Jamie Love, and Jason Davies.
4 // http://projects.instantcognition.com/protovis/bulletchart/
5 nv.models.bulletChart = function() {
6   "use strict";
7   //============================================================
8   // Public Variables with Default Settings
9   //------------------------------------------------------------
10
11   var bullet = nv.models.bullet()
12     ;
13
14   var orient = 'left' // TODO top & bottom
15     , reverse = false
16     , margin = {top: 5, right: 40, bottom: 20, left: 120}
17     , ranges = function(d) { return d.ranges }
18     , markers = function(d) { return d.markers }
19     , measures = function(d) { return d.measures }
20     , width = null
21     , height = 55
22     , tickFormat = null
23     , tooltips = true
24     , tooltip = function(key, x, y, e, graph) {
25         return '<h3>' + x + '</h3>' +
26                '<p>' + y + '</p>'
27       }
28     , noData = 'No Data Available.'
29     , dispatch = d3.dispatch('tooltipShow', 'tooltipHide')
30     ;
31
32   //============================================================
33
34
35   //============================================================
36   // Private Variables
37   //------------------------------------------------------------
38
39   var showTooltip = function(e, offsetElement) {
40     var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ) + margin.left,
41         top = e.pos[1] + ( offsetElement.offsetTop || 0) + margin.top,
42         content = tooltip(e.key, e.label, e.value, e, chart);
43
44     nv.tooltip.show([left, top], content, e.value < 0 ? 'e' : 'w', null, offsetElement);
45   };
46
47   //============================================================
48
49
50   function chart(selection) {
51     selection.each(function(d, i) {
52       var container = d3.select(this);
53
54       var availableWidth = (width  || parseInt(container.style('width')) || 960)
55                              - margin.left - margin.right,
56           availableHeight = height - margin.top - margin.bottom,
57           that = this;
58
59
60       chart.update = function() { chart(selection) };
61       chart.container = this;
62
63       //------------------------------------------------------------
64       // Display No Data message if there's nothing to show.
65
66       if (!d || !ranges.call(this, d, i)) {
67         var noDataText = container.selectAll('.nv-noData').data([noData]);
68
69         noDataText.enter().append('text')
70           .attr('class', 'nvd3 nv-noData')
71           .attr('dy', '-.7em')
72           .style('text-anchor', 'middle');
73
74         noDataText
75           .attr('x', margin.left + availableWidth / 2)
76           .attr('y', 18 + margin.top + availableHeight / 2)
77           .text(function(d) { return d });
78
79         return chart;
80       } else {
81         container.selectAll('.nv-noData').remove();
82       }
83
84       //------------------------------------------------------------
85
86
87
88       var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
89           markerz = markers.call(this, d, i).slice().sort(d3.descending),
90           measurez = measures.call(this, d, i).slice().sort(d3.descending);
91
92
93       //------------------------------------------------------------
94       // Setup containers and skeleton of chart
95
96       var wrap = container.selectAll('g.nv-wrap.nv-bulletChart').data([d]);
97       var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bulletChart');
98       var gEnter = wrapEnter.append('g');
99       var g = wrap.select('g');
100
101       gEnter.append('g').attr('class', 'nv-bulletWrap');
102       gEnter.append('g').attr('class', 'nv-titles');
103
104       wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
105
106       //------------------------------------------------------------
107
108
109       // Compute the new x-scale.
110       var x1 = d3.scale.linear()
111           .domain([0, Math.max(rangez[0], markerz[0], measurez[0])])  // TODO: need to allow forceX and forceY, and xDomain, yDomain
112           .range(reverse ? [availableWidth, 0] : [0, availableWidth]);
113
114       // Retrieve the old x-scale, if this is an update.
115       var x0 = this.__chart__ || d3.scale.linear()
116           .domain([0, Infinity])
117           .range(x1.range());
118
119       // Stash the new scale.
120       this.__chart__ = x1;
121
122       /*
123       // Derive width-scales from the x-scales.
124       var w0 = bulletWidth(x0),
125           w1 = bulletWidth(x1);
126
127       function bulletWidth(x) {
128         var x0 = x(0);
129         return function(d) {
130           return Math.abs(x(d) - x(0));
131         };
132       }
133
134       function bulletTranslate(x) {
135         return function(d) {
136           return 'translate(' + x(d) + ',0)';
137         };
138       }
139       */
140
141       var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
142           w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
143
144
145       var title = gEnter.select('.nv-titles').append('g')
146           .attr('text-anchor', 'end')
147           .attr('transform', 'translate(-6,' + (height - margin.top - margin.bottom) / 2 + ')');
148       title.append('text')
149           .attr('class', 'nv-title')
150           .text(function(d) { return d.title; });
151
152       title.append('text')
153           .attr('class', 'nv-subtitle')
154           .attr('dy', '1em')
155           .text(function(d) { return d.subtitle; });
156
157
158
159       bullet
160         .width(availableWidth)
161         .height(availableHeight)
162
163       var bulletWrap = g.select('.nv-bulletWrap');
164
165       d3.transition(bulletWrap).call(bullet);
166
167
168
169       // Compute the tick format.
170       var format = tickFormat || x1.tickFormat( availableWidth / 100 );
171
172       // Update the tick groups.
173       var tick = g.selectAll('g.nv-tick')
174           .data(x1.ticks( availableWidth / 50 ), function(d) {
175             return this.textContent || format(d);
176           });
177
178       // Initialize the ticks with the old scale, x0.
179       var tickEnter = tick.enter().append('g')
180           .attr('class', 'nv-tick')
181           .attr('transform', function(d) { return 'translate(' + x0(d) + ',0)' })
182           .style('opacity', 1e-6);
183
184       tickEnter.append('line')
185           .attr('y1', availableHeight)
186           .attr('y2', availableHeight * 7 / 6);
187
188       tickEnter.append('text')
189           .attr('text-anchor', 'middle')
190           .attr('dy', '1em')
191           .attr('y', availableHeight * 7 / 6)
192           .text(format);
193
194
195       // Transition the updating ticks to the new scale, x1.
196       var tickUpdate = d3.transition(tick)
197           .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
198           .style('opacity', 1);
199
200       tickUpdate.select('line')
201           .attr('y1', availableHeight)
202           .attr('y2', availableHeight * 7 / 6);
203
204       tickUpdate.select('text')
205           .attr('y', availableHeight * 7 / 6);
206
207       // Transition the exiting ticks to the new scale, x1.
208       d3.transition(tick.exit())
209           .attr('transform', function(d) { return 'translate(' + x1(d) + ',0)' })
210           .style('opacity', 1e-6)
211           .remove();
212
213
214       //============================================================
215       // Event Handling/Dispatching (in chart's scope)
216       //------------------------------------------------------------
217
218       dispatch.on('tooltipShow', function(e) {
219         e.key = d.title;
220         if (tooltips) showTooltip(e, that.parentNode);
221       });
222
223       //============================================================
224
225     });
226
227     d3.timer.flush();
228
229     return chart;
230   }
231
232
233   //============================================================
234   // Event Handling/Dispatching (out of chart's scope)
235   //------------------------------------------------------------
236
237   bullet.dispatch.on('elementMouseover.tooltip', function(e) {
238     dispatch.tooltipShow(e);
239   });
240
241   bullet.dispatch.on('elementMouseout.tooltip', function(e) {
242     dispatch.tooltipHide(e);
243   });
244
245   dispatch.on('tooltipHide', function() {
246     if (tooltips) nv.tooltip.cleanup();
247   });
248
249   //============================================================
250
251
252   //============================================================
253   // Expose Public Variables
254   //------------------------------------------------------------
255
256   chart.dispatch = dispatch;
257   chart.bullet = bullet;
258
259   d3.rebind(chart, bullet, 'color');
260
261   chart.options = nv.utils.optionsFunc.bind(chart);
262   
263   // left, right, top, bottom
264   chart.orient = function(x) {
265     if (!arguments.length) return orient;
266     orient = x;
267     reverse = orient == 'right' || orient == 'bottom';
268     return chart;
269   };
270
271   // ranges (bad, satisfactory, good)
272   chart.ranges = function(x) {
273     if (!arguments.length) return ranges;
274     ranges = x;
275     return chart;
276   };
277
278   // markers (previous, goal)
279   chart.markers = function(x) {
280     if (!arguments.length) return markers;
281     markers = x;
282     return chart;
283   };
284
285   // measures (actual, forecast)
286   chart.measures = function(x) {
287     if (!arguments.length) return measures;
288     measures = x;
289     return chart;
290   };
291
292   chart.width = function(x) {
293     if (!arguments.length) return width;
294     width = x;
295     return chart;
296   };
297
298   chart.height = function(x) {
299     if (!arguments.length) return height;
300     height = x;
301     return chart;
302   };
303
304   chart.margin = function(_) {
305     if (!arguments.length) return margin;
306     margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
307     margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
308     margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
309     margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
310     return chart;
311   };
312
313   chart.tickFormat = function(x) {
314     if (!arguments.length) return tickFormat;
315     tickFormat = x;
316     return chart;
317   };
318
319   chart.tooltips = function(_) {
320     if (!arguments.length) return tooltips;
321     tooltips = _;
322     return chart;
323   };
324
325   chart.tooltipContent = function(_) {
326     if (!arguments.length) return tooltip;
327     tooltip = _;
328     return chart;
329   };
330
331   chart.noData = function(_) {
332     if (!arguments.length) return noData;
333     noData = _;
334     return chart;
335   };
336
337   //============================================================
338
339
340   return chart;
341 };
342
343