[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/graphics-vml/ -> graphics-vml.js (source)

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1