(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = global || self, global.artplayerPluginGif = factory()); }(this, function () { 'use strict'; function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } var defineProperty = _defineProperty; function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { defineProperty(target, key, source[key]); }); } return target; } var objectSpread = _objectSpread; var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var gifshot = createCommonjsModule(function (module, exports) { (function(window, document, navigator, undefined$1) { /* utils.js ======== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ var utils = { URL: window.URL || window.webkitURL || window.mozURL || window.msURL, getUserMedia: function () { var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; return getUserMedia ? getUserMedia.bind(navigator) : getUserMedia; }(), requestAnimFrame: window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame, requestTimeout: function requestTimeout(callback, delay) { callback = callback || utils.noop; delay = delay || 0; if (!utils.requestAnimFrame) { return setTimeout(callback, delay); } var start = new Date().getTime(); var handle = new Object(); var requestAnimFrame = utils.requestAnimFrame; var loop = function loop() { var current = new Date().getTime(); var delta = current - start; delta >= delay ? callback.call() : handle.value = requestAnimFrame(loop); }; handle.value = requestAnimFrame(loop); return handle; }, Blob: window.Blob || window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder, btoa: function () { var btoa = window.btoa || function (input) { var output = ''; var i = 0; var l = input.length; var key = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; var chr1 = void 0; var chr2 = void 0; var chr3 = void 0; var enc1 = void 0; var enc2 = void 0; var enc3 = void 0; var enc4 = void 0; while (i < l) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = (chr1 & 3) << 4 | chr2 >> 4; enc3 = (chr2 & 15) << 2 | chr3 >> 6; enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + key.charAt(enc1) + key.charAt(enc2) + key.charAt(enc3) + key.charAt(enc4); } return output; }; return btoa ? btoa.bind(window) : utils.noop; }(), isObject: function isObject(obj) { return obj && Object.prototype.toString.call(obj) === '[object Object]'; }, isEmptyObject: function isEmptyObject(obj) { return utils.isObject(obj) && !Object.keys(obj).length; }, isArray: function isArray(arr) { return arr && Array.isArray(arr); }, isFunction: function isFunction(func) { return func && typeof func === 'function'; }, isElement: function isElement(elem) { return elem && elem.nodeType === 1; }, isString: function isString(value) { return typeof value === 'string' || Object.prototype.toString.call(value) === '[object String]'; }, isSupported: { canvas: function canvas() { var el = document.createElement('canvas'); return el && el.getContext && el.getContext('2d'); }, webworkers: function webworkers() { return window.Worker; }, blob: function blob() { return utils.Blob; }, Uint8Array: function Uint8Array() { return window.Uint8Array; }, Uint32Array: function Uint32Array() { return window.Uint32Array; }, videoCodecs: function () { var testEl = document.createElement('video'); var supportObj = { 'mp4': false, 'h264': false, 'ogv': false, 'ogg': false, 'webm': false }; try { if (testEl && testEl.canPlayType) { // Check for MPEG-4 support supportObj.mp4 = testEl.canPlayType('video/mp4; codecs="mp4v.20.8"') !== ''; // Check for h264 support supportObj.h264 = (testEl.canPlayType('video/mp4; codecs="avc1.42E01E"') || testEl.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"')) !== ''; // Check for Ogv support supportObj.ogv = testEl.canPlayType('video/ogg; codecs="theora"') !== ''; // Check for Ogg support supportObj.ogg = testEl.canPlayType('video/ogg; codecs="theora"') !== ''; // Check for Webm support supportObj.webm = testEl.canPlayType('video/webm; codecs="vp8, vorbis"') !== -1; } } catch (e) {} return supportObj; }() }, noop: function noop() {}, each: function each(collection, callback) { var x = void 0; var len = void 0; if (utils.isArray(collection)) { x = -1; len = collection.length; while (++x < len) { if (callback(x, collection[x]) === false) { break; } } } else if (utils.isObject(collection)) { for (x in collection) { if (collection.hasOwnProperty(x)) { if (callback(x, collection[x]) === false) { break; } } } } }, mergeOptions: function mergeOptions(defaultOptions, userOptions) { if (!utils.isObject(defaultOptions) || !utils.isObject(userOptions) || !Object.keys) { return; } var newObj = {}; utils.each(defaultOptions, function (key, val) { newObj[key] = defaultOptions[key]; }); utils.each(userOptions, function (key, val) { var currentUserOption = userOptions[key]; if (!utils.isObject(currentUserOption)) { newObj[key] = currentUserOption; } else { if (!defaultOptions[key]) { newObj[key] = currentUserOption; } else { newObj[key] = utils.mergeOptions(defaultOptions[key], currentUserOption); } } }); return newObj; }, setCSSAttr: function setCSSAttr(elem, attr, val) { if (!utils.isElement(elem)) { return; } if (utils.isString(attr) && utils.isString(val)) { elem.style[attr] = val; } else if (utils.isObject(attr)) { utils.each(attr, function (key, val) { elem.style[key] = val; }); } }, removeElement: function removeElement(node) { if (!utils.isElement(node)) { return; } if (node.parentNode) { node.parentNode.removeChild(node); } }, createWebWorker: function createWebWorker(content) { if (!utils.isString(content)) { return {}; } try { var blob = new utils.Blob([content], { 'type': 'text/javascript' }); var objectUrl = utils.URL.createObjectURL(blob); var worker = new Worker(objectUrl); return { 'objectUrl': objectUrl, 'worker': worker }; } catch (e) { return '' + e; } }, getExtension: function getExtension(src) { return src.substr(src.lastIndexOf('.') + 1, src.length); }, getFontSize: function getFontSize() { var options = arguments.length > 0 && arguments[0] !== undefined$1 ? arguments[0] : {}; if (!document.body || options.resizeFont === false) { return options.fontSize; } var text = options.text; var containerWidth = options.gifWidth; var fontSize = parseInt(options.fontSize, 10); var minFontSize = parseInt(options.minFontSize, 10); var div = document.createElement('div'); var span = document.createElement('span'); div.setAttribute('width', containerWidth); div.appendChild(span); span.innerHTML = text; span.style.fontSize = fontSize + 'px'; span.style.textIndent = '-9999px'; span.style.visibility = 'hidden'; document.body.appendChild(span); while (span.offsetWidth > containerWidth && fontSize >= minFontSize) { span.style.fontSize = --fontSize + 'px'; } document.body.removeChild(span); return fontSize + 'px'; }, webWorkerError: false }; var utils$2 = Object.freeze({ default: utils }); /* error.js ======== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Dependencies var error = { validate: function validate(skipObj) { skipObj = utils.isObject(skipObj) ? skipObj : {}; var errorObj = {}; utils.each(error.validators, function (indece, currentValidator) { var errorCode = currentValidator.errorCode; if (!skipObj[errorCode] && !currentValidator.condition) { errorObj = currentValidator; errorObj.error = true; return false; } }); delete errorObj.condition; return errorObj; }, isValid: function isValid(skipObj) { var errorObj = error.validate(skipObj); var isValid = errorObj.error !== true ? true : false; return isValid; }, validators: [{ condition: utils.isFunction(utils.getUserMedia), errorCode: 'getUserMedia', errorMsg: 'The getUserMedia API is not supported in your browser' }, { condition: utils.isSupported.canvas(), errorCode: 'canvas', errorMsg: 'Canvas elements are not supported in your browser' }, { condition: utils.isSupported.webworkers(), errorCode: 'webworkers', errorMsg: 'The Web Workers API is not supported in your browser' }, { condition: utils.isFunction(utils.URL), errorCode: 'window.URL', errorMsg: 'The window.URL API is not supported in your browser' }, { condition: utils.isSupported.blob(), errorCode: 'window.Blob', errorMsg: 'The window.Blob File API is not supported in your browser' }, { condition: utils.isSupported.Uint8Array(), errorCode: 'window.Uint8Array', errorMsg: 'The window.Uint8Array function constructor is not supported in your browser' }, { condition: utils.isSupported.Uint32Array(), errorCode: 'window.Uint32Array', errorMsg: 'The window.Uint32Array function constructor is not supported in your browser' }], messages: { videoCodecs: { errorCode: 'videocodec', errorMsg: 'The video codec you are trying to use is not supported in your browser' } } }; var error$2 = Object.freeze({ default: error }); /* defaultOptions.js ================= */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Helpers var noop = function noop() {}; var defaultOptions = { sampleInterval: 10, numWorkers: 2, filter: '', gifWidth: 200, gifHeight: 200, interval: 0.1, numFrames: 10, frameDuration: 1, keepCameraOn: false, images: [], video: null, webcamVideoElement: null, cameraStream: null, text: '', fontWeight: 'normal', fontSize: '16px', minFontSize: '10px', resizeFont: false, fontFamily: 'sans-serif', fontColor: '#ffffff', textAlign: 'center', textBaseline: 'bottom', textXCoordinate: null, textYCoordinate: null, progressCallback: noop, completeCallback: noop, saveRenderingContexts: false, savedRenderingContexts: [], crossOrigin: 'Anonymous' }; var defaultOptions$2 = Object.freeze({ default: defaultOptions }); /* isSupported.js ============== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Dependencies function isSupported() { return error.isValid(); } /* isWebCamGIFSupported.js ======================= */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ function isWebCamGIFSupported() { return error.isValid(); } /* isSupported.js ============== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Dependencies function isSupported$1() { var options = { getUserMedia: true }; return error.isValid(options); } /* isExistingVideoGIFSupported.js ============================== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Dependencies function isExistingVideoGIFSupported(codecs) { var hasValidCodec = false; if (utils.isArray(codecs) && codecs.length) { utils.each(codecs, function (indece, currentCodec) { if (utils.isSupported.videoCodecs[currentCodec]) { hasValidCodec = true; } }); if (!hasValidCodec) { return false; } } else if (utils.isString(codecs) && codecs.length) { if (!utils.isSupported.videoCodecs[codecs]) { return false; } } return error.isValid({ 'getUserMedia': true }); } /* NeuQuant.js =========== */ /* * NeuQuant Neural-Net Quantization Algorithm * ------------------------------------------ * * Copyright (c) 1994 Anthony Dekker * * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See * "Kohonen neural networks for optimal colour quantization" in "Network: * Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of * the algorithm. * * Any party obtaining a copy of these files from the author, directly or * indirectly, is granted, free of charge, a full and unrestricted irrevocable, * world-wide, paid up, royalty-free, nonexclusive right and license to deal in * this software and documentation files (the "Software"), including without * limitation the rights to use, copy, modify, merge, publish, distribute, * sublicense, and/or sell copies of the Software, and to permit persons who * receive copies from any such party to do so, with the only requirement being * that this copyright notice remain intact. */ /* * This class handles Neural-Net quantization algorithm * @author Kevin Weiner (original Java version - kweiner@fmsware.com) * @author Thibault Imbert (AS3 version - bytearray.org) * @version 0.1 AS3 implementation * @version 0.2 JS->AS3 "translation" by antimatter15 * @version 0.3 JS clean up + using modern JS idioms by sole - http://soledadpenades.com * Also implement fix in color conversion described at http://stackoverflow.com/questions/16371712/neuquant-js-javascript-color-quantization-hidden-bug-in-js-conversion */ function NeuQuant() { var netsize = 256; // number of colours used // four primes near 500 - assume no image has a length so large // that it is divisible by all four primes var prime1 = 499; var prime2 = 491; var prime3 = 487; var prime4 = 503; // minimum size for input image var minpicturebytes = 3 * prime4; // Network Definitions var maxnetpos = netsize - 1; var netbiasshift = 4; // bias for colour values var ncycles = 100; // no. of learning cycles // defs for freq and bias var intbiasshift = 16; // bias for fractions var intbias = 1 << intbiasshift; var gammashift = 10; // gamma = 1024 var betashift = 10; var beta = intbias >> betashift; // beta = 1/1024 var betagamma = intbias << gammashift - betashift; // defs for decreasing radius factor // For 256 colors, radius starts at 32.0 biased by 6 bits // and decreases by a factor of 1/30 each cycle var initrad = netsize >> 3; var radiusbiasshift = 6; var radiusbias = 1 << radiusbiasshift; var initradius = initrad * radiusbias; var radiusdec = 30; // defs for decreasing alpha factor // Alpha starts at 1.0 biased by 10 bits var alphabiasshift = 10; var initalpha = 1 << alphabiasshift; var alphadec; // radbias and alpharadbias used for radpower calculation var radbiasshift = 8; var radbias = 1 << radbiasshift; var alpharadbshift = alphabiasshift + radbiasshift; var alpharadbias = 1 << alpharadbshift; // Input image var thepicture; // Height * Width * 3 var lengthcount; // Sampling factor 1..30 var samplefac; // The network itself var network; var netindex = []; // for network lookup - really 256 var bias = []; // bias and freq arrays for learning var freq = []; var radpower = []; function NeuQuantConstructor(thepic, len, sample) { var i; var p; thepicture = thepic; lengthcount = len; samplefac = sample; network = new Array(netsize); for (i = 0; i < netsize; i++) { network[i] = new Array(4); p = network[i]; p[0] = p[1] = p[2] = (i << netbiasshift + 8) / netsize | 0; freq[i] = intbias / netsize | 0; // 1 / netsize bias[i] = 0; } } function colorMap() { var map = []; var index = new Array(netsize); for (var i = 0; i < netsize; i++) { index[network[i][3]] = i; }var k = 0; for (var l = 0; l < netsize; l++) { var j = index[l]; map[k++] = network[j][0]; map[k++] = network[j][1]; map[k++] = network[j][2]; } return map; } // Insertion sort of network and building of netindex[0..255] // (to do after unbias) function inxbuild() { var i; var j; var smallpos; var smallval; var p; var q; var previouscol; var startpos; previouscol = 0; startpos = 0; for (i = 0; i < netsize; i++) { p = network[i]; smallpos = i; smallval = p[1]; // index on g // find smallest in i..netsize-1 for (j = i + 1; j < netsize; j++) { q = network[j]; if (q[1] < smallval) { // index on g smallpos = j; smallval = q[1]; // index on g } } q = network[smallpos]; // swap p (i) and q (smallpos) entries if (i != smallpos) { j = q[0]; q[0] = p[0]; p[0] = j; j = q[1]; q[1] = p[1]; p[1] = j; j = q[2]; q[2] = p[2]; p[2] = j; j = q[3]; q[3] = p[3]; p[3] = j; } // smallval entry is now in position i if (smallval != previouscol) { netindex[previouscol] = startpos + i >> 1; for (j = previouscol + 1; j < smallval; j++) { netindex[j] = i; } previouscol = smallval; startpos = i; } } netindex[previouscol] = startpos + maxnetpos >> 1; for (j = previouscol + 1; j < 256; j++) { netindex[j] = maxnetpos; // really 256 } } // Main Learning Loop function learn() { var i; var j; var b; var g; var r; var radius; var rad; var alpha; var step; var delta; var samplepixels; var p; var pix; var lim; if (lengthcount < minpicturebytes) { samplefac = 1; } alphadec = 30 + (samplefac - 1) / 3; p = thepicture; pix = 0; lim = lengthcount; samplepixels = lengthcount / (3 * samplefac); delta = samplepixels / ncycles | 0; alpha = initalpha; radius = initradius; rad = radius >> radiusbiasshift; if (rad <= 1) { rad = 0; } for (i = 0; i < rad; i++) { radpower[i] = alpha * ((rad * rad - i * i) * radbias / (rad * rad)); } if (lengthcount < minpicturebytes) { step = 3; } else if (lengthcount % prime1 !== 0) { step = 3 * prime1; } else { if (lengthcount % prime2 !== 0) { step = 3 * prime2; } else { if (lengthcount % prime3 !== 0) { step = 3 * prime3; } else { step = 3 * prime4; } } } i = 0; while (i < samplepixels) { b = (p[pix + 0] & 0xff) << netbiasshift; g = (p[pix + 1] & 0xff) << netbiasshift; r = (p[pix + 2] & 0xff) << netbiasshift; j = contest(b, g, r); altersingle(alpha, j, b, g, r); if (rad !== 0) { // Alter neighbours alterneigh(rad, j, b, g, r); } pix += step; if (pix >= lim) { pix -= lengthcount; } i++; if (delta === 0) { delta = 1; } if (i % delta === 0) { alpha -= alpha / alphadec; radius -= radius / radiusdec; rad = radius >> radiusbiasshift; if (rad <= 1) { rad = 0; } for (j = 0; j < rad; j++) { radpower[j] = alpha * ((rad * rad - j * j) * radbias / (rad * rad)); } } } } // Search for BGR values 0..255 (after net is unbiased) and return colour index function map(b, g, r) { var i; var j; var dist; var a; var bestd; var p; var best; // Biggest possible distance is 256 * 3 bestd = 1000; best = -1; i = netindex[g]; // index on g j = i - 1; // start at netindex[g] and work outwards while (i < netsize || j >= 0) { if (i < netsize) { p = network[i]; dist = p[1] - g; // inx key if (dist >= bestd) { i = netsize; // stop iter } else { i++; if (dist < 0) { dist = -dist; } a = p[0] - b; if (a < 0) { a = -a; } dist += a; if (dist < bestd) { a = p[2] - r; if (a < 0) { a = -a; } dist += a; if (dist < bestd) { bestd = dist; best = p[3]; } } } } if (j >= 0) { p = network[j]; dist = g - p[1]; // inx key - reverse dif if (dist >= bestd) { j = -1; // stop iter } else { j--; if (dist < 0) { dist = -dist; } a = p[0] - b; if (a < 0) { a = -a; } dist += a; if (dist < bestd) { a = p[2] - r; if (a < 0) { a = -a; } dist += a; if (dist < bestd) { bestd = dist; best = p[3]; } } } } } return best; } function process() { learn(); unbiasnet(); inxbuild(); return colorMap(); } // Unbias network to give byte values 0..255 and record position i // to prepare for sort function unbiasnet() { var i; for (i = 0; i < netsize; i++) { network[i][0] >>= netbiasshift; network[i][1] >>= netbiasshift; network[i][2] >>= netbiasshift; network[i][3] = i; // record colour no } } // Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) // in radpower[|i-j|] function alterneigh(rad, i, b, g, r) { var j; var k; var lo; var hi; var a; var m; var p; lo = i - rad; if (lo < -1) { lo = -1; } hi = i + rad; if (hi > netsize) { hi = netsize; } j = i + 1; k = i - 1; m = 1; while (j < hi || k > lo) { a = radpower[m++]; if (j < hi) { p = network[j++]; try { p[0] -= a * (p[0] - b) / alpharadbias | 0; p[1] -= a * (p[1] - g) / alpharadbias | 0; p[2] -= a * (p[2] - r) / alpharadbias | 0; } catch (e) {} } if (k > lo) { p = network[k--]; try { p[0] -= a * (p[0] - b) / alpharadbias | 0; p[1] -= a * (p[1] - g) / alpharadbias | 0; p[2] -= a * (p[2] - r) / alpharadbias | 0; } catch (e) {} } } } // Move neuron i towards biased (b,g,r) by factor alpha function altersingle(alpha, i, b, g, r) { // alter hit neuron var n = network[i]; var alphaMult = alpha / initalpha; n[0] -= alphaMult * (n[0] - b) | 0; n[1] -= alphaMult * (n[1] - g) | 0; n[2] -= alphaMult * (n[2] - r) | 0; } // Search for biased BGR values function contest(b, g, r) { // finds closest neuron (min dist) and updates freq // finds best neuron (min dist-bias) and returns position // for frequently chosen neurons, freq[i] is high and bias[i] is negative // bias[i] = gamma*((1/netsize)-freq[i]) var i; var dist; var a; var biasdist; var betafreq; var bestpos; var bestbiaspos; var bestd; var bestbiasd; var n; bestd = ~(1 << 31); bestbiasd = bestd; bestpos = -1; bestbiaspos = bestpos; for (i = 0; i < netsize; i++) { n = network[i]; dist = n[0] - b; if (dist < 0) { dist = -dist; } a = n[1] - g; if (a < 0) { a = -a; } dist += a; a = n[2] - r; if (a < 0) { a = -a; } dist += a; if (dist < bestd) { bestd = dist; bestpos = i; } biasdist = dist - (bias[i] >> intbiasshift - netbiasshift); if (biasdist < bestbiasd) { bestbiasd = biasdist; bestbiaspos = i; } betafreq = freq[i] >> betashift; freq[i] -= betafreq; bias[i] += betafreq << gammashift; } freq[bestpos] += beta; bias[bestpos] -= betagamma; return bestbiaspos; } NeuQuantConstructor.apply(this, arguments); var exports = {}; exports.map = map; exports.process = process; return exports; } /* processFrameWorker.js ===================== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ function workerCode() { var self = this; try { self.onmessage = function (ev) { var data = ev.data || {}; var response; if (data.gifshot) { response = workerMethods.run(data); postMessage(response); } }; } catch (e) {} var workerMethods = { dataToRGB: function dataToRGB(data, width, height) { var length = width * height * 4; var i = 0; var rgb = []; while (i < length) { rgb.push(data[i++]); rgb.push(data[i++]); rgb.push(data[i++]); i++; // for the alpha channel which we don't care about } return rgb; }, componentizedPaletteToArray: function componentizedPaletteToArray(paletteRGB) { paletteRGB = paletteRGB || []; var paletteArray = []; for (var i = 0; i < paletteRGB.length; i += 3) { var r = paletteRGB[i]; var g = paletteRGB[i + 1]; var b = paletteRGB[i + 2]; paletteArray.push(r << 16 | g << 8 | b); } return paletteArray; }, // This is the "traditional" Animated_GIF style of going from RGBA to indexed color frames 'processFrameWithQuantizer': function processFrameWithQuantizer(imageData, width, height, sampleInterval) { var rgbComponents = this.dataToRGB(imageData, width, height); var nq = new NeuQuant(rgbComponents, rgbComponents.length, sampleInterval); var paletteRGB = nq.process(); var paletteArray = new Uint32Array(this.componentizedPaletteToArray(paletteRGB)); var numberPixels = width * height; var indexedPixels = new Uint8Array(numberPixels); var k = 0; for (var i = 0; i < numberPixels; i++) { var r = rgbComponents[k++]; var g = rgbComponents[k++]; var b = rgbComponents[k++]; indexedPixels[i] = nq.map(r, g, b); } return { pixels: indexedPixels, palette: paletteArray }; }, 'run': function run(frame) { frame = frame || {}; var _frame = frame, height = _frame.height, palette = _frame.palette, sampleInterval = _frame.sampleInterval, width = _frame.width; var imageData = frame.data; return this.processFrameWithQuantizer(imageData, width, height, sampleInterval); } }; return workerMethods; } /* gifWriter.js ============ */ // (c) Dean McNamee , 2013. // // https://github.com/deanm/omggif // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. // // omggif is a JavaScript implementation of a GIF 89a encoder and decoder, // including animation and compression. It does not rely on any specific // underlying system, so should run in the browser, Node, or Plask. function gifWriter(buf, width, height, gopts) { var p = 0; gopts = gopts === undefined$1 ? {} : gopts; var loop_count = gopts.loop === undefined$1 ? null : gopts.loop; var global_palette = gopts.palette === undefined$1 ? null : gopts.palette; if (width <= 0 || height <= 0 || width > 65535 || height > 65535) throw "Width/Height invalid."; function check_palette_and_num_colors(palette) { var num_colors = palette.length; if (num_colors < 2 || num_colors > 256 || num_colors & num_colors - 1) throw "Invalid code/color length, must be power of 2 and 2 .. 256."; return num_colors; } // - Header. buf[p++] = 0x47; buf[p++] = 0x49; buf[p++] = 0x46; // GIF buf[p++] = 0x38; buf[p++] = 0x39; buf[p++] = 0x61; // 89a // Handling of Global Color Table (palette) and background index. var gp_num_colors_pow2 = 0; var background = 0; // - Logical Screen Descriptor. // NOTE(deanm): w/h apparently ignored by implementations, but set anyway. buf[p++] = width & 0xff; buf[p++] = width >> 8 & 0xff; buf[p++] = height & 0xff; buf[p++] = height >> 8 & 0xff; // NOTE: Indicates 0-bpp original color resolution (unused?). buf[p++] = (global_palette !== null ? 0x80 : 0) | // Global Color Table Flag. gp_num_colors_pow2; // NOTE: No sort flag (unused?). buf[p++] = background; // Background Color Index. buf[p++] = 0; // Pixel aspect ratio (unused?). if (loop_count !== null) { // Netscape block for looping. if (loop_count < 0 || loop_count > 65535) throw "Loop count invalid."; // Extension code, label, and length. buf[p++] = 0x21; buf[p++] = 0xff; buf[p++] = 0x0b; // NETSCAPE2.0 buf[p++] = 0x4e; buf[p++] = 0x45; buf[p++] = 0x54; buf[p++] = 0x53; buf[p++] = 0x43; buf[p++] = 0x41; buf[p++] = 0x50; buf[p++] = 0x45; buf[p++] = 0x32; buf[p++] = 0x2e; buf[p++] = 0x30; // Sub-block buf[p++] = 0x03; buf[p++] = 0x01; buf[p++] = loop_count & 0xff; buf[p++] = loop_count >> 8 & 0xff; buf[p++] = 0x00; // Terminator. } var ended = false; this.addFrame = function (x, y, w, h, indexed_pixels, opts) { if (ended === true) { --p; ended = false; } // Un-end. opts = opts === undefined$1 ? {} : opts; // TODO(deanm): Bounds check x, y. Do they need to be within the virtual // canvas width/height, I imagine? if (x < 0 || y < 0 || x > 65535 || y > 65535) throw "x/y invalid."; if (w <= 0 || h <= 0 || w > 65535 || h > 65535) throw "Width/Height invalid."; if (indexed_pixels.length < w * h) throw "Not enough pixels for the frame size."; var using_local_palette = true; var palette = opts.palette; if (palette === undefined$1 || palette === null) { using_local_palette = false; palette = global_palette; } if (palette === undefined$1 || palette === null) throw "Must supply either a local or global palette."; var num_colors = check_palette_and_num_colors(palette); // Compute the min_code_size (power of 2), destroying num_colors. var min_code_size = 0; while (num_colors >>= 1) { ++min_code_size; }num_colors = 1 << min_code_size; // Now we can easily get it back. var delay = opts.delay === undefined$1 ? 0 : opts.delay; // From the spec: // 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. // 4-7 - To be defined. // NOTE(deanm): Dispose background doesn't really work, apparently most // browsers ignore the background palette index and clear to transparency. var disposal = opts.disposal === undefined$1 ? 0 : opts.disposal; if (disposal < 0 || disposal > 3) // 4-7 is reserved. throw "Disposal out of range."; var use_transparency = false; var transparent_index = 0; if (opts.transparent !== undefined$1 && opts.transparent !== null) { use_transparency = true; transparent_index = opts.transparent; if (transparent_index < 0 || transparent_index >= num_colors) throw "Transparent color index."; } if (disposal !== 0 || use_transparency || delay !== 0) { // - Graphics Control Extension buf[p++] = 0x21; buf[p++] = 0xf9; // Extension / Label. buf[p++] = 4; // Byte size. buf[p++] = disposal << 2 | (use_transparency === true ? 1 : 0); buf[p++] = delay & 0xff; buf[p++] = delay >> 8 & 0xff; buf[p++] = transparent_index; // Transparent color index. buf[p++] = 0; // Block Terminator. } // - Image Descriptor buf[p++] = 0x2c; // Image Seperator. buf[p++] = x & 0xff; buf[p++] = x >> 8 & 0xff; // Left. buf[p++] = y & 0xff; buf[p++] = y >> 8 & 0xff; // Top. buf[p++] = w & 0xff; buf[p++] = w >> 8 & 0xff; buf[p++] = h & 0xff; buf[p++] = h >> 8 & 0xff; // NOTE: No sort flag (unused?). // TODO(deanm): Support interlace. buf[p++] = using_local_palette === true ? 0x80 | min_code_size - 1 : 0; // - Local Color Table if (using_local_palette === true) { for (var i = 0, il = palette.length; i < il; ++i) { var rgb = palette[i]; buf[p++] = rgb >> 16 & 0xff; buf[p++] = rgb >> 8 & 0xff; buf[p++] = rgb & 0xff; } } p = GifWriterOutputLZWCodeStream(buf, p, min_code_size < 2 ? 2 : min_code_size, indexed_pixels); }; this.end = function () { if (ended === false) { buf[p++] = 0x3b; // Trailer. ended = true; } return p; }; // Main compression routine, palette indexes -> LZW code stream. // |index_stream| must have at least one entry. function GifWriterOutputLZWCodeStream(buf, p, min_code_size, index_stream) { buf[p++] = min_code_size; var cur_subblock = p++; // Pointing at the length field. var clear_code = 1 << min_code_size; var code_mask = clear_code - 1; var eoi_code = clear_code + 1; var next_code = eoi_code + 1; var cur_code_size = min_code_size + 1; // Number of bits per code. var cur_shift = 0; // We have at most 12-bit codes, so we should have to hold a max of 19 // bits here (and then we would write out). var cur = 0; function emit_bytes_to_buffer(bit_block_size) { while (cur_shift >= bit_block_size) { buf[p++] = cur & 0xff; cur >>= 8; cur_shift -= 8; if (p === cur_subblock + 256) { // Finished a subblock. buf[cur_subblock] = 255; cur_subblock = p++; } } } function emit_code(c) { cur |= c << cur_shift; cur_shift += cur_code_size; emit_bytes_to_buffer(8); } // I am not an expert on the topic, and I don't want to write a thesis. // However, it is good to outline here the basic algorithm and the few data // structures and optimizations here that make this implementation fast. // The basic idea behind LZW is to build a table of previously seen runs // addressed by a short id (herein called output code). All data is // referenced by a code, which represents one or more values from the // original input stream. All input bytes can be referenced as the same // value as an output code. So if you didn't want any compression, you // could more or less just output the original bytes as codes (there are // some details to this, but it is the idea). In order to achieve // compression, values greater then the input range (codes can be up to // 12-bit while input only 8-bit) represent a sequence of previously seen // inputs. The decompressor is able to build the same mapping while // decoding, so there is always a shared common knowledge between the // encoding and decoder, which is also important for "timing" aspects like // how to handle variable bit width code encoding. // // One obvious but very important consequence of the table system is there // is always a unique id (at most 12-bits) to map the runs. 'A' might be // 4, then 'AA' might be 10, 'AAA' 11, 'AAAA' 12, etc. This relationship // can be used for an effecient lookup strategy for the code mapping. We // need to know if a run has been seen before, and be able to map that run // to the output code. Since we start with known unique ids (input bytes), // and then from those build more unique ids (table entries), we can // continue this chain (almost like a linked list) to always have small // integer values that represent the current byte chains in the encoder. // This means instead of tracking the input bytes (AAAABCD) to know our // current state, we can track the table entry for AAAABC (it is guaranteed // to exist by the nature of the algorithm) and the next character D. // Therefor the tuple of (table_entry, byte) is guaranteed to also be // unique. This allows us to create a simple lookup key for mapping input // sequences to codes (table indices) without having to store or search // any of the code sequences. So if 'AAAA' has a table entry of 12, the // tuple of ('AAAA', K) for any input byte K will be unique, and can be our // key. This leads to a integer value at most 20-bits, which can always // fit in an SMI value and be used as a fast sparse array / object key. // Output code for the current contents of the index buffer. var ib_code = index_stream[0] & code_mask; // Load first input index. var code_table = {}; // Key'd on our 20-bit "tuple". emit_code(clear_code); // Spec says first code should be a clear code. // First index already loaded, process the rest of the stream. for (var i = 1, il = index_stream.length; i < il; ++i) { var k = index_stream[i] & code_mask; var cur_key = ib_code << 8 | k; // (prev, k) unique tuple. var cur_code = code_table[cur_key]; // buffer + k. // Check if we have to create a new code table entry. if (cur_code === undefined$1) { // We don't have buffer + k. // Emit index buffer (without k). // This is an inline version of emit_code, because this is the core // writing routine of the compressor (and V8 cannot inline emit_code // because it is a closure here in a different context). Additionally // we can call emit_byte_to_buffer less often, because we can have // 30-bits (from our 31-bit signed SMI), and we know our codes will only // be 12-bits, so can safely have 18-bits there without overflow. // emit_code(ib_code); cur |= ib_code << cur_shift; cur_shift += cur_code_size; while (cur_shift >= 8) { buf[p++] = cur & 0xff; cur >>= 8; cur_shift -= 8; if (p === cur_subblock + 256) { // Finished a subblock. buf[cur_subblock] = 255; cur_subblock = p++; } } if (next_code === 4096) { // Table full, need a clear. emit_code(clear_code); next_code = eoi_code + 1; cur_code_size = min_code_size + 1; code_table = {}; } else { // Table not full, insert a new entry. // Increase our variable bit code sizes if necessary. This is a bit // tricky as it is based on "timing" between the encoding and // decoder. From the encoders perspective this should happen after // we've already emitted the index buffer and are about to create the // first table entry that would overflow our current code bit size. if (next_code >= 1 << cur_code_size) ++cur_code_size; code_table[cur_key] = next_code++; // Insert into code table. } ib_code = k; // Index buffer to single input k. } else { ib_code = cur_code; // Index buffer to sequence in code table. } } emit_code(ib_code); // There will still be something in the index buffer. emit_code(eoi_code); // End Of Information. // Flush / finalize the sub-blocks stream to the buffer. emit_bytes_to_buffer(1); // Finish the sub-blocks, writing out any unfinished lengths and // terminating with a sub-block of length 0. If we have already started // but not yet used a sub-block it can just become the terminator. if (cur_subblock + 1 === p) { // Started but unused. buf[cur_subblock] = 0; } else { // Started and used, write length and additional terminator block. buf[cur_subblock] = p - cur_subblock - 1; buf[p++] = 0; } return p; } } /* animatedGIF.js ============== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Dependencies // Helpers var noop$2 = function noop() {}; var AnimatedGIF = function AnimatedGIF(options) { this.canvas = null; this.ctx = null; this.repeat = 0; this.frames = []; this.numRenderedFrames = 0; this.onRenderCompleteCallback = noop$2; this.onRenderProgressCallback = noop$2; this.workers = []; this.availableWorkers = []; this.generatingGIF = false; this.options = options; // Constructs and initializes the the web workers appropriately this.initializeWebWorkers(options); }; AnimatedGIF.prototype = { 'workerMethods': workerCode(), 'initializeWebWorkers': function initializeWebWorkers(options) { var self = this; var processFrameWorkerCode = NeuQuant.toString() + '(' + workerCode.toString() + '());'; var webWorkerObj = void 0; var objectUrl = void 0; var webWorker = void 0; var numWorkers = void 0; var x = -1; var workerError = ''; numWorkers = options.numWorkers; while (++x < numWorkers) { webWorkerObj = utils.createWebWorker(processFrameWorkerCode); if (utils.isObject(webWorkerObj)) { objectUrl = webWorkerObj.objectUrl; webWorker = webWorkerObj.worker; self.workers.push({ worker: webWorker, objectUrl: objectUrl }); self.availableWorkers.push(webWorker); } else { workerError = webWorkerObj; utils.webWorkerError = !!webWorkerObj; } } this.workerError = workerError; this.canvas = document.createElement('canvas'); this.canvas.width = options.gifWidth; this.canvas.height = options.gifHeight; this.ctx = this.canvas.getContext('2d'); this.frames = []; }, // Return a worker for processing a frame getWorker: function getWorker() { return this.availableWorkers.pop(); }, // Restores a worker to the pool freeWorker: function freeWorker(worker) { this.availableWorkers.push(worker); }, byteMap: function () { var byteMap = []; for (var i = 0; i < 256; i++) { byteMap[i] = String.fromCharCode(i); } return byteMap; }(), bufferToString: function bufferToString(buffer) { var numberValues = buffer.length; var str = ''; var x = -1; while (++x < numberValues) { str += this.byteMap[buffer[x]]; } return str; }, onFrameFinished: function onFrameFinished(progressCallback) { // The GIF is not written until we're done with all the frames // because they might not be processed in the same order var self = this; var frames = self.frames; var options = self.options; var hasExistingImages = !!(options.images || []).length; var allDone = frames.every(function (frame) { return !frame.beingProcessed && frame.done; }); self.numRenderedFrames++; if (hasExistingImages) { progressCallback(self.numRenderedFrames / frames.length); } self.onRenderProgressCallback(self.numRenderedFrames * 0.75 / frames.length); if (allDone) { if (!self.generatingGIF) { self.generateGIF(frames, self.onRenderCompleteCallback); } } else { utils.requestTimeout(function () { self.processNextFrame(); }, 1); } }, processFrame: function processFrame(position) { var AnimatedGifContext = this; var options = this.options; var _options = this.options, progressCallback = _options.progressCallback, sampleInterval = _options.sampleInterval; var frames = this.frames; var frame = void 0; var worker = void 0; var done = function done() { var ev = arguments.length > 0 && arguments[0] !== undefined$1 ? arguments[0] : {}; var data = ev.data; // Delete original data, and free memory delete frame.data; frame.pixels = Array.prototype.slice.call(data.pixels); frame.palette = Array.prototype.slice.call(data.palette); frame.done = true; frame.beingProcessed = false; AnimatedGifContext.freeWorker(worker); AnimatedGifContext.onFrameFinished(progressCallback); }; frame = frames[position]; if (frame.beingProcessed || frame.done) { this.onFrameFinished(); return; } frame.sampleInterval = sampleInterval; frame.beingProcessed = true; frame.gifshot = true; worker = this.getWorker(); if (worker) { // Process the frame in a web worker worker.onmessage = done; worker.postMessage(frame); } else { // Process the frame in the current thread done({ 'data': AnimatedGifContext.workerMethods.run(frame) }); } }, startRendering: function startRendering(completeCallback) { this.onRenderCompleteCallback = completeCallback; for (var i = 0; i < this.options.numWorkers && i < this.frames.length; i++) { this.processFrame(i); } }, processNextFrame: function processNextFrame() { var position = -1; for (var i = 0; i < this.frames.length; i++) { var frame = this.frames[i]; if (!frame.done && !frame.beingProcessed) { position = i; break; } } if (position >= 0) { this.processFrame(position); } }, // Takes the already processed data in frames and feeds it to a new // GifWriter instance in order to get the binary GIF file generateGIF: function generateGIF(frames, callback) { // TODO: Weird: using a simple JS array instead of a typed array, // the files are WAY smaller o_o. Patches/explanations welcome! var buffer = []; // new Uint8Array(width * height * frames.length * 5); var gifOptions = { loop: this.repeat }; var options = this.options; var interval = options.interval; var frameDuration = options.frameDuration; var existingImages = options.images; var hasExistingImages = !!existingImages.length; var height = options.gifHeight; var width = options.gifWidth; var gifWriter$$1 = new gifWriter(buffer, width, height, gifOptions); var onRenderProgressCallback = this.onRenderProgressCallback; var delay = hasExistingImages ? interval * 100 : 0; var bufferToString = void 0; var gif = void 0; this.generatingGIF = true; utils.each(frames, function (iterator, frame) { var framePalette = frame.palette; onRenderProgressCallback(0.75 + 0.25 * frame.position * 1.0 / frames.length); for (var i = 0; i < frameDuration; i++) { gifWriter$$1.addFrame(0, 0, width, height, frame.pixels, { palette: framePalette, delay: delay }); } }); gifWriter$$1.end(); onRenderProgressCallback(1.0); this.frames = []; this.generatingGIF = false; if (utils.isFunction(callback)) { bufferToString = this.bufferToString(buffer); gif = 'data:image/gif;base64,' + utils.btoa(bufferToString); callback(gif); } }, // From GIF: 0 = loop forever, null = not looping, n > 0 = loop n times and stop setRepeat: function setRepeat(r) { this.repeat = r; }, addFrame: function addFrame(element, gifshotOptions) { gifshotOptions = utils.isObject(gifshotOptions) ? gifshotOptions : {}; var self = this; var ctx = self.ctx; var options = self.options; var width = options.gifWidth; var height = options.gifHeight; var fontSize = utils.getFontSize(gifshotOptions); var _gifshotOptions = gifshotOptions, filter = _gifshotOptions.filter, fontColor = _gifshotOptions.fontColor, fontFamily = _gifshotOptions.fontFamily, fontWeight = _gifshotOptions.fontWeight, gifHeight = _gifshotOptions.gifHeight, gifWidth = _gifshotOptions.gifWidth, text = _gifshotOptions.text, textAlign = _gifshotOptions.textAlign, textBaseline = _gifshotOptions.textBaseline; var textXCoordinate = gifshotOptions.textXCoordinate ? gifshotOptions.textXCoordinate : textAlign === 'left' ? 1 : textAlign === 'right' ? width : width / 2; var textYCoordinate = gifshotOptions.textYCoordinate ? gifshotOptions.textYCoordinate : textBaseline === 'top' ? 1 : textBaseline === 'center' ? height / 2 : height; var font = fontWeight + ' ' + fontSize + ' ' + fontFamily; var imageData = void 0; try { ctx.filter = filter; ctx.drawImage(element, 0, 0, width, height); if (text) { ctx.font = font; ctx.fillStyle = fontColor; ctx.textAlign = textAlign; ctx.textBaseline = textBaseline; ctx.fillText(text, textXCoordinate, textYCoordinate); } imageData = ctx.getImageData(0, 0, width, height); self.addFrameImageData(imageData); } catch (e) { return '' + e; } }, addFrameImageData: function addFrameImageData() { var imageData = arguments.length > 0 && arguments[0] !== undefined$1 ? arguments[0] : {}; var frames = this.frames; var imageDataArray = imageData.data; this.frames.push({ 'data': imageDataArray, 'width': imageData.width, 'height': imageData.height, 'palette': null, 'dithering': null, 'done': false, 'beingProcessed': false, 'position': frames.length }); }, onRenderProgress: function onRenderProgress(callback) { this.onRenderProgressCallback = callback; }, isRendering: function isRendering() { return this.generatingGIF; }, getBase64GIF: function getBase64GIF(completeCallback) { var self = this; var onRenderComplete = function onRenderComplete(gif) { self.destroyWorkers(); utils.requestTimeout(function () { completeCallback(gif); }, 0); }; self.startRendering(onRenderComplete); }, destroyWorkers: function destroyWorkers() { if (this.workerError) { return; } var workers = this.workers; // Explicitly ask web workers to die so they are explicitly GC'ed utils.each(workers, function (iterator, workerObj) { var worker = workerObj.worker; var objectUrl = workerObj.objectUrl; worker.terminate(); utils.URL.revokeObjectURL(objectUrl); }); } }; /* getBase64GIF.js =============== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ function getBase64GIF(animatedGifInstance, callback) { // This is asynchronous, rendered with WebWorkers animatedGifInstance.getBase64GIF(function (image) { callback({ error: false, errorCode: '', errorMsg: '', image: image }); }); } /* existingImages.js ================= */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ function existingImages() { var obj = arguments.length > 0 && arguments[0] !== undefined$1 ? arguments[0] : {}; var callback = obj.callback, images = obj.images, options = obj.options; var imagesLength = obj.imagesLength; var skipObj = { 'getUserMedia': true, 'window.URL': true }; var errorObj = error.validate(skipObj); var loadedImages = []; var loadedImagesLength = 0; var tempImage = void 0; var ag = void 0; if (errorObj.error) { return callback(errorObj); } // change workerPath to point to where Animated_GIF.worker.js is ag = new AnimatedGIF(options); utils.each(images, function (index, image) { var currentImage = image; if (image.src) { currentImage = currentImage.src; } if (utils.isElement(currentImage)) { if (options.crossOrigin) { currentImage.crossOrigin = options.crossOrigin; } loadedImages[index] = currentImage; loadedImagesLength += 1; if (loadedImagesLength === imagesLength) { addLoadedImagesToGif(); } } else if (utils.isString(currentImage)) { tempImage = new Image(); if (options.crossOrigin) { tempImage.crossOrigin = options.crossOrigin; } (function (tempImage) { if (image.text) { tempImage.text = image.text; } tempImage.onerror = function (e) { var obj = void 0; --imagesLength; // skips over images that error out if (imagesLength === 0) { obj = {}; obj.error = 'None of the requested images was capable of being retrieved'; return callback(obj); } }; tempImage.onload = function (e) { if (image.text) { loadedImages[index] = { img: tempImage, text: tempImage.text }; } else { loadedImages[index] = tempImage; } loadedImagesLength += 1; if (loadedImagesLength === imagesLength) { addLoadedImagesToGif(); } utils.removeElement(tempImage); }; tempImage.src = currentImage; })(tempImage); utils.setCSSAttr(tempImage, { position: 'fixed', opacity: '0' }); document.body.appendChild(tempImage); } }); function addLoadedImagesToGif() { utils.each(loadedImages, function (index, loadedImage) { if (loadedImage) { if (loadedImage.text) { ag.addFrame(loadedImage.img, options, loadedImage.text); } else { ag.addFrame(loadedImage, options); } } }); getBase64GIF(ag, callback); } } /* screenShot.js ============= */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Dependencies // Helpers var noop$3 = function noop() {}; var screenShot = { getGIF: function getGIF() { var options = arguments.length > 0 && arguments[0] !== undefined$1 ? arguments[0] : {}; var callback = arguments[1]; callback = utils.isFunction(callback) ? callback : noop$3; var canvas = document.createElement('canvas'); var context = void 0; var existingImages = options.images; var hasExistingImages = !!existingImages.length; var cameraStream = options.cameraStream, crop = options.crop, filter = options.filter, fontColor = options.fontColor, fontFamily = options.fontFamily, fontWeight = options.fontWeight, keepCameraOn = options.keepCameraOn, numWorkers = options.numWorkers, progressCallback = options.progressCallback, saveRenderingContexts = options.saveRenderingContexts, savedRenderingContexts = options.savedRenderingContexts, text = options.text, textAlign = options.textAlign, textBaseline = options.textBaseline, videoElement = options.videoElement, videoHeight = options.videoHeight, videoWidth = options.videoWidth, webcamVideoElement = options.webcamVideoElement; var gifWidth = Number(options.gifWidth); var gifHeight = Number(options.gifHeight); var interval = Number(options.interval); var sampleInterval = Number(options.sampleInterval); var waitBetweenFrames = hasExistingImages ? 0 : interval * 1000; var renderingContextsToSave = []; var numFrames = savedRenderingContexts.length ? savedRenderingContexts.length : options.numFrames; var pendingFrames = numFrames; var ag = new AnimatedGIF(options); var fontSize = utils.getFontSize(options); var textXCoordinate = options.textXCoordinate ? options.textXCoordinate : textAlign === 'left' ? 1 : textAlign === 'right' ? gifWidth : gifWidth / 2; var textYCoordinate = options.textYCoordinate ? options.textYCoordinate : textBaseline === 'top' ? 1 : textBaseline === 'center' ? gifHeight / 2 : gifHeight; var font = fontWeight + ' ' + fontSize + ' ' + fontFamily; var sourceX = crop ? Math.floor(crop.scaledWidth / 2) : 0; var sourceWidth = crop ? videoWidth - crop.scaledWidth : 0; var sourceY = crop ? Math.floor(crop.scaledHeight / 2) : 0; var sourceHeight = crop ? videoHeight - crop.scaledHeight : 0; var captureFrames = function captureSingleFrame() { var framesLeft = pendingFrames - 1; if (savedRenderingContexts.length) { context.putImageData(savedRenderingContexts[numFrames - pendingFrames], 0, 0); finishCapture(); } else { drawVideo(); } function drawVideo() { try { // Makes sure the canvas video heights/widths are in bounds if (sourceWidth > videoWidth) { sourceWidth = videoWidth; } if (sourceHeight > videoHeight) { sourceHeight = videoHeight; } if (sourceX < 0) { sourceX = 0; } if (sourceY < 0) { sourceY = 0; } context.filter = filter; context.drawImage(videoElement, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, gifWidth, gifHeight); finishCapture(); } catch (e) { // There is a Firefox bug that sometimes throws NS_ERROR_NOT_AVAILABLE and // and IndexSizeError errors when drawing a video element to the canvas if (e.name === 'NS_ERROR_NOT_AVAILABLE') { // Wait 100ms before trying again utils.requestTimeout(drawVideo, 100); } else { throw e; } } } function finishCapture() { var imageData = void 0; if (saveRenderingContexts) { renderingContextsToSave.push(context.getImageData(0, 0, gifWidth, gifHeight)); } // If there is text to display, make sure to display it on the canvas after the image is drawn if (text) { context.font = font; context.fillStyle = fontColor; context.textAlign = textAlign; context.textBaseline = textBaseline; context.fillText(text, textXCoordinate, textYCoordinate); } imageData = context.getImageData(0, 0, gifWidth, gifHeight); ag.addFrameImageData(imageData); pendingFrames = framesLeft; // Call back with an r value indicating how far along we are in capture progressCallback((numFrames - pendingFrames) / numFrames); if (framesLeft > 0) { // test utils.requestTimeout(captureSingleFrame, waitBetweenFrames); } if (!pendingFrames) { ag.getBase64GIF(function (image) { callback({ 'error': false, 'errorCode': '', 'errorMsg': '', 'image': image, 'cameraStream': cameraStream, 'videoElement': videoElement, 'webcamVideoElement': webcamVideoElement, 'savedRenderingContexts': renderingContextsToSave, 'keepCameraOn': keepCameraOn }); }); } } }; numFrames = numFrames !== undefined$1 ? numFrames : 10; interval = interval !== undefined$1 ? interval : 0.1; // In seconds canvas.width = gifWidth; canvas.height = gifHeight; context = canvas.getContext('2d'); (function capture() { if (!savedRenderingContexts.length && videoElement.currentTime === 0) { utils.requestTimeout(capture, 100); return; } captureFrames(); })(); }, getCropDimensions: function getCropDimensions() { var obj = arguments.length > 0 && arguments[0] !== undefined$1 ? arguments[0] : {}; var width = obj.videoWidth; var height = obj.videoHeight; var gifWidth = obj.gifWidth; var gifHeight = obj.gifHeight; var result = { width: 0, height: 0, scaledWidth: 0, scaledHeight: 0 }; if (width > height) { result.width = Math.round(width * (gifHeight / height)) - gifWidth; result.scaledWidth = Math.round(result.width * (height / gifHeight)); } else { result.height = Math.round(height * (gifWidth / width)) - gifHeight; result.scaledHeight = Math.round(result.height * (width / gifWidth)); } return result; } }; /* videoStream.js ============== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Dependencies var videoStream = { loadedData: false, defaultVideoDimensions: { width: 640, height: 480 }, findVideoSize: function findVideoSizeMethod(obj) { findVideoSizeMethod.attempts = findVideoSizeMethod.attempts || 0; var cameraStream = obj.cameraStream, completedCallback = obj.completedCallback, videoElement = obj.videoElement; if (!videoElement) { return; } if (videoElement.videoWidth > 0 && videoElement.videoHeight > 0) { videoElement.removeEventListener('loadeddata', videoStream.findVideoSize); completedCallback({ videoElement: videoElement, cameraStream: cameraStream, videoWidth: videoElement.videoWidth, videoHeight: videoElement.videoHeight }); } else { if (findVideoSizeMethod.attempts < 10) { findVideoSizeMethod.attempts += 1; utils.requestTimeout(function () { videoStream.findVideoSize(obj); }, 400); } else { completedCallback({ videoElement: videoElement, cameraStream: cameraStream, videoWidth: videoStream.defaultVideoDimensions.width, videoHeight: videoStream.defaultVideoDimensions.height }); } } }, onStreamingTimeout: function onStreamingTimeout(callback) { if (utils.isFunction(callback)) { callback({ error: true, errorCode: 'getUserMedia', errorMsg: 'There was an issue with the getUserMedia API - Timed out while trying to start streaming', image: null, cameraStream: {} }); } }, stream: function stream(obj) { var existingVideo = utils.isArray(obj.existingVideo) ? obj.existingVideo[0] : obj.existingVideo; var cameraStream = obj.cameraStream, completedCallback = obj.completedCallback, streamedCallback = obj.streamedCallback, videoElement = obj.videoElement; if (utils.isFunction(streamedCallback)) { streamedCallback(); } if (existingVideo) { if (utils.isString(existingVideo)) { videoElement.src = existingVideo; videoElement.innerHTML = ''; } else if (existingVideo instanceof Blob) { try { videoElement.src = utils.URL.createObjectURL(existingVideo); } catch (e) {} videoElement.innerHTML = ''; } } else if (videoElement.mozSrcObject) { videoElement.mozSrcObject = cameraStream; } else if (utils.URL) { try { videoElement.srcObject = cameraStream; videoElement.src = utils.URL.createObjectURL(cameraStream); } catch (e) { videoElement.srcObject = cameraStream; } } videoElement.play(); utils.requestTimeout(function checkLoadedData() { checkLoadedData.count = checkLoadedData.count || 0; if (videoStream.loadedData === true) { videoStream.findVideoSize({ videoElement: videoElement, cameraStream: cameraStream, completedCallback: completedCallback }); videoStream.loadedData = false; } else { checkLoadedData.count += 1; if (checkLoadedData.count > 10) { videoStream.findVideoSize({ videoElement: videoElement, cameraStream: cameraStream, completedCallback: completedCallback }); } else { checkLoadedData(); } } }, 0); }, startStreaming: function startStreaming(obj) { var errorCallback = utils.isFunction(obj.error) ? obj.error : utils.noop; var streamedCallback = utils.isFunction(obj.streamed) ? obj.streamed : utils.noop; var completedCallback = utils.isFunction(obj.completed) ? obj.completed : utils.noop; var crossOrigin = obj.crossOrigin, existingVideo = obj.existingVideo, lastCameraStream = obj.lastCameraStream, options = obj.options, webcamVideoElement = obj.webcamVideoElement; var videoElement = utils.isElement(existingVideo) ? existingVideo : webcamVideoElement ? webcamVideoElement : document.createElement('video'); if (crossOrigin) { videoElement.crossOrigin = options.crossOrigin; } videoElement.autoplay = true; videoElement.loop = true; videoElement.muted = true; videoElement.addEventListener('loadeddata', function (event) { videoStream.loadedData = true; if (options.offset) { videoElement.currentTime = options.offset; } }); if (existingVideo) { videoStream.stream({ videoElement: videoElement, existingVideo: existingVideo, completedCallback: completedCallback }); } else if (lastCameraStream) { videoStream.stream({ videoElement: videoElement, cameraStream: lastCameraStream, streamedCallback: streamedCallback, completedCallback: completedCallback }); } else { utils.getUserMedia({ video: true }, function (stream) { videoStream.stream({ videoElement: videoElement, cameraStream: stream, streamedCallback: streamedCallback, completedCallback: completedCallback }); }, errorCallback); } }, startVideoStreaming: function startVideoStreaming(callback) { var options = arguments.length > 1 && arguments[1] !== undefined$1 ? arguments[1] : {}; var timeoutLength = options.timeout !== undefined$1 ? options.timeout : 0; var originalCallback = options.callback; var webcamVideoElement = options.webcamVideoElement; var noGetUserMediaSupportTimeout = void 0; // Some browsers apparently have support for video streaming because of the // presence of the getUserMedia function, but then do not answer our // calls for streaming. // So we'll set up this timeout and if nothing happens after a while, we'll // conclude that there's no actual getUserMedia support. if (timeoutLength > 0) { noGetUserMediaSupportTimeout = utils.requestTimeout(function () { videoStream.onStreamingTimeout(originalCallback); }, 10000); } videoStream.startStreaming({ error: function error() { originalCallback({ error: true, errorCode: 'getUserMedia', errorMsg: 'There was an issue with the getUserMedia API - the user probably denied permission', image: null, cameraStream: {} }); }, streamed: function streamed() { // The streaming started somehow, so we can assume there is getUserMedia support clearTimeout(noGetUserMediaSupportTimeout); }, completed: function completed() { var obj = arguments.length > 0 && arguments[0] !== undefined$1 ? arguments[0] : {}; var cameraStream = obj.cameraStream, videoElement = obj.videoElement, videoHeight = obj.videoHeight, videoWidth = obj.videoWidth; callback({ cameraStream: cameraStream, videoElement: videoElement, videoHeight: videoHeight, videoWidth: videoWidth }); }, lastCameraStream: options.lastCameraStream, webcamVideoElement: webcamVideoElement, crossOrigin: options.crossOrigin, options: options }); }, stopVideoStreaming: function stopVideoStreaming(obj) { obj = utils.isObject(obj) ? obj : {}; var _obj = obj, keepCameraOn = _obj.keepCameraOn, videoElement = _obj.videoElement, webcamVideoElement = _obj.webcamVideoElement; var cameraStream = obj.cameraStream || {}; var cameraStreamTracks = cameraStream.getTracks ? cameraStream.getTracks() || [] : []; var hasCameraStreamTracks = !!cameraStreamTracks.length; var firstCameraStreamTrack = cameraStreamTracks[0]; if (!keepCameraOn && hasCameraStreamTracks) { if (utils.isFunction(firstCameraStreamTrack.stop)) { // Stops the camera stream firstCameraStreamTrack.stop(); } } if (utils.isElement(videoElement) && !webcamVideoElement) { // Pauses the video, revokes the object URL (freeing up memory), and remove the video element videoElement.pause(); // Destroys the object url if (utils.isFunction(utils.URL.revokeObjectURL) && !utils.webWorkerError) { if (videoElement.src) { utils.URL.revokeObjectURL(videoElement.src); } } // Removes the video element from the DOM utils.removeElement(videoElement); } } }; /* stopVideoStreaming.js ===================== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ function stopVideoStreaming(options) { options = utils.isObject(options) ? options : {}; videoStream.stopVideoStreaming(options); } /* createAndGetGIF.js ================== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Dependencies function createAndGetGIF(obj, callback) { var options = obj.options || {}; var images = options.images, video = options.video; var gifWidth = Number(options.gifWidth); var gifHeight = Number(options.gifHeight); var numFrames = Number(options.numFrames); var cameraStream = obj.cameraStream, videoElement = obj.videoElement, videoWidth = obj.videoWidth, videoHeight = obj.videoHeight; var cropDimensions = screenShot.getCropDimensions({ videoWidth: videoWidth, videoHeight: videoHeight, gifHeight: gifHeight, gifWidth: gifWidth }); var completeCallback = callback; options.crop = cropDimensions; options.videoElement = videoElement; options.videoWidth = videoWidth; options.videoHeight = videoHeight; options.cameraStream = cameraStream; if (!utils.isElement(videoElement)) { return; } videoElement.width = gifWidth + cropDimensions.width; videoElement.height = gifHeight + cropDimensions.height; if (!options.webcamVideoElement) { utils.setCSSAttr(videoElement, { position: 'fixed', opacity: '0' }); document.body.appendChild(videoElement); } // Firefox doesn't seem to obey autoplay if the element is not in the DOM when the content // is loaded, so we must manually trigger play after adding it, or the video will be frozen videoElement.play(); screenShot.getGIF(options, function (obj) { if ((!images || !images.length) && (!video || !video.length)) { stopVideoStreaming(obj); } completeCallback(obj); }); } /* existingVideo.js ================ */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Dependencies function existingVideo() { var obj = arguments.length > 0 && arguments[0] !== undefined$1 ? arguments[0] : {}; var callback = obj.callback, existingVideo = obj.existingVideo, options = obj.options; var skipObj = { getUserMedia: true, 'window.URL': true }; var errorObj = error.validate(skipObj); var videoType = void 0; var videoSrc = void 0; if (errorObj.error) { return callback(errorObj); } if (utils.isElement(existingVideo) && existingVideo.src) { videoSrc = existingVideo.src; videoType = utils.getExtension(videoSrc); if (!utils.isSupported.videoCodecs[videoType]) { return callback(error.messages.videoCodecs); } } else if (utils.isArray(existingVideo)) { utils.each(existingVideo, function (iterator, videoSrc) { if (videoSrc instanceof Blob) { videoType = videoSrc.type.substr(videoSrc.type.lastIndexOf('/') + 1, videoSrc.length); } else { videoType = videoSrc.substr(videoSrc.lastIndexOf('.') + 1, videoSrc.length); } if (utils.isSupported.videoCodecs[videoType]) { existingVideo = videoSrc; return false; } }); } videoStream.startStreaming({ completed: function completed(obj) { obj.options = options || {}; createAndGetGIF(obj, callback); }, existingVideo: existingVideo, crossOrigin: options.crossOrigin, options: options }); } /* existingWebcam.js ================= */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Dependencies function existingWebcam() { var obj = arguments.length > 0 && arguments[0] !== undefined$1 ? arguments[0] : {}; var callback = obj.callback, lastCameraStream = obj.lastCameraStream, options = obj.options, webcamVideoElement = obj.webcamVideoElement; if (!isWebCamGIFSupported()) { return callback(error.validate()); } if (options.savedRenderingContexts.length) { screenShot.getGIF(options, function (obj) { callback(obj); }); return; } videoStream.startVideoStreaming(function () { var obj = arguments.length > 0 && arguments[0] !== undefined$1 ? arguments[0] : {}; obj.options = options || {}; createAndGetGIF(obj, callback); }, { lastCameraStream: lastCameraStream, callback: callback, webcamVideoElement: webcamVideoElement, crossOrigin: options.crossOrigin }); } /* createGIF.js ============ */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Dependencies function createGIF(userOptions, callback) { callback = utils.isFunction(userOptions) ? userOptions : callback; userOptions = utils.isObject(userOptions) ? userOptions : {}; if (!utils.isFunction(callback)) { return; } var options = utils.mergeOptions(defaultOptions, userOptions) || {}; var lastCameraStream = userOptions.cameraStream; var images = options.images; var imagesLength = images ? images.length : 0; var video = options.video; var webcamVideoElement = options.webcamVideoElement; options = utils.mergeOptions(options, { 'gifWidth': Math.floor(options.gifWidth), 'gifHeight': Math.floor(options.gifHeight) }); // If the user would like to create a GIF from an existing image(s) if (imagesLength) { existingImages({ 'images': images, 'imagesLength': imagesLength, 'callback': callback, 'options': options }); } else if (video) { // If the user would like to create a GIF from an existing HTML5 video existingVideo({ 'existingVideo': video, callback: callback, options: options }); } else { // If the user would like to create a GIF from a webcam stream existingWebcam({ lastCameraStream: lastCameraStream, callback: callback, webcamVideoElement: webcamVideoElement, options: options }); } } /* takeSnapShot.js =============== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ function takeSnapShot(userOptions, callback) { callback = utils.isFunction(userOptions) ? userOptions : callback; userOptions = utils.isObject(userOptions) ? userOptions : {}; if (!utils.isFunction(callback)) { return; } var mergedOptions = utils.mergeOptions(defaultOptions, userOptions); var options = utils.mergeOptions(mergedOptions, { 'interval': .1, 'numFrames': 1, 'gifWidth': Math.floor(mergedOptions.gifWidth), 'gifHeight': Math.floor(mergedOptions.gifHeight) }); createGIF(options, callback); } /* API.js ====== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Dependencies var API = { 'utils': utils$2, 'error': error$2, 'defaultOptions': defaultOptions$2, 'createGIF': createGIF, 'takeSnapShot': takeSnapShot, 'stopVideoStreaming': stopVideoStreaming, 'isSupported': isSupported, 'isWebCamGIFSupported': isWebCamGIFSupported, 'isExistingVideoGIFSupported': isExistingVideoGIFSupported, 'isExistingImagesGIFSupported': isSupported$1, 'VERSION': '0.4.5' }; /* index.js ======== */ /* Copyright 2017 Yahoo Inc. * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms. */ // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, and plain browser loading if (typeof undefined$1 === 'function' && undefined$1.amd) { undefined$1([], function () { return API; }); } else { module.exports = API; } }(typeof window !== "undefined" ? window : {}, typeof document !== "undefined" ? document : { createElement: function() {} }, typeof window !== "undefined" ? window.navigator : {})); }); var b64toBlob = createCommonjsModule(function (module) { (function(root, globalName, factory) { if (module.exports) { // Node: module.exports = factory(); // Use module export as simulated ES6 default export: module.exports.default = module.exports; } else { // Browser: window[globalName] = factory(); } }(commonjsGlobal, 'b64toBlob', function() { return function b64toBlob(b64Data, contentType, sliceSize) { contentType = contentType || ''; sliceSize = sliceSize || 512; var byteCharacters = atob(b64Data); var byteArrays = []; for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) { var slice = byteCharacters.slice(offset, offset + sliceSize); var byteNumbers = new Array(slice.length); for (var i = 0; i < slice.length; i++) { byteNumbers[i] = slice.charCodeAt(i); } var byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } var blob = new Blob(byteArrays, {type: contentType}); return blob; }; })); }); function artplayerPluginGif(art) { var _art$constructor$util = art.constructor.utils, errorHandle = _art$constructor$util.errorHandle, clamp = _art$constructor$util.clamp, downloadFile = _art$constructor$util.downloadFile; var i18n = art.i18n, notice = art.notice, layers = art.layers, player = art.player, loading = art.loading, _art$option = art.option, theme = _art$option.theme, title = _art$option.title, proxy = art.events.proxy, $video = art.template.$video; i18n.update({ 'zh-cn': { 'Long press, gif length is between 1 second and 10 seconds': '长按,gif 长度为 1 ~ 10 秒', 'Gif time is too short': 'Gif 时间太短', 'Start creating gif...': '开始创建 gif...', 'Create gif successfully': '创建 gif 成功', 'There is another gif in the processing': '正有另一个 gif 在创建中', 'Release the mouse to start': '放开鼠标即可开始' }, 'zh-tw': { 'Long press, gif length is between 1 second and 10 seconds': '長按,gif 長度為 1 ~ 10 秒', 'Gif time is too short': 'Gif 時間太短', 'Start creating gif...': '開始創建 gif...', 'Create gif successfully': '創建 gif 成功', 'There is another gif in the processing': '正有另一個 gif 在創建中', 'Release the mouse to start': '放開鼠標即可開始' } }); var layer = layers.add({ name: 'artplayer-plugin-gif-progress', style: { position: 'absolute', top: '0', left: '0', height: '3px', width: '0%', 'background-color': theme } }); var $progress = layer.$ref; var timeLimit = 10000; var isProcessing = false; var pressStartTime = 0; var progressTimer = null; var isPress = false; var offset = 0; art.on('destroy', function () { if (progressTimer) { clearTimeout(progressTimer); } }); function cleanTimer() { $progress.style.width = '0%'; clearTimeout(progressTimer); progressTimer = null; } function createGif() { cleanTimer(); var pressTime = new Date() - pressStartTime; if (isProcessing) { notice.show(i18n.get('There is another gif in the processing')); } else if (pressTime < 1000) { notice.show(i18n.get('Gif time is too short')); } else { var numFrames = Math.floor(clamp(pressTime, 1000, timeLimit) / 100); var videoWidth = $video.videoWidth, videoHeight = $video.videoHeight; art.plugins.artplayerPluginGif.create({ numFrames: numFrames, offset: Math.floor(offset), gifHeight: 200, gifWidth: videoWidth / videoHeight * 200 }, function (image) { downloadFile(image, "".concat(title || 'unnamed', ".gif")); }); } } return { name: 'artplayerPluginGif', attach: function attach($gif) { proxy($gif, 'mousedown', function () { isPress = true; cleanTimer(); offset = player.currentTime; pressStartTime = new Date(); notice.show(i18n.get('Long press, gif length is between 1 second and 10 seconds')); (function loop() { progressTimer = setTimeout(function () { var width = parseInt($progress.style.width, 10); if (width <= 100) { $progress.style.width = "".concat(width + 1, "%"); loop(); } else { notice.show(i18n.get('Release the mouse to start')); } }, timeLimit / 100); })(); }); proxy(document, 'mouseup', function () { if (isPress) { isPress = false; createGif(); offset = 0; } }); }, create: function create() { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var callback = arguments.length > 1 ? arguments[1] : undefined; isProcessing = true; loading.show = true; art.emit('artplayerPluginGif:start'); notice.show(i18n.get('Start creating gif...'), false); gifshot.createGIF(objectSpread({}, config, { video: [$video.src], crossOrigin: 'anonymous' }), function (obj) { if (obj.error) { notice.show(obj.errorMsg); errorHandle(false, obj.errorMsg); } else if (typeof callback === 'function') { var base64String = obj.image.split(',')[1]; var blob = b64toBlob(base64String, 'image/gif'); var blobUrl = URL.createObjectURL(blob); notice.show(i18n.get('Create gif successfully')); art.emit('artplayerPluginGif', blobUrl); callback(blobUrl); } isProcessing = false; loading.show = false; art.emit('artplayerPluginGif:end'); }); } }; } return artplayerPluginGif; })); //# sourceMappingURL=artplayer-plugin-gif.js.map