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/
8 YUI.add('graphics-vml', function (Y, NAME) {
10 var IMPLEMENTATION = "vml",
12 SPLITPATHPATTERN = /[a-z][^a-z]*/ig,
13 SPLITARGSPATTERN = /[\-]?[0-9]*[0-9|\.][0-9]*/g,
15 IS_NUM = Y_LANG.isNumber,
16 IS_ARRAY = Y_LANG.isArray,
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.
133 curveTo: function() {
134 this._curveTo.apply(this, [Y.Array(arguments), false]);
139 * Draws a bezier curve.
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.
150 relativeCurveTo: function() {
151 this._curveTo.apply(this, [Y.Array(arguments), true]);
156 * Implements curveTo methods.
159 * @param {Array} args The arguments to be used.
160 * @param {Boolean} relative Indicates whether or not to use relative coordinates.
163 _curveTo: function(args, relative) {
180 command = relative ? " v " : " c ",
181 relativeX = relative ? parseFloat(this._currentX) : 0,
182 relativeY = relative ? parseFloat(this._currentY) : 0;
183 len = args.length - 5;
185 for(i = 0; i < len; i = i + 6)
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]);
209 cp1x = cp1x + relativeX;
210 cp1y = cp1y + relativeY;
211 cp2x = cp2x + relativeX;
212 cp2y = cp2y + 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);
226 this._addToPath(path);
230 * Draws a quadratic bezier curve.
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.
239 quadraticCurveTo: function() {
240 this._quadraticCurveTo.apply(this, [Y.Array(arguments), false]);
245 * Draws a quadratic bezier curve relative to the current position.
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.
254 relativeQuadraticCurveTo: function() {
255 this._quadraticCurveTo.apply(this, [Y.Array(arguments), true]);
260 * Implements quadraticCurveTo methods.
262 * @method _quadraticCurveTo
263 * @param {Array} args The arguments to be used.
264 * @param {Boolean} relative Indicates whether or not to use relative coordinates.
267 _quadraticCurveTo: function(args, relative) {
276 currentX = this._currentX,
277 currentY = this._currentY,
279 len = args.length - 3,
281 relativeX = relative ? parseFloat(this._currentX) : 0,
282 relativeY = relative ? parseFloat(this._currentY) : 0;
283 for(i = 0; i < len; i = i + 4)
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);
300 this._curveTo.apply(this, [bezierArgs, false]);
307 * @param {Number} x x-coordinate
308 * @param {Number} y y-coordinate
309 * @param {Number} w width
310 * @param {Number} h height
313 drawRect: function(x, y, w, h) {
315 this.lineTo(x + w, y);
316 this.lineTo(x + w, y + h);
317 this.lineTo(x, y + h);
325 * Draws a rectangle with rounded corners.
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
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);
350 * Draws a circle. Used internally by `CanvasCircle` class.
353 * @param {Number} x y-coordinate
354 * @param {Number} y x-coordinate
355 * @param {Number} r radius
359 drawCircle: function(x, y, radius) {
365 this._drawingComplete = false;
366 this._trackSize(x + circum, y + circum);
367 this.moveTo((x + circum), (y + radius));
370 this._round(x + radius) +
372 this._round(y + radius) +
374 this._round(radius) +
376 this._round(radius) +
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
396 drawEllipse: function(x, y, w, h) {
402 this._drawingComplete = false;
403 this._trackSize(x + w, y + h);
404 this.moveTo((x + w), (y + yRadius));
407 this._round(x + radius) +
409 this._round(x + radius) +
411 this._round(y + yRadius) +
413 this._round(radius) +
415 this._round(yRadius) +
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
435 drawDiamond: function(x, y, width, height)
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);
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.
460 drawWedge: function(x, y, startAngle, arc, radius)
462 var diameter = radius * 2;
463 if(Math.abs(arc) > 360)
469 startAngle *= -65535;
471 startAngle = Math.round(startAngle);
472 arc = Math.round(arc);
480 this._round(radius) +
482 this._round(radius) +
488 this._trackSize(diameter, diameter);
493 * Draws a line segment from the current drawing position to the specified x and y coordinates.
496 * @param {Number} point1 x-coordinate for the end point.
497 * @param {Number} point2 y-coordinate for the end point.
502 this._lineTo.apply(this, [Y.Array(arguments), false]);
507 * Draws a line segment using the current line style from the current drawing position to the relative x and y coordinates.
509 * @method relativeLineTo
510 * @param {Number} point1 x-coordinate for the end point.
511 * @param {Number} point2 y-coordinate for the end point.
514 relativeLineTo: function()
516 this._lineTo.apply(this, [Y.Array(arguments), true]);
521 * Implements lineTo methods.
524 * @param {Array} args The arguments to be used.
525 * @param {Boolean} relative Indicates whether or not to use relative coordinates.
528 _lineTo: function(args, relative) {
529 var point1 = args[0],
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);
547 this._trackSize.apply(this, [x, y]);
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);
561 this._trackSize.apply(this, [x, y]);
564 this._addToPath(path);
569 * Moves the current drawing position to specified x and y coordinates.
572 * @param {Number} x x-coordinate for the end point.
573 * @param {Number} y y-coordinate for the end point.
578 this._moveTo.apply(this, [Y.Array(arguments), false]);
583 * Moves the current drawing position relative to specified x and y coordinates.
585 * @method relativeMoveTo
586 * @param {Number} x x-coordinate for the end point.
587 * @param {Number} y y-coordinate for the end point.
590 relativeMoveTo: function()
592 this._moveTo.apply(this, [Y.Array(arguments), true]);
597 * Implements moveTo methods.
600 * @param {Array} args The arguments to be used.
601 * @param {Boolean} relative Indicates whether or not to use relative coordinates.
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);
613 this._trackSize(x, y);
624 _closePath: function()
626 var fill = this.get("fill"),
627 stroke = this.get("stroke"),
629 w = this.get("width"),
630 h = this.get("height"),
633 multiplier = this._coordSpaceMultiplier;
634 this._fillChangeHandler();
635 this._strokeChangeHandler();
638 if(fill && fill.color)
649 node.path = path + pathEnd;
651 if(!isNaN(w) && !isNaN(h))
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";
660 this._movePath = null;
661 this._updateTransform();
665 * Completes a drawing operation.
677 * Ends a fill and stroke
682 closePath: function()
684 this._addToPath(" x e");
703 this._movePath = null;
708 * Returns the points on a curve
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.
716 getBezierData: function(points, t) {
717 var n = points.length,
722 for (i = 0; i < n; ++i){
723 tmp[i] = [points[i][0], points[i][1]]; // save input
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];
732 return [ tmp[0][0], tmp[0][1] ];
736 * Calculates the bounding box for a curve
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.
744 _setCurveBoundingBox: function(pts, w, h)
747 left = this._currentX,
749 top = this._currentY,
751 len = Math.round(Math.sqrt((w * w) + (h * h))),
754 for(i = 0; i < len; ++i)
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);
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);
771 * Updates the size of the graphics object
774 * @param {Number} w width
775 * @param {Number} h height
778 _trackSize: function(w, h) {
779 if (w > this._right) {
790 if (h > this._bottom)
794 this._width = this._right - this._left;
795 this._height = this._bottom - this._top;
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.
820 * @param {Object} cfg (optional) Attribute configs
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({
834 * Indicates the type of shape
843 * Init method, invoked during construction.
844 * Calls `initializer` method.
851 this.initializer.apply(this, arguments);
855 * Initializes the shape
858 * @method _initialize
860 initializer: function(cfg)
863 graphic = cfg.graphic,
864 data = this.get("data");
868 this._setGraphic(graphic);
872 host._parsePathData(data);
874 this._updateHandler();
878 * Set the Graphic instance for the shape.
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.
886 _setGraphic: function(render)
889 if(render instanceof Y.VMLGraphic)
891 this._graphic = render;
895 graphic = new Y.VMLGraphic({
898 graphic._appendShape(this);
899 this._graphic = graphic;
900 this._appendStrokeAndFill();
905 * Appends fill and stroke nodes to the shape.
907 * @method _appendStrokeAndFill
910 _appendStrokeAndFill: function()
914 this.node.appendChild(this._strokeNode);
918 this.node.appendChild(this._fillNode);
923 * Creates the dom node for the shape.
926 * @return HTMLElement
929 createNode: function()
932 concat = this._camelCaseConcat,
935 w = this.get("width"),
936 h = this.get("height"),
941 visibility = this.get("visible") ? "visible" : "hidden",
953 type = this._type === "path" ? "shape" : this._type;
954 classString = _getClassName(SHAPE) +
956 _getClassName(concat(IMPLEMENTATION, SHAPE)) +
958 _getClassName(name) +
960 _getClassName(concat(IMPLEMENTATION, name)) +
964 stroke = this._getStrokeProps();
965 fill = this._getFillProps();
969 ' xmlns="urn:schemas-microsft.com:vml" id="' +
973 '" style="behavior:url(#default#VML);display:inline-block;position:absolute;left:' +
985 if(stroke && stroke.weight && stroke.weight > 0)
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"' +
997 ' style="behavior:url(#default#VML);display:inline-block;"' +
998 ' opacity="' + opacity + '"';
1001 strokestring += ' endcap="' + endcap + '"';
1005 strokestring += ' joinstyle="' + joinstyle + '"';
1009 strokestring += ' miterlimit="' + miterlimit + '"';
1013 strokestring += ' dashstyle="' + dashstyle + '"';
1015 strokestring += '></stroke>';
1016 this._strokeNode = DOCUMENT.createElement(strokestring);
1017 nodestring += ' stroked="t"';
1021 nodestring += ' stroked="f"';
1027 fillstring = fill.node;
1028 this._fillNode = DOCUMENT.createElement(fillstring);
1032 nodestring += ' fillcolor="' + fill.color + '"';
1034 nodestring += ' filled="' + fill.filled + '"';
1039 nodestring += '</' + type + '>';
1041 node = DOCUMENT.createElement(nodestring);
1044 this._strokeFlag = false;
1045 this._fillFlag = false;
1049 * Add a class name to each node.
1052 * @param {String} className the class name to add to the node's class attribute
1054 addClass: function(className)
1056 var node = this.node;
1057 Y_DOM.addClass(node, className);
1061 * Removes a class name from each node.
1063 * @method removeClass
1064 * @param {String} className the class name to remove from the node's class attribute
1066 removeClass: function(className)
1068 var node = this.node;
1069 Y_DOM.removeClass(node, className);
1073 * Gets the current position of the node in page coordinates.
1076 * @return Array The XY position of the shape.
1080 var graphic = this._graphic,
1081 parentXY = graphic.getXY(),
1084 return [parentXY[0] + x, parentXY[1] + y];
1088 * Set the position of the shape in page coordinates, regardless of how the node is positioned.
1091 * @param {Array} Contains x & y values for new position (coordinates are page-based)
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]);
1103 * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy.
1106 * @param {VMLShape | HTMLElement} needle The possible node or descendent
1107 * @return Boolean Whether or not this shape is the needle or its ancestor.
1109 contains: function(needle)
1111 var node = needle instanceof Y.Node ? needle._node : needle;
1112 return node === this.node;
1116 * Compares nodes to determine if they match.
1117 * Node instances can be compared to each other and/or HTMLElements.
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.
1122 compareTo: function(refNode) {
1123 var node = this.node;
1124 return node === refNode;
1128 * Test if the supplied node matches the supplied selector.
1131 * @param {String} selector The CSS selector to test against.
1132 * @return Boolean Wheter or not the shape matches the selector.
1134 test: function(selector)
1136 return Y_SELECTOR.test(this.node, selector);
1140 * Calculates and returns properties for setting an initial stroke.
1142 * @method _getStrokeProps
1147 _getStrokeProps: function()
1150 stroke = this.get("stroke"),
1159 if(stroke && stroke.weight && stroke.weight > 0)
1162 linecap = stroke.linecap || "flat";
1163 linejoin = stroke.linejoin || "round";
1164 if(linecap !== "round" && linecap !== "square")
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))
1181 len = dashstyle.length;
1182 for(i = 0; i < len; ++i)
1185 dash[i] = val / stroke.weight;
1188 if(linejoin === "round" || linejoin === "bevel")
1190 props.joinstyle = linejoin;
1194 linejoin = parseInt(linejoin, 10);
1195 if(IS_NUM(linejoin))
1197 props.miterlimit = Math.max(linejoin, 1);
1198 props.joinstyle = "miter";
1201 props.dashstyle = dash;
1207 * Adds a stroke to the shape node.
1209 * @method _strokeChangeHandler
1212 _strokeChangeHandler: function()
1214 if(!this._strokeFlag)
1218 var node = this.node,
1219 stroke = this.get("stroke"),
1228 if(stroke && stroke.weight && stroke.weight > 0)
1230 linecap = stroke.linecap || "flat";
1231 linejoin = stroke.linejoin || "round";
1232 if(linecap !== "round" && linecap !== "square")
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)
1246 this._strokeNode = this._createGraphicNode("stroke");
1247 node.appendChild(this._strokeNode);
1249 this._strokeNode.endcap = linecap;
1250 this._strokeNode.opacity = stroke.opacity;
1251 if(IS_ARRAY(dashstyle))
1254 len = dashstyle.length;
1255 for(i = 0; i < len; ++i)
1258 dash[i] = val / stroke.weight;
1261 if(linejoin === "round" || linejoin === "bevel")
1263 this._strokeNode.joinstyle = linejoin;
1267 linejoin = parseInt(linejoin, 10);
1268 if(IS_NUM(linejoin))
1270 this._strokeNode.miterlimit = Math.max(linejoin, 1);
1271 this._strokeNode.joinstyle = "miter";
1274 this._strokeNode.dashstyle = dash;
1275 this._strokeNode.on = true;
1279 if(this._strokeNode)
1281 this._strokeNode.on = false;
1283 node.stroked = false;
1285 this._strokeFlag = false;
1289 * Calculates and returns properties for setting an initial fill.
1291 * @method _getFillProps
1296 _getFillProps: function()
1298 var fill = this.get("fill"),
1309 if(fill.type === "radial" || fill.type === "linear")
1311 fillOpacity = parseFloat(fill.opacity);
1312 fillOpacity = IS_NUM(fillOpacity) ? fillOpacity : 1;
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 + '"';
1320 if(gradient.hasOwnProperty(i))
1322 fillstring += ' ' + i + '="' + gradient[i] + '"';
1325 fillstring += ' />';
1326 props.node = fillstring;
1330 fillOpacity = parseFloat(fill.opacity);
1332 props.color = fill.color;
1333 if(IS_NUM(fillOpacity))
1335 fillOpacity = Math.max(Math.min(fillOpacity, 1), 0);
1336 props.opacity = fillOpacity;
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 + '"/>';
1345 props.filled = filled;
1351 * Adds a fill to the shape node.
1353 * @method _fillChangeHandler
1356 _fillChangeHandler: function()
1362 var node = this.node,
1363 fill = this.get("fill"),
1371 if(fill.type === "radial" || fill.type === "linear")
1374 gradient = this._getGradientFill(fill);
1379 if(gradient.hasOwnProperty(i))
1383 this._fillNode.colors.value = gradient[i];
1387 this._fillNode[i] = gradient[i];
1394 fillstring = '<fill xmlns="urn:schemas-microsft.com:vml"' +
1395 ' class="vmlfill"' +
1396 ' style="behavior:url(#default#VML);display:inline-block;"';
1399 if(gradient.hasOwnProperty(i))
1401 fillstring += ' ' + i + '="' + gradient[i] + '"';
1404 fillstring += ' />';
1405 this._fillNode = DOCUMENT.createElement(fillstring);
1406 node.appendChild(this._fillNode);
1411 node.fillcolor = fill.color;
1412 fillOpacity = parseFloat(fill.opacity);
1414 if(IS_NUM(fillOpacity) && fillOpacity < 1)
1416 fill.opacity = fillOpacity;
1419 if(this._fillNode.getAttribute("type") !== "solid")
1421 this._fillNode.type = "solid";
1423 this._fillNode.opacity = fillOpacity;
1427 fillstring = '<fill xmlns="urn:schemas-microsft.com:vml"' +
1428 ' class="vmlfill"' +
1429 ' style="behavior:url(#default#VML);display:inline-block;"' +
1431 ' opacity="' + fillOpacity + '"' +
1433 this._fillNode = DOCUMENT.createElement(fillstring);
1434 node.appendChild(this._fillNode);
1437 else if(this._fillNode)
1439 this._fillNode.opacity = 1;
1440 this._fillNode.type = "solid";
1444 node.filled = filled;
1445 this._fillFlag = false;
1448 //not used. remove next release.
1449 _updateFillNode: function(node)
1453 this._fillNode = this._createGraphicNode("fill");
1454 node.appendChild(this._fillNode);
1459 * Calculates and returns an object containing gradient properties for a fill node.
1461 * @method _getGradientFill
1462 * @param {Object} fill Object containing fill properties.
1466 _getGradientFill: function(fill)
1468 var gradientProps = {},
1472 w = this.get("width"),
1473 h = this.get("height"),
1489 rotation = fill.rotation || 0;
1490 if(type === "linear")
1494 rotation = Math.abs(rotation - 270);
1496 else if(rotation < 360)
1498 rotation = 270 + (360 - rotation);
1504 gradientProps.type = "gradient";//"gradientunscaled";
1505 gradientProps.angle = rotation;
1507 else if(type === "radial")
1509 gradientBoxWidth = w * (r * 2);
1510 gradientBoxHeight = h * (r * 2);
1511 fx = r * 2 * (fx - 0.5);
1512 fy = r * 2 * (fy - 0.5);
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) + "%";
1521 for(i = 0;i < len; ++i) {
1524 opacity = stop.opacity;
1525 opacity = isNumber(opacity) ? opacity : 1;
1526 pct = stop.offset || i/(len-1);
1528 pct = Math.round(100 * pct) + "%";
1529 oi = i > 0 ? i + 1 : "";
1530 gradientProps["opacity" + oi] = opacity + "";
1531 colorstring += ", " + pct + " " + color;
1533 if(parseFloat(pct) < 100)
1535 colorstring += ", 100% " + color;
1537 gradientProps.colors = colorstring.substr(2);
1538 return gradientProps;
1542 * Adds a transform to the shape.
1544 * @method _addTransform
1545 * @param {String} type The transform being applied.
1546 * @param {Array} args The arguments for the transform.
1549 _addTransform: function(type, args)
1551 args = Y.Array(args);
1552 this._transform = Y_LANG.trim(this._transform + " " + type + "(" + args.join(", ") + ")");
1554 this._transforms.push(args);
1555 if(this.initialized)
1557 this._updateTransform();
1562 * Applies all transforms.
1564 * @method _updateTransform
1567 _updateTransform: function()
1569 var node = this.node,
1577 matrix = this.matrix,
1578 normalizedMatrix = this._normalizedMatrix,
1579 isPathShape = this instanceof Y.VMLPath,
1581 len = this._transforms.length;
1582 if(this._transforms && this._transforms.length > 0)
1584 transformOrigin = this.get("transformOrigin");
1588 normalizedMatrix.translate(this._left, this._top);
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)
1600 key = this._transforms[i].shift();
1603 normalizedMatrix[key].apply(normalizedMatrix, this._transforms[i]);
1604 matrix[key].apply(matrix, this._transforms[i]);
1609 normalizedMatrix.translate(-this._left, -this._top);
1611 transform = normalizedMatrix.a + "," +
1612 normalizedMatrix.c + "," +
1613 normalizedMatrix.b + "," +
1614 normalizedMatrix.d + "," +
1618 this._graphic.addToRedrawQueue(this);
1623 this._skew = DOCUMENT.createElement(
1624 '<skew class="vmlskew"' +
1625 ' xmlns="urn:schemas-microsft.com:vml"' +
1627 ' style="behavior:url(#default#VML);display:inline-block;"' +
1630 this.node.appendChild(this._skew);
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;
1637 if(this._type !== "path")
1639 this._transforms = [];
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";
1647 * Normalizes the skew offset values between -32767 and 32767.
1649 * @method _getSkewOffsetValue
1650 * @param {Number} val The value to normalize
1654 _getSkewOffsetValue: function(val)
1656 var sign = Y.MatrixUtil.sign(val),
1657 absVal = Math.abs(val);
1658 val = Math.min(absVal, 32767) * sign;
1663 * Storage for translateX
1665 * @property _translateX
1672 * Storage for translateY
1674 * @property _translateY
1681 * Storage for the transform attribute.
1683 * @property _transform
1690 * Specifies a 2d translation.
1693 * @param {Number} x The value to translate on the x-axis.
1694 * @param {Number} y The value to translate on the y-axis.
1696 translate: function(x, y)
1698 this._translateX += x;
1699 this._translateY += y;
1700 this._addTransform("translate", arguments);
1704 * Translates the shape along the x-axis. When translating x and y coordinates,
1705 * use the `translate` method.
1707 * @method translateX
1708 * @param {Number} x The value to translate.
1710 translateX: function(x)
1712 this._translateX += x;
1713 this._addTransform("translateX", arguments);
1717 * Performs a translate on the y-coordinate. When translating x and y coordinates,
1718 * use the `translate` method.
1720 * @method translateY
1721 * @param {Number} y The value to translate.
1723 translateY: function(y)
1725 this._translateY += y;
1726 this._addTransform("translateY", arguments);
1730 * Skews the shape around the x-axis and y-axis.
1733 * @param {Number} x The value to skew on the x-axis.
1734 * @param {Number} y The value to skew on the y-axis.
1738 this._addTransform("skew", arguments);
1742 * Skews the shape around the x-axis.
1745 * @param {Number} x x-coordinate
1749 this._addTransform("skewX", arguments);
1753 * Skews the shape around the y-axis.
1756 * @param {Number} y y-coordinate
1760 this._addTransform("skewY", arguments);
1764 * Rotates the shape clockwise around it transformOrigin.
1767 * @param {Number} deg The degree of the rotation.
1771 this._addTransform("rotate", arguments);
1775 * Specifies a 2d scaling operation.
1778 * @param {Number} val
1782 this._addTransform("scale", arguments);
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.
1790 * @param {String} type event type
1791 * @param {Object} callback function
1794 on: function(type, fn)
1796 if(Y.Node.DOM_EVENTS[type])
1798 return Y.on(type, fn, "#" + this.get("id"));
1800 return Y.on.apply(this, arguments);
1814 * Updates `Shape` based on attribute changes.
1816 * @method _updateHandler
1819 _updateHandler: function()
1823 host._fillChangeHandler();
1824 host._strokeChangeHandler();
1825 node.style.width = this.get("width") + "px";
1826 node.style.height = this.get("height") + "px";
1828 host._updateTransform();
1832 * Creates a graphic node
1834 * @method _createGraphicNode
1835 * @param {String} type node type to create
1836 * @return HTMLElement
1839 _createGraphicNode: function(type)
1841 type = type || this._type;
1842 return DOCUMENT.createElement(
1844 ' xmlns="urn:schemas-microsft.com:vml"' +
1845 ' style="behavior:url(#default#VML);display:inline-block;"' +
1846 ' class="vml' + type + '"' +
1852 * Value function for fill attribute
1855 * @method _getDefaultFill
1858 _getDefaultFill: function() {
1871 * Value function for stroke attribute
1874 * @method _getDefaultStroke
1877 _getDefaultStroke: function()
1888 * Sets the value of an attribute.
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
1899 AttributeLite.prototype.set.apply(host, arguments);
1900 if(host.initialized)
1902 host._updateHandler();
1907 * Returns the bounds for a shape.
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.
1915 getBounds: function()
1917 var isPathShape = this instanceof Y.VMLPath,
1918 w = this.get("width"),
1919 h = this.get("height"),
1926 w = this._right - this._left;
1927 h = this._bottom - this._top;
1929 return this._getContentRect(w, h, x, y);
1933 * Calculates the bounding box for the shape.
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
1942 _getContentRect: function(w, h, x, y)
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(),
1950 len = transforms.length,
1954 isPathShape = this instanceof Y.VMLPath;
1957 matrix.translate(this._left, this._top);
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)
1964 transform = transforms[i];
1965 key = transform.shift();
1968 matrix[key].apply(matrix, transform);
1971 matrix.translate(-transformX, -transformY);
1974 matrix.translate(-this._left, -this._top);
1976 contentRect = matrix.getContentRect(w, h, x, y);
1981 * Places the shape above all other shapes.
1987 var graphic = this.get("graphic");
1990 graphic._toFront(this);
1995 * Places the shape underneath all other shapes.
2001 var graphic = this.get("graphic");
2004 graphic._toBack(this);
2009 * Parses path data string and call mapped methods.
2011 * @method _parsePathData
2012 * @param {String} val The path data
2015 _parsePathData: function(val)
2020 commandArray = Y.Lang.trim(val.match(SPLITPATHPATTERN)),
2024 symbolToMethod = this._pathSymbolToMethod;
2028 len = commandArray.length || 0;
2029 for(i = 0; i < len; i = i + 1)
2031 str = commandArray[i];
2032 methodSymbol = str.substr(0, 1);
2033 args = str.substr(1).match(SPLITARGSPATTERN);
2034 method = symbolToMethod[methodSymbol];
2039 this[method].apply(this, args);
2043 this[method].apply(this);
2058 var graphic = this.get("graphic");
2061 graphic.removeShape(this);
2070 * Implementation for shape destruction
2075 _destroy: function()
2081 this.node.removeChild(this._fillNode);
2082 this._fillNode = null;
2084 if(this._strokeNode)
2086 this.node.removeChild(this._strokeNode);
2087 this._strokeNode = null;
2089 Y.Event.purgeElement(this.node, true);
2090 if(this.node.parentNode)
2092 this.node.parentNode.removeChild(this.node);
2097 }, Y.VMLDrawing.prototype));
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].
2104 * @config transformOrigin
2115 * <p>A string containing, in order, transform operations applied to the shape instance. The `transform` string can contain the following values:
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>
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({
2136 transform: "rotate(45)"
2138 * <p>The code below would apply `translate` and `rotate` to an existing shape.</p>
2140 myRect.set("transform", "translate(40, 50) rotate(45)");
2145 setter: function(val)
2151 this._normalizedMatrix.init();
2152 this._transforms = this.matrix.getTransformArray(val);
2153 len = this._transforms.length;
2154 for(i = 0;i < len; ++i)
2156 transform = this._transforms[i];
2158 this._transform = val;
2164 return this._transform;
2169 * Indicates the x position of shape.
2179 * Indicates the y position of shape.
2189 * Unique id for class instance.
2200 setter: function(val)
2202 var node = this.node;
2205 node.setAttribute("id", val);
2228 * Indicates whether the shape is visible.
2236 setter: function(val){
2237 var node = this.node,
2238 visibility = val ? "visible" : "hidden";
2241 node.style.visibility = visibility;
2248 * Contains information about the fill of the shape.
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.
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>
2260 * <p>If a `linear` or `radial` is specified as the fill type. The following additional property is used:
2262 * <dt>stops</dt><dd>An array of objects containing the following properties:
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>
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>
2278 * <p>The corresponding `SVGShape` class implements the following additional properties.</p>
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>
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>
2291 * <p>These properties are not currently implemented in `CanvasShape` or `VMLShape`.</p>
2297 valueFn: "_getDefaultFill",
2299 setter: function(val)
2303 tmpl = this.get("fill") || this._getDefaultFill();
2307 //ensure, fill type is solid if color is explicitly passed.
2308 if(val.hasOwnProperty("color"))
2314 if(val.hasOwnProperty(i))
2321 if(fill && fill.color)
2323 if(fill.color === undefined || fill.color === "none")
2328 this._fillFlag = true;
2334 * Contains information about the stroke of the shape.
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:
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>
2348 * <dt>linejoin</dt><dd>Specifies a linejoin for the stroke. The following values can be specified:
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>
2362 valueFn: "_getDefaultStroke",
2364 setter: function(val)
2369 tmpl = this.get("stroke") || this._getDefaultStroke();
2372 if(val.hasOwnProperty("weight"))
2374 wt = parseInt(val.weight, 10);
2382 if(val.hasOwnProperty(i))
2389 this._strokeFlag = true;
2394 //Not used. Remove in future.
2399 // Only implemented in SVG
2400 // Determines whether the instance will receive mouse events.
2402 // @config pointerEvents
2406 value: "visiblePainted"
2410 * Dom node for the shape.
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.
2435 setter: function(val)
2437 if(this.get("node"))
2439 this._parsePathData(val);
2446 * Reference to the container Graphic.
2456 return this._graphic;
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.
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, {
2480 * Indicates the width of the shape
2488 var val = Math.max(this._right - this._left, 0);
2494 * Indicates the height of the shape
2502 return Math.max(this._bottom - this._top, 0);
2507 * Indicates the path used for the node.
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.
2533 VMLRect = function()
2535 VMLRect.superclass.constructor.apply(this, arguments);
2537 VMLRect.NAME = "rect";
2538 Y.extend(VMLRect, Y.VMLShape, {
2540 * Indicates the type of shape
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.
2560 VMLEllipse = function()
2562 VMLEllipse.superclass.constructor.apply(this, arguments);
2565 VMLEllipse.NAME = "ellipse";
2567 Y.extend(VMLEllipse, Y.VMLShape, {
2569 * Indicates the type of shape
2577 VMLEllipse.ATTRS = Y.merge(Y.VMLShape.ATTRS, {
2579 * Horizontal radius for the ellipse.
2589 var val = this.get("width");
2590 val = Math.round((val/2) * 100)/100;
2594 setter: function(val)
2597 this.set("width", w);
2603 * Vertical radius for the ellipse.
2614 var val = this.get("height");
2615 val = Math.round((val/2) * 100)/100;
2619 setter: function(val)
2622 this.set("height", h);
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.
2638 VMLCircle = function()
2640 VMLCircle.superclass.constructor.apply(this, arguments);
2643 VMLCircle.NAME = "circle";
2645 Y.extend(VMLCircle, VMLShape, {
2647 * Indicates the type of shape
2656 VMLCircle.ATTRS = Y.merge(VMLShape.ATTRS, {
2658 * Radius for the circle.
2670 * Indicates the width of the shape
2676 setter: function(val)
2678 this.set("radius", val/2);
2684 var radius = this.get("radius"),
2685 val = radius && radius > 0 ? radius * 2 : 0;
2691 * Indicates the height of the shape
2697 setter: function(val)
2699 this.set("radius", val/2);
2705 var radius = this.get("radius"),
2706 val = radius && radius > 0 ? radius * 2 : 0;
2711 Y.VMLCircle = VMLCircle;
2716 * @class VMLPieSlice
2719 VMLPieSlice = function()
2721 VMLPieSlice.superclass.constructor.apply(this, arguments);
2723 VMLPieSlice.NAME = "vmlPieSlice";
2724 Y.extend(VMLPieSlice, Y.VMLShape, Y.mix({
2726 * Indicates the type of shape
2735 * Change event listener
2738 * @method _updateHandler
2742 var x = this.get("cx"),
2744 startAngle = this.get("startAngle"),
2745 arc = this.get("arc"),
2746 radius = this.get("radius");
2748 this.drawWedge(x, y, startAngle, arc, radius);
2751 }, Y.VMLDrawing.prototype));
2752 VMLPieSlice.ATTRS = Y.mix({
2761 * Starting angle in relation to a circle in which to begin the pie slice drawing.
2763 * @config startAngle
2781 * Radius of the circle in which the pie slice is drawn
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.
2801 VMLGraphic = function() {
2802 VMLGraphic.superclass.constructor.apply(this, arguments);
2805 VMLGraphic.NAME = "vmlGraphic";
2807 VMLGraphic.ATTRS = {
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.
2813 * @type Node | String
2818 * Unique id for class instance.
2829 setter: function(val)
2831 var node = this._node;
2834 node.setAttribute("id", val);
2841 * Key value pairs in which a shape instance is associated with its id.
2852 return this._shapes;
2857 * Object containing size and coordinate data for the content of a Graphic in relation to the coordSpace node.
2859 * @config contentBounds
2867 return this._contentBounds;
2872 * The html element that represents to coordinate system of the Graphic instance.
2887 * Indicates the width of the `Graphic`.
2893 setter: function(val)
2897 this._node.style.width = val + "px";
2904 * Indicates the height of the `Graphic`.
2910 setter: function(val)
2914 this._node.style.height = val + "px";
2921 * Determines the sizing of the Graphic.
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>
2937 * @type Boolean | String
2945 * Determines how content is sized when <code>autoSize</code> is set to <code>sizeContentToGraphic</code>.
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>
2961 * @config preserveAspectRatio
2965 preserveAspectRatio: {
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.
2973 * @config resizeDown
2981 * Indicates the x-coordinate for the instance.
2992 setter: function(val)
2997 this._node.style.left = val + "px";
3004 * Indicates the y-coordinate for the instance.
3015 setter: function(val)
3020 this._node.style.top = val + "px";
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.
3042 setter: function(val)
3044 this._toggleVisible(val);
3050 Y.extend(VMLGraphic, Y.GraphicBase, {
3052 * Sets the value of an attribute.
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
3063 attr = arguments[0],
3067 preserveAspectRatio: true,
3071 forceRedraw = false;
3072 AttributeLite.prototype.set.apply(host, arguments);
3073 if(host._state.autoDraw === true && Y.Object.size(this._shapes) > 0)
3075 if(Y_LANG.isString && redrawAttrs[attr])
3079 else if(Y_LANG.isObject(attr))
3081 for(key in redrawAttrs)
3083 if(redrawAttrs.hasOwnProperty(key) && attr[key])
3098 * Storage for `x` attribute.
3107 * Storage for `y` attribute.
3116 * Gets the current position of the graphic instance in page coordinates.
3119 * @return Array The XY position of the shape.
3123 var node = this.parentNode,
3129 xy = Y.DOM.getXY(node);
3135 xy = Y.DOM._getOffset(this._node);
3141 * Initializes the class.
3143 * @method initializer
3146 initializer: function() {
3147 var render = this.get("render"),
3148 visibility = this.get("visible") ? "visible" : "hidden";
3150 this._contentBounds = {
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"));
3163 this.render(render);
3168 * Adds the graphics node to the dom.
3171 * @param {HTMLElement} parentNode node in which to render the graphics node into.
3173 render: function(render) {
3174 var parentNode = render || DOCUMENT.body,
3178 if(render instanceof Y.Node)
3180 parentNode = render._node;
3182 else if(Y.Lang.isString(render))
3184 parentNode = Y.Selector.query(render, DOCUMENT.body, true);
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);
3196 * Removes all nodes.
3202 this.removeAllShapes();
3205 this._removeChildren(this._node);
3206 if(this._node.parentNode)
3208 this._node.parentNode.removeChild(this._node);
3215 * Generates a shape instance by type.
3218 * @param {Object} cfg attributes for the shape
3221 addShape: function(cfg)
3224 if(!this.get("visible"))
3226 cfg.visible = false;
3228 var ShapeClass = this._getShapeClass(cfg.type),
3229 shape = new ShapeClass(cfg);
3230 this._appendShape(shape);
3231 shape._appendStrokeAndFill();
3236 * Adds a shape instance to the graphic instance.
3238 * @method _appendShape
3239 * @param {Shape} shape The shape instance to be added to the graphic.
3242 _appendShape: function(shape)
3244 var node = shape.node,
3245 parentNode = this._frag || this._node;
3246 if(this.get("autoDraw") || this.get("autoSize") === "sizeContentToGraphic")
3248 parentNode.appendChild(node);
3252 this._getDocFrag().appendChild(node);
3257 * Removes a shape instance from from the graphic instance.
3259 * @method removeShape
3260 * @param {Shape|String} shape The instance or id of the shape to be removed.
3262 removeShape: function(shape)
3264 if(!(shape instanceof VMLShape))
3266 if(Y_LANG.isString(shape))
3268 shape = this._shapes[shape];
3271 if(shape && (shape instanceof VMLShape))
3274 this._shapes[shape.get("id")] = null;
3275 delete this._shapes[shape.get("id")];
3277 if(this.get("autoDraw"))
3284 * Removes all shape instances from the dom.
3286 * @method removeAllShapes
3288 removeAllShapes: function()
3290 var shapes = this._shapes,
3294 if(shapes.hasOwnProperty(i))
3296 shapes[i].destroy();
3303 * Removes all child nodes.
3305 * @method _removeChildren
3309 _removeChildren: function(node)
3311 if(node.hasChildNodes())
3314 while(node.firstChild)
3316 child = node.firstChild;
3317 this._removeChildren(child);
3318 node.removeChild(child);
3324 * Clears the graphics object.
3329 this.removeAllShapes();
3330 this._removeChildren(this._node);
3334 * Toggles visibility
3336 * @method _toggleVisible
3337 * @param {Boolean} val indicates visibilitye
3340 _toggleVisible: function(val)
3343 shapes = this._shapes,
3344 visibility = val ? "visible" : "hidden";
3349 if(shapes.hasOwnProperty(i))
3351 shapes[i].set("visible", val);
3357 this._node.style.visibility = visibility;
3361 this._node.style.visibility = visibility;
3366 * Sets the size of the graphics object.
3369 * @param w {Number} width to set for the instance.
3370 * @param h {Number} height to set for the instance.
3372 setSize: function(w, h) {
3375 this._node.style.width = w + 'px';
3376 this._node.style.height = h + 'px';
3380 * Sets the positon of the graphics object.
3382 * @method setPosition
3383 * @param {Number} x x-coordinate for the object.
3384 * @param {Number} y y-coordinate for the object.
3386 setPosition: function(x, y)
3390 this._node.style.left = x + "px";
3391 this._node.style.top = y + "px";
3395 * Creates a group element
3397 * @method _createGraphic
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;"' +
3410 * Creates a graphic node
3412 * @method _createGraphicNode
3413 * @param {String} type node type to create
3414 * @param {String} pe specified pointer-events value
3415 * @return HTMLElement
3418 _createGraphicNode: function(type)
3420 return DOCUMENT.createElement(
3423 ' xmlns="urn:schemas-microsft.com:vml"' +
3424 ' style="behavior:url(#default#VML);display:inline-block;zoom:1;"' +
3431 * Returns a shape based on the id of its dom node.
3433 * @method getShapeById
3434 * @param {String} id Dom id of the shape's node attribute.
3437 getShapeById: function(id)
3439 return this._shapes[id];
3443 * Returns a shape class. Used by `addShape`.
3445 * @method _getShapeClass
3446 * @param {Shape | String} val Indicates which shape class.
3450 _getShapeClass: function(val)
3452 var shape = this._shapeClass[val];
3461 * Look up for shape classes. Used by `addShape` to retrieve a class for instantiation.
3463 * @property _shapeClass
3468 circle: Y.VMLCircle,
3471 ellipse: Y.VMLEllipse,
3472 pieslice: Y.VMLPieSlice
3476 * Allows for creating multiple shapes in order to batch appending and redraw operations.
3479 * @param {Function} method Method to execute.
3481 batch: function(method)
3483 var autoDraw = this.get("autoDraw");
3484 this.set("autoDraw", false);
3486 this.set("autoDraw", autoDraw);
3490 * Returns a document fragment to for attaching shapes.
3492 * @method _getDocFrag
3493 * @return DocumentFragment
3496 _getDocFrag: function()
3500 this._frag = DOCUMENT.createDocumentFragment();
3506 * Adds a shape to the redraw queue and calculates the contentBounds.
3508 * @method addToRedrawQueue
3509 * @param shape {VMLShape}
3512 addToRedrawQueue: function(shape)
3516 this._shapes[shape.get("id")] = shape;
3517 if(!this.get("resizeDown"))
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;
3529 if(this.get("autoDraw"))
3536 * Redraws all shapes.
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")),
3550 box = this.get("resizeDown") ? this._getUpdatedContentBounds() : this._contentBounds,
3554 bottom = box.bottom,
3555 contentWidth = right - left,
3556 contentHeight = bottom - top,
3562 visible = this.get("visible");
3563 this._node.style.visibility = "hidden";
3566 if(autoSize === "sizeContentToGraphic")
3568 preserveAspectRatio = this.get("preserveAspectRatio");
3569 if(preserveAspectRatio === "none" || contentWidth/contentHeight === nodeWidth/nodeHeight)
3571 xCoordOrigin = left;
3573 xCoordSize = contentWidth;
3574 yCoordSize = contentHeight;
3578 if(contentWidth * nodeHeight/contentHeight > nodeWidth)
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;
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;
3599 this._node.style.width = nodeWidth + "px";
3600 this._node.style.height = nodeHeight + "px";
3601 this._node.coordOrigin = xCoordOrigin + ", " + yCoordOrigin;
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;
3613 this._node.coordSize = xCoordSize + ", " + yCoordSize;
3617 this._node.style.width = nodeWidth + "px";
3618 this._node.style.height = nodeHeight + "px";
3619 this._node.coordSize = nodeWidth + ", " + nodeHeight;
3623 this._node.appendChild(this._frag);
3628 this._node.style.visibility = "visible";
3633 * Determines the value for either an x or y coordinate to be used for the <code>coordOrigin</code> of the Graphic.
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.
3642 _calculateCoordOrigin: function(position, size, coordsSize)
3651 coord = (size - coordsSize)/2;
3654 coord = (size - coordsSize);
3661 * Recalculates and returns the `contentBounds` for the `Graphic` instance.
3663 * @method _getUpdatedContentBounds
3667 _getUpdatedContentBounds: function()
3672 queue = this._shapes,
3676 if(queue.hasOwnProperty(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;
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;
3695 * Inserts shape on the top of the tree.
3698 * @param {VMLShape} Shape to add.
3701 _toFront: function(shape)
3703 var contentNode = this._node;
3704 if(shape instanceof Y.VMLShape)
3706 shape = shape.get("node");
3708 if(contentNode && shape)
3710 contentNode.appendChild(shape);
3715 * Inserts shape as the first child of the content node.
3718 * @param {VMLShape} Shape to add.
3721 _toBack: function(shape)
3723 var contentNode = this._node,
3725 if(shape instanceof Y.VMLShape)
3727 shape = shape.get("node");
3729 if(contentNode && shape)
3731 targetNode = contentNode.firstChild;
3734 contentNode.insertBefore(shape, targetNode);
3738 contentNode.appendChild(shape);
3743 Y.VMLGraphic = VMLGraphic;
3747 }, '3.13.0', {"requires": ["graphics"]});