diff --git a/doc/platforms-html5.dox b/doc/platforms-html5.dox
index 8eaceb3a9..920dff145 100644
--- a/doc/platforms-html5.dox
+++ b/doc/platforms-html5.dox
@@ -302,32 +302,41 @@ endif()
underlying emulation libraries and may vary across Emscripten versions.
Running multiple graphical applications on a single page is possible, but
-requires a few changes. By default, Emscripten and `EmscriptenApplication.js`
-populate a global @cb{.js} Module @ce object with things like callbacks and the
-canvas element to render into. This is not suitable when using more than one
-application. Emscripten's `-s MODULARIZE` option creates a script
-that defines a function with a local @cb{.js} Module @ce object instead.
-This way each application runs inside its own function with a separate
-environment.
-
-To let `EmscriptenApplication.js` access the local @cb{.js} Module @ce,
-it must be prepended to the application with the `--pre-js` compiler option,
-at which point you also don't have to bundle it separately anymore:
+requires a few changes. By default, Emscripten populates a global @cb{.js} Module @ce
+object with things like callbacks and the canvas element to render into. This is not
+suitable when using more than one application. Emscripten's `MODULARIZE` option
+creates a script that defines a function with a local @cb{.js} Module @ce object
+instead. This way each application runs inside its own function with a separate
+environment:
@code{.cmake}
target_link_libraries(my-application PRIVATE "-s MODULARIZE -s EXPORT_NAME=createModule")
-target_link_libraries(my-application PRIVATE "--pre-js ${MAGNUM_EMSCRIPTENAPPLICATION_JS}")
@endcode
-Instead of running instantly, the application must be instantiated
-using the function name set with `EXPORT_NAME`:
+Instead of running instantly, the application can then be instantiated
+using the function name set with `EXPORT_NAME`. The function optionally
+takes an object to populate the local @cb{.js} Module @ce.
+
+While `EmscriptenApplication.js` populates the @cb{.js} Module @ce object by default,
+it also defines a function @cb{.js} createMagnumModule @ce that lets you create
+new module objects to pass to the Emscripten module function. This allows you
+to include `EmscriptenApplication.js` only once per page without having to bundle it with
+Emscripten's `--pre-js` option. To override the default values, pass an object to
+@cb{.js} createMagnumModule @ce. This is the easiest way to set the canvas or
+status elements of each application:
@code{.html-jinja}
+
@endcode
diff --git a/src/Magnum/Platform/EmscriptenApplication.js b/src/Magnum/Platform/EmscriptenApplication.js
index ebd627568..48ce2f79d 100644
--- a/src/Magnum/Platform/EmscriptenApplication.js
+++ b/src/Magnum/Platform/EmscriptenApplication.js
@@ -25,70 +25,93 @@
"use strict"; /* it summons the Cthulhu in a proper way, they say */
-var Module = typeof Module !== "undefined" ? Module : {};
-
-Module.preRun = [];
-Module.postRun = [];
-
-Module.arguments = [];
-
-Module.printErr = function(_message) {
- console.error(Array.prototype.slice.call(arguments).join(' '));
-};
-
-Module.print = function(_message) {
- console.log(Array.prototype.slice.call(arguments).join(' '));
-};
-
-Module.onAbort = function() {
- Module.canvas.style.opacity = 0.333;
- Module.canvas.style.zIndex = -1;
- Module.setStatus("Oops :(");
- Module.setStatusDescription("The app crashed. Refresh the page or check the browser console for details.");
-};
-
-Module.canvas = document.getElementById('canvas');
-Module.status = document.getElementById('status');
-Module.statusDescription = document.getElementById('status-description');
-
-Module.setStatus = function(message) {
- /* Emscripten calls setStatus("") after a timeout even if the app
- aborts. That would erase the crash message, so don't allow that */
- if(Module.status && Module.status.innerHTML != "Oops :(")
- Module.status.innerHTML = message;
-};
-
-Module.setStatusDescription = function(message) {
- if(Module.statusDescription)
- Module.statusDescription.innerHTML = message;
-};
-
-Module.totalDependencies = 0;
-
-Module.monitorRunDependencies = function(left) {
- this.totalDependencies = Math.max(this.totalDependencies, left);
-
- if(left) {
- Module.setStatus('Downloading...');
- Module.setStatusDescription((this.totalDependencies - left) + ' / ' + this.totalDependencies);
- } else {
- Module.setStatus('Download complete');
- Module.setStatusDescription('');
+function createMagnumModule(init) {
+ /* Take the Emscripten-supplied Module object as a base */
+ const module = Object.assign({}, Module);
+
+ /* Update it with our things */
+ Object.assign(module, {
+ preRun: [],
+ postRun: [],
+
+ arguments: [],
+
+ printErr: function(_message) {
+ console.error(Array.prototype.slice.call(arguments).join(' '));
+ },
+
+ print: function(_message) {
+ console.log(Array.prototype.slice.call(arguments).join(' '));
+ },
+
+ onAbort: function() {
+ module.canvas.style.opacity = 0.333;
+ module.canvas.style.zIndex = -1;
+ module.setStatus("Oops :(");
+ module.setStatusDescription("The app crashed. Refresh the page or check the browser console for details.");
+ },
+
+ canvas: document.getElementById('canvas'),
+ status: document.getElementById('status'),
+ statusDescription: document.getElementById('status-description'),
+
+ setStatus: function(message) {
+ /* Emscripten calls setStatus("") after a timeout even if the app
+ aborts. That would erase the crash message, so don't allow that */
+ if(module.status && module.status.innerHTML != "Oops :(")
+ module.status.innerHTML = message;
+ },
+
+ setStatusDescription: function(message) {
+ if(module.statusDescription)
+ module.statusDescription.innerHTML = message;
+ },
+
+ totalDependencies: 0,
+
+ monitorRunDependencies: function(left) {
+ this.totalDependencies = Math.max(this.totalDependencies, left);
+
+ if(left) {
+ module.setStatus('Downloading...');
+ module.setStatusDescription((this.totalDependencies - left) + ' / ' + this.totalDependencies);
+ } else {
+ module.setStatus('Download complete');
+ module.setStatusDescription('');
+ }
+ }
+ });
+
+ /* Parse arguments, e.g. /app/?foo=bar&fizz&buzz=3 goes to the app as
+ ['--foo', 'bar', '--fizz', '--buzz', '3'] */
+ const args = decodeURIComponent(window.location.search.substr(1)).trim().split('&');
+ for(let i = 0; i != args.length; ++i) {
+ let j = args[i].indexOf('=');
+ /* Key + value */
+ if(j != -1) {
+ module.arguments.push('--' + args[i].substring(0, j));
+ module.arguments.push(args[i].substring(j + 1));
+
+ /* Just key */
+ } else module.arguments.push('--' + args[i]);
}
-};
-
-/* Parse arguments, e.g. /app/?foo=bar&fizz&buzz=3 goes to the app as
- ['--foo', 'bar', '--fizz', '--buzz', '3'] */
-var args = decodeURIComponent(window.location.search.substr(1)).trim().split('&');
-for(var i = 0; i != args.length; ++i) {
- var j = args[i].indexOf('=');
- /* Key + value */
- if(j != -1) {
- Module.arguments.push('--' + args[i].substring(0, j));
- Module.arguments.push(args[i].substring(j + 1));
-
- /* Just key */
- } else Module.arguments.push('--' + args[i]);
+
+ /* Let the user-supplied object overwrite all the above */
+ Object.assign(module, init);
+
+ /* We can do this here because at this point `module.status` should be correct */
+ module.setStatus("Downloading...");
+
+ return module;
}
-Module.setStatus('Downloading...');
+/* Default global Module object */
+var Module = createMagnumModule();
+
+/* UMD export */
+if(typeof exports === 'object' && typeof module === 'object') /* CommonJS/Node */
+ module.exports = createMagnumModule;
+else if(typeof define === 'function' && define['amd']) /* AMD */
+ define([], function() { return createMagnumModule; });
+else if(typeof exports === 'object') /* CommonJS strict */
+ exports["createMagnumModule"] = createMagnumModule;
diff --git a/src/Magnum/Platform/Test/CMakeLists.txt b/src/Magnum/Platform/Test/CMakeLists.txt
index ce365eb00..74bc57f2f 100644
--- a/src/Magnum/Platform/Test/CMakeLists.txt
+++ b/src/Magnum/Platform/Test/CMakeLists.txt
@@ -60,11 +60,10 @@ if(WITH_EMSCRIPTENAPPLICATION)
target_compile_definitions(PlatformMultipleEmscriptenApplicationTest PRIVATE CUSTOM_CLEAR_COLOR=0.2,0,0.2,1)
target_link_libraries(PlatformMultipleEmscriptenApplicationTest PRIVATE "-s MODULARIZE -s EXPORT_NAME=createModule")
target_link_libraries(PlatformMultipleEmscriptenApplicationTest PRIVATE "-s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1")
- target_link_libraries(PlatformMultipleEmscriptenApplicationTest PRIVATE "--pre-js ${CMAKE_CURRENT_SOURCE_DIR}/../EmscriptenApplication.js")
- target_link_libraries(PlatformMultipleEmscriptenApplicationTest PRIVATE "--pre-js ${CMAKE_CURRENT_SOURCE_DIR}/MultipleEmscriptenApplicationTestPre.js")
add_custom_command(TARGET PlatformMultipleEmscriptenApplicationTest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/../WebApplication.css
+ ${CMAKE_CURRENT_SOURCE_DIR}/../EmscriptenApplication.js
$
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/MultipleEmscriptenApplicationTest.html
diff --git a/src/Magnum/Platform/Test/MultipleEmscriptenApplicationTest.html b/src/Magnum/Platform/Test/MultipleEmscriptenApplicationTest.html
index d226ab9ae..4edb0b263 100644
--- a/src/Magnum/Platform/Test/MultipleEmscriptenApplicationTest.html
+++ b/src/Magnum/Platform/Test/MultipleEmscriptenApplicationTest.html
@@ -5,6 +5,7 @@
Magnum Multiple EmscriptenApplication Test
+