3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
7 YUI.add('graphics-vml', function (Y, NAME) {
9 var IMPLEMENTATION = "vml",
11 SPLITPATHPATTERN = /[a-z][^a-z]*/ig,
12 SPLITARGSPATTERN = /[-]?[0-9]*[0-9|\.][0-9]*/g,
14 IS_NUM = Y_LANG.isNumber,
15 IS_ARRAY = Y_LANG.isArray,
16 IS_STRING = Y_LANG.isString,
18 Y_SELECTOR = Y.Selector,
19 DOCUMENT = Y.config.doc,
20 AttributeLite = Y.AttributeLite,
28 _getClassName = Y.ClassNameManager.getClassName;
30 function VMLDrawing() {}
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.
42 VMLDrawing.prototype = {
44 * Maps path to methods
46 * @property _pathSymbolToMethod
50 _pathSymbolToMethod: {
57 Q: "quadraticCurveTo",
58 q: "relativeQuadraticCurveTo",
64 * Value for rounding up to coordsize
66 * @property _coordSpaceMultiplier
70 _coordSpaceMultiplier: 100,
73 * Rounds dimensions and position values based on the coordinate space.
76 * @param {Number} The value for rounding
82 return Math.round(val * this._coordSpaceMultiplier);
86 * Concatanates the path.
89 * @param {String} val The value to add to the path string.
92 _addToPath: function(val)
94 this._path = this._path || "";
97 this._path += this._movePath;
98 this._movePath = null;
104 * Current x position of the drawing.
106 * @property _currentX
113 * Current y position of the drqwing.
115 * @property _currentY
122 * Draws a bezier curve.
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.
132 curveTo: function() {
133 this._curveTo.apply(this, [Y.Array(arguments), false]);
137 * Draws a bezier curve.
139 * @method relativeCurveTo
140 * @param {Number} cp1x x-coordinate for the first control point.
141 * @param {Number} cp1y y-coordinate for the first control point.
142 * @param {Number} cp2x x-coordinate for the second control point.
143 * @param {Number} cp2y y-coordinate for the second control point.
144 * @param {Number} x x-coordinate for the end point.
145 * @param {Number} y y-coordinate for the end point.
147 relativeCurveTo: function() {
148 this._curveTo.apply(this, [Y.Array(arguments), true]);
152 * Implements curveTo methods.
155 * @param {Array} args The arguments to be used.
156 * @param {Boolean} relative Indicates whether or not to use relative coordinates.
159 _curveTo: function(args, relative) {
176 command = relative ? " v " : " c ",
177 relativeX = relative ? parseFloat(this._currentX) : 0,
178 relativeY = relative ? parseFloat(this._currentY) : 0;
179 len = args.length - 5;
181 for(i = 0; i < len; i = i + 6)
183 cp1x = parseFloat(args[i]);
184 cp1y = parseFloat(args[i + 1]);
185 cp2x = parseFloat(args[i + 2]);
186 cp2y = parseFloat(args[i + 3]);
187 x = parseFloat(args[i + 4]);
188 y = parseFloat(args[i + 5]);
193 path = path + this._round(cp1x) + ", " + this._round(cp1y) + ", " + this._round(cp2x) + ", " + this._round(cp2y) + ", " + this._round(x) + ", " + this._round(y);
194 cp1x = cp1x + relativeX;
195 cp1y = cp1y + relativeY;
196 cp2x = cp2x + relativeX;
197 cp2y = cp2y + relativeY;
200 right = Math.max(x, Math.max(cp1x, cp2x));
201 bottom = Math.max(y, Math.max(cp1y, cp2y));
202 left = Math.min(x, Math.min(cp1x, cp2x));
203 top = Math.min(y, Math.min(cp1y, cp2y));
204 w = Math.abs(right - left);
205 h = Math.abs(bottom - top);
206 pts = [[this._currentX, this._currentY] , [cp1x, cp1y], [cp2x, cp2y], [x, y]];
207 this._setCurveBoundingBox(pts, w, h);
211 this._addToPath(path);
215 * Draws a quadratic bezier curve.
217 * @method quadraticCurveTo
218 * @param {Number} cpx x-coordinate for the control point.
219 * @param {Number} cpy y-coordinate for the control point.
220 * @param {Number} x x-coordinate for the end point.
221 * @param {Number} y y-coordinate for the end point.
223 quadraticCurveTo: function() {
224 this._quadraticCurveTo.apply(this, [Y.Array(arguments), false]);
228 * Draws a quadratic bezier curve relative to the current position.
230 * @method relativeQuadraticCurveTo
231 * @param {Number} cpx x-coordinate for the control point.
232 * @param {Number} cpy y-coordinate for the control point.
233 * @param {Number} x x-coordinate for the end point.
234 * @param {Number} y y-coordinate for the end point.
236 relativeQuadraticCurveTo: function() {
237 this._quadraticCurveTo.apply(this, [Y.Array(arguments), true]);
241 * Implements quadraticCurveTo methods.
243 * @method _quadraticCurveTo
244 * @param {Array} args The arguments to be used.
245 * @param {Boolean} relative Indicates whether or not to use relative coordinates.
248 _quadraticCurveTo: function(args, relative) {
257 currentX = this._currentX,
258 currentY = this._currentY,
260 len = args.length - 3,
262 relativeX = relative ? parseFloat(this._currentX) : 0,
263 relativeY = relative ? parseFloat(this._currentY) : 0;
264 for(i = 0; i < len; i = i + 4)
266 cpx = parseFloat(args[i]) + relativeX;
267 cpy = parseFloat(args[i + 1]) + relativeY;
268 x = parseFloat(args[i + 2]) + relativeX;
269 y = parseFloat(args[i + 3]) + relativeY;
270 cp1x = currentX + 0.67*(cpx - currentX);
271 cp1y = currentY + 0.67*(cpy - currentY);
272 cp2x = cp1x + (x - currentX) * 0.34;
273 cp2y = cp1y + (y - currentY) * 0.34;
274 bezierArgs.push(cp1x);
275 bezierArgs.push(cp1y);
276 bezierArgs.push(cp2x);
277 bezierArgs.push(cp2y);
281 this._curveTo.apply(this, [bezierArgs, false]);
288 * @param {Number} x x-coordinate
289 * @param {Number} y y-coordinate
290 * @param {Number} w width
291 * @param {Number} h height
293 drawRect: function(x, y, w, h) {
295 this.lineTo(x + w, y);
296 this.lineTo(x + w, y + h);
297 this.lineTo(x, y + h);
305 * Draws a rectangle with rounded corners.
308 * @param {Number} x x-coordinate
309 * @param {Number} y y-coordinate
310 * @param {Number} w width
311 * @param {Number} h height
312 * @param {Number} ew width of the ellipse used to draw the rounded corners
313 * @param {Number} eh height of the ellipse used to draw the rounded corners
315 drawRoundRect: function(x, y, w, h, ew, eh) {
316 this.moveTo(x, y + eh);
317 this.lineTo(x, y + h - eh);
318 this.quadraticCurveTo(x, y + h, x + ew, y + h);
319 this.lineTo(x + w - ew, y + h);
320 this.quadraticCurveTo(x + w, y + h, x + w, y + h - eh);
321 this.lineTo(x + w, y + eh);
322 this.quadraticCurveTo(x + w, y, x + w - ew, y);
323 this.lineTo(x + ew, y);
324 this.quadraticCurveTo(x, y, x, y + eh);
329 * Draws a circle. Used internally by `CanvasCircle` class.
332 * @param {Number} x y-coordinate
333 * @param {Number} y x-coordinate
334 * @param {Number} r radius
337 drawCircle: function(x, y, radius) {
343 this._drawingComplete = false;
344 this._trackSize(x + circum, y + circum);
345 this.moveTo((x + circum), (y + radius));
346 this._addToPath(" ae " + this._round(x + radius) + ", " + this._round(y + radius) + ", " + this._round(radius) + ", " + this._round(radius) + ", " + startAngle + ", " + endAngle);
353 * @method drawEllipse
354 * @param {Number} x x-coordinate
355 * @param {Number} y y-coordinate
356 * @param {Number} w width
357 * @param {Number} h height
360 drawEllipse: function(x, y, w, h) {
366 this._drawingComplete = false;
367 this._trackSize(x + w, y + h);
368 this.moveTo((x + w), (y + yRadius));
369 this._addToPath(" ae " + this._round(x + radius) + ", " + this._round(x + radius) + ", " + this._round(y + yRadius) + ", " + this._round(radius) + ", " + this._round(yRadius) + ", " + startAngle + ", " + endAngle);
376 * @method drawDiamond
377 * @param {Number} x y-coordinate
378 * @param {Number} y x-coordinate
379 * @param {Number} width width
380 * @param {Number} height height
383 drawDiamond: function(x, y, width, height)
385 var midWidth = width * 0.5,
386 midHeight = height * 0.5;
387 this.moveTo(x + midWidth, y);
388 this.lineTo(x + width, y + midHeight);
389 this.lineTo(x + midWidth, y + height);
390 this.lineTo(x, y + midHeight);
391 this.lineTo(x + midWidth, y);
399 * @param {Number} x x-coordinate of the wedge's center point
400 * @param {Number} y y-coordinate of the wedge's center point
401 * @param {Number} startAngle starting angle in degrees
402 * @param {Number} arc sweep of the wedge. Negative values draw clockwise.
403 * @param {Number} radius radius of wedge. If [optional] yRadius is defined, then radius is the x radius.
404 * @param {Number} yRadius [optional] y radius for wedge.
407 drawWedge: function(x, y, startAngle, arc, radius)
409 var diameter = radius * 2;
410 if(Math.abs(arc) > 360)
416 startAngle *= -65535;
418 startAngle = Math.round(startAngle);
419 arc = Math.round(arc);
421 this._addToPath(" ae " + this._round(x) + ", " + this._round(y) + ", " + this._round(radius) + " " + this._round(radius) + ", " + startAngle + ", " + arc);
422 this._trackSize(diameter, diameter);
427 * Draws a line segment from the current drawing position to the specified x and y coordinates.
430 * @param {Number} point1 x-coordinate for the end point.
431 * @param {Number} point2 y-coordinate for the end point.
435 this._lineTo.apply(this, [Y.Array(arguments), false]);
439 * Draws a line segment using the current line style from the current drawing position to the relative x and y coordinates.
441 * @method relativeLineTo
442 * @param {Number} point1 x-coordinate for the end point.
443 * @param {Number} point2 y-coordinate for the end point.
445 relativeLineTo: function()
447 this._lineTo.apply(this, [Y.Array(arguments), true]);
451 * Implements lineTo methods.
454 * @param {Array} args The arguments to be used.
455 * @param {Boolean} relative Indicates whether or not to use relative coordinates.
458 _lineTo: function(args, relative) {
459 var point1 = args[0],
464 path = relative ? " r " : " l ",
465 relativeX = relative ? parseFloat(this._currentX) : 0,
466 relativeY = relative ? parseFloat(this._currentY) : 0;
467 if (typeof point1 == "string" || typeof point1 == "number") {
468 len = args.length - 1;
469 for (i = 0; i < len; i = i + 2) {
470 x = parseFloat(args[i]);
471 y = parseFloat(args[i + 1]);
472 path += ' ' + this._round(x) + ', ' + this._round(y);
477 this._trackSize.apply(this, [x, y]);
483 for (i = 0; i < len; i = i + 1) {
484 x = parseFloat(args[i][0]);
485 y = parseFloat(args[i][1]);
486 path += ' ' + this._round(x) + ', ' + this._round(y);
491 this._trackSize.apply(this, [x, y]);
494 this._addToPath(path);
499 * Moves the current drawing position to specified x and y coordinates.
502 * @param {Number} x x-coordinate for the end point.
503 * @param {Number} y y-coordinate for the end point.
507 this._moveTo.apply(this, [Y.Array(arguments), false]);
511 * Moves the current drawing position relative to specified x and y coordinates.
513 * @method relativeMoveTo
514 * @param {Number} x x-coordinate for the end point.
515 * @param {Number} y y-coordinate for the end point.
517 relativeMoveTo: function()
519 this._moveTo.apply(this, [Y.Array(arguments), true]);
523 * Implements moveTo methods.
526 * @param {Array} args The arguments to be used.
527 * @param {Boolean} relative Indicates whether or not to use relative coordinates.
530 _moveTo: function(args, relative) {
531 var x = parseFloat(args[0]),
532 y = parseFloat(args[1]),
533 command = relative ? " t " : " m ",
534 relativeX = relative ? parseFloat(this._currentX) : 0,
535 relativeY = relative ? parseFloat(this._currentY) : 0;
536 this._movePath = command + this._round(x) + ", " + this._round(y);
539 this._trackSize(x, y);
550 _closePath: function()
552 var fill = this.get("fill"),
553 stroke = this.get("stroke"),
555 w = this.get("width"),
556 h = this.get("height"),
559 multiplier = this._coordSpaceMultiplier;
560 this._fillChangeHandler();
561 this._strokeChangeHandler();
564 if(fill && fill.color)
575 node.path = path + pathEnd;
577 if(!isNaN(w) && !isNaN(h))
579 node.coordOrigin = this._left + ", " + this._top;
580 node.coordSize = (w * multiplier) + ", " + (h * multiplier);
581 node.style.position = "absolute";
582 node.style.width = w + "px";
583 node.style.height = h + "px";
586 this._movePath = null;
587 this._updateTransform();
591 * Completes a drawing operation.
601 * Ends a fill and stroke
605 closePath: function()
607 this._addToPath(" x e");
624 this._movePath = null;
628 * Returns the points on a curve
630 * @method getBezierData
631 * @param Array points Array containing the begin, end and control points of a curve.
632 * @param Number t The value for incrementing the next set of points.
636 getBezierData: function(points, t) {
637 var n = points.length,
642 for (i = 0; i < n; ++i){
643 tmp[i] = [points[i][0], points[i][1]]; // save input
646 for (j = 1; j < n; ++j) {
647 for (i = 0; i < n - j; ++i) {
648 tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
649 tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
652 return [ tmp[0][0], tmp[0][1] ];
656 * Calculates the bounding box for a curve
658 * @method _setCurveBoundingBox
659 * @param Array pts Array containing points for start, end and control points of a curve.
660 * @param Number w Width used to calculate the number of points to describe the curve.
661 * @param Number h Height used to calculate the number of points to describe the curve.
664 _setCurveBoundingBox: function(pts, w, h)
667 left = this._currentX,
669 top = this._currentY,
671 len = Math.round(Math.sqrt((w * w) + (h * h))),
674 for(i = 0; i < len; ++i)
676 xy = this.getBezierData(pts, t * i);
677 left = isNaN(left) ? xy[0] : Math.min(xy[0], left);
678 right = isNaN(right) ? xy[0] : Math.max(xy[0], right);
679 top = isNaN(top) ? xy[1] : Math.min(xy[1], top);
680 bottom = isNaN(bottom) ? xy[1] : Math.max(xy[1], bottom);
682 left = Math.round(left * 10)/10;
683 right = Math.round(right * 10)/10;
684 top = Math.round(top * 10)/10;
685 bottom = Math.round(bottom * 10)/10;
686 this._trackSize(right, bottom);
687 this._trackSize(left, top);
691 * Updates the size of the graphics object
694 * @param {Number} w width
695 * @param {Number} h height
698 _trackSize: function(w, h) {
699 if (w > this._right) {
710 if (h > this._bottom)
714 this._width = this._right - this._left;
715 this._height = this._bottom - this._top;
730 Y.VMLDrawing = VMLDrawing;
732 * <a href="http://www.w3.org/TR/NOTE-VML">VML</a> implementation of the <a href="Shape.html">`Shape`</a> class.
733 * `VMLShape` is not intended to be used directly. Instead, use the <a href="Shape.html">`Shape`</a> class.
734 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> and <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a>
735 * capabilities, the <a href="Shape.html">`Shape`</a> class will point to the `VMLShape` class.
740 * @param {Object} cfg (optional) Attribute configs
742 VMLShape = function()
744 this._transforms = [];
745 this.matrix = new Y.Matrix();
746 this._normalizedMatrix = new Y.Matrix();
747 VMLShape.superclass.constructor.apply(this, arguments);
750 VMLShape.NAME = "shape";
752 Y.extend(VMLShape, Y.GraphicBase, Y.mix({
754 * Indicates the type of shape
763 * Init method, invoked during construction.
764 * Calls `initializer` method.
771 this.initializer.apply(this, arguments);
775 * Initializes the shape
778 * @method _initialize
780 initializer: function(cfg)
783 graphic = cfg.graphic,
784 data = this.get("data");
788 this._setGraphic(graphic);
792 host._parsePathData(data);
794 this._updateHandler();
798 * Set the Graphic instance for the shape.
800 * @method _setGraphic
801 * @param {Graphic | Node | HTMLElement | String} render This param is used to determine the graphic instance. If it is a `Graphic` instance, it will be assigned
802 * to the `graphic` attribute. Otherwise, a new Graphic instance will be created and rendered into the dom element that the render represents.
805 _setGraphic: function(render)
808 if(render instanceof Y.VMLGraphic)
810 this._graphic = render;
814 render = Y.one(render);
815 graphic = new Y.VMLGraphic({
818 graphic._appendShape(this);
819 this._graphic = graphic;
820 this._appendStrokeAndFill();
825 * Appends fill and stroke nodes to the shape.
827 * @method _appendStrokeAndFill
830 _appendStrokeAndFill: function()
834 this.node.appendChild(this._strokeNode);
838 this.node.appendChild(this._fillNode);
843 * Creates the dom node for the shape.
846 * @return HTMLElement
849 createNode: function()
852 concat = this._camelCaseConcat,
855 w = this.get("width"),
856 h = this.get("height"),
861 visibility = this.get("visible") ? "visible" : "hidden",
873 type = this._type == "path" ? "shape" : this._type;
874 classString = _getClassName(SHAPE) + " " + _getClassName(concat(IMPLEMENTATION, SHAPE)) + " " + _getClassName(name) + " " + _getClassName(concat(IMPLEMENTATION, name)) + " " + IMPLEMENTATION + type;
875 stroke = this._getStrokeProps();
876 fill = this._getFillProps();
878 nodestring = '<' + type + ' xmlns="urn:schemas-microsft.com:vml" id="' + id + '" class="' + classString + '" style="behavior:url(#default#VML);display:inline-block;position:absolute;left:' + x + 'px;top:' + y + 'px;width:' + w + 'px;height:' + h + 'px;visibility:' + visibility + '"';
880 if(stroke && stroke.weight && stroke.weight > 0)
882 endcap = stroke.endcap;
883 opacity = parseFloat(stroke.opacity);
884 joinstyle = stroke.joinstyle;
885 miterlimit = stroke.miterlimit;
886 dashstyle = stroke.dashstyle;
887 nodestring += ' stroked="t" strokecolor="' + stroke.color + '" strokeWeight="' + stroke.weight + 'px"';
889 strokestring = '<stroke class="vmlstroke" xmlns="urn:schemas-microsft.com:vml" on="t" style="behavior:url(#default#VML);display:inline-block;"';
890 strokestring += ' opacity="' + opacity + '"';
893 strokestring += ' endcap="' + endcap + '"';
897 strokestring += ' joinstyle="' + joinstyle + '"';
901 strokestring += ' miterlimit="' + miterlimit + '"';
905 strokestring += ' dashstyle="' + dashstyle + '"';
907 strokestring += '></stroke>';
908 this._strokeNode = DOCUMENT.createElement(strokestring);
909 nodestring += ' stroked="t"';
913 nodestring += ' stroked="f"';
919 fillstring = fill.node;
920 this._fillNode = DOCUMENT.createElement(fillstring);
924 nodestring += ' fillcolor="' + fill.color + '"';
926 nodestring += ' filled="' + fill.filled + '"';
931 nodestring += '</' + type + '>';
933 node = DOCUMENT.createElement(nodestring);
936 this._strokeFlag = false;
937 this._fillFlag = false;
941 * Add a class name to each node.
944 * @param {String} className the class name to add to the node's class attribute
946 addClass: function(className)
948 var node = this.node;
949 Y_DOM.addClass(node, className);
953 * Removes a class name from each node.
955 * @method removeClass
956 * @param {String} className the class name to remove from the node's class attribute
958 removeClass: function(className)
960 var node = this.node;
961 Y_DOM.removeClass(node, className);
965 * Gets the current position of the node in page coordinates.
968 * @return Array The XY position of the shape.
972 var graphic = this._graphic,
973 parentXY = graphic.getXY(),
976 return [parentXY[0] + x, parentXY[1] + y];
980 * Set the position of the shape in page coordinates, regardless of how the node is positioned.
983 * @param {Array} Contains x & y values for new position (coordinates are page-based)
988 var graphic = this._graphic,
989 parentXY = graphic.getXY();
990 this.set("x", xy[0] - parentXY[0]);
991 this.set("y", xy[1] - parentXY[1]);
995 * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy.
998 * @param {VMLShape | HTMLElement} needle The possible node or descendent
999 * @return Boolean Whether or not this shape is the needle or its ancestor.
1001 contains: function(needle)
1003 return needle === Y.one(this.node);
1007 * Compares nodes to determine if they match.
1008 * Node instances can be compared to each other and/or HTMLElements.
1010 * @param {HTMLElement | Node} refNode The reference node to compare to the node.
1011 * @return {Boolean} True if the nodes match, false if they do not.
1013 compareTo: function(refNode) {
1014 var node = this.node;
1016 return node === refNode;
1020 * Test if the supplied node matches the supplied selector.
1023 * @param {String} selector The CSS selector to test against.
1024 * @return Boolean Wheter or not the shape matches the selector.
1026 test: function(selector)
1028 return Y_SELECTOR.test(this.node, selector);
1032 * Calculates and returns properties for setting an initial stroke.
1034 * @method _getStrokeProps
1039 _getStrokeProps: function()
1042 stroke = this.get("stroke"),
1051 if(stroke && stroke.weight && stroke.weight > 0)
1054 linecap = stroke.linecap || "flat";
1055 linejoin = stroke.linejoin || "round";
1056 if(linecap != "round" && linecap != "square")
1060 strokeOpacity = parseFloat(stroke.opacity);
1061 dashstyle = stroke.dashstyle || "none";
1062 stroke.color = stroke.color || "#000000";
1063 stroke.weight = stroke.weight || 1;
1064 stroke.opacity = IS_NUM(strokeOpacity) ? strokeOpacity : 1;
1065 props.stroked = true;
1066 props.color = stroke.color;
1067 props.weight = stroke.weight;
1068 props.endcap = linecap;
1069 props.opacity = stroke.opacity;
1070 if(IS_ARRAY(dashstyle))
1073 len = dashstyle.length;
1074 for(i = 0; i < len; ++i)
1077 dash[i] = val / stroke.weight;
1080 if(linejoin == "round" || linejoin == "bevel")
1082 props.joinstyle = linejoin;
1086 linejoin = parseInt(linejoin, 10);
1087 if(IS_NUM(linejoin))
1089 props.miterlimit = Math.max(linejoin, 1);
1090 props.joinstyle = "miter";
1093 props.dashstyle = dash;
1099 * Adds a stroke to the shape node.
1101 * @method _strokeChangeHandler
1104 _strokeChangeHandler: function(e)
1106 if(!this._strokeFlag)
1110 var node = this.node,
1111 stroke = this.get("stroke"),
1120 if(stroke && stroke.weight && stroke.weight > 0)
1122 linecap = stroke.linecap || "flat";
1123 linejoin = stroke.linejoin || "round";
1124 if(linecap != "round" && linecap != "square")
1128 strokeOpacity = parseFloat(stroke.opacity);
1129 dashstyle = stroke.dashstyle || "none";
1130 stroke.color = stroke.color || "#000000";
1131 stroke.weight = stroke.weight || 1;
1132 stroke.opacity = IS_NUM(strokeOpacity) ? strokeOpacity : 1;
1133 node.stroked = true;
1134 node.strokeColor = stroke.color;
1135 node.strokeWeight = stroke.weight + "px";
1136 if(!this._strokeNode)
1138 this._strokeNode = this._createGraphicNode("stroke");
1139 node.appendChild(this._strokeNode);
1141 this._strokeNode.endcap = linecap;
1142 this._strokeNode.opacity = stroke.opacity;
1143 if(IS_ARRAY(dashstyle))
1146 len = dashstyle.length;
1147 for(i = 0; i < len; ++i)
1150 dash[i] = val / stroke.weight;
1153 if(linejoin == "round" || linejoin == "bevel")
1155 this._strokeNode.joinstyle = linejoin;
1159 linejoin = parseInt(linejoin, 10);
1160 if(IS_NUM(linejoin))
1162 this._strokeNode.miterlimit = Math.max(linejoin, 1);
1163 this._strokeNode.joinstyle = "miter";
1166 this._strokeNode.dashstyle = dash;
1167 this._strokeNode.on = true;
1171 if(this._strokeNode)
1173 this._strokeNode.on = false;
1175 node.stroked = false;
1177 this._strokeFlag = false;
1181 * Calculates and returns properties for setting an initial fill.
1183 * @method _getFillProps
1188 _getFillProps: function()
1190 var fill = this.get("fill"),
1201 if(fill.type == "radial" || fill.type == "linear")
1203 fillOpacity = parseFloat(fill.opacity);
1204 fillOpacity = IS_NUM(fillOpacity) ? fillOpacity : 1;
1206 gradient = this._getGradientFill(fill);
1207 fillstring = '<fill xmlns="urn:schemas-microsft.com:vml" class="vmlfill" style="behavior:url(#default#VML);display:inline-block;" opacity="' + fillOpacity + '"';
1210 if(gradient.hasOwnProperty(i))
1212 fillstring += ' ' + i + '="' + gradient[i] + '"';
1215 fillstring += ' />';
1216 props.node = fillstring;
1220 fillOpacity = parseFloat(fill.opacity);
1222 props.color = fill.color;
1223 if(IS_NUM(fillOpacity))
1225 fillOpacity = Math.max(Math.min(fillOpacity, 1), 0);
1226 props.opacity = fillOpacity;
1229 props.node = '<fill xmlns="urn:schemas-microsft.com:vml" class="vmlfill" style="behavior:url(#default#VML);display:inline-block;" type="solid" opacity="' + fillOpacity + '"/>';
1233 props.filled = filled;
1239 * Adds a fill to the shape node.
1241 * @method _fillChangeHandler
1244 _fillChangeHandler: function(e)
1250 var node = this.node,
1251 fill = this.get("fill"),
1259 if(fill.type == "radial" || fill.type == "linear")
1262 gradient = this._getGradientFill(fill);
1267 if(gradient.hasOwnProperty(i))
1271 this._fillNode.colors.value = gradient[i];
1275 this._fillNode[i] = gradient[i];
1282 fillstring = '<fill xmlns="urn:schemas-microsft.com:vml" class="vmlfill" style="behavior:url(#default#VML);display:inline-block;"';
1285 if(gradient.hasOwnProperty(i))
1287 fillstring += ' ' + i + '="' + gradient[i] + '"';
1290 fillstring += ' />';
1291 this._fillNode = DOCUMENT.createElement(fillstring);
1292 node.appendChild(this._fillNode);
1297 node.fillcolor = fill.color;
1298 fillOpacity = parseFloat(fill.opacity);
1300 if(IS_NUM(fillOpacity) && fillOpacity < 1)
1302 fill.opacity = fillOpacity;
1305 if(this._fillNode.getAttribute("type") != "solid")
1307 this._fillNode.type = "solid";
1309 this._fillNode.opacity = fillOpacity;
1313 fillstring = '<fill xmlns="urn:schemas-microsft.com:vml" class="vmlfill" style="behavior:url(#default#VML);display:inline-block;" type="solid" opacity="' + fillOpacity + '"/>';
1314 this._fillNode = DOCUMENT.createElement(fillstring);
1315 node.appendChild(this._fillNode);
1318 else if(this._fillNode)
1320 this._fillNode.opacity = 1;
1321 this._fillNode.type = "solid";
1325 node.filled = filled;
1326 this._fillFlag = false;
1329 //not used. remove next release.
1330 _updateFillNode: function(node)
1334 this._fillNode = this._createGraphicNode("fill");
1335 node.appendChild(this._fillNode);
1340 * Calculates and returns an object containing gradient properties for a fill node.
1342 * @method _getGradientFill
1343 * @param {Object} fill Object containing fill properties.
1347 _getGradientFill: function(fill)
1349 var gradientProps = {},
1353 w = this.get("width"),
1354 h = this.get("height"),
1370 rotation = fill.rotation || 0;
1371 if(type === "linear")
1375 rotation = Math.abs(rotation - 270);
1377 else if(rotation < 360)
1379 rotation = 270 + (360 - rotation);
1385 gradientProps.type = "gradient";//"gradientunscaled";
1386 gradientProps.angle = rotation;
1388 else if(type === "radial")
1390 gradientBoxWidth = w * (r * 2);
1391 gradientBoxHeight = h * (r * 2);
1392 fx = r * 2 * (fx - 0.5);
1393 fy = r * 2 * (fy - 0.5);
1396 gradientProps.focussize = (gradientBoxWidth/w)/10 + "% " + (gradientBoxHeight/h)/10 + "%";
1397 gradientProps.alignshape = false;
1398 gradientProps.type = "gradientradial";
1399 gradientProps.focus = "100%";
1400 gradientProps.focusposition = Math.round(fx * 100) + "% " + Math.round(fy * 100) + "%";
1402 for(i = 0;i < len; ++i) {
1405 opacity = stop.opacity;
1406 opacity = isNumber(opacity) ? opacity : 1;
1407 pct = stop.offset || i/(len-1);
1409 pct = Math.round(100 * pct) + "%";
1410 oi = i > 0 ? i + 1 : "";
1411 gradientProps["opacity" + oi] = opacity + "";
1412 colorstring += ", " + pct + " " + color;
1414 if(parseFloat(pct) < 100)
1416 colorstring += ", 100% " + color;
1418 gradientProps.colors = colorstring.substr(2);
1419 return gradientProps;
1423 * Adds a transform to the shape.
1425 * @method _addTransform
1426 * @param {String} type The transform being applied.
1427 * @param {Array} args The arguments for the transform.
1430 _addTransform: function(type, args)
1432 args = Y.Array(args);
1433 this._transform = Y_LANG.trim(this._transform + " " + type + "(" + args.join(", ") + ")");
1435 this._transforms.push(args);
1436 if(this.initialized)
1438 this._updateTransform();
1443 * Applies all transforms.
1445 * @method _updateTransform
1448 _updateTransform: function()
1450 var node = this.node,
1458 matrix = this.matrix,
1459 normalizedMatrix = this._normalizedMatrix,
1460 isPathShape = this instanceof Y.VMLPath,
1462 len = this._transforms.length;
1463 if(this._transforms && this._transforms.length > 0)
1465 transformOrigin = this.get("transformOrigin");
1469 normalizedMatrix.translate(this._left, this._top);
1471 //vml skew matrix transformOrigin ranges from -0.5 to 0.5.
1472 //subtract 0.5 from values
1473 tx = transformOrigin[0] - 0.5;
1474 ty = transformOrigin[1] - 0.5;
1476 //ensure the values are within the appropriate range to avoid errors
1477 tx = Math.max(-0.5, Math.min(0.5, tx));
1478 ty = Math.max(-0.5, Math.min(0.5, ty));
1479 for(i = 0; i < len; ++i)
1481 key = this._transforms[i].shift();
1484 normalizedMatrix[key].apply(normalizedMatrix, this._transforms[i]);
1485 matrix[key].apply(matrix, this._transforms[i]);
1490 normalizedMatrix.translate(-this._left, -this._top);
1492 transform = normalizedMatrix.a + "," +
1493 normalizedMatrix.c + "," +
1494 normalizedMatrix.b + "," +
1495 normalizedMatrix.d + "," +
1499 this._graphic.addToRedrawQueue(this);
1504 this._skew = DOCUMENT.createElement( '<skew class="vmlskew" xmlns="urn:schemas-microsft.com:vml" on="false" style="behavior:url(#default#VML);display:inline-block;" />');
1505 this.node.appendChild(this._skew);
1507 this._skew.matrix = transform;
1508 this._skew.on = true;
1509 //this._skew.offset = this._getSkewOffsetValue(normalizedMatrix.dx) + "px, " + this._getSkewOffsetValue(normalizedMatrix.dy) + "px";
1510 this._skew.origin = tx + ", " + ty;
1512 if(this._type != "path")
1514 this._transforms = [];
1516 //add the translate to the x and y coordinates
1517 node.style.left = (x + this._getSkewOffsetValue(normalizedMatrix.dx)) + "px";
1518 node.style.top = (y + this._getSkewOffsetValue(normalizedMatrix.dy)) + "px";
1522 * Normalizes the skew offset values between -32767 and 32767.
1524 * @method _getSkewOffsetValue
1525 * @param {Number} val The value to normalize
1529 _getSkewOffsetValue: function(val)
1531 var sign = Y.MatrixUtil.sign(val),
1532 absVal = Math.abs(val);
1533 val = Math.min(absVal, 32767) * sign;
1538 * Storage for translateX
1540 * @property _translateX
1547 * Storage for translateY
1549 * @property _translateY
1556 * Storage for the transform attribute.
1558 * @property _transform
1565 * Specifies a 2d translation.
1568 * @param {Number} x The value to translate on the x-axis.
1569 * @param {Number} y The value to translate on the y-axis.
1571 translate: function(x, y)
1573 this._translateX += x;
1574 this._translateY += y;
1575 this._addTransform("translate", arguments);
1579 * Translates the shape along the x-axis. When translating x and y coordinates,
1580 * use the `translate` method.
1582 * @method translateX
1583 * @param {Number} x The value to translate.
1585 translateX: function(x)
1587 this._translateX += x;
1588 this._addTransform("translateX", arguments);
1592 * Performs a translate on the y-coordinate. When translating x and y coordinates,
1593 * use the `translate` method.
1595 * @method translateY
1596 * @param {Number} y The value to translate.
1598 translateY: function(y)
1600 this._translateY += y;
1601 this._addTransform("translateY", arguments);
1605 * Skews the shape around the x-axis and y-axis.
1608 * @param {Number} x The value to skew on the x-axis.
1609 * @param {Number} y The value to skew on the y-axis.
1611 skew: function(x, y)
1613 this._addTransform("skew", arguments);
1617 * Skews the shape around the x-axis.
1620 * @param {Number} x x-coordinate
1624 this._addTransform("skewX", arguments);
1628 * Skews the shape around the y-axis.
1631 * @param {Number} y y-coordinate
1635 this._addTransform("skewY", arguments);
1639 * Rotates the shape clockwise around it transformOrigin.
1642 * @param {Number} deg The degree of the rotation.
1644 rotate: function(deg)
1646 this._addTransform("rotate", arguments);
1650 * Specifies a 2d scaling operation.
1653 * @param {Number} val
1655 scale: function(x, y)
1657 this._addTransform("scale", arguments);
1661 * Overrides default `on` method. Checks to see if its a dom interaction event. If so,
1662 * return an event attached to the `node` element. If not, return the normal functionality.
1665 * @param {String} type event type
1666 * @param {Object} callback function
1669 on: function(type, fn)
1671 if(Y.Node.DOM_EVENTS[type])
1673 return Y.one("#" + this.get("id")).on(type, fn);
1675 return Y.on.apply(this, arguments);
1689 * Updates `Shape` based on attribute changes.
1691 * @method _updateHandler
1694 _updateHandler: function(e)
1698 host._fillChangeHandler();
1699 host._strokeChangeHandler();
1700 node.style.width = this.get("width") + "px";
1701 node.style.height = this.get("height") + "px";
1703 host._updateTransform();
1707 * Creates a graphic node
1709 * @method _createGraphicNode
1710 * @param {String} type node type to create
1711 * @return HTMLElement
1714 _createGraphicNode: function(type)
1716 type = type || this._type;
1717 return DOCUMENT.createElement('<' + type + ' xmlns="urn:schemas-microsft.com:vml" style="behavior:url(#default#VML);display:inline-block;" class="vml' + type + '"/>');
1721 * Value function for fill attribute
1724 * @method _getDefaultFill
1727 _getDefaultFill: function() {
1740 * Value function for stroke attribute
1743 * @method _getDefaultStroke
1746 _getDefaultStroke: function()
1757 * Sets the value of an attribute.
1760 * @param {String|Object} name The name of the attribute. Alternatively, an object of key value pairs can
1761 * be passed in to set multiple attributes at once.
1762 * @param {Any} value The value to set the attribute to. This value is ignored if an object is received as
1768 AttributeLite.prototype.set.apply(host, arguments);
1769 if(host.initialized)
1771 host._updateHandler();
1776 * Returns the bounds for a shape.
1778 * Calculates the a new bounding box from the original corner coordinates (base on size and position) and the transform matrix.
1779 * The calculated bounding box is used by the graphic instance to calculate its viewBox.
1784 getBounds: function()
1786 var isPathShape = this instanceof Y.VMLPath,
1787 w = this.get("width"),
1788 h = this.get("height"),
1795 w = this._right - this._left;
1796 h = this._bottom - this._top;
1798 return this._getContentRect(w, h, x, y);
1802 * Calculates the bounding box for the shape.
1804 * @method _getContentRect
1805 * @param {Number} w width of the shape
1806 * @param {Number} h height of the shape
1807 * @param {Number} x x-coordinate of the shape
1808 * @param {Number} y y-coordinate of the shape
1811 _getContentRect: function(w, h, x, y)
1813 var transformOrigin = this.get("transformOrigin"),
1814 transformX = transformOrigin[0] * w,
1815 transformY = transformOrigin[1] * h,
1816 transforms = this.matrix.getTransformArray(this.get("transform")),
1817 matrix = new Y.Matrix(),
1819 len = transforms.length,
1823 isPathShape = this instanceof Y.VMLPath;
1826 matrix.translate(this._left, this._top);
1828 transformX = !isNaN(transformX) ? transformX : 0;
1829 transformY = !isNaN(transformY) ? transformY : 0;
1830 matrix.translate(transformX, transformY);
1831 for(i = 0; i < len; i = i + 1)
1833 transform = transforms[i];
1834 key = transform.shift();
1837 matrix[key].apply(matrix, transform);
1840 matrix.translate(-transformX, -transformY);
1843 matrix.translate(-this._left, -this._top);
1845 contentRect = matrix.getContentRect(w, h, x, y);
1850 * Places the shape above all other shapes.
1856 var graphic = this.get("graphic");
1859 graphic._toFront(this);
1864 * Places the shape underneath all other shapes.
1870 var graphic = this.get("graphic");
1873 graphic._toBack(this);
1878 * Parses path data string and call mapped methods.
1880 * @method _parsePathData
1881 * @param {String} val The path data
1884 _parsePathData: function(val)
1889 commandArray = Y.Lang.trim(val.match(SPLITPATHPATTERN)),
1893 symbolToMethod = this._pathSymbolToMethod;
1897 len = commandArray.length || 0;
1898 for(i = 0; i < len; i = i + 1)
1900 str = commandArray[i];
1901 methodSymbol = str.substr(0, 1);
1902 args = str.substr(1).match(SPLITARGSPATTERN);
1903 method = symbolToMethod[methodSymbol];
1908 this[method].apply(this, args);
1912 this[method].apply(this);
1927 var graphic = this.get("graphic");
1930 graphic.removeShape(this);
1939 * Implementation for shape destruction
1944 _destroy: function()
1950 this.node.removeChild(this._fillNode);
1951 this._fillNode = null;
1953 if(this._strokeNode)
1955 this.node.removeChild(this._strokeNode);
1956 this._strokeNode = null;
1958 Y.one(this.node).remove(true);
1961 }, Y.VMLDrawing.prototype));
1965 * An array of x, y values which indicates the transformOrigin in which to rotate the shape. Valid values range between 0 and 1 representing a
1966 * fraction of the shape's corresponding bounding box dimension. The default value is [0.5, 0.5].
1968 * @config transformOrigin
1979 * <p>A string containing, in order, transform operations applied to the shape instance. The `transform` string can contain the following values:
1982 * <dt>rotate</dt><dd>Rotates the shape clockwise around it transformOrigin.</dd>
1983 * <dt>translate</dt><dd>Specifies a 2d translation.</dd>
1984 * <dt>skew</dt><dd>Skews the shape around the x-axis and y-axis.</dd>
1985 * <dt>scale</dt><dd>Specifies a 2d scaling operation.</dd>
1986 * <dt>translateX</dt><dd>Translates the shape along the x-axis.</dd>
1987 * <dt>translateY</dt><dd>Translates the shape along the y-axis.</dd>
1988 * <dt>skewX</dt><dd>Skews the shape around the x-axis.</dd>
1989 * <dt>skewY</dt><dd>Skews the shape around the y-axis.</dd>
1990 * <dt>matrix</dt><dd>Specifies a 2D transformation matrix comprised of the specified six values.</dd>
1993 * <p>Applying transforms through the transform attribute will reset the transform matrix and apply a new transform. The shape class also contains corresponding methods for each transform
1994 * that will apply the transform to the current matrix. The below code illustrates how you might use the `transform` attribute to instantiate a recangle with a rotation of 45 degrees.</p>
1995 var myRect = new Y.Rect({
1999 transform: "rotate(45)"
2001 * <p>The code below would apply `translate` and `rotate` to an existing shape.</p>
2003 myRect.set("transform", "translate(40, 50) rotate(45)");
2008 setter: function(val)
2014 this._normalizedMatrix.init();
2015 this._transforms = this.matrix.getTransformArray(val);
2016 len = this._transforms.length;
2017 for(i = 0;i < len; ++i)
2019 transform = this._transforms[i];
2021 this._transform = val;
2027 return this._transform;
2032 * Indicates the x position of shape.
2042 * Indicates the y position of shape.
2052 * Unique id for class instance.
2063 setter: function(val)
2065 var node = this.node;
2068 node.setAttribute("id", val);
2091 * Indicates whether the shape is visible.
2099 setter: function(val){
2100 var node = this.node,
2101 visibility = val ? "visible" : "hidden";
2104 node.style.visibility = visibility;
2111 * Contains information about the fill of the shape.
2113 * <dt>color</dt><dd>The color of the fill.</dd>
2114 * <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the fill. The default value is 1.</dd>
2115 * <dt>type</dt><dd>Type of fill.
2117 * <dt>solid</dt><dd>Solid single color fill. (default)</dd>
2118 * <dt>linear</dt><dd>Linear gradient fill.</dd>
2119 * <dt>radial</dt><dd>Radial gradient fill.</dd>
2123 * <p>If a `linear` or `radial` is specified as the fill type. The following additional property is used:
2125 * <dt>stops</dt><dd>An array of objects containing the following properties:
2127 * <dt>color</dt><dd>The color of the stop.</dd>
2128 * <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the stop. The default value is 1. Note: No effect for IE 6 - 8</dd>
2129 * <dt>offset</dt><dd>Number between 0 and 1 indicating where the color stop is positioned.</dd>
2132 * <p>Linear gradients also have the following property:</p>
2133 * <dt>rotation</dt><dd>Linear gradients flow left to right by default. The rotation property allows you to change the flow by rotation. (e.g. A rotation of 180 would make the gradient pain from right to left.)</dd>
2134 * <p>Radial gradients have the following additional properties:</p>
2135 * <dt>r</dt><dd>Radius of the gradient circle.</dd>
2136 * <dt>fx</dt><dd>Focal point x-coordinate of the gradient.</dd>
2137 * <dt>fy</dt><dd>Focal point y-coordinate of the gradient.</dd>
2139 * <p>The corresponding `SVGShape` class implements the following additional properties.</p>
2142 * <p>The x-coordinate of the center of the gradient circle. Determines where the color stop begins. The default value 0.5.</p>
2145 * <p>The y-coordinate of the center of the gradient circle. Determines where the color stop begins. The default value 0.5.</p>
2148 * <p>These properties are not currently implemented in `CanvasShape` or `VMLShape`.</p>
2154 valueFn: "_getDefaultFill",
2156 setter: function(val)
2160 tmpl = this.get("fill") || this._getDefaultFill();
2164 //ensure, fill type is solid if color is explicitly passed.
2165 if(val.hasOwnProperty("color"))
2171 if(val.hasOwnProperty(i))
2178 if(fill && fill.color)
2180 if(fill.color === undefined || fill.color == "none")
2185 this._fillFlag = true;
2191 * Contains information about the stroke of the shape.
2193 * <dt>color</dt><dd>The color of the stroke.</dd>
2194 * <dt>weight</dt><dd>Number that indicates the width of the stroke.</dd>
2195 * <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the stroke. The default value is 1.</dd>
2196 * <dt>dashstyle</dt>Indicates whether to draw a dashed stroke. When set to "none", a solid stroke is drawn. When set to an array, the first index indicates the
2197 * length of the dash. The second index indicates the length of gap.
2198 * <dt>linecap</dt><dd>Specifies the linecap for the stroke. The following values can be specified:
2200 * <dt>butt (default)</dt><dd>Specifies a butt linecap.</dd>
2201 * <dt>square</dt><dd>Specifies a sqare linecap.</dd>
2202 * <dt>round</dt><dd>Specifies a round linecap.</dd>
2205 * <dt>linejoin</dt><dd>Specifies a linejoin for the stroke. The following values can be specified:
2207 * <dt>round (default)</dt><dd>Specifies that the linejoin will be round.</dd>
2208 * <dt>bevel</dt><dd>Specifies a bevel for the linejoin.</dd>
2209 * <dt>miter limit</dt><dd>An integer specifying the miter limit of a miter linejoin. If you want to specify a linejoin of miter, you simply specify the limit as opposed to having
2210 * separate miter and miter limit values.</dd>
2219 valueFn: "_getDefaultStroke",
2221 setter: function(val)
2226 tmpl = this.get("stroke") || this._getDefaultStroke();
2229 if(val.hasOwnProperty("weight"))
2231 wt = parseInt(val.weight, 10);
2239 if(val.hasOwnProperty(i))
2246 this._strokeFlag = true;
2251 //Not used. Remove in future.
2256 // Only implemented in SVG
2257 // Determines whether the instance will receive mouse events.
2259 // @config pointerEvents
2263 value: "visiblePainted"
2267 * Dom node for the shape.
2283 * Represents an SVG Path string. This will be parsed and added to shape's API to represent the SVG data across all implementations. Note that when using VML or SVG
2284 * implementations, part of this content will be added to the DOM using respective VML/SVG attributes. If your content comes from an untrusted source, you will need
2285 * to ensure that no malicious code is included in that content.
2291 setter: function(val)
2293 if(this.get("node"))
2295 this._parsePathData(val);
2302 * Reference to the container Graphic.
2312 return this._graphic;
2316 Y.VMLShape = VMLShape;
2318 * <a href="http://www.w3.org/TR/NOTE-VML">VML</a> implementation of the <a href="Path.html">`Path`</a> class.
2319 * `VMLPath` is not intended to be used directly. Instead, use the <a href="Path.html">`Path`</a> class.
2320 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> and <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a>
2321 * capabilities, the <a href="Path.html">`Path`</a> class will point to the `VMLPath` class.
2327 VMLPath = function()
2329 VMLPath.superclass.constructor.apply(this, arguments);
2332 VMLPath.NAME = "path";
2333 Y.extend(VMLPath, Y.VMLShape);
2334 VMLPath.ATTRS = Y.merge(Y.VMLShape.ATTRS, {
2336 * Indicates the width of the shape
2344 var val = Math.max(this._right - this._left, 0);
2350 * Indicates the height of the shape
2358 return Math.max(this._bottom - this._top, 0);
2363 * Indicates the path used for the node.
2378 Y.VMLPath = VMLPath;
2380 * <a href="http://www.w3.org/TR/NOTE-VML">VML</a> implementation of the <a href="Rect.html">`Rect`</a> class.
2381 * `VMLRect` is not intended to be used directly. Instead, use the <a href="Rect.html">`Rect`</a> class.
2382 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> and <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a>
2383 * capabilities, the <a href="Rect.html">`Rect`</a> class will point to the `VMLRect` class.
2389 VMLRect = function()
2391 VMLRect.superclass.constructor.apply(this, arguments);
2393 VMLRect.NAME = "rect";
2394 Y.extend(VMLRect, Y.VMLShape, {
2396 * Indicates the type of shape
2404 VMLRect.ATTRS = Y.VMLShape.ATTRS;
2405 Y.VMLRect = VMLRect;
2407 * <a href="http://www.w3.org/TR/NOTE-VML">VML</a> implementation of the <a href="Ellipse.html">`Ellipse`</a> class.
2408 * `VMLEllipse` is not intended to be used directly. Instead, use the <a href="Ellipse.html">`Ellipse`</a> class.
2409 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> and <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a>
2410 * capabilities, the <a href="Ellipse.html">`Ellipse`</a> class will point to the `VMLEllipse` class.
2416 VMLEllipse = function()
2418 VMLEllipse.superclass.constructor.apply(this, arguments);
2421 VMLEllipse.NAME = "ellipse";
2423 Y.extend(VMLEllipse, Y.VMLShape, {
2425 * Indicates the type of shape
2433 VMLEllipse.ATTRS = Y.merge(Y.VMLShape.ATTRS, {
2435 * Horizontal radius for the ellipse.
2445 var val = this.get("width");
2446 val = Math.round((val/2) * 100)/100;
2450 setter: function(val)
2453 this.set("width", w);
2459 * Vertical radius for the ellipse.
2470 var val = this.get("height");
2471 val = Math.round((val/2) * 100)/100;
2475 setter: function(val)
2478 this.set("height", h);
2483 Y.VMLEllipse = VMLEllipse;
2485 * <a href="http://www.w3.org/TR/NOTE-VML">VML</a> implementation of the <a href="Circle.html">`Circle`</a> class.
2486 * `VMLCircle` is not intended to be used directly. Instead, use the <a href="Circle.html">`Circle`</a> class.
2487 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> and <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a>
2488 * capabilities, the <a href="Circle.html">`Circle`</a> class will point to the `VMLCircle` class.
2494 VMLCircle = function(cfg)
2496 VMLCircle.superclass.constructor.apply(this, arguments);
2499 VMLCircle.NAME = "circle";
2501 Y.extend(VMLCircle, VMLShape, {
2503 * Indicates the type of shape
2512 VMLCircle.ATTRS = Y.merge(VMLShape.ATTRS, {
2514 * Radius for the circle.
2526 * Indicates the width of the shape
2532 setter: function(val)
2534 this.set("radius", val/2);
2540 var radius = this.get("radius"),
2541 val = radius && radius > 0 ? radius * 2 : 0;
2547 * Indicates the height of the shape
2553 setter: function(val)
2555 this.set("radius", val/2);
2561 var radius = this.get("radius"),
2562 val = radius && radius > 0 ? radius * 2 : 0;
2567 Y.VMLCircle = VMLCircle;
2572 * @class VMLPieSlice
2575 VMLPieSlice = function()
2577 VMLPieSlice.superclass.constructor.apply(this, arguments);
2579 VMLPieSlice.NAME = "vmlPieSlice";
2580 Y.extend(VMLPieSlice, Y.VMLShape, Y.mix({
2582 * Indicates the type of shape
2591 * Change event listener
2594 * @method _updateHandler
2598 var x = this.get("cx"),
2600 startAngle = this.get("startAngle"),
2601 arc = this.get("arc"),
2602 radius = this.get("radius");
2604 this.drawWedge(x, y, startAngle, arc, radius);
2607 }, Y.VMLDrawing.prototype));
2608 VMLPieSlice.ATTRS = Y.mix({
2617 * Starting angle in relation to a circle in which to begin the pie slice drawing.
2619 * @config startAngle
2637 * Radius of the circle in which the pie slice is drawn
2645 }, Y.VMLShape.ATTRS);
2646 Y.VMLPieSlice = VMLPieSlice;
2648 * <a href="http://www.w3.org/TR/NOTE-VML">VML</a> implementation of the <a href="Graphic.html">`Graphic`</a> class.
2649 * `VMLGraphic` is not intended to be used directly. Instead, use the <a href="Graphic.html">`Graphic`</a> class.
2650 * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> and <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a>
2651 * capabilities, the <a href="Graphic.html">`Graphic`</a> class will point to the `VMLGraphic` class.
2657 VMLGraphic = function() {
2658 VMLGraphic.superclass.constructor.apply(this, arguments);
2661 VMLGraphic.NAME = "vmlGraphic";
2663 VMLGraphic.ATTRS = {
2665 * Whether or not to render the `Graphic` automatically after to a specified parent node after init. This can be a Node instance or a CSS selector string.
2668 * @type Node | String
2673 * Unique id for class instance.
2684 setter: function(val)
2686 var node = this._node;
2689 node.setAttribute("id", val);
2696 * Key value pairs in which a shape instance is associated with its id.
2707 return this._shapes;
2712 * Object containing size and coordinate data for the content of a Graphic in relation to the coordSpace node.
2714 * @config contentBounds
2722 return this._contentBounds;
2727 * The html element that represents to coordinate system of the Graphic instance.
2742 * Indicates the width of the `Graphic`.
2748 setter: function(val)
2752 this._node.style.width = val + "px";
2759 * Indicates the height of the `Graphic`.
2765 setter: function(val)
2769 this._node.style.height = val + "px";
2776 * Determines the sizing of the Graphic.
2779 * <dt>sizeContentToGraphic</dt><dd>The Graphic's width and height attributes are, either explicitly set through the <code>width</code> and <code>height</code>
2780 * attributes or are determined by the dimensions of the parent element. The content contained in the Graphic will be sized to fit with in the Graphic instance's
2781 * dimensions. When using this setting, the <code>preserveAspectRatio</code> attribute will determine how the contents are sized.</dd>
2782 * <dt>sizeGraphicToContent</dt><dd>(Also accepts a value of true) The Graphic's width and height are determined by the size and positioning of the content.</dd>
2783 * <dt>false</dt><dd>The Graphic's width and height attributes are, either explicitly set through the <code>width</code> and <code>height</code>
2784 * attributes or are determined by the dimensions of the parent element. The contents of the Graphic instance are not affected by this setting.</dd>
2789 * @type Boolean | String
2797 * Determines how content is sized when <code>autoSize</code> is set to <code>sizeContentToGraphic</code>.
2800 * <dt>none<dt><dd>Do not force uniform scaling. Scale the graphic content of the given element non-uniformly if necessary
2801 * such that the element's bounding box exactly matches the viewport rectangle.</dd>
2802 * <dt>xMinYMin</dt><dd>Force uniform scaling position along the top left of the Graphic's node.</dd>
2803 * <dt>xMidYMin</dt><dd>Force uniform scaling horizontally centered and positioned at the top of the Graphic's node.<dd>
2804 * <dt>xMaxYMin</dt><dd>Force uniform scaling positioned horizontally from the right and vertically from the top.</dd>
2805 * <dt>xMinYMid</dt>Force uniform scaling positioned horizontally from the left and vertically centered.</dd>
2806 * <dt>xMidYMid (the default)</dt><dd>Force uniform scaling with the content centered.</dd>
2807 * <dt>xMaxYMid</dt><dd>Force uniform scaling positioned horizontally from the right and vertically centered.</dd>
2808 * <dt>xMinYMax</dt><dd>Force uniform scaling positioned horizontally from the left and vertically from the bottom.</dd>
2809 * <dt>xMidYMax</dt><dd>Force uniform scaling horizontally centered and position vertically from the bottom.</dd>
2810 * <dt>xMaxYMax</dt><dd>Force uniform scaling positioned horizontally from the right and vertically from the bottom.</dd>
2813 * @config preserveAspectRatio
2817 preserveAspectRatio: {
2822 * The contentBounds will resize to greater values but not values. (for performance)
2823 * When resizing the contentBounds down is desirable, set the resizeDown value to true.
2825 * @config resizeDown
2833 * Indicates the x-coordinate for the instance.
2844 setter: function(val)
2849 this._node.style.left = val + "px";
2856 * Indicates the y-coordinate for the instance.
2867 setter: function(val)
2872 this._node.style.top = val + "px";
2879 * Indicates whether or not the instance will automatically redraw after a change is made to a shape.
2880 * This property will get set to false when batching operations.
2894 setter: function(val)
2896 this._toggleVisible(val);
2902 Y.extend(VMLGraphic, Y.GraphicBase, {
2904 * Sets the value of an attribute.
2907 * @param {String|Object} name The name of the attribute. Alternatively, an object of key value pairs can
2908 * be passed in to set multiple attributes at once.
2909 * @param {Any} value The value to set the attribute to. This value is ignored if an object is received as
2912 set: function(attr, value)
2918 preserveAspectRatio: true,
2922 forceRedraw = false;
2923 AttributeLite.prototype.set.apply(host, arguments);
2924 if(host._state.autoDraw === true && Y.Object.size(this._shapes) > 0)
2926 if(Y_LANG.isString && redrawAttrs[attr])
2930 else if(Y_LANG.isObject(attr))
2932 for(key in redrawAttrs)
2934 if(redrawAttrs.hasOwnProperty(key) && attr[key])
2949 * Storage for `x` attribute.
2958 * Storage for `y` attribute.
2967 * Gets the current position of the graphic instance in page coordinates.
2970 * @return Array The XY position of the shape.
2974 var node = this.parentNode,
2980 xy = Y.one(node).getXY();
2986 xy = Y.DOM._getOffset(this._node);
2992 * Initializes the class.
2994 * @method initializer
2997 initializer: function(config) {
2998 var render = this.get("render"),
2999 visibility = this.get("visible") ? "visible" : "hidden";
3001 this._contentBounds = {
3007 this._node = this._createGraphic();
3008 this._node.style.left = this.get("x") + "px";
3009 this._node.style.top = this.get("y") + "px";
3010 this._node.style.visibility = visibility;
3011 this._node.setAttribute("id", this.get("id"));
3014 this.render(render);
3019 * Adds the graphics node to the dom.
3022 * @param {HTMLElement} parentNode node in which to render the graphics node into.
3024 render: function(render) {
3025 var parentNode = Y.one(render),
3026 w = this.get("width") || parseInt(parentNode.getComputedStyle("width"), 10),
3027 h = this.get("height") || parseInt(parentNode.getComputedStyle("height"), 10);
3028 parentNode = parentNode || DOCUMENT.body;
3029 parentNode.appendChild(this._node);
3030 this.parentNode = parentNode;
3031 this.set("width", w);
3032 this.set("height", h);
3037 * Removes all nodes.
3044 Y.one(this._node).remove(true);
3048 * Generates a shape instance by type.
3051 * @param {Object} cfg attributes for the shape
3054 addShape: function(cfg)
3057 if(!this.get("visible"))
3059 cfg.visible = false;
3061 var shapeClass = this._getShapeClass(cfg.type),
3062 shape = new shapeClass(cfg);
3063 this._appendShape(shape);
3064 shape._appendStrokeAndFill();
3069 * Adds a shape instance to the graphic instance.
3071 * @method _appendShape
3072 * @param {Shape} shape The shape instance to be added to the graphic.
3075 _appendShape: function(shape)
3077 var node = shape.node,
3078 parentNode = this._frag || this._node;
3079 if(this.get("autoDraw") || this.get("autoSize") == "sizeContentToGraphic")
3081 parentNode.appendChild(node);
3085 this._getDocFrag().appendChild(node);
3090 * Removes a shape instance from from the graphic instance.
3092 * @method removeShape
3093 * @param {Shape|String} shape The instance or id of the shape to be removed.
3095 removeShape: function(shape)
3097 if(!(shape instanceof VMLShape))
3099 if(Y_LANG.isString(shape))
3101 shape = this._shapes[shape];
3104 if(shape && (shape instanceof VMLShape))
3107 this._shapes[shape.get("id")] = null;
3108 delete this._shapes[shape.get("id")];
3110 if(this.get("autoDraw"))
3117 * Removes all shape instances from the dom.
3119 * @method removeAllShapes
3121 removeAllShapes: function()
3123 var shapes = this._shapes,
3127 if(shapes.hasOwnProperty(i))
3129 shapes[i].destroy();
3136 * Removes all child nodes.
3138 * @method _removeChildren
3142 _removeChildren: function(node)
3144 if(node.hasChildNodes())
3147 while(node.firstChild)
3149 child = node.firstChild;
3150 this._removeChildren(child);
3151 node.removeChild(child);
3157 * Clears the graphics object.
3162 this.removeAllShapes();
3163 this._removeChildren(this._node);
3167 * Toggles visibility
3169 * @method _toggleVisible
3170 * @param {Boolean} val indicates visibilitye
3173 _toggleVisible: function(val)
3176 shapes = this._shapes,
3177 visibility = val ? "visible" : "hidden";
3182 if(shapes.hasOwnProperty(i))
3184 shapes[i].set("visible", val);
3190 this._node.style.visibility = visibility;
3194 this._node.style.visibility = visibility;
3199 * Sets the size of the graphics object.
3202 * @param w {Number} width to set for the instance.
3203 * @param h {Number} height to set for the instance.
3205 setSize: function(w, h) {
3208 this._node.style.width = w + 'px';
3209 this._node.style.height = h + 'px';
3213 * Sets the positon of the graphics object.
3215 * @method setPosition
3216 * @param {Number} x x-coordinate for the object.
3217 * @param {Number} y y-coordinate for the object.
3219 setPosition: function(x, y)
3223 this._node.style.left = x + "px";
3224 this._node.style.top = y + "px";
3228 * Creates a group element
3230 * @method _createGraphic
3233 _createGraphic: function() {
3234 var group = DOCUMENT.createElement('<group xmlns="urn:schemas-microsft.com:vml" style="behavior:url(#default#VML);padding:0px 0px 0px 0px;display:block;position:absolute;top:0px;left:0px;zoom:1;" />');
3239 * Creates a graphic node
3241 * @method _createGraphicNode
3242 * @param {String} type node type to create
3243 * @param {String} pe specified pointer-events value
3244 * @return HTMLElement
3247 _createGraphicNode: function(type)
3249 return DOCUMENT.createElement('<' + type + ' xmlns="urn:schemas-microsft.com:vml" style="behavior:url(#default#VML);display:inline-block;zoom:1;" />');
3254 * Returns a shape based on the id of its dom node.
3256 * @method getShapeById
3257 * @param {String} id Dom id of the shape's node attribute.
3260 getShapeById: function(id)
3262 return this._shapes[id];
3266 * Returns a shape class. Used by `addShape`.
3268 * @method _getShapeClass
3269 * @param {Shape | String} val Indicates which shape class.
3273 _getShapeClass: function(val)
3275 var shape = this._shapeClass[val];
3284 * Look up for shape classes. Used by `addShape` to retrieve a class for instantiation.
3286 * @property _shapeClass
3291 circle: Y.VMLCircle,
3294 ellipse: Y.VMLEllipse,
3295 pieslice: Y.VMLPieSlice
3299 * Allows for creating multiple shapes in order to batch appending and redraw operations.
3302 * @param {Function} method Method to execute.
3304 batch: function(method)
3306 var autoDraw = this.get("autoDraw");
3307 this.set("autoDraw", false);
3309 this.set("autoDraw", autoDraw);
3313 * Returns a document fragment to for attaching shapes.
3315 * @method _getDocFrag
3316 * @return DocumentFragment
3319 _getDocFrag: function()
3323 this._frag = DOCUMENT.createDocumentFragment();
3329 * Adds a shape to the redraw queue and calculates the contentBounds.
3331 * @method addToRedrawQueue
3332 * @param shape {VMLShape}
3335 addToRedrawQueue: function(shape)
3339 this._shapes[shape.get("id")] = shape;
3340 if(!this.get("resizeDown"))
3342 shapeBox = shape.getBounds();
3343 box = this._contentBounds;
3344 box.left = box.left < shapeBox.left ? box.left : shapeBox.left;
3345 box.top = box.top < shapeBox.top ? box.top : shapeBox.top;
3346 box.right = box.right > shapeBox.right ? box.right : shapeBox.right;
3347 box.bottom = box.bottom > shapeBox.bottom ? box.bottom : shapeBox.bottom;
3348 box.width = box.right - box.left;
3349 box.height = box.bottom - box.top;
3350 this._contentBounds = box;
3352 if(this.get("autoDraw"))
3359 * Redraws all shapes.
3366 var autoSize = this.get("autoSize"),
3367 preserveAspectRatio,
3368 node = this.parentNode,
3369 nodeWidth = parseFloat(node.getComputedStyle("width")),
3370 nodeHeight = parseFloat(node.getComputedStyle("height")),
3373 box = this.get("resizeDown") ? this._getUpdatedContentBounds() : this._contentBounds,
3377 bottom = box.bottom,
3378 contentWidth = right - left,
3379 contentHeight = bottom - top,
3385 visible = this.get("visible");
3386 this._node.style.visibility = "hidden";
3389 if(autoSize == "sizeContentToGraphic")
3391 preserveAspectRatio = this.get("preserveAspectRatio");
3392 if(preserveAspectRatio == "none" || contentWidth/contentHeight === nodeWidth/nodeHeight)
3394 xCoordOrigin = left;
3396 xCoordSize = contentWidth;
3397 yCoordSize = contentHeight;
3401 if(contentWidth * nodeHeight/contentHeight > nodeWidth)
3403 aspectRatio = nodeHeight/nodeWidth;
3404 xCoordSize = contentWidth;
3405 yCoordSize = contentWidth * aspectRatio;
3406 scaledHeight = (nodeWidth * (contentHeight/contentWidth)) * (yCoordSize/nodeHeight);
3407 yCoordOrigin = this._calculateCoordOrigin(preserveAspectRatio.slice(5).toLowerCase(), scaledHeight, yCoordSize);
3408 yCoordOrigin = top + yCoordOrigin;
3409 xCoordOrigin = left;
3413 aspectRatio = nodeWidth/nodeHeight;
3414 xCoordSize = contentHeight * aspectRatio;
3415 yCoordSize = contentHeight;
3416 scaledWidth = (nodeHeight * (contentWidth/contentHeight)) * (xCoordSize/nodeWidth);
3417 xCoordOrigin = this._calculateCoordOrigin(preserveAspectRatio.slice(1, 4).toLowerCase(), scaledWidth, xCoordSize);
3418 xCoordOrigin = xCoordOrigin + left;
3422 this._node.style.width = nodeWidth + "px";
3423 this._node.style.height = nodeHeight + "px";
3424 this._node.coordOrigin = xCoordOrigin + ", " + yCoordOrigin;
3428 xCoordSize = contentWidth;
3429 yCoordSize = contentHeight;
3430 this._node.style.width = contentWidth + "px";
3431 this._node.style.height = contentHeight + "px";
3432 this._state.width = contentWidth;
3433 this._state.height = contentHeight;
3436 this._node.coordSize = xCoordSize + ", " + yCoordSize;
3440 this._node.style.width = nodeWidth + "px";
3441 this._node.style.height = nodeHeight + "px";
3442 this._node.coordSize = nodeWidth + ", " + nodeHeight;
3446 this._node.appendChild(this._frag);
3451 this._node.style.visibility = "visible";
3456 * Determines the value for either an x or y coordinate to be used for the <code>coordOrigin</code> of the Graphic.
3458 * @method _calculateCoordOrigin
3459 * @param {String} position The position for placement. Possible values are min, mid and max.
3460 * @param {Number} size The total scaled size of the content.
3461 * @param {Number} coordsSize The coordsSize for the Graphic.
3465 _calculateCoordOrigin: function(position, size, coordsSize)
3474 coord = (size - coordsSize)/2;
3477 coord = (size - coordsSize);
3484 * Recalculates and returns the `contentBounds` for the `Graphic` instance.
3486 * @method _getUpdatedContentBounds
3490 _getUpdatedContentBounds: function()
3495 queue = this._shapes,
3499 if(queue.hasOwnProperty(i))
3502 bounds = shape.getBounds();
3503 box.left = Y_LANG.isNumber(box.left) ? Math.min(box.left, bounds.left) : bounds.left;
3504 box.top = Y_LANG.isNumber(box.top) ? Math.min(box.top, bounds.top) : bounds.top;
3505 box.right = Y_LANG.isNumber(box.right) ? Math.max(box.right, bounds.right) : bounds.right;
3506 box.bottom = Y_LANG.isNumber(box.bottom) ? Math.max(box.bottom, bounds.bottom) : bounds.bottom;
3509 box.left = Y_LANG.isNumber(box.left) ? box.left : 0;
3510 box.top = Y_LANG.isNumber(box.top) ? box.top : 0;
3511 box.right = Y_LANG.isNumber(box.right) ? box.right : 0;
3512 box.bottom = Y_LANG.isNumber(box.bottom) ? box.bottom : 0;
3513 this._contentBounds = box;
3518 * Inserts shape on the top of the tree.
3521 * @param {VMLShape} Shape to add.
3524 _toFront: function(shape)
3526 var contentNode = this._node;
3527 if(shape instanceof Y.VMLShape)
3529 shape = shape.get("node");
3531 if(contentNode && shape)
3533 contentNode.appendChild(shape);
3538 * Inserts shape as the first child of the content node.
3541 * @param {VMLShape} Shape to add.
3544 _toBack: function(shape)
3546 var contentNode = this._node,
3548 if(shape instanceof Y.VMLShape)
3550 shape = shape.get("node");
3552 if(contentNode && shape)
3554 targetNode = contentNode.firstChild;
3557 contentNode.insertBefore(shape, targetNode);
3561 contentNode.appendChild(shape);
3566 Y.VMLGraphic = VMLGraphic;
3570 }, '3.7.1', {"requires": ["graphics"]});