remove some unneeded bits from Canvas.js
[dygraphs.git] / plotkit_v091 / PlotKit / Canvas.js
1 /*
2 PlotKit Canvas
3 ==============
4
5 Provides HTML Canvas Renderer. This is supported under:
6
7 - Safari 2.0
8 - Mozilla Firefox 1.5
9 - Opera 9.0 preview 2
10 - IE 6 (via VML Emulation)
11
12 It uses DIVs for labels.
13
14 Copyright
15 ---------
16 Copyright 2005,2006 (c) Alastair Tse <alastair^liquidx.net>
17 For use under the BSD license. <http://www.liquidx.net/plotkit>
18
19 */
20 // --------------------------------------------------------------------
21 // Check required components
22 // --------------------------------------------------------------------
23
24 try {
25 if (typeof(PlotKit.Base) == 'undefined')
26 {
27 throw "";
28 }
29 }
30 catch (e) {
31 throw "PlotKit.Layout depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.{Base,Layout}"
32 }
33
34
35 // ------------------------------------------------------------------------
36 // Defines the renderer class
37 // ------------------------------------------------------------------------
38
39 if (typeof(PlotKit.CanvasRenderer) == 'undefined') {
40 PlotKit.CanvasRenderer = {};
41 }
42
43 PlotKit.CanvasRenderer.NAME = "PlotKit.CanvasRenderer";
44 PlotKit.CanvasRenderer.VERSION = PlotKit.VERSION;
45
46 PlotKit.CanvasRenderer.__repr__ = function() {
47 return "[" + this.NAME + " " + this.VERSION + "]";
48 };
49
50 PlotKit.CanvasRenderer.toString = function() {
51 return this.__repr__();
52 }
53
54 PlotKit.CanvasRenderer = function(element, layout, options) {
55 if (arguments.length > 0)
56 this.__init__(element, layout, options);
57 };
58
59 PlotKit.CanvasRenderer.prototype.__init__ = function(element, layout, options) {
60 var isNil = MochiKit.Base.isUndefinedOrNull;
61 var Color = MochiKit.Color.Color;
62
63 // default options
64 this.options = {
65 "strokeWidth": 0.5,
66 "drawXAxis": true,
67 "drawYAxis": true,
68 "axisLineColor": Color.blackColor(),
69 "axisLineWidth": 0.5,
70 "axisTickSize": 3,
71 "axisLabelColor": Color.blackColor(),
72 "axisLabelFont": "Arial",
73 "axisLabelFontSize": 9,
74 "axisLabelWidth": 50,
75 };
76 MochiKit.Base.update(this.options, options ? options : {});
77
78 this.layout = layout;
79 this.element = MochiKit.DOM.getElement(element);
80 this.container = this.element.parentNode;
81
82 // Stuff relating to Canvas on IE support
83 this.isIE = PlotKit.Base.excanvasSupported();
84
85 if (this.isIE && !isNil(G_vmlCanvasManager)) {
86 this.IEDelay = 0.5;
87 this.maxTries = 5;
88 this.renderDelay = null;
89 this.clearDelay = null;
90 this.element = G_vmlCanvasManager.initElement(this.element);
91 }
92
93 this.height = this.element.height;
94 this.width = this.element.width;
95
96 // --- check whether everything is ok before we return
97
98 if (isNil(this.element))
99 throw "CanvasRenderer() - passed canvas is not found";
100
101 if (!this.isIE && !(PlotKit.CanvasRenderer.isSupported(this.element)))
102 throw "CanvasRenderer() - Canvas is not supported.";
103
104 if (isNil(this.container) || (this.container.nodeName.toLowerCase() != "div"))
105 throw "CanvasRenderer() - <canvas> needs to be enclosed in <div>";
106
107 // internal state
108 this.xlabels = new Array();
109 this.ylabels = new Array();
110 this.isFirstRender = true;
111
112 this.area = {
113 x: this.options.yAxisLabelWidth + 2 * this.options.axisTickSize,
114 y: 0
115 };
116 this.area.w = this.width - this.area.x - this.options.rightGap;
117 this.area.h = this.height - this.options.axisLabelFontSize -
118 2 * this.options.axisTickSize;
119
120 MochiKit.DOM.updateNodeAttributes(this.container,
121 {"style":{ "position": "relative", "width": this.width + "px"}});
122 };
123
124
125 PlotKit.CanvasRenderer.prototype._renderLineAxis = function() {
126 this._renderAxis();
127 };
128
129
130 PlotKit.CanvasRenderer.prototype._renderAxis = function() {
131 if (!this.options.drawXAxis && !this.options.drawYAxis)
132 return;
133
134 var context = this.element.getContext("2d");
135
136 var labelStyle = {"style":
137 {"position": "absolute",
138 "fontSize": this.options.axisLabelFontSize + "px",
139 "zIndex": 10,
140 "color": this.options.axisLabelColor.toRGBString(),
141 "width": this.options.axisLabelWidth + "px",
142 "overflow": "hidden"
143 }
144 };
145
146 // axis lines
147 context.save();
148 context.strokeStyle = this.options.axisLineColor.toRGBString();
149 context.lineWidth = this.options.axisLineWidth;
150
151
152 if (this.options.drawYAxis) {
153 if (this.layout.yticks) {
154 var drawTick = function(tick) {
155 if (typeof(tick) == "function") return;
156 var x = this.area.x;
157 var y = this.area.y + tick[0] * this.area.h;
158 context.beginPath();
159 context.moveTo(x, y);
160 context.lineTo(x - this.options.axisTickSize, y);
161 context.closePath();
162 context.stroke();
163
164 var label = DIV(labelStyle, tick[1]);
165 var top = (y - this.options.axisLabelFontSize / 2);
166 if (top < 0) top = 0;
167
168 if (top + this.options.axisLabelFontSize + 3 > this.height) {
169 label.style.bottom = "0px";
170 } else {
171 label.style.top = top + "px";
172 }
173 label.style.left = "0px";
174 label.style.textAlign = "right";
175 label.style.width = this.options.yAxisLabelWidth + "px";
176 MochiKit.DOM.appendChildNodes(this.container, label);
177 this.ylabels.push(label);
178 };
179
180 MochiKit.Iter.forEach(this.layout.yticks, bind(drawTick, this));
181
182 // The lowest tick on the y-axis often overlaps with the leftmost
183 // tick on the x-axis. Shift the bottom tick up a little bit to
184 // compensate if necessary.
185 var bottomTick = this.ylabels[0];
186 var fontSize = this.options.axisLabelFontSize;
187 var bottom = parseInt(bottomTick.style.top) + fontSize;
188 if (bottom > this.height - fontSize) {
189 bottomTick.style.top = (parseInt(bottomTick.style.top) -
190 fontSize / 2) + "px";
191 }
192 }
193
194 context.beginPath();
195 context.moveTo(this.area.x, this.area.y);
196 context.lineTo(this.area.x, this.area.y + this.area.h);
197 context.closePath();
198 context.stroke();
199 }
200
201 if (this.options.drawXAxis) {
202 if (this.layout.xticks) {
203 var drawTick = function(tick) {
204 if (typeof(dataset) == "function") return;
205
206 var x = this.area.x + tick[0] * this.area.w;
207 var y = this.area.y + this.area.h;
208 context.beginPath();
209 context.moveTo(x, y);
210 context.lineTo(x, y + this.options.axisTickSize);
211 context.closePath();
212 context.stroke();
213
214 var label = DIV(labelStyle, tick[1]);
215 label.style.textAlign = "center";
216 label.style.bottom = "0px";
217
218 var left = (x - this.options.axisLabelWidth/2);
219 if (left + this.options.axisLabelWidth > this.width) {
220 left = this.width - this.options.xAxisLabelWidth;
221 label.style.textAlign = "right";
222 }
223 if (left < 0) {
224 left = 0;
225 label.style.textAlign = "left";
226 }
227
228 label.style.left = left + "px";
229 label.style.width = this.options.xAxisLabelWidth + "px";
230 MochiKit.DOM.appendChildNodes(this.container, label);
231 this.xlabels.push(label);
232 };
233
234 MochiKit.Iter.forEach(this.layout.xticks, bind(drawTick, this));
235 }
236
237 context.beginPath();
238 context.moveTo(this.area.x, this.area.y + this.area.h);
239 context.lineTo(this.area.x + this.area.w, this.area.y + this.area.h);
240 context.closePath();
241 context.stroke();
242 }
243
244 context.restore();
245
246 };
247
248 PlotKit.CanvasRenderer.prototype.clear = function() {
249 if (this.isIE) {
250 // VML takes a while to start up, so we just poll every this.IEDelay
251 try {
252 if (this.clearDelay) {
253 this.clearDelay.cancel();
254 this.clearDelay = null;
255 }
256 var context = this.element.getContext("2d");
257 }
258 catch (e) {
259 this.isFirstRender = false;
260 this.clearDelay = MochiKit.Async.wait(this.IEDelay);
261 this.clearDelay.addCallback(bind(this.clear, this));
262 return;
263 }
264 }
265
266 var context = this.element.getContext("2d");
267 context.clearRect(0, 0, this.width, this.height);
268
269 MochiKit.Iter.forEach(this.xlabels, MochiKit.DOM.removeElement);
270 MochiKit.Iter.forEach(this.ylabels, MochiKit.DOM.removeElement);
271 this.xlabels = new Array();
272 this.ylabels = new Array();
273 };
274
275 // ----------------------------------------------------------------
276 // Everything below here is experimental and undocumented.
277 // ----------------------------------------------------------------
278
279
280 PlotKit.CanvasRenderer.isSupported = function(canvasName) {
281 var canvas = null;
282 try {
283 if (MochiKit.Base.isUndefinedOrNull(canvasName))
284 canvas = MochiKit.DOM.CANVAS({});
285 else
286 canvas = MochiKit.DOM.getElement(canvasName);
287 var context = canvas.getContext("2d");
288 }
289 catch (e) {
290 var ie = navigator.appVersion.match(/MSIE (\d\.\d)/);
291 var opera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
292 if ((!ie) || (ie[1] < 6) || (opera))
293 return false;
294 return true;
295 }
296 return true;
297 };