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-svg', function (Y, NAME) {
10 var IMPLEMENTATION = "svg",
12 SPLITPATHPATTERN = /[a-z][^a-z]*/ig,
13 SPLITARGSPATTERN = /[\-]?[0-9]*[0-9|\.][0-9]*/g,
15 AttributeLite = Y.AttributeLite,
23 DOCUMENT = Y.config.doc,
24 _getClassName = Y.ClassNameManager.getClassName;
26 function SVGDrawing(){}
29 * <a href="http://www.w3.org/TR/SVG/">SVG</a> implementation of the <a href="Drawing.html">`Drawing`</a> class.
30 * `SVGDrawing` is not intended to be used directly. Instead, use the <a href="Drawing.html">`Drawing`</a> class.
31 * If the browser has <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities, the <a href="Drawing.html">`Drawing`</a>
32 * class will point to the `SVGDrawing` class.
38 SVGDrawing.prototype = {
40 * Rounds a value to the nearest hundredth.
43 * @param {Number} val Value to be rounded.
47 _round: function(val) {
48 return Math.round(val * 100)/100;
52 * Maps path to methods
54 * @property _pathSymbolToMethod
58 _pathSymbolToMethod: {
65 Q: "quadraticCurveTo",
66 q: "relativeQuadraticCurveTo",
72 * Current x position of the drawing.
81 * Current y position of the drqwing.
90 * Indicates the type of shape
100 * Draws a bezier curve.
103 * @param {Number} cp1x x-coordinate for the first control point.
104 * @param {Number} cp1y y-coordinate for the first control point.
105 * @param {Number} cp2x x-coordinate for the second control point.
106 * @param {Number} cp2y y-coordinate for the second control point.
107 * @param {Number} x x-coordinate for the end point.
108 * @param {Number} y y-coordinate for the end point.
111 curveTo: function() {
112 this._curveTo.apply(this, [Y.Array(arguments), false]);
117 * Draws a bezier curve relative to the current coordinates.
119 * @method relativeCurveTo
120 * @param {Number} cp1x x-coordinate for the first control point.
121 * @param {Number} cp1y y-coordinate for the first control point.
122 * @param {Number} cp2x x-coordinate for the second control point.
123 * @param {Number} cp2y y-coordinate for the second control point.
124 * @param {Number} x x-coordinate for the end point.
125 * @param {Number} y y-coordinate for the end point.
128 relativeCurveTo: function() {
129 this._curveTo.apply(this, [Y.Array(arguments), true]);
134 * Implements curveTo methods.
137 * @param {Array} args The arguments to be used.
138 * @param {Boolean} relative Indicates whether or not to use relative coordinates.
141 _curveTo: function(args, relative) {
159 command = relative ? "c" : "C",
160 relativeX = relative ? parseFloat(this._currentX) : 0,
161 relativeY = relative ? parseFloat(this._currentY) : 0;
162 this._pathArray = this._pathArray || [];
163 if(this._pathType !== command)
165 this._pathType = command;
166 currentArray = [command];
167 this._pathArray.push(currentArray);
171 currentArray = this._pathArray[Math.max(0, this._pathArray.length - 1)];
175 this._pathArray.push(currentArray);
178 pathArrayLen = this._pathArray.length - 1;
179 this._pathArray[pathArrayLen] = this._pathArray[pathArrayLen].concat(args);
180 len = args.length - 5;
181 for(i = 0; i < len; i = i + 6)
183 cp1x = parseFloat(args[i]) + relativeX;
184 cp1y = parseFloat(args[i + 1]) + relativeY;
185 cp2x = parseFloat(args[i + 2]) + relativeX;
186 cp2y = parseFloat(args[i + 3]) + relativeY;
187 x = parseFloat(args[i + 4]) + relativeX;
188 y = parseFloat(args[i + 5]) + relativeY;
189 right = Math.max(x, Math.max(cp1x, cp2x));
190 bottom = Math.max(y, Math.max(cp1y, cp2y));
191 left = Math.min(x, Math.min(cp1x, cp2x));
192 top = Math.min(y, Math.min(cp1y, cp2y));
193 w = Math.abs(right - left);
194 h = Math.abs(bottom - top);
195 pts = [[this._currentX, this._currentY] , [cp1x, cp1y], [cp2x, cp2y], [x, y]];
196 this._setCurveBoundingBox(pts, w, h);
203 * Draws a quadratic bezier curve.
205 * @method quadraticCurveTo
206 * @param {Number} cpx x-coordinate for the control point.
207 * @param {Number} cpy y-coordinate for the control point.
208 * @param {Number} x x-coordinate for the end point.
209 * @param {Number} y y-coordinate for the end point.
212 quadraticCurveTo: function() {
213 this._quadraticCurveTo.apply(this, [Y.Array(arguments), false]);
218 * Draws a quadratic bezier curve relative to the current position.
220 * @method quadraticCurveTo
221 * @param {Number} cpx x-coordinate for the control point.
222 * @param {Number} cpy y-coordinate for the control point.
223 * @param {Number} x x-coordinate for the end point.
224 * @param {Number} y y-coordinate for the end point.
227 relativeQuadraticCurveTo: function() {
228 this._quadraticCurveTo.apply(this, [Y.Array(arguments), true]);
233 * Implements quadraticCurveTo methods.
235 * @method _quadraticCurveTo
236 * @param {Array} args The arguments to be used.
237 * @param {Boolean} relative Indicates whether or not to use relative coordinates.
240 _quadraticCurveTo: function(args, relative) {
256 command = relative ? "q" : "Q",
257 relativeX = relative ? parseFloat(this._currentX) : 0,
258 relativeY = relative ? parseFloat(this._currentY) : 0;
259 this._pathArray = this._pathArray || [];
260 if(this._pathType !== command)
262 this._pathType = command;
263 currentArray = [command];
264 this._pathArray.push(currentArray);
268 currentArray = this._pathArray[Math.max(0, this._pathArray.length - 1)];
272 this._pathArray.push(currentArray);
275 pathArrayLen = this._pathArray.length - 1;
276 this._pathArray[pathArrayLen] = this._pathArray[pathArrayLen].concat(args);
277 len = args.length - 3;
278 for(i = 0; i < len; i = i + 4)
280 cpx = parseFloat(args[i]) + relativeX;
281 cpy = parseFloat(args[i + 1]) + relativeY;
282 x = parseFloat(args[i + 2]) + relativeX;
283 y = parseFloat(args[i + 3]) + relativeY;
284 right = Math.max(x, cpx);
285 bottom = Math.max(y, cpy);
286 left = Math.min(x, cpx);
287 top = Math.min(y, cpy);
288 w = Math.abs(right - left);
289 h = Math.abs(bottom - top);
290 pts = [[this._currentX, this._currentY] , [cpx, cpy], [x, y]];
291 this._setCurveBoundingBox(pts, w, h);
301 * @param {Number} x x-coordinate
302 * @param {Number} y y-coordinate
303 * @param {Number} w width
304 * @param {Number} h height
307 drawRect: function(x, y, w, h) {
309 this.lineTo(x + w, y);
310 this.lineTo(x + w, y + h);
311 this.lineTo(x, y + h);
317 * Draws a rectangle with rounded corners.
319 * @method drawRoundRect
320 * @param {Number} x x-coordinate
321 * @param {Number} y y-coordinate
322 * @param {Number} w width
323 * @param {Number} h height
324 * @param {Number} ew width of the ellipse used to draw the rounded corners
325 * @param {Number} eh height of the ellipse used to draw the rounded corners
328 drawRoundRect: function(x, y, w, h, ew, eh) {
329 this.moveTo(x, y + eh);
330 this.lineTo(x, y + h - eh);
331 this.quadraticCurveTo(x, y + h, x + ew, y + h);
332 this.lineTo(x + w - ew, y + h);
333 this.quadraticCurveTo(x + w, y + h, x + w, y + h - eh);
334 this.lineTo(x + w, y + eh);
335 this.quadraticCurveTo(x + w, y, x + w - ew, y);
336 this.lineTo(x + ew, y);
337 this.quadraticCurveTo(x, y, x, y + eh);
345 * @param {Number} x y-coordinate
346 * @param {Number} y x-coordinate
347 * @param {Number} r radius
351 drawCircle: function(x, y, radius) {
352 var circum = radius * 2;
353 this._drawingComplete = false;
354 this._trackSize(x, y);
355 this._trackSize(x + circum, y + circum);
356 this._pathArray = this._pathArray || [];
357 this._pathArray.push(["M", x + radius, y]);
358 this._pathArray.push(["A", radius, radius, 0, 1, 0, x + radius, y + circum]);
359 this._pathArray.push(["A", radius, radius, 0, 1, 0, x + radius, y]);
368 * @method drawEllipse
369 * @param {Number} x x-coordinate
370 * @param {Number} y y-coordinate
371 * @param {Number} w width
372 * @param {Number} h height
376 drawEllipse: function(x, y, w, h) {
377 var radius = w * 0.5,
379 this._drawingComplete = false;
380 this._trackSize(x, y);
381 this._trackSize(x + w, y + h);
382 this._pathArray = this._pathArray || [];
383 this._pathArray.push(["M", x + radius, y]);
384 this._pathArray.push(["A", radius, yRadius, 0, 1, 0, x + radius, y + h]);
385 this._pathArray.push(["A", radius, yRadius, 0, 1, 0, x + radius, y]);
394 * @method drawDiamond
395 * @param {Number} x y-coordinate
396 * @param {Number} y x-coordinate
397 * @param {Number} width width
398 * @param {Number} height height
402 drawDiamond: function(x, y, width, height)
404 var midWidth = width * 0.5,
405 midHeight = height * 0.5;
406 this.moveTo(x + midWidth, y);
407 this.lineTo(x + width, y + midHeight);
408 this.lineTo(x + midWidth, y + height);
409 this.lineTo(x, y + midHeight);
410 this.lineTo(x + midWidth, y);
418 * @param {Number} x x-coordinate of the wedge's center point
419 * @param {Number} y y-coordinate of the wedge's center point
420 * @param {Number} startAngle starting angle in degrees
421 * @param {Number} arc sweep of the wedge. Negative values draw clockwise.
422 * @param {Number} radius radius of wedge. If [optional] yRadius is defined, then radius is the x radius.
423 * @param {Number} yRadius [optional] y radius for wedge.
427 drawWedge: function(x, y, startAngle, arc, radius, yRadius)
441 diameter = radius * 2,
444 this._pathArray = this._pathArray || [];
445 yRadius = yRadius || radius;
446 if(this._pathType !== "M")
448 this._pathType = "M";
449 currentArray = ["M"];
450 this._pathArray.push(currentArray);
454 currentArray = this._getCurrentArray();
456 pathArrayLen = this._pathArray.length - 1;
457 this._pathArray[pathArrayLen].push(x);
458 this._pathArray[pathArrayLen].push(x);
460 // limit sweep to reasonable numbers
461 if(Math.abs(arc) > 360)
466 // First we calculate how many segments are needed
468 segs = Math.ceil(Math.abs(arc) / 45);
470 // Now calculate the sweep of each segment.
471 segAngle = arc / segs;
473 // The math requires radians rather than degrees. To convert from degrees
474 // use the formula (degrees/180)*Math.PI to get radians.
475 theta = -(segAngle / 180) * Math.PI;
477 // convert angle startAngle to radians
478 angle = (startAngle / 180) * Math.PI;
481 // draw a line from the center to the start of the curve
482 ax = x + Math.cos(startAngle / 180 * Math.PI) * radius;
483 ay = y + Math.sin(startAngle / 180 * Math.PI) * yRadius;
484 this._pathType = "L";
486 this._pathArray[pathArrayLen] = ["L"];
487 this._pathArray[pathArrayLen].push(this._round(ax));
488 this._pathArray[pathArrayLen].push(this._round(ay));
490 this._pathType = "Q";
491 this._pathArray[pathArrayLen] = ["Q"];
492 for(i = 0; i < segs; ++i)
495 angleMid = angle - (theta / 2);
496 bx = x + Math.cos(angle) * radius;
497 by = y + Math.sin(angle) * yRadius;
498 cx = x + Math.cos(angleMid) * (radius / Math.cos(theta / 2));
499 cy = y + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2));
500 this._pathArray[pathArrayLen].push(this._round(cx));
501 this._pathArray[pathArrayLen].push(this._round(cy));
502 this._pathArray[pathArrayLen].push(this._round(bx));
503 this._pathArray[pathArrayLen].push(this._round(by));
508 this._trackSize(diameter, diameter);
513 * Draws a line segment using the current line style from the current drawing position to the specified x and y coordinates.
516 * @param {Number} point1 x-coordinate for the end point.
517 * @param {Number} point2 y-coordinate for the end point.
522 this._lineTo.apply(this, [Y.Array(arguments), false]);
527 * Draws a line segment using the current line style from the current drawing position to the relative x and y coordinates.
529 * @method relativeLineTo
530 * @param {Number} point1 x-coordinate for the end point.
531 * @param {Number} point2 y-coordinate for the end point.
534 relativeLineTo: function()
536 this._lineTo.apply(this, [Y.Array(arguments), true]);
541 * Implements lineTo methods.
544 * @param {Array} args The arguments to be used.
545 * @param {Boolean} relative Indicates whether or not to use relative coordinates.
548 _lineTo: function(args, relative) {
549 var point1 = args[0],
556 command = relative ? "l" : "L",
557 relativeX = relative ? parseFloat(this._currentX) : 0,
558 relativeY = relative ? parseFloat(this._currentY) : 0;
559 this._pathArray = this._pathArray || [];
560 this._shapeType = "path";
562 if(this._pathType !== command)
564 this._pathType = command;
565 currentArray = [command];
566 this._pathArray.push(currentArray);
570 currentArray = this._getCurrentArray();
572 pathArrayLen = this._pathArray.length - 1;
573 if (typeof point1 === 'string' || typeof point1 === 'number') {
574 for (i = 0; i < len; i = i + 2) {
575 x = parseFloat(args[i]);
576 y = parseFloat(args[i + 1]);
577 this._pathArray[pathArrayLen].push(x);
578 this._pathArray[pathArrayLen].push(y);
583 this._trackSize.apply(this, [x, y]);
588 for (i = 0; i < len; ++i) {
589 x = parseFloat(args[i][0]);
590 y = parseFloat(args[i][1]);
591 this._pathArray[pathArrayLen].push(x);
592 this._pathArray[pathArrayLen].push(y);
597 this._trackSize.apply(this, [x, y]);
603 * Moves the current drawing position to specified x and y coordinates.
606 * @param {Number} x x-coordinate for the end point.
607 * @param {Number} y y-coordinate for the end point.
612 this._moveTo.apply(this, [Y.Array(arguments), false]);
617 * Moves the current drawing position relative to specified x and y coordinates.
619 * @method relativeMoveTo
620 * @param {Number} x x-coordinate for the end point.
621 * @param {Number} y y-coordinate for the end point.
624 relativeMoveTo: function()
626 this._moveTo.apply(this, [Y.Array(arguments), true]);
631 * Implements moveTo methods.
634 * @param {Array} args The arguments to be used.
635 * @param {Boolean} relative Indicates whether or not to use relative coordinates.
638 _moveTo: function(args, relative) {
641 x = parseFloat(args[0]),
642 y = parseFloat(args[1]),
643 command = relative ? "m" : "M",
644 relativeX = relative ? parseFloat(this._currentX) : 0,
645 relativeY = relative ? parseFloat(this._currentY) : 0;
646 this._pathArray = this._pathArray || [];
647 this._pathType = command;
648 currentArray = [command];
649 this._pathArray.push(currentArray);
650 pathArrayLen = this._pathArray.length - 1;
651 this._pathArray[pathArrayLen] = this._pathArray[pathArrayLen].concat([x, y]);
656 this._trackSize(x, y);
660 * Completes a drawing operation.
687 this._pathArray = [];
699 _closePath: function()
709 left = parseFloat(this._left),
710 top = parseFloat(this._top),
711 fill = this.get("fill");
714 pathArray = this._pathArray.concat();
715 while(pathArray && pathArray.length > 0)
717 segmentArray = pathArray.shift();
718 len = segmentArray.length;
719 pathType = segmentArray[0];
722 path += pathType + segmentArray[1] + "," + segmentArray[2];
724 else if(pathType === "z" || pathType === "Z")
728 else if(pathType === "C" || pathType === "c")
730 path += pathType + (segmentArray[1] - left)+ "," + (segmentArray[2] - top);
734 path += " " + pathType + parseFloat(segmentArray[1] - left);
744 for(i = 2; i < len; ++i)
746 val = (i % 2 === 0) ? top : left;
747 val = segmentArray[i] - val;
748 path += ", " + parseFloat(val);
752 val = " " + parseFloat(segmentArray[3]) + " " + parseFloat(segmentArray[4]);
753 val += "," + parseFloat(segmentArray[5]) + " " + parseFloat(segmentArray[6] - left);
754 val += "," + parseFloat(segmentArray[7] - top);
759 for(i = 3; i < len - 1; i = i + 2)
761 val = parseFloat(segmentArray[i] - left);
763 val = val + parseFloat(segmentArray[i + 1] - top);
769 if(fill && fill.color)
776 node.setAttribute("d", path);
780 this._fillChangeHandler();
781 this._strokeChangeHandler();
782 this._updateTransform();
787 * Ends a fill and stroke
792 closePath: function()
794 this._pathArray.push(["z"]);
799 * Returns the current array of drawing commands.
801 * @method _getCurrentArray
805 _getCurrentArray: function()
807 var currentArray = this._pathArray[Math.max(0, this._pathArray.length - 1)];
811 this._pathArray.push(currentArray);
817 * Returns the points on a curve
819 * @method getBezierData
820 * @param Array points Array containing the begin, end and control points of a curve.
821 * @param Number t The value for incrementing the next set of points.
825 getBezierData: function(points, t) {
826 var n = points.length,
831 for (i = 0; i < n; ++i){
832 tmp[i] = [points[i][0], points[i][1]]; // save input
835 for (j = 1; j < n; ++j) {
836 for (i = 0; i < n - j; ++i) {
837 tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
838 tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
841 return [ tmp[0][0], tmp[0][1] ];
845 * Calculates the bounding box for a curve
847 * @method _setCurveBoundingBox
848 * @param Array pts Array containing points for start, end and control points of a curve.
849 * @param Number w Width used to calculate the number of points to describe the curve.
850 * @param Number h Height used to calculate the number of points to describe the curve.
853 _setCurveBoundingBox: function(pts, w, h)
856 left = this._currentX,
858 top = this._currentY,
860 len = Math.round(Math.sqrt((w * w) + (h * h))),
863 for(i = 0; i < len; ++i)
865 xy = this.getBezierData(pts, t * i);
866 left = isNaN(left) ? xy[0] : Math.min(xy[0], left);
867 right = isNaN(right) ? xy[0] : Math.max(xy[0], right);
868 top = isNaN(top) ? xy[1] : Math.min(xy[1], top);
869 bottom = isNaN(bottom) ? xy[1] : Math.max(xy[1], bottom);
871 left = Math.round(left * 10)/10;
872 right = Math.round(right * 10)/10;
873 top = Math.round(top * 10)/10;
874 bottom = Math.round(bottom * 10)/10;
875 this._trackSize(right, bottom);
876 this._trackSize(left, top);
880 * Updates the size of the graphics object
883 * @param {Number} w width
884 * @param {Number} h height
887 _trackSize: function(w, h) {
888 if (w > this._right) {
899 if (h > this._bottom)
903 this._width = this._right - this._left;
904 this._height = this._bottom - this._top;
907 Y.SVGDrawing = SVGDrawing;
909 * <a href="http://www.w3.org/TR/SVG/">SVG</a> implementation of the <a href="Shape.html">`Shape`</a> class.
910 * `SVGShape` is not intended to be used directly. Instead, use the <a href="Shape.html">`Shape`</a> class.
911 * If the browser has <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities, the <a href="Shape.html">`Shape`</a>
912 * class will point to the `SVGShape` class.
917 * @param {Object} cfg (optional) Attribute configs
919 SVGShape = function()
921 this._transforms = [];
922 this.matrix = new Y.Matrix();
923 this._normalizedMatrix = new Y.Matrix();
924 SVGShape.superclass.constructor.apply(this, arguments);
927 SVGShape.NAME = "shape";
929 Y.extend(SVGShape, Y.GraphicBase, Y.mix({
931 * Storage for x attribute.
939 * Storage for y attribute.
947 * Init method, invoked during construction.
948 * Calls `initializer` method.
955 this.initializer.apply(this, arguments);
959 * Initializes the shape
962 * @method initializer
964 initializer: function(cfg)
967 graphic = cfg.graphic,
968 data = this.get("data");
972 host._setGraphic(graphic);
976 host._parsePathData(data);
978 host._updateHandler();
982 * Set the Graphic instance for the shape.
984 * @method _setGraphic
985 * @param {Graphic | Node | HTMLElement | String} render This param is used to determine the graphic instance. If it is a
986 * `Graphic` instance, it will be assigned to the `graphic` attribute. Otherwise, a new Graphic instance will be created
987 * and rendered into the dom element that the render represents.
990 _setGraphic: function(render)
993 if(render instanceof Y.SVGGraphic)
995 this._graphic = render;
999 graphic = new Y.SVGGraphic({
1002 graphic._appendShape(this);
1003 this._graphic = graphic;
1008 * Add a class name to each node.
1011 * @param {String} className the class name to add to the node's class attribute
1013 addClass: function(className)
1015 var node = this.node;
1016 node.className.baseVal = Y_LANG.trim([node.className.baseVal, className].join(' '));
1020 * Removes a class name from each node.
1022 * @method removeClass
1023 * @param {String} className the class name to remove from the node's class attribute
1025 removeClass: function(className)
1027 var node = this.node,
1028 classString = node.className.baseVal;
1029 classString = classString.replace(new RegExp(className + ' '), className).replace(new RegExp(className), '');
1030 node.className.baseVal = classString;
1034 * Gets the current position of the node in page coordinates.
1037 * @return Array The XY position of the shape.
1041 var graphic = this._graphic,
1042 parentXY = graphic.getXY(),
1045 return [parentXY[0] + x, parentXY[1] + y];
1049 * Set the position of the shape in page coordinates, regardless of how the node is positioned.
1052 * @param {Array} Contains x & y values for new position (coordinates are page-based)
1056 var graphic = this._graphic,
1057 parentXY = graphic.getXY();
1058 this._x = xy[0] - parentXY[0];
1059 this._y = xy[1] - parentXY[1];
1060 this.set("transform", this.get("transform"));
1064 * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy.
1067 * @param {SVGShape | HTMLElement} needle The possible node or descendent
1068 * @return Boolean Whether or not this shape is the needle or its ancestor.
1070 contains: function(needle)
1072 var node = needle instanceof Y.Node ? needle._node : needle;
1073 return node === this.node;
1077 * Compares nodes to determine if they match.
1078 * Node instances can be compared to each other and/or HTMLElements.
1080 * @param {HTMLElement | Node} refNode The reference node to compare to the node.
1081 * @return {Boolean} True if the nodes match, false if they do not.
1083 compareTo: function(refNode) {
1084 var node = this.node;
1086 return node === refNode;
1090 * Test if the supplied node matches the supplied selector.
1093 * @param {String} selector The CSS selector to test against.
1094 * @return Boolean Wheter or not the shape matches the selector.
1096 test: function(selector)
1098 return Y.Selector.test(this.node, selector);
1102 * Value function for fill attribute
1105 * @method _getDefaultFill
1108 _getDefaultFill: function() {
1121 * Value function for stroke attribute
1124 * @method _getDefaultStroke
1127 _getDefaultStroke: function()
1138 * Creates the dom node for the shape.
1140 * @method createNode
1141 * @return HTMLElement
1144 createNode: function()
1147 node = DOCUMENT.createElementNS("http://www.w3.org/2000/svg", "svg:" + this._type),
1148 id = host.get("id"),
1150 concat = host._camelCaseConcat,
1151 pointerEvents = host.get("pointerEvents");
1154 _getClassName(SHAPE) +
1156 _getClassName(concat(IMPLEMENTATION, SHAPE)) +
1158 _getClassName(name) +
1160 _getClassName(concat(IMPLEMENTATION, name))
1164 node.setAttribute("id", id);
1168 node.setAttribute("pointer-events", pointerEvents);
1170 if(!host.get("visible"))
1172 Y.DOM.setStyle(node, "visibility", "hidden");
1178 * Overrides default `on` method. Checks to see if its a dom interaction event. If so,
1179 * return an event attached to the `node` element. If not, return the normal functionality.
1182 * @param {String} type event type
1183 * @param {Object} callback function
1186 on: function(type, fn)
1188 if(Y.Node.DOM_EVENTS[type])
1190 return Y.on(type, fn, "#" + this.get("id"));
1192 return Y.on.apply(this, arguments);
1196 * Adds a stroke to the shape node.
1198 * @method _strokeChangeHandler
1201 _strokeChangeHandler: function()
1203 var node = this.node,
1204 stroke = this.get("stroke"),
1209 if(stroke && stroke.weight && stroke.weight > 0)
1211 linejoin = stroke.linejoin || "round";
1212 strokeOpacity = parseFloat(stroke.opacity);
1213 dashstyle = stroke.dashstyle || "none";
1214 dash = Y_LANG.isArray(dashstyle) ? dashstyle.toString() : dashstyle;
1215 stroke.color = stroke.color || "#000000";
1216 stroke.weight = stroke.weight || 1;
1217 stroke.opacity = Y_LANG.isNumber(strokeOpacity) ? strokeOpacity : 1;
1218 stroke.linecap = stroke.linecap || "butt";
1219 node.setAttribute("stroke-dasharray", dash);
1220 node.setAttribute("stroke", stroke.color);
1221 node.setAttribute("stroke-linecap", stroke.linecap);
1222 node.setAttribute("stroke-width", stroke.weight);
1223 node.setAttribute("stroke-opacity", stroke.opacity);
1224 if(linejoin === "round" || linejoin === "bevel")
1226 node.setAttribute("stroke-linejoin", linejoin);
1230 linejoin = parseInt(linejoin, 10);
1231 if(Y_LANG.isNumber(linejoin))
1233 node.setAttribute("stroke-miterlimit", Math.max(linejoin, 1));
1234 node.setAttribute("stroke-linejoin", "miter");
1240 node.setAttribute("stroke", "none");
1245 * Adds a fill to the shape node.
1247 * @method _fillChangeHandler
1250 _fillChangeHandler: function()
1252 var node = this.node,
1253 fill = this.get("fill"),
1259 if(type === "linear" || type === "radial")
1261 this._setGradientFill(fill);
1262 node.setAttribute("fill", "url(#grad" + this.get("id") + ")");
1264 else if(!fill.color)
1266 node.setAttribute("fill", "none");
1270 fillOpacity = parseFloat(fill.opacity);
1271 fillOpacity = Y_LANG.isNumber(fillOpacity) ? fillOpacity : 1;
1272 node.setAttribute("fill", fill.color);
1273 node.setAttribute("fill-opacity", fillOpacity);
1278 node.setAttribute("fill", "none");
1283 * Creates a gradient fill
1285 * @method _setGradientFill
1286 * @param {String} type gradient type
1289 _setGradientFill: function(fill) {
1295 isNumber = Y_LANG.isNumber,
1296 graphic = this._graphic,
1298 gradientNode = graphic.getGradientNode("grad" + this.get("id"), type),
1300 w = this.get("width"),
1301 h = this.get("height"),
1302 rotation = fill.rotation || 0,
1303 radCon = Math.PI/180,
1304 tanRadians = parseFloat(parseFloat(Math.tan(rotation * radCon)).toFixed(8)),
1319 if(type === "linear")
1323 if(Math.abs(tanRadians) * w/2 >= h/2)
1335 x1 = cx - ((cy - y1)/tanRadians);
1336 x2 = cx - ((cy - y2)/tanRadians);
1340 if(rotation > 90 && rotation < 270)
1350 y1 = ((tanRadians * (cx - x1)) - cy) * -1;
1351 y2 = ((tanRadians * (cx - x2)) - cy) * -1;
1354 x1 = Math.round(100 * x1/w);
1355 x2 = Math.round(100 * x2/w);
1356 y1 = Math.round(100 * y1/h);
1357 y2 = Math.round(100 * y2/h);
1359 //Set default value if not valid
1360 x1 = isNumber(x1) ? x1 : 0;
1361 x2 = isNumber(x2) ? x2 : 100;
1362 y1 = isNumber(y1) ? y1 : 0;
1363 y2 = isNumber(y2) ? y2 : 0;
1365 gradientNode.setAttribute("spreadMethod", "pad");
1366 gradientNode.setAttribute("width", w);
1367 gradientNode.setAttribute("height", h);
1368 gradientNode.setAttribute("x1", x1 + "%");
1369 gradientNode.setAttribute("x2", x2 + "%");
1370 gradientNode.setAttribute("y1", y1 + "%");
1371 gradientNode.setAttribute("y2", y2 + "%");
1375 gradientNode.setAttribute("cx", (cx * 100) + "%");
1376 gradientNode.setAttribute("cy", (cy * 100) + "%");
1377 gradientNode.setAttribute("fx", (fx * 100) + "%");
1378 gradientNode.setAttribute("fy", (fy * 100) + "%");
1379 gradientNode.setAttribute("r", (r * 100) + "%");
1384 for(i = 0; i < len; ++i)
1386 if(this._stops && this._stops.length > 0)
1388 stopNode = this._stops.shift();
1393 stopNode = graphic._createGraphicNode("stop");
1397 opacity = stop.opacity;
1399 offset = stop.offset || i/(len - 1);
1400 offset = Math.round(offset * 100) + "%";
1401 opacity = isNumber(opacity) ? opacity : 1;
1402 opacity = Math.max(0, Math.min(1, opacity));
1403 def = (i + 1) / len;
1404 stopNode.setAttribute("offset", offset);
1405 stopNode.setAttribute("stop-color", color);
1406 stopNode.setAttribute("stop-opacity", opacity);
1409 gradientNode.appendChild(stopNode);
1411 stopNodes.push(stopNode);
1413 while(this._stops && this._stops.length > 0)
1415 gradientNode.removeChild(this._stops.shift());
1417 this._stops = stopNodes;
1423 * Sets the value of an attribute.
1426 * @param {String|Object} name The name of the attribute. Alternatively, an object of key value pairs can
1427 * be passed in to set multiple attributes at once.
1428 * @param {Any} value The value to set the attribute to. This value is ignored if an object is received as
1434 AttributeLite.prototype.set.apply(host, arguments);
1435 if(host.initialized)
1437 host._updateHandler();
1442 * Specifies a 2d translation.
1445 * @param {Number} x The value to transate on the x-axis.
1446 * @param {Number} y The value to translate on the y-axis.
1448 translate: function()
1450 this._addTransform("translate", arguments);
1454 * Translates the shape along the x-axis. When translating x and y coordinates,
1455 * use the `translate` method.
1457 * @method translateX
1458 * @param {Number} x The value to translate.
1460 translateX: function()
1462 this._addTransform("translateX", arguments);
1466 * Translates the shape along the y-axis. When translating x and y coordinates,
1467 * use the `translate` method.
1469 * @method translateY
1470 * @param {Number} y The value to translate.
1472 translateY: function()
1474 this._addTransform("translateY", arguments);
1478 * Skews the shape around the x-axis and y-axis.
1481 * @param {Number} x The value to skew on the x-axis.
1482 * @param {Number} y The value to skew on the y-axis.
1486 this._addTransform("skew", arguments);
1490 * Skews the shape around the x-axis.
1493 * @param {Number} x x-coordinate
1497 this._addTransform("skewX", arguments);
1501 * Skews the shape around the y-axis.
1504 * @param {Number} y y-coordinate
1508 this._addTransform("skewY", arguments);
1512 * Rotates the shape clockwise around it transformOrigin.
1515 * @param {Number} deg The degree of the rotation.
1519 this._addTransform("rotate", arguments);
1523 * Specifies a 2d scaling operation.
1526 * @param {Number} val
1530 this._addTransform("scale", arguments);
1534 * Adds a transform to the shape.
1536 * @method _addTransform
1537 * @param {String} type The transform being applied.
1538 * @param {Array} args The arguments for the transform.
1541 _addTransform: function(type, args)
1543 args = Y.Array(args);
1544 this._transform = Y_LANG.trim(this._transform + " " + type + "(" + args.join(", ") + ")");
1546 this._transforms.push(args);
1547 if(this.initialized)
1549 this._updateTransform();
1554 * Applies all transforms.
1556 * @method _updateTransform
1559 _updateTransform: function()
1561 var isPath = this._type === "path",
1570 matrix = this.matrix,
1571 normalizedMatrix = this._normalizedMatrix,
1573 len = this._transforms.length;
1575 if(isPath || (this._transforms && this._transforms.length > 0))
1579 transformOrigin = this.get("transformOrigin");
1580 tx = x + (transformOrigin[0] * this.get("width"));
1581 ty = y + (transformOrigin[1] * this.get("height"));
1582 //need to use translate for x/y coords
1585 //adjust origin for custom shapes
1586 if(!(this instanceof Y.SVGPath))
1588 tx = this._left + (transformOrigin[0] * this.get("width"));
1589 ty = this._top + (transformOrigin[1] * this.get("height"));
1591 normalizedMatrix.init({dx: x + this._left, dy: y + this._top});
1593 normalizedMatrix.translate(tx, ty);
1594 for(i = 0; i < len; ++i)
1596 key = this._transforms[i].shift();
1599 normalizedMatrix[key].apply(normalizedMatrix, this._transforms[i]);
1600 matrix[key].apply(matrix, this._transforms[i]);
1604 this._transforms[i].unshift(key);
1607 normalizedMatrix.translate(-tx, -ty);
1608 transform = "matrix(" + normalizedMatrix.a + "," +
1609 normalizedMatrix.b + "," +
1610 normalizedMatrix.c + "," +
1611 normalizedMatrix.d + "," +
1612 normalizedMatrix.dx + "," +
1613 normalizedMatrix.dy + ")";
1615 this._graphic.addToRedrawQueue(this);
1618 node.setAttribute("transform", transform);
1622 this._transforms = [];
1634 var node = this.node;
1635 node.setAttribute("width", this.get("width"));
1636 node.setAttribute("height", this.get("height"));
1637 node.setAttribute("x", this._x);
1638 node.setAttribute("y", this._y);
1639 node.style.left = this._x + "px";
1640 node.style.top = this._y + "px";
1641 this._fillChangeHandler();
1642 this._strokeChangeHandler();
1643 this._updateTransform();
1647 * Updates `Shape` based on attribute changes.
1649 * @method _updateHandler
1652 _updateHandler: function()
1658 * Storage for the transform attribute.
1660 * @property _transform
1667 * Returns the bounds for a shape.
1669 * Calculates the a new bounding box from the original corner coordinates (base on size and position) and the transform matrix.
1670 * The calculated bounding box is used by the graphic instance to calculate its viewBox.
1675 getBounds: function()
1677 var type = this._type,
1678 stroke = this.get("stroke"),
1679 w = this.get("width"),
1680 h = this.get("height"),
1681 x = type === "path" ? 0 : this._x,
1682 y = type === "path" ? 0 : this._y,
1686 if(stroke && stroke.weight)
1690 w = (x + w + wt) - (x - wt);
1691 h = (y + h + wt) - (y - wt);
1695 return this._normalizedMatrix.getContentRect(w, h, x, y);
1699 * Places the shape above all other shapes.
1705 var graphic = this.get("graphic");
1708 graphic._toFront(this);
1713 * Places the shape underneath all other shapes.
1719 var graphic = this.get("graphic");
1722 graphic._toBack(this);
1727 * Parses path data string and call mapped methods.
1729 * @method _parsePathData
1730 * @param {String} val The path data
1733 _parsePathData: function(val)
1738 commandArray = Y.Lang.trim(val.match(SPLITPATHPATTERN)),
1742 symbolToMethod = this._pathSymbolToMethod;
1746 len = commandArray.length || 0;
1747 for(i = 0; i < len; i = i + 1)
1749 str = commandArray[i];
1750 methodSymbol = str.substr(0, 1);
1751 args = str.substr(1).match(SPLITARGSPATTERN);
1752 method = symbolToMethod[methodSymbol];
1757 this[method].apply(this, args);
1761 this[method].apply(this);
1770 * Destroys the shape instance.
1776 var graphic = this.get("graphic");
1779 graphic.removeShape(this);
1788 * Implementation for shape destruction
1793 _destroy: function()
1797 Y.Event.purgeElement(this.node, true);
1798 if(this.node.parentNode)
1800 this.node.parentNode.removeChild(this.node);
1805 }, Y.SVGDrawing.prototype));
1809 * An array of x, y values which indicates the transformOrigin in which to rotate the shape. Valid values range between 0 and 1 representing a
1810 * fraction of the shape's corresponding bounding box dimension. The default value is [0.5, 0.5].
1812 * @config transformOrigin
1823 * <p>A string containing, in order, transform operations applied to the shape instance. The `transform` string can contain the following values:
1826 * <dt>rotate</dt><dd>Rotates the shape clockwise around it transformOrigin.</dd>
1827 * <dt>translate</dt><dd>Specifies a 2d translation.</dd>
1828 * <dt>skew</dt><dd>Skews the shape around the x-axis and y-axis.</dd>
1829 * <dt>scale</dt><dd>Specifies a 2d scaling operation.</dd>
1830 * <dt>translateX</dt><dd>Translates the shape along the x-axis.</dd>
1831 * <dt>translateY</dt><dd>Translates the shape along the y-axis.</dd>
1832 * <dt>skewX</dt><dd>Skews the shape around the x-axis.</dd>
1833 * <dt>skewY</dt><dd>Skews the shape around the y-axis.</dd>
1834 * <dt>matrix</dt><dd>Specifies a 2D transformation matrix comprised of the specified six values.</dd>
1837 * <p>Applying transforms through the transform attribute will reset the transform matrix and apply a new transform. The shape class also contains
1838 * corresponding methods for each transform that will apply the transform to the current matrix. The below code illustrates how you might use the
1839 * `transform` attribute to instantiate a recangle with a rotation of 45 degrees.</p>
1840 var myRect = new Y.Rect({
1844 transform: "rotate(45)"
1846 * <p>The code below would apply `translate` and `rotate` to an existing shape.</p>
1848 myRect.set("transform", "translate(40, 50) rotate(45)");
1853 setter: function(val)
1856 this._normalizedMatrix.init();
1857 this._transforms = this.matrix.getTransformArray(val);
1858 this._transform = val;
1864 return this._transform;
1869 * Unique id for class instance.
1880 setter: function(val)
1882 var node = this.node;
1885 node.setAttribute("id", val);
1892 * Indicates the x position of shape.
1903 setter: function(val)
1905 var transform = this.get("transform");
1909 this.set("transform", transform);
1915 * Indicates the y position of shape.
1926 setter: function(val)
1928 var transform = this.get("transform");
1932 this.set("transform", transform);
1938 * Indicates the width of the shape
1948 * Indicates the height of the shape
1958 * Indicates whether the shape is visible.
1966 setter: function(val){
1967 var visibility = val ? "visible" : "hidden";
1970 this.node.style.visibility = visibility;
1977 * Contains information about the fill of the shape.
1979 * <dt>color</dt><dd>The color of the fill.</dd>
1980 * <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the fill. The default value is 1.</dd>
1981 * <dt>type</dt><dd>Type of fill.
1983 * <dt>solid</dt><dd>Solid single color fill. (default)</dd>
1984 * <dt>linear</dt><dd>Linear gradient fill.</dd>
1985 * <dt>radial</dt><dd>Radial gradient fill.</dd>
1989 * <p>If a `linear` or `radial` is specified as the fill type. The following additional property is used:
1991 * <dt>stops</dt><dd>An array of objects containing the following properties:
1993 * <dt>color</dt><dd>The color of the stop.</dd>
1994 * <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the stop. The default value is 1.
1995 * Note: No effect for IE 6 - 8</dd>
1996 * <dt>offset</dt><dd>Number between 0 and 1 indicating where the color stop is positioned.</dd>
1999 * <p>Linear gradients also have the following property:</p>
2000 * <dt>rotation</dt><dd>Linear gradients flow left to right by default. The rotation property allows you to change the
2001 * flow by rotation. (e.g. A rotation of 180 would make the gradient pain from right to left.)</dd>
2002 * <p>Radial gradients have the following additional properties:</p>
2003 * <dt>r</dt><dd>Radius of the gradient circle.</dd>
2004 * <dt>fx</dt><dd>Focal point x-coordinate of the gradient.</dd>
2005 * <dt>fy</dt><dd>Focal point y-coordinate of the gradient.</dd>
2007 * <p>The x-coordinate of the center of the gradient circle. Determines where the color stop begins. The default value 0.5.</p>
2008 * <p><strong>Note: </strong>Currently, this property is not implemented for corresponding `CanvasShape` and
2009 * `VMLShape` classes which are used on Android or IE 6 - 8.</p>
2012 * <p>The y-coordinate of the center of the gradient circle. Determines where the color stop begins. The default value 0.5.</p>
2013 * <p><strong>Note: </strong>Currently, this property is not implemented for corresponding `CanvasShape` and `VMLShape`
2014 * classes which are used on Android or IE 6 - 8.</p>
2022 valueFn: "_getDefaultFill",
2024 setter: function(val)
2027 tmpl = this.get("fill") || this._getDefaultFill();
2028 fill = (val) ? Y.merge(tmpl, val) : null;
2029 if(fill && fill.color)
2031 if(fill.color === undefined || fill.color === "none")
2041 * Contains information about the stroke of the shape.
2043 * <dt>color</dt><dd>The color of the stroke.</dd>
2044 * <dt>weight</dt><dd>Number that indicates the width of the stroke.</dd>
2045 * <dt>opacity</dt><dd>Number between 0 and 1 that indicates the opacity of the stroke. The default value is 1.</dd>
2046 * <dt>dashstyle</dt>Indicates whether to draw a dashed stroke. When set to "none", a solid stroke is drawn. When set
2047 * to an array, the first index indicates the length of the dash. The second index indicates the length of gap.
2048 * <dt>linecap</dt><dd>Specifies the linecap for the stroke. The following values can be specified:
2050 * <dt>butt (default)</dt><dd>Specifies a butt linecap.</dd>
2051 * <dt>square</dt><dd>Specifies a sqare linecap.</dd>
2052 * <dt>round</dt><dd>Specifies a round linecap.</dd>
2055 * <dt>linejoin</dt><dd>Specifies a linejoin for the stroke. The following values can be specified:
2057 * <dt>round (default)</dt><dd>Specifies that the linejoin will be round.</dd>
2058 * <dt>bevel</dt><dd>Specifies a bevel for the linejoin.</dd>
2059 * <dt>miter limit</dt><dd>An integer specifying the miter limit of a miter linejoin. If you want to specify a linejoin
2060 * of miter, you simply specify the limit as opposed to having separate miter and miter limit values.</dd>
2069 valueFn: "_getDefaultStroke",
2071 setter: function(val)
2073 var tmpl = this.get("stroke") || this._getDefaultStroke(),
2075 if(val && val.hasOwnProperty("weight"))
2077 wt = parseInt(val.weight, 10);
2083 return (val) ? Y.merge(tmpl, val) : null;
2087 // Only implemented in SVG
2088 // Determines whether the instance will receive mouse events.
2090 // @config pointerEvents
2096 var val = "visiblePainted",
2100 node.setAttribute("pointer-events", val);
2105 setter: function(val)
2107 var node = this.node;
2110 node.setAttribute("pointer-events", val);
2117 * Dom node for the shape.
2133 * Represents an SVG Path string. This will be parsed and added to shape's API to represent the SVG data across all
2134 * implementations. Note that when using VML or SVG implementations, part of this content will be added to the DOM using
2135 * respective VML/SVG attributes. If your content comes from an untrusted source, you will need to ensure that no
2136 * malicious code is included in that content.
2142 setter: function(val)
2144 if(this.get("node"))
2146 this._parsePathData(val);
2153 * Reference to the parent graphic instance
2164 return this._graphic;
2168 Y.SVGShape = SVGShape;
2171 * <a href="http://www.w3.org/TR/SVG/">SVG</a> implementation of the <a href="Path.html">`Path`</a> class.
2172 * `SVGPath` is not intended to be used directly. Instead, use the <a href="Path.html">`Path`</a> class.
2173 * If the browser has <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities, the <a href="Path.html">`Path`</a>
2174 * class will point to the `SVGPath` class.
2181 SVGPath = function()
2183 SVGPath.superclass.constructor.apply(this, arguments);
2185 SVGPath.NAME = "path";
2186 Y.extend(SVGPath, Y.SVGShape, {
2188 * Left edge of the path
2197 * Right edge of the path
2206 * Top edge of the path
2215 * Bottom edge of the path
2224 * Indicates the type of shape
2243 SVGPath.ATTRS = Y.merge(Y.SVGShape.ATTRS, {
2245 * Indicates the path used for the node.
2261 * Indicates the width of the shape
2269 var val = Math.max(this._right - this._left, 0);
2275 * Indicates the height of the shape
2283 return Math.max(this._bottom - this._top, 0);
2287 Y.SVGPath = SVGPath;
2289 * <a href="http://www.w3.org/TR/SVG/">SVG</a> implementation of the <a href="Rect.html">`Rect`</a> class.
2290 * `SVGRect` is not intended to be used directly. Instead, use the <a href="Rect.html">`Rect`</a> class.
2291 * If the browser has <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities, the <a href="Rect.html">`Rect`</a>
2292 * class will point to the `SVGRect` class.
2298 SVGRect = function()
2300 SVGRect.superclass.constructor.apply(this, arguments);
2302 SVGRect.NAME = "rect";
2303 Y.extend(SVGRect, Y.SVGShape, {
2305 * Indicates the type of shape
2313 SVGRect.ATTRS = Y.SVGShape.ATTRS;
2314 Y.SVGRect = SVGRect;
2316 * <a href="http://www.w3.org/TR/SVG/">SVG</a> implementation of the <a href="Ellipse.html">`Ellipse`</a> class.
2317 * `SVGEllipse` is not intended to be used directly. Instead, use the <a href="Ellipse.html">`Ellipse`</a> class.
2318 * If the browser has <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities, the <a href="Ellipse.html">`Ellipse`</a>
2319 * class will point to the `SVGEllipse` class.
2325 SVGEllipse = function()
2327 SVGEllipse.superclass.constructor.apply(this, arguments);
2330 SVGEllipse.NAME = "ellipse";
2332 Y.extend(SVGEllipse, SVGShape, {
2334 * Indicates the type of shape
2343 * Updates the shape.
2350 var node = this.node,
2351 w = this.get("width"),
2352 h = this.get("height"),
2359 node.setAttribute("rx", xRadius);
2360 node.setAttribute("ry", yRadius);
2361 node.setAttribute("cx", cx);
2362 node.setAttribute("cy", cy);
2363 this._fillChangeHandler();
2364 this._strokeChangeHandler();
2365 this._updateTransform();
2369 SVGEllipse.ATTRS = Y.merge(SVGShape.ATTRS, {
2371 * Horizontal radius for the ellipse.
2377 setter: function(val)
2379 this.set("width", val * 2);
2384 var val = this.get("width");
2394 * Vertical radius for the ellipse.
2401 setter: function(val)
2403 this.set("height", val * 2);
2408 var val = this.get("height");
2417 Y.SVGEllipse = SVGEllipse;
2419 * <a href="http://www.w3.org/TR/SVG/">SVG</a> implementation of the <a href="Circle.html">`Circle`</a> class.
2420 * `SVGCircle` is not intended to be used directly. Instead, use the <a href="Circle.html">`Circle`</a> class.
2421 * If the browser has <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities, the <a href="Circle.html">`Circle`</a>
2422 * class will point to the `SVGCircle` class.
2428 SVGCircle = function()
2430 SVGCircle.superclass.constructor.apply(this, arguments);
2433 SVGCircle.NAME = "circle";
2435 Y.extend(SVGCircle, Y.SVGShape, {
2438 * Indicates the type of shape
2447 * Updates the shape.
2454 var node = this.node,
2457 radius = this.get("radius"),
2460 node.setAttribute("r", radius);
2461 node.setAttribute("cx", cx);
2462 node.setAttribute("cy", cy);
2463 this._fillChangeHandler();
2464 this._strokeChangeHandler();
2465 this._updateTransform();
2469 SVGCircle.ATTRS = Y.merge(Y.SVGShape.ATTRS, {
2471 * Indicates the width of the shape
2477 setter: function(val)
2479 this.set("radius", val/2);
2485 return this.get("radius") * 2;
2490 * Indicates the height of the shape
2496 setter: function(val)
2498 this.set("radius", val/2);
2504 return this.get("radius") * 2;
2509 * Radius of the circle
2518 Y.SVGCircle = SVGCircle;
2523 * @class SVGPieSlice
2526 SVGPieSlice = function()
2528 SVGPieSlice.superclass.constructor.apply(this, arguments);
2530 SVGPieSlice.NAME = "svgPieSlice";
2531 Y.extend(SVGPieSlice, Y.SVGShape, Y.mix({
2533 * Indicates the type of shape
2542 * Change event listener
2545 * @method _updateHandler
2549 var x = this.get("cx"),
2551 startAngle = this.get("startAngle"),
2552 arc = this.get("arc"),
2553 radius = this.get("radius");
2555 this.drawWedge(x, y, startAngle, arc, radius);
2558 }, Y.SVGDrawing.prototype));
2559 SVGPieSlice.ATTRS = Y.mix({
2568 * Starting angle in relation to a circle in which to begin the pie slice drawing.
2570 * @config startAngle
2588 * Radius of the circle in which the pie slice is drawn
2596 }, Y.SVGShape.ATTRS);
2597 Y.SVGPieSlice = SVGPieSlice;
2599 * <a href="http://www.w3.org/TR/SVG/">SVG</a> implementation of the <a href="Graphic.html">`Graphic`</a> class.
2600 * `SVGGraphic` is not intended to be used directly. Instead, use the <a href="Graphic.html">`Graphic`</a> class.
2601 * If the browser has <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities, the <a href="Graphic.html">`Graphic`</a>
2602 * class will point to the `SVGGraphic` class.
2608 SVGGraphic = function() {
2609 SVGGraphic.superclass.constructor.apply(this, arguments);
2612 SVGGraphic.NAME = "svgGraphic";
2614 SVGGraphic.ATTRS = {
2616 * Whether or not to render the `Graphic` automatically after to a specified parent node after init. This can be a Node
2617 * instance or a CSS selector string.
2620 * @type Node | String
2625 * Unique id for class instance.
2636 setter: function(val)
2638 var node = this._node;
2641 node.setAttribute("id", val);
2648 * Key value pairs in which a shape instance is associated with its id.
2659 return this._shapes;
2664 * Object containing size and coordinate data for the content of a Graphic in relation to the coordSpace node.
2666 * @config contentBounds
2675 return this._contentBounds;
2680 * The html element that represents to coordinate system of the Graphic instance.
2696 * Indicates the width of the `Graphic`.
2702 setter: function(val)
2706 this._node.style.width = val + "px";
2713 * Indicates the height of the `Graphic`.
2719 setter: function(val)
2723 this._node.style.height = val + "px";
2730 * Determines the sizing of the Graphic.
2733 * <dt>sizeContentToGraphic</dt><dd>The Graphic's width and height attributes are, either explicitly set through the
2734 * <code>width</code> and <code>height</code> attributes or are determined by the dimensions of the parent element. The
2735 * content contained in the Graphic will be sized to fit with in the Graphic instance's dimensions. When using this
2736 * setting, the <code>preserveAspectRatio</code> attribute will determine how the contents are sized.</dd>
2737 * <dt>sizeGraphicToContent</dt><dd>(Also accepts a value of true) The Graphic's width and height are determined by the
2738 * size and positioning of the content.</dd>
2739 * <dt>false</dt><dd>The Graphic's width and height attributes are, either explicitly set through the <code>width</code>
2740 * and <code>height</code> attributes or are determined by the dimensions of the parent element. The contents of the
2741 * Graphic instance are not affected by this setting.</dd>
2746 * @type Boolean | String
2754 * Determines how content is sized when <code>autoSize</code> is set to <code>sizeContentToGraphic</code>.
2757 * <dt>none<dt><dd>Do not force uniform scaling. Scale the graphic content of the given element non-uniformly if necessary
2758 * such that the element's bounding box exactly matches the viewport rectangle.</dd>
2759 * <dt>xMinYMin</dt><dd>Force uniform scaling position along the top left of the Graphic's node.</dd>
2760 * <dt>xMidYMin</dt><dd>Force uniform scaling horizontally centered and positioned at the top of the Graphic's node.<dd>
2761 * <dt>xMaxYMin</dt><dd>Force uniform scaling positioned horizontally from the right and vertically from the top.</dd>
2762 * <dt>xMinYMid</dt>Force uniform scaling positioned horizontally from the left and vertically centered.</dd>
2763 * <dt>xMidYMid (the default)</dt><dd>Force uniform scaling with the content centered.</dd>
2764 * <dt>xMaxYMid</dt><dd>Force uniform scaling positioned horizontally from the right and vertically centered.</dd>
2765 * <dt>xMinYMax</dt><dd>Force uniform scaling positioned horizontally from the left and vertically from the bottom.</dd>
2766 * <dt>xMidYMax</dt><dd>Force uniform scaling horizontally centered and position vertically from the bottom.</dd>
2767 * <dt>xMaxYMax</dt><dd>Force uniform scaling positioned horizontally from the right and vertically from the bottom.</dd>
2770 * @config preserveAspectRatio
2774 preserveAspectRatio: {
2779 * The contentBounds will resize to greater values but not to smaller values. (for performance)
2780 * When resizing the contentBounds down is desirable, set the resizeDown value to true.
2782 * @config resizeDown
2790 * Indicates the x-coordinate for the instance.
2801 setter: function(val)
2806 this._node.style.left = val + "px";
2813 * Indicates the y-coordinate for the instance.
2824 setter: function(val)
2829 this._node.style.top = val + "px";
2836 * Indicates whether or not the instance will automatically redraw after a change is made to a shape.
2837 * This property will get set to false when batching operations.
2851 setter: function(val)
2853 this._toggleVisible(val);
2859 // Indicates the pointer-events setting for the svg:svg element.
2861 // @config pointerEvents
2869 Y.extend(SVGGraphic, Y.GraphicBase, {
2871 * Sets the value of an attribute.
2874 * @param {String|Object} name The name of the attribute. Alternatively, an object of key value pairs can
2875 * be passed in to set multiple attributes at once.
2876 * @param {Any} value The value to set the attribute to. This value is ignored if an object is received as
2882 attr = arguments[0],
2886 preserveAspectRatio: true,
2890 forceRedraw = false;
2891 AttributeLite.prototype.set.apply(host, arguments);
2892 if(host._state.autoDraw === true && Y.Object.size(this._shapes) > 0)
2894 if(Y_LANG.isString && redrawAttrs[attr])
2898 else if(Y_LANG.isObject(attr))
2900 for(key in redrawAttrs)
2902 if(redrawAttrs.hasOwnProperty(key) && attr[key])
2917 * Storage for `x` attribute.
2926 * Storage for `y` attribute.
2935 * Gets the current position of the graphic instance in page coordinates.
2938 * @return Array The XY position of the shape.
2942 var node = this._node,
2946 xy = Y.DOM.getXY(node);
2952 * Initializes the class.
2954 * @method initializer
2957 initializer: function() {
2958 var render = this.get("render"),
2959 visibility = this.get("visible") ? "visible" : "hidden";
2961 this._contentBounds = {
2967 this._gradients = {};
2968 this._node = DOCUMENT.createElement('div');
2969 this._node.style.position = "absolute";
2970 this._node.style.left = this.get("x") + "px";
2971 this._node.style.top = this.get("y") + "px";
2972 this._node.style.visibility = visibility;
2973 this._contentNode = this._createGraphics();
2974 this._contentNode.style.visibility = visibility;
2975 this._contentNode.setAttribute("id", this.get("id"));
2976 this._node.appendChild(this._contentNode);
2979 this.render(render);
2984 * Adds the graphics node to the dom.
2987 * @param {HTMLElement} parentNode node in which to render the graphics node into.
2989 render: function(render) {
2990 var parentNode = render || DOCUMENT.body,
2993 if(render instanceof Y.Node)
2995 parentNode = render._node;
2997 else if(Y.Lang.isString(render))
2999 parentNode = Y.Selector.query(render, DOCUMENT.body, true);
3001 w = this.get("width") || parseInt(Y.DOM.getComputedStyle(parentNode, "width"), 10);
3002 h = this.get("height") || parseInt(Y.DOM.getComputedStyle(parentNode, "height"), 10);
3003 parentNode.appendChild(this._node);
3004 this.set("width", w);
3005 this.set("height", h);
3010 * Removes all nodes.
3016 this.removeAllShapes();
3017 if(this._contentNode)
3019 this._removeChildren(this._contentNode);
3020 if(this._contentNode.parentNode)
3022 this._contentNode.parentNode.removeChild(this._contentNode);
3024 this._contentNode = null;
3028 this._removeChildren(this._node);
3029 if(this._node.parentNode)
3031 this._node.parentNode.removeChild(this._node);
3038 * Generates a shape instance by type.
3041 * @param {Object} cfg attributes for the shape
3044 addShape: function(cfg)
3047 if(!this.get("visible"))
3049 cfg.visible = false;
3051 var ShapeClass = this._getShapeClass(cfg.type),
3052 shape = new ShapeClass(cfg);
3053 this._appendShape(shape);
3058 * Adds a shape instance to the graphic instance.
3060 * @method _appendShape
3061 * @param {Shape} shape The shape instance to be added to the graphic.
3064 _appendShape: function(shape)
3066 var node = shape.node,
3067 parentNode = this._frag || this._contentNode;
3068 if(this.get("autoDraw"))
3070 parentNode.appendChild(node);
3074 this._getDocFrag().appendChild(node);
3079 * Removes a shape instance from from the graphic instance.
3081 * @method removeShape
3082 * @param {Shape|String} shape The instance or id of the shape to be removed.
3084 removeShape: function(shape)
3086 if(!(shape instanceof SVGShape))
3088 if(Y_LANG.isString(shape))
3090 shape = this._shapes[shape];
3093 if(shape && shape instanceof SVGShape)
3096 delete this._shapes[shape.get("id")];
3098 if(this.get("autoDraw"))
3106 * Removes all shape instances from the dom.
3108 * @method removeAllShapes
3110 removeAllShapes: function()
3112 var shapes = this._shapes,
3116 if(shapes.hasOwnProperty(i))
3118 shapes[i]._destroy();
3125 * Removes all child nodes.
3127 * @method _removeChildren
3128 * @param {HTMLElement} node
3131 _removeChildren: function(node)
3133 if(node.hasChildNodes())
3136 while(node.firstChild)
3138 child = node.firstChild;
3139 this._removeChildren(child);
3140 node.removeChild(child);
3146 * Clears the graphics object.
3151 this.removeAllShapes();
3155 * Toggles visibility
3157 * @method _toggleVisible
3158 * @param {Boolean} val indicates visibilitye
3161 _toggleVisible: function(val)
3164 shapes = this._shapes,
3165 visibility = val ? "visible" : "hidden";
3170 if(shapes.hasOwnProperty(i))
3172 shapes[i].set("visible", val);
3176 if(this._contentNode)
3178 this._contentNode.style.visibility = visibility;
3182 this._node.style.visibility = visibility;
3187 * Returns a shape class. Used by `addShape`.
3189 * @method _getShapeClass
3190 * @param {Shape | String} val Indicates which shape class.
3194 _getShapeClass: function(val)
3196 var shape = this._shapeClass[val];
3205 * Look up for shape classes. Used by `addShape` to retrieve a class for instantiation.
3207 * @property _shapeClass
3212 circle: Y.SVGCircle,
3215 ellipse: Y.SVGEllipse,
3216 pieslice: Y.SVGPieSlice
3220 * Returns a shape based on the id of its dom node.
3222 * @method getShapeById
3223 * @param {String} id Dom id of the shape's node attribute.
3226 getShapeById: function(id)
3228 var shape = this._shapes[id];
3233 * Allows for creating multiple shapes in order to batch appending and redraw operations.
3236 * @param {Function} method Method to execute.
3238 batch: function(method)
3240 var autoDraw = this.get("autoDraw");
3241 this.set("autoDraw", false);
3243 this.set("autoDraw", autoDraw);
3247 * Returns a document fragment to for attaching shapes.
3249 * @method _getDocFrag
3250 * @return DocumentFragment
3253 _getDocFrag: function()
3257 this._frag = DOCUMENT.createDocumentFragment();
3263 * Redraws all shapes.
3270 var autoSize = this.get("autoSize"),
3271 preserveAspectRatio = this.get("preserveAspectRatio"),
3272 box = this.get("resizeDown") ? this._getUpdatedContentBounds() : this._contentBounds,
3276 bottom = box.bottom,
3277 width = right - left,
3278 height = bottom - top,
3286 if(autoSize === "sizeContentToGraphic")
3289 computedWidth = parseFloat(Y.DOM.getComputedStyle(node, "width"));
3290 computedHeight = parseFloat(Y.DOM.getComputedStyle(node, "height"));
3291 computedLeft = computedTop = 0;
3292 this._contentNode.setAttribute("preserveAspectRatio", preserveAspectRatio);
3296 computedWidth = width;
3297 computedHeight = height;
3298 computedLeft = left;
3300 this._state.width = width;
3301 this._state.height = height;
3304 this._node.style.width = width + "px";
3305 this._node.style.height = height + "px";
3311 computedWidth = width;
3312 computedHeight = height;
3313 computedLeft = left;
3316 if(this._contentNode)
3318 this._contentNode.style.left = computedLeft + "px";
3319 this._contentNode.style.top = computedTop + "px";
3320 this._contentNode.setAttribute("width", computedWidth);
3321 this._contentNode.setAttribute("height", computedHeight);
3322 this._contentNode.style.width = computedWidth + "px";
3323 this._contentNode.style.height = computedHeight + "px";
3324 this._contentNode.setAttribute("viewBox", "" + left + " " + top + " " + width + " " + height + "");
3328 if(this._contentNode)
3330 this._contentNode.appendChild(this._frag);
3337 * Adds a shape to the redraw queue and calculates the contentBounds. Used internally
3338 * by `Shape` instances.
3340 * @method addToRedrawQueue
3341 * @param shape {SVGShape}
3344 addToRedrawQueue: function(shape)
3348 this._shapes[shape.get("id")] = shape;
3349 if(!this.get("resizeDown"))
3351 shapeBox = shape.getBounds();
3352 box = this._contentBounds;
3353 box.left = box.left < shapeBox.left ? box.left : shapeBox.left;
3354 box.top = box.top < shapeBox.top ? box.top : shapeBox.top;
3355 box.right = box.right > shapeBox.right ? box.right : shapeBox.right;
3356 box.bottom = box.bottom > shapeBox.bottom ? box.bottom : shapeBox.bottom;
3357 box.width = box.right - box.left;
3358 box.height = box.bottom - box.top;
3359 this._contentBounds = box;
3361 if(this.get("autoDraw"))
3368 * Recalculates and returns the `contentBounds` for the `Graphic` instance.
3370 * @method _getUpdatedContentBounds
3374 _getUpdatedContentBounds: function()
3379 queue = this._shapes,
3383 if(queue.hasOwnProperty(i))
3386 bounds = shape.getBounds();
3387 box.left = Y_LANG.isNumber(box.left) ? Math.min(box.left, bounds.left) : bounds.left;
3388 box.top = Y_LANG.isNumber(box.top) ? Math.min(box.top, bounds.top) : bounds.top;
3389 box.right = Y_LANG.isNumber(box.right) ? Math.max(box.right, bounds.right) : bounds.right;
3390 box.bottom = Y_LANG.isNumber(box.bottom) ? Math.max(box.bottom, bounds.bottom) : bounds.bottom;
3393 box.left = Y_LANG.isNumber(box.left) ? box.left : 0;
3394 box.top = Y_LANG.isNumber(box.top) ? box.top : 0;
3395 box.right = Y_LANG.isNumber(box.right) ? box.right : 0;
3396 box.bottom = Y_LANG.isNumber(box.bottom) ? box.bottom : 0;
3397 this._contentBounds = box;
3402 * Creates a contentNode element
3404 * @method _createGraphics
3407 _createGraphics: function() {
3408 var contentNode = this._createGraphicNode("svg"),
3409 pointerEvents = this.get("pointerEvents");
3410 contentNode.style.position = "absolute";
3411 contentNode.style.top = "0px";
3412 contentNode.style.left = "0px";
3413 contentNode.style.overflow = "auto";
3414 contentNode.setAttribute("overflow", "auto");
3415 contentNode.setAttribute("pointer-events", pointerEvents);
3420 * Creates a graphic node
3422 * @method _createGraphicNode
3423 * @param {String} type node type to create
3424 * @param {String} pe specified pointer-events value
3425 * @return HTMLElement
3428 _createGraphicNode: function(type, pe)
3430 var node = DOCUMENT.createElementNS("http://www.w3.org/2000/svg", "svg:" + type),
3432 if(type !== "defs" && type !== "stop" && type !== "linearGradient" && type !== "radialGradient")
3434 node.setAttribute("pointer-events", v);
3440 * Returns a reference to a gradient definition based on an id and type.
3442 * @method getGradientNode
3443 * @param {String} key id that references the gradient definition
3444 * @param {String} type description of the gradient type
3445 * @return HTMLElement
3448 getGradientNode: function(key, type)
3450 var gradients = this._gradients,
3452 nodeType = type + "Gradient";
3453 if(gradients.hasOwnProperty(key) && gradients[key].tagName.indexOf(type) > -1)
3455 gradient = this._gradients[key];
3459 gradient = this._createGraphicNode(nodeType);
3462 this._defs = this._createGraphicNode("defs");
3463 this._contentNode.appendChild(this._defs);
3465 this._defs.appendChild(gradient);
3466 key = key || "gradient" + Math.round(100000 * Math.random());
3467 gradient.setAttribute("id", key);
3468 if(gradients.hasOwnProperty(key))
3470 this._defs.removeChild(gradients[key]);
3472 gradients[key] = gradient;
3478 * Inserts shape on the top of the tree.
3481 * @param {SVGShape} Shape to add.
3484 _toFront: function(shape)
3486 var contentNode = this._contentNode;
3487 if(shape instanceof Y.SVGShape)
3489 shape = shape.get("node");
3491 if(contentNode && shape)
3493 contentNode.appendChild(shape);
3498 * Inserts shape as the first child of the content node.
3501 * @param {SVGShape} Shape to add.
3504 _toBack: function(shape)
3506 var contentNode = this._contentNode,
3508 if(shape instanceof Y.SVGShape)
3510 shape = shape.get("node");
3512 if(contentNode && shape)
3514 targetNode = contentNode.firstChild;
3517 contentNode.insertBefore(shape, targetNode);
3521 contentNode.appendChild(shape);
3527 Y.SVGGraphic = SVGGraphic;
3531 }, '3.13.0', {"requires": ["graphics"]});