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