Last active
October 22, 2020 23:54
-
-
Save WolfgangDrescher/63d24cd755e648afa998270dd3d7c283 to your computer and use it in GitHub Desktop.
Verovio JavaScript Worker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="score"></div> | |
<script src="/verovio-toolkit-proxy.js"></script> | |
<script> | |
// Create a new instance of VerovioToolkitProxy | |
const proxy = new VerovioToolkitProxy(); | |
// Listen and wait for Module to emit onRuntimeInitialized | |
proxy.onRuntimeInitialized().then(async () => { | |
// Fetch a MEI file | |
const response = await fetch('file.mei'); | |
const data = await response.text(); | |
// Set inner HTML of #score to verovio response | |
// Note that method calls on VerovioToolkitProxy instances are asynchronous | |
// and will return Promises. So the must be awaited with the `await` | |
// keyword or `.then(() => {...})` | |
document.getElementById('score').innerHTML = await proxy.loadData(data); | |
}); | |
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Use factory pattern to export VerovioToolkitProxy | |
(function (root, factory) { | |
if (typeof module === "object" && typeof module.exports === "object") { | |
module.exports = factory(); | |
} else { | |
root.VerovioToolkitProxy = factory(); | |
} | |
})(this, function () { | |
// Wrapper class to allow to resolve a Promise outside of its scope | |
class Deferred { | |
constructor() { | |
this.promise = new Promise((resolve, reject) => { | |
this.reject = reject | |
this.resolve = resolve | |
}); | |
} | |
} | |
// HashMap to store all method calls on the verovio toolkit worker | |
const workerTasks = {}; | |
// Helper function to create a UUID | |
const uuidv4 = function () { | |
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | |
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); | |
return v.toString(16); | |
}); | |
}; | |
// Helper function to identify method calls in worker onMessage listener | |
const createTaksId = function () { | |
let id; | |
do { | |
id = uuidv4(); | |
} while (workerTasks[id]); | |
return id; | |
}; | |
class VerovioToolkitProxy { | |
constructor() { | |
this.worker = new Worker('verovio-worker.js'); | |
// Listen to response of the service worker | |
this.worker.addEventListener('message', function (event) { | |
const { id, result } = event.data | |
// Check if there is a Deferred instance in workerTasks HashMap with | |
// passed id of the Worker | |
const deferred = workerTasks[id]; | |
if(deferred) { | |
// If so resolve deferred promise and pass verovio toolkit return value | |
deferred.resolve(result); | |
} | |
}, false); | |
// Retrun a Proxy so it is possible to catch all property and method calls | |
// of a VerovioToolkitProxy instance | |
return new Proxy(this, { | |
get: (target, method) => { | |
return function () { | |
const args = Array.prototype.slice.call(arguments); | |
const id = createTaksId(); | |
// Post a message to service worker with UUID, method name of the | |
// verovio toolkit function and passed arguments | |
target.worker.postMessage({ | |
id, | |
method, | |
arguments: args, | |
}); | |
// Create a new Deferred instance and store it in workerTasks HashMap | |
const deferred = new Deferred(); | |
workerTasks[id] = deferred; | |
// Return the (currently still unresolved) Promise of the Deferred instance | |
return deferred.promise; | |
}; | |
}, | |
}); | |
} | |
} | |
// Pass VerovioToolkitProxy to factory | |
return VerovioToolkitProxy; | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
importScripts("verovio-toolkit-2.5.0.js"); | |
// Check if we are in a web worker | |
if (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope) { | |
// Initializing an empty object to prevent if in onMessage listener the toolkit | |
// is beeing accessed before Module.onRuntimeInitialized | |
let verovioToolkit = {}; | |
// Wrapper class to allow to resolve a Promise outside of its scope | |
class Deferred { | |
constructor() { | |
this.promise = new Promise((resolve, reject) => { | |
this.reject = reject | |
this.resolve = resolve | |
}); | |
} | |
} | |
// Global deferred Promise that can be resolved when Module is initialized | |
const moduleIsReady = new Deferred(); | |
// Create a new toolkit instance when Module is ready | |
Module.onRuntimeInitialized = function () { | |
verovioToolkit = new verovio.toolkit(); | |
moduleIsReady.resolve(); | |
}; | |
// Listen to messages send to this worker | |
addEventListener('message', function (event) { | |
// Destruct properties passed to this message event | |
const { id, method, arguments } = event.data; | |
// postMessage on a `onRuntimeInitialized` method as soon as | |
// Module is initialized | |
if(method === 'onRuntimeInitialized') { | |
moduleIsReady.promise.then(() => { | |
postMessage({ | |
id, | |
method, | |
arguments, | |
result: null, | |
}, event); | |
}); | |
return; | |
} | |
// Check if verovio toolkit has passed method | |
const fn = verovioToolkit[method || null]; | |
let result; | |
if(fn) { | |
// Call verovio toolkit method and pass arguments | |
result = fn.apply(null, arguments || []); | |
} | |
// Always respond to worker calls with postMessage | |
postMessage({ | |
id, | |
method, | |
arguments, | |
result, | |
}, event); | |
}, false); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment