Add OK and Cancel button for grid editing
[phpmyadmin/arisferyanto.git] / js / canvg / flashcanvas.js
blob6ecaa942aef03ea649b483d5894da92e2b898458
1 /*
2 * FlashCanvas
4 * Copyright (c) 2009 Tim Cameron Ryan
5 * Copyright (c) 2009-2011 FlashCanvas Project
6 * Released under the MIT/X License
7 */
9 // Reference:
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) {
19 * Constant
22 var NULL = null;
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";
39 // DOMException code
40 var INDEX_SIZE_ERR = 1;
41 var NOT_SUPPORTED_ERR = 9;
42 var INVALID_STATE_ERR = 11;
43 var SYNTAX_ERR = 12;
44 var TYPE_MISMATCH_ERR = 17;
45 var SECURITY_ERR = 18;
47 /**
48 * @constructor
50 function Lookup(array) {
51 for (var i = 0, n = array.length; i < n; i++)
52 this[array[i]] = i;
55 var properties = new Lookup([
56 // Canvas element
57 "toDataURL",
59 // CanvasRenderingContext2D
60 "save",
61 "restore",
62 "scale",
63 "rotate",
64 "translate",
65 "transform",
66 "setTransform",
67 "globalAlpha",
68 "globalCompositeOperation",
69 "strokeStyle",
70 "fillStyle",
71 "createLinearGradient",
72 "createRadialGradient",
73 "createPattern",
74 "lineWidth",
75 "lineCap",
76 "lineJoin",
77 "miterLimit",
78 "shadowOffsetX",
79 "shadowOffsetY",
80 "shadowBlur",
81 "shadowColor",
82 "clearRect",
83 "fillRect",
84 "strokeRect",
85 "beginPath",
86 "closePath",
87 "moveTo",
88 "lineTo",
89 "quadraticCurveTo",
90 "bezierCurveTo",
91 "arcTo",
92 "rect",
93 "arc",
94 "fill",
95 "stroke",
96 "clip",
97 "isPointInPath",
98 // "drawFocusRing",
99 "font",
100 "textAlign",
101 "textBaseline",
102 "fillText",
103 "strokeText",
104 "measureText",
105 "drawImage",
106 "createImageData",
107 "getImageData",
108 "putImageData",
110 // CanvasGradient
111 "addColorStop",
113 // Internal use
114 "direction",
115 "resize"
118 // Whether swf is ready for use
119 var isReady = {};
121 // Monitor the number of loading files
122 var lock = {};
124 // Canvas elements
125 var canvases = {};
127 // SPAN element embedded in the canvas
128 var spans = {};
131 * 2D context
132 * @constructor
134 var CanvasRenderingContext2D = function(canvas, swf) {
135 // back-reference to the canvas
136 this.canvas = canvas;
138 // back-reference to the swf
139 this._swf = swf;
141 // unique ID of canvas
142 this._canvasId = swf.id.slice(8);
144 // initialize drawing states
145 this._initialize();
147 // Count CanvasGradient and CanvasPattern objects
148 this._gradientPatternId = 0;
150 // Directionality of the canvas element
151 this._direction = "";
153 // frame update interval
154 var self = this;
155 setInterval(function() {
156 if (lock[self._canvasId] === 0) {
157 self._executeCommand();
159 }, 30);
162 CanvasRenderingContext2D.prototype = {
164 * state
167 save: function() {
168 // write all properties
169 this._setCompositing();
170 this._setShadows();
171 this._setStrokeStyle();
172 this._setFillStyle();
173 this._setLineStyles();
174 this._setFontStyles();
176 // push state
177 this._stateStack.push([
178 this._globalAlpha,
179 this._globalCompositeOperation,
180 this._strokeStyle,
181 this._fillStyle,
182 this._lineWidth,
183 this._lineCap,
184 this._lineJoin,
185 this._miterLimit,
186 this._shadowOffsetX,
187 this._shadowOffsetY,
188 this._shadowBlur,
189 this._shadowColor,
190 this._font,
191 this._textAlign,
192 this._textBaseline
195 this._queue.push(properties.save);
198 restore: function() {
199 // pop state
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);
224 * transformations
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);
248 * compositing
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);
264 * colors and styles
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
301 // exception.
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.
313 if (!image) {
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.
322 if (tagName) {
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.
328 return;
329 } else {
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) {
337 src = image.src;
338 } else {
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();
355 ++lock[canvasId];
358 return new CanvasPattern(this);
362 * line caps/joins
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);
386 * shadows
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);
410 * rects
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();
419 this._setShadows();
420 this._setFillStyle();
421 this._queue.push(properties.fillRect, x, y, w, h);
424 strokeRect: function(x, y, w, h) {
425 this._setCompositing();
426 this._setShadows();
427 this._setStrokeStyle();
428 this._setLineStyles();
429 this._queue.push(properties.strokeRect, x, y, w, h);
433 * path API
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);
482 fill: function() {
483 this._setCompositing();
484 this._setShadows();
485 this._setFillStyle();
486 this._queue.push(properties.fill);
489 stroke: function() {
490 this._setCompositing();
491 this._setShadows();
492 this._setStrokeStyle();
493 this._setLineStyles();
494 this._queue.push(properties.stroke);
497 clip: function() {
498 this._queue.push(properties.clip);
501 isPointInPath: function(x, y) {
502 // TODO: Implement
506 * text
509 _setFontStyles: function() {
510 var queue = this._queue;
511 if (this._font !== this.font) {
512 try {
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);
520 } catch(e) {
521 // If this.font cannot be parsed as a CSS font value, then it
522 // must be ignored.
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();
542 this._setShadows();
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();
551 this._setShadows();
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];
559 try {
560 span.style.font = this.font;
561 } catch(e) {
562 // If this.font cannot be parsed as a CSS font value, then it must
563 // be ignored.
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);
574 * drawing images
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.
580 if (!image) {
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.
589 if (tagName) {
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.
595 return;
596 } else {
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) {
604 src = image.src;
605 } else {
606 throwException(TYPE_MISMATCH_ERR);
609 this._setCompositing();
610 this._setShadows();
612 // Special characters in the filename need escaping.
613 src = encodeXML(src);
615 if (argc === 3) {
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);
627 } else {
628 return;
631 if (isReady[canvasId]) {
632 this._executeCommand();
633 ++lock[canvasId];
638 * pixel manipulation
641 // ImageData createImageData(in float sw, in float sh);
642 // ImageData createImageData(in ImageData imagedata);
643 createImageData: function() {
644 // TODO: Implement
647 // ImageData getImageData(in float sx, in float sy, in float sw, in float sh);
648 getImageData: function(sx, sy, sw, sh) {
649 // TODO: Implement
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) {
654 // TODO: Implement
658 * private methods
661 _initialize: function() {
662 // compositing
663 this.globalAlpha = this._globalAlpha = 1.0;
664 this.globalCompositeOperation = this._globalCompositeOperation = "source-over";
666 // colors and styles
667 this.strokeStyle = this._strokeStyle = "#000000";
668 this.fillStyle = this._fillStyle = "#000000";
670 // line caps/joins
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;
676 // shadows
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)";
682 // text
683 this.font = this._font = "10px sans-serif";
684 this.textAlign = this._textAlign = "start";
685 this.textBaseline = this._textBaseline = "alphabetic";
687 // command queue
688 this._queue = [];
690 // stack of drawing states
691 this._stateStack = [];
694 _flush: function() {
695 var queue = this._queue;
696 this._queue = [];
697 return queue;
700 _executeCommand: function() {
701 // execute commands
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("&#0;") + "</string></arguments></invoke>"
711 _resize: function(width, height) {
712 // Flush commands in the queue
713 this._executeCommand();
715 // Clear back to the initial state
716 this._initialize();
718 // Adjust the size of Flash to that of the canvas
719 if (width > 0) {
720 this._swf.width = width;
722 if (height > 0) {
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
733 * @constructor
735 var CanvasGradient = function(ctx) {
736 this._ctx = 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);
752 * CanvasPattern stub
753 * @constructor
755 var CanvasPattern = function(ctx) {
756 this.id = ctx._gradientPatternId++;
760 * TextMetrics stub
761 * @constructor
763 var TextMetrics = function(width) {
764 this.width = width;
768 * DOMException
769 * @constructor
771 var DOMException = function(code) {
772 this.code = code;
773 this.message = DOMExceptionNames[code];
776 DOMException.prototype = new Error;
778 var DOMExceptionNames = {
779 1: "INDEX_SIZE_ERR",
780 9: "NOT_SUPPORTED_ERR",
781 11: "INVALID_STATE_ERR",
782 12: "SYNTAX_ERR",
783 17: "TYPE_MISMATCH_ERR",
784 18: "SECURITY_ERR"
788 * Event handlers
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]);
802 function onFocus() {
803 // forward the event to the parent
804 var swf = event.srcElement, canvas = swf.parentNode;
805 swf.blur();
806 canvas.focus();
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);
823 } else {
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
836 for (prop in swf) {
837 if (typeof swf[prop] === "function") {
838 swf[prop] = NULL;
842 // clean up the references of canvas.getContext and canvas.toDataURL
843 for (prop in canvas) {
844 if (typeof canvas[prop] === "function") {
845 canvas[prop] = NULL;
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;
863 * FlashCanvas API
866 var FlashCanvas = {
867 initElement: function(canvas) {
868 // Check whether the initialization is required or not.
869 if (canvas.getContext) {
870 return canvas;
873 // initialize lock
874 var canvasId = getUniqueId();
875 var objectId = OBJECT_ID_PREFIX + canvasId;
876 isReady[canvasId] = false;
877 lock[canvasId] = 1;
879 // Set the width and height attributes.
880 setCanvasSize(canvas);
882 // embed swf and SPAN element
883 canvas.innerHTML =
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">' +
890 '</object>' +
891 '<span style="margin:0;padding:0;border:0;display:inline-block;position:static;height:1em;overflow:visible;white-space:nowrap">' +
892 '</span>';
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;
903 } else {
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;
910 }, 0);
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);
921 // canvas API
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 : "");
930 } else {
931 ctx._queue.push(properties.toDataURL, type);
933 return ctx._executeCommand();
936 // add event listener
937 swf.attachEvent(ON_FOCUS, onFocus);
939 return canvas;
942 saveImage: function(canvas) {
943 var swf = canvas.firstChild;
944 swf.saveImage();
947 setOptions: function(options) {
948 // TODO: Implement
951 trigger: function(canvasId, type) {
952 var canvas = canvases[canvasId];
953 canvas.fireEvent("on" + type);
956 unlock: function(canvasId, ready) {
957 if (lock[canvasId]) {
958 --lock[canvasId];
960 if (ready) {
961 var canvas = canvases[canvasId];
962 var swf = canvas.firstChild;
963 var width;
964 var height;
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
975 if (width > 0) {
976 swf.width = width;
978 if (height > 0) {
979 swf.height = height;
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;
993 * Utility methods
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) {
1003 return script.src;
1004 } else {
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, "&amp;").replace(/</g, "&lt;");
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) {
1034 width = 300;
1036 if (isNaN(height) || height < 0) {
1037 height = 150;
1040 canvas.width = width;
1041 canvas.height = height;
1045 * initialization
1048 // IE HTML5 shiv
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();
1058 } else {
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);
1069 req.send(NULL);
1073 * public API
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] = {
1083 init: function(){},
1084 init_: function(){},
1085 initElement: FlashCanvas.initElement
1088 // Prevent Closure Compiler from removing the function.
1089 keep = CanvasRenderingContext2D.measureText;
1091 })(window, document);