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