diff --git a/.jshintrc b/.jshintrc index e9fd964f..a2c5aef5 100644 --- a/.jshintrc +++ b/.jshintrc @@ -21,6 +21,7 @@ "_": false, "angular": false, "require": false, - "module": false + "module": false, + "window": false } } diff --git a/README.md b/README.md index 1d0c1403..2c8375be 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,8 @@ angular.module('myApp').config(function (widgetsProvider) { You must set the manifest generator function in order for the directive to work. This is how we can know for a specific plugin, what files should be loaded and with which module name to create the injector. Above you can see an example for a manifest generator, but you can do whatever you like. You can put both relative and absolute URL's, of course. +*Note:* In case [requirejs](http://requirejs.org/) is available in the global scope, it will be used to load the javascript files. So if your widget needs more than one js file, you can include [requirejs](http://requirejs.org/) and use AMD to load them. + You can actually set multiple manifest generators and they will be evaluated in the order that they were defined. So a generator is allowed to return `undefined` in case it simply wants a different generator to handle it. The way the generators responses are handled is that the last generator that didn't return `undefined` will be used, unless a different generator returned a result with higher `priority`. ## Service Usage (widget) @@ -125,7 +127,7 @@ BTW, one event that is shared by default is `$locationChangeStart`. This is in o ## How to use in the real world -This framework is best used by having a separate project for each widget. During development, the developer sees only his own widget. All widgets should be built in a consistent manner, usually with one concatenated minified .js and .css files. +This framework is best used by having a separate project for each widget. During development, the developer sees only his own widget. All widgets should be built in a consistent manner, usually with one concatenated minified .js and .css files. ## License diff --git a/app/scripts/services/tag-appender.js b/app/scripts/services/tag-appender.js index f97db379..3746e684 100644 --- a/app/scripts/services/tag-appender.js +++ b/app/scripts/services/tag-appender.js @@ -14,6 +14,16 @@ angular.module('angularWidgetInternal') return function (url, filetype) { var deferred = $q.defer(); + if (filetype === 'js' && window.requirejs && headElement === document.getElementsByTagName('head')[0]) { + window.requirejs([url], function (module) { + deferred.resolve(module); + $rootScope.$digest(); + }, function (err) { + deferred.reject(err); + $rootScope.$digest(); + }); + return deferred.promise; + } if (requireCache.indexOf(url) !== -1) { deferred.resolve(); return deferred.promise; diff --git a/bower.json b/bower.json index 8e064ed2..1c7916c5 100644 --- a/bower.json +++ b/bower.json @@ -27,6 +27,7 @@ "es5-shim": "~2.1.0", "angular-route": ">= 1.2.0", "angular-ui-router": "~0.2.11", - "angular-widget": "~0.1.30" + "angular-widget": "~0.1.30", + "requirejs": "~2.1.19" } } diff --git a/karma.conf.js b/karma.conf.js index 49a424de..9f9390e1 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -33,8 +33,10 @@ module.exports = function (config) { '{app,.tmp}/*.js', '{app,.tmp}/scripts/*.js', '{app,.tmp}/scripts/*/**/*.js', + {pattern:'{,.tmp}/test/mock/mock-lazyloaded-file.js',included:false}, '{,.tmp/}test/**/*.js', - '{app,.tmp}/views/**/*.html' + '{app,.tmp}/views/**/*.html', + 'app/bower_components/requirejs/require.js' ], // list of files / patterns to exclude diff --git a/test/.jshintrc b/test/.jshintrc index 97fa3b49..07e32062 100644 --- a/test/.jshintrc +++ b/test/.jshintrc @@ -47,6 +47,7 @@ "using": false, "pause": false, "resume": false, - "sleep": false + "sleep": false, + "window": false } } diff --git a/test/mock/mock-lazyloaded-file.js b/test/mock/mock-lazyloaded-file.js new file mode 100644 index 00000000..1b3956c7 --- /dev/null +++ b/test/mock/mock-lazyloaded-file.js @@ -0,0 +1 @@ +window.lazyLoadingWorking = true; diff --git a/test/spec/services/tag-appender.spec.js b/test/spec/services/tag-appender.spec.js index 915757b4..26ade7c1 100644 --- a/test/spec/services/tag-appender.spec.js +++ b/test/spec/services/tag-appender.spec.js @@ -10,6 +10,59 @@ describe('Unit testing tagAppender service', function () { }); }); + describe('Loading with requirejs when available and headElement is head', function () { + + var moduleName = 'base/test/mock/mock-lazyloaded-file.js'; + + beforeEach(function () { + //Restoring the head element makes requirejs come into action + module({ + headElement: window.document.getElementsByTagName('head')[0] + }); + }); + + afterEach(inject(function ($window){ + $window.requirejs.undef(moduleName); + $window.lazyLoadingWorking = undefined; + })); + + it('should load the javascript files', inject (function (tagAppender, $window) { + var done = false; + expect($window.lazyLoadingWorking).toBeFalsy(); + tagAppender(moduleName, 'js').then(function (mod) { + expect($window.lazyLoadingWorking).toBe(true); + done = true; + }); + waitsFor(function() { + return done; + }); + })); + + it('should fail when file doesn\'t exist', inject (function (tagAppender) { + var done = false; + tagAppender('base/test/mock/non-existing-file.js', 'js').catch(function () { + done = true; + }); + waitsFor(function() { + return done; + }); + })); + + it('should not fail when same file loads two times', inject (function (tagAppender, $window) { + var done = false; + expect($window.lazyLoadingWorking).toBeFalsy(); + tagAppender(moduleName, 'js').then(function () { + tagAppender(moduleName, 'js').then(function () { + expect($window.lazyLoadingWorking).toBe(true); + done= true; + }); + }); + waitsFor(function() { + return done; + }); + })); + }); + it('should append script tag when js file is added', inject(function (tagAppender) { tagAppender('dummy.js', 'js'); expect(headElement.appendChild.calls.length).toBe(1);