├── .gitignore ├── GifTest ├── .settings │ └── org.eclipse.core.resources.prefs ├── html-template │ ├── playerProductInstall.swf │ ├── history │ │ ├── history.css │ │ ├── historyFrame.html │ │ └── history.js │ ├── index.template.html │ └── swfobject.js ├── .flexProperties ├── .project ├── .actionScriptProperties └── src │ └── GifTest.mxml ├── AS3GifPlayer ├── .settings │ └── org.eclipse.core.resources.prefs ├── .project ├── src │ └── com │ │ └── worlize │ │ └── gif │ │ ├── constants │ │ ├── GifVersion.as │ │ ├── DisposalType.as │ │ ├── BlockType.as │ │ └── DefaultPalette.as │ │ ├── errors │ │ ├── FileTypeError.as │ │ └── OutOfDataError.as │ │ ├── blocks │ │ ├── IGIFBlockCodec.as │ │ ├── TrailerBlock.as │ │ ├── UnknownExtension.as │ │ ├── CommentExtension.as │ │ ├── ImageDataBlock.as │ │ ├── PlainTextExtension.as │ │ ├── HeaderBlock.as │ │ ├── NetscapeExtension.as │ │ ├── ColorTableBlock.as │ │ ├── ApplicationExtension.as │ │ ├── GraphicControlExtension.as │ │ ├── ImageDescriptorBlock.as │ │ ├── LogicalScreenDescriptorBlock.as │ │ └── DataBlock.as │ │ ├── events │ │ ├── GIFDecoderEvent.as │ │ ├── GIFPlayerEvent.as │ │ └── AsyncDecodeErrorEvent.as │ │ ├── GIFFrame.as │ │ ├── GIFPlayer.as │ │ └── GIFDecoder.as ├── .actionScriptProperties └── .flexLibProperties ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin-release 3 | bin-debug 4 | AS3GifPlayer/bin 5 | *.swc 6 | -------------------------------------------------------------------------------- /GifTest/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | #Mon Jan 02 20:21:32 PST 2012 2 | eclipse.preferences.version=1 3 | encoding/=utf-8 4 | -------------------------------------------------------------------------------- /AS3GifPlayer/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | #Wed Jan 04 02:32:22 PST 2012 2 | eclipse.preferences.version=1 3 | encoding/=utf-8 4 | -------------------------------------------------------------------------------- /GifTest/html-template/playerProductInstall.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theturtle32/Flash-Animated-GIF-Library/HEAD/GifTest/html-template/playerProductInstall.swf -------------------------------------------------------------------------------- /GifTest/.flexProperties: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /GifTest/html-template/history/history.css: -------------------------------------------------------------------------------- 1 | /* This CSS stylesheet defines styles used by required elements in a flex application page that supports browser history */ 2 | 3 | #ie_historyFrame { width: 0px; height: 0px; display:none } 4 | #firefox_anchorDiv { width: 0px; height: 0px; display:none } 5 | #safari_formDiv { width: 0px; height: 0px; display:none } 6 | #safari_rememberDiv { width: 0px; height: 0px; display:none } 7 | -------------------------------------------------------------------------------- /GifTest/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | GifTest 4 | 5 | 6 | 7 | 8 | 9 | com.adobe.flexbuilder.project.flexbuilder 10 | 11 | 12 | 13 | 14 | 15 | com.adobe.flexbuilder.project.flexnature 16 | com.adobe.flexbuilder.project.actionscriptnature 17 | 18 | 19 | -------------------------------------------------------------------------------- /AS3GifPlayer/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | AS3GifPlayer 4 | 5 | 6 | 7 | 8 | 9 | com.adobe.flexbuilder.project.flexbuilder 10 | 11 | 12 | 13 | 14 | 15 | com.adobe.flexbuilder.project.flexlibnature 16 | com.adobe.flexbuilder.project.actionscriptnature 17 | 18 | 19 | -------------------------------------------------------------------------------- /GifTest/html-template/history/historyFrame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 27 | Hidden frame for Browser History support. 28 | 29 | 30 | -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/constants/GifVersion.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.constants 18 | { 19 | public final class GifVersion 20 | { 21 | public static const VERSION_87A:String = "87a"; 22 | public static const VERSION_89A:String = "89a"; 23 | } 24 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/errors/FileTypeError.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.errors 18 | { 19 | public class FileTypeError extends Error 20 | { 21 | public function FileTypeError(message:*="", id:*=0) 22 | { 23 | super(message, id); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/errors/OutOfDataError.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.errors 18 | { 19 | public class OutOfDataError extends Error 20 | { 21 | public function OutOfDataError(message:*="", id:*=0) 22 | { 23 | super(message, id); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/constants/DisposalType.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.constants 18 | { 19 | public final class DisposalType 20 | { 21 | public static const NO_DISPOSAL:uint = 0; 22 | public static const DO_NOT_DISPOSE:uint = 1; 23 | public static const RESTORE_BACKGROUND_COLOR:uint = 2; 24 | public static const RESTORE_TO_PREVIOUS:uint = 3; 25 | } 26 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/IGIFBlockCodec.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import flash.utils.ByteArray; 20 | import flash.utils.IDataInput; 21 | import flash.utils.IDataOutput; 22 | 23 | public interface IGIFBlockCodec 24 | { 25 | function decode(stream:IDataInput):void; 26 | function encode(stream:ByteArray=null):ByteArray; 27 | function dispose():void; 28 | } 29 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/events/GIFDecoderEvent.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.events 18 | { 19 | import com.worlize.gif.GIFFrame; 20 | 21 | import flash.events.Event; 22 | 23 | public class GIFDecoderEvent extends Event 24 | { 25 | public static const DECODE_COMPLETE:String = "decodeComplete"; 26 | 27 | public function GIFDecoderEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false) 28 | { 29 | super(type, bubbles, cancelable); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/constants/BlockType.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.constants 18 | { 19 | public final class BlockType 20 | { 21 | public static const APPLICATION_EXT:uint = 0xFF; 22 | public static const COMMENT_EXT:uint = 0xFE; 23 | public static const EXTENSION:uint = 0x21; 24 | public static const IMAGE_DESCRIPTOR:uint = 0x2C; 25 | public static const GRAPHIC_CONTROL_EXT:uint = 0xF9; 26 | public static const PLAIN_TEXT_EXT:uint = 0x01; 27 | public static const TRAILER:uint = 0x3B; 28 | } 29 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a library that I had to write in order to facilitate animated avatars and room objects in Worlize. Haven't gotten around to writing any documentation. For a usage example, take a look at GifTest/src/GifTest.mxml and if you would like me to spend some time writing up documentation, vote for it by sending me a message! ;-) 2 | 3 | In my tests it is two orders of magnitude faster than Thibault Imbert's [AS3 Gif Player Class](http://www.bytearray.org/?p=95), and it manages to correctly render every valid GIF file I could find. Its parser is quite strict, however, so if you have a GIF that works in the browser but not with this library it probably means your browser is being overly lenient with the corrupt image data. 4 | 5 | The speed gains are achieved by specifically avoiding doing any pixel decoding at all and instead splitting and re-packaging each frame of the animation into its own freestanding gif file, handing the resulting single-frame GIF files to Flash to decode the frame's image data internally via the Loader class. 6 | 7 | Try the test app: 8 | [Try the test app](http://theturtle32.github.io/Flash-Animated-GIF-Library) 9 | 10 | Check out [Worlize](http://www.worlize.com) and try uploading an animated GIF as your avatar! :) 11 | 12 | This library is made available under the Apache License, Version 2.0 13 | -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/events/GIFPlayerEvent.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.events 18 | { 19 | import flash.events.Event; 20 | 21 | public class GIFPlayerEvent extends Event 22 | { 23 | public static const COMPLETE:String = "complete"; 24 | public static const FRAME_RENDERED:String = "frameRendered"; 25 | 26 | public var frameIndex:uint; 27 | 28 | public function GIFPlayerEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false) 29 | { 30 | super(type, bubbles, cancelable); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /AS3GifPlayer/.actionScriptProperties: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/TrailerBlock.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.constants.BlockType; 20 | 21 | import flash.utils.ByteArray; 22 | import flash.utils.Endian; 23 | import flash.utils.IDataInput; 24 | 25 | public class TrailerBlock implements IGIFBlockCodec 26 | { 27 | public function decode(stream:IDataInput):void { 28 | // No-op 29 | } 30 | 31 | public function encode(ba:ByteArray=null):ByteArray 32 | { 33 | if (ba === null) { 34 | ba = new ByteArray(); 35 | ba.endian = Endian.LITTLE_ENDIAN; 36 | } 37 | ba.writeByte(BlockType.TRAILER); 38 | 39 | return ba; 40 | } 41 | 42 | public function dispose():void { 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/events/AsyncDecodeErrorEvent.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.events 18 | { 19 | import flash.events.ErrorEvent; 20 | import flash.events.Event; 21 | 22 | public class AsyncDecodeErrorEvent extends ErrorEvent 23 | { 24 | public static const ASYNC_DECODE_ERROR:String = "asyncDecodeError"; 25 | 26 | public function AsyncDecodeErrorEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false, text:String="", id:int=0) 27 | { 28 | super(type, bubbles, cancelable, text, id); 29 | } 30 | 31 | override public function clone():Event { 32 | var event:AsyncDecodeErrorEvent = new AsyncDecodeErrorEvent(type, bubbles, cancelable, text, errorID); 33 | return event; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /GifTest/.actionScriptProperties: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/UnknownExtension.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.constants.BlockType; 20 | 21 | import flash.utils.ByteArray; 22 | import flash.utils.Endian; 23 | import flash.utils.IDataInput; 24 | 25 | public class UnknownExtension implements IGIFBlockCodec 26 | { 27 | public var extensionLabel:uint; 28 | public var bytes:ByteArray; 29 | 30 | public function decode(stream:IDataInput):void { 31 | bytes = DataBlock.decodeDataBlocks(stream); 32 | } 33 | 34 | public function encode(ba:ByteArray=null):ByteArray { 35 | if (ba === null) { 36 | ba = new ByteArray(); 37 | ba.endian = Endian.LITTLE_ENDIAN; 38 | } 39 | 40 | ba.writeByte(BlockType.EXTENSION); 41 | ba.writeByte(extensionLabel); 42 | ba.writeBytes(DataBlock.encodeDataBlocks(bytes)); 43 | 44 | return ba; 45 | } 46 | 47 | public function dispose():void { 48 | if (bytes) { 49 | bytes.clear(); 50 | bytes = null; 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/CommentExtension.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.constants.BlockType; 20 | 21 | import flash.utils.ByteArray; 22 | import flash.utils.Endian; 23 | import flash.utils.IDataInput; 24 | 25 | public class CommentExtension implements IGIFBlockCodec 26 | { 27 | public var text:String; 28 | 29 | public function decode(stream:IDataInput):void { 30 | var textBytes:ByteArray = DataBlock.decodeDataBlocks(stream); 31 | text = textBytes.readMultiByte(textBytes.length, 'ascii'); 32 | } 33 | 34 | public function encode(ba:ByteArray=null):ByteArray { 35 | if (ba === null) { 36 | ba = new ByteArray(); 37 | ba.endian = Endian.LITTLE_ENDIAN; 38 | } 39 | var textBytes:ByteArray = new ByteArray(); 40 | textBytes.writeMultiByte(text, 'ascii'); 41 | 42 | ba.writeByte(BlockType.EXTENSION); 43 | ba.writeByte(BlockType.COMMENT_EXT); 44 | ba.writeBytes(DataBlock.encodeDataBlocks(textBytes)); 45 | 46 | return ba; 47 | } 48 | 49 | public function dispose():void { 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /AS3GifPlayer/.flexLibProperties: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/ImageDataBlock.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.errors.FileTypeError; 20 | import com.worlize.gif.errors.OutOfDataError; 21 | 22 | import flash.utils.ByteArray; 23 | import flash.utils.Endian; 24 | import flash.utils.IDataInput; 25 | 26 | public class ImageDataBlock implements IGIFBlockCodec 27 | { 28 | public var bytes:ByteArray; 29 | 30 | public var lzwMinCodeSize:uint; 31 | 32 | public function decode(stream:IDataInput):void { 33 | if (stream.bytesAvailable < 1) { 34 | throw new OutOfDataError("Out of data while reading image data block LZW minimum code size."); 35 | } 36 | lzwMinCodeSize = stream.readUnsignedByte(); 37 | if (lzwMinCodeSize < 2 || lzwMinCodeSize > 8) { 38 | throw new FileTypeError("Invalid GIF Data: LZW minimum code size is out of bounds."); 39 | } 40 | try { 41 | bytes = DataBlock.decodeDataBlocks(stream); 42 | } 43 | catch (e:OutOfDataError) { 44 | throw new OutOfDataError("Out of data while reading image data."); 45 | } 46 | } 47 | 48 | public function encode(ba:ByteArray=null):ByteArray { 49 | if (ba === null) { 50 | ba = new ByteArray(); 51 | ba.endian = Endian.LITTLE_ENDIAN; 52 | } 53 | 54 | ba.writeByte(lzwMinCodeSize); 55 | ba.writeBytes(DataBlock.encodeDataBlocks(bytes)); 56 | 57 | return ba; 58 | } 59 | 60 | public function dispose():void { 61 | if (bytes) { 62 | bytes.clear(); 63 | bytes = null; 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/PlainTextExtension.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.constants.BlockType; 20 | import com.worlize.gif.errors.FileTypeError; 21 | 22 | import flash.utils.ByteArray; 23 | import flash.utils.Endian; 24 | import flash.utils.IDataInput; 25 | 26 | public class PlainTextExtension implements IGIFBlockCodec 27 | { 28 | public var text:String; 29 | public var metadataBlock:DataBlock; 30 | 31 | public function decode(stream:IDataInput):void { 32 | try { 33 | // Read metadata 34 | metadataBlock = new DataBlock(); 35 | metadataBlock.decode(stream); 36 | 37 | // Read actual text 38 | var textBytes:ByteArray = DataBlock.decodeDataBlocks(stream); 39 | } 40 | catch (e:FileTypeError) { 41 | throw new FileTypeError("Error while decoding a plain text block."); 42 | } 43 | 44 | // Decode ASCII text 45 | text = textBytes.readMultiByte(textBytes.length, "ascii"); 46 | } 47 | 48 | public function encode(ba:ByteArray=null):ByteArray { 49 | if (ba === null) { 50 | ba = new ByteArray(); 51 | ba.endian = Endian.LITTLE_ENDIAN; 52 | } 53 | 54 | ba.writeByte(BlockType.EXTENSION); 55 | ba.writeByte(BlockType.PLAIN_TEXT_EXT); 56 | 57 | // Write metadata block 58 | ba.writeBytes(metadataBlock.encode()); 59 | 60 | // Write actual text 61 | var textBytes:ByteArray = new ByteArray(); 62 | textBytes.writeMultiByte(text, "ascii"); 63 | ba.writeBytes(DataBlock.encodeDataBlocks(textBytes)); 64 | 65 | return ba; 66 | } 67 | 68 | public function dispose():void { 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/HeaderBlock.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.constants.GifVersion; 20 | import com.worlize.gif.errors.FileTypeError; 21 | import com.worlize.gif.errors.OutOfDataError; 22 | 23 | import flash.utils.ByteArray; 24 | import flash.utils.Endian; 25 | import flash.utils.IDataInput; 26 | 27 | public class HeaderBlock implements IGIFBlockCodec 28 | { 29 | public var bytes:ByteArray; 30 | 31 | public var identifier:String; 32 | public var version:String; 33 | 34 | public function decode(stream:IDataInput):void { 35 | if (stream.bytesAvailable < 6) { 36 | throw new OutOfDataError("Ran out of data while reading GIF header."); 37 | } 38 | 39 | bytes = new ByteArray(); 40 | stream.readBytes(bytes, 0, 6); 41 | 42 | identifier = bytes.readMultiByte(6, 'ascii'); 43 | 44 | if (identifier.slice(0,3) !== "GIF") { 45 | throw new FileTypeError("Invalid file type."); 46 | } 47 | 48 | version = identifier.slice(3); 49 | if (version !== GifVersion.VERSION_87A && version !== GifVersion.VERSION_89A) { 50 | throw new FileTypeError("Unsupported GIF version: " + version); 51 | } 52 | 53 | bytes.position = 0; 54 | } 55 | 56 | public function encode(ba:ByteArray=null):ByteArray { 57 | if (ba === null) { 58 | ba = new ByteArray(); 59 | ba.endian = Endian.LITTLE_ENDIAN; 60 | } 61 | ba.writeMultiByte('GIF', 'ascii'); 62 | if (['89a','87a'].indexOf(version) === -1) { 63 | throw new Error("Invalid GIF version specified."); 64 | } 65 | ba.writeMultiByte(version, 'ascii'); 66 | return ba; 67 | } 68 | 69 | public function dispose():void { 70 | if (bytes) { 71 | bytes.clear(); 72 | bytes = null; 73 | } 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/NetscapeExtension.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.errors.FileTypeError; 20 | 21 | import flash.utils.ByteArray; 22 | import flash.utils.Endian; 23 | import flash.utils.IDataInput; 24 | 25 | public class NetscapeExtension implements IGIFBlockCodec 26 | { 27 | public static const APP_IDENTIFIER:String = "NETSCAPE"; 28 | public static const APP_AUTH_CODE:String = "2.0"; 29 | 30 | protected var appExtension:ApplicationExtension; 31 | public var loopCount:uint = 0; 32 | 33 | function NetscapeExtension() { 34 | appExtension = new ApplicationExtension(); 35 | appExtension.appIdentifier = APP_IDENTIFIER; 36 | appExtension.appAuthCode = APP_AUTH_CODE; 37 | } 38 | 39 | public function decodeFromApplicationExtension(extension:ApplicationExtension):void { 40 | appExtension = extension; 41 | if (appExtension.appIdentifier !== APP_IDENTIFIER || 42 | appExtension.appAuthCode !== APP_AUTH_CODE) { 43 | throw new FileTypeError("Unknown application data block. Was expecting a NETSCAPE2.0 block."); 44 | } 45 | if (appExtension.bytes.readUnsignedByte() === 1) { 46 | loopCount = appExtension.bytes.readUnsignedShort(); 47 | } 48 | } 49 | 50 | public function decode(stream:IDataInput):void { 51 | var ext:ApplicationExtension = new ApplicationExtension(); 52 | ext.decode(stream); 53 | decodeFromApplicationExtension(ext); 54 | } 55 | 56 | public function encode(ba:ByteArray=null):ByteArray { 57 | var bytes:ByteArray; 58 | appExtension.bytes = bytes = new ByteArray(); 59 | bytes.endian = Endian.LITTLE_ENDIAN; 60 | bytes.writeByte(1); // Identify loop count parameter 61 | bytes.writeShort(loopCount); 62 | bytes.position = 0; 63 | return appExtension.encode(ba); 64 | } 65 | 66 | public function dispose():void { 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/ColorTableBlock.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.errors.OutOfDataError; 20 | 21 | import flash.utils.ByteArray; 22 | import flash.utils.Endian; 23 | import flash.utils.IDataInput; 24 | 25 | public class ColorTableBlock implements IGIFBlockCodec 26 | { 27 | public var numColors:uint; 28 | public var table:Vector.; 29 | 30 | protected var cachedEncodedBytes:ByteArray; 31 | 32 | public function decode(stream:IDataInput):void { 33 | var bytesToRead:int = 3 * numColors; 34 | 35 | if (stream.bytesAvailable < bytesToRead) { 36 | throw new OutOfDataError("Out of data while decoding color table"); 37 | } 38 | 39 | var ba:ByteArray = new ByteArray(); 40 | ba.writeByte(0); 41 | stream.readBytes(ba, 1, bytesToRead); 42 | 43 | table = new Vector.(256); // max size to avoid bounds checks 44 | 45 | ba.endian = Endian.BIG_ENDIAN; 46 | for (var i:uint = 0; i < numColors; i++) 47 | { 48 | ba.position -= 1; 49 | table[i] = ba.readUnsignedInt() | 0xFF000000; 50 | } 51 | } 52 | 53 | public function encode(ba:ByteArray=null):ByteArray { 54 | if (!cachedEncodedBytes) { 55 | cachedEncodedBytes = new ByteArray(); 56 | cachedEncodedBytes.endian = Endian.LITTLE_ENDIAN; 57 | for (var i:uint = 0; i < numColors; i++) { 58 | var c:uint = table[i]; 59 | cachedEncodedBytes.writeByte((c & 0x00FF0000) >> 16); // Red 60 | cachedEncodedBytes.writeByte((c & 0x0000FF00) >> 8); // Green 61 | cachedEncodedBytes.writeByte( c & 0x000000FF); // Blue 62 | } 63 | } 64 | if (ba === null) { 65 | ba = new ByteArray(); 66 | ba.endian = Endian.LITTLE_ENDIAN; 67 | } 68 | ba.writeBytes(cachedEncodedBytes); 69 | return ba; 70 | } 71 | 72 | public function dispose():void { 73 | if (cachedEncodedBytes) { 74 | cachedEncodedBytes.clear(); 75 | cachedEncodedBytes = null; 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/ApplicationExtension.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.constants.BlockType; 20 | 21 | import flash.utils.ByteArray; 22 | import flash.utils.Endian; 23 | import flash.utils.IDataInput; 24 | 25 | public class ApplicationExtension implements IGIFBlockCodec 26 | { 27 | public var appIdentifier:String; 28 | public var appAuthCode:String; 29 | public var bytes:ByteArray; 30 | 31 | public function decode(stream:IDataInput):void { 32 | // Read application identifier 33 | var identifierBlock:DataBlock = new DataBlock(); 34 | identifierBlock.decode(stream); 35 | 36 | // Decode application identifier 37 | appIdentifier = identifierBlock.bytes.readMultiByte(8, "ascii"); 38 | appAuthCode = identifierBlock.bytes.readMultiByte(3, "ascii"); 39 | 40 | // Read actual application data 41 | bytes = DataBlock.decodeDataBlocks(stream); 42 | } 43 | 44 | public function encode(ba:ByteArray=null):ByteArray { 45 | if (appIdentifier.length !== 8) { 46 | throw new Error("Application Extension field appIdentifier must be exactly 8 characters long."); 47 | } 48 | if (appAuthCode.length !== 3) { 49 | throw new Error("Application Extension field appAuthCode must be exactly 3 characters long."); 50 | } 51 | var identifierBlock:DataBlock = new DataBlock(); 52 | identifierBlock.bytes = new ByteArray(); 53 | identifierBlock.bytes.writeMultiByte(appIdentifier, "ascii"); 54 | identifierBlock.bytes.writeMultiByte(appAuthCode, "ascii"); 55 | 56 | if (ba === null) { 57 | ba = new ByteArray(); 58 | ba.endian = Endian.LITTLE_ENDIAN; 59 | } 60 | 61 | // Write out the data 62 | ba.writeByte(BlockType.EXTENSION); 63 | ba.writeByte(BlockType.APPLICATION_EXT); 64 | ba.writeBytes(identifierBlock.encode()); 65 | ba.writeBytes(DataBlock.encodeDataBlocks(bytes)); 66 | 67 | return ba; 68 | } 69 | 70 | public function dispose():void { 71 | if (bytes) { 72 | bytes.clear(); 73 | bytes = null; 74 | } 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/GIFFrame.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif 18 | { 19 | import com.worlize.gif.blocks.ColorTableBlock; 20 | import com.worlize.gif.blocks.ImageDescriptorBlock; 21 | import com.worlize.gif.events.AsyncDecodeErrorEvent; 22 | import com.worlize.gif.events.GIFDecoderEvent; 23 | 24 | import flash.display.Bitmap; 25 | import flash.display.BitmapData; 26 | import flash.display.Loader; 27 | import flash.events.Event; 28 | import flash.events.EventDispatcher; 29 | import flash.events.IEventDispatcher; 30 | import flash.events.IOErrorEvent; 31 | import flash.utils.ByteArray; 32 | 33 | [Event(name="asyncDecodeError",type="com.worlize.gif.events.AsyncDecodeErrorEvent")] 34 | [Event(name="decodeComplete",type="com.worlize.gif.events.GIFDecoderEvent")] 35 | public class GIFFrame extends EventDispatcher 36 | { 37 | public var bitmapData:BitmapData; 38 | public var gifData:ByteArray; 39 | 40 | public var top:uint; 41 | public var left:uint; 42 | public var width:uint; 43 | public var height:uint; 44 | public var backgroundColor:uint; 45 | public var backgroundColorIndex:uint; 46 | public var hasTransparency:Boolean; 47 | public var transparencyIndex:uint; 48 | public var delayMs:uint; 49 | public var disposalType:uint; 50 | public var frameNumber:uint; 51 | 52 | private var startTime:uint; 53 | private var loader:Loader; 54 | 55 | public function GIFFrame(target:IEventDispatcher=null) 56 | { 57 | super(target); 58 | } 59 | 60 | public function decode():void { 61 | if (loader) { 62 | // remove listeners 63 | } 64 | bitmapData = null; 65 | loader = new Loader(); 66 | loader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleLoaderComplete); 67 | loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, handleLoaderIOError); 68 | startTime = (new Date()).valueOf(); 69 | loader.loadBytes(gifData); 70 | } 71 | 72 | public function abortDecode():void { 73 | loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, handleLoaderComplete); 74 | loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, handleLoaderIOError); 75 | } 76 | 77 | protected function handleLoaderComplete(event:Event):void { 78 | var endTime:uint = (new Date()).valueOf(); 79 | gifData.clear(); 80 | bitmapData = (loader.content as Bitmap).bitmapData; 81 | dispatchEvent(new GIFDecoderEvent(GIFDecoderEvent.DECODE_COMPLETE)); 82 | } 83 | 84 | protected function handleLoaderIOError(event:IOErrorEvent):void { 85 | dispatchEvent(new AsyncDecodeErrorEvent(AsyncDecodeErrorEvent.ASYNC_DECODE_ERROR)); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/GraphicControlExtension.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.constants.BlockType; 20 | import com.worlize.gif.constants.DisposalType; 21 | import com.worlize.gif.errors.FileTypeError; 22 | import com.worlize.gif.errors.OutOfDataError; 23 | 24 | import flash.utils.ByteArray; 25 | import flash.utils.Endian; 26 | import flash.utils.IDataInput; 27 | 28 | public class GraphicControlExtension implements IGIFBlockCodec 29 | { 30 | private var reserved:uint = 0; 31 | public var disposalMethod:uint = DisposalType.RESTORE_BACKGROUND_COLOR; 32 | public var userInputExpected:Boolean = false; 33 | public var hasTransparency:Boolean = false; 34 | public var delayTime:uint = 0; 35 | public var transparencyIndex:uint = 0; 36 | 37 | // Decode expects the extension block header to already have been 38 | // read in previously. It begins reading the data block. 39 | public function decode(stream:IDataInput):void { 40 | if (stream.bytesAvailable < 6) { 41 | throw new OutOfDataError("Out of data while reading graphic control extension."); 42 | } 43 | if (stream.readUnsignedByte() !== 4) { // Fixed Block Size 44 | throw new FileTypeError("Graphic Control Extension has invalid size."); 45 | } 46 | packed = stream.readUnsignedByte(); 47 | delayTime = stream.readUnsignedShort(); 48 | transparencyIndex = stream.readUnsignedByte(); 49 | stream.readUnsignedByte(); // Block terminator 50 | } 51 | 52 | // Outputs the encoded extension data, INCLUDING the enclosing 53 | // extension block and the block terminator. 54 | public function encode(ba:ByteArray=null):ByteArray { 55 | if (ba === null) { 56 | ba = new ByteArray(); 57 | ba.endian = Endian.LITTLE_ENDIAN; 58 | } 59 | ba.writeByte(BlockType.EXTENSION); 60 | ba.writeByte(BlockType.GRAPHIC_CONTROL_EXT); 61 | ba.writeByte(4); // Block size 62 | ba.writeByte(packed); 63 | ba.writeShort(delayTime); 64 | ba.writeByte(transparencyIndex); 65 | ba.writeByte(0); // Block terminator 66 | return ba; 67 | } 68 | 69 | 70 | protected function set packed(byte:uint):void { 71 | reserved = (byte & 0xE0) >> 5; 72 | disposalMethod = (byte & 0x1C) >> 2; 73 | if (disposalMethod > 3) { 74 | throw new FileTypeError("Invalid disposal method: " + disposalMethod); 75 | } 76 | userInputExpected = (byte & 0x02) === 0x02; 77 | hasTransparency = (byte & 0x01) === 0x01; 78 | } 79 | 80 | protected function get packed():uint { 81 | var byte:uint = 0; 82 | byte |= (reserved & 0x03) << 5; 83 | byte |= (disposalMethod & 0x03) << 2; 84 | byte |= (userInputExpected ? 0x02 : 0x00); 85 | byte |= (hasTransparency ? 0x01 : 0x00); 86 | return byte; 87 | } 88 | 89 | public function dispose():void { 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/ImageDescriptorBlock.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.constants.BlockType; 20 | import com.worlize.gif.errors.OutOfDataError; 21 | 22 | import flash.utils.ByteArray; 23 | import flash.utils.Endian; 24 | import flash.utils.IDataInput; 25 | 26 | public class ImageDescriptorBlock implements IGIFBlockCodec 27 | { 28 | public var offsetLeft:uint; 29 | public var offsetTop:uint; 30 | public var width:uint; 31 | public var height:uint; 32 | public var haslct:Boolean; 33 | public var interlaced:Boolean; 34 | public var lctSorted:Boolean; 35 | public var reserved:uint; 36 | public var lctNumColors:uint; 37 | 38 | public function decode(stream:IDataInput):void { 39 | if (stream.bytesAvailable < 9) { 40 | throw new OutOfDataError("Out of data while reading image descriptor block."); 41 | } 42 | offsetLeft = stream.readUnsignedShort(); 43 | offsetTop = stream.readUnsignedShort(); 44 | width = stream.readUnsignedShort(); 45 | height = stream.readUnsignedShort(); 46 | packed = stream.readUnsignedByte(); 47 | } 48 | 49 | public function encode(ba:ByteArray=null):ByteArray { 50 | if (ba === null) { 51 | ba = new ByteArray(); 52 | ba.endian = Endian.LITTLE_ENDIAN; 53 | } 54 | 55 | ba.writeByte(BlockType.IMAGE_DESCRIPTOR); 56 | ba.writeShort(offsetLeft); 57 | ba.writeShort(offsetTop); 58 | ba.writeShort(width); 59 | ba.writeShort(height); 60 | ba.writeByte(packed); 61 | 62 | return ba; 63 | } 64 | 65 | public function set packed(byte:uint):void { 66 | haslct = Boolean(byte & 0x80); 67 | interlaced = Boolean(byte & 0x40); 68 | lctSorted = Boolean(byte & 0x20); 69 | reserved = (byte & 0x18) >> 3; 70 | lctNumColors = 2 << (byte & 0x07); 71 | } 72 | 73 | public function get packed():uint { 74 | var byte:uint = 0; 75 | if (haslct) { 76 | byte |= 0x80 77 | } 78 | if (interlaced) { 79 | byte |= 0x40; 80 | } 81 | if (lctSorted) { 82 | byte |= 0x20; 83 | } 84 | byte |= ((reserved & 0x03) << 3); 85 | 86 | switch (lctNumColors) { 87 | case 2: 88 | byte |= 0; 89 | break; 90 | case 4: 91 | byte |= 1; 92 | break; 93 | case 8: 94 | byte |= 2; 95 | break; 96 | case 16: 97 | byte |= 3; 98 | break; 99 | case 32: 100 | byte |= 4; 101 | break; 102 | case 64: 103 | byte |= 5; 104 | break; 105 | case 128: 106 | byte |= 6; 107 | break; 108 | case 256: 109 | byte |= 7; 110 | break; 111 | default: 112 | throw new Error("Invalid local color table size: " + lctNumColors); 113 | break; 114 | } 115 | 116 | return byte; 117 | } 118 | 119 | public function dispose():void { 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/LogicalScreenDescriptorBlock.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.errors.OutOfDataError; 20 | 21 | import flash.utils.ByteArray; 22 | import flash.utils.Endian; 23 | import flash.utils.IDataInput; 24 | 25 | public class LogicalScreenDescriptorBlock implements IGIFBlockCodec 26 | { 27 | public var width:uint = 0; 28 | public var height:uint = 0; 29 | public var hasgct:Boolean = false; 30 | public var gctColorResolution:uint = 0; 31 | public var gctSorted:Boolean = false; 32 | public var gctNumColors:uint = 0; 33 | public var backgroundColorIndex:uint = 0; 34 | public var pixelAspect:uint = 0; 35 | 36 | private var _packed:uint; 37 | 38 | public function decode(stream:IDataInput):void { 39 | if (stream.bytesAvailable < 7) { 40 | throw new OutOfDataError("Out of data while reading logical screen descriptor."); 41 | } 42 | 43 | // logical screen size 44 | width = stream.readUnsignedShort(); 45 | height = stream.readUnsignedShort(); 46 | packed = stream.readUnsignedByte(); 47 | backgroundColorIndex = stream.readUnsignedByte(); // background color index 48 | pixelAspect = stream.readUnsignedByte(); // pixel aspect ratio 49 | } 50 | 51 | public function encode(ba:ByteArray = null):ByteArray { 52 | if (ba === null) { 53 | ba = new ByteArray(); 54 | ba.endian = Endian.LITTLE_ENDIAN; 55 | } 56 | 57 | ba.writeShort(width); 58 | ba.writeShort(height); 59 | ba.writeByte(packed); 60 | ba.writeByte(backgroundColorIndex); 61 | ba.writeByte(pixelAspect); 62 | 63 | return ba; 64 | } 65 | 66 | // packed fields 67 | public function set packed(newValue:uint):void { 68 | hasgct = (newValue & 0x80) !== 0; // 1 : global color table flag 69 | gctColorResolution = (newValue & 0x70) >> 4; // 2-4 : color resolution 70 | gctSorted = (newValue & 0x08) !== 0; // 5 : gct sort flag 71 | gctNumColors = 2 << (newValue & 0x07); // 6-8 : gct size 72 | } 73 | 74 | public function get packed():uint { 75 | var byte:uint = 0; 76 | if (hasgct) { 77 | byte |= 0x80; 78 | } 79 | byte |= (gctColorResolution & 0x07) << 4; 80 | if (gctSorted) { 81 | byte |= 0x08; 82 | } 83 | switch (gctNumColors) { 84 | case 2: 85 | byte |= 0; 86 | break; 87 | case 4: 88 | byte |= 1; 89 | break; 90 | case 8: 91 | byte |= 2; 92 | break; 93 | case 16: 94 | byte |= 3; 95 | break; 96 | case 32: 97 | byte |= 4; 98 | break; 99 | case 64: 100 | byte |= 5; 101 | break; 102 | case 128: 103 | byte |= 6; 104 | break; 105 | case 256: 106 | byte |= 7; 107 | break; 108 | default: 109 | throw new Error("Invalid global color table size: " + gctNumColors); 110 | break; 111 | } 112 | 113 | return byte; 114 | } 115 | 116 | public function dispose():void { 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/blocks/DataBlock.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.blocks 18 | { 19 | import com.worlize.gif.errors.FileTypeError; 20 | import com.worlize.gif.errors.OutOfDataError; 21 | 22 | import flash.utils.ByteArray; 23 | import flash.utils.Endian; 24 | import flash.utils.IDataInput; 25 | import flash.utils.IDataOutput; 26 | 27 | public class DataBlock implements IGIFBlockCodec 28 | { 29 | public var blockSize:uint; 30 | public var bytes:ByteArray; 31 | public var isTerminator:Boolean = false; 32 | 33 | public static function decodeDataBlocks(stream:IDataInput):ByteArray { 34 | var ba:ByteArray = new ByteArray(); 35 | ba.endian = Endian.LITTLE_ENDIAN; 36 | var length:uint; 37 | do { 38 | if (stream.bytesAvailable === 0) { 39 | throw new OutOfDataError("Out of data while reading data block."); 40 | } 41 | length = stream.readUnsignedByte(); 42 | if (length > 0) { 43 | if (stream.bytesAvailable < length) { 44 | throw new OutOfDataError("Out of data while reading data block."); 45 | } 46 | stream.readBytes(ba, ba.length, length); 47 | } 48 | } 49 | while (length > 0); 50 | return ba; 51 | } 52 | 53 | public static function encodeDataBlocks(data:ByteArray):ByteArray { 54 | data.position = 0; 55 | 56 | var ba:ByteArray = new ByteArray(); 57 | ba.endian = Endian.LITTLE_ENDIAN; 58 | 59 | var pos:int = 0; 60 | var length:int = data.length; 61 | while (pos < length) { 62 | var bytesThisBlock:int = Math.min(length-pos, 0xFF); 63 | ba.writeByte(bytesThisBlock); 64 | ba.writeBytes(data, pos, bytesThisBlock); 65 | pos += bytesThisBlock; 66 | } 67 | ba.writeByte(0); // Block terminator 68 | return ba; 69 | } 70 | 71 | public function decode(stream:IDataInput):void { 72 | if (stream.bytesAvailable === 0) { 73 | throw new OutOfDataError("Out of data while reading data block."); 74 | } 75 | blockSize = stream.readUnsignedByte(); 76 | bytes = new ByteArray(); 77 | bytes.endian = Endian.LITTLE_ENDIAN; 78 | if (blockSize === 0) { 79 | isTerminator = true; 80 | return; 81 | } 82 | if (blockSize > 0) { 83 | if (stream.bytesAvailable < blockSize) { 84 | throw new OutOfDataError("Out of data while reading data block."); 85 | } 86 | stream.readBytes(bytes, 0, blockSize); 87 | } 88 | } 89 | 90 | public function encode(ba:ByteArray=null):ByteArray { 91 | if (ba === null) { 92 | ba = new ByteArray(); 93 | ba.endian = Endian.LITTLE_ENDIAN; 94 | } 95 | if (isTerminator) { 96 | ba.writeByte(0); // zero-length block is a terminator. 97 | return ba; 98 | } 99 | 100 | if (bytes.length > 0xFF) { 101 | throw new RangeError("Block size cannot exceed 255"); 102 | } 103 | ba.writeByte(bytes.length); 104 | ba.writeBytes(bytes, 0, bytes.length); 105 | return ba; 106 | } 107 | 108 | public function dispose():void { 109 | if (bytes) { 110 | bytes.clear(); 111 | bytes = null; 112 | } 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/constants/DefaultPalette.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif.constants 18 | { 19 | public final class DefaultPalette 20 | { 21 | public static const WINDOWS:Vector. = new [ 22 | 23 | 0xFF000000,0xFF800000,0xFF008000,0xFF808000, 24 | 0xFF000080,0xFF800080,0xFF008080,0xFFC0C0C0, 25 | 0xFFC0DCC0,0xFFA6CAF0,0xFF2A3FAA,0xFF2A3FFF, 26 | 0xFF2A5F00,0xFF2A5F55,0xFF2A5FAA,0xFF2A5FFF, 27 | 0xFF2A7F00,0xFF2A7F55,0xFF2A7FAA,0xFF2A7FFF, 28 | 0xFF2A9F00,0xFF2A9F55,0xFF2A9FAA,0xFF2A9FFF, 29 | 0xFF2ABF00,0xFF2ABF55,0xFF2ABFAA,0xFF2ABFFF, 30 | 0xFF2ADF00,0xFF2ADF55,0xFF2ADFAA,0xFF2ADFFF, 31 | 32 | 0xFF2AFF00,0xFF2AFF55,0xFF2AFFAA,0xFF2AFFFF, 33 | 0xFF550000,0xFF550055,0xFF5500AA,0xFF5500FF, 34 | 0xFF551F00,0xFF551F55,0xFF551FAA,0xFF551FFF, 35 | 0xFF553F00,0xFF553F55,0xFF553FAA,0xFF553FFF, 36 | 0xFF555F00,0xFF555F55,0xFF555FAA,0xFF555FFF, 37 | 0xFF557F00,0xFF557F55,0xFF557FAA,0xFF557FFF, 38 | 0xFF559F00,0xFF559F55,0xFF559FAA,0xFF559FFF, 39 | 0xFF55BF00,0xFF55BF55,0xFF55BFAA,0xFF55BFFF, 40 | 41 | 0xFF55DF00,0xFF55DF55,0xFF55DFAA,0xFF55DFFF, 42 | 0xFF55FF00,0xFF55FF55,0xFF55FFAA,0xFF55FFFF, 43 | 0xFF7F0000,0xFF7F0055,0xFF7F00AA,0xFF7F00FF, 44 | 0xFF7F1F00,0xFF7F1F55,0xFF7F1FAA,0xFF7F1FFF, 45 | 0xFF7F3F00,0xFF7F3F55,0xFF7F3FAA,0xFF7F3FFF, 46 | 0xFF7F5F00,0xFF7F5F55,0xFF7F5FAA,0xFF7F5FFF, 47 | 0xFF7F7F00,0xFF7F7F55,0xFF7F7FAA,0xFF7F7FFF, 48 | 0xFF7F9F00,0xFF7F9F55,0xFF7F9FAA,0xFF7F9FFF, 49 | 50 | 0xFF7FBF00,0xFF7FBF55,0xFF7FBFAA,0xFF7FBFFF, 51 | 0xFF7FDF00,0xFF7FDF55,0xFF7FDFAA,0xFF7FDFFF, 52 | 0xFF7FFF00,0xFF7FFF55,0xFF7FFFAA,0xFF7FFFFF, 53 | 0xFFAA0000,0xFFAA0055,0xFFAA00AA,0xFFAA00FF, 54 | 0xFFAA1F00,0xFFAA1F55,0xFFAA1FAA,0xFFAA1FFF, 55 | 0xFFAA3F00,0xFFAA3F55,0xFFAA3FAA,0xFFAA3FFF, 56 | 0xFFAA5F00,0xFFAA5F55,0xFFAA5FAA,0xFFAA5FFF, 57 | 0xFFAA7F00,0xFFAA7F55,0xFFAA7FAA,0xFFAA7FFF, 58 | 59 | 0xFFAA9F00,0xFFAA9F55,0xFFAA9FAA,0xFFAA9FFF, 60 | 0xFFAABF00,0xFFAABF55,0xFFAABFAA,0xFFAABFFF, 61 | 0xFFAADF00,0xFFAADF55,0xFFAADFAA,0xFFAADFFF, 62 | 0xFFAAFF00,0xFFAAFF55,0xFFAAFFAA,0xFFAAFFFF, 63 | 0xFFD40000,0xFFD40055,0xFFD400AA,0xFFD400FF, 64 | 0xFFD41F00,0xFFD41F55,0xFFD41FAA,0xFFD41FFF, 65 | 0xFFD43F00,0xFFD43F55,0xFFD43FAA,0xFFD43FFF, 66 | 0xFFD45F00,0xFFD45F55,0xFFD45FAA,0xFFD45FFF, 67 | 68 | 0xFFD47F00,0xFFD47F55,0xFFD47FAA,0xFFD47FFF, 69 | 0xFFD49F00,0xFFD49F55,0xFFD49FAA,0xFFD49FFF, 70 | 0xFFD4BF00,0xFFD4BF55,0xFFD4BFAA,0xFFD4BFFF, 71 | 0xFFD4DF00,0xFFD4DF55,0xFFD4DFAA,0xFFD4DFFF, 72 | 0xFFD4FF00,0xFFD4FF55,0xFFD4FFAA,0xFFD4FFFF, 73 | 0xFFFF0055,0xFFFF00AA,0xFFFF1F00,0xFFFF1F55, 74 | 0xFFFF1FAA,0xFFFF1FFF,0xFFFF3F00,0xFFFF3F55, 75 | 0xFFFF3FAA,0xFFFF3FFF,0xFFFF5F00,0xFFFF5F55, 76 | 77 | 0xFFFF5FAA,0xFFFF5FFF,0xFFFF7F00,0xFFFF7F55, 78 | 0xFFFF7FAA,0xFFFF7FFF,0xFFFF9F00,0xFFFF9F55, 79 | 0xFFFF9FAA,0xFFFF9FFF,0xFFFFBF00,0xFFFFBF55, 80 | 0xFFFFBFAA,0xFFFFBFFF,0xFFFFDF00,0xFFFFDF55, 81 | 0xFFFFDFAA,0xFFFFDFFF,0xFFFFFF55,0xFFFFFFAA, 82 | 0xFFCCCCFF,0xFFFFCCFF,0xFF33FFFF,0xFF66FFFF, 83 | 0xFF99FFFF,0xFFCCFFFF,0xFF007F00,0xFF007F55, 84 | 0xFF007FAA,0xFF007FFF,0xFF009F00,0xFF009F55, 85 | 86 | 0xFF009FAA,0xFF009FFF,0xFF00BF00,0xFF00BF55, 87 | 0xFF00BFAA,0xFF00BFFF,0xFF00DF00,0xFF00DF55, 88 | 0xFF00DFAA,0xFF00DFFF,0xFF00FF55,0xFF00FFAA, 89 | 0xFF2A0000,0xFF2A0055,0xFF2A00AA,0xFF2A00FF, 90 | 0xFF2A1F00,0xFF2A1F55,0xFF2A1FAA,0xFF2A1FFF, 91 | 0xFF2A3F00,0xFF2A3F55,0xFFFFFBF0,0xFFA0A0A4, 92 | 0xFF808080,0xFFFF0000,0xFF00FF00,0xFFFFFF00, 93 | 0xFF0000FF,0xFFFF00FF,0xFF00FFFF,0xFFFFFFFF 94 | 95 | ]; 96 | 97 | WINDOWS.fixed = true; 98 | } 99 | } -------------------------------------------------------------------------------- /GifTest/src/GifTest.mxml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /GifTest/html-template/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | ${title} 15 | 16 | 17 | 23 | 30 | 31 | 32 | 36 | 37 | 38 | 61 | 62 | 63 | 67 |
68 |

69 | To view this page ensure that Adobe Flash Player version 70 | ${version_major}.${version_minor}.${version_revision} or greater is installed. 71 |

72 | 77 |
78 | 79 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/GIFPlayer.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif 18 | { 19 | import com.worlize.gif.constants.DisposalType; 20 | import com.worlize.gif.events.AsyncDecodeErrorEvent; 21 | import com.worlize.gif.events.GIFDecoderEvent; 22 | import com.worlize.gif.events.GIFPlayerEvent; 23 | 24 | import flash.display.Bitmap; 25 | import flash.display.BitmapData; 26 | import flash.events.Event; 27 | import flash.events.TimerEvent; 28 | import flash.geom.Matrix; 29 | import flash.geom.Rectangle; 30 | import flash.utils.ByteArray; 31 | import flash.utils.Timer; 32 | 33 | [Event(name="complete",type="com.worlize.gif.events.GIFPlayerEvent")] 34 | [Event(name="frameRendered",type="com.worlize.gif.events.GIFPlayerEvent")] 35 | [Event(name="asyncDecodeError",type="com.worlize.gif.events.AsyncDecodeErrorEvent")] 36 | public class GIFPlayer extends Bitmap 37 | { 38 | [Bindable] 39 | public var autoPlay:Boolean; 40 | [Bindable] 41 | public var enableFrameSkipping:Boolean = false; 42 | 43 | private var wasPlaying:Boolean = false; 44 | private var minFrameDelay:Number; 45 | private var useSmoothing:Boolean = false; 46 | private var lastQuantizationError:Number = 0; 47 | private var gifDecoder:GIFDecoder; 48 | private var timer:Timer = new Timer(0, 0); 49 | private var currentLoop:uint; 50 | private var _loopCount:uint; 51 | private var _currentFrame:int = -1; 52 | private var _frameCount:uint = 0; 53 | private var _frames:Vector.; 54 | private var _ready:Boolean = false; 55 | private var _imageWidth:Number; 56 | private var _imageHeight:Number; 57 | 58 | public function GIFPlayer(autoPlay:Boolean = true) { 59 | this.autoPlay = autoPlay; 60 | if (stage) { 61 | initMinFrameDelay(); 62 | } 63 | addEventListener(Event.ADDED_TO_STAGE, initMinFrameDelay); 64 | timer.addEventListener(TimerEvent.TIMER, handleTimer); 65 | addEventListener(Event.REMOVED_FROM_STAGE, handleRemovedFromStage); 66 | } 67 | 68 | protected function handleRemovedFromStage(event:Event):void { 69 | wasPlaying = playing; 70 | stop(); 71 | addEventListener(Event.ADDED_TO_STAGE, handleAddedToStage); 72 | } 73 | 74 | protected function handleAddedToStage(event:Event):void { 75 | removeEventListener(Event.ADDED_TO_STAGE, handleAddedToStage); 76 | if (wasPlaying) { 77 | play(); 78 | } 79 | } 80 | 81 | override public function set smoothing(newValue:Boolean):void { 82 | if (useSmoothing !== newValue) { 83 | useSmoothing = newValue; 84 | super.smoothing = newValue; 85 | } 86 | } 87 | 88 | private function initMinFrameDelay(event:Event=null):void { 89 | // stage.frameRate = 60; 90 | minFrameDelay = 1000 / stage.frameRate; 91 | } 92 | 93 | public function loadBytes(data:ByteArray):void { 94 | reset(); 95 | gifDecoder.decodeBytes(data); 96 | } 97 | 98 | public function reset():void { 99 | stop(); 100 | setReady(false); 101 | unloadDecoder(); 102 | dispose(); 103 | initDecoder(); 104 | } 105 | 106 | protected function initDecoder():void { 107 | gifDecoder = new GIFDecoder(); 108 | gifDecoder.addEventListener(GIFDecoderEvent.DECODE_COMPLETE, handleDecodeComplete); 109 | gifDecoder.addEventListener(AsyncDecodeErrorEvent.ASYNC_DECODE_ERROR, handleAsyncDecodeError); 110 | } 111 | 112 | protected function unloadDecoder():void { 113 | if (gifDecoder) { 114 | gifDecoder.removeEventListener(GIFDecoderEvent.DECODE_COMPLETE, handleDecodeComplete); 115 | gifDecoder.removeEventListener(AsyncDecodeErrorEvent.ASYNC_DECODE_ERROR, handleAsyncDecodeError); 116 | gifDecoder = null; 117 | } 118 | } 119 | 120 | protected function handleDecodeComplete(event:GIFDecoderEvent):void { 121 | // trace("Decode complete. Total decoding time: " + gifDecoder.totalDecodeTime + " Blocking decode time: " + gifDecoder.blockingDecodeTime); 122 | currentLoop = 0; 123 | _loopCount = gifDecoder.loopCount; 124 | _frames = gifDecoder.frames; 125 | _frameCount = _frames.length; 126 | _currentFrame = -1; 127 | _imageWidth = gifDecoder.width; 128 | _imageHeight = gifDecoder.height; 129 | gifDecoder.cleanup(); 130 | unloadDecoder(); 131 | setReady(true); 132 | dispatchEvent(new Event('totalFramesChange')); 133 | dispatchEvent(new GIFPlayerEvent(GIFPlayerEvent.COMPLETE)); 134 | if (stage) { 135 | if (autoPlay) { 136 | gotoAndPlay(1); 137 | } 138 | else { 139 | gotoAndStop(1); 140 | } 141 | } 142 | } 143 | 144 | private function handleTimer(event:TimerEvent):void { 145 | step(); 146 | } 147 | 148 | public function gotoAndPlay(requestedIndex:uint):void { 149 | lastQuantizationError = 0; 150 | goto(requestedIndex-1); 151 | play(); 152 | } 153 | 154 | public function gotoAndStop(requestedIndex:uint):void { 155 | stop(); 156 | lastQuantizationError = 0; 157 | goto(requestedIndex-1); 158 | } 159 | 160 | public function play():void { 161 | if (_frameCount <= 0) { 162 | throw new Error("Nothing to play"); 163 | } 164 | // single-frame file, nothing to play but we can 165 | // render the first frame. 166 | if (_frameCount === 1) { 167 | goto(0); 168 | } 169 | else { 170 | timer.start(); 171 | lastQuantizationError = 0; 172 | } 173 | } 174 | 175 | public function stop():void { 176 | if (timer.running) { 177 | timer.stop(); 178 | } 179 | } 180 | 181 | private function quantizeFrameDelay(input:Number, increment:Number):Number { 182 | var adjustedInput:Number = input + lastQuantizationError; 183 | var output:Number = Math.round((adjustedInput)/increment)*increment; 184 | lastQuantizationError = adjustedInput - output; 185 | return output; 186 | } 187 | 188 | private function step():void { 189 | if (_currentFrame + 1 >= _frameCount) { 190 | currentLoop ++; 191 | if (_loopCount === 0 || currentLoop < _loopCount) { 192 | goto(0); 193 | } 194 | else { 195 | stop(); 196 | } 197 | } 198 | else { 199 | goto(_currentFrame + 1); 200 | } 201 | } 202 | 203 | // This private API function uses zero-based indices, while the public 204 | // facing API uses one-based indices 205 | private function goto(requestedIndex:uint):void { 206 | if (requestedIndex >= _frameCount || requestedIndex < 0) { 207 | throw new RangeError("The requested frame is out of bounds."); 208 | } 209 | if (requestedIndex === _currentFrame) { 210 | return; 211 | } 212 | 213 | // Store current frame index 214 | _currentFrame = requestedIndex; 215 | 216 | var requestedDelay:Number = (currentFrameObject.delayMs < 20) ? 100 : currentFrameObject.delayMs; 217 | var delay:Number = Math.round(quantizeFrameDelay(requestedDelay, minFrameDelay)); 218 | delay = Math.max(delay - minFrameDelay/2, 0); 219 | timer.delay = delay; 220 | 221 | if (delay === 0 && enableFrameSkipping) { 222 | // skip the frame 223 | step(); 224 | return; 225 | } 226 | 227 | // Update the display 228 | bitmapData = currentFrameObject.bitmapData; 229 | if (useSmoothing) { 230 | super.smoothing = true; 231 | } 232 | 233 | var renderedEvent:GIFPlayerEvent = new GIFPlayerEvent(GIFPlayerEvent.FRAME_RENDERED); 234 | renderedEvent.frameIndex = _currentFrame; 235 | dispatchEvent(renderedEvent); 236 | } 237 | 238 | public function dispose():void { 239 | if (_frames === null) { return; } 240 | for (var i:int = 0; i < _frames.length; i ++) { 241 | _frames[i].bitmapData.dispose(); 242 | } 243 | } 244 | 245 | public function get playing():Boolean { 246 | return timer.running; 247 | } 248 | 249 | [Bindable(event="frameRendered")] 250 | public function get currentFrame():uint { 251 | return _currentFrame+1; 252 | } 253 | 254 | [Bindable(event="totalFramesChange")] 255 | public function get totalFrames():uint { 256 | return _frameCount; 257 | } 258 | 259 | [Bindable(event="readyChange")] 260 | public function get ready():Boolean { 261 | return _ready; 262 | } 263 | 264 | protected function setReady(newValue:Boolean):void { 265 | if (_ready !== newValue) { 266 | _ready = newValue; 267 | dispatchEvent(new Event("readyChange")); 268 | } 269 | } 270 | 271 | public function get loopCount():uint { 272 | return _loopCount; 273 | } 274 | 275 | protected function get currentFrameObject():GIFFrame { 276 | return _frames[_currentFrame]; 277 | } 278 | 279 | protected function get previousFrameObject():GIFFrame { 280 | if (_currentFrame === 0) { return null; } 281 | return _frames[_currentFrame-1]; 282 | } 283 | 284 | protected function handleAsyncDecodeError(event:AsyncDecodeErrorEvent):void { 285 | dispatchEvent(event.clone()); 286 | } 287 | 288 | override public function get width():Number { 289 | return _imageWidth; 290 | } 291 | 292 | override public function get height():Number { 293 | return _imageHeight; 294 | } 295 | } 296 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /AS3GifPlayer/src/com/worlize/gif/GIFDecoder.as: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | * Copyright 2012 Worlize Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ***********************************************************************/ 16 | 17 | package com.worlize.gif 18 | { 19 | import com.worlize.gif.blocks.ApplicationExtension; 20 | import com.worlize.gif.blocks.ColorTableBlock; 21 | import com.worlize.gif.blocks.CommentExtension; 22 | import com.worlize.gif.blocks.GraphicControlExtension; 23 | import com.worlize.gif.blocks.HeaderBlock; 24 | import com.worlize.gif.blocks.IGIFBlockCodec; 25 | import com.worlize.gif.blocks.ImageDataBlock; 26 | import com.worlize.gif.blocks.ImageDescriptorBlock; 27 | import com.worlize.gif.blocks.LogicalScreenDescriptorBlock; 28 | import com.worlize.gif.blocks.NetscapeExtension; 29 | import com.worlize.gif.blocks.PlainTextExtension; 30 | import com.worlize.gif.blocks.TrailerBlock; 31 | import com.worlize.gif.blocks.UnknownExtension; 32 | import com.worlize.gif.constants.BlockType; 33 | import com.worlize.gif.constants.DefaultPalette; 34 | import com.worlize.gif.constants.DisposalType; 35 | import com.worlize.gif.errors.FileTypeError; 36 | import com.worlize.gif.errors.OutOfDataError; 37 | import com.worlize.gif.events.AsyncDecodeErrorEvent; 38 | import com.worlize.gif.events.GIFDecoderEvent; 39 | 40 | import flash.display.BitmapData; 41 | import flash.events.EventDispatcher; 42 | import flash.events.IEventDispatcher; 43 | import flash.filters.GlowFilter; 44 | import flash.geom.Matrix; 45 | import flash.geom.Point; 46 | import flash.geom.Rectangle; 47 | import flash.text.TextField; 48 | import flash.text.TextFormat; 49 | import flash.utils.ByteArray; 50 | import flash.utils.Endian; 51 | 52 | //import mx.controls.Image; 53 | 54 | [Event(name="asyncDecodeError",type="com.worlize.gif.events.AsyncDecodeErrorEvent")] 55 | [Event(name="decodeComplete",type="com.worlize.gif.events.GIFDecoderEvent")] 56 | public class GIFDecoder extends EventDispatcher 57 | { 58 | protected var data:ByteArray; 59 | 60 | protected var header:HeaderBlock; 61 | protected var lsd:LogicalScreenDescriptorBlock; 62 | protected var graphicControlExtension:GraphicControlExtension; 63 | 64 | protected var globalColorTable:ColorTableBlock; 65 | protected var fallbackColorTable:ColorTableBlock; 66 | protected var activeColorTable:ColorTableBlock; 67 | 68 | public var width:uint; 69 | public var height:uint; 70 | 71 | public var backgroundColor:uint = 0xFFFFFFFF; 72 | public var loopCount:uint = 1; 73 | 74 | public var frameDecodedCount:uint = 0; 75 | public var framesToDecode:uint = 0; 76 | public var frames:Vector.; 77 | 78 | private var startTime:uint; 79 | public var totalDecodeTime:uint; 80 | public var blockingDecodeTime:uint; 81 | 82 | private var _hasError:Boolean = false; 83 | 84 | // Store the exact sequence of blocks from the stream 85 | protected var blockSequence:Vector.; 86 | 87 | public function get hasError():Boolean { 88 | return _hasError; 89 | } 90 | 91 | public function decodeBytes(inputData:ByteArray):void { 92 | startTime = (new Date()).valueOf(); 93 | data = inputData; 94 | data.endian = Endian.LITTLE_ENDIAN; 95 | blockSequence = new Vector.(); 96 | frames = new Vector.(); 97 | framesToDecode = 0; 98 | 99 | try { 100 | readMetadata(); 101 | readContents(); 102 | } 103 | catch(e:Error) { 104 | abortDecode(); 105 | _hasError = true; 106 | var errorEvent:AsyncDecodeErrorEvent = new AsyncDecodeErrorEvent(AsyncDecodeErrorEvent.ASYNC_DECODE_ERROR); 107 | errorEvent.text = e.message; 108 | dispatchEvent(errorEvent); 109 | return; 110 | } 111 | var endTime:uint = (new Date()).valueOf(); 112 | blockingDecodeTime = endTime - startTime; 113 | } 114 | 115 | public function cleanup():void { 116 | data.clear(); 117 | data = null; 118 | for (var i:int = 0; i < blockSequence.length; i++) { 119 | blockSequence[i].dispose(); 120 | } 121 | } 122 | 123 | private function readMetadata():void { 124 | // File Format Header 125 | header = new HeaderBlock(); 126 | header.decode(data); 127 | blockSequence.push(header); 128 | 129 | // Logical Screen Descriptor 130 | lsd = new LogicalScreenDescriptorBlock(); 131 | lsd.decode(data); 132 | width = lsd.width; 133 | height = lsd.height; 134 | blockSequence.push(lsd); 135 | 136 | // If we have a global color table, lets decode it. 137 | if (lsd.hasgct) { 138 | globalColorTable = new ColorTableBlock(); 139 | globalColorTable.numColors = lsd.gctNumColors; 140 | globalColorTable.decode(data); 141 | blockSequence.push(globalColorTable); 142 | 143 | // Find the actual background color 144 | backgroundColor = globalColorTable.table[lsd.backgroundColorIndex]; 145 | } 146 | else { 147 | // Initialize the fallback color table just in case. 148 | fallbackColorTable = new ColorTableBlock(); 149 | fallbackColorTable.numColors = 256; 150 | fallbackColorTable.table = DefaultPalette.WINDOWS; 151 | } 152 | } 153 | 154 | private function readContents():void { 155 | var complete:Boolean = false; 156 | var code:uint; 157 | while (!complete) { 158 | if (data.bytesAvailable < 1) { 159 | throw new OutOfDataError("Out of data while looking for next block."); 160 | } 161 | code = data.readUnsignedByte(); 162 | switch(code) { 163 | // Decode the extension 164 | case BlockType.EXTENSION: 165 | decodeExtension(); 166 | break; 167 | 168 | // We've got an image to decode 169 | case BlockType.IMAGE_DESCRIPTOR: 170 | decodeImage(); 171 | break; 172 | 173 | // We've reached the end of the file! 174 | case BlockType.TRAILER: 175 | blockSequence.push(new TrailerBlock()); 176 | complete = true; 177 | break; 178 | 179 | // case 0x00: // bad byte, but keep going and see what happens? 180 | // No -- this is invalid. 181 | // break; 182 | default: 183 | throw new FileTypeError("Invalid data encountered while decoding GIF stream."); 184 | break; 185 | } 186 | } 187 | } 188 | 189 | private function decodeExtension():void { 190 | var code:uint; 191 | if (data.bytesAvailable < 1) { 192 | throw new OutOfDataError("Out of data while trying to read extension"); 193 | } 194 | code = data.readUnsignedByte(); 195 | 196 | switch(code) { 197 | case BlockType.APPLICATION_EXT: 198 | decodeAppExtension(); 199 | break; 200 | 201 | case BlockType.GRAPHIC_CONTROL_EXT: 202 | graphicControlExtension = new GraphicControlExtension(); 203 | graphicControlExtension.decode(data); 204 | blockSequence.push(graphicControlExtension); 205 | break; 206 | 207 | case BlockType.COMMENT_EXT: 208 | var commentExt:CommentExtension = new CommentExtension(); 209 | commentExt.decode(data); 210 | blockSequence.push(commentExt); 211 | break; 212 | 213 | case BlockType.PLAIN_TEXT_EXT: 214 | var plainTextExt:PlainTextExtension = new PlainTextExtension(); 215 | plainTextExt.decode(data); 216 | blockSequence.push(commentExt); 217 | break; 218 | 219 | default: 220 | throw new FileTypeError("Invalid GIF data - invalid extension type encountered.") 221 | // var unknownExt:UnknownExtension = new UnknownExtension(); 222 | // unknownExt.extensionLabel = code; 223 | // unknownExt.decode(data); 224 | // blockSequence.push(unknownExt); 225 | break; 226 | } 227 | } 228 | 229 | private function decodeAppExtension():void { 230 | var appExt:ApplicationExtension = new ApplicationExtension(); 231 | appExt.decode(data); 232 | 233 | // We only support the NETSCAPE2.0 app extension. All others 234 | // are ignored. 235 | if (appExt.appIdentifier === NetscapeExtension.APP_IDENTIFIER && 236 | appExt.appAuthCode === NetscapeExtension.APP_AUTH_CODE) 237 | { 238 | var netscapeExt:NetscapeExtension = new NetscapeExtension(); 239 | netscapeExt.decodeFromApplicationExtension(appExt); 240 | blockSequence.push(netscapeExt); 241 | loopCount = netscapeExt.loopCount; 242 | } 243 | else { 244 | blockSequence.push(appExt); 245 | } 246 | } 247 | 248 | private function decodeImage():void { 249 | var imageDescriptor:ImageDescriptorBlock = new ImageDescriptorBlock(); 250 | imageDescriptor.decode(data); 251 | blockSequence.push(imageDescriptor); 252 | 253 | if (imageDescriptor.haslct) { 254 | var localColorTable:ColorTableBlock = new ColorTableBlock(); 255 | localColorTable.numColors = imageDescriptor.lctNumColors; 256 | localColorTable.decode(data); 257 | blockSequence.push(localColorTable); 258 | 259 | // Set local color table as active 260 | activeColorTable = localColorTable; 261 | } 262 | else if (globalColorTable) { 263 | // Set global color table as active 264 | activeColorTable = globalColorTable; 265 | } 266 | else { 267 | // If no color table is available, use the default old-school 268 | // Windows 256-color palette. 269 | activeColorTable = fallbackColorTable; 270 | } 271 | 272 | var imageDataBlock:ImageDataBlock = new ImageDataBlock(); 273 | imageDataBlock.decode(data); 274 | blockSequence.push(imageDataBlock); 275 | 276 | frames.push(buildFrame(imageDescriptor, imageDataBlock)); 277 | 278 | // Clean up the state after finishing with a frame. 279 | graphicControlExtension = null; 280 | } 281 | 282 | private function buildFrame(imageDescriptor:ImageDescriptorBlock, imageData:ImageDataBlock):GIFFrame { 283 | // ByteArray to hold a new GIF file of just this one frame 284 | var ba:ByteArray = new ByteArray(); 285 | ba.endian = Endian.LITTLE_ENDIAN; 286 | 287 | header.encode(ba); 288 | 289 | var localLsd:LogicalScreenDescriptorBlock = new LogicalScreenDescriptorBlock(); 290 | localLsd.pixelAspect = lsd.pixelAspect; 291 | // Render the image at the size of this particular frame only 292 | localLsd.width = imageDescriptor.width; 293 | localLsd.height = imageDescriptor.height; 294 | localLsd.backgroundColorIndex = lsd.backgroundColorIndex; 295 | localLsd.gctColorResolution = lsd.gctColorResolution; 296 | // Encode the active color table, whether local or global, 297 | // as the global color table of the new file. 298 | localLsd.gctNumColors = activeColorTable.numColors; 299 | localLsd.hasgct = true; 300 | localLsd.gctSorted = false; 301 | localLsd.encode(ba); 302 | 303 | // Include the correct color table 304 | activeColorTable.encode(ba); 305 | 306 | if (graphicControlExtension) { 307 | var localGCE:GraphicControlExtension = new GraphicControlExtension(); 308 | // We're making a single frame image so this shouldn't matter 309 | localGCE.delayTime = 0; 310 | localGCE.userInputExpected = false; 311 | localGCE.disposalMethod = graphicControlExtension.disposalMethod; 312 | 313 | // The transparency aspects of the GCE are what's important here 314 | localGCE.hasTransparency = graphicControlExtension.hasTransparency; 315 | localGCE.transparencyIndex = graphicControlExtension.transparencyIndex; 316 | localGCE.encode(ba); 317 | } 318 | 319 | var localId:ImageDescriptorBlock = new ImageDescriptorBlock(); 320 | // No local color table, only a global one 321 | localId.haslct = false; 322 | localId.lctNumColors = 2; 323 | localId.lctSorted = false; 324 | localId.width = imageDescriptor.width; 325 | localId.height = imageDescriptor.height; 326 | // Since we're rendering this image at the size of only this 327 | // individual frame, we don't want it offset at all. We will 328 | // manually apply the offset when compositing the frames together. 329 | localId.offsetLeft = 0; 330 | localId.offsetTop = 0; 331 | localId.reserved = 0; 332 | localId.interlaced = imageDescriptor.interlaced; 333 | localId.encode(ba); 334 | 335 | imageData.encode(ba); 336 | 337 | // Terminate the file properly. 338 | var trailerBlock:TrailerBlock = new TrailerBlock(); 339 | trailerBlock.encode(ba); 340 | 341 | ba.position = 0; 342 | 343 | var frame:GIFFrame = new GIFFrame(); 344 | frame.gifData = ba; 345 | frame.left = imageDescriptor.offsetLeft; 346 | frame.top = imageDescriptor.offsetTop; 347 | frame.width = imageDescriptor.width; 348 | frame.height = imageDescriptor.height; 349 | frame.backgroundColor = activeColorTable.table[localLsd.backgroundColorIndex]; 350 | frame.backgroundColorIndex = localLsd.backgroundColorIndex; 351 | if (graphicControlExtension) { 352 | frame.delayMs = graphicControlExtension.delayTime * 10; 353 | frame.disposalType = graphicControlExtension.disposalMethod; 354 | frame.hasTransparency = graphicControlExtension.hasTransparency; 355 | frame.transparencyIndex = graphicControlExtension.transparencyIndex; 356 | } 357 | else { 358 | frame.hasTransparency = false; 359 | frame.transparencyIndex = 0; 360 | frame.delayMs = 42; // default 24fps 361 | frame.disposalType = DisposalType.RESTORE_BACKGROUND_COLOR; 362 | } 363 | 364 | // Start decoding the image data 365 | framesToDecode ++; 366 | frame.addEventListener(GIFDecoderEvent.DECODE_COMPLETE, handleFrameDecodeComplete); 367 | frame.addEventListener(AsyncDecodeErrorEvent.ASYNC_DECODE_ERROR, handleFrameAsyncDecodeError); 368 | frame.decode(); 369 | return frame; 370 | } 371 | 372 | 373 | protected function renderCompositedFrames():void { 374 | var startTime:uint = (new Date()).valueOf(); 375 | var w:uint = width; 376 | var h:uint = height; 377 | var frame:GIFFrame; 378 | var prevFrame:GIFFrame; 379 | var lastNoDisposalFrame:GIFFrame; 380 | for (var i:uint=0; i < framesToDecode; i++) { 381 | frame = frames[i]; 382 | 383 | var source:BitmapData = frame.bitmapData; 384 | var dest:BitmapData = new BitmapData(w, h, true, 0x00FFFFFF); 385 | var backgroundColor:uint; 386 | var rect:Rectangle; 387 | if (prevFrame === null) { 388 | // Start the first frame with the correct background fill. 389 | // ODD NOTE: We always start with a transparent canvas to 390 | // match Firefox and Chrome's rendering behavior. 391 | 392 | // backgroundColor = frame.backgroundColor; 393 | // if (frame.hasTransparency) { 394 | // backgroundColor &= 0x00FFFFFF; 395 | // } 396 | // dest.fillRect(new Rectangle(0,0,w,h), backgroundColor); 397 | } 398 | else { 399 | // Start with the exact pixels from the previous frame 400 | dest.copyPixels(prevFrame.bitmapData, new Rectangle(0, 0, w, h), new Point(0, 0)); 401 | 402 | var dispose:uint = prevFrame.disposalType; 403 | 404 | // Remove previous frame according to specified disposal rules 405 | if ((dispose === DisposalType.RESTORE_TO_PREVIOUS && lastNoDisposalFrame === null) || 406 | (dispose === DisposalType.RESTORE_BACKGROUND_COLOR)) 407 | { 408 | // Fill with either the background color or transparency. 409 | backgroundColor = prevFrame.backgroundColor; 410 | // ODD NOTE: We always fill with transparency to 411 | // match Firefox and Chrome's rendering behavior. 412 | // if (prevFrame.hasTransparency) { 413 | // Apply correct transparency 414 | backgroundColor &= 0x00FFFFFF; 415 | // } 416 | rect = new Rectangle(prevFrame.left, prevFrame.top, prevFrame.width, prevFrame.height); 417 | dest.fillRect(rect, backgroundColor); 418 | } 419 | else if (dispose === DisposalType.NO_DISPOSAL || dispose == DisposalType.DO_NOT_DISPOSE) { 420 | // Do nothing, keep entire previous image as starting point 421 | lastNoDisposalFrame = prevFrame; 422 | } 423 | else if (dispose === DisposalType.RESTORE_TO_PREVIOUS && i >= 1) { 424 | // Restore previously occupied rectangle to what was there before 425 | var f:GIFFrame = lastNoDisposalFrame; 426 | rect = new Rectangle(prevFrame.left, prevFrame.top, prevFrame.width, prevFrame.height) ; 427 | dest.copyPixels(f.bitmapData, rect, new Point(prevFrame.left, prevFrame.top)); 428 | } 429 | } 430 | 431 | rect = new Rectangle(0,0,frame.width,frame.height); 432 | dest.copyPixels(frame.bitmapData, rect, new Point(frame.left, frame.top), null, null, true); 433 | 434 | // Reclaim the memory for the original image piece that we 435 | // no longer have any use for. 436 | frame.bitmapData.dispose(); 437 | 438 | frame.bitmapData = dest; 439 | prevFrame = frame; 440 | } 441 | var endTime:uint = (new Date()).valueOf(); 442 | blockingDecodeTime += (endTime - startTime); 443 | } 444 | 445 | public function encode():ByteArray { 446 | var ba:ByteArray = new ByteArray(); 447 | ba.endian = Endian.LITTLE_ENDIAN; 448 | 449 | for (var i:int=0,len:int=blockSequence.length; i < len; i++) { 450 | blockSequence[i].encode(ba); 451 | } 452 | 453 | ba.position = 0; 454 | return ba; 455 | } 456 | 457 | protected function handleFrameDecodeComplete(event:GIFDecoderEvent):void { 458 | frameDecodedCount ++; 459 | if (frameDecodedCount === framesToDecode && !_hasError) { 460 | renderCompositedFrames(); 461 | 462 | var endTime:uint = (new Date()).valueOf(); 463 | totalDecodeTime = endTime - startTime; 464 | 465 | var decoderEvent:GIFDecoderEvent = new GIFDecoderEvent(GIFDecoderEvent.DECODE_COMPLETE); 466 | dispatchEvent(decoderEvent); 467 | } 468 | } 469 | 470 | protected function handleFrameAsyncDecodeError(event:AsyncDecodeErrorEvent):void { 471 | abortDecode(); 472 | var errorEvent:AsyncDecodeErrorEvent = new AsyncDecodeErrorEvent(AsyncDecodeErrorEvent.ASYNC_DECODE_ERROR); 473 | errorEvent.text = "An error was encountered while Flash was decoding an image frame."; 474 | dispatchEvent(errorEvent); 475 | } 476 | 477 | protected function abortDecode():void { 478 | for (var i:int=0,len:int=frames.length; i < len; i++) { 479 | frames[i].abortDecode(); 480 | } 481 | cleanup(); 482 | } 483 | } 484 | } 485 | -------------------------------------------------------------------------------- /GifTest/html-template/history/history.js: -------------------------------------------------------------------------------- 1 | BrowserHistoryUtils = { 2 | addEvent: function(elm, evType, fn, useCapture) { 3 | useCapture = useCapture || false; 4 | if (elm.addEventListener) { 5 | elm.addEventListener(evType, fn, useCapture); 6 | return true; 7 | } 8 | else if (elm.attachEvent) { 9 | var r = elm.attachEvent('on' + evType, fn); 10 | return r; 11 | } 12 | else { 13 | elm['on' + evType] = fn; 14 | } 15 | } 16 | } 17 | 18 | BrowserHistory = (function() { 19 | // type of browser 20 | var browser = { 21 | ie: false, 22 | ie8: false, 23 | firefox: false, 24 | safari: false, 25 | opera: false, 26 | version: -1 27 | }; 28 | 29 | // Default app state URL to use when no fragment ID present 30 | var defaultHash = ''; 31 | 32 | // Last-known app state URL 33 | var currentHref = document.location.href; 34 | 35 | // Initial URL (used only by IE) 36 | var initialHref = document.location.href; 37 | 38 | // Initial URL (used only by IE) 39 | var initialHash = document.location.hash; 40 | 41 | // History frame source URL prefix (used only by IE) 42 | var historyFrameSourcePrefix = 'history/historyFrame.html?'; 43 | 44 | // History maintenance (used only by Safari) 45 | var currentHistoryLength = -1; 46 | 47 | // Flag to denote the existence of onhashchange 48 | var browserHasHashChange = false; 49 | 50 | var historyHash = []; 51 | 52 | var initialState = createState(initialHref, initialHref + '#' + initialHash, initialHash); 53 | 54 | var backStack = []; 55 | var forwardStack = []; 56 | 57 | var currentObjectId = null; 58 | 59 | //UserAgent detection 60 | var useragent = navigator.userAgent.toLowerCase(); 61 | 62 | if (useragent.indexOf("opera") != -1) { 63 | browser.opera = true; 64 | } else if (useragent.indexOf("msie") != -1) { 65 | browser.ie = true; 66 | browser.version = parseFloat(useragent.substring(useragent.indexOf('msie') + 4)); 67 | if (browser.version == 8) 68 | { 69 | browser.ie = false; 70 | browser.ie8 = true; 71 | } 72 | } else if (useragent.indexOf("safari") != -1) { 73 | browser.safari = true; 74 | browser.version = parseFloat(useragent.substring(useragent.indexOf('safari') + 7)); 75 | } else if (useragent.indexOf("gecko") != -1) { 76 | browser.firefox = true; 77 | } 78 | 79 | if (browser.ie == true && browser.version == 7) { 80 | window["_ie_firstload"] = false; 81 | } 82 | 83 | function hashChangeHandler() 84 | { 85 | currentHref = document.location.href; 86 | var flexAppUrl = getHash(); 87 | //ADR: to fix multiple 88 | if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { 89 | var pl = getPlayers(); 90 | for (var i = 0; i < pl.length; i++) { 91 | pl[i].browserURLChange(flexAppUrl); 92 | } 93 | } else { 94 | getPlayer().browserURLChange(flexAppUrl); 95 | } 96 | } 97 | 98 | // Accessor functions for obtaining specific elements of the page. 99 | function getHistoryFrame() 100 | { 101 | return document.getElementById('ie_historyFrame'); 102 | } 103 | 104 | function getFormElement() 105 | { 106 | return document.getElementById('safari_formDiv'); 107 | } 108 | 109 | function getRememberElement() 110 | { 111 | return document.getElementById("safari_remember_field"); 112 | } 113 | 114 | // Get the Flash player object for performing ExternalInterface callbacks. 115 | // Updated for changes to SWFObject2. 116 | function getPlayer(id) { 117 | var i; 118 | 119 | if (id && document.getElementById(id)) { 120 | var r = document.getElementById(id); 121 | if (typeof r.SetVariable != "undefined") { 122 | return r; 123 | } 124 | else { 125 | var o = r.getElementsByTagName("object"); 126 | var e = r.getElementsByTagName("embed"); 127 | for (i = 0; i < o.length; i++) { 128 | if (typeof o[i].browserURLChange != "undefined") 129 | return o[i]; 130 | } 131 | for (i = 0; i < e.length; i++) { 132 | if (typeof e[i].browserURLChange != "undefined") 133 | return e[i]; 134 | } 135 | } 136 | } 137 | else { 138 | var o = document.getElementsByTagName("object"); 139 | var e = document.getElementsByTagName("embed"); 140 | for (i = 0; i < e.length; i++) { 141 | if (typeof e[i].browserURLChange != "undefined") 142 | { 143 | return e[i]; 144 | } 145 | } 146 | for (i = 0; i < o.length; i++) { 147 | if (typeof o[i].browserURLChange != "undefined") 148 | { 149 | return o[i]; 150 | } 151 | } 152 | } 153 | return undefined; 154 | } 155 | 156 | function getPlayers() { 157 | var i; 158 | var players = []; 159 | if (players.length == 0) { 160 | var tmp = document.getElementsByTagName('object'); 161 | for (i = 0; i < tmp.length; i++) 162 | { 163 | if (typeof tmp[i].browserURLChange != "undefined") 164 | players.push(tmp[i]); 165 | } 166 | } 167 | if (players.length == 0 || players[0].object == null) { 168 | var tmp = document.getElementsByTagName('embed'); 169 | for (i = 0; i < tmp.length; i++) 170 | { 171 | if (typeof tmp[i].browserURLChange != "undefined") 172 | players.push(tmp[i]); 173 | } 174 | } 175 | return players; 176 | } 177 | 178 | function getIframeHash() { 179 | var doc = getHistoryFrame().contentWindow.document; 180 | var hash = String(doc.location.search); 181 | if (hash.length == 1 && hash.charAt(0) == "?") { 182 | hash = ""; 183 | } 184 | else if (hash.length >= 2 && hash.charAt(0) == "?") { 185 | hash = hash.substring(1); 186 | } 187 | return hash; 188 | } 189 | 190 | /* Get the current location hash excluding the '#' symbol. */ 191 | function getHash() { 192 | // It would be nice if we could use document.location.hash here, 193 | // but it's faulty sometimes. 194 | var idx = document.location.href.indexOf('#'); 195 | return (idx >= 0) ? document.location.href.substr(idx+1) : ''; 196 | } 197 | 198 | /* Get the current location hash excluding the '#' symbol. */ 199 | function setHash(hash) { 200 | // It would be nice if we could use document.location.hash here, 201 | // but it's faulty sometimes. 202 | if (hash == '') hash = '#' 203 | document.location.hash = hash; 204 | } 205 | 206 | function createState(baseUrl, newUrl, flexAppUrl) { 207 | return { 'baseUrl': baseUrl, 'newUrl': newUrl, 'flexAppUrl': flexAppUrl, 'title': null }; 208 | } 209 | 210 | /* Add a history entry to the browser. 211 | * baseUrl: the portion of the location prior to the '#' 212 | * newUrl: the entire new URL, including '#' and following fragment 213 | * flexAppUrl: the portion of the location following the '#' only 214 | */ 215 | function addHistoryEntry(baseUrl, newUrl, flexAppUrl) { 216 | 217 | //delete all the history entries 218 | forwardStack = []; 219 | 220 | if (browser.ie) { 221 | //Check to see if we are being asked to do a navigate for the first 222 | //history entry, and if so ignore, because it's coming from the creation 223 | //of the history iframe 224 | if (flexAppUrl == defaultHash && document.location.href == initialHref && window['_ie_firstload']) { 225 | currentHref = initialHref; 226 | return; 227 | } 228 | if ((!flexAppUrl || flexAppUrl == defaultHash) && window['_ie_firstload']) { 229 | newUrl = baseUrl + '#' + defaultHash; 230 | flexAppUrl = defaultHash; 231 | } else { 232 | // for IE, tell the history frame to go somewhere without a '#' 233 | // in order to get this entry into the browser history. 234 | getHistoryFrame().src = historyFrameSourcePrefix + flexAppUrl; 235 | } 236 | setHash(flexAppUrl); 237 | } else { 238 | 239 | //ADR 240 | if (backStack.length == 0 && initialState.flexAppUrl == flexAppUrl) { 241 | initialState = createState(baseUrl, newUrl, flexAppUrl); 242 | } else if(backStack.length > 0 && backStack[backStack.length - 1].flexAppUrl == flexAppUrl) { 243 | backStack[backStack.length - 1] = createState(baseUrl, newUrl, flexAppUrl); 244 | } 245 | 246 | if (browser.safari && !browserHasHashChange) { 247 | // for Safari, submit a form whose action points to the desired URL 248 | if (browser.version <= 419.3) { 249 | var file = window.location.pathname.toString(); 250 | file = file.substring(file.lastIndexOf("/")+1); 251 | getFormElement().innerHTML = '
'; 252 | //get the current elements and add them to the form 253 | var qs = window.location.search.substring(1); 254 | var qs_arr = qs.split("&"); 255 | for (var i = 0; i < qs_arr.length; i++) { 256 | var tmp = qs_arr[i].split("="); 257 | var elem = document.createElement("input"); 258 | elem.type = "hidden"; 259 | elem.name = tmp[0]; 260 | elem.value = tmp[1]; 261 | document.forms.historyForm.appendChild(elem); 262 | } 263 | document.forms.historyForm.submit(); 264 | } else { 265 | top.location.hash = flexAppUrl; 266 | } 267 | // We also have to maintain the history by hand for Safari 268 | historyHash[history.length] = flexAppUrl; 269 | _storeStates(); 270 | } else { 271 | // Otherwise, just tell the browser to go there 272 | setHash(flexAppUrl); 273 | } 274 | } 275 | backStack.push(createState(baseUrl, newUrl, flexAppUrl)); 276 | } 277 | 278 | function _storeStates() { 279 | if (browser.safari) { 280 | getRememberElement().value = historyHash.join(","); 281 | } 282 | } 283 | 284 | function handleBackButton() { 285 | //The "current" page is always at the top of the history stack. 286 | var current = backStack.pop(); 287 | if (!current) { return; } 288 | var last = backStack[backStack.length - 1]; 289 | if (!last && backStack.length == 0){ 290 | last = initialState; 291 | } 292 | forwardStack.push(current); 293 | } 294 | 295 | function handleForwardButton() { 296 | //summary: private method. Do not call this directly. 297 | 298 | var last = forwardStack.pop(); 299 | if (!last) { return; } 300 | backStack.push(last); 301 | } 302 | 303 | function handleArbitraryUrl() { 304 | //delete all the history entries 305 | forwardStack = []; 306 | } 307 | 308 | /* Called periodically to poll to see if we need to detect navigation that has occurred */ 309 | function checkForUrlChange() { 310 | 311 | if (browser.ie) { 312 | if (currentHref != document.location.href && currentHref + '#' != document.location.href) { 313 | //This occurs when the user has navigated to a specific URL 314 | //within the app, and didn't use browser back/forward 315 | //IE seems to have a bug where it stops updating the URL it 316 | //shows the end-user at this point, but programatically it 317 | //appears to be correct. Do a full app reload to get around 318 | //this issue. 319 | if (browser.version < 7) { 320 | currentHref = document.location.href; 321 | document.location.reload(); 322 | } else { 323 | if (getHash() != getIframeHash()) { 324 | // this.iframe.src = this.blankURL + hash; 325 | var sourceToSet = historyFrameSourcePrefix + getHash(); 326 | getHistoryFrame().src = sourceToSet; 327 | currentHref = document.location.href; 328 | } 329 | } 330 | } 331 | } 332 | 333 | if (browser.safari && !browserHasHashChange) { 334 | // For Safari, we have to check to see if history.length changed. 335 | if (currentHistoryLength >= 0 && history.length != currentHistoryLength) { 336 | //alert("did change: " + history.length + ", " + historyHash.length + "|" + historyHash[history.length] + "|>" + historyHash.join("|")); 337 | var flexAppUrl = getHash(); 338 | if (browser.version < 528.16 /* Anything earlier than Safari 4.0 */) 339 | { 340 | // If it did change and we're running Safari 3.x or earlier, 341 | // then we have to look the old state up in our hand-maintained 342 | // array since document.location.hash won't have changed, 343 | // then call back into BrowserManager. 344 | currentHistoryLength = history.length; 345 | flexAppUrl = historyHash[currentHistoryLength]; 346 | } 347 | 348 | //ADR: to fix multiple 349 | if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { 350 | var pl = getPlayers(); 351 | for (var i = 0; i < pl.length; i++) { 352 | pl[i].browserURLChange(flexAppUrl); 353 | } 354 | } else { 355 | getPlayer().browserURLChange(flexAppUrl); 356 | } 357 | _storeStates(); 358 | } 359 | } 360 | if (browser.firefox && !browserHasHashChange) { 361 | if (currentHref != document.location.href) { 362 | var bsl = backStack.length; 363 | 364 | var urlActions = { 365 | back: false, 366 | forward: false, 367 | set: false 368 | } 369 | 370 | if ((window.location.hash == initialHash || window.location.href == initialHref) && (bsl == 1)) { 371 | urlActions.back = true; 372 | // FIXME: could this ever be a forward button? 373 | // we can't clear it because we still need to check for forwards. Ugg. 374 | // clearInterval(this.locationTimer); 375 | handleBackButton(); 376 | } 377 | 378 | // first check to see if we could have gone forward. We always halt on 379 | // a no-hash item. 380 | if (forwardStack.length > 0) { 381 | if (forwardStack[forwardStack.length-1].flexAppUrl == getHash()) { 382 | urlActions.forward = true; 383 | handleForwardButton(); 384 | } 385 | } 386 | 387 | // ok, that didn't work, try someplace back in the history stack 388 | if ((bsl >= 2) && (backStack[bsl - 2])) { 389 | if (backStack[bsl - 2].flexAppUrl == getHash()) { 390 | urlActions.back = true; 391 | handleBackButton(); 392 | } 393 | } 394 | 395 | if (!urlActions.back && !urlActions.forward) { 396 | var foundInStacks = { 397 | back: -1, 398 | forward: -1 399 | } 400 | 401 | for (var i = 0; i < backStack.length; i++) { 402 | if (backStack[i].flexAppUrl == getHash() && i != (bsl - 2)) { 403 | arbitraryUrl = true; 404 | foundInStacks.back = i; 405 | } 406 | } 407 | for (var i = 0; i < forwardStack.length; i++) { 408 | if (forwardStack[i].flexAppUrl == getHash() && i != (bsl - 2)) { 409 | arbitraryUrl = true; 410 | foundInStacks.forward = i; 411 | } 412 | } 413 | handleArbitraryUrl(); 414 | } 415 | 416 | // Firefox changed; do a callback into BrowserManager to tell it. 417 | currentHref = document.location.href; 418 | var flexAppUrl = getHash(); 419 | //ADR: to fix multiple 420 | if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { 421 | var pl = getPlayers(); 422 | for (var i = 0; i < pl.length; i++) { 423 | pl[i].browserURLChange(flexAppUrl); 424 | } 425 | } else { 426 | getPlayer().browserURLChange(flexAppUrl); 427 | } 428 | } 429 | } 430 | } 431 | 432 | var _initialize = function () { 433 | 434 | browserHasHashChange = ("onhashchange" in document.body); 435 | 436 | if (browser.ie) 437 | { 438 | var scripts = document.getElementsByTagName('script'); 439 | for (var i = 0, s; s = scripts[i]; i++) { 440 | if (s.src.indexOf("history.js") > -1) { 441 | var iframe_location = (new String(s.src)).replace("history.js", "historyFrame.html"); 442 | } 443 | } 444 | historyFrameSourcePrefix = iframe_location + "?"; 445 | var src = historyFrameSourcePrefix; 446 | 447 | var iframe = document.createElement("iframe"); 448 | iframe.id = 'ie_historyFrame'; 449 | iframe.name = 'ie_historyFrame'; 450 | iframe.src = 'javascript:false;'; 451 | 452 | try { 453 | document.body.appendChild(iframe); 454 | } catch(e) { 455 | setTimeout(function() { 456 | document.body.appendChild(iframe); 457 | }, 0); 458 | } 459 | } 460 | 461 | if (browser.safari && !browserHasHashChange) 462 | { 463 | var rememberDiv = document.createElement("div"); 464 | rememberDiv.id = 'safari_rememberDiv'; 465 | document.body.appendChild(rememberDiv); 466 | rememberDiv.innerHTML = ''; 467 | 468 | var formDiv = document.createElement("div"); 469 | formDiv.id = 'safari_formDiv'; 470 | document.body.appendChild(formDiv); 471 | 472 | var reloader_content = document.createElement('div'); 473 | reloader_content.id = 'safarireloader'; 474 | var scripts = document.getElementsByTagName('script'); 475 | for (var i = 0, s; s = scripts[i]; i++) { 476 | if (s.src.indexOf("history.js") > -1) { 477 | html = (new String(s.src)).replace(".js", ".html"); 478 | } 479 | } 480 | reloader_content.innerHTML = ''; 481 | document.body.appendChild(reloader_content); 482 | reloader_content.style.position = 'absolute'; 483 | reloader_content.style.left = reloader_content.style.top = '-9999px'; 484 | iframe = reloader_content.getElementsByTagName('iframe')[0]; 485 | 486 | if (document.getElementById("safari_remember_field").value != "" ) { 487 | historyHash = document.getElementById("safari_remember_field").value.split(","); 488 | } 489 | } 490 | 491 | if (browserHasHashChange) 492 | document.body.onhashchange = hashChangeHandler; 493 | } 494 | 495 | return { 496 | historyHash: historyHash, 497 | backStack: function() { return backStack; }, 498 | forwardStack: function() { return forwardStack }, 499 | getPlayer: getPlayer, 500 | initialize: function(src) { 501 | _initialize(src); 502 | }, 503 | setURL: function(url) { 504 | document.location.href = url; 505 | }, 506 | getURL: function() { 507 | return document.location.href; 508 | }, 509 | getTitle: function() { 510 | return document.title; 511 | }, 512 | setTitle: function(title) { 513 | try { 514 | backStack[backStack.length - 1].title = title; 515 | } catch(e) { } 516 | //if on safari, set the title to be the empty string. 517 | if (browser.safari) { 518 | if (title == "") { 519 | try { 520 | var tmp = window.location.href.toString(); 521 | title = tmp.substring((tmp.lastIndexOf("/")+1), tmp.lastIndexOf("#")); 522 | } catch(e) { 523 | title = ""; 524 | } 525 | } 526 | } 527 | document.title = title; 528 | }, 529 | setDefaultURL: function(def) 530 | { 531 | defaultHash = def; 532 | def = getHash(); 533 | //trailing ? is important else an extra frame gets added to the history 534 | //when navigating back to the first page. Alternatively could check 535 | //in history frame navigation to compare # and ?. 536 | if (browser.ie) 537 | { 538 | window['_ie_firstload'] = true; 539 | var sourceToSet = historyFrameSourcePrefix + def; 540 | var func = function() { 541 | getHistoryFrame().src = sourceToSet; 542 | window.location.replace("#" + def); 543 | setInterval(checkForUrlChange, 50); 544 | } 545 | try { 546 | func(); 547 | } catch(e) { 548 | window.setTimeout(function() { func(); }, 0); 549 | } 550 | } 551 | 552 | if (browser.safari) 553 | { 554 | currentHistoryLength = history.length; 555 | if (historyHash.length == 0) { 556 | historyHash[currentHistoryLength] = def; 557 | var newloc = "#" + def; 558 | window.location.replace(newloc); 559 | } else { 560 | //alert(historyHash[historyHash.length-1]); 561 | } 562 | setInterval(checkForUrlChange, 50); 563 | } 564 | 565 | 566 | if (browser.firefox || browser.opera) 567 | { 568 | var reg = new RegExp("#" + def + "$"); 569 | if (window.location.toString().match(reg)) { 570 | } else { 571 | var newloc ="#" + def; 572 | window.location.replace(newloc); 573 | } 574 | setInterval(checkForUrlChange, 50); 575 | } 576 | 577 | }, 578 | 579 | /* Set the current browser URL; called from inside BrowserManager to propagate 580 | * the application state out to the container. 581 | */ 582 | setBrowserURL: function(flexAppUrl, objectId) { 583 | if (browser.ie && typeof objectId != "undefined") { 584 | currentObjectId = objectId; 585 | } 586 | //fromIframe = fromIframe || false; 587 | //fromFlex = fromFlex || false; 588 | //alert("setBrowserURL: " + flexAppUrl); 589 | //flexAppUrl = (flexAppUrl == "") ? defaultHash : flexAppUrl ; 590 | 591 | var pos = document.location.href.indexOf('#'); 592 | var baseUrl = pos != -1 ? document.location.href.substr(0, pos) : document.location.href; 593 | var newUrl = baseUrl + '#' + flexAppUrl; 594 | 595 | if (document.location.href != newUrl && document.location.href + '#' != newUrl) { 596 | currentHref = newUrl; 597 | addHistoryEntry(baseUrl, newUrl, flexAppUrl); 598 | currentHistoryLength = history.length; 599 | } 600 | }, 601 | 602 | browserURLChange: function(flexAppUrl) { 603 | var objectId = null; 604 | if (browser.ie && currentObjectId != null) { 605 | objectId = currentObjectId; 606 | } 607 | 608 | if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { 609 | var pl = getPlayers(); 610 | for (var i = 0; i < pl.length; i++) { 611 | try { 612 | pl[i].browserURLChange(flexAppUrl); 613 | } catch(e) { } 614 | } 615 | } else { 616 | try { 617 | getPlayer(objectId).browserURLChange(flexAppUrl); 618 | } catch(e) { } 619 | } 620 | 621 | currentObjectId = null; 622 | }, 623 | getUserAgent: function() { 624 | return navigator.userAgent; 625 | }, 626 | getPlatform: function() { 627 | return navigator.platform; 628 | } 629 | 630 | } 631 | 632 | })(); 633 | 634 | // Initialization 635 | 636 | // Automated unit testing and other diagnostics 637 | 638 | function setURL(url) 639 | { 640 | document.location.href = url; 641 | } 642 | 643 | function backButton() 644 | { 645 | history.back(); 646 | } 647 | 648 | function forwardButton() 649 | { 650 | history.forward(); 651 | } 652 | 653 | function goForwardOrBackInHistory(step) 654 | { 655 | history.go(step); 656 | } 657 | 658 | //BrowserHistoryUtils.addEvent(window, "load", function() { BrowserHistory.initialize(); }); 659 | (function(i) { 660 | var u =navigator.userAgent;var e=/*@cc_on!@*/false; 661 | var st = setTimeout; 662 | if(/webkit/i.test(u)){ 663 | st(function(){ 664 | var dr=document.readyState; 665 | if(dr=="loaded"||dr=="complete"){i()} 666 | else{st(arguments.callee,10);}},10); 667 | } else if((/mozilla/i.test(u)&&!/(compati)/.test(u)) || (/opera/i.test(u))){ 668 | document.addEventListener("DOMContentLoaded",i,false); 669 | } else if(e){ 670 | (function(){ 671 | var t=document.createElement('doc:rdy'); 672 | try{t.doScroll('left'); 673 | i();t=null; 674 | }catch(e){st(arguments.callee,0);}})(); 675 | } else{ 676 | window.onload=i; 677 | } 678 | })( function() {BrowserHistory.initialize();} ); 679 | -------------------------------------------------------------------------------- /GifTest/html-template/swfobject.js: -------------------------------------------------------------------------------- 1 | /*! SWFObject v2.2 2 | is released under the MIT License 3 | */ 4 | 5 | var swfobject = function() { 6 | 7 | var UNDEF = "undefined", 8 | OBJECT = "object", 9 | SHOCKWAVE_FLASH = "Shockwave Flash", 10 | SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash", 11 | FLASH_MIME_TYPE = "application/x-shockwave-flash", 12 | EXPRESS_INSTALL_ID = "SWFObjectExprInst", 13 | ON_READY_STATE_CHANGE = "onreadystatechange", 14 | 15 | win = window, 16 | doc = document, 17 | nav = navigator, 18 | 19 | plugin = false, 20 | domLoadFnArr = [main], 21 | regObjArr = [], 22 | objIdArr = [], 23 | listenersArr = [], 24 | storedAltContent, 25 | storedAltContentId, 26 | storedCallbackFn, 27 | storedCallbackObj, 28 | isDomLoaded = false, 29 | isExpressInstallActive = false, 30 | dynamicStylesheet, 31 | dynamicStylesheetMedia, 32 | autoHideShow = true, 33 | 34 | /* Centralized function for browser feature detection 35 | - User agent string detection is only used when no good alternative is possible 36 | - Is executed directly for optimal performance 37 | */ 38 | ua = function() { 39 | var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF, 40 | u = nav.userAgent.toLowerCase(), 41 | p = nav.platform.toLowerCase(), 42 | windows = p ? /win/.test(p) : /win/.test(u), 43 | mac = p ? /mac/.test(p) : /mac/.test(u), 44 | webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit 45 | ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html 46 | playerVersion = [0,0,0], 47 | d = null; 48 | if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) { 49 | d = nav.plugins[SHOCKWAVE_FLASH].description; 50 | if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+ 51 | plugin = true; 52 | ie = false; // cascaded feature detection for Internet Explorer 53 | d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1"); 54 | playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10); 55 | playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10); 56 | playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0; 57 | } 58 | } 59 | else if (typeof win.ActiveXObject != UNDEF) { 60 | try { 61 | var a = new ActiveXObject(SHOCKWAVE_FLASH_AX); 62 | if (a) { // a will return null when ActiveX is disabled 63 | d = a.GetVariable("$version"); 64 | if (d) { 65 | ie = true; // cascaded feature detection for Internet Explorer 66 | d = d.split(" ")[1].split(","); 67 | playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; 68 | } 69 | } 70 | } 71 | catch(e) {} 72 | } 73 | return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac }; 74 | }(), 75 | 76 | /* Cross-browser onDomLoad 77 | - Will fire an event as soon as the DOM of a web page is loaded 78 | - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/ 79 | - Regular onload serves as fallback 80 | */ 81 | onDomLoad = function() { 82 | if (!ua.w3) { return; } 83 | if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically 84 | callDomLoadFunctions(); 85 | } 86 | if (!isDomLoaded) { 87 | if (typeof doc.addEventListener != UNDEF) { 88 | doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false); 89 | } 90 | if (ua.ie && ua.win) { 91 | doc.attachEvent(ON_READY_STATE_CHANGE, function() { 92 | if (doc.readyState == "complete") { 93 | doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee); 94 | callDomLoadFunctions(); 95 | } 96 | }); 97 | if (win == top) { // if not inside an iframe 98 | (function(){ 99 | if (isDomLoaded) { return; } 100 | try { 101 | doc.documentElement.doScroll("left"); 102 | } 103 | catch(e) { 104 | setTimeout(arguments.callee, 0); 105 | return; 106 | } 107 | callDomLoadFunctions(); 108 | })(); 109 | } 110 | } 111 | if (ua.wk) { 112 | (function(){ 113 | if (isDomLoaded) { return; } 114 | if (!/loaded|complete/.test(doc.readyState)) { 115 | setTimeout(arguments.callee, 0); 116 | return; 117 | } 118 | callDomLoadFunctions(); 119 | })(); 120 | } 121 | addLoadEvent(callDomLoadFunctions); 122 | } 123 | }(); 124 | 125 | function callDomLoadFunctions() { 126 | if (isDomLoaded) { return; } 127 | try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early 128 | var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span")); 129 | t.parentNode.removeChild(t); 130 | } 131 | catch (e) { return; } 132 | isDomLoaded = true; 133 | var dl = domLoadFnArr.length; 134 | for (var i = 0; i < dl; i++) { 135 | domLoadFnArr[i](); 136 | } 137 | } 138 | 139 | function addDomLoadEvent(fn) { 140 | if (isDomLoaded) { 141 | fn(); 142 | } 143 | else { 144 | domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+ 145 | } 146 | } 147 | 148 | /* Cross-browser onload 149 | - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/ 150 | - Will fire an event as soon as a web page including all of its assets are loaded 151 | */ 152 | function addLoadEvent(fn) { 153 | if (typeof win.addEventListener != UNDEF) { 154 | win.addEventListener("load", fn, false); 155 | } 156 | else if (typeof doc.addEventListener != UNDEF) { 157 | doc.addEventListener("load", fn, false); 158 | } 159 | else if (typeof win.attachEvent != UNDEF) { 160 | addListener(win, "onload", fn); 161 | } 162 | else if (typeof win.onload == "function") { 163 | var fnOld = win.onload; 164 | win.onload = function() { 165 | fnOld(); 166 | fn(); 167 | }; 168 | } 169 | else { 170 | win.onload = fn; 171 | } 172 | } 173 | 174 | /* Main function 175 | - Will preferably execute onDomLoad, otherwise onload (as a fallback) 176 | */ 177 | function main() { 178 | if (plugin) { 179 | testPlayerVersion(); 180 | } 181 | else { 182 | matchVersions(); 183 | } 184 | } 185 | 186 | /* Detect the Flash Player version for non-Internet Explorer browsers 187 | - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description: 188 | a. Both release and build numbers can be detected 189 | b. Avoid wrong descriptions by corrupt installers provided by Adobe 190 | c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports 191 | - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available 192 | */ 193 | function testPlayerVersion() { 194 | var b = doc.getElementsByTagName("body")[0]; 195 | var o = createElement(OBJECT); 196 | o.setAttribute("type", FLASH_MIME_TYPE); 197 | var t = b.appendChild(o); 198 | if (t) { 199 | var counter = 0; 200 | (function(){ 201 | if (typeof t.GetVariable != UNDEF) { 202 | var d = t.GetVariable("$version"); 203 | if (d) { 204 | d = d.split(" ")[1].split(","); 205 | ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; 206 | } 207 | } 208 | else if (counter < 10) { 209 | counter++; 210 | setTimeout(arguments.callee, 10); 211 | return; 212 | } 213 | b.removeChild(o); 214 | t = null; 215 | matchVersions(); 216 | })(); 217 | } 218 | else { 219 | matchVersions(); 220 | } 221 | } 222 | 223 | /* Perform Flash Player and SWF version matching; static publishing only 224 | */ 225 | function matchVersions() { 226 | var rl = regObjArr.length; 227 | if (rl > 0) { 228 | for (var i = 0; i < rl; i++) { // for each registered object element 229 | var id = regObjArr[i].id; 230 | var cb = regObjArr[i].callbackFn; 231 | var cbObj = {success:false, id:id}; 232 | if (ua.pv[0] > 0) { 233 | var obj = getElementById(id); 234 | if (obj) { 235 | if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match! 236 | setVisibility(id, true); 237 | if (cb) { 238 | cbObj.success = true; 239 | cbObj.ref = getObjectById(id); 240 | cb(cbObj); 241 | } 242 | } 243 | else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported 244 | var att = {}; 245 | att.data = regObjArr[i].expressInstall; 246 | att.width = obj.getAttribute("width") || "0"; 247 | att.height = obj.getAttribute("height") || "0"; 248 | if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); } 249 | if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); } 250 | // parse HTML object param element's name-value pairs 251 | var par = {}; 252 | var p = obj.getElementsByTagName("param"); 253 | var pl = p.length; 254 | for (var j = 0; j < pl; j++) { 255 | if (p[j].getAttribute("name").toLowerCase() != "movie") { 256 | par[p[j].getAttribute("name")] = p[j].getAttribute("value"); 257 | } 258 | } 259 | showExpressInstall(att, par, id, cb); 260 | } 261 | else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF 262 | displayAltContent(obj); 263 | if (cb) { cb(cbObj); } 264 | } 265 | } 266 | } 267 | else { // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content) 268 | setVisibility(id, true); 269 | if (cb) { 270 | var o = getObjectById(id); // test whether there is an HTML object element or not 271 | if (o && typeof o.SetVariable != UNDEF) { 272 | cbObj.success = true; 273 | cbObj.ref = o; 274 | } 275 | cb(cbObj); 276 | } 277 | } 278 | } 279 | } 280 | } 281 | 282 | function getObjectById(objectIdStr) { 283 | var r = null; 284 | var o = getElementById(objectIdStr); 285 | if (o && o.nodeName == "OBJECT") { 286 | if (typeof o.SetVariable != UNDEF) { 287 | r = o; 288 | } 289 | else { 290 | var n = o.getElementsByTagName(OBJECT)[0]; 291 | if (n) { 292 | r = n; 293 | } 294 | } 295 | } 296 | return r; 297 | } 298 | 299 | /* Requirements for Adobe Express Install 300 | - only one instance can be active at a time 301 | - fp 6.0.65 or higher 302 | - Win/Mac OS only 303 | - no Webkit engines older than version 312 304 | */ 305 | function canExpressInstall() { 306 | return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312); 307 | } 308 | 309 | /* Show the Adobe Express Install dialog 310 | - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75 311 | */ 312 | function showExpressInstall(att, par, replaceElemIdStr, callbackFn) { 313 | isExpressInstallActive = true; 314 | storedCallbackFn = callbackFn || null; 315 | storedCallbackObj = {success:false, id:replaceElemIdStr}; 316 | var obj = getElementById(replaceElemIdStr); 317 | if (obj) { 318 | if (obj.nodeName == "OBJECT") { // static publishing 319 | storedAltContent = abstractAltContent(obj); 320 | storedAltContentId = null; 321 | } 322 | else { // dynamic publishing 323 | storedAltContent = obj; 324 | storedAltContentId = replaceElemIdStr; 325 | } 326 | att.id = EXPRESS_INSTALL_ID; 327 | if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; } 328 | if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; } 329 | doc.title = doc.title.slice(0, 47) + " - Flash Player Installation"; 330 | var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn", 331 | fv = "MMredirectURL=" + encodeURI(window.location).toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title; 332 | if (typeof par.flashvars != UNDEF) { 333 | par.flashvars += "&" + fv; 334 | } 335 | else { 336 | par.flashvars = fv; 337 | } 338 | // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it, 339 | // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work 340 | if (ua.ie && ua.win && obj.readyState != 4) { 341 | var newObj = createElement("div"); 342 | replaceElemIdStr += "SWFObjectNew"; 343 | newObj.setAttribute("id", replaceElemIdStr); 344 | obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf 345 | obj.style.display = "none"; 346 | (function(){ 347 | if (obj.readyState == 4) { 348 | obj.parentNode.removeChild(obj); 349 | } 350 | else { 351 | setTimeout(arguments.callee, 10); 352 | } 353 | })(); 354 | } 355 | createSWF(att, par, replaceElemIdStr); 356 | } 357 | } 358 | 359 | /* Functions to abstract and display alternative content 360 | */ 361 | function displayAltContent(obj) { 362 | if (ua.ie && ua.win && obj.readyState != 4) { 363 | // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it, 364 | // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work 365 | var el = createElement("div"); 366 | obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content 367 | el.parentNode.replaceChild(abstractAltContent(obj), el); 368 | obj.style.display = "none"; 369 | (function(){ 370 | if (obj.readyState == 4) { 371 | obj.parentNode.removeChild(obj); 372 | } 373 | else { 374 | setTimeout(arguments.callee, 10); 375 | } 376 | })(); 377 | } 378 | else { 379 | obj.parentNode.replaceChild(abstractAltContent(obj), obj); 380 | } 381 | } 382 | 383 | function abstractAltContent(obj) { 384 | var ac = createElement("div"); 385 | if (ua.win && ua.ie) { 386 | ac.innerHTML = obj.innerHTML; 387 | } 388 | else { 389 | var nestedObj = obj.getElementsByTagName(OBJECT)[0]; 390 | if (nestedObj) { 391 | var c = nestedObj.childNodes; 392 | if (c) { 393 | var cl = c.length; 394 | for (var i = 0; i < cl; i++) { 395 | if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) { 396 | ac.appendChild(c[i].cloneNode(true)); 397 | } 398 | } 399 | } 400 | } 401 | } 402 | return ac; 403 | } 404 | 405 | /* Cross-browser dynamic SWF creation 406 | */ 407 | function createSWF(attObj, parObj, id) { 408 | var r, el = getElementById(id); 409 | if (ua.wk && ua.wk < 312) { return r; } 410 | if (el) { 411 | if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content 412 | attObj.id = id; 413 | } 414 | if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML 415 | var att = ""; 416 | for (var i in attObj) { 417 | if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries 418 | if (i.toLowerCase() == "data") { 419 | parObj.movie = attObj[i]; 420 | } 421 | else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword 422 | att += ' class="' + attObj[i] + '"'; 423 | } 424 | else if (i.toLowerCase() != "classid") { 425 | att += ' ' + i + '="' + attObj[i] + '"'; 426 | } 427 | } 428 | } 429 | var par = ""; 430 | for (var j in parObj) { 431 | if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries 432 | par += ''; 433 | } 434 | } 435 | el.outerHTML = '' + par + ''; 436 | objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only) 437 | r = getElementById(attObj.id); 438 | } 439 | else { // well-behaving browsers 440 | var o = createElement(OBJECT); 441 | o.setAttribute("type", FLASH_MIME_TYPE); 442 | for (var m in attObj) { 443 | if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries 444 | if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword 445 | o.setAttribute("class", attObj[m]); 446 | } 447 | else if (m.toLowerCase() != "classid") { // filter out IE specific attribute 448 | o.setAttribute(m, attObj[m]); 449 | } 450 | } 451 | } 452 | for (var n in parObj) { 453 | if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element 454 | createObjParam(o, n, parObj[n]); 455 | } 456 | } 457 | el.parentNode.replaceChild(o, el); 458 | r = o; 459 | } 460 | } 461 | return r; 462 | } 463 | 464 | function createObjParam(el, pName, pValue) { 465 | var p = createElement("param"); 466 | p.setAttribute("name", pName); 467 | p.setAttribute("value", pValue); 468 | el.appendChild(p); 469 | } 470 | 471 | /* Cross-browser SWF removal 472 | - Especially needed to safely and completely remove a SWF in Internet Explorer 473 | */ 474 | function removeSWF(id) { 475 | var obj = getElementById(id); 476 | if (obj && obj.nodeName == "OBJECT") { 477 | if (ua.ie && ua.win) { 478 | obj.style.display = "none"; 479 | (function(){ 480 | if (obj.readyState == 4) { 481 | removeObjectInIE(id); 482 | } 483 | else { 484 | setTimeout(arguments.callee, 10); 485 | } 486 | })(); 487 | } 488 | else { 489 | obj.parentNode.removeChild(obj); 490 | } 491 | } 492 | } 493 | 494 | function removeObjectInIE(id) { 495 | var obj = getElementById(id); 496 | if (obj) { 497 | for (var i in obj) { 498 | if (typeof obj[i] == "function") { 499 | obj[i] = null; 500 | } 501 | } 502 | obj.parentNode.removeChild(obj); 503 | } 504 | } 505 | 506 | /* Functions to optimize JavaScript compression 507 | */ 508 | function getElementById(id) { 509 | var el = null; 510 | try { 511 | el = doc.getElementById(id); 512 | } 513 | catch (e) {} 514 | return el; 515 | } 516 | 517 | function createElement(el) { 518 | return doc.createElement(el); 519 | } 520 | 521 | /* Updated attachEvent function for Internet Explorer 522 | - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks 523 | */ 524 | function addListener(target, eventType, fn) { 525 | target.attachEvent(eventType, fn); 526 | listenersArr[listenersArr.length] = [target, eventType, fn]; 527 | } 528 | 529 | /* Flash Player and SWF content version matching 530 | */ 531 | function hasPlayerVersion(rv) { 532 | var pv = ua.pv, v = rv.split("."); 533 | v[0] = parseInt(v[0], 10); 534 | v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0" 535 | v[2] = parseInt(v[2], 10) || 0; 536 | return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false; 537 | } 538 | 539 | /* Cross-browser dynamic CSS creation 540 | - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php 541 | */ 542 | function createCSS(sel, decl, media, newStyle) { 543 | if (ua.ie && ua.mac) { return; } 544 | var h = doc.getElementsByTagName("head")[0]; 545 | if (!h) { return; } // to also support badly authored HTML pages that lack a head element 546 | var m = (media && typeof media == "string") ? media : "screen"; 547 | if (newStyle) { 548 | dynamicStylesheet = null; 549 | dynamicStylesheetMedia = null; 550 | } 551 | if (!dynamicStylesheet || dynamicStylesheetMedia != m) { 552 | // create dynamic stylesheet + get a global reference to it 553 | var s = createElement("style"); 554 | s.setAttribute("type", "text/css"); 555 | s.setAttribute("media", m); 556 | dynamicStylesheet = h.appendChild(s); 557 | if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) { 558 | dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1]; 559 | } 560 | dynamicStylesheetMedia = m; 561 | } 562 | // add style rule 563 | if (ua.ie && ua.win) { 564 | if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) { 565 | dynamicStylesheet.addRule(sel, decl); 566 | } 567 | } 568 | else { 569 | if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) { 570 | dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}")); 571 | } 572 | } 573 | } 574 | 575 | function setVisibility(id, isVisible) { 576 | if (!autoHideShow) { return; } 577 | var v = isVisible ? "visible" : "hidden"; 578 | if (isDomLoaded && getElementById(id)) { 579 | getElementById(id).style.visibility = v; 580 | } 581 | else { 582 | createCSS("#" + id, "visibility:" + v); 583 | } 584 | } 585 | 586 | /* Filter to avoid XSS attacks 587 | */ 588 | function urlEncodeIfNecessary(s) { 589 | var regex = /[\\\"<>\.;]/; 590 | var hasBadChars = regex.exec(s) != null; 591 | return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s; 592 | } 593 | 594 | /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only) 595 | */ 596 | var cleanup = function() { 597 | if (ua.ie && ua.win) { 598 | window.attachEvent("onunload", function() { 599 | // remove listeners to avoid memory leaks 600 | var ll = listenersArr.length; 601 | for (var i = 0; i < ll; i++) { 602 | listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]); 603 | } 604 | // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect 605 | var il = objIdArr.length; 606 | for (var j = 0; j < il; j++) { 607 | removeSWF(objIdArr[j]); 608 | } 609 | // cleanup library's main closures to avoid memory leaks 610 | for (var k in ua) { 611 | ua[k] = null; 612 | } 613 | ua = null; 614 | for (var l in swfobject) { 615 | swfobject[l] = null; 616 | } 617 | swfobject = null; 618 | }); 619 | } 620 | }(); 621 | 622 | return { 623 | /* Public API 624 | - Reference: http://code.google.com/p/swfobject/wiki/documentation 625 | */ 626 | registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) { 627 | if (ua.w3 && objectIdStr && swfVersionStr) { 628 | var regObj = {}; 629 | regObj.id = objectIdStr; 630 | regObj.swfVersion = swfVersionStr; 631 | regObj.expressInstall = xiSwfUrlStr; 632 | regObj.callbackFn = callbackFn; 633 | regObjArr[regObjArr.length] = regObj; 634 | setVisibility(objectIdStr, false); 635 | } 636 | else if (callbackFn) { 637 | callbackFn({success:false, id:objectIdStr}); 638 | } 639 | }, 640 | 641 | getObjectById: function(objectIdStr) { 642 | if (ua.w3) { 643 | return getObjectById(objectIdStr); 644 | } 645 | }, 646 | 647 | embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) { 648 | var callbackObj = {success:false, id:replaceElemIdStr}; 649 | if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) { 650 | setVisibility(replaceElemIdStr, false); 651 | addDomLoadEvent(function() { 652 | widthStr += ""; // auto-convert to string 653 | heightStr += ""; 654 | var att = {}; 655 | if (attObj && typeof attObj === OBJECT) { 656 | for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs 657 | att[i] = attObj[i]; 658 | } 659 | } 660 | att.data = swfUrlStr; 661 | att.width = widthStr; 662 | att.height = heightStr; 663 | var par = {}; 664 | if (parObj && typeof parObj === OBJECT) { 665 | for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs 666 | par[j] = parObj[j]; 667 | } 668 | } 669 | if (flashvarsObj && typeof flashvarsObj === OBJECT) { 670 | for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs 671 | if (typeof par.flashvars != UNDEF) { 672 | par.flashvars += "&" + k + "=" + flashvarsObj[k]; 673 | } 674 | else { 675 | par.flashvars = k + "=" + flashvarsObj[k]; 676 | } 677 | } 678 | } 679 | if (hasPlayerVersion(swfVersionStr)) { // create SWF 680 | var obj = createSWF(att, par, replaceElemIdStr); 681 | if (att.id == replaceElemIdStr) { 682 | setVisibility(replaceElemIdStr, true); 683 | } 684 | callbackObj.success = true; 685 | callbackObj.ref = obj; 686 | } 687 | else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install 688 | att.data = xiSwfUrlStr; 689 | showExpressInstall(att, par, replaceElemIdStr, callbackFn); 690 | return; 691 | } 692 | else { // show alternative content 693 | setVisibility(replaceElemIdStr, true); 694 | } 695 | if (callbackFn) { callbackFn(callbackObj); } 696 | }); 697 | } 698 | else if (callbackFn) { callbackFn(callbackObj); } 699 | }, 700 | 701 | switchOffAutoHideShow: function() { 702 | autoHideShow = false; 703 | }, 704 | 705 | ua: ua, 706 | 707 | getFlashPlayerVersion: function() { 708 | return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] }; 709 | }, 710 | 711 | hasFlashPlayerVersion: hasPlayerVersion, 712 | 713 | createSWF: function(attObj, parObj, replaceElemIdStr) { 714 | if (ua.w3) { 715 | return createSWF(attObj, parObj, replaceElemIdStr); 716 | } 717 | else { 718 | return undefined; 719 | } 720 | }, 721 | 722 | showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) { 723 | if (ua.w3 && canExpressInstall()) { 724 | showExpressInstall(att, par, replaceElemIdStr, callbackFn); 725 | } 726 | }, 727 | 728 | removeSWF: function(objElemIdStr) { 729 | if (ua.w3) { 730 | removeSWF(objElemIdStr); 731 | } 732 | }, 733 | 734 | createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) { 735 | if (ua.w3) { 736 | createCSS(selStr, declStr, mediaStr, newStyleBoolean); 737 | } 738 | }, 739 | 740 | addDomLoadEvent: addDomLoadEvent, 741 | 742 | addLoadEvent: addLoadEvent, 743 | 744 | getQueryParamValue: function(param) { 745 | var q = doc.location.search || doc.location.hash; 746 | if (q) { 747 | if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark 748 | if (param == null) { 749 | return urlEncodeIfNecessary(q); 750 | } 751 | var pairs = q.split("&"); 752 | for (var i = 0; i < pairs.length; i++) { 753 | if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) { 754 | return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1))); 755 | } 756 | } 757 | } 758 | return ""; 759 | }, 760 | 761 | // For internal usage only 762 | expressInstallCallback: function() { 763 | if (isExpressInstallActive) { 764 | var obj = getElementById(EXPRESS_INSTALL_ID); 765 | if (obj && storedAltContent) { 766 | obj.parentNode.replaceChild(storedAltContent, obj); 767 | if (storedAltContentId) { 768 | setVisibility(storedAltContentId, true); 769 | if (ua.ie && ua.win) { storedAltContent.style.display = "block"; } 770 | } 771 | if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); } 772 | } 773 | isExpressInstallActive = false; 774 | } 775 | } 776 | }; 777 | }(); 778 | --------------------------------------------------------------------------------