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