From 2f2e71c7d893286370328299c93d46473ae599c8 Mon Sep 17 00:00:00 2001 From: CBenni Date: Thu, 28 Dec 2017 13:19:33 +0000 Subject: [PATCH 1/4] Added browser-based gif decoder, encoder and renderer --- .gitignore | 2 + gifsupport/README.txt | 3 + gifsupport/gif.js | 3 + gifsupport/gif.worker.js | 3 + gifsupport/index.html | 20 +++ gifsupport/index.js | 315 +++++++++++++++++++++++++++++++++++++++ gifsupport/license.txt | 12 ++ 7 files changed, 358 insertions(+) create mode 100644 gifsupport/README.txt create mode 100644 gifsupport/gif.js create mode 100644 gifsupport/gif.worker.js create mode 100644 gifsupport/index.html create mode 100644 gifsupport/index.js create mode 100644 gifsupport/license.txt diff --git a/.gitignore b/.gitignore index f4e767e..f5df014 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ node_modules *.log run_forever ffmpeg.exe +gifsupport/js/ +gifsupport/*.map diff --git a/gifsupport/README.txt b/gifsupport/README.txt new file mode 100644 index 0000000..6aec385 --- /dev/null +++ b/gifsupport/README.txt @@ -0,0 +1,3 @@ +A Pen created at CodePen.io. You can find this one at https://codepen.io/CBenni/pen/YYZKeO. + + \ No newline at end of file diff --git a/gifsupport/gif.js b/gifsupport/gif.js new file mode 100644 index 0000000..2e4d204 --- /dev/null +++ b/gifsupport/gif.js @@ -0,0 +1,3 @@ +// gif.js 0.2.0 - https://github.com/jnordberg/gif.js +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.GIF=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0&&this._events[type].length>m){this._events[type].warned=true;console.error("(node) warning: possible EventEmitter memory "+"leak detected. %d listeners added. "+"Use emitter.setMaxListeners() to increase limit.",this._events[type].length);if(typeof console.trace==="function"){console.trace()}}}return this};EventEmitter.prototype.on=EventEmitter.prototype.addListener;EventEmitter.prototype.once=function(type,listener){if(!isFunction(listener))throw TypeError("listener must be a function");var fired=false;function g(){this.removeListener(type,g);if(!fired){fired=true;listener.apply(this,arguments)}}g.listener=listener;this.on(type,g);return this};EventEmitter.prototype.removeListener=function(type,listener){var list,position,length,i;if(!isFunction(listener))throw TypeError("listener must be a function");if(!this._events||!this._events[type])return this;list=this._events[type];length=list.length;position=-1;if(list===listener||isFunction(list.listener)&&list.listener===listener){delete this._events[type];if(this._events.removeListener)this.emit("removeListener",type,listener)}else if(isObject(list)){for(i=length;i-- >0;){if(list[i]===listener||list[i].listener&&list[i].listener===listener){position=i;break}}if(position<0)return this;if(list.length===1){list.length=0;delete this._events[type]}else{list.splice(position,1)}if(this._events.removeListener)this.emit("removeListener",type,listener)}return this};EventEmitter.prototype.removeAllListeners=function(type){var key,listeners;if(!this._events)return this;if(!this._events.removeListener){if(arguments.length===0)this._events={};else if(this._events[type])delete this._events[type];return this}if(arguments.length===0){for(key in this._events){if(key==="removeListener")continue;this.removeAllListeners(key)}this.removeAllListeners("removeListener");this._events={};return this}listeners=this._events[type];if(isFunction(listeners)){this.removeListener(type,listeners)}else if(listeners){while(listeners.length)this.removeListener(type,listeners[listeners.length-1])}delete this._events[type];return this};EventEmitter.prototype.listeners=function(type){var ret;if(!this._events||!this._events[type])ret=[];else if(isFunction(this._events[type]))ret=[this._events[type]];else ret=this._events[type].slice();return ret};EventEmitter.prototype.listenerCount=function(type){if(this._events){var evlistener=this._events[type];if(isFunction(evlistener))return 1;else if(evlistener)return evlistener.length}return 0};EventEmitter.listenerCount=function(emitter,type){return emitter.listenerCount(type)};function isFunction(arg){return typeof arg==="function"}function isNumber(arg){return typeof arg==="number"}function isObject(arg){return typeof arg==="object"&&arg!==null}function isUndefined(arg){return arg===void 0}},{}],2:[function(require,module,exports){var UA,browser,mode,platform,ua;ua=navigator.userAgent.toLowerCase();platform=navigator.platform.toLowerCase();UA=ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,"unknown",0];mode=UA[1]==="ie"&&document.documentMode;browser={name:UA[1]==="version"?UA[3]:UA[1],version:mode||parseFloat(UA[1]==="opera"&&UA[4]?UA[4]:UA[2]),platform:{name:ua.match(/ip(?:ad|od|hone)/)?"ios":(ua.match(/(?:webos|android)/)||platform.match(/mac|win|linux/)||["other"])[0]}};browser[browser.name]=true;browser[browser.name+parseInt(browser.version,10)]=true;browser.platform[browser.platform.name]=true;module.exports=browser},{}],3:[function(require,module,exports){var EventEmitter,GIF,browser,extend=function(child,parent){for(var key in parent){if(hasProp.call(parent,key))child[key]=parent[key]}function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child},hasProp={}.hasOwnProperty,indexOf=[].indexOf||function(item){for(var i=0,l=this.length;iref;i=0<=ref?++j:--j){results.push(null)}return results}.call(this);numWorkers=this.spawnWorkers();if(this.options.globalPalette===true){this.renderNextFrame()}else{for(i=j=0,ref=numWorkers;0<=ref?jref;i=0<=ref?++j:--j){this.renderNextFrame()}}this.emit("start");return this.emit("progress",0)};GIF.prototype.abort=function(){var worker;while(true){worker=this.activeWorkers.shift();if(worker==null){break}this.log("killing active worker");worker.terminate()}this.running=false;return this.emit("abort")};GIF.prototype.spawnWorkers=function(){var j,numWorkers,ref,results;numWorkers=Math.min(this.options.workers,this.frames.length);(function(){results=[];for(var j=ref=this.freeWorkers.length;ref<=numWorkers?jnumWorkers;ref<=numWorkers?j++:j--){results.push(j)}return results}).apply(this).forEach(function(_this){return function(i){var worker;_this.log("spawning worker "+i);worker=new Worker(_this.options.workerScript);worker.onmessage=function(event){_this.activeWorkers.splice(_this.activeWorkers.indexOf(worker),1);_this.freeWorkers.push(worker);return _this.frameFinished(event.data)};return _this.freeWorkers.push(worker)}}(this));return numWorkers};GIF.prototype.frameFinished=function(frame){var i,j,ref;this.log("frame "+frame.index+" finished - "+this.activeWorkers.length+" active");this.finishedFrames++;this.emit("progress",this.finishedFrames/this.frames.length);this.imageParts[frame.index]=frame;if(this.options.globalPalette===true){this.options.globalPalette=frame.globalPalette;this.log("global palette analyzed");if(this.frames.length>2){for(i=j=1,ref=this.freeWorkers.length;1<=ref?jref;i=1<=ref?++j:--j){this.renderNextFrame()}}}if(indexOf.call(this.imageParts,null)>=0){return this.renderNextFrame()}else{return this.finishRendering()}};GIF.prototype.finishRendering=function(){var data,frame,i,image,j,k,l,len,len1,len2,len3,offset,page,ref,ref1,ref2;len=0;ref=this.imageParts;for(j=0,len1=ref.length;j=this.frames.length){return}frame=this.frames[this.nextFrame++];worker=this.freeWorkers.shift();task=this.getTask(frame);this.log("starting frame "+(task.index+1)+" of "+this.frames.length);this.activeWorkers.push(worker);return worker.postMessage(task)};GIF.prototype.getContextData=function(ctx){return ctx.getImageData(0,0,this.options.width,this.options.height).data};GIF.prototype.getImageData=function(image){var ctx;if(this._canvas==null){this._canvas=document.createElement("canvas");this._canvas.width=this.options.width;this._canvas.height=this.options.height}ctx=this._canvas.getContext("2d");ctx.setFill=this.options.background;ctx.fillRect(0,0,this.options.width,this.options.height);ctx.drawImage(image,0,0);return this.getContextData(ctx)};GIF.prototype.getTask=function(frame){var index,task;index=this.frames.indexOf(frame);task={index:index,last:index===this.frames.length-1,delay:frame.delay,transparent:frame.transparent,width:this.options.width,height:this.options.height,quality:this.options.quality,dither:this.options.dither,globalPalette:this.options.globalPalette,repeat:this.options.repeat,canTransfer:browser.name==="chrome"};if(frame.data!=null){task.data=frame.data}else if(frame.context!=null){task.data=this.getContextData(frame.context)}else if(frame.image!=null){task.data=this.getImageData(frame.image)}else{throw new Error("Invalid frame")}return task};GIF.prototype.log=function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];if(!this.options.debug){return}return console.log.apply(console,args)};return GIF}(EventEmitter);module.exports=GIF},{"./browser.coffee":2,events:1}]},{},[3])(3)}); +//# sourceMappingURL=gif.js.map diff --git a/gifsupport/gif.worker.js b/gifsupport/gif.worker.js new file mode 100644 index 0000000..269624e --- /dev/null +++ b/gifsupport/gif.worker.js @@ -0,0 +1,3 @@ +// gif.worker.js 0.2.0 - https://github.com/jnordberg/gif.js +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o=ByteArray.pageSize)this.newPage();this.pages[this.page][this.cursor++]=val};ByteArray.prototype.writeUTFBytes=function(string){for(var l=string.length,i=0;i=0)this.dispose=disposalCode};GIFEncoder.prototype.setRepeat=function(repeat){this.repeat=repeat};GIFEncoder.prototype.setTransparent=function(color){this.transparent=color};GIFEncoder.prototype.addFrame=function(imageData){this.image=imageData;this.colorTab=this.globalPalette&&this.globalPalette.slice?this.globalPalette:null;this.getImagePixels();this.analyzePixels();if(this.globalPalette===true)this.globalPalette=this.colorTab;if(this.firstFrame){this.writeLSD();this.writePalette();if(this.repeat>=0){this.writeNetscapeExt()}}this.writeGraphicCtrlExt();this.writeImageDesc();if(!this.firstFrame&&!this.globalPalette)this.writePalette();this.writePixels();this.firstFrame=false};GIFEncoder.prototype.finish=function(){this.out.writeByte(59)};GIFEncoder.prototype.setQuality=function(quality){if(quality<1)quality=1;this.sample=quality};GIFEncoder.prototype.setDither=function(dither){if(dither===true)dither="FloydSteinberg";this.dither=dither};GIFEncoder.prototype.setGlobalPalette=function(palette){this.globalPalette=palette};GIFEncoder.prototype.getGlobalPalette=function(){return this.globalPalette&&this.globalPalette.slice&&this.globalPalette.slice(0)||this.globalPalette};GIFEncoder.prototype.writeHeader=function(){this.out.writeUTFBytes("GIF89a")};GIFEncoder.prototype.analyzePixels=function(){if(!this.colorTab){this.neuQuant=new NeuQuant(this.pixels,this.sample);this.neuQuant.buildColormap();this.colorTab=this.neuQuant.getColormap()}if(this.dither){this.ditherPixels(this.dither.replace("-serpentine",""),this.dither.match(/-serpentine/)!==null)}else{this.indexPixels()}this.pixels=null;this.colorDepth=8;this.palSize=7;if(this.transparent!==null){this.transIndex=this.findClosest(this.transparent,true)}};GIFEncoder.prototype.indexPixels=function(imgq){var nPix=this.pixels.length/3;this.indexedPixels=new Uint8Array(nPix);var k=0;for(var j=0;j=0&&x1+x=0&&y1+y>16,(c&65280)>>8,c&255,used)};GIFEncoder.prototype.findClosestRGB=function(r,g,b,used){if(this.colorTab===null)return-1;if(this.neuQuant&&!used){return this.neuQuant.lookupRGB(r,g,b)}var c=b|g<<8|r<<16;var minpos=0;var dmin=256*256*256;var len=this.colorTab.length;for(var i=0,index=0;i=0){disp=dispose&7}disp<<=2;this.out.writeByte(0|disp|0|transp);this.writeShort(this.delay);this.out.writeByte(this.transIndex);this.out.writeByte(0)};GIFEncoder.prototype.writeImageDesc=function(){this.out.writeByte(44);this.writeShort(0);this.writeShort(0);this.writeShort(this.width);this.writeShort(this.height);if(this.firstFrame||this.globalPalette){this.out.writeByte(0)}else{this.out.writeByte(128|0|0|0|this.palSize)}};GIFEncoder.prototype.writeLSD=function(){this.writeShort(this.width);this.writeShort(this.height);this.out.writeByte(128|112|0|this.palSize);this.out.writeByte(0);this.out.writeByte(0)};GIFEncoder.prototype.writeNetscapeExt=function(){this.out.writeByte(33);this.out.writeByte(255);this.out.writeByte(11);this.out.writeUTFBytes("NETSCAPE2.0");this.out.writeByte(3);this.out.writeByte(1);this.writeShort(this.repeat);this.out.writeByte(0)};GIFEncoder.prototype.writePalette=function(){this.out.writeBytes(this.colorTab);var n=3*256-this.colorTab.length;for(var i=0;i>8&255)};GIFEncoder.prototype.writePixels=function(){var enc=new LZWEncoder(this.width,this.height,this.indexedPixels,this.colorDepth);enc.encode(this.out)};GIFEncoder.prototype.stream=function(){return this.out};module.exports=GIFEncoder},{"./LZWEncoder.js":2,"./TypedNeuQuant.js":3}],2:[function(require,module,exports){var EOF=-1;var BITS=12;var HSIZE=5003;var masks=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535];function LZWEncoder(width,height,pixels,colorDepth){var initCodeSize=Math.max(2,colorDepth);var accum=new Uint8Array(256);var htab=new Int32Array(HSIZE);var codetab=new Int32Array(HSIZE);var cur_accum,cur_bits=0;var a_count;var free_ent=0;var maxcode;var clear_flg=false;var g_init_bits,ClearCode,EOFCode;function char_out(c,outs){accum[a_count++]=c;if(a_count>=254)flush_char(outs)}function cl_block(outs){cl_hash(HSIZE);free_ent=ClearCode+2;clear_flg=true;output(ClearCode,outs)}function cl_hash(hsize){for(var i=0;i=0){disp=hsize_reg-i;if(i===0)disp=1;do{if((i-=disp)<0)i+=hsize_reg;if(htab[i]===fcode){ent=codetab[i];continue outer_loop}}while(htab[i]>=0)}output(ent,outs);ent=c;if(free_ent<1<0){outs.writeByte(a_count);outs.writeBytes(accum,0,a_count);a_count=0}}function MAXCODE(n_bits){return(1<0)cur_accum|=code<=8){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}if(free_ent>maxcode||clear_flg){if(clear_flg){maxcode=MAXCODE(n_bits=g_init_bits);clear_flg=false}else{++n_bits;if(n_bits==BITS)maxcode=1<0){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}flush_char(outs)}}this.encode=encode}module.exports=LZWEncoder},{}],3:[function(require,module,exports){var ncycles=100;var netsize=256;var maxnetpos=netsize-1;var netbiasshift=4;var intbiasshift=16;var intbias=1<>betashift;var betagamma=intbias<>3;var radiusbiasshift=6;var radiusbias=1<>3);var i,v;for(i=0;i>=netbiasshift;network[i][1]>>=netbiasshift;network[i][2]>>=netbiasshift;network[i][3]=i}}function altersingle(alpha,i,b,g,r){network[i][0]-=alpha*(network[i][0]-b)/initalpha;network[i][1]-=alpha*(network[i][1]-g)/initalpha;network[i][2]-=alpha*(network[i][2]-r)/initalpha}function alterneigh(radius,i,b,g,r){var lo=Math.abs(i-radius);var hi=Math.min(i+radius,netsize);var j=i+1;var k=i-1;var m=1;var p,a;while(jlo){a=radpower[m++];if(jlo){p=network[k--];p[0]-=a*(p[0]-b)/alpharadbias;p[1]-=a*(p[1]-g)/alpharadbias;p[2]-=a*(p[2]-r)/alpharadbias}}}function contest(b,g,r){var bestd=~(1<<31);var bestbiasd=bestd;var bestpos=-1;var bestbiaspos=bestpos;var i,n,dist,biasdist,betafreq;for(i=0;i>intbiasshift-netbiasshift);if(biasdist>betashift;freq[i]-=betafreq;bias[i]+=betafreq<>1;for(j=previouscol+1;j>1;for(j=previouscol+1;j<256;j++)netindex[j]=maxnetpos}function inxsearch(b,g,r){var a,p,dist;var bestd=1e3;var best=-1;var i=netindex[g];var j=i-1;while(i=0){if(i=bestd)i=netsize;else{i++;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist=0){p=network[j];dist=g-p[1];if(dist>=bestd)j=-1;else{j--;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist>radiusbiasshift;if(rad<=1)rad=0;for(i=0;i=lengthcount)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 + + + + + A Pen by CBenni + + + + + + + + + + + + + + diff --git a/gifsupport/index.js b/gifsupport/index.js new file mode 100644 index 0000000..6029623 --- /dev/null +++ b/gifsupport/index.js @@ -0,0 +1,315 @@ +function loadFromUrl(url) { + return new Promise(function (resolve, reject) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url); + xhr.responseType = "arraybuffer"; + xhr.onload = function () { + if (this.status >= 200 && this.status < 300) { + resolve({ + type: xhr.getResponseHeader("Content-Type"), + data: xhr.response + }); + } else { + reject({ + status: this.status, + statusText: xhr.statusText + }); + } + }; + xhr.onerror = function () { + reject({ + status: this.status, + statusText: xhr.statusText + }); + }; + xhr.send(); + }); +} + +function _arrayBufferToBase64( buffer ) { + var binary = ''; + var bytes = new Uint8Array( buffer ); + var len = bytes.byteLength; + for (var i = 0; i < len; i++) { + binary += String.fromCharCode( bytes[ i ] ); + } + return window.btoa( binary ); +} + +function createCanvas(width, height) { + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + return canvas; +} + +class ImageEx { + constructor(url) { + this.url = url; + this.loaded = loadFromUrl(url).then(result => { + this.type = result.type; + this.data = result.data; + if(this.type === "image/gif") { + return this.initGif(); + } else { + return this.initStatic(); + } + }) + } + + + initGif() { + const reader = new GifReader(new Uint8Array(this.data)); + this.width = reader.width; + this.height = reader.height; + this.frames = this.decodeFrames(reader); + + this.renderAllFrames(); + } + + initStatic(){ + // todo: in node, we wanna use this.data + const img = new Image(); + var arrayBufferView = new Uint8Array( this.data ); + var blob = new Blob( [ arrayBufferView ], { type: this.type } ); + var urlCreator = window.URL || window.webkitURL; + var imageUrl = urlCreator.createObjectURL( blob ); + img.src = imageUrl; + return new Promise(resolve => { + img.onload = () => { + this.width = img.width; + this.height = img.height; + this.frames = [{ + actualOffset: 0, + actualDelay: Infinity, + delay: Infinity + }] + this.spriteSheet = createCanvas(this.width, this.height); + const spriteSheetCtx = this.spriteSheet.getContext("2d"); + spriteSheetCtx.drawImage(img, 0, 0); + resolve(); + } + }) + } + + decodeFrames(reader) { + const frames = []; + let offset = 0; + for(let i=0;i { + return ctx.clearRect(0,0,canvas.width,canvas.height); + } + break; + case 3: + saved = ctx.getImageData(0,0,canvas.width,canvas.height); + disposeFrame = () => { + return ctx.putImageData(saved, 0, 0); + } + break; + default: + this.disposeFrame = null; + } + + // draw current frame + ctx.drawImage(frame.buffer, frame.x, frame.y); + // draw the frame onto the sprite sheet + spriteSheetCtx.drawImage(canvas, this.width * i, 0); + } + } + + createBufferCanvas(frame, width, height) { + const canvas = createCanvas(frame.width,frame.height); + const ctx = canvas.getContext("2d"); + + const imageData = ctx.createImageData(width, height); + imageData.data.set(frame.pixels); + + ctx.putImageData(imageData, - frame.x, -frame.y); + return canvas; + } + + drawFrame(ctx, frameNum, x, y, args = {}) { + console.log("this", this) + const sx = frameNum * this.width + (args.sx || 0); + const sy = args.sy || 0; + const swidth = Math.min(args.swidth || this.width, this.width) - (args.sx || 0); + const sheight = args.sheight || this.height; + + console.log(`Drawing frame ${frameNum} at`) + console.log("sx",sx) + console.log("sy",sy) + console.log("sw",swidth) + console.log("sh",sheight) + console.log("x",x) + console.log("y",y) + console.log("w",args.width) + console.log("h",args.height) + + ctx.drawImage(this.spriteSheet, sx, sy, swidth, sheight, x, y, args.width || swidth, args.height || sheight); + //ctx.drawImage(this.spriteSheet, 0, 0, 112, 112, 0, 0, 112, 112) + } + +} +class CanvasEx { + constructor(width, height) { + this.width = width; + this.height = height; + this.frames = []; + this.totalDuration = Infinity; + } + + addFrame(actualDelay, delay) { + if((actualDelay === undefined || actualDelay === null) + && (delay === undefined || delay === null)) throw new Error("Delay has to be set!"); + const canvas = createCanvas(this.width, this.height); + const frame = { + actualOffset: this.totalDuration, + delay: delay ? delay : Math.max(Math.round(actualDelay/10), 2), + actualDelay: actualDelay ? actualDelay : Math.max(delay * 10, 20), + canvas, + ctx: canvas.getContext("2d") + } + this.totalDuration += delay; + this.frames.push(frame); + } + + drawImage(img,x,y,args = {}) { + let {sx,sy,swidth,sheight,width,height,beginAt = 0,endAt = Infinity} = args; + console.log("Drawing image ",img) + console.log("At ",x,y,args) + beginAt = Math.max(0, beginAt); + endAt = Math.min(Math.max(img.totalDuration || 0, this.totalDuration), endAt); + if(img.frames && img.frames.length > 1) { + if(this.frames.length > 1) throw new Error("Cannot render animations onto animated canvases!"); + this.totalDuration = img.totalDuration; + // we are drawing an animated image onto a static one. + // for each frame in the image, create a frame on this one, cloning the original picture (if any), + // render the original on each frame, and draw the frame on top. + for(let i=this.frames.length;i 0) { + this.frames[i].ctx.drawImage(this.frames[0].canvas, 0, 0); + } + } + for(let i=0;i{ + _cnv.drawImage(img, 0, 0); + //img.drawFrame(_ctx, 0, 0, 0) + return img2.loaded +}).then(()=>{ + _cnv.drawImage(img2, 0, 0, {width: 128, height: 128}); +}).then(()=>{ + console.log("Drawing CanvasEx to screen", _cnv) + _cnv.drawFrame(_ctx, 0, 0, 0); + + console.log("Starting gif render") + const GifEncoder = new GIF({ + workers: 2, + quality: 10, + transparent: 'rgba(0,0,0,0)' + }); + + for(let i=0;i<_cnv.frames.length;++i) { + const frame = _cnv.frames[i]; + console.log("Rendering frame "+i) + GifEncoder.addFrame(frame.canvas, {delay: frame.delay}) + } + + GifEncoder.on('finished', function(blob) { + console.log("Done rendering!", URL.createObjectURL(blob)) + document.getElementById("i").src = URL.createObjectURL(blob); + }); + + GifEncoder.render(); +}) + +var curFrm = 0; +setInterval(()=>{ + _ctx.clearRect(0,0,200,200); + _cnv.drawFrame(_ctx, curFrm++ % _cnv.frames.length, 0, 0); +}, 100) + + + +/*img2.loaded.then(() => { + _ctx.drawImage(img2.spriteSheet, 0, 100) +}) + +img.loaded.then(()=>_ctx.drawImage(img.spriteSheet,0,0))*/ \ No newline at end of file diff --git a/gifsupport/license.txt b/gifsupport/license.txt new file mode 100644 index 0000000..be92a99 --- /dev/null +++ b/gifsupport/license.txt @@ -0,0 +1,12 @@ + + + From 3ad870d990c1a0e12379cf72af828b752774b431 Mon Sep 17 00:00:00 2001 From: CBenni Date: Sun, 31 Dec 2017 01:54:52 +0100 Subject: [PATCH 2/4] Merged test stuff into beebot --- .editorconfig | 24 + .eslintrc.json | 49 ++ gifsupport-node/README.txt | 3 + gifsupport-node/gif.js | 3 + gifsupport-node/gif.worker.js | 3 + gifsupport-node/imageex.js | 303 +++++++++ gifsupport-node/index.html | 20 + gifsupport-node/index.js | 286 ++++++++ gifsupport-node/license.txt | 12 + gifsupport/index.js | 264 ++++---- imageex.js | 291 ++++++++ index.js | 410 ++++++------ package.json | 8 + yarn.lock | 1183 ++++++++++++++++++++++++++++++++- 14 files changed, 2491 insertions(+), 368 deletions(-) create mode 100644 .editorconfig create mode 100644 .eslintrc.json create mode 100644 gifsupport-node/README.txt create mode 100644 gifsupport-node/gif.js create mode 100644 gifsupport-node/gif.worker.js create mode 100644 gifsupport-node/imageex.js create mode 100644 gifsupport-node/index.html create mode 100644 gifsupport-node/index.js create mode 100644 gifsupport-node/license.txt create mode 100644 imageex.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..da7246c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 2 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..4105463 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,49 @@ +{ + "extends": "airbnb-base", + "env": { + "node": true, + "es6": true + }, + "root": true, + "rules": { + "object-curly-spacing": [ + 2, + "always" + ], + "comma-dangle": [ + 2, + "never" + ], + "no-param-reassign": "off", + "no-plusplus": "off", + "no-bitwise": "off", + "no-continue": "off", + "no-unused-expressions": "off", + "arrow-parens": [ + 2, + "as-needed" + ], + "require-jsdoc": "off", + "no-underscore-dangle": "off", + "no-invalid-this": "off", + "no-mixed-operators": "off", + "newline-per-chained-call": "off", + "class-methods-use-this": "off", + "max-len": [ + 1, + 150 + ], + "linebreak-style": [ + 1, + "unix" + ], + "indent": [ + 2, + 2, + { + "SwitchCase": 1, + "MemberExpression": 0 + } + ] + } +} \ No newline at end of file diff --git a/gifsupport-node/README.txt b/gifsupport-node/README.txt new file mode 100644 index 0000000..6aec385 --- /dev/null +++ b/gifsupport-node/README.txt @@ -0,0 +1,3 @@ +A Pen created at CodePen.io. You can find this one at https://codepen.io/CBenni/pen/YYZKeO. + + \ No newline at end of file diff --git a/gifsupport-node/gif.js b/gifsupport-node/gif.js new file mode 100644 index 0000000..2e4d204 --- /dev/null +++ b/gifsupport-node/gif.js @@ -0,0 +1,3 @@ +// gif.js 0.2.0 - https://github.com/jnordberg/gif.js +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.GIF=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0&&this._events[type].length>m){this._events[type].warned=true;console.error("(node) warning: possible EventEmitter memory "+"leak detected. %d listeners added. "+"Use emitter.setMaxListeners() to increase limit.",this._events[type].length);if(typeof console.trace==="function"){console.trace()}}}return this};EventEmitter.prototype.on=EventEmitter.prototype.addListener;EventEmitter.prototype.once=function(type,listener){if(!isFunction(listener))throw TypeError("listener must be a function");var fired=false;function g(){this.removeListener(type,g);if(!fired){fired=true;listener.apply(this,arguments)}}g.listener=listener;this.on(type,g);return this};EventEmitter.prototype.removeListener=function(type,listener){var list,position,length,i;if(!isFunction(listener))throw TypeError("listener must be a function");if(!this._events||!this._events[type])return this;list=this._events[type];length=list.length;position=-1;if(list===listener||isFunction(list.listener)&&list.listener===listener){delete this._events[type];if(this._events.removeListener)this.emit("removeListener",type,listener)}else if(isObject(list)){for(i=length;i-- >0;){if(list[i]===listener||list[i].listener&&list[i].listener===listener){position=i;break}}if(position<0)return this;if(list.length===1){list.length=0;delete this._events[type]}else{list.splice(position,1)}if(this._events.removeListener)this.emit("removeListener",type,listener)}return this};EventEmitter.prototype.removeAllListeners=function(type){var key,listeners;if(!this._events)return this;if(!this._events.removeListener){if(arguments.length===0)this._events={};else if(this._events[type])delete this._events[type];return this}if(arguments.length===0){for(key in this._events){if(key==="removeListener")continue;this.removeAllListeners(key)}this.removeAllListeners("removeListener");this._events={};return this}listeners=this._events[type];if(isFunction(listeners)){this.removeListener(type,listeners)}else if(listeners){while(listeners.length)this.removeListener(type,listeners[listeners.length-1])}delete this._events[type];return this};EventEmitter.prototype.listeners=function(type){var ret;if(!this._events||!this._events[type])ret=[];else if(isFunction(this._events[type]))ret=[this._events[type]];else ret=this._events[type].slice();return ret};EventEmitter.prototype.listenerCount=function(type){if(this._events){var evlistener=this._events[type];if(isFunction(evlistener))return 1;else if(evlistener)return evlistener.length}return 0};EventEmitter.listenerCount=function(emitter,type){return emitter.listenerCount(type)};function isFunction(arg){return typeof arg==="function"}function isNumber(arg){return typeof arg==="number"}function isObject(arg){return typeof arg==="object"&&arg!==null}function isUndefined(arg){return arg===void 0}},{}],2:[function(require,module,exports){var UA,browser,mode,platform,ua;ua=navigator.userAgent.toLowerCase();platform=navigator.platform.toLowerCase();UA=ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,"unknown",0];mode=UA[1]==="ie"&&document.documentMode;browser={name:UA[1]==="version"?UA[3]:UA[1],version:mode||parseFloat(UA[1]==="opera"&&UA[4]?UA[4]:UA[2]),platform:{name:ua.match(/ip(?:ad|od|hone)/)?"ios":(ua.match(/(?:webos|android)/)||platform.match(/mac|win|linux/)||["other"])[0]}};browser[browser.name]=true;browser[browser.name+parseInt(browser.version,10)]=true;browser.platform[browser.platform.name]=true;module.exports=browser},{}],3:[function(require,module,exports){var EventEmitter,GIF,browser,extend=function(child,parent){for(var key in parent){if(hasProp.call(parent,key))child[key]=parent[key]}function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child},hasProp={}.hasOwnProperty,indexOf=[].indexOf||function(item){for(var i=0,l=this.length;iref;i=0<=ref?++j:--j){results.push(null)}return results}.call(this);numWorkers=this.spawnWorkers();if(this.options.globalPalette===true){this.renderNextFrame()}else{for(i=j=0,ref=numWorkers;0<=ref?jref;i=0<=ref?++j:--j){this.renderNextFrame()}}this.emit("start");return this.emit("progress",0)};GIF.prototype.abort=function(){var worker;while(true){worker=this.activeWorkers.shift();if(worker==null){break}this.log("killing active worker");worker.terminate()}this.running=false;return this.emit("abort")};GIF.prototype.spawnWorkers=function(){var j,numWorkers,ref,results;numWorkers=Math.min(this.options.workers,this.frames.length);(function(){results=[];for(var j=ref=this.freeWorkers.length;ref<=numWorkers?jnumWorkers;ref<=numWorkers?j++:j--){results.push(j)}return results}).apply(this).forEach(function(_this){return function(i){var worker;_this.log("spawning worker "+i);worker=new Worker(_this.options.workerScript);worker.onmessage=function(event){_this.activeWorkers.splice(_this.activeWorkers.indexOf(worker),1);_this.freeWorkers.push(worker);return _this.frameFinished(event.data)};return _this.freeWorkers.push(worker)}}(this));return numWorkers};GIF.prototype.frameFinished=function(frame){var i,j,ref;this.log("frame "+frame.index+" finished - "+this.activeWorkers.length+" active");this.finishedFrames++;this.emit("progress",this.finishedFrames/this.frames.length);this.imageParts[frame.index]=frame;if(this.options.globalPalette===true){this.options.globalPalette=frame.globalPalette;this.log("global palette analyzed");if(this.frames.length>2){for(i=j=1,ref=this.freeWorkers.length;1<=ref?jref;i=1<=ref?++j:--j){this.renderNextFrame()}}}if(indexOf.call(this.imageParts,null)>=0){return this.renderNextFrame()}else{return this.finishRendering()}};GIF.prototype.finishRendering=function(){var data,frame,i,image,j,k,l,len,len1,len2,len3,offset,page,ref,ref1,ref2;len=0;ref=this.imageParts;for(j=0,len1=ref.length;j=this.frames.length){return}frame=this.frames[this.nextFrame++];worker=this.freeWorkers.shift();task=this.getTask(frame);this.log("starting frame "+(task.index+1)+" of "+this.frames.length);this.activeWorkers.push(worker);return worker.postMessage(task)};GIF.prototype.getContextData=function(ctx){return ctx.getImageData(0,0,this.options.width,this.options.height).data};GIF.prototype.getImageData=function(image){var ctx;if(this._canvas==null){this._canvas=document.createElement("canvas");this._canvas.width=this.options.width;this._canvas.height=this.options.height}ctx=this._canvas.getContext("2d");ctx.setFill=this.options.background;ctx.fillRect(0,0,this.options.width,this.options.height);ctx.drawImage(image,0,0);return this.getContextData(ctx)};GIF.prototype.getTask=function(frame){var index,task;index=this.frames.indexOf(frame);task={index:index,last:index===this.frames.length-1,delay:frame.delay,transparent:frame.transparent,width:this.options.width,height:this.options.height,quality:this.options.quality,dither:this.options.dither,globalPalette:this.options.globalPalette,repeat:this.options.repeat,canTransfer:browser.name==="chrome"};if(frame.data!=null){task.data=frame.data}else if(frame.context!=null){task.data=this.getContextData(frame.context)}else if(frame.image!=null){task.data=this.getImageData(frame.image)}else{throw new Error("Invalid frame")}return task};GIF.prototype.log=function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];if(!this.options.debug){return}return console.log.apply(console,args)};return GIF}(EventEmitter);module.exports=GIF},{"./browser.coffee":2,events:1}]},{},[3])(3)}); +//# sourceMappingURL=gif.js.map diff --git a/gifsupport-node/gif.worker.js b/gifsupport-node/gif.worker.js new file mode 100644 index 0000000..269624e --- /dev/null +++ b/gifsupport-node/gif.worker.js @@ -0,0 +1,3 @@ +// gif.worker.js 0.2.0 - https://github.com/jnordberg/gif.js +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o=ByteArray.pageSize)this.newPage();this.pages[this.page][this.cursor++]=val};ByteArray.prototype.writeUTFBytes=function(string){for(var l=string.length,i=0;i=0)this.dispose=disposalCode};GIFEncoder.prototype.setRepeat=function(repeat){this.repeat=repeat};GIFEncoder.prototype.setTransparent=function(color){this.transparent=color};GIFEncoder.prototype.addFrame=function(imageData){this.image=imageData;this.colorTab=this.globalPalette&&this.globalPalette.slice?this.globalPalette:null;this.getImagePixels();this.analyzePixels();if(this.globalPalette===true)this.globalPalette=this.colorTab;if(this.firstFrame){this.writeLSD();this.writePalette();if(this.repeat>=0){this.writeNetscapeExt()}}this.writeGraphicCtrlExt();this.writeImageDesc();if(!this.firstFrame&&!this.globalPalette)this.writePalette();this.writePixels();this.firstFrame=false};GIFEncoder.prototype.finish=function(){this.out.writeByte(59)};GIFEncoder.prototype.setQuality=function(quality){if(quality<1)quality=1;this.sample=quality};GIFEncoder.prototype.setDither=function(dither){if(dither===true)dither="FloydSteinberg";this.dither=dither};GIFEncoder.prototype.setGlobalPalette=function(palette){this.globalPalette=palette};GIFEncoder.prototype.getGlobalPalette=function(){return this.globalPalette&&this.globalPalette.slice&&this.globalPalette.slice(0)||this.globalPalette};GIFEncoder.prototype.writeHeader=function(){this.out.writeUTFBytes("GIF89a")};GIFEncoder.prototype.analyzePixels=function(){if(!this.colorTab){this.neuQuant=new NeuQuant(this.pixels,this.sample);this.neuQuant.buildColormap();this.colorTab=this.neuQuant.getColormap()}if(this.dither){this.ditherPixels(this.dither.replace("-serpentine",""),this.dither.match(/-serpentine/)!==null)}else{this.indexPixels()}this.pixels=null;this.colorDepth=8;this.palSize=7;if(this.transparent!==null){this.transIndex=this.findClosest(this.transparent,true)}};GIFEncoder.prototype.indexPixels=function(imgq){var nPix=this.pixels.length/3;this.indexedPixels=new Uint8Array(nPix);var k=0;for(var j=0;j=0&&x1+x=0&&y1+y>16,(c&65280)>>8,c&255,used)};GIFEncoder.prototype.findClosestRGB=function(r,g,b,used){if(this.colorTab===null)return-1;if(this.neuQuant&&!used){return this.neuQuant.lookupRGB(r,g,b)}var c=b|g<<8|r<<16;var minpos=0;var dmin=256*256*256;var len=this.colorTab.length;for(var i=0,index=0;i=0){disp=dispose&7}disp<<=2;this.out.writeByte(0|disp|0|transp);this.writeShort(this.delay);this.out.writeByte(this.transIndex);this.out.writeByte(0)};GIFEncoder.prototype.writeImageDesc=function(){this.out.writeByte(44);this.writeShort(0);this.writeShort(0);this.writeShort(this.width);this.writeShort(this.height);if(this.firstFrame||this.globalPalette){this.out.writeByte(0)}else{this.out.writeByte(128|0|0|0|this.palSize)}};GIFEncoder.prototype.writeLSD=function(){this.writeShort(this.width);this.writeShort(this.height);this.out.writeByte(128|112|0|this.palSize);this.out.writeByte(0);this.out.writeByte(0)};GIFEncoder.prototype.writeNetscapeExt=function(){this.out.writeByte(33);this.out.writeByte(255);this.out.writeByte(11);this.out.writeUTFBytes("NETSCAPE2.0");this.out.writeByte(3);this.out.writeByte(1);this.writeShort(this.repeat);this.out.writeByte(0)};GIFEncoder.prototype.writePalette=function(){this.out.writeBytes(this.colorTab);var n=3*256-this.colorTab.length;for(var i=0;i>8&255)};GIFEncoder.prototype.writePixels=function(){var enc=new LZWEncoder(this.width,this.height,this.indexedPixels,this.colorDepth);enc.encode(this.out)};GIFEncoder.prototype.stream=function(){return this.out};module.exports=GIFEncoder},{"./LZWEncoder.js":2,"./TypedNeuQuant.js":3}],2:[function(require,module,exports){var EOF=-1;var BITS=12;var HSIZE=5003;var masks=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535];function LZWEncoder(width,height,pixels,colorDepth){var initCodeSize=Math.max(2,colorDepth);var accum=new Uint8Array(256);var htab=new Int32Array(HSIZE);var codetab=new Int32Array(HSIZE);var cur_accum,cur_bits=0;var a_count;var free_ent=0;var maxcode;var clear_flg=false;var g_init_bits,ClearCode,EOFCode;function char_out(c,outs){accum[a_count++]=c;if(a_count>=254)flush_char(outs)}function cl_block(outs){cl_hash(HSIZE);free_ent=ClearCode+2;clear_flg=true;output(ClearCode,outs)}function cl_hash(hsize){for(var i=0;i=0){disp=hsize_reg-i;if(i===0)disp=1;do{if((i-=disp)<0)i+=hsize_reg;if(htab[i]===fcode){ent=codetab[i];continue outer_loop}}while(htab[i]>=0)}output(ent,outs);ent=c;if(free_ent<1<0){outs.writeByte(a_count);outs.writeBytes(accum,0,a_count);a_count=0}}function MAXCODE(n_bits){return(1<0)cur_accum|=code<=8){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}if(free_ent>maxcode||clear_flg){if(clear_flg){maxcode=MAXCODE(n_bits=g_init_bits);clear_flg=false}else{++n_bits;if(n_bits==BITS)maxcode=1<0){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}flush_char(outs)}}this.encode=encode}module.exports=LZWEncoder},{}],3:[function(require,module,exports){var ncycles=100;var netsize=256;var maxnetpos=netsize-1;var netbiasshift=4;var intbiasshift=16;var intbias=1<>betashift;var betagamma=intbias<>3;var radiusbiasshift=6;var radiusbias=1<>3);var i,v;for(i=0;i>=netbiasshift;network[i][1]>>=netbiasshift;network[i][2]>>=netbiasshift;network[i][3]=i}}function altersingle(alpha,i,b,g,r){network[i][0]-=alpha*(network[i][0]-b)/initalpha;network[i][1]-=alpha*(network[i][1]-g)/initalpha;network[i][2]-=alpha*(network[i][2]-r)/initalpha}function alterneigh(radius,i,b,g,r){var lo=Math.abs(i-radius);var hi=Math.min(i+radius,netsize);var j=i+1;var k=i-1;var m=1;var p,a;while(jlo){a=radpower[m++];if(jlo){p=network[k--];p[0]-=a*(p[0]-b)/alpharadbias;p[1]-=a*(p[1]-g)/alpharadbias;p[2]-=a*(p[2]-r)/alpharadbias}}}function contest(b,g,r){var bestd=~(1<<31);var bestbiasd=bestd;var bestpos=-1;var bestbiaspos=bestpos;var i,n,dist,biasdist,betafreq;for(i=0;i>intbiasshift-netbiasshift);if(biasdist>betashift;freq[i]-=betafreq;bias[i]+=betafreq<>1;for(j=previouscol+1;j>1;for(j=previouscol+1;j<256;j++)netindex[j]=maxnetpos}function inxsearch(b,g,r){var a,p,dist;var bestd=1e3;var best=-1;var i=netindex[g];var j=i-1;while(i=0){if(i=bestd)i=netsize;else{i++;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist=0){p=network[j];dist=g-p[1];if(dist>=bestd)j=-1;else{j--;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist>radiusbiasshift;if(rad<=1)rad=0;for(i=0;i=lengthcount)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 ({ + type: res.headers['content-type'], + data: res.body + })); +} + +function createCanvas(width, height) { + const canvas = new Canvas(width, height); + return canvas; +} + +class ImageEx { + constructor(url) { + this.url = url; + this.loaded = loadFromUrl(url).then(result => { + this.type = result.type; + this.data = result.data; + if (this.type === 'image/gif') { + this.initGif(); + } else { + this.initStatic(); + } + return this; + }); + } + + + initGif() { + const reader = new GifReader(new Uint8Array(this.data)); + this.width = reader.width; + this.height = reader.height; + this.frames = this.decodeFrames(reader); + + this.renderAllFrames(); + } + + initStatic() { + const img = new Image(); + img.src = this.data; + return new Promise(resolve => { + img.onload = () => { + this.width = img.width; + this.height = img.height; + this.frames = [{ + actualOffset: 0, + actualDelay: Infinity, + delay: Infinity + }]; + this.spriteSheet = createCanvas(this.width, this.height); + const spriteSheetCtx = this.spriteSheet.getContext('2d'); + spriteSheetCtx.drawImage(img, 0, 0); + resolve(); + }; + }); + } + + decodeFrames(reader) { + const frames = []; + let offset = 0; + for (let i = 0; i < reader.numFrames(); ++i) { + const frameInfo = reader.frameInfo(i); + frameInfo.pixels = new Uint8ClampedArray(reader.width * reader.height * 4); + reader.decodeAndBlitFrameRGBA(i, frameInfo.pixels); + frameInfo.buffer = this.createBufferCanvas(frameInfo, this.width, this.height); + frameInfo.actualOffset = offset; + frameInfo.actualDelay = Math.max(frameInfo.delay * 10, 20); + offset += frameInfo.actualDelay; + frames.push(frameInfo); + } + this.totalDuration = offset; + return frames; + } + + renderAllFrames() { + let disposeFrame = null; + const canvas = createCanvas(this.width, this.height); + const ctx = canvas.getContext('2d'); + let saved; + this.spriteSheet = createCanvas(this.width * this.frames.length, this.height); + const spriteSheetCtx = this.spriteSheet.getContext('2d'); + for (let i = 0; i < this.frames.length; ++i) { + const frame = this.frames[i]; + if (typeof disposeFrame === 'function') disposeFrame(); + + switch (frame.disposal) { + case 2: + disposeFrame = () => ctx.clearRect(0, 0, canvas.width, canvas.height); + break; + case 3: + saved = ctx.getImageData(0, 0, canvas.width, canvas.height); + disposeFrame = () => ctx.putImageData(saved, 0, 0); // eslint-disable-line no-loop-func + break; + default: + this.disposeFrame = null; + } + + // draw current frame + ctx.drawImage(frame.buffer, frame.x, frame.y); + // draw the frame onto the sprite sheet + spriteSheetCtx.drawImage(canvas, this.width * i, 0); + } + } + + createBufferCanvas(frame, width, height) { + const canvas = createCanvas(frame.width, frame.height); + const ctx = canvas.getContext('2d'); + + const imageData = ctx.createImageData(width, height); + imageData.data.set(frame.pixels); + + ctx.putImageData(imageData, -frame.x, -frame.y); + return canvas; + } + + drawFrame(ctx, frameNum, x, y, args = {}) { + const sx = frameNum * this.width + (args.sx || 0); + const sy = args.sy || 0; + const swidth = Math.min(args.swidth || this.width, this.width) - (args.sx || 0); + const sheight = args.sheight || this.height; + + console.log(`Drawing frame ${frameNum} at`); + console.log('sx', sx); + console.log('sy', sy); + console.log('sw', swidth); + console.log('sh', sheight); + console.log('x', x); + console.log('y', y); + console.log('w', args.width); + console.log('h', args.height); + + ctx.drawImage(this.spriteSheet, sx, sy, swidth, sheight, x, y, args.width || swidth, args.height || sheight); + // ctx.drawImage(this.spriteSheet, 0, 0, 112, 112, 0, 0, 112, 112) + } +} +class CanvasEx { + constructor(width, height) { + this.width = width; + this.height = height; + this.frames = []; + this.totalDuration = Infinity; + } + + addFrame(actualDelay, delay) { + if ((actualDelay === undefined || actualDelay === null) + && (delay === undefined || delay === null)) throw new Error('Delay has to be set!'); + const canvas = createCanvas(this.width, this.height); + const frame = { + actualOffset: this.totalDuration, + delay: delay || Math.max(Math.round(actualDelay / 10), 2), + actualDelay: actualDelay || Math.max(delay * 10, 20), + canvas, + ctx: canvas.getContext('2d') + }; + this.totalDuration += delay; + this.frames.push(frame); + } + + drawImage(img, x, y, args = {}) { + console.log('Drawing image ', img); + console.log('At ', x, y, args); + if (img.frames && img.frames.length > 1) { + if (this.frames.length > 1) throw new Error('Cannot render animations onto animated canvases!'); + this.totalDuration = img.totalDuration; + // we are drawing an animated image onto a static one. + // for each frame in the image, create a frame on this one, cloning the original picture (if any), + // render the original on each frame, and draw the frame on top. + for (let i = this.frames.length; i < img.frames.length; ++i) { + const frame = img.frames[i]; + // console.log(`Adding frame ${i}:`, frame); + this.addFrame(null, frame.delay); + if (this.frames.length > 0) { + this.frames[i].ctx.drawImage(this.frames[0].canvas, 0, 0); + } + } + for (let i = 0; i < img.frames.length; ++i) { + // console.log(`Drawing frame ${i}:`, img.frames[i]); + // draw the i-th source frame to the i-th target frame + img.drawFrame(this.frames[i].ctx, i, x, y, args); + } + console.log('Done drawing animated image, CanvasEx is now', this); + } else { + // we are drawing a static image on top of a (possibly animated) image. + // for each frame, just draw, nothing fancy. + if (img.frames) { + // the image cant have more than one frame, and if it has 0, we dont need to do anything at all + if (img.frames.length === 1) { + for (let i = 0; i < this.frames.length; ++i) { + img.drawFrame(this.frames[i].ctx, 0, x, y, args); + } + } + } else { + for (let i = 0; i < this.frames.length; ++i) { + this.drawImage(this.frames[i].ctx, img, x, y, args); + } + } + console.log('Done drawing static image, CanvasEx is now', this); + } + } + + drawFrame(ctx, frameNum, x, y, args = {}) { + // console.log(`Drawing frame ${frameNum} of`, this) + // ctx.drawImage(this.frames[frameNum].canvas, x, y); + this.drawImage(ctx, this.frames[frameNum].canvas, x, y, args); + } + + _drawImage(ctx, img, x, y, args = {}) { + if (args.transform) { + ctx.save(); + _.each(args.transform, (value, property) => { + ctx[property](...value); + }); + } + if (args.sx !== undefined || args.sy !== undefined || args.swidth !== undefined || args.sheight !== undefined) { + ctx.drawImage(img, args.sx, args.sy, args.swidth, args.sheight, x, y, args.width, args.height); + } else { + ctx.drawImage(img, x, y, args.width, args.height); + } + if (args.transform) { + ctx.restore(); + } + } + + export(outStream) { + const gif = new GifEncoder(this.width, this.height); + gif.createReadStream().pipe(outStream); + gif.setTransparent(0); + gif.setRepeat(0); + gif.start(); + for (let i = 0; i < this.frames.length; ++i) { + const frame = this.frames[i]; + gif.setDelay(frame.delay); + gif.addFrame(frame.ctx); + } + gif.finish(); + } +} + +module.exports = { + CanvasEx, + ImageEx +}; +/* +// const img = new ImageEx("https://cors-anywhere.herokuapp.com/https://cdn.betterttv.net/emote/554da1a289d53f2d12781907/3x"); + +const img = new ImageEx('https://cors-anywhere.herokuapp.com/https://cdn.discordapp.com/emojis/393563453824040983.gif'); +const _canvas = document.getElementById('c'); +const _ctx = _canvas.getContext('2d'); + +const img2 = new ImageEx('https://rawgit.com/CBenni/beebot/master/resources/SnowGlobe.png'); + +const _cnv = new CanvasEx(128, 128); +img.loaded.then(() => { + _cnv.drawImage(img, 0, 0); + // img.drawFrame(_ctx, 0, 0, 0) + return img2.loaded; +}).then(() => { + _cnv.drawImage(img2, 0, 0, { width: 128, height: 128 }); +}).then(() => { + console.log('Drawing CanvasEx to screen', _cnv); + _cnv.drawFrame(_ctx, 0, 0, 0); + + console.log('Starting gif render'); + const GifEncoder = new GIF({ + workers: 2, + quality: 10, + transparent: 'rgba(0,0,0,0)' + }); + + for (let i = 0; i < _cnv.frames.length; ++i) { + const frame = _cnv.frames[i]; + console.log(`Rendering frame ${i}`); + GifEncoder.addFrame(frame.canvas, { delay: frame.delay }); + } + + GifEncoder.on('finished', blob => { + console.log('Done rendering!', URL.createObjectURL(blob)); + document.getElementById('i').src = URL.createObjectURL(blob); + }); + + GifEncoder.render(); +}); + +let curFrm = 0; +setInterval(() => { + _ctx.clearRect(0, 0, 200, 200); + _cnv.drawFrame(_ctx, curFrm++ % _cnv.frames.length, 0, 0); +}, 100); + + +/* img2.loaded.then(() => { + _ctx.drawImage(img2.spriteSheet, 0, 100) +}) + +img.loaded.then(()=>_ctx.drawImage(img.spriteSheet,0,0)) */ diff --git a/gifsupport-node/index.html b/gifsupport-node/index.html new file mode 100644 index 0000000..3e01af9 --- /dev/null +++ b/gifsupport-node/index.html @@ -0,0 +1,20 @@ + + + + + + A Pen by CBenni + + + + + + + + + + + + + + diff --git a/gifsupport-node/index.js b/gifsupport-node/index.js new file mode 100644 index 0000000..95458fe --- /dev/null +++ b/gifsupport-node/index.js @@ -0,0 +1,286 @@ +const _ = require('lodash'); +const express = require('express'); +const { ImageEx, CanvasEx } = require('./imageex'); + +const request = require('request'); +const Discord = require('discord.js'); + +const Canvas = require('canvas'); + +const { ImageEx, CanvasEx } = require("./imageex"); + +const twemoji = require('./twemoji'); + +const app = express(); +const config = require('./config.default.json'); + +try { + _.extend(config, require('./config')); +} catch (err) { + console.log('No config.json found!'); +} + +const cache = {}; + +function all(x, c) { + _.isArray(x) ? _.each(x, c) : c(x); +} + +templates = config.templates; + +for (templateName in templates) { + const data = templates[templateName]; + all(data, template => { + template.image = new Image(); + template.image.src = template.src; + }); +} + +// drawing: we keep the image fixed in its default position and draw the template on top/below it + +// calculates the x or y position of the template to be drawn +// size = width or height of the template/image +// anchor = the corresponding anchor config +function calculatePosition(scale, anchor, imageSize) { + return imageSize * anchor.position / 100 - anchor.offset * scale; +} + +function render(template, img, size, flipH) { + let imgWidth = img.width; + let imgHeight = img.height; + if (size && size.height) { + imgHeight = size.height; + if (!size.width) imgWidth = imgWidth * size.height / img.height; + } + if (size && size.width) { + imgWidth = size.width; + if (!size.height) imgHeight = imgHeight * size.width / img.width; + } + + const xScale = imgWidth / template.anchor.x.size; + const yScale = imgHeight / template.anchor.y.size; + const templateScale = Math.max(0, Math.min(10, Math.max(xScale || 0, yScale || 0))); + console.log('templateScale', templateScale); + + + let templateOffsetX = calculatePosition(templateScale, template.anchor.x, imgWidth); + let templateOffsetY = calculatePosition(templateScale, template.anchor.y, imgHeight); + + let imageOffsetX = 0; + let imageOffsetY = 0; + let resultingWidth = imgWidth; // start with the image boundaries as defined by the image + let resultingHeight = imgHeight; + + if (templateOffsetX < 0) { + resultingWidth -= templateOffsetX; + imageOffsetX = -templateOffsetX; + templateOffsetX = 0; + } + if (templateOffsetY < 0) { + resultingHeight -= templateOffsetY; + imageOffsetY = -templateOffsetY; + templateOffsetY = 0; + } + if (templateOffsetX + template.image.width * templateScale > resultingWidth) { + resultingWidth = templateOffsetX + template.image.width * templateScale; + } + if (templateOffsetY + template.image.height * templateScale > resultingHeight) { + resultingHeight = templateOffsetY + template.image.height * templateScale; + } + + const toDraw = [{ + z: 1, + image: img, + x: flipH ? resultingWidth - imageOffsetX - imgWidth : imageOffsetX, + y: imageOffsetY, + h: imgHeight, + w: imgWidth, + name: 'image' + }, { + z: template.z || 0, + image: template.image, + x: templateOffsetX, + y: templateOffsetY, + h: template.image.height * templateScale, + w: template.image.width * templateScale, + name: `template ${template.src}`, + flipH + }].sort((u, v) => u.z > v.z); + + const canvas = new Canvas(resultingWidth, resultingHeight); + const ctx = canvas.getContext('2d'); + + for (let i = 0; i < toDraw.length; ++i) { + const subject = toDraw[i]; + console.log(`Drawing ${subject.name}${subject.flipH ? ' (flipped)' : ''}`); + try { + if (subject.flipH) { + ctx.save(); + ctx.translate(resultingWidth, 0); + ctx.scale(-1, 1); + } + ctx.drawImage(subject.image, subject.x, subject.y, subject.w, subject.h); + if (subject.flipH) { + ctx.restore(); + } + } catch (err) { + console.error(err); + throw new Error(JSON.stringify({ status: 400, error: 'Invalid template' })); + } + } + + // return the image and cache it + return (canvas); +} + +app.get('/:templateName/', async (req, res) => { + if (!templates[req.params.templateName]) return res.status(404).end(); + try { + const canvas = render(templates[req.params.templateName], await loadImage(req.query.url)); + console.log(canvas); + res.setHeader('Content-Type', 'image/png'); + return canvas.pngStream().pipe(res); + } catch (err) { + console.log(err); + return res.status(400).end(err.message); + } +}); + +app.listen(3002, () => { + console.log('Beebot app listening on port 3002!'); +}); + + +// Discord stuff + + +const client = new Discord.Client({ + autoReconnect: true +}); +// manage roles permission is required +const invitelink = `https://discordapp.com/oauth2/authorize?client_id=${ + config.discord.client_id}&scope=bot&permissions=0`; +const authlink = `https://discordapp.com/oauth2/authorize?client_id=${ + config.discord.client_id}&scope=email`; +console.log(`Bot invite link: ${invitelink}`); + +client.login(config.discord.token).catch(error => { + if (error) { + console.error("Couldn't login: ", error.toString()); + } +}); + +function findEmoji(str) { + const discordEmote = /<:(\w+):(\d+)>/g.exec(str); + if (discordEmote) { + return { + name: discordEmote[1], + id: discordEmote[2], + url: `https://cdn.discordapp.com/emojis/${discordEmote[2]}.png` + }; + } + + let unicodeEmoji; + twemoji.parse(str, (name, emoji) => { + if (unicodeEmoji) return false; + unicodeEmoji = { + name, + id: name, + url: `${emoji.base + emoji.size}/${name}${emoji.ext}` + }; + return false; + }); + return unicodeEmoji; +} + +function loadImage(url) { + return new Promise((resolve, reject) => { + console.log(`Getting ${url}`); + if (url) { + request.get({ url, encoding: null }, (e, r, data) => { + if (e) { + return reject({ status: (r && r.statusCode || 500), error: e }); + } + const img = new Image(); + img.src = data; + resolve(img); + }); + } + }); +} +function reverseString(str) { + return str.split('').reverse().join(''); +} + +const commands = Object.keys(templates).map(x => `/${x}`).join(', '); +const otherCommands = { + invite: `Invite link: <${invitelink}>`, + help: `Available commands: ${commands}.\nUse \\\\ to flip the template horizontally.\nInvite link: <${invitelink}>`, + beebot: `Available commands: ${commands}.\nUse \\\\ to flip the template horizontally.\nInvite link: <${invitelink}>` +}; + + +client.on('message', async message => { + console.log(`[${message.guild.name} - ${message.channel.name}] ${message.author.username}#${message.author.discriminator}: ${message.cleanContent}`); + + let commandParsed = /^([\/\\])(\w+)\b/.exec(message.cleanContent); + if (commandParsed) { + const [, direction, command] = commandParsed; + if (otherCommands[command]) { + const text = otherCommands[command]; + message.channel.send(direction === '\\' ? reverseString(text) : text); + return; + } + } + + const messageSplit = message.cleanContent.split(' '); + const emoji = findEmoji(message.cleanContent); + let result = null; + let count = 0; + try { + if (emoji) { + let name = emoji.name; + for (let i = 0; i < messageSplit.length && count < 4; ++i) { + commandParsed = /^([\/\\])(\w+)\b/.exec(messageSplit[i]); + if (commandParsed) { + const [, direction, command] = commandParsed; + console.log('Got command ', direction, command, direction === '\\' ? 'flipped' : 'not flipped'); + if (templates[command]) { + count++; + name += command; + if (result === null) result = await loadImage(emoji.url); + const templateData = templates[command]; + all(templateData, template => { + result = render(template, result, null, direction === '\\'); + }); + } + } else if (i === 0) return; + } + if (result) { + const messageOptions = { + files: [ + { attachment: result.toBuffer(), name: `${name}.png` } + ] + }; + message.channel.send('', messageOptions); + } + } + } catch (err) { + console.error(err); + } +}); + + +app.get('/', (req, res) => { + const img = new ImageEx(req.query.url || 'https://cdn.discordapp.com/emojis/393563453824040983.gif'); + img.loaded.then(() => { + res.setHeader('Content-Type', 'image/gif'); + const canvas = new CanvasEx(img.width, img.height); + canvas.drawImage(img, 0, 0); + canvas.export(res); + }); +}); + +app.listen(3002, () => { + console.log('Beebot app listening on port 3002!'); +}); diff --git a/gifsupport-node/license.txt b/gifsupport-node/license.txt new file mode 100644 index 0000000..be92a99 --- /dev/null +++ b/gifsupport-node/license.txt @@ -0,0 +1,12 @@ + + + diff --git a/gifsupport/index.js b/gifsupport/index.js index 6029623..00d888b 100644 --- a/gifsupport/index.js +++ b/gifsupport/index.js @@ -1,12 +1,12 @@ function loadFromUrl(url) { - return new Promise(function (resolve, reject) { - var xhr = new XMLHttpRequest(); - xhr.open("GET", url); - xhr.responseType = "arraybuffer"; + return new Promise(((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.responseType = 'arraybuffer'; xhr.onload = function () { if (this.status >= 200 && this.status < 300) { resolve({ - type: xhr.getResponseHeader("Content-Type"), + type: xhr.getResponseHeader('Content-Type'), data: xhr.response }); } else { @@ -23,17 +23,17 @@ function loadFromUrl(url) { }); }; xhr.send(); - }); + })); } -function _arrayBufferToBase64( buffer ) { - var binary = ''; - var bytes = new Uint8Array( buffer ); - var len = bytes.byteLength; - for (var i = 0; i < len; i++) { - binary += String.fromCharCode( bytes[ i ] ); - } - return window.btoa( binary ); +function _arrayBufferToBase64(buffer) { + let binary = ''; + const bytes = new Uint8Array(buffer); + const len = bytes.byteLength; + for (let i = 0; i < len; i++) { + binary += String.fromCharCode(bytes[i]); + } + return window.btoa(binary); } function createCanvas(width, height) { @@ -49,31 +49,30 @@ class ImageEx { this.loaded = loadFromUrl(url).then(result => { this.type = result.type; this.data = result.data; - if(this.type === "image/gif") { + if (this.type === 'image/gif') { return this.initGif(); - } else { - return this.initStatic(); } - }) + return this.initStatic(); + }); } - - + + initGif() { const reader = new GifReader(new Uint8Array(this.data)); this.width = reader.width; this.height = reader.height; this.frames = this.decodeFrames(reader); - + this.renderAllFrames(); } - - initStatic(){ + + initStatic() { // todo: in node, we wanna use this.data const img = new Image(); - var arrayBufferView = new Uint8Array( this.data ); - var blob = new Blob( [ arrayBufferView ], { type: this.type } ); - var urlCreator = window.URL || window.webkitURL; - var imageUrl = urlCreator.createObjectURL( blob ); + const arrayBufferView = new Uint8Array(this.data); + const blob = new Blob([arrayBufferView], { type: this.type }); + const urlCreator = window.URL || window.webkitURL; + const imageUrl = urlCreator.createObjectURL(blob); img.src = imageUrl; return new Promise(resolve => { img.onload = () => { @@ -83,22 +82,22 @@ class ImageEx { actualOffset: 0, actualDelay: Infinity, delay: Infinity - }] + }]; this.spriteSheet = createCanvas(this.width, this.height); - const spriteSheetCtx = this.spriteSheet.getContext("2d"); + const spriteSheetCtx = this.spriteSheet.getContext('2d'); spriteSheetCtx.drawImage(img, 0, 0); resolve(); - } - }) + }; + }); } - + decodeFrames(reader) { const frames = []; let offset = 0; - for(let i=0;i { - return ctx.clearRect(0,0,canvas.width,canvas.height); - } + disposeFrame = () => ctx.clearRect(0, 0, canvas.width, canvas.height); break; case 3: - saved = ctx.getImageData(0,0,canvas.width,canvas.height); - disposeFrame = () => { - return ctx.putImageData(saved, 0, 0); - } + saved = ctx.getImageData(0, 0, canvas.width, canvas.height); + disposeFrame = () => ctx.putImageData(saved, 0, 0); break; default: this.disposeFrame = null; } - + // draw current frame ctx.drawImage(frame.buffer, frame.x, frame.y); // draw the frame onto the sprite sheet spriteSheetCtx.drawImage(canvas, this.width * i, 0); } } - + createBufferCanvas(frame, width, height) { - const canvas = createCanvas(frame.width,frame.height); - const ctx = canvas.getContext("2d"); - + const canvas = createCanvas(frame.width, frame.height); + const ctx = canvas.getContext('2d'); + const imageData = ctx.createImageData(width, height); imageData.data.set(frame.pixels); - - ctx.putImageData(imageData, - frame.x, -frame.y); + + ctx.putImageData(imageData, -frame.x, -frame.y); return canvas; } - + drawFrame(ctx, frameNum, x, y, args = {}) { - console.log("this", this) + console.log('this', this); const sx = frameNum * this.width + (args.sx || 0); const sy = args.sy || 0; const swidth = Math.min(args.swidth || this.width, this.width) - (args.sx || 0); const sheight = args.sheight || this.height; - - console.log(`Drawing frame ${frameNum} at`) - console.log("sx",sx) - console.log("sy",sy) - console.log("sw",swidth) - console.log("sh",sheight) - console.log("x",x) - console.log("y",y) - console.log("w",args.width) - console.log("h",args.height) - + + console.log(`Drawing frame ${frameNum} at`); + console.log('sx', sx); + console.log('sy', sy); + console.log('sw', swidth); + console.log('sh', sheight); + console.log('x', x); + console.log('y', y); + console.log('w', args.width); + console.log('h', args.height); + ctx.drawImage(this.spriteSheet, sx, sy, swidth, sheight, x, y, args.width || swidth, args.height || sheight); - //ctx.drawImage(this.spriteSheet, 0, 0, 112, 112, 0, 0, 112, 112) + // ctx.drawImage(this.spriteSheet, 0, 0, 112, 112, 0, 0, 112, 112) } - } class CanvasEx { constructor(width, height) { @@ -183,76 +177,77 @@ class CanvasEx { this.frames = []; this.totalDuration = Infinity; } - + addFrame(actualDelay, delay) { - if((actualDelay === undefined || actualDelay === null) - && (delay === undefined || delay === null)) throw new Error("Delay has to be set!"); + if ((actualDelay === undefined || actualDelay === null) + && (delay === undefined || delay === null)) throw new Error('Delay has to be set!'); const canvas = createCanvas(this.width, this.height); const frame = { actualOffset: this.totalDuration, - delay: delay ? delay : Math.max(Math.round(actualDelay/10), 2), - actualDelay: actualDelay ? actualDelay : Math.max(delay * 10, 20), + delay: delay || Math.max(Math.round(actualDelay / 10), 2), + actualDelay: actualDelay || Math.max(delay * 10, 20), canvas, - ctx: canvas.getContext("2d") - } + ctx: canvas.getContext('2d') + }; this.totalDuration += delay; this.frames.push(frame); } - - drawImage(img,x,y,args = {}) { - let {sx,sy,swidth,sheight,width,height,beginAt = 0,endAt = Infinity} = args; - console.log("Drawing image ",img) - console.log("At ",x,y,args) + + drawImage(img, x, y, args = {}) { + let { + sx, sy, swidth, sheight, width, height, beginAt = 0, endAt = Infinity + } = args; + console.log('Drawing image ', img); + console.log('At ', x, y, args); beginAt = Math.max(0, beginAt); endAt = Math.min(Math.max(img.totalDuration || 0, this.totalDuration), endAt); - if(img.frames && img.frames.length > 1) { - if(this.frames.length > 1) throw new Error("Cannot render animations onto animated canvases!"); + if (img.frames && img.frames.length > 1) { + if (this.frames.length > 1) throw new Error('Cannot render animations onto animated canvases!'); this.totalDuration = img.totalDuration; // we are drawing an animated image onto a static one. - // for each frame in the image, create a frame on this one, cloning the original picture (if any), + // for each frame in the image, create a frame on this one, cloning the original picture (if any), // render the original on each frame, and draw the frame on top. - for(let i=this.frames.length;i 0) { + if (this.frames.length > 0) { this.frames[i].ctx.drawImage(this.frames[0].canvas, 0, 0); } } - for(let i=0;i{ +img.loaded.then(() => { _cnv.drawImage(img, 0, 0); - //img.drawFrame(_ctx, 0, 0, 0) - return img2.loaded -}).then(()=>{ - _cnv.drawImage(img2, 0, 0, {width: 128, height: 128}); -}).then(()=>{ - console.log("Drawing CanvasEx to screen", _cnv) + // img.drawFrame(_ctx, 0, 0, 0) + return img2.loaded; +}).then(() => { + _cnv.drawImage(img2, 0, 0, { width: 128, height: 128 }); +}).then(() => { + console.log('Drawing CanvasEx to screen', _cnv); _cnv.drawFrame(_ctx, 0, 0, 0); - - console.log("Starting gif render") + + console.log('Starting gif render'); const GifEncoder = new GIF({ workers: 2, quality: 10, - transparent: 'rgba(0,0,0,0)' + transparent: 'rgba(0,0,0,0)' }); - for(let i=0;i<_cnv.frames.length;++i) { + for (let i = 0; i < _cnv.frames.length; ++i) { const frame = _cnv.frames[i]; - console.log("Rendering frame "+i) - GifEncoder.addFrame(frame.canvas, {delay: frame.delay}) + console.log(`Rendering frame ${i}`); + GifEncoder.addFrame(frame.canvas, { delay: frame.delay }); } - GifEncoder.on('finished', function(blob) { - console.log("Done rendering!", URL.createObjectURL(blob)) - document.getElementById("i").src = URL.createObjectURL(blob); + GifEncoder.on('finished', blob => { + console.log('Done rendering!', URL.createObjectURL(blob)); + document.getElementById('i').src = URL.createObjectURL(blob); }); GifEncoder.render(); -}) +}); -var curFrm = 0; -setInterval(()=>{ - _ctx.clearRect(0,0,200,200); +let curFrm = 0; +setInterval(() => { + _ctx.clearRect(0, 0, 200, 200); _cnv.drawFrame(_ctx, curFrm++ % _cnv.frames.length, 0, 0); -}, 100) +}, 100); - -/*img2.loaded.then(() => { +/* img2.loaded.then(() => { _ctx.drawImage(img2.spriteSheet, 0, 100) }) -img.loaded.then(()=>_ctx.drawImage(img.spriteSheet,0,0))*/ \ No newline at end of file +img.loaded.then(()=>_ctx.drawImage(img.spriteSheet,0,0)) */ diff --git a/imageex.js b/imageex.js new file mode 100644 index 0000000..6785619 --- /dev/null +++ b/imageex.js @@ -0,0 +1,291 @@ +const got = require('got'); +const Canvas = require('canvas'); +const { GifReader } = require('omggif'); +const GifEncoder = require('gifencoder'); + +const Image = { Canvas }; + +function loadFromUrl(url) { + return got(url, { encoding: null }).then(res => ({ + type: res.headers['content-type'], + data: res.body + })); +} + +function createCanvas(width, height) { + const canvas = new Canvas(width, height); + return canvas; +} + +class ImageEx { + constructor(url) { + this.url = url; + this.loaded = loadFromUrl(url).then(result => { + this.type = result.type; + this.data = result.data; + if (this.type === 'image/gif') { + return this.initGif(); + } + return this.initStatic(); + }); + } + + + initGif() { + const reader = new GifReader(new Uint8Array(this.data)); + this.width = reader.width; + this.height = reader.height; + this.frames = this.decodeFrames(reader); + + this.renderAllFrames(); + } + + initStatic() { + const img = new Image(); + img.src = this.data; + return new Promise(resolve => { + img.onload = () => { + this.width = img.width; + this.height = img.height; + this.frames = [{ + actualOffset: 0, + actualDelay: Infinity, + delay: Infinity + }]; + this.spriteSheet = createCanvas(this.width, this.height); + const spriteSheetCtx = this.spriteSheet.getContext('2d'); + spriteSheetCtx.drawImage(img, 0, 0); + resolve(); + }; + }); + } + + decodeFrames(reader) { + const frames = []; + let offset = 0; + for (let i = 0; i < reader.numFrames(); ++i) { + const frameInfo = reader.frameInfo(i); + frameInfo.pixels = new Uint8ClampedArray(reader.width * reader.height * 4); + reader.decodeAndBlitFrameRGBA(i, frameInfo.pixels); + frameInfo.buffer = this.createBufferCanvas(frameInfo, this.width, this.height); + frameInfo.actualOffset = offset; + frameInfo.actualDelay = Math.max(frameInfo.delay * 10, 20); + offset += frameInfo.actualDelay; + frames.push(frameInfo); + } + this.totalDuration = offset; + return frames; + } + + renderAllFrames() { + let disposeFrame = null; + const canvas = createCanvas(this.width, this.height); + const ctx = canvas.getContext('2d'); + let saved; + this.spriteSheet = createCanvas(this.width * this.frames.length, this.height); + const spriteSheetCtx = this.spriteSheet.getContext('2d'); + for (let i = 0; i < this.frames.length; ++i) { + const frame = this.frames[i]; + if (typeof disposeFrame === 'function') disposeFrame(); + + switch (frame.disposal) { + case 2: + disposeFrame = () => ctx.clearRect(0, 0, canvas.width, canvas.height); + break; + case 3: + saved = ctx.getImageData(0, 0, canvas.width, canvas.height); + disposeFrame = () => ctx.putImageData(saved, 0, 0); // eslint-disable-line no-loop-func + break; + default: + this.disposeFrame = null; + } + + // draw current frame + ctx.drawImage(frame.buffer, frame.x, frame.y); + // draw the frame onto the sprite sheet + spriteSheetCtx.drawImage(canvas, this.width * i, 0); + } + } + + createBufferCanvas(frame, width, height) { + const canvas = createCanvas(frame.width, frame.height); + const ctx = canvas.getContext('2d'); + + const imageData = ctx.createImageData(width, height); + imageData.data.set(frame.pixels); + + ctx.putImageData(imageData, -frame.x, -frame.y); + return canvas; + } + + drawFrame(ctx, frameNum, x, y, args = {}) { + const sx = frameNum * this.width + (args.sx || 0); + const sy = args.sy || 0; + const swidth = Math.min(args.swidth || this.width, this.width) - (args.sx || 0); + const sheight = args.sheight || this.height; + + console.log(`Drawing frame ${frameNum} at`); + console.log('sx', sx); + console.log('sy', sy); + console.log('sw', swidth); + console.log('sh', sheight); + console.log('x', x); + console.log('y', y); + console.log('w', args.width); + console.log('h', args.height); + + ctx.drawImage(this.spriteSheet, sx, sy, swidth, sheight, x, y, args.width || swidth, args.height || sheight); + // ctx.drawImage(this.spriteSheet, 0, 0, 112, 112, 0, 0, 112, 112) + } +} +class CanvasEx { + constructor(width, height) { + this.width = width; + this.height = height; + this.frames = []; + this.totalDuration = Infinity; + } + + addFrame(actualDelay, delay) { + if ((actualDelay === undefined || actualDelay === null) + && (delay === undefined || delay === null)) throw new Error('Delay has to be set!'); + const canvas = createCanvas(this.width, this.height); + const frame = { + actualOffset: this.totalDuration, + delay: delay || Math.max(Math.round(actualDelay / 10), 2), + actualDelay: actualDelay || Math.max(delay * 10, 20), + canvas, + ctx: canvas.getContext('2d') + }; + this.totalDuration += delay; + this.frames.push(frame); + } + + drawImage(img, x, y, args = {}) { + console.log('Drawing image ', img); + console.log('At ', x, y, args); + if (img.frames && img.frames.length > 1) { + if (this.frames.length > 1) throw new Error('Cannot render animations onto animated canvases!'); + this.totalDuration = img.totalDuration; + // we are drawing an animated image onto a static one. + // for each frame in the image, create a frame on this one, cloning the original picture (if any), + // render the original on each frame, and draw the frame on top. + for (let i = this.frames.length; i < img.frames.length; ++i) { + const frame = img.frames[i]; + // console.log(`Adding frame ${i}:`, frame); + this.addFrame(null, frame.delay); + if (this.frames.length > 0) { + this.frames[i].ctx.drawImage(this.frames[0].canvas, 0, 0); + } + } + for (let i = 0; i < img.frames.length; ++i) { + // console.log(`Drawing frame ${i}:`, img.frames[i]); + // draw the i-th source frame to the i-th target frame + img.drawFrame(this.frames[i].ctx, i, x, y, args); + } + console.log('Done drawing animated image, CanvasEx is now', this); + } else { + // we are drawing a static image on top of a (possibly animated) image. + // for each frame, just draw, nothing fancy. + if (img.frames) { + // the image cant have more than one frame, and if it has 0, we dont need to do anything at all + if (img.frames.length === 1) { + for (let i = 0; i < this.frames.length; ++i) { + img.drawFrame(this.frames[i].ctx, 0, x, y, args); + } + } + } else { + for (let i = 0; i < this.frames.length; ++i) { + this.drawImage(this.frames[i].ctx, img, x, y, args); + } + } + console.log('Done drawing static image, CanvasEx is now', this); + } + } + + drawFrame(ctx, frameNum, x, y, args = {}) { + // console.log(`Drawing frame ${frameNum} of`, this) + // ctx.drawImage(this.frames[frameNum].canvas, x, y); + this.drawImage(ctx, this.frames[frameNum].canvas, x, y, args); + } + + _drawImage(ctx, img, x, y, args = {}) { + if (args.sx !== undefined || args.sy !== undefined || args.swidth !== undefined || args.sheight !== undefined) { + ctx.drawImage(img, args.sx, args.sy, args.swidth, args.sheight, x, y, args.width, args.height); + } else { + ctx.drawImage(img, x, y, args.width, args.height); + } + } + + export(outStream) { + const gif = new GifEncoder(this.width, this.height); + gif.createReadStream().pipe(outStream); + gif.setTransparent(0); + gif.setRepeat(0); + gif.start(); + for (let i = 0; i < this.frames.length; ++i) { + const frame = this.frames[i]; + gif.setDelay(frame.delay); + gif.addFrame(frame.ctx); + } + gif.finish(); + } +} + +module.exports = { + CanvasEx, + ImageEx +}; +/* +// const img = new ImageEx("https://cors-anywhere.herokuapp.com/https://cdn.betterttv.net/emote/554da1a289d53f2d12781907/3x"); + +const img = new ImageEx('https://cors-anywhere.herokuapp.com/https://cdn.discordapp.com/emojis/393563453824040983.gif'); +const _canvas = document.getElementById('c'); +const _ctx = _canvas.getContext('2d'); + +const img2 = new ImageEx('https://rawgit.com/CBenni/beebot/master/resources/SnowGlobe.png'); + +const _cnv = new CanvasEx(128, 128); +img.loaded.then(() => { + _cnv.drawImage(img, 0, 0); + // img.drawFrame(_ctx, 0, 0, 0) + return img2.loaded; +}).then(() => { + _cnv.drawImage(img2, 0, 0, { width: 128, height: 128 }); +}).then(() => { + console.log('Drawing CanvasEx to screen', _cnv); + _cnv.drawFrame(_ctx, 0, 0, 0); + + console.log('Starting gif render'); + const GifEncoder = new GIF({ + workers: 2, + quality: 10, + transparent: 'rgba(0,0,0,0)' + }); + + for (let i = 0; i < _cnv.frames.length; ++i) { + const frame = _cnv.frames[i]; + console.log(`Rendering frame ${i}`); + GifEncoder.addFrame(frame.canvas, { delay: frame.delay }); + } + + GifEncoder.on('finished', blob => { + console.log('Done rendering!', URL.createObjectURL(blob)); + document.getElementById('i').src = URL.createObjectURL(blob); + }); + + GifEncoder.render(); +}); + +let curFrm = 0; +setInterval(() => { + _ctx.clearRect(0, 0, 200, 200); + _cnv.drawFrame(_ctx, curFrm++ % _cnv.frames.length, 0, 0); +}, 100); + + +/* img2.loaded.then(() => { + _ctx.drawImage(img2.spriteSheet, 0, 100) +}) + +img.loaded.then(()=>_ctx.drawImage(img.spriteSheet,0,0)) */ diff --git a/index.js b/index.js index b2c23fd..eeac557 100644 --- a/index.js +++ b/index.js @@ -1,36 +1,32 @@ -var _ = require('lodash'); -var express = require("express"); -var request = require("request"); -var Discord = require('discord.js'); +const _ = require('lodash'); +const express = require('express'); +const Discord = require('discord.js'); -const Canvas = require("canvas"); -const Image = Canvas.Image; +const { CanvasEx, ImageEx } = require('./imageex'); -const twemoji = require("./twemoji"); +const twemoji = require('./twemoji'); + +const app = express(); +const config = require('./config.default.json'); -var app = express() -var config = require("./config.default.json"); try { - _.extend(config, require("./config")); -}catch(err) { - console.log("No config.json found!"); + _.extend(config, require('./config')); // eslint-disable-line global-require +} catch (err) { + console.log('No config.json found!'); } -var cache = {}; - function all(x, c) { - _.isArray(x) ? _.each(x,c) : c(x); + _.isArray(x) ? _.each(x, c) : c(x); } -templates = config.templates; +const { templates } = config; -for(templateName in templates) { - const data = templates[templateName]; - all(data, template => { - template.image = new Image(); - template.image.src = template.src; - }) -} +_.each(templates, (template, templateName) => { + const data = templates[templateName]; + all(data, templatePart => { + template.image = new ImageEx(templatePart.src); + }); +}); // drawing: we keep the image fixed in its default position and draw the template on top/below it @@ -38,233 +34,215 @@ for(templateName in templates) { // size = width or height of the template/image // anchor = the corresponding anchor config function calculatePosition(scale, anchor, imageSize) { - return imageSize * anchor.position / 100 - anchor.offset * scale; + return imageSize * anchor.position / 100 - anchor.offset * scale; } function render(template, img, size, flipH) { - var imgWidth = img.width; - var imgHeight = img.height; - if (size && size.height) { - imgHeight = size.height; - if (!size.width) imgWidth = imgWidth * size.height / img.height; - } - if (size && size.width) { - imgWidth = size.width; - if (!size.height) imgHeight = imgHeight * size.width / img.width; - } + let imgWidth = img.width; + let imgHeight = img.height; + if (size && size.height) { + imgHeight = size.height; + if (!size.width) imgWidth = imgWidth * size.height / img.height; + } + if (size && size.width) { + imgWidth = size.width; + if (!size.height) imgHeight = imgHeight * size.width / img.width; + } - const xScale = imgWidth / template.anchor.x.size; - const yScale = imgHeight / template.anchor.y.size; - const templateScale = Math.max(0, Math.min(10, Math.max(xScale || 0, yScale || 0))); - console.log("templateScale",templateScale) - + const xScale = imgWidth / template.anchor.x.size; + const yScale = imgHeight / template.anchor.y.size; + const templateScale = Math.max(0, Math.min(10, Math.max(xScale || 0, yScale || 0))); + console.log('templateScale', templateScale); - let templateOffsetX = calculatePosition(templateScale, template.anchor.x, imgWidth); - let templateOffsetY = calculatePosition(templateScale, template.anchor.y, imgHeight); - let imageOffsetX = 0; - let imageOffsetY = 0; - let resultingWidth = imgWidth; // start with the image boundaries as defined by the image - let resultingHeight = imgHeight; + let templateOffsetX = calculatePosition(templateScale, template.anchor.x, imgWidth); + let templateOffsetY = calculatePosition(templateScale, template.anchor.y, imgHeight); - if(templateOffsetX < 0) { - resultingWidth -= templateOffsetX; - imageOffsetX = -templateOffsetX; - templateOffsetX = 0; - } - if(templateOffsetY < 0) { - resultingHeight -= templateOffsetY; - imageOffsetY = -templateOffsetY; - templateOffsetY = 0; - } - if(templateOffsetX + template.image.width * templateScale > resultingWidth) { - resultingWidth = templateOffsetX + template.image.width * templateScale; - } - if(templateOffsetY + template.image.height * templateScale > resultingHeight) { - resultingHeight = templateOffsetY + template.image.height * templateScale; - } + let imageOffsetX = 0; + let imageOffsetY = 0; + let resultingWidth = imgWidth; // start with the image boundaries as defined by the image + let resultingHeight = imgHeight; - const toDraw = [{ - z: 1, - image: img, - x: flipH ? resultingWidth - imageOffsetX - imgWidth : imageOffsetX, - y: imageOffsetY, - h: imgHeight, - w: imgWidth, - name: "image" - }, { - z: template.z || 0, - image: template.image, - x: templateOffsetX, - y: templateOffsetY, - h: template.image.height * templateScale, - w: template.image.width * templateScale, - name: "template "+template.src, - flipH - }].sort((u,v) => u.z > v.z); + if (templateOffsetX < 0) { + resultingWidth -= templateOffsetX; + imageOffsetX = -templateOffsetX; + templateOffsetX = 0; + } + if (templateOffsetY < 0) { + resultingHeight -= templateOffsetY; + imageOffsetY = -templateOffsetY; + templateOffsetY = 0; + } + if (templateOffsetX + template.image.width * templateScale > resultingWidth) { + resultingWidth = templateOffsetX + template.image.width * templateScale; + } + if (templateOffsetY + template.image.height * templateScale > resultingHeight) { + resultingHeight = templateOffsetY + template.image.height * templateScale; + } - var canvas = new Canvas(resultingWidth, resultingHeight); - var ctx = canvas.getContext("2d"); + const toDraw = [{ + z: 1, + image: img, + x: flipH ? resultingWidth - imageOffsetX - imgWidth : imageOffsetX, + y: imageOffsetY, + h: imgHeight, + w: imgWidth, + name: 'image' + }, { + z: template.z || 0, + image: template.image, + x: templateOffsetX, + y: templateOffsetY, + h: template.image.height * templateScale, + w: template.image.width * templateScale, + name: `template ${template.src}`, + flipH + }].sort((u, v) => u.z > v.z); - for(let i=0;i { + if (!templates[req.params.templateName]) return res.status(404).end(); + try { + const img = new ImageEx(req.query.url); + const canvas = render(templates[req.params.templateName], await img.loaded); + console.log(canvas); + res.setHeader('Content-Type', 'image/png'); + return canvas.pngStream().pipe(res); + } catch (err) { + console.log(err); + return res.status(400).end(err.message); + } }); -app.listen(3002, function () { - console.log('Beebot app listening on port 3002!') -}) - +app.listen(3002, () => { + console.log('Beebot app listening on port 3002!'); +}); // Discord stuff -var client = new Discord.Client({ - autoReconnect: true +const client = new Discord.Client({ + autoReconnect: true }); // manage roles permission is required -const invitelink = 'https://discordapp.com/oauth2/authorize?client_id=' - + config.discord.client_id + '&scope=bot&permissions=0'; -const authlink = 'https://discordapp.com/oauth2/authorize?client_id=' - + config.discord.client_id + '&scope=email'; -console.log("Bot invite link: " + invitelink); +const invitelink = `https://discordapp.com/oauth2/authorize?client_id=${ + config.discord.client_id}&scope=bot&permissions=0`; +const authlink = `https://discordapp.com/oauth2/authorize?client_id=${ + config.discord.client_id}&scope=email`; +console.log(`Bot invite link: ${invitelink}`); -client.login(config.discord.token).catch(function (error) { - if (error) { - console.error("Couldn't login: ", error.toString()); - } +client.login(config.discord.token).catch(error => { + if (error) { + console.error("Couldn't login: ", error.toString()); + } }); function findEmoji(str) { - const discordEmote = /<:(\w+):(\d+)>/g.exec(str) - if (discordEmote) { - return { - name: discordEmote[1], - id: discordEmote[2], - url: `https://cdn.discordapp.com/emojis/${discordEmote[2]}.png` - } - } + const discordEmote = /<:(\w+):(\d+)>/g.exec(str); + if (discordEmote) { + return { + name: discordEmote[1], + id: discordEmote[2], + url: `https://cdn.discordapp.com/emojis/${discordEmote[2]}.png` + }; + } - let unicodeEmoji; - twemoji.parse(str, (name, emoji) => { - if (unicodeEmoji) return false; - unicodeEmoji = { - name, - id: name, - url: emoji.base + emoji.size + "/" + name + emoji.ext - } - return false; - }); - return unicodeEmoji; + let unicodeEmoji; + twemoji.parse(str, (name, emoji) => { + if (unicodeEmoji) return false; + unicodeEmoji = { + name, + id: name, + url: `${emoji.base + emoji.size}/${name}${emoji.ext}` + }; + return false; + }); + return unicodeEmoji; } -function loadImage(url) { - return new Promise((resolve, reject) => { - console.log("Getting " + url); - if (url) { - request.get({ url: url, encoding: null }, function (e, r, data) { - if (e) { - return reject({ status: (r && r.statusCode || 500), error: e }); - } - var img = new Image(); - img.src = data; - resolve(img); - }) - } - }); -} function reverseString(str) { - return str.split("").reverse().join(""); + return str.split('').reverse().join(''); } -const commands = Object.keys(templates).map(x => '/'+x).join(', '); +const commands = Object.keys(templates).map(x => `/${x}`).join(', '); const otherCommands = { - "invite": `Invite link: <${invitelink}>`, - "help": `Available commands: ${commands}.\nUse \\\\ to flip the template horizontally.\nInvite link: <${invitelink}>`, - "beebot": `Available commands: ${commands}.\nUse \\\\ to flip the template horizontally.\nInvite link: <${invitelink}>` -} + invite: `Invite link: <${invitelink}>`, + help: `Available commands: ${commands}.\nUse \\\\ to flip the template horizontally.\nInvite link: <${invitelink}>`, + beebot: `Available commands: ${commands}.\nUse \\\\ to flip the template horizontally.\nInvite link: <${invitelink}>` +}; -client.on('message', async function (message) { - console.log(`[${message.guild.name} - ${message.channel.name}] ${message.author.username}#${message.author.discriminator}: ${message.cleanContent}`); +client.on('message', async message => { + console.log(`[${message.guild.name} - ${message.channel.name}] ${message.author.username}#${message.author.discriminator}: ${message.cleanContent}`); - let commandParsed = /^([\/\\])(\w+)\b/.exec(message.cleanContent); - if(commandParsed) { - const [, direction, command] = commandParsed; - if(otherCommands[command]) { - const text = otherCommands[command]; - message.channel.send(direction === "\\" ? reverseString(text) : text); - return; - } - } + let commandParsed = /^([/\\])(\w+)\b/.exec(message.cleanContent); + if (commandParsed) { + const [, direction, command] = commandParsed; + if (otherCommands[command]) { + const text = otherCommands[command]; + message.channel.send(direction === '\\' ? reverseString(text) : text); + return; + } + } - const messageSplit = message.cleanContent.split(" "); - const emoji = findEmoji(message.cleanContent); - let result = null; - let count = 0; - try { - if (emoji) { - let name = emoji.name; - for (var i = 0; i < messageSplit.length && count < 4; ++i) { - commandParsed = /^([\/\\])(\w+)\b/.exec(messageSplit[i]); - if (commandParsed) { - const [, direction, command] = commandParsed; - console.log("Got command ",direction, command, direction === '\\' ? "flipped":"not flipped"); - if(templates[command]) { - count++; - name += command; - if (result === null) result = await loadImage(emoji.url); - const templateData = templates[command]; - all(templateData, template => { - result = render(template, result, null, direction === '\\'); - }) - } - } else { - if(i===0) return; - } - } - if (result) { - var messageOptions = { - files: [ - { attachment: result.toBuffer(), name: name + ".png" } - ] - } - message.channel.send("", messageOptions); - } - } - } catch(err) { - console.error(err); - } -}); \ No newline at end of file + const messageSplit = message.cleanContent.split(' '); + const emoji = findEmoji(message.cleanContent); + let result = null; + let count = 0; + try { + if (emoji) { + let { name } = emoji; + for (let i = 0; i < messageSplit.length && count < 4; ++i) { + commandParsed = /^([/\\])(\w+)\b/.exec(messageSplit[i]); + if (commandParsed) { + const [, direction, command] = commandParsed; + console.log('Got command ', direction, command, direction === '\\' ? 'flipped' : 'not flipped'); + if (templates[command]) { + count++; + name += command; + if (result === null) { + result = new ImageEx(emoji.url); + await result.loaded; // eslint-disable-line no-await-in-loop + } + const templateData = templates[command]; + all(templateData, template => { // eslint-disable-line no-loop-func + result = render(template, result, null, direction === '\\'); + }); + } + } else if (i === 0) return; + } + if (result) { + const messageOptions = { + files: [ + { attachment: result.toBuffer(), name: `${name}.png` } + ] + }; + message.channel.send('', messageOptions); + } + } + } catch (err) { + console.error(err); + } +}); diff --git a/package.json b/package.json index 485c52a..63e0a56 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,15 @@ "canvas": "^1.6.5", "discord.js": "^11.1.0", "express": "^4.15.2", + "gifencoder": "^1.1.0", + "got": "^8.0.1", "lodash": "^4.17.4", + "omggif": "^1.0.9", "request": "^2.81.0" + }, + "devDependencies": { + "eslint": "^4.14.0", + "eslint-config-airbnb-base": "^12.1.0", + "eslint-plugin-import": "^2.8.0" } } diff --git a/yarn.lock b/yarn.lock index 3018983..a36c9f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,10 @@ # yarn lockfile v1 +"@sindresorhus/is@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.6.0.tgz#383f456b26bc96c7889f0332079f4358b16c58dc" + accepts@~1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f" @@ -9,7 +13,25 @@ accepts@~1.3.4: mime-types "~2.1.16" negotiator "0.6.1" -ajv@^5.1.0: +acorn-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + dependencies: + acorn "^3.0.4" + +acorn@^3.0.4: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + +acorn@^5.2.1: + version "5.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.3.0.tgz#7446d39459c54fb49a80e6ee6478149b940ec822" + +ajv-keywords@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" + +ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" dependencies: @@ -18,10 +40,52 @@ ajv@^5.1.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" +ansi-escapes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" + dependencies: + color-convert "^1.9.0" + +argparse@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + dependencies: + sprintf-js "~1.0.2" + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" @@ -46,6 +110,18 @@ aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" +babel-code-frame@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + bcrypt-pbkdf@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" @@ -79,10 +155,43 @@ boom@5.x.x: dependencies: hoek "4.x.x" +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +builtin-modules@^1.0.0, builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" +cacheable-request@^2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" + dependencies: + clone-response "1.0.2" + get-stream "3.0.0" + http-cache-semantics "3.8.1" + keyv "3.0.0" + lowercase-keys "1.0.0" + normalize-url "2.0.1" + responselike "1.0.2" + +caller-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + dependencies: + callsites "^0.2.0" + +callsites@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" + canvas@^1.6.5: version "1.6.9" resolved "https://registry.yarnpkg.com/canvas/-/canvas-1.6.9.tgz#e3f95cec7b16bf2d6f3fc725c02d940d3258f69b" @@ -93,16 +202,84 @@ caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" + dependencies: + ansi-styles "^3.1.0" + escape-string-regexp "^1.0.5" + supports-color "^4.0.0" + +chardet@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" + +circular-json@^0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + +clone-response@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + dependencies: + mimic-response "^1.0.0" + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" +color-convert@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" + dependencies: + color-name "^1.1.1" + +color-name@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" dependencies: delayed-stream "~1.0.0" +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" @@ -119,10 +296,18 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" -core-util-is@1.0.2: +core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" +cross-spawn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + cryptiles@3.x.x: version "3.1.2" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" @@ -135,12 +320,44 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -debug@2.6.9: +debug@2.6.9, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: ms "2.0.0" +debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + dependencies: + mimic-response "^1.0.0" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +del@^2.0.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" + dependencies: + globby "^5.0.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + rimraf "^2.2.8" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -163,6 +380,23 @@ discord.js@^11.1.0: tweetnacl "^1.0.0" ws "^3.1.0" +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.2.tgz#68f96ce8efc56cc42651f1faadb4f175273b0075" + dependencies: + esutils "^2.0.2" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" @@ -177,10 +411,144 @@ encodeurl@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" +error-ex@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + dependencies: + is-arrayish "^0.2.1" + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +eslint-config-airbnb-base@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz#386441e54a12ccd957b0a92564a4bafebd747944" + dependencies: + eslint-restricted-globals "^0.1.1" + +eslint-import-resolver-node@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz#4422574cde66a9a7b099938ee4d508a199e0e3cc" + dependencies: + debug "^2.6.8" + resolve "^1.2.0" + +eslint-module-utils@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449" + dependencies: + debug "^2.6.8" + pkg-dir "^1.0.0" + +eslint-plugin-import@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.8.0.tgz#fa1b6ef31fcb3c501c09859c1b86f1fc5b986894" + dependencies: + builtin-modules "^1.1.1" + contains-path "^0.1.0" + debug "^2.6.8" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.1" + eslint-module-utils "^2.1.1" + has "^1.0.1" + lodash.cond "^4.3.0" + minimatch "^3.0.3" + read-pkg-up "^2.0.0" + +eslint-restricted-globals@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7" + +eslint-scope@^3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-visitor-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" + +eslint@^4.14.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.14.0.tgz#96609768d1dd23304faba2d94b7fefe5a5447a82" + dependencies: + ajv "^5.3.0" + babel-code-frame "^6.22.0" + chalk "^2.1.0" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.1.0" + doctrine "^2.0.2" + eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" + espree "^3.5.2" + esquery "^1.0.0" + esutils "^2.0.2" + file-entry-cache "^2.0.0" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.0.1" + ignore "^3.3.3" + imurmurhash "^0.1.4" + inquirer "^3.0.6" + is-resolvable "^1.0.0" + js-yaml "^3.9.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.4" + minimatch "^3.0.2" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + pluralize "^7.0.0" + progress "^2.0.0" + require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" + strip-json-comments "~2.0.1" + table "^4.0.1" + text-table "~0.2.0" + +espree@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.2.tgz#756ada8b979e9dcfcdb30aad8d1a9304a905e1ca" + dependencies: + acorn "^5.2.1" + acorn-jsx "^3.0.0" + +esprima@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" + +esquery@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa" + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" + dependencies: + estraverse "^4.1.0" + object-assign "^4.0.1" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -224,14 +592,18 @@ extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" -extsprintf@1.3.0: +external-editor@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48" + dependencies: + chardet "^0.4.0" + iconv-lite "^0.4.17" + tmp "^0.0.33" + +extsprintf@1.3.0, extsprintf@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - fast-deep-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" @@ -240,6 +612,23 @@ fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" + dependencies: + flat-cache "^1.2.1" + object-assign "^4.0.1" + finalhandler@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" @@ -252,6 +641,28 @@ finalhandler@1.1.0: statuses "~1.3.1" unpipe "~1.0.0" +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + +flat-cache@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" + dependencies: + circular-json "^0.3.1" + del "^2.0.2" + graceful-fs "^4.1.2" + write "^0.2.1" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -272,12 +683,91 @@ fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" +from2@^2.1.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +function-bind@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + +get-stream@3.0.0, get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" dependencies: assert-plus "^1.0.0" +gifencoder@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/gifencoder/-/gifencoder-1.1.0.tgz#50e7d98c4b0cf97edd0d885d7e1b997f88802de3" + +glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.0.1: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.1.0.tgz#632644457f5f0e3ae711807183700ebf2e4633e4" + +globby@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" + dependencies: + array-union "^1.0.1" + arrify "^1.0.0" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +got@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/got/-/got-8.0.1.tgz#6d7f8bb3eb99e5af912efe26a104476441e08e7f" + dependencies: + "@sindresorhus/is" "^0.6.0" + cacheable-request "^2.1.1" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + into-stream "^3.1.0" + is-retry-allowed "^1.1.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + mimic-response "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^2.0.1" + pify "^3.0.0" + safe-buffer "^5.1.1" + timed-out "^4.0.1" + url-parse-lax "^3.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -289,6 +779,32 @@ har-validator@~5.0.3: ajv "^5.1.0" har-schema "^2.0.0" +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + +has-symbol-support-x@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.1.tgz#66ec2e377e0c7d7ccedb07a3a84d77510ff1bc4c" + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + dependencies: + has-symbol-support-x "^1.4.1" + +has@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + dependencies: + function-bind "^1.0.2" + hawk@~6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" @@ -302,6 +818,14 @@ hoek@4.x.x: version "4.2.0" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" +hosted-git-info@^2.1.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" + +http-cache-semantics@3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" + http-errors@1.6.2, http-errors@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" @@ -319,30 +843,151 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -iconv-lite@0.4.19: +iconv-lite@0.4.19, iconv-lite@^0.4.17: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" -inherits@2.0.3: +ignore@^3.3.3: + version "3.3.7" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" +inquirer@^3.0.6: + version "3.3.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.0.4" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx-lite "^4.0.8" + rx-lite-aggregates "^4.0.8" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + +into-stream@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" + dependencies: + from2 "^2.1.1" + p-is-promise "^1.1.0" + ipaddr.js@1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0" +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + +is-path-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + +is-path-in-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" + dependencies: + path-is-inside "^1.0.1" + +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + +is-resolvable@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.1.tgz#acca1cd36dbe44b974b924321555a70ba03b1cf4" + +is-retry-allowed@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" + is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" +isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@^3.9.1: + version "3.10.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + json-schema-traverse@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" @@ -351,6 +996,10 @@ json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -364,7 +1013,40 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -lodash@^4.17.4: +keyv@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" + dependencies: + json-buffer "3.0.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +lodash.cond@^4.3.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" + +lodash@^4.17.4, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -372,6 +1054,17 @@ long@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" +lowercase-keys@1.0.0, lowercase-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" + +lru-cache@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -398,44 +1091,234 @@ mime@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" +mimic-fn@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" + +mimic-response@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e" + +minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + nan@^2.4.0: version "2.8.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +normalize-package-data@^2.3.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-url@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" + dependencies: + prepend-http "^2.0.0" + query-string "^5.0.1" + sort-keys "^2.0.0" + oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" +object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +omggif@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.9.tgz#dcb7024dacd50c52b4d303f04802c91c057c765f" + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" dependencies: ee-first "1.1.1" +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + +p-is-promise@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" + +p-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +p-timeout@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" + dependencies: + p-finally "^1.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + parseurl@~1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.1, path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + dependencies: + pify "^2.0.0" + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + dependencies: + find-up "^1.0.0" + +pluralize@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + prism-media@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-0.0.1.tgz#a3425c9cabd50d1c6c02e543941a11895667bd10" +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +progress@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" + proxy-addr@~2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec" @@ -443,6 +1326,10 @@ proxy-addr@~2.0.2: forwarded "~0.1.2" ipaddr.js "1.5.2" +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -451,6 +1338,14 @@ qs@6.5.1, qs@~6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" +query-string@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.0.1.tgz#6e2b86fe0e08aef682ecbe86e85834765402bd88" + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" @@ -464,6 +1359,33 @@ raw-body@2.3.2: iconv-lite "0.4.19" unpipe "1.0.0" +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +readable-stream@^2.0.0, readable-stream@^2.2.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + request@^2.81.0: version "2.83.0" resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" @@ -491,10 +1413,66 @@ request@^2.81.0: tunnel-agent "^0.6.0" uuid "^3.1.0" -safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0: +require-uncached@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" + dependencies: + caller-path "^0.1.0" + resolve-from "^1.0.0" + +resolve-from@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + +resolve@^1.2.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" + dependencies: + path-parse "^1.0.5" + +responselike@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +rimraf@^2.2.8: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + dependencies: + is-promise "^2.1.0" + +rx-lite-aggregates@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" + dependencies: + rx-lite "*" + +rx-lite@*, rx-lite@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + +safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +"semver@2 || 3 || 4 || 5", semver@^5.3.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + send@0.16.1: version "0.16.1" resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3" @@ -530,6 +1508,26 @@ setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +slice-ansi@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" + dependencies: + is-fullwidth-code-point "^2.0.0" + snekfetch@^3.3.0: version "3.6.1" resolved "https://registry.yarnpkg.com/snekfetch/-/snekfetch-3.6.1.tgz#9b77fa946cfa15c7041940593a2689dc547427ea" @@ -540,6 +1538,30 @@ sntp@2.x.x: dependencies: hoek "4.x.x" +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + dependencies: + is-plain-obj "^1.0.0" + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + sshpk@^1.7.0: version "1.13.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" @@ -554,18 +1576,90 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -"statuses@>= 1.3.1 < 2": - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - -statuses@~1.3.1: +"statuses@>= 1.3.1 < 2", statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + +string-width@^2.1.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + stringstream@~0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^4.0.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" + dependencies: + has-flag "^2.0.0" + +table@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" + dependencies: + ajv "^5.2.3" + ajv-keywords "^2.1.0" + chalk "^2.1.0" + lodash "^4.17.4" + slice-ansi "1.0.0" + string-width "^2.1.1" + +text-table@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + dependencies: + os-tmpdir "~1.0.2" + tough-cookie@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" @@ -586,6 +1680,12 @@ tweetnacl@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.0.tgz#713d8b818da42068740bf68386d0479e66fc8a7b" +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + type-is@~1.6.15: version "1.6.15" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" @@ -593,6 +1693,10 @@ type-is@~1.6.15: media-typer "0.3.0" mime-types "~2.1.15" +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + ultron@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" @@ -601,6 +1705,20 @@ unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + dependencies: + prepend-http "^2.0.0" + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -609,6 +1727,13 @@ uuid@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -621,6 +1746,26 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +which@^1.2.9: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" + dependencies: + isexe "^2.0.0" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + dependencies: + mkdirp "^0.5.1" + ws@^3.1.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" @@ -628,3 +1773,7 @@ ws@^3.1.0: async-limiter "~1.0.0" safe-buffer "~5.1.0" ultron "~1.1.0" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" From 0176bd063f406ac13a60d7c682919cd258469c35 Mon Sep 17 00:00:00 2001 From: CBenni Date: Sun, 31 Dec 2017 13:59:14 +0100 Subject: [PATCH 3/4] Added gif support --- gifsupport-node/README.txt | 3 - gifsupport-node/gif.js | 3 - gifsupport-node/gif.worker.js | 3 - gifsupport-node/imageex.js | 303 --------------------------------- gifsupport-node/index.html | 20 --- gifsupport-node/index.js | 286 ------------------------------- gifsupport-node/license.txt | 12 -- gifsupport/README.txt | 3 - gifsupport/gif.js | 3 - gifsupport/gif.worker.js | 3 - gifsupport/index.html | 20 --- gifsupport/index.js | 309 ---------------------------------- gifsupport/license.txt | 12 -- imageex.js | 117 +++++++++---- index.js | 27 ++- package.json | 4 +- yarn.lock | 6 +- 17 files changed, 106 insertions(+), 1028 deletions(-) delete mode 100644 gifsupport-node/README.txt delete mode 100644 gifsupport-node/gif.js delete mode 100644 gifsupport-node/gif.worker.js delete mode 100644 gifsupport-node/imageex.js delete mode 100644 gifsupport-node/index.html delete mode 100644 gifsupport-node/index.js delete mode 100644 gifsupport-node/license.txt delete mode 100644 gifsupport/README.txt delete mode 100644 gifsupport/gif.js delete mode 100644 gifsupport/gif.worker.js delete mode 100644 gifsupport/index.html delete mode 100644 gifsupport/index.js delete mode 100644 gifsupport/license.txt diff --git a/gifsupport-node/README.txt b/gifsupport-node/README.txt deleted file mode 100644 index 6aec385..0000000 --- a/gifsupport-node/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -A Pen created at CodePen.io. You can find this one at https://codepen.io/CBenni/pen/YYZKeO. - - \ No newline at end of file diff --git a/gifsupport-node/gif.js b/gifsupport-node/gif.js deleted file mode 100644 index 2e4d204..0000000 --- a/gifsupport-node/gif.js +++ /dev/null @@ -1,3 +0,0 @@ -// gif.js 0.2.0 - https://github.com/jnordberg/gif.js -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.GIF=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0&&this._events[type].length>m){this._events[type].warned=true;console.error("(node) warning: possible EventEmitter memory "+"leak detected. %d listeners added. "+"Use emitter.setMaxListeners() to increase limit.",this._events[type].length);if(typeof console.trace==="function"){console.trace()}}}return this};EventEmitter.prototype.on=EventEmitter.prototype.addListener;EventEmitter.prototype.once=function(type,listener){if(!isFunction(listener))throw TypeError("listener must be a function");var fired=false;function g(){this.removeListener(type,g);if(!fired){fired=true;listener.apply(this,arguments)}}g.listener=listener;this.on(type,g);return this};EventEmitter.prototype.removeListener=function(type,listener){var list,position,length,i;if(!isFunction(listener))throw TypeError("listener must be a function");if(!this._events||!this._events[type])return this;list=this._events[type];length=list.length;position=-1;if(list===listener||isFunction(list.listener)&&list.listener===listener){delete this._events[type];if(this._events.removeListener)this.emit("removeListener",type,listener)}else if(isObject(list)){for(i=length;i-- >0;){if(list[i]===listener||list[i].listener&&list[i].listener===listener){position=i;break}}if(position<0)return this;if(list.length===1){list.length=0;delete this._events[type]}else{list.splice(position,1)}if(this._events.removeListener)this.emit("removeListener",type,listener)}return this};EventEmitter.prototype.removeAllListeners=function(type){var key,listeners;if(!this._events)return this;if(!this._events.removeListener){if(arguments.length===0)this._events={};else if(this._events[type])delete this._events[type];return this}if(arguments.length===0){for(key in this._events){if(key==="removeListener")continue;this.removeAllListeners(key)}this.removeAllListeners("removeListener");this._events={};return this}listeners=this._events[type];if(isFunction(listeners)){this.removeListener(type,listeners)}else if(listeners){while(listeners.length)this.removeListener(type,listeners[listeners.length-1])}delete this._events[type];return this};EventEmitter.prototype.listeners=function(type){var ret;if(!this._events||!this._events[type])ret=[];else if(isFunction(this._events[type]))ret=[this._events[type]];else ret=this._events[type].slice();return ret};EventEmitter.prototype.listenerCount=function(type){if(this._events){var evlistener=this._events[type];if(isFunction(evlistener))return 1;else if(evlistener)return evlistener.length}return 0};EventEmitter.listenerCount=function(emitter,type){return emitter.listenerCount(type)};function isFunction(arg){return typeof arg==="function"}function isNumber(arg){return typeof arg==="number"}function isObject(arg){return typeof arg==="object"&&arg!==null}function isUndefined(arg){return arg===void 0}},{}],2:[function(require,module,exports){var UA,browser,mode,platform,ua;ua=navigator.userAgent.toLowerCase();platform=navigator.platform.toLowerCase();UA=ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,"unknown",0];mode=UA[1]==="ie"&&document.documentMode;browser={name:UA[1]==="version"?UA[3]:UA[1],version:mode||parseFloat(UA[1]==="opera"&&UA[4]?UA[4]:UA[2]),platform:{name:ua.match(/ip(?:ad|od|hone)/)?"ios":(ua.match(/(?:webos|android)/)||platform.match(/mac|win|linux/)||["other"])[0]}};browser[browser.name]=true;browser[browser.name+parseInt(browser.version,10)]=true;browser.platform[browser.platform.name]=true;module.exports=browser},{}],3:[function(require,module,exports){var EventEmitter,GIF,browser,extend=function(child,parent){for(var key in parent){if(hasProp.call(parent,key))child[key]=parent[key]}function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child},hasProp={}.hasOwnProperty,indexOf=[].indexOf||function(item){for(var i=0,l=this.length;iref;i=0<=ref?++j:--j){results.push(null)}return results}.call(this);numWorkers=this.spawnWorkers();if(this.options.globalPalette===true){this.renderNextFrame()}else{for(i=j=0,ref=numWorkers;0<=ref?jref;i=0<=ref?++j:--j){this.renderNextFrame()}}this.emit("start");return this.emit("progress",0)};GIF.prototype.abort=function(){var worker;while(true){worker=this.activeWorkers.shift();if(worker==null){break}this.log("killing active worker");worker.terminate()}this.running=false;return this.emit("abort")};GIF.prototype.spawnWorkers=function(){var j,numWorkers,ref,results;numWorkers=Math.min(this.options.workers,this.frames.length);(function(){results=[];for(var j=ref=this.freeWorkers.length;ref<=numWorkers?jnumWorkers;ref<=numWorkers?j++:j--){results.push(j)}return results}).apply(this).forEach(function(_this){return function(i){var worker;_this.log("spawning worker "+i);worker=new Worker(_this.options.workerScript);worker.onmessage=function(event){_this.activeWorkers.splice(_this.activeWorkers.indexOf(worker),1);_this.freeWorkers.push(worker);return _this.frameFinished(event.data)};return _this.freeWorkers.push(worker)}}(this));return numWorkers};GIF.prototype.frameFinished=function(frame){var i,j,ref;this.log("frame "+frame.index+" finished - "+this.activeWorkers.length+" active");this.finishedFrames++;this.emit("progress",this.finishedFrames/this.frames.length);this.imageParts[frame.index]=frame;if(this.options.globalPalette===true){this.options.globalPalette=frame.globalPalette;this.log("global palette analyzed");if(this.frames.length>2){for(i=j=1,ref=this.freeWorkers.length;1<=ref?jref;i=1<=ref?++j:--j){this.renderNextFrame()}}}if(indexOf.call(this.imageParts,null)>=0){return this.renderNextFrame()}else{return this.finishRendering()}};GIF.prototype.finishRendering=function(){var data,frame,i,image,j,k,l,len,len1,len2,len3,offset,page,ref,ref1,ref2;len=0;ref=this.imageParts;for(j=0,len1=ref.length;j=this.frames.length){return}frame=this.frames[this.nextFrame++];worker=this.freeWorkers.shift();task=this.getTask(frame);this.log("starting frame "+(task.index+1)+" of "+this.frames.length);this.activeWorkers.push(worker);return worker.postMessage(task)};GIF.prototype.getContextData=function(ctx){return ctx.getImageData(0,0,this.options.width,this.options.height).data};GIF.prototype.getImageData=function(image){var ctx;if(this._canvas==null){this._canvas=document.createElement("canvas");this._canvas.width=this.options.width;this._canvas.height=this.options.height}ctx=this._canvas.getContext("2d");ctx.setFill=this.options.background;ctx.fillRect(0,0,this.options.width,this.options.height);ctx.drawImage(image,0,0);return this.getContextData(ctx)};GIF.prototype.getTask=function(frame){var index,task;index=this.frames.indexOf(frame);task={index:index,last:index===this.frames.length-1,delay:frame.delay,transparent:frame.transparent,width:this.options.width,height:this.options.height,quality:this.options.quality,dither:this.options.dither,globalPalette:this.options.globalPalette,repeat:this.options.repeat,canTransfer:browser.name==="chrome"};if(frame.data!=null){task.data=frame.data}else if(frame.context!=null){task.data=this.getContextData(frame.context)}else if(frame.image!=null){task.data=this.getImageData(frame.image)}else{throw new Error("Invalid frame")}return task};GIF.prototype.log=function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];if(!this.options.debug){return}return console.log.apply(console,args)};return GIF}(EventEmitter);module.exports=GIF},{"./browser.coffee":2,events:1}]},{},[3])(3)}); -//# sourceMappingURL=gif.js.map diff --git a/gifsupport-node/gif.worker.js b/gifsupport-node/gif.worker.js deleted file mode 100644 index 269624e..0000000 --- a/gifsupport-node/gif.worker.js +++ /dev/null @@ -1,3 +0,0 @@ -// gif.worker.js 0.2.0 - https://github.com/jnordberg/gif.js -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o=ByteArray.pageSize)this.newPage();this.pages[this.page][this.cursor++]=val};ByteArray.prototype.writeUTFBytes=function(string){for(var l=string.length,i=0;i=0)this.dispose=disposalCode};GIFEncoder.prototype.setRepeat=function(repeat){this.repeat=repeat};GIFEncoder.prototype.setTransparent=function(color){this.transparent=color};GIFEncoder.prototype.addFrame=function(imageData){this.image=imageData;this.colorTab=this.globalPalette&&this.globalPalette.slice?this.globalPalette:null;this.getImagePixels();this.analyzePixels();if(this.globalPalette===true)this.globalPalette=this.colorTab;if(this.firstFrame){this.writeLSD();this.writePalette();if(this.repeat>=0){this.writeNetscapeExt()}}this.writeGraphicCtrlExt();this.writeImageDesc();if(!this.firstFrame&&!this.globalPalette)this.writePalette();this.writePixels();this.firstFrame=false};GIFEncoder.prototype.finish=function(){this.out.writeByte(59)};GIFEncoder.prototype.setQuality=function(quality){if(quality<1)quality=1;this.sample=quality};GIFEncoder.prototype.setDither=function(dither){if(dither===true)dither="FloydSteinberg";this.dither=dither};GIFEncoder.prototype.setGlobalPalette=function(palette){this.globalPalette=palette};GIFEncoder.prototype.getGlobalPalette=function(){return this.globalPalette&&this.globalPalette.slice&&this.globalPalette.slice(0)||this.globalPalette};GIFEncoder.prototype.writeHeader=function(){this.out.writeUTFBytes("GIF89a")};GIFEncoder.prototype.analyzePixels=function(){if(!this.colorTab){this.neuQuant=new NeuQuant(this.pixels,this.sample);this.neuQuant.buildColormap();this.colorTab=this.neuQuant.getColormap()}if(this.dither){this.ditherPixels(this.dither.replace("-serpentine",""),this.dither.match(/-serpentine/)!==null)}else{this.indexPixels()}this.pixels=null;this.colorDepth=8;this.palSize=7;if(this.transparent!==null){this.transIndex=this.findClosest(this.transparent,true)}};GIFEncoder.prototype.indexPixels=function(imgq){var nPix=this.pixels.length/3;this.indexedPixels=new Uint8Array(nPix);var k=0;for(var j=0;j=0&&x1+x=0&&y1+y>16,(c&65280)>>8,c&255,used)};GIFEncoder.prototype.findClosestRGB=function(r,g,b,used){if(this.colorTab===null)return-1;if(this.neuQuant&&!used){return this.neuQuant.lookupRGB(r,g,b)}var c=b|g<<8|r<<16;var minpos=0;var dmin=256*256*256;var len=this.colorTab.length;for(var i=0,index=0;i=0){disp=dispose&7}disp<<=2;this.out.writeByte(0|disp|0|transp);this.writeShort(this.delay);this.out.writeByte(this.transIndex);this.out.writeByte(0)};GIFEncoder.prototype.writeImageDesc=function(){this.out.writeByte(44);this.writeShort(0);this.writeShort(0);this.writeShort(this.width);this.writeShort(this.height);if(this.firstFrame||this.globalPalette){this.out.writeByte(0)}else{this.out.writeByte(128|0|0|0|this.palSize)}};GIFEncoder.prototype.writeLSD=function(){this.writeShort(this.width);this.writeShort(this.height);this.out.writeByte(128|112|0|this.palSize);this.out.writeByte(0);this.out.writeByte(0)};GIFEncoder.prototype.writeNetscapeExt=function(){this.out.writeByte(33);this.out.writeByte(255);this.out.writeByte(11);this.out.writeUTFBytes("NETSCAPE2.0");this.out.writeByte(3);this.out.writeByte(1);this.writeShort(this.repeat);this.out.writeByte(0)};GIFEncoder.prototype.writePalette=function(){this.out.writeBytes(this.colorTab);var n=3*256-this.colorTab.length;for(var i=0;i>8&255)};GIFEncoder.prototype.writePixels=function(){var enc=new LZWEncoder(this.width,this.height,this.indexedPixels,this.colorDepth);enc.encode(this.out)};GIFEncoder.prototype.stream=function(){return this.out};module.exports=GIFEncoder},{"./LZWEncoder.js":2,"./TypedNeuQuant.js":3}],2:[function(require,module,exports){var EOF=-1;var BITS=12;var HSIZE=5003;var masks=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535];function LZWEncoder(width,height,pixels,colorDepth){var initCodeSize=Math.max(2,colorDepth);var accum=new Uint8Array(256);var htab=new Int32Array(HSIZE);var codetab=new Int32Array(HSIZE);var cur_accum,cur_bits=0;var a_count;var free_ent=0;var maxcode;var clear_flg=false;var g_init_bits,ClearCode,EOFCode;function char_out(c,outs){accum[a_count++]=c;if(a_count>=254)flush_char(outs)}function cl_block(outs){cl_hash(HSIZE);free_ent=ClearCode+2;clear_flg=true;output(ClearCode,outs)}function cl_hash(hsize){for(var i=0;i=0){disp=hsize_reg-i;if(i===0)disp=1;do{if((i-=disp)<0)i+=hsize_reg;if(htab[i]===fcode){ent=codetab[i];continue outer_loop}}while(htab[i]>=0)}output(ent,outs);ent=c;if(free_ent<1<0){outs.writeByte(a_count);outs.writeBytes(accum,0,a_count);a_count=0}}function MAXCODE(n_bits){return(1<0)cur_accum|=code<=8){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}if(free_ent>maxcode||clear_flg){if(clear_flg){maxcode=MAXCODE(n_bits=g_init_bits);clear_flg=false}else{++n_bits;if(n_bits==BITS)maxcode=1<0){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}flush_char(outs)}}this.encode=encode}module.exports=LZWEncoder},{}],3:[function(require,module,exports){var ncycles=100;var netsize=256;var maxnetpos=netsize-1;var netbiasshift=4;var intbiasshift=16;var intbias=1<>betashift;var betagamma=intbias<>3;var radiusbiasshift=6;var radiusbias=1<>3);var i,v;for(i=0;i>=netbiasshift;network[i][1]>>=netbiasshift;network[i][2]>>=netbiasshift;network[i][3]=i}}function altersingle(alpha,i,b,g,r){network[i][0]-=alpha*(network[i][0]-b)/initalpha;network[i][1]-=alpha*(network[i][1]-g)/initalpha;network[i][2]-=alpha*(network[i][2]-r)/initalpha}function alterneigh(radius,i,b,g,r){var lo=Math.abs(i-radius);var hi=Math.min(i+radius,netsize);var j=i+1;var k=i-1;var m=1;var p,a;while(jlo){a=radpower[m++];if(jlo){p=network[k--];p[0]-=a*(p[0]-b)/alpharadbias;p[1]-=a*(p[1]-g)/alpharadbias;p[2]-=a*(p[2]-r)/alpharadbias}}}function contest(b,g,r){var bestd=~(1<<31);var bestbiasd=bestd;var bestpos=-1;var bestbiaspos=bestpos;var i,n,dist,biasdist,betafreq;for(i=0;i>intbiasshift-netbiasshift);if(biasdist>betashift;freq[i]-=betafreq;bias[i]+=betafreq<>1;for(j=previouscol+1;j>1;for(j=previouscol+1;j<256;j++)netindex[j]=maxnetpos}function inxsearch(b,g,r){var a,p,dist;var bestd=1e3;var best=-1;var i=netindex[g];var j=i-1;while(i=0){if(i=bestd)i=netsize;else{i++;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist=0){p=network[j];dist=g-p[1];if(dist>=bestd)j=-1;else{j--;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist>radiusbiasshift;if(rad<=1)rad=0;for(i=0;i=lengthcount)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 ({ - type: res.headers['content-type'], - data: res.body - })); -} - -function createCanvas(width, height) { - const canvas = new Canvas(width, height); - return canvas; -} - -class ImageEx { - constructor(url) { - this.url = url; - this.loaded = loadFromUrl(url).then(result => { - this.type = result.type; - this.data = result.data; - if (this.type === 'image/gif') { - this.initGif(); - } else { - this.initStatic(); - } - return this; - }); - } - - - initGif() { - const reader = new GifReader(new Uint8Array(this.data)); - this.width = reader.width; - this.height = reader.height; - this.frames = this.decodeFrames(reader); - - this.renderAllFrames(); - } - - initStatic() { - const img = new Image(); - img.src = this.data; - return new Promise(resolve => { - img.onload = () => { - this.width = img.width; - this.height = img.height; - this.frames = [{ - actualOffset: 0, - actualDelay: Infinity, - delay: Infinity - }]; - this.spriteSheet = createCanvas(this.width, this.height); - const spriteSheetCtx = this.spriteSheet.getContext('2d'); - spriteSheetCtx.drawImage(img, 0, 0); - resolve(); - }; - }); - } - - decodeFrames(reader) { - const frames = []; - let offset = 0; - for (let i = 0; i < reader.numFrames(); ++i) { - const frameInfo = reader.frameInfo(i); - frameInfo.pixels = new Uint8ClampedArray(reader.width * reader.height * 4); - reader.decodeAndBlitFrameRGBA(i, frameInfo.pixels); - frameInfo.buffer = this.createBufferCanvas(frameInfo, this.width, this.height); - frameInfo.actualOffset = offset; - frameInfo.actualDelay = Math.max(frameInfo.delay * 10, 20); - offset += frameInfo.actualDelay; - frames.push(frameInfo); - } - this.totalDuration = offset; - return frames; - } - - renderAllFrames() { - let disposeFrame = null; - const canvas = createCanvas(this.width, this.height); - const ctx = canvas.getContext('2d'); - let saved; - this.spriteSheet = createCanvas(this.width * this.frames.length, this.height); - const spriteSheetCtx = this.spriteSheet.getContext('2d'); - for (let i = 0; i < this.frames.length; ++i) { - const frame = this.frames[i]; - if (typeof disposeFrame === 'function') disposeFrame(); - - switch (frame.disposal) { - case 2: - disposeFrame = () => ctx.clearRect(0, 0, canvas.width, canvas.height); - break; - case 3: - saved = ctx.getImageData(0, 0, canvas.width, canvas.height); - disposeFrame = () => ctx.putImageData(saved, 0, 0); // eslint-disable-line no-loop-func - break; - default: - this.disposeFrame = null; - } - - // draw current frame - ctx.drawImage(frame.buffer, frame.x, frame.y); - // draw the frame onto the sprite sheet - spriteSheetCtx.drawImage(canvas, this.width * i, 0); - } - } - - createBufferCanvas(frame, width, height) { - const canvas = createCanvas(frame.width, frame.height); - const ctx = canvas.getContext('2d'); - - const imageData = ctx.createImageData(width, height); - imageData.data.set(frame.pixels); - - ctx.putImageData(imageData, -frame.x, -frame.y); - return canvas; - } - - drawFrame(ctx, frameNum, x, y, args = {}) { - const sx = frameNum * this.width + (args.sx || 0); - const sy = args.sy || 0; - const swidth = Math.min(args.swidth || this.width, this.width) - (args.sx || 0); - const sheight = args.sheight || this.height; - - console.log(`Drawing frame ${frameNum} at`); - console.log('sx', sx); - console.log('sy', sy); - console.log('sw', swidth); - console.log('sh', sheight); - console.log('x', x); - console.log('y', y); - console.log('w', args.width); - console.log('h', args.height); - - ctx.drawImage(this.spriteSheet, sx, sy, swidth, sheight, x, y, args.width || swidth, args.height || sheight); - // ctx.drawImage(this.spriteSheet, 0, 0, 112, 112, 0, 0, 112, 112) - } -} -class CanvasEx { - constructor(width, height) { - this.width = width; - this.height = height; - this.frames = []; - this.totalDuration = Infinity; - } - - addFrame(actualDelay, delay) { - if ((actualDelay === undefined || actualDelay === null) - && (delay === undefined || delay === null)) throw new Error('Delay has to be set!'); - const canvas = createCanvas(this.width, this.height); - const frame = { - actualOffset: this.totalDuration, - delay: delay || Math.max(Math.round(actualDelay / 10), 2), - actualDelay: actualDelay || Math.max(delay * 10, 20), - canvas, - ctx: canvas.getContext('2d') - }; - this.totalDuration += delay; - this.frames.push(frame); - } - - drawImage(img, x, y, args = {}) { - console.log('Drawing image ', img); - console.log('At ', x, y, args); - if (img.frames && img.frames.length > 1) { - if (this.frames.length > 1) throw new Error('Cannot render animations onto animated canvases!'); - this.totalDuration = img.totalDuration; - // we are drawing an animated image onto a static one. - // for each frame in the image, create a frame on this one, cloning the original picture (if any), - // render the original on each frame, and draw the frame on top. - for (let i = this.frames.length; i < img.frames.length; ++i) { - const frame = img.frames[i]; - // console.log(`Adding frame ${i}:`, frame); - this.addFrame(null, frame.delay); - if (this.frames.length > 0) { - this.frames[i].ctx.drawImage(this.frames[0].canvas, 0, 0); - } - } - for (let i = 0; i < img.frames.length; ++i) { - // console.log(`Drawing frame ${i}:`, img.frames[i]); - // draw the i-th source frame to the i-th target frame - img.drawFrame(this.frames[i].ctx, i, x, y, args); - } - console.log('Done drawing animated image, CanvasEx is now', this); - } else { - // we are drawing a static image on top of a (possibly animated) image. - // for each frame, just draw, nothing fancy. - if (img.frames) { - // the image cant have more than one frame, and if it has 0, we dont need to do anything at all - if (img.frames.length === 1) { - for (let i = 0; i < this.frames.length; ++i) { - img.drawFrame(this.frames[i].ctx, 0, x, y, args); - } - } - } else { - for (let i = 0; i < this.frames.length; ++i) { - this.drawImage(this.frames[i].ctx, img, x, y, args); - } - } - console.log('Done drawing static image, CanvasEx is now', this); - } - } - - drawFrame(ctx, frameNum, x, y, args = {}) { - // console.log(`Drawing frame ${frameNum} of`, this) - // ctx.drawImage(this.frames[frameNum].canvas, x, y); - this.drawImage(ctx, this.frames[frameNum].canvas, x, y, args); - } - - _drawImage(ctx, img, x, y, args = {}) { - if (args.transform) { - ctx.save(); - _.each(args.transform, (value, property) => { - ctx[property](...value); - }); - } - if (args.sx !== undefined || args.sy !== undefined || args.swidth !== undefined || args.sheight !== undefined) { - ctx.drawImage(img, args.sx, args.sy, args.swidth, args.sheight, x, y, args.width, args.height); - } else { - ctx.drawImage(img, x, y, args.width, args.height); - } - if (args.transform) { - ctx.restore(); - } - } - - export(outStream) { - const gif = new GifEncoder(this.width, this.height); - gif.createReadStream().pipe(outStream); - gif.setTransparent(0); - gif.setRepeat(0); - gif.start(); - for (let i = 0; i < this.frames.length; ++i) { - const frame = this.frames[i]; - gif.setDelay(frame.delay); - gif.addFrame(frame.ctx); - } - gif.finish(); - } -} - -module.exports = { - CanvasEx, - ImageEx -}; -/* -// const img = new ImageEx("https://cors-anywhere.herokuapp.com/https://cdn.betterttv.net/emote/554da1a289d53f2d12781907/3x"); - -const img = new ImageEx('https://cors-anywhere.herokuapp.com/https://cdn.discordapp.com/emojis/393563453824040983.gif'); -const _canvas = document.getElementById('c'); -const _ctx = _canvas.getContext('2d'); - -const img2 = new ImageEx('https://rawgit.com/CBenni/beebot/master/resources/SnowGlobe.png'); - -const _cnv = new CanvasEx(128, 128); -img.loaded.then(() => { - _cnv.drawImage(img, 0, 0); - // img.drawFrame(_ctx, 0, 0, 0) - return img2.loaded; -}).then(() => { - _cnv.drawImage(img2, 0, 0, { width: 128, height: 128 }); -}).then(() => { - console.log('Drawing CanvasEx to screen', _cnv); - _cnv.drawFrame(_ctx, 0, 0, 0); - - console.log('Starting gif render'); - const GifEncoder = new GIF({ - workers: 2, - quality: 10, - transparent: 'rgba(0,0,0,0)' - }); - - for (let i = 0; i < _cnv.frames.length; ++i) { - const frame = _cnv.frames[i]; - console.log(`Rendering frame ${i}`); - GifEncoder.addFrame(frame.canvas, { delay: frame.delay }); - } - - GifEncoder.on('finished', blob => { - console.log('Done rendering!', URL.createObjectURL(blob)); - document.getElementById('i').src = URL.createObjectURL(blob); - }); - - GifEncoder.render(); -}); - -let curFrm = 0; -setInterval(() => { - _ctx.clearRect(0, 0, 200, 200); - _cnv.drawFrame(_ctx, curFrm++ % _cnv.frames.length, 0, 0); -}, 100); - - -/* img2.loaded.then(() => { - _ctx.drawImage(img2.spriteSheet, 0, 100) -}) - -img.loaded.then(()=>_ctx.drawImage(img.spriteSheet,0,0)) */ diff --git a/gifsupport-node/index.html b/gifsupport-node/index.html deleted file mode 100644 index 3e01af9..0000000 --- a/gifsupport-node/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - A Pen by CBenni - - - - - - - - - - - - - - diff --git a/gifsupport-node/index.js b/gifsupport-node/index.js deleted file mode 100644 index 95458fe..0000000 --- a/gifsupport-node/index.js +++ /dev/null @@ -1,286 +0,0 @@ -const _ = require('lodash'); -const express = require('express'); -const { ImageEx, CanvasEx } = require('./imageex'); - -const request = require('request'); -const Discord = require('discord.js'); - -const Canvas = require('canvas'); - -const { ImageEx, CanvasEx } = require("./imageex"); - -const twemoji = require('./twemoji'); - -const app = express(); -const config = require('./config.default.json'); - -try { - _.extend(config, require('./config')); -} catch (err) { - console.log('No config.json found!'); -} - -const cache = {}; - -function all(x, c) { - _.isArray(x) ? _.each(x, c) : c(x); -} - -templates = config.templates; - -for (templateName in templates) { - const data = templates[templateName]; - all(data, template => { - template.image = new Image(); - template.image.src = template.src; - }); -} - -// drawing: we keep the image fixed in its default position and draw the template on top/below it - -// calculates the x or y position of the template to be drawn -// size = width or height of the template/image -// anchor = the corresponding anchor config -function calculatePosition(scale, anchor, imageSize) { - return imageSize * anchor.position / 100 - anchor.offset * scale; -} - -function render(template, img, size, flipH) { - let imgWidth = img.width; - let imgHeight = img.height; - if (size && size.height) { - imgHeight = size.height; - if (!size.width) imgWidth = imgWidth * size.height / img.height; - } - if (size && size.width) { - imgWidth = size.width; - if (!size.height) imgHeight = imgHeight * size.width / img.width; - } - - const xScale = imgWidth / template.anchor.x.size; - const yScale = imgHeight / template.anchor.y.size; - const templateScale = Math.max(0, Math.min(10, Math.max(xScale || 0, yScale || 0))); - console.log('templateScale', templateScale); - - - let templateOffsetX = calculatePosition(templateScale, template.anchor.x, imgWidth); - let templateOffsetY = calculatePosition(templateScale, template.anchor.y, imgHeight); - - let imageOffsetX = 0; - let imageOffsetY = 0; - let resultingWidth = imgWidth; // start with the image boundaries as defined by the image - let resultingHeight = imgHeight; - - if (templateOffsetX < 0) { - resultingWidth -= templateOffsetX; - imageOffsetX = -templateOffsetX; - templateOffsetX = 0; - } - if (templateOffsetY < 0) { - resultingHeight -= templateOffsetY; - imageOffsetY = -templateOffsetY; - templateOffsetY = 0; - } - if (templateOffsetX + template.image.width * templateScale > resultingWidth) { - resultingWidth = templateOffsetX + template.image.width * templateScale; - } - if (templateOffsetY + template.image.height * templateScale > resultingHeight) { - resultingHeight = templateOffsetY + template.image.height * templateScale; - } - - const toDraw = [{ - z: 1, - image: img, - x: flipH ? resultingWidth - imageOffsetX - imgWidth : imageOffsetX, - y: imageOffsetY, - h: imgHeight, - w: imgWidth, - name: 'image' - }, { - z: template.z || 0, - image: template.image, - x: templateOffsetX, - y: templateOffsetY, - h: template.image.height * templateScale, - w: template.image.width * templateScale, - name: `template ${template.src}`, - flipH - }].sort((u, v) => u.z > v.z); - - const canvas = new Canvas(resultingWidth, resultingHeight); - const ctx = canvas.getContext('2d'); - - for (let i = 0; i < toDraw.length; ++i) { - const subject = toDraw[i]; - console.log(`Drawing ${subject.name}${subject.flipH ? ' (flipped)' : ''}`); - try { - if (subject.flipH) { - ctx.save(); - ctx.translate(resultingWidth, 0); - ctx.scale(-1, 1); - } - ctx.drawImage(subject.image, subject.x, subject.y, subject.w, subject.h); - if (subject.flipH) { - ctx.restore(); - } - } catch (err) { - console.error(err); - throw new Error(JSON.stringify({ status: 400, error: 'Invalid template' })); - } - } - - // return the image and cache it - return (canvas); -} - -app.get('/:templateName/', async (req, res) => { - if (!templates[req.params.templateName]) return res.status(404).end(); - try { - const canvas = render(templates[req.params.templateName], await loadImage(req.query.url)); - console.log(canvas); - res.setHeader('Content-Type', 'image/png'); - return canvas.pngStream().pipe(res); - } catch (err) { - console.log(err); - return res.status(400).end(err.message); - } -}); - -app.listen(3002, () => { - console.log('Beebot app listening on port 3002!'); -}); - - -// Discord stuff - - -const client = new Discord.Client({ - autoReconnect: true -}); -// manage roles permission is required -const invitelink = `https://discordapp.com/oauth2/authorize?client_id=${ - config.discord.client_id}&scope=bot&permissions=0`; -const authlink = `https://discordapp.com/oauth2/authorize?client_id=${ - config.discord.client_id}&scope=email`; -console.log(`Bot invite link: ${invitelink}`); - -client.login(config.discord.token).catch(error => { - if (error) { - console.error("Couldn't login: ", error.toString()); - } -}); - -function findEmoji(str) { - const discordEmote = /<:(\w+):(\d+)>/g.exec(str); - if (discordEmote) { - return { - name: discordEmote[1], - id: discordEmote[2], - url: `https://cdn.discordapp.com/emojis/${discordEmote[2]}.png` - }; - } - - let unicodeEmoji; - twemoji.parse(str, (name, emoji) => { - if (unicodeEmoji) return false; - unicodeEmoji = { - name, - id: name, - url: `${emoji.base + emoji.size}/${name}${emoji.ext}` - }; - return false; - }); - return unicodeEmoji; -} - -function loadImage(url) { - return new Promise((resolve, reject) => { - console.log(`Getting ${url}`); - if (url) { - request.get({ url, encoding: null }, (e, r, data) => { - if (e) { - return reject({ status: (r && r.statusCode || 500), error: e }); - } - const img = new Image(); - img.src = data; - resolve(img); - }); - } - }); -} -function reverseString(str) { - return str.split('').reverse().join(''); -} - -const commands = Object.keys(templates).map(x => `/${x}`).join(', '); -const otherCommands = { - invite: `Invite link: <${invitelink}>`, - help: `Available commands: ${commands}.\nUse \\\\ to flip the template horizontally.\nInvite link: <${invitelink}>`, - beebot: `Available commands: ${commands}.\nUse \\\\ to flip the template horizontally.\nInvite link: <${invitelink}>` -}; - - -client.on('message', async message => { - console.log(`[${message.guild.name} - ${message.channel.name}] ${message.author.username}#${message.author.discriminator}: ${message.cleanContent}`); - - let commandParsed = /^([\/\\])(\w+)\b/.exec(message.cleanContent); - if (commandParsed) { - const [, direction, command] = commandParsed; - if (otherCommands[command]) { - const text = otherCommands[command]; - message.channel.send(direction === '\\' ? reverseString(text) : text); - return; - } - } - - const messageSplit = message.cleanContent.split(' '); - const emoji = findEmoji(message.cleanContent); - let result = null; - let count = 0; - try { - if (emoji) { - let name = emoji.name; - for (let i = 0; i < messageSplit.length && count < 4; ++i) { - commandParsed = /^([\/\\])(\w+)\b/.exec(messageSplit[i]); - if (commandParsed) { - const [, direction, command] = commandParsed; - console.log('Got command ', direction, command, direction === '\\' ? 'flipped' : 'not flipped'); - if (templates[command]) { - count++; - name += command; - if (result === null) result = await loadImage(emoji.url); - const templateData = templates[command]; - all(templateData, template => { - result = render(template, result, null, direction === '\\'); - }); - } - } else if (i === 0) return; - } - if (result) { - const messageOptions = { - files: [ - { attachment: result.toBuffer(), name: `${name}.png` } - ] - }; - message.channel.send('', messageOptions); - } - } - } catch (err) { - console.error(err); - } -}); - - -app.get('/', (req, res) => { - const img = new ImageEx(req.query.url || 'https://cdn.discordapp.com/emojis/393563453824040983.gif'); - img.loaded.then(() => { - res.setHeader('Content-Type', 'image/gif'); - const canvas = new CanvasEx(img.width, img.height); - canvas.drawImage(img, 0, 0); - canvas.export(res); - }); -}); - -app.listen(3002, () => { - console.log('Beebot app listening on port 3002!'); -}); diff --git a/gifsupport-node/license.txt b/gifsupport-node/license.txt deleted file mode 100644 index be92a99..0000000 --- a/gifsupport-node/license.txt +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/gifsupport/README.txt b/gifsupport/README.txt deleted file mode 100644 index 6aec385..0000000 --- a/gifsupport/README.txt +++ /dev/null @@ -1,3 +0,0 @@ -A Pen created at CodePen.io. You can find this one at https://codepen.io/CBenni/pen/YYZKeO. - - \ No newline at end of file diff --git a/gifsupport/gif.js b/gifsupport/gif.js deleted file mode 100644 index 2e4d204..0000000 --- a/gifsupport/gif.js +++ /dev/null @@ -1,3 +0,0 @@ -// gif.js 0.2.0 - https://github.com/jnordberg/gif.js -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.GIF=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0&&this._events[type].length>m){this._events[type].warned=true;console.error("(node) warning: possible EventEmitter memory "+"leak detected. %d listeners added. "+"Use emitter.setMaxListeners() to increase limit.",this._events[type].length);if(typeof console.trace==="function"){console.trace()}}}return this};EventEmitter.prototype.on=EventEmitter.prototype.addListener;EventEmitter.prototype.once=function(type,listener){if(!isFunction(listener))throw TypeError("listener must be a function");var fired=false;function g(){this.removeListener(type,g);if(!fired){fired=true;listener.apply(this,arguments)}}g.listener=listener;this.on(type,g);return this};EventEmitter.prototype.removeListener=function(type,listener){var list,position,length,i;if(!isFunction(listener))throw TypeError("listener must be a function");if(!this._events||!this._events[type])return this;list=this._events[type];length=list.length;position=-1;if(list===listener||isFunction(list.listener)&&list.listener===listener){delete this._events[type];if(this._events.removeListener)this.emit("removeListener",type,listener)}else if(isObject(list)){for(i=length;i-- >0;){if(list[i]===listener||list[i].listener&&list[i].listener===listener){position=i;break}}if(position<0)return this;if(list.length===1){list.length=0;delete this._events[type]}else{list.splice(position,1)}if(this._events.removeListener)this.emit("removeListener",type,listener)}return this};EventEmitter.prototype.removeAllListeners=function(type){var key,listeners;if(!this._events)return this;if(!this._events.removeListener){if(arguments.length===0)this._events={};else if(this._events[type])delete this._events[type];return this}if(arguments.length===0){for(key in this._events){if(key==="removeListener")continue;this.removeAllListeners(key)}this.removeAllListeners("removeListener");this._events={};return this}listeners=this._events[type];if(isFunction(listeners)){this.removeListener(type,listeners)}else if(listeners){while(listeners.length)this.removeListener(type,listeners[listeners.length-1])}delete this._events[type];return this};EventEmitter.prototype.listeners=function(type){var ret;if(!this._events||!this._events[type])ret=[];else if(isFunction(this._events[type]))ret=[this._events[type]];else ret=this._events[type].slice();return ret};EventEmitter.prototype.listenerCount=function(type){if(this._events){var evlistener=this._events[type];if(isFunction(evlistener))return 1;else if(evlistener)return evlistener.length}return 0};EventEmitter.listenerCount=function(emitter,type){return emitter.listenerCount(type)};function isFunction(arg){return typeof arg==="function"}function isNumber(arg){return typeof arg==="number"}function isObject(arg){return typeof arg==="object"&&arg!==null}function isUndefined(arg){return arg===void 0}},{}],2:[function(require,module,exports){var UA,browser,mode,platform,ua;ua=navigator.userAgent.toLowerCase();platform=navigator.platform.toLowerCase();UA=ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,"unknown",0];mode=UA[1]==="ie"&&document.documentMode;browser={name:UA[1]==="version"?UA[3]:UA[1],version:mode||parseFloat(UA[1]==="opera"&&UA[4]?UA[4]:UA[2]),platform:{name:ua.match(/ip(?:ad|od|hone)/)?"ios":(ua.match(/(?:webos|android)/)||platform.match(/mac|win|linux/)||["other"])[0]}};browser[browser.name]=true;browser[browser.name+parseInt(browser.version,10)]=true;browser.platform[browser.platform.name]=true;module.exports=browser},{}],3:[function(require,module,exports){var EventEmitter,GIF,browser,extend=function(child,parent){for(var key in parent){if(hasProp.call(parent,key))child[key]=parent[key]}function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child},hasProp={}.hasOwnProperty,indexOf=[].indexOf||function(item){for(var i=0,l=this.length;iref;i=0<=ref?++j:--j){results.push(null)}return results}.call(this);numWorkers=this.spawnWorkers();if(this.options.globalPalette===true){this.renderNextFrame()}else{for(i=j=0,ref=numWorkers;0<=ref?jref;i=0<=ref?++j:--j){this.renderNextFrame()}}this.emit("start");return this.emit("progress",0)};GIF.prototype.abort=function(){var worker;while(true){worker=this.activeWorkers.shift();if(worker==null){break}this.log("killing active worker");worker.terminate()}this.running=false;return this.emit("abort")};GIF.prototype.spawnWorkers=function(){var j,numWorkers,ref,results;numWorkers=Math.min(this.options.workers,this.frames.length);(function(){results=[];for(var j=ref=this.freeWorkers.length;ref<=numWorkers?jnumWorkers;ref<=numWorkers?j++:j--){results.push(j)}return results}).apply(this).forEach(function(_this){return function(i){var worker;_this.log("spawning worker "+i);worker=new Worker(_this.options.workerScript);worker.onmessage=function(event){_this.activeWorkers.splice(_this.activeWorkers.indexOf(worker),1);_this.freeWorkers.push(worker);return _this.frameFinished(event.data)};return _this.freeWorkers.push(worker)}}(this));return numWorkers};GIF.prototype.frameFinished=function(frame){var i,j,ref;this.log("frame "+frame.index+" finished - "+this.activeWorkers.length+" active");this.finishedFrames++;this.emit("progress",this.finishedFrames/this.frames.length);this.imageParts[frame.index]=frame;if(this.options.globalPalette===true){this.options.globalPalette=frame.globalPalette;this.log("global palette analyzed");if(this.frames.length>2){for(i=j=1,ref=this.freeWorkers.length;1<=ref?jref;i=1<=ref?++j:--j){this.renderNextFrame()}}}if(indexOf.call(this.imageParts,null)>=0){return this.renderNextFrame()}else{return this.finishRendering()}};GIF.prototype.finishRendering=function(){var data,frame,i,image,j,k,l,len,len1,len2,len3,offset,page,ref,ref1,ref2;len=0;ref=this.imageParts;for(j=0,len1=ref.length;j=this.frames.length){return}frame=this.frames[this.nextFrame++];worker=this.freeWorkers.shift();task=this.getTask(frame);this.log("starting frame "+(task.index+1)+" of "+this.frames.length);this.activeWorkers.push(worker);return worker.postMessage(task)};GIF.prototype.getContextData=function(ctx){return ctx.getImageData(0,0,this.options.width,this.options.height).data};GIF.prototype.getImageData=function(image){var ctx;if(this._canvas==null){this._canvas=document.createElement("canvas");this._canvas.width=this.options.width;this._canvas.height=this.options.height}ctx=this._canvas.getContext("2d");ctx.setFill=this.options.background;ctx.fillRect(0,0,this.options.width,this.options.height);ctx.drawImage(image,0,0);return this.getContextData(ctx)};GIF.prototype.getTask=function(frame){var index,task;index=this.frames.indexOf(frame);task={index:index,last:index===this.frames.length-1,delay:frame.delay,transparent:frame.transparent,width:this.options.width,height:this.options.height,quality:this.options.quality,dither:this.options.dither,globalPalette:this.options.globalPalette,repeat:this.options.repeat,canTransfer:browser.name==="chrome"};if(frame.data!=null){task.data=frame.data}else if(frame.context!=null){task.data=this.getContextData(frame.context)}else if(frame.image!=null){task.data=this.getImageData(frame.image)}else{throw new Error("Invalid frame")}return task};GIF.prototype.log=function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];if(!this.options.debug){return}return console.log.apply(console,args)};return GIF}(EventEmitter);module.exports=GIF},{"./browser.coffee":2,events:1}]},{},[3])(3)}); -//# sourceMappingURL=gif.js.map diff --git a/gifsupport/gif.worker.js b/gifsupport/gif.worker.js deleted file mode 100644 index 269624e..0000000 --- a/gifsupport/gif.worker.js +++ /dev/null @@ -1,3 +0,0 @@ -// gif.worker.js 0.2.0 - https://github.com/jnordberg/gif.js -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o=ByteArray.pageSize)this.newPage();this.pages[this.page][this.cursor++]=val};ByteArray.prototype.writeUTFBytes=function(string){for(var l=string.length,i=0;i=0)this.dispose=disposalCode};GIFEncoder.prototype.setRepeat=function(repeat){this.repeat=repeat};GIFEncoder.prototype.setTransparent=function(color){this.transparent=color};GIFEncoder.prototype.addFrame=function(imageData){this.image=imageData;this.colorTab=this.globalPalette&&this.globalPalette.slice?this.globalPalette:null;this.getImagePixels();this.analyzePixels();if(this.globalPalette===true)this.globalPalette=this.colorTab;if(this.firstFrame){this.writeLSD();this.writePalette();if(this.repeat>=0){this.writeNetscapeExt()}}this.writeGraphicCtrlExt();this.writeImageDesc();if(!this.firstFrame&&!this.globalPalette)this.writePalette();this.writePixels();this.firstFrame=false};GIFEncoder.prototype.finish=function(){this.out.writeByte(59)};GIFEncoder.prototype.setQuality=function(quality){if(quality<1)quality=1;this.sample=quality};GIFEncoder.prototype.setDither=function(dither){if(dither===true)dither="FloydSteinberg";this.dither=dither};GIFEncoder.prototype.setGlobalPalette=function(palette){this.globalPalette=palette};GIFEncoder.prototype.getGlobalPalette=function(){return this.globalPalette&&this.globalPalette.slice&&this.globalPalette.slice(0)||this.globalPalette};GIFEncoder.prototype.writeHeader=function(){this.out.writeUTFBytes("GIF89a")};GIFEncoder.prototype.analyzePixels=function(){if(!this.colorTab){this.neuQuant=new NeuQuant(this.pixels,this.sample);this.neuQuant.buildColormap();this.colorTab=this.neuQuant.getColormap()}if(this.dither){this.ditherPixels(this.dither.replace("-serpentine",""),this.dither.match(/-serpentine/)!==null)}else{this.indexPixels()}this.pixels=null;this.colorDepth=8;this.palSize=7;if(this.transparent!==null){this.transIndex=this.findClosest(this.transparent,true)}};GIFEncoder.prototype.indexPixels=function(imgq){var nPix=this.pixels.length/3;this.indexedPixels=new Uint8Array(nPix);var k=0;for(var j=0;j=0&&x1+x=0&&y1+y>16,(c&65280)>>8,c&255,used)};GIFEncoder.prototype.findClosestRGB=function(r,g,b,used){if(this.colorTab===null)return-1;if(this.neuQuant&&!used){return this.neuQuant.lookupRGB(r,g,b)}var c=b|g<<8|r<<16;var minpos=0;var dmin=256*256*256;var len=this.colorTab.length;for(var i=0,index=0;i=0){disp=dispose&7}disp<<=2;this.out.writeByte(0|disp|0|transp);this.writeShort(this.delay);this.out.writeByte(this.transIndex);this.out.writeByte(0)};GIFEncoder.prototype.writeImageDesc=function(){this.out.writeByte(44);this.writeShort(0);this.writeShort(0);this.writeShort(this.width);this.writeShort(this.height);if(this.firstFrame||this.globalPalette){this.out.writeByte(0)}else{this.out.writeByte(128|0|0|0|this.palSize)}};GIFEncoder.prototype.writeLSD=function(){this.writeShort(this.width);this.writeShort(this.height);this.out.writeByte(128|112|0|this.palSize);this.out.writeByte(0);this.out.writeByte(0)};GIFEncoder.prototype.writeNetscapeExt=function(){this.out.writeByte(33);this.out.writeByte(255);this.out.writeByte(11);this.out.writeUTFBytes("NETSCAPE2.0");this.out.writeByte(3);this.out.writeByte(1);this.writeShort(this.repeat);this.out.writeByte(0)};GIFEncoder.prototype.writePalette=function(){this.out.writeBytes(this.colorTab);var n=3*256-this.colorTab.length;for(var i=0;i>8&255)};GIFEncoder.prototype.writePixels=function(){var enc=new LZWEncoder(this.width,this.height,this.indexedPixels,this.colorDepth);enc.encode(this.out)};GIFEncoder.prototype.stream=function(){return this.out};module.exports=GIFEncoder},{"./LZWEncoder.js":2,"./TypedNeuQuant.js":3}],2:[function(require,module,exports){var EOF=-1;var BITS=12;var HSIZE=5003;var masks=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535];function LZWEncoder(width,height,pixels,colorDepth){var initCodeSize=Math.max(2,colorDepth);var accum=new Uint8Array(256);var htab=new Int32Array(HSIZE);var codetab=new Int32Array(HSIZE);var cur_accum,cur_bits=0;var a_count;var free_ent=0;var maxcode;var clear_flg=false;var g_init_bits,ClearCode,EOFCode;function char_out(c,outs){accum[a_count++]=c;if(a_count>=254)flush_char(outs)}function cl_block(outs){cl_hash(HSIZE);free_ent=ClearCode+2;clear_flg=true;output(ClearCode,outs)}function cl_hash(hsize){for(var i=0;i=0){disp=hsize_reg-i;if(i===0)disp=1;do{if((i-=disp)<0)i+=hsize_reg;if(htab[i]===fcode){ent=codetab[i];continue outer_loop}}while(htab[i]>=0)}output(ent,outs);ent=c;if(free_ent<1<0){outs.writeByte(a_count);outs.writeBytes(accum,0,a_count);a_count=0}}function MAXCODE(n_bits){return(1<0)cur_accum|=code<=8){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}if(free_ent>maxcode||clear_flg){if(clear_flg){maxcode=MAXCODE(n_bits=g_init_bits);clear_flg=false}else{++n_bits;if(n_bits==BITS)maxcode=1<0){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}flush_char(outs)}}this.encode=encode}module.exports=LZWEncoder},{}],3:[function(require,module,exports){var ncycles=100;var netsize=256;var maxnetpos=netsize-1;var netbiasshift=4;var intbiasshift=16;var intbias=1<>betashift;var betagamma=intbias<>3;var radiusbiasshift=6;var radiusbias=1<>3);var i,v;for(i=0;i>=netbiasshift;network[i][1]>>=netbiasshift;network[i][2]>>=netbiasshift;network[i][3]=i}}function altersingle(alpha,i,b,g,r){network[i][0]-=alpha*(network[i][0]-b)/initalpha;network[i][1]-=alpha*(network[i][1]-g)/initalpha;network[i][2]-=alpha*(network[i][2]-r)/initalpha}function alterneigh(radius,i,b,g,r){var lo=Math.abs(i-radius);var hi=Math.min(i+radius,netsize);var j=i+1;var k=i-1;var m=1;var p,a;while(jlo){a=radpower[m++];if(jlo){p=network[k--];p[0]-=a*(p[0]-b)/alpharadbias;p[1]-=a*(p[1]-g)/alpharadbias;p[2]-=a*(p[2]-r)/alpharadbias}}}function contest(b,g,r){var bestd=~(1<<31);var bestbiasd=bestd;var bestpos=-1;var bestbiaspos=bestpos;var i,n,dist,biasdist,betafreq;for(i=0;i>intbiasshift-netbiasshift);if(biasdist>betashift;freq[i]-=betafreq;bias[i]+=betafreq<>1;for(j=previouscol+1;j>1;for(j=previouscol+1;j<256;j++)netindex[j]=maxnetpos}function inxsearch(b,g,r){var a,p,dist;var bestd=1e3;var best=-1;var i=netindex[g];var j=i-1;while(i=0){if(i=bestd)i=netsize;else{i++;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist=0){p=network[j];dist=g-p[1];if(dist>=bestd)j=-1;else{j--;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist>radiusbiasshift;if(rad<=1)rad=0;for(i=0;i=lengthcount)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 - - - - - A Pen by CBenni - - - - - - - - - - - - - - diff --git a/gifsupport/index.js b/gifsupport/index.js deleted file mode 100644 index 00d888b..0000000 --- a/gifsupport/index.js +++ /dev/null @@ -1,309 +0,0 @@ -function loadFromUrl(url) { - return new Promise(((resolve, reject) => { - const xhr = new XMLHttpRequest(); - xhr.open('GET', url); - xhr.responseType = 'arraybuffer'; - xhr.onload = function () { - if (this.status >= 200 && this.status < 300) { - resolve({ - type: xhr.getResponseHeader('Content-Type'), - data: xhr.response - }); - } else { - reject({ - status: this.status, - statusText: xhr.statusText - }); - } - }; - xhr.onerror = function () { - reject({ - status: this.status, - statusText: xhr.statusText - }); - }; - xhr.send(); - })); -} - -function _arrayBufferToBase64(buffer) { - let binary = ''; - const bytes = new Uint8Array(buffer); - const len = bytes.byteLength; - for (let i = 0; i < len; i++) { - binary += String.fromCharCode(bytes[i]); - } - return window.btoa(binary); -} - -function createCanvas(width, height) { - const canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - return canvas; -} - -class ImageEx { - constructor(url) { - this.url = url; - this.loaded = loadFromUrl(url).then(result => { - this.type = result.type; - this.data = result.data; - if (this.type === 'image/gif') { - return this.initGif(); - } - return this.initStatic(); - }); - } - - - initGif() { - const reader = new GifReader(new Uint8Array(this.data)); - this.width = reader.width; - this.height = reader.height; - this.frames = this.decodeFrames(reader); - - this.renderAllFrames(); - } - - initStatic() { - // todo: in node, we wanna use this.data - const img = new Image(); - const arrayBufferView = new Uint8Array(this.data); - const blob = new Blob([arrayBufferView], { type: this.type }); - const urlCreator = window.URL || window.webkitURL; - const imageUrl = urlCreator.createObjectURL(blob); - img.src = imageUrl; - return new Promise(resolve => { - img.onload = () => { - this.width = img.width; - this.height = img.height; - this.frames = [{ - actualOffset: 0, - actualDelay: Infinity, - delay: Infinity - }]; - this.spriteSheet = createCanvas(this.width, this.height); - const spriteSheetCtx = this.spriteSheet.getContext('2d'); - spriteSheetCtx.drawImage(img, 0, 0); - resolve(); - }; - }); - } - - decodeFrames(reader) { - const frames = []; - let offset = 0; - for (let i = 0; i < reader.numFrames(); ++i) { - const frameInfo = reader.frameInfo(i); - frameInfo.pixels = new Uint8ClampedArray(reader.width * reader.height * 4); - reader.decodeAndBlitFrameRGBA(i, frameInfo.pixels); - frameInfo.buffer = this.createBufferCanvas(frameInfo, this.width, this.height); - frameInfo.actualOffset = offset; - frameInfo.actualDelay = Math.max(frameInfo.delay * 10, 20); - offset += frameInfo.actualDelay; - frames.push(frameInfo); - } - this.totalDuration = offset; - return frames; - } - - renderAllFrames() { - let disposeFrame = null; - const canvas = createCanvas(this.width, this.height); - const ctx = canvas.getContext('2d'); - let saved; - this.spriteSheet = createCanvas(this.width * this.frames.length, this.height); - const spriteSheetCtx = this.spriteSheet.getContext('2d'); - for (let i = 0; i < this.frames.length; ++i) { - const frame = this.frames[i]; - if (typeof disposeFrame === 'function') disposeFrame(); - - switch (frame.disposal) { - case 2: - disposeFrame = () => ctx.clearRect(0, 0, canvas.width, canvas.height); - break; - case 3: - saved = ctx.getImageData(0, 0, canvas.width, canvas.height); - disposeFrame = () => ctx.putImageData(saved, 0, 0); - break; - default: - this.disposeFrame = null; - } - - // draw current frame - ctx.drawImage(frame.buffer, frame.x, frame.y); - // draw the frame onto the sprite sheet - spriteSheetCtx.drawImage(canvas, this.width * i, 0); - } - } - - createBufferCanvas(frame, width, height) { - const canvas = createCanvas(frame.width, frame.height); - const ctx = canvas.getContext('2d'); - - const imageData = ctx.createImageData(width, height); - imageData.data.set(frame.pixels); - - ctx.putImageData(imageData, -frame.x, -frame.y); - return canvas; - } - - drawFrame(ctx, frameNum, x, y, args = {}) { - console.log('this', this); - const sx = frameNum * this.width + (args.sx || 0); - const sy = args.sy || 0; - const swidth = Math.min(args.swidth || this.width, this.width) - (args.sx || 0); - const sheight = args.sheight || this.height; - - console.log(`Drawing frame ${frameNum} at`); - console.log('sx', sx); - console.log('sy', sy); - console.log('sw', swidth); - console.log('sh', sheight); - console.log('x', x); - console.log('y', y); - console.log('w', args.width); - console.log('h', args.height); - - ctx.drawImage(this.spriteSheet, sx, sy, swidth, sheight, x, y, args.width || swidth, args.height || sheight); - // ctx.drawImage(this.spriteSheet, 0, 0, 112, 112, 0, 0, 112, 112) - } -} -class CanvasEx { - constructor(width, height) { - this.width = width; - this.height = height; - this.frames = []; - this.totalDuration = Infinity; - } - - addFrame(actualDelay, delay) { - if ((actualDelay === undefined || actualDelay === null) - && (delay === undefined || delay === null)) throw new Error('Delay has to be set!'); - const canvas = createCanvas(this.width, this.height); - const frame = { - actualOffset: this.totalDuration, - delay: delay || Math.max(Math.round(actualDelay / 10), 2), - actualDelay: actualDelay || Math.max(delay * 10, 20), - canvas, - ctx: canvas.getContext('2d') - }; - this.totalDuration += delay; - this.frames.push(frame); - } - - drawImage(img, x, y, args = {}) { - let { - sx, sy, swidth, sheight, width, height, beginAt = 0, endAt = Infinity - } = args; - console.log('Drawing image ', img); - console.log('At ', x, y, args); - beginAt = Math.max(0, beginAt); - endAt = Math.min(Math.max(img.totalDuration || 0, this.totalDuration), endAt); - if (img.frames && img.frames.length > 1) { - if (this.frames.length > 1) throw new Error('Cannot render animations onto animated canvases!'); - this.totalDuration = img.totalDuration; - // we are drawing an animated image onto a static one. - // for each frame in the image, create a frame on this one, cloning the original picture (if any), - // render the original on each frame, and draw the frame on top. - for (let i = this.frames.length; i < img.frames.length; ++i) { - const frame = img.frames[i]; - console.log(`Adding frame ${i}:`, frame); - this.addFrame(null, frame.delay); - if (this.frames.length > 0) { - this.frames[i].ctx.drawImage(this.frames[0].canvas, 0, 0); - } - } - for (let i = 0; i < img.frames.length; ++i) { - console.log(`Drawing frame ${i}:`, img.frames[i]); - // draw the i-th source frame to the i-th target frame - img.drawFrame(this.frames[i].ctx, i, x, y, args); - } - console.log('Done drawing animated image, CanvasEx is now', this); - } else { - // we are drawing a static image on top of a (possibly animated) image. - // for each frame, just draw, nothing fancy. - if (img.frames) { - // the image cant have more than one frame, and if it has 0, we dont need to do anything at all - if (img.frames.length === 1) { - for (let i = 0; i < this.frames.length; ++i) { - img.drawFrame(this.frames[i].ctx, 0, x, y, args); - } - } - } else { - for (let i = 0; i < this.frames.length; ++i) { - this.drawImage(this.frames[i].ctx, img, x, y, args); - } - } - console.log('Done drawing static image, CanvasEx is now', this); - } - } - - drawFrame(ctx, frameNum, x, y, args = {}) { - // console.log(`Drawing frame ${frameNum} of`, this) - ctx.drawImage(this.frames[frameNum].canvas, x, y); - // this.drawImage(ctx, this.frames[frameNum].canvas, x,y,args); - } - - _drawImage(ctx, img, x, y, args = {}) { - if (args.sx !== undefined || args.sy !== undefined || args.swidth !== undefined || args.sheight !== undefined) { - ctx.drawImage(img, args.sx, args.sy, args.swidth, args.sheight, x, y, args.width, args.height); - } else { - ctx.drawImage(img, x, y, args.width, args.height); - } - } -} - -// const img = new ImageEx("https://cors-anywhere.herokuapp.com/https://cdn.betterttv.net/emote/554da1a289d53f2d12781907/3x"); - -const img = new ImageEx('https://cors-anywhere.herokuapp.com/https://cdn.discordapp.com/emojis/393563453824040983.gif'); -const _canvas = document.getElementById('c'); -const _ctx = _canvas.getContext('2d'); - -const img2 = new ImageEx('https://rawgit.com/CBenni/beebot/master/resources/SnowGlobe.png'); - -const _cnv = new CanvasEx(128, 128); -img.loaded.then(() => { - _cnv.drawImage(img, 0, 0); - // img.drawFrame(_ctx, 0, 0, 0) - return img2.loaded; -}).then(() => { - _cnv.drawImage(img2, 0, 0, { width: 128, height: 128 }); -}).then(() => { - console.log('Drawing CanvasEx to screen', _cnv); - _cnv.drawFrame(_ctx, 0, 0, 0); - - console.log('Starting gif render'); - const GifEncoder = new GIF({ - workers: 2, - quality: 10, - transparent: 'rgba(0,0,0,0)' - }); - - for (let i = 0; i < _cnv.frames.length; ++i) { - const frame = _cnv.frames[i]; - console.log(`Rendering frame ${i}`); - GifEncoder.addFrame(frame.canvas, { delay: frame.delay }); - } - - GifEncoder.on('finished', blob => { - console.log('Done rendering!', URL.createObjectURL(blob)); - document.getElementById('i').src = URL.createObjectURL(blob); - }); - - GifEncoder.render(); -}); - -let curFrm = 0; -setInterval(() => { - _ctx.clearRect(0, 0, 200, 200); - _cnv.drawFrame(_ctx, curFrm++ % _cnv.frames.length, 0, 0); -}, 100); - - -/* img2.loaded.then(() => { - _ctx.drawImage(img2.spriteSheet, 0, 100) -}) - -img.loaded.then(()=>_ctx.drawImage(img.spriteSheet,0,0)) */ diff --git a/gifsupport/license.txt b/gifsupport/license.txt deleted file mode 100644 index be92a99..0000000 --- a/gifsupport/license.txt +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/imageex.js b/imageex.js index 6785619..e550f96 100644 --- a/imageex.js +++ b/imageex.js @@ -1,15 +1,29 @@ +const fs = require('fs'); const got = require('got'); const Canvas = require('canvas'); +const streamBuffers = require('stream-buffers'); +const mime = require('mime-types'); const { GifReader } = require('omggif'); const GifEncoder = require('gifencoder'); -const Image = { Canvas }; +const { Image } = Canvas; -function loadFromUrl(url) { - return got(url, { encoding: null }).then(res => ({ - type: res.headers['content-type'], - data: res.body - })); +function loadFromUri(uri) { + if (uri.startsWith('http')) { + return got(uri, { encoding: null }).then(res => ({ + type: res.headers['content-type'], + data: res.body + })); + } + return new Promise((resolve, reject) => { + fs.readFile(uri, (err, data) => { + if (err) reject(err); + resolve({ + type: mime.lookup(uri), + data + }); + }); + }); } function createCanvas(width, height) { @@ -18,15 +32,18 @@ function createCanvas(width, height) { } class ImageEx { - constructor(url) { - this.url = url; - this.loaded = loadFromUrl(url).then(result => { + constructor(uri) { + this.uri = uri; + this.loaded = loadFromUri(uri).then(result => { this.type = result.type; this.data = result.data; if (this.type === 'image/gif') { - return this.initGif(); + console.log(uri, 'loaded'); + this.initGif(); + } else { + this.initStatic(); } - return this.initStatic(); + return this; }); } @@ -38,26 +55,24 @@ class ImageEx { this.frames = this.decodeFrames(reader); this.renderAllFrames(); + + return this; } initStatic() { const img = new Image(); img.src = this.data; - return new Promise(resolve => { - img.onload = () => { - this.width = img.width; - this.height = img.height; - this.frames = [{ - actualOffset: 0, - actualDelay: Infinity, - delay: Infinity - }]; - this.spriteSheet = createCanvas(this.width, this.height); - const spriteSheetCtx = this.spriteSheet.getContext('2d'); - spriteSheetCtx.drawImage(img, 0, 0); - resolve(); - }; - }); + + this.width = img.width; + this.height = img.height; + this.frames = [{ + actualOffset: 0, + actualDelay: Infinity, + delay: Infinity + }]; + this.spriteSheet = createCanvas(this.width, this.height); + const spriteSheetCtx = this.spriteSheet.getContext('2d'); + spriteSheetCtx.drawImage(img, 0, 0); } decodeFrames(reader) { @@ -148,7 +163,7 @@ class CanvasEx { addFrame(actualDelay, delay) { if ((actualDelay === undefined || actualDelay === null) - && (delay === undefined || delay === null)) throw new Error('Delay has to be set!'); + && (delay === undefined || delay === null)) throw new Error('Delay has to be set!'); const canvas = createCanvas(this.width, this.height); const frame = { actualOffset: this.totalDuration, @@ -190,6 +205,10 @@ class CanvasEx { if (img.frames) { // the image cant have more than one frame, and if it has 0, we dont need to do anything at all if (img.frames.length === 1) { + // if theres no frames at all, add one + if (this.frames.length === 0) { + this.addFrame(Infinity); + } for (let i = 0; i < this.frames.length; ++i) { img.drawFrame(this.frames[i].ctx, 0, x, y, args); } @@ -218,17 +237,41 @@ class CanvasEx { } export(outStream) { - const gif = new GifEncoder(this.width, this.height); - gif.createReadStream().pipe(outStream); - gif.setTransparent(0); - gif.setRepeat(0); - gif.start(); - for (let i = 0; i < this.frames.length; ++i) { - const frame = this.frames[i]; - gif.setDelay(frame.delay); - gif.addFrame(frame.ctx); + if (this.frames.length > 1) { + if (outStream.setHeader) outStream.setHeader('Content-Type', 'image/gif'); + const gif = new GifEncoder(this.width, this.height); + gif.createReadStream().pipe(outStream); + // gif.setTransparent(0xfefe01); + gif.setRepeat(0); + gif.start(); + for (let i = 0; i < this.frames.length; ++i) { + const frame = this.frames[i]; + gif.setDelay(frame.delay); + gif.addFrame(frame.ctx); + } + gif.finish(); + } else if (this.frames.length === 1) { + if (outStream.setHeader) outStream.setHeader('Content-Type', 'image/png'); + const stream = this.frames[0].canvas.pngStream(); + stream.pipe(outStream); + } else { + throw new Error('No image data to be exported'); } - gif.finish(); + } + + toBuffer() { + const buf = new streamBuffers.WritableStreamBuffer({ + initialSize: this.height * this.width * 4 * this.frames.length, + incrementAmount: this.height * this.width * 4 + }); + this.export(buf); + + return new Promise(resolve => { + buf.on('finish', () => { + console.log('Render completed (1)'); + resolve(buf.getContents()); + }); + }); } } diff --git a/index.js b/index.js index eeac557..a53df40 100644 --- a/index.js +++ b/index.js @@ -110,7 +110,7 @@ function render(template, img, size, flipH) { transform.translate = [resultingWidth, 0]; transform.scale = [-1, 1]; } - canvas.drawImage(subject.image, subject.x, subject.y, { transform }); + canvas.drawImage(subject.image, subject.x, subject.y, { width: subject.w, height: subject.h, transform }); } catch (err) { console.error(err); throw new Error(JSON.stringify({ status: 400, error: 'Invalid template' })); @@ -127,8 +127,7 @@ app.get('/:templateName/', async (req, res) => { const img = new ImageEx(req.query.url); const canvas = render(templates[req.params.templateName], await img.loaded); console.log(canvas); - res.setHeader('Content-Type', 'image/png'); - return canvas.pngStream().pipe(res); + return canvas.export(res); } catch (err) { console.log(err); return res.status(400).end(err.message); @@ -160,12 +159,14 @@ client.login(config.discord.token).catch(error => { }); function findEmoji(str) { - const discordEmote = /<:(\w+):(\d+)>/g.exec(str); + const discordEmote = /<(a?):(\w+):(\d+)>/g.exec(str); if (discordEmote) { + const ext = discordEmote[1] === 'a' ? 'gif' : 'png'; return { - name: discordEmote[1], - id: discordEmote[2], - url: `https://cdn.discordapp.com/emojis/${discordEmote[2]}.png` + name: discordEmote[2], + id: discordEmote[3], + url: `https://cdn.discordapp.com/emojis/${discordEmote[3]}.${ext}`, + ext }; } @@ -234,15 +235,23 @@ client.on('message', async message => { } else if (i === 0) return; } if (result) { + const attachment = await result.toBuffer(); + console.log('Render completed, data:', attachment); const messageOptions = { files: [ - { attachment: result.toBuffer(), name: `${name}.png` } + { attachment, name: `${name}.${emoji.ext}` } ] }; - message.channel.send('', messageOptions); + await message.channel.send('', messageOptions); } } } catch (err) { console.error(err); } }); + +process.on('uncaughtException', exception => { + console.log(exception); // to see your exception details in the console + // if you are on production, maybe you can send the exception details to your + // email as well ? +}); diff --git a/package.json b/package.json index 63e0a56..a7cae9e 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,10 @@ "gifencoder": "^1.1.0", "got": "^8.0.1", "lodash": "^4.17.4", + "mime-types": "^2.1.17", "omggif": "^1.0.9", - "request": "^2.81.0" + "request": "^2.81.0", + "stream-buffers": "^3.0.1" }, "devDependencies": { "eslint": "^4.14.0", diff --git a/yarn.lock b/yarn.lock index a36c9f5..2e42139 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1081,7 +1081,7 @@ mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" -mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.17: +mime-types@^2.1.12, mime-types@^2.1.17, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.17: version "2.1.17" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" dependencies: @@ -1580,6 +1580,10 @@ sshpk@^1.7.0: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" +stream-buffers@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.1.tgz#68a38c5faadeded79ff79988d368e3fb1325ef06" + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" From d918e240bde1b0ddea16002229768cd186573bc0 Mon Sep 17 00:00:00 2001 From: CBenni Date: Tue, 2 Jan 2018 13:54:54 +0100 Subject: [PATCH 4/4] Fixed delay for delays smaller or equal to 1 --- imageex.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/imageex.js b/imageex.js index e550f96..ff4cb4a 100644 --- a/imageex.js +++ b/imageex.js @@ -52,8 +52,10 @@ class ImageEx { const reader = new GifReader(new Uint8Array(this.data)); this.width = reader.width; this.height = reader.height; + console.log('Decoding frames'); this.frames = this.decodeFrames(reader); + console.log('Frames decoded!'); this.renderAllFrames(); return this; @@ -165,6 +167,11 @@ class CanvasEx { if ((actualDelay === undefined || actualDelay === null) && (delay === undefined || delay === null)) throw new Error('Delay has to be set!'); const canvas = createCanvas(this.width, this.height); + + if (!Number.isNaN(delay) && delay <= 1) { + delay = 10; + } + const frame = { actualOffset: this.totalDuration, delay: delay || Math.max(Math.round(actualDelay / 10), 2), @@ -246,7 +253,7 @@ class CanvasEx { gif.start(); for (let i = 0; i < this.frames.length; ++i) { const frame = this.frames[i]; - gif.setDelay(frame.delay); + gif.setDelay(frame.actualDelay); gif.addFrame(frame.ctx); } gif.finish();