ld31-space-diggers/vendors/playground.js
Ruben Müller 979aabcad4 Fix
2014-12-07 22:50:23 +01:00

1440 lines
31 KiB
JavaScript

/*
Playground 1.0.2
http://canvasquery.org
(c) 2012-2014 http://rezoner.net
Playground may be freely distributed under the MIT license.
*/
function playground(args) {
return new Playground(args);
};
/* utitlities */
playground.extend = function() {
for (var i = 1; i < arguments.length; i++) {
for (var j in arguments[i]) {
arguments[0][j] = arguments[i][j];
}
}
return arguments[0];
};
playground.throttle = function(fn, threshold) {
threshold || (threshold = 250);
var last,
deferTimer;
return function() {
var context = this;
var now = +new Date,
args = arguments;
if (last && now < last + threshold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function() {
last = now;
fn.apply(context, args);
}, threshold);
} else {
last = now;
fn.apply(context, args);
}
};
};
/* constructor */
function Playground(args) {
/* defaults */
playground.extend(this, {
smoothing: 1,
scale: 1,
preventKeyboardDefault: true,
preventContextMenu: true
}, args);
if (!this.width || !this.height) this.fitToContainer = true;
if (!this.container) this.container = document.body;
if (this.container !== document.body) this.customContainer = true;
if (typeof this.container === "string") this.container = document.querySelector(this.container);
/* state */
this.state = {};
/* layer */
if (!args.layer) {
cq.smoothing = this.smoothing;
if (window.CocoonJS) {
this.layer = cq.cocoon(1, 1);
this.layer.appendTo(this.container);
this.screen = this.layer;
} else {
this.layer = cq(1, 1);
if (this.scaleToFit) {
this.screen = cq(1, 1);
this.screen.appendTo(this.container);
} else {
this.layer.appendTo(this.container);
this.screen = this.layer;
}
}
}
var canvas = this.screen.canvas;
/* events */
this.eventsHandler = this.eventsHandler.bind(this);
/* mouse */
this.mouse = new playground.Mouse(canvas);
this.mouse.on("event", this.eventsHandler);
this.mouse.preventContextMenu = this.preventContextMenu;
/* touch */
this.touch = new playground.Touch(canvas);
this.touch.on("event", this.eventsHandler);
/* keyboard */
this.keyboard = new playground.Keyboard();
this.keyboard.preventDefault = this.preventKeyboardDefault;
this.keyboard.on("event", this.eventsHandler);
/* gamepads */
this.gamepads = new playground.Gamepads();
this.gamepads.on("event", this.eventsHandler);
/* window resize */
window.addEventListener("resize", this.resizeHandler.bind(this));
setTimeout(this.resizeHandler.bind(this), 1);
/* video recorder */
this.videoRecorder = new playground.VideoRecorder(this);
/* game loop */
var self = this;
var lastTick = Date.now();
function step() {
requestAnimationFrame(step);
var delta = Date.now() - lastTick;
lastTick = Date.now();
if (delta > 1000) return;
var dt = delta / 1000;
if (self.loader.count <= 0) {
if (self.step) self.step(dt);
if (self.state.step) self.state.step(dt);
if (self.render) self.render(dt);
if (self.state.render) self.state.render(dt);
if (self.postrender) self.postrender(dt);
if (self.state.postrender) self.state.postrender(dt);
} else {
self.renderLoader(dt);
}
if (self.scaleToFit) {
self.screen.save();
self.screen.translate(self.offsetX, self.offsetY);
self.screen.scale(self.scale, self.scale);
// self.layer.drawImage(self.scanlines.canvas, 0, 0);
self.screen.drawImage(self.layer.canvas, 0, 0);
self.screen.restore();
}
self.gamepads.step(dt);
self.videoRecorder.step(dt);
};
requestAnimationFrame(step);
/* assets */
/* default audio format */
var canPlayMp3 = (new Audio).canPlayType("audio/mp3");
var canPlayOgg = (new Audio).canPlayType('audio/ogg; codecs="vorbis"');
if (canPlayMp3) this.audioFormat = "mp3";
else this.audioFormat = "ogg";
this.loader = new playground.Loader();
this.images = {};
this.sounds = {};
this.loadFoo(0.5);
if (this.create) setTimeout(this.create.bind(this));
this.loader.on("ready", function() {
if (self.ready) self.ready();
self.ready = function() {};
});
};
Playground.prototype = {
setState: function(state) {
state.app = this;
if (this.state && this.state.leave) this.state.leave();
this.state = state;
if (this.state && this.state.enter) this.state.enter();
},
eventsHandler: function(event, data) {
if (this[event]) this[event](data);
if (this.state[event]) this.state[event](data);
},
resizeHandler: function() {
if (this.customContainer) {
var containerWidth = this.container.offsetWidth;
var containerHeight = this.container.offsetHeight;
} else {
var containerWidth = window.innerWidth;
var containerHeight = window.innerHeight;
}
if (this.fitToContainer) {
this.width = this.containerWidth;
this.height = this.containerHeight;
}
if (!this.scaleToFit) {
if (this.fitToContainer) {
this.width = containerWidth;
this.height = containerHeight;
}
this.offsetX = 0;
this.offsetY = 0;
} else {
this.screen.width = containerWidth;
this.screen.height = containerHeight;
this.scale = Math.min(containerWidth / this.width, containerHeight / this.height);
if (this.roundScale) this.scale = Math.max(1, Math.floor(this.scale));
this.offsetX = containerWidth / 2 - this.scale * (this.width / 2) | 0;
this.offsetY = containerHeight / 2 - this.scale * (this.height / 2) | 0;
this.mouse.scale = this.scale;
this.mouse.offsetX = this.offsetX;
this.mouse.offsetY = this.offsetY;
this.touch.scale = this.scale;
this.touch.offsetX = this.offsetX;
this.touch.offsetY = this.offsetY;
}
this.layer.width = this.width;
this.layer.height = this.height;
this.center = {
x: this.width / 2 | 0,
y: this.height / 2 | 0
};
this.screen.clear("#000");
this.eventsHandler("resize");
this.mouse.update();
this.touch.update();
},
renderLoader: function() {
var height = this.height / 10 | 0;
var x = 32;
var width = this.width - x * 2;
var y = this.height / 2 - height / 2 | 0;
this.layer.clear("#000");
this.layer.strokeStyle("#fff").lineWidth(2).strokeRect(x, y, width, height);
this.layer.fillStyle("#fff").fillRect(x, y, width * this.loader.progress | 0, height);
},
record: function(args) {
this.videoRecorder.toggle(args);
},
/* imaginary timeout to delay loading */
loadFoo: function(timeout) {
if (!this.foofooLoader) this.foofooLoader = 0;
var loader = this.loader;
this.loader.add("foo " + timeout);
setTimeout(function() {
loader.ready("foo " + timeout);
}, (this.foofooLoader += timeout * 1000));
},
/* images */
loadImages: function() {
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
/* polymorphism at its finest */
if (typeof arg === "object") {
for (var key in arg) this.addImages(arg[key]);
} else {
/* if argument is not an object/array let's try to load it */
var filename = arg;
var loader = this.loader;
var fileinfo = filename.match(/(.*)\..*/);
var key = fileinfo ? fileinfo[1] : filename;
/* filename defaults to png */
if (!fileinfo) filename += ".png";
var path = "images/" + filename;
this.loader.add(path);
var image = this.images[key] = new Image;
image.addEventListener("load", function() {
loader.ready(path);
});
image.addEventListener("error", function() {
loader.error(path);
});
image.src = path;
}
}
},
/* sounds */
loadSounds: function() {
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
/* polymorphism at its finest */
if (typeof arg === "object") {
for (var key in arg) this.loadSounds(arg[key]);
} else {
/* if argument is not an object/array let's try to load it */
var filename = arg;
var loader = this.loader;
var key = filename;
filename += "." + this.audioFormat;
var path = "sounds/" + filename;
this.loader.add(path);
var audio = this.sounds[key] = new Audio;
audio.addEventListener("canplay", function() {
loader.ready(path);
});
audio.addEventListener("error", function() {
loader.error(path);
});
audio.src = path;
}
}
},
loadFont: function(name) {
var styleNode = document.createElement("style");
styleNode.type = "text/css";
var formats = {
"woff": "woff",
"ttf": "truetype"
};
var sources = "";
for (var ext in formats) {
var type = formats[ext];
sources += " url(\"fonts/" + name + "." + ext + "\") format('" + type + "');"
}
styleNode.textContent = "@font-face { font-family: '" + name + "'; src: " + sources + " }";
document.head.appendChild(styleNode);
var layer = cq(32, 32);
layer.font("10px huj");
layer.fillText(16, 16, 16).trim();
var width = layer.width;
var height = layer.height;
this.loader.add("font " + name);
var self = this;
function check() {
var layer = cq(32, 32);
layer.font("10px " + name).fillText(16, 16, 16);
layer.trim();
if (layer.width !== width || layer.height !== height) {
self.loader.ready("font " + name);
} else {
setTimeout(check, 250);
}
};
check();
},
playSound: function(key, loop) {
var sound = this.sounds[key];
sound.currentTime = 0;
sound.loop = loop;
sound.play();
return sound;
},
stopSound: function(sound) {
if (typeof sound === "string") sound = this.sounds[sound];
sound.pause();
}
};
playground.Events = function() {
this.listeners = {};
};
playground.Events.prototype = {
on: function(event, callback) {
if (!this.listeners[event]) this.listeners[event] = [];
this.listeners[event].push(callback);
return callback;
},
once: function(event, callback) {
callback.once = true;
if (!this.listeners[event]) this.listeners[event] = [];
this.listeners[event].push(callback);
return callback;
},
off: function(event, callback) {
for (var i = 0, len = this.listeners[event].length; i < len; i++) {
if (this.listeners[event][i]._remove) {
this.listeners[event].splice(i--, 1);
len--;
}
}
},
trigger: function(event, data) {
/* if you prefer events pipe */
if (this.listeners["event"]) {
for (var i = 0, len = this.listeners["event"].length; i < len; i++) {
this.listeners["event"][i](event, data);
}
}
/* or subscribed to single event */
if (this.listeners[event]) {
for (var i = 0, len = this.listeners[event].length; i < len; i++) {
var listener = this.listeners[event][i];
listener(data);
if (listener.once) {
this.listeners[event].splice(i--, 1);
}
}
}
}
};
/* Mouse */
playground.Mouse = function(element) {
var self = this;
playground.Events.call(this);
this.element = element;
this.buttons = {};
this.mousemoveEvent = {};
this.mousedownEvent = {};
this.mouseupEvent = {};
this.mousewheelEvent = {};
this.x = 0;
this.y = 0;
this.offsetX = 0;
this.offsetY = 0;
this.scale = 1;
element.addEventListener("mousemove", this.mousemove.bind(this));
element.addEventListener("mousedown", this.mousedown.bind(this));
element.addEventListener("mouseup", this.mouseup.bind(this));
this.enableMousewheel();
this.element.addEventListener("contextmenu", function(e) {
if (self.preventContextMenu) e.preventDefault();
});
};
playground.Mouse.prototype = {
getElementOffset: function(element) {
var offsetX = 0;
var offsetY = 0;
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
}
while ((element = element.offsetParent));
return {
x: offsetX,
y: offsetY
};
},
update: function() {
this.elementOffset = this.getElementOffset(this.element);
},
mousemove: function(e) {
this.x = this.mousemoveEvent.x = (e.pageX - this.elementOffset.x - this.offsetX) / this.scale | 0;
this.y = this.mousemoveEvent.y = (e.pageY - this.elementOffset.y - this.offsetY) / this.scale | 0;
this.mousemoveEvent.original = e;
this.trigger("mousemove", this.mousemoveEvent);
},
mousedown: function(e) {
var buttonName = ["left", "middle", "right"][e.button];
this.mousedownEvent.x = this.mousemoveEvent.x;
this.mousedownEvent.y = this.mousemoveEvent.y;
this.mousedownEvent.button = buttonName;
this.mousedownEvent.original = e;
this[buttonName] = true;
this.trigger("mousedown", this.mousedownEvent);
},
mouseup: function(e) {
var buttonName = ["left", "middle", "right"][e.button];
this.mouseupEvent.x = this.mousemoveEvent.x;
this.mouseupEvent.y = this.mousemoveEvent.y;
this.mouseupEvent.button = buttonName;
this.mouseupEvent.original = e;
this[buttonName] = false;
this.trigger("mouseup", this.mouseupEvent);
},
mousewheel: function(e) {
this.mousewheelEvent.x = this.mousemoveEvent.x;
this.mousewheelEvent.y = this.mousemoveEvent.y;
this.mousewheelEvent.button = ["none", "left", "middle", "right"][e.button];
this.mousewheelEvent.original = e;
this[e.button] = false;
this.trigger("mousewheel", this.mousewheelEvent);
},
enableMousewheel: function() {
var eventNames = 'onwheel' in document || document.documentMode >= 9 ? ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'];
var callback = this.mousewheel.bind(this);
var self = this;
for (var i = eventNames.length; i;) {
self.element.addEventListener(eventNames[--i], playground.throttle(function(event) {
var orgEvent = event || window.event,
args = [].slice.call(arguments, 1),
delta = 0,
deltaX = 0,
deltaY = 0,
absDelta = 0,
absDeltaXY = 0,
fn;
event.type = "mousewheel";
// Old school scrollwheel delta
if (orgEvent.wheelDelta) {
delta = orgEvent.wheelDelta;
}
if (orgEvent.detail) {
delta = orgEvent.detail * -1;
}
// New school wheel delta (wheel event)
if (orgEvent.deltaY) {
deltaY = orgEvent.deltaY * -1;
delta = deltaY;
}
// Webkit
if (orgEvent.wheelDeltaY !== undefined) {
deltaY = orgEvent.wheelDeltaY;
}
var result = delta ? delta : deltaY;
self.mousewheelEvent.x = self.mousemoveEvent.x;
self.mousewheelEvent.y = self.mousemoveEvent.y;
self.mousewheelEvent.delta = result / Math.abs(result);
self.mousewheelEvent.original = orgEvent;
callback(self.mousewheelEvent);
event.preventDefault();
}, 40), false);
}
}
};
playground.extend(playground.Mouse.prototype, playground.Events.prototype);
/* Touch */
playground.Touch = function(element) {
playground.Events.call(this);
this.element = element;
this.buttons = {};
this.touchmoveEvent = {};
this.touchstartEvent = {};
this.touchendEvent = {};
this.x = 0;
this.y = 0;
this.offsetX = 0;
this.offsetY = 0;
this.scale = 1;
element.addEventListener("touchmove", this.touchmove.bind(this));
element.addEventListener("touchstart", this.touchstart.bind(this));
element.addEventListener("touchend", this.touchend.bind(this));
};
playground.Touch.prototype = {
getElementOffset: function(element) {
var offsetX = 0;
var offsetY = 0;
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
}
while ((element = element.offsetParent));
return {
x: offsetX,
y: offsetY
};
},
update: function() {
this.elementOffset = this.getElementOffset(this.element);
},
touchmove: function(e) {
var touch = e.touches[0] || e.changedTouches[0];
this.x = this.touchmoveEvent.x = (touch.pageX - this.elementOffset.x - this.offsetX) / this.scale | 0;
this.y = this.touchmoveEvent.y = (touch.pageY - this.elementOffset.y - this.offsetY) / this.scale | 0;
this.touchmoveEvent.original = e;
this.touchmoveEvent.identifier = e.identifier;
this.trigger("touchmove", this.touchmoveEvent);
e.preventDefault();
},
touchstart: function(e) {
this.touchstartEvent.x = this.touchmoveEvent.x;
this.touchstartEvent.y = this.touchmoveEvent.y;
this.touchstartEvent.original = e;
this.touchstartEvent.identifier = e.identifier;
this.pressed = true;
this.trigger("touchstart", this.touchstartEvent);
},
touchend: function(e) {
this.touchendEvent.x = this.touchmoveEvent.x;
this.touchendEvent.y = this.touchmoveEvent.y;
this.touchendEvent.original = e;
this.touchendEvent.identifier = e.identifier;
this.pressed = false;
this.trigger("touchend", this.touchendEvent);
}
};
playground.extend(playground.Touch.prototype, playground.Events.prototype);
/* Keyboard */
playground.Keyboard = function() {
playground.Events.call(this);
this.keys = {};
document.addEventListener("keydown", this.keydown.bind(this));
document.addEventListener("keyup", this.keyup.bind(this));
document.addEventListener("keypress", this.keypress.bind(this));
this.keydownEvent = {};
this.keyupEvent = {};
};
playground.Keyboard.prototype = {
keycodes: {
37: "left",
38: "up",
39: "right",
40: "down",
45: "insert",
46: "delete",
8: "backspace",
9: "tab",
13: "enter",
16: "shift",
17: "ctrl",
18: "alt",
19: "pause",
20: "capslock",
27: "escape",
32: "space",
33: "pageup",
34: "pagedown",
35: "end",
36: "home",
112: "f1",
113: "f2",
114: "f3",
115: "f4",
116: "f5",
117: "f6",
118: "f7",
119: "f8",
120: "f9",
121: "f10",
122: "f11",
123: "f12",
144: "numlock",
145: "scrolllock",
186: "semicolon",
187: "equal",
188: "comma",
189: "dash",
190: "period",
191: "slash",
192: "graveaccent",
219: "openbracket",
220: "backslash",
221: "closebraket",
222: "singlequote"
},
keypress: function(e) {
},
keydown: function(e) {
if (e.which >= 48 && e.which <= 90) var keyName = String.fromCharCode(e.which).toLowerCase();
else var keyName = this.keycodes[e.which];
if (this.keys[keyName]) return;
this.keydownEvent.key = keyName;
this.keydownEvent.original = e;
this.keys[keyName] = true;
this.trigger("keydown", this.keydownEvent);
if (this.preventDefault && document.activeElement === document.body) {
e.returnValue = false;
e.keyCode = 0;
e.preventDefault();
e.stopPropagation();
}
},
keyup: function(e) {
if (e.which >= 48 && e.which <= 90) var keyName = String.fromCharCode(e.which).toLowerCase();
else var keyName = this.keycodes[e.which];
this.keyupEvent.key = keyName;
this.keyupEvent.original = e;
this.keys[keyName] = false;
this.trigger("keyup", this.keyupEvent);
}
};
playground.extend(playground.Keyboard.prototype, playground.Events.prototype);
/* Gamepad */
playground.Gamepads = function() {
playground.Events.call(this);
this.getGamepads = navigator.getGamepads || navigator.webkitGetGamepads;
this.gamepadmoveEvent = {};
this.gamepaddownEvent = {};
this.gamepadupEvent = {};
this.gamepads = {};
};
playground.Gamepads.prototype = {
buttons: {
0: "1",
1: "2",
2: "3",
3: "4",
4: "l1",
5: "r1",
6: "l2",
7: "r2",
8: "select",
9: "start",
12: "up",
13: "down",
14: "left",
15: "right"
},
zeroState: function() {
var buttons = [];
for (var i = 0; i <= 15; i++) {
buttons.push({
pressed: false,
value: 0
});
}
return {
axes: [],
buttons: buttons
};
},
createGamepad: function() {
var result = {
buttons: {},
sticks: [{
x: 0,
y: 0
}, {
x: 0,
y: 0
}]
};
for (var i = 0; i < 16; i++) {
var key = this.buttons[i];
result.buttons[key] = false;
}
return result;
},
step: function() {
if (!navigator.getGamepads) return;
var gamepads = navigator.getGamepads();
for (var i = 0; i < gamepads.length; i++) {
var current = gamepads[i];
if (!current) continue;
if (!this[i]) this[i] = this.createGamepad();
/* have to concat the current.buttons because the are read-only */
var buttons = [].concat(current.buttons);
/* hack for missing dpads */
for (var h = 12; h <= 15; h++) {
if (!buttons[h]) buttons[h] = {
pressed: false,
value: 0
};
}
var previous = this[i];
/* axes (sticks) to buttons */
if (current.axes) {
if (current.axes[0] < 0) buttons[14].pressed = true;
if (current.axes[0] > 0) buttons[15].pressed = true;
if (current.axes[1] < 0) buttons[12].pressed = true;
if (current.axes[1] > 0) buttons[13].pressed = true;
previous.sticks[0].x = current.axes[0].value;
previous.sticks[0].y = current.axes[1].value;
previous.sticks[1].x = current.axes[2].value;
previous.sticks[1].y = current.axes[3].value;
}
/* check buttons changes */
for (var j = 0; j < buttons.length; j++) {
var key = this.buttons[j];
/* gamepad down */
if (buttons[j].pressed && !previous.buttons[key]) {
previous.buttons[key] = true;
this.gamepaddownEvent.button = this.buttons[j];
this.gamepaddownEvent.gamepad = i;
this.trigger("gamepaddown", this.gamepaddownEvent);
}
/* gamepad up */
else if (!buttons[j].pressed && previous.buttons[key]) {
previous.buttons[key] = false;
this.gamepadupEvent.button = this.buttons[j];
this.gamepadupEvent.gamepad = i;
this.trigger("gamepadup", this.gamepadupEvent);
}
}
}
}
};
playground.extend(playground.Gamepads.prototype, playground.Events.prototype);
/* Loader */
playground.Loader = function() {
playground.Events.call(this);
this.reset();
};
playground.Loader.prototype = {
/* loader */
add: function(id) {
this.queue++;
this.count++;
this.trigger("add", id);
},
error: function(id) {
console.log("unable to load " + id);
this.trigger("error", id);
},
ready: function(id) {
this.queue--;
this.progress = 1 - this.queue / this.count;
this.trigger("load", id);
if (this.queue <= 0) {
this.trigger("ready");
this.reset();
}
},
reset: function() {
this.progress = 0;
this.queue = 0;
this.count = 0;
}
};
playground.extend(playground.Loader.prototype, playground.Events.prototype);
CanvasQuery.Layer.prototype.playground = function(args) {
args.layer = this;
return playground(args);
};
/* Video recorder */
/* whammy - https://github.com/antimatter15/whammy */
window.Whammy = function() {
function h(a, b) {
for (var c = r(a), c = [{
id: 440786851,
data: [{
data: 1,
id: 17030
}, {
data: 1,
id: 17143
}, {
data: 4,
id: 17138
}, {
data: 8,
id: 17139
}, {
data: "webm",
id: 17026
}, {
data: 2,
id: 17031
}, {
data: 2,
id: 17029
}]
}, {
id: 408125543,
data: [{
id: 357149030,
data: [{
data: 1E6,
id: 2807729
}, {
data: "whammy",
id: 19840
}, {
data: "whammy",
id: 22337
}, {
data: s(c.duration),
id: 17545
}]
}, {
id: 374648427,
data: [{
id: 174,
data: [{
data: 1,
id: 215
}, {
data: 1,
id: 25541
}, {
data: 0,
id: 156
}, {
data: "und",
id: 2274716
}, {
data: "V_VP8",
id: 134
}, {
data: "VP8",
id: 2459272
}, {
data: 1,
id: 131
}, {
id: 224,
data: [{
data: c.width,
id: 176
}, {
data: c.height,
id: 186
}]
}]
}]
}]
}], e = 0, d = 0; e < a.length;) {
var g = [],
f = 0;
do g.push(a[e]), f += a[e].duration, e++; while (e < a.length && 3E4 > f);
var h = 0,
g = {
id: 524531317,
data: [{
data: d,
id: 231
}].concat(g.map(function(a) {
var b = t({
discardable: 0,
frame: a.data.slice(4),
invisible: 0,
keyframe: 1,
lacing: 0,
trackNum: 1,
timecode: Math.round(h)
});
h += a.duration;
return {
data: b,
id: 163
}
}))
};
c[1].data.push(g);
d += f
}
return m(c, b)
}
function r(a) {
for (var b = a[0].width, c = a[0].height, e = a[0].duration,
d = 1; d < a.length; d++) {
if (a[d].width != b) throw "Frame " + (d + 1) + " has a different width";
if (a[d].height != c) throw "Frame " + (d + 1) + " has a different height";
if (0 > a[d].duration || 32767 < a[d].duration) throw "Frame " + (d + 1) + " has a weird duration (must be between 0 and 32767)";
e += a[d].duration
}
return {
duration: e,
width: b,
height: c
}
}
function u(a) {
for (var b = []; 0 < a;) b.push(a & 255), a >>= 8;
return new Uint8Array(b.reverse())
}
function n(a) {
var b = [];
a = (a.length % 8 ? Array(9 - a.length % 8).join("0") : "") + a;
for (var c = 0; c < a.length; c += 8) b.push(parseInt(a.substr(c,
8), 2));
return new Uint8Array(b)
}
function m(a, b) {
for (var c = [], e = 0; e < a.length; e++) {
var d = a[e].data;
"object" == typeof d && (d = m(d, b));
"number" == typeof d && (d = n(d.toString(2)));
if ("string" == typeof d) {
for (var g = new Uint8Array(d.length), f = 0; f < d.length; f++) g[f] = d.charCodeAt(f);
d = g
}
f = d.size || d.byteLength || d.length;
g = Math.ceil(Math.ceil(Math.log(f) / Math.log(2)) / 8);
f = f.toString(2);
f = Array(7 * g + 8 - f.length).join("0") + f;
g = Array(g).join("0") + "1" + f;
c.push(u(a[e].id));
c.push(n(g));
c.push(d)
}
return b ? (c = p(c), new Uint8Array(c)) :
new Blob(c, {
type: "video/webm"
})
}
function p(a, b) {
null == b && (b = []);
for (var c = 0; c < a.length; c++) "object" == typeof a[c] ? p(a[c], b) : b.push(a[c]);
return b
}
function t(a) {
var b = 0;
a.keyframe && (b |= 128);
a.invisible && (b |= 8);
a.lacing && (b |= a.lacing << 1);
a.discardable && (b |= 1);
if (127 < a.trackNum) throw "TrackNumber > 127 not supported";
return [a.trackNum | 128, a.timecode >> 8, a.timecode & 255, b].map(function(a) {
return String.fromCharCode(a)
}).join("") + a.frame
}
function q(a) {
for (var b = a.RIFF[0].WEBP[0], c = b.indexOf("\u009d\u0001*"),
e = 0, d = []; 4 > e; e++) d[e] = b.charCodeAt(c + 3 + e);
e = d[1] << 8 | d[0];
c = e & 16383;
e = d[3] << 8 | d[2];
return {
width: c,
height: e & 16383,
data: b,
riff: a
}
}
function k(a) {
for (var b = 0, c = {}; b < a.length;) {
var e = a.substr(b, 4),
d = parseInt(a.substr(b + 4, 4).split("").map(function(a) {
a = a.charCodeAt(0).toString(2);
return Array(8 - a.length + 1).join("0") + a
}).join(""), 2),
g = a.substr(b + 4 + 4, d),
b = b + (8 + d);
c[e] = c[e] || [];
"RIFF" == e || "LIST" == e ? c[e].push(k(g)) : c[e].push(g)
}
return c
}
function s(a) {
return [].slice.call(new Uint8Array((new Float64Array([a])).buffer),
0).map(function(a) {
return String.fromCharCode(a)
}).reverse().join("")
}
function l(a, b) {
this.frames = [];
this.duration = 1E3 / a;
this.quality = b || .8
}
l.prototype.add = function(a, b) {
if ("undefined" != typeof b && this.duration) throw "you can't pass a duration if the fps is set";
if ("undefined" == typeof b && !this.duration) throw "if you don't have the fps set, you ned to have durations here.";
"canvas" in a && (a = a.canvas);
if ("toDataURL" in a) a = a.toDataURL("image/webp", this.quality);
else if ("string" != typeof a) throw "frame must be a a HTMLCanvasElement, a CanvasRenderingContext2D or a DataURI formatted string";
if (!/^data:image\/webp;base64,/ig.test(a)) throw "Input must be formatted properly as a base64 encoded DataURI of type image/webp";
this.frames.push({
image: a,
duration: b || this.duration
})
};
l.prototype.compile = function(a) {
return new h(this.frames.map(function(a) {
var c = q(k(atob(a.image.slice(23))));
c.duration = a.duration;
return c
}), a)
};
return {
Video: l,
fromImageArray: function(a, b, c) {
return h(a.map(function(a) {
a = q(k(atob(a.slice(23))));
a.duration = 1E3 / b;
return a
}), c)
},
toWebM: h
}
}();
playground.VideoRecorder = function(app, args) {
this.app = app;
};
playground.VideoRecorder.prototype = {
setup: function(args) {
this.region = false;
playground.extend(this, {
followMouse: false,
framerate: 20,
scale: 1
}, args);
if (!this.region) {
this.region = [0, 0, this.app.layer.width, this.app.layer.height];
}
this.playbackRate = this.framerate / 60;
this.layer = cq(this.region[2] * this.scale | 0, this.region[3] * this.scale | 0);
},
start: function(args) {
this.setup(args);
this.encoder = new Whammy.Video(this.framerate);
this.captureTimeout = 0;
this.recording = true;
},
step: function(delta) {
if (this.encoder) {
this.captureTimeout -= delta * 1000;
if (this.captureTimeout <= 0) {
this.captureTimeout = 1000 / this.framerate + this.captureTimeout;
this.layer.drawImage(this.app.layer.canvas, this.region[0], this.region[1], this.region[2], this.region[3], 0, 0, this.layer.width, this.layer.height);
this.encoder.add(this.layer.canvas);
}
this.app.screen.save().lineWidth(8).strokeStyle("#c00").strokeRect(0, 0, this.app.screen.width, this.app.screen.height).restore();
}
},
stop: function() {
if (!this.encoder) return;
var output = this.encoder.compile();
var url = (window.webkitURL || window.URL).createObjectURL(output);
window.open(url);
this.recording = false;
delete this.encoder;
},
toggle: function(args) {
if (this.encoder) this.stop();
else this.start(args);
}
};