-
-
Save laurelnaiad/5691371 to your computer and use it in GitHub Desktop.
nateabele's autoload patch, comments by stu-salsbury
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
/** | |
* Naive AngularJS module autoloader; depends on require.js. | |
* | |
* Usage: | |
* | |
* angular.autoLoad({ | |
* 'ngResource': '/js/angular-resource-1.0.1.min.js', | |
* 'google-maps': '/js/angular-google-maps.js', | |
* 'ui.bootstrap': '/js/ui/ui-bootstrap-custom-tpls-0.4.0.min.js', | |
* 'module.split.over.multiple.files': ['/js/file1.js', '/js/file2.js'] | |
* }); | |
* | |
* Then initialize your modules with their dependencies as normal, and the correct scripts will be | |
* autoloaded appropriately. | |
* | |
* Notes: | |
* As mentioned, this is currently implemented with required.js. It should be rewritten using $q. | |
* ALSO, Angular must be boostrapped manually (http://docs.angularjs.org/guide/bootstrap) *after* | |
* your autoload configuration is initialized, and *before* your application modules are loaded. | |
* | |
* SCS: $q would give us asynchrony, but without AMD, aren't we left with ngInclude, which doesn't support | |
* cross-origin loading, or jQuery (which does)? I guess this could be factored out to give the user choices... | |
* | |
* SCS: I don't yet understand the comment about boostrap timing... hoping to be able to add dependencies | |
* at runtime and rebootrap -- looks like this might be possible. | |
* | |
* SCS: would like to factor out the autoLoad url map and expose this as a service. Perhaps create a different service | |
* for module/component-to-URL mappings? | |
* | |
*/ | |
(function(angular) { | |
//backup the core module and boostrap functions | |
//track the number of outstanding requests | |
var _module = angular.module, _bootstrap = angular.bootstrap, _requests = 0, _inited = false; | |
var _loadMap = {}, _invokeQueue = {}, _loaded = {}, _funcCache = {}, _ = {}; | |
//takes array of items or single | |
//item and puts it into the list | |
//obj if they/it are/is not already | |
//in list | |
var _appendAndFlatten = function(list, item) { | |
if (!angular.isArray(item)) { | |
if (list.indexOf(item) === -1) { | |
list.push(item); | |
} | |
return list; | |
} | |
angular.forEach(item, function(i) { | |
if (list.indexOf(i) === -1) { | |
list.push(i); | |
} | |
}); | |
return list; | |
}; | |
angular.extend(angular, { | |
//overwrite the module function | |
module: function(name, requires, configFn) { | |
if (!requires) { | |
//no dependencies -- just call the core function | |
return _module.call(angular, name, requires, configFn); | |
} | |
//if we get here, there are dependencies | |
//break them up and load them individually | |
var autoLoad = []; | |
//put all specified module dependencies that are not yet loaded | |
//and *ARE* in the _loadMap into the autoLoad list | |
angular.forEach(requires, function(mod) { | |
try { | |
//try to retrieve a module | |
angular.module(mod); | |
} catch (e) { | |
if (!e.toString().match(/No module/)) { | |
//something went wrong, and it is NOT that the | |
//module has yet to be loaded -- throw it | |
throw e; | |
} | |
//if we get here, then the module hasn't been loaded | |
if (_loadMap[mod]) { | |
//if the module is in the _loadMap, then | |
//ensure it is in the autoLoad list. | |
//if the _loadMap doesn't have a URL for the module | |
//then it won't be loaded | |
autoLoad = _appendAndFlatten(autoLoad, _loadMap[mod]); | |
} | |
} | |
}); | |
//there are no dependencies yet to be loaded (other than 'ng'), | |
//so just load the module with the core function | |
if (!autoLoad.length || (autoLoad.length === 1 && autoLoad[0] === "ng")) { | |
_inited = true; | |
return _module.call(angular, name, requires, configFn); | |
} | |
//if we get here, we need to manually load the module (?) | |
//load the main module using core (without its dependencies) | |
//what we're getting back is the invokeQueue | |
var module = _module.call(angular, name, [], configFn); | |
//loop the module's properties | |
angular.forEach(module, function(val, key) { | |
if (!angular.isFunction(val)) { | |
//if it's not a function, do nothing | |
return; | |
} | |
//the val is one of the invokeLater functions | |
// (?or maybe the run function?); | |
//replace the function | |
module[key] = function() { | |
//when this function is called, it adds the property name and the args | |
//to the invokeQueue array at the module's name | |
_invokeQueue[name].push([key, Array.prototype.slice.call(arguments)]); | |
//and returns the module | |
return module; | |
}; | |
//put the function itself in the cache keyed by its original name | |
_funcCache[key] = val; | |
}); | |
// set up the _invokeQueue array for the module | |
_invokeQueue[name] = []; | |
//begin the loading of modules set up for autoLoad | |
console.log("loading", autoLoad); | |
//load the modules using AMD | |
require(autoLoad, function() { | |
//get the config functions from the cache | |
//and put them back in the module | |
for (var n in _funcCache) { | |
module[n] = _funcCache[n]; | |
} | |
//put the dependencies in the module | |
module.requires = requires; | |
//if there was a config function, put it back | |
if (angular.isFunction(configFn)) { | |
module.config(configFn); | |
} | |
//loop the invoke queue for the module | |
//and invoke the functions | |
for (var i = 0; i < _invokeQueue[name].length; i++) { | |
var call = _invokeQueue[name][i]; | |
module[call[0]].apply(module, call[1]); | |
} | |
//decrement the request counter | |
//this is happening out of process since we're in the | |
//require function | |
_requests--; | |
//if there is was a bootstrap function, | |
//invoke the reference we grabbed to it | |
//IF we've already initialized | |
//this *might* (?) allow to re-bootstrap | |
//after subsequent calls to module | |
if (_.businessTime) { | |
_.businessTime(); | |
} | |
}); | |
//one more outstanding request | |
//this is in-process so it happens before the require callback above | |
_requests++; | |
//initialized -- everything is set up to go | |
_inited = true; | |
return module; | |
}, | |
//add properties of the modules object | |
//to the _loadMap | |
autoLoad: function(modules) { | |
angular.extend(_loadMap, modules); | |
}, | |
//replace the angular bootstrap function | |
bootstrap: function() { | |
//grab the args | |
var args = Array.prototype.slice.call(arguments); | |
//this invokes the core boostrap function | |
//IF we've initialized already | |
//and there are no outstanding requests | |
_.businessTime = function() { | |
if (_inited && _requests === 0) { | |
return _bootstrap.apply(angular, args); | |
} | |
}; | |
_.businessTime(); | |
} | |
}); | |
})(angular); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment