diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be7b194..d01d0ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,13 +2,13 @@ name: CI on: [push, pull_request] jobs: - node-20-typescript: + node-22-typescript: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: '20' + node-version: '22' - run: npm install - run: npm run build - run: git diff --exit-code diff --git a/.gitignore b/.gitignore index 40b878d..b947077 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules/ \ No newline at end of file +node_modules/ +dist/ diff --git a/build.js b/build.js deleted file mode 100644 index 716ff5c..0000000 --- a/build.js +++ /dev/null @@ -1,99 +0,0 @@ -import { spawn } from 'child_process'; - -function cmd(program, args) { - console.log('CMD:', program, args); - const p = spawn(program, args.flat()); // NOTE: flattening the args array enables you to group related arguments for better self-documentation of the running command - p.stdout.on('data', (data) => process.stdout.write(data)); - p.stderr.on('data', (data) => process.stderr.write(data)); - p.on('close', (code) => { - if (code !== 0) { - console.error(program, args, 'exited with', code); - } - }); - return p; -} - -const commonTscFlags = [ - '--strict', - '--removeComments', - '--skipLibCheck', -]; - -const mainTs = [ - 'ts/eval.ts', - 'ts/filters.ts', - 'ts/grecha.ts', - 'ts/index.ts' -]; - -function tscMain(...extraParams) { - cmd('tsc', [ - ...commonTscFlags, - ['--outDir', 'js'], - ...extraParams, - mainTs, - ]); -} - -function tscServiceWorker(...extraParams) { - cmd('tsc', [ - ...commonTscFlags, - ['--lib', 'webworker'], - ['--outFile', 'serviceworker.js'], - ...extraParams, - 'serviceworker.ts' - ]); -} - -function build(part, ...args) { - switch (part) { - case undefined: - tscServiceWorker(); - tscMain(); - break; - case 'main': - tscMain(); - break; - case 'serviceworker': - tscServiceWorker(); - break; - default: - throw new Error(`Unknown build part ${part}. Available parts: main, serviceworker.`); - } -} - -function watch(part, ...args) { - switch (part) { - case undefined: - tscMain('-w', '--preserveWatchOutput'); - tscServiceWorker('-w', '--preserveWatchOutput'); - break; - case 'main': - tscMain('-w', '--preserveWatchOutput'); - break; - case 'serviceworker': - tscServiceWorker('-w', '--preserveWatchOutput'); - break; - default: - throw new Error(`Unknown watch part ${part}. Available parts: main, serviceworker.`); - } -} - -const [nodePath, scriptPath, command, ...args] = process.argv; -switch (command) { -case undefined: -case 'build': - build(...args); - break; -case 'watch': - watch(...args); - break; -case 'serve': - // TODO: maybe replace Python with something from Node itself? - // Python is a pretty unreasonable dependency. - cmd('python3', [['-m', 'http.server'], '6969']); - watch(); - break; -default: - throw new Error(`Unknown command ${command}. Available commands: build, watch.`); -} diff --git a/gif.js b/gif.js deleted file mode 100644 index f066142..0000000 --- a/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(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i0&&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.fillStyle=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,dispose:frame.dispose,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/gif.js.map b/gif.js.map deleted file mode 100644 index e146d8c..0000000 --- a/gif.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["node_modules/browser-pack/_prelude.js","node_modules/events/events.js","src/browser.coffee","src/gif.coffee"],"names":["f","exports","module","define","amd","g","window","global","self","this","GIF","r","e","n","t","o","i","c","require","u","a","Error","code","p","call","length","1","EventEmitter","_events","_maxListeners","undefined","prototype","defaultMaxListeners","setMaxListeners","isNumber","isNaN","TypeError","emit","type","er","handler","len","args","listeners","error","isObject","arguments","err","context","isUndefined","isFunction","Array","slice","apply","addListener","listener","m","newListener","push","warned","console","trace","on","once","fired","removeListener","list","position","splice","removeAllListeners","key","ret","listenerCount","evlistener","emitter","arg","UA","browser","mode","platform","ua","navigator","userAgent","toLowerCase","match","document","documentMode","name","version","parseFloat","parseInt","extend","child","parent","hasProp","ctor","constructor","__super__","superClass","defaults","frameDefaults","workerScript","workers","repeat","background","quality","width","height","transparent","debug","dither","delay","copy","dispose","options","base","value","running","frames","freeWorkers","activeWorkers","setOptions","setOption","_canvas","results","addFrame","image","frame","ImageData","data","CanvasRenderingContext2D","WebGLRenderingContext","getContextData","childNodes","getImageData","render","j","numWorkers","ref","nextFrame","finishedFrames","imageParts","spawnWorkers","globalPalette","renderNextFrame","abort","worker","shift","log","terminate","Math","min","forEach","_this","Worker","onmessage","event","indexOf","frameFinished","index","finishRendering","k","l","len1","len2","len3","offset","page","ref1","ref2","pageSize","cursor","round","Uint8Array","set","Blob","task","getTask","postMessage","ctx","createElement","getContext","fillStyle","fillRect","drawImage","last","canTransfer"],"mappings":"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","sourceRoot":"","sourcesContent":["(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error(\"Cannot find module '\"+i+\"'\");throw a.code=\"MODULE_NOT_FOUND\",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u=\"function\"==typeof require&&require,i=0;i 0 && this._events[type].length > m) {\n this._events[type].warned = true;\n console.error('(node) warning: possible EventEmitter memory ' +\n 'leak detected. %d listeners added. ' +\n 'Use emitter.setMaxListeners() to increase limit.',\n this._events[type].length);\n if (typeof console.trace === 'function') {\n // not supported in IE 10\n console.trace();\n }\n }\n }\n\n return this;\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.once = function(type, listener) {\n if (!isFunction(listener))\n throw TypeError('listener must be a function');\n\n var fired = false;\n\n function g() {\n this.removeListener(type, g);\n\n if (!fired) {\n fired = true;\n listener.apply(this, arguments);\n }\n }\n\n g.listener = listener;\n this.on(type, g);\n\n return this;\n};\n\n// emits a 'removeListener' event iff the listener was removed\nEventEmitter.prototype.removeListener = function(type, listener) {\n var list, position, length, i;\n\n if (!isFunction(listener))\n throw TypeError('listener must be a function');\n\n if (!this._events || !this._events[type])\n return this;\n\n list = this._events[type];\n length = list.length;\n position = -1;\n\n if (list === listener ||\n (isFunction(list.listener) && list.listener === listener)) {\n delete this._events[type];\n if (this._events.removeListener)\n this.emit('removeListener', type, listener);\n\n } else if (isObject(list)) {\n for (i = length; i-- > 0;) {\n if (list[i] === listener ||\n (list[i].listener && list[i].listener === listener)) {\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (list.length === 1) {\n list.length = 0;\n delete this._events[type];\n } else {\n list.splice(position, 1);\n }\n\n if (this._events.removeListener)\n this.emit('removeListener', type, listener);\n }\n\n return this;\n};\n\nEventEmitter.prototype.removeAllListeners = function(type) {\n var key, listeners;\n\n if (!this._events)\n return this;\n\n // not listening for removeListener, no need to emit\n if (!this._events.removeListener) {\n if (arguments.length === 0)\n this._events = {};\n else if (this._events[type])\n delete this._events[type];\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n for (key in this._events) {\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = {};\n return this;\n }\n\n listeners = this._events[type];\n\n if (isFunction(listeners)) {\n this.removeListener(type, listeners);\n } else if (listeners) {\n // LIFO order\n while (listeners.length)\n this.removeListener(type, listeners[listeners.length - 1]);\n }\n delete this._events[type];\n\n return this;\n};\n\nEventEmitter.prototype.listeners = function(type) {\n var ret;\n if (!this._events || !this._events[type])\n ret = [];\n else if (isFunction(this._events[type]))\n ret = [this._events[type]];\n else\n ret = this._events[type].slice();\n return ret;\n};\n\nEventEmitter.prototype.listenerCount = function(type) {\n if (this._events) {\n var evlistener = this._events[type];\n\n if (isFunction(evlistener))\n return 1;\n else if (evlistener)\n return evlistener.length;\n }\n return 0;\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n return emitter.listenerCount(type);\n};\n\nfunction isFunction(arg) {\n return typeof arg === 'function';\n}\n\nfunction isNumber(arg) {\n return typeof arg === 'number';\n}\n\nfunction isObject(arg) {\n return typeof arg === 'object' && arg !== null;\n}\n\nfunction isUndefined(arg) {\n return arg === void 0;\n}\n","### CoffeeScript version of the browser detection from MooTools ###\n\nua = navigator.userAgent.toLowerCase()\nplatform = navigator.platform.toLowerCase()\nUA = ua.match(/(opera|ie|firefox|chrome|version)[\\s\\/:]([\\w\\d\\.]+)?.*?(safari|version[\\s\\/:]([\\w\\d\\.]+)|$)/) or [null, 'unknown', 0]\nmode = UA[1] == 'ie' && document.documentMode\n\nbrowser =\n name: if UA[1] is 'version' then UA[3] else UA[1]\n version: mode or parseFloat(if UA[1] is 'opera' && UA[4] then UA[4] else UA[2])\n\n platform:\n name: if ua.match(/ip(?:ad|od|hone)/) then 'ios' else (ua.match(/(?:webos|android)/) or platform.match(/mac|win|linux/) or ['other'])[0]\n\nbrowser[browser.name] = true\nbrowser[browser.name + parseInt(browser.version, 10)] = true\nbrowser.platform[browser.platform.name] = true\n\nmodule.exports = browser\n","{EventEmitter} = require 'events'\nbrowser = require './browser.coffee'\n\nclass GIF extends EventEmitter\n\n defaults =\n workerScript: 'gif.worker.js'\n workers: 2\n repeat: 0 # repeat forever, -1 = repeat once\n background: '#fff'\n quality: 10 # pixel sample interval, lower is better\n width: null # size derermined from first frame if possible\n height: null\n transparent: null\n debug: false\n dither: false # see GIFEncoder.js for dithering options\n\n frameDefaults =\n delay: 500 # ms\n copy: false\n dispose: -1\n\n constructor: (options) ->\n @running = false\n\n @options = {}\n @frames = []\n\n @freeWorkers = []\n @activeWorkers = []\n\n @setOptions options\n for key, value of defaults\n @options[key] ?= value\n\n setOption: (key, value) ->\n @options[key] = value\n if @_canvas? and key in ['width', 'height']\n @_canvas[key] = value\n\n setOptions: (options) ->\n @setOption key, value for own key, value of options\n\n addFrame: (image, options={}) ->\n frame = {}\n frame.transparent = @options.transparent\n for key of frameDefaults\n frame[key] = options[key] or frameDefaults[key]\n\n # use the images width and height for options unless already set\n @setOption 'width', image.width unless @options.width?\n @setOption 'height', image.height unless @options.height?\n\n if ImageData? and image instanceof ImageData\n frame.data = image.data\n else if (CanvasRenderingContext2D? and image instanceof CanvasRenderingContext2D) or (WebGLRenderingContext? and image instanceof WebGLRenderingContext)\n if options.copy\n frame.data = @getContextData image\n else\n frame.context = image\n else if image.childNodes?\n if options.copy\n frame.data = @getImageData image\n else\n frame.image = image\n else\n throw new Error 'Invalid image'\n\n @frames.push frame\n\n render: ->\n throw new Error 'Already running' if @running\n\n if not @options.width? or not @options.height?\n throw new Error 'Width and height must be set prior to rendering'\n\n @running = true\n @nextFrame = 0\n @finishedFrames = 0\n\n @imageParts = (null for i in [0...@frames.length])\n numWorkers = @spawnWorkers()\n # we need to wait for the palette\n if @options.globalPalette == true\n @renderNextFrame()\n else\n @renderNextFrame() for i in [0...numWorkers]\n\n @emit 'start'\n @emit 'progress', 0\n\n abort: ->\n loop\n worker = @activeWorkers.shift()\n break unless worker?\n @log 'killing active worker'\n worker.terminate()\n @running = false\n @emit 'abort'\n\n # private\n\n spawnWorkers: ->\n numWorkers = Math.min(@options.workers, @frames.length)\n [@freeWorkers.length...numWorkers].forEach (i) =>\n @log \"spawning worker #{ i }\"\n worker = new Worker @options.workerScript\n worker.onmessage = (event) =>\n @activeWorkers.splice @activeWorkers.indexOf(worker), 1\n @freeWorkers.push worker\n @frameFinished event.data\n @freeWorkers.push worker\n return numWorkers\n\n frameFinished: (frame) ->\n @log \"frame #{ frame.index } finished - #{ @activeWorkers.length } active\"\n @finishedFrames++\n @emit 'progress', @finishedFrames / @frames.length\n @imageParts[frame.index] = frame\n # remember calculated palette, spawn the rest of the workers\n if @options.globalPalette == true\n @options.globalPalette = frame.globalPalette\n @log 'global palette analyzed'\n @renderNextFrame() for i in [1...@freeWorkers.length] if @frames.length > 2\n if null in @imageParts\n @renderNextFrame()\n else\n @finishRendering()\n\n finishRendering: ->\n len = 0\n for frame in @imageParts\n len += (frame.data.length - 1) * frame.pageSize + frame.cursor\n len += frame.pageSize - frame.cursor\n @log \"rendering finished - filesize #{ Math.round(len / 1000) }kb\"\n data = new Uint8Array len\n offset = 0\n for frame in @imageParts\n for page, i in frame.data\n data.set page, offset\n if i is frame.data.length - 1\n offset += frame.cursor\n else\n offset += frame.pageSize\n\n image = new Blob [data],\n type: 'image/gif'\n\n @emit 'finished', image, data\n\n renderNextFrame: ->\n throw new Error 'No free workers' if @freeWorkers.length is 0\n return if @nextFrame >= @frames.length # no new frame to render\n\n frame = @frames[@nextFrame++]\n worker = @freeWorkers.shift()\n task = @getTask frame\n\n @log \"starting frame #{ task.index + 1 } of #{ @frames.length }\"\n @activeWorkers.push worker\n worker.postMessage task#, [task.data.buffer]\n\n getContextData: (ctx) ->\n return ctx.getImageData(0, 0, @options.width, @options.height).data\n\n getImageData: (image) ->\n if not @_canvas?\n @_canvas = document.createElement 'canvas'\n @_canvas.width = @options.width\n @_canvas.height = @options.height\n\n ctx = @_canvas.getContext '2d'\n ctx.fillStyle = @options.background\n ctx.fillRect 0, 0, @options.width, @options.height\n ctx.drawImage image, 0, 0\n\n return @getContextData ctx\n\n getTask: (frame) ->\n index = @frames.indexOf frame\n task =\n index: index\n last: index is (@frames.length - 1)\n delay: frame.delay\n dispose: frame.dispose\n transparent: frame.transparent\n width: @options.width\n height: @options.height\n quality: @options.quality\n dither: @options.dither\n globalPalette: @options.globalPalette\n repeat: @options.repeat\n canTransfer: (browser.name is 'chrome')\n\n if frame.data?\n task.data = frame.data\n else if frame.context?\n task.data = @getContextData frame.context\n else if frame.image?\n task.data = @getImageData frame.image\n else\n throw new Error 'Invalid frame'\n\n return task\n\n log: (args...) ->\n return unless @options.debug\n console.log args...\n\n\nmodule.exports = GIF\n"]} \ No newline at end of file diff --git a/gif.worker.js b/gif.worker.js deleted file mode 100644 index 845c684..0000000 --- a/gif.worker.js +++ /dev/null @@ -1,3 +0,0 @@ -// gif.worker.js 0.2.0 - https://github.com/jnordberg/gif.js -(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i=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=this.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= ByteArray.pageSize) this.newPage();\n this.pages[this.page][this.cursor++] = val;\n};\n\nByteArray.prototype.writeUTFBytes = function(string) {\n for (var l = string.length, i = 0; i < l; i++)\n this.writeByte(string.charCodeAt(i));\n};\n\nByteArray.prototype.writeBytes = function(array, offset, length) {\n for (var l = length || array.length, i = offset || 0; i < l; i++)\n this.writeByte(array[i]);\n};\n\nfunction GIFEncoder(width, height) {\n // image size\n this.width = ~~width;\n this.height = ~~height;\n\n // transparent color if given\n this.transparent = null;\n\n // transparent index in color table\n this.transIndex = 0;\n\n // -1 = no repeat, 0 = forever. anything else is repeat count\n this.repeat = -1;\n\n // frame delay (hundredths)\n this.delay = 0;\n\n this.image = null; // current frame\n this.pixels = null; // BGR byte array from frame\n this.indexedPixels = null; // converted frame indexed to palette\n this.colorDepth = null; // number of bit planes\n this.colorTab = null; // RGB palette\n this.neuQuant = null; // NeuQuant instance that was used to generate this.colorTab.\n this.usedEntry = new Array(); // active palette entries\n this.palSize = 7; // color table size (bits-1)\n this.dispose = -1; // disposal code (-1 = use default)\n this.firstFrame = true;\n this.sample = 10; // default sample interval for quantizer\n this.dither = false; // default dithering\n this.globalPalette = false;\n\n this.out = new ByteArray();\n}\n\n/*\n Sets the delay time between each frame, or changes it for subsequent frames\n (applies to last frame added)\n*/\nGIFEncoder.prototype.setDelay = function(milliseconds) {\n this.delay = Math.round(milliseconds / 10);\n};\n\n/*\n Sets frame rate in frames per second.\n*/\nGIFEncoder.prototype.setFrameRate = function(fps) {\n this.delay = Math.round(100 / fps);\n};\n\n/*\n Sets the GIF frame disposal code for the last added frame and any\n subsequent frames.\n\n Default is 0 if no transparent color has been set, otherwise 2.\n*/\nGIFEncoder.prototype.setDispose = function(disposalCode) {\n if (disposalCode >= 0) this.dispose = disposalCode;\n};\n\n/*\n Sets the number of times the set of GIF frames should be played.\n\n -1 = play once\n 0 = repeat indefinitely\n\n Default is -1\n\n Must be invoked before the first image is added\n*/\n\nGIFEncoder.prototype.setRepeat = function(repeat) {\n this.repeat = repeat;\n};\n\n/*\n Sets the transparent color for the last added frame and any subsequent\n frames. Since all colors are subject to modification in the quantization\n process, the color in the final palette for each frame closest to the given\n color becomes the transparent color for that frame. May be set to null to\n indicate no transparent color.\n*/\nGIFEncoder.prototype.setTransparent = function(color) {\n this.transparent = color;\n};\n\n/*\n Adds next GIF frame. The frame is not written immediately, but is\n actually deferred until the next frame is received so that timing\n data can be inserted. Invoking finish() flushes all frames.\n*/\nGIFEncoder.prototype.addFrame = function(imageData) {\n this.image = imageData;\n\n this.colorTab = this.globalPalette && this.globalPalette.slice ? this.globalPalette : null;\n\n this.getImagePixels(); // convert to correct format if necessary\n this.analyzePixels(); // build color table & map pixels\n\n if (this.globalPalette === true) this.globalPalette = this.colorTab;\n\n if (this.firstFrame) {\n this.writeLSD(); // logical screen descriptior\n this.writePalette(); // global color table\n if (this.repeat >= 0) {\n // use NS app extension to indicate reps\n this.writeNetscapeExt();\n }\n }\n\n this.writeGraphicCtrlExt(); // write graphic control extension\n this.writeImageDesc(); // image descriptor\n if (!this.firstFrame && !this.globalPalette) this.writePalette(); // local color table\n this.writePixels(); // encode and write pixel data\n\n this.firstFrame = false;\n};\n\n/*\n Adds final trailer to the GIF stream, if you don't call the finish method\n the GIF stream will not be valid.\n*/\nGIFEncoder.prototype.finish = function() {\n this.out.writeByte(0x3b); // gif trailer\n};\n\n/*\n Sets quality of color quantization (conversion of images to the maximum 256\n colors allowed by the GIF specification). Lower values (minimum = 1)\n produce better colors, but slow processing significantly. 10 is the\n default, and produces good color mapping at reasonable speeds. Values\n greater than 20 do not yield significant improvements in speed.\n*/\nGIFEncoder.prototype.setQuality = function(quality) {\n if (quality < 1) quality = 1;\n this.sample = quality;\n};\n\n/*\n Sets dithering method. Available are:\n - FALSE no dithering\n - TRUE or FloydSteinberg\n - FalseFloydSteinberg\n - Stucki\n - Atkinson\n You can add '-serpentine' to use serpentine scanning\n*/\nGIFEncoder.prototype.setDither = function(dither) {\n if (dither === true) dither = 'FloydSteinberg';\n this.dither = dither;\n};\n\n/*\n Sets global palette for all frames.\n You can provide TRUE to create global palette from first picture.\n Or an array of r,g,b,r,g,b,...\n*/\nGIFEncoder.prototype.setGlobalPalette = function(palette) {\n this.globalPalette = palette;\n};\n\n/*\n Returns global palette used for all frames.\n If setGlobalPalette(true) was used, then this function will return\n calculated palette after the first frame is added.\n*/\nGIFEncoder.prototype.getGlobalPalette = function() {\n return (this.globalPalette && this.globalPalette.slice && this.globalPalette.slice(0)) || this.globalPalette;\n};\n\n/*\n Writes GIF file header\n*/\nGIFEncoder.prototype.writeHeader = function() {\n this.out.writeUTFBytes(\"GIF89a\");\n};\n\n/*\n Analyzes current frame colors and creates color map.\n*/\nGIFEncoder.prototype.analyzePixels = function() {\n if (!this.colorTab) {\n this.neuQuant = new NeuQuant(this.pixels, this.sample);\n this.neuQuant.buildColormap(); // create reduced palette\n this.colorTab = this.neuQuant.getColormap();\n }\n\n // map image pixels to new palette\n if (this.dither) {\n this.ditherPixels(this.dither.replace('-serpentine', ''), this.dither.match(/-serpentine/) !== null);\n } else {\n this.indexPixels();\n }\n\n this.pixels = null;\n this.colorDepth = 8;\n this.palSize = 7;\n\n // get closest match to transparent color if specified\n if (this.transparent !== null) {\n this.transIndex = this.findClosest(this.transparent, true);\n }\n};\n\n/*\n Index pixels, without dithering\n*/\nGIFEncoder.prototype.indexPixels = function(imgq) {\n var nPix = this.pixels.length / 3;\n this.indexedPixels = new Uint8Array(nPix);\n var k = 0;\n for (var j = 0; j < nPix; j++) {\n var index = this.findClosestRGB(\n this.pixels[k++] & 0xff,\n this.pixels[k++] & 0xff,\n this.pixels[k++] & 0xff\n );\n this.usedEntry[index] = true;\n this.indexedPixels[j] = index;\n }\n};\n\n/*\n Taken from http://jsbin.com/iXofIji/2/edit by PAEz\n*/\nGIFEncoder.prototype.ditherPixels = function(kernel, serpentine) {\n var kernels = {\n FalseFloydSteinberg: [\n [3 / 8, 1, 0],\n [3 / 8, 0, 1],\n [2 / 8, 1, 1]\n ],\n FloydSteinberg: [\n [7 / 16, 1, 0],\n [3 / 16, -1, 1],\n [5 / 16, 0, 1],\n [1 / 16, 1, 1]\n ],\n Stucki: [\n [8 / 42, 1, 0],\n [4 / 42, 2, 0],\n [2 / 42, -2, 1],\n [4 / 42, -1, 1],\n [8 / 42, 0, 1],\n [4 / 42, 1, 1],\n [2 / 42, 2, 1],\n [1 / 42, -2, 2],\n [2 / 42, -1, 2],\n [4 / 42, 0, 2],\n [2 / 42, 1, 2],\n [1 / 42, 2, 2]\n ],\n Atkinson: [\n [1 / 8, 1, 0],\n [1 / 8, 2, 0],\n [1 / 8, -1, 1],\n [1 / 8, 0, 1],\n [1 / 8, 1, 1],\n [1 / 8, 0, 2]\n ]\n };\n\n if (!kernel || !kernels[kernel]) {\n throw 'Unknown dithering kernel: ' + kernel;\n }\n\n var ds = kernels[kernel];\n var index = 0,\n height = this.height,\n width = this.width,\n data = this.pixels;\n var direction = serpentine ? -1 : 1;\n\n this.indexedPixels = new Uint8Array(this.pixels.length / 3);\n\n for (var y = 0; y < height; y++) {\n\n if (serpentine) direction = direction * -1;\n\n for (var x = (direction == 1 ? 0 : width - 1), xend = (direction == 1 ? width : 0); x !== xend; x += direction) {\n\n index = (y * width) + x;\n // Get original colour\n var idx = index * 3;\n var r1 = data[idx];\n var g1 = data[idx + 1];\n var b1 = data[idx + 2];\n\n // Get converted colour\n idx = this.findClosestRGB(r1, g1, b1);\n this.usedEntry[idx] = true;\n this.indexedPixels[index] = idx;\n idx *= 3;\n var r2 = this.colorTab[idx];\n var g2 = this.colorTab[idx + 1];\n var b2 = this.colorTab[idx + 2];\n\n var er = r1 - r2;\n var eg = g1 - g2;\n var eb = b1 - b2;\n\n for (var i = (direction == 1 ? 0: ds.length - 1), end = (direction == 1 ? ds.length : 0); i !== end; i += direction) {\n var x1 = ds[i][1]; // *direction; // Should this by timesd by direction?..to make the kernel go in the opposite direction....got no idea....\n var y1 = ds[i][2];\n if (x1 + x >= 0 && x1 + x < width && y1 + y >= 0 && y1 + y < height) {\n var d = ds[i][0];\n idx = index + x1 + (y1 * width);\n idx *= 3;\n\n data[idx] = Math.max(0, Math.min(255, data[idx] + er * d));\n data[idx + 1] = Math.max(0, Math.min(255, data[idx + 1] + eg * d));\n data[idx + 2] = Math.max(0, Math.min(255, data[idx + 2] + eb * d));\n }\n }\n }\n }\n};\n\n/*\n Returns index of palette color closest to c\n*/\nGIFEncoder.prototype.findClosest = function(c, used) {\n return this.findClosestRGB((c & 0xFF0000) >> 16, (c & 0x00FF00) >> 8, (c & 0x0000FF), used);\n};\n\nGIFEncoder.prototype.findClosestRGB = function(r, g, b, used) {\n if (this.colorTab === null) return -1;\n\n if (this.neuQuant && !used) {\n return this.neuQuant.lookupRGB(r, g, b);\n }\n\n var c = b | (g << 8) | (r << 16);\n\n var minpos = 0;\n var dmin = 256 * 256 * 256;\n var len = this.colorTab.length;\n\n for (var i = 0, index = 0; i < len; index++) {\n var dr = r - (this.colorTab[i++] & 0xff);\n var dg = g - (this.colorTab[i++] & 0xff);\n var db = b - (this.colorTab[i++] & 0xff);\n var d = dr * dr + dg * dg + db * db;\n if ((!used || this.usedEntry[index]) && (d < dmin)) {\n dmin = d;\n minpos = index;\n }\n }\n\n return minpos;\n};\n\n/*\n Extracts image pixels into byte array pixels\n (removes alphachannel from canvas imagedata)\n*/\nGIFEncoder.prototype.getImagePixels = function() {\n var w = this.width;\n var h = this.height;\n this.pixels = new Uint8Array(w * h * 3);\n\n var data = this.image;\n var srcPos = 0;\n var count = 0;\n\n for (var i = 0; i < h; i++) {\n for (var j = 0; j < w; j++) {\n this.pixels[count++] = data[srcPos++];\n this.pixels[count++] = data[srcPos++];\n this.pixels[count++] = data[srcPos++];\n srcPos++;\n }\n }\n};\n\n/*\n Writes Graphic Control Extension\n*/\nGIFEncoder.prototype.writeGraphicCtrlExt = function() {\n this.out.writeByte(0x21); // extension introducer\n this.out.writeByte(0xf9); // GCE label\n this.out.writeByte(4); // data block size\n\n var transp, disp;\n if (this.transparent === null) {\n transp = 0;\n disp = 0; // dispose = no action\n } else {\n transp = 1;\n disp = 2; // force clear if using transparent color\n }\n\n if (this.dispose >= 0) {\n disp = this.dispose & 7; // user override\n }\n disp <<= 2;\n\n // packed fields\n this.out.writeByte(\n 0 | // 1:3 reserved\n disp | // 4:6 disposal\n 0 | // 7 user input - 0 = none\n transp // 8 transparency flag\n );\n\n this.writeShort(this.delay); // delay x 1/100 sec\n this.out.writeByte(this.transIndex); // transparent color index\n this.out.writeByte(0); // block terminator\n};\n\n/*\n Writes Image Descriptor\n*/\nGIFEncoder.prototype.writeImageDesc = function() {\n this.out.writeByte(0x2c); // image separator\n this.writeShort(0); // image position x,y = 0,0\n this.writeShort(0);\n this.writeShort(this.width); // image size\n this.writeShort(this.height);\n\n // packed fields\n if (this.firstFrame || this.globalPalette) {\n // no LCT - GCT is used for first (or only) frame\n this.out.writeByte(0);\n } else {\n // specify normal LCT\n this.out.writeByte(\n 0x80 | // 1 local color table 1=yes\n 0 | // 2 interlace - 0=no\n 0 | // 3 sorted - 0=no\n 0 | // 4-5 reserved\n this.palSize // 6-8 size of color table\n );\n }\n};\n\n/*\n Writes Logical Screen Descriptor\n*/\nGIFEncoder.prototype.writeLSD = function() {\n // logical screen size\n this.writeShort(this.width);\n this.writeShort(this.height);\n\n // packed fields\n this.out.writeByte(\n 0x80 | // 1 : global color table flag = 1 (gct used)\n 0x70 | // 2-4 : color resolution = 7\n 0x00 | // 5 : gct sort flag = 0\n this.palSize // 6-8 : gct size\n );\n\n this.out.writeByte(0); // background color index\n this.out.writeByte(0); // pixel aspect ratio - assume 1:1\n};\n\n/*\n Writes Netscape application extension to define repeat count.\n*/\nGIFEncoder.prototype.writeNetscapeExt = function() {\n this.out.writeByte(0x21); // extension introducer\n this.out.writeByte(0xff); // app extension label\n this.out.writeByte(11); // block size\n this.out.writeUTFBytes('NETSCAPE2.0'); // app id + auth code\n this.out.writeByte(3); // sub-block size\n this.out.writeByte(1); // loop sub-block id\n this.writeShort(this.repeat); // loop count (extra iterations, 0=repeat forever)\n this.out.writeByte(0); // block terminator\n};\n\n/*\n Writes color table\n*/\nGIFEncoder.prototype.writePalette = function() {\n this.out.writeBytes(this.colorTab);\n var n = (3 * 256) - this.colorTab.length;\n for (var i = 0; i < n; i++)\n this.out.writeByte(0);\n};\n\nGIFEncoder.prototype.writeShort = function(pValue) {\n this.out.writeByte(pValue & 0xFF);\n this.out.writeByte((pValue >> 8) & 0xFF);\n};\n\n/*\n Encodes and writes pixel data\n*/\nGIFEncoder.prototype.writePixels = function() {\n var enc = new LZWEncoder(this.width, this.height, this.indexedPixels, this.colorDepth);\n enc.encode(this.out);\n};\n\n/*\n Retrieves the GIF stream\n*/\nGIFEncoder.prototype.stream = function() {\n return this.out;\n};\n\nmodule.exports = GIFEncoder;\n","/*\n LZWEncoder.js\n\n Authors\n Kevin Weiner (original Java version - kweiner@fmsware.com)\n Thibault Imbert (AS3 version - bytearray.org)\n Johan Nordberg (JS version - code@johan-nordberg.com)\n\n Acknowledgements\n GIFCOMPR.C - GIF Image compression routines\n Lempel-Ziv compression based on 'compress'. GIF modifications by\n David Rowley (mgardi@watdcsu.waterloo.edu)\n GIF Image compression - modified 'compress'\n Based on: compress.c - File compression ala IEEE Computer, June 1984.\n By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)\n Jim McKie (decvax!mcvax!jim)\n Steve Davies (decvax!vax135!petsd!peora!srd)\n Ken Turkowski (decvax!decwrl!turtlevax!ken)\n James A. Woods (decvax!ihnp4!ames!jaw)\n Joe Orost (decvax!vax135!petsd!joe)\n*/\n\nvar EOF = -1;\nvar BITS = 12;\nvar HSIZE = 5003; // 80% occupancy\nvar masks = [0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F,\n 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF,\n 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF];\n\nfunction LZWEncoder(width, height, pixels, colorDepth) {\n var initCodeSize = Math.max(2, colorDepth);\n\n var accum = new Uint8Array(256);\n var htab = new Int32Array(HSIZE);\n var codetab = new Int32Array(HSIZE);\n\n var cur_accum, cur_bits = 0;\n var a_count;\n var free_ent = 0; // first unused entry\n var maxcode;\n\n // block compression parameters -- after all codes are used up,\n // and compression rate changes, start over.\n var clear_flg = false;\n\n // Algorithm: use open addressing double hashing (no chaining) on the\n // prefix code / next character combination. We do a variant of Knuth's\n // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime\n // secondary probe. Here, the modular division first probe is gives way\n // to a faster exclusive-or manipulation. Also do block compression with\n // an adaptive reset, whereby the code table is cleared when the compression\n // ratio decreases, but after the table fills. The variable-length output\n // codes are re-sized at this point, and a special CLEAR code is generated\n // for the decompressor. Late addition: construct the table according to\n // file size for noticeable speed improvement on small files. Please direct\n // questions about this implementation to ames!jaw.\n var g_init_bits, ClearCode, EOFCode;\n\n // Add a character to the end of the current packet, and if it is 254\n // characters, flush the packet to disk.\n function char_out(c, outs) {\n accum[a_count++] = c;\n if (a_count >= 254) flush_char(outs);\n }\n\n // Clear out the hash table\n // table clear for block compress\n function cl_block(outs) {\n cl_hash(HSIZE);\n free_ent = ClearCode + 2;\n clear_flg = true;\n output(ClearCode, outs);\n }\n\n // Reset code table\n function cl_hash(hsize) {\n for (var i = 0; i < hsize; ++i) htab[i] = -1;\n }\n\n function compress(init_bits, outs) {\n var fcode, c, i, ent, disp, hsize_reg, hshift;\n\n // Set up the globals: g_init_bits - initial number of bits\n g_init_bits = init_bits;\n\n // Set up the necessary values\n clear_flg = false;\n n_bits = g_init_bits;\n maxcode = MAXCODE(n_bits);\n\n ClearCode = 1 << (init_bits - 1);\n EOFCode = ClearCode + 1;\n free_ent = ClearCode + 2;\n\n a_count = 0; // clear packet\n\n ent = nextPixel();\n\n hshift = 0;\n for (fcode = HSIZE; fcode < 65536; fcode *= 2) ++hshift;\n hshift = 8 - hshift; // set hash code range bound\n hsize_reg = HSIZE;\n cl_hash(hsize_reg); // clear hash table\n\n output(ClearCode, outs);\n\n outer_loop: while ((c = nextPixel()) != EOF) {\n fcode = (c << BITS) + ent;\n i = (c << hshift) ^ ent; // xor hashing\n if (htab[i] === fcode) {\n ent = codetab[i];\n continue;\n } else if (htab[i] >= 0) { // non-empty slot\n disp = hsize_reg - i; // secondary hash (after G. Knott)\n if (i === 0) disp = 1;\n do {\n if ((i -= disp) < 0) i += hsize_reg;\n if (htab[i] === fcode) {\n ent = codetab[i];\n continue outer_loop;\n }\n } while (htab[i] >= 0);\n }\n output(ent, outs);\n ent = c;\n if (free_ent < 1 << BITS) {\n codetab[i] = free_ent++; // code -> hashtable\n htab[i] = fcode;\n } else {\n cl_block(outs);\n }\n }\n\n // Put out the final code.\n output(ent, outs);\n output(EOFCode, outs);\n }\n\n function encode(outs) {\n outs.writeByte(initCodeSize); // write \"initial code size\" byte\n remaining = width * height; // reset navigation variables\n curPixel = 0;\n compress(initCodeSize + 1, outs); // compress and write the pixel data\n outs.writeByte(0); // write block terminator\n }\n\n // Flush the packet to disk, and reset the accumulator\n function flush_char(outs) {\n if (a_count > 0) {\n outs.writeByte(a_count);\n outs.writeBytes(accum, 0, a_count);\n a_count = 0;\n }\n }\n\n function MAXCODE(n_bits) {\n return (1 << n_bits) - 1;\n }\n\n // Return the next pixel from the image\n function nextPixel() {\n if (remaining === 0) return EOF;\n --remaining;\n var pix = pixels[curPixel++];\n return pix & 0xff;\n }\n\n function output(code, outs) {\n cur_accum &= masks[cur_bits];\n\n if (cur_bits > 0) cur_accum |= (code << cur_bits);\n else cur_accum = code;\n\n cur_bits += n_bits;\n\n while (cur_bits >= 8) {\n char_out((cur_accum & 0xff), outs);\n cur_accum >>= 8;\n cur_bits -= 8;\n }\n\n // If the next entry is going to be too big for the code size,\n // then increase it, if possible.\n if (free_ent > maxcode || clear_flg) {\n if (clear_flg) {\n maxcode = MAXCODE(n_bits = g_init_bits);\n clear_flg = false;\n } else {\n ++n_bits;\n if (n_bits == BITS) maxcode = 1 << BITS;\n else maxcode = MAXCODE(n_bits);\n }\n }\n\n if (code == EOFCode) {\n // At EOF, write the rest of the buffer.\n while (cur_bits > 0) {\n char_out((cur_accum & 0xff), outs);\n cur_accum >>= 8;\n cur_bits -= 8;\n }\n flush_char(outs);\n }\n }\n\n this.encode = encode;\n}\n\nmodule.exports = LZWEncoder;\n","/* NeuQuant Neural-Net Quantization Algorithm\n * ------------------------------------------\n *\n * Copyright (c) 1994 Anthony Dekker\n *\n * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994.\n * See \"Kohonen neural networks for optimal colour quantization\"\n * in \"Network: Computation in Neural Systems\" Vol. 5 (1994) pp 351-367.\n * for a discussion of the algorithm.\n * See also http://members.ozemail.com.au/~dekker/NEUQUANT.HTML\n *\n * Any party obtaining a copy of these files from the author, directly or\n * indirectly, is granted, free of charge, a full and unrestricted irrevocable,\n * world-wide, paid up, royalty-free, nonexclusive right and license to deal\n * in this software and documentation files (the \"Software\"), including without\n * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,\n * and/or sell copies of the Software, and to permit persons who receive\n * copies from any such party to do so, with the only requirement being\n * that this copyright notice remain intact.\n *\n * (JavaScript port 2012 by Johan Nordberg)\n */\n\nvar ncycles = 100; // number of learning cycles\nvar netsize = 256; // number of colors used\nvar maxnetpos = netsize - 1;\n\n// defs for freq and bias\nvar netbiasshift = 4; // bias for colour values\nvar intbiasshift = 16; // bias for fractions\nvar intbias = (1 << intbiasshift);\nvar gammashift = 10;\nvar gamma = (1 << gammashift);\nvar betashift = 10;\nvar beta = (intbias >> betashift); /* beta = 1/1024 */\nvar betagamma = (intbias << (gammashift - betashift));\n\n// defs for decreasing radius factor\nvar initrad = (netsize >> 3); // for 256 cols, radius starts\nvar radiusbiasshift = 6; // at 32.0 biased by 6 bits\nvar radiusbias = (1 << radiusbiasshift);\nvar initradius = (initrad * radiusbias); //and decreases by a\nvar radiusdec = 30; // factor of 1/30 each cycle\n\n// defs for decreasing alpha factor\nvar alphabiasshift = 10; // alpha starts at 1.0\nvar initalpha = (1 << alphabiasshift);\nvar alphadec; // biased by 10 bits\n\n/* radbias and alpharadbias used for radpower calculation */\nvar radbiasshift = 8;\nvar radbias = (1 << radbiasshift);\nvar alpharadbshift = (alphabiasshift + radbiasshift);\nvar alpharadbias = (1 << alpharadbshift);\n\n// four primes near 500 - assume no image has a length so large that it is\n// divisible by all four primes\nvar prime1 = 499;\nvar prime2 = 491;\nvar prime3 = 487;\nvar prime4 = 503;\nvar minpicturebytes = (3 * prime4);\n\n/*\n Constructor: NeuQuant\n\n Arguments:\n\n pixels - array of pixels in RGB format\n samplefac - sampling factor 1 to 30 where lower is better quality\n\n >\n > pixels = [r, g, b, r, g, b, r, g, b, ..]\n >\n*/\nfunction NeuQuant(pixels, samplefac) {\n var network; // int[netsize][4]\n var netindex; // for network lookup - really 256\n\n // bias and freq arrays for learning\n var bias;\n var freq;\n var radpower;\n\n /*\n Private Method: init\n\n sets up arrays\n */\n function init() {\n network = [];\n netindex = new Int32Array(256);\n bias = new Int32Array(netsize);\n freq = new Int32Array(netsize);\n radpower = new Int32Array(netsize >> 3);\n\n var i, v;\n for (i = 0; i < netsize; i++) {\n v = (i << (netbiasshift + 8)) / netsize;\n network[i] = new Float64Array([v, v, v, 0]);\n //network[i] = [v, v, v, 0]\n freq[i] = intbias / netsize;\n bias[i] = 0;\n }\n }\n\n /*\n Private Method: unbiasnet\n\n unbiases network to give byte values 0..255 and record position i to prepare for sort\n */\n function unbiasnet() {\n for (var i = 0; i < netsize; i++) {\n network[i][0] >>= netbiasshift;\n network[i][1] >>= netbiasshift;\n network[i][2] >>= netbiasshift;\n network[i][3] = i; // record color number\n }\n }\n\n /*\n Private Method: altersingle\n\n moves neuron *i* towards biased (b,g,r) by factor *alpha*\n */\n function altersingle(alpha, i, b, g, r) {\n network[i][0] -= (alpha * (network[i][0] - b)) / initalpha;\n network[i][1] -= (alpha * (network[i][1] - g)) / initalpha;\n network[i][2] -= (alpha * (network[i][2] - r)) / initalpha;\n }\n\n /*\n Private Method: alterneigh\n\n moves neurons in *radius* around index *i* towards biased (b,g,r) by factor *alpha*\n */\n function alterneigh(radius, i, b, g, r) {\n var lo = Math.abs(i - radius);\n var hi = Math.min(i + radius, netsize);\n\n var j = i + 1;\n var k = i - 1;\n var m = 1;\n\n var p, a;\n while ((j < hi) || (k > lo)) {\n a = radpower[m++];\n\n if (j < hi) {\n p = network[j++];\n p[0] -= (a * (p[0] - b)) / alpharadbias;\n p[1] -= (a * (p[1] - g)) / alpharadbias;\n p[2] -= (a * (p[2] - r)) / alpharadbias;\n }\n\n if (k > lo) {\n p = network[k--];\n p[0] -= (a * (p[0] - b)) / alpharadbias;\n p[1] -= (a * (p[1] - g)) / alpharadbias;\n p[2] -= (a * (p[2] - r)) / alpharadbias;\n }\n }\n }\n\n /*\n Private Method: contest\n\n searches for biased BGR values\n */\n function contest(b, g, r) {\n /*\n finds closest neuron (min dist) and updates freq\n finds best neuron (min dist-bias) and returns position\n for frequently chosen neurons, freq[i] is high and bias[i] is negative\n bias[i] = gamma * ((1 / netsize) - freq[i])\n */\n\n var bestd = ~(1 << 31);\n var bestbiasd = bestd;\n var bestpos = -1;\n var bestbiaspos = bestpos;\n\n var i, n, dist, biasdist, betafreq;\n for (i = 0; i < netsize; i++) {\n n = network[i];\n\n dist = Math.abs(n[0] - b) + Math.abs(n[1] - g) + Math.abs(n[2] - r);\n if (dist < bestd) {\n bestd = dist;\n bestpos = i;\n }\n\n biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift));\n if (biasdist < bestbiasd) {\n bestbiasd = biasdist;\n bestbiaspos = i;\n }\n\n betafreq = (freq[i] >> betashift);\n freq[i] -= betafreq;\n bias[i] += (betafreq << gammashift);\n }\n\n freq[bestpos] += beta;\n bias[bestpos] -= betagamma;\n\n return bestbiaspos;\n }\n\n /*\n Private Method: inxbuild\n\n sorts network and builds netindex[0..255]\n */\n function inxbuild() {\n var i, j, p, q, smallpos, smallval, previouscol = 0, startpos = 0;\n for (i = 0; i < netsize; i++) {\n p = network[i];\n smallpos = i;\n smallval = p[1]; // index on g\n // find smallest in i..netsize-1\n for (j = i + 1; j < netsize; j++) {\n q = network[j];\n if (q[1] < smallval) { // index on g\n smallpos = j;\n smallval = q[1]; // index on g\n }\n }\n q = network[smallpos];\n // swap p (i) and q (smallpos) entries\n if (i != smallpos) {\n j = q[0]; q[0] = p[0]; p[0] = j;\n j = q[1]; q[1] = p[1]; p[1] = j;\n j = q[2]; q[2] = p[2]; p[2] = j;\n j = q[3]; q[3] = p[3]; p[3] = j;\n }\n // smallval entry is now in position i\n\n if (smallval != previouscol) {\n netindex[previouscol] = (startpos + i) >> 1;\n for (j = previouscol + 1; j < smallval; j++)\n netindex[j] = i;\n previouscol = smallval;\n startpos = i;\n }\n }\n netindex[previouscol] = (startpos + maxnetpos) >> 1;\n for (j = previouscol + 1; j < 256; j++)\n netindex[j] = maxnetpos; // really 256\n }\n\n /*\n Private Method: inxsearch\n\n searches for BGR values 0..255 and returns a color index\n */\n function inxsearch(b, g, r) {\n var a, p, dist;\n\n var bestd = 1000; // biggest possible dist is 256*3\n var best = -1;\n\n var i = netindex[g]; // index on g\n var j = i - 1; // start at netindex[g] and work outwards\n\n while ((i < netsize) || (j >= 0)) {\n if (i < netsize) {\n p = network[i];\n dist = p[1] - g; // inx key\n if (dist >= bestd) i = netsize; // stop iter\n else {\n i++;\n if (dist < 0) dist = -dist;\n a = p[0] - b; if (a < 0) a = -a;\n dist += a;\n if (dist < bestd) {\n a = p[2] - r; if (a < 0) a = -a;\n dist += a;\n if (dist < bestd) {\n bestd = dist;\n best = p[3];\n }\n }\n }\n }\n if (j >= 0) {\n p = network[j];\n dist = g - p[1]; // inx key - reverse dif\n if (dist >= bestd) j = -1; // stop iter\n else {\n j--;\n if (dist < 0) dist = -dist;\n a = p[0] - b; if (a < 0) a = -a;\n dist += a;\n if (dist < bestd) {\n a = p[2] - r; if (a < 0) a = -a;\n dist += a;\n if (dist < bestd) {\n bestd = dist;\n best = p[3];\n }\n }\n }\n }\n }\n\n return best;\n }\n\n /*\n Private Method: learn\n\n \"Main Learning Loop\"\n */\n function learn() {\n var i;\n\n var lengthcount = pixels.length;\n var alphadec = 30 + ((samplefac - 1) / 3);\n var samplepixels = lengthcount / (3 * samplefac);\n var delta = ~~(samplepixels / ncycles);\n var alpha = initalpha;\n var radius = initradius;\n\n var rad = radius >> radiusbiasshift;\n\n if (rad <= 1) rad = 0;\n for (i = 0; i < rad; i++)\n radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad));\n\n var step;\n if (lengthcount < minpicturebytes) {\n samplefac = 1;\n step = 3;\n } else if ((lengthcount % prime1) !== 0) {\n step = 3 * prime1;\n } else if ((lengthcount % prime2) !== 0) {\n step = 3 * prime2;\n } else if ((lengthcount % prime3) !== 0) {\n step = 3 * prime3;\n } else {\n step = 3 * prime4;\n }\n\n var b, g, r, j;\n var pix = 0; // current pixel\n\n i = 0;\n while (i < samplepixels) {\n b = (pixels[pix] & 0xff) << netbiasshift;\n g = (pixels[pix + 1] & 0xff) << netbiasshift;\n r = (pixels[pix + 2] & 0xff) << netbiasshift;\n\n j = contest(b, g, r);\n\n altersingle(alpha, j, b, g, r);\n if (rad !== 0) alterneigh(rad, j, b, g, r); // alter neighbours\n\n pix += step;\n if (pix >= lengthcount) pix -= lengthcount;\n\n i++;\n\n if (delta === 0) delta = 1;\n if (i % delta === 0) {\n alpha -= alpha / alphadec;\n radius -= radius / radiusdec;\n rad = radius >> radiusbiasshift;\n\n if (rad <= 1) rad = 0;\n for (j = 0; j < rad; j++)\n radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad));\n }\n }\n }\n\n /*\n Method: buildColormap\n\n 1. initializes network\n 2. trains it\n 3. removes misconceptions\n 4. builds colorindex\n */\n function buildColormap() {\n init();\n learn();\n unbiasnet();\n inxbuild();\n }\n this.buildColormap = buildColormap;\n\n /*\n Method: getColormap\n\n builds colormap from the index\n\n returns array in the format:\n\n >\n > [r, g, b, r, g, b, r, g, b, ..]\n >\n */\n function getColormap() {\n var map = [];\n var index = [];\n\n for (var i = 0; i < netsize; i++)\n index[network[i][3]] = i;\n\n var k = 0;\n for (var l = 0; l < netsize; l++) {\n var j = index[l];\n map[k++] = (network[j][0]);\n map[k++] = (network[j][1]);\n map[k++] = (network[j][2]);\n }\n return map;\n }\n this.getColormap = getColormap;\n\n /*\n Method: lookupRGB\n\n looks for the closest *r*, *g*, *b* color in the map and\n returns its index\n */\n this.lookupRGB = inxsearch;\n}\n\nmodule.exports = NeuQuant;\n","GIFEncoder = require './GIFEncoder.js'\n\nrenderFrame = (frame) ->\n encoder = new GIFEncoder frame.width, frame.height\n\n if frame.index is 0\n encoder.writeHeader()\n else\n encoder.firstFrame = false\n\n encoder.setTransparent frame.transparent\n encoder.setDispose frame.dispose\n encoder.setRepeat frame.repeat\n encoder.setDelay frame.delay\n encoder.setQuality frame.quality\n encoder.setDither frame.dither\n encoder.setGlobalPalette frame.globalPalette\n encoder.addFrame frame.data\n encoder.finish() if frame.last\n if frame.globalPalette == true\n frame.globalPalette = encoder.getGlobalPalette()\n\n stream = encoder.stream()\n frame.data = stream.pages\n frame.cursor = stream.cursor\n frame.pageSize = stream.constructor.pageSize\n\n if frame.canTransfer\n transfer = (page.buffer for page in frame.data)\n self.postMessage frame, transfer\n else\n self.postMessage frame\n\nself.onmessage = (event) -> renderFrame event.data\n"]} \ No newline at end of file diff --git a/img/error.svg b/img/error.svg deleted file mode 100644 index 3d5944e..0000000 --- a/img/error.svg +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - diff --git a/index.html b/index.html index 3fab8c7..7b3007e 100644 --- a/index.html +++ b/index.html @@ -44,10 +44,10 @@

3. Render the GIF

- - - - - + + + + + diff --git a/js/eval.js b/js/eval.js deleted file mode 100644 index 44c0700..0000000 --- a/js/eval.js +++ /dev/null @@ -1,223 +0,0 @@ -"use strict"; -var BinaryPrec; -(function (BinaryPrec) { - BinaryPrec[BinaryPrec["PREC0"] = 0] = "PREC0"; - BinaryPrec[BinaryPrec["PREC1"] = 1] = "PREC1"; - BinaryPrec[BinaryPrec["COUNT_PRECS"] = 2] = "COUNT_PRECS"; -})(BinaryPrec || (BinaryPrec = {})); -var BINARY_OPS = { - '+': { - func: function (lhs, rhs) { return lhs + rhs; }, - prec: BinaryPrec.PREC0 - }, - '-': { - func: function (lhs, rhs) { return lhs - rhs; }, - prec: BinaryPrec.PREC0 - }, - '*': { - func: function (lhs, rhs) { return lhs * rhs; }, - prec: BinaryPrec.PREC1 - }, - '/': { - func: function (lhs, rhs) { return lhs / rhs; }, - prec: BinaryPrec.PREC1 - }, - '%': { - func: function (lhs, rhs) { return lhs % rhs; }, - prec: BinaryPrec.PREC1 - } -}; -var UNARY_OPS = { - '-': function (arg) { return -arg; } -}; -var Lexer = (function () { - function Lexer(src) { - this.src = src; - } - Lexer.prototype.unnext = function (token) { - this.src = token + this.src; - }; - Lexer.prototype.next = function () { - this.src = this.src.trimStart(); - if (this.src.length == 0) { - return null; - } - function is_token_break(c) { - var syntax = '(),'; - return c in BINARY_OPS || c in UNARY_OPS || syntax.includes(c); - } - if (is_token_break(this.src[0])) { - var token_1 = this.src[0]; - this.src = this.src.slice(1); - return token_1; - } - for (var i = 0; i < this.src.length; ++i) { - if (is_token_break(this.src[i]) || this.src[i] == ' ') { - var token_2 = this.src.slice(0, i); - this.src = this.src.slice(i); - return token_2; - } - } - var token = this.src; - this.src = ''; - return token; - }; - return Lexer; -}()); -function parse_primary(lexer) { - var token = lexer.next(); - if (token !== null) { - if (token in UNARY_OPS) { - var operand = parse_expr(lexer); - return { - "kind": "unary_op", - "payload": { - "op": token, - "operand": operand - } - }; - } - else if (token === '(') { - var expr = parse_expr(lexer); - token = lexer.next(); - if (token !== ')') { - throw new Error("Expected ')' but got '" + token + "'"); - } - return expr; - } - else if (token === ')') { - throw new Error("No primary expression starts with ')'"); - } - else { - var next_token = lexer.next(); - if (next_token === '(') { - var args = []; - next_token = lexer.next(); - if (next_token === ')') { - return { - "kind": "funcall", - "payload": { - "name": token, - "args": args - } - }; - } - if (next_token === null) { - throw Error("Unexpected end of input"); - } - lexer.unnext(next_token); - args.push(parse_expr(lexer)); - next_token = lexer.next(); - while (next_token == ',') { - args.push(parse_expr(lexer)); - next_token = lexer.next(); - } - if (next_token !== ')') { - throw Error("Expected ')' but got '" + next_token + "'"); - } - return { - "kind": "funcall", - "payload": { - "name": token, - "args": args - } - }; - } - else { - if (next_token !== null) { - lexer.unnext(next_token); - } - return { - "kind": "symbol", - "payload": { - "value": token - } - }; - } - } - } - else { - throw new Error('Expected primary expression but reached the end of the input'); - } -} -function parse_expr(lexer, prec) { - if (prec === void 0) { prec = BinaryPrec.PREC0; } - if (prec >= BinaryPrec.COUNT_PRECS) { - return parse_primary(lexer); - } - var lhs = parse_expr(lexer, prec + 1); - var op_token = lexer.next(); - if (op_token !== null) { - if (op_token in BINARY_OPS && BINARY_OPS[op_token].prec == prec) { - var rhs = parse_expr(lexer, prec); - return { - "kind": "binary_op", - "payload": { - "op": op_token, - "lhs": lhs, - "rhs": rhs - } - }; - } - else { - lexer.unnext(op_token); - } - } - return lhs; -} -function compile_expr(src) { - var lexer = new Lexer(src); - var result = parse_expr(lexer); - var token = lexer.next(); - if (token !== null) { - console.log(typeof (token)); - console.log(token); - throw new Error("Unexpected token '" + token + "'"); - } - return result; -} -function run_expr(expr, user_context) { - var _a; - if (user_context === void 0) { user_context = {}; } - console.assert(typeof (expr) === 'object'); - switch (expr.kind) { - case 'symbol': { - var symbol = expr.payload; - var value = symbol.value; - var number = Number(value); - if (isNaN(number)) { - if (user_context.vars && value in user_context.vars) { - return user_context.vars[value]; - } - throw new Error("Unknown variable '" + value + "'"); - } - else { - return number; - } - } - case 'unary_op': { - var unary_op = expr.payload; - if (unary_op.op in UNARY_OPS) { - return UNARY_OPS[unary_op.op](run_expr(unary_op.operand, user_context)); - } - throw new Error("Unknown unary operator '" + unary_op.op + "'"); - } - case 'binary_op': { - var binary_op = expr.payload; - if (binary_op.op in BINARY_OPS) { - return BINARY_OPS[binary_op.op].func(run_expr(binary_op.lhs, user_context), run_expr(binary_op.rhs, user_context)); - } - throw new Error("Unknown binary operator '" + binary_op.op + "'"); - } - case 'funcall': { - var funcall = expr.payload; - if (user_context.funcs && funcall.name in user_context.funcs) { - return (_a = user_context.funcs)[funcall.name].apply(_a, funcall.args.map(function (arg) { return run_expr(arg, user_context); })); - } - throw new Error("Unknown function '" + funcall.name + "'"); - } - default: { - throw new Error("Unexpected AST node '" + expr.kind + "'"); - } - } -} diff --git a/js/filters.js b/js/filters.js deleted file mode 100644 index d634ca5..0000000 --- a/js/filters.js +++ /dev/null @@ -1,241 +0,0 @@ -"use strict"; -var filters = { - "Hop": { - "transparent": 0x00FF00 + "", - "duration": "interval * 2", - "params": { - "interval": { - "label": "Interval", - "type": "float", - "init": 0.85, - "min": 0.01, - "max": 2.00, - "step": 0.01 - }, - "ground": { - "label": "Ground", - "type": "float", - "init": 0.5, - "min": -1.0, - "max": 1.0, - "step": 0.01 - }, - "scale": { - "label": "Scale", - "type": "float", - "init": 0.40, - "min": 0.0, - "max": 1.0, - "step": 0.01 - }, - "jump_height": { - "label": "Jump Height", - "type": "float", - "init": 4.0, - "min": 1.0, - "max": 10.0, - "step": 0.01 - }, - "hops": { - "label": "Hops Count", - "type": "float", - "init": 2.0, - "min": 1.0, - "max": 5.0, - "step": 1.0 - } - }, - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\nuniform float time;\nuniform vec2 emoteSize;\n\nuniform float interval;\nuniform float ground;\nuniform float scale;\nuniform float jump_height;\nuniform float hops;\n\nvarying vec2 uv;\n\nfloat sliding_from_left_to_right(float time_interval) {\n return (mod(time, time_interval) - time_interval * 0.5) / (time_interval * 0.5);\n}\n\nfloat flipping_directions(float time_interval) {\n return 1.0 - 2.0 * mod(floor(time / time_interval), 2.0);\n}\n\nvoid main() {\n float x_time_interval = interval;\n float y_time_interval = x_time_interval / (2.0 * hops);\n vec2 offset = vec2(\n sliding_from_left_to_right(x_time_interval) * flipping_directions(x_time_interval) * (1.0 - scale),\n ((sliding_from_left_to_right(y_time_interval) * flipping_directions(y_time_interval) + 1.0) / jump_height) - ground);\n\n gl_Position = vec4(\n meshPosition * scale + offset,\n 0.0,\n 1.0);\n\n uv = (meshPosition + vec2(1.0, 1.0)) / 2.0;\n\n uv.x = (flipping_directions(x_time_interval) + 1.0) / 2.0 - uv.x * flipping_directions(x_time_interval);\n}\n", - "fragment": "#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_FragColor = texture2D(emote, vec2(uv.x, 1.0 - uv.y));\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Hopper": { - "transparent": 0x00FF00 + "", - "duration": "0.85", - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\nuniform float time;\n\nvarying vec2 uv;\n\nfloat sliding_from_left_to_right(float time_interval) {\n return (mod(time, time_interval) - time_interval * 0.5) / (time_interval * 0.5);\n}\n\nfloat flipping_directions(float time_interval) {\n return 1.0 - 2.0 * mod(floor(time / time_interval), 2.0);\n}\n\nvoid main() {\n float scale = 0.40;\n float hops = 2.0;\n float x_time_interval = 0.85 / 2.0;\n float y_time_interval = x_time_interval / (2.0 * hops);\n float height = 0.5;\n vec2 offset = vec2(\n sliding_from_left_to_right(x_time_interval) * flipping_directions(x_time_interval) * (1.0 - scale),\n ((sliding_from_left_to_right(y_time_interval) * flipping_directions(y_time_interval) + 1.0) / 4.0) - height);\n\n gl_Position = vec4(\n meshPosition * scale + offset,\n 0.0,\n 1.0);\n\n uv = (meshPosition + vec2(1.0, 1.0)) / 2.0;\n\n uv.x = (flipping_directions(x_time_interval) + 1.0) / 2.0 - uv.x * flipping_directions(x_time_interval);\n}\n", - "fragment": "#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_FragColor = texture2D(emote, vec2(uv.x, 1.0 - uv.y));\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Overheat": { - "transparent": 0x00FF00 + "", - "duration": "0.85 / 8.0 * 2.0", - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\nuniform float time;\n\nvarying vec2 uv;\n\nfloat sliding_from_left_to_right(float time_interval) {\n return (mod(time, time_interval) - time_interval * 0.5) / (time_interval * 0.5);\n}\n\nfloat flipping_directions(float time_interval) {\n return 1.0 - 2.0 * mod(floor(time / time_interval), 2.0);\n}\n\nvoid main() {\n float scale = 0.40;\n float hops = 2.0;\n float x_time_interval = 0.85 / 8.0;\n float y_time_interval = x_time_interval / (2.0 * hops);\n float height = 0.5;\n vec2 offset = vec2(\n sliding_from_left_to_right(x_time_interval) * flipping_directions(x_time_interval) * (1.0 - scale),\n ((sliding_from_left_to_right(y_time_interval) * flipping_directions(y_time_interval) + 1.0) / 4.0) - height);\n\n gl_Position = vec4(\n meshPosition * scale + offset,\n 0.0,\n 1.0);\n\n uv = (meshPosition + vec2(1.0, 1.0)) / 2.0;\n\n uv.x = (flipping_directions(x_time_interval) + 1.0) / 2.0 - uv.x * flipping_directions(x_time_interval);\n}\n", - "fragment": "#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_FragColor = texture2D(emote, vec2(uv.x, 1.0 - uv.y)) * vec4(1.0, 0.0, 0.0, 1.0);\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Bounce": { - "transparent": 0x00FF00 + "", - "duration": "Math.PI / period", - "params": { - "period": { - "type": "float", - "init": 5.0, - "min": 1.0, - "max": 10.0, - "step": 0.1 - }, - "scale": { - "type": "float", - "init": 0.30, - "min": 0.0, - "max": 1.0, - "step": 0.01 - } - }, - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform float period;\nuniform float scale;\n\nvarying vec2 uv;\n\nvoid main() {\n vec2 offset = vec2(0.0, (2.0 * abs(sin(time * period)) - 1.0) * (1.0 - scale));\n gl_Position = vec4(meshPosition * scale + offset, 0.0, 1.0);\n uv = (meshPosition + 1.0) / 2.0;\n}\n", - "fragment": "\n#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_FragColor = texture2D(emote, vec2(uv.x, 1.0 - uv.y));\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Circle": { - "transparent": 0x00FF00 + "", - "duration": "Math.PI / 4.0", - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\n\nuniform vec2 resolution;\nuniform float time;\n\nvarying vec2 uv;\n\nvec2 rotate(vec2 v, float a) {\n\tfloat s = sin(a);\n\tfloat c = cos(a);\n\tmat2 m = mat2(c, -s, s, c);\n\treturn m * v;\n}\n\nvoid main() {\n float scale = 0.30;\n float period_interval = 8.0;\n float pi = 3.141592653589793238;\n vec2 outer_circle = vec2(cos(period_interval * time), sin(period_interval * time)) * (1.0 - scale);\n vec2 inner_circle = rotate(meshPosition * scale, (-period_interval * time) + pi / 2.0);\n gl_Position = vec4(\n inner_circle + outer_circle,\n 0.0,\n 1.0);\n uv = (meshPosition + 1.0) / 2.0;\n}\n", - "fragment": "\n#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nvoid main() {\n float speed = 1.0;\n gl_FragColor = texture2D(emote, vec2(uv.x, 1.0 - uv.y));\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Slide": { - "transparent": 0x00FF00 + "", - "duration": "0.85 * 2", - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\nuniform float time;\n\nvarying vec2 uv;\n\nfloat sliding_from_left_to_right(float time_interval) {\n return (mod(time, time_interval) - time_interval * 0.5) / (time_interval * 0.5);\n}\n\nfloat flipping_directions(float time_interval) {\n return 1.0 - 2.0 * mod(floor(time / time_interval), 2.0);\n}\n\nvoid main() {\n float scale = 0.40;\n float hops = 2.0;\n float x_time_interval = 0.85;\n float y_time_interval = x_time_interval / (2.0 * hops);\n float height = 0.5;\n vec2 offset = vec2(\n sliding_from_left_to_right(x_time_interval) * flipping_directions(x_time_interval) * (1.0 - scale),\n - height);\n\n gl_Position = vec4(\n meshPosition * scale + offset,\n 0.0,\n 1.0);\n\n uv = (meshPosition + vec2(1.0, 1.0)) / 2.0;\n\n uv.x = (flipping_directions(x_time_interval) + 1.0) / 2.0 - uv.x * flipping_directions(x_time_interval);\n}\n", - "fragment": "#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_FragColor = texture2D(emote, vec2(uv.x, 1.0 - uv.y));\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Laughing": { - "transparent": 0x00FF00 + "", - "duration": "Math.PI / 12.0", - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\nuniform float time;\n\nvarying vec2 uv;\n\nvoid main() {\n float a = 0.3;\n float t = (sin(24.0 * time) * a + a) / 2.0;\n\n gl_Position = vec4(\n meshPosition - vec2(0.0, t),\n 0.0,\n 1.0);\n uv = (meshPosition + vec2(1.0, 1.0)) / 2.0;\n}\n", - "fragment": "#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_FragColor = texture2D(emote, vec2(uv.x, 1.0 - uv.y));\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Blob": { - "transparent": 0x00FF00 + "", - "duration": "Math.PI / 3", - "vertex": "#version 100\n\nprecision mediump float;\n\nattribute vec2 meshPosition;\n\nuniform vec2 resolution;\nuniform float time;\n\nvarying vec2 uv;\n\nvoid main() {\n float stretch = sin(6.0 * time) * 0.5 + 1.0;\n\n vec2 offset = vec2(0.0, 1.0 - stretch);\n gl_Position = vec4(\n meshPosition * vec2(stretch, 2.0 - stretch) + offset,\n 0.0,\n 1.0);\n uv = (meshPosition + vec2(1.0, 1.0)) / 2.0;\n}\n", - "fragment": "#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_FragColor = texture2D(emote, vec2(uv.x, 1.0 - uv.y));\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Go": { - "transparent": 0x00FF00 + "", - "duration": "1 / 4", - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\n\nuniform vec2 resolution;\nuniform float time;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_Position = vec4(meshPosition, 0.0, 1.0);\n uv = (meshPosition + 1.0) / 2.0;\n}\n", - "fragment": "\n#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nfloat slide(float speed, float value) {\n return mod(value - speed * time, 1.0);\n}\n\nvoid main() {\n float speed = 4.0;\n gl_FragColor = texture2D(emote, vec2(slide(speed, uv.x), 1.0 - uv.y));\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Elevator": { - "transparent": 0x00FF00 + "", - "duration": "1 / 4", - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\n\nuniform vec2 resolution;\nuniform float time;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_Position = vec4(meshPosition, 0.0, 1.0);\n uv = (meshPosition + 1.0) / 2.0;\n}\n", - "fragment": "\n#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nfloat slide(float speed, float value) {\n return mod(value - speed * time, 1.0);\n}\n\nvoid main() {\n float speed = 4.0;\n gl_FragColor = texture2D(\n emote,\n vec2(uv.x, slide(speed, 1.0 - uv.y)));\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Rain": { - "transparent": 0x00FF00 + "", - "duration": "1", - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\n\nuniform vec2 resolution;\nuniform float time;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_Position = vec4(meshPosition, 0.0, 1.0);\n uv = (meshPosition + 1.0) / 2.0;\n}\n", - "fragment": "\n#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nfloat slide(float speed, float value) {\n return mod(value - speed * time, 1.0);\n}\n\nvoid main() {\n float speed = 1.0;\n gl_FragColor = texture2D(\n emote,\n vec2(mod(4.0 * slide(speed, uv.x), 1.0),\n mod(4.0 * slide(speed, 1.0 - uv.y), 1.0)));\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Pride": { - "transparent": null, - "duration": "2.0", - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\n\nuniform vec2 resolution;\nuniform float time;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_Position = vec4(meshPosition, 0.0, 1.0);\n uv = (meshPosition + 1.0) / 2.0;\n}\n", - "fragment": "\n#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nvec3 hsl2rgb(vec3 c) {\n vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0);\n return c.z + c.y * (rgb-0.5)*(1.0-abs(2.0*c.z-1.0));\n}\n\nvoid main() {\n float speed = 1.0;\n\n vec4 pixel = texture2D(emote, vec2(uv.x, 1.0 - uv.y));\n pixel.w = floor(pixel.w + 0.5);\n pixel = vec4(mix(vec3(1.0), pixel.xyz, pixel.w), 1.0);\n vec4 rainbow = vec4(hsl2rgb(vec3((time - uv.x - uv.y) * 0.5, 1.0, 0.80)), 1.0);\n gl_FragColor = pixel * rainbow;\n}\n" - }, - "Hard": { - "transparent": 0x00FF00 + "", - "duration": "2.0 * Math.PI / intensity", - "params": { - "zoom": { - "type": "float", - "init": 1.4, - "min": 0.0, - "max": 6.9, - "step": 0.1 - }, - "intensity": { - "type": "float", - "init": 32.0, - "min": 1.0, - "max": 42.0, - "step": 1.0 - }, - "amplitude": { - "type": "float", - "init": 1.0 / 8.0, - "min": 0.0, - "max": 1.0 / 2.0, - "step": 0.001 - } - }, - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform float zoom;\nuniform float intensity;\nuniform float amplitude;\n\nvarying vec2 uv;\n\nvoid main() {\n vec2 shaking = vec2(cos(intensity * time), sin(intensity * time)) * amplitude;\n gl_Position = vec4(meshPosition * zoom + shaking, 0.0, 1.0);\n uv = (meshPosition + 1.0) / 2.0;\n}\n", - "fragment": "\n#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_FragColor = texture2D(emote, vec2(uv.x, 1.0 - uv.y));\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Peek": { - "transparent": 0x00FF00 + "", - "duration": "2.0 * Math.PI", - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\n\nuniform vec2 resolution;\nuniform float time;\n\nvarying vec2 uv;\n\nvoid main() {\n float time_clipped= mod(time * 2.0, (4.0 * 3.14));\n\n float s1 = float(time_clipped < (2.0 * 3.14));\n float s2 = 1.0 - s1;\n\n float hold1 = float(time_clipped > (0.5 * 3.14) && time_clipped < (2.0 * 3.14));\n float hold2 = 1.0 - float(time_clipped > (2.5 * 3.14) && time_clipped < (4.0 * 3.14));\n\n float cycle_1 = 1.0 - ((s1 * sin(time_clipped) * (1.0 - hold1)) + hold1);\n float cycle_2 = s2 * hold2 * (sin(time_clipped) - 1.0); \n\n gl_Position = vec4(meshPosition.x + 1.0 + cycle_1 + cycle_2 , meshPosition.y, 0.0, 1.0);\n uv = (meshPosition + 1.0) / 2.0;\n}\n", - "fragment": "\n#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_FragColor = texture2D(emote, vec2(uv.x, 1.0 - uv.y));\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - }, - "Matrix": { - "transparent": null, - "duration": "3.0", - "vertex": "\n #version 100\n precision mediump float;\n\n attribute vec2 meshPosition;\n\n uniform vec2 resolution;\n uniform float time;\n\n varying vec2 _uv;\n\n void main()\n {\n _uv = (meshPosition + 1.0) / 2.0;\n gl_Position = vec4(meshPosition.x, meshPosition.y, 0.0, 1.0);\n }\n ", - "fragment": "\n #version 100\n precision mediump float;\n\n uniform vec2 resolution;\n uniform float time;\n uniform sampler2D emote;\n\n varying vec2 _uv;\n\n float clamp01(float value)\n {\n return clamp(value, 0.0, 1.0);\n }\n\n float sdf_zero(vec2 uv)\n {\n float inside = step(0.15, abs(uv.x)) + step(0.3, abs(uv.y));\n float outside = step(0.35, abs(uv.x)) + step(0.4, abs(uv.y));\n return clamp01(inside) - clamp01(outside);\n }\n\n float sdf_one(vec2 uv)\n {\n float top = step(0.25, -uv.y) * step(0.0, uv.x);\n float inside = (step(0.2, uv.x) + top);\n float outside = step(0.35, abs(uv.x)) + step(0.4, abs(uv.y));\n return clamp01(clamp01(inside) - clamp01(outside));\n }\n\n // Random float. No precomputed gradients mean this works for any number of grid coordinates\n float random01(vec2 n)\n {\n float random = 2920.0 * sin(n.x * 21942.0 + n.y * 171324.0 + 8912.0) *\n cos(n.x * 23157.0 * n.y * 217832.0 + 9758.0);\n \n return (sin(random) + 1.0) / 2.0;\n }\n\n float loop_time(float time_frame)\n {\n float times = floor(time / time_frame);\n return time - (times * time_frame);\n }\n\n void main()\n {\n vec2 uv = _uv;\n uv.y = 1.0 - _uv.y;\n \n float number_of_numbers = 8.0;\n float number_change_rate = 2.0;\n float amount_of_numbers = 0.6; // from 0 - 1\n \n vec4 texture_color = texture2D(emote, uv);\n vec4 number_color = vec4(0, 0.7, 0, 1);\n\n float looped_time = loop_time(3.0); \n\n vec2 translation = vec2(0, looped_time * -8.0);\n\n vec2 pos_idx = floor(uv * number_of_numbers + translation);\n float rnd_number = step(0.5, random01(pos_idx + floor(looped_time * number_change_rate)));\n float rnd_show = step(1.0 - amount_of_numbers, random01(pos_idx + vec2(99,99)));\n\n vec2 nuv = uv * number_of_numbers + translation;\n nuv = fract(nuv);\n\n float one = sdf_one(nuv - 0.5) * rnd_number;\n float zero = sdf_zero(nuv - 0.5) * (1.0 - rnd_number);\n float number = (one + zero) * rnd_show;\n\n float is_texture = 1.0 - number;\n float is_number = number;\n\n vec4 col = (texture_color * is_texture) + (number_color * is_number);\n\n gl_FragColor = col;\n gl_FragColor.w = 1.0;\n }\n " - }, - "Flag": { - "transparent": 0x00FF00 + "", - "duration": "Math.PI", - "vertex": "\n #version 100\n precision mediump float;\n\n attribute vec2 meshPosition;\n\n uniform vec2 resolution;\n uniform float time;\n\n varying vec2 _uv;\n\n void main()\n {\n _uv = (meshPosition + 1.0) / 2.0;\n _uv.y = 1.0 - _uv.y;\n gl_Position = vec4(meshPosition.x, meshPosition.y, 0.0, 1.0);\n }\n ", - "fragment": "\n #version 100\n precision mediump float;\n\n varying vec2 _uv;\n uniform sampler2D emote;\n uniform float time;\n\n float sin01(float value)\n {\n return (sin(value) + 1.0) / 2.0;\n }\n\n //pos is left bottom point.\n float sdf_rect(vec2 pos, vec2 size, vec2 uv)\n {\n float left = pos.x;\n float right = pos.x + size.x;\n float bottom = pos.y;\n float top = pos.y + size.y;\n return (step(bottom, uv.y) - step(top, uv.y)) * (step(left, uv.x) - step(right, uv.x)); \n }\n\n void main() {\n float stick_width = 0.1;\n float flag_height = 0.75;\n float wave_size = 0.08;\n vec4 stick_color = vec4(107.0 / 256.0, 59.0 / 256.0, 9.0 / 256.0,1);\n \n vec2 flag_uv = _uv;\n flag_uv.x = (1.0 / (1.0 - stick_width)) * (flag_uv.x - stick_width);\n flag_uv.y *= 1.0 / flag_height;\n\n float flag_close_to_stick = smoothstep(0.0, 0.5, flag_uv.x);\n flag_uv.y += sin((-time * 2.0) + (flag_uv.x * 8.0)) * flag_close_to_stick * wave_size;\n\n float is_flag = sdf_rect(vec2(0,0), vec2(1.0, 1.0), flag_uv);\n float is_flag_stick = sdf_rect(vec2(0.0, 0.0), vec2(stick_width, 1), _uv);\n\n vec4 emote_color = texture2D(emote, flag_uv);\n vec4 texture_color = (emote_color * is_flag) + (stick_color * is_flag_stick);\n\n gl_FragColor = texture_color;\n }\n " - }, - "Thanosed": { - "transparent": 0x00FF00 + "", - "duration": "duration", - "params": { - "duration": { - "type": "float", - "init": 6.0, - "min": 1.0, - "max": 16.0, - "step": 1.0 - }, - "delay": { - "type": "float", - "init": 0.2, - "min": 0.0, - "max": 1.0, - "step": 0.1 - }, - "pixelization": { - "type": "float", - "init": 1.0, - "min": 1.0, - "max": 3.0, - "step": 1.0 - } - }, - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\n\nuniform vec2 resolution;\nuniform float time;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_Position = vec4(meshPosition, 0.0, 1.0);\n uv = (meshPosition + 1.0) / 2.0;\n}\n", - "fragment": "\n#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\nuniform float duration;\nuniform float delay;\nuniform float pixelization;\n\nuniform sampler2D emote;\n\nvarying vec2 uv;\n\n// https://www.aussiedwarf.com/2017/05/09/Random10Bit.html\nfloat rand(vec2 co){\n vec3 product = vec3( sin( dot(co, vec2(0.129898,0.78233))),\n sin( dot(co, vec2(0.689898,0.23233))),\n sin( dot(co, vec2(0.434198,0.51833))) );\n vec3 weighting = vec3(4.37585453723, 2.465973, 3.18438);\n return fract(dot(weighting, product));\n}\n\nvoid main() {\n float pixelated_resolution = 112.0 / pixelization;\n vec2 pixelated_uv = floor(uv * pixelated_resolution);\n float noise = (rand(pixelated_uv) + 1.0) / 2.0;\n float slope = (0.2 + noise * 0.8) * (1.0 - (0.0 + uv.x * 0.5));\n float time_interval = 1.1 + delay * 2.0;\n float progress = 0.2 + delay + slope - mod(time_interval * time / duration, time_interval);\n float mask = progress > 0.1 ? 1.0 : 0.0;\n vec4 pixel = texture2D(emote, vec2(uv.x * (progress > 0.5 ? 1.0 : progress * 2.0), 1.0 - uv.y));\n pixel.w = floor(pixel.w + 0.5);\n gl_FragColor = pixel * vec4(vec3(1.0), mask);\n}\n" - }, - "Ripple": { - "transparent": 0x00FF00 + "", - "duration": "2 * Math.PI / b", - "params": { - "a": { - "label": "Wave Length", - "type": "float", - "init": 12.0, - "min": 0.01, - "max": 24.0, - "step": 0.01 - }, - "b": { - "label": "Time Freq", - "type": "float", - "init": 4.0, - "min": 0.01, - "max": 8.0, - "step": 0.01 - }, - "c": { - "label": "Amplitude", - "type": "float", - "init": 0.03, - "min": 0.01, - "max": 0.06, - "step": 0.01 - } - }, - "vertex": "#version 100\nprecision mediump float;\n\nattribute vec2 meshPosition;\n\nuniform vec2 resolution;\nuniform float time;\n\nvarying vec2 uv;\n\nvoid main() {\n gl_Position = vec4(meshPosition, 0.0, 1.0);\n uv = (meshPosition + 1.0) / 2.0;\n}\n", - "fragment": "#version 100\n\nprecision mediump float;\n\nuniform vec2 resolution;\nuniform float time;\n\nuniform sampler2D emote;\n\nuniform float a;\nuniform float b;\nuniform float c;\n\nvarying vec2 uv;\n\nvoid main() {\n vec2 pos = vec2(uv.x, 1.0 - uv.y);\n vec2 center = vec2(0.5);\n vec2 dir = pos - center;\n float x = length(dir);\n float y = sin(x + time);\n vec4 pixel = texture2D(emote, pos + cos(x*a - time*b)*c*(dir/x));\n gl_FragColor = pixel;\n gl_FragColor.w = floor(gl_FragColor.w + 0.5);\n}\n" - } -}; diff --git a/js/grecha.js b/js/grecha.js deleted file mode 100644 index 879ca3c..0000000 --- a/js/grecha.js +++ /dev/null @@ -1,124 +0,0 @@ -"use strict"; -var __spreadArray = (this && this.__spreadArray) || function (to, from) { - for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) - to[j] = from[i]; - return to; -}; -var LOREM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; -function tag(name) { - var children = []; - for (var _i = 1; _i < arguments.length; _i++) { - children[_i - 1] = arguments[_i]; - } - var result = document.createElement(name); - for (var _a = 0, children_1 = children; _a < children_1.length; _a++) { - var child = children_1[_a]; - if (typeof (child) === 'string') { - result.appendChild(document.createTextNode(child)); - } - else { - result.appendChild(child); - } - } - result.att$ = function (name, value) { - this.setAttribute(name, value); - return this; - }; - result.onclick$ = function (callback) { - this.onclick = callback; - return this; - }; - return result; -} -function canvas() { - var children = []; - for (var _i = 0; _i < arguments.length; _i++) { - children[_i] = arguments[_i]; - } - return tag.apply(void 0, __spreadArray(["canvas"], children)); -} -function h1() { - var children = []; - for (var _i = 0; _i < arguments.length; _i++) { - children[_i] = arguments[_i]; - } - return tag.apply(void 0, __spreadArray(["h1"], children)); -} -function h2() { - var children = []; - for (var _i = 0; _i < arguments.length; _i++) { - children[_i] = arguments[_i]; - } - return tag.apply(void 0, __spreadArray(["h2"], children)); -} -function h3() { - var children = []; - for (var _i = 0; _i < arguments.length; _i++) { - children[_i] = arguments[_i]; - } - return tag.apply(void 0, __spreadArray(["h3"], children)); -} -function p() { - var children = []; - for (var _i = 0; _i < arguments.length; _i++) { - children[_i] = arguments[_i]; - } - return tag.apply(void 0, __spreadArray(["p"], children)); -} -function a() { - var children = []; - for (var _i = 0; _i < arguments.length; _i++) { - children[_i] = arguments[_i]; - } - return tag.apply(void 0, __spreadArray(["a"], children)); -} -function div() { - var children = []; - for (var _i = 0; _i < arguments.length; _i++) { - children[_i] = arguments[_i]; - } - return tag.apply(void 0, __spreadArray(["div"], children)); -} -function span() { - var children = []; - for (var _i = 0; _i < arguments.length; _i++) { - children[_i] = arguments[_i]; - } - return tag.apply(void 0, __spreadArray(["span"], children)); -} -function select() { - var children = []; - for (var _i = 0; _i < arguments.length; _i++) { - children[_i] = arguments[_i]; - } - return tag.apply(void 0, __spreadArray(["select"], children)); -} -function img(src) { - return tag("img").att$("src", src); -} -function input(type) { - return tag("input").att$("type", type); -} -function router(routes) { - var result = div(); - function syncHash() { - var hashLocation = document.location.hash.split('#')[1]; - if (!hashLocation) { - hashLocation = '/'; - } - if (!(hashLocation in routes)) { - var route404 = '/404'; - console.assert(route404 in routes); - hashLocation = route404; - } - while (result.firstChild) { - result.removeChild(result.lastChild); - } - result.appendChild(routes[hashLocation]); - return result; - } - ; - syncHash(); - window.addEventListener("hashchange", syncHash); - return result; -} diff --git a/js/index.js b/js/index.js deleted file mode 100644 index 5d46558..0000000 --- a/js/index.js +++ /dev/null @@ -1,431 +0,0 @@ -"use strict"; -var feature_params = false; -var vertexAttribs = { - "meshPosition": 0 -}; -var TRIANGLE_PAIR = 2; -var TRIANGLE_VERTICIES = 3; -var VEC2_COUNT = 2; -var VEC2_X = 0; -var VEC2_Y = 1; -var CANVAS_WIDTH = 112; -var CANVAS_HEIGHT = 112; -function compileShaderSource(gl, source, shaderType) { - function shaderTypeToString() { - switch (shaderType) { - case gl.VERTEX_SHADER: return 'Vertex'; - case gl.FRAGMENT_SHADER: return 'Fragment'; - default: return shaderType; - } - } - var shader = gl.createShader(shaderType); - if (shader === null) { - throw new Error("Could not create a new shader"); - } - gl.shaderSource(shader, source); - gl.compileShader(shader); - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - throw new Error("Could not compile " + shaderTypeToString() + " shader: " + gl.getShaderInfoLog(shader)); - } - return shader; -} -function linkShaderProgram(gl, shaders, vertexAttribs) { - var program = gl.createProgram(); - if (program === null) { - throw new Error('Could not create a new shader program'); - } - for (var _i = 0, shaders_1 = shaders; _i < shaders_1.length; _i++) { - var shader = shaders_1[_i]; - gl.attachShader(program, shader); - } - for (var vertexName in vertexAttribs) { - gl.bindAttribLocation(program, vertexAttribs[vertexName], vertexName); - } - gl.linkProgram(program); - if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { - throw new Error("Could not link shader program: " + gl.getProgramInfoLog(program)); - } - return program; -} -function createTextureFromImage(gl, image) { - var textureId = gl.createTexture(); - if (textureId === null) { - throw new Error('Could not create a new WebGL texture'); - } - gl.bindTexture(gl.TEXTURE_2D, textureId); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - return textureId; -} -function loadFilterProgram(gl, filter, vertexAttribs) { - var _a; - var vertexShader = compileShaderSource(gl, filter.vertex, gl.VERTEX_SHADER); - var fragmentShader = compileShaderSource(gl, filter.fragment, gl.FRAGMENT_SHADER); - var id = linkShaderProgram(gl, [vertexShader, fragmentShader], vertexAttribs); - gl.deleteShader(vertexShader); - gl.deleteShader(fragmentShader); - gl.useProgram(id); - var uniforms = { - "resolution": gl.getUniformLocation(id, 'resolution'), - "time": gl.getUniformLocation(id, 'time'), - "emoteSize": gl.getUniformLocation(id, 'emoteSize') - }; - var paramsPanel = div().att$("class", "widget-element"); - var paramsInputs = {}; - var _loop_1 = function (paramName) { - if (paramName in uniforms) { - throw new Error("Redefinition of existing uniform parameter " + paramName); - } - switch (filter.params[paramName].type) { - case "float": - { - var valuePreview_1 = span(filter.params[paramName].init.toString()); - var valueInput = input("range"); - if (filter.params[paramName].min !== undefined) { - valueInput.att$("min", filter.params[paramName].min); - } - if (filter.params[paramName].max !== undefined) { - valueInput.att$("max", filter.params[paramName].max); - } - if (filter.params[paramName].step !== undefined) { - valueInput.att$("step", filter.params[paramName].step); - } - if (filter.params[paramName].init !== undefined) { - valueInput.att$("value", filter.params[paramName].init); - } - paramsInputs[paramName] = valueInput; - valueInput.oninput = function () { - valuePreview_1.innerText = this.value; - paramsPanel.dispatchEvent(new CustomEvent("paramsChanged")); - }; - var label = (_a = filter.params[paramName].label) !== null && _a !== void 0 ? _a : paramName; - paramsPanel.appendChild(div(span(label + ": "), valuePreview_1, div(valueInput))); - } - break; - default: { - throw new Error("Filter parameters do not support type " + filter.params[paramName].type); - } - } - uniforms[paramName] = gl.getUniformLocation(id, paramName); - }; - for (var paramName in filter.params) { - _loop_1(paramName); - } - paramsPanel.paramsSnapshot$ = function () { - var snapshot = {}; - for (var paramName in paramsInputs) { - snapshot[paramName] = { - "uniform": uniforms[paramName], - "value": Number(paramsInputs[paramName].value) - }; - } - return snapshot; - }; - return { - "id": id, - "uniforms": uniforms, - "duration": compile_expr(filter.duration), - "transparent": filter.transparent, - "paramsPanel": paramsPanel - }; -} -function ImageSelector() { - var imageInput = input("file"); - var imagePreview = img("img/tsodinClown.png") - .att$("class", "widget-element") - .att$("width", CANVAS_WIDTH); - var root = div(div(imageInput).att$("class", "widget-element"), imagePreview).att$("class", "widget"); - root.selectedImage$ = function () { - return imagePreview; - }; - root.selectedFileName$ = function () { - function removeFileNameExt(fileName) { - if (fileName.includes('.')) { - return fileName.split('.').slice(0, -1).join('.'); - } - else { - return fileName; - } - } - var file = imageInput.files[0]; - return file ? removeFileNameExt(file.name) : 'result'; - }; - root.updateFiles$ = function (files) { - imageInput.files = files; - imageInput.onchange(); - }; - imagePreview.addEventListener('load', function () { - root.dispatchEvent(new CustomEvent("imageSelected", { - detail: { - imageData: this - } - })); - }); - imagePreview.addEventListener('error', function () { - imageInput.value = ''; - this.src = 'img/error.png'; - }); - imageInput.onchange = function () { - imagePreview.src = URL.createObjectURL(this.files[0]); - }; - return root; -} -function FilterList() { - var root = select(); - for (var name_1 in filters) { - root.add(new Option(name_1)); - } - root.selectedFilter$ = function () { - return filters[root.selectedOptions[0].value]; - }; - root.onchange = function () { - root.dispatchEvent(new CustomEvent('filterChanged', { - detail: { - filter: root.selectedFilter$() - } - })); - }; - root.addEventListener('wheel', function (e) { - e.preventDefault(); - if (e.deltaY < 0) { - root.selectedIndex = Math.max(root.selectedIndex - 1, 0); - } - if (e.deltaY > 0) { - root.selectedIndex = Math.min(root.selectedIndex + 1, root.length - 1); - } - root.onchange(); - }); - return root; -} -function FilterSelector() { - var filterList_ = FilterList(); - var filterPreview = canvas() - .att$("width", CANVAS_WIDTH) - .att$("height", CANVAS_HEIGHT); - var root = div(div("Filter: ", filterList_) - .att$("class", "widget-element"), filterPreview.att$("class", "widget-element")).att$("class", "widget"); - var gl = filterPreview.getContext("webgl", { antialias: false, alpha: false }); - if (!gl) { - throw new Error("Could not initialize WebGL context"); - } - { - gl.enable(gl.BLEND); - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - { - var meshPositionBufferData = new Float32Array(TRIANGLE_PAIR * TRIANGLE_VERTICIES * VEC2_COUNT); - for (var triangle = 0; triangle < TRIANGLE_PAIR; ++triangle) { - for (var vertex = 0; vertex < TRIANGLE_VERTICIES; ++vertex) { - var quad = triangle + vertex; - var index = triangle * TRIANGLE_VERTICIES * VEC2_COUNT + - vertex * VEC2_COUNT; - meshPositionBufferData[index + VEC2_X] = (2 * (quad & 1) - 1); - meshPositionBufferData[index + VEC2_Y] = (2 * ((quad >> 1) & 1) - 1); - } - } - var meshPositionBuffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, meshPositionBuffer); - gl.bufferData(gl.ARRAY_BUFFER, meshPositionBufferData, gl.STATIC_DRAW); - var meshPositionAttrib = vertexAttribs['meshPosition']; - gl.vertexAttribPointer(meshPositionAttrib, VEC2_COUNT, gl.FLOAT, false, 0, 0); - gl.enableVertexAttribArray(meshPositionAttrib); - } - } - var emoteImage = undefined; - var emoteTexture = undefined; - var program = undefined; - function syncParams() { - if (program) { - var snapshot = program.paramsPanel.paramsSnapshot$(); - for (var paramName in snapshot) { - gl.uniform1f(snapshot[paramName].uniform, snapshot[paramName].value); - } - } - } - program = loadFilterProgram(gl, filterList_.selectedFilter$(), vertexAttribs); - program.paramsPanel.addEventListener('paramsChanged', syncParams); - if (feature_params) { - root.appendChild(program.paramsPanel); - } - syncParams(); - root.updateImage$ = function (newEmoteImage) { - emoteImage = newEmoteImage; - if (emoteTexture) { - gl.deleteTexture(emoteTexture); - } - emoteTexture = createTextureFromImage(gl, emoteImage); - }; - filterList_.addEventListener('filterChanged', function (e) { - if (program) { - gl.deleteProgram(program.id); - program.paramsPanel.removeEventListener('paramsChanged', syncParams); - if (feature_params) { - root.removeChild(program.paramsPanel); - } - } - program = loadFilterProgram(gl, e.detail.filter, vertexAttribs); - program.paramsPanel.addEventListener('paramsChanged', syncParams); - if (feature_params) { - root.appendChild(program.paramsPanel); - } - syncParams(); - }); - root.render$ = function (filename) { - if (program === undefined) { - console.warn('Could not rendering anything because the filter was not selected'); - return undefined; - } - if (emoteImage == undefined) { - console.warn('Could not rendering anything because the image was not selected'); - return undefined; - } - var gif = new GIF({ - workers: 5, - quality: 10, - width: CANVAS_WIDTH, - height: CANVAS_HEIGHT, - transparent: program.transparent - }); - var context = { - "vars": { - "Math.PI": Math.PI - } - }; - if (context.vars !== undefined) { - var snapshot = program.paramsPanel.paramsSnapshot$(); - for (var paramName in snapshot) { - context.vars[paramName] = snapshot[paramName].value; - } - } - var fps = 30; - var dt = 1.0 / fps; - var duration = Math.min(run_expr(program.duration, context), 60); - var renderProgress = document.getElementById("render-progress"); - if (renderProgress === null) { - throw new Error('Could not find "render-progress"'); - } - var renderSpinner = document.getElementById("render-spinner"); - if (renderSpinner === null) { - throw new Error('Could not find "render-spinner"'); - } - var renderPreview = document.getElementById("render-preview"); - if (renderPreview === null) { - throw new Error('Could not find "render-preview"'); - } - var renderDownload = document.getElementById("render-download"); - if (renderDownload === null) { - throw new Error('Could not find "render-download"'); - } - renderPreview.style.display = "none"; - renderSpinner.style.display = "block"; - var t = 0.0; - while (t <= duration) { - gl.uniform1f(program.uniforms.time, t); - gl.uniform2f(program.uniforms.resolution, CANVAS_WIDTH, CANVAS_HEIGHT); - gl.uniform2f(program.uniforms.emoteSize, emoteImage.width, emoteImage.height); - gl.clearColor(0.0, 1.0, 0.0, 1.0); - gl.clear(gl.COLOR_BUFFER_BIT); - gl.drawArrays(gl.TRIANGLES, 0, TRIANGLE_PAIR * TRIANGLE_VERTICIES); - var pixels = new Uint8ClampedArray(4 * CANVAS_WIDTH * CANVAS_HEIGHT); - gl.readPixels(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, gl.RGBA, gl.UNSIGNED_BYTE, pixels); - { - var center = Math.floor(CANVAS_HEIGHT / 2); - for (var y = 0; y < center; ++y) { - var row = 4 * CANVAS_WIDTH; - for (var x = 0; x < row; ++x) { - var ai = y * 4 * CANVAS_WIDTH + x; - var bi = (CANVAS_HEIGHT - y - 1) * 4 * CANVAS_WIDTH + x; - var a_1 = pixels[ai]; - var b = pixels[bi]; - pixels[ai] = b; - pixels[bi] = a_1; - } - } - } - gif.addFrame(new ImageData(pixels, CANVAS_WIDTH, CANVAS_HEIGHT), { - delay: dt * 1000, - dispose: 2 - }); - renderProgress.style.width = (t / duration) * 50 + "%"; - t += dt; - } - gif.on('finished', function (blob) { - renderPreview.src = URL.createObjectURL(blob); - renderPreview.style.display = "block"; - renderDownload.href = renderPreview.src; - renderDownload.download = filename; - renderDownload.style.display = "block"; - renderSpinner.style.display = "none"; - }); - gif.on('progress', function (p) { - renderProgress.style.width = 50 + p * 50 + "%"; - }); - gif.render(); - return gif; - }; - { - var step_1 = function (timestamp) { - gl.clearColor(0.0, 1.0, 0.0, 1.0); - gl.clear(gl.COLOR_BUFFER_BIT); - if (program && emoteImage) { - gl.uniform1f(program.uniforms.time, timestamp * 0.001); - gl.uniform2f(program.uniforms.resolution, filterPreview.width, filterPreview.height); - gl.uniform2f(program.uniforms.emoteSize, emoteImage.width, emoteImage.height); - gl.drawArrays(gl.TRIANGLES, 0, TRIANGLE_PAIR * TRIANGLE_VERTICIES); - } - window.requestAnimationFrame(step_1); - }; - window.requestAnimationFrame(step_1); - } - return root; -} -window.onload = function () { - if ("serviceWorker" in navigator) { - navigator.serviceWorker.register('serviceworker.js').then(function (registration) { - console.log("Registered a Service Worker ", registration); - }, function (error) { - console.error("Could not register a Service Worker ", error); - }); - } - else { - console.error("Service Workers are not supported in this browser."); - } - feature_params = new URLSearchParams(document.location.search).has("feature-params"); - var filterSelectorEntry = document.getElementById('filter-selector-entry'); - if (filterSelectorEntry === null) { - throw new Error('Could not find "filter-selector-entry"'); - } - var imageSelectorEntry = document.getElementById('image-selector-entry'); - if (imageSelectorEntry === null) { - throw new Error('Could not find "image-selector-entry"'); - } - var imageSelector = ImageSelector(); - var filterSelector = FilterSelector(); - imageSelector.addEventListener('imageSelected', function (e) { - filterSelector.updateImage$(e.detail.imageData); - }); - filterSelectorEntry.appendChild(filterSelector); - imageSelectorEntry.appendChild(imageSelector); - document.ondrop = function (event) { - var _a; - event.preventDefault(); - imageSelector.updateFiles$((_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.files); - }; - document.ondragover = function (event) { - event.preventDefault(); - }; - var gif = undefined; - var renderButton = document.getElementById("render"); - if (renderButton === null) { - throw new Error('Could not find "render"'); - } - renderButton.onclick = function () { - if (gif && gif.running) { - gif.abort(); - } - var fileName = imageSelector.selectedFileName$(); - gif = filterSelector.render$(fileName + ".gif"); - }; -}; diff --git a/package-lock.json b/package-lock.json index 6de1ed3..c54c2ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,74 +8,1438 @@ "name": "emotejam", "version": "1.0.0", "license": "MIT", + "dependencies": { + "@dhdbstjr98/gif.js": "^1.0.2" + }, "devDependencies": { - "@types/gif.js": "^0.2.1", - "@types/node": "^20.9.3", - "typescript": "^4.3.2" + "@types/gif.js": "^0.2.5", + "@types/node": "^22", + "typescript": "^5.5", + "vite": "^5.4.0" + } + }, + "node_modules/@dhdbstjr98/gif.js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@dhdbstjr98/gif.js/-/gif.js-1.0.2.tgz", + "integrity": "sha512-0XZTm2r9z5HLANwRnBOmHZOdFrLRzm7Sh0x5PIQGfEPa21bD1eb2VSOoAcyCMe1gXaBeekd7/l0JCgm0C6o3YA==", + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "events": "^3.3.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz", + "integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz", + "integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz", + "integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz", + "integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz", + "integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz", + "integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz", + "integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz", + "integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz", + "integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz", + "integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz", + "integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", + "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz", + "integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz", + "integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz", + "integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz", + "integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/events": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz", + "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/gif.js": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@types/gif.js/-/gif.js-0.2.1.tgz", - "integrity": "sha512-oZAPX8pgueiAngu3HfynjdtsDNt4EiD1fs5An//LtBKvOdnc4Wq8/S7GkAKpP80+29WyVwGEGVEUXPyFhbQ2+g==", - "dev": true + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@types/gif.js/-/gif.js-0.2.5.tgz", + "integrity": "sha512-OdDQYh9v7td9ztjaooBSqjUBAyAuui2xwDDmQcyRLd6c9T0iWgkebAoCBEdEEBoZG3ekJE/6UnH63Dzq0S3bvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/events": "*" + } }, "node_modules/@types/node": { - "version": "20.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.3.tgz", - "integrity": "sha512-nk5wXLAXGBKfrhLB0cyHGbSqopS+nz0BUgZkUQqSHSSgdee0kssp1IAqlQOu333bW+gMNs2QREx7iynm19Abxw==", + "version": "22.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.3.0.tgz", + "integrity": "sha512-nrWpWVaDZuaVc5X84xJ0vNrLvomM205oQyLsRt7OHNZbSHslcWsvgFR7O7hire2ZonjLrWBbedmotmIlJDVd6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.18.2" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", + "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.20.0", + "@rollup/rollup-android-arm64": "4.20.0", + "@rollup/rollup-darwin-arm64": "4.20.0", + "@rollup/rollup-darwin-x64": "4.20.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.20.0", + "@rollup/rollup-linux-arm-musleabihf": "4.20.0", + "@rollup/rollup-linux-arm64-gnu": "4.20.0", + "@rollup/rollup-linux-arm64-musl": "4.20.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.20.0", + "@rollup/rollup-linux-riscv64-gnu": "4.20.0", + "@rollup/rollup-linux-s390x-gnu": "4.20.0", + "@rollup/rollup-linux-x64-gnu": "4.20.0", + "@rollup/rollup-linux-x64-musl": "4.20.0", + "@rollup/rollup-win32-arm64-msvc": "4.20.0", + "@rollup/rollup-win32-ia32-msvc": "4.20.0", + "@rollup/rollup-win32-x64-msvc": "4.20.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" } }, "node_modules/typescript": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", - "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.18.2.tgz", + "integrity": "sha512-5ruQbENj95yDYJNS3TvcaxPMshV7aizdv/hWYjGIKoANWKjhWNBsr2YEuYZKodQulB1b8l7ILOuDQep3afowQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", + "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.40", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } } }, "dependencies": { - "@types/gif.js": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@types/gif.js/-/gif.js-0.2.1.tgz", - "integrity": "sha512-oZAPX8pgueiAngu3HfynjdtsDNt4EiD1fs5An//LtBKvOdnc4Wq8/S7GkAKpP80+29WyVwGEGVEUXPyFhbQ2+g==", + "@dhdbstjr98/gif.js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@dhdbstjr98/gif.js/-/gif.js-1.0.2.tgz", + "integrity": "sha512-0XZTm2r9z5HLANwRnBOmHZOdFrLRzm7Sh0x5PIQGfEPa21bD1eb2VSOoAcyCMe1gXaBeekd7/l0JCgm0C6o3YA==", + "requires": { + "buffer": "^6.0.3", + "events": "^3.3.0" + } + }, + "@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz", + "integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz", + "integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz", + "integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz", + "integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz", + "integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-musleabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz", + "integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz", + "integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz", + "integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz", + "integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-riscv64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz", + "integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz", + "integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", + "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz", + "integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz", + "integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz", + "integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz", + "integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==", + "dev": true, + "optional": true + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "@types/events": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz", + "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==", "dev": true }, + "@types/gif.js": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@types/gif.js/-/gif.js-0.2.5.tgz", + "integrity": "sha512-OdDQYh9v7td9ztjaooBSqjUBAyAuui2xwDDmQcyRLd6c9T0iWgkebAoCBEdEEBoZG3ekJE/6UnH63Dzq0S3bvw==", + "dev": true, + "requires": { + "@types/events": "*" + } + }, "@types/node": { - "version": "20.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.3.tgz", - "integrity": "sha512-nk5wXLAXGBKfrhLB0cyHGbSqopS+nz0BUgZkUQqSHSSgdee0kssp1IAqlQOu333bW+gMNs2QREx7iynm19Abxw==", + "version": "22.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.3.0.tgz", + "integrity": "sha512-nrWpWVaDZuaVc5X84xJ0vNrLvomM205oQyLsRt7OHNZbSHslcWsvgFR7O7hire2ZonjLrWBbedmotmIlJDVd6g==", + "dev": true, + "requires": { + "undici-types": "~6.18.2" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "requires": { - "undici-types": "~5.26.4" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true + }, + "picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "dev": true, + "requires": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + } + }, + "rollup": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", + "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.20.0", + "@rollup/rollup-android-arm64": "4.20.0", + "@rollup/rollup-darwin-arm64": "4.20.0", + "@rollup/rollup-darwin-x64": "4.20.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.20.0", + "@rollup/rollup-linux-arm-musleabihf": "4.20.0", + "@rollup/rollup-linux-arm64-gnu": "4.20.0", + "@rollup/rollup-linux-arm64-musl": "4.20.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.20.0", + "@rollup/rollup-linux-riscv64-gnu": "4.20.0", + "@rollup/rollup-linux-s390x-gnu": "4.20.0", + "@rollup/rollup-linux-x64-gnu": "4.20.0", + "@rollup/rollup-linux-x64-musl": "4.20.0", + "@rollup/rollup-win32-arm64-msvc": "4.20.0", + "@rollup/rollup-win32-ia32-msvc": "4.20.0", + "@rollup/rollup-win32-x64-msvc": "4.20.0", + "@types/estree": "1.0.5", + "fsevents": "~2.3.2" + } + }, + "source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true + }, "typescript": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", - "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true }, "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.18.2.tgz", + "integrity": "sha512-5ruQbENj95yDYJNS3TvcaxPMshV7aizdv/hWYjGIKoANWKjhWNBsr2YEuYZKodQulB1b8l7ILOuDQep3afowQQ==", "dev": true + }, + "vite": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", + "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", + "dev": true, + "requires": { + "esbuild": "^0.21.3", + "fsevents": "~2.3.3", + "postcss": "^8.4.40", + "rollup": "^4.13.0" + } } } } diff --git a/package.json b/package.json index a56e15a..ee782fe 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,10 @@ "main": "index.js", "type": "module", "scripts": { - "build": "node build.js build", - "watch": "node build.js watch", - "serve": "node build.js serve", + "build": "vite build", + "dev": "vite ", + "serve": "vite preview", + "lint" : "tsc --noEmit", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { @@ -30,8 +31,12 @@ }, "homepage": "https://github.com/tsoding/emoteJAM#readme", "devDependencies": { - "@types/gif.js": "^0.2.1", - "@types/node": "^20.9.3", - "typescript": "^4.3.2" + "@types/gif.js": "^0.2.5", + "@types/node": "^22", + "typescript": "^5.5", + "vite": "^5.4.0" + }, + "dependencies": { + "@dhdbstjr98/gif.js": "^1.0.2" } } diff --git a/img/error.png b/public/img/error.png similarity index 100% rename from img/error.png rename to public/img/error.png diff --git a/img/tsodinClown.png b/public/img/tsodinClown.png similarity index 100% rename from img/tsodinClown.png rename to public/img/tsodinClown.png diff --git a/serviceworker.js b/serviceworker.js deleted file mode 100644 index 2360621..0000000 --- a/serviceworker.js +++ /dev/null @@ -1,128 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; - return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (_) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -var cacheName = "emoteJAM-v1"; -var assets = [ - 'index.html', - 'css/bright.css', - 'css/main.css', - 'css/reset.css', - 'gif.js', - 'gif.worker.js', - 'img/tsodinClown.png', - 'js/eval.js', - 'js/filters.js', - 'js/grecha.js', - 'js/index.js', -]; -self.addEventListener("install", function (e) { - console.log("[Service Worker] Install"); - var event = e; - event.waitUntil((function () { return __awaiter(void 0, void 0, void 0, function () { - var cache; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - console.log("[Service Worker] Caching all the assets"); - return [4, caches.open(cacheName)]; - case 1: - cache = _a.sent(); - cache.addAll(assets); - return [2]; - } - }); - }); })()); -}); -self.addEventListener("activate", function (e) { - console.log("[Service Worker] Activate"); - var event = e; - event.waitUntil((function () { return __awaiter(void 0, void 0, void 0, function () { - var keys, _a, _b, _i, key; - return __generator(this, function (_c) { - switch (_c.label) { - case 0: - console.log("[Service Worker] Cleaning up all caches"); - return [4, caches.keys()]; - case 1: - keys = _c.sent(); - _a = []; - for (_b in keys) - _a.push(_b); - _i = 0; - _c.label = 2; - case 2: - if (!(_i < _a.length)) return [3, 5]; - key = _a[_i]; - if (!(key !== cacheName)) return [3, 4]; - return [4, caches["delete"](key)]; - case 3: - _c.sent(); - _c.label = 4; - case 4: - _i++; - return [3, 2]; - case 5: return [2]; - } - }); - }); })()); -}); -self.addEventListener("fetch", function (e) { - var event = e; - event.respondWith((function () { return __awaiter(void 0, void 0, void 0, function () { - var cache, response; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: - console.log("[Service Worker] Fetch " + event.request.url); - return [4, caches.open(cacheName)]; - case 1: - cache = _a.sent(); - return [4, cache.match(event.request.url)]; - case 2: - response = _a.sent(); - if (!(response === undefined)) return [3, 4]; - console.log("[Service Worker] Response for " + event.request.url + " is not available in cache. Making an actual request..."); - return [4, fetch(event.request.url)]; - case 3: - response = _a.sent(); - cache.put(event.request.url, response.clone()); - _a.label = 4; - case 4: return [2, response]; - } - }); - }); })()); -}); diff --git a/serviceworker.ts b/serviceworker.ts deleted file mode 100644 index 58be3a4..0000000 --- a/serviceworker.ts +++ /dev/null @@ -1,53 +0,0 @@ -const cacheName = "emoteJAM-v1"; -const assets = [ - 'index.html', - 'css/bright.css', - 'css/main.css', - 'css/reset.css', - 'gif.js', - 'gif.worker.js', - 'img/tsodinClown.png', - 'js/eval.js', - 'js/filters.js', - 'js/grecha.js', - 'js/index.js', -]; - -self.addEventListener("install", e => { - console.log("[Service Worker] Install"); - const event = e as ExtendableEvent; - event.waitUntil((async () => { - console.log("[Service Worker] Caching all the assets"); - const cache = await caches.open(cacheName); - cache.addAll(assets); - })()); -}); - -self.addEventListener("activate", e => { - console.log("[Service Worker] Activate"); - const event = e as ExtendableEvent; - event.waitUntil((async() => { - console.log("[Service Worker] Cleaning up all caches"); - const keys = await caches.keys(); - for (let key in keys) { - if (key !== cacheName) { - await caches.delete(key); - } - } - })()); -}); - -self.addEventListener("fetch", (e) => { - const event = e as FetchEvent; - event.respondWith((async () => { - console.log(`[Service Worker] Fetch ${event.request.url}`); - const cache = await caches.open(cacheName); - let response = await cache.match(event.request.url); - if (response === undefined) { - console.log(`[Service Worker] Response for ${event.request.url} is not available in cache. Making an actual request...`); - response = await fetch(event.request.url); - cache.put(event.request.url, response.clone()); - } - return response; - })()); -}); diff --git a/ts/eval.ts b/src/eval.ts similarity index 78% rename from ts/eval.ts rename to src/eval.ts index 5ccc100..847dd67 100644 --- a/ts/eval.ts +++ b/src/eval.ts @@ -1,18 +1,20 @@ -type BinaryOp = '+' | '-' | '*' | '/' | '%'; -type BinaryOpFunc = (lhs: number, rhs: number) => number; +export type BinaryOp = '+' | '-' | '*' | '/' | '%'; +export type BinaryOpFunc = (lhs: number, rhs: number) => number; -enum BinaryPrec { - PREC0 = 0, - PREC1, - COUNT_PRECS -} +const BinaryPrec = { // avoid dangerous enum in TS + PREC0: 0, + PREC1 : 1, + COUNT_PRECS : 2 +} as const; + +type BinaryPrec = typeof BinaryPrec[keyof typeof BinaryPrec]; interface BinaryOpDef { func: BinaryOpFunc, prec: BinaryPrec } -const BINARY_OPS: {[op in BinaryOp]: BinaryOpDef} = { +const BINARY_OPS: Record = { '+': { func: (lhs, rhs) => lhs + rhs, prec: BinaryPrec.PREC0 @@ -38,11 +40,11 @@ const BINARY_OPS: {[op in BinaryOp]: BinaryOpDef} = { type UnaryOp = '-'; type UnaryOpFunc = (arg: number) => number; -const UNARY_OPS: {[op in UnaryOp]: UnaryOpFunc} = { +const UNARY_OPS: Record = { '-': (arg: number) => -arg, }; -class Lexer { +export class Lexer { src: string constructor(src: string) { @@ -65,14 +67,14 @@ class Lexer { return c in BINARY_OPS || c in UNARY_OPS || syntax.includes(c); } - if (is_token_break(this.src[0])) { - const token = this.src[0]; + if (is_token_break(this.src[0]!)) { + const token = this.src[0]!; this.src = this.src.slice(1); return token; } for (let i = 0; i < this.src.length; ++i) { - if (is_token_break(this.src[i]) || this.src[i] == ' ') { + if (is_token_break(this.src[i]!) || this.src[i] == ' ') { const token = this.src.slice(0, i); this.src = this.src.slice(i); return token; @@ -100,14 +102,14 @@ interface BinaryOpExpr { interface FuncallExpr { name: string, - args: Array, + args: Expr[], } interface SymbolExpr { value: string } -interface Expr { +export interface Expr { kind: ExprKind, payload: UnaryOpExpr | BinaryOpExpr | FuncallExpr | SymbolExpr } @@ -136,15 +138,15 @@ function parse_primary(lexer: Lexer): Expr { } else { let next_token = lexer.next(); if (next_token === '(') { - const args: Array = []; + const args: Expr[] = []; next_token = lexer.next(); if (next_token === ')') { return { - "kind": "funcall", - "payload": { - "name": token, - "args": args, + kind: "funcall", + payload: { + name: token, + args: args, } }; } @@ -167,10 +169,10 @@ function parse_primary(lexer: Lexer): Expr { } return { - "kind": "funcall", - "payload": { - "name": token, - "args": args, + kind: "funcall", + payload: { + name: token, + args: args, } }; } else { @@ -178,9 +180,9 @@ function parse_primary(lexer: Lexer): Expr { lexer.unnext(next_token); } return { - "kind": "symbol", - "payload": { - "value": token + kind: "symbol", + payload: { + value: token } }; } @@ -195,18 +197,18 @@ function parse_expr(lexer: Lexer, prec: BinaryPrec = BinaryPrec.PREC0): Expr { return parse_primary(lexer); } - let lhs = parse_expr(lexer, prec + 1); + let lhs = parse_expr(lexer, (prec + 1) as BinaryPrec); let op_token = lexer.next(); if (op_token !== null) { if (op_token in BINARY_OPS && BINARY_OPS[op_token as BinaryOp].prec == prec) { let rhs = parse_expr(lexer, prec); return { - "kind": "binary_op", - "payload": { - "op": op_token as BinaryOp, - "lhs": lhs, - "rhs": rhs, + kind: "binary_op", + payload: { + op: op_token as BinaryOp, + lhs, + rhs, } }; } else { @@ -217,7 +219,7 @@ function parse_expr(lexer: Lexer, prec: BinaryPrec = BinaryPrec.PREC0): Expr { return lhs; } -function compile_expr(src: string): Expr { +export function compile_expr(src: string): Expr { const lexer = new Lexer(src); const result = parse_expr(lexer); const token = lexer.next(); @@ -229,12 +231,12 @@ function compile_expr(src: string): Expr { return result; } -interface UserContext { - vars?: {[name: string]: number}, - funcs?: {[name: string]: (...xs: number[]) => number}, +export interface UserContext { + vars?: Record, + funcs?: Record number>, } -function run_expr(expr: Expr, user_context: UserContext = {}): number { +export function run_expr(expr: Expr, user_context: UserContext = {}): number { console.assert(typeof(expr) === 'object'); switch (expr.kind) { @@ -244,7 +246,7 @@ function run_expr(expr: Expr, user_context: UserContext = {}): number { const number = Number(value); if (isNaN(number)) { if (user_context.vars && value in user_context.vars) { - return user_context.vars[value]; + return user_context.vars[value]!; } throw new Error(`Unknown variable '${value}'`); @@ -279,7 +281,7 @@ function run_expr(expr: Expr, user_context: UserContext = {}): number { const funcall = expr.payload as FuncallExpr; if (user_context.funcs && funcall.name in user_context.funcs) { - return user_context.funcs[funcall.name](...funcall.args.map((arg) => run_expr(arg, user_context))); + return user_context.funcs[funcall.name]!(...funcall.args.map((arg) => run_expr(arg, user_context))); } throw new Error(`Unknown function '${funcall.name}'`); diff --git a/ts/filters.ts b/src/filters.ts similarity index 76% rename from ts/filters.ts rename to src/filters.ts index 0351601..3763156 100644 --- a/ts/filters.ts +++ b/src/filters.ts @@ -1,4 +1,4 @@ -interface Param { +type Param = { label?: string, type: string, init: number, @@ -7,69 +7,69 @@ interface Param { step?: number, } -interface Filter { +export type Filter = { transparent: string | null, duration: string, - params?: { - [name: string]: Param - }, + params?: Record, vertex: string, fragment: string } +type FilterType = "Hop" | "Hopper" | "Overheat" | "Bounce" | "Circle" | "Slide" | "Laughing" | "Blob" | "Go" | "Elevator" | "Rain" | "Pride" | "Hard" | "Peek" | "Matrix" | "Flag" | "Thanosed" | "Ripple"; + // TODO(#58): add params to all of the filters // TODO(#61): human readable titles for the filter params -const filters: {[name: string]: Filter} = { - "Hop": { - "transparent": 0x00FF00 + "", - "duration": "interval * 2", +export const filters: Record = { + Hop: { + transparent: String(0x00FF00), + duration: "interval * 2", // TODO(#62): when you have too many params the UI gets really cluttered - "params": { + params: { // TODO(#65): filter params should have help tooltips associated with them - "interval": { - "label": "Interval", - "type": "float", - "init": 0.85, - "min": 0.01, - "max": 2.00, - "step": 0.01, + interval: { + label: "Interval", + type: "float", + init: 0.85, + min: 0.01, + max: 2.00, + step: 0.01, }, - "ground": { - "label": "Ground", - "type": "float", - "init": 0.5, - "min": -1.0, - "max": 1.0, - "step": 0.01, + ground: { + label: "Ground", + type: "float", + init: 0.5, + min: -1.0, + max: 1.0, + step: 0.01, }, - "scale": { - "label": "Scale", - "type": "float", - "init": 0.40, - "min": 0.0, - "max": 1.0, - "step": 0.01, + scale: { + label: "Scale", + type: "float", + init: 0.40, + min: 0.0, + max: 1.0, + step: 0.01, }, // TODO(#63): jump_height in the "Hop" filter does not make any sense // If it's bigger the emote should jump higher. Right now it is the other way around. - "jump_height": { - "label": "Jump Height", - "type": "float", - "init": 4.0, - "min": 1.0, - "max": 10.0, - "step": 0.01, + jump_height: { + label: "Jump Height", + type: "float", + init: 4.0, + min: 1.0, + max: 10.0, + step: 0.01, }, - "hops": { - "label": "Hops Count", - "type": "float", - "init": 2.0, - "min": 1.0, - "max": 5.0, - "step": 1.0, + hops: { + label: "Hops Count", + type: "float", + init: 2.0, + min: 1.0, + max: 5.0, + step: 1.0, } }, - "vertex": `#version 100 + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -109,7 +109,7 @@ void main() { uv.x = (flipping_directions(x_time_interval) + 1.0) / 2.0 - uv.x * flipping_directions(x_time_interval); } `, - "fragment": `#version 100 + fragment: `#version 100 precision mediump float; @@ -126,10 +126,10 @@ void main() { } ` }, - "Hopper": { - "transparent": 0x00FF00 + "", - "duration": "0.85", - "vertex": `#version 100 + Hopper: { + transparent: String(0x00FF00), + duration: "0.85", + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -165,7 +165,7 @@ void main() { uv.x = (flipping_directions(x_time_interval) + 1.0) / 2.0 - uv.x * flipping_directions(x_time_interval); } `, - "fragment": `#version 100 + fragment: `#version 100 precision mediump float; @@ -182,10 +182,10 @@ void main() { } ` }, - "Overheat": { - "transparent": 0x00FF00 + "", - "duration": "0.85 / 8.0 * 2.0", - "vertex": `#version 100 + Overheat: { + transparent: String(0x00FF00), + duration: "0.85 / 8.0 * 2.0", + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -221,7 +221,7 @@ void main() { uv.x = (flipping_directions(x_time_interval) + 1.0) / 2.0 - uv.x * flipping_directions(x_time_interval); } `, - "fragment": `#version 100 + fragment: `#version 100 precision mediump float; @@ -238,26 +238,26 @@ void main() { } ` }, - "Bounce": { - "transparent": 0x00FF00 + "", - "duration": "Math.PI / period", - "params": { - "period": { - "type": "float", - "init": 5.0, - "min": 1.0, - "max": 10.0, - "step": 0.1, + Bounce: { + transparent: String(0x00FF00), + duration: "Math.PI / period", + params: { + period: { + type: "float", + init: 5.0, + min: 1.0, + max: 10.0, + step: 0.1, }, - "scale": { - "type": "float", - "init": 0.30, - "min": 0.0, - "max": 1.0, - "step": 0.01, + scale: { + type: "float", + init: 0.30, + min: 0.0, + max: 1.0, + step: 0.01, } }, - "vertex": `#version 100 + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -276,7 +276,7 @@ void main() { uv = (meshPosition + 1.0) / 2.0; } `, - "fragment": ` + fragment: ` #version 100 precision mediump float; @@ -294,10 +294,10 @@ void main() { } `, }, - "Circle": { - "transparent": 0x00FF00 + "", - "duration": "Math.PI / 4.0", - "vertex": `#version 100 + Circle: { + transparent: String(0x00FF00), + duration: "Math.PI / 4.0", + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -327,7 +327,7 @@ void main() { uv = (meshPosition + 1.0) / 2.0; } `, - "fragment": ` + fragment: ` #version 100 precision mediump float; @@ -346,10 +346,10 @@ void main() { } `, }, - "Slide": { - "transparent": 0x00FF00 + "", - "duration": "0.85 * 2", - "vertex": `#version 100 + Slide: { + transparent: String(0x00FF00), + duration: "0.85 * 2", + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -385,7 +385,7 @@ void main() { uv.x = (flipping_directions(x_time_interval) + 1.0) / 2.0 - uv.x * flipping_directions(x_time_interval); } `, - "fragment": `#version 100 + fragment: `#version 100 precision mediump float; @@ -402,10 +402,10 @@ void main() { } ` }, - "Laughing": { - "transparent": 0x00FF00 + "", - "duration": "Math.PI / 12.0", - "vertex": `#version 100 + Laughing: { + transparent: String(0x00FF00), + duration: "Math.PI / 12.0", + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -424,7 +424,7 @@ void main() { uv = (meshPosition + vec2(1.0, 1.0)) / 2.0; } `, - "fragment": `#version 100 + fragment: `#version 100 precision mediump float; @@ -441,10 +441,10 @@ void main() { } ` }, - "Blob": { - "transparent": 0x00FF00 + "", - "duration": "Math.PI / 3", - "vertex": `#version 100 + Blob: { + transparent: String(0x00FF00), + duration: "Math.PI / 3", + vertex: `#version 100 precision mediump float; @@ -466,7 +466,7 @@ void main() { uv = (meshPosition + vec2(1.0, 1.0)) / 2.0; } `, - "fragment": `#version 100 + fragment: `#version 100 precision mediump float; @@ -483,10 +483,10 @@ void main() { } ` }, - "Go": { - "transparent": 0x00FF00 + "", - "duration": "1 / 4", - "vertex": `#version 100 + Go: { + transparent: String(0x00FF00), + duration: "1 / 4", + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -501,7 +501,7 @@ void main() { uv = (meshPosition + 1.0) / 2.0; } `, - "fragment": ` + fragment: ` #version 100 precision mediump float; @@ -524,10 +524,10 @@ void main() { } `, }, - "Elevator": { - "transparent": 0x00FF00 + "", - "duration": "1 / 4", - "vertex": `#version 100 + Elevator: { + transparent: String(0x00FF00), + duration: "1 / 4", + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -542,7 +542,7 @@ void main() { uv = (meshPosition + 1.0) / 2.0; } `, - "fragment": ` + fragment: ` #version 100 precision mediump float; @@ -567,10 +567,10 @@ void main() { } `, }, - "Rain": { - "transparent": 0x00FF00 + "", - "duration": "1", - "vertex": `#version 100 + Rain: { + transparent: String(0x00FF00), + duration: "1", + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -585,7 +585,7 @@ void main() { uv = (meshPosition + 1.0) / 2.0; } `, - "fragment": ` + fragment: ` #version 100 precision mediump float; @@ -611,10 +611,10 @@ void main() { } `, }, - "Pride": { - "transparent": null, - "duration": "2.0", - "vertex": `#version 100 + Pride: { + transparent: null, + duration: "2.0", + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -629,7 +629,7 @@ void main() { uv = (meshPosition + 1.0) / 2.0; } `, - "fragment": ` + fragment: ` #version 100 precision mediump float; @@ -657,33 +657,33 @@ void main() { } `, }, - "Hard": { - "transparent": 0x00FF00 + "", - "duration": "2.0 * Math.PI / intensity", - "params": { - "zoom": { - "type": "float", - "init": 1.4, - "min": 0.0, - "max": 6.9, - "step": 0.1, + Hard: { + transparent: String(0x00FF00), + duration: "2.0 * Math.PI / intensity", + params: { + zoom: { + type: "float", + init: 1.4, + min: 0.0, + max: 6.9, + step: 0.1, }, - "intensity": { - "type": "float", - "init": 32.0, - "min": 1.0, - "max": 42.0, - "step": 1.0, + intensity: { + type: "float", + init: 32.0, + min: 1.0, + max: 42.0, + step: 1.0, }, - "amplitude": { - "type": "float", - "init": 1.0 / 8.0, - "min": 0.0, - "max": 1.0 / 2.0, - "step": 0.001, + amplitude: { + type: "float", + init: 1.0 / 8.0, + min: 0.0, + max: 1.0 / 2.0, + step: 0.001, }, }, - "vertex": `#version 100 + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -703,7 +703,7 @@ void main() { uv = (meshPosition + 1.0) / 2.0; } `, - "fragment": ` + fragment: ` #version 100 precision mediump float; @@ -721,10 +721,10 @@ void main() { } `, }, - "Peek":{ - "transparent": 0x00FF00 + "", - "duration": "2.0 * Math.PI" , - "vertex": `#version 100 + Peek:{ + transparent: String(0x00FF00), + duration: "2.0 * Math.PI" , + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -744,13 +744,13 @@ void main() { float hold2 = 1.0 - float(time_clipped > (2.5 * 3.14) && time_clipped < (4.0 * 3.14)); float cycle_1 = 1.0 - ((s1 * sin(time_clipped) * (1.0 - hold1)) + hold1); - float cycle_2 = s2 * hold2 * (sin(time_clipped) - 1.0); + float cycle_2 = s2 * hold2 * (sin(time_clipped) - 1.0); gl_Position = vec4(meshPosition.x + 1.0 + cycle_1 + cycle_2 , meshPosition.y, 0.0, 1.0); uv = (meshPosition + 1.0) / 2.0; } `, - "fragment": ` + fragment: ` #version 100 precision mediump float; @@ -766,12 +766,12 @@ void main() { gl_FragColor = texture2D(emote, vec2(uv.x, 1.0 - uv.y)); gl_FragColor.w = floor(gl_FragColor.w + 0.5); } -`, +`, }, - "Matrix": { - "transparent": null, - "duration": "3.0", - "vertex":` + Matrix: { + transparent: null, + duration: "3.0", + vertex:` #version 100 precision mediump float; @@ -788,7 +788,7 @@ void main() { gl_Position = vec4(meshPosition.x, meshPosition.y, 0.0, 1.0); } `, - "fragment": ` + fragment: ` #version 100 precision mediump float; @@ -823,7 +823,7 @@ void main() { { float random = 2920.0 * sin(n.x * 21942.0 + n.y * 171324.0 + 8912.0) * cos(n.x * 23157.0 * n.y * 217832.0 + 9758.0); - + return (sin(random) + 1.0) / 2.0; } @@ -837,15 +837,15 @@ void main() { { vec2 uv = _uv; uv.y = 1.0 - _uv.y; - + float number_of_numbers = 8.0; float number_change_rate = 2.0; float amount_of_numbers = 0.6; // from 0 - 1 - + vec4 texture_color = texture2D(emote, uv); vec4 number_color = vec4(0, 0.7, 0, 1); - float looped_time = loop_time(3.0); + float looped_time = loop_time(3.0); vec2 translation = vec2(0, looped_time * -8.0); @@ -870,10 +870,10 @@ void main() { } ` }, - "Flag":{ - "transparent": 0x00FF00 + "", - "duration": "Math.PI", - "vertex":` + Flag: { + transparent: String(0x00FF00), + duration: "Math.PI", + vertex:` #version 100 precision mediump float; @@ -891,7 +891,7 @@ void main() { gl_Position = vec4(meshPosition.x, meshPosition.y, 0.0, 1.0); } `, - "fragment" :` + fragment :` #version 100 precision mediump float; @@ -911,7 +911,7 @@ void main() { float right = pos.x + size.x; float bottom = pos.y; float top = pos.y + size.y; - return (step(bottom, uv.y) - step(top, uv.y)) * (step(left, uv.x) - step(right, uv.x)); + return (step(bottom, uv.y) - step(top, uv.y)) * (step(left, uv.x) - step(right, uv.x)); } void main() { @@ -919,7 +919,7 @@ void main() { float flag_height = 0.75; float wave_size = 0.08; vec4 stick_color = vec4(107.0 / 256.0, 59.0 / 256.0, 9.0 / 256.0,1); - + vec2 flag_uv = _uv; flag_uv.x = (1.0 / (1.0 - stick_width)) * (flag_uv.x - stick_width); flag_uv.y *= 1.0 / flag_height; @@ -937,33 +937,33 @@ void main() { } ` }, - "Thanosed": { - "transparent": 0x00FF00 + "", - "duration": "duration", - "params": { - "duration": { - "type": "float", - "init": 6.0, - "min": 1.0, - "max": 16.0, - "step": 1.0, + Thanosed: { + transparent: String(0x00FF00), + duration: "duration", + params: { + duration: { + type: "float", + init: 6.0, + min: 1.0, + max: 16.0, + step: 1.0, }, - "delay": { - "type": "float", - "init": 0.2, - "min": 0.0, - "max": 1.0, - "step": 0.1, + delay: { + type: "float", + init: 0.2, + min: 0.0, + max: 1.0, + step: 0.1, }, - "pixelization": { - "type": "float", - "init": 1.0, - "min": 1.0, - "max": 3.0, - "step": 1.0, + pixelization: { + type: "float", + init: 1.0, + min: 1.0, + max: 3.0, + step: 1.0, }, }, - "vertex": `#version 100 + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -978,7 +978,7 @@ void main() { uv = (meshPosition + 1.0) / 2.0; } `, - "fragment": ` + fragment: ` #version 100 precision mediump float; @@ -1016,36 +1016,36 @@ void main() { } `, }, - "Ripple": { - "transparent": 0x00FF00 + "", - "duration": "2 * Math.PI / b", - "params": { - "a": { - "label": "Wave Length", - "type": "float", - "init": 12.0, - "min": 0.01, - "max": 24.0, - "step": 0.01, + Ripple: { + transparent: String(0x00FF00), + duration: "2 * Math.PI / b", + params: { + a: { + label: "Wave Length", + type: "float", + init: 12.0, + min: 0.01, + max: 24.0, + step: 0.01, }, - "b": { - "label": "Time Freq", - "type": "float", - "init": 4.0, - "min": 0.01, - "max": 8.0, - "step": 0.01, + b: { + label: "Time Freq", + type: "float", + init: 4.0, + min: 0.01, + max: 8.0, + step: 0.01, }, - "c": { - "label": "Amplitude", - "type": "float", - "init": 0.03, - "min": 0.01, - "max": 0.06, - "step": 0.01, + c: { + label: "Amplitude", + type: "float", + init: 0.03, + min: 0.01, + max: 0.06, + step: 0.01, } }, - "vertex": `#version 100 + vertex: `#version 100 precision mediump float; attribute vec2 meshPosition; @@ -1060,7 +1060,7 @@ void main() { uv = (meshPosition + 1.0) / 2.0; } `, - "fragment": `#version 100 + fragment: `#version 100 precision mediump float; diff --git a/src/grecha.ts b/src/grecha.ts new file mode 100644 index 0000000..59a1dc1 --- /dev/null +++ b/src/grecha.ts @@ -0,0 +1,85 @@ +import type { Filter } from "./filters"; +import type { Snapshot } from "./index"; + +type Child = string | HTMLElement | Tag; + +export type Tag = T & { + att$ : (name: string, value: string) => Tag; + onclick$ : (callback: (this: GlobalEventHandlers, ev: MouseEvent) => Tag) => Tag; + paramsSnapshot$ : () => Snapshot; + selectedImage$ : () => Tag; + selectedFileName$ : () => string; + updateFiles$ : (files: FileList) => void; + selectedFilter$ : () => Filter; + render$ : (filename: string) => GIF | undefined; + updateImage$ : Function; // TODO: change this to the actual function type + + files : FileList; + value : any; +}; + +export function tag(name: string, ...children: Child[]): Tag { + const result = document.createElement(name) as Tag; + for (const child of children) { + if (typeof child === 'string') { + result.appendChild(document.createTextNode(child)); + } else { + result.appendChild(child); + } + } + + result.att$ = function (this, name : string, value) { + this.setAttribute(name, value); + return this; + }; + + + result.onclick$ = function(this, callback) { + this.onclick = callback; + return this; + }; + + return result; +} + +export const canvas = (...children: Child[]) => tag("canvas", ...children); +export const h1 = (...children: Child[]) => tag("h1", ...children); +export const h2 = (...children: Child[]) => tag("h2", ...children); +export const h3 = (...children: Child[]) => tag("h3", ...children); +export const p = (...children: Child[]) => tag("p", ...children); +export const a = (...children: Child[]) => tag("a", ...children); +export const div = (...children: Child[]) => tag("div", ...children); +export const span = (...children: Child[]) => tag("span", ...children); +export const select = (...children: Child[]) => tag("select", ...children); + +export const img = (src: string) => tag("img").att$("src", src); +export const input = (type: string) => tag("input").att$("type", type); + +export function router(routes: Record) { + const result = div(); + + function syncHash() { + let hashLocation = document.location.hash.split('#')[1]; + if (!hashLocation) { + hashLocation = '/'; + } + + if (!(hashLocation in routes)) { + const route404 = '/404'; + console.assert(route404 in routes); + hashLocation = route404; + } + + while (result.firstChild) { + result.removeChild(result.lastChild!); + } + result.appendChild(routes[hashLocation]!); + + return result; + }; + + syncHash(); + window.addEventListener("hashchange", syncHash); + + return result; +} diff --git a/ts/index.ts b/src/index.ts similarity index 67% rename from ts/index.ts rename to src/index.ts index f45e7ab..0093eed 100644 --- a/ts/index.ts +++ b/src/index.ts @@ -1,8 +1,11 @@ -let feature_params = false; +import GIF from '@dhdbstjr98/gif.js'; +import { compile_expr, run_expr, type Expr, type UserContext } from "./eval"; +import { type Filter, filters } from "./filters"; +import { canvas, div, img, input, select, span, type Tag } from "./grecha"; -interface VertexAttribs { - [name: string]: number -} +let feature_params = false; + +type VertexAttribs = Record; const vertexAttribs: VertexAttribs = { "meshPosition": 0 @@ -43,12 +46,12 @@ function linkShaderProgram(gl: WebGLRenderingContext, shaders: WebGLShader[], ve throw new Error('Could not create a new shader program'); } - for (let shader of shaders) { + for (const shader of shaders) { gl.attachShader(program, shader); } - for (let vertexName in vertexAttribs) { - gl.bindAttribLocation(program, vertexAttribs[vertexName], vertexName); + for (const [vertexName, vertexVal] of Object.entries(vertexAttribs)) { + gl.bindAttribLocation(program, vertexVal, vertexName); } gl.linkProgram(program); @@ -59,7 +62,7 @@ function linkShaderProgram(gl: WebGLRenderingContext, shaders: WebGLShader[], ve } function createTextureFromImage(gl: WebGLRenderingContext, image: TexImageSource): WebGLTexture { - let textureId = gl.createTexture(); + const textureId = gl.createTexture(); if (textureId === null) { throw new Error('Could not create a new WebGL texture'); } @@ -82,78 +85,76 @@ function createTextureFromImage(gl: WebGLRenderingContext, image: TexImageSource return textureId; } -interface Uniforms { - [name: string]: WebGLUniformLocation | null -} +type Uniforms = Record; interface CompiledFilter { id: WebGLProgram, uniforms: Uniforms, duration: Expr, transparent: string | null, - paramsPanel: Tag + paramsPanel: Tag } -interface Snapshot { - [name: string]: { - uniform: WebGLUniformLocation | null, - value: number | null - } -} +export type Snapshot = Record // TODO(#54): pre-load all of the filters and just switch between them without loading/unloading them constantly function loadFilterProgram(gl: WebGLRenderingContext, filter: Filter, vertexAttribs: VertexAttribs): CompiledFilter { - let vertexShader = compileShaderSource(gl, filter.vertex, gl.VERTEX_SHADER); - let fragmentShader = compileShaderSource(gl, filter.fragment, gl.FRAGMENT_SHADER); - let id = linkShaderProgram(gl, [vertexShader, fragmentShader], vertexAttribs); + const vertexShader = compileShaderSource(gl, filter.vertex, gl.VERTEX_SHADER); + const fragmentShader = compileShaderSource(gl, filter.fragment, gl.FRAGMENT_SHADER); + const id = linkShaderProgram(gl, [vertexShader, fragmentShader], vertexAttribs); gl.deleteShader(vertexShader); gl.deleteShader(fragmentShader); gl.useProgram(id); - let uniforms: Uniforms = { - "resolution": gl.getUniformLocation(id, 'resolution'), - "time": gl.getUniformLocation(id, 'time'), - "emoteSize": gl.getUniformLocation(id, 'emoteSize'), + const uniforms: Uniforms = { + resolution: gl.getUniformLocation(id, 'resolution'), + time: gl.getUniformLocation(id, 'time'), + emoteSize: gl.getUniformLocation(id, 'emoteSize'), }; // TODO(#55): there no "reset to default" button in the params panel of a filter - let paramsPanel = div().att$("class", "widget-element"); - let paramsInputs: {[name: string]: Tag} = {}; + const paramsPanel = div().att$("class", "widget-element"); + const paramsInputs: Record> = {}; + + // if (!filter.params) throw new Error("unknown error"); - for (let paramName in filter.params) { + for (const [paramName, paramVal] of Object.entries(filter.params ?? [])) { if (paramName in uniforms) { throw new Error(`Redefinition of existing uniform parameter ${paramName}`); } - - switch (filter.params[paramName].type) { + + switch (paramVal.type) { case "float": { - const valuePreview = span(filter.params[paramName].init.toString()); + const valuePreview = span(paramVal.init.toString()); const valueInput = input("range"); - if (filter.params[paramName].min !== undefined) { - valueInput.att$("min", filter.params[paramName].min); + if (paramVal.min !== undefined) { + valueInput.att$("min", String(paramVal.min)); } - if (filter.params[paramName].max !== undefined) { - valueInput.att$("max", filter.params[paramName].max); + if (paramVal.max !== undefined) { + valueInput.att$("max", String(paramVal.max)); } - if (filter.params[paramName].step !== undefined) { - valueInput.att$("step", filter.params[paramName].step); + if (paramVal.step !== undefined) { + valueInput.att$("step", String(paramVal.step)); } - if (filter.params[paramName].init !== undefined) { - valueInput.att$("value", filter.params[paramName].init); + if (paramVal.init !== undefined) { + valueInput.att$("value", String(paramVal.init)); } paramsInputs[paramName] = valueInput; - valueInput.oninput = function () { + valueInput.oninput = function(this) { valuePreview.innerText = this.value; paramsPanel.dispatchEvent(new CustomEvent("paramsChanged")); }; - const label: string = filter.params[paramName].label ?? paramName; + const label = paramVal.label ?? paramName; paramsPanel.appendChild(div( span(`${label}: `), valuePreview, @@ -162,7 +163,7 @@ function loadFilterProgram(gl: WebGLRenderingContext, filter: Filter, vertexAttr } break; default: { - throw new Error(`Filter parameters do not support type ${filter.params[paramName].type}`) + throw new Error(`Filter parameters do not support type ${paramVal.type}`) } } @@ -170,23 +171,23 @@ function loadFilterProgram(gl: WebGLRenderingContext, filter: Filter, vertexAttr } - paramsPanel.paramsSnapshot$ = function() { + paramsPanel.paramsSnapshot$ = () => { let snapshot: Snapshot = {}; - for (let paramName in paramsInputs) { + for (const paramName in paramsInputs) { snapshot[paramName] = { - "uniform": uniforms[paramName], - "value": Number(paramsInputs[paramName].value) + uniform: uniforms[paramName]!, + value: Number(paramsInputs[paramName]!.value) }; } return snapshot; }; return { - "id": id, - "uniforms": uniforms, - "duration": compile_expr(filter.duration), - "transparent": filter.transparent, - "paramsPanel": paramsPanel, + id, + uniforms, + duration: compile_expr(filter.duration), + transparent: filter.transparent, + paramsPanel: paramsPanel, }; } @@ -194,17 +195,17 @@ function ImageSelector() { const imageInput = input("file"); const imagePreview = img("img/tsodinClown.png") .att$("class", "widget-element") - .att$("width", CANVAS_WIDTH); + .att$("width", String(CANVAS_WIDTH)); const root = div( div(imageInput).att$("class", "widget-element"), imagePreview ).att$("class", "widget"); - root.selectedImage$ = function() { + root.selectedImage$ = () => { return imagePreview; }; - root.selectedFileName$ = function() { + root.selectedFileName$ = () => { function removeFileNameExt(fileName: string): string { if (fileName.includes('.')) { return fileName.split('.').slice(0, -1).join('.'); @@ -217,12 +218,12 @@ function ImageSelector() { return file ? removeFileNameExt(file.name) : 'result'; }; - root.updateFiles$ = function(files: FileList) { + root.updateFiles$ = (files) => { imageInput.files = files; - imageInput.onchange(); + imageInput.onchange?.(); } - imagePreview.addEventListener('load', function(this: HTMLImageElement) { + imagePreview.addEventListener('load', function(this) { root.dispatchEvent(new CustomEvent("imageSelected", { detail: { imageData: this @@ -230,12 +231,12 @@ function ImageSelector() { })); }); - imagePreview.addEventListener('error', function(this: HTMLImageElement) { + imagePreview.addEventListener('error', function(this) { imageInput.value = ''; this.src = 'img/error.png'; }); - imageInput.onchange = function() { + imageInput.onchange = function(this) { imagePreview.src = URL.createObjectURL(this.files[0]); }; @@ -246,15 +247,15 @@ function FilterList() { const root = select(); // Populating the FilterList - for (let name in filters) { + for (const name in filters) { root.add(new Option(name)); } - root.selectedFilter$ = function() { + root.selectedFilter$ = () => { return filters[root.selectedOptions[0].value]; }; - root.onchange = function() { + root.onchange = () => { root.dispatchEvent(new CustomEvent('filterChanged', { detail: { filter: root.selectedFilter$() @@ -262,7 +263,7 @@ function FilterList() { })); }; - root.addEventListener('wheel', function(e: WheelEvent) { + root.addEventListener('wheel', (e: WheelEvent) => { e.preventDefault(); if (e.deltaY < 0) { root.selectedIndex = Math.max(root.selectedIndex - 1, 0); @@ -270,7 +271,7 @@ function FilterList() { if (e.deltaY > 0) { root.selectedIndex = Math.min(root.selectedIndex + 1, root.length - 1); } - root.onchange(); + root.onchange?.(); }); return root; @@ -279,8 +280,8 @@ function FilterList() { function FilterSelector() { const filterList_ = FilterList(); const filterPreview = canvas() - .att$("width", CANVAS_WIDTH) - .att$("height", CANVAS_HEIGHT); + .att$("width", String(CANVAS_WIDTH)) + .att$("height", String(CANVAS_HEIGHT)); const root = div( div("Filter: ", filterList_) .att$("class", "widget-element"), @@ -293,38 +294,33 @@ function FilterSelector() { } // Initialize GL + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + // Mesh Position { - gl.enable(gl.BLEND); - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - - // Mesh Position - { - let meshPositionBufferData = new Float32Array(TRIANGLE_PAIR * TRIANGLE_VERTICIES * VEC2_COUNT); - for (let triangle = 0; triangle < TRIANGLE_PAIR; ++triangle) { - for (let vertex = 0; vertex < TRIANGLE_VERTICIES; ++vertex) { - const quad = triangle + vertex; - const index = - triangle * TRIANGLE_VERTICIES * VEC2_COUNT + - vertex * VEC2_COUNT; - meshPositionBufferData[index + VEC2_X] = (2 * (quad & 1) - 1); - meshPositionBufferData[index + VEC2_Y] = (2 * ((quad >> 1) & 1) - 1); - } + const meshPositionBufferData = new Float32Array(TRIANGLE_PAIR * TRIANGLE_VERTICIES * VEC2_COUNT); + for (let triangle = 0; triangle < TRIANGLE_PAIR; ++triangle) { + for (let vertex = 0; vertex < TRIANGLE_VERTICIES; ++vertex) { + const quad = triangle + vertex; + const index = + triangle * TRIANGLE_VERTICIES * VEC2_COUNT + + vertex * VEC2_COUNT; + meshPositionBufferData[index + VEC2_X] = (2 * (quad & 1) - 1); + meshPositionBufferData[index + VEC2_Y] = (2 * ((quad >> 1) & 1) - 1); } - - let meshPositionBuffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, meshPositionBuffer); - gl.bufferData(gl.ARRAY_BUFFER, meshPositionBufferData, gl.STATIC_DRAW); - - const meshPositionAttrib = vertexAttribs['meshPosition']; - gl.vertexAttribPointer( - meshPositionAttrib, - VEC2_COUNT, - gl.FLOAT, - false, - 0, - 0); - gl.enableVertexAttribArray(meshPositionAttrib); } + const meshPositionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, meshPositionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, meshPositionBufferData, gl.STATIC_DRAW); + const meshPositionAttrib = vertexAttribs['meshPosition']; + gl.vertexAttribPointer( + meshPositionAttrib!, + VEC2_COUNT, + gl.FLOAT, + false, + 0, + 0); + gl.enableVertexAttribArray(meshPositionAttrib!); } // TODO(#49): FilterSelector does not handle loadFilterProgram() failures @@ -336,8 +332,8 @@ function FilterSelector() { function syncParams() { if (program) { const snapshot = program.paramsPanel.paramsSnapshot$(); - for (let paramName in snapshot) { - gl.uniform1f(snapshot[paramName].uniform, snapshot[paramName].value); + for (const paramName in snapshot) { + gl!.uniform1f(snapshot[paramName]!.uniform, snapshot[paramName]!.value!); } } } @@ -349,7 +345,7 @@ function FilterSelector() { } syncParams(); - root.updateImage$ = function(newEmoteImage: HTMLImageElement) { + root.updateImage$ = (newEmoteImage: HTMLImageElement) => { emoteImage = newEmoteImage; if (emoteTexture) { gl.deleteTexture(emoteTexture); @@ -374,7 +370,7 @@ function FilterSelector() { syncParams(); }); - root.render$ = function (filename: string): any | undefined { + root.render$ = (filename) => { if (program === undefined) { console.warn('Could not rendering anything because the filter was not selected'); return undefined; @@ -395,14 +391,14 @@ function FilterSelector() { }); const context: UserContext = { - "vars": { + vars: { "Math.PI": Math.PI } }; if (context.vars !== undefined) { const snapshot = program.paramsPanel.paramsSnapshot$(); - for (let paramName in snapshot) { - context.vars[paramName] = snapshot[paramName].value; + for (const paramName in snapshot) { + context.vars[paramName] = snapshot[paramName]!.value!; } } @@ -433,15 +429,15 @@ function FilterSelector() { let t = 0.0; while (t <= duration) { - gl.uniform1f(program.uniforms.time, t); - gl.uniform2f(program.uniforms.resolution, CANVAS_WIDTH, CANVAS_HEIGHT); - gl.uniform2f(program.uniforms.emoteSize, emoteImage.width, emoteImage.height); + gl.uniform1f(program.uniforms.time!, t); + gl.uniform2f(program.uniforms.resolution!, CANVAS_WIDTH, CANVAS_HEIGHT); + gl.uniform2f(program.uniforms.emoteSize!, emoteImage.width, emoteImage.height); gl.clearColor(0.0, 1.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); gl.drawArrays(gl.TRIANGLES, 0, TRIANGLE_PAIR * TRIANGLE_VERTICIES); - let pixels = new Uint8ClampedArray(4 * CANVAS_WIDTH * CANVAS_HEIGHT); + const pixels = new Uint8ClampedArray(4 * CANVAS_WIDTH * CANVAS_HEIGHT); gl.readPixels(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, gl.RGBA, gl.UNSIGNED_BYTE, pixels); // Flip the image vertically { @@ -453,8 +449,8 @@ function FilterSelector() { const bi = (CANVAS_HEIGHT - y - 1) * 4 * CANVAS_WIDTH + x; const a = pixels[ai]; const b = pixels[bi]; - pixels[ai] = b; - pixels[bi] = a; + pixels[ai] = b!; + pixels[bi] = a!; } } } @@ -490,14 +486,14 @@ function FilterSelector() { // Rendering Loop { - const step = function(timestamp: number) { + const step = (timestamp: number) => { gl.clearColor(0.0, 1.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); if (program && emoteImage) { - gl.uniform1f(program.uniforms.time, timestamp * 0.001); - gl.uniform2f(program.uniforms.resolution, filterPreview.width, filterPreview.height); - gl.uniform2f(program.uniforms.emoteSize, emoteImage.width, emoteImage.height); + gl.uniform1f(program.uniforms.time!, timestamp * 0.001); + gl.uniform2f(program.uniforms.resolution!, filterPreview.width, filterPreview.height); + gl.uniform2f(program.uniforms.emoteSize!, emoteImage.width, emoteImage.height); gl.drawArrays(gl.TRIANGLES, 0, TRIANGLE_PAIR * TRIANGLE_VERTICIES); } @@ -512,19 +508,6 @@ function FilterSelector() { } window.onload = () => { - if ("serviceWorker" in navigator) { - navigator.serviceWorker.register('serviceworker.js').then( - (registration) => { - console.log("Registered a Service Worker ", registration); - }, - (error) => { - console.error("Could not register a Service Worker ", error); - }, - ); - } else { - console.error("Service Workers are not supported in this browser."); - } - feature_params = new URLSearchParams(document.location.search).has("feature-params"); const filterSelectorEntry = document.getElementById('filter-selector-entry'); @@ -538,19 +521,19 @@ window.onload = () => { const imageSelector = ImageSelector(); const filterSelector = FilterSelector(); - imageSelector.addEventListener('imageSelected', function(e: CustomEvent) { + imageSelector.addEventListener('imageSelected', ((e: CustomEvent) => { filterSelector.updateImage$(e.detail.imageData); - }); + }) as EventListener); filterSelectorEntry.appendChild(filterSelector); imageSelectorEntry.appendChild(imageSelector); // drag file from anywhere - document.ondrop = function(event: DragEvent) { + document.ondrop = (event: DragEvent) => { event.preventDefault(); - imageSelector.updateFiles$(event.dataTransfer?.files); + imageSelector.updateFiles$(event.dataTransfer?.files!); } - document.ondragover = function(event) { + document.ondragover = (event) => { event.preventDefault(); } @@ -561,12 +544,11 @@ window.onload = () => { if (renderButton === null) { throw new Error('Could not find "render"'); } - renderButton.onclick = function() { - if (gif && gif.running) { + renderButton.onclick = () => { + if (gif?.running) { gif.abort(); } const fileName = imageSelector.selectedFileName$(); gif = filterSelector.render$(`${fileName}.gif`); }; } -// TODO(#75): run typescript compiler on CI diff --git a/ts/grecha.ts b/ts/grecha.ts deleted file mode 100644 index dcef8d2..0000000 --- a/ts/grecha.ts +++ /dev/null @@ -1,108 +0,0 @@ -const LOREM: string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; - -type Child = string | HTMLElement; -// TODO(#73): make tag more typesafe -// Essentially get rid of the `any` -type Tag = any; - -function tag(name: string, ...children: Child[]): Tag { - const result: Tag = document.createElement(name); - for (const child of children) { - if (typeof(child) === 'string') { - result.appendChild(document.createTextNode(child)); - } else { - result.appendChild(child); - } - } - - result.att$ = function(name: string, value: string) { - this.setAttribute(name, value); - return this; - }; - - - result.onclick$ = function(callback: (this: GlobalEventHandlers, ev: MouseEvent) => Tag) { - this.onclick = callback; - return this; - }; - - return result; -} - -function canvas(...children: Child[]): Tag { - return tag("canvas", ...children); -} - -function h1(...children: Child[]): Tag { - return tag("h1", ...children); -} - -function h2(...children: Child[]): Tag { - return tag("h2", ...children); -} - -function h3(...children: Child[]): Tag { - return tag("h3", ...children); -} - -function p(...children: Child[]): Tag { - return tag("p", ...children); -} - -function a(...children: Child[]): Tag { - return tag("a", ...children); -} - -function div(...children: Child[]): Tag { - return tag("div", ...children); -} - -function span(...children: Child[]): Tag { - return tag("span", ...children); -} - -function select(...children: Child[]): Tag { - return tag("select", ...children); -} - - -function img(src: string): Tag { - return tag("img").att$("src", src); -} - -function input(type: string): Tag { - return tag("input").att$("type", type); -} - -interface Routes { - [route: string]: Tag -} - -function router(routes: Routes): Tag { - let result = div(); - - function syncHash() { - let hashLocation = document.location.hash.split('#')[1]; - if (!hashLocation) { - hashLocation = '/'; - } - - if (!(hashLocation in routes)) { - const route404 = '/404'; - console.assert(route404 in routes); - hashLocation = route404; - } - - while (result.firstChild) { - result.removeChild(result.lastChild); - } - result.appendChild(routes[hashLocation]); - - return result; - }; - - syncHash(); - window.addEventListener("hashchange", syncHash); - - return result; -} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8c95007 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,65 @@ +{ + "compilerOptions": { + "target": "ES5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "lib": ["DOM", "DOM.Iterable", "ES5"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "ESNext", /* Specify what module code is generated. */ + // "rootDir": "./ts", /* Specify the root folder within your source files. */ + "moduleResolution": "Bundler", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + "paths": { + "@dhdbstjr98/gif.js": ["./node_modules/@types/gif.js"] + }, + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + + /* Emit */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./js", /* Specify an output folder for all emitted files. */ + "removeComments": true, /* Disable emitting comments. */ + + /* Interop Constraints */ + "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + + /* Completeness */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "exclude" : [ + "src/gif.js", + "src/gif.worker.js", + "build.js", + "js/**", + "dist/**" + ] +}