annotate js/bt/other_libs/excanvas_0002/excanvas.js @ 47:cbfe386cb51b

Add a function to refresh opened libraries. The source URL of each libraries is now tracked, which allows for auto refreshing the libraries based on various events. The obvious use case is to refresh the library when an atom has been added to Scald, for example via a Popups dialog.
author Franck Deroche <defr@ows.fr>
date Mon, 15 Feb 2010 14:08:04 +0000
parents 0d557e6e73f7
children
rev   line source
eads@18 1 // Copyright 2006 Google Inc.
eads@18 2 //
eads@18 3 // Licensed under the Apache License, Version 2.0 (the "License");
eads@18 4 // you may not use this file except in compliance with the License.
eads@18 5 // You may obtain a copy of the License at
eads@18 6 //
eads@18 7 // http://www.apache.org/licenses/LICENSE-2.0
eads@18 8 //
eads@18 9 // Unless required by applicable law or agreed to in writing, software
eads@18 10 // distributed under the License is distributed on an "AS IS" BASIS,
eads@18 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
eads@18 12 // See the License for the specific language governing permissions and
eads@18 13 // limitations under the License.
eads@18 14
eads@18 15
eads@18 16 // Known Issues:
eads@18 17 //
eads@18 18 // * Patterns are not implemented.
eads@18 19 // * Radial gradient are not implemented. The VML version of these look very
eads@18 20 // different from the canvas one.
eads@18 21 // * Clipping paths are not implemented.
eads@18 22 // * Coordsize. The width and height attribute have higher priority than the
eads@18 23 // width and height style values which isn't correct.
eads@18 24 // * Painting mode isn't implemented.
eads@18 25 // * Canvas width/height should is using content-box by default. IE in
eads@18 26 // Quirks mode will draw the canvas using border-box. Either change your
eads@18 27 // doctype to HTML5
eads@18 28 // (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
eads@18 29 // or use Box Sizing Behavior from WebFX
eads@18 30 // (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
eads@18 31 // * Optimize. There is always room for speed improvements.
eads@18 32
eads@18 33 // only add this code if we do not already have a canvas implementation
eads@18 34 if (!window.CanvasRenderingContext2D) {
eads@18 35
eads@18 36 (function () {
eads@18 37
eads@18 38 // alias some functions to make (compiled) code shorter
eads@18 39 var m = Math;
eads@18 40 var mr = m.round;
eads@18 41 var ms = m.sin;
eads@18 42 var mc = m.cos;
eads@18 43
eads@18 44 // this is used for sub pixel precision
eads@18 45 var Z = 10;
eads@18 46 var Z2 = Z / 2;
eads@18 47
eads@18 48 var G_vmlCanvasManager_ = {
eads@18 49 init: function (opt_doc) {
eads@18 50 var doc = opt_doc || document;
eads@18 51 if (/MSIE/.test(navigator.userAgent) && !window.opera) {
eads@18 52 var self = this;
eads@18 53 doc.attachEvent("onreadystatechange", function () {
eads@18 54 self.init_(doc);
eads@18 55 });
eads@18 56 }
eads@18 57 },
eads@18 58
eads@18 59 init_: function (doc) {
eads@18 60 if (doc.readyState == "complete") {
eads@18 61 // create xmlns
eads@18 62 if (!doc.namespaces["g_vml_"]) {
eads@18 63 doc.namespaces.add("g_vml_", "urn:schemas-microsoft-com:vml");
eads@18 64 }
eads@18 65
eads@18 66 // setup default css
eads@18 67 var ss = doc.createStyleSheet();
eads@18 68 ss.cssText = "canvas{display:inline-block;overflow:hidden;" +
eads@18 69 // default size is 300x150 in Gecko and Opera
eads@18 70 "text-align:left;width:300px;height:150px}" +
eads@18 71 "g_vml_\\:*{behavior:url(#default#VML)}";
eads@18 72
eads@18 73 // find all canvas elements
eads@18 74 var els = doc.getElementsByTagName("canvas");
eads@18 75 for (var i = 0; i < els.length; i++) {
eads@18 76 if (!els[i].getContext) {
eads@18 77 this.initElement(els[i]);
eads@18 78 }
eads@18 79 }
eads@18 80 }
eads@18 81 },
eads@18 82
eads@18 83 fixElement_: function (el) {
eads@18 84 // in IE before version 5.5 we would need to add HTML: to the tag name
eads@18 85 // but we do not care about IE before version 6
eads@18 86 var outerHTML = el.outerHTML;
eads@18 87
eads@18 88 var newEl = el.ownerDocument.createElement(outerHTML);
eads@18 89 // if the tag is still open IE has created the children as siblings and
eads@18 90 // it has also created a tag with the name "/FOO"
eads@18 91 if (outerHTML.slice(-2) != "/>") {
eads@18 92 var tagName = "/" + el.tagName;
eads@18 93 var ns;
eads@18 94 // remove content
eads@18 95 while ((ns = el.nextSibling) && ns.tagName != tagName) {
eads@18 96 ns.removeNode();
eads@18 97 }
eads@18 98 // remove the incorrect closing tag
eads@18 99 if (ns) {
eads@18 100 ns.removeNode();
eads@18 101 }
eads@18 102 }
eads@18 103 el.parentNode.replaceChild(newEl, el);
eads@18 104 return newEl;
eads@18 105 },
eads@18 106
eads@18 107 /**
eads@18 108 * Public initializes a canvas element so that it can be used as canvas
eads@18 109 * element from now on. This is called automatically before the page is
eads@18 110 * loaded but if you are creating elements using createElement you need to
eads@18 111 * make sure this is called on the element.
eads@18 112 * @param {HTMLElement} el The canvas element to initialize.
eads@18 113 * @return {HTMLElement} the element that was created.
eads@18 114 */
eads@18 115 initElement: function (el) {
eads@18 116 el = this.fixElement_(el);
eads@18 117 el.getContext = function () {
eads@18 118 if (this.context_) {
eads@18 119 return this.context_;
eads@18 120 }
eads@18 121 return this.context_ = new CanvasRenderingContext2D_(this);
eads@18 122 };
eads@18 123
eads@18 124 // do not use inline function because that will leak memory
eads@18 125 el.attachEvent('onpropertychange', onPropertyChange);
eads@18 126 el.attachEvent('onresize', onResize);
eads@18 127
eads@18 128 var attrs = el.attributes;
eads@18 129 if (attrs.width && attrs.width.specified) {
eads@18 130 // TODO: use runtimeStyle and coordsize
eads@18 131 // el.getContext().setWidth_(attrs.width.nodeValue);
eads@18 132 el.style.width = attrs.width.nodeValue + "px";
eads@18 133 } else {
eads@18 134 el.width = el.clientWidth;
eads@18 135 }
eads@18 136 if (attrs.height && attrs.height.specified) {
eads@18 137 // TODO: use runtimeStyle and coordsize
eads@18 138 // el.getContext().setHeight_(attrs.height.nodeValue);
eads@18 139 el.style.height = attrs.height.nodeValue + "px";
eads@18 140 } else {
eads@18 141 el.height = el.clientHeight;
eads@18 142 }
eads@18 143 //el.getContext().setCoordsize_()
eads@18 144 return el;
eads@18 145 }
eads@18 146 };
eads@18 147
eads@18 148 function onPropertyChange(e) {
eads@18 149 var el = e.srcElement;
eads@18 150
eads@18 151 switch (e.propertyName) {
eads@18 152 case 'width':
eads@18 153 el.style.width = el.attributes.width.nodeValue + "px";
eads@18 154 el.getContext().clearRect();
eads@18 155 break;
eads@18 156 case 'height':
eads@18 157 el.style.height = el.attributes.height.nodeValue + "px";
eads@18 158 el.getContext().clearRect();
eads@18 159 break;
eads@18 160 }
eads@18 161 }
eads@18 162
eads@18 163 function onResize(e) {
eads@18 164 var el = e.srcElement;
eads@18 165 if (el.firstChild) {
eads@18 166 el.firstChild.style.width = el.clientWidth + 'px';
eads@18 167 el.firstChild.style.height = el.clientHeight + 'px';
eads@18 168 }
eads@18 169 }
eads@18 170
eads@18 171 G_vmlCanvasManager_.init();
eads@18 172
eads@18 173 // precompute "00" to "FF"
eads@18 174 var dec2hex = [];
eads@18 175 for (var i = 0; i < 16; i++) {
eads@18 176 for (var j = 0; j < 16; j++) {
eads@18 177 dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
eads@18 178 }
eads@18 179 }
eads@18 180
eads@18 181 function createMatrixIdentity() {
eads@18 182 return [
eads@18 183 [1, 0, 0],
eads@18 184 [0, 1, 0],
eads@18 185 [0, 0, 1]
eads@18 186 ];
eads@18 187 }
eads@18 188
eads@18 189 function matrixMultiply(m1, m2) {
eads@18 190 var result = createMatrixIdentity();
eads@18 191
eads@18 192 for (var x = 0; x < 3; x++) {
eads@18 193 for (var y = 0; y < 3; y++) {
eads@18 194 var sum = 0;
eads@18 195
eads@18 196 for (var z = 0; z < 3; z++) {
eads@18 197 sum += m1[x][z] * m2[z][y];
eads@18 198 }
eads@18 199
eads@18 200 result[x][y] = sum;
eads@18 201 }
eads@18 202 }
eads@18 203 return result;
eads@18 204 }
eads@18 205
eads@18 206 function copyState(o1, o2) {
eads@18 207 o2.fillStyle = o1.fillStyle;
eads@18 208 o2.lineCap = o1.lineCap;
eads@18 209 o2.lineJoin = o1.lineJoin;
eads@18 210 o2.lineWidth = o1.lineWidth;
eads@18 211 o2.miterLimit = o1.miterLimit;
eads@18 212 o2.shadowBlur = o1.shadowBlur;
eads@18 213 o2.shadowColor = o1.shadowColor;
eads@18 214 o2.shadowOffsetX = o1.shadowOffsetX;
eads@18 215 o2.shadowOffsetY = o1.shadowOffsetY;
eads@18 216 o2.strokeStyle = o1.strokeStyle;
eads@18 217 o2.arcScaleX_ = o1.arcScaleX_;
eads@18 218 o2.arcScaleY_ = o1.arcScaleY_;
eads@18 219 }
eads@18 220
eads@18 221 function processStyle(styleString) {
eads@18 222 var str, alpha = 1;
eads@18 223
eads@18 224 styleString = String(styleString);
eads@18 225 if (styleString.substring(0, 3) == "rgb") {
eads@18 226 var start = styleString.indexOf("(", 3);
eads@18 227 var end = styleString.indexOf(")", start + 1);
eads@18 228 var guts = styleString.substring(start + 1, end).split(",");
eads@18 229
eads@18 230 str = "#";
eads@18 231 for (var i = 0; i < 3; i++) {
eads@18 232 str += dec2hex[Number(guts[i])];
eads@18 233 }
eads@18 234
eads@18 235 if ((guts.length == 4) && (styleString.substr(3, 1) == "a")) {
eads@18 236 alpha = guts[3];
eads@18 237 }
eads@18 238 } else {
eads@18 239 str = styleString;
eads@18 240 }
eads@18 241
eads@18 242 return [str, alpha];
eads@18 243 }
eads@18 244
eads@18 245 function processLineCap(lineCap) {
eads@18 246 switch (lineCap) {
eads@18 247 case "butt":
eads@18 248 return "flat";
eads@18 249 case "round":
eads@18 250 return "round";
eads@18 251 case "square":
eads@18 252 default:
eads@18 253 return "square";
eads@18 254 }
eads@18 255 }
eads@18 256
eads@18 257 /**
eads@18 258 * This class implements CanvasRenderingContext2D interface as described by
eads@18 259 * the WHATWG.
eads@18 260 * @param {HTMLElement} surfaceElement The element that the 2D context should
eads@18 261 * be associated with
eads@18 262 */
eads@18 263 function CanvasRenderingContext2D_(surfaceElement) {
eads@18 264 this.m_ = createMatrixIdentity();
eads@18 265
eads@18 266 this.mStack_ = [];
eads@18 267 this.aStack_ = [];
eads@18 268 this.currentPath_ = [];
eads@18 269
eads@18 270 // Canvas context properties
eads@18 271 this.strokeStyle = "#000";
eads@18 272 this.fillStyle = "#000";
eads@18 273
eads@18 274 this.lineWidth = 1;
eads@18 275 this.lineJoin = "miter";
eads@18 276 this.lineCap = "butt";
eads@18 277 this.miterLimit = Z * 1;
eads@18 278 this.globalAlpha = 1;
eads@18 279 this.canvas = surfaceElement;
eads@18 280
eads@18 281 var el = surfaceElement.ownerDocument.createElement('div');
eads@18 282 el.style.width = surfaceElement.clientWidth + 'px';
eads@18 283 el.style.height = surfaceElement.clientHeight + 'px';
eads@18 284 el.style.overflow = 'hidden';
eads@18 285 el.style.position = 'absolute';
eads@18 286 surfaceElement.appendChild(el);
eads@18 287
eads@18 288 this.element_ = el;
eads@18 289 this.arcScaleX_ = 1;
eads@18 290 this.arcScaleY_ = 1;
eads@18 291 };
eads@18 292
eads@18 293 var contextPrototype = CanvasRenderingContext2D_.prototype;
eads@18 294 contextPrototype.clearRect = function() {
eads@18 295 this.element_.innerHTML = "";
eads@18 296 this.currentPath_ = [];
eads@18 297 };
eads@18 298
eads@18 299 contextPrototype.beginPath = function() {
eads@18 300 // TODO: Branch current matrix so that save/restore has no effect
eads@18 301 // as per safari docs.
eads@18 302
eads@18 303 this.currentPath_ = [];
eads@18 304 };
eads@18 305
eads@18 306 contextPrototype.moveTo = function(aX, aY) {
eads@18 307 this.currentPath_.push({type: "moveTo", x: aX, y: aY});
eads@18 308 this.currentX_ = aX;
eads@18 309 this.currentY_ = aY;
eads@18 310 };
eads@18 311
eads@18 312 contextPrototype.lineTo = function(aX, aY) {
eads@18 313 this.currentPath_.push({type: "lineTo", x: aX, y: aY});
eads@18 314 this.currentX_ = aX;
eads@18 315 this.currentY_ = aY;
eads@18 316 };
eads@18 317
eads@18 318 contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
eads@18 319 aCP2x, aCP2y,
eads@18 320 aX, aY) {
eads@18 321 this.currentPath_.push({type: "bezierCurveTo",
eads@18 322 cp1x: aCP1x,
eads@18 323 cp1y: aCP1y,
eads@18 324 cp2x: aCP2x,
eads@18 325 cp2y: aCP2y,
eads@18 326 x: aX,
eads@18 327 y: aY});
eads@18 328 this.currentX_ = aX;
eads@18 329 this.currentY_ = aY;
eads@18 330 };
eads@18 331
eads@18 332 contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
eads@18 333 // the following is lifted almost directly from
eads@18 334 // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
eads@18 335 var cp1x = this.currentX_ + 2.0 / 3.0 * (aCPx - this.currentX_);
eads@18 336 var cp1y = this.currentY_ + 2.0 / 3.0 * (aCPy - this.currentY_);
eads@18 337 var cp2x = cp1x + (aX - this.currentX_) / 3.0;
eads@18 338 var cp2y = cp1y + (aY - this.currentY_) / 3.0;
eads@18 339 this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, aX, aY);
eads@18 340 };
eads@18 341
eads@18 342 contextPrototype.arc = function(aX, aY, aRadius,
eads@18 343 aStartAngle, aEndAngle, aClockwise) {
eads@18 344 aRadius *= Z;
eads@18 345 var arcType = aClockwise ? "at" : "wa";
eads@18 346
eads@18 347 var xStart = aX + (mc(aStartAngle) * aRadius) - Z2;
eads@18 348 var yStart = aY + (ms(aStartAngle) * aRadius) - Z2;
eads@18 349
eads@18 350 var xEnd = aX + (mc(aEndAngle) * aRadius) - Z2;
eads@18 351 var yEnd = aY + (ms(aEndAngle) * aRadius) - Z2;
eads@18 352
eads@18 353 // IE won't render arches drawn counter clockwise if xStart == xEnd.
eads@18 354 if (xStart == xEnd && !aClockwise) {
eads@18 355 xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
eads@18 356 // that can be represented in binary
eads@18 357 }
eads@18 358
eads@18 359 this.currentPath_.push({type: arcType,
eads@18 360 x: aX,
eads@18 361 y: aY,
eads@18 362 radius: aRadius,
eads@18 363 xStart: xStart,
eads@18 364 yStart: yStart,
eads@18 365 xEnd: xEnd,
eads@18 366 yEnd: yEnd});
eads@18 367
eads@18 368 };
eads@18 369
eads@18 370 contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
eads@18 371 this.moveTo(aX, aY);
eads@18 372 this.lineTo(aX + aWidth, aY);
eads@18 373 this.lineTo(aX + aWidth, aY + aHeight);
eads@18 374 this.lineTo(aX, aY + aHeight);
eads@18 375 this.closePath();
eads@18 376 };
eads@18 377
eads@18 378 contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
eads@18 379 // Will destroy any existing path (same as FF behaviour)
eads@18 380 this.beginPath();
eads@18 381 this.moveTo(aX, aY);
eads@18 382 this.lineTo(aX + aWidth, aY);
eads@18 383 this.lineTo(aX + aWidth, aY + aHeight);
eads@18 384 this.lineTo(aX, aY + aHeight);
eads@18 385 this.closePath();
eads@18 386 this.stroke();
eads@18 387 };
eads@18 388
eads@18 389 contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
eads@18 390 // Will destroy any existing path (same as FF behaviour)
eads@18 391 this.beginPath();
eads@18 392 this.moveTo(aX, aY);
eads@18 393 this.lineTo(aX + aWidth, aY);
eads@18 394 this.lineTo(aX + aWidth, aY + aHeight);
eads@18 395 this.lineTo(aX, aY + aHeight);
eads@18 396 this.closePath();
eads@18 397 this.fill();
eads@18 398 };
eads@18 399
eads@18 400 contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
eads@18 401 var gradient = new CanvasGradient_("gradient");
eads@18 402 return gradient;
eads@18 403 };
eads@18 404
eads@18 405 contextPrototype.createRadialGradient = function(aX0, aY0,
eads@18 406 aR0, aX1,
eads@18 407 aY1, aR1) {
eads@18 408 var gradient = new CanvasGradient_("gradientradial");
eads@18 409 gradient.radius1_ = aR0;
eads@18 410 gradient.radius2_ = aR1;
eads@18 411 gradient.focus_.x = aX0;
eads@18 412 gradient.focus_.y = aY0;
eads@18 413 return gradient;
eads@18 414 };
eads@18 415
eads@18 416 contextPrototype.drawImage = function (image, var_args) {
eads@18 417 var dx, dy, dw, dh, sx, sy, sw, sh;
eads@18 418
eads@18 419 // to find the original width we overide the width and height
eads@18 420 var oldRuntimeWidth = image.runtimeStyle.width;
eads@18 421 var oldRuntimeHeight = image.runtimeStyle.height;
eads@18 422 image.runtimeStyle.width = 'auto';
eads@18 423 image.runtimeStyle.height = 'auto';
eads@18 424
eads@18 425 // get the original size
eads@18 426 var w = image.width;
eads@18 427 var h = image.height;
eads@18 428
eads@18 429 // and remove overides
eads@18 430 image.runtimeStyle.width = oldRuntimeWidth;
eads@18 431 image.runtimeStyle.height = oldRuntimeHeight;
eads@18 432
eads@18 433 if (arguments.length == 3) {
eads@18 434 dx = arguments[1];
eads@18 435 dy = arguments[2];
eads@18 436 sx = sy = 0;
eads@18 437 sw = dw = w;
eads@18 438 sh = dh = h;
eads@18 439 } else if (arguments.length == 5) {
eads@18 440 dx = arguments[1];
eads@18 441 dy = arguments[2];
eads@18 442 dw = arguments[3];
eads@18 443 dh = arguments[4];
eads@18 444 sx = sy = 0;
eads@18 445 sw = w;
eads@18 446 sh = h;
eads@18 447 } else if (arguments.length == 9) {
eads@18 448 sx = arguments[1];
eads@18 449 sy = arguments[2];
eads@18 450 sw = arguments[3];
eads@18 451 sh = arguments[4];
eads@18 452 dx = arguments[5];
eads@18 453 dy = arguments[6];
eads@18 454 dw = arguments[7];
eads@18 455 dh = arguments[8];
eads@18 456 } else {
eads@18 457 throw "Invalid number of arguments";
eads@18 458 }
eads@18 459
eads@18 460 var d = this.getCoords_(dx, dy);
eads@18 461
eads@18 462 var w2 = sw / 2;
eads@18 463 var h2 = sh / 2;
eads@18 464
eads@18 465 var vmlStr = [];
eads@18 466
eads@18 467 var W = 10;
eads@18 468 var H = 10;
eads@18 469
eads@18 470 // For some reason that I've now forgotten, using divs didn't work
eads@18 471 vmlStr.push(' <g_vml_:group',
eads@18 472 ' coordsize="', Z * W, ',', Z * H, '"',
eads@18 473 ' coordorigin="0,0"' ,
eads@18 474 ' style="width:', W, ';height:', H, ';position:absolute;');
eads@18 475
eads@18 476 // If filters are necessary (rotation exists), create them
eads@18 477 // filters are bog-slow, so only create them if abbsolutely necessary
eads@18 478 // The following check doesn't account for skews (which don't exist
eads@18 479 // in the canvas spec (yet) anyway.
eads@18 480
eads@18 481 if (this.m_[0][0] != 1 || this.m_[0][1]) {
eads@18 482 var filter = [];
eads@18 483
eads@18 484 // Note the 12/21 reversal
eads@18 485 filter.push("M11='", this.m_[0][0], "',",
eads@18 486 "M12='", this.m_[1][0], "',",
eads@18 487 "M21='", this.m_[0][1], "',",
eads@18 488 "M22='", this.m_[1][1], "',",
eads@18 489 "Dx='", mr(d.x / Z), "',",
eads@18 490 "Dy='", mr(d.y / Z), "'");
eads@18 491
eads@18 492 // Bounding box calculation (need to minimize displayed area so that
eads@18 493 // filters don't waste time on unused pixels.
eads@18 494 var max = d;
eads@18 495 var c2 = this.getCoords_(dx + dw, dy);
eads@18 496 var c3 = this.getCoords_(dx, dy + dh);
eads@18 497 var c4 = this.getCoords_(dx + dw, dy + dh);
eads@18 498
eads@18 499 max.x = Math.max(max.x, c2.x, c3.x, c4.x);
eads@18 500 max.y = Math.max(max.y, c2.y, c3.y, c4.y);
eads@18 501
eads@18 502 vmlStr.push("padding:0 ", mr(max.x / Z), "px ", mr(max.y / Z),
eads@18 503 "px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
eads@18 504 filter.join(""), ", sizingmethod='clip');")
eads@18 505 } else {
eads@18 506 vmlStr.push("top:", mr(d.y / Z), "px;left:", mr(d.x / Z), "px;")
eads@18 507 }
eads@18 508
eads@18 509 vmlStr.push(' ">' ,
eads@18 510 '<g_vml_:image src="', image.src, '"',
eads@18 511 ' style="width:', Z * dw, ';',
eads@18 512 ' height:', Z * dh, ';"',
eads@18 513 ' cropleft="', sx / w, '"',
eads@18 514 ' croptop="', sy / h, '"',
eads@18 515 ' cropright="', (w - sx - sw) / w, '"',
eads@18 516 ' cropbottom="', (h - sy - sh) / h, '"',
eads@18 517 ' />',
eads@18 518 '</g_vml_:group>');
eads@18 519
eads@18 520 this.element_.insertAdjacentHTML("BeforeEnd",
eads@18 521 vmlStr.join(""));
eads@18 522 };
eads@18 523
eads@18 524 contextPrototype.stroke = function(aFill) {
eads@18 525 var lineStr = [];
eads@18 526 var lineOpen = false;
eads@18 527 var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
eads@18 528 var color = a[0];
eads@18 529 var opacity = a[1] * this.globalAlpha;
eads@18 530
eads@18 531 var W = 10;
eads@18 532 var H = 10;
eads@18 533
eads@18 534 lineStr.push('<g_vml_:shape',
eads@18 535 ' fillcolor="', color, '"',
eads@18 536 ' filled="', Boolean(aFill), '"',
eads@18 537 ' style="position:absolute;width:', W, ';height:', H, ';"',
eads@18 538 ' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
eads@18 539 ' stroked="', !aFill, '"',
eads@18 540 ' strokeweight="', this.lineWidth, '"',
eads@18 541 ' strokecolor="', color, '"',
eads@18 542 ' path="');
eads@18 543
eads@18 544 var newSeq = false;
eads@18 545 var min = {x: null, y: null};
eads@18 546 var max = {x: null, y: null};
eads@18 547
eads@18 548 for (var i = 0; i < this.currentPath_.length; i++) {
eads@18 549 var p = this.currentPath_[i];
eads@18 550
eads@18 551 if (p.type == "moveTo") {
eads@18 552 lineStr.push(" m ");
eads@18 553 var c = this.getCoords_(p.x, p.y);
eads@18 554 lineStr.push(mr(c.x), ",", mr(c.y));
eads@18 555 } else if (p.type == "lineTo") {
eads@18 556 lineStr.push(" l ");
eads@18 557 var c = this.getCoords_(p.x, p.y);
eads@18 558 lineStr.push(mr(c.x), ",", mr(c.y));
eads@18 559 } else if (p.type == "close") {
eads@18 560 lineStr.push(" x ");
eads@18 561 } else if (p.type == "bezierCurveTo") {
eads@18 562 lineStr.push(" c ");
eads@18 563 var c = this.getCoords_(p.x, p.y);
eads@18 564 var c1 = this.getCoords_(p.cp1x, p.cp1y);
eads@18 565 var c2 = this.getCoords_(p.cp2x, p.cp2y);
eads@18 566 lineStr.push(mr(c1.x), ",", mr(c1.y), ",",
eads@18 567 mr(c2.x), ",", mr(c2.y), ",",
eads@18 568 mr(c.x), ",", mr(c.y));
eads@18 569 } else if (p.type == "at" || p.type == "wa") {
eads@18 570 lineStr.push(" ", p.type, " ");
eads@18 571 var c = this.getCoords_(p.x, p.y);
eads@18 572 var cStart = this.getCoords_(p.xStart, p.yStart);
eads@18 573 var cEnd = this.getCoords_(p.xEnd, p.yEnd);
eads@18 574
eads@18 575 lineStr.push(mr(c.x - this.arcScaleX_ * p.radius), ",",
eads@18 576 mr(c.y - this.arcScaleY_ * p.radius), " ",
eads@18 577 mr(c.x + this.arcScaleX_ * p.radius), ",",
eads@18 578 mr(c.y + this.arcScaleY_ * p.radius), " ",
eads@18 579 mr(cStart.x), ",", mr(cStart.y), " ",
eads@18 580 mr(cEnd.x), ",", mr(cEnd.y));
eads@18 581 }
eads@18 582
eads@18 583
eads@18 584 // TODO: Following is broken for curves due to
eads@18 585 // move to proper paths.
eads@18 586
eads@18 587 // Figure out dimensions so we can do gradient fills
eads@18 588 // properly
eads@18 589 if(c) {
eads@18 590 if (min.x == null || c.x < min.x) {
eads@18 591 min.x = c.x;
eads@18 592 }
eads@18 593 if (max.x == null || c.x > max.x) {
eads@18 594 max.x = c.x;
eads@18 595 }
eads@18 596 if (min.y == null || c.y < min.y) {
eads@18 597 min.y = c.y;
eads@18 598 }
eads@18 599 if (max.y == null || c.y > max.y) {
eads@18 600 max.y = c.y;
eads@18 601 }
eads@18 602 }
eads@18 603 }
eads@18 604 lineStr.push(' ">');
eads@18 605
eads@18 606 if (typeof this.fillStyle == "object") {
eads@18 607 var focus = {x: "50%", y: "50%"};
eads@18 608 var width = (max.x - min.x);
eads@18 609 var height = (max.y - min.y);
eads@18 610 var dimension = (width > height) ? width : height;
eads@18 611
eads@18 612 focus.x = mr((this.fillStyle.focus_.x / width) * 100 + 50) + "%";
eads@18 613 focus.y = mr((this.fillStyle.focus_.y / height) * 100 + 50) + "%";
eads@18 614
eads@18 615 var colors = [];
eads@18 616
eads@18 617 // inside radius (%)
eads@18 618 if (this.fillStyle.type_ == "gradientradial") {
eads@18 619 var inside = (this.fillStyle.radius1_ / dimension * 100);
eads@18 620
eads@18 621 // percentage that outside radius exceeds inside radius
eads@18 622 var expansion = (this.fillStyle.radius2_ / dimension * 100) - inside;
eads@18 623 } else {
eads@18 624 var inside = 0;
eads@18 625 var expansion = 100;
eads@18 626 }
eads@18 627
eads@18 628 var insidecolor = {offset: null, color: null};
eads@18 629 var outsidecolor = {offset: null, color: null};
eads@18 630
eads@18 631 // We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
eads@18 632 // won't interpret it correctly
eads@18 633 this.fillStyle.colors_.sort(function (cs1, cs2) {
eads@18 634 return cs1.offset - cs2.offset;
eads@18 635 });
eads@18 636
eads@18 637 for (var i = 0; i < this.fillStyle.colors_.length; i++) {
eads@18 638 var fs = this.fillStyle.colors_[i];
eads@18 639
eads@18 640 colors.push( (fs.offset * expansion) + inside, "% ", fs.color, ",");
eads@18 641
eads@18 642 if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
eads@18 643 insidecolor.offset = fs.offset;
eads@18 644 insidecolor.color = fs.color;
eads@18 645 }
eads@18 646
eads@18 647 if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
eads@18 648 outsidecolor.offset = fs.offset;
eads@18 649 outsidecolor.color = fs.color;
eads@18 650 }
eads@18 651 }
eads@18 652 colors.pop();
eads@18 653
eads@18 654 lineStr.push('<g_vml_:fill',
eads@18 655 ' color="', outsidecolor.color, '"',
eads@18 656 ' color2="', insidecolor.color, '"',
eads@18 657 ' type="', this.fillStyle.type_, '"',
eads@18 658 ' focusposition="', focus.x, ', ', focus.y, '"',
eads@18 659 ' colors="', colors.join(""), '"',
eads@18 660 ' opacity="', opacity, '" />');
eads@18 661 } else if (aFill) {
eads@18 662 lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity, '" />');
eads@18 663 } else {
eads@18 664 lineStr.push(
eads@18 665 '<g_vml_:stroke',
eads@18 666 ' opacity="', opacity,'"',
eads@18 667 ' joinstyle="', this.lineJoin, '"',
eads@18 668 ' miterlimit="', this.miterLimit, '"',
eads@18 669 ' endcap="', processLineCap(this.lineCap) ,'"',
eads@18 670 ' weight="', this.lineWidth, 'px"',
eads@18 671 ' color="', color,'" />'
eads@18 672 );
eads@18 673 }
eads@18 674
eads@18 675 lineStr.push("</g_vml_:shape>");
eads@18 676
eads@18 677 this.element_.insertAdjacentHTML("beforeEnd", lineStr.join(""));
eads@18 678
eads@18 679 this.currentPath_ = [];
eads@18 680 };
eads@18 681
eads@18 682 contextPrototype.fill = function() {
eads@18 683 this.stroke(true);
eads@18 684 }
eads@18 685
eads@18 686 contextPrototype.closePath = function() {
eads@18 687 this.currentPath_.push({type: "close"});
eads@18 688 };
eads@18 689
eads@18 690 /**
eads@18 691 * @private
eads@18 692 */
eads@18 693 contextPrototype.getCoords_ = function(aX, aY) {
eads@18 694 return {
eads@18 695 x: Z * (aX * this.m_[0][0] + aY * this.m_[1][0] + this.m_[2][0]) - Z2,
eads@18 696 y: Z * (aX * this.m_[0][1] + aY * this.m_[1][1] + this.m_[2][1]) - Z2
eads@18 697 }
eads@18 698 };
eads@18 699
eads@18 700 contextPrototype.save = function() {
eads@18 701 var o = {};
eads@18 702 copyState(this, o);
eads@18 703 this.aStack_.push(o);
eads@18 704 this.mStack_.push(this.m_);
eads@18 705 this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
eads@18 706 };
eads@18 707
eads@18 708 contextPrototype.restore = function() {
eads@18 709 copyState(this.aStack_.pop(), this);
eads@18 710 this.m_ = this.mStack_.pop();
eads@18 711 };
eads@18 712
eads@18 713 contextPrototype.translate = function(aX, aY) {
eads@18 714 var m1 = [
eads@18 715 [1, 0, 0],
eads@18 716 [0, 1, 0],
eads@18 717 [aX, aY, 1]
eads@18 718 ];
eads@18 719
eads@18 720 this.m_ = matrixMultiply(m1, this.m_);
eads@18 721 };
eads@18 722
eads@18 723 contextPrototype.rotate = function(aRot) {
eads@18 724 var c = mc(aRot);
eads@18 725 var s = ms(aRot);
eads@18 726
eads@18 727 var m1 = [
eads@18 728 [c, s, 0],
eads@18 729 [-s, c, 0],
eads@18 730 [0, 0, 1]
eads@18 731 ];
eads@18 732
eads@18 733 this.m_ = matrixMultiply(m1, this.m_);
eads@18 734 };
eads@18 735
eads@18 736 contextPrototype.scale = function(aX, aY) {
eads@18 737 this.arcScaleX_ *= aX;
eads@18 738 this.arcScaleY_ *= aY;
eads@18 739 var m1 = [
eads@18 740 [aX, 0, 0],
eads@18 741 [0, aY, 0],
eads@18 742 [0, 0, 1]
eads@18 743 ];
eads@18 744
eads@18 745 this.m_ = matrixMultiply(m1, this.m_);
eads@18 746 };
eads@18 747
eads@18 748 /******** STUBS ********/
eads@18 749 contextPrototype.clip = function() {
eads@18 750 // TODO: Implement
eads@18 751 };
eads@18 752
eads@18 753 contextPrototype.arcTo = function() {
eads@18 754 // TODO: Implement
eads@18 755 };
eads@18 756
eads@18 757 contextPrototype.createPattern = function() {
eads@18 758 return new CanvasPattern_;
eads@18 759 };
eads@18 760
eads@18 761 // Gradient / Pattern Stubs
eads@18 762 function CanvasGradient_(aType) {
eads@18 763 this.type_ = aType;
eads@18 764 this.radius1_ = 0;
eads@18 765 this.radius2_ = 0;
eads@18 766 this.colors_ = [];
eads@18 767 this.focus_ = {x: 0, y: 0};
eads@18 768 }
eads@18 769
eads@18 770 CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
eads@18 771 aColor = processStyle(aColor);
eads@18 772 this.colors_.push({offset: 1-aOffset, color: aColor});
eads@18 773 };
eads@18 774
eads@18 775 function CanvasPattern_() {}
eads@18 776
eads@18 777 // set up externs
eads@18 778 G_vmlCanvasManager = G_vmlCanvasManager_;
eads@18 779 CanvasRenderingContext2D = CanvasRenderingContext2D_;
eads@18 780 CanvasGradient = CanvasGradient_;
eads@18 781 CanvasPattern = CanvasPattern_;
eads@18 782
eads@18 783 })();
eads@18 784
eads@18 785 } // if