- /*
- Plexer.js v.1.1
- Bootstraps a function to a web worker, if available, else creates an instance
- By Norbert Landsteiner 2013, mass:werk - media environments, www.masswerk.at
- License: MIT-License (Please keep this header anyway)
- Project page: http://www.masswerk.at/plexer
- */
- function Plexer( funcRef, forceInstance ) {
- /*
- new Plexer( <func-ref> [, <force-instance> [, <rest>* ]] )
- func-ref: Function - function to bootstrap
- force-instance: Boolean - do not use worker, force instance (for testing), optional
- rest: Function(s) - any external functions to be included to the worker
- */
- var instance, worker, urlConstructor, blobUrl, terminated,
- usesWorker=false, hasTransferables=false, runmode,
- workerCallbacks={}, externalRefs=[];
- function bootstrap() {
- var blob, t, srcString, i, l;
- terminated=false;
- if (!forceInstance) {
- try {
- urlConstructor = self.URL || self.webkitURL;
- if ((self.Blob || self.BlobBuilder) && self.Worker && urlConstructor) {
- srcString=funcRef.toString();
- srcString='('+srcString.replace(/\{/, '{\n'+_workerMessageReceiver.toString()+'\nself.addEventListener("message", _workerMessageReceiver, false);\n')+')()';
- for (i=0, l=externalRefs.length; i<l; i++) srcString+='\n'+externalRefs[i].toString();
- if (self.Blob) {
- blob=new Blob([srcString], {type: 'text/javascript'});
- }
- else {
- blob=new BlobBuilder().append(srcString).getBlob('text/javascript');
- }
- blobUrl=urlConstructor.createObjectURL(blob);
- worker=new Worker(blobUrl);
- if (!worker.postMessage) worker.postMessage=worker.webkitPostMessage;
- try {
- t=new ArrayBuffer(1);
- worker.postMessage(t, [t]);
- if (!t.byteLength) hasTransferables=true;
- }
- catch(e2) {}
- worker.addEventListener('message', _workerMessageHandler, false);
- worker.addEventListener('error', _workerErrorHandler, false);
- runmode='worker';
- }
- }
- catch(e) {
- if (urlConstructor && blobUrl) {
- try {
- urlConstructor.revokeObjectURL(blobUrl);
- }
- catch(e) {}
- }
- worker=urlConstructor=blobUrl=null;
- }
- }
- if (!worker) {
- instance=new funcRef();
- runmode='instance';
- }
- if (self.addEventListener) {
- self.addEventListener('unload', terminateReference, false);
- }
- else if (self.attachEvent) {
- self.attachEvent('onunload', terminateReference);
- }
- }
- // will be injected into the worker
- function _workerMessageReceiver(msg) {
- var args, d, i, l, n, method, f, r, map, data;
- if (typeof msg.data == 'object' && typeof msg.data.plexer_job == 'string') {
- d=msg.data;
- if (d.length) {
- args=[];
- for (i=0, l=d.length; i<l; i++) args.push(d['a'+i]);
- }
- else {
- args=d.args;
- }
- method=d.plexer_job;
- f=self[method];
- if (typeof f == 'function') {
- r=f.apply(f, args);
- if (d.ticket) {
- if (!self.postMessage) self.postMessage=self.webkitPostMessage;
- data = {'plexer_receipt': method, 'ticket': d.ticket};
- if (d.preserve) data.preserve=true;
- n=Number(d.transferables);
- if (!isNaN(n) && n && r!==undefined) {
- if (Object.prototype.toString.call(r)!='[object Array]') r=[r];
- map=[];
- for (i=0, l=r.length; i<l; i++) {
- data['r'+i]=r[i];
- if (n>0) {
- map.push(r[i]);
- n--;
- }
- }
- data.length=l;
- self.postMessage(data, map);
- }
- else {
- data.result=r;
- self.postMessage(data);
- }
- }
- }
- else {
- throw new Error('Method "'+method+'" not found or not a function.');
- }
- }
- }
- function _workerMessageHandler(msg) {
- var result, d, i, l, method, f;
- if (typeof msg.data == 'object' && typeof msg.data.plexer_receipt == 'string' && typeof msg.data.ticket == 'string') {
- d=msg.data;
- if (d.length) {
- result=[];
- for (i=0, l=d.length; i<l; i++) result.push(d['r'+i]);
- }
- else {
- result=d.result;
- }
- f=workerCallbacks[d.ticket];
- delete workerCallbacks[d.ticket]
- if (typeof f == 'function') {
- if (d.length || (!d.preserve && Object.prototype.toString.call(result)=='[object Array]')) {
- f.apply(f, result);
- }
- else {
- f(result);
- }
- }
- }
- }
- function _workerErrorHandler(e) {
- if (e && self.console) {
- console.log('Plexer-Worker: '+e.message);
- }
- }
- // public functions / interface
- /*
- public/private
- takes no argument as method .@terminate(): void,
- takes an event as agrument when called internally as a handler (onunload)
- returns: void
- */
- function terminateReference(event) {
- if (worker) {
- try {
- worker.removeEventListener('message', _workerMessageHandler);
- worker.removeEventListener('error', _workerErrorHandler);
- worker.terminate();
- }
- catch(e) {}
- }
- if (urlConstructor && blobUrl) {
- try {
- urlConstructor.revokeObjectURL(blobUrl);
- }
- catch(e) {}
- }
- worker=urlConstructor=blobUrl=instance=null;
- workerCallbacks={};
- if (self.addEventListener) {
- self.removeEventListener('unload', terminateReference);
- }
- else if (self.attachEvent) {
- self.detachEvent('onunload', terminateReference);
- }
- if (event || window.event) {
- if (!event) event=window.event;
- if (event.type=='unload') {
- funcRef=null;
- externalRefs.length=0;
- }
- }
- terminated=true;
- }
- /*
- .@run( <method-name> [, <arguments-array> [, <callback-function> [, <transferables-up> [, <transferables-down> [, <preserve-arrays>]]]]] ): void
- method-name: String - name of the method to be called (required)
- arguments-array: Array of arguments to be passed to the method or empty (null | undefined), default: []
- callback-function: Function to receive any results, default: null
- transferables-up: Number of first n arguments to be sent to the worker as transferable objects, default: 0
- transferables-down: Number of first n arguments to be sent back by the worker as transferable objects, default: 0
- preserve-arrays: Boolean - false: apply an array-result as the callback's arguments-array (default)
- - true: if the result is an array, return the array-reference
- returns: void
- */
- function run(method, args, callback, transferablesUp, transferablesDown, preserveArrays) {
- var data, map, ticket, i, l, f, r;
- if (Object.prototype.toString.call(method)!='[object String]') {
- throw new Error('Plexer: 1st argument (method-name) must be a string.');
- }
- if (args === null || args === undefined) {
- args = [];
- }
- else if (Object.prototype.toString.call(args)!='[object Array]') {
- throw new Error('Plexer: 2nd argument (call-arguments) must be an array.');
- }
- if (callback && typeof callback != 'function') {
- throw new Error('Plexer: Callback is not a function.');
- }
- if (terminated) bootstrap();
- if (worker) {
- if (!hasTransferables) {
- transferablesUp=transferablesDown=0;
- }
- else {
- if (typeof transferablesUp != 'string') Number(transferablesUp);
- if (typeof transferablesDown != 'string') Number(transferablesDown);
- if (!isNaN(transferablesUp) && transferablesUp>0) {
- transferablesUp=Math.floor(transferablesUp);
- }
- else {
- transferablesUp=0;
- }
- if (!isNaN(transferablesDown) && transferablesDown>0) {
- transferablesDown=Math.floor(transferablesDown);
- }
- else {
- transferablesDown=0;
- }
- }
- data = {'plexer_job': method};
- if (callback) {
- ticket = Number(Math.floor(Math.random()*Math.pow(2,32))).toString(16)+'_'+Number(new Date().getTime()).toString(16);
- data.ticket=ticket;
- workerCallbacks[ticket]=callback;
- }
- if (preserveArrays) data.preserve=true;
- if (transferablesDown) data.transferables=transferablesDown;
- if (transferablesUp && args.length) {
- map=[];
- for (i=0, l=args.length; i<l; i++) {
- data['a'+i]=args[i];
- if (transferablesUp) {
- map.push(args[i]);
- transferablesUp--;
- }
- }
- data.length=l;
- worker.postMessage(data, map);
- }
- else {
- data.args=args;
- worker.postMessage(data);
- }
- }
- else {
- f=instance[method];
- if (typeof f == 'function') {
- r=f.apply(f, args);
- if (callback) {
- if (preserveArrays || Object.prototype.toString.call(r)!='[object Array]') {
- setTimeout( function() { callback(r); }, 1);
- }
- else {
- setTimeout( function() { callback.apply(callback, r); }, 1);
- }
- }
- }
- else {
- throw new Error('Plexer (as in instance): Method "'+method+'" not found or not a function.');
- }
- }
- }
- /*
- .@isTerminated(): Boolean
- returns: Boolean (true: yes, is terminated)
- */
- function isTerminated() {
- return terminated;
- }
- /*
- .@getRunmode(): String
- returns: String ('worker' | 'instance')
- */
- function getRunmode() {
- return runmode;
- }
- /*
- constructor tasks
- */
- if (typeof funcRef == 'function') {
- for (var i=2; i<arguments.length; i++) {
- if (typeof arguments[i] == 'function') externalRefs.push(arguments[i]);
- }
- bootstrap();
- }
- else {
- throw new Error('Plexer: First argument must be a function reference.');
- }
- return new function() {
- this.run=run;
- this.terminate=terminateReference;
- this.isTerminated=isTerminated;
- this.getRunmode=getRunmode;
- }
- }
Back to Plexer.js | Download plexer.zip (v.1.1, full-version, minified, and docs).