diff --git a/dist/angular-workers.js b/dist/angular-workers.js index 492b271..31e64d5 100644 --- a/dist/angular-workers.js +++ b/dist/angular-workers.js @@ -3,6 +3,7 @@ angular.module('FredrikSandell.worker-pool', []).service('WorkerService', [ function ($q) { var that = {}; var urlToAngular; + var urlToLibraries = []; var serviceToUrlMap = {}; /*jshint laxcomma:true */ /*jshint quotmark: false */ @@ -48,6 +49,9 @@ angular.module('FredrikSandell.worker-pool', []).service('WorkerService', [ urlToAngular = urlToAngularJs; return that; }; + that.addLibrary = function (url) { + urlToLibraries.push(url); + }; that.addDependency = function (serviceName, moduleName, url) { serviceToUrlMap[serviceName] = { url: url, @@ -80,6 +84,9 @@ angular.module('FredrikSandell.worker-pool', []).service('WorkerService', [ }; function createIncludeStatements(listOfServiceNames) { var includeString = ''; + angular.forEach(urlToLibraries, function (libraryUrl) { + includeString += 'importScripts(\'' + libraryUrl + '\');\n'; + }); angular.forEach(listOfServiceNames, function (serviceName) { if (serviceToUrlMap[serviceName]) { includeString += 'importScripts(\'' + serviceToUrlMap[serviceName].url + '\');'; diff --git a/dist/angular-workers.min.js b/dist/angular-workers.min.js index b0a681f..277eb99 100644 --- a/dist/angular-workers.min.js +++ b/dist/angular-workers.min.js @@ -1 +1 @@ -angular.module("FredrikSandell.worker-pool",[]).service("WorkerService",["$q",function(a){function b(a){var b="";return angular.forEach(a,function(a){j[a]&&(b+="importScripts('"+j[a].url+"');")}),b}function c(a){var b=[];return angular.forEach(a,function(a){j[a]&&b.push("'"+j[a].moduleName+"'")}),b.join(",")}function d(a){var d=a.filter(function(a){return"input"!==a&&"output"!==a&&"$q"!==a}),e={dependencies:d,moduleList:c(d),angularDepsAsStrings:d.length>0?","+d.map(function(a){return"'"+a+"'"}).join(","):"",angularDepsAsParamList:d.length>0?","+d.join(","):"",servicesIncludeStatements:b(d)};return e.workerFuncParamList="input,output"+e.angularDepsAsParamList,e}function e(a,b){return k.replace("",h).replace("",b.servicesIncludeStatements).replace("",b.moduleList).replace("",b.angularDepsAsStrings).replace("",b.angularDepsAsParamList).replace("",a.toString())}function f(a){return a.slice(0,a.length-1)}function g(a,b){return"("+a.toString()+")("+b+")"}var h,i={},j={},k=["","var window = self;","self.history = {};","self.Node = function(){};","var document = {"," readyState: 'complete',"," cookie: '',"," querySelector: function () {},"," createElement: function () {"," return {"," pathname: '',"," setAttribute: function () {}"," };"," }","};","importScripts('');","","angular = window.angular;","var workerApp = angular.module('WorkerApp', []);","workerApp.run(['$q', function ($q) {"," self.addEventListener('message', function(e) {"," var input = e.data;"," var output = $q.defer();"," var promise = output.promise;"," promise.then(function(success) {"," self.postMessage({event:'success', data : success});"," }, function(reason) {"," self.postMessage({event:'failure', data : reason});"," }, function(update) {"," self.postMessage({event:'update', data : update});"," });"," ;"," });"," self.postMessage({event:'initDone'});","}]);","angular.bootstrap(null, ['WorkerApp']);"].join("\n");i.setAngularUrl=function(a){return h=a,i},i.addDependency=function(a,b,c){return j[a]={url:c,moduleName:b},i},i.createAngularWorker=function(b){if(!Array.isArray(b)||b.length<3||"function"!=typeof b[b.length-1])throw"Input needs to be: ['input','output'/*optional additional dependencies*/,\n function(workerInput, deferredOutput /*optional additional dependencies*/)\n {/*worker body*/}]";if("string"!=typeof h)throw"The url to angular must be defined before worker creation";var c=a.defer(),i=d(f(b)),j=(window.URL?URL:webkitURL).createObjectURL(new Blob([e(g(b[b.length-1],i.workerFuncParamList),i)],{type:"application/javascript"})),k=new Worker(j);return k.addEventListener("message",function(a){var b=a.data.event;"initDone"===b?c.resolve(l(k)):c.reject(a)}),c.promise};var l=function(b){var c={};return c.worker=b,c.run=function(c){var d=a.defer();return b.addEventListener("message",function(a){var b=a.data.event;if("initDone"===b)throw"Received worker initialization in run method. This should already have occurred!";"success"===b?d.resolve(a.data.data):"failure"===b?d.reject(a.data.data):"update"===b?d.notify(a.data.data):d.reject(a)}),b.postMessage(c),d.promise},c.terminate=function(){b.terminate()},c};return i}]); \ No newline at end of file +angular.module("FredrikSandell.worker-pool",[]).service("WorkerService",["$q",function(a){function b(a){var b="";return angular.forEach(j,function(a){b+="importScripts('"+a+"');\n"}),angular.forEach(a,function(a){k[a]&&(b+="importScripts('"+k[a].url+"');")}),b}function c(a){var b=[];return angular.forEach(a,function(a){k[a]&&b.push("'"+k[a].moduleName+"'")}),b.join(",")}function d(a){var d=a.filter(function(a){return"input"!==a&&"output"!==a&&"$q"!==a}),e={dependencies:d,moduleList:c(d),angularDepsAsStrings:d.length>0?","+d.map(function(a){return"'"+a+"'"}).join(","):"",angularDepsAsParamList:d.length>0?","+d.join(","):"",servicesIncludeStatements:b(d)};return e.workerFuncParamList="input,output"+e.angularDepsAsParamList,e}function e(a,b){return l.replace("",h).replace("",b.servicesIncludeStatements).replace("",b.moduleList).replace("",b.angularDepsAsStrings).replace("",b.angularDepsAsParamList).replace("",a.toString())}function f(a){return a.slice(0,a.length-1)}function g(a,b){return"("+a.toString()+")("+b+")"}var h,i={},j=[],k={},l=["","var window = self;","self.history = {};","self.Node = function () {};","var document = {"," readyState: 'complete',"," cookie: '',"," querySelector: function () {},"," createElement: function () {"," return {"," pathname: '',"," setAttribute: function () {}"," };"," }","};","importScripts('');","","angular = window.angular;","var workerApp = angular.module('WorkerApp', []);","workerApp.run(['$q', function ($q) {"," self.addEventListener('message', function(e) {"," var input = e.data;"," var output = $q.defer();"," var promise = output.promise;"," promise.then(function(success) {"," self.postMessage({event:'success', data : success});"," }, function(reason) {"," self.postMessage({event:'failure', data : reason});"," }, function(update) {"," self.postMessage({event:'update', data : update});"," });"," ;"," });"," self.postMessage({event:'initDone'});","}]);","angular.bootstrap(null, ['WorkerApp']);"].join("\n");i.setAngularUrl=function(a){return h=a,i},i.addLibrary=function(a){j.push(a)},i.addDependency=function(a,b,c){return k[a]={url:c,moduleName:b},i},i.createAngularWorker=function(b){if(!Array.isArray(b)||b.length<3||"function"!=typeof b[b.length-1])throw"Input needs to be: ['input','output'/*optional additional dependencies*/,\n function(workerInput, deferredOutput /*optional additional dependencies*/)\n {/*worker body*/}]";if("string"!=typeof h)throw"The url to angular must be defined before worker creation";var c=a.defer(),i=d(f(b)),j=(window.URL?URL:webkitURL).createObjectURL(new Blob([e(g(b[b.length-1],i.workerFuncParamList),i)],{type:"application/javascript"})),k=new Worker(j);return k.addEventListener("message",function(a){var b=a.data.event;"initDone"===b?c.resolve(m(k)):c.reject(a)}),c.promise};var m=function(b){var c={};return c.worker=b,c.run=function(c){var d=a.defer();return b.addEventListener("message",function(a){var b=a.data.event;if("initDone"===b)throw"Received worker initialization in run method. This should already have occurred!";"success"===b?d.resolve(a.data.data):"failure"===b?d.reject(a.data.data):"update"===b?d.notify(a.data.data):d.reject(a)}),b.postMessage(c),d.promise},c.terminate=function(){b.terminate()},c};return i}]); \ No newline at end of file diff --git a/src/angular-workers.js b/src/angular-workers.js index e0c6820..771871b 100644 --- a/src/angular-workers.js +++ b/src/angular-workers.js @@ -2,6 +2,7 @@ angular.module('FredrikSandell.worker-pool', []) .service('WorkerService', ['$q', function ($q) { var that = {}; var urlToAngular; + var urlToLibraries = []; var serviceToUrlMap = {}; /*jshint laxcomma:true */ /*jshint quotmark: false */ @@ -47,11 +48,15 @@ angular.module('FredrikSandell.worker-pool', []) , "angular.bootstrap(null, ['WorkerApp']);"].join("\n"); - that.setAngularUrl = function(urlToAngularJs) { + that.setAngularUrl = function(urlToAngularJs) { urlToAngular = urlToAngularJs; return that; }; + that.addLibrary = function(url) { + urlToLibraries.push(url); + }; + that.addDependency = function (serviceName, moduleName, url) { serviceToUrlMap[serviceName] = { url : url, @@ -104,6 +109,9 @@ angular.module('FredrikSandell.worker-pool', []) function createIncludeStatements(listOfServiceNames) { var includeString = ''; + angular.forEach(urlToLibraries, function(libraryUrl){ + includeString += 'importScripts(\'' + libraryUrl + '\');\n'; + }); angular.forEach(listOfServiceNames, function (serviceName) { if (serviceToUrlMap[serviceName]) { includeString += 'importScripts(\''+serviceToUrlMap[serviceName].url+'\');'; diff --git a/src/test/ng-underscore.js b/src/test/ng-underscore.js new file mode 100644 index 0000000..fcfa072 --- /dev/null +++ b/src/test/ng-underscore.js @@ -0,0 +1,3 @@ +angular.module('underscore', []).factory('_', ['$window', function($window) { + return $window._; // assumes underscore has already been loaded on the page +}]); diff --git a/test/unit/angular-workers-add-librarySpec.js b/test/unit/angular-workers-add-librarySpec.js new file mode 100644 index 0000000..73262bd --- /dev/null +++ b/test/unit/angular-workers-add-librarySpec.js @@ -0,0 +1,106 @@ +describe('FredrikSandell.worker-pool', function () { + + var WorkerService = null; + var rootScope = null; + + beforeEach(function() { + module('FredrikSandell.worker-pool'); + inject(function (_WorkerService_, $rootScope) { + WorkerService = _WorkerService_; + rootScope = $rootScope; + }); + }); + beforeEach(function() { + // set the URL to obtain angular + WorkerService.setAngularUrl('https://code.angularjs.org/1.5.7/angular.js'); + // set the URL of other mandatory libraries + WorkerService.addLibrary('https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js'); + }); + + it('should be an object', function () { + expect(typeof WorkerService).toBe('object'); + }); + + it('should have a method addLibrary()', function () { + expect(WorkerService.addLibrary).toBeDefined(); + expect(typeof WorkerService.addLibrary).toBe('function'); + }); + + it('should have a method createAngularWorker()', function () { + expect(WorkerService.createAngularWorker).toBeDefined(); + expect(typeof WorkerService.createAngularWorker).toBe('function'); + }); + + it('should have a method setAngularUrl()', function () { + expect(WorkerService.setAngularUrl).toBeDefined(); + expect(typeof WorkerService.setAngularUrl).toBe('function'); + }); + + function waitUntilCompletedToTriggerPromiseResolve(completed, rootScope) { + //must wait before triggering digest loop which resolves of the promises + //worker must be given time to initialize + var checker = setInterval(function(){ + if(completed) { + clearInterval(checker); + } else { + rootScope.$apply(); + } + }, 100); + } + + it('createAngularWorker() should return a valid AngularWorker object', function (done) { + var completed = false; + + WorkerService.addDependency( + '_', + 'underscore', + 'http://localhost:9876/base/src/test/ng-underscore.js' + ); + var worker = WorkerService.createAngularWorker(['input', 'output', '_', function (input, output) { + //the worker method does not matter + //should always return a promise resolving to an initialized worker + }]); + + worker.then(function(worker) { + expect(typeof worker).toBe('object'); + expect(typeof worker.run).toBe('function'); + expect(typeof worker.terminate).toBe('function'); + }, function(data) { + expect(true).toBe(false); //initialization should be ok + })['finally'](function() { + done(); + completed = true; + }); + waitUntilCompletedToTriggerPromiseResolve(completed, rootScope); + }); + + it('should be possible inject custom dependencies in worker', function (done) { + var completed = false; + + WorkerService.addDependency( + '_', + 'underscore', + 'http://localhost:9876/base/src/test/ng-underscore.js' + ); + var workerPromise = WorkerService.createAngularWorker(['input', 'output', '_', function (input, output, PiCalculatorService) { + output.resolve(_.size(input)); + + }]); + + workerPromise.then(function(worker) { + return worker.run([1,2,3]); //execute the run method on the fully initialized worker + }, function error(reason) { + expect(false).toBe(true); + + }).then(function success(data) { + expect(data).toEqual(3); //worker should not succeed in this test + })['finally'](function() { + done(); + completed = true; + }); + + waitUntilCompletedToTriggerPromiseResolve(completed, rootScope); + }); + + +});