mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 15:40:22 +00:00
439 lines
14 KiB
JavaScript
439 lines
14 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* @ngdoc function
|
|
* @name angular.injector
|
|
* @function
|
|
*
|
|
* @description
|
|
* Creates an injector function that can be used for retrieving services as well as for
|
|
* dependency injection (see {@link guide/dev_guide.di dependency injection}).
|
|
*
|
|
|
|
* @param {Array.<string|Function>} modules A list of module functions or their aliases. See
|
|
* {@link angular.module}. The `ng` module must be explicitly added.
|
|
* @returns {function()} Injector function. See {@link angular.module.AUTO.$injector $injector}.
|
|
*
|
|
* @example
|
|
* Typical usage
|
|
* <pre>
|
|
* // create an injector
|
|
* var $injector = angular.injector(['ng']);
|
|
*
|
|
* // use the injector to kick of your application
|
|
* // use the type inference to auto inject arguments, or use implicit injection
|
|
* $injector.invoke(function($rootScope, $compile, $document){
|
|
* $compile($document)($rootScope);
|
|
* $rootScope.$digest();
|
|
* });
|
|
* </pre>
|
|
*/
|
|
|
|
|
|
/**
|
|
* @ngdoc overview
|
|
* @name angular.module.AUTO
|
|
* @description
|
|
*
|
|
* Implicit module which gets automatically added to each {@link angular.module.AUTO.$injector $injector}.
|
|
*/
|
|
|
|
var FN_ARGS = /^function\s*[^\(]*\(([^\)]*)\)/m;
|
|
var FN_ARG_SPLIT = /,/;
|
|
var FN_ARG = /^\s*(.+?)\s*$/;
|
|
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
|
function inferInjectionArgs(fn) {
|
|
assertArgFn(fn);
|
|
if (!fn.$inject) {
|
|
var args = fn.$inject = [];
|
|
var fnText = fn.toString().replace(STRIP_COMMENTS, '');
|
|
var argDecl = fnText.match(FN_ARGS);
|
|
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
|
|
arg.replace(FN_ARG, function(all, name){
|
|
args.push(name);
|
|
});
|
|
});
|
|
}
|
|
return fn.$inject;
|
|
}
|
|
|
|
///////////////////////////////////////
|
|
|
|
/**
|
|
* @ngdoc object
|
|
* @name angular.module.AUTO.$injector
|
|
* @function
|
|
*
|
|
* @description
|
|
*
|
|
* `$injector` is used to retrieve object instances as defined by
|
|
* {@link angular.module.AUTO.$provide provider}, instantiate types, invoke methods,
|
|
* and load modules.
|
|
*
|
|
* The following always holds true:
|
|
*
|
|
* <pre>
|
|
* var $injector = angular.injector();
|
|
* expect($injector.get('$injector')).toBe($injector);
|
|
* expect($injector.invoke(function($injector){
|
|
* return $injector;
|
|
* }).toBe($injector);
|
|
* </pre>
|
|
*
|
|
* # Injection Function Annotation
|
|
*
|
|
* JavaScript does not have annotations, and annotations are needed for dependency injection. The
|
|
* following ways are all valid way of annotating function with injection arguments and are equivalent.
|
|
*
|
|
* <pre>
|
|
* // inferred (only works if code not minified/obfuscated)
|
|
* $inject.invoke(function(serviceA){});
|
|
*
|
|
* // annotated
|
|
* function explicit(serviceA) {};
|
|
* explicit.$inject = ['serviceA'];
|
|
* $inject.invoke(explicit);
|
|
*
|
|
* // inline
|
|
* $inject.invoke(['serviceA', function(serviceA){}]);
|
|
* </pre>
|
|
*
|
|
* ## Inference
|
|
*
|
|
* In JavaScript calling `toString()` on a function returns the function definition. The definition can then be
|
|
* parsed and the function arguments can be extracted. *NOTE:* This does not work with minfication, and obfuscation
|
|
* tools since these tools change the argument names.
|
|
*
|
|
* ## `$inject` Annotation
|
|
* By adding a `$inject` property onto a function the injection parameters can be specified.
|
|
*
|
|
* ## Inline
|
|
* As an array of injection names, where the last item in the array is the function to call.
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name angular.module.AUTO.$injector#get
|
|
* @methodOf angular.module.AUTO.$injector
|
|
*
|
|
* @description
|
|
* Return an instance of the service.
|
|
*
|
|
* @param {string} name The name of the instance to retrieve.
|
|
* @return {*} The instance.
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name angular.module.AUTO.$injector#invoke
|
|
* @methodOf angular.module.AUTO.$injector
|
|
*
|
|
* @description
|
|
* Invoke the method and supply the method arguments from the `$injector`.
|
|
*
|
|
* @param {!function} fn The function to invoke. The function arguments come form the function annotation.
|
|
* @param {Object=} self The `this` for the invoked method.
|
|
* @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before
|
|
* the `$injector` is consulted.
|
|
* @return the value returned by the invoked `fn` function.
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name angular.module.AUTO.$injector#instantiate
|
|
* @methodOf angular.module.AUTO.$injector
|
|
* @description
|
|
* Create a new instance of JS type. The method takes a constructor function invokes the new operator and supplies
|
|
* all of the arguments to the constructor function as specified by the constructor annotation.
|
|
*
|
|
* @param {function} Type Annotated constructor function.
|
|
* @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before
|
|
* the `$injector` is consulted.
|
|
* @return new instance of `Type`.
|
|
*/
|
|
|
|
|
|
/**
|
|
* @ngdoc object
|
|
* @name angular.module.AUTO.$provide
|
|
*
|
|
* @description
|
|
*
|
|
* Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance.
|
|
* The providers share the same name as the instance they create with the `Provide` suffixed to them.
|
|
*
|
|
* A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of
|
|
* a service. The Provider can have additional methods which would allow for configuration of the provider.
|
|
*
|
|
* <pre>
|
|
* function GreetProvider() {
|
|
* var salutation = 'Hello';
|
|
*
|
|
* this.salutation = function(text) {
|
|
* salutation = text;
|
|
* };
|
|
*
|
|
* this.$get = function() {
|
|
* return function (name) {
|
|
* return salutation + ' ' + name + '!';
|
|
* };
|
|
* };
|
|
* }
|
|
*
|
|
* describe('Greeter', function(){
|
|
*
|
|
* beforeEach(module(function($provide) {
|
|
* $provide.service('greet', GreetProvider);
|
|
* });
|
|
*
|
|
* it('should greet', inject(function(greet) {
|
|
* expect(greet('angular')).toEqual('Hello angular!');
|
|
* }));
|
|
*
|
|
* it('should allow configuration of salutation', function() {
|
|
* module(function(greetProvider) {
|
|
* greetProvider.salutation('Ahoj');
|
|
* });
|
|
* inject(function(greet) {
|
|
* expect(greet('angular')).toEqual('Ahoj angular!');
|
|
* });
|
|
* )};
|
|
*
|
|
* });
|
|
* </pre>
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name angular.module.AUTO.$provide#service
|
|
* @methodOf angular.module.AUTO.$provide
|
|
* @description
|
|
*
|
|
* Register a provider for a service. The providers can be retrieved and can have additional configuration methods.
|
|
*
|
|
* @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provide'` key.
|
|
* @param {(Object|function())} provider If the provider is:
|
|
*
|
|
* - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
|
|
* {@link angular.module.AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created.
|
|
* - `Constructor`: a new instance of the provider will be created using
|
|
* {@link angular.module.AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name angular.module.AUTO.$provide#factory
|
|
* @methodOf angular.module.AUTO.$provide
|
|
* @description
|
|
*
|
|
* A short hand for configuring services if only `$get` method is required.
|
|
*
|
|
* @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provide'` key.
|
|
* @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand for
|
|
* `$provide.service(name, {$get:$getFn})`.
|
|
*/
|
|
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name angular.module.AUTO.$provide#value
|
|
* @methodOf angular.module.AUTO.$provide
|
|
* @description
|
|
*
|
|
* A short hand for configuring services if the `$get` method is a constant.
|
|
*
|
|
* @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provide'` key.
|
|
* @param {function()} value The $getFn for the instance creation. Internally this is a short hand for
|
|
* `$provide.service(name, {$get:function(){ return value; }})`.
|
|
*/
|
|
|
|
|
|
function createInjector(modulesToLoad) {
|
|
var providerSuffix = 'Provider',
|
|
path = [],
|
|
loadedModules = new HashMap(),
|
|
providerCache = {
|
|
$provide: {
|
|
service: supportObject(service),
|
|
factory: supportObject(factory),
|
|
value: supportObject(value),
|
|
decorator: decorator
|
|
}
|
|
},
|
|
providerInjector = createInternalInjector(providerCache, function() {
|
|
throw Error("Unknown provider: " + path.join(' <- '));
|
|
}),
|
|
instanceCache = {},
|
|
instanceInjector = (instanceCache.$injector =
|
|
createInternalInjector(instanceCache, function(servicename) {
|
|
var provider = providerInjector.get(servicename + providerSuffix);
|
|
return instanceInjector.invoke(provider.$get, provider);
|
|
}));
|
|
|
|
|
|
forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
|
|
|
|
return instanceInjector;
|
|
|
|
////////////////////////////////////
|
|
// $provider
|
|
////////////////////////////////////
|
|
|
|
function supportObject(delegate) {
|
|
return function(key, value) {
|
|
if (isObject(key)) {
|
|
forEach(key, reverseParams(delegate));
|
|
} else {
|
|
delegate(key, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
function service(name, provider) {
|
|
if (isFunction(provider)){
|
|
provider = providerInjector.instantiate(provider);
|
|
}
|
|
if (!provider.$get) {
|
|
throw Error('Provider ' + name + ' must define $get factory method.');
|
|
}
|
|
providerCache[name + providerSuffix] = provider;
|
|
}
|
|
|
|
function factory(name, factoryFn) { service(name, { $get:factoryFn }); }
|
|
|
|
function value(name, value) { factory(name, valueFn(value)); }
|
|
|
|
function decorator(serviceName, decorFn) {
|
|
var origProvider = providerInjector.get(serviceName + providerSuffix),
|
|
orig$get = origProvider.$get;
|
|
|
|
origProvider.$get = function() {
|
|
var origInstance = instanceInjector.invoke(orig$get, origProvider);
|
|
return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
|
|
};
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// Module Loading
|
|
////////////////////////////////////
|
|
function loadModules(modulesToLoad){
|
|
var runBlocks = [];
|
|
forEach(modulesToLoad, function(module) {
|
|
if (loadedModules.get(module)) return;
|
|
loadedModules.put(module, true);
|
|
if (isString(module)) {
|
|
var moduleFn = angularModule(module);
|
|
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
|
|
|
|
try {
|
|
for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
|
|
var invokeArgs = invokeQueue[i],
|
|
provider = invokeArgs[0] == '$injector'
|
|
? providerInjector
|
|
: providerInjector.get(invokeArgs[0]);
|
|
|
|
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
|
|
}
|
|
} catch (e) {
|
|
if (e.message) e.message += ' from ' + module;
|
|
throw e;
|
|
}
|
|
} else if (isFunction(module)) {
|
|
try {
|
|
runBlocks.push(providerInjector.invoke(module));
|
|
} catch (e) {
|
|
if (e.message) e.message += ' from ' + module;
|
|
throw e;
|
|
}
|
|
} else if (isArray(module)) {
|
|
try {
|
|
runBlocks.push(providerInjector.invoke(module));
|
|
} catch (e) {
|
|
if (e.message) e.message += ' from ' + String(module[module.length - 1]);
|
|
throw e;
|
|
}
|
|
} else {
|
|
assertArgFn(module, 'module');
|
|
}
|
|
});
|
|
return runBlocks;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// internal Injector
|
|
////////////////////////////////////
|
|
|
|
function createInternalInjector(cache, factory) {
|
|
|
|
function getService(serviceName) {
|
|
if (typeof serviceName !== 'string') {
|
|
throw Error('Service name expected');
|
|
}
|
|
if (cache.hasOwnProperty(serviceName)) {
|
|
return cache[serviceName];
|
|
} else {
|
|
try {
|
|
path.unshift(serviceName);
|
|
return cache[serviceName] = factory(serviceName);
|
|
} finally {
|
|
path.shift();
|
|
}
|
|
}
|
|
}
|
|
|
|
function invoke(fn, self, locals){
|
|
var args = [],
|
|
$injectAnnotation,
|
|
$injectAnnotationIndex,
|
|
key;
|
|
|
|
if (typeof fn == 'function') {
|
|
$injectAnnotation = inferInjectionArgs(fn);
|
|
$injectAnnotationIndex = $injectAnnotation.length;
|
|
} else {
|
|
if (isArray(fn)) {
|
|
$injectAnnotation = fn;
|
|
$injectAnnotationIndex = $injectAnnotation.length;
|
|
fn = $injectAnnotation[--$injectAnnotationIndex];
|
|
}
|
|
assertArgFn(fn, 'fn');
|
|
}
|
|
|
|
while($injectAnnotationIndex--) {
|
|
key = $injectAnnotation[$injectAnnotationIndex];
|
|
args.unshift(locals && locals.hasOwnProperty(key) ? locals[key] : getService(key));
|
|
}
|
|
|
|
// Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke
|
|
switch (self ? -1 : args.length) {
|
|
case 0: return fn();
|
|
case 1: return fn(args[0]);
|
|
case 2: return fn(args[0], args[1]);
|
|
case 3: return fn(args[0], args[1], args[2]);
|
|
case 4: return fn(args[0], args[1], args[2], args[3]);
|
|
case 5: return fn(args[0], args[1], args[2], args[3], args[4]);
|
|
case 6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
|
|
case 7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
|
|
case 8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
|
|
case 9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
|
|
case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
|
|
default: return fn.apply(self, args);
|
|
}
|
|
}
|
|
|
|
function instantiate(Type, locals){
|
|
var Constructor = function(){},
|
|
instance;
|
|
Constructor.prototype = Type.prototype;
|
|
instance = new Constructor();
|
|
return invoke(Type, instance, locals) || instance;
|
|
}
|
|
|
|
return {
|
|
invoke: invoke,
|
|
instantiate: instantiate,
|
|
get: getService
|
|
};
|
|
}
|
|
}
|