Initial OpenECOMP policy/engine commit
[policy/engine.git] / ecomp-sdk-app / src / main / webapp / app / fusion / external / d3 / js / models / axis.js
1 nv.models.axis = function() {
2   "use strict";
3   //============================================================
4   // Public Variables with Default Settings
5   //------------------------------------------------------------
6
7   var axis = d3.svg.axis()
8     ;
9
10   var margin = {top: 0, right: 0, bottom: 0, left: 0}
11     , width = 75 //only used for tickLabel currently
12     , height = 60 //only used for tickLabel currently
13     , scale = d3.scale.linear()
14     , axisLabelText = null
15     , showMaxMin = true //TODO: showMaxMin should be disabled on all ordinal scaled axes
16     , highlightZero = true
17     , rotateLabels = 0
18     , rotateYLabel = true
19     , staggerLabels = false
20     , isOrdinal = false
21     , ticks = null
22         , logScale = false
23     , axisLabelDistance = 12 //The larger this number is, the closer the axis label is to the axis.
24     ;
25
26   axis
27     .scale(scale)
28     .orient('bottom')
29     .tickFormat(function(d) { return d })
30     ;
31
32   //============================================================
33
34
35   //============================================================
36   // Private Variables
37   //------------------------------------------------------------
38
39   var scale0;
40
41   //============================================================
42
43
44   function chart(selection) {
45     selection.each(function(data) {
46       var container = d3.select(this);
47
48
49       //------------------------------------------------------------
50       // Setup containers and skeleton of chart
51
52       var wrap = container.selectAll('g.nv-wrap.nv-axis').data([data]);
53       var wrapEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-axis');
54       var gEnter = wrapEnter.append('g');
55       var g = wrap.select('g')
56
57       //------------------------------------------------------------
58
59
60       if (ticks !== null)
61         axis.ticks(ticks);
62       else if (axis.orient() == 'top' || axis.orient() == 'bottom')
63         axis.ticks(Math.abs(scale.range()[1] - scale.range()[0]) / 100);
64
65
66       //TODO: consider calculating width/height based on whether or not label is added, for reference in charts using this component
67
68
69       g.transition().call(axis);
70
71       scale0 = scale0 || axis.scale();
72
73       var fmt = axis.tickFormat();
74       if (fmt == null) {
75         fmt = scale0.tickFormat();
76       }
77
78       var axisLabel = g.selectAll('text.nv-axislabel')
79           .data([axisLabelText || null]);
80       axisLabel.exit().remove();
81       switch (axis.orient()) {
82         case 'top':
83           axisLabel.enter().append('text').attr('class', 'nv-axislabel');
84           var w = (scale.range().length==2) ? scale.range()[1] : (scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]));
85           axisLabel
86               .attr('text-anchor', 'middle')
87               .attr('y', 0)
88               .attr('x', w/2);
89           if (showMaxMin) {
90             var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
91                            .data(scale.domain());
92             axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text');
93             axisMaxMin.exit().remove();
94             axisMaxMin
95                 .attr('transform', function(d,i) {
96                   return 'translate(' + scale(d) + ',0)'
97                 })
98               .select('text')
99                 .attr('dy', '0em')
100                 .attr('y', -axis.tickPadding())
101                 .attr('text-anchor', 'middle')
102                 .text(function(d,i) {
103                   //var v = fmt(d);
104                                   var v = d;
105                                   if(logScale) {
106                                         v = Math.pow(10,v);
107                                         v = fmt(v);
108                                         //fmt = d3.format(',.2f');
109                                         //v = fmt(v);
110                                   } else
111                                   v = fmt(v);
112                   return ('' + v).match('NaN') ? '' : v;
113                 });
114             axisMaxMin.transition()
115                 .attr('transform', function(d,i) {
116                   return 'translate(' + scale.range()[i] + ',0)'
117                 });
118           }
119           break;
120         case 'bottom':
121           var xLabelMargin = 36;
122           var maxTextWidth = 30;
123           var xTicks = g.selectAll('g').select("text");
124           if (rotateLabels%360) {
125             //Calculate the longest xTick width
126             xTicks.each(function(d,i){
127               var width = this.getBBox().width;
128               if(width > maxTextWidth) maxTextWidth = width;
129             });
130             //Convert to radians before calculating sin. Add 30 to margin for healthy padding.
131             var sin = Math.abs(Math.sin(rotateLabels*Math.PI/180));
132             var xLabelMargin = (sin ? sin*maxTextWidth : maxTextWidth)+30;
133             //Rotate all xTicks
134             xTicks
135               .attr('transform', function(d,i,j) { return 'rotate(' + rotateLabels + ' 0,0)' })
136               .style('text-anchor', rotateLabels%360 > 0 ? 'start' : 'end');
137           }
138           axisLabel.enter().append('text').attr('class', 'nv-axislabel');
139           var w = (scale.range().length==2) ? scale.range()[1] : (scale.range()[scale.range().length-1]+(scale.range()[1]-scale.range()[0]));
140           axisLabel
141               .attr('text-anchor', 'middle')
142               .attr('y', xLabelMargin)
143               .attr('x', w/2);
144           if (showMaxMin) {
145           //if (showMaxMin && !isOrdinal) {
146             var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
147                            //.data(scale.domain())
148                            .data([scale.domain()[0], scale.domain()[scale.domain().length - 1]]);
149             axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text');
150             axisMaxMin.exit().remove();
151             axisMaxMin
152                 .attr('transform', function(d,i) {
153                   return 'translate(' + (scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0)) + ',0)'
154                 })
155               .select('text')
156                 .attr('dy', '.71em')
157                 .attr('y', axis.tickPadding())
158                 .attr('transform', function(d,i,j) { return 'rotate(' + rotateLabels + ' 0,0)' })
159                 .style('text-anchor', rotateLabels ? (rotateLabels%360 > 0 ? 'start' : 'end') : 'middle')
160                 .text(function(d,i) {
161                   //var v = fmt(d);
162                                   var v = d;
163                                   if(logScale) {
164                                         v = Math.pow(10,v);
165                                         v = fmt(v);
166                                         //fmt = d3.format(',.2f');
167                                         //v = fmt(v);
168                                   } else
169                                   v = fmt(v);
170                   return ('' + v).match('NaN') ? '' : v;
171                 });
172             axisMaxMin.transition()
173                 .attr('transform', function(d,i) {
174                   //return 'translate(' + scale.range()[i] + ',0)'
175                   //return 'translate(' + scale(d) + ',0)'
176                   return 'translate(' + (scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0)) + ',0)'
177                 });
178           }
179           if (staggerLabels)
180             xTicks
181                 .attr('transform', function(d,i) { return 'translate(0,' + (i % 2 == 0 ? '0' : '12') + ')' });
182
183           break;
184         case 'right':
185           axisLabel.enter().append('text').attr('class', 'nv-axislabel');
186           axisLabel
187               .style('text-anchor', rotateYLabel ? 'middle' : 'begin')
188               .attr('transform', rotateYLabel ? 'rotate(90)' : '')
189               .attr('y', rotateYLabel ? (-Math.max(margin.right,width) + 12) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
190               .attr('x', rotateYLabel ? (scale.range()[0] / 2) : axis.tickPadding());
191           if (showMaxMin) {
192             var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
193                            .data(scale.domain());
194             axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text')
195                 .style('opacity', 0);
196             axisMaxMin.exit().remove();
197             axisMaxMin
198                 .attr('transform', function(d,i) {
199                   return 'translate(0,' + scale(d) + ')'
200                 })
201               .select('text')
202                 .attr('dy', '.32em')
203                 .attr('y', 0)
204                 .attr('x', axis.tickPadding())
205                 .style('text-anchor', 'start')
206                 .text(function(d,i) {
207                   //var v = fmt(d);
208                                   var v = d;
209                                   if(logScale) {
210                                         v = Math.pow(10,v);
211                                         v = fmt(v);
212                                         //fmt = d3.format(',.2f');
213                                         //v = fmt(v);
214                                   } else
215                                     v = fmt(v);
216                   return ('' + v).match('NaN') ? '' : v;
217                 });
218             axisMaxMin.transition()
219                 .attr('transform', function(d,i) {
220                   return 'translate(0,' + scale.range()[i] + ')'
221                 })
222               .select('text')
223                 .style('opacity', 1);
224           }
225           break;
226         case 'left':
227           /*
228           //For dynamically placing the label. Can be used with dynamically-sized chart axis margins
229           var yTicks = g.selectAll('g').select("text");
230           yTicks.each(function(d,i){
231             var labelPadding = this.getBBox().width + axis.tickPadding() + 16;
232             if(labelPadding > width) width = labelPadding;
233           });
234           */
235           axisLabel.enter().append('text').attr('class', 'nv-axislabel');
236           axisLabel
237               .style('text-anchor', rotateYLabel ? 'middle' : 'end')
238               .attr('transform', rotateYLabel ? 'rotate(-90)' : '')
239               .attr('y', rotateYLabel ? (-Math.max(margin.left,width) + axisLabelDistance) : -10) //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
240               .attr('x', rotateYLabel ? (-scale.range()[0] / 2) : -axis.tickPadding());
241           if (showMaxMin) {
242             var axisMaxMin = wrap.selectAll('g.nv-axisMaxMin')
243                            .data(scale.domain());
244             axisMaxMin.enter().append('g').attr('class', 'nv-axisMaxMin').append('text')
245                 .style('opacity', 0);
246             axisMaxMin.exit().remove();
247             axisMaxMin
248                 .attr('transform', function(d,i) {
249                   return 'translate(0,' + scale0(d) + ')'
250                 })
251               .select('text')
252                 .attr('dy', '.32em')
253                 .attr('y', 0)
254                 .attr('x', -axis.tickPadding())
255                 .attr('text-anchor', 'end')
256                 .text(function(d,i) {
257                   //var v = fmt(d);
258                                   var v = d;
259                                   if(logScale) {
260                                         v = Math.pow(10,v);
261                                         v = fmt(v);
262                                         //fmt = d3.format(',.2f');
263                                         //v = fmt(v);
264                                   } else
265                                         v = fmt(v);
266                   return ('' + v).match('NaN') ? '' : v;
267                 });
268             axisMaxMin.transition()
269                 .attr('transform', function(d,i) {
270                   return 'translate(0,' + scale.range()[i] + ')'
271                 })
272               .select('text')
273                 .style('opacity', 1);
274           }
275           break;
276       }
277       axisLabel
278           .text(function(d) { return d });
279
280
281       if (showMaxMin && (axis.orient() === 'left' || axis.orient() === 'right')) {
282         //check if max and min overlap other values, if so, hide the values that overlap
283         g.selectAll('g') // the g's wrapping each tick
284             .each(function(d,i) {
285               d3.select(this).select('text').attr('opacity', 1);
286                           var v; 
287                           if(logScale) {
288                                 v = Math.pow(10,d);
289                                 v = fmt(v);
290                                 //fmt = d3.format(',.2f');
291                                 //v = fmt(v);
292                           } else {
293                             v = fmt(d);
294                           }
295
296                           //d3.select(this).select('text').text(fmt(Math.pow(10,d)));
297                           d3.select(this).select('text').text(v);
298               if (scale(d) < scale.range()[1] + 10 || scale(d) > scale.range()[0] - 10) { // 10 is assuming text height is 16... if d is 0, leave it!
299                 if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
300                   d3.select(this).attr('opacity', 0);
301                 
302                 d3.select(this).select('text').attr('opacity', 0); // Don't remove the ZERO line!!
303               }
304             });
305
306         //if Max and Min = 0 only show min, Issue #281
307         if (scale.domain()[0] == scale.domain()[1] && scale.domain()[0] == 0)
308           wrap.selectAll('g.nv-axisMaxMin')
309             .style('opacity', function(d,i) { return !i ? 1 : 0 });
310
311       }
312
313       if (showMaxMin && (axis.orient() === 'top' || axis.orient() === 'bottom')) {
314         var maxMinRange = [];
315         wrap.selectAll('g.nv-axisMaxMin')
316             .each(function(d,i) {
317               try {
318                   if (i) // i== 1, max position
319                       maxMinRange.push(scale(d) - this.getBBox().width - 4)  //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
320                   else // i==0, min position
321                       maxMinRange.push(scale(d) + this.getBBox().width + 4)
322               }catch (err) {
323                   if (i) // i== 1, max position
324                       maxMinRange.push(scale(d) - 4)  //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
325                   else // i==0, min position
326                       maxMinRange.push(scale(d) + 4)
327               }
328             });
329         g.selectAll('g') // the g's wrapping each tick
330             .each(function(d,i) {
331                           var v; 
332                           if(logScale) {
333                                 v = Math.pow(10,d);
334                                 //v = fmt(v);
335                                 //fmt = d3.format(',.2f');
336                                 v = fmt(v);
337                           } else {
338                             v = fmt(d);
339                           }
340                           //alert(v);
341
342                           //d3.select(this).select('text').text(fmt(Math.pow(10,d)));
343                           d3.select(this).select('text').text(v);
344                                                 
345               if (scale(d) < maxMinRange[0] || scale(d) > maxMinRange[1]) {
346                 if (d > 1e-10 || d < -1e-10) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
347                   d3.select(this).remove();
348                 else
349                   d3.select(this).select('text').remove(); // Don't remove the ZERO line!!
350               }
351             });
352       }
353
354
355       //highlight zero line ... Maybe should not be an option and should just be in CSS?
356       if (highlightZero)
357         g.selectAll('.tick')
358           .filter(function(d) { return !parseFloat(Math.round(d.__data__*100000)/1000000) && (d.__data__ !== undefined) }) //this is because sometimes the 0 tick is a very small fraction, TODO: think of cleaner technique
359             .classed('zero', true);
360
361       //store old scales for use in transitions on update
362       scale0 = scale.copy();
363
364     });
365
366     return chart;
367   }
368
369
370   //============================================================
371   // Expose Public Variables
372   //------------------------------------------------------------
373
374   // expose chart's sub-components
375   chart.axis = axis;
376
377   d3.rebind(chart, axis, 'orient', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat');
378   d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands'); //these are also accessible by chart.scale(), but added common ones directly for ease of use
379
380   chart.options = nv.utils.optionsFunc.bind(chart);
381   
382   chart.margin = function(_) {
383     if(!arguments.length) return margin;
384     margin.top    = typeof _.top    != 'undefined' ? _.top    : margin.top;
385     margin.right  = typeof _.right  != 'undefined' ? _.right  : margin.right;
386     margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
387     margin.left   = typeof _.left   != 'undefined' ? _.left   : margin.left;
388     return chart;
389   }
390
391   chart.width = function(_) {
392     if (!arguments.length) return width;
393     width = _;
394     return chart;
395   };
396
397   chart.ticks = function(_) {
398     if (!arguments.length) return ticks;
399     ticks = _;
400     return chart;
401   };
402
403   chart.height = function(_) {
404     if (!arguments.length) return height;
405     height = _;
406     return chart;
407   };
408
409   chart.axisLabel = function(_) {
410     if (!arguments.length) return axisLabelText;
411     axisLabelText = _;
412     return chart;
413   }
414
415   chart.showMaxMin = function(_) {
416     if (!arguments.length) return showMaxMin;
417     showMaxMin = _;
418     return chart;
419   }
420
421   chart.logScale = function(_) {
422     if (!arguments.length) return logScale;
423     logScale = _;
424     return chart;
425   }
426   
427   chart.highlightZero = function(_) {
428     if (!arguments.length) return highlightZero;
429     highlightZero = _;
430     return chart;
431   }
432
433   chart.scale = function(_) {
434     if (!arguments.length) return scale;
435     scale = _;
436     axis.scale(scale);
437     isOrdinal = typeof scale.rangeBands === 'function';
438     d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands');
439     return chart;
440   }
441
442   chart.rotateYLabel = function(_) {
443     if(!arguments.length) return rotateYLabel;
444     rotateYLabel = _;
445     return chart;
446   }
447
448   chart.rotateLabels = function(_) {
449     if(!arguments.length) return rotateLabels;
450     rotateLabels = _;
451     return chart;
452   }
453
454   chart.staggerLabels = function(_) {
455     if (!arguments.length) return staggerLabels;
456     staggerLabels = _;
457     return chart;
458   };
459
460   chart.axisLabelDistance = function(_) {
461     if (!arguments.length) return axisLabelDistance;
462     axisLabelDistance = _;
463     return chart;
464   };
465
466   //============================================================
467
468
469   return chart;
470 }