Initial OpenECOMP policy/engine commit
[policy/engine.git] / ecomp-sdk-app / src / main / webapp / static / fusion / raptor / dy3 / js / plugins / axes.js
1 /**
2  * @license
3  * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
4  * MIT-licensed (http://opensource.org/licenses/MIT)
5  */
6
7 /*global Dygraph:false */
8
9 Dygraph.Plugins.Axes = (function() {
10
11 "use strict";
12
13 /*
14 Bits of jankiness:
15 - Direct layout access
16 - Direct area access
17 - Should include calculation of ticks, not just the drawing.
18
19 Options left to make axis-friendly.
20   ('axisTickSize')
21   ('drawAxesAtZero')
22   ('xAxisHeight')
23
24 These too. What is the difference between axisLablelWidth and {x,y}AxisLabelWidth?
25   ('axisLabelWidth')
26   ('xAxisLabelWidth')
27   ('yAxisLabelWidth')
28 */
29
30 /**
31  * Draws the axes. This includes the labels on the x- and y-axes, as well
32  * as the tick marks on the axes.
33  * It does _not_ draw the grid lines which span the entire chart.
34  */
35 var axes = function() {
36   this.xlabels_ = [];
37   this.ylabels_ = [];
38 };
39
40 axes.prototype.toString = function() {
41   return "Axes Plugin";
42 };
43
44 axes.prototype.activate = function(g) {
45   return {
46     layout: this.layout,
47     clearChart: this.clearChart,
48     willDrawChart: this.willDrawChart
49   };
50 };
51
52 axes.prototype.layout = function(e) {
53   var g = e.dygraph;
54
55   if (g.getOption('drawYAxis')) {
56     var w = g.getOption('yAxisLabelWidth') + 2 * g.getOption('axisTickSize');
57     e.reserveSpaceLeft(w);
58   }
59
60   if (g.getOption('drawXAxis')) {
61     var h;
62     // NOTE: I think this is probably broken now, since g.getOption() now
63     // hits the dictionary. (That is, g.getOption('xAxisHeight') now always
64     // has a value.)
65     if (g.getOption('xAxisHeight')) {
66       h = g.getOption('xAxisHeight');
67     } else {
68       h = g.getOptionForAxis('axisLabelFontSize', 'x') + 2 * g.getOption('axisTickSize');
69     }
70     e.reserveSpaceBottom(h);
71   }
72
73   if (g.numAxes() == 2) {
74     // TODO(danvk): introduce a 'drawAxis' per-axis property.
75     if (g.getOption('drawYAxis')) {
76       // TODO(danvk): per-axis setting.
77       var w = g.getOption('yAxisLabelWidth') + 2 * g.getOption('axisTickSize');
78       e.reserveSpaceRight(w);
79     }
80   } else if (g.numAxes() > 2) {
81     g.error("Only two y-axes are supported at this time. (Trying " +
82             "to use " + g.numAxes() + ")");
83   }
84 };
85
86 axes.prototype.detachLabels = function() {
87   function removeArray(ary) {
88     for (var i = 0; i < ary.length; i++) {
89       var el = ary[i];
90       if (el.parentNode) el.parentNode.removeChild(el);
91     }
92   }
93
94   removeArray(this.xlabels_);
95   removeArray(this.ylabels_);
96   this.xlabels_ = [];
97   this.ylabels_ = [];
98 };
99
100 axes.prototype.clearChart = function(e) {
101   this.detachLabels();
102 };
103
104 axes.prototype.willDrawChart = function(e) {
105   var g = e.dygraph;
106   if (!g.getOption('drawXAxis') && !g.getOption('drawYAxis')) return;
107   
108   // Round pixels to half-integer boundaries for crisper drawing.
109   function halfUp(x)  { return Math.round(x) + 0.5; }
110   function halfDown(y){ return Math.round(y) - 0.5; }
111
112   var context = e.drawingContext;
113   var containerDiv = e.canvas.parentNode;
114   var canvasWidth = e.canvas.width;
115   var canvasHeight = e.canvas.height;
116
117   var label, x, y, tick, i;
118
119   var makeLabelStyle = function(axis) {
120     return {
121       position: "absolute",
122       fontSize: g.getOptionForAxis('axisLabelFontSize', axis) + "px",
123       zIndex: 10,
124       color: g.getOptionForAxis('axisLabelColor', axis),
125       width: g.getOption('axisLabelWidth') + "px",
126       // height: g.getOptionForAxis('axisLabelFontSize', 'x') + 2 + "px",
127       lineHeight: "normal",  // Something other than "normal" line-height screws up label positioning.
128       overflow: "hidden"
129     };
130   };
131
132   var labelStyles = {
133     x : makeLabelStyle('x'),
134     y : makeLabelStyle('y'),
135     y2 : makeLabelStyle('y2')
136   };
137
138   var makeDiv = function(txt, axis, prec_axis) {
139     /*
140      * This seems to be called with the following three sets of axis/prec_axis:
141      * x: undefined
142      * y: y1
143      * y: y2
144      */
145     var div = document.createElement("div");
146     var labelStyle = labelStyles[prec_axis == 'y2' ? 'y2' : axis];
147     for (var name in labelStyle) {
148       if (labelStyle.hasOwnProperty(name)) {
149         div.style[name] = labelStyle[name];
150       }
151     }
152     var inner_div = document.createElement("div");
153     inner_div.className = 'dygraph-axis-label' +
154                           ' dygraph-axis-label-' + axis +
155                           (prec_axis ? ' dygraph-axis-label-' + prec_axis : '');
156     inner_div.innerHTML = txt;
157     div.appendChild(inner_div);
158     return div;
159   };
160
161   // axis lines
162   context.save();
163
164   var layout = g.layout_;
165   var area = e.dygraph.plotter_.area;
166
167   if (g.getOption('drawYAxis')) {
168     if (layout.yticks && layout.yticks.length > 0) {
169       var num_axes = g.numAxes();
170       for (i = 0; i < layout.yticks.length; i++) {
171         tick = layout.yticks[i];
172         if (typeof(tick) == "function") return;
173         x = area.x;
174         var sgn = 1;
175         var prec_axis = 'y1';
176         if (tick[0] == 1) {  // right-side y-axis
177           x = area.x + area.w;
178           sgn = -1;
179           prec_axis = 'y2';
180         }
181         var fontSize = g.getOptionForAxis('axisLabelFontSize', prec_axis);
182         y = area.y + tick[1] * area.h;
183
184         /* Tick marks are currently clipped, so don't bother drawing them.
185         context.beginPath();
186         context.moveTo(halfUp(x), halfDown(y));
187         context.lineTo(halfUp(x - sgn * this.attr_('axisTickSize')), halfDown(y));
188         context.closePath();
189         context.stroke();
190         */
191
192         label = makeDiv(tick[2], 'y', num_axes == 2 ? prec_axis : null);
193         var top = (y - fontSize / 2);
194         if (top < 0) top = 0;
195
196         if (top + fontSize + 3 > canvasHeight) {
197           label.style.bottom = "0px";
198         } else {
199           label.style.top = top + "px";
200         }
201         if (tick[0] === 0) {
202           label.style.left = (area.x - g.getOption('yAxisLabelWidth') - g.getOption('axisTickSize')) + "px";
203           label.style.textAlign = "right";
204         } else if (tick[0] == 1) {
205           label.style.left = (area.x + area.w +
206                               g.getOption('axisTickSize')) + "px";
207           label.style.textAlign = "left";
208         }
209         label.style.width = g.getOption('yAxisLabelWidth') + "px";
210         containerDiv.appendChild(label);
211         this.ylabels_.push(label);
212       }
213
214       // The lowest tick on the y-axis often overlaps with the leftmost
215       // tick on the x-axis. Shift the bottom tick up a little bit to
216       // compensate if necessary.
217       var bottomTick = this.ylabels_[0];
218       // Interested in the y2 axis also?
219       var fontSize = g.getOptionForAxis('axisLabelFontSize', "y");
220       var bottom = parseInt(bottomTick.style.top, 10) + fontSize;
221       if (bottom > canvasHeight - fontSize) {
222         bottomTick.style.top = (parseInt(bottomTick.style.top, 10) -
223             fontSize / 2) + "px";
224       }
225     }
226
227     // draw a vertical line on the left to separate the chart from the labels.
228     var axisX;
229     if (g.getOption('drawAxesAtZero')) {
230       var r = g.toPercentXCoord(0);
231       if (r > 1 || r < 0 || isNaN(r)) r = 0;
232       axisX = halfUp(area.x + r * area.w);
233     } else {
234       axisX = halfUp(area.x);
235     }
236
237     context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y');
238     context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y');
239
240     context.beginPath();
241     context.moveTo(axisX, halfDown(area.y));
242     context.lineTo(axisX, halfDown(area.y + area.h));
243     context.closePath();
244     context.stroke();
245
246     // if there's a secondary y-axis, draw a vertical line for that, too.
247     if (g.numAxes() == 2) {
248       context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y2');
249       context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y2');
250       context.beginPath();
251       context.moveTo(halfDown(area.x + area.w), halfDown(area.y));
252       context.lineTo(halfDown(area.x + area.w), halfDown(area.y + area.h));
253       context.closePath();
254       context.stroke();
255     }
256   }
257
258   if (g.getOption('drawXAxis')) {
259     if (layout.xticks) {
260       for (i = 0; i < layout.xticks.length; i++) {
261         tick = layout.xticks[i];
262         x = area.x + tick[0] * area.w;
263         y = area.y + area.h;
264
265         /* Tick marks are currently clipped, so don't bother drawing them.
266         context.beginPath();
267         context.moveTo(halfUp(x), halfDown(y));
268         context.lineTo(halfUp(x), halfDown(y + this.attr_('axisTickSize')));
269         context.closePath();
270         context.stroke();
271         */
272
273         label = makeDiv(tick[1], 'x');
274         label.style.textAlign = "center";
275         label.style.top = (y + g.getOption('axisTickSize')) + 'px';
276
277         var left = (x - g.getOption('axisLabelWidth')/2);
278         if (left + g.getOption('axisLabelWidth') > canvasWidth) {
279           left = canvasWidth - g.getOption('xAxisLabelWidth');
280           label.style.textAlign = "right";
281         }
282         if (left < 0) {
283           left = 0;
284           label.style.textAlign = "left";
285         }
286
287         label.style.left = left + "px";
288         label.style.width = g.getOption('xAxisLabelWidth') + "px";
289         containerDiv.appendChild(label);
290         this.xlabels_.push(label);
291       }
292     }
293
294     context.strokeStyle = g.getOptionForAxis('axisLineColor', 'x');
295     context.lineWidth = g.getOptionForAxis('axisLineWidth', 'x');
296     context.beginPath();
297     var axisY;
298     if (g.getOption('drawAxesAtZero')) {
299       var r = g.toPercentYCoord(0, 0);
300       if (r > 1 || r < 0) r = 1;
301       axisY = halfDown(area.y + r * area.h);
302     } else {
303       axisY = halfDown(area.y + area.h);
304     }
305     context.moveTo(halfUp(area.x), axisY);
306     context.lineTo(halfUp(area.x + area.w), axisY);
307     context.closePath();
308     context.stroke();
309   }
310
311   context.restore();
312 };
313
314 return axes;
315 })();