Files
ArtPlayer/docs/uncompiled/artplayer-plugin-gif.js
T
2019-06-12 15:24:34 +08:00

3052 lines
96 KiB
JavaScript

(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 <dean@gmail.com>, 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 = '<source src="' + existingVideo + '" type="video/' + utils.getExtension(existingVideo) + '" />';
} else if (existingVideo instanceof Blob) {
try {
videoElement.src = utils.URL.createObjectURL(existingVideo);
} catch (e) {}
videoElement.innerHTML = '<source src="' + existingVideo + '" type="' + existingVideo.type + '" />';
}
} 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