Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(88)

Side by Side Diff: static/js/plotkit/Canvas.js

Issue 22969002: Add stats js files to static dir (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/chromium-build
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « static/js/plotkit/Base.js ('k') | static/js/plotkit/LICENSE » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 =======
3 PlotKit is a collection of Javascript classes that allows
4 you to quickly visualise data using different types of charts.
5
6 http://www.liquidx.net/plotkit/
7
8 See LICENSE file for copyright information.
9
10 =======
11 Provides HTML Canvas Renderer. This is supported under:
12
13 - Safari 2.0
14 - Mozilla Firefox 1.5
15 - Opera 9.0 preview 2
16 - IE 6 (via VML Emulation)
17
18 It uses DIVs for labels.
19 */
20 // --------------------------------------------------------------------
21 // Check required components
22 // --------------------------------------------------------------------
23
24 try {
25 if ((typeof(PlotKit.Base) == 'undefined') ||
26 (typeof(PlotKit.Layout) == 'undefined'))
27 {
28 throw "";
29 }
30 }
31 catch (e) {
32 throw "PlotKit.Layout depends on MochiKit.{Base,Color,DOM,Format} and PlotKi t.{Base,Layout}"
33 }
34
35
36 // ------------------------------------------------------------------------
37 // Defines the renderer class
38 // ------------------------------------------------------------------------
39
40 if (typeof(PlotKit.CanvasRenderer) == 'undefined') {
41 PlotKit.CanvasRenderer = {};
42 }
43
44 PlotKit.CanvasRenderer.NAME = "PlotKit.CanvasRenderer";
45 PlotKit.CanvasRenderer.VERSION = PlotKit.VERSION;
46
47 PlotKit.CanvasRenderer.__repr__ = function() {
48 return "[" + this.NAME + " " + this.VERSION + "]";
49 };
50
51 PlotKit.CanvasRenderer.toString = function() {
52 return this.__repr__();
53 }
54
55 PlotKit.CanvasRenderer = function(element, layout, options) {
56 if (arguments.length > 0)
57 this.__init__(element, layout, options);
58 };
59
60 PlotKit.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(),
68 "padding": {left: 30, right: 30, top: 5, bottom: 10},
69 "colorScheme": PlotKit.Base.palette(PlotKit.Base.baseColors()[0]),
70 "strokeColor": Color.whiteColor(),
71 "strokeColorTransform": "asStrokeColor",
72 "strokeWidth": 0.5,
73 "shouldFill": true,
74 "shouldStroke": true,
75 "drawXAxis": true,
76 "drawYAxis": true,
77 "axisLineColor": Color.blackColor(),
78 "axisLineWidth": 0.5,
79 "axisTickSize": 3,
80 "axisLabelColor": Color.blackColor(),
81 "axisLabelFont": "Arial",
82 "axisLabelFontSize": 9,
83 "axisLabelWidth": 50,
84 "pieRadius": 0.4,
85 "enableEvents": true
86 };
87 MochiKit.Base.update(this.options, options ? options : {});
88
89 this.layout = layout;
90 this.element = MochiKit.DOM.getElement(element);
91 this.container = this.element.parentNode;
92
93 // Stuff relating to Canvas on IE support
94 this.isIE = PlotKit.Base.excanvasSupported();
95
96 if (this.isIE && !isNil(G_vmlCanvasManager)) {
97 this.IEDelay = 0.5;
98 this.maxTries = 5;
99 this.renderDelay = null;
100 this.clearDelay = null;
101 this.element = G_vmlCanvasManager.initElement(this.element);
102 }
103
104 this.height = this.element.height;
105 this.width = this.element.width;
106
107 // --- check whether everything is ok before we return
108
109 if (isNil(this.element))
110 throw "CanvasRenderer() - passed canvas is not found";
111
112 if (!this.isIE && !(PlotKit.CanvasRenderer.isSupported(this.element)))
113 throw "CanvasRenderer() - Canvas is not supported.";
114
115 if (isNil(this.container) || (this.container.nodeName.toLowerCase() != "div" ))
116 throw "CanvasRenderer() - <canvas> needs to be enclosed in <div>";
117
118 // internal state
119 this.xlabels = new Array();
120 this.ylabels = new Array();
121 this.isFirstRender = true;
122
123 this.area = {
124 x: this.options.padding.left,
125 y: this.options.padding.top,
126 w: this.width - this.options.padding.left - this.options.padding.right,
127 h: this.height - this.options.padding.top - this.options.padding.bottom
128 };
129
130 MochiKit.DOM.updateNodeAttributes(this.container,
131 {"style":{ "position": "relative", "width": this.width + "px"}});
132
133 // load event system if we have Signals
134 /* Disabled until we have a proper implementation
135 try {
136 this.event_isinside = null;
137 if (MochiKit.Signal && this.options.enableEvents) {
138 this._initialiseEvents();
139 }
140 }
141 catch (e) {
142 // still experimental
143 }
144 */
145 };
146
147 PlotKit.CanvasRenderer.prototype.render = function() {
148 if (this.isIE) {
149 // VML takes a while to start up, so we just poll every this.IEDelay
150 try {
151 if (this.renderDelay) {
152 this.renderDelay.cancel();
153 this.renderDelay = null;
154 }
155 var context = this.element.getContext("2d");
156 }
157 catch (e) {
158 this.isFirstRender = false;
159 if (this.maxTries-- > 0) {
160 this.renderDelay = MochiKit.Async.wait(this.IEDelay);
161 this.renderDelay.addCallback(bind(this.render, this));
162 }
163 return;
164 }
165 }
166
167 if (this.options.drawBackground)
168 this._renderBackground();
169
170 if (this.layout.style == "bar") {
171 this._renderBarChart();
172 this._renderBarAxis();
173 }
174 else if (this.layout.style == "pie") {
175 this._renderPieChart();
176 this._renderPieAxis();
177 }
178 else if (this.layout.style == "line") {
179 this._renderLineChart();
180 this._renderLineAxis();
181 }
182 };
183
184 PlotKit.CanvasRenderer.prototype._renderBarChartWrap = function(data, plotFunc) {
185 var context = this.element.getContext("2d");
186 var colorCount = this.options.colorScheme.length;
187 var colorScheme = this.options.colorScheme;
188 var setNames = MochiKit.Base.keys(this.layout.datasets);
189 var setCount = setNames.length;
190
191 for (var i = 0; i < setCount; i++) {
192 var setName = setNames[i];
193 var color = colorScheme[i%colorCount];
194 context.save();
195 context.fillStyle = color.toRGBString();
196 if (this.options.strokeColor)
197 context.strokeStyle = this.options.strokeColor.toRGBString();
198 else if (this.options.strokeColorTransform)
199 context.strokeStyle = color[this.options.strokeColorTransform]().toR GBString();
200
201 context.lineWidth = this.options.strokeWidth;
202 var forEachFunc = function(obj) {
203 if (obj.name == setName)
204 plotFunc(context, obj);
205 };
206
207 MochiKit.Iter.forEach(data, bind(forEachFunc, this));
208 context.restore();
209 }
210 };
211
212 PlotKit.CanvasRenderer.prototype._renderBarChart = function() {
213 var bind = MochiKit.Base.bind;
214
215 var drawRect = function(context, bar) {
216 var x = this.area.w * bar.x + this.area.x;
217 var y = this.area.h * bar.y + this.area.y;
218 var w = this.area.w * bar.w;
219 var h = this.area.h * bar.h;
220 if ((w < 1) || (h < 1))
221 return;
222 if (this.options.shouldFill)
223 context.fillRect(x, y, w, h);
224 if (this.options.shouldStroke)
225 context.strokeRect(x, y, w, h);
226 };
227 this._renderBarChartWrap(this.layout.bars, bind(drawRect, this));
228 };
229
230 PlotKit.CanvasRenderer.prototype._renderLineChart = function() {
231 var context = this.element.getContext("2d");
232 var colorCount = this.options.colorScheme.length;
233 var colorScheme = this.options.colorScheme;
234 var setNames = MochiKit.Base.keys(this.layout.datasets);
235 var setCount = setNames.length;
236 var bind = MochiKit.Base.bind;
237 var partial = MochiKit.Base.partial;
238
239 for (var i = 0; i < setCount; i++) {
240 var setName = setNames[i];
241 var color = colorScheme[i%colorCount];
242 var strokeX = this.options.strokeColorTransform;
243
244 // setup graphics context
245 context.save();
246 context.fillStyle = color.toRGBString();
247 if (this.options.strokeColor)
248 context.strokeStyle = this.options.strokeColor.toRGBString();
249 else if (this.options.strokeColorTransform)
250 context.strokeStyle = color[strokeX]().toRGBString();
251
252 context.lineWidth = this.options.strokeWidth;
253
254 // create paths
255 var makePath = function(ctx) {
256 ctx.beginPath();
257 ctx.moveTo(this.area.x, this.area.y + this.area.h);
258 var addPoint = function(ctx_, point) {
259 if (point.name == setName)
260 ctx_.lineTo(this.area.w * point.x + this.area.x,
261 this.area.h * point.y + this.area.y);
262 };
263 MochiKit.Iter.forEach(this.layout.points, partial(addPoint, ctx), th is);
264 ctx.lineTo(this.area.w + this.area.x,
265 this.area.h + this.area.y);
266 ctx.lineTo(this.area.x, this.area.y + this.area.h);
267 ctx.closePath();
268 };
269
270 if (this.options.shouldFill) {
271 bind(makePath, this)(context);
272 context.fill();
273 }
274 if (this.options.shouldStroke) {
275 bind(makePath, this)(context);
276 context.stroke();
277 }
278
279 context.restore();
280 }
281 };
282
283 PlotKit.CanvasRenderer.prototype._renderPieChart = function() {
284 var context = this.element.getContext("2d");
285 var colorCount = this.options.colorScheme.length;
286 var slices = this.layout.slices;
287
288 var centerx = this.area.x + this.area.w * 0.5;
289 var centery = this.area.y + this.area.h * 0.5;
290 var radius = Math.min(this.area.w * this.options.pieRadius,
291 this.area.h * this.options.pieRadius);
292
293 if (this.isIE) {
294 centerx = parseInt(centerx);
295 centery = parseInt(centery);
296 radius = parseInt(radius);
297 }
298
299
300 // NOTE NOTE!! Canvas Tag draws the circle clockwise from the y = 0, x = 1
301 // so we have to subtract 90 degrees to make it start at y = 1, x = 0
302
303 for (var i = 0; i < slices.length; i++) {
304 var color = this.options.colorScheme[i%colorCount];
305 context.save();
306 context.fillStyle = color.toRGBString();
307
308 var makePath = function() {
309 context.beginPath();
310 context.moveTo(centerx, centery);
311 context.arc(centerx, centery, radius,
312 slices[i].startAngle - Math.PI/2,
313 slices[i].endAngle - Math.PI/2,
314 false);
315 context.lineTo(centerx, centery);
316 context.closePath();
317 };
318
319 if (Math.abs(slices[i].startAngle - slices[i].endAngle) > 0.001) {
320 if (this.options.shouldFill) {
321 makePath();
322 context.fill();
323 }
324
325 if (this.options.shouldStroke) {
326 makePath();
327 context.lineWidth = this.options.strokeWidth;
328 if (this.options.strokeColor)
329 context.strokeStyle = this.options.strokeColor.toRGBString() ;
330 else if (this.options.strokeColorTransform)
331 context.strokeStyle = color[this.options.strokeColorTransfor m]().toRGBString();
332 context.stroke();
333 }
334 }
335 context.restore();
336 }
337 };
338
339 PlotKit.CanvasRenderer.prototype._renderBarAxis = function() {
340 this._renderAxis();
341 }
342
343 PlotKit.CanvasRenderer.prototype._renderLineAxis = function() {
344 this._renderAxis();
345 };
346
347
348 PlotKit.CanvasRenderer.prototype._renderAxis = function() {
349 if (!this.options.drawXAxis && !this.options.drawYAxis)
350 return;
351
352 var context = this.element.getContext("2d");
353
354 var labelStyle = {"style":
355 {"position": "absolute",
356 "fontSize": this.options.axisLabelFontSize + "px",
357 "zIndex": 10,
358 "color": this.options.axisLabelColor.toRGBString(),
359 "width": this.options.axisLabelWidth + "px",
360 "overflow": "hidden"
361 }
362 };
363
364 // axis lines
365 context.save();
366 context.strokeStyle = this.options.axisLineColor.toRGBString();
367 context.lineWidth = this.options.axisLineWidth;
368
369
370 if (this.options.drawYAxis) {
371 if (this.layout.yticks) {
372 var drawTick = function(tick) {
373 if (typeof(tick) == "function") return;
374 var x = this.area.x;
375 var y = this.area.y + tick[0] * this.area.h;
376 context.beginPath();
377 context.moveTo(x, y);
378 context.lineTo(x - this.options.axisTickSize, y);
379 context.closePath();
380 context.stroke();
381
382 var label = DIV(labelStyle, tick[1]);
383 label.style.top = (y - this.options.axisLabelFontSize) + "px";
384 label.style.left = (x - this.options.padding.left - this.options .axisTickSize) + "px";
385 label.style.textAlign = "right";
386 label.style.width = (this.options.padding.left - this.options.ax isTickSize * 2) + "px";
387 MochiKit.DOM.appendChildNodes(this.container, label);
388 this.ylabels.push(label);
389 };
390
391 MochiKit.Iter.forEach(this.layout.yticks, bind(drawTick, this));
392 }
393
394 context.beginPath();
395 context.moveTo(this.area.x, this.area.y);
396 context.lineTo(this.area.x, this.area.y + this.area.h);
397 context.closePath();
398 context.stroke();
399 }
400
401 if (this.options.drawXAxis) {
402 if (this.layout.xticks) {
403 var drawTick = function(tick) {
404 if (typeof(dataset) == "function") return;
405
406 var x = this.area.x + tick[0] * this.area.w;
407 var y = this.area.y + this.area.h;
408 context.beginPath();
409 context.moveTo(x, y);
410 context.lineTo(x, y + this.options.axisTickSize);
411 context.closePath();
412 context.stroke();
413
414 var label = DIV(labelStyle, tick[1]);
415 label.style.top = (y + this.options.axisTickSize) + "px";
416 label.style.left = (x - this.options.axisLabelWidth/2) + "px";
417 label.style.textAlign = "center";
418 label.style.width = this.options.axisLabelWidth + "px";
419 MochiKit.DOM.appendChildNodes(this.container, label);
420 this.xlabels.push(label);
421 };
422
423 MochiKit.Iter.forEach(this.layout.xticks, bind(drawTick, this));
424 }
425
426 context.beginPath();
427 context.moveTo(this.area.x, this.area.y + this.area.h);
428 context.lineTo(this.area.x + this.area.w, this.area.y + this.area.h);
429 context.closePath();
430 context.stroke();
431 }
432
433 context.restore();
434
435 };
436
437 PlotKit.CanvasRenderer.prototype._renderPieAxis = function() {
438 if (!this.options.drawXAxis)
439 return;
440
441 if (this.layout.xticks) {
442 // make a lookup dict for x->slice values
443 var lookup = new Array();
444 for (var i = 0; i < this.layout.slices.length; i++) {
445 lookup[this.layout.slices[i].xval] = this.layout.slices[ i];
446 }
447
448 var centerx = this.area.x + this.area.w * 0.5;
449 var centery = this.area.y + this.area.h * 0.5;
450 var radius = Math.min(this.area.w * this.options.pieRadius,
451 this.area.h * this.options.pieRadius);
452 var labelWidth = this.options.axisLabelWidth;
453
454 for (var i = 0; i < this.layout.xticks.length; i++) {
455 var slice = lookup[this.layout.xticks[i][0]];
456 if (MochiKit.Base.isUndefinedOrNull(slice))
457 continue;
458
459
460 var angle = (slice.startAngle + slice.endAngle)/2;
461 // normalize the angle
462 var normalisedAngle = angle;
463 if (normalisedAngle > Math.PI * 2)
464 normalisedAngle = normalisedAngle - Math.PI * 2;
465 else if (normalisedAngle < 0)
466 normalisedAngle = normalisedAngle + Math.PI * 2;
467
468 var labelx = centerx + Math.sin(normalisedAngle) * (radi us + 10);
469 var labely = centery - Math.cos(normalisedAngle) * (radius + 10) ;
470
471 var attrib = {"position": "absolute",
472 "zIndex": 11,
473 "width": labelWidth + "px",
474 "fontSize": this.options.axisLabelFontSize + "px",
475 "overflow": "hidden",
476 "color": this.options.axisLabe lColor.toHexString()
477 };
478
479 if (normalisedAngle <= Math.PI * 0.5) {
480 // text on top and align left
481 attrib["textAlign"] = "left";
482 attrib["verticalAlign"] = "top";
483 attrib["left"] = labelx + "px";
484 attrib["top"] = (labely - this.options.axisLabelFontSize) + "px";
485 }
486 else if ((normalisedAngle > Math.PI * 0.5) && (normalisedAngle < = Math.PI)) {
487 // text on bottom and align left
488 attrib["textAlign"] = "left";
489 attrib["verticalAlign"] = "bottom";
490 attrib["left"] = labelx + "px";
491 attrib["top"] = labely + "px";
492
493 }
494 else if ((normalisedAngle > Math.PI) && (normalisedAngle <= Math .PI*1.5)) {
495 // text on bottom and align right
496 attrib["textAlign"] = "right";
497 attrib["verticalAlign"] = "bottom";
498 attrib["left"] = (labelx - labelWidth) + "px";
499 attrib["top"] = labely + "px";
500 }
501 else {
502 // text on top and align right
503 attrib["textAlign"] = "right";
504 attrib["verticalAlign"] = "bottom";
505 attrib["left"] = (labelx - labelWidth) + "px";
506 attrib["top"] = (labely - this.options.axisLabelFontSize) + "px";
507 }
508
509 var label = DIV({'style': attrib}, this.layout.xticks[i] [1]);
510 this.xlabels.push(label);
511 MochiKit.DOM.appendChildNodes(this.container, label);
512 }
513
514 }
515 };
516
517 PlotKit.CanvasRenderer.prototype._renderBackground = function() {
518 var context = this.element.getContext("2d");
519 context.save();
520 context.fillStyle = this.options.backgroundColor.toRGBString();
521 context.fillRect(0, 0, this.width, this.height);
522 context.restore();
523 };
524
525 PlotKit.CanvasRenderer.prototype.clear = function() {
526 if (this.isIE) {
527 // VML takes a while to start up, so we just poll every this.IEDelay
528 try {
529 if (this.clearDelay) {
530 this.clearDelay.cancel();
531 this.clearDelay = null;
532 }
533 var context = this.element.getContext("2d");
534 }
535 catch (e) {
536 this.isFirstRender = false;
537 this.clearDelay = MochiKit.Async.wait(this.IEDelay);
538 this.clearDelay.addCallback(bind(this.clear, this));
539 return;
540 }
541 }
542
543 var context = this.element.getContext("2d");
544 context.clearRect(0, 0, this.width, this.height);
545
546 MochiKit.Iter.forEach(this.xlabels, MochiKit.DOM.removeElement);
547 MochiKit.Iter.forEach(this.ylabels, MochiKit.DOM.removeElement);
548 this.xlabels = new Array();
549 this.ylabels = new Array();
550 };
551
552 // ----------------------------------------------------------------
553 // Everything below here is experimental and undocumented.
554 // ----------------------------------------------------------------
555
556 PlotKit.CanvasRenderer.prototype._initialiseEvents = function() {
557 var connect = MochiKit.Signal.connect;
558 var bind = MochiKit.Base.bind;
559 //MochiKit.Signal.registerSignals(this, ['onmouseover', 'onclick', 'onmouseo ut', 'onmousemove']);
560 //connect(this.element, 'onmouseover', bind(this.onmouseover, this));
561 //connect(this.element, 'onmouseout', bind(this.onmouseout, this));
562 //connect(this.element, 'onmousemove', bind(this.onmousemove, this));
563 connect(this.element, 'onclick', bind(this.onclick, this));
564 };
565
566 PlotKit.CanvasRenderer.prototype._resolveObject = function(e) {
567 // does not work in firefox
568 //var x = (e.event().offsetX - this.area.x) / this.area.w;
569 //var y = (e.event().offsetY - this.area.y) / this.area.h;
570
571 var x = (e.mouse().page.x - PlotKit.Base.findPosX(this.element) - this.area. x) / this.area.w;
572 var y = (e.mouse().page.y - PlotKit.Base.findPosY(this.element) - this.area. y) / this.area.h;
573
574 //log(x, y);
575
576 var isHit = this.layout.hitTest(x, y);
577 if (isHit)
578 return isHit;
579 return null;
580 };
581
582 PlotKit.CanvasRenderer.prototype._createEventObject = function(layoutObj, e) {
583 if (layoutObj == null) {
584 return null;
585 }
586
587 e.chart = layoutObj
588 return e;
589 };
590
591
592 PlotKit.CanvasRenderer.prototype.onclick = function(e) {
593 var layoutObject = this._resolveObject(e);
594 var eventObject = this._createEventObject(layoutObject, e);
595 if (eventObject != null)
596 MochiKit.Signal.signal(this, "onclick", eventObject);
597 };
598
599 PlotKit.CanvasRenderer.prototype.onmouseover = function(e) {
600 var layoutObject = this._resolveObject(e);
601 var eventObject = this._createEventObject(layoutObject, e);
602 if (eventObject != null)
603 signal(this, "onmouseover", eventObject);
604 };
605
606 PlotKit.CanvasRenderer.prototype.onmouseout = function(e) {
607 var layoutObject = this._resolveObject(e);
608 var eventObject = this._createEventObject(layoutObject, e);
609 if (eventObject == null)
610 signal(this, "onmouseout", e);
611 else
612 signal(this, "onmouseout", eventObject);
613
614 };
615
616 PlotKit.CanvasRenderer.prototype.onmousemove = function(e) {
617 var layoutObject = this._resolveObject(e);
618 var eventObject = this._createEventObject(layoutObject, e);
619
620 if ((layoutObject == null) && (this.event_isinside == null)) {
621 // TODO: should we emit an event anyway?
622 return;
623 }
624
625 if ((layoutObject != null) && (this.event_isinside == null))
626 signal(this, "onmouseover", eventObject);
627
628 if ((layoutObject == null) && (this.event_isinside != null))
629 signal(this, "onmouseout", eventObject);
630
631 if ((layoutObject != null) && (this.event_isinside != null))
632 signal(this, "onmousemove", eventObject);
633
634 this.event_isinside = layoutObject;
635 //log("move", x, y);
636 };
637
638 PlotKit.CanvasRenderer.isSupported = function(canvasName) {
639 var canvas = null;
640 try {
641 if (MochiKit.Base.isUndefinedOrNull(canvasName))
642 canvas = MochiKit.DOM.CANVAS({});
643 else
644 canvas = MochiKit.DOM.getElement(canvasName);
645 var context = canvas.getContext("2d");
646 }
647 catch (e) {
648 var ie = navigator.appVersion.match(/MSIE (\d\.\d)/);
649 var opera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
650 if ((!ie) || (ie[1] < 6) || (opera))
651 return false;
652 return true;
653 }
654 return true;
655 };
656
657 // Namespace Iniitialisation
658
659 PlotKit.Canvas = {}
660 PlotKit.Canvas.CanvasRenderer = PlotKit.CanvasRenderer;
661
662 PlotKit.Canvas.EXPORT = [
663 "CanvasRenderer"
664 ];
665
666 PlotKit.Canvas.EXPORT_OK = [
667 "CanvasRenderer"
668 ];
669
670 PlotKit.Canvas.__new__ = function() {
671 var m = MochiKit.Base;
672
673 m.nameFunctions(this);
674
675 this.EXPORT_TAGS = {
676 ":common": this.EXPORT,
677 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
678 };
679 };
680
681 PlotKit.Canvas.__new__();
682 MochiKit.Base._exportSymbols(this, PlotKit.Canvas);
OLDNEW
« no previous file with comments | « static/js/plotkit/Base.js ('k') | static/js/plotkit/LICENSE » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698