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