2
var Recorder = (function() {
4
var init = 'reproduce=';
5
var initLocation = window.location.search.indexOf(init);
6
var replaying = initLocation >= 0;
7
var raf = window['requestAnimationFrame'] ||
8
window['mozRequestAnimationFrame'] ||
9
window['webkitRequestAnimationFrame'] ||
10
window['msRequestAnimationFrame'] ||
11
window['oRequestAnimationFrame'];
16
recorder.frameCounter = 0; // the frame counter is used to know when to replay events
17
recorder.start = function() {
18
alert("Starting recording! Don't forget to Recorder.finish() afterwards!");
20
recorder.frameCounter++;
24
recorder.started = true;
27
recorder.randoms = [];
28
recorder.random = Math.random;
29
Math.random = function() {
30
var ret = recorder.random();
31
recorder.randoms.push(ret);
34
// Date.now, performance.now
36
recorder.dnow = Date.now;
37
Date.now = function() {
38
var ret = recorder.dnow();
39
recorder.dnows.push(ret);
43
var pnow = performance.now || performance.webkitNow || performance.mozNow || performance.oNow || performance.msNow;
44
recorder.pnow = function() { return pnow.call(performance) };
45
performance.now = function() {
46
var ret = recorder.pnow();
47
recorder.pnows.push(ret);
51
recorder.devents = []; // document events
52
recorder.onEvent = function(which, callback) {
53
document['on' + which] = function(event) {
54
if (!recorder.started) return true;
55
event.frameCounter = recorder.frameCounter;
56
recorder.devents.push(event);
57
return callback(event); // XXX do we need to record the return value?
60
recorder.tevents = []; // custom-target events. Currently we assume a single such custom target (aside from document), e.g., a canvas for the game.
61
recorder.addListener = function(target, which, callback, arg) {
62
target.addEventListener(which, function(event) {
63
if (!recorder.started) return true;
64
event.frameCounter = recorder.frameCounter;
65
recorder.tevents.push(event);
66
return callback(event); // XXX do we need to record the return value?
70
recorder.finish = function() {
71
// Reorder data because pop() is faster than shift()
72
recorder.randoms.reverse();
73
recorder.dnows.reverse();
74
recorder.pnows.reverse();
75
recorder.devents.reverse();
76
recorder.tevents.reverse();
77
// Make JSON.stringify work on data from native event objects (and only store relevant ones)
78
var importantProperties = {
80
movementX: 1, mozMovementX: 1, webkitMovementX: 1,
81
movementY: 1, mozMovementY: 1, webkitMovementY: 1,
90
function importantize(event) {
92
for (var prop in importantProperties) {
94
ret[prop] = event[prop];
99
recorder.devents = recorder.devents.map(importantize);
100
recorder.tevents = recorder.tevents.map(importantize);
102
alert('Writing out data, remember to save!');
103
setTimeout(function() {
105
document.write(JSON.stringify(recorder));
112
var dataPath = window.location.search.substring(initLocation + init.length);
113
var baseURL = window.location.toString().replace('://', 'cheez999').split('?')[0].split('/').slice(0, -1).join('/').replace('cheez999', '://');
114
if (baseURL[baseURL.length-1] != '/') baseURL += '/';
115
var path = baseURL + dataPath;
116
alert('Loading replay from ' + path);
117
var request = new XMLHttpRequest();
118
request.open('GET', path, false);
120
var raw = request.responseText;
121
raw = raw.substring(raw.indexOf('{'), raw.lastIndexOf('}')+1); // remove <html> etc
122
recorder = JSON.parse(raw);
125
recorder.frameCounter = 0; // the frame counter is used to know when to replay events
126
recorder.start = function() {
128
recorder.frameCounter++;
130
// replay relevant events for this frame
131
while (recorder.devents.length && recorder.devents[recorder.devents.length-1].frameCounter <= recorder.frameCounter) {
132
var event = recorder.devents.pop();
133
recorder['on' + event.type](event);
135
while (recorder.tevents.length && recorder.tevents[recorder.tevents.length-1].frameCounter <= recorder.frameCounter) {
136
var event = recorder.tevents.pop();
137
recorder['event' + event.type](event);
143
recorder.random = Math.random;
144
Math.random = function() {
145
if (recorder.randoms.length > 0) {
146
return recorder.randoms.pop();
149
throw 'consuming too many random values!';
152
// Date.now, performance.now
153
recorder.dnow = Date.now;
154
Date.now = function() {
155
if (recorder.dnows.length > 0) {
156
return recorder.dnows.pop();
159
throw 'consuming too many Date.now values!';
162
var pnow = performance.now || performance.webkitNow || performance.mozNow || performance.oNow || performance.msNow;
163
recorder.pnow = function() { return pnow.call(performance) };
164
performance.now = function() {
165
if (recorder.pnows.length > 0) {
166
return recorder.pnows.pop();
169
throw 'consuming too many performance.now values!';
173
recorder.onEvent = function(which, callback) {
174
recorder['on' + which] = callback;
176
recorder.eventCallbacks = {};
177
recorder.addListener = function(target, which, callback, arg) {
178
recorder['event' + which] = callback;
180
recorder.onFinish = [];
181
// Benchmarking hooks - emscripten specific
182
setTimeout(function() {
184
var totalSquared = 0;
188
Module.preMainLoop = function() {
189
curr = recorder.pnow();
191
Module.postMainLoop = function() {
192
var time = recorder.pnow() - curr;
194
totalSquared += time*time;
195
maxTime = Math.max(maxTime, time);
198
recorder.onFinish.push(function() {
199
var mean = totalTime / iterations;
200
var meanSquared = totalSquared / iterations;
201
console.log('mean frame : ' + mean + ' ms');
202
console.log('frame std dev: ' + Math.sqrt(meanSquared - (mean*mean)) + ' ms');
203
console.log('max frame : ' + maxTime + ' ms');
207
recorder.finish = function() {
208
recorder.onFinish.forEach(function(finish) {
213
recorder.replaying = replaying;