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