[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/graphics-canvas/ -> graphics-canvas.js (source)

   1  /*
   2  YUI 3.17.2 (build 9c3c78e)
   3  Copyright 2014 Yahoo! Inc. All rights reserved.
   4  Licensed under the BSD License.
   5  http://yuilibrary.com/license/
   6  */
   7  
   8  YUI.add('graphics-canvas', function (Y, NAME) {
   9  
  10  var IMPLEMENTATION = "canvas",
  11      SHAPE = "shape",
  12      SPLITPATHPATTERN = /[a-z][^a-z]*/ig,
  13      SPLITARGSPATTERN = /[\-]?[0-9]*[0-9|\.][0-9]*/g,
  14      DOCUMENT = Y.config.doc,
  15      Y_LANG = Y.Lang,
  16      AttributeLite = Y.AttributeLite,
  17      CanvasShape,
  18      CanvasPath,
  19      CanvasRect,
  20      CanvasEllipse,
  21      CanvasCircle,
  22      CanvasPieSlice,
  23      Y_DOM = Y.DOM,
  24      Y_Color = Y.Color,
  25      PARSE_INT = parseInt,
  26      PARSE_FLOAT = parseFloat,
  27      IS_NUMBER = Y_LANG.isNumber,
  28      RE = RegExp,
  29      TORGB = Y_Color.toRGB,
  30      TOHEX = Y_Color.toHex,
  31      _getClassName = Y.ClassNameManager.getClassName;
  32  
  33  /**
  34   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Drawing.html">`Drawing`</a> class.
  35   * `CanvasDrawing` is not intended to be used directly. Instead, use the <a href="Drawing.html">`Drawing`</a> class.
  36   * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
  37   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Drawing.html">`Drawing`</a>
  38   * class will point to the `CanvasDrawing` class.
  39   *
  40   * @module graphics
  41   * @class CanvasDrawing
  42   * @constructor
  43   */
  44  function CanvasDrawing()
  45  {
  46  }
  47  
  48  CanvasDrawing.prototype = {
  49      /**
  50       * Maps path to methods
  51       *
  52       * @property _pathSymbolToMethod
  53       * @type Object
  54       * @private
  55       */
  56      _pathSymbolToMethod: {
  57          M: "moveTo",
  58          m: "relativeMoveTo",
  59          L: "lineTo",
  60          l: "relativeLineTo",
  61          C: "curveTo",
  62          c: "relativeCurveTo",
  63          Q: "quadraticCurveTo",
  64          q: "relativeQuadraticCurveTo",
  65          z: "closePath",
  66          Z: "closePath"
  67      },
  68  
  69      /**
  70       * Current x position of the drawing.
  71       *
  72       * @property _currentX
  73       * @type Number
  74       * @private
  75       */
  76      _currentX: 0,
  77  
  78      /**
  79       * Current y position of the drqwing.
  80       *
  81       * @property _currentY
  82       * @type Number
  83       * @private
  84       */
  85      _currentY: 0,
  86  
  87      /**
  88       * Parses hex color string and alpha value to rgba
  89       *
  90       * @method _toRGBA
  91       * @param {Object} val Color value to parse. Can be hex string, rgb or name.
  92       * @param {Number} alpha Numeric value between 0 and 1 representing the alpha level.
  93       * @private
  94       */
  95      _toRGBA: function(val, alpha) {
  96          alpha = (alpha !== undefined) ? alpha : 1;
  97          if (!Y_Color.re_RGB.test(val)) {
  98              val = TOHEX(val);
  99          }
 100  
 101          if(Y_Color.re_hex.exec(val)) {
 102              val = 'rgba(' + [
 103                  PARSE_INT(RE.$1, 16),
 104                  PARSE_INT(RE.$2, 16),
 105                  PARSE_INT(RE.$3, 16)
 106              ].join(',') + ',' + alpha + ')';
 107          }
 108          return val;
 109      },
 110  
 111      /**
 112       * Converts color to rgb format
 113       *
 114       * @method _toRGB
 115       * @param val Color value to convert.
 116       * @private
 117       */
 118      _toRGB: function(val) {
 119          return TORGB(val);
 120      },
 121  
 122      /**
 123       * Sets the size of the graphics object.
 124       *
 125       * @method setSize
 126       * @param w {Number} width to set for the instance.
 127       * @param h {Number} height to set for the instance.
 128       * @private
 129       */
 130      setSize: function(w, h) {
 131          if(this.get("autoSize"))
 132          {
 133              if(w > this.node.getAttribute("width"))
 134              {
 135                  this.node.style.width = w + "px";
 136                  this.node.setAttribute("width", w);
 137              }
 138              if(h > this.node.getAttribute("height"))
 139              {
 140                  this.node.style.height = h + "px";
 141                  this.node.setAttribute("height", h);
 142              }
 143          }
 144      },
 145  
 146      /**
 147       * Tracks coordinates. Used to calculate the start point of dashed lines.
 148       *
 149       * @method _updateCoords
 150       * @param {Number} x x-coordinate
 151       * @param {Number} y y-coordinate
 152       * @private
 153       */
 154      _updateCoords: function(x, y)
 155      {
 156          this._xcoords.push(x);
 157          this._ycoords.push(y);
 158          this._currentX = x;
 159          this._currentY = y;
 160      },
 161  
 162      /**
 163       * Clears the coordinate arrays. Called at the end of a drawing operation.
 164       *
 165       * @method _clearAndUpdateCoords
 166       * @private
 167       */
 168      _clearAndUpdateCoords: function()
 169      {
 170          var x = this._xcoords.pop() || 0,
 171              y = this._ycoords.pop() || 0;
 172          this._updateCoords(x, y);
 173      },
 174  
 175      /**
 176       * Moves the shape's dom node.
 177       *
 178       * @method _updateNodePosition
 179       * @private
 180       */
 181      _updateNodePosition: function()
 182      {
 183          var node = this.get("node"),
 184              x = this.get("x"),
 185              y = this.get("y");
 186          node.style.position = "absolute";
 187          node.style.left = (x + this._left) + "px";
 188          node.style.top = (y + this._top) + "px";
 189      },
 190  
 191      /**
 192       * Queues up a method to be executed when a shape redraws.
 193       *
 194       * @method _updateDrawingQueue
 195       * @param {Array} val An array containing data that can be parsed into a method and arguments. The value at zero-index
 196       * of the array is a string reference of the drawing method that will be called. All subsequent indices are argument for
 197       * that method. For example, `lineTo(10, 100)` would be structured as:
 198       * `["lineTo", 10, 100]`.
 199       * @private
 200       */
 201      _updateDrawingQueue: function(val)
 202      {
 203          this._methods.push(val);
 204      },
 205  
 206      /**
 207       * Draws a line segment from the current drawing position to the specified x and y coordinates.
 208       *
 209       * @method lineTo
 210       * @param {Number} point1 x-coordinate for the end point.
 211       * @param {Number} point2 y-coordinate for the end point.
 212       * @chainable
 213       */
 214      lineTo: function()
 215      {
 216          this._lineTo.apply(this, [Y.Array(arguments), false]);
 217          return this;
 218      },
 219  
 220      /**
 221       * Draws a line segment from the current drawing position to the relative x and y coordinates.
 222       *
 223       * @method relativeLineTo
 224       * @param {Number} point1 x-coordinate for the end point.
 225       * @param {Number} point2 y-coordinate for the end point.
 226       * @chainable
 227       */
 228      relativeLineTo: function()
 229      {
 230          this._lineTo.apply(this, [Y.Array(arguments), true]);
 231          return this;
 232      },
 233  
 234      /**
 235       * Implements lineTo methods.
 236       *
 237       * @method _lineTo
 238       * @param {Array} args The arguments to be used.
 239       * @param {Boolean} relative Indicates whether or not to use relative coordinates.
 240       * @private
 241       */
 242      _lineTo: function(args, relative)
 243      {
 244          var point1 = args[0],
 245              i,
 246              len,
 247              x,
 248              y,
 249              wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
 250              relativeX = relative ? parseFloat(this._currentX) : 0,
 251              relativeY = relative ? parseFloat(this._currentY) : 0;
 252          if(!this._lineToMethods)
 253          {
 254              this._lineToMethods = [];
 255          }
 256          len = args.length - 1;
 257          if (typeof point1 === 'string' || typeof point1 === 'number') {
 258              for (i = 0; i < len; i = i + 2) {
 259                  x = parseFloat(args[i]);
 260                  y = parseFloat(args[i + 1]);
 261                  x = x + relativeX;
 262                  y = y + relativeY;
 263                  this._updateDrawingQueue(["lineTo", x, y]);
 264                  this._trackSize(x - wt, y - wt);
 265                  this._trackSize(x + wt, y + wt);
 266                  this._updateCoords(x, y);
 267              }
 268          }
 269          else
 270          {
 271              for (i = 0; i < len; i = i + 1)
 272              {
 273                  x = parseFloat(args[i][0]);
 274                  y = parseFloat(args[i][1]);
 275                  this._updateDrawingQueue(["lineTo", x, y]);
 276                  this._lineToMethods[this._lineToMethods.length] = this._methods[this._methods.length - 1];
 277                  this._trackSize(x - wt, y - wt);
 278                  this._trackSize(x + wt, y + wt);
 279                  this._updateCoords(x, y);
 280              }
 281          }
 282          this._drawingComplete = false;
 283          return this;
 284      },
 285  
 286      /**
 287       * Moves the current drawing position to specified x and y coordinates.
 288       *
 289       * @method moveTo
 290       * @param {Number} x x-coordinate for the end point.
 291       * @param {Number} y y-coordinate for the end point.
 292       * @chainable
 293       */
 294      moveTo: function()
 295      {
 296          this._moveTo.apply(this, [Y.Array(arguments), false]);
 297          return this;
 298      },
 299  
 300      /**
 301       * Moves the current drawing position relative to specified x and y coordinates.
 302       *
 303       * @method relativeMoveTo
 304       * @param {Number} x x-coordinate for the end point.
 305       * @param {Number} y y-coordinate for the end point.
 306       * @chainable
 307       */
 308      relativeMoveTo: function()
 309      {
 310          this._moveTo.apply(this, [Y.Array(arguments), true]);
 311          return this;
 312      },
 313  
 314      /**
 315       * Implements moveTo methods.
 316       *
 317       * @method _moveTo
 318       * @param {Array} args The arguments to be used.
 319       * @param {Boolean} relative Indicates whether or not to use relative coordinates.
 320       * @private
 321       */
 322      _moveTo: function(args, relative) {
 323          var wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
 324              relativeX = relative ? parseFloat(this._currentX) : 0,
 325              relativeY = relative ? parseFloat(this._currentY) : 0,
 326              x = parseFloat(args[0]) + relativeX,
 327              y = parseFloat(args[1]) + relativeY;
 328          this._updateDrawingQueue(["moveTo", x, y]);
 329          this._trackSize(x - wt, y - wt);
 330          this._trackSize(x + wt, y + wt);
 331          this._updateCoords(x, y);
 332          this._drawingComplete = false;
 333          return this;
 334      },
 335  
 336      /**
 337       * Draws a bezier curve.
 338       *
 339       * @method curveTo
 340       * @param {Number} cp1x x-coordinate for the first control point.
 341       * @param {Number} cp1y y-coordinate for the first control point.
 342       * @param {Number} cp2x x-coordinate for the second control point.
 343       * @param {Number} cp2y y-coordinate for the second control point.
 344       * @param {Number} x x-coordinate for the end point.
 345       * @param {Number} y y-coordinate for the end point.
 346       * @chainable
 347       */
 348      curveTo: function() {
 349          this._curveTo.apply(this, [Y.Array(arguments), false]);
 350          return this;
 351      },
 352  
 353      /**
 354       * Draws a bezier curve relative to the current coordinates.
 355       *
 356       * @method relativeCurveTo
 357       * @param {Number} cp1x x-coordinate for the first control point.
 358       * @param {Number} cp1y y-coordinate for the first control point.
 359       * @param {Number} cp2x x-coordinate for the second control point.
 360       * @param {Number} cp2y y-coordinate for the second control point.
 361       * @param {Number} x x-coordinate for the end point.
 362       * @param {Number} y y-coordinate for the end point.
 363       * @chainable
 364       */
 365      relativeCurveTo: function() {
 366          this._curveTo.apply(this, [Y.Array(arguments), true]);
 367          return this;
 368      },
 369  
 370      /**
 371       * Implements curveTo methods.
 372       *
 373       * @method _curveTo
 374       * @param {Array} args The arguments to be used.
 375       * @param {Boolean} relative Indicates whether or not to use relative coordinates.
 376       * @private
 377       */
 378      _curveTo: function(args, relative) {
 379          var w,
 380              h,
 381              cp1x,
 382              cp1y,
 383              cp2x,
 384              cp2y,
 385              x,
 386              y,
 387              pts,
 388              right,
 389              left,
 390              bottom,
 391              top,
 392              i,
 393              len,
 394              relativeX = relative ? parseFloat(this._currentX) : 0,
 395              relativeY = relative ? parseFloat(this._currentY) : 0;
 396          len = args.length - 5;
 397          for(i = 0; i < len; i = i + 6)
 398          {
 399              cp1x = parseFloat(args[i]) + relativeX;
 400              cp1y = parseFloat(args[i + 1]) + relativeY;
 401              cp2x = parseFloat(args[i + 2]) + relativeX;
 402              cp2y = parseFloat(args[i + 3]) + relativeY;
 403              x = parseFloat(args[i + 4]) + relativeX;
 404              y = parseFloat(args[i + 5]) + relativeY;
 405              this._updateDrawingQueue(["bezierCurveTo", cp1x, cp1y, cp2x, cp2y, x, y]);
 406              this._drawingComplete = false;
 407              right = Math.max(x, Math.max(cp1x, cp2x));
 408              bottom = Math.max(y, Math.max(cp1y, cp2y));
 409              left = Math.min(x, Math.min(cp1x, cp2x));
 410              top = Math.min(y, Math.min(cp1y, cp2y));
 411              w = Math.abs(right - left);
 412              h = Math.abs(bottom - top);
 413              pts = [[this._currentX, this._currentY] , [cp1x, cp1y], [cp2x, cp2y], [x, y]];
 414              this._setCurveBoundingBox(pts, w, h);
 415              this._currentX = x;
 416              this._currentY = y;
 417          }
 418      },
 419  
 420      /**
 421       * Draws a quadratic bezier curve.
 422       *
 423       * @method quadraticCurveTo
 424       * @param {Number} cpx x-coordinate for the control point.
 425       * @param {Number} cpy y-coordinate for the control point.
 426       * @param {Number} x x-coordinate for the end point.
 427       * @param {Number} y y-coordinate for the end point.
 428       * @chainable
 429       */
 430      quadraticCurveTo: function() {
 431          this._quadraticCurveTo.apply(this, [Y.Array(arguments), false]);
 432          return this;
 433      },
 434  
 435      /**
 436       * Draws a quadratic bezier curve relative to the current position.
 437       *
 438       * @method relativeQuadraticCurveTo
 439       * @param {Number} cpx x-coordinate for the control point.
 440       * @param {Number} cpy y-coordinate for the control point.
 441       * @param {Number} x x-coordinate for the end point.
 442       * @param {Number} y y-coordinate for the end point.
 443       * @chainable
 444       */
 445      relativeQuadraticCurveTo: function() {
 446          this._quadraticCurveTo.apply(this, [Y.Array(arguments), true]);
 447          return this;
 448      },
 449  
 450      /**
 451       * Implements quadraticCurveTo methods.
 452       *
 453       * @method _quadraticCurveTo
 454       * @param {Array} args The arguments to be used.
 455       * @param {Boolean} relative Indicates whether or not to use relative coordinates.
 456       * @private
 457       */
 458      _quadraticCurveTo: function(args, relative) {
 459          var cpx,
 460              cpy,
 461              x,
 462              y,
 463              w,
 464              h,
 465              pts,
 466              right,
 467              left,
 468              bottom,
 469              top,
 470              i,
 471              len = args.length - 3,
 472              relativeX = relative ? parseFloat(this._currentX) : 0,
 473              relativeY = relative ? parseFloat(this._currentY) : 0;
 474          for(i = 0; i < len; i = i + 4)
 475          {
 476              cpx = parseFloat(args[i]) + relativeX;
 477              cpy = parseFloat(args[i + 1]) + relativeY;
 478              x = parseFloat(args[i + 2]) + relativeX;
 479              y = parseFloat(args[i + 3]) + relativeY;
 480              this._drawingComplete = false;
 481              right = Math.max(x, cpx);
 482              bottom = Math.max(y, cpy);
 483              left = Math.min(x, cpx);
 484              top = Math.min(y, cpy);
 485              w = Math.abs(right - left);
 486              h = Math.abs(bottom - top);
 487              pts = [[this._currentX, this._currentY] , [cpx, cpy], [x, y]];
 488              this._setCurveBoundingBox(pts, w, h);
 489              this._updateDrawingQueue(["quadraticCurveTo", cpx, cpy, x, y]);
 490              this._updateCoords(x, y);
 491          }
 492          return this;
 493      },
 494  
 495      /**
 496       * Draws a circle. Used internally by `CanvasCircle` class.
 497       *
 498       * @method drawCircle
 499       * @param {Number} x y-coordinate
 500       * @param {Number} y x-coordinate
 501       * @param {Number} r radius
 502       * @chainable
 503       * @protected
 504       */
 505      drawCircle: function(x, y, radius) {
 506          var startAngle = 0,
 507              endAngle = 2 * Math.PI,
 508              wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
 509              circum = radius * 2;
 510              circum += wt;
 511          this._drawingComplete = false;
 512          this._trackSize(x + circum, y + circum);
 513          this._trackSize(x - wt, y - wt);
 514          this._updateCoords(x, y);
 515          this._updateDrawingQueue(["arc", x + radius, y + radius, radius, startAngle, endAngle, false]);
 516          return this;
 517      },
 518  
 519      /**
 520       * Draws a diamond.
 521       *
 522       * @method drawDiamond
 523       * @param {Number} x y-coordinate
 524       * @param {Number} y x-coordinate
 525       * @param {Number} width width
 526       * @param {Number} height height
 527       * @chainable
 528       * @protected
 529       */
 530      drawDiamond: function(x, y, width, height)
 531      {
 532          var midWidth = width * 0.5,
 533              midHeight = height * 0.5;
 534          this.moveTo(x + midWidth, y);
 535          this.lineTo(x + width, y + midHeight);
 536          this.lineTo(x + midWidth, y + height);
 537          this.lineTo(x, y + midHeight);
 538          this.lineTo(x + midWidth, y);
 539          return this;
 540      },
 541  
 542      /**
 543       * Draws an ellipse. Used internally by `CanvasEllipse` class.
 544       *
 545       * @method drawEllipse
 546       * @param {Number} x x-coordinate
 547       * @param {Number} y y-coordinate
 548       * @param {Number} w width
 549       * @param {Number} h height
 550       * @chainable
 551       * @protected
 552       */
 553      drawEllipse: function(x, y, w, h) {
 554          var l = 8,
 555              theta = -(45/180) * Math.PI,
 556              angle = 0,
 557              angleMid,
 558              radius = w/2,
 559              yRadius = h/2,
 560              i,
 561              centerX = x + radius,
 562              centerY = y + yRadius,
 563              ax, ay, bx, by, cx, cy,
 564              wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0;
 565  
 566          ax = centerX + Math.cos(0) * radius;
 567          ay = centerY + Math.sin(0) * yRadius;
 568          this.moveTo(ax, ay);
 569          for(i = 0; i < l; i++)
 570          {
 571              angle += theta;
 572              angleMid = angle - (theta / 2);
 573              bx = centerX + Math.cos(angle) * radius;
 574              by = centerY + Math.sin(angle) * yRadius;
 575              cx = centerX + Math.cos(angleMid) * (radius / Math.cos(theta / 2));
 576              cy = centerY + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2));
 577              this._updateDrawingQueue(["quadraticCurveTo", cx, cy, bx, by]);
 578          }
 579          this._trackSize(x + w + wt, y + h + wt);
 580          this._trackSize(x - wt, y - wt);
 581          this._updateCoords(x, y);
 582          return this;
 583      },
 584  
 585      /**
 586       * Draws a rectangle.
 587       *
 588       * @method drawRect
 589       * @param {Number} x x-coordinate
 590       * @param {Number} y y-coordinate
 591       * @param {Number} w width
 592       * @param {Number} h height
 593       * @chainable
 594       */
 595      drawRect: function(x, y, w, h) {
 596          this._drawingComplete = false;
 597          this.moveTo(x, y);
 598          this.lineTo(x + w, y);
 599          this.lineTo(x + w, y + h);
 600          this.lineTo(x, y + h);
 601          this.lineTo(x, y);
 602          return this;
 603      },
 604  
 605      /**
 606       * Draws a rectangle with rounded corners.
 607       *
 608       * @method drawRect
 609       * @param {Number} x x-coordinate
 610       * @param {Number} y y-coordinate
 611       * @param {Number} w width
 612       * @param {Number} h height
 613       * @param {Number} ew width of the ellipse used to draw the rounded corners
 614       * @param {Number} eh height of the ellipse used to draw the rounded corners
 615       * @chainable
 616       */
 617      drawRoundRect: function(x, y, w, h, ew, eh) {
 618          this._drawingComplete = false;
 619          this.moveTo( x, y + eh);
 620          this.lineTo(x, y + h - eh);
 621          this.quadraticCurveTo(x, y + h, x + ew, y + h);
 622          this.lineTo(x + w - ew, y + h);
 623          this.quadraticCurveTo(x + w, y + h, x + w, y + h - eh);
 624          this.lineTo(x + w, y + eh);
 625          this.quadraticCurveTo(x + w, y, x + w - ew, y);
 626          this.lineTo(x + ew, y);
 627          this.quadraticCurveTo(x, y, x, y + eh);
 628          return this;
 629      },
 630  
 631      /**
 632       * Draws a wedge.
 633       *
 634       * @method drawWedge
 635       * @param {Number} x x-coordinate of the wedge's center point
 636       * @param {Number} y y-coordinate of the wedge's center point
 637       * @param {Number} startAngle starting angle in degrees
 638       * @param {Number} arc sweep of the wedge. Negative values draw clockwise.
 639       * @param {Number} radius radius of wedge. If [optional] yRadius is defined, then radius is the x radius.
 640       * @param {Number} yRadius [optional] y radius for wedge.
 641       * @chainable
 642       * @private
 643       */
 644      drawWedge: function(x, y, startAngle, arc, radius, yRadius)
 645      {
 646          var wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
 647              segs,
 648              segAngle,
 649              theta,
 650              angle,
 651              angleMid,
 652              ax,
 653              ay,
 654              bx,
 655              by,
 656              cx,
 657              cy,
 658              i = 0;
 659          yRadius = yRadius || radius;
 660  
 661          this._drawingComplete = false;
 662          // move to x,y position
 663          this._updateDrawingQueue(["moveTo", x, y]);
 664  
 665          yRadius = yRadius || radius;
 666  
 667          // limit sweep to reasonable numbers
 668          if(Math.abs(arc) > 360)
 669          {
 670              arc = 360;
 671          }
 672  
 673          // First we calculate how many segments are needed
 674          // for a smooth arc.
 675          segs = Math.ceil(Math.abs(arc) / 45);
 676  
 677          // Now calculate the sweep of each segment.
 678          segAngle = arc / segs;
 679  
 680          // The math requires radians rather than degrees. To convert from degrees
 681          // use the formula (degrees/180)*Math.PI to get radians.
 682          theta = -(segAngle / 180) * Math.PI;
 683  
 684          // convert angle startAngle to radians
 685          angle = (startAngle / 180) * Math.PI;
 686  
 687          // draw the curve in segments no larger than 45 degrees.
 688          if(segs > 0)
 689          {
 690              // draw a line from the center to the start of the curve
 691              ax = x + Math.cos(startAngle / 180 * Math.PI) * radius;
 692              ay = y + Math.sin(startAngle / 180 * Math.PI) * yRadius;
 693              this.lineTo(ax, ay);
 694              // Loop for drawing curve segments
 695              for(i = 0; i < segs; ++i)
 696              {
 697                  angle += theta;
 698                  angleMid = angle - (theta / 2);
 699                  bx = x + Math.cos(angle) * radius;
 700                  by = y + Math.sin(angle) * yRadius;
 701                  cx = x + Math.cos(angleMid) * (radius / Math.cos(theta / 2));
 702                  cy = y + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2));
 703                  this._updateDrawingQueue(["quadraticCurveTo", cx, cy, bx, by]);
 704              }
 705              // close the wedge by drawing a line to the center
 706              this._updateDrawingQueue(["lineTo", x, y]);
 707          }
 708          this._trackSize(-wt , -wt);
 709          this._trackSize((radius * 2) + wt, (radius * 2) + wt);
 710          return this;
 711      },
 712  
 713      /**
 714       * Completes a drawing operation.
 715       *
 716       * @method end
 717       * @chainable
 718       */
 719      end: function() {
 720          this._closePath();
 721          return this;
 722      },
 723  
 724      /**
 725       * Ends a fill and stroke
 726       *
 727       * @method closePath
 728       * @chainable
 729       */
 730      closePath: function()
 731      {
 732          this._updateDrawingQueue(["closePath"]);
 733          this._updateDrawingQueue(["beginPath"]);
 734          return this;
 735      },
 736  
 737      /**
 738       * Clears the graphics object.
 739       *
 740       * @method clear
 741       * @chainable
 742       */
 743      clear: function() {
 744          this._initProps();
 745          if(this.node)
 746          {
 747              this._context.clearRect(0, 0, this.node.width, this.node.height);
 748          }
 749          return this;
 750      },
 751  
 752  
 753      /**
 754       * Returns a linear gradient fill
 755       *
 756       * @method _getLinearGradient
 757       * @return CanvasGradient
 758       * @private
 759       */
 760      _getLinearGradient: function() {
 761          var isNumber = Y.Lang.isNumber,
 762              fill = this.get("fill"),
 763              stops = fill.stops,
 764              opacity,
 765              color,
 766              stop,
 767              i,
 768              len = stops.length,
 769              gradient,
 770              x = 0,
 771              y = 0,
 772              w = this.get("width"),
 773              h = this.get("height"),
 774              r = fill.rotation || 0,
 775              x1, x2, y1, y2,
 776              cx = x + w/2,
 777              cy = y + h/2,
 778              offset,
 779              radCon = Math.PI/180,
 780              tanRadians = parseFloat(parseFloat(Math.tan(r * radCon)).toFixed(8));
 781          if(Math.abs(tanRadians) * w/2 >= h/2)
 782          {
 783              if(r < 180)
 784              {
 785                  y1 = y;
 786                  y2 = y + h;
 787              }
 788              else
 789              {
 790                  y1 = y + h;
 791                  y2 = y;
 792              }
 793              x1 = cx - ((cy - y1)/tanRadians);
 794              x2 = cx - ((cy - y2)/tanRadians);
 795          }
 796          else
 797          {
 798              if(r > 90 && r < 270)
 799              {
 800                  x1 = x + w;
 801                  x2 = x;
 802              }
 803              else
 804              {
 805                  x1 = x;
 806                  x2 = x + w;
 807              }
 808              y1 = ((tanRadians * (cx - x1)) - cy) * -1;
 809              y2 = ((tanRadians * (cx - x2)) - cy) * -1;
 810          }
 811          gradient = this._context.createLinearGradient(x1, y1, x2, y2);
 812          for(i = 0; i < len; ++i)
 813          {
 814              stop = stops[i];
 815              opacity = stop.opacity;
 816              color = stop.color;
 817              offset = stop.offset;
 818              if(isNumber(opacity))
 819              {
 820                  opacity = Math.max(0, Math.min(1, opacity));
 821                  color = this._toRGBA(color, opacity);
 822              }
 823              else
 824              {
 825                  color = TORGB(color);
 826              }
 827              offset = stop.offset || i/(len - 1);
 828              gradient.addColorStop(offset, color);
 829          }
 830          return gradient;
 831      },
 832  
 833      /**
 834       * Returns a radial gradient fill
 835       *
 836       * @method _getRadialGradient
 837       * @return CanvasGradient
 838       * @private
 839       */
 840      _getRadialGradient: function() {
 841          var isNumber = Y.Lang.isNumber,
 842              fill = this.get("fill"),
 843              r = fill.r,
 844              fx = fill.fx,
 845              fy = fill.fy,
 846              stops = fill.stops,
 847              opacity,
 848              color,
 849              stop,
 850              i,
 851              len = stops.length,
 852              gradient,
 853              x = 0,
 854              y = 0,
 855              w = this.get("width"),
 856              h = this.get("height"),
 857              x1, x2, y1, y2, r2,
 858              xc, yc, xn, yn, d,
 859              offset,
 860              ratio,
 861              stopMultiplier;
 862          xc = x + w/2;
 863          yc = y + h/2;
 864          x1 = w * fx;
 865          y1 = h * fy;
 866          x2 = x + w/2;
 867          y2 = y + h/2;
 868          r2 = w * r;
 869          d = Math.sqrt( Math.pow(Math.abs(xc - x1), 2) + Math.pow(Math.abs(yc - y1), 2) );
 870          if(d >= r2)
 871          {
 872              ratio = d/r2;
 873              //hack. gradient won't show if it is exactly on the edge of the arc
 874              if(ratio === 1)
 875              {
 876                  ratio = 1.01;
 877              }
 878              xn = (x1 - xc)/ratio;
 879              yn = (y1 - yc)/ratio;
 880              xn = xn > 0 ? Math.floor(xn) : Math.ceil(xn);
 881              yn = yn > 0 ? Math.floor(yn) : Math.ceil(yn);
 882              x1 = xc + xn;
 883              y1 = yc + yn;
 884          }
 885  
 886          //If the gradient radius is greater than the circle's, adjusting the radius stretches the gradient properly.
 887          //If the gradient radius is less than the circle's, adjusting the radius of the gradient will not work.
 888          //Instead, adjust the color stops to reflect the smaller radius.
 889          if(r >= 0.5)
 890          {
 891              gradient = this._context.createRadialGradient(x1, y1, r, x2, y2, r * w);
 892              stopMultiplier = 1;
 893          }
 894          else
 895          {
 896              gradient = this._context.createRadialGradient(x1, y1, r, x2, y2, w/2);
 897              stopMultiplier = r * 2;
 898          }
 899          for(i = 0; i < len; ++i)
 900          {
 901              stop = stops[i];
 902              opacity = stop.opacity;
 903              color = stop.color;
 904              offset = stop.offset;
 905              if(isNumber(opacity))
 906              {
 907                  opacity = Math.max(0, Math.min(1, opacity));
 908                  color = this._toRGBA(color, opacity);
 909              }
 910              else
 911              {
 912                  color = TORGB(color);
 913              }
 914              offset = stop.offset || i/(len - 1);
 915              offset *= stopMultiplier;
 916              if(offset <= 1)
 917              {
 918                  gradient.addColorStop(offset, color);
 919              }
 920          }
 921          return gradient;
 922      },
 923  
 924  
 925      /**
 926       * Clears all values
 927       *
 928       * @method _initProps
 929       * @private
 930       */
 931      _initProps: function() {
 932          this._methods = [];
 933          this._lineToMethods = [];
 934          this._xcoords = [0];
 935          this._ycoords = [0];
 936          this._width = 0;
 937          this._height = 0;
 938          this._left = 0;
 939          this._top = 0;
 940          this._right = 0;
 941          this._bottom = 0;
 942          this._currentX = 0;
 943          this._currentY = 0;
 944      },
 945  
 946      /**
 947       * Indicates a drawing has completed.
 948       *
 949       * @property _drawingComplete
 950       * @type Boolean
 951       * @private
 952       */
 953      _drawingComplete: false,
 954  
 955      /**
 956       * Creates canvas element
 957       *
 958       * @method _createGraphic
 959       * @return HTMLCanvasElement
 960       * @private
 961       */
 962      _createGraphic: function() {
 963          var graphic = Y.config.doc.createElement('canvas');
 964          return graphic;
 965      },
 966  
 967      /**
 968       * Returns the points on a curve
 969       *
 970       * @method getBezierData
 971       * @param Array points Array containing the begin, end and control points of a curve.
 972       * @param Number t The value for incrementing the next set of points.
 973       * @return Array
 974       * @private
 975       */
 976      getBezierData: function(points, t) {
 977          var n = points.length,
 978              tmp = [],
 979              i,
 980              j;
 981  
 982          for (i = 0; i < n; ++i){
 983              tmp[i] = [points[i][0], points[i][1]]; // save input
 984          }
 985  
 986          for (j = 1; j < n; ++j) {
 987              for (i = 0; i < n - j; ++i) {
 988                  tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
 989                  tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
 990              }
 991          }
 992          return [ tmp[0][0], tmp[0][1] ];
 993      },
 994  
 995      /**
 996       * Calculates the bounding box for a curve
 997       *
 998       * @method _setCurveBoundingBox
 999       * @param Array pts Array containing points for start, end and control points of a curve.
1000       * @param Number w Width used to calculate the number of points to describe the curve.
1001       * @param Number h Height used to calculate the number of points to describe the curve.
1002       * @private
1003       */
1004      _setCurveBoundingBox: function(pts, w, h)
1005      {
1006          var i = 0,
1007              left = this._currentX,
1008              right = left,
1009              top = this._currentY,
1010              bottom = top,
1011              len = Math.round(Math.sqrt((w * w) + (h * h))),
1012              t = 1/len,
1013              wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
1014              xy;
1015          for(i = 0; i < len; ++i)
1016          {
1017              xy = this.getBezierData(pts, t * i);
1018              left = isNaN(left) ? xy[0] : Math.min(xy[0], left);
1019              right = isNaN(right) ? xy[0] : Math.max(xy[0], right);
1020              top = isNaN(top) ? xy[1] : Math.min(xy[1], top);
1021              bottom = isNaN(bottom) ? xy[1] : Math.max(xy[1], bottom);
1022          }
1023          left = Math.round(left * 10)/10;
1024          right = Math.round(right * 10)/10;
1025          top = Math.round(top * 10)/10;
1026          bottom = Math.round(bottom * 10)/10;
1027          this._trackSize(right + wt, bottom + wt);
1028          this._trackSize(left - wt, top - wt);
1029      },
1030  
1031      /**
1032       * Updates the size of the graphics object
1033       *
1034       * @method _trackSize
1035       * @param {Number} w width
1036       * @param {Number} h height
1037       * @private
1038       */
1039      _trackSize: function(w, h) {
1040          if (w > this._right) {
1041              this._right = w;
1042          }
1043          if(w < this._left)
1044          {
1045              this._left = w;
1046          }
1047          if (h < this._top)
1048          {
1049              this._top = h;
1050          }
1051          if (h > this._bottom)
1052          {
1053              this._bottom = h;
1054          }
1055          this._width = this._right - this._left;
1056          this._height = this._bottom - this._top;
1057      }
1058  };
1059  Y.CanvasDrawing = CanvasDrawing;
1060  /**
1061   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Shape.html">`Shape`</a> class.
1062   * `CanvasShape` is not intended to be used directly. Instead, use the <a href="Shape.html">`Shape`</a> class.
1063   * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
1064   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Shape.html">`Shape`</a>
1065   * class will point to the `CanvasShape` class.
1066   *
1067   * @module graphics
1068   * @class CanvasShape
1069   * @constructor
1070   */
1071  CanvasShape = function()
1072  {
1073      this._transforms = [];
1074      this.matrix = new Y.Matrix();
1075      CanvasShape.superclass.constructor.apply(this, arguments);
1076  };
1077  
1078  CanvasShape.NAME = "shape";
1079  
1080  Y.extend(CanvasShape, Y.GraphicBase, Y.mix({
1081      /**
1082       * Init method, invoked during construction.
1083       * Calls `initializer` method.
1084       *
1085       * @method init
1086       * @protected
1087       */
1088      init: function()
1089      {
1090          this.initializer.apply(this, arguments);
1091      },
1092  
1093      /**
1094       * Initializes the shape
1095       *
1096       * @private
1097       * @method _initialize
1098       */
1099      initializer: function(cfg)
1100      {
1101          var host = this,
1102              graphic = cfg.graphic,
1103              data = this.get("data");
1104          host._initProps();
1105          host.createNode();
1106          host._xcoords = [0];
1107          host._ycoords = [0];
1108          if(graphic)
1109          {
1110              this._setGraphic(graphic);
1111          }
1112          if(data)
1113          {
1114              host._parsePathData(data);
1115          }
1116          host._updateHandler();
1117      },
1118  
1119      /**
1120       * Set the Graphic instance for the shape.
1121       *
1122       * @method _setGraphic
1123       * @param {Graphic | Node | HTMLElement | String} render This param is used to determine the graphic instance. If it is a
1124       * `Graphic` instance, it will be assigned to the `graphic` attribute. Otherwise, a new Graphic instance will be created
1125       * and rendered into the dom element that the render represents.
1126       * @private
1127       */
1128      _setGraphic: function(render)
1129      {
1130          var graphic;
1131          if(render instanceof Y.CanvasGraphic)
1132          {
1133              this._graphic = render;
1134          }
1135          else
1136          {
1137              graphic = new Y.CanvasGraphic({
1138                  render: render
1139              });
1140              graphic._appendShape(this);
1141              this._graphic = graphic;
1142          }
1143      },
1144  
1145      /**
1146       * Add a class name to each node.
1147       *
1148       * @method addClass
1149       * @param {String} className the class name to add to the node's class attribute
1150       */
1151      addClass: function(className)
1152      {
1153          var node = this.get("node");
1154          Y.DOM.addClass(node, className);
1155      },
1156  
1157      /**
1158       * Removes a class name from each node.
1159       *
1160       * @method removeClass
1161       * @param {String} className the class name to remove from the node's class attribute
1162       */
1163      removeClass: function(className)
1164      {
1165          var node = this.get("node");
1166          Y.DOM.removeClass(node, className);
1167      },
1168  
1169      /**
1170       * Gets the current position of the node in page coordinates.
1171       *
1172       * @method getXY
1173       * @return Array The XY position of the shape.
1174       */
1175      getXY: function()
1176      {
1177          var graphic = this.get("graphic"),
1178              parentXY = graphic.getXY(),
1179              x = this.get("x"),
1180              y = this.get("y");
1181          return [parentXY[0] + x, parentXY[1] + y];
1182      },
1183  
1184      /**
1185       * Set the position of the shape in page coordinates, regardless of how the node is positioned.
1186       *
1187       * @method setXY
1188       * @param {Array} Contains X & Y values for new position (coordinates are page-based)
1189       */
1190      setXY: function(xy)
1191      {
1192          var graphic = this.get("graphic"),
1193              parentXY = graphic.getXY(),
1194              x = xy[0] - parentXY[0],
1195              y = xy[1] - parentXY[1];
1196          this._set("x", x);
1197          this._set("y", y);
1198          this._updateNodePosition(x, y);
1199      },
1200  
1201      /**
1202       * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy.
1203       *
1204       * @method contains
1205       * @param {CanvasShape | HTMLElement} needle The possible node or descendent
1206       * @return Boolean Whether or not this shape is the needle or its ancestor.
1207       */
1208      contains: function(needle)
1209      {
1210          var node = needle instanceof Y.Node ? needle._node : needle;
1211          return node === this.node;
1212      },
1213  
1214      /**
1215       * Test if the supplied node matches the supplied selector.
1216       *
1217       * @method test
1218       * @param {String} selector The CSS selector to test against.
1219       * @return Boolean Wheter or not the shape matches the selector.
1220       */
1221      test: function(selector)
1222      {
1223          return Y.Selector.test(this.node, selector);
1224      },
1225  
1226      /**
1227       * Compares nodes to determine if they match.
1228       * Node instances can be compared to each other and/or HTMLElements.
1229       * @method compareTo
1230       * @param {HTMLElement | Node} refNode The reference node to compare to the node.
1231       * @return {Boolean} True if the nodes match, false if they do not.
1232       */
1233      compareTo: function(refNode) {
1234          var node = this.node;
1235          return node === refNode;
1236      },
1237  
1238      /**
1239       * Value function for fill attribute
1240       *
1241       * @method _getDefaultFill
1242       * @return Object
1243       * @private
1244       */
1245      _getDefaultFill: function() {
1246          return {
1247              type: "solid",
1248              opacity: 1,
1249              cx: 0.5,
1250              cy: 0.5,
1251              fx: 0.5,
1252              fy: 0.5,
1253              r: 0.5
1254          };
1255      },
1256  
1257      /**
1258       * Value function for stroke attribute
1259       *
1260       * @method _getDefaultStroke
1261       * @return Object
1262       * @private
1263       */
1264      _getDefaultStroke: function()
1265      {
1266          return {
1267              weight: 1,
1268              dashstyle: "none",
1269              color: "#000",
1270              opacity: 1.0
1271          };
1272      },
1273  
1274      /**
1275       * Left edge of the path
1276       *
1277       * @property _left
1278       * @type Number
1279       * @private
1280       */
1281      _left: 0,
1282  
1283      /**
1284       * Right edge of the path
1285       *
1286       * @property _right
1287       * @type Number
1288       * @private
1289       */
1290      _right: 0,
1291  
1292      /**
1293       * Top edge of the path
1294       *
1295       * @property _top
1296       * @type Number
1297       * @private
1298       */
1299      _top: 0,
1300  
1301      /**
1302       * Bottom edge of the path
1303       *
1304       * @property _bottom
1305       * @type Number
1306       * @private
1307       */
1308      _bottom: 0,
1309  
1310      /**
1311       * Creates the dom node for the shape.
1312       *
1313       * @method createNode
1314       * @return HTMLElement
1315       * @private
1316       */
1317      createNode: function()
1318      {
1319          var host = this,
1320              node = Y.config.doc.createElement('canvas'),
1321              id = host.get("id"),
1322              concat = host._camelCaseConcat,
1323              name = host.name;
1324          host._context = node.getContext('2d');
1325          node.setAttribute("overflow", "visible");
1326          node.style.overflow = "visible";
1327          if(!host.get("visible"))
1328          {
1329              node.style.visibility = "hidden";
1330          }
1331          node.setAttribute("id", id);
1332          id = "#" + id;
1333          host.node = node;
1334          host.addClass(
1335              _getClassName(SHAPE) +
1336              " " +
1337              _getClassName(concat(IMPLEMENTATION, SHAPE)) +
1338              " " +
1339              _getClassName(name) +
1340              " " +
1341              _getClassName(concat(IMPLEMENTATION, name))
1342          );
1343      },
1344  
1345      /**
1346       * Overrides default `on` method. Checks to see if its a dom interaction event. If so,
1347       * return an event attached to the `node` element. If not, return the normal functionality.
1348       *
1349       * @method on
1350       * @param {String} type event type
1351       * @param {Object} callback function
1352       * @private
1353       */
1354      on: function(type, fn)
1355      {
1356          if(Y.Node.DOM_EVENTS[type])
1357          {
1358              return Y.on(type, fn, "#" + this.get("id"));
1359          }
1360          return Y.on.apply(this, arguments);
1361      },
1362  
1363      /**
1364       * Adds a stroke to the shape node.
1365       *
1366       * @method _strokeChangeHandler
1367       * @param {Object} stroke Properties of the `stroke` attribute.
1368       * @private
1369       */
1370      _setStrokeProps: function(stroke)
1371      {
1372          var color,
1373              weight,
1374              opacity,
1375              linejoin,
1376              linecap,
1377              dashstyle;
1378          if(stroke)
1379          {
1380              color = stroke.color;
1381              weight = PARSE_FLOAT(stroke.weight);
1382              opacity = PARSE_FLOAT(stroke.opacity);
1383              linejoin = stroke.linejoin || "round";
1384              linecap = stroke.linecap || "butt";
1385              dashstyle = stroke.dashstyle;
1386              this._miterlimit = null;
1387              this._dashstyle = (dashstyle && Y.Lang.isArray(dashstyle) && dashstyle.length > 1) ? dashstyle : null;
1388              this._strokeWeight = weight;
1389  
1390              if (IS_NUMBER(weight) && weight > 0)
1391              {
1392                  this._stroke = 1;
1393              }
1394              else
1395              {
1396                  this._stroke = 0;
1397              }
1398              if (IS_NUMBER(opacity)) {
1399                  this._strokeStyle = this._toRGBA(color, opacity);
1400              }
1401              else
1402              {
1403                  this._strokeStyle = color;
1404              }
1405              this._linecap = linecap;
1406              if(linejoin === "round" || linejoin === "bevel")
1407              {
1408                  this._linejoin = linejoin;
1409              }
1410              else
1411              {
1412                  linejoin = parseInt(linejoin, 10);
1413                  if(IS_NUMBER(linejoin))
1414                  {
1415                      this._miterlimit =  Math.max(linejoin, 1);
1416                      this._linejoin = "miter";
1417                  }
1418              }
1419          }
1420          else
1421          {
1422              this._stroke = 0;
1423          }
1424      },
1425  
1426      /**
1427       * Sets the value of an attribute.
1428       *
1429       * @method set
1430       * @param {String|Object} name The name of the attribute. Alternatively, an object of key value pairs can
1431       * be passed in to set multiple attributes at once.
1432       * @param {Any} value The value to set the attribute to. This value is ignored if an object is received as
1433       * the name param.
1434       */
1435      set: function()
1436      {
1437          var host = this;
1438          AttributeLite.prototype.set.apply(host, arguments);
1439          if(host.initialized)
1440          {
1441              host._updateHandler();
1442          }
1443      },
1444  
1445      /**
1446       * Adds a fill to the shape node.
1447       *
1448       * @method _setFillProps
1449       * @param {Object} fill Properties of the `fill` attribute.
1450       * @private
1451       */
1452      _setFillProps: function(fill)
1453      {
1454          var isNumber = IS_NUMBER,
1455              color,
1456              opacity,
1457              type;
1458          if(fill)
1459          {
1460              color = fill.color;
1461              type = fill.type;
1462              if(type === "linear" || type === "radial")
1463              {
1464                  this._fillType = type;
1465              }
1466              else if(color)
1467              {
1468                  opacity = fill.opacity;
1469                  if (isNumber(opacity))
1470                  {
1471                      opacity = Math.max(0, Math.min(1, opacity));
1472                      color = this._toRGBA(color, opacity);
1473                  }
1474                  else
1475                  {
1476                      color = TORGB(color);
1477                  }
1478  
1479                  this._fillColor = color;
1480                  this._fillType = 'solid';
1481              }
1482              else
1483              {
1484                  this._fillColor = null;
1485              }
1486          }
1487          else
1488          {
1489              this._fillType = null;
1490              this._fillColor = null;
1491          }
1492      },
1493  
1494      /**
1495       * Specifies a 2d translation.
1496       *
1497       * @method translate
1498       * @param {Number} x The value to transate on the x-axis.
1499       * @param {Number} y The value to translate on the y-axis.
1500       */
1501      translate: function(x, y)
1502      {
1503          this._translateX += x;
1504          this._translateY += y;
1505          this._addTransform("translate", arguments);
1506      },
1507  
1508      /**
1509       * Translates the shape along the x-axis. When translating x and y coordinates,
1510       * use the `translate` method.
1511       *
1512       * @method translateX
1513       * @param {Number} x The value to translate.
1514       */
1515      translateX: function(x)
1516      {
1517          this._translateX += x;
1518          this._addTransform("translateX", arguments);
1519      },
1520  
1521      /**
1522       * Performs a translate on the y-coordinate. When translating x and y coordinates,
1523       * use the `translate` method.
1524       *
1525       * @method translateY
1526       * @param {Number} y The value to translate.
1527       */
1528      translateY: function(y)
1529      {
1530          this._translateY += y;
1531          this._addTransform("translateY", arguments);
1532      },
1533  
1534      /**
1535       * Skews the shape around the x-axis and y-axis.
1536       *
1537       * @method skew
1538       * @param {Number} x The value to skew on the x-axis.
1539       * @param {Number} y The value to skew on the y-axis.
1540       */
1541      skew: function()
1542      {
1543          this._addTransform("skew", arguments);
1544      },
1545  
1546      /**
1547       * Skews the shape around the x-axis.
1548       *
1549       * @method skewX
1550       * @param {Number} x x-coordinate
1551       */
1552      skewX: function()
1553      {
1554          this._addTransform("skewX", arguments);
1555      },
1556  
1557      /**
1558       * Skews the shape around the y-axis.
1559       *
1560       * @method skewY
1561       * @param {Number} y y-coordinate
1562       */
1563      skewY: function()
1564      {
1565          this._addTransform("skewY", arguments);
1566      },
1567  
1568      /**
1569       * Rotates the shape clockwise around it transformOrigin.
1570       *
1571       * @method rotate
1572       * @param {Number} deg The degree of the rotation.
1573       */
1574      rotate: function()
1575      {
1576          this._addTransform("rotate", arguments);
1577      },
1578  
1579      /**
1580       * Specifies a 2d scaling operation.
1581       *
1582       * @method scale
1583       * @param {Number} val
1584       */
1585      scale: function()
1586      {
1587          this._addTransform("scale", arguments);
1588      },
1589  
1590      /**
1591       * Storage for the transform attribute.
1592       *
1593       * @property _transform
1594       * @type String
1595       * @private
1596       */
1597      _transform: "",
1598  
1599      /**
1600       * Adds a transform to the shape.
1601       *
1602       * @method _addTransform
1603       * @param {String} type The transform being applied.
1604       * @param {Array} args The arguments for the transform.
1605       * @private
1606       */
1607      _addTransform: function(type, args)
1608      {
1609          args = Y.Array(args);
1610          this._transform = Y_LANG.trim(this._transform + " " + type + "(" + args.join(", ") + ")");
1611          args.unshift(type);
1612          this._transforms.push(args);
1613          if(this.initialized)
1614          {
1615              this._updateTransform();
1616          }
1617      },
1618  
1619      /**
1620       * Applies all transforms.
1621       *
1622       * @method _updateTransform
1623       * @private
1624       */
1625      _updateTransform: function()
1626      {
1627          var node = this.node,
1628              key,
1629              transform,
1630              transformOrigin = this.get("transformOrigin"),
1631              matrix = this.matrix,
1632              i,
1633              len = this._transforms.length;
1634  
1635          if(this._transforms && this._transforms.length > 0)
1636          {
1637              for(i = 0; i < len; ++i)
1638              {
1639                  key = this._transforms[i].shift();
1640                  if(key)
1641                  {
1642                      matrix[key].apply(matrix, this._transforms[i]);
1643                  }
1644              }
1645              transform = matrix.toCSSText();
1646          }
1647  
1648          this._graphic.addToRedrawQueue(this);
1649          transformOrigin = (100 * transformOrigin[0]) + "% " + (100 * transformOrigin[1]) + "%";
1650          Y_DOM.setStyle(node, "transformOrigin", transformOrigin);
1651          if(transform)
1652          {
1653              Y_DOM.setStyle(node, "transform", transform);
1654          }
1655          this._transforms = [];
1656      },
1657  
1658      /**
1659       * Updates `Shape` based on attribute changes.
1660       *
1661       * @method _updateHandler
1662       * @private
1663       */
1664      _updateHandler: function()
1665      {
1666          this._draw();
1667          this._updateTransform();
1668      },
1669  
1670      /**
1671       * Updates the shape.
1672       *
1673       * @method _draw
1674       * @private
1675       */
1676      _draw: function()
1677      {
1678          var node = this.node;
1679          this.clear();
1680          this._closePath();
1681          node.style.left = this.get("x") + "px";
1682          node.style.top = this.get("y") + "px";
1683      },
1684  
1685      /**
1686       * Completes a shape or drawing
1687       *
1688       * @method _closePath
1689       * @private
1690       */
1691      _closePath: function()
1692      {
1693          if(!this._methods)
1694          {
1695              return;
1696          }
1697          var node = this.get("node"),
1698              w = this._right - this._left,
1699              h = this._bottom - this._top,
1700              context = this._context,
1701              methods = [],
1702              cachedMethods = this._methods.concat(),
1703              i,
1704              j,
1705              method,
1706              args,
1707              argsLen,
1708              len = 0;
1709          this._context.clearRect(0, 0, node.width, node.height);
1710          if(this._methods)
1711          {
1712              len = cachedMethods.length;
1713              if(!len || len < 1)
1714              {
1715                  return;
1716              }
1717              for(i = 0; i < len; ++i)
1718              {
1719                  methods[i] = cachedMethods[i].concat();
1720                  args = methods[i];
1721                  argsLen = (args[0] === "quadraticCurveTo" || args[0] === "bezierCurveTo") ? args.length : 3;
1722                  for(j = 1; j < argsLen; ++j)
1723                  {
1724                      if(j % 2 === 0)
1725                      {
1726                          args[j] = args[j] - this._top;
1727                      }
1728                      else
1729                      {
1730                          args[j] = args[j] - this._left;
1731                      }
1732                  }
1733              }
1734              node.setAttribute("width", Math.min(w, 2000));
1735              node.setAttribute("height", Math.min(2000, h));
1736              context.beginPath();
1737              for(i = 0; i < len; ++i)
1738              {
1739                  args = methods[i].concat();
1740                  if(args && args.length > 0)
1741                  {
1742                      method = args.shift();
1743                      if(method)
1744                      {
1745                          if(method === "closePath")
1746                          {
1747                              context.closePath();
1748                              this._strokeAndFill(context);
1749                          }
1750                          else if(method && method === "lineTo" && this._dashstyle)
1751                          {
1752                              args.unshift(this._xcoords[i] - this._left, this._ycoords[i] - this._top);
1753                              this._drawDashedLine.apply(this, args);
1754                          }
1755                          else
1756                          {
1757                              context[method].apply(context, args);
1758                          }
1759                      }
1760                  }
1761              }
1762  
1763              this._strokeAndFill(context);
1764              this._drawingComplete = true;
1765              this._clearAndUpdateCoords();
1766              this._updateNodePosition();
1767              this._methods = cachedMethods;
1768          }
1769      },
1770  
1771      /**
1772       * Completes a stroke and/or fill operation on the context.
1773       *
1774       * @method _strokeAndFill
1775       * @param {Context} Reference to the context element of the canvas instance.
1776       * @private
1777       */
1778      _strokeAndFill: function(context)
1779      {
1780          if (this._fillType)
1781          {
1782              if(this._fillType === "linear")
1783              {
1784                  context.fillStyle = this._getLinearGradient();
1785              }
1786              else if(this._fillType === "radial")
1787              {
1788                  context.fillStyle = this._getRadialGradient();
1789              }
1790              else
1791              {
1792                  context.fillStyle = this._fillColor;
1793              }
1794              context.closePath();
1795              context.fill();
1796          }
1797  
1798          if (this._stroke) {
1799              if(this._strokeWeight)
1800              {
1801                  context.lineWidth = this._strokeWeight;
1802              }
1803              context.lineCap = this._linecap;
1804              context.lineJoin = this._linejoin;
1805              if(this._miterlimit)
1806              {
1807                  context.miterLimit = this._miterlimit;
1808              }
1809              context.strokeStyle = this._strokeStyle;
1810              context.stroke();
1811          }
1812      },
1813  
1814      /**
1815       * Draws a dashed line between two points.
1816       *
1817       * @method _drawDashedLine
1818       * @param {Number} xStart    The x position of the start of the line
1819       * @param {Number} yStart    The y position of the start of the line
1820       * @param {Number} xEnd        The x position of the end of the line
1821       * @param {Number} yEnd        The y position of the end of the line
1822       * @private
1823       */
1824      _drawDashedLine: function(xStart, yStart, xEnd, yEnd)
1825      {
1826          var context = this._context,
1827              dashsize = this._dashstyle[0],
1828              gapsize = this._dashstyle[1],
1829              segmentLength = dashsize + gapsize,
1830              xDelta = xEnd - xStart,
1831              yDelta = yEnd - yStart,
1832              delta = Math.sqrt(Math.pow(xDelta, 2) + Math.pow(yDelta, 2)),
1833              segmentCount = Math.floor(Math.abs(delta / segmentLength)),
1834              radians = Math.atan2(yDelta, xDelta),
1835              xCurrent = xStart,
1836              yCurrent = yStart,
1837              i;
1838          xDelta = Math.cos(radians) * segmentLength;
1839          yDelta = Math.sin(radians) * segmentLength;
1840  
1841          for(i = 0; i < segmentCount; ++i)
1842          {
1843              context.moveTo(xCurrent, yCurrent);
1844              context.lineTo(xCurrent + Math.cos(radians) * dashsize, yCurrent + Math.sin(radians) * dashsize);
1845              xCurrent += xDelta;
1846              yCurrent += yDelta;
1847          }
1848  
1849          context.moveTo(xCurrent, yCurrent);
1850          delta = Math.sqrt((xEnd - xCurrent) * (xEnd - xCurrent) + (yEnd - yCurrent) * (yEnd - yCurrent));
1851  
1852          if(delta > dashsize)
1853          {
1854              context.lineTo(xCurrent + Math.cos(radians) * dashsize, yCurrent + Math.sin(radians) * dashsize);
1855          }
1856          else if(delta > 0)
1857          {
1858              context.lineTo(xCurrent + Math.cos(radians) * delta, yCurrent + Math.sin(radians) * delta);
1859          }
1860  
1861          context.moveTo(xEnd, yEnd);
1862      },
1863  
1864      /**
1865       * Returns the bounds for a shape.
1866       *
1867       * Calculates the a new bounding box from the original corner coordinates (base on size and position) and the transform matrix.
1868       * The calculated bounding box is used by the graphic instance to calculate its viewBox.
1869       *
1870       * @method getBounds
1871       * @return Object
1872       */
1873      getBounds: function()
1874      {
1875          var type = this._type,
1876              w = this.get("width"),
1877              h = this.get("height"),
1878              x = this.get("x"),
1879              y = this.get("y");
1880          if(type === "path")
1881          {
1882              x = x + this._left;
1883              y = y + this._top;
1884              w = this._right - this._left;
1885              h = this._bottom - this._top;
1886          }
1887          return this._getContentRect(w, h, x, y);
1888      },
1889  
1890      /**
1891       * Calculates the bounding box for the shape.
1892       *
1893       * @method _getContentRect
1894       * @param {Number} w width of the shape
1895       * @param {Number} h height of the shape
1896       * @param {Number} x x-coordinate of the shape
1897       * @param {Number} y y-coordinate of the shape
1898       * @private
1899       */
1900      _getContentRect: function(w, h, x, y)
1901      {
1902          var transformOrigin = this.get("transformOrigin"),
1903              transformX = transformOrigin[0] * w,
1904              transformY = transformOrigin[1] * h,
1905              transforms = this.matrix.getTransformArray(this.get("transform")),
1906              matrix = new Y.Matrix(),
1907              i,
1908              len = transforms.length,
1909              transform,
1910              key,
1911              contentRect;
1912          if(this._type === "path")
1913          {
1914              transformX = transformX + x;
1915              transformY = transformY + y;
1916          }
1917          transformX = !isNaN(transformX) ? transformX : 0;
1918          transformY = !isNaN(transformY) ? transformY : 0;
1919          matrix.translate(transformX, transformY);
1920          for(i = 0; i < len; i = i + 1)
1921          {
1922              transform = transforms[i];
1923              key = transform.shift();
1924              if(key)
1925              {
1926                  matrix[key].apply(matrix, transform);
1927              }
1928          }
1929          matrix.translate(-transformX, -transformY);
1930          contentRect = matrix.getContentRect(w, h, x, y);
1931          return contentRect;
1932      },
1933  
1934      /**
1935       * Places the shape above all other shapes.
1936       *
1937       * @method toFront
1938       */
1939      toFront: function()
1940      {
1941          var graphic = this.get("graphic");
1942          if(graphic)
1943          {
1944              graphic._toFront(this);
1945          }
1946      },
1947  
1948      /**
1949       * Places the shape underneath all other shapes.
1950       *
1951       * @method toFront
1952       */
1953      toBack: function()
1954      {
1955          var graphic = this.get("graphic");
1956          if(graphic)
1957          {
1958              graphic._toBack(this);
1959          }
1960      },
1961  
1962      /**
1963       * Parses path data string and call mapped methods.
1964       *
1965       * @method _parsePathData
1966       * @param {String} val The path data
1967       * @private
1968       */
1969      _parsePathData: function(val)
1970      {
1971          var method,
1972              methodSymbol,
1973              args,
1974              commandArray = Y.Lang.trim(val.match(SPLITPATHPATTERN)),
1975              i,
1976              len,
1977              str,
1978              symbolToMethod = this._pathSymbolToMethod;
1979          if(commandArray)
1980          {
1981              this.clear();
1982              len = commandArray.length || 0;
1983              for(i = 0; i < len; i = i + 1)
1984              {
1985                  str = commandArray[i];
1986                  methodSymbol = str.substr(0, 1);
1987                  args = str.substr(1).match(SPLITARGSPATTERN);
1988                  method = symbolToMethod[methodSymbol];
1989                  if(method)
1990                  {
1991                      if(args)
1992                      {
1993                          this[method].apply(this, args);
1994                      }
1995                      else
1996                      {
1997                          this[method].apply(this);
1998                      }
1999                  }
2000              }
2001              this.end();
2002          }
2003      },
2004  
2005      /**
2006       * Destroys the shape instance.
2007       *
2008       * @method destroy
2009       */
2010      destroy: function()
2011      {
2012          var graphic = this.get("graphic");
2013          if(graphic)
2014          {
2015              graphic.removeShape(this);
2016          }
2017          else
2018          {
2019              this._destroy();
2020          }
2021      },
2022  
2023      /**
2024       *  Implementation for shape destruction
2025       *
2026       *  @method destroy
2027       *  @protected
2028       */
2029      _destroy: function()
2030      {
2031          if(this.node)
2032          {
2033              Y.Event.purgeElement(this.node, true);
2034              if(this.node.parentNode)
2035              {
2036                  this.node.style.visibility = "";
2037                  this.node.parentNode.removeChild(this.node);
2038              }
2039              this._context = null;
2040              this.node = null;
2041          }
2042      }
2043  }, Y.CanvasDrawing.prototype));
2044  
2045  CanvasShape.ATTRS =  {
2046      /**
2047       * An array of x, y values which indicates the transformOrigin in which to rotate the shape. Valid values range between 0 and 1 representing a
2048       * fraction of the shape's corresponding bounding box dimension. The default value is [0.5, 0.5].
2049       *
2050       * @config transformOrigin
2051       * @type Array
2052       */
2053      transformOrigin: {
2054          valueFn: function()
2055          {
2056              return [0.5, 0.5];
2057          }
2058      },
2059  
2060      /**
2061       * <p>A string containing, in order, transform operations applied to the shape instance. The `transform` string can contain the following values:
2062       *
2063       *    <dl>
2064       *        <dt>rotate</dt><dd>Rotates the shape clockwise around it transformOrigin.</dd>
2065       *        <dt>translate</dt><dd>Specifies a 2d translation.</dd>
2066       *        <dt>skew</dt><dd>Skews the shape around the x-axis and y-axis.</dd>
2067       *        <dt>scale</dt><dd>Specifies a 2d scaling operation.</dd>
2068       *        <dt>translateX</dt><dd>Translates the shape along the x-axis.</dd>
2069       *        <dt>translateY</dt><dd>Translates the shape along the y-axis.</dd>
2070       *        <dt>skewX</dt><dd>Skews the shape around the x-axis.</dd>
2071       *        <dt>skewY</dt><dd>Skews the shape around the y-axis.</dd>
2072       *        <dt>matrix</dt><dd>Specifies a 2D transformation matrix comprised of the specified six values.</dd>
2073       *    </dl>
2074       * </p>
2075       * <p>Applying transforms through the transform attribute will reset the transform matrix and apply a new transform. The shape class also contains
2076       * corresponding methods for each transform that will apply the transform to the current matrix. The below code illustrates how you might use the
2077       * `transform` attribute to instantiate a recangle with a rotation of 45 degrees.</p>
2078              var myRect = new Y.Rect({
2079                  type:"rect",
2080                  width: 50,
2081                  height: 40,
2082                  transform: "rotate(45)"
2083              };
2084       * <p>The code below would apply `translate` and `rotate` to an existing shape.</p>
2085  
2086          myRect.set("transform", "translate(40, 50) rotate(45)");
2087       * @config transform
2088       * @type String
2089       */
2090      transform: {
2091          setter: function(val)
2092          {
2093              this.matrix.init();
2094              this._transforms = this.matrix.getTransformArray(val);
2095              this._transform = val;
2096              return val;
2097          },
2098  
2099          getter: function()
2100          {
2101              return this._transform;
2102          }
2103      },
2104  
2105      /**
2106       * Dom node for the shape
2107       *
2108       * @config node
2109       * @type HTMLElement
2110       * @readOnly
2111       */
2112      node: {
2113          readOnly: true,
2114  
2115          getter: function()
2116          {
2117              return this.node;
2118          }
2119      },
2120  
2121      /**
2122       * Unique id for class instance.
2123       *
2124       * @config id
2125       * @type String
2126       */
2127      id: {
2128          valueFn: function()
2129          {
2130              return Y.guid();
2131          },
2132  
2133          setter: function(val)
2134          {
2135              var node = this.node;
2136              if(node)
2137              {
2138                  node.setAttribute("id", val);
2139              }
2140              return val;
2141          }
2142      },
2143  
2144      /**
2145       * Indicates the width of the shape
2146       *
2147       * @config width
2148       * @type Number
2149       */
2150      width: {
2151          value: 0
2152      },
2153  
2154      /**
2155       * Indicates the height of the shape
2156       *
2157       * @config height
2158       * @type Number
2159       */
2160      height: {
2161          value: 0
2162      },
2163  
2164      /**
2165       * Indicates the x position of shape.
2166       *
2167       * @config x
2168       * @type Number
2169       */
2170      x: {
2171          value: 0
2172      },
2173  
2174      /**
2175       * Indicates the y position of shape.
2176       *
2177       * @config y
2178       * @type Number
2179       */
2180      y: {
2181          value: 0
2182      },
2183  
2184      /**
2185       * Indicates whether the shape is visible.
2186       *
2187       * @config visible
2188       * @type Boolean
2189       */
2190      visible: {
2191          value: true,
2192  
2193          setter: function(val){
2194              var node = this.get("node"),
2195                  visibility = val ? "visible" : "hidden";
2196              if(node)
2197              {
2198                  node.style.visibility = visibility;
2199              }
2200              return val;
2201          }
2202      },
2203  
2204      /**
2205       * Contains information about the fill of the shape.
2206       *  <dl>
2207       *      <dt>color</dt><dd>The color of the fill.</dd>
2208       *      <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the fill. The default value is 1.</dd>
2209       *      <dt>type</dt><dd>Type of fill.
2210       *          <dl>
2211       *              <dt>solid</dt><dd>Solid single color fill. (default)</dd>
2212       *              <dt>linear</dt><dd>Linear gradient fill.</dd>
2213       *              <dt>radial</dt><dd>Radial gradient fill.</dd>
2214       *          </dl>
2215       *      </dd>
2216       *  </dl>
2217       *  <p>If a `linear` or `radial` is specified as the fill type. The following additional property is used:
2218       *  <dl>
2219       *      <dt>stops</dt><dd>An array of objects containing the following properties:
2220       *          <dl>
2221       *              <dt>color</dt><dd>The color of the stop.</dd>
2222       *              <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the stop. The default value is 1.
2223       *              Note: No effect for IE 6 - 8</dd>
2224       *              <dt>offset</dt><dd>Number between 0 and 1 indicating where the color stop is positioned.</dd>
2225       *          </dl>
2226       *      </dd>
2227       *      <p>Linear gradients also have the following property:</p>
2228       *      <dt>rotation</dt><dd>Linear gradients flow left to right by default. The rotation property allows you to change the
2229       *      flow by rotation. (e.g. A rotation of 180 would make the gradient pain from right to left.)</dd>
2230       *      <p>Radial gradients have the following additional properties:</p>
2231       *      <dt>r</dt><dd>Radius of the gradient circle.</dd>
2232       *      <dt>fx</dt><dd>Focal point x-coordinate of the gradient.</dd>
2233       *      <dt>fy</dt><dd>Focal point y-coordinate of the gradient.</dd>
2234       *  </dl>
2235       *  <p>The corresponding `SVGShape` class implements the following additional properties.</p>
2236       *  <dl>
2237       *      <dt>cx</dt><dd>
2238       *          <p>The x-coordinate of the center of the gradient circle. Determines where the color stop begins. The default value 0.5.</p>
2239       *          <p><strong>Note: </strong>Currently, this property is not implemented for corresponding `CanvasShape` and
2240       *          `VMLShape` classes which are used on Android or IE 6 - 8.</p>
2241       *      </dd>
2242       *      <dt>cy</dt><dd>
2243       *          <p>The y-coordinate of the center of the gradient circle. Determines where the color stop begins. The default value 0.5.</p>
2244       *          <p><strong>Note: </strong>Currently, this property is not implemented for corresponding `CanvasShape` and `VMLShape`
2245       *          classes which are used on Android or IE 6 - 8.</p>
2246       *      </dd>
2247       *  </dl>
2248       *  <p>These properties are not currently implemented in `CanvasShape` or `VMLShape`.</p>
2249       *
2250       * @config fill
2251       * @type Object
2252       */
2253      fill: {
2254          valueFn: "_getDefaultFill",
2255  
2256          setter: function(val)
2257          {
2258              var fill,
2259                  tmpl = this.get("fill") || this._getDefaultFill();
2260              fill = (val) ? Y.merge(tmpl, val) : null;
2261              if(fill && fill.color)
2262              {
2263                  if(fill.color === undefined || fill.color === "none")
2264                  {
2265                      fill.color = null;
2266                  }
2267              }
2268              this._setFillProps(fill);
2269              return fill;
2270          }
2271      },
2272  
2273      /**
2274       * Contains information about the stroke of the shape.
2275       *  <dl>
2276       *      <dt>color</dt><dd>The color of the stroke.</dd>
2277       *      <dt>weight</dt><dd>Number that indicates the width of the stroke.</dd>
2278       *      <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the stroke. The default value is 1.</dd>
2279       *      <dt>dashstyle</dt>Indicates whether to draw a dashed stroke. When set to "none", a solid stroke is drawn. When set
2280       *      to an array, the first index indicates the length of the dash. The second index indicates the length of gap.
2281       *      <dt>linecap</dt><dd>Specifies the linecap for the stroke. The following values can be specified:
2282       *          <dl>
2283       *              <dt>butt (default)</dt><dd>Specifies a butt linecap.</dd>
2284       *              <dt>square</dt><dd>Specifies a sqare linecap.</dd>
2285       *              <dt>round</dt><dd>Specifies a round linecap.</dd>
2286       *          </dl>
2287       *      </dd>
2288       *      <dt>linejoin</dt><dd>Specifies a linejoin for the stroke. The following values can be specified:
2289       *          <dl>
2290       *              <dt>round (default)</dt><dd>Specifies that the linejoin will be round.</dd>
2291       *              <dt>bevel</dt><dd>Specifies a bevel for the linejoin.</dd>
2292       *              <dt>miter limit</dt><dd>An integer specifying the miter limit of a miter linejoin. If you want to specify a linejoin
2293       *              of miter, you simply specify the limit as opposed to having separate miter and miter limit values.</dd>
2294       *          </dl>
2295       *      </dd>
2296       *  </dl>
2297       *
2298       * @config stroke
2299       * @type Object
2300       */
2301      stroke: {
2302          valueFn: "_getDefaultStroke",
2303  
2304          setter: function(val)
2305          {
2306              var tmpl = this.get("stroke") || this._getDefaultStroke(),
2307                  wt;
2308              if(val && val.hasOwnProperty("weight"))
2309              {
2310                  wt = parseInt(val.weight, 10);
2311                  if(!isNaN(wt))
2312                  {
2313                      val.weight = wt;
2314                  }
2315              }
2316              val = (val) ? Y.merge(tmpl, val) : null;
2317              this._setStrokeProps(val);
2318              return val;
2319          }
2320      },
2321  
2322      //Not used. Remove in future.
2323      autoSize: {
2324          value: false
2325      },
2326  
2327      // Only implemented in SVG
2328      // Determines whether the instance will receive mouse events.
2329      //
2330      // @config pointerEvents
2331      // @type string
2332      //
2333      pointerEvents: {
2334          value: "visiblePainted"
2335      },
2336  
2337      /**
2338       * Represents an SVG Path string. This will be parsed and added to shape's API to represent the SVG data across all
2339       * implementations. Note that when using VML or SVG implementations, part of this content will be added to the DOM using
2340       * respective VML/SVG attributes. If your content comes from an untrusted source, you will need to ensure that no
2341       * malicious code is included in that content.
2342       *
2343       * @config data
2344       * @type String
2345       */
2346      data: {
2347          setter: function(val)
2348          {
2349              if(this.get("node"))
2350              {
2351                  this._parsePathData(val);
2352              }
2353              return val;
2354          }
2355      },
2356  
2357      /**
2358       * Reference to the container Graphic.
2359       *
2360       * @config graphic
2361       * @type Graphic
2362       */
2363      graphic: {
2364          readOnly: true,
2365  
2366          getter: function()
2367          {
2368              return this._graphic;
2369          }
2370      }
2371  };
2372  Y.CanvasShape = CanvasShape;
2373  /**
2374   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Path.html">`Path`</a> class.
2375   * `CanvasPath` is not intended to be used directly. Instead, use the <a href="Path.html">`Path`</a> class.
2376   * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2377   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Path.html">`Path`</a>
2378   * class will point to the `CanvasPath` class.
2379   *
2380   * @module graphics
2381   * @class CanvasPath
2382   * @extends CanvasShape
2383   */
2384  CanvasPath = function()
2385  {
2386      CanvasPath.superclass.constructor.apply(this, arguments);
2387  };
2388  CanvasPath.NAME = "path";
2389  Y.extend(CanvasPath, Y.CanvasShape, {
2390      /**
2391       * Indicates the type of shape
2392       *
2393       * @property _type
2394       * @type String
2395       * @private
2396       */
2397      _type: "path",
2398  
2399      /**
2400       * Draws the shape.
2401       *
2402       * @method _draw
2403       * @private
2404       */
2405      _draw: function()
2406      {
2407          this._closePath();
2408          this._updateTransform();
2409      },
2410  
2411      /**
2412       * Creates the dom node for the shape.
2413       *
2414       * @method createNode
2415       * @return HTMLElement
2416       * @private
2417       */
2418      createNode: function()
2419      {
2420          var host = this,
2421              node = Y.config.doc.createElement('canvas'),
2422              name = host.name,
2423              concat = host._camelCaseConcat,
2424              id = host.get("id");
2425          host._context = node.getContext('2d');
2426          node.setAttribute("overflow", "visible");
2427          node.setAttribute("pointer-events", "none");
2428          node.style.pointerEvents = "none";
2429          node.style.overflow = "visible";
2430          node.setAttribute("id", id);
2431          id = "#" + id;
2432          host.node = node;
2433          host.addClass(
2434              _getClassName(SHAPE) +
2435              " " +
2436              _getClassName(concat(IMPLEMENTATION, SHAPE)) +
2437              " " +
2438              _getClassName(name) +
2439              " " +
2440              _getClassName(concat(IMPLEMENTATION, name))
2441          );
2442      },
2443  
2444      /**
2445       * Completes a drawing operation.
2446       *
2447       * @method end
2448       */
2449      end: function()
2450      {
2451          this._draw();
2452          return this;
2453      }
2454  });
2455  
2456  CanvasPath.ATTRS = Y.merge(Y.CanvasShape.ATTRS, {
2457      /**
2458       * Indicates the width of the shape
2459       *
2460       * @config width
2461       * @type Number
2462       */
2463      width: {
2464          getter: function()
2465          {
2466              var offset = this._stroke && this._strokeWeight ? (this._strokeWeight * 2) : 0;
2467              return this._width - offset;
2468          },
2469  
2470          setter: function(val)
2471          {
2472              this._width = val;
2473              return val;
2474          }
2475      },
2476  
2477      /**
2478       * Indicates the height of the shape
2479       *
2480       * @config height
2481       * @type Number
2482       */
2483      height: {
2484          getter: function()
2485          {
2486              var offset = this._stroke && this._strokeWeight ? (this._strokeWeight * 2) : 0;
2487              return this._height - offset;
2488          },
2489  
2490          setter: function(val)
2491          {
2492              this._height = val;
2493              return val;
2494          }
2495      },
2496  
2497      /**
2498       * Indicates the path used for the node.
2499       *
2500       * @config path
2501       * @type String
2502       * @readOnly
2503       */
2504      path: {
2505          readOnly: true,
2506  
2507          getter: function()
2508          {
2509              return this._path;
2510          }
2511      }
2512  });
2513  Y.CanvasPath = CanvasPath;
2514  /**
2515   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Rect.html">`Rect`</a> class.
2516   * `CanvasRect` is not intended to be used directly. Instead, use the <a href="Rect.html">`Rect`</a> class.
2517   * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2518   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Rect.html">`Rect`</a>
2519   * class will point to the `CanvasRect` class.
2520   *
2521   * @module graphics
2522   * @class CanvasRect
2523   * @constructor
2524   */
2525  CanvasRect = function()
2526  {
2527      CanvasRect.superclass.constructor.apply(this, arguments);
2528  };
2529  CanvasRect.NAME = "rect";
2530  Y.extend(CanvasRect, Y.CanvasShape, {
2531      /**
2532       * Indicates the type of shape
2533       *
2534       * @property _type
2535       * @type String
2536       * @private
2537       */
2538      _type: "rect",
2539  
2540      /**
2541       * Draws the shape.
2542       *
2543       * @method _draw
2544       * @private
2545       */
2546      _draw: function()
2547      {
2548          var w = this.get("width"),
2549              h = this.get("height");
2550          this.clear();
2551          this.drawRect(0, 0, w, h);
2552          this._closePath();
2553      }
2554  });
2555  CanvasRect.ATTRS = Y.CanvasShape.ATTRS;
2556  Y.CanvasRect = CanvasRect;
2557  /**
2558   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Ellipse.html">`Ellipse`</a> class.
2559   * `CanvasEllipse` is not intended to be used directly. Instead, use the <a href="Ellipse.html">`Ellipse`</a> class.
2560   * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2561   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Ellipse.html">`Ellipse`</a>
2562   * class will point to the `CanvasEllipse` class.
2563   *
2564   * @module graphics
2565   * @class CanvasEllipse
2566   * @constructor
2567   */
2568  CanvasEllipse = function()
2569  {
2570      CanvasEllipse.superclass.constructor.apply(this, arguments);
2571  };
2572  
2573  CanvasEllipse.NAME = "ellipse";
2574  
2575  Y.extend(CanvasEllipse, CanvasShape, {
2576      /**
2577       * Indicates the type of shape
2578       *
2579       * @property _type
2580       * @type String
2581       * @private
2582       */
2583      _type: "ellipse",
2584  
2585      /**
2586       * Draws the shape.
2587       *
2588       * @method _draw
2589       * @private
2590       */
2591      _draw: function()
2592      {
2593          var w = this.get("width"),
2594              h = this.get("height");
2595          this.clear();
2596          this.drawEllipse(0, 0, w, h);
2597          this._closePath();
2598      }
2599  });
2600  CanvasEllipse.ATTRS = Y.merge(CanvasShape.ATTRS, {
2601      /**
2602       * Horizontal radius for the ellipse.
2603       *
2604       * @config xRadius
2605       * @type Number
2606       */
2607      xRadius: {
2608          setter: function(val)
2609          {
2610              this.set("width", val * 2);
2611          },
2612  
2613          getter: function()
2614          {
2615              var val = this.get("width");
2616              if(val)
2617              {
2618                  val *= 0.5;
2619              }
2620              return val;
2621          }
2622      },
2623  
2624      /**
2625       * Vertical radius for the ellipse.
2626       *
2627       * @config yRadius
2628       * @type Number
2629       * @readOnly
2630       */
2631      yRadius: {
2632          setter: function(val)
2633          {
2634              this.set("height", val * 2);
2635          },
2636  
2637          getter: function()
2638          {
2639              var val = this.get("height");
2640              if(val)
2641              {
2642                  val *= 0.5;
2643              }
2644              return val;
2645          }
2646      }
2647  });
2648  Y.CanvasEllipse = CanvasEllipse;
2649  /**
2650   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Circle.html">`Circle`</a> class.
2651   * `CanvasCircle` is not intended to be used directly. Instead, use the <a href="Circle.html">`Circle`</a> class.
2652   * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2653   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Circle.html">`Circle`</a>
2654   * class will point to the `CanvasCircle` class.
2655   *
2656   * @module graphics
2657   * @class CanvasCircle
2658   * @constructor
2659   */
2660  CanvasCircle = function()
2661  {
2662      CanvasCircle.superclass.constructor.apply(this, arguments);
2663  };
2664  
2665  CanvasCircle.NAME = "circle";
2666  
2667  Y.extend(CanvasCircle, Y.CanvasShape, {
2668      /**
2669       * Indicates the type of shape
2670       *
2671       * @property _type
2672       * @type String
2673       * @private
2674       */
2675      _type: "circle",
2676  
2677      /**
2678       * Draws the shape.
2679       *
2680       * @method _draw
2681       * @private
2682       */
2683      _draw: function()
2684      {
2685          var radius = this.get("radius");
2686          if(radius)
2687          {
2688              this.clear();
2689              this.drawCircle(0, 0, radius);
2690              this._closePath();
2691          }
2692      }
2693  });
2694  
2695  CanvasCircle.ATTRS = Y.merge(Y.CanvasShape.ATTRS, {
2696      /**
2697       * Indicates the width of the shape
2698       *
2699       * @config width
2700       * @type Number
2701       */
2702      width: {
2703          setter: function(val)
2704          {
2705              this.set("radius", val/2);
2706              return val;
2707          },
2708  
2709          getter: function()
2710          {
2711              return this.get("radius") * 2;
2712          }
2713      },
2714  
2715      /**
2716       * Indicates the height of the shape
2717       *
2718       * @config height
2719       * @type Number
2720       */
2721      height: {
2722          setter: function(val)
2723          {
2724              this.set("radius", val/2);
2725              return val;
2726          },
2727  
2728          getter: function()
2729          {
2730              return this.get("radius") * 2;
2731          }
2732      },
2733  
2734      /**
2735       * Radius of the circle
2736       *
2737       * @config radius
2738       * @type Number
2739       */
2740      radius: {
2741          lazyAdd: false
2742      }
2743  });
2744  Y.CanvasCircle = CanvasCircle;
2745  /**
2746   * Draws pie slices
2747   *
2748   * @module graphics
2749   * @class CanvasPieSlice
2750   * @constructor
2751   */
2752  CanvasPieSlice = function()
2753  {
2754      CanvasPieSlice.superclass.constructor.apply(this, arguments);
2755  };
2756  CanvasPieSlice.NAME = "canvasPieSlice";
2757  Y.extend(CanvasPieSlice, Y.CanvasShape, {
2758      /**
2759       * Indicates the type of shape
2760       *
2761       * @property _type
2762       * @type String
2763       * @private
2764       */
2765      _type: "path",
2766  
2767      /**
2768       * Change event listener
2769       *
2770       * @private
2771       * @method _updateHandler
2772       */
2773      _draw: function()
2774      {
2775          var x = this.get("cx"),
2776              y = this.get("cy"),
2777              startAngle = this.get("startAngle"),
2778              arc = this.get("arc"),
2779              radius = this.get("radius");
2780          this.clear();
2781          this._left = x;
2782          this._right = radius;
2783          this._top = y;
2784          this._bottom = radius;
2785          this.drawWedge(x, y, startAngle, arc, radius);
2786          this.end();
2787      }
2788   });
2789  CanvasPieSlice.ATTRS = Y.mix({
2790      cx: {
2791          value: 0
2792      },
2793  
2794      cy: {
2795          value: 0
2796      },
2797      /**
2798       * Starting angle in relation to a circle in which to begin the pie slice drawing.
2799       *
2800       * @config startAngle
2801       * @type Number
2802       */
2803      startAngle: {
2804          value: 0
2805      },
2806  
2807      /**
2808       * Arc of the slice.
2809       *
2810       * @config arc
2811       * @type Number
2812       */
2813      arc: {
2814          value: 0
2815      },
2816  
2817      /**
2818       * Radius of the circle in which the pie slice is drawn
2819       *
2820       * @config radius
2821       * @type Number
2822       */
2823      radius: {
2824          value: 0
2825      }
2826  }, Y.CanvasShape.ATTRS);
2827  Y.CanvasPieSlice = CanvasPieSlice;
2828  /**
2829   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the `Graphic` class.
2830   * `CanvasGraphic` is not intended to be used directly. Instead, use the <a href="Graphic.html">`Graphic`</a> class.
2831   * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
2832   * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Graphic.html">`Graphic`</a>
2833   * class will point to the `CanvasGraphic` class.
2834   *
2835   * @module graphics
2836   * @class CanvasGraphic
2837   * @constructor
2838   */
2839  function CanvasGraphic() {
2840  
2841      CanvasGraphic.superclass.constructor.apply(this, arguments);
2842  }
2843  
2844  CanvasGraphic.NAME = "canvasGraphic";
2845  
2846  CanvasGraphic.ATTRS = {
2847      /**
2848       * Whether or not to render the `Graphic` automatically after to a specified parent node after init. This can be a Node
2849       * instance or a CSS selector string.
2850       *
2851       * @config render
2852       * @type Node | String
2853       */
2854      render: {},
2855  
2856      /**
2857       * Unique id for class instance.
2858       *
2859       * @config id
2860       * @type String
2861       */
2862      id: {
2863          valueFn: function()
2864          {
2865              return Y.guid();
2866          },
2867  
2868          setter: function(val)
2869          {
2870              var node = this._node;
2871              if(node)
2872              {
2873                  node.setAttribute("id", val);
2874              }
2875              return val;
2876          }
2877      },
2878  
2879      /**
2880       * Key value pairs in which a shape instance is associated with its id.
2881       *
2882       *  @config shapes
2883       *  @type Object
2884       *  @readOnly
2885       */
2886      shapes: {
2887          readOnly: true,
2888  
2889          getter: function()
2890          {
2891              return this._shapes;
2892          }
2893      },
2894  
2895      /**
2896       *  Object containing size and coordinate data for the content of a Graphic in relation to the graphic instance's position.
2897       *
2898       *  @config contentBounds
2899       *  @type Object
2900       *  @readOnly
2901       */
2902      contentBounds: {
2903          readOnly: true,
2904  
2905          getter: function()
2906          {
2907              return this._contentBounds;
2908          }
2909      },
2910  
2911      /**
2912       *  The outermost html element of the Graphic instance.
2913       *
2914       *  @config node
2915       *  @type HTMLElement
2916       *  @readOnly
2917       */
2918      node: {
2919          readOnly: true,
2920  
2921          getter: function()
2922          {
2923              return this._node;
2924          }
2925      },
2926  
2927      /**
2928       * Indicates the width of the `Graphic`.
2929       *
2930       * @config width
2931       * @type Number
2932       */
2933      width: {
2934          setter: function(val)
2935          {
2936              if(this._node)
2937              {
2938                  this._node.style.width = val + "px";
2939              }
2940              return val;
2941          }
2942      },
2943  
2944      /**
2945       * Indicates the height of the `Graphic`.
2946       *
2947       * @config height
2948       * @type Number
2949       */
2950      height: {
2951          setter: function(val)
2952          {
2953              if(this._node)
2954              {
2955                  this._node.style.height = val + "px";
2956              }
2957              return val;
2958          }
2959      },
2960  
2961      /**
2962       *  Determines the sizing of the Graphic.
2963       *
2964       *  <dl>
2965       *      <dt>sizeContentToGraphic</dt><dd>The Graphic's width and height attributes are, either explicitly set through the
2966       *      <code>width</code> and <code>height</code> attributes or are determined by the dimensions of the parent element. The
2967       *      content contained in the Graphic will be sized to fit with in the Graphic instance's dimensions. When using this
2968       *      setting, the <code>preserveAspectRatio</code> attribute will determine how the contents are sized.</dd>
2969       *      <dt>sizeGraphicToContent</dt><dd>(Also accepts a value of true) The Graphic's width and height are determined by the
2970       *      size and positioning of the content.</dd>
2971       *      <dt>false</dt><dd>The Graphic's width and height attributes are, either explicitly set through the <code>width</code>
2972       *      and <code>height</code> attributes or are determined by the dimensions of the parent element. The contents of the
2973       *      Graphic instance are not affected by this setting.</dd>
2974       *  </dl>
2975       *
2976       *
2977       *  @config autoSize
2978       *  @type Boolean | String
2979       *  @default false
2980       */
2981      autoSize: {
2982          value: false
2983      },
2984  
2985      /**
2986       * Determines how content is sized when <code>autoSize</code> is set to <code>sizeContentToGraphic</code>.
2987       *
2988       *  <dl>
2989       *      <dt>none<dt><dd>Do not force uniform scaling. Scale the graphic content of the given element non-uniformly if necessary
2990       *      such that the element's bounding box exactly matches the viewport rectangle.</dd>
2991       *      <dt>xMinYMin</dt><dd>Force uniform scaling position along the top left of the Graphic's node.</dd>
2992       *      <dt>xMidYMin</dt><dd>Force uniform scaling horizontally centered and positioned at the top of the Graphic's node.<dd>
2993       *      <dt>xMaxYMin</dt><dd>Force uniform scaling positioned horizontally from the right and vertically from the top.</dd>
2994       *      <dt>xMinYMid</dt>Force uniform scaling positioned horizontally from the left and vertically centered.</dd>
2995       *      <dt>xMidYMid (the default)</dt><dd>Force uniform scaling with the content centered.</dd>
2996       *      <dt>xMaxYMid</dt><dd>Force uniform scaling positioned horizontally from the right and vertically centered.</dd>
2997       *      <dt>xMinYMax</dt><dd>Force uniform scaling positioned horizontally from the left and vertically from the bottom.</dd>
2998       *      <dt>xMidYMax</dt><dd>Force uniform scaling horizontally centered and position vertically from the bottom.</dd>
2999       *      <dt>xMaxYMax</dt><dd>Force uniform scaling positioned horizontally from the right and vertically from the bottom.</dd>
3000       *  </dl>
3001       *
3002       * @config preserveAspectRatio
3003       * @type String
3004       * @default xMidYMid
3005       */
3006      preserveAspectRatio: {
3007          value: "xMidYMid"
3008      },
3009  
3010      /**
3011       * The contentBounds will resize to greater values but not smaller values. (for performance)
3012       * When resizing the contentBounds down is desirable, set the resizeDown value to true.
3013       *
3014       * @config resizeDown
3015       * @type Boolean
3016       */
3017      resizeDown: {
3018          value: false
3019      },
3020  
3021      /**
3022       * Indicates the x-coordinate for the instance.
3023       *
3024       * @config x
3025       * @type Number
3026       */
3027      x: {
3028          getter: function()
3029          {
3030              return this._x;
3031          },
3032  
3033          setter: function(val)
3034          {
3035              this._x = val;
3036              if(this._node)
3037              {
3038                  this._node.style.left = val + "px";
3039              }
3040              return val;
3041          }
3042      },
3043  
3044      /**
3045       * Indicates the y-coordinate for the instance.
3046       *
3047       * @config y
3048       * @type Number
3049       */
3050      y: {
3051          getter: function()
3052          {
3053              return this._y;
3054          },
3055  
3056          setter: function(val)
3057          {
3058              this._y = val;
3059              if(this._node)
3060              {
3061                  this._node.style.top = val + "px";
3062              }
3063              return val;
3064          }
3065      },
3066  
3067      /**
3068       * Indicates whether or not the instance will automatically redraw after a change is made to a shape.
3069       * This property will get set to false when batching operations.
3070       *
3071       * @config autoDraw
3072       * @type Boolean
3073       * @default true
3074       * @private
3075       */
3076      autoDraw: {
3077          value: true
3078      },
3079  
3080      /**
3081       * Indicates whether the `Graphic` and its children are visible.
3082       *
3083       * @config visible
3084       * @type Boolean
3085       */
3086      visible: {
3087          value: true,
3088  
3089          setter: function(val)
3090          {
3091              this._toggleVisible(val);
3092              return val;
3093          }
3094      }
3095  };
3096  
3097  Y.extend(CanvasGraphic, Y.GraphicBase, {
3098      /**
3099       * Sets the value of an attribute.
3100       *
3101       * @method set
3102       * @param {String|Object} name The name of the attribute. Alternatively, an object of key value pairs can
3103       * be passed in to set multiple attributes at once.
3104       * @param {Any} value The value to set the attribute to. This value is ignored if an object is received as
3105       * the name param.
3106       */
3107      set: function()
3108      {
3109          var host = this,
3110              attr = arguments[0],
3111              redrawAttrs = {
3112                  autoDraw: true,
3113                  autoSize: true,
3114                  preserveAspectRatio: true,
3115                  resizeDown: true
3116              },
3117              key,
3118              forceRedraw = false;
3119          AttributeLite.prototype.set.apply(host, arguments);
3120          if(host._state.autoDraw === true && Y.Object.size(this._shapes) > 0)
3121          {
3122              if(Y_LANG.isString && redrawAttrs[attr])
3123              {
3124                  forceRedraw = true;
3125              }
3126              else if(Y_LANG.isObject(attr))
3127              {
3128                  for(key in redrawAttrs)
3129                  {
3130                      if(redrawAttrs.hasOwnProperty(key) && attr[key])
3131                      {
3132                          forceRedraw = true;
3133                          break;
3134                      }
3135                  }
3136              }
3137          }
3138          if(forceRedraw)
3139          {
3140              host._redraw();
3141          }
3142      },
3143  
3144      /**
3145       * Storage for `x` attribute.
3146       *
3147       * @property _x
3148       * @type Number
3149       * @private
3150       */
3151      _x: 0,
3152  
3153      /**
3154       * Storage for `y` attribute.
3155       *
3156       * @property _y
3157       * @type Number
3158       * @private
3159       */
3160      _y: 0,
3161  
3162      /**
3163       * Gets the current position of the graphic instance in page coordinates.
3164       *
3165       * @method getXY
3166       * @return Array The XY position of the shape.
3167       */
3168      getXY: function()
3169      {
3170          var node = this._node,
3171              xy;
3172          if(node)
3173          {
3174              xy = Y.DOM.getXY(node);
3175          }
3176          return xy;
3177      },
3178  
3179      /**
3180       * Initializes the class.
3181       *
3182       * @method initializer
3183       * @param {Object} config Optional attributes
3184       * @private
3185       */
3186      initializer: function() {
3187          var render = this.get("render"),
3188              visibility = this.get("visible") ? "visible" : "hidden",
3189              w = this.get("width") || 0,
3190              h = this.get("height") || 0;
3191          this._shapes = {};
3192          this._redrawQueue = {};
3193          this._contentBounds = {
3194              left: 0,
3195              top: 0,
3196              right: 0,
3197              bottom: 0
3198          };
3199          this._node = DOCUMENT.createElement('div');
3200          this._node.style.position = "absolute";
3201          this._node.style.visibility = visibility;
3202          this.set("width", w);
3203          this.set("height", h);
3204          if(render)
3205          {
3206              this.render(render);
3207          }
3208      },
3209  
3210      /**
3211       * Adds the graphics node to the dom.
3212       *
3213       * @method render
3214       * @param {HTMLElement} parentNode node in which to render the graphics node into.
3215       */
3216      render: function(render) {
3217          var parentNode = render || DOCUMENT.body,
3218              node = this._node,
3219              w,
3220              h;
3221          if(render instanceof Y.Node)
3222          {
3223              parentNode = render._node;
3224          }
3225          else if(Y.Lang.isString(render))
3226          {
3227              parentNode = Y.Selector.query(render, DOCUMENT.body, true);
3228          }
3229          w = this.get("width") || parseInt(Y.DOM.getComputedStyle(parentNode, "width"), 10);
3230          h = this.get("height") || parseInt(Y.DOM.getComputedStyle(parentNode, "height"), 10);
3231          parentNode.appendChild(node);
3232          node.style.display = "block";
3233          node.style.position = "absolute";
3234          node.style.left = this.get("x") + "px";
3235          node.style.top = this.get("y") + "px";
3236          this.set("width", w);
3237          this.set("height", h);
3238          this.parentNode = parentNode;
3239          return this;
3240      },
3241  
3242      /**
3243       * Removes all nodes.
3244       *
3245       * @method destroy
3246       */
3247      destroy: function()
3248      {
3249          this.removeAllShapes();
3250          if(this._node)
3251          {
3252              this._removeChildren(this._node);
3253              if(this._node.parentNode)
3254              {
3255                  this._node.parentNode.removeChild(this._node);
3256              }
3257              this._node = null;
3258          }
3259      },
3260  
3261      /**
3262       * Generates a shape instance by type.
3263       *
3264       * @method addShape
3265       * @param {Object} cfg attributes for the shape
3266       * @return Shape
3267       */
3268      addShape: function(cfg)
3269      {
3270          cfg.graphic = this;
3271          if(!this.get("visible"))
3272          {
3273              cfg.visible = false;
3274          }
3275          var ShapeClass = this._getShapeClass(cfg.type),
3276              shape = new ShapeClass(cfg);
3277          this._appendShape(shape);
3278          return shape;
3279      },
3280  
3281      /**
3282       * Adds a shape instance to the graphic instance.
3283       *
3284       * @method _appendShape
3285       * @param {Shape} shape The shape instance to be added to the graphic.
3286       * @private
3287       */
3288      _appendShape: function(shape)
3289      {
3290          var node = shape.node,
3291              parentNode = this._frag || this._node;
3292          if(this.get("autoDraw"))
3293          {
3294              parentNode.appendChild(node);
3295          }
3296          else
3297          {
3298              this._getDocFrag().appendChild(node);
3299          }
3300      },
3301  
3302      /**
3303       * Removes a shape instance from from the graphic instance.
3304       *
3305       * @method removeShape
3306       * @param {Shape|String} shape The instance or id of the shape to be removed.
3307       */
3308      removeShape: function(shape)
3309      {
3310          if(!(shape instanceof CanvasShape))
3311          {
3312              if(Y_LANG.isString(shape))
3313              {
3314                  shape = this._shapes[shape];
3315              }
3316          }
3317          if(shape && shape instanceof CanvasShape)
3318          {
3319              shape._destroy();
3320              delete this._shapes[shape.get("id")];
3321          }
3322          if(this.get("autoDraw"))
3323          {
3324              this._redraw();
3325          }
3326          return shape;
3327      },
3328  
3329      /**
3330       * Removes all shape instances from the dom.
3331       *
3332       * @method removeAllShapes
3333       */
3334      removeAllShapes: function()
3335      {
3336          var shapes = this._shapes,
3337              i;
3338          for(i in shapes)
3339          {
3340              if(shapes.hasOwnProperty(i))
3341              {
3342                  shapes[i].destroy();
3343              }
3344          }
3345          this._shapes = {};
3346      },
3347  
3348      /**
3349       * Clears the graphics object.
3350       *
3351       * @method clear
3352       */
3353      clear: function() {
3354          this.removeAllShapes();
3355      },
3356  
3357      /**
3358       * Removes all child nodes.
3359       *
3360       * @method _removeChildren
3361       * @param {HTMLElement} node
3362       * @private
3363       */
3364      _removeChildren: function(node)
3365      {
3366          if(node && node.hasChildNodes())
3367          {
3368              var child;
3369              while(node.firstChild)
3370              {
3371                  child = node.firstChild;
3372                  this._removeChildren(child);
3373                  node.removeChild(child);
3374              }
3375          }
3376      },
3377  
3378      /**
3379       * Toggles visibility
3380       *
3381       * @method _toggleVisible
3382       * @param {Boolean} val indicates visibilitye
3383       * @private
3384       */
3385      _toggleVisible: function(val)
3386      {
3387          var i,
3388              shapes = this._shapes,
3389              visibility = val ? "visible" : "hidden";
3390          if(shapes)
3391          {
3392              for(i in shapes)
3393              {
3394                  if(shapes.hasOwnProperty(i))
3395                  {
3396                      shapes[i].set("visible", val);
3397                  }
3398              }
3399          }
3400          if(this._node)
3401          {
3402              this._node.style.visibility = visibility;
3403          }
3404      },
3405  
3406      /**
3407       * Returns a shape class. Used by `addShape`.
3408       *
3409       * @method _getShapeClass
3410       * @param {Shape | String} val Indicates which shape class.
3411       * @return Function
3412       * @private
3413       */
3414      _getShapeClass: function(val)
3415      {
3416          var shape = this._shapeClass[val];
3417          if(shape)
3418          {
3419              return shape;
3420          }
3421          return val;
3422      },
3423  
3424      /**
3425       * Look up for shape classes. Used by `addShape` to retrieve a class for instantiation.
3426       *
3427       * @property _shapeClass
3428       * @type Object
3429       * @private
3430       */
3431      _shapeClass: {
3432          circle: Y.CanvasCircle,
3433          rect: Y.CanvasRect,
3434          path: Y.CanvasPath,
3435          ellipse: Y.CanvasEllipse,
3436          pieslice: Y.CanvasPieSlice
3437      },
3438  
3439      /**
3440       * Returns a shape based on the id of its dom node.
3441       *
3442       * @method getShapeById
3443       * @param {String} id Dom id of the shape's node attribute.
3444       * @return Shape
3445       */
3446      getShapeById: function(id)
3447      {
3448          var shape = this._shapes[id];
3449          return shape;
3450      },
3451  
3452      /**
3453       * Allows for creating multiple shapes in order to batch appending and redraw operations.
3454       *
3455       * @method batch
3456       * @param {Function} method Method to execute.
3457       */
3458      batch: function(method)
3459      {
3460          var autoDraw = this.get("autoDraw");
3461          this.set("autoDraw", false);
3462          method();
3463          this.set("autoDraw", autoDraw);
3464      },
3465  
3466      /**
3467       * Returns a document fragment to for attaching shapes.
3468       *
3469       * @method _getDocFrag
3470       * @return DocumentFragment
3471       * @private
3472       */
3473      _getDocFrag: function()
3474      {
3475          if(!this._frag)
3476          {
3477              this._frag = DOCUMENT.createDocumentFragment();
3478          }
3479          return this._frag;
3480      },
3481  
3482      /**
3483       * Redraws all shapes.
3484       *
3485       * @method _redraw
3486       * @private
3487       */
3488      _redraw: function()
3489      {
3490          var autoSize = this.get("autoSize"),
3491              preserveAspectRatio = this.get("preserveAspectRatio"),
3492              box = this.get("resizeDown") ? this._getUpdatedContentBounds() : this._contentBounds,
3493              contentWidth,
3494              contentHeight,
3495              w,
3496              h,
3497              xScale,
3498              yScale,
3499              translateX = 0,
3500              translateY = 0,
3501              matrix,
3502              node = this.get("node");
3503          if(autoSize)
3504          {
3505              if(autoSize === "sizeContentToGraphic")
3506              {
3507                  contentWidth = box.right - box.left;
3508                  contentHeight = box.bottom - box.top;
3509                  w = parseFloat(Y_DOM.getComputedStyle(node, "width"));
3510                  h = parseFloat(Y_DOM.getComputedStyle(node, "height"));
3511                  matrix = new Y.Matrix();
3512                  if(preserveAspectRatio === "none")
3513                  {
3514                      xScale = w/contentWidth;
3515                      yScale = h/contentHeight;
3516                  }
3517                  else
3518                  {
3519                      if(contentWidth/contentHeight !== w/h)
3520                      {
3521                          if(contentWidth * h/contentHeight > w)
3522                          {
3523                              xScale = yScale = w/contentWidth;
3524                              translateY = this._calculateTranslate(preserveAspectRatio.slice(5).toLowerCase(), contentHeight * w/contentWidth, h);
3525                          }
3526                          else
3527                          {
3528                              xScale = yScale = h/contentHeight;
3529                              translateX = this._calculateTranslate(preserveAspectRatio.slice(1, 4).toLowerCase(), contentWidth * h/contentHeight, w);
3530                          }
3531                      }
3532                  }
3533                  Y_DOM.setStyle(node, "transformOrigin", "0% 0%");
3534                  translateX = translateX - (box.left * xScale);
3535                  translateY = translateY - (box.top * yScale);
3536                  matrix.translate(translateX, translateY);
3537                  matrix.scale(xScale, yScale);
3538                  Y_DOM.setStyle(node, "transform", matrix.toCSSText());
3539              }
3540              else
3541              {
3542                  this.set("width", box.right);
3543                  this.set("height", box.bottom);
3544              }
3545          }
3546          if(this._frag)
3547          {
3548              this._node.appendChild(this._frag);
3549              this._frag = null;
3550          }
3551      },
3552  
3553      /**
3554       * Determines the value for either an x or y value to be used for the <code>translate</code> of the Graphic.
3555       *
3556       * @method _calculateTranslate
3557       * @param {String} position The position for placement. Possible values are min, mid and max.
3558       * @param {Number} contentSize The total size of the content.
3559       * @param {Number} boundsSize The total size of the Graphic.
3560       * @return Number
3561       * @private
3562       */
3563      _calculateTranslate: function(position, contentSize, boundsSize)
3564      {
3565          var ratio = boundsSize - contentSize,
3566              coord;
3567          switch(position)
3568          {
3569              case "mid" :
3570                  coord = ratio * 0.5;
3571              break;
3572              case "max" :
3573                  coord = ratio;
3574              break;
3575              default :
3576                  coord = 0;
3577              break;
3578          }
3579          return coord;
3580      },
3581  
3582      /**
3583       * Adds a shape to the redraw queue and calculates the contentBounds. Used internally
3584       * by `Shape` instances.
3585       *
3586       * @method addToRedrawQueue
3587       * @param Shape shape The shape instance to add to the queue
3588       * @protected
3589       */
3590      addToRedrawQueue: function(shape)
3591      {
3592          var shapeBox,
3593              box;
3594          this._shapes[shape.get("id")] = shape;
3595          if(!this.get("resizeDown"))
3596          {
3597              shapeBox = shape.getBounds();
3598              box = this._contentBounds;
3599              box.left = box.left < shapeBox.left ? box.left : shapeBox.left;
3600              box.top = box.top < shapeBox.top ? box.top : shapeBox.top;
3601              box.right = box.right > shapeBox.right ? box.right : shapeBox.right;
3602              box.bottom = box.bottom > shapeBox.bottom ? box.bottom : shapeBox.bottom;
3603              this._contentBounds = box;
3604          }
3605          if(this.get("autoDraw"))
3606          {
3607              this._redraw();
3608          }
3609      },
3610  
3611      /**
3612       * Recalculates and returns the `contentBounds` for the `Graphic` instance.
3613       *
3614       * @method _getUpdatedContentBounds
3615       * @return {Object}
3616       * @private
3617       */
3618      _getUpdatedContentBounds: function()
3619      {
3620          var bounds,
3621              i,
3622              shape,
3623              queue = this._shapes,
3624              box = {};
3625          for(i in queue)
3626          {
3627              if(queue.hasOwnProperty(i))
3628              {
3629                  shape = queue[i];
3630                  bounds = shape.getBounds();
3631                  box.left = Y_LANG.isNumber(box.left) ? Math.min(box.left, bounds.left) : bounds.left;
3632                  box.top = Y_LANG.isNumber(box.top) ? Math.min(box.top, bounds.top) : bounds.top;
3633                  box.right = Y_LANG.isNumber(box.right) ? Math.max(box.right, bounds.right) : bounds.right;
3634                  box.bottom = Y_LANG.isNumber(box.bottom) ? Math.max(box.bottom, bounds.bottom) : bounds.bottom;
3635              }
3636          }
3637          box.left = Y_LANG.isNumber(box.left) ? box.left : 0;
3638          box.top = Y_LANG.isNumber(box.top) ? box.top : 0;
3639          box.right = Y_LANG.isNumber(box.right) ? box.right : 0;
3640          box.bottom = Y_LANG.isNumber(box.bottom) ? box.bottom : 0;
3641          this._contentBounds = box;
3642          return box;
3643      },
3644  
3645      /**
3646       * Inserts shape on the top of the tree.
3647       *
3648       * @method _toFront
3649       * @param {CanvasShape} Shape to add.
3650       * @private
3651       */
3652      _toFront: function(shape)
3653      {
3654          var contentNode = this.get("node");
3655          if(shape instanceof Y.CanvasShape)
3656          {
3657              shape = shape.get("node");
3658          }
3659          if(contentNode && shape)
3660          {
3661              contentNode.appendChild(shape);
3662          }
3663      },
3664  
3665      /**
3666       * Inserts shape as the first child of the content node.
3667       *
3668       * @method _toBack
3669       * @param {CanvasShape} Shape to add.
3670       * @private
3671       */
3672      _toBack: function(shape)
3673      {
3674          var contentNode = this.get("node"),
3675              targetNode;
3676          if(shape instanceof Y.CanvasShape)
3677          {
3678              shape = shape.get("node");
3679          }
3680          if(contentNode && shape)
3681          {
3682              targetNode = contentNode.firstChild;
3683              if(targetNode)
3684              {
3685                  contentNode.insertBefore(shape, targetNode);
3686              }
3687              else
3688              {
3689                  contentNode.appendChild(shape);
3690              }
3691          }
3692      }
3693  });
3694  
3695  Y.CanvasGraphic = CanvasGraphic;
3696  
3697  
3698  }, '3.17.2', {"requires": ["graphics", "color-base"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1