3 * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
4 * MIT-licensed (http://opensource.org/licenses/MIT)
7 Dygraph
.Plugins
.Axes
= (function() {
12 - Direct layout access
14 - Should include calculation of ticks, not just the drawing.
19 * Draws the axes. This includes the labels on the x- and y-axes, as well
20 * as the tick marks on the axes.
21 * It does _not_ draw the grid lines which span the entire chart.
23 var axes
= function() {
28 axes
.prototype.toString
= function() {
32 axes
.prototype.activate
= function(g
) {
35 clearChart
: this.clearChart
,
36 drawChart
: this.drawChart
40 axes
.prototype.layout
= function(e
) {
43 if (g
.getOption('drawYAxis')) {
44 var w
= g
.getOption('yAxisLabelWidth') + 2 * g
.getOption('axisTickSize');
45 var y_axis_rect
= e
.reserveSpaceLeft(w
);
48 if (g
.getOption('drawXAxis')) {
50 if (g
.getOption('xAxisHeight')) {
51 h
= g
.getOption('xAxisHeight');
53 h
= g
.getOption('axisLabelFontSize') + 2 * g
.getOption('axisTickSize');
55 var x_axis_rect
= e
.reserveSpaceBottom(h
);
58 if (g
.numAxes() == 2) {
59 // TODO(danvk): per-axis setting.
60 var w
= g
.getOption('yAxisLabelWidth') + 2 * g
.getOption('axisTickSize');
61 var y2_axis_rect
= e
.reserveSpaceRight(w
);
62 } else if (g
.numAxes() > 2) {
63 g
.error("Only two y-axes are supported at this time. (Trying " +
64 "to use " + g
.numAxes() + ")");
68 axes
.prototype.detachLabels
= function() {
69 function removeArray(ary
) {
70 for (var i
= 0; i
< ary
.length
; i
++) {
72 if (el
.parentNode
) el
.parentNode
.removeChild(el
);
76 removeArray(this.xlabels_
);
77 removeArray(this.ylabels_
);
82 axes
.prototype.clearChart
= function(e
) {
87 axes
.prototype.drawChart
= function(e
) {
89 if (!g
.getOption('drawXAxis') && !g
.getOption('drawYAxis')) return;
91 // Round pixels to half-integer boundaries for crisper drawing.
92 function halfUp(x
) { return Math
.round(x
) + 0.5; }
93 function halfDown(y
){ return Math
.round(y
) - 0.5; }
95 var context
= e
.drawingContext
;
96 var containerDiv
= e
.canvas
.parentNode
;
98 var label
, x
, y
, tick
, i
;
101 position
: "absolute",
102 fontSize
: g
.getOption('axisLabelFontSize') + "px",
104 color
: g
.getOption('axisLabelColor'),
105 width
: g
.getOption('axisLabelWidth') + "px",
106 // height: this.attr_('axisLabelFontSize') + 2 + "px",
107 lineHeight
: "normal", // Something other than "normal" line-height screws up label positioning.
110 var makeDiv
= function(txt
, axis
, prec_axis
) {
111 var div
= document
.createElement("div");
112 for (var name
in labelStyle
) {
113 if (labelStyle
.hasOwnProperty(name
)) {
114 div
.style
[name
] = labelStyle
[name
];
117 var inner_div
= document
.createElement("div");
118 inner_div
.className
= 'dygraph-axis-label' +
119 ' dygraph-axis-label-' + axis
+
120 (prec_axis
? ' dygraph-axis-label-' + prec_axis
: '');
121 inner_div
.innerHTML
= txt
;
122 div
.appendChild(inner_div
);
128 context
.strokeStyle
= g
.getOption('axisLineColor');
129 context
.lineWidth
= g
.getOption('axisLineWidth');
131 var layout
= g
.layout_
;
132 var area
= e
.dygraph
.plotter_
.area
;
134 if (g
.getOption('drawYAxis')) {
135 if (layout
.yticks
&& layout
.yticks
.length
> 0) {
136 var num_axes
= g
.numAxes();
137 for (i
= 0; i
< layout
.yticks
.length
; i
++) {
138 tick
= layout
.yticks
[i
];
139 if (typeof(tick
) == "function") return;
142 var prec_axis
= 'y1';
143 if (tick
[0] == 1) { // right-side y-axis
148 y
= area
.y
+ tick
[1] * area
.h
;
150 /* Tick marks are currently clipped, so don't bother drawing them.
152 context.moveTo(halfUp(x), halfDown(y));
153 context.lineTo(halfUp(x - sgn * this.attr_('axisTickSize')), halfDown(y));
158 label
= makeDiv(tick
[2], 'y', num_axes
== 2 ? prec_axis
: null);
159 var top
= (y
- g
.getOption('axisLabelFontSize') / 2);
160 if (top
< 0) top
= 0;
162 if (top
+ g
.getOption('axisLabelFontSize') + 3 > this.height
) {
163 label
.style
.bottom
= "0px";
165 label
.style
.top
= top
+ "px";
168 label
.style
.left
= (area
.x
- g
.getOption('yAxisLabelWidth') - g
.getOption('axisTickSize')) + "px";
169 label
.style
.textAlign
= "right";
170 } else if (tick
[0] == 1) {
171 label
.style
.left
= (area
.x
+ area
.w
+
172 g
.getOption('axisTickSize')) + "px";
173 label
.style
.textAlign
= "left";
175 label
.style
.width
= g
.getOption('yAxisLabelWidth') + "px";
176 containerDiv
.appendChild(label
);
177 this.ylabels_
.push(label
);
180 // The lowest tick on the y-axis often overlaps with the leftmost
181 // tick on the x-axis. Shift the bottom tick up a little bit to
182 // compensate if necessary.
183 var bottomTick
= this.ylabels_
[0];
184 var fontSize
= g
.getOption('axisLabelFontSize');
185 var bottom
= parseInt(bottomTick
.style
.top
, 10) + fontSize
;
186 if (bottom
> this.height
- fontSize
) {
187 bottomTick
.style
.top
= (parseInt(bottomTick
.style
.top
, 10) -
188 fontSize
/ 2) + "px";
192 // draw a vertical line on the left to separate the chart from the labels.
194 if (g
.getOption('drawAxesAtZero')) {
195 var r
= this.dygraph_
.toPercentXCoord(0);
196 if (r
> 1 || r
< 0) r
= 0;
197 axisX
= halfUp(area
.x
+ r
* area
.w
);
199 axisX
= halfUp(area
.x
);
202 context
.moveTo(axisX
, halfDown(area
.y
));
203 context
.lineTo(axisX
, halfDown(area
.y
+ area
.h
));
207 // if there's a secondary y-axis, draw a vertical line for that, too.
208 if (g
.numAxes() == 2) {
210 context
.moveTo(halfDown(area
.x
+ area
.w
), halfDown(area
.y
));
211 context
.lineTo(halfDown(area
.x
+ area
.w
), halfDown(area
.y
+ area
.h
));
217 if (g
.getOption('drawXAxis')) {
219 for (i
= 0; i
< layout
.xticks
.length
; i
++) {
220 tick
= layout
.xticks
[i
];
221 x
= area
.x
+ tick
[0] * area
.w
;
224 /* Tick marks are currently clipped, so don't bother drawing them.
226 context.moveTo(halfUp(x), halfDown(y));
227 context.lineTo(halfUp(x), halfDown(y + this.attr_('axisTickSize')));
232 label
= makeDiv(tick
[1], 'x');
233 label
.style
.textAlign
= "center";
234 label
.style
.top
= (y
+ g
.getOption('axisTickSize')) + 'px';
236 var left
= (x
- g
.getOption('axisLabelWidth')/2);
237 if (left
+ g
.getOption('axisLabelWidth') > this.width
) {
238 left
= this.width
- g
.getOption('xAxisLabelWidth');
239 label
.style
.textAlign
= "right";
243 label
.style
.textAlign
= "left";
246 label
.style
.left
= left
+ "px";
247 label
.style
.width
= g
.getOption('xAxisLabelWidth') + "px";
248 containerDiv
.appendChild(label
);
249 this.xlabels_
.push(label
);
255 if (g
.getOption('drawAxesAtZero')) {
256 var r
= g
.toPercentYCoord(0, 0);
257 if (r
> 1 || r
< 0) r
= 1;
258 axisY
= halfDown(area
.y
+ r
* area
.h
);
260 axisY
= halfDown(area
.y
+ area
.h
);
262 context
.moveTo(halfUp(area
.x
), axisY
);
263 context
.lineTo(halfUp(area
.x
+ area
.w
), axisY
);