| OLD | NEW | 
|---|
| (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); | 
| OLD | NEW | 
|---|