1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333 |
- /** @preserve FlashCanvas, ${buildDate} ${commitID}
- * Copyright 2012 Willow Systems Corp
- * Copyright (c) 2009 Tim Cameron Ryan
- * Copyright (c) 2009-2011 FlashCanvas Project
- * Released under the MIT/X License
- */
- // Reference:
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html
- // http://dev.w3.org/html5/spec/the-canvas-element.html
- // If the browser is IE and does not support HTML5 Canvas
- if (window["ActiveXObject"] && !window["CanvasRenderingContext2D"]) {
- (function() {
- 'use strict'
- var window = this
- , document = window.document
- , undefined
- /*
- * Constant
- */
- var NULL = null;
- var CANVAS = "canvas";
- var CANVAS_RENDERING_CONTEXT_2D = "CanvasRenderingContext2D";
- var CANVAS_GRADIENT = "CanvasGradient";
- var CANVAS_PATTERN = "CanvasPattern";
- var FLASH_CANVAS = "FlashCanvas";
- var OBJECT_ID_PREFIX = "external";
- var ON_FOCUS = "onfocus";
- var ON_PROPERTY_CHANGE = "onpropertychange";
- var ON_READY_STATE_CHANGE = "onreadystatechange";
- var ON_UNLOAD = "onunload";
- var BASE_URL = (function(){
- var scripts = this.document.getElementsByTagName("script")
- // async script tag injections lead to our script NOT being the last. so
- // var script = scripts[scripts.length - 1];
- // will not work
- // so we just loop over scripts and look for "flashcanvas"
- // and go for "last script tag's src" only if path is not matched
- // (may happen when flashcanvas script is loaded with name not containing 'flashcanvas')
- // backwardCompatibilityUrl: original script was looking at last script tag's src.
- // we simulate that for cases when proper URL is not found elsewhere.
- var backwardCompatibilityUrl = ''
- var i = scripts.length
- if (i) {
- backwardCompatibilityUrl = scripts[i - 1].src || ''
- while (i){
- script = scripts[i - 1] // yes, we look from the back of the queue
- if (script.src && script.src.match('flashcanvas')) {
- // we are trying to return absolute path:
- // @see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
- // @see http://stackoverflow.com/questions/984510/what-is-my-script-src-url
- if (document.documentMode >= 8) {
- return script.src;
- } else {
- return script.getAttribute("src", 4);
- }
- }
- ;i--;
- }
- }
- return backwardCompatibilityUrl
- }).call(window).replace(/[^\/]+$/, "") // last part trims all chars following last '/'
- // DOMException code
- var INDEX_SIZE_ERR = 1;
- var NOT_SUPPORTED_ERR = 9;
- var INVALID_STATE_ERR = 11;
- var SYNTAX_ERR = 12;
- var TYPE_MISMATCH_ERR = 17;
- var SECURITY_ERR = 18;
- /**
- * @constructor
- */
- function Lookup(array) {
- for (var i = 0, n = array.length; i < n; i++)
- this[array[i]] = i;
- }
- var properties = new Lookup([
- // Canvas element
- "toDataURL",
- // CanvasRenderingContext2D
- "save",
- "restore",
- "scale",
- "rotate",
- "translate",
- "transform",
- "setTransform",
- "globalAlpha",
- "globalCompositeOperation",
- "strokeStyle",
- "fillStyle",
- "createLinearGradient",
- "createRadialGradient",
- "createPattern",
- "lineWidth",
- "lineCap",
- "lineJoin",
- "miterLimit",
- "shadowOffsetX",
- "shadowOffsetY",
- "shadowBlur",
- "shadowColor",
- "clearRect",
- "fillRect",
- "strokeRect",
- "beginPath",
- "closePath",
- "moveTo",
- "lineTo",
- "quadraticCurveTo",
- "bezierCurveTo",
- "arcTo",
- "rect",
- "arc",
- "fill",
- "stroke",
- "clip",
- "isPointInPath",
- // "drawFocusRing",
- "font",
- "textAlign",
- "textBaseline",
- "fillText",
- "strokeText",
- "measureText",
- "drawImage",
- "createImageData",
- "getImageData",
- "putImageData",
- // CanvasGradient
- "addColorStop",
- // Internal use
- "direction",
- "resize"
- ]);
- // Whether swf is ready for use
- var isReady = {};
- // Cache of images loaded by createPattern() or drawImage()
- var images = {};
- // Monitor the number of loading files
- var lock = {};
- // Callback functions passed to loadImage()
- var callbacks = {};
- // SPAN element embedded in the canvas
- var spans = {};
- var elementIsOrphan = function(e){
- var topOfDOM = false
- e = e.parentNode
- while (e && !topOfDOM){
- topOfDOM = e.body
- e = e.parentNode
- }
- return !topOfDOM
- }
- /**
- * 2D context
- * @constructor
- */
- var CanvasRenderingContext2D = function(canvas, swf) {
- // back-reference to the canvas
- this.canvas = canvas;
- // back-reference to the swf
- this._swf = swf;
- // unique ID of canvas
- this._canvasId = swf.id.slice(8);
- // initialize drawing states
- this._initialize();
- // Count CanvasGradient and CanvasPattern objects
- this._gradientPatternId = 0;
- // Directionality of the canvas element
- this._direction = "";
- // This ensures that font properties of the canvas element is
- // transmitted to Flash.
- this._font = "";
- // frame update interval
- var self = this
- this._executeCommandIntervalID = setInterval(function() {
- if (elementIsOrphan(self.canvas)) {
- clearInterval(self._executeCommandIntervalID)
- } else {
- if (lock[self._canvasId] === 0) {
- self._executeCommand();
- }
- }
- }, 30)
- };
- CanvasRenderingContext2D.prototype = {
- /*
- * state
- */
- save: function() {
- // write all properties
- this._setCompositing();
- this._setShadows();
- this._setStrokeStyle();
- this._setFillStyle();
- this._setLineStyles();
- this._setFontStyles();
- // push state
- this._stateStack.push([
- this._globalAlpha,
- this._globalCompositeOperation,
- this._strokeStyle,
- this._fillStyle,
- this._lineWidth,
- this._lineCap,
- this._lineJoin,
- this._miterLimit,
- this._shadowOffsetX,
- this._shadowOffsetY,
- this._shadowBlur,
- this._shadowColor,
- this._font,
- this._textAlign,
- this._textBaseline
- ]);
- this._queue.push(properties.save);
- },
- restore: function() {
- // pop state
- var stateStack = this._stateStack;
- if (stateStack.length) {
- var state = stateStack.pop();
- this.globalAlpha = state[0];
- this.globalCompositeOperation = state[1];
- this.strokeStyle = state[2];
- this.fillStyle = state[3];
- this.lineWidth = state[4];
- this.lineCap = state[5];
- this.lineJoin = state[6];
- this.miterLimit = state[7];
- this.shadowOffsetX = state[8];
- this.shadowOffsetY = state[9];
- this.shadowBlur = state[10];
- this.shadowColor = state[11];
- this.font = state[12];
- this.textAlign = state[13];
- this.textBaseline = state[14];
- }
- this._queue.push(properties.restore);
- },
- /*
- * transformations
- */
- scale: function(x, y) {
- this._queue.push(properties.scale, x, y);
- },
- rotate: function(angle) {
- this._queue.push(properties.rotate, angle);
- },
- translate: function(x, y) {
- this._queue.push(properties.translate, x, y);
- },
- transform: function(m11, m12, m21, m22, dx, dy) {
- this._queue.push(properties.transform, m11, m12, m21, m22, dx, dy);
- },
- setTransform: function(m11, m12, m21, m22, dx, dy) {
- this._queue.push(properties.setTransform, m11, m12, m21, m22, dx, dy);
- },
- /*
- * compositing
- */
- _setCompositing: function() {
- var queue = this._queue;
- if (this._globalAlpha !== this.globalAlpha) {
- this._globalAlpha = this.globalAlpha;
- queue.push(properties.globalAlpha, this._globalAlpha);
- }
- if (this._globalCompositeOperation !== this.globalCompositeOperation) {
- this._globalCompositeOperation = this.globalCompositeOperation;
- queue.push(properties.globalCompositeOperation, this._globalCompositeOperation);
- }
- },
- /*
- * colors and styles
- */
- _setStrokeStyle: function() {
- if (this._strokeStyle !== this.strokeStyle) {
- var style = this._strokeStyle = this.strokeStyle;
- if (typeof style === "string") {
- // OK
- } else if (style instanceof CanvasGradient ||
- style instanceof CanvasPattern) {
- style = style.id;
- } else {
- return;
- }
- this._queue.push(properties.strokeStyle, style);
- }
- },
- _setFillStyle: function() {
- if (this._fillStyle !== this.fillStyle) {
- var style = this._fillStyle = this.fillStyle;
- if (typeof style === "string") {
- // OK
- } else if (style instanceof CanvasGradient ||
- style instanceof CanvasPattern) {
- style = style.id;
- } else {
- return;
- }
- this._queue.push(properties.fillStyle, style);
- }
- },
- createLinearGradient: function(x0, y0, x1, y1) {
- // If any of the arguments are not finite numbers, throws a
- // NOT_SUPPORTED_ERR exception.
- if (!(isFinite(x0) && isFinite(y0) && isFinite(x1) && isFinite(y1))) {
- throwException(NOT_SUPPORTED_ERR);
- }
- this._queue.push(properties.createLinearGradient, x0, y0, x1, y1);
- return new CanvasGradient(this);
- },
- createRadialGradient: function(x0, y0, r0, x1, y1, r1) {
- // If any of the arguments are not finite numbers, throws a
- // NOT_SUPPORTED_ERR exception.
- if (!(isFinite(x0) && isFinite(y0) && isFinite(r0) &&
- isFinite(x1) && isFinite(y1) && isFinite(r1))) {
- throwException(NOT_SUPPORTED_ERR);
- }
- // If either of the radii are negative, throws an INDEX_SIZE_ERR
- // exception.
- if (r0 < 0 || r1 < 0) {
- throwException(INDEX_SIZE_ERR);
- }
- this._queue.push(properties.createRadialGradient, x0, y0, r0, x1, y1, r1);
- return new CanvasGradient(this);
- },
- createPattern: function(image, repetition) {
- // If the image is null, the implementation must raise a
- // TYPE_MISMATCH_ERR exception.
- if (!image) {
- throwException(TYPE_MISMATCH_ERR);
- }
- var tagName = image.tagName, src;
- var canvasId = this._canvasId;
- // If the first argument isn't an img, canvas, or video element,
- // throws a TYPE_MISMATCH_ERR exception.
- if (tagName) {
- tagName = tagName.toLowerCase();
- if (tagName === "img") {
- src = image.getAttribute("src", 2);
- } else if (tagName === CANVAS || tagName === "video") {
- // For now, only HTMLImageElement is supported.
- return;
- } else {
- throwException(TYPE_MISMATCH_ERR);
- }
- }
- // Additionally, we accept any object that has a src property.
- // This is useful when you'd like to specify a long data URI.
- else if (image.src) {
- src = image.src;
- } else {
- throwException(TYPE_MISMATCH_ERR);
- }
- // If the second argument isn't one of the allowed values, throws a
- // SYNTAX_ERR exception.
- if (!(repetition === "repeat" || repetition === "no-repeat" ||
- repetition === "repeat-x" || repetition === "repeat-y" ||
- repetition === "" || repetition === NULL)) {
- throwException(SYNTAX_ERR);
- }
- // Special characters in the filename need escaping.
- this._queue.push(properties.createPattern, encodeXML(src), repetition);
- // If this is the first time to access the URL, the canvas should be
- // locked while the image is being loaded asynchronously.
- if (!images[canvasId][src] && isReady[canvasId]) {
- this._executeCommand();
- ++lock[canvasId];
- images[canvasId][src] = true;
- }
- return new CanvasPattern(this);
- },
- /*
- * line caps/joins
- */
- _setLineStyles: function() {
- var queue = this._queue;
- if (this._lineWidth !== this.lineWidth) {
- this._lineWidth = this.lineWidth;
- queue.push(properties.lineWidth, this._lineWidth);
- }
- if (this._lineCap !== this.lineCap) {
- this._lineCap = this.lineCap;
- queue.push(properties.lineCap, this._lineCap);
- }
- if (this._lineJoin !== this.lineJoin) {
- this._lineJoin = this.lineJoin;
- queue.push(properties.lineJoin, this._lineJoin);
- }
- if (this._miterLimit !== this.miterLimit) {
- this._miterLimit = this.miterLimit;
- queue.push(properties.miterLimit, this._miterLimit);
- }
- },
- /*
- * shadows
- */
- _setShadows: function() {
- var queue = this._queue;
- if (this._shadowOffsetX !== this.shadowOffsetX) {
- this._shadowOffsetX = this.shadowOffsetX;
- queue.push(properties.shadowOffsetX, this._shadowOffsetX);
- }
- if (this._shadowOffsetY !== this.shadowOffsetY) {
- this._shadowOffsetY = this.shadowOffsetY;
- queue.push(properties.shadowOffsetY, this._shadowOffsetY);
- }
- if (this._shadowBlur !== this.shadowBlur) {
- this._shadowBlur = this.shadowBlur;
- queue.push(properties.shadowBlur, this._shadowBlur);
- }
- if (this._shadowColor !== this.shadowColor) {
- this._shadowColor = this.shadowColor;
- queue.push(properties.shadowColor, this._shadowColor);
- }
- },
- /*
- * rects
- */
- clearRect: function(x, y, w, h) {
- this._queue.push(properties.clearRect, x, y, w, h);
- },
- fillRect: function(x, y, w, h) {
- this._setCompositing();
- this._setShadows();
- this._setFillStyle();
- this._queue.push(properties.fillRect, x, y, w, h);
- },
- strokeRect: function(x, y, w, h) {
- this._setCompositing();
- this._setShadows();
- this._setStrokeStyle();
- this._setLineStyles();
- this._queue.push(properties.strokeRect, x, y, w, h);
- },
- /*
- * path API
- */
- beginPath: function() {
- this._queue.push(properties.beginPath);
- },
- closePath: function() {
- this._queue.push(properties.closePath);
- },
- moveTo: function(x, y) {
- this._queue.push(properties.moveTo, x, y);
- },
- lineTo: function(x, y) {
- this._queue.push(properties.lineTo, x, y);
- },
- quadraticCurveTo: function(cpx, cpy, x, y) {
- this._queue.push(properties.quadraticCurveTo, cpx, cpy, x, y);
- },
- bezierCurveTo: function(cp1x, cp1y, cp2x, cp2y, x, y) {
- this._queue.push(properties.bezierCurveTo, cp1x, cp1y, cp2x, cp2y, x, y);
- },
- arcTo: function(x1, y1, x2, y2, radius) {
- // Throws an INDEX_SIZE_ERR exception if the given radius is negative.
- if (radius < 0 && isFinite(radius)) {
- throwException(INDEX_SIZE_ERR);
- }
- this._queue.push(properties.arcTo, x1, y1, x2, y2, radius);
- },
- rect: function(x, y, w, h) {
- this._queue.push(properties.rect, x, y, w, h);
- },
- arc: function(x, y, radius, startAngle, endAngle, anticlockwise) {
- // Throws an INDEX_SIZE_ERR exception if the given radius is negative.
- if (radius < 0 && isFinite(radius)) {
- throwException(INDEX_SIZE_ERR);
- }
- this._queue.push(properties.arc, x, y, radius, startAngle, endAngle, anticlockwise ? 1 : 0);
- },
- fill: function() {
- this._setCompositing();
- this._setShadows();
- this._setFillStyle();
- this._queue.push(properties.fill);
- },
- stroke: function() {
- this._setCompositing();
- this._setShadows();
- this._setStrokeStyle();
- this._setLineStyles();
- this._queue.push(properties.stroke);
- },
- clip: function() {
- this._queue.push(properties.clip);
- },
- isPointInPath: function(x, y) {
- // TODO: Implement
- },
- /*
- * text
- */
- _setFontStyles: function() {
- var queue = this._queue;
- if (this._font !== this.font) {
- try {
- var span = spans[this._canvasId];
- span.style.font = this._font = this.font;
- var style = span.currentStyle;
- var fontSize = span.offsetHeight;
- var font = [style.fontStyle, style.fontWeight, fontSize, style.fontFamily].join(" ");
- queue.push(properties.font, font);
- } catch(e) {
- // If this.font cannot be parsed as a CSS font value, then it
- // must be ignored.
- }
- }
- if (this._textAlign !== this.textAlign) {
- this._textAlign = this.textAlign;
- queue.push(properties.textAlign, this._textAlign);
- }
- if (this._textBaseline !== this.textBaseline) {
- this._textBaseline = this.textBaseline;
- queue.push(properties.textBaseline, this._textBaseline);
- }
- if (this._direction !== this.canvas.currentStyle.direction) {
- this._direction = this.canvas.currentStyle.direction;
- queue.push(properties.direction, this._direction);
- }
- },
- fillText: function(text, x, y, maxWidth) {
- this._setCompositing();
- this._setFillStyle();
- this._setShadows();
- this._setFontStyles();
- this._queue.push(properties.fillText, encodeXML(text), x, y,
- maxWidth === undefined ? Infinity : maxWidth);
- },
- strokeText: function(text, x, y, maxWidth) {
- this._setCompositing();
- this._setStrokeStyle();
- this._setShadows();
- this._setFontStyles();
- this._queue.push(properties.strokeText, encodeXML(text), x, y,
- maxWidth === undefined ? Infinity : maxWidth);
- },
- measureText: function(text) {
- var span = spans[this._canvasId];
- try {
- span.style.font = this.font;
- } catch(e) {
- // If this.font cannot be parsed as a CSS font value, then it must
- // be ignored.
- }
- // Replace space characters with tab characters because innerText
- // removes trailing white spaces.
- span.innerText = text.replace(/[ \n\f\r]/g, "\t");
- return new TextMetrics(span.offsetWidth);
- },
- /*
- * drawing images
- */
- drawImage: function(image, x1, y1, w1, h1, x2, y2, w2, h2) {
- // If the image is null, the implementation must raise a
- // TYPE_MISMATCH_ERR exception.
- if (!image) {
- throwException(TYPE_MISMATCH_ERR);
- }
- var tagName = image.tagName, src, argc = arguments.length;
- var canvasId = this._canvasId;
- // If the first argument isn't an img, canvas, or video element,
- // throws a TYPE_MISMATCH_ERR exception.
- if (tagName) {
- tagName = tagName.toLowerCase();
- if (tagName === "img") {
- src = image.getAttribute("src", 2);
- } else if (tagName === CANVAS || tagName === "video") {
- // For now, only HTMLImageElement is supported.
- return;
- } else {
- throwException(TYPE_MISMATCH_ERR);
- }
- }
- // Additionally, we accept any object that has a src property.
- // This is useful when you'd like to specify a long data URI.
- else if (image.src) {
- src = image.src;
- } else {
- throwException(TYPE_MISMATCH_ERR);
- }
- this._setCompositing();
- this._setShadows();
- // Special characters in the filename need escaping.
- src = encodeXML(src);
- if (argc === 3) {
- this._queue.push(properties.drawImage, argc, src, x1, y1);
- } else if (argc === 5) {
- this._queue.push(properties.drawImage, argc, src, x1, y1, w1, h1);
- } else if (argc === 9) {
- // If one of the sw or sh arguments is zero, the implementation
- // must raise an INDEX_SIZE_ERR exception.
- if (w1 === 0 || h1 === 0) {
- throwException(INDEX_SIZE_ERR);
- }
- this._queue.push(properties.drawImage, argc, src, x1, y1, w1, h1, x2, y2, w2, h2);
- } else {
- return;
- }
- // If this is the first time to access the URL, the canvas should be
- // locked while the image is being loaded asynchronously.
- if (!images[canvasId][src] && isReady[canvasId]) {
- this._executeCommand();
- ++lock[canvasId];
- images[canvasId][src] = true;
- }
- },
- /*
- * pixel manipulation
- */
- // ImageData createImageData(in float sw, in float sh);
- // ImageData createImageData(in ImageData imagedata);
- createImageData: function() {
- // TODO: Implement
- },
- // ImageData getImageData(in float sx, in float sy, in float sw, in float sh);
- getImageData: function(sx, sy, sw, sh) {
- // TODO: Implement
- },
- // void putImageData(in ImageData imagedata, in float dx, in float dy, [Optional] in float dirtyX, in float dirtyY, in float dirtyWidth, in float dirtyHeight);
- putImageData: function(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight) {
- // TODO: Implement
- },
- /*
- * extended functions
- */
- loadImage: function(image, onload, onerror) {
- var tagName = image.tagName, src;
- var canvasId = this._canvasId;
- // Get the URL of the image.
- if (tagName) {
- if (tagName.toLowerCase() === "img") {
- src = image.getAttribute("src", 2);
- }
- } else if (image.src) {
- src = image.src;
- }
- // Do nothing in the following cases:
- // - The first argument is neither an img element nor an object
- // with a src property,
- // - The image has been already cached.
- if (!src || images[canvasId][src]) {
- return;
- }
- // Store the objects.
- if (onload || onerror) {
- callbacks[canvasId][src] = [image, onload, onerror];
- }
- // Load the image without drawing.
- this._queue.push(properties.drawImage, 1, encodeXML(src));
- // Execute the command immediately if possible.
- if (isReady[canvasId]) {
- this._executeCommand();
- ++lock[canvasId];
- images[canvasId][src] = true;
- }
- },
- /*
- * private methods
- */
- _initialize: function() {
- // compositing
- this.globalAlpha = this._globalAlpha = 1.0;
- this.globalCompositeOperation = this._globalCompositeOperation = "source-over";
- // colors and styles
- this.strokeStyle = this._strokeStyle = "#000000";
- this.fillStyle = this._fillStyle = "#000000";
- // line caps/joins
- this.lineWidth = this._lineWidth = 1.0;
- this.lineCap = this._lineCap = "butt";
- this.lineJoin = this._lineJoin = "miter";
- this.miterLimit = this._miterLimit = 10.0;
- // shadows
- this.shadowOffsetX = this._shadowOffsetX = 0;
- this.shadowOffsetY = this._shadowOffsetY = 0;
- this.shadowBlur = this._shadowBlur = 0;
- this.shadowColor = this._shadowColor = "rgba(0, 0, 0, 0.0)";
- // text
- this.font = this._font = "10px sans-serif";
- this.textAlign = this._textAlign = "start";
- this.textBaseline = this._textBaseline = "alphabetic";
- // command queue
- this._queue = [];
- // stack of drawing states
- this._stateStack = [];
- },
- _flush: function() {
- var queue = this._queue;
- this._queue = [];
- return queue;
- },
- _executeCommand: function() {
- // execute commands
- var commands = this._flush();
- if (commands.length > 0) {
- try {
- return eval( this._swf.CallFunction(
- '<invoke name="executeCommand" returntype="javascript"><arguments><string>'
- + commands.join("�") + "</string></arguments></invoke>"
- ))
- } catch (ex) {
- }
- }
- },
- _resize: function(width, height) {
- // Flush commands in the queue
- this._executeCommand();
- // Clear back to the initial state
- this._initialize();
- // Adjust the size of Flash to that of the canvas
- if (width > 0) {
- this._swf.width = width;
- }
- if (height > 0) {
- this._swf.height = height;
- }
- // Execute a resize command at the start of the next frame
- this._queue.push(properties.resize, width, height);
- }
- };
- /**
- * CanvasGradient stub
- * @constructor
- */
- var CanvasGradient = function(ctx) {
- this._ctx = ctx;
- this.id = ctx._gradientPatternId++;
- };
- CanvasGradient.prototype = {
- addColorStop: function(offset, color) {
- // Throws an INDEX_SIZE_ERR exception if the offset is out of range.
- if (isNaN(offset) || offset < 0 || offset > 1) {
- throwException(INDEX_SIZE_ERR);
- }
- this._ctx._queue.push(properties.addColorStop, this.id, offset, color);
- }
- };
- /**
- * CanvasPattern stub
- * @constructor
- */
- var CanvasPattern = function(ctx) {
- this.id = ctx._gradientPatternId++;
- };
- /**
- * TextMetrics stub
- * @constructor
- */
- var TextMetrics = function(width) {
- this.width = width;
- };
- /**
- * DOMException
- * @constructor
- */
- var DOMException = function(code) {
- var DOMExceptionNames = {
- 1: "INDEX_SIZE_ERR",
- 9: "NOT_SUPPORTED_ERR",
- 11: "INVALID_STATE_ERR",
- 12: "SYNTAX_ERR",
- 17: "TYPE_MISMATCH_ERR",
- 18: "SECURITY_ERR"
- }
- this.code = code;
- this.message = DOMExceptionNames[code];
- };
- DOMException.prototype = new Error;
- /*
- * Event handlers
- */
- /*
- * FlashCanvas global object API (not the Canvas API, just initializer etc.)
- */
- /**
- Generates a URL pointing to fashcanvas.swf file by inspecing constants and Window-specific
- settings and deriving the path appropirate for that Window.
- @public
- @function
- @param window {Object} Pointer to Window (top, child frames) object instance into which we will dig.
- @returns {String} relative or absolute path to the swf file.
- */
- function getSwfUrl(window) {
- return ( (window[FLASH_CANVAS + "Options"] || {})["swfPath"] || BASE_URL ) + "flashcanvas.swf"
- }
- var registeredEvents = 'registeredEvents'
- , canvasesProp = 'canvases'
- , initWindow = 'initWindow'
- , initElement = 'initElement'
- , saveImage = 'saveImage'
- , unlock = 'unlock'
- , trigger = 'trigger'
- var FlashCanvas = {}
- FlashCanvas[registeredEvents] = {} // 'canvasID':[[eventName, handler],...]
- FlashCanvas[canvasesProp] = {}
- FlashCanvas[initWindow] = function(window){
- var document = window.document
- // IE HTML5 shiv
- document.createElement(CANVAS);
- // setup default CSS
- document.createStyleSheet().cssText =
- CANVAS + "{display:inline-block;overflow:hidden;width:300px;height:150px}";
- var canvases = this[canvasesProp]
- var registeredEvents = this.registeredEvents
- var onUnload = function() {
- window.detachEvent(ON_UNLOAD, onUnload);
- var canvas
- , swf
- , prop
- , NULL = null
- , parentWindow
- , i, l, e
- for (var canvasId in canvases) {
- canvas = canvases[canvasId]
- swf = canvas.firstChild
- parentWindow = canvas.ownerDocument.defaultView ? canvas.ownerDocument.defaultView : canvas.ownerDocument.parentWindow
- // parent frame may be handling canvas elemns in self and in children frames. We only kill
- // the canvases in "windows" that "unloaded"
- if (window === parentWindow) {
- // clean up the references of swf.executeCommand and swf.resize
- for (prop in swf) {
- if (typeof swf[prop] === "function") {
- swf[prop] = NULL;
- }
- }
- // clean up the references of canvas.getContext and canvas.toDataURL
- for (prop in canvas) {
- if (typeof canvas[prop] === "function") {
- canvas[prop] = NULL;
- }
- }
- i = 0
- l = registeredEvents[canvasId].length
- for (; i !== l; i++) {
- e = registeredEvents[canvasId][i] // it's an array: [eventName, eventHandler]
- swf.detachEvent(e[0], e[1]);
- canvas.detachEvent(e[0], e[1]);
- }
- }
- }
- // delete exported symbols
- window[CANVAS_RENDERING_CONTEXT_2D] = NULL;
- window[CANVAS_GRADIENT] = NULL;
- window[CANVAS_PATTERN] = NULL;
- window[FLASH_CANVAS] = NULL;
- }
- // prevent IE6 memory leaks
- window.attachEvent(ON_UNLOAD, onUnload);
- window[CANVAS_RENDERING_CONTEXT_2D] = CanvasRenderingContext2D;
- window[CANVAS_GRADIENT] = CanvasGradient;
- window[CANVAS_PATTERN] = CanvasPattern;
- window[FLASH_CANVAS] = FlashCanvas;
- // preload SWF file if it's in the same domain
- var swfUrl = getSwfUrl(window)
- if (swfUrl.indexOf(window.location.protocol + "//" + window.location.host + "/") === 0) {
- window.setTimeout(function(){
- var req = new ActiveXObject("Microsoft.XMLHTTP");
- req.open("GET", swfUrl, false);
- req.send(NULL);
- }, 0)
- }
- function onReadyStateChange() {
- if (window.document.readyState === "complete") {
- window.document.detachEvent(ON_READY_STATE_CHANGE, onReadyStateChange);
- var canvases = window.document.getElementsByTagName(CANVAS);
- for (var i = 0, n = canvases.length; i < n; ++i) {
- FlashCanvas[initElement](canvases[i]);
- }
- }
- }
- // initialize canvas elements
- if (window.document.readyState === "complete") {
- onReadyStateChange();
- } else {
- window.document.attachEvent(ON_READY_STATE_CHANGE, onReadyStateChange);
- }
- }
- FlashCanvas[initElement] = function(canvas) {
- // Check whether the initialization is required or not.
- if (canvas.getContext) {
- return canvas;
- }
- // when init is called from parent frame over canvas sitting in child frame,
- // FlashCanvas does not pick up the right "window" or "document" - the one from child frame.
- // to avoid making the users specify window, document, we sniff them out from canvas element.
- var document = canvas.ownerDocument
- , window = document.defaultView ? document.defaultView : document.parentWindow
- if (!window[CANVAS_RENDERING_CONTEXT_2D]) {
- // this may happen when FlashCanvas.initElement is called from parent fram on a canvas in child frame
- // child frame's `window` will not have the canvas methods
- this[initWindow](window)
- }
- // initialize lock
- var canvasId = getUniqueId();
- var objectId = OBJECT_ID_PREFIX + canvasId;
- isReady[canvasId] = false;
- images[canvasId] = {};
- lock[canvasId] = 1;
- callbacks[canvasId] = {};
- this.registeredEvents[canvasId] = []
- // Set the width and height attributes.
- setCanvasSize(canvas);
- var swfUrl = getSwfUrl(window)
- // on iframes with src = 'about:blank' location.protocol is "about:"
- // so, let's not go crafty nuts about this:
- var protocol = window.location.protocol === 'https:' ? 'https:' : 'http:'
- // embed swf and SPAN element
- canvas.innerHTML =
- '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"' +
- ' codebase="' + protocol + '//fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0"' +
- ' width="100%" height="100%" id="' + objectId + '">' +
- '<param name="allowScriptAccess" value="always">' +
- '<param name="flashvars" value="id=' + objectId + '">' +
- '<param name="wmode" value="transparent">' +
- // '<param name="movie" value="'+swfUrl+'">'
- '</object>' +
- '<span style="margin:0;padding:0;border:0;display:inline-block;position:static;height:1em;overflow:visible;white-space:nowrap">' +
- '</span>';
- this[canvasesProp][canvasId] = canvas;
- var swf = canvas.firstChild;
- spans[canvasId] = canvas.lastChild;
- // Check whether the canvas element is in the DOM tree
- var documentContains = document.body.contains;
- if (documentContains(canvas)) {
- // Load swf file immediately
- swf["movie"] = swfUrl;
- } else {
- // Wait until the element is added to the DOM tree
- var intervalId = window.setInterval(function() {
- if (documentContains(canvas)) {
- window.clearInterval(intervalId);
- swf["movie"] = swfUrl;
- }
- }, 2);
- }
- // If the browser is IE6 or in quirks mode
- if (document.compatMode === "BackCompat" || !window.XMLHttpRequest) {
- spans[canvasId].style.overflow = "hidden";
- }
- // initialize context
- var ctx = new CanvasRenderingContext2D(canvas, swf);
- // canvas API
- canvas.getContext = function(contextId) {
- return contextId === "2d" ? ctx : NULL;
- };
- canvas.toDataURL = function(type, quality) {
- if (("" + type).toLowerCase() === "image/jpeg") {
- ctx._queue.push(
- properties.toDataURL
- , type
- , typeof quality === "number" ? quality : ""
- )
- } else {
- ctx._queue.push(properties.toDataURL, type);
- }
- return ctx._executeCommand();
- };
- // the events handler functions are declared within initElement because
- // when it is inited against an iframe, the "window" object points
- // elswhere. Thus, we create new set of event handlers for each "window"
- // In other words, "window" below is preset.
- // forward the event to the parent
- var onFocus = function(e) {
- var swf = e ? e.srcElement : window.event.srcElement
- , canvas = swf.parentNode
- swf.blur();
- canvas.focus();
- }
- this.registeredEvents[canvasId].push(
- [ON_FOCUS, onFocus]
- )
- // add event listener
- swf.attachEvent(ON_FOCUS, onFocus);
- return canvas;
- }
- FlashCanvas[saveImage] = function(canvas) {
- var swf = canvas.firstChild;
- swf[saveImage]();
- }
- FlashCanvas.setOptions = function(options) {
- // TODO: Implement
- }
- FlashCanvas[trigger] = function(canvasId, type) {
- var canvas = this[canvasesProp][canvasId];
- canvas.fireEvent("on" + type);
- }
- FlashCanvas[unlock] = function(canvasId, url, error) {
- try {
-
- var canvas, swf, width, height;
- var _callback, image, callback;
- var document, window
- // If Flash becomes ready
- if (url === undefined) {
- canvas = this[canvasesProp][canvasId];
- swf = canvas.firstChild;
- // when init is called from parent frame over canvas sitting in child frame,
- // FlashCanvas does not pick up the right "window" or "document" - the one from child frame.
- // to avoid making the users specify window, document, we sniff them out from canvas element.
- document = canvas.ownerDocument
- window = document.defaultView ? document.defaultView : document.parentWindow
- // Set the width and height attributes of the canvas element.
- setCanvasSize(canvas);
- width = canvas.width;
- height = canvas.height;
- canvas.style.width = width + "px";
- canvas.style.height = height + "px";
- // Adjust the size of Flash to that of the canvas
- if (width > 0) {
- swf.width = width;
- }
- if (height > 0) {
- swf.height = height;
- }
- swf.resize(width, height);
- // the events handler functions are declared within initElement because
- // when it is inited against an iframe, the "window" object points
- // elswhere. Thus, we create new set of event handlers for each "window"
- // In other words, "window" below is NOT resolved runtime. It's preset.
- var onPropertyChange = function(e) {
- var e = e ? e : window.event
- , prop = e.propertyName
- if (prop === "width" || prop === "height") {
- var canvas = e.srcElement;
- var value = canvas[prop];
- var number = parseInt(value, 10);
- if (isNaN(number) || number < 0) {
- number = (prop === "width") ? 300 : 150;
- }
- if (value === number) {
- canvas.style[prop] = number + "px";
- canvas.getContext("2d")._resize(canvas.width, canvas.height);
- } else {
- canvas[prop] = number;
- }
- }
- }
- this.registeredEvents[canvasId].push(
- [ON_PROPERTY_CHANGE, onPropertyChange]
- )
- // Add event listener
- canvas.attachEvent(ON_PROPERTY_CHANGE, onPropertyChange);
- // ExternalInterface is now ready for use
- isReady[canvasId] = true;
- // Call the onload event handler
- if (typeof canvas.onload === "function") {
- window.setTimeout(function() {
- canvas.onload();
- }, 0);
- }
- }
- // If callback functions were defined
- else if (_callback = callbacks[canvasId][url]) {
- image = _callback[0];
- callback = _callback[1 + error];
- delete callbacks[canvasId][url];
- // Call the onload or onerror callback function.
- if (typeof callback === "function") {
- callback.call(image);
- }
- }
- if (lock[canvasId]) {
- --lock[canvasId];
- }
- } catch (ex) {
- // .unlock is called from within try catch inside flash. We never see errors if we don't
- // capture and display them.
- console.log("Call to FlashCanvas.unlock had thrown an error: ", ex.message)
- throw ex
- }
- }
- /*
- * Utility methods
- */
- // Get a unique ID composed of alphanumeric characters.
- function getUniqueId() {
- return Math.random().toString(36).slice(2) || "0";
- }
- // Escape characters not permitted in XML.
- function encodeXML(str) {
- return ("" + str).replace(/&/g, "&").replace(/</g, "<");
- }
- function throwException(code) {
- throw new DOMException(code);
- }
- // The width and height attributes of a canvas element must have values that
- // are valid non-negative integers.
- function setCanvasSize(canvas) {
- var width = parseInt(canvas.width, 10);
- var height = parseInt(canvas.height, 10);
- if (isNaN(width) || width < 0) {
- width = 300;
- }
- if (isNaN(height) || height < 0) {
- height = 150;
- }
- canvas.width = width;
- canvas.height = height;
- }
- /*
- * initialization
- */
- FlashCanvas.initWindow(window, document)
- // Prevent Closure Compiler from removing the function.
- keep = [
- CanvasRenderingContext2D.measureText,
- CanvasRenderingContext2D.loadImage
- ];
- }).call(window);
- }
|