mirror of
				https://github.com/jiawanlong/Cesium-Examples.git
				synced 2025-11-03 16:54:16 +00:00 
			
		
		
		
	
		
			
	
	
		
			991 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			991 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
									SuperGif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									Example usage:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										<img src="./example1_preview.gif" rel:animated_src="./example1.gif" width="360" height="360" rel:auto_play="1" />
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										<script type="text/javascript">
							 | 
						||
| 
								 | 
							
											$$('img').each(function (img_tag) {
							 | 
						||
| 
								 | 
							
												if (/.*\.gif/.test(img_tag.src)) {
							 | 
						||
| 
								 | 
							
													var rub = new SuperGif({ gif: img_tag } );
							 | 
						||
| 
								 | 
							
													rub.load();
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											});
							 | 
						||
| 
								 | 
							
										</script>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									Image tag attributes:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										rel:animated_src -	If this url is specified, it's loaded into the player instead of src.
							 | 
						||
| 
								 | 
							
															This allows a preview frame to be shown until animated gif data is streamed into the canvas
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										rel:auto_play -		Defaults to 1 if not specified. If set to zero, a call to the play() method is needed
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									Constructor options args
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										gif 				Required. The DOM element of an img tag.
							 | 
						||
| 
								 | 
							
										loop_mode			Optional. Setting this to false will force disable looping of the gif.
							 | 
						||
| 
								 | 
							
										auto_play 			Optional. Same as the rel:auto_play attribute above, this arg overrides the img tag info.
							 | 
						||
| 
								 | 
							
										max_width			Optional. Scale images over max_width down to max_width. Helpful with mobile.
							 | 
						||
| 
								 | 
							
								 		on_end				Optional. Add a callback for when the gif reaches the end of a single loop (one iteration). The first argument passed will be the gif HTMLElement.
							 | 
						||
| 
								 | 
							
										loop_delay			Optional. The amount of time to pause (in ms) after each single loop (iteration).
							 | 
						||
| 
								 | 
							
										draw_while_loading	Optional. Determines whether the gif will be drawn to the canvas whilst it is loaded.
							 | 
						||
| 
								 | 
							
										show_progress_bar	Optional. Only applies when draw_while_loading is set to true.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									Instance methods
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// loading
							 | 
						||
| 
								 | 
							
										load( callback )		Loads the gif specified by the src or rel:animated_src sttributie of the img tag into a canvas element and then calls callback if one is passed
							 | 
						||
| 
								 | 
							
										load_url( src, callback )	Loads the gif file specified in the src argument into a canvas element and then calls callback if one is passed
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// play controls
							 | 
						||
| 
								 | 
							
										play -				Start playing the gif
							 | 
						||
| 
								 | 
							
										pause -				Stop playing the gif
							 | 
						||
| 
								 | 
							
										move_to(i) -		Move to frame i of the gif
							 | 
						||
| 
								 | 
							
										move_relative(i) -	Move i frames ahead (or behind if i < 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// getters
							 | 
						||
| 
								 | 
							
										get_canvas			The canvas element that the gif is playing in. Handy for assigning event handlers to.
							 | 
						||
| 
								 | 
							
										get_playing			Whether or not the gif is currently playing
							 | 
						||
| 
								 | 
							
										get_loading			Whether or not the gif has finished loading/parsing
							 | 
						||
| 
								 | 
							
										get_auto_play		Whether or not the gif is set to play automatically
							 | 
						||
| 
								 | 
							
										get_length			The number of frames in the gif
							 | 
						||
| 
								 | 
							
										get_current_frame	The index of the currently displayed frame of the gif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										For additional customization (viewport inside iframe) these params may be passed:
							 | 
						||
| 
								 | 
							
										c_w, c_h - width and height of canvas
							 | 
						||
| 
								 | 
							
										vp_t, vp_l, vp_ w, vp_h - top, left, width and height of the viewport
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										A bonus: few articles to understand what is going on
							 | 
						||
| 
								 | 
							
											http://enthusiasms.org/post/16976438906
							 | 
						||
| 
								 | 
							
											http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
							 | 
						||
| 
								 | 
							
											http://humpy77.deviantart.com/journal/Frame-Delay-Times-for-Animated-GIFs-214150546
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								(function (root, factory) {
							 | 
						||
| 
								 | 
							
								    if (typeof define === 'function' && define.amd) {
							 | 
						||
| 
								 | 
							
								        define([], factory);
							 | 
						||
| 
								 | 
							
								    } else if (typeof exports === 'object') {
							 | 
						||
| 
								 | 
							
								        module.exports = factory();
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								        root.SuperGif = factory();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}(this, function () {
							 | 
						||
| 
								 | 
							
								    // Generic functions
							 | 
						||
| 
								 | 
							
								    var bitsToNum = function (ba) {
							 | 
						||
| 
								 | 
							
								        return ba.reduce(function (s, n) {
							 | 
						||
| 
								 | 
							
								            return s * 2 + n;
							 | 
						||
| 
								 | 
							
								        }, 0);
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var byteToBitArr = function (bite) {
							 | 
						||
| 
								 | 
							
								        var a = [];
							 | 
						||
| 
								 | 
							
								        for (var i = 7; i >= 0; i--) {
							 | 
						||
| 
								 | 
							
								            a.push( !! (bite & (1 << i)));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return a;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Stream
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @constructor
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    // Make compiler happy.
							 | 
						||
| 
								 | 
							
								    var Stream = function (data) {
							 | 
						||
| 
								 | 
							
								        this.data = data;
							 | 
						||
| 
								 | 
							
								        this.len = this.data.length;
							 | 
						||
| 
								 | 
							
								        this.pos = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this.readByte = function () {
							 | 
						||
| 
								 | 
							
								            if (this.pos >= this.data.length) {
							 | 
						||
| 
								 | 
							
								                throw new Error('Attempted to read past end of stream.');
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (data instanceof Uint8Array)
							 | 
						||
| 
								 | 
							
								                return data[this.pos++];
							 | 
						||
| 
								 | 
							
								            else
							 | 
						||
| 
								 | 
							
								                return data.charCodeAt(this.pos++) & 0xFF;
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this.readBytes = function (n) {
							 | 
						||
| 
								 | 
							
								            var bytes = [];
							 | 
						||
| 
								 | 
							
								            for (var i = 0; i < n; i++) {
							 | 
						||
| 
								 | 
							
								                bytes.push(this.readByte());
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return bytes;
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this.read = function (n) {
							 | 
						||
| 
								 | 
							
								            var s = '';
							 | 
						||
| 
								 | 
							
								            for (var i = 0; i < n; i++) {
							 | 
						||
| 
								 | 
							
								                s += String.fromCharCode(this.readByte());
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return s;
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        this.readUnsigned = function () { // Little-endian.
							 | 
						||
| 
								 | 
							
								            var a = this.readBytes(2);
							 | 
						||
| 
								 | 
							
								            return (a[1] << 8) + a[0];
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var lzwDecode = function (minCodeSize, data) {
							 | 
						||
| 
								 | 
							
								        // TODO: Now that the GIF parser is a bit different, maybe this should get an array of bytes instead of a String?
							 | 
						||
| 
								 | 
							
								        var pos = 0; // Maybe this streaming thing should be merged with the Stream?
							 | 
						||
| 
								 | 
							
								        var readCode = function (size) {
							 | 
						||
| 
								 | 
							
								            var code = 0;
							 | 
						||
| 
								 | 
							
								            for (var i = 0; i < size; i++) {
							 | 
						||
| 
								 | 
							
								                if (data.charCodeAt(pos >> 3) & (1 << (pos & 7))) {
							 | 
						||
| 
								 | 
							
								                    code |= 1 << i;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                pos++;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return code;
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var output = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var clearCode = 1 << minCodeSize;
							 | 
						||
| 
								 | 
							
								        var eoiCode = clearCode + 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var codeSize = minCodeSize + 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var dict = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var clear = function () {
							 | 
						||
| 
								 | 
							
								            dict = [];
							 | 
						||
| 
								 | 
							
								            codeSize = minCodeSize + 1;
							 | 
						||
| 
								 | 
							
								            for (var i = 0; i < clearCode; i++) {
							 | 
						||
| 
								 | 
							
								                dict[i] = [i];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            dict[clearCode] = [];
							 | 
						||
| 
								 | 
							
								            dict[eoiCode] = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var code;
							 | 
						||
| 
								 | 
							
								        var last;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        while (true) {
							 | 
						||
| 
								 | 
							
								            last = code;
							 | 
						||
| 
								 | 
							
								            code = readCode(codeSize);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (code === clearCode) {
							 | 
						||
| 
								 | 
							
								                clear();
							 | 
						||
| 
								 | 
							
								                continue;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (code === eoiCode) break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (code < dict.length) {
							 | 
						||
| 
								 | 
							
								                if (last !== clearCode) {
							 | 
						||
| 
								 | 
							
								                    dict.push(dict[last].concat(dict[code][0]));
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                if (code !== dict.length) throw new Error('Invalid LZW code.');
							 | 
						||
| 
								 | 
							
								                dict.push(dict[last].concat(dict[last][0]));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            output.push.apply(output, dict[code]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (dict.length === (1 << codeSize) && codeSize < 12) {
							 | 
						||
| 
								 | 
							
								                // If we're at the last code and codeSize is 12, the next code will be a clearCode, and it'll be 12 bits long.
							 | 
						||
| 
								 | 
							
								                codeSize++;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // I don't know if this is technically an error, but some GIFs do it.
							 | 
						||
| 
								 | 
							
								        //if (Math.ceil(pos / 8) !== data.length) throw new Error('Extraneous LZW bytes.');
							 | 
						||
| 
								 | 
							
								        return output;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // The actual parsing; returns an object with properties.
							 | 
						||
| 
								 | 
							
								    var parseGIF = function (st, handler) {
							 | 
						||
| 
								 | 
							
								        handler || (handler = {});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // LZW (GIF-specific)
							 | 
						||
| 
								 | 
							
								        var parseCT = function (entries) { // Each entry is 3 bytes, for RGB.
							 | 
						||
| 
								 | 
							
								            var ct = [];
							 | 
						||
| 
								 | 
							
								            for (var i = 0; i < entries; i++) {
							 | 
						||
| 
								 | 
							
								                ct.push(st.readBytes(3));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return ct;
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var readSubBlocks = function () {
							 | 
						||
| 
								 | 
							
								            var size, data;
							 | 
						||
| 
								 | 
							
								            data = '';
							 | 
						||
| 
								 | 
							
								            do {
							 | 
						||
| 
								 | 
							
								                size = st.readByte();
							 | 
						||
| 
								 | 
							
								                data += st.read(size);
							 | 
						||
| 
								 | 
							
								            } while (size !== 0);
							 | 
						||
| 
								 | 
							
								            return data;
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var parseHeader = function () {
							 | 
						||
| 
								 | 
							
								            var hdr = {};
							 | 
						||
| 
								 | 
							
								            hdr.sig = st.read(3);
							 | 
						||
| 
								 | 
							
								            hdr.ver = st.read(3);
							 | 
						||
| 
								 | 
							
								            if (hdr.sig !== 'GIF') throw new Error('Not a GIF file.'); // XXX: This should probably be handled more nicely.
							 | 
						||
| 
								 | 
							
								            hdr.width = st.readUnsigned();
							 | 
						||
| 
								 | 
							
								            hdr.height = st.readUnsigned();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var bits = byteToBitArr(st.readByte());
							 | 
						||
| 
								 | 
							
								            hdr.gctFlag = bits.shift();
							 | 
						||
| 
								 | 
							
								            hdr.colorRes = bitsToNum(bits.splice(0, 3));
							 | 
						||
| 
								 | 
							
								            hdr.sorted = bits.shift();
							 | 
						||
| 
								 | 
							
								            hdr.gctSize = bitsToNum(bits.splice(0, 3));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            hdr.bgColor = st.readByte();
							 | 
						||
| 
								 | 
							
								            hdr.pixelAspectRatio = st.readByte(); // if not 0, aspectRatio = (pixelAspectRatio + 15) / 64
							 | 
						||
| 
								 | 
							
								            if (hdr.gctFlag) {
							 | 
						||
| 
								 | 
							
								                hdr.gct = parseCT(1 << (hdr.gctSize + 1));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            handler.hdr && handler.hdr(hdr);
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var parseExt = function (block) {
							 | 
						||
| 
								 | 
							
								            var parseGCExt = function (block) {
							 | 
						||
| 
								 | 
							
								                var blockSize = st.readByte(); // Always 4
							 | 
						||
| 
								 | 
							
								                var bits = byteToBitArr(st.readByte());
							 | 
						||
| 
								 | 
							
								                block.reserved = bits.splice(0, 3); // Reserved; should be 000.
							 | 
						||
| 
								 | 
							
								                block.disposalMethod = bitsToNum(bits.splice(0, 3));
							 | 
						||
| 
								 | 
							
								                block.userInput = bits.shift();
							 | 
						||
| 
								 | 
							
								                block.transparencyGiven = bits.shift();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                block.delayTime = st.readUnsigned();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                block.transparencyIndex = st.readByte();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                block.terminator = st.readByte();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                handler.gce && handler.gce(block);
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var parseComExt = function (block) {
							 | 
						||
| 
								 | 
							
								                block.comment = readSubBlocks();
							 | 
						||
| 
								 | 
							
								                handler.com && handler.com(block);
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var parsePTExt = function (block) {
							 | 
						||
| 
								 | 
							
								                // No one *ever* uses this. If you use it, deal with parsing it yourself.
							 | 
						||
| 
								 | 
							
								                var blockSize = st.readByte(); // Always 12
							 | 
						||
| 
								 | 
							
								                block.ptHeader = st.readBytes(12);
							 | 
						||
| 
								 | 
							
								                block.ptData = readSubBlocks();
							 | 
						||
| 
								 | 
							
								                handler.pte && handler.pte(block);
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var parseAppExt = function (block) {
							 | 
						||
| 
								 | 
							
								                var parseNetscapeExt = function (block) {
							 | 
						||
| 
								 | 
							
								                    var blockSize = st.readByte(); // Always 3
							 | 
						||
| 
								 | 
							
								                    block.unknown = st.readByte(); // ??? Always 1? What is this?
							 | 
						||
| 
								 | 
							
								                    block.iterations = st.readUnsigned();
							 | 
						||
| 
								 | 
							
								                    block.terminator = st.readByte();
							 | 
						||
| 
								 | 
							
								                    handler.app && handler.app.NETSCAPE && handler.app.NETSCAPE(block);
							 | 
						||
| 
								 | 
							
								                };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                var parseUnknownAppExt = function (block) {
							 | 
						||
| 
								 | 
							
								                    block.appData = readSubBlocks();
							 | 
						||
| 
								 | 
							
								                    // FIXME: This won't work if a handler wants to match on any identifier.
							 | 
						||
| 
								 | 
							
								                    handler.app && handler.app[block.identifier] && handler.app[block.identifier](block);
							 | 
						||
| 
								 | 
							
								                };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                var blockSize = st.readByte(); // Always 11
							 | 
						||
| 
								 | 
							
								                block.identifier = st.read(8);
							 | 
						||
| 
								 | 
							
								                block.authCode = st.read(3);
							 | 
						||
| 
								 | 
							
								                switch (block.identifier) {
							 | 
						||
| 
								 | 
							
								                    case 'NETSCAPE':
							 | 
						||
| 
								 | 
							
								                        parseNetscapeExt(block);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    default:
							 | 
						||
| 
								 | 
							
								                        parseUnknownAppExt(block);
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var parseUnknownExt = function (block) {
							 | 
						||
| 
								 | 
							
								                block.data = readSubBlocks();
							 | 
						||
| 
								 | 
							
								                handler.unknown && handler.unknown(block);
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            block.label = st.readByte();
							 | 
						||
| 
								 | 
							
								            switch (block.label) {
							 | 
						||
| 
								 | 
							
								                case 0xF9:
							 | 
						||
| 
								 | 
							
								                    block.extType = 'gce';
							 | 
						||
| 
								 | 
							
								                    parseGCExt(block);
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                case 0xFE:
							 | 
						||
| 
								 | 
							
								                    block.extType = 'com';
							 | 
						||
| 
								 | 
							
								                    parseComExt(block);
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                case 0x01:
							 | 
						||
| 
								 | 
							
								                    block.extType = 'pte';
							 | 
						||
| 
								 | 
							
								                    parsePTExt(block);
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                case 0xFF:
							 | 
						||
| 
								 | 
							
								                    block.extType = 'app';
							 | 
						||
| 
								 | 
							
								                    parseAppExt(block);
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                default:
							 | 
						||
| 
								 | 
							
								                    block.extType = 'unknown';
							 | 
						||
| 
								 | 
							
								                    parseUnknownExt(block);
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var parseImg = function (img) {
							 | 
						||
| 
								 | 
							
								            var deinterlace = function (pixels, width) {
							 | 
						||
| 
								 | 
							
								                // Of course this defeats the purpose of interlacing. And it's *probably*
							 | 
						||
| 
								 | 
							
								                // the least efficient way it's ever been implemented. But nevertheless...
							 | 
						||
| 
								 | 
							
								                var newPixels = new Array(pixels.length);
							 | 
						||
| 
								 | 
							
								                var rows = pixels.length / width;
							 | 
						||
| 
								 | 
							
								                var cpRow = function (toRow, fromRow) {
							 | 
						||
| 
								 | 
							
								                    var fromPixels = pixels.slice(fromRow * width, (fromRow + 1) * width);
							 | 
						||
| 
								 | 
							
								                    newPixels.splice.apply(newPixels, [toRow * width, width].concat(fromPixels));
							 | 
						||
| 
								 | 
							
								                };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // See appendix E.
							 | 
						||
| 
								 | 
							
								                var offsets = [0, 4, 2, 1];
							 | 
						||
| 
								 | 
							
								                var steps = [8, 8, 4, 2];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                var fromRow = 0;
							 | 
						||
| 
								 | 
							
								                for (var pass = 0; pass < 4; pass++) {
							 | 
						||
| 
								 | 
							
								                    for (var toRow = offsets[pass]; toRow < rows; toRow += steps[pass]) {
							 | 
						||
| 
								 | 
							
								                        cpRow(toRow, fromRow)
							 | 
						||
| 
								 | 
							
								                        fromRow++;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                return newPixels;
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            img.leftPos = st.readUnsigned();
							 | 
						||
| 
								 | 
							
								            img.topPos = st.readUnsigned();
							 | 
						||
| 
								 | 
							
								            img.width = st.readUnsigned();
							 | 
						||
| 
								 | 
							
								            img.height = st.readUnsigned();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var bits = byteToBitArr(st.readByte());
							 | 
						||
| 
								 | 
							
								            img.lctFlag = bits.shift();
							 | 
						||
| 
								 | 
							
								            img.interlaced = bits.shift();
							 | 
						||
| 
								 | 
							
								            img.sorted = bits.shift();
							 | 
						||
| 
								 | 
							
								            img.reserved = bits.splice(0, 2);
							 | 
						||
| 
								 | 
							
								            img.lctSize = bitsToNum(bits.splice(0, 3));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (img.lctFlag) {
							 | 
						||
| 
								 | 
							
								                img.lct = parseCT(1 << (img.lctSize + 1));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            img.lzwMinCodeSize = st.readByte();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var lzwData = readSubBlocks();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            img.pixels = lzwDecode(img.lzwMinCodeSize, lzwData);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (img.interlaced) { // Move
							 | 
						||
| 
								 | 
							
								                img.pixels = deinterlace(img.pixels, img.width);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            handler.img && handler.img(img);
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var parseBlock = function () {
							 | 
						||
| 
								 | 
							
								            var block = {};
							 | 
						||
| 
								 | 
							
								            block.sentinel = st.readByte();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            switch (String.fromCharCode(block.sentinel)) { // For ease of matching
							 | 
						||
| 
								 | 
							
								                case '!':
							 | 
						||
| 
								 | 
							
								                    block.type = 'ext';
							 | 
						||
| 
								 | 
							
								                    parseExt(block);
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                case ',':
							 | 
						||
| 
								 | 
							
								                    block.type = 'img';
							 | 
						||
| 
								 | 
							
								                    parseImg(block);
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                case ';':
							 | 
						||
| 
								 | 
							
								                    block.type = 'eof';
							 | 
						||
| 
								 | 
							
								                    handler.eof && handler.eof(block);
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                default:
							 | 
						||
| 
								 | 
							
								                    throw new Error('Unknown block: 0x' + block.sentinel.toString(16)); // TODO: Pad this with a 0.
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (block.type !== 'eof') setTimeout(parseBlock, 0);
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var parse = function () {
							 | 
						||
| 
								 | 
							
								            parseHeader();
							 | 
						||
| 
								 | 
							
								            setTimeout(parseBlock, 0);
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        parse();
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var SuperGif = function ( opts ) {
							 | 
						||
| 
								 | 
							
								        var options = {
							 | 
						||
| 
								 | 
							
								            //viewport position
							 | 
						||
| 
								 | 
							
								            vp_l: 0,
							 | 
						||
| 
								 | 
							
								            vp_t: 0,
							 | 
						||
| 
								 | 
							
								            vp_w: null,
							 | 
						||
| 
								 | 
							
								            vp_h: null,
							 | 
						||
| 
								 | 
							
								            //canvas sizes
							 | 
						||
| 
								 | 
							
								            c_w: null,
							 | 
						||
| 
								 | 
							
								            c_h: null
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								        for (var i in opts ) { options[i] = opts[i] }
							 | 
						||
| 
								 | 
							
								        if (options.vp_w && options.vp_h) options.is_vp = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var stream;
							 | 
						||
| 
								 | 
							
								        var hdr;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var loadError = null;
							 | 
						||
| 
								 | 
							
								        var loading = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var transparency = null;
							 | 
						||
| 
								 | 
							
								        var delay = null;
							 | 
						||
| 
								 | 
							
								        var disposalMethod = null;
							 | 
						||
| 
								 | 
							
								        var disposalRestoreFromIdx = null;
							 | 
						||
| 
								 | 
							
								        var lastDisposalMethod = null;
							 | 
						||
| 
								 | 
							
								        var frame = null;
							 | 
						||
| 
								 | 
							
								        var lastImg = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var playing = true;
							 | 
						||
| 
								 | 
							
								        var forward = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var ctx_scaled = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var frames = [];
							 | 
						||
| 
								 | 
							
								        var frameOffsets = []; // elements have .x and .y properties
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var gif = options.gif;
							 | 
						||
| 
								 | 
							
								        if (typeof options.auto_play == 'undefined')
							 | 
						||
| 
								 | 
							
								            options.auto_play = (!gif.getAttribute('rel:auto_play') || gif.getAttribute('rel:auto_play') == '1');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var onEndListener = (options.hasOwnProperty('on_end') ? options.on_end : null);
							 | 
						||
| 
								 | 
							
								        var loopDelay = (options.hasOwnProperty('loop_delay') ? options.loop_delay : 0);
							 | 
						||
| 
								 | 
							
								        var overrideLoopMode = (options.hasOwnProperty('loop_mode') ? options.loop_mode : 'auto');
							 | 
						||
| 
								 | 
							
								        var drawWhileLoading = (options.hasOwnProperty('draw_while_loading') ? options.draw_while_loading : true);
							 | 
						||
| 
								 | 
							
								        var showProgressBar = drawWhileLoading ? (options.hasOwnProperty('show_progress_bar') ? options.show_progress_bar : true) : false;
							 | 
						||
| 
								 | 
							
								        var progressBarHeight = (options.hasOwnProperty('progressbar_height') ? options.progressbar_height : 25);
							 | 
						||
| 
								 | 
							
								        var progressBarBackgroundColor = (options.hasOwnProperty('progressbar_background_color') ? options.progressbar_background_color : 'rgba(255,255,255,0.4)');
							 | 
						||
| 
								 | 
							
								        var progressBarForegroundColor = (options.hasOwnProperty('progressbar_foreground_color') ? options.progressbar_foreground_color : 'rgba(255,0,22,.8)');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var clear = function () {
							 | 
						||
| 
								 | 
							
								            transparency = null;
							 | 
						||
| 
								 | 
							
								            delay = null;
							 | 
						||
| 
								 | 
							
								            lastDisposalMethod = disposalMethod;
							 | 
						||
| 
								 | 
							
								            disposalMethod = null;
							 | 
						||
| 
								 | 
							
								            frame = null;
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // XXX: There's probably a better way to handle catching exceptions when
							 | 
						||
| 
								 | 
							
								        // callbacks are involved.
							 | 
						||
| 
								 | 
							
								        var doParse = function () {
							 | 
						||
| 
								 | 
							
								            try {
							 | 
						||
| 
								 | 
							
								                parseGIF(stream, handler);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            catch (err) {
							 | 
						||
| 
								 | 
							
								                doLoadError('parse');
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var doText = function (text) {
							 | 
						||
| 
								 | 
							
								            toolbar.innerHTML = text; // innerText? Escaping? Whatever.
							 | 
						||
| 
								 | 
							
								            toolbar.style.visibility = 'visible';
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var setSizes = function(w, h) {
							 | 
						||
| 
								 | 
							
								            canvas.width = w * get_canvas_scale();
							 | 
						||
| 
								 | 
							
								            canvas.height = h * get_canvas_scale();
							 | 
						||
| 
								 | 
							
								            toolbar.style.minWidth = ( w * get_canvas_scale() ) + 'px';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            tmpCanvas.width = w;
							 | 
						||
| 
								 | 
							
								            tmpCanvas.height = h;
							 | 
						||
| 
								 | 
							
								            tmpCanvas.style.width = w + 'px';
							 | 
						||
| 
								 | 
							
								            tmpCanvas.style.height = h + 'px';
							 | 
						||
| 
								 | 
							
								            tmpCanvas.getContext('2d').setTransform(1, 0, 0, 1, 0, 0);
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var setFrameOffset = function(frame, offset) {
							 | 
						||
| 
								 | 
							
								            if (!frameOffsets[frame]) {
							 | 
						||
| 
								 | 
							
								                frameOffsets[frame] = offset;
							 | 
						||
| 
								 | 
							
								                return;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (typeof offset.x !== 'undefined') {
							 | 
						||
| 
								 | 
							
								                frameOffsets[frame].x = offset.x;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (typeof offset.y !== 'undefined') {
							 | 
						||
| 
								 | 
							
								                frameOffsets[frame].y = offset.y;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var doShowProgress = function (pos, length, draw) {
							 | 
						||
| 
								 | 
							
								            if (draw && showProgressBar) {
							 | 
						||
| 
								 | 
							
								                var height = progressBarHeight;
							 | 
						||
| 
								 | 
							
								                var left, mid, top, width;
							 | 
						||
| 
								 | 
							
								                if (options.is_vp) {
							 | 
						||
| 
								 | 
							
								                    if (!ctx_scaled) {
							 | 
						||
| 
								 | 
							
								                        top = (options.vp_t + options.vp_h - height);
							 | 
						||
| 
								 | 
							
								                        height = height;
							 | 
						||
| 
								 | 
							
								                        left = options.vp_l;
							 | 
						||
| 
								 | 
							
								                        mid = left + (pos / length) * options.vp_w;
							 | 
						||
| 
								 | 
							
								                        width = canvas.width;
							 | 
						||
| 
								 | 
							
								                    } else {
							 | 
						||
| 
								 | 
							
								                        top = (options.vp_t + options.vp_h - height) / get_canvas_scale();
							 | 
						||
| 
								 | 
							
								                        height = height / get_canvas_scale();
							 | 
						||
| 
								 | 
							
								                        left = (options.vp_l / get_canvas_scale() );
							 | 
						||
| 
								 | 
							
								                        mid = left + (pos / length) * (options.vp_w / get_canvas_scale());
							 | 
						||
| 
								 | 
							
								                        width = canvas.width / get_canvas_scale();
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    //some debugging, draw rect around viewport
							 | 
						||
| 
								 | 
							
								                    if (false) {
							 | 
						||
| 
								 | 
							
								                        if (!ctx_scaled) {
							 | 
						||
| 
								 | 
							
								                            var l = options.vp_l, t = options.vp_t;
							 | 
						||
| 
								 | 
							
								                            var w = options.vp_w, h = options.vp_h;
							 | 
						||
| 
								 | 
							
								                        } else {
							 | 
						||
| 
								 | 
							
								                            var l = options.vp_l/get_canvas_scale(), t = options.vp_t/get_canvas_scale();
							 | 
						||
| 
								 | 
							
								                            var w = options.vp_w/get_canvas_scale(), h = options.vp_h/get_canvas_scale();
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                        ctx.rect(l,t,w,h);
							 | 
						||
| 
								 | 
							
								                        ctx.stroke();
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                else {
							 | 
						||
| 
								 | 
							
								                    top = (canvas.height - height) / (ctx_scaled ? get_canvas_scale() : 1);
							 | 
						||
| 
								 | 
							
								                    mid = ((pos / length) * canvas.width) / (ctx_scaled ? get_canvas_scale() : 1);
							 | 
						||
| 
								 | 
							
								                    width = canvas.width / (ctx_scaled ? get_canvas_scale() : 1 );
							 | 
						||
| 
								 | 
							
								                    height /= ctx_scaled ? get_canvas_scale() : 1;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                ctx.fillStyle = progressBarBackgroundColor;
							 | 
						||
| 
								 | 
							
								                ctx.fillRect(mid, top, width - mid, height);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                ctx.fillStyle = progressBarForegroundColor;
							 | 
						||
| 
								 | 
							
								                ctx.fillRect(0, top, mid, height);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var doLoadError = function (originOfError) {
							 | 
						||
| 
								 | 
							
								            var drawError = function () {
							 | 
						||
| 
								 | 
							
								                ctx.fillStyle = 'black';
							 | 
						||
| 
								 | 
							
								                ctx.fillRect(0, 0, options.c_w ? options.c_w : hdr.width, options.c_h ? options.c_h : hdr.height);
							 | 
						||
| 
								 | 
							
								                ctx.strokeStyle = 'red';
							 | 
						||
| 
								 | 
							
								                ctx.lineWidth = 3;
							 | 
						||
| 
								 | 
							
								                ctx.moveTo(0, 0);
							 | 
						||
| 
								 | 
							
								                ctx.lineTo(options.c_w ? options.c_w : hdr.width, options.c_h ? options.c_h : hdr.height);
							 | 
						||
| 
								 | 
							
								                ctx.moveTo(0, options.c_h ? options.c_h : hdr.height);
							 | 
						||
| 
								 | 
							
								                ctx.lineTo(options.c_w ? options.c_w : hdr.width, 0);
							 | 
						||
| 
								 | 
							
								                ctx.stroke();
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            loadError = originOfError;
							 | 
						||
| 
								 | 
							
								            hdr = {
							 | 
						||
| 
								 | 
							
								                width: gif.width,
							 | 
						||
| 
								 | 
							
								                height: gif.height
							 | 
						||
| 
								 | 
							
								            }; // Fake header.
							 | 
						||
| 
								 | 
							
								            frames = [];
							 | 
						||
| 
								 | 
							
								            drawError();
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var doHdr = function (_hdr) {
							 | 
						||
| 
								 | 
							
								            hdr = _hdr;
							 | 
						||
| 
								 | 
							
								            setSizes(hdr.width, hdr.height)
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var doGCE = function (gce) {
							 | 
						||
| 
								 | 
							
								            pushFrame();
							 | 
						||
| 
								 | 
							
								            clear();
							 | 
						||
| 
								 | 
							
								            transparency = gce.transparencyGiven ? gce.transparencyIndex : null;
							 | 
						||
| 
								 | 
							
								            delay = gce.delayTime;
							 | 
						||
| 
								 | 
							
								            disposalMethod = gce.disposalMethod;
							 | 
						||
| 
								 | 
							
								            // We don't have much to do with the rest of GCE.
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var pushFrame = function () {
							 | 
						||
| 
								 | 
							
								            if (!frame) return;
							 | 
						||
| 
								 | 
							
								            frames.push({
							 | 
						||
| 
								 | 
							
								                            data: frame.getImageData(0, 0, hdr.width, hdr.height),
							 | 
						||
| 
								 | 
							
								                            delay: delay
							 | 
						||
| 
								 | 
							
								                        });
							 | 
						||
| 
								 | 
							
								            frameOffsets.push({ x: 0, y: 0 });
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var doImg = function (img) {
							 | 
						||
| 
								 | 
							
								            if (!frame) frame = tmpCanvas.getContext('2d');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var currIdx = frames.length;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            //ct = color table, gct = global color table
							 | 
						||
| 
								 | 
							
								            var ct = img.lctFlag ? img.lct : hdr.gct; // TODO: What if neither exists?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            /*
							 | 
						||
| 
								 | 
							
								            Disposal method indicates the way in which the graphic is to
							 | 
						||
| 
								 | 
							
								            be treated after being displayed.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            Values :    0 - No disposal specified. The decoder is
							 | 
						||
| 
								 | 
							
								                            not required to take any action.
							 | 
						||
| 
								 | 
							
								                        1 - Do not dispose. The graphic is to be left
							 | 
						||
| 
								 | 
							
								                            in place.
							 | 
						||
| 
								 | 
							
								                        2 - Restore to background color. The area used by the
							 | 
						||
| 
								 | 
							
								                            graphic must be restored to the background color.
							 | 
						||
| 
								 | 
							
								                        3 - Restore to previous. The decoder is required to
							 | 
						||
| 
								 | 
							
								                            restore the area overwritten by the graphic with
							 | 
						||
| 
								 | 
							
								                            what was there prior to rendering the graphic.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                            Importantly, "previous" means the frame state
							 | 
						||
| 
								 | 
							
								                            after the last disposal of method 0, 1, or 2.
							 | 
						||
| 
								 | 
							
								            */
							 | 
						||
| 
								 | 
							
								            if (currIdx > 0) {
							 | 
						||
| 
								 | 
							
								                if (lastDisposalMethod === 3) {
							 | 
						||
| 
								 | 
							
								                    // Restore to previous
							 | 
						||
| 
								 | 
							
								                    // If we disposed every frame including first frame up to this point, then we have
							 | 
						||
| 
								 | 
							
								                    // no composited frame to restore to. In this case, restore to background instead.
							 | 
						||
| 
								 | 
							
								                    if (disposalRestoreFromIdx !== null) {
							 | 
						||
| 
								 | 
							
								                    	frame.putImageData(frames[disposalRestoreFromIdx].data, 0, 0);
							 | 
						||
| 
								 | 
							
								                    } else {
							 | 
						||
| 
								 | 
							
								                    	frame.clearRect(lastImg.leftPos, lastImg.topPos, lastImg.width, lastImg.height);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    disposalRestoreFromIdx = currIdx - 1;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (lastDisposalMethod === 2) {
							 | 
						||
| 
								 | 
							
								                    // Restore to background color
							 | 
						||
| 
								 | 
							
								                    // Browser implementations historically restore to transparent; we do the same.
							 | 
						||
| 
								 | 
							
								                    // http://www.wizards-toolkit.org/discourse-server/viewtopic.php?f=1&t=21172#p86079
							 | 
						||
| 
								 | 
							
								                    frame.clearRect(lastImg.leftPos, lastImg.topPos, lastImg.width, lastImg.height);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            // else, Undefined/Do not dispose.
							 | 
						||
| 
								 | 
							
								            // frame contains final pixel data from the last frame; do nothing
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            //Get existing pixels for img region after applying disposal method
							 | 
						||
| 
								 | 
							
								            var imgData = frame.getImageData(img.leftPos, img.topPos, img.width, img.height);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            //apply color table colors
							 | 
						||
| 
								 | 
							
								            img.pixels.forEach(function (pixel, i) {
							 | 
						||
| 
								 | 
							
								                // imgData.data === [R,G,B,A,R,G,B,A,...]
							 | 
						||
| 
								 | 
							
								                if (pixel !== transparency) {
							 | 
						||
| 
								 | 
							
								                    imgData.data[i * 4 + 0] = ct[pixel][0];
							 | 
						||
| 
								 | 
							
								                    imgData.data[i * 4 + 1] = ct[pixel][1];
							 | 
						||
| 
								 | 
							
								                    imgData.data[i * 4 + 2] = ct[pixel][2];
							 | 
						||
| 
								 | 
							
								                    imgData.data[i * 4 + 3] = 255; // Opaque.
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            frame.putImageData(imgData, img.leftPos, img.topPos);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (!ctx_scaled) {
							 | 
						||
| 
								 | 
							
								                ctx.scale(get_canvas_scale(),get_canvas_scale());
							 | 
						||
| 
								 | 
							
								                ctx_scaled = true;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // We could use the on-page canvas directly, except that we draw a progress
							 | 
						||
| 
								 | 
							
								            // bar for each image chunk (not just the final image).
							 | 
						||
| 
								 | 
							
								            if (drawWhileLoading) {
							 | 
						||
| 
								 | 
							
								                ctx.drawImage(tmpCanvas, 0, 0);
							 | 
						||
| 
								 | 
							
								                drawWhileLoading = options.auto_play;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            lastImg = img;
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var player = (function () {
							 | 
						||
| 
								 | 
							
								            var i = -1;
							 | 
						||
| 
								 | 
							
								            var iterationCount = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var showingInfo = false;
							 | 
						||
| 
								 | 
							
								            var pinned = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            /**
							 | 
						||
| 
								 | 
							
								             * Gets the index of the frame "up next".
							 | 
						||
| 
								 | 
							
								             * @returns {number}
							 | 
						||
| 
								 | 
							
								             */
							 | 
						||
| 
								 | 
							
								            var getNextFrameNo = function () {
							 | 
						||
| 
								 | 
							
								                var delta = (forward ? 1 : -1);
							 | 
						||
| 
								 | 
							
								                return (i + delta + frames.length) % frames.length;
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var stepFrame = function (amount) { // XXX: Name is confusing.
							 | 
						||
| 
								 | 
							
								                i = i + amount;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                putFrame();
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var step = (function () {
							 | 
						||
| 
								 | 
							
								                var stepping = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                var completeLoop = function () {
							 | 
						||
| 
								 | 
							
								                    if (onEndListener !== null)
							 | 
						||
| 
								 | 
							
								                        onEndListener(gif);
							 | 
						||
| 
								 | 
							
								                    iterationCount++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if (overrideLoopMode !== false || iterationCount < 0) {
							 | 
						||
| 
								 | 
							
								                        doStep();
							 | 
						||
| 
								 | 
							
								                    } else {
							 | 
						||
| 
								 | 
							
								                        stepping = false;
							 | 
						||
| 
								 | 
							
								                        playing = false;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                var doStep = function () {
							 | 
						||
| 
								 | 
							
								                    stepping = playing;
							 | 
						||
| 
								 | 
							
								                    if (!stepping) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    stepFrame(1);
							 | 
						||
| 
								 | 
							
								                    var delay = frames[i].delay * 10;
							 | 
						||
| 
								 | 
							
								                    if (!delay) delay = 100; // FIXME: Should this even default at all? What should it be?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    var nextFrameNo = getNextFrameNo();
							 | 
						||
| 
								 | 
							
								                    if (nextFrameNo === 0) {
							 | 
						||
| 
								 | 
							
								                        delay += loopDelay;
							 | 
						||
| 
								 | 
							
								                        setTimeout(completeLoop, delay);
							 | 
						||
| 
								 | 
							
								                    } else {
							 | 
						||
| 
								 | 
							
								                        setTimeout(doStep, delay);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                return function () {
							 | 
						||
| 
								 | 
							
								                    if (!stepping) setTimeout(doStep, 0);
							 | 
						||
| 
								 | 
							
								                };
							 | 
						||
| 
								 | 
							
								            }());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var putFrame = function () {
							 | 
						||
| 
								 | 
							
								                var offset;
							 | 
						||
| 
								 | 
							
								                i = parseInt(i, 10);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (i > frames.length - 1){
							 | 
						||
| 
								 | 
							
								                    i = 0;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (i < 0){
							 | 
						||
| 
								 | 
							
								                    i = 0;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                offset = frameOffsets[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                tmpCanvas.getContext("2d").putImageData(frames[i].data, offset.x, offset.y);
							 | 
						||
| 
								 | 
							
								                ctx.globalCompositeOperation = "copy";
							 | 
						||
| 
								 | 
							
								                ctx.drawImage(tmpCanvas, 0, 0);
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var play = function () {
							 | 
						||
| 
								 | 
							
								                playing = true;
							 | 
						||
| 
								 | 
							
								                step();
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var pause = function () {
							 | 
						||
| 
								 | 
							
								                playing = false;
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return {
							 | 
						||
| 
								 | 
							
								                init: function () {
							 | 
						||
| 
								 | 
							
								                    if (loadError) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if ( ! (options.c_w && options.c_h) ) {
							 | 
						||
| 
								 | 
							
								                        ctx.scale(get_canvas_scale(),get_canvas_scale());
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if (options.auto_play) {
							 | 
						||
| 
								 | 
							
								                        step();
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    else {
							 | 
						||
| 
								 | 
							
								                        i = 0;
							 | 
						||
| 
								 | 
							
								                        putFrame();
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                },
							 | 
						||
| 
								 | 
							
								                step: step,
							 | 
						||
| 
								 | 
							
								                play: play,
							 | 
						||
| 
								 | 
							
								                pause: pause,
							 | 
						||
| 
								 | 
							
								                playing: playing,
							 | 
						||
| 
								 | 
							
								                move_relative: stepFrame,
							 | 
						||
| 
								 | 
							
								                current_frame: function() { return i; },
							 | 
						||
| 
								 | 
							
								                length: function() { return frames.length },
							 | 
						||
| 
								 | 
							
								                move_to: function ( frame_idx ) {
							 | 
						||
| 
								 | 
							
								                    i = frame_idx;
							 | 
						||
| 
								 | 
							
								                    putFrame();
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var doDecodeProgress = function (draw) {
							 | 
						||
| 
								 | 
							
								            doShowProgress(stream.pos, stream.data.length, draw);
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var doNothing = function () {};
							 | 
						||
| 
								 | 
							
								        /**
							 | 
						||
| 
								 | 
							
								         * @param{boolean=} draw Whether to draw progress bar or not; this is not idempotent because of translucency.
							 | 
						||
| 
								 | 
							
								         *                       Note that this means that the text will be unsynchronized with the progress bar on non-frames;
							 | 
						||
| 
								 | 
							
								         *                       but those are typically so small (GCE etc.) that it doesn't really matter. TODO: Do this properly.
							 | 
						||
| 
								 | 
							
								         */
							 | 
						||
| 
								 | 
							
								        var withProgress = function (fn, draw) {
							 | 
						||
| 
								 | 
							
								            return function (block) {
							 | 
						||
| 
								 | 
							
								                fn(block);
							 | 
						||
| 
								 | 
							
								                doDecodeProgress(draw);
							 | 
						||
| 
								 | 
							
								            };
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var handler = {
							 | 
						||
| 
								 | 
							
								            hdr: withProgress(doHdr),
							 | 
						||
| 
								 | 
							
								            gce: withProgress(doGCE),
							 | 
						||
| 
								 | 
							
								            com: withProgress(doNothing),
							 | 
						||
| 
								 | 
							
								            // I guess that's all for now.
							 | 
						||
| 
								 | 
							
								            app: {
							 | 
						||
| 
								 | 
							
								                // TODO: Is there much point in actually supporting iterations?
							 | 
						||
| 
								 | 
							
								                NETSCAPE: withProgress(doNothing)
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								            img: withProgress(doImg, true),
							 | 
						||
| 
								 | 
							
								            eof: function (block) {
							 | 
						||
| 
								 | 
							
								                //toolbar.style.display = '';
							 | 
						||
| 
								 | 
							
								                pushFrame();
							 | 
						||
| 
								 | 
							
								                doDecodeProgress(false);
							 | 
						||
| 
								 | 
							
								                if ( ! (options.c_w && options.c_h) ) {
							 | 
						||
| 
								 | 
							
								                    canvas.width = hdr.width * get_canvas_scale();
							 | 
						||
| 
								 | 
							
								                    canvas.height = hdr.height * get_canvas_scale();
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                player.init();
							 | 
						||
| 
								 | 
							
								                loading = false;
							 | 
						||
| 
								 | 
							
								                if (load_callback) {
							 | 
						||
| 
								 | 
							
								                    load_callback(gif);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var init = function () {
							 | 
						||
| 
								 | 
							
								            var parent = gif.parentNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            var div = document.createElement('div');
							 | 
						||
| 
								 | 
							
								            canvas = document.createElement('canvas');
							 | 
						||
| 
								 | 
							
								            ctx = canvas.getContext('2d');
							 | 
						||
| 
								 | 
							
								            toolbar = document.createElement('div');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            tmpCanvas = document.createElement('canvas');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            div.width = canvas.width = gif.width;
							 | 
						||
| 
								 | 
							
								            div.height = canvas.height = gif.height;
							 | 
						||
| 
								 | 
							
								            toolbar.style.minWidth = gif.width + 'px';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            div.className = 'jsgif';
							 | 
						||
| 
								 | 
							
								            toolbar.className = 'jsgif_toolbar';
							 | 
						||
| 
								 | 
							
								            div.appendChild(canvas);
							 | 
						||
| 
								 | 
							
								            div.appendChild(toolbar);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            parent.insertBefore(div, gif);
							 | 
						||
| 
								 | 
							
								            parent.removeChild(gif);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (options.c_w && options.c_h) setSizes(options.c_w, options.c_h);
							 | 
						||
| 
								 | 
							
								            initialized=true;
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var get_canvas_scale = function() {
							 | 
						||
| 
								 | 
							
								            var scale;
							 | 
						||
| 
								 | 
							
								            if (options.max_width && hdr && hdr.width > options.max_width) {
							 | 
						||
| 
								 | 
							
								                scale = options.max_width / hdr.width;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else {
							 | 
						||
| 
								 | 
							
								                scale = 1;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            return scale;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var canvas, ctx, toolbar, tmpCanvas;
							 | 
						||
| 
								 | 
							
								        var initialized = false;
							 | 
						||
| 
								 | 
							
								        var load_callback = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var load_setup = function(callback) {
							 | 
						||
| 
								 | 
							
								            if (loading) return false;
							 | 
						||
| 
								 | 
							
								            if (callback) load_callback = callback;
							 | 
						||
| 
								 | 
							
								            else load_callback = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            loading = true;
							 | 
						||
| 
								 | 
							
								            frames = [];
							 | 
						||
| 
								 | 
							
								            clear();
							 | 
						||
| 
								 | 
							
								            disposalRestoreFromIdx = null;
							 | 
						||
| 
								 | 
							
								            lastDisposalMethod = null;
							 | 
						||
| 
								 | 
							
								            frame = null;
							 | 
						||
| 
								 | 
							
								            lastImg = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return {
							 | 
						||
| 
								 | 
							
								            // play controls
							 | 
						||
| 
								 | 
							
								            play: player.play,
							 | 
						||
| 
								 | 
							
								            pause: player.pause,
							 | 
						||
| 
								 | 
							
								            move_relative: player.move_relative,
							 | 
						||
| 
								 | 
							
								            move_to: player.move_to,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // getters for instance vars
							 | 
						||
| 
								 | 
							
								            get_playing      : function() { return playing },
							 | 
						||
| 
								 | 
							
								            get_canvas       : function() { return canvas },
							 | 
						||
| 
								 | 
							
								            get_canvas_scale : function() { return get_canvas_scale() },
							 | 
						||
| 
								 | 
							
								            get_loading      : function() { return loading },
							 | 
						||
| 
								 | 
							
								            get_auto_play    : function() { return options.auto_play },
							 | 
						||
| 
								 | 
							
								            get_length       : function() { return player.length() },
							 | 
						||
| 
								 | 
							
								            get_current_frame: function() { return player.current_frame() },
							 | 
						||
| 
								 | 
							
								            load_url: function(src,callback){
							 | 
						||
| 
								 | 
							
								                if (!load_setup(callback)) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                var h = new XMLHttpRequest();
							 | 
						||
| 
								 | 
							
								                // new browsers (XMLHttpRequest2-compliant)
							 | 
						||
| 
								 | 
							
								                h.open('GET', src, true);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if ('overrideMimeType' in h) {
							 | 
						||
| 
								 | 
							
								                    h.overrideMimeType('text/plain; charset=x-user-defined');
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // old browsers (XMLHttpRequest-compliant)
							 | 
						||
| 
								 | 
							
								                else if ('responseType' in h) {
							 | 
						||
| 
								 | 
							
								                    h.responseType = 'arraybuffer';
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // IE9 (Microsoft.XMLHTTP-compliant)
							 | 
						||
| 
								 | 
							
								                else {
							 | 
						||
| 
								 | 
							
								                    h.setRequestHeader('Accept-Charset', 'x-user-defined');
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                h.onloadstart = function() {
							 | 
						||
| 
								 | 
							
								                    // Wait until connection is opened to replace the gif element with a canvas to avoid a blank img
							 | 
						||
| 
								 | 
							
								                    if (!initialized) init();
							 | 
						||
| 
								 | 
							
								                };
							 | 
						||
| 
								 | 
							
								                h.onload = function(e) {
							 | 
						||
| 
								 | 
							
								                    if (this.status != 200) {
							 | 
						||
| 
								 | 
							
								                        doLoadError('xhr - response');
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    // emulating response field for IE9
							 | 
						||
| 
								 | 
							
								                    if (!('response' in this)) {
							 | 
						||
| 
								 | 
							
								                        this.response = new VBArray(this.responseText).toArray().map(String.fromCharCode).join('');
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    var data = this.response;
							 | 
						||
| 
								 | 
							
								                    if (data.toString().indexOf("ArrayBuffer") > 0) {
							 | 
						||
| 
								 | 
							
								                        data = new Uint8Array(data);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    stream = new Stream(data);
							 | 
						||
| 
								 | 
							
								                    setTimeout(doParse, 0);
							 | 
						||
| 
								 | 
							
								                };
							 | 
						||
| 
								 | 
							
								                h.onprogress = function (e) {
							 | 
						||
| 
								 | 
							
								                    if (e.lengthComputable) doShowProgress(e.loaded, e.total, true);
							 | 
						||
| 
								 | 
							
								                };
							 | 
						||
| 
								 | 
							
								                h.onerror = function() { doLoadError('xhr'); };
							 | 
						||
| 
								 | 
							
								                h.send();
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								            load: function (callback) {
							 | 
						||
| 
								 | 
							
								                this.load_url(gif.getAttribute('rel:animated_src') || gif.src,callback);
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								            load_raw: function(arr, callback) {
							 | 
						||
| 
								 | 
							
								                if (!load_setup(callback)) return;
							 | 
						||
| 
								 | 
							
								                if (!initialized) init();
							 | 
						||
| 
								 | 
							
								                stream = new Stream(arr);
							 | 
						||
| 
								 | 
							
								                setTimeout(doParse, 0);
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								            set_frame_offset: setFrameOffset
							 | 
						||
| 
								 | 
							
								        };
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return SuperGif;
							 | 
						||
| 
								 | 
							
								}));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 |