Initial OpenECOMP policy/engine commit
[policy/engine.git] / ecomp-sdk-app / src / main / webapp / static / fusion / raptor / d3 / js / models / bullet.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
6 nv.models.bullet = function() {
7   "use strict";
8   //============================================================
9   // Public Variables with Default Settings
10   //------------------------------------------------------------
11
12   var margin = {top: 0, right: 0, bottom: 0, left: 0}
13     , orient = 'left' // TODO top & bottom
14     , reverse = false
15     , ranges = function(d) { return d.ranges }
16     , markers = function(d) { return d.markers }
17     , measures = function(d) { return d.measures }
18     , rangeLabels = function(d) { return d.rangeLabels ? d.rangeLabels : [] }
19     , markerLabels = function(d) { return d.markerLabels ? d.markerLabels : []  }
20     , measureLabels = function(d) { return d.measureLabels ? d.measureLabels : []  }
21     , forceX = [0] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
22     , width = 380
23     , height = 30
24     , tickFormat = null
25     , color = nv.utils.getColor(['#1f77b4'])
26     , dispatch = d3.dispatch('elementMouseover', 'elementMouseout')
27     ;
28
29   //============================================================
30
31
32   function chart(selection) {
33     selection.each(function(d, i) {
34       var availableWidth = width - margin.left - margin.right,
35           availableHeight = height - margin.top - margin.bottom,
36           container = d3.select(this);
37
38       var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
39           markerz = markers.call(this, d, i).slice().sort(d3.descending),
40           measurez = measures.call(this, d, i).slice().sort(d3.descending),
41           rangeLabelz = rangeLabels.call(this, d, i).slice(),
42           markerLabelz = markerLabels.call(this, d, i).slice(),
43           measureLabelz = measureLabels.call(this, d, i).slice();
44
45
46       //------------------------------------------------------------
47       // Setup Scales
48
49       // Compute the new x-scale.
50       var x1 = d3.scale.linear()
51           .domain( d3.extent(d3.merge([forceX, rangez])) )
52           .range(reverse ? [availableWidth, 0] : [0, availableWidth]);
53
54       // Retrieve the old x-scale, if this is an update.
55       var x0 = this.__chart__ || d3.scale.linear()
56           .domain([0, Infinity])
57           .range(x1.range());
58
59       // Stash the new scale.
60       this.__chart__ = x1;
61
62
63       var rangeMin = d3.min(rangez), //rangez[2]
64           rangeMax = d3.max(rangez), //rangez[0]
65           rangeAvg = rangez[1];
66
67       //------------------------------------------------------------
68
69
70       //------------------------------------------------------------
71       // Setup containers and skeleton of chart
72
73       var wrap = container.selectAll('g.nv-wrap.nv-bullet').data([d]);
74       var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-bullet');
75       var gEnter = wrapEnter.append('g');
76       var g = wrap.select('g');
77
78       gEnter.append('rect').attr('class', 'nv-range nv-rangeMax');
79       gEnter.append('rect').attr('class', 'nv-range nv-rangeAvg');
80       gEnter.append('rect').attr('class', 'nv-range nv-rangeMin');
81       gEnter.append('rect').attr('class', 'nv-measure');
82       gEnter.append('path').attr('class', 'nv-markerTriangle');
83
84       wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
85
86       //------------------------------------------------------------
87
88
89
90       var w0 = function(d) { return Math.abs(x0(d) - x0(0)) }, // TODO: could optimize by precalculating x0(0) and x1(0)
91           w1 = function(d) { return Math.abs(x1(d) - x1(0)) };
92       var xp0 = function(d) { return d < 0 ? x0(d) : x0(0) },
93           xp1 = function(d) { return d < 0 ? x1(d) : x1(0) };
94
95
96       g.select('rect.nv-rangeMax')
97           .attr('height', availableHeight)
98           .attr('width', w1(rangeMax > 0 ? rangeMax : rangeMin))
99           .attr('x', xp1(rangeMax > 0 ? rangeMax : rangeMin))
100           .datum(rangeMax > 0 ? rangeMax : rangeMin)
101           /*
102           .attr('x', rangeMin < 0 ?
103                          rangeMax > 0 ?
104                              x1(rangeMin)
105                            : x1(rangeMax)
106                        : x1(0))
107                       */
108
109       g.select('rect.nv-rangeAvg')
110           .attr('height', availableHeight)
111           .attr('width', w1(rangeAvg))
112           .attr('x', xp1(rangeAvg))
113           .datum(rangeAvg)
114           /*
115           .attr('width', rangeMax <= 0 ?
116                              x1(rangeMax) - x1(rangeAvg)
117                            : x1(rangeAvg) - x1(rangeMin))
118           .attr('x', rangeMax <= 0 ?
119                          x1(rangeAvg)
120                        : x1(rangeMin))
121                       */
122
123       g.select('rect.nv-rangeMin')
124           .attr('height', availableHeight)
125           .attr('width', w1(rangeMax))
126           .attr('x', xp1(rangeMax))
127           .attr('width', w1(rangeMax > 0 ? rangeMin : rangeMax))
128           .attr('x', xp1(rangeMax > 0 ? rangeMin : rangeMax))
129           .datum(rangeMax > 0 ? rangeMin : rangeMax)
130           /*
131           .attr('width', rangeMax <= 0 ?
132                              x1(rangeAvg) - x1(rangeMin)
133                            : x1(rangeMax) - x1(rangeAvg))
134           .attr('x', rangeMax <= 0 ?
135                          x1(rangeMin)
136                        : x1(rangeAvg))
137                       */
138
139       g.select('rect.nv-measure')
140           .style('fill', color)
141           .attr('height', availableHeight / 3)
142           .attr('y', availableHeight / 3)
143           .attr('width', measurez < 0 ?
144                              x1(0) - x1(measurez[0])
145                            : x1(measurez[0]) - x1(0))
146           .attr('x', xp1(measurez))
147           .on('mouseover', function() {
148               dispatch.elementMouseover({
149                 value: measurez[0],
150                 label: measureLabelz[0] || 'Current',
151                 pos: [x1(measurez[0]), availableHeight/2]
152               })
153           })
154           .on('mouseout', function() {
155               dispatch.elementMouseout({
156                 value: measurez[0],
157                 label: measureLabelz[0] || 'Current'
158               })
159           })
160
161       var h3 =  availableHeight / 6;
162       if (markerz[0]) {
163         g.selectAll('path.nv-markerTriangle')
164             .attr('transform', function(d) { return 'translate(' + x1(markerz[0]) + ',' + (availableHeight / 2) + ')' })
165             .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z')
166             .on('mouseover', function() {
167               dispatch.elementMouseover({
168                 value: markerz[0],
169                 label: markerLabelz[0] || 'Previous',
170                 pos: [x1(markerz[0]), availableHeight/2]
171               })
172             })
173             .on('mouseout', function() {
174               dispatch.elementMouseout({
175                 value: markerz[0],
176                 label: markerLabelz[0] || 'Previous'
177               })
178             });
179       } else {
180         g.selectAll('path.nv-markerTriangle').remove();
181       }
182
183
184       wrap.selectAll('.nv-range')
185           .on('mouseover', function(d,i) {
186             var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
187
188             dispatch.elementMouseover({
189               value: d,
190               label: label,
191               pos: [x1(d), availableHeight/2]
192             })
193           })
194           .on('mouseout', function(d,i) {
195             var label = rangeLabelz[i] || (!i ? "Maximum" : i == 1 ? "Mean" : "Minimum");
196
197             dispatch.elementMouseout({
198               value: d,
199               label: label
200             })
201           })
202
203 /* // THIS IS THE PREVIOUS BULLET IMPLEMENTATION, WILL REMOVE SHORTLY
204       // Update the range rects.
205       var range = g.selectAll('rect.nv-range')
206           .data(rangez);
207
208       range.enter().append('rect')
209           .attr('class', function(d, i) { return 'nv-range nv-s' + i; })
210           .attr('width', w0)
211           .attr('height', availableHeight)
212           .attr('x', reverse ? x0 : 0)
213           .on('mouseover', function(d,i) { 
214               dispatch.elementMouseover({
215                 value: d,
216                 label: (i <= 0) ? 'Maximum' : (i > 1) ? 'Minimum' : 'Mean', //TODO: make these labels a variable
217                 pos: [x1(d), availableHeight/2]
218               })
219           })
220           .on('mouseout', function(d,i) { 
221               dispatch.elementMouseout({
222                 value: d,
223                 label: (i <= 0) ? 'Minimum' : (i >=1) ? 'Maximum' : 'Mean' //TODO: make these labels a variable
224               })
225           })
226
227       d3.transition(range)
228           .attr('x', reverse ? x1 : 0)
229           .attr('width', w1)
230           .attr('height', availableHeight);
231
232
233       // Update the measure rects.
234       var measure = g.selectAll('rect.nv-measure')
235           .data(measurez);
236
237       measure.enter().append('rect')
238           .attr('class', function(d, i) { return 'nv-measure nv-s' + i; })
239           .style('fill', function(d,i) { return color(d,i ) })
240           .attr('width', w0)
241           .attr('height', availableHeight / 3)
242           .attr('x', reverse ? x0 : 0)
243           .attr('y', availableHeight / 3)
244           .on('mouseover', function(d) { 
245               dispatch.elementMouseover({
246                 value: d,
247                 label: 'Current', //TODO: make these labels a variable
248                 pos: [x1(d), availableHeight/2]
249               })
250           })
251           .on('mouseout', function(d) { 
252               dispatch.elementMouseout({
253                 value: d,
254                 label: 'Current' //TODO: make these labels a variable
255               })
256           })
257
258       d3.transition(measure)
259           .attr('width', w1)
260           .attr('height', availableHeight / 3)
261           .attr('x', reverse ? x1 : 0)
262           .attr('y', availableHeight / 3);
263
264
265
266       // Update the marker lines.
267       var marker = g.selectAll('path.nv-markerTriangle')
268           .data(markerz);
269
270       var h3 =  availableHeight / 6;
271       marker.enter().append('path')
272           .attr('class', 'nv-markerTriangle')
273           .attr('transform', function(d) { return 'translate(' + x0(d) + ',' + (availableHeight / 2) + ')' })
274           .attr('d', 'M0,' + h3 + 'L' + h3 + ',' + (-h3) + ' ' + (-h3) + ',' + (-h3) + 'Z')
275           .on('mouseover', function(d,i) {
276               dispatch.elementMouseover({
277                 value: d,
278                 label: 'Previous',
279                 pos: [x1(d), availableHeight/2]
280               })
281           })
282           .on('mouseout', function(d,i) {
283               dispatch.elementMouseout({
284                 value: d,
285                 label: 'Previous'
286               })
287           });
288
289       d3.transition(marker)
290           .attr('transform', function(d) { return 'translate(' + (x1(d) - x1(0)) + ',' + (availableHeight / 2) + ')' });
291
292       marker.exit().remove();
293 */
294
295     });
296
297     // d3.timer.flush();  // Not needed?
298
299     return chart;
300   }
301
302
303   //============================================================
304   // Expose Public Variables
305   //------------------------------------------------------------
306
307   chart.dispatch = dispatch;
308
309   chart.options = nv.utils.optionsFunc.bind(chart);
310   
311   // left, right, top, bottom
312   chart.orient = function(_) {
313     if (!arguments.length) return orient;
314     orient = _;
315     reverse = orient == 'right' || orient == 'bottom';
316     return chart;
317   };
318
319   // ranges (bad, satisfactory, good)
320   chart.ranges = function(_) {
321     if (!arguments.length) return ranges;
322     ranges = _;
323     return chart;
324   };
325
326   // markers (previous, goal)
327   chart.markers = function(_) {
328     if (!arguments.length) return markers;
329     markers = _;
330     return chart;
331   };
332
333   // measures (actual, forecast)
334   chart.measures = function(_) {
335     if (!arguments.length) return measures;
336     measures = _;
337     return chart;
338   };
339
340   chart.forceX = function(_) {
341     if (!arguments.length) return forceX;
342     forceX = _;
343     return chart;
344   };
345
346   chart.width = function(_) {
347     if (!arguments.length) return width;
348     width = _;
349     return chart;
350   };
351
352   chart.height = function(_) {
353     if (!arguments.length) return height;
354     height = _;
355     return chart;
356   };
357
358   chart.margin = function(_) {
359     if (!arguments.length) return margin;
360     margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
361     margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
362     margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
363     margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
364     return chart;
365   };
366
367   chart.tickFormat = function(_) {
368     if (!arguments.length) return tickFormat;
369     tickFormat = _;
370     return chart;
371   };
372
373   chart.color = function(_) {
374     if (!arguments.length) return color;
375     color = nv.utils.getColor(_);
376     return chart;
377   };
378
379   //============================================================
380
381
382   return chart;
383 };
384
385