4 * Copyright (c) 2009 Tim Cameron Ryan
5 * Copyright (c) 2009-2011 FlashCanvas Project
6 * Released under the MIT/X License
10 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html
11 // http://dev.w3.org/html5/spec/the-canvas-element.html
13 // If the browser is IE and does not support HTML5 Canvas
14 if (window["ActiveXObject"] && !window["CanvasRenderingContext2D"]) {
16 (function(window, document, undefined) {
23 var CANVAS = "canvas";
24 var CANVAS_RENDERING_CONTEXT_2D = "CanvasRenderingContext2D";
25 var CANVAS_GRADIENT = "CanvasGradient";
26 var CANVAS_PATTERN = "CanvasPattern";
27 var FLASH_CANVAS = "FlashCanvas";
28 var G_VML_CANVAS_MANAGER = "G_vmlCanvasManager";
29 var OBJECT_ID_PREFIX = "external";
30 var ON_FOCUS = "onfocus";
31 var ON_PROPERTY_CHANGE = "onpropertychange";
32 var ON_READY_STATE_CHANGE = "onreadystatechange";
33 var ON_UNLOAD = "onunload";
35 var config = window[FLASH_CANVAS + "Options"] || {};
36 var BASE_URL = config["swfPath"] || getScriptUrl().replace(/[^\/]+$/, "");
37 var SWF_URL = BASE_URL + "flashcanvas.swf";
40 var INDEX_SIZE_ERR = 1;
41 var NOT_SUPPORTED_ERR = 9;
42 var INVALID_STATE_ERR = 11;
44 var TYPE_MISMATCH_ERR = 17;
45 var SECURITY_ERR = 18;
50 function Lookup(array) {
51 for (var i = 0, n = array.length; i < n; i++)
55 var properties = new Lookup([
59 // CanvasRenderingContext2D
68 "globalCompositeOperation",
71 "createLinearGradient",
72 "createRadialGradient",
118 // Whether swf is ready for use
121 // Monitor the number of loading files
127 // SPAN element embedded in the canvas
134 var CanvasRenderingContext2D = function(canvas, swf) {
135 // back-reference to the canvas
136 this.canvas = canvas;
138 // back-reference to the swf
141 // unique ID of canvas
142 this._canvasId = swf.id.slice(8);
144 // initialize drawing states
147 // Count CanvasGradient and CanvasPattern objects
148 this._gradientPatternId = 0;
150 // Directionality of the canvas element
151 this._direction = "";
153 // frame update interval
155 setInterval(function() {
156 if (lock[self._canvasId] === 0) {
157 self._executeCommand();
162 CanvasRenderingContext2D.prototype = {
168 // write all properties
169 this._setCompositing();
171 this._setStrokeStyle();
172 this._setFillStyle();
173 this._setLineStyles();
174 this._setFontStyles();
177 this._stateStack.push([
179 this._globalCompositeOperation,
195 this._queue.push(properties.save);
198 restore: function() {
200 var stateStack = this._stateStack;
201 if (stateStack.length) {
202 var state = stateStack.pop();
203 this.globalAlpha = state[0];
204 this.globalCompositeOperation = state[1];
205 this.strokeStyle = state[2];
206 this.fillStyle = state[3];
207 this.lineWidth = state[4];
208 this.lineCap = state[5];
209 this.lineJoin = state[6];
210 this.miterLimit = state[7];
211 this.shadowOffsetX = state[8];
212 this.shadowOffsetY = state[9];
213 this.shadowBlur = state[10];
214 this.shadowColor = state[11];
215 this.font = state[12];
216 this.textAlign = state[13];
217 this.textBaseline = state[14];
220 this._queue.push(properties.restore);
227 scale: function(x, y) {
228 this._queue.push(properties.scale, x, y);
231 rotate: function(angle) {
232 this._queue.push(properties.rotate, angle);
235 translate: function(x, y) {
236 this._queue.push(properties.translate, x, y);
239 transform: function(m11, m12, m21, m22, dx, dy) {
240 this._queue.push(properties.transform, m11, m12, m21, m22, dx, dy);
243 setTransform: function(m11, m12, m21, m22, dx, dy) {
244 this._queue.push(properties.setTransform, m11, m12, m21, m22, dx, dy);
251 _setCompositing: function() {
252 var queue = this._queue;
253 if (this._globalAlpha !== this.globalAlpha) {
254 this._globalAlpha = this.globalAlpha;
255 queue.push(properties.globalAlpha, this._globalAlpha);
257 if (this._globalCompositeOperation !== this.globalCompositeOperation) {
258 this._globalCompositeOperation = this.globalCompositeOperation;
259 queue.push(properties.globalCompositeOperation, this._globalCompositeOperation);
267 _setStrokeStyle: function() {
268 if (this._strokeStyle !== this.strokeStyle) {
269 var style = this._strokeStyle = this.strokeStyle;
270 this._queue.push(properties.strokeStyle, (typeof style === "object") ? style.id : style);
274 _setFillStyle: function() {
275 if (this._fillStyle !== this.fillStyle) {
276 var style = this._fillStyle = this.fillStyle;
277 this._queue.push(properties.fillStyle, (typeof style === "object") ? style.id : style);
281 createLinearGradient: function(x0, y0, x1, y1) {
282 // If any of the arguments are not finite numbers, throws a
283 // NOT_SUPPORTED_ERR exception.
284 if (!(isFinite(x0) && isFinite(y0) && isFinite(x1) && isFinite(y1))) {
285 throwException(NOT_SUPPORTED_ERR);
288 this._queue.push(properties.createLinearGradient, x0, y0, x1, y1);
289 return new CanvasGradient(this);
292 createRadialGradient: function(x0, y0, r0, x1, y1, r1) {
293 // If any of the arguments are not finite numbers, throws a
294 // NOT_SUPPORTED_ERR exception.
295 if (!(isFinite(x0) && isFinite(y0) && isFinite(r0) &&
296 isFinite(x1) && isFinite(y1) && isFinite(r1))) {
297 throwException(NOT_SUPPORTED_ERR);
300 // If either of the radii are negative, throws an INDEX_SIZE_ERR
302 if (r0 < 0 || r1 < 0) {
303 throwException(INDEX_SIZE_ERR);
306 this._queue.push(properties.createRadialGradient, x0, y0, r0, x1, y1, r1);
307 return new CanvasGradient(this);
310 createPattern: function(image, repetition) {
311 // If the image is null, the implementation must raise a
312 // TYPE_MISMATCH_ERR exception.
314 throwException(TYPE_MISMATCH_ERR);
317 var tagName = image.tagName, src;
318 var canvasId = this._canvasId;
320 // If the first argument isn't an img, canvas, or video element,
321 // throws a TYPE_MISMATCH_ERR exception.
323 tagName = tagName.toLowerCase();
324 if (tagName === "img") {
325 src = image.getAttribute("src", 2);
326 } else if (tagName === CANVAS || tagName === "video") {
327 // For now, only HTMLImageElement is supported.
330 throwException(TYPE_MISMATCH_ERR);
334 // Additionally, we accept any object that has a src property.
335 // This is useful when you'd like to specify a long data URI.
336 else if (image.src) {
339 throwException(TYPE_MISMATCH_ERR);
342 // If the second argument isn't one of the allowed values, throws a
343 // SYNTAX_ERR exception.
344 if (!(repetition === "repeat" || repetition === "no-repeat" ||
345 repetition === "repeat-x" || repetition === "repeat-y" ||
346 repetition === "" || repetition === NULL)) {
347 throwException(SYNTAX_ERR);
350 // Special characters in the filename need escaping.
351 this._queue.push(properties.createPattern, encodeXML(src), repetition);
353 if (isReady[canvasId]) {
354 this._executeCommand();
358 return new CanvasPattern(this);
365 _setLineStyles: function() {
366 var queue = this._queue;
367 if (this._lineWidth !== this.lineWidth) {
368 this._lineWidth = this.lineWidth;
369 queue.push(properties.lineWidth, this._lineWidth);
371 if (this._lineCap !== this.lineCap) {
372 this._lineCap = this.lineCap;
373 queue.push(properties.lineCap, this._lineCap);
375 if (this._lineJoin !== this.lineJoin) {
376 this._lineJoin = this.lineJoin;
377 queue.push(properties.lineJoin, this._lineJoin);
379 if (this._miterLimit !== this.miterLimit) {
380 this._miterLimit = this.miterLimit;
381 queue.push(properties.miterLimit, this._miterLimit);
389 _setShadows: function() {
390 var queue = this._queue;
391 if (this._shadowOffsetX !== this.shadowOffsetX) {
392 this._shadowOffsetX = this.shadowOffsetX;
393 queue.push(properties.shadowOffsetX, this._shadowOffsetX);
395 if (this._shadowOffsetY !== this.shadowOffsetY) {
396 this._shadowOffsetY = this.shadowOffsetY;
397 queue.push(properties.shadowOffsetY, this._shadowOffsetY);
399 if (this._shadowBlur !== this.shadowBlur) {
400 this._shadowBlur = this.shadowBlur;
401 queue.push(properties.shadowBlur, this._shadowBlur);
403 if (this._shadowColor !== this.shadowColor) {
404 this._shadowColor = this.shadowColor;
405 queue.push(properties.shadowColor, this._shadowColor);
413 clearRect: function(x, y, w, h) {
414 this._queue.push(properties.clearRect, x, y, w, h);
417 fillRect: function(x, y, w, h) {
418 this._setCompositing();
420 this._setFillStyle();
421 this._queue.push(properties.fillRect, x, y, w, h);
424 strokeRect: function(x, y, w, h) {
425 this._setCompositing();
427 this._setStrokeStyle();
428 this._setLineStyles();
429 this._queue.push(properties.strokeRect, x, y, w, h);
436 beginPath: function() {
437 this._queue.push(properties.beginPath);
440 closePath: function() {
441 this._queue.push(properties.closePath);
444 moveTo: function(x, y) {
445 this._queue.push(properties.moveTo, x, y);
448 lineTo: function(x, y) {
449 this._queue.push(properties.lineTo, x, y);
452 quadraticCurveTo: function(cpx, cpy, x, y) {
453 this._queue.push(properties.quadraticCurveTo, cpx, cpy, x, y);
456 bezierCurveTo: function(cp1x, cp1y, cp2x, cp2y, x, y) {
457 this._queue.push(properties.bezierCurveTo, cp1x, cp1y, cp2x, cp2y, x, y);
460 arcTo: function(x1, y1, x2, y2, radius) {
461 // Throws an INDEX_SIZE_ERR exception if the given radius is negative.
462 if (radius < 0 && isFinite(radius)) {
463 throwException(INDEX_SIZE_ERR);
466 this._queue.push(properties.arcTo, x1, y1, x2, y2, radius);
469 rect: function(x, y, w, h) {
470 this._queue.push(properties.rect, x, y, w, h);
473 arc: function(x, y, radius, startAngle, endAngle, anticlockwise) {
474 // Throws an INDEX_SIZE_ERR exception if the given radius is negative.
475 if (radius < 0 && isFinite(radius)) {
476 throwException(INDEX_SIZE_ERR);
479 this._queue.push(properties.arc, x, y, radius, startAngle, endAngle, anticlockwise ? 1 : 0);
483 this._setCompositing();
485 this._setFillStyle();
486 this._queue.push(properties.fill);
490 this._setCompositing();
492 this._setStrokeStyle();
493 this._setLineStyles();
494 this._queue.push(properties.stroke);
498 this._queue.push(properties.clip);
501 isPointInPath: function(x, y) {
509 _setFontStyles: function() {
510 var queue = this._queue;
511 if (this._font !== this.font) {
513 var span = spans[this._canvasId];
514 span.style.font = this._font = this.font;
516 var style = span.currentStyle;
517 var fontSize = span.offsetHeight;
518 var font = [style.fontStyle, style.fontWeight, fontSize, style.fontFamily].join(" ");
519 queue.push(properties.font, font);
521 // If this.font cannot be parsed as a CSS font value, then it
525 if (this._textAlign !== this.textAlign) {
526 this._textAlign = this.textAlign;
527 queue.push(properties.textAlign, this._textAlign);
529 if (this._textBaseline !== this.textBaseline) {
530 this._textBaseline = this.textBaseline;
531 queue.push(properties.textBaseline, this._textBaseline);
533 if (this._direction !== this.canvas.currentStyle.direction) {
534 this._direction = this.canvas.currentStyle.direction;
535 queue.push(properties.direction, this._direction);
539 fillText: function(text, x, y, maxWidth) {
540 this._setCompositing();
541 this._setFillStyle();
543 this._setFontStyles();
544 this._queue.push(properties.fillText, encodeXML(text), x, y,
545 maxWidth === undefined ? Infinity : maxWidth);
548 strokeText: function(text, x, y, maxWidth) {
549 this._setCompositing();
550 this._setStrokeStyle();
552 this._setFontStyles();
553 this._queue.push(properties.strokeText, encodeXML(text), x, y,
554 maxWidth === undefined ? Infinity : maxWidth);
557 measureText: function(text) {
558 var span = spans[this._canvasId];
560 span.style.font = this.font;
562 // If this.font cannot be parsed as a CSS font value, then it must
566 // Replace space characters with tab characters because innerText
567 // removes trailing white spaces.
568 span.innerText = text.replace(/[ \n\f\r]/g, "\t");
570 return new TextMetrics(span.offsetWidth);
577 drawImage: function(image, x1, y1, w1, h1, x2, y2, w2, h2) {
578 // If the image is null, the implementation must raise a
579 // TYPE_MISMATCH_ERR exception.
581 throwException(TYPE_MISMATCH_ERR);
584 var tagName = image.tagName, src, argc = arguments.length;
585 var canvasId = this._canvasId;
587 // If the first argument isn't an img, canvas, or video element,
588 // throws a TYPE_MISMATCH_ERR exception.
590 tagName = tagName.toLowerCase();
591 if (tagName === "img") {
592 src = image.getAttribute("src", 2);
593 } else if (tagName === CANVAS || tagName === "video") {
594 // For now, only HTMLImageElement is supported.
597 throwException(TYPE_MISMATCH_ERR);
601 // Additionally, we accept any object that has a src property.
602 // This is useful when you'd like to specify a long data URI.
603 else if (image.src) {
606 throwException(TYPE_MISMATCH_ERR);
609 this._setCompositing();
612 // Special characters in the filename need escaping.
613 src = encodeXML(src);
616 this._queue.push(properties.drawImage, argc, src, x1, y1);
617 } else if (argc === 5) {
618 this._queue.push(properties.drawImage, argc, src, x1, y1, w1, h1);
619 } else if (argc === 9) {
620 // If one of the sw or sh arguments is zero, the implementation
621 // must raise an INDEX_SIZE_ERR exception.
622 if (w1 === 0 || h1 === 0) {
623 throwException(INDEX_SIZE_ERR);
626 this._queue.push(properties.drawImage, argc, src, x1, y1, w1, h1, x2, y2, w2, h2);
631 if (isReady[canvasId]) {
632 this._executeCommand();
641 // ImageData createImageData(in float sw, in float sh);
642 // ImageData createImageData(in ImageData imagedata);
643 createImageData: function() {
647 // ImageData getImageData(in float sx, in float sy, in float sw, in float sh);
648 getImageData: function(sx, sy, sw, sh) {
652 // void putImageData(in ImageData imagedata, in float dx, in float dy, [Optional] in float dirtyX, in float dirtyY, in float dirtyWidth, in float dirtyHeight);
653 putImageData: function(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) {
661 _initialize: function() {
663 this.globalAlpha = this._globalAlpha = 1.0;
664 this.globalCompositeOperation = this._globalCompositeOperation = "source-over";
667 this.strokeStyle = this._strokeStyle = "#000000";
668 this.fillStyle = this._fillStyle = "#000000";
671 this.lineWidth = this._lineWidth = 1.0;
672 this.lineCap = this._lineCap = "butt";
673 this.lineJoin = this._lineJoin = "miter";
674 this.miterLimit = this._miterLimit = 10.0;
677 this.shadowOffsetX = this._shadowOffsetX = 0;
678 this.shadowOffsetY = this._shadowOffsetY = 0;
679 this.shadowBlur = this._shadowBlur = 0;
680 this.shadowColor = this._shadowColor = "rgba(0, 0, 0, 0.0)";
683 this.font = this._font = "10px sans-serif";
684 this.textAlign = this._textAlign = "start";
685 this.textBaseline = this._textBaseline = "alphabetic";
690 // stack of drawing states
691 this._stateStack = [];
695 var queue = this._queue;
700 _executeCommand: function() {
702 var commands = this._flush();
703 if (commands.length > 0) {
704 return eval(this._swf.CallFunction(
705 '<invoke name="executeCommand" returntype="javascript"><arguments><string>'
706 + commands.join("�") + "</string></arguments></invoke>"
711 _resize: function(width, height) {
712 // Flush commands in the queue
713 this._executeCommand();
715 // Clear back to the initial state
718 // Adjust the size of Flash to that of the canvas
720 this._swf.width = width;
723 this._swf.height = height;
726 // Execute a resize command at the start of the next frame
727 this._queue.push(properties.resize, width, height);
732 * CanvasGradient stub
735 var CanvasGradient = function(ctx) {
737 this.id = ctx._gradientPatternId++;
740 CanvasGradient.prototype = {
741 addColorStop: function(offset, color) {
742 // Throws an INDEX_SIZE_ERR exception if the offset is out of range.
743 if (isNaN(offset) || offset < 0 || offset > 1) {
744 throwException(INDEX_SIZE_ERR);
747 this._ctx._queue.push(properties.addColorStop, this.id, offset, color);
755 var CanvasPattern = function(ctx) {
756 this.id = ctx._gradientPatternId++;
763 var TextMetrics = function(width) {
771 var DOMException = function(code) {
773 this.message = DOMExceptionNames[code];
776 DOMException.prototype = new Error;
778 var DOMExceptionNames = {
780 9: "NOT_SUPPORTED_ERR",
781 11: "INVALID_STATE_ERR",
783 17: "TYPE_MISMATCH_ERR",
791 function onReadyStateChange() {
792 if (document.readyState === "complete") {
793 document.detachEvent(ON_READY_STATE_CHANGE, onReadyStateChange);
795 var canvases = document.getElementsByTagName(CANVAS);
796 for (var i = 0, n = canvases.length; i < n; ++i) {
797 FlashCanvas.initElement(canvases[i]);
803 // forward the event to the parent
804 var swf = event.srcElement, canvas = swf.parentNode;
809 function onPropertyChange() {
810 var prop = event.propertyName;
811 if (prop === "width" || prop === "height") {
812 var canvas = event.srcElement;
813 var value = canvas[prop];
814 var number = parseInt(value, 10);
816 if (isNaN(number) || number < 0) {
817 number = (prop === "width") ? 300 : 150;
820 if (value === number) {
821 canvas.style[prop] = number + "px";
822 canvas.getContext("2d")._resize(canvas.width, canvas.height);
824 canvas[prop] = number;
829 function onUnload() {
830 window.detachEvent(ON_UNLOAD, onUnload);
832 for (var canvasId in canvases) {
833 var canvas = canvases[canvasId], swf = canvas.firstChild, prop;
835 // clean up the references of swf.executeCommand and swf.resize
837 if (typeof swf[prop] === "function") {
842 // clean up the references of canvas.getContext and canvas.toDataURL
843 for (prop in canvas) {
844 if (typeof canvas[prop] === "function") {
849 // remove event listeners
850 swf.detachEvent(ON_FOCUS, onFocus);
851 canvas.detachEvent(ON_PROPERTY_CHANGE, onPropertyChange);
854 // delete exported symbols
855 window[CANVAS_RENDERING_CONTEXT_2D] = NULL;
856 window[CANVAS_GRADIENT] = NULL;
857 window[CANVAS_PATTERN] = NULL;
858 window[FLASH_CANVAS] = NULL;
859 window[G_VML_CANVAS_MANAGER] = NULL;
867 initElement: function(canvas) {
868 // Check whether the initialization is required or not.
869 if (canvas.getContext) {
874 var canvasId = getUniqueId();
875 var objectId = OBJECT_ID_PREFIX + canvasId;
876 isReady[canvasId] = false;
879 // Set the width and height attributes.
880 setCanvasSize(canvas);
882 // embed swf and SPAN element
884 '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"' +
885 ' codebase="' + location.protocol + '//fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0"' +
886 ' width="100%" height="100%" id="' + objectId + '">' +
887 '<param name="allowScriptAccess" value="always">' +
888 '<param name="flashvars" value="id=' + objectId + '">' +
889 '<param name="wmode" value="transparent">' +
891 '<span style="margin:0;padding:0;border:0;display:inline-block;position:static;height:1em;overflow:visible;white-space:nowrap">' +
894 canvases[canvasId] = canvas;
895 var swf = canvas.firstChild;
896 spans[canvasId] = canvas.lastChild;
898 // Check whether the canvas element is in the DOM tree
899 var documentContains = document.body.contains;
900 if (documentContains(canvas)) {
901 // Load swf file immediately
902 swf["movie"] = SWF_URL;
904 // Wait until the element is added to the DOM tree
905 var intervalId = setInterval(function() {
906 if (documentContains(canvas)) {
907 clearInterval(intervalId);
908 swf["movie"] = SWF_URL;
913 // If the browser is IE6 or in quirks mode
914 if (document.compatMode === "BackCompat" || !window.XMLHttpRequest) {
915 spans[canvasId].style.overflow = "hidden";
918 // initialize context
919 var ctx = new CanvasRenderingContext2D(canvas, swf);
922 canvas.getContext = function(contextId) {
923 return contextId === "2d" ? ctx : NULL;
926 canvas.toDataURL = function(type, quality) {
927 if (("" + type).replace(/[A-Z]+/g, toLowerCase) === "image/jpeg") {
928 ctx._queue.push(properties.toDataURL, type,
929 typeof quality === "number" ? quality : "");
931 ctx._queue.push(properties.toDataURL, type);
933 return ctx._executeCommand();
936 // add event listener
937 swf.attachEvent(ON_FOCUS, onFocus);
942 saveImage: function(canvas) {
943 var swf = canvas.firstChild;
947 setOptions: function(options) {
951 trigger: function(canvasId, type) {
952 var canvas = canvases[canvasId];
953 canvas.fireEvent("on" + type);
956 unlock: function(canvasId, ready) {
957 if (lock[canvasId]) {
961 var canvas = canvases[canvasId];
962 var swf = canvas.firstChild;
966 // Set the width and height attributes of the canvas element.
967 setCanvasSize(canvas);
968 width = canvas.width;
969 height = canvas.height;
971 canvas.style.width = width + "px";
972 canvas.style.height = height + "px";
974 // Adjust the size of Flash to that of the canvas
981 swf.resize(width, height);
983 // Add event listener
984 canvas.attachEvent(ON_PROPERTY_CHANGE, onPropertyChange);
986 // ExternalInterface is now ready for use
987 isReady[canvasId] = true;
996 // Get the absolute URL of flashcanvas.js
997 function getScriptUrl() {
998 var scripts = document.getElementsByTagName("script");
999 var script = scripts[scripts.length - 1];
1001 // @see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
1002 if (document.documentMode >= 8) {
1005 return script.getAttribute("src", 4);
1009 // Get a unique ID composed of alphanumeric characters.
1010 function getUniqueId() {
1011 return Math.random().toString(36).slice(2) || "0";
1014 // Escape characters not permitted in XML.
1015 function encodeXML(str) {
1016 return ("" + str).replace(/&/g, "&").replace(/</g, "<");
1019 function toLowerCase(str) {
1020 return str.toLowerCase();
1023 function throwException(code) {
1024 throw new DOMException(code);
1027 // The width and height attributes of a canvas element must have values that
1028 // are valid non-negative integers.
1029 function setCanvasSize(canvas) {
1030 var width = parseInt(canvas.width, 10);
1031 var height = parseInt(canvas.height, 10);
1033 if (isNaN(width) || width < 0) {
1036 if (isNaN(height) || height < 0) {
1040 canvas.width = width;
1041 canvas.height = height;
1049 document.createElement(CANVAS);
1051 // setup default CSS
1052 document.createStyleSheet().cssText =
1053 CANVAS + "{display:inline-block;overflow:hidden;width:300px;height:150px}";
1055 // initialize canvas elements
1056 if (document.readyState === "complete") {
1057 onReadyStateChange();
1059 document.attachEvent(ON_READY_STATE_CHANGE, onReadyStateChange);
1062 // prevent IE6 memory leaks
1063 window.attachEvent(ON_UNLOAD, onUnload);
1065 // preload SWF file if it's in the same domain
1066 if (SWF_URL.indexOf(location.protocol + "//" + location.host + "/") === 0) {
1067 var req = new ActiveXObject("Microsoft.XMLHTTP");
1068 req.open("GET", SWF_URL, false);
1076 window[CANVAS_RENDERING_CONTEXT_2D] = CanvasRenderingContext2D;
1077 window[CANVAS_GRADIENT] = CanvasGradient;
1078 window[CANVAS_PATTERN] = CanvasPattern;
1079 window[FLASH_CANVAS] = FlashCanvas;
1081 // ExplorerCanvas-compatible APIs for convenience
1082 window[G_VML_CANVAS_MANAGER] = {
1084 init_: function(){},
1085 initElement: FlashCanvas.initElement
1088 // Prevent Closure Compiler from removing the function.
1089 keep = CanvasRenderingContext2D.measureText;
1091 })(window, document);