remove dependence on Layout.js
[dygraphs.git] / plotkit_v091 / PlotKit / Canvas.js
CommitLineData
6a1aa64f
DV
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
24try {
efe0829a 25 if (typeof(PlotKit.Base) == 'undefined')
6a1aa64f
DV
26 {
27 throw "";
28 }
29}
30catch (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
39if (typeof(PlotKit.CanvasRenderer) == 'undefined') {
40 PlotKit.CanvasRenderer = {};
41}
42
43PlotKit.CanvasRenderer.NAME = "PlotKit.CanvasRenderer";
44PlotKit.CanvasRenderer.VERSION = PlotKit.VERSION;
45
46PlotKit.CanvasRenderer.__repr__ = function() {
47 return "[" + this.NAME + " " + this.VERSION + "]";
48};
49
50PlotKit.CanvasRenderer.toString = function() {
51 return this.__repr__();
52}
53
54PlotKit.CanvasRenderer = function(element, layout, options) {
55 if (arguments.length > 0)
56 this.__init__(element, layout, options);
57};
58
59PlotKit.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 "drawBackground": true,
66 "backgroundColor": Color.whiteColor(),
6a1aa64f
DV
67 "colorScheme": PlotKit.Base.palette(PlotKit.Base.baseColors()[0]),
68 "strokeColor": Color.whiteColor(),
69 "strokeColorTransform": "asStrokeColor",
70 "strokeWidth": 0.5,
71 "shouldFill": true,
72 "shouldStroke": true,
73 "drawXAxis": true,
74 "drawYAxis": true,
75 "axisLineColor": Color.blackColor(),
76 "axisLineWidth": 0.5,
77 "axisTickSize": 3,
78 "axisLabelColor": Color.blackColor(),
79 "axisLabelFont": "Arial",
80 "axisLabelFontSize": 9,
81 "axisLabelWidth": 50,
82 "pieRadius": 0.4,
83 "enableEvents": true
84 };
85 MochiKit.Base.update(this.options, options ? options : {});
86
87 this.layout = layout;
88 this.element = MochiKit.DOM.getElement(element);
89 this.container = this.element.parentNode;
90
91 // Stuff relating to Canvas on IE support
92 this.isIE = PlotKit.Base.excanvasSupported();
93
94 if (this.isIE && !isNil(G_vmlCanvasManager)) {
95 this.IEDelay = 0.5;
96 this.maxTries = 5;
97 this.renderDelay = null;
98 this.clearDelay = null;
99 this.element = G_vmlCanvasManager.initElement(this.element);
100 }
101
102 this.height = this.element.height;
103 this.width = this.element.width;
104
105 // --- check whether everything is ok before we return
106
107 if (isNil(this.element))
108 throw "CanvasRenderer() - passed canvas is not found";
109
110 if (!this.isIE && !(PlotKit.CanvasRenderer.isSupported(this.element)))
111 throw "CanvasRenderer() - Canvas is not supported.";
112
113 if (isNil(this.container) || (this.container.nodeName.toLowerCase() != "div"))
114 throw "CanvasRenderer() - <canvas> needs to be enclosed in <div>";
115
116 // internal state
117 this.xlabels = new Array();
118 this.ylabels = new Array();
119 this.isFirstRender = true;
120
121 this.area = {
8846615a
DV
122 x: this.options.yAxisLabelWidth + 2 * this.options.axisTickSize,
123 y: 0
6a1aa64f 124 };
8846615a
DV
125 this.area.w = this.width - this.area.x - this.options.rightGap;
126 this.area.h = this.height - this.options.axisLabelFontSize -
127 2 * this.options.axisTickSize;
6a1aa64f
DV
128
129 MochiKit.DOM.updateNodeAttributes(this.container,
130 {"style":{ "position": "relative", "width": this.width + "px"}});
6a1aa64f
DV
131};
132
6a1aa64f
DV
133
134PlotKit.CanvasRenderer.prototype._renderLineAxis = function() {
135 this._renderAxis();
136};
137
138
139PlotKit.CanvasRenderer.prototype._renderAxis = function() {
140 if (!this.options.drawXAxis && !this.options.drawYAxis)
141 return;
142
143 var context = this.element.getContext("2d");
144
145 var labelStyle = {"style":
146 {"position": "absolute",
147 "fontSize": this.options.axisLabelFontSize + "px",
148 "zIndex": 10,
149 "color": this.options.axisLabelColor.toRGBString(),
150 "width": this.options.axisLabelWidth + "px",
151 "overflow": "hidden"
152 }
153 };
154
155 // axis lines
156 context.save();
157 context.strokeStyle = this.options.axisLineColor.toRGBString();
158 context.lineWidth = this.options.axisLineWidth;
159
160
161 if (this.options.drawYAxis) {
162 if (this.layout.yticks) {
163 var drawTick = function(tick) {
164 if (typeof(tick) == "function") return;
165 var x = this.area.x;
166 var y = this.area.y + tick[0] * this.area.h;
167 context.beginPath();
168 context.moveTo(x, y);
169 context.lineTo(x - this.options.axisTickSize, y);
170 context.closePath();
171 context.stroke();
172
173 var label = DIV(labelStyle, tick[1]);
8846615a
DV
174 var top = (y - this.options.axisLabelFontSize / 2);
175 if (top < 0) top = 0;
176
177 if (top + this.options.axisLabelFontSize + 3 > this.height) {
178 label.style.bottom = "0px";
179 } else {
180 label.style.top = top + "px";
181 }
182 label.style.left = "0px";
6a1aa64f 183 label.style.textAlign = "right";
8846615a 184 label.style.width = this.options.yAxisLabelWidth + "px";
6a1aa64f
DV
185 MochiKit.DOM.appendChildNodes(this.container, label);
186 this.ylabels.push(label);
187 };
188
189 MochiKit.Iter.forEach(this.layout.yticks, bind(drawTick, this));
8846615a
DV
190
191 // The lowest tick on the y-axis often overlaps with the leftmost
192 // tick on the x-axis. Shift the bottom tick up a little bit to
193 // compensate if necessary.
194 var bottomTick = this.ylabels[0];
195 var fontSize = this.options.axisLabelFontSize;
196 var bottom = parseInt(bottomTick.style.top) + fontSize;
197 if (bottom > this.height - fontSize) {
198 bottomTick.style.top = (parseInt(bottomTick.style.top) -
199 fontSize / 2) + "px";
200 }
6a1aa64f
DV
201 }
202
203 context.beginPath();
204 context.moveTo(this.area.x, this.area.y);
205 context.lineTo(this.area.x, this.area.y + this.area.h);
206 context.closePath();
207 context.stroke();
208 }
209
210 if (this.options.drawXAxis) {
211 if (this.layout.xticks) {
212 var drawTick = function(tick) {
213 if (typeof(dataset) == "function") return;
214
215 var x = this.area.x + tick[0] * this.area.w;
216 var y = this.area.y + this.area.h;
217 context.beginPath();
218 context.moveTo(x, y);
219 context.lineTo(x, y + this.options.axisTickSize);
220 context.closePath();
221 context.stroke();
222
223 var label = DIV(labelStyle, tick[1]);
6a1aa64f 224 label.style.textAlign = "center";
8846615a
DV
225 label.style.bottom = "0px";
226
227 var left = (x - this.options.axisLabelWidth/2);
228 if (left + this.options.axisLabelWidth > this.width) {
229 left = this.width - this.options.xAxisLabelWidth;
230 label.style.textAlign = "right";
231 }
232 if (left < 0) {
233 left = 0;
234 label.style.textAlign = "left";
235 }
236
237 label.style.left = left + "px";
238 label.style.width = this.options.xAxisLabelWidth + "px";
6a1aa64f
DV
239 MochiKit.DOM.appendChildNodes(this.container, label);
240 this.xlabels.push(label);
241 };
242
243 MochiKit.Iter.forEach(this.layout.xticks, bind(drawTick, this));
244 }
245
246 context.beginPath();
247 context.moveTo(this.area.x, this.area.y + this.area.h);
248 context.lineTo(this.area.x + this.area.w, this.area.y + this.area.h);
249 context.closePath();
250 context.stroke();
251 }
252
253 context.restore();
254
255};
256
6a1aa64f
DV
257PlotKit.CanvasRenderer.prototype.clear = function() {
258 if (this.isIE) {
259 // VML takes a while to start up, so we just poll every this.IEDelay
260 try {
261 if (this.clearDelay) {
262 this.clearDelay.cancel();
263 this.clearDelay = null;
264 }
265 var context = this.element.getContext("2d");
266 }
267 catch (e) {
268 this.isFirstRender = false;
269 this.clearDelay = MochiKit.Async.wait(this.IEDelay);
270 this.clearDelay.addCallback(bind(this.clear, this));
271 return;
272 }
273 }
274
275 var context = this.element.getContext("2d");
276 context.clearRect(0, 0, this.width, this.height);
277
278 MochiKit.Iter.forEach(this.xlabels, MochiKit.DOM.removeElement);
279 MochiKit.Iter.forEach(this.ylabels, MochiKit.DOM.removeElement);
280 this.xlabels = new Array();
281 this.ylabels = new Array();
282};
283
284// ----------------------------------------------------------------
285// Everything below here is experimental and undocumented.
286// ----------------------------------------------------------------
287
6a1aa64f
DV
288
289PlotKit.CanvasRenderer.isSupported = function(canvasName) {
290 var canvas = null;
291 try {
292 if (MochiKit.Base.isUndefinedOrNull(canvasName))
293 canvas = MochiKit.DOM.CANVAS({});
294 else
295 canvas = MochiKit.DOM.getElement(canvasName);
296 var context = canvas.getContext("2d");
297 }
298 catch (e) {
299 var ie = navigator.appVersion.match(/MSIE (\d\.\d)/);
300 var opera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
301 if ((!ie) || (ie[1] < 6) || (opera))
302 return false;
303 return true;
304 }
305 return true;
306};
307
308// Namespace Iniitialisation
309
310PlotKit.Canvas = {}
311PlotKit.Canvas.CanvasRenderer = PlotKit.CanvasRenderer;
312
313PlotKit.Canvas.EXPORT = [
314 "CanvasRenderer"
315];
316
317PlotKit.Canvas.EXPORT_OK = [
318 "CanvasRenderer"
319];
320
321PlotKit.Canvas.__new__ = function() {
322 var m = MochiKit.Base;
323
324 m.nameFunctions(this);
325
326 this.EXPORT_TAGS = {
327 ":common": this.EXPORT,
328 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
329 };
330};
331
332PlotKit.Canvas.__new__();
333MochiKit.Base._exportSymbols(this, PlotKit.Canvas);
334