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