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