diff --git a/gulp/publish-frontend.js b/gulp/publish-frontend.js
index 95e3939..af11e67 100644
--- a/gulp/publish-frontend.js
+++ b/gulp/publish-frontend.js
@@ -34,7 +34,6 @@ const jsSources = [
'server/scripts/vendor/auto/nosleep.js',
'server/scripts/vendor/auto/swiped-events.js',
'server/scripts/index.js',
- 'server/scripts/libgif.js',
'server/scripts/vendor/auto/luxon.js',
'server/scripts/vendor/auto/suncalc.js',
'server/scripts/modules/draw.js',
diff --git a/server/scripts/libgif.js b/server/scripts/libgif.js
deleted file mode 100644
index 29f294a..0000000
--- a/server/scripts/libgif.js
+++ /dev/null
@@ -1,1169 +0,0 @@
-/*
- SuperGif
-
- Example usage:
-
-
-
-
-
- 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 src = options.src;
-
- if (!options.gif)
- {
- options.gif = document.createElement("img");
- options.gif.setAttribute("rel:animated_src", src);
- //options.gif.setAttribute("rel:auto_play", "1");
- options.gif.src = "";
- }
-
- 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);
-
- options.canvas.getContext("2d").drawImage(canvas, options.x, options.y)
- };
-
- 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.style.display = 'none';
- div.appendChild(canvas);
- div.appendChild(toolbar);
-
- if (parent)
- {
- 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 if (options.max_height && hdr && hdr.height > options.max_height)
- {
- scale = options.max_height / hdr.height;
- }
- else
- {
- scale = 1;
- }
- return scale;
- }
-
- var canvas, ctx, toolbar, tmpCanvas;
- var initialized = false;
- var load_callback = false;
- var first_time = true;
-
- 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,
-
- // mjb
- getX: function ()
- {
- return options.x;
- },
- setX: function (value)
- {
- options.x = value;
- },
- getY: function ()
- {
- return options.y;
- },
- setY: function (value)
- {
- options.y = value;
- },
- setFirstTime: function ()
- {
- first_time = true;
- }
-
- };
- };
-
- return SuperGif;
-}));
-
diff --git a/views/index.ejs b/views/index.ejs
index 4187c5d..1c1b405 100644
--- a/views/index.ejs
+++ b/views/index.ejs
@@ -30,7 +30,6 @@
-