Requires

Provides

VML implementation for ART

Authors:
[Simo Kinnunen](http://twitter.com/sorccu), [Valerio Proietti](http://mad4milk.net), [Sebastian Markbåge](http://calyptus.eu/)
  1. 12
  2. 13
  3. 14
  4. 15
  5. 16
(function(){ var precision = 100, UID = 0; var defaultBox = { left: 0, top: 0, width: 500, height: 500 };

VML Base Class

  1. 20
  2. 21
  3. 22
  4. 23
  5. 24
  6. 25
  7. 26
  8. 27
  9. 28
  10. 29
  11. 30
  12. 31
  13. 32
  14. 33
  15. 34
  16. 35
  17. 36
  18. 37
  19. 38
  20. 39
  21. 40
  22. 41
  23. 42
  24. 43
  25. 44
  26. 45
  27. 46
  28. 47
  29. 48
  30. 49
  31. 50
  32. 51
  33. 52
  34. 53
  35. 54
  36. 55
  37. 56
  38. 57
  39. 58
  40. 59
  41. 60
  42. 61
  43. 62
  44. 63
ART.VML = new Class({ Extends: ART.Element, Implements: ART.Container, initialize: function(width, height){ this.vml = document.createElement('vml'); this.element = document.createElement('av:group'); this.vml.appendChild(this.element); this.children = []; if (width != null && height != null) this.resize(width, height); }, inject: function(element){ if (element.element) element = element.element; element.appendChild(this.vml); return this; }, resize: function(width, height){ this.width = width; this.height = height; var style = this.vml.style; style.pixelWidth = width; style.pixelHeight = height; style = this.element.style; style.width = width; style.height = height; var halfPixel = (0.5 * precision); this.element.coordorigin = halfPixel + ',' + halfPixel; this.element.coordsize = (width * precision) + ',' + (height * precision); return this; }, toElement: function(){ return this.vml; } });

VML Initialization

  1. 67
  2. 68
  3. 69
  4. 70
  5. 71
  6. 72
  7. 73
  8. 74
  9. 75
  10. 76
  11. 77
  12. 78
  13. 79
  14. 80
  15. 81
  16. 82
  17. 83
  18. 84
  19. 85
  20. 86
  21. 87
  22. 88
  23. 89
  24. 90
  25. 91
  26. 92
var VMLCSS = 'behavior:url(#default#VML);display:inline-block;position:absolute;left:0px;top:0px;'; var styleSheet, styledTags = {}, styleTag = function(tag){ if (styleSheet) styledTags[tag] = styleSheet.addRule('av\\:' + tag, VMLCSS); }; ART.VML.init = function(document){ var namespaces = document.namespaces; if (!namespaces) return false; namespaces.add('av', 'urn:schemas-microsoft-com:vml'); namespaces.add('ao', 'urn:schemas-microsoft-com:office:office'); styleSheet = document.createStyleSheet(); styleSheet.addRule('vml', 'display:inline-block;position:relative;overflow:hidden;'); styleTag('skew'); styleTag('fill'); styleTag('stroke'); styleTag('path'); styleTag('textpath'); styleTag('group'); return true; };

VML Element Class

  1. 96
  2. 97
  3. 98
  4. 99
  5. 100
  6. 101
  7. 102
  8. 103
  9. 104
  10. 105
  11. 106
  12. 107
  13. 108
ART.VML.Element = new Class({ Extends: ART.Element, Implements: ART.Transform, initialize: function(tag){ this.uid = String.uniqueID(); if (!(tag in styledTags)) styleTag(tag); var element = this.element = document.createElement('av:' + tag); element.setAttribute('id', 'e' + this.uid); },

dom

  1. 112
  2. 113
  3. 114
  4. 115
  5. 116
  6. 117
  7. 118
  8. 119
  9. 120
  10. 121
  11. 122
  12. 123
  13. 124
  14. 125
  15. 126
  16. 127
  17. 128
  18. 129
inject: function(container){ this.eject(); this.container = container; container.children.include(this); this._transform(); this.parent(container); return this; }, eject: function(){ if (this.container){ this.container.children.erase(this); this.container = null; this.parent(); } return this; },

visibility

  1. 133
  2. 134
  3. 135
  4. 136
  5. 137
  6. 138
  7. 139
  8. 140
  9. 141
hide: function(){ this.element.style.display = 'none'; return this; }, show: function(){ this.element.style.display = ''; return this; },

interaction

  1. 145
  2. 146
  3. 147
  4. 148
  5. 149
  6. 150
  7. 151
indicate: function(cursor, tooltip){ if (cursor) this.element.style.cursor = cursor; if (tooltip) this.element.title = tooltip; return this; } });

VML Group Class

  1. 155
  2. 156
  3. 157
  4. 158
  5. 159
  6. 160
  7. 161
  8. 162
  9. 163
  10. 164
  11. 165
ART.VML.Group = new Class({ Extends: ART.VML.Element, Implements: ART.Container, initialize: function(width, height){ this.parent('group'); this.width = width; this.height = height; this.children = []; },

dom

  1. 169
  2. 170
  3. 171
  4. 172
  5. 173
  6. 174
  7. 175
  8. 176
  9. 177
  10. 178
  11. 179
  12. 180
  13. 181
  14. 182
  15. 183
  16. 184
  17. 185
  18. 186
  19. 187
  20. 188
  21. 189
  22. 190
  23. 191
  24. 192
  25. 193
  26. 194
  27. 195
  28. 196
  29. 197
inject: function(container){ this.parent(container); this._transform(); return this; }, eject: function(){ this.parent(); return this; }, _transform: function(){ var element = this.element; element.coordorigin = '0,0'; element.coordsize = '1000,1000'; element.style.left = 0; element.style.top = 0; element.style.width = 1000; element.style.height = 1000; element.style.rotation = 0; var container = this.container; this._activeTransform = container ? new ART.Transform(container._activeTransform).transform(this) : this; var children = this.children; for (var i = 0, l = children.length; i < l; i++) children[i]._transform(); } });

VML Base Shape Class

  1. 201
  2. 202
  3. 203
  4. 204
  5. 205
  6. 206
  7. 207
  8. 208
  9. 209
  10. 210
  11. 211
  12. 212
  13. 213
  14. 214
  15. 215
  16. 216
  17. 217
  18. 218
  19. 219
  20. 220
ART.VML.Base = new Class({ Extends: ART.VML.Element, initialize: function(tag){ this.parent(tag); var element = this.element; var skew = this.skewElement = document.createElement('av:skew'); skew.on = true; element.appendChild(skew); var fill = this.fillElement = document.createElement('av:fill'); fill.on = false; element.appendChild(fill); var stroke = this.strokeElement = document.createElement('av:stroke'); stroke.on = false; element.appendChild(stroke); },

transform

  1. 224
  2. 225
_transform: function(){ var container = this.container;

Active Transformation Matrix

  1. 228
var m = container ? new ART.Transform(container._activeTransform).transform(this) : this;

Box in shape user space

  1. 232
  2. 233
  3. 234
  4. 235
  5. 236
  6. 237
var box = this._boxCoords || this._size || defaultBox; var originX = box.left || 0, originY = box.top || 0, width = box.width || 1, height = box.height || 1;

Flipped

  1. 240
  2. 241
  3. 242
  4. 243
  5. 244
var flip = m.yx / m.xx > m.yy / m.xy; if (m.xx < 0 ? m.xy >= 0 : m.xy < 0) flip = !flip; flip = flip ? -1 : 1; m = new ART.Transform().scale(flip, 1).transform(m);

Rotation is approximated based on the transform

  1. 247
var rotation = Math.atan2(-m.xy, m.yy) * 180 / Math.PI;

Reverse the rotation, leaving the final transform in box space

  1. 250
  2. 251
  3. 252
  4. 253
  5. 254
  6. 255
  7. 256
  8. 257
  9. 258
  10. 259
  11. 260
  12. 261
var rad = rotation * Math.PI / 180, sin = Math.sin(rad), cos = Math.cos(rad); var transform = new ART.Transform( (m.xx * cos - m.xy * sin), (m.yx * cos - m.yy * sin) * flip, (m.xy * cos + m.xx * sin) * flip, (m.yy * cos + m.yx * sin) ); var rotationTransform = new ART.Transform().rotate(rotation, 0, 0); var shapeToBox = new ART.Transform().rotate(-rotation, 0, 0).transform(m).moveTo(0,0);

Scale box after reversing rotation

  1. 264
  2. 265
width *= Math.abs(shapeToBox.xx); height *= Math.abs(shapeToBox.yy);

Place box

  1. 268
var left = m.x, top = m.y;

Compensate for offset by center origin rotation

  1. 271
  2. 272
  3. 273
  4. 274
var vx = -width / 2, vy = -height / 2; var point = rotationTransform.point(vx, vy); left -= point.x - vx; top -= point.y - vy;

Adjust box position based on offset

  1. 277
  2. 278
  3. 279
  4. 280
  5. 281
  6. 282
var rsm = new ART.Transform(m).moveTo(0,0); point = rsm.point(originX, originY); left += point.x; top += point.y; if (flip < 0) left = -left - width;

Place transformation origin

  1. 285
  2. 286
  3. 287
  4. 288
  5. 289
  6. 290
  7. 291
  8. 292
  9. 293
  10. 294
  11. 295
  12. 296
var point0 = rsm.point(-originX, -originY); var point1 = rotationTransform.point(width, height); var point2 = rotationTransform.point(width, 0); var point3 = rotationTransform.point(0, height); var minX = Math.min(0, point1.x, point2.x, point3.x), maxX = Math.max(0, point1.x, point2.x, point3.x), minY = Math.min(0, point1.y, point2.y, point3.y), maxY = Math.max(0, point1.y, point2.y, point3.y); var transformOriginX = (point0.x - point1.x / 2) / (maxX - minX) * flip, transformOriginY = (point0.y - point1.y / 2) / (maxY - minY);

Adjust the origin

  1. 299
  2. 300
  3. 301
point = shapeToBox.point(originX, originY); originX = point.x; originY = point.y;

Scale stroke

  1. 304
  2. 305
var strokeWidth = this._strokeWidth; if (strokeWidth){

Scale is the hypothenus between the two vectors TODO: Use area calculation instead

  1. 308
  2. 309
  3. 310
var vx = m.xx + m.xy, vy = m.yy + m.yx; strokeWidth *= Math.sqrt(vx * vx + vy * vy) / Math.sqrt(2); }

convert to multiplied precision space

  1. 313
  2. 314
  3. 315
  4. 316
  5. 317
  6. 318
originX *= precision; originY *= precision; left *= precision; top *= precision; width *= precision; height *= precision;

Set box

  1. 321
  2. 322
  3. 323
  4. 324
  5. 325
  6. 326
  7. 327
  8. 328
  9. 329
var element = this.element; element.coordorigin = originX + ',' + originY; element.coordsize = width + ',' + height; element.style.left = left + 'px'; element.style.top = top + 'px'; element.style.width = width; element.style.height = height; element.style.rotation = rotation.toFixed(8); element.style.flip = flip < 0 ? 'x' : '';

Set transform

  1. 332
  2. 333
  3. 334
var skew = this.skewElement; skew.matrix = [transform.xx.toFixed(4), transform.xy.toFixed(4), transform.yx.toFixed(4), transform.yy.toFixed(4), 0, 0]; skew.origin = transformOriginX + ',' + transformOriginY;

Set stroke

  1. 337
  2. 338
this.strokeElement.weight = strokeWidth + 'px'; },

styles

  1. 342
  2. 343
_createGradient: function(style, stops){ var fill = this.fillElement;

Temporarily eject the fill from the DOM

  1. 346
  2. 347
  3. 348
  4. 349
  5. 350
  6. 351
  7. 352
  8. 353
  9. 354
  10. 355
  11. 356
  12. 357
  13. 358
  14. 359
this.element.removeChild(fill); fill.type = style; fill.method = 'none'; fill.rotate = true; var colors = [], color1, color2; var addColor = function(offset, color){ color = Color.detach(color); if (color1 == null) color1 = color2 = color; else color2 = color; colors.push(offset + ' ' + color[0]); };

Enumerate stops, assumes offsets are enumerated in order

  1. 362
  2. 363
  3. 364
  4. 365
  5. 366
if ('length' in stops) for (var i = 0, l = stops.length - 1; i <= l; i++) addColor(i / l, stops[i]); else for (var offset in stops) addColor(offset, stops[offset]); fill.color = color1[0]; fill.color2 = color2[0];

if (fill.colors) fill.colors.value = colors; else

  1. 369
fill.colors = colors;

Opacity order gets flipped when color stops are specified

  1. 372
  2. 373
  3. 374
  4. 375
  5. 376
  6. 377
  7. 378
  8. 379
  9. 380
  10. 381
  11. 382
  12. 383
  13. 384
  14. 385
  15. 386
  16. 387
  17. 388
  18. 389
  19. 390
  20. 391
  21. 392
  22. 393
  23. 394
  24. 395
  25. 396
  26. 397
  27. 398
  28. 399
  29. 400
  30. 401
  31. 402
  32. 403
  33. 404
  34. 405
  35. 406
  36. 407
  37. 408
  38. 409
  39. 410
  40. 411
  41. 412
  42. 413
  43. 414
  44. 415
  45. 416
  46. 417
  47. 418
  48. 419
  49. 420
  50. 421
  51. 422
  52. 423
  53. 424
  54. 425
  55. 426
  56. 427
  57. 428
  58. 429
  59. 430
  60. 431
  61. 432
  62. 433
  63. 434
  64. 435
  65. 436
  66. 437
  67. 438
  68. 439
  69. 440
  70. 441
  71. 442
  72. 443
  73. 444
  74. 445
  75. 446
  76. 447
  77. 448
  78. 449
  79. 450
  80. 451
  81. 452
  82. 453
  83. 454
  84. 455
  85. 456
  86. 457
  87. 458
  88. 459
  89. 460
  90. 461
  91. 462
  92. 463
  93. 464
  94. 465
  95. 466
  96. 467
  97. 468
  98. 469
  99. 470
  100. 471
  101. 472
  102. 473
  103. 474
  104. 475
  105. 476
  106. 477
  107. 478
  108. 479
  109. 480
  110. 481
  111. 482
  112. 483
  113. 484
  114. 485
  115. 486
  116. 487
  117. 488
  118. 489
  119. 490
  120. 491
fill.opacity = color2[1]; fill['ao:opacity2'] = color1[1]; fill.on = true; this.element.appendChild(fill); return fill; }, _setColor: function(type, color){ var element = this[type + 'Element']; if (color == null){ element.on = false; } else { color = Color.detach(color); element.color = color[0]; element.opacity = color[1]; element.on = true; } }, fill: function(color){ if (arguments.length > 1){ this.fillLinear(arguments); } else { this._boxCoords = defaultBox; var fill = this.fillElement; fill.type = 'solid'; fill.color2 = ''; fill['ao:opacity2'] = ''; if (fill.colors) fill.colors.value = ''; this._setColor('fill', color); } return this; }, fillRadial: function(stops, focusX, focusY, radiusX, radiusY, centerX, centerY){ var fill = this._createGradient('gradientradial', stops); if (focusX == null) focusX = this.left + this.width * 0.5; if (focusY == null) focusY = this.top + this.height * 0.5; if (radiusY == null) radiusY = radiusX || (this.height * 0.5); if (radiusX == null) radiusX = this.width * 0.5; if (centerX == null) centerX = focusX; if (centerY == null) centerY = focusY; centerX += centerX - focusX; centerY += centerY - focusY; var box = this._boxCoords = { left: centerX - radiusX * 2, top: centerY - radiusY * 2, width: radiusX * 4, height: radiusY * 4 }; focusX -= box.left; focusY -= box.top; focusX /= box.width; focusY /= box.height; fill.focussize = '0 0'; fill.focusposition = focusX + ',' + focusY; fill.focus = '50%'; this._transform(); return this; }, fillLinear: function(stops, x1, y1, x2, y2){ var fill = this._createGradient('gradient', stops); fill.focus = '100%'; if (arguments.length == 5){ var w = Math.abs(x2 - x1), h = Math.abs(y2 - y1); this._boxCoords = { left: Math.min(x1, x2), top: Math.min(y1, y2), width: w < 1 ? h : w, height: h < 1 ? w : h }; fill.angle = (360 + Math.atan2((x2 - x1) / h, (y2 - y1) / w) * 180 / Math.PI) % 360; } else { this._boxCoords = null; fill.angle = (x1 == null) ? 0 : (90 + x1) % 360; } this._transform(); return this; }, fillImage: function(url, width, height, left, top, color1, color2){ var fill = this.fillElement; if (color1 != null){ color1 = Color.detach(color1); if (color2 != null) color2 = Color.detach(color2); fill.type = 'pattern'; fill.color = color1[0]; fill.color2 = color2 == null ? color1[0] : color2[0]; fill.opacity = color2 == null ? 0 : color2[1]; fill['ao:opacity2'] = color1[1]; } else { fill.type = 'tile'; fill.color = ''; fill.color2 = ''; fill.opacity = 1; fill['ao:opacity2'] = 1; } if (fill.colors) fill.colors.value = ''; fill.rotate = true; fill.src = url; fill.size = '1,1'; fill.position = '0,0'; fill.origin = '0,0'; fill.aspect = 'ignore'; // ignore, atleast, atmost fill.on = true; if (!left) left = 0; if (!top) top = 0; this._boxCoords = width ? { left: left + 0.5, top: top + 0.5, width: width, height: height } : null; this._transform(); return this; },

stroke

  1. 495
  2. 496
  3. 497
  4. 498
  5. 499
  6. 500
  7. 501
  8. 502
  9. 503
  10. 504
  11. 505
  12. 506
stroke: function(color, width, cap, join){ var stroke = this.strokeElement; this._strokeWidth = (width != null) ? width : 1; stroke.weight = (width != null) ? width + 'px' : 1; stroke.endcap = (cap != null) ? ((cap == 'butt') ? 'flat' : cap) : 'round'; stroke.joinstyle = (join != null) ? join : 'round'; this._setColor('stroke', color); return this; } });

VML Shape Class

  1. 510
  2. 511
  3. 512
  4. 513
  5. 514
  6. 515
  7. 516
  8. 517
  9. 518
  10. 519
  11. 520
  12. 521
  13. 522
  14. 523
  15. 524
  16. 525
ART.VML.Shape = new Class({ Extends: ART.VML.Base, initialize: function(path, width, height){ this.parent('shape'); var p = this.pathElement = document.createElement('av:path'); p.gradientshapeok = true; this.element.appendChild(p); this.width = width; this.height = height; if (path != null) this.draw(path); },

SVG to VML

  1. 529
  2. 530
  3. 531
  4. 532
  5. 533
  6. 534
  7. 535
  8. 536
  9. 537
  10. 538
  11. 539
  12. 540
  13. 541
  14. 542
draw: function(path, width, height){ if (!(path instanceof ART.Path)) path = new ART.Path(path); this._vml = path.toVML(precision); this._size = path.measure(); if (width != null) this.width = width; if (height != null) this.height = height; if (!this._boxCoords) this._transform(); this._redraw(this._prefix, this._suffix); return this; },

radial gradient workaround

  1. 546
  2. 547
  3. 548
  4. 549
  5. 550
  6. 551
  7. 552
  8. 553
_redraw: function(prefix, suffix){ var vml = this._vml || ''; this._prefix = prefix; this._suffix = suffix if (prefix){ vml = [ prefix, vml, suffix,

Don’t stroke the path with the extra ellipse, redraw the stroked path separately

  1. 555
  2. 556
  3. 557
  4. 558
  5. 559
  6. 560
  7. 561
  8. 562
  9. 563
  10. 564
  11. 565
  12. 566
  13. 567
  14. 568
  15. 569
  16. 570
  17. 571
  18. 572
  19. 573
  20. 574
  21. 575
  22. 576
  23. 577
  24. 578
  25. 579
  26. 580
  27. 581
  28. 582
  29. 583
  30. 584
  31. 585
  32. 586
  33. 587
  34. 588
  35. 589
  36. 590
  37. 591
  38. 592
  39. 593
  40. 594
  41. 595
  42. 596
  43. 597
'ns e', vml, 'nf' ].join(' '); } this.element.path = vml + 'e'; }, fill: function(){ this._redraw(); return this.parent.apply(this, arguments); }, fillLinear: function(){ this._redraw(); return this.parent.apply(this, arguments); }, fillImage: function(){ this._redraw(); return this.parent.apply(this, arguments); }, fillRadial: function(stops, focusX, focusY, radiusX, radiusY, centerX, centerY){ var fill = this._createGradient('gradientradial', stops); if (focusX == null) focusX = (this.left || 0) + (this.width || 0) * 0.5; if (focusY == null) focusY = (this.top || 0) + (this.height || 0) * 0.5; if (radiusY == null) radiusY = radiusX || (this.height * 0.5) || 0; if (radiusX == null) radiusX = (this.width || 0) * 0.5; if (centerX == null) centerX = focusX; if (centerY == null) centerY = focusY; centerX += centerX - focusX; centerY += centerY - focusY; var cx = Math.round(centerX * precision), cy = Math.round(centerY * precision), rx = Math.round(radiusX * 2 * precision), ry = Math.round(radiusY * 2 * precision), arc = ['wa', cx - rx, cy - ry, cx + rx, cy + ry].join(' '); this._redraw(

Resolve rendering bug

  1. 599
['m', cx, cy - ry, 'l', cx, cy - ry].join(' '),

Draw an ellipse around the path to force an elliptical gradient on any shape

  1. 601
  2. 602
  3. 603
  4. 604
  5. 605
  6. 606
  7. 607
  8. 608
  9. 609
  10. 610
  11. 611
  12. 612
  13. 613
  14. 614
  15. 615
  16. 616
  17. 617
  18. 618
  19. 619
  20. 620
  21. 621
  22. 622
  23. 623
  24. 624
  25. 625
  26. 626
  27. 627
  28. 628
  29. 629
  30. 630
  31. 631
  32. 632
  33. 633
  34. 634
  35. 635
  36. 636
  37. 637
  38. 638
  39. 639
  40. 640
  41. 641
  42. 642
  43. 643
  44. 644
  45. 645
  46. 646
  47. 647
  48. 648
  49. 649
  50. 650
  51. 651
  52. 652
  53. 653
  54. 654
  55. 655
[ 'm', cx, cy - ry, arc, cx, cy - ry, cx, cy + ry, arc, cx, cy + ry, cx, cy - ry, arc, cx, cy - ry, cx, cy + ry, arc, cx, cy + ry, cx, cy - ry ].join(' ') ); this._boxCoords = { left: focusX - 2, top: focusY - 2, width: 4, height: 4 }; fill.focusposition = '0.5,0.5'; fill.focussize = '0 0'; fill.focus = '50%'; this._transform(); return this; } }); var fontAnchors = { start: 'left', middle: 'center', end: 'right' }; ART.VML.Text = new Class({ Extends: ART.VML.Base, initialize: function(text, font, alignment, path){ this.parent('shape'); var p = this.pathElement = document.createElement('av:path'); p.textpathok = true; this.element.appendChild(p); p = this.textPathElement = document.createElement("av:textpath"); p.on = true; p.style['v-text-align'] = 'left'; this.element.appendChild(p); this.draw.apply(this, arguments); }, draw: function(text, font, alignment, path){ var element = this.element, textPath = this.textPathElement, style = textPath.style; textPath.string = text; if (font){ if (typeof font == 'string'){ style.font = font; } else { for (var key in font){ var ckey = key.camelCase ? key.camelCase() : key; if (ckey == 'fontFamily') style[ckey] = "'" + font[key] + "'";

NOT UNIVERSALLY SUPPORTED OPTIONS else if (ckey == ‘kerning’) style[‘v-text-kern’] = !!font[key]; else if (ckey == ‘rotateGlyphs’) style[‘v-rotate-letters’] = !!font[key]; else if (ckey == ‘letterSpacing’) style[‘v-text-spacing’] = Number(font[key]) + ‘’;

  1. 660
  2. 661
  3. 662
  4. 663
  5. 664
  6. 665
  7. 666
  8. 667
  9. 668
  10. 669
  11. 670
  12. 671
  13. 672
  14. 673
  15. 674
  16. 675
else style[ckey] = font[key]; } } } if (alignment) style['v-text-align'] = fontAnchors[alignment] || alignment; if (path){ this.currentPath = path = new ART.Path(path); this.element.path = path.toVML(precision); } else if (!this.currentPath){ var i = -1, offsetRows = '\n'; while ((i = text.indexOf('\n', i + 1)) > -1) offsetRows += '\n'; textPath.string = offsetRows + textPath.string; this.element.path = 'm0,0l1,0'; }

Measuring the bounding box is currently necessary for gradients etc.

Clone element because the element is dead once it has been in the DOM

  1. 680
  2. 681
element = element.cloneNode(true); style = element.style;

Reset coordinates while measuring

  1. 684
  2. 685
  3. 686
  4. 687
  5. 688
  6. 689
  7. 690
  8. 691
element.coordorigin = '0,0'; element.coordsize = '10000,10000'; style.left = '0px'; style.top = '0px'; style.width = '10000px'; style.height = '10000px'; style.rotation = 0; element.removeChild(element.firstChild); // Remove skew

Inject the clone into the document

  1. 695
  2. 696
  3. 697
  4. 698
  5. 699
  6. 700
  7. 701
  8. 702
  9. 703
  10. 704
  11. 705
  12. 706
  13. 707
  14. 708
  15. 709
  16. 710
  17. 711
  18. 712
  19. 713
  20. 714
  21. 715
  22. 716
  23. 717
  24. 718
  25. 719
  26. 720
  27. 721
var canvas = new ART.VML(1, 1), group = new ART.VML.Group(), // Wrapping it in a group seems to alleviate some client rect weirdness body = element.ownerDocument.body; canvas.inject(body); group.element.appendChild(element); group.inject(canvas); var ebb = element.getBoundingClientRect(), cbb = canvas.toElement().getBoundingClientRect(); canvas.eject(); this.left = ebb.left - cbb.left; this.top = ebb.top - cbb.top; this.width = ebb.right - ebb.left; this.height = ebb.bottom - ebb.top; this.right = ebb.right - cbb.left; this.bottom = ebb.bottom - cbb.top; this._transform(); this._size = { left: this.left, top: this.top, width: this.width, height: this.height}; return this; } });

VML Path Extensions

  1. 725
  2. 726
  3. 727
  4. 728
  5. 729
  6. 730
  7. 731
  8. 732
  9. 733
  10. 734
  11. 735
  12. 736
  13. 737
  14. 738
  15. 739
  16. 740
  17. 741
  18. 742
  19. 743
  20. 744
  21. 745
  22. 746
  23. 747
  24. 748
  25. 749
  26. 750
  27. 751
  28. 752
  29. 753
  30. 754
  31. 755
  32. 756
  33. 757
  34. 758
  35. 759
  36. 760
  37. 761
  38. 762
  39. 763
  40. 764
  41. 765
  42. 766
  43. 767
  44. 768
  45. 769
  46. 770
  47. 771
  48. 772
  49. 773
var path, p, round = Math.round; function moveTo(sx, sy, x, y){ path.push('m', round(x * p), round(y * p)); }; function lineTo(sx, sy, x, y){ path.push('l', round(x * p), round(y * p)); }; function curveTo(sx, sy, p1x, p1y, p2x, p2y, x, y){ path.push('c', round(p1x * p), round(p1y * p), round(p2x * p), round(p2y * p), round(x * p), round(y * p) ); }; function arcTo(sx, sy, ex, ey, cx, cy, r, sa, ea, ccw){ cx *= p; cy *= p; r *= p; path.push(ccw ? 'at' : 'wa', round(cx - r), round(cy - r), round(cx + r), round(cy + r), round(sx * p), round(sy * p), round(ex * p), round(ey * p) ); }; function close(){ path.push('x'); }; ART.Path.implement({ toVML: function(precision){ if (this.cache.vml == null){ path = []; p = precision; this.visit(lineTo, curveTo, arcTo, moveTo, close); this.cache.vml = path.join(' '); } return this.cache.vml; } }); })();