move custom_rhino out of plotkit
[dygraphs.git] / plotkit_v091 / PlotKit / Canvas.js
1 PlotKit.CanvasRenderer = function(element, layout, options) {
2 if (arguments.length > 0)
3 this.__init__(element, layout, options);
4 };
5
6 PlotKit.CanvasRenderer.prototype.__init__ = function(element, layout, options) {
7 var isNil = MochiKit.Base.isUndefinedOrNull;
8 var Color = MochiKit.Color.Color;
9
10 // default options
11 this.options = {
12 "strokeWidth": 0.5,
13 "drawXAxis": true,
14 "drawYAxis": true,
15 "axisLineColor": Color.blackColor(),
16 "axisLineWidth": 0.5,
17 "axisTickSize": 3,
18 "axisLabelColor": Color.blackColor(),
19 "axisLabelFont": "Arial",
20 "axisLabelFontSize": 9,
21 "axisLabelWidth": 50,
22 };
23 MochiKit.Base.update(this.options, options ? options : {});
24
25 this.layout = layout;
26 this.element = MochiKit.DOM.getElement(element);
27 this.container = this.element.parentNode;
28
29 // Stuff relating to Canvas on IE support
30 this.isIE = PlotKit.Base.excanvasSupported();
31
32 if (this.isIE && !isNil(G_vmlCanvasManager)) {
33 this.IEDelay = 0.5;
34 this.maxTries = 5;
35 this.renderDelay = null;
36 this.clearDelay = null;
37 this.element = G_vmlCanvasManager.initElement(this.element);
38 }
39
40 this.height = this.element.height;
41 this.width = this.element.width;
42
43 // --- check whether everything is ok before we return
44
45 if (isNil(this.element))
46 throw "CanvasRenderer() - passed canvas is not found";
47
48 if (!this.isIE && !(PlotKit.CanvasRenderer.isSupported(this.element)))
49 throw "CanvasRenderer() - Canvas is not supported.";
50
51 if (isNil(this.container) || (this.container.nodeName.toLowerCase() != "div"))
52 throw "CanvasRenderer() - <canvas> needs to be enclosed in <div>";
53
54 // internal state
55 this.xlabels = new Array();
56 this.ylabels = new Array();
57
58 this.area = {
59 x: this.options.yAxisLabelWidth + 2 * this.options.axisTickSize,
60 y: 0
61 };
62 this.area.w = this.width - this.area.x - this.options.rightGap;
63 this.area.h = this.height - this.options.axisLabelFontSize -
64 2 * this.options.axisTickSize;
65
66 MochiKit.DOM.updateNodeAttributes(this.container,
67 {"style":{ "position": "relative", "width": this.width + "px"}});
68 };
69
70
71 PlotKit.CanvasRenderer.prototype._renderLineAxis = function() {
72 this._renderAxis();
73 };
74
75
76 PlotKit.CanvasRenderer.prototype._renderAxis = function() {
77 if (!this.options.drawXAxis && !this.options.drawYAxis)
78 return;
79
80 var context = this.element.getContext("2d");
81
82 var labelStyle = {"style":
83 {"position": "absolute",
84 "fontSize": this.options.axisLabelFontSize + "px",
85 "zIndex": 10,
86 "color": this.options.axisLabelColor.toRGBString(),
87 "width": this.options.axisLabelWidth + "px",
88 "overflow": "hidden"
89 }
90 };
91
92 // axis lines
93 context.save();
94 context.strokeStyle = this.options.axisLineColor.toRGBString();
95 context.lineWidth = this.options.axisLineWidth;
96
97
98 if (this.options.drawYAxis) {
99 if (this.layout.yticks) {
100 var drawTick = function(tick) {
101 if (typeof(tick) == "function") return;
102 var x = this.area.x;
103 var y = this.area.y + tick[0] * this.area.h;
104 context.beginPath();
105 context.moveTo(x, y);
106 context.lineTo(x - this.options.axisTickSize, y);
107 context.closePath();
108 context.stroke();
109
110 var label = DIV(labelStyle, tick[1]);
111 var top = (y - this.options.axisLabelFontSize / 2);
112 if (top < 0) top = 0;
113
114 if (top + this.options.axisLabelFontSize + 3 > this.height) {
115 label.style.bottom = "0px";
116 } else {
117 label.style.top = top + "px";
118 }
119 label.style.left = "0px";
120 label.style.textAlign = "right";
121 label.style.width = this.options.yAxisLabelWidth + "px";
122 MochiKit.DOM.appendChildNodes(this.container, label);
123 this.ylabels.push(label);
124 };
125
126 MochiKit.Iter.forEach(this.layout.yticks, bind(drawTick, this));
127
128 // The lowest tick on the y-axis often overlaps with the leftmost
129 // tick on the x-axis. Shift the bottom tick up a little bit to
130 // compensate if necessary.
131 var bottomTick = this.ylabels[0];
132 var fontSize = this.options.axisLabelFontSize;
133 var bottom = parseInt(bottomTick.style.top) + fontSize;
134 if (bottom > this.height - fontSize) {
135 bottomTick.style.top = (parseInt(bottomTick.style.top) -
136 fontSize / 2) + "px";
137 }
138 }
139
140 context.beginPath();
141 context.moveTo(this.area.x, this.area.y);
142 context.lineTo(this.area.x, this.area.y + this.area.h);
143 context.closePath();
144 context.stroke();
145 }
146
147 if (this.options.drawXAxis) {
148 if (this.layout.xticks) {
149 var drawTick = function(tick) {
150 if (typeof(dataset) == "function") return;
151
152 var x = this.area.x + tick[0] * this.area.w;
153 var y = this.area.y + this.area.h;
154 context.beginPath();
155 context.moveTo(x, y);
156 context.lineTo(x, y + this.options.axisTickSize);
157 context.closePath();
158 context.stroke();
159
160 var label = DIV(labelStyle, tick[1]);
161 label.style.textAlign = "center";
162 label.style.bottom = "0px";
163
164 var left = (x - this.options.axisLabelWidth/2);
165 if (left + this.options.axisLabelWidth > this.width) {
166 left = this.width - this.options.xAxisLabelWidth;
167 label.style.textAlign = "right";
168 }
169 if (left < 0) {
170 left = 0;
171 label.style.textAlign = "left";
172 }
173
174 label.style.left = left + "px";
175 label.style.width = this.options.xAxisLabelWidth + "px";
176 MochiKit.DOM.appendChildNodes(this.container, label);
177 this.xlabels.push(label);
178 };
179
180 MochiKit.Iter.forEach(this.layout.xticks, bind(drawTick, this));
181 }
182
183 context.beginPath();
184 context.moveTo(this.area.x, this.area.y + this.area.h);
185 context.lineTo(this.area.x + this.area.w, this.area.y + this.area.h);
186 context.closePath();
187 context.stroke();
188 }
189
190 context.restore();
191
192 };
193
194 PlotKit.CanvasRenderer.prototype.clear = function() {
195 if (this.isIE) {
196 // VML takes a while to start up, so we just poll every this.IEDelay
197 try {
198 if (this.clearDelay) {
199 this.clearDelay.cancel();
200 this.clearDelay = null;
201 }
202 var context = this.element.getContext("2d");
203 }
204 catch (e) {
205 this.clearDelay = MochiKit.Async.wait(this.IEDelay);
206 this.clearDelay.addCallback(bind(this.clear, this));
207 return;
208 }
209 }
210
211 var context = this.element.getContext("2d");
212 context.clearRect(0, 0, this.width, this.height);
213
214 MochiKit.Iter.forEach(this.xlabels, MochiKit.DOM.removeElement);
215 MochiKit.Iter.forEach(this.ylabels, MochiKit.DOM.removeElement);
216 this.xlabels = new Array();
217 this.ylabels = new Array();
218 };
219
220 // ----------------------------------------------------------------
221 // Everything below here is experimental and undocumented.
222 // ----------------------------------------------------------------
223
224
225 PlotKit.CanvasRenderer.isSupported = function(canvasName) {
226 var canvas = null;
227 try {
228 if (MochiKit.Base.isUndefinedOrNull(canvasName))
229 canvas = MochiKit.DOM.CANVAS({});
230 else
231 canvas = MochiKit.DOM.getElement(canvasName);
232 var context = canvas.getContext("2d");
233 }
234 catch (e) {
235 var ie = navigator.appVersion.match(/MSIE (\d\.\d)/);
236 var opera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
237 if ((!ie) || (ie[1] < 6) || (opera))
238 return false;
239 return true;
240 }
241 return true;
242 };