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