From 1c3c342fe305b9e75f1f5f9232ce84bedfcdf672 Mon Sep 17 00:00:00 2001 From: Julie Lala Date: Wed, 18 Dec 2013 22:14:47 -0500 Subject: ui template --- js/record.concat.js | 357 +++++++++++++++++++++++++++++++++++++++++++++++++++- js/record.min.js | 2 +- js/ui-template.js | 13 ++ 3 files changed, 365 insertions(+), 7 deletions(-) create mode 100644 js/ui-template.js (limited to 'js') diff --git a/js/record.concat.js b/js/record.concat.js index 4a03964..b16fbcd 100644 --- a/js/record.concat.js +++ b/js/record.concat.js @@ -277,13 +277,345 @@ function mime(uri) { return dataUriToBlob; })() -;// Total frames to record -var FRAMES_PER_GIF = 36; +;function shuffle(a){ + var aa = new Array(a.length); + aa[0] = a[0]; -// Frames per second to read from the video -var FPS = 12; + for (var i = 1; i < a.length; i++) { + var j = Math.floor( Math.random() * i ); + aa[i] = aa[j]; + aa[j] = a[i]; + } + return aa; +} +function sample(a, n) { + var aa = shuffle(a); + return aa.slice(0,n); +} +;var nextTick = (function(){ + // postMessage behaves badly on IE8 + if (window.ActiveXObject || !window.postMessage) { + var nextTick = function(fn) { + setTimeout(fn, 0); + } + } else { + // based on setZeroTimeout by David Baron + // - http://dbaron.org/log/20100309-faster-timeouts + var timeouts = [] + , name = 'next-tick-zero-timeout' + + window.addEventListener('message', function(e){ + if (e.source == window && e.data == name) { + if (e.stopPropagation) e.stopPropagation(); + if (timeouts.length) timeouts.shift()(); + } + }, true); + + var nextTick = function(fn){ + timeouts.push(fn); + window.postMessage(name, '*'); + } + } + + return nextTick; +})() + +var Uid = (function(){ + var id = 0 + return function(){ return id++ + "" } +})() + + +var tokenize = (function(){ + var tokenize = function(str, splitOn){ + return str + .trim() + .split(splitOn || tokenize.default); + }; + + tokenize.default = /\s+/g; + + return tokenize; +})() + +// globber("*".split(":"), "a:b:c".split(":")) => true +// globber("*:c".split(":"), "a:b:c".split(":")) => true +// globber("a:*".split(":"), "a:b:c".split(":")) => true +// globber("a:*:c".split(":"), "a:b:c".split(":")) => true + +// based on codegolf.stackexchange.com/questions/467/implement-glob-matcher +var globber = function(patterns, strings) { + // console.log("globber called with: " + patterns.join(":"), strings.join(":")) + var first = patterns[0], + rest = patterns.slice(1), + len = strings.length, + matchFound; + + if(first === '*') { + for(var i = 0; i <= len; ++i) { + // console.log("* " + i + " trying " + rest.join(":") + " with " + strings.slice(i).join(":")) + if(globber(rest, strings.slice(i))) return true; + } + return false; + } else { + matchFound = (first === strings[0]); + // console.log ("literal matching " + first + " " + strings[0] + " " + !!matched) + } + + return matchFound && ((!rest.length && !len) || globber(rest, strings.slice(1))); +}; + +var setproto = function(obj, proto){ + if (obj.__proto__) + obj.__proto__ = proto; + else + for (var key in proto) + obj[key] = proto[key]; +}; + + +var Tube = (function(){ + var globcache = {}; + var Tube = function(opts){ + opts = opts || {}; + if (opts.queue){ + var c = function(){ + var args = arguments; + // queueOrNextTick (function(){ c.send.apply(c, args) }); + nextTick (function(){ c.send.apply(c, args) }); + return c; + }; + } else { + var c = function(){ + c.send.apply(c, arguments); + return c; + }; + } + + setproto(c, Tube.proto); + c.listeners = {}; + c.globListeners = {}; + + return c; + }; + + Tube.total = {}; + Tube.proto = {}; + + /* + adds fns as listeners to a channel + + on("msg", fn, {opts}) + on("msg", [fn, fn2], {opts}) + on("msg msg2 msg3", fn, {opts}) + on({"msg": fn, "msg2": fn2}, {opts}) + */ + + Tube.proto.on = function(){ + var chan = this; + if (typeof arguments[0] === "string") { + //if (arguments.length > 1) { // on("msg", f) + var msgMap = {}; + msgMap[arguments[0]] = arguments[1]; + var opts = arguments[2] || {}; + } else { // on({"msg": f, ...}) + var msgMap = arguments[0]; + var opts = arguments[1] || {}; + } + + for (var string in msgMap){ + var msgs = string.split(" "); + var fs = msgMap[string]; + if (!Array.isArray(fs)) fs = [fs]; + + for(var i=0, f; f=fs[i]; i++){ + if (!f.uid) f.uid = Uid(); + } + + for(var i=0, msg; msg=msgs[i]; i++){ + var listeners = (msg.indexOf("*") === -1) ? + chan.listeners : + chan.globListeners; + + // todo: this probably wastes a lot of memory? + // make a copy of the listener, add to it, and replace the listener + // why not just push directly? + // send might be iterating over it... and that will fuck up the iteration + + listeners[msg] = (msg in listeners) ? + listeners[msg].concat(fs) : + fs.concat(); + } + } + + return chan; + }; + + /* + off() + off("a:b:c") + off(f) + off("a:b:c", f) + off("a:b:c d:e:f") + off([f, f2]) + off({"a": f, "b": f2}) + */ -// Per-frame delay in milliseconds + Tube.proto.off = function(){ var chan = this; + + var listeners, i, msgs, msg; + + // off() : delete all listeners. but replace, instead of delete + if (arguments.length === 0) { + chan.listeners = {}; + chan.globListeners = {}; + return chan; + } + + // off("a:b:c d:e:f") + // remove all matching listeners + if (arguments.length === 1 && typeof arguments[0] === "string"){ + // question... will this fuck up send if we delete in the middle of it dispatching? + msgs = arguments[0].split(" "); + + for (i=0; msg=msgs[i]; i++){ + delete chan.listeners[msg]; + delete chan.globListeners[msg]; + } + return chan; + } + + // off(f) or off([f, f2]) + // remove all matching functions + if (typeof arguments[0] === "function" || Array.isArray(arguments[0])) { + var fs = (typeof arguments[0] === "function") ? + [arguments[0]] : + arguments[0]; + // TODO + return chan; + } + + // off("a:b:c", f) or off({"a": f, "b": f2}) + if (arguments.length > 1) { // off("msg", f) + var msgMap = {}; + msgMap[arguments[0]] = arguments[1]; + } else { // off({"msg": f, ...}) + var msgMap = arguments[0]; + } + + for (var string in msgMap){ + msgs = string.split(" "); + + var fs = msgMap[string]; + if (typeof fs === "function") fs = [fs]; + + for(var i=0; msg=msgs[i]; i++){ + if (msg in chan.listeners) + listeners = chan.listeners; + else if (msg in chan.globListeners) + listeners = chan.globListeners; + else + continue; + + // gotta do this carefully in case we are still iterating through the listener in send + // build a new array and assign it to the property, instead of mutating it. + + // console.log(" length of listeners[" + msg + "]: " + listeners[msg].length) + // console.log(listeners[msg].join(",")); + // console.log(fs.join(",")); + + listeners[msg] = listeners[msg].filter( + function(f){ return fs.indexOf(f) === -1 } + ); + + // console.log(" length of listeners[" + msg + "]: " + listeners[msg].length) + + } + } + + return chan; + + }; + + /* + c = Tube() + c.on("foo", fn) + c("foo", "bar", []) + + will call fn("bar", [], "foo") + */ + + Tube.proto.send = function(msgString /*, data... */){ + // todo: don't do this? + if (!Tube.total[msgString]) Tube.total[msgString] = 0 + Tube.total[msgString]+=1; + + var listener, + listeners = this.listeners, + globListeners = this.globListeners, + //args = Array.prototype.splice.call(arguments, 1), + msgs = tokenize(msgString), + msg, f; + + if (arguments.length) { + var args = Array.prototype.splice.call(arguments, 1); + args.push(msgString); + + } else { + var args = []; + } + + for (var m=0; msg=msgs[m]; m++){ + + var fsToRun = []; + var uidKeyFnValue = {}; + var uidKeyMsgStringValue = {}; + + // note this will die on errors + // todo: implement http://dean.edwards.name/weblog/2009/03/callbacks-vs-events/ + // exact matches + if (listener = listeners[msg]) { + for (var i=0; f=listener[i]; i++){ + // fsToRun.push([f, msg]); + uidKeyFnValue[f.uid] = f; + uidKeyMsgStringValue[f.uid] = msg; + } + } + + // glob matches + var msgSplit = msg.split(":"); + + for (var pattern in globListeners){ + + if (pattern !== "*") { // * always matches + var patternSplit = globcache[pattern] || (globcache[pattern] = pattern.split(":")); + if (!globber(patternSplit, msgSplit)) continue; + } + + listener = globListeners[pattern]; + + for (var i=0; f=listener[i]; i++){ + //f.apply(window, args); // hm possibly pass the actual message to the func + // fsToRun.push([f, msg]); + uidKeyFnValue[f.uid] = f; + uidKeyMsgStringValue[f.uid] = msg; + } + } + + var fns = []; + for (var f in uidKeyFnValue) fns.push(uidKeyFnValue[f]); + + for (var i=0, f; f=fns[i]; i++) + f.apply(f, args); + + } + return this; + }; + + return Tube; +})() + +;// Per-frame delay in milliseconds var DELAY = Math.floor( 1000 / FPS ); // Number of WebWorkers to create @@ -392,7 +724,7 @@ function GifEncoder(){ var ww = []; base.init = function(){ for (var i = 0; i < WORKERS; i++) { - var worker = new Worker('js/vendor/gif-encode/worker.js'); + var worker = new Worker('http://asdf.us/gif-recorder/js/gif-encode/worker.concat.js'); worker.onmessage = base.receiveWork; ww.push(worker); } @@ -556,6 +888,19 @@ function GifEncoder(){ } } +;var TEMPLATE = [ + '
', + '
', + '
', + 'frames ', + 'delay ', + '', + '', + '', + '
', + '
' +].join("") + ;(function(){ var canvases = document.getElementsByTagName("canvas") if (canvases.length == 0) { alert("no canvas found"); return; } diff --git a/js/record.min.js b/js/record.min.js index 6644460..b3cc4f6 100644 --- a/js/record.min.js +++ b/js/record.min.js @@ -1,2 +1,2 @@ /* asdf.us/dither */ -function GifEncoder(){function a(){var a=this,b=0,c=[];a.init=function(){for(var b=0;WORKERS>b;b++){var d=new Worker("js/vendor/gif-encode/worker.js");d.onmessage=a.receiveWork,c.push(d)}};var d={};a.hire=function(a,b){d[a]=b},a.work=function(a){c[++b%c.length].postMessage(a)},a.receiveWork=function(a){a.data.task in d&&d[a.data.task](a)},a.init()}function b(a){console.log("[WORKER]",a.data.message)}function c(a){console.log(Date.now()-q,"quantization done"),i=a.data.neuquant,j=a.data.colortab,f.quantized=!0,f.tube("quantized")}function d(a){var b=a.data.frame_index,c=a.data.frame_data;m[b]=c,f.tube("encoded-frame",m.length,k.length);for(var d=0;dE&&(C-=E,l()),F==D?m():requestAnimationFrame(j)}function k(){J=new Array(D);for(var a=0;D>a;a++)J[a]=document.createElement("canvas"),J[a].height=y,J[a].width=x}function l(){var a=J[F++],b=a.getContext("2d");b.fillStyle=document.body.backgroundColor,b.fillRect(0,0,x,y),b.drawImage(v,z,A,x,y,0,0,x,y)}function m(){w.reset(),w.addFrames(J,E),n("encoding");try{w.encode()}catch(a){throw rendering=!1,n(a),a}}function n(a){document.getElementById("status").innerHTML=a}function o(a,b){n("encoded "+a+" / "+b)}function p(a){n(s(a.length))}function q(a){var b=new Image;I=b.src=a,document.getElementById("preview").innerHTML="",document.getElementById("preview").appendChild(b),rendering=!1,f("record"),f("save")}function r(){if(I){var a=(window.location.host+window.location.pathname).replace(/[^a-zA-Z0-9]/g,"-").replace(/-+/,"-"),b=dataUriToBlob(I);saveAs(b,a+"-"+ +new Date+".gif")}}function s(a){return 1e3>a?a+" bytes":1e6>a?t(a/1e3)+" kb":1e9>a?t(a/1e6)+" mb":"WAY TOO BIG DUDE"}function t(a){var b=Math.floor(a);return b+"."+Math.round(10*(a-b))}var u=document.getElementsByTagName("canvas");if(0==u.length)return alert("no canvas found"),void 0;var v=u[0],w=new GifEncoder;w.on("encoded-frame",o),w.on("rendered",p),w.on("rendered-url",q);var x,y,z,A,B,C,D,E,F,G,H,I,J=[],K=!1;a()}(); \ No newline at end of file +function shuffle(a){var b=new Array(a.length);b[0]=a[0];for(var c=1;cb;b++){var d=new Worker("http://asdf.us/gif-recorder/js/gif-encode/worker.concat.js");d.onmessage=a.receiveWork,c.push(d)}};var d={};a.hire=function(a,b){d[a]=b},a.work=function(a){c[++b%c.length].postMessage(a)},a.receiveWork=function(a){a.data.task in d&&d[a.data.task](a)},a.init()}function b(a){console.log("[WORKER]",a.data.message)}function c(a){console.log(Date.now()-q,"quantization done"),i=a.data.neuquant,j=a.data.colortab,f.quantized=!0,f.tube("quantized")}function d(a){var b=a.data.frame_index,c=a.data.frame_data;m[b]=c,f.tube("encoded-frame",m.length,k.length);for(var d=0;d=g;++g)if(globber(e,b.slice(g)))return!0;return!1}return c=d===b[0],c&&(!e.length&&!f||globber(e,b.slice(1)))},setproto=function(a,b){if(a.__proto__)a.__proto__=b;else for(var c in b)a[c]=b[c]},Tube=function(){var a={},b=function(a){if(a=a||{},a.queue)var c=function(){var a=arguments;return nextTick(function(){c.send.apply(c,a)}),c};else var c=function(){return c.send.apply(c,arguments),c};return setproto(c,b.proto),c.listeners={},c.globListeners={},c};return b.total={},b.proto={},b.proto.on=function(){var a=this;if("string"==typeof arguments[0]){var b={};b[arguments[0]]=arguments[1];{arguments[2]||{}}}else{var b=arguments[0];arguments[1]||{}}for(var c in b){var d=c.split(" "),e=b[c];Array.isArray(e)||(e=[e]);for(var f,g=0;f=e[g];g++)f.uid||(f.uid=Uid());for(var h,g=0;h=d[g];g++){var i=-1===h.indexOf("*")?a.listeners:a.globListeners;i[h]=h in i?i[h].concat(e):e.concat()}}return a},b.proto.off=function(){var a,b,c,d,e=this;if(0===arguments.length)return e.listeners={},e.globListeners={},e;if(1===arguments.length&&"string"==typeof arguments[0]){for(c=arguments[0].split(" "),b=0;d=c[b];b++)delete e.listeners[d],delete e.globListeners[d];return e}if("function"==typeof arguments[0]||Array.isArray(arguments[0])){var f="function"==typeof arguments[0]?[arguments[0]]:arguments[0];return e}if(arguments.length>1){var g={};g[arguments[0]]=arguments[1]}else var g=arguments[0];for(var h in g){c=h.split(" ");var f=g[h];"function"==typeof f&&(f=[f]);for(var b=0;d=c[b];b++){if(d in e.listeners)a=e.listeners;else{if(!(d in e.globListeners))continue;a=e.globListeners}a[d]=a[d].filter(function(a){return-1===f.indexOf(a)})}}return e},b.proto.send=function(c){b.total[c]||(b.total[c]=0),b.total[c]+=1;var d,e,f,g=this.listeners,h=this.globListeners,i=tokenize(c);if(arguments.length){var j=Array.prototype.splice.call(arguments,1);j.push(c)}else var j=[];for(var k=0;e=i[k];k++){var l={},m={};if(d=g[e])for(var n=0;f=d[n];n++)l[f.uid]=f,m[f.uid]=e;var o=e.split(":");for(var p in h){if("*"!==p){var q=a[p]||(a[p]=p.split(":"));if(!globber(q,o))continue}d=h[p];for(var n=0;f=d[n];n++)l[f.uid]=f,m[f.uid]=e}var r=[];for(var f in l)r.push(l[f]);for(var f,n=0;f=r[n];n++)f.apply(f,j)}return this},b}(),DELAY=Math.floor(1e3/FPS),WORKERS=4,FRAMES_TO_QUANTIZE=4,DO_UPLOAD=!0,TEMPLATE=['
','
','
','frames ','delay ','','','','
',"
"].join("");!function(){function a(){document.body.style.width="100%",document.body.style.height="100%",document.body.parentNode.style.width="100%",document.body.parentNode.style.height="100%";var a=document.getElementById("template").innerHTML,c=document.createElement("div");c.innerHTML=a,document.body.appendChild(c),b()}function b(){G=document.getElementById("curtain"),H=document.getElementById("outline"),controls=document.getElementById("outline"),document.getElementById("record").addEventListener("click",i,!1),document.getElementById("save").addEventListener("click",r,!1),G.addEventListener("mousedown",c,!1),G.addEventListener("mousemove",d,!1),G.addEventListener("mouseup",e,!1),H.style.display="none",controls.style.display="none"}function c(a){z=a.pageX,A=a.pageY,x=1,y=1,K=!0,H.style.left=h(z-1),H.style.top=h(A-1),H.style.width=h(x),H.style.height=h(y),H.style.display="block"}function d(a){K&&(x=a.pageX-z,y=a.pageY-A,H.style.width=h(x),H.style.height=h(y))}function e(){K=!1,controls.style.display="block",f("record"),document.getElementById("record").focus()}function f(a){document.getElementById(a).removeAttribute("disabled")}function g(a){document.getElementById(a).setAttribute("disabled","disabled")}function h(a){return a+"px"}function i(){D=parseInt(document.getElementById("framecount").value),E=1e3*parseFloat(document.getElementById("framedelay").value),F=0,C=0,k(),l(),B=+new Date,requestAnimationFrame(j),n("recording"),g("record")}function j(){var a=(document.createElement("canvas"),+new Date);C+=a-B,B=a,C>E&&(C-=E,l()),F==D?m():requestAnimationFrame(j)}function k(){J=new Array(D);for(var a=0;D>a;a++)J[a]=document.createElement("canvas"),J[a].height=y,J[a].width=x}function l(){var a=J[F++],b=a.getContext("2d");b.fillStyle=document.body.backgroundColor,b.fillRect(0,0,x,y),b.drawImage(v,z,A,x,y,0,0,x,y)}function m(){w.reset(),w.addFrames(J,E),n("encoding");try{w.encode()}catch(a){throw rendering=!1,n(a),a}}function n(a){document.getElementById("status").innerHTML=a}function o(a,b){n("encoded "+a+" / "+b)}function p(a){n(s(a.length))}function q(a){var b=new Image;I=b.src=a,document.getElementById("preview").innerHTML="",document.getElementById("preview").appendChild(b),rendering=!1,f("record"),f("save")}function r(){if(I){var a=(window.location.host+window.location.pathname).replace(/[^a-zA-Z0-9]/g,"-").replace(/-+/,"-"),b=dataUriToBlob(I);saveAs(b,a+"-"+ +new Date+".gif")}}function s(a){return 1e3>a?a+" bytes":1e6>a?t(a/1e3)+" kb":1e9>a?t(a/1e6)+" mb":"WAY TOO BIG DUDE"}function t(a){var b=Math.floor(a);return b+"."+Math.round(10*(a-b))}var u=document.getElementsByTagName("canvas");if(0==u.length)return alert("no canvas found"),void 0;var v=u[0],w=new GifEncoder;w.on("encoded-frame",o),w.on("rendered",p),w.on("rendered-url",q);var x,y,z,A,B,C,D,E,F,G,H,I,J=[],K=!1;a()}(); \ No newline at end of file diff --git a/js/ui-template.js b/js/ui-template.js new file mode 100644 index 0000000..9c3a6a7 --- /dev/null +++ b/js/ui-template.js @@ -0,0 +1,13 @@ +var TEMPLATE = [ + '
', + '
', + '
', + 'frames ', + 'delay ', + '', + '', + '', + '
', + '
' +].join("") + -- cgit v1.2.3-70-g09d2