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