3
// Utilities for browser environments
5
mergeInto(LibraryManager.library, {
6
$Browser__postset: 'Module["requestFullScreen"] = function(lockPointer, resizeCanvas) { Browser.requestFullScreen(lockPointer, resizeCanvas) };\n' + // exports
7
'Module["requestAnimationFrame"] = function(func) { Browser.requestAnimationFrame(func) };\n' +
8
'Module["pauseMainLoop"] = function() { Browser.mainLoop.pause() };\n' +
9
'Module["resumeMainLoop"] = function() { Browser.mainLoop.resume() };\n',
17
Browser.mainLoop.shouldPause = true;
20
if (Browser.mainLoop.paused) {
21
Browser.mainLoop.paused = false;
22
Browser.mainLoop.scheduler();
24
Browser.mainLoop.shouldPause = false;
26
updateStatus: function() {
27
if (Module['setStatus']) {
28
var message = Module['statusMessage'] || 'Please wait...';
29
var remaining = Browser.mainLoop.remainingBlockers;
30
var expected = Browser.mainLoop.expectedBlockers;
32
if (remaining < expected) {
33
Module['setStatus'](message + ' (' + (expected - remaining) + '/' + expected + ')');
35
Module['setStatus'](message);
38
Module['setStatus']('');
45
moduleContextCreatedCallbacks: [],
49
if (Browser.initted) return;
50
Browser.initted = true;
53
Browser.hasBlobConstructor = true;
55
Browser.hasBlobConstructor = false;
56
console.log("warning: no blob constructor, cannot create blobs with mimetypes");
58
Browser.BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : (!Browser.hasBlobConstructor ? console.log("warning: no BlobBuilder") : null));
59
Browser.URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs");
61
// Support for plugins that can process preloaded files. You can add more of these to
62
// your app by creating and appending to Module.preloadPlugins.
64
// Each plugin is asked if it can handle a file based on the file's name. If it can,
65
// it is given the file's raw data. When it is done, it calls a callback with the file's
66
// (possibly modified) data. For example, a plugin might decompress a file, or it
67
// might create some side data structure for use later (like an Image element, etc.).
69
function getMimetype(name) {
78
}[name.substr(name.lastIndexOf('.')+1)];
81
if (!Module["preloadPlugins"]) Module["preloadPlugins"] = [];
84
imagePlugin['canHandle'] = function(name) {
85
return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/.exec(name);
87
imagePlugin['handle'] = function(byteArray, name, onload, onerror) {
89
if (Browser.hasBlobConstructor) {
91
b = new Blob([byteArray], { type: getMimetype(name) });
93
Runtime.warnOnce('Blob constructor present but fails: ' + e + '; falling back to blob builder');
97
var bb = new Browser.BlobBuilder();
98
bb.append((new Uint8Array(byteArray)).buffer); // we need to pass a buffer, and must copy the array to get the right data range
101
var url = Browser.URLObject.createObjectURL(b);
103
assert(typeof url == 'string', 'createObjectURL must return a url as a string');
105
var img = new Image();
106
img.onload = function() {
107
assert(img.complete, 'Image ' + name + ' could not be decoded');
108
var canvas = document.createElement('canvas');
109
canvas.width = img.width;
110
canvas.height = img.height;
111
var ctx = canvas.getContext('2d');
112
ctx.drawImage(img, 0, 0);
113
Module["preloadedImages"][name] = canvas;
114
Browser.URLObject.revokeObjectURL(url);
115
if (onload) onload(byteArray);
117
img.onerror = function(event) {
118
console.log('Image ' + url + ' could not be decoded');
119
if (onerror) onerror();
123
Module['preloadPlugins'].push(imagePlugin);
125
var audioPlugin = {};
126
audioPlugin['canHandle'] = function(name) {
127
return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 };
129
audioPlugin['handle'] = function(byteArray, name, onload, onerror) {
131
function finish(audio) {
134
Module["preloadedAudios"][name] = audio;
135
if (onload) onload(byteArray);
140
Module["preloadedAudios"][name] = new Audio(); // empty shim
141
if (onerror) onerror();
143
if (Browser.hasBlobConstructor) {
145
var b = new Blob([byteArray], { type: getMimetype(name) });
149
var url = Browser.URLObject.createObjectURL(b); // XXX we never revoke this!
151
assert(typeof url == 'string', 'createObjectURL must return a url as a string');
153
var audio = new Audio();
154
audio.addEventListener('canplaythrough', function() { finish(audio) }, false); // use addEventListener due to chromium bug 124926
155
audio.onerror = function(event) {
157
console.log('warning: browser could not fully decode audio ' + name + ', trying slower base64 approach');
158
function encode64(data) {
159
var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
164
for (var i = 0; i < data.length; i++) {
165
leftchar = (leftchar << 8) | data[i];
167
while (leftbits >= 6) {
168
var curr = (leftchar >> (leftbits-6)) & 0x3f;
174
ret += BASE[(leftchar&3) << 4];
176
} else if (leftbits == 4) {
177
ret += BASE[(leftchar&0xf) << 2];
182
audio.src = 'data:audio/x-' + name.substr(-3) + ';base64,' + encode64(byteArray);
183
finish(audio); // we don't wait for confirmation this worked - but it's worth trying
186
// workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror
187
setTimeout(function() {
188
finish(audio); // try to use it even though it is not necessarily ready to play
194
Module['preloadPlugins'].push(audioPlugin);
196
// Canvas event setup
198
var canvas = Module['canvas'];
199
canvas.requestPointerLock = canvas['requestPointerLock'] ||
200
canvas['mozRequestPointerLock'] ||
201
canvas['webkitRequestPointerLock'];
202
canvas.exitPointerLock = document['exitPointerLock'] ||
203
document['mozExitPointerLock'] ||
204
document['webkitExitPointerLock'];
205
canvas.exitPointerLock = canvas.exitPointerLock.bind(document);
207
function pointerLockChange() {
208
Browser.pointerLock = document['pointerLockElement'] === canvas ||
209
document['mozPointerLockElement'] === canvas ||
210
document['webkitPointerLockElement'] === canvas;
213
document.addEventListener('pointerlockchange', pointerLockChange, false);
214
document.addEventListener('mozpointerlockchange', pointerLockChange, false);
215
document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
217
if (Module['elementPointerLock']) {
218
canvas.addEventListener("click", function(ev) {
219
if (!Browser.pointerLock && canvas.requestPointerLock) {
220
canvas.requestPointerLock();
227
createContext: function(canvas, useWebGL, setInModule) {
228
#if !USE_TYPED_ARRAYS
230
Module.print('(USE_TYPED_ARRAYS needs to be enabled for WebGL)');
237
ctx = canvas.getContext('experimental-webgl', {
239
preserveDrawingBuffer: true,
244
ctx = canvas.getContext('2d');
246
if (!ctx) throw ':(';
248
Module.print('Could not create canvas - ' + e);
253
// Useful to debug native webgl apps: var Module = { printErr: function(x) { console.log(x) } };
256
for (var prop in tempCtx) {
258
switch (typeof tempCtx[prop]) {
260
wrapper[prop] = function() {
262
var printArgs = Array.prototype.slice.call(arguments).map(Runtime.prettyPrint);
263
Module.printErr('[gl_f:' + prop + ':' + printArgs + ']');
265
var ret = tempCtx[prop].apply(tempCtx, arguments);
266
if (GL.debug && typeof ret != 'undefined') {
267
Module.printErr('[ gl:' + prop + ':return:' + Runtime.prettyPrint(ret) + ']');
273
case 'number': case 'string': {
274
wrapper.__defineGetter__(prop, function() {
275
//Module.printErr('[gl_g:' + prop + ':' + tempCtx[prop] + ']');
276
return tempCtx[prop];
278
wrapper.__defineSetter__(prop, function(value) {
280
Module.printErr('[gl_s:' + prop + ':' + value + ']');
282
tempCtx[prop] = value;
291
// Set the background of the WebGL canvas to black
292
canvas.style.backgroundColor = "black";
294
// Warn on context loss
295
canvas.addEventListener('webglcontextlost', function(event) {
296
alert('WebGL context lost. You will need to reload the page.');
301
Module.useWebGL = useWebGL;
302
Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() });
308
destroyContext: function(canvas, useWebGL, setInModule) {},
310
fullScreenHandlersInstalled: false,
311
lockPointer: undefined,
312
resizeCanvas: undefined,
313
requestFullScreen: function(lockPointer, resizeCanvas) {
314
this.lockPointer = lockPointer;
315
this.resizeCanvas = resizeCanvas;
316
if (typeof this.lockPointer === 'undefined') this.lockPointer = true;
317
if (typeof this.resizeCanvas === 'undefined') this.resizeCanvas = false;
319
var canvas = Module['canvas'];
320
function fullScreenChange() {
321
Browser.isFullScreen = false;
322
if ((document['webkitFullScreenElement'] || document['webkitFullscreenElement'] ||
323
document['mozFullScreenElement'] || document['mozFullscreenElement'] ||
324
document['fullScreenElement'] || document['fullscreenElement']) === canvas) {
325
canvas.cancelFullScreen = document['cancelFullScreen'] ||
326
document['mozCancelFullScreen'] ||
327
document['webkitCancelFullScreen'];
328
canvas.cancelFullScreen = canvas.cancelFullScreen.bind(document);
329
if (Browser.lockPointer) canvas.requestPointerLock();
330
Browser.isFullScreen = true;
331
if (Browser.resizeCanvas) Browser.setFullScreenCanvasSize();
332
} else if (Browser.resizeCanvas){
333
Browser.setWindowedCanvasSize();
335
if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullScreen);
338
if (!this.fullScreenHandlersInstalled) {
339
this.fullScreenHandlersInstalled = true;
340
document.addEventListener('fullscreenchange', fullScreenChange, false);
341
document.addEventListener('mozfullscreenchange', fullScreenChange, false);
342
document.addEventListener('webkitfullscreenchange', fullScreenChange, false);
345
canvas.requestFullScreen = canvas['requestFullScreen'] ||
346
canvas['mozRequestFullScreen'] ||
347
(canvas['webkitRequestFullScreen'] ? function() { canvas['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null);
348
canvas.requestFullScreen();
351
requestAnimationFrame: function(func) {
352
if (!window.requestAnimationFrame) {
353
window.requestAnimationFrame = window['requestAnimationFrame'] ||
354
window['mozRequestAnimationFrame'] ||
355
window['webkitRequestAnimationFrame'] ||
356
window['msRequestAnimationFrame'] ||
357
window['oRequestAnimationFrame'] ||
358
window['setTimeout'];
360
window.requestAnimationFrame(func);
363
getMovementX: function(event) {
364
return event['movementX'] ||
365
event['mozMovementX'] ||
366
event['webkitMovementX'] ||
370
getMovementY: function(event) {
371
return event['movementY'] ||
372
event['mozMovementY'] ||
373
event['webkitMovementY'] ||
377
xhrLoad: function(url, onload, onerror) {
378
var xhr = new XMLHttpRequest();
379
xhr.open('GET', url, true);
380
xhr.responseType = 'arraybuffer';
381
xhr.onload = function() {
382
if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0
383
onload(xhr.response);
388
xhr.onerror = onerror;
392
asyncLoad: function(url, onload, onerror, noRunDep) {
393
Browser.xhrLoad(url, function(arrayBuffer) {
394
assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).');
395
onload(new Uint8Array(arrayBuffer));
396
if (!noRunDep) removeRunDependency('al ' + url);
401
throw 'Loading data file "' + url + '" failed.';
404
if (!noRunDep) addRunDependency('al ' + url);
409
updateResizeListeners: function() {
410
var canvas = Module['canvas'];
411
Browser.resizeListeners.forEach(function(listener) {
412
listener(canvas.width, canvas.height);
416
setCanvasSize: function(width, height, noUpdates) {
417
var canvas = Module['canvas'];
418
canvas.width = width;
419
canvas.height = height;
420
if (!noUpdates) Browser.updateResizeListeners();
425
setFullScreenCanvasSize: function() {
426
var canvas = Module['canvas'];
427
this.windowedWidth = canvas.width;
428
this.windowedHeight = canvas.height;
429
canvas.width = screen.width;
430
canvas.height = screen.height;
431
var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}};
432
flags = flags | 0x00800000; // set SDL_FULLSCREEN flag
433
{{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}}
434
Browser.updateResizeListeners();
437
setWindowedCanvasSize: function() {
438
var canvas = Module['canvas'];
439
canvas.width = this.windowedWidth;
440
canvas.height = this.windowedHeight;
441
var flags = {{{ makeGetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'i32', 0, 1) }}};
442
flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag
443
{{{ makeSetValue('SDL.screen+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}}
444
Browser.updateResizeListeners();
449
emscripten_async_wget: function(url, file, onload, onerror) {
450
var _url = Pointer_stringify(url);
451
var _file = Pointer_stringify(file);
452
var index = _file.lastIndexOf('/');
453
FS.createPreloadedFile(
454
_file.substr(0, index),
455
_file.substr(index +1),
458
if (onload) Runtime.dynCall('vi', onload, [file]);
461
if (onerror) Runtime.dynCall('vi', onerror, [file]);
466
emscripten_async_wget_data: function(url, arg, onload, onerror) {
467
Browser.asyncLoad(Pointer_stringify(url), function(byteArray) {
468
var buffer = _malloc(byteArray.length);
469
HEAPU8.set(byteArray, buffer);
470
Runtime.dynCall('viii', onload, [arg, buffer, byteArray.length]);
473
if (onerror) Runtime.dynCall('vi', onerror, [arg]);
474
}, true /* no need for run dependency, this is async but will not do any prepare etc. step */ );
477
emscripten_async_wget2: function(url, file, request, param, arg, onload, onerror, onprogress) {
478
var _url = Pointer_stringify(url);
479
var _file = Pointer_stringify(file);
480
var _request = Pointer_stringify(request);
481
var _param = Pointer_stringify(param);
482
var index = _file.lastIndexOf('/');
484
var http = new XMLHttpRequest();
485
http.open(_request, _url, true);
486
http.responseType = 'arraybuffer';
489
http.onload = function(e) {
490
if (http.status == 200) {
491
FS.createDataFile( _file.substr(0, index), _file.substr(index + 1), new Uint8Array(http.response), true, true);
492
if (onload) Runtime.dynCall('vii', onload, [arg, file]);
494
if (onerror) Runtime.dynCall('vii', onerror, [arg, http.status]);
499
http.onerror = function(e) {
500
if (onerror) Runtime.dynCall('vii', onerror, [arg, http.status]);
504
http.onprogress = function(e) {
505
var percentComplete = (e.position / e.totalSize)*100;
506
if (onprogress) Runtime.dynCall('vii', onprogress, [arg, percentComplete]);
509
// Useful because the browser can limit the number of redirection
511
if (http.channel instanceof Ci.nsIHttpChannel)
512
http.channel.redirectionLimit = 0;
513
} catch (ex) { /* whatever */ }
515
if (_request == "POST") {
516
//Send the proper header information along with the request
517
http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
518
http.setRequestHeader("Content-length", _param.length);
519
http.setRequestHeader("Connection", "close");
526
emscripten_async_prepare: function(file, onload, onerror) {
527
var _file = Pointer_stringify(file);
528
var data = FS.analyzePath(_file);
529
if (!data.exists) return -1;
530
var index = _file.lastIndexOf('/');
531
FS.createPreloadedFile(
532
_file.substr(0, index),
533
_file.substr(index +1),
534
new Uint8Array(data.object.contents), true, true,
536
if (onload) Runtime.dynCall('vi', onload, [file]);
539
if (onerror) Runtime.dynCall('vi', onerror, [file]);
541
true // don'tCreateFile - it's already there
546
emscripten_async_prepare_data: function(data, size, suffix, arg, onload, onerror) {
547
var _suffix = Pointer_stringify(suffix);
548
if (!Browser.asyncPrepareDataCounter) Browser.asyncPrepareDataCounter = 0;
549
var name = 'prepare_data_' + (Browser.asyncPrepareDataCounter++) + '.' + _suffix;
550
var cname = _malloc(name.length+1);
551
writeStringToMemory(name, cname);
552
FS.createPreloadedFile(
555
{{{ makeHEAPView('U8', 'data', 'data + size') }}},
558
if (onload) Runtime.dynCall('vii', onload, [arg, cname]);
561
if (onerror) Runtime.dynCall('vi', onerror, [arg]);
563
true // don'tCreateFile - it's already there
568
emscripten_async_run_script__deps: ['emscripten_run_script'],
569
emscripten_async_run_script: function(script, millis) {
570
Module['noExitRuntime'] = true;
572
// TODO: cache these to avoid generating garbage
573
setTimeout(function() {
574
_emscripten_run_script(script);
578
emscripten_set_main_loop: function(func, fps, simulateInfiniteLoop) {
579
Module['noExitRuntime'] = true;
581
Browser.mainLoop.runner = function() {
582
if (Browser.mainLoop.queue.length > 0) {
583
var start = Date.now();
584
var blocker = Browser.mainLoop.queue.shift();
585
blocker.func(blocker.arg);
586
if (Browser.mainLoop.remainingBlockers) {
587
var remaining = Browser.mainLoop.remainingBlockers;
588
var next = remaining%1 == 0 ? remaining-1 : Math.floor(remaining);
589
if (blocker.counted) {
590
Browser.mainLoop.remainingBlockers = next;
592
// not counted, but move the progress along a tiny bit
593
next = next + 0.5; // do not steal all the next one's progress
594
Browser.mainLoop.remainingBlockers = (8*remaining + next)/9;
597
console.log('main loop blocker "' + blocker.name + '" took ' + (Date.now() - start) + ' ms'); //, left: ' + Browser.mainLoop.remainingBlockers);
598
Browser.mainLoop.updateStatus();
599
setTimeout(Browser.mainLoop.runner, 0);
602
if (Browser.mainLoop.shouldPause) {
603
// catch pauses from non-main loop sources
604
Browser.mainLoop.paused = true;
605
Browser.mainLoop.shouldPause = false;
609
if (Module['preMainLoop']) {
610
Module['preMainLoop']();
613
Runtime.dynCall('v', func);
615
if (Module['postMainLoop']) {
616
Module['postMainLoop']();
619
if (Browser.mainLoop.shouldPause) {
620
// catch pauses from the main loop itself
621
Browser.mainLoop.paused = true;
622
Browser.mainLoop.shouldPause = false;
625
Browser.mainLoop.scheduler();
627
if (fps && fps > 0) {
628
Browser.mainLoop.scheduler = function() {
629
setTimeout(Browser.mainLoop.runner, 1000/fps); // doing this each time means that on exception, we stop
632
Browser.mainLoop.scheduler = function() {
633
Browser.requestAnimationFrame(Browser.mainLoop.runner);
636
Browser.mainLoop.scheduler();
638
if (simulateInfiniteLoop) {
639
throw 'SimulateInfiniteLoop';
643
emscripten_cancel_main_loop: function() {
644
Browser.mainLoop.scheduler = null;
645
Browser.mainLoop.shouldPause = true;
648
emscripten_pause_main_loop: function() {
649
Browser.mainLoop.pause();
652
emscripten_resume_main_loop: function() {
653
Browser.mainLoop.resume();
656
_emscripten_push_main_loop_blocker: function(func, arg, name) {
657
Browser.mainLoop.queue.push({ func: function() {
658
Runtime.dynCall('vi', func, [arg]);
659
}, name: Pointer_stringify(name), counted: true });
660
Browser.mainLoop.updateStatus();
663
_emscripten_push_uncounted_main_loop_blocker: function(func, arg, name) {
664
Browser.mainLoop.queue.push({ func: function() {
665
Runtime.dynCall('vi', func, [arg]);
666
}, name: Pointer_stringify(name), counted: false });
667
Browser.mainLoop.updateStatus();
670
emscripten_set_main_loop_expected_blockers: function(num) {
671
Browser.mainLoop.expectedBlockers = num;
672
Browser.mainLoop.remainingBlockers = num;
673
Browser.mainLoop.updateStatus();
676
emscripten_async_call: function(func, arg, millis) {
677
Module['noExitRuntime'] = true;
680
Runtime.getFuncWrapper(func, 'vi')(arg);
684
setTimeout(wrapper, millis);
686
Browser.requestAnimationFrame(wrapper);
690
emscripten_hide_mouse: function() {
691
var styleSheet = document.styleSheets[0];
692
var rules = styleSheet.cssRules;
693
for (var i = 0; i < rules.length; i++) {
694
if (rules[i].cssText.substr(0, 5) == 'canvas') {
695
styleSheet.deleteRule(i);
699
styleSheet.insertRule('canvas.emscripten { border: 1px solid black; cursor: none; }', 0);
702
emscripten_set_canvas_size: function(width, height) {
703
Browser.setCanvasSize(width, height);
706
emscripten_get_now: function() {
707
if (ENVIRONMENT_IS_NODE) {
708
var t = process['hrtime']();
709
return t[0] * 1e3 + t[1] / 1e6;
711
else if (window['performance'] && window['performance']['now']) {
712
return window['performance']['now']();
718
emscripten_create_worker: function(url) {
719
url = Pointer_stringify(url);
720
var id = Browser.workers.length;
722
worker: new Worker(url),
728
info.worker.onmessage = function(msg) {
729
var info = Browser.workers[id];
730
if (!info) return; // worker was destroyed meanwhile
731
var callbackId = msg.data['callbackId'];
732
var callbackInfo = info.callbacks[callbackId];
733
if (!callbackInfo) return; // no callback or callback removed meanwhile
735
info.callbacks[callbackId] = null; // TODO: reuse callbackIds, compress this
736
var data = msg.data['data'];
738
if (!data.byteLength) data = new Uint8Array(data);
739
if (!info.buffer || info.bufferSize < data.length) {
740
if (info.buffer) _free(info.buffer);
741
info.bufferSize = data.length;
742
info.buffer = _malloc(data.length);
744
HEAPU8.set(data, info.buffer);
745
callbackInfo.func(info.buffer, data.length, callbackInfo.arg);
747
callbackInfo.func(0, 0, callbackInfo.arg);
750
Browser.workers.push(info);
754
emscripten_destroy_worker: function(id) {
755
var info = Browser.workers[id];
756
info.worker.terminate();
757
if (info.buffer) _free(info.buffer);
758
Browser.workers[id] = null;
761
emscripten_call_worker: function(id, funcName, data, size, callback, arg) {
762
funcName = Pointer_stringify(funcName);
763
var info = Browser.workers[id];
766
callbackId = info.callbacks.length;
767
info.callbacks.push({
768
func: Runtime.getFuncWrapper(callback, 'viii'),
773
info.worker.postMessage({
774
'funcName': funcName,
775
'callbackId': callbackId,
776
'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 // XXX copy to a new typed array as a workaround for chrome bug 169705
780
emscripten_worker_respond: function(data, size) {
781
if (!inWorkerCall) throw 'not in worker call!';
782
if (workerResponded) throw 'already responded!';
783
workerResponded = true;
785
'callbackId': workerCallbackId,
786
'data': data ? new Uint8Array({{{ makeHEAPView('U8', 'data', 'data + size') }}}) : 0 // XXX copy to a new typed array as a workaround for chrome bug 169705
790
emscripten_get_worker_queue_size: function(id) {
791
var info = Browser.workers[id];
792
if (!info) return -1;
797
/* Useful stuff for browser debugging
799
function slowLog(label, text) {
800
if (!slowLog.labels) slowLog.labels = {};
801
if (!slowLog.labels[label]) slowLog.labels[label] = 0;
802
var now = Date.now();
803
if (now - slowLog.labels[label] > 1000) {
804
Module.print(label + ': ' + text);
805
slowLog.labels[label] = now;