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