mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
811 lines
27 KiB
JavaScript
811 lines
27 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/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 AUTO.$injector $injector}.
|
|
*
|
|
* @example
|
|
* Typical usage
|
|
* <pre>
|
|
* // create an injector
|
|
* var $injector = angular.injector(['ng']);
|
|
*
|
|
* // use the injector to kick off 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>
|
|
*
|
|
* Sometimes you want to get access to the injector of a currently running Angular app
|
|
* from outside Angular. Perhaps, you want to inject and compile some markup after the
|
|
* application has been bootstrapped. You can do this using extra `injector()` added
|
|
* to JQuery/jqLite elements. See {@link angular.element}.
|
|
*
|
|
* *This is fairly rare but could be the case if a third party library is injecting the
|
|
* markup.*
|
|
*
|
|
* In the following example a new block of HTML containing a `ng-controller`
|
|
* directive is added to the end of the document body by JQuery. We then compile and link
|
|
* it into the current AngularJS scope.
|
|
*
|
|
* <pre>
|
|
* var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
|
|
* $(document.body).append($div);
|
|
*
|
|
* angular.element(document).injector().invoke(function($compile) {
|
|
* var scope = angular.element($div).scope();
|
|
* $compile($div)(scope);
|
|
* });
|
|
* </pre>
|
|
*/
|
|
|
|
|
|
/**
|
|
* @ngdoc overview
|
|
* @name AUTO
|
|
* @description
|
|
*
|
|
* Implicit module which gets automatically added to each {@link AUTO.$injector $injector}.
|
|
*/
|
|
|
|
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
|
|
var FN_ARG_SPLIT = /,/;
|
|
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
|
|
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
|
var $injectorMinErr = minErr('$injector');
|
|
function annotate(fn) {
|
|
var $inject,
|
|
fnText,
|
|
argDecl,
|
|
last;
|
|
|
|
if (typeof fn == 'function') {
|
|
if (!($inject = fn.$inject)) {
|
|
$inject = [];
|
|
if (fn.length) {
|
|
fnText = fn.toString().replace(STRIP_COMMENTS, '');
|
|
argDecl = fnText.match(FN_ARGS);
|
|
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
|
|
arg.replace(FN_ARG, function(all, underscore, name){
|
|
$inject.push(name);
|
|
});
|
|
});
|
|
}
|
|
fn.$inject = $inject;
|
|
}
|
|
} else if (isArray(fn)) {
|
|
last = fn.length - 1;
|
|
assertArgFn(fn[last], 'fn');
|
|
$inject = fn.slice(0, last);
|
|
} else {
|
|
assertArgFn(fn, 'fn', true);
|
|
}
|
|
return $inject;
|
|
}
|
|
|
|
///////////////////////////////////////
|
|
|
|
/**
|
|
* @ngdoc object
|
|
* @name AUTO.$injector
|
|
* @function
|
|
*
|
|
* @description
|
|
*
|
|
* `$injector` is used to retrieve object instances as defined by
|
|
* {@link 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 are all valid ways of annotating function with injection arguments and are equivalent.
|
|
*
|
|
* <pre>
|
|
* // inferred (only works if code not minified/obfuscated)
|
|
* $injector.invoke(function(serviceA){});
|
|
*
|
|
* // annotated
|
|
* function explicit(serviceA) {};
|
|
* explicit.$inject = ['serviceA'];
|
|
* $injector.invoke(explicit);
|
|
*
|
|
* // inline
|
|
* $injector.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
|
|
* minification, 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 AUTO.$injector#get
|
|
* @methodOf 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 AUTO.$injector#invoke
|
|
* @methodOf AUTO.$injector
|
|
*
|
|
* @description
|
|
* Invoke the method and supply the method arguments from the `$injector`.
|
|
*
|
|
* @param {!function} fn The function to invoke. Function parameters are injected according to the
|
|
* {@link guide/di $inject Annotation} rules.
|
|
* @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.
|
|
* @returns {*} the value returned by the invoked `fn` function.
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name AUTO.$injector#has
|
|
* @methodOf AUTO.$injector
|
|
*
|
|
* @description
|
|
* Allows the user to query if the particular service exist.
|
|
*
|
|
* @param {string} Name of the service to query.
|
|
* @returns {boolean} returns true if injector has given service.
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name AUTO.$injector#instantiate
|
|
* @methodOf 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.
|
|
* @returns {Object} new instance of `Type`.
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name AUTO.$injector#annotate
|
|
* @methodOf AUTO.$injector
|
|
*
|
|
* @description
|
|
* Returns an array of service names which the function is requesting for injection. This API is
|
|
* used by the injector to determine which services need to be injected into the function when the
|
|
* function is invoked. There are three ways in which the function can be annotated with the needed
|
|
* dependencies.
|
|
*
|
|
* # Argument names
|
|
*
|
|
* The simplest form is to extract the dependencies from the arguments of the function. This is done
|
|
* by converting the function into a string using `toString()` method and extracting the argument
|
|
* names.
|
|
* <pre>
|
|
* // Given
|
|
* function MyController($scope, $route) {
|
|
* // ...
|
|
* }
|
|
*
|
|
* // Then
|
|
* expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
|
|
* </pre>
|
|
*
|
|
* This method does not work with code minification / obfuscation. For this reason the following
|
|
* annotation strategies are supported.
|
|
*
|
|
* # The `$inject` property
|
|
*
|
|
* If a function has an `$inject` property and its value is an array of strings, then the strings
|
|
* represent names of services to be injected into the function.
|
|
* <pre>
|
|
* // Given
|
|
* var MyController = function(obfuscatedScope, obfuscatedRoute) {
|
|
* // ...
|
|
* }
|
|
* // Define function dependencies
|
|
* MyController['$inject'] = ['$scope', '$route'];
|
|
*
|
|
* // Then
|
|
* expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
|
|
* </pre>
|
|
*
|
|
* # The array notation
|
|
*
|
|
* It is often desirable to inline Injected functions and that's when setting the `$inject` property
|
|
* is very inconvenient. In these situations using the array notation to specify the dependencies in
|
|
* a way that survives minification is a better choice:
|
|
*
|
|
* <pre>
|
|
* // We wish to write this (not minification / obfuscation safe)
|
|
* injector.invoke(function($compile, $rootScope) {
|
|
* // ...
|
|
* });
|
|
*
|
|
* // We are forced to write break inlining
|
|
* var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
|
|
* // ...
|
|
* };
|
|
* tmpFn.$inject = ['$compile', '$rootScope'];
|
|
* injector.invoke(tmpFn);
|
|
*
|
|
* // To better support inline function the inline annotation is supported
|
|
* injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
|
|
* // ...
|
|
* }]);
|
|
*
|
|
* // Therefore
|
|
* expect(injector.annotate(
|
|
* ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
|
|
* ).toEqual(['$compile', '$rootScope']);
|
|
* </pre>
|
|
*
|
|
* @param {function|Array.<string|Function>} fn Function for which dependent service names need to
|
|
* be retrieved as described above.
|
|
*
|
|
* @returns {Array.<string>} The names of the services which the function requires.
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
* @ngdoc object
|
|
* @name AUTO.$provide
|
|
*
|
|
* @description
|
|
*
|
|
* The {@link AUTO.$provide $provide} service has a number of methods for registering components
|
|
* with the {@link AUTO.$injector $injector}. Many of these functions are also exposed on
|
|
* {@link angular.Module}.
|
|
*
|
|
* An Angular **service** is a singleton object created by a **service factory**. These **service
|
|
* factories** are functions which, in turn, are created by a **service provider**.
|
|
* The **service providers** are constructor functions. When instantiated they must contain a
|
|
* property called `$get`, which holds the **service factory** function.
|
|
*
|
|
* When you request a service, the {@link AUTO.$injector $injector} is responsible for finding the
|
|
* correct **service provider**, instantiating it and then calling its `$get` **service factory**
|
|
* function to get the instance of the **service**.
|
|
*
|
|
* Often services have no configuration options and there is no need to add methods to the service
|
|
* provider. The provider will be no more than a constructor function with a `$get` property. For
|
|
* these cases the {@link AUTO.$provide $provide} service has additional helper methods to register
|
|
* services without specifying a provider.
|
|
*
|
|
* * {@link AUTO.$provide#methods_provider provider(provider)} - registers a **service provider** with the
|
|
* {@link AUTO.$injector $injector}
|
|
* * {@link AUTO.$provide#methods_constant constant(obj)} - registers a value/object that can be accessed by
|
|
* providers and services.
|
|
* * {@link AUTO.$provide#methods_value value(obj)} - registers a value/object that can only be accessed by
|
|
* services, not providers.
|
|
* * {@link AUTO.$provide#methods_factory factory(fn)} - registers a service **factory function**, `fn`,
|
|
* that will be wrapped in a **service provider** object, whose `$get` property will contain the
|
|
* given factory function.
|
|
* * {@link AUTO.$provide#methods_service service(class)} - registers a **constructor function**, `class` that
|
|
* that will be wrapped in a **service provider** object, whose `$get` property will instantiate
|
|
* a new object using the given constructor function.
|
|
*
|
|
* See the individual methods for more information and examples.
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name AUTO.$provide#provider
|
|
* @methodOf AUTO.$provide
|
|
* @description
|
|
*
|
|
* Register a **provider function** with the {@link AUTO.$injector $injector}. Provider functions
|
|
* are constructor functions, whose instances are responsible for "providing" a factory for a
|
|
* service.
|
|
*
|
|
* Service provider names start with the name of the service they provide followed by `Provider`.
|
|
* For example, the {@link ng.$log $log} service has a provider called
|
|
* {@link ng.$logProvider $logProvider}.
|
|
*
|
|
* Service provider objects can have additional methods which allow configuration of the provider
|
|
* and its service. Importantly, you can configure what kind of service is created by the `$get`
|
|
* method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
|
|
* method {@link ng.$logProvider#debugEnabled debugEnabled}
|
|
* which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
|
|
* console or not.
|
|
*
|
|
* @param {string} name The name of the instance. NOTE: the provider will be available under `name +
|
|
'Provider'` 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 AUTO.$injector#invoke $injector.invoke()} when an instance needs to be
|
|
* created.
|
|
* - `Constructor`: a new instance of the provider will be created using
|
|
* {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as
|
|
* `object`.
|
|
*
|
|
* @returns {Object} registered provider instance
|
|
|
|
* @example
|
|
*
|
|
* The following example shows how to create a simple event tracking service and register it using
|
|
* {@link AUTO.$provide#methods_provider $provide.provider()}.
|
|
*
|
|
* <pre>
|
|
* // Define the eventTracker provider
|
|
* function EventTrackerProvider() {
|
|
* var trackingUrl = '/track';
|
|
*
|
|
* // A provider method for configuring where the tracked events should been saved
|
|
* this.setTrackingUrl = function(url) {
|
|
* trackingUrl = url;
|
|
* };
|
|
*
|
|
* // The service factory function
|
|
* this.$get = ['$http', function($http) {
|
|
* var trackedEvents = {};
|
|
* return {
|
|
* // Call this to track an event
|
|
* event: function(event) {
|
|
* var count = trackedEvents[event] || 0;
|
|
* count += 1;
|
|
* trackedEvents[event] = count;
|
|
* return count;
|
|
* },
|
|
* // Call this to save the tracked events to the trackingUrl
|
|
* save: function() {
|
|
* $http.post(trackingUrl, trackedEvents);
|
|
* }
|
|
* };
|
|
* }];
|
|
* }
|
|
*
|
|
* describe('eventTracker', function() {
|
|
* var postSpy;
|
|
*
|
|
* beforeEach(module(function($provide) {
|
|
* // Register the eventTracker provider
|
|
* $provide.provider('eventTracker', EventTrackerProvider);
|
|
* }));
|
|
*
|
|
* beforeEach(module(function(eventTrackerProvider) {
|
|
* // Configure eventTracker provider
|
|
* eventTrackerProvider.setTrackingUrl('/custom-track');
|
|
* }));
|
|
*
|
|
* it('tracks events', inject(function(eventTracker) {
|
|
* expect(eventTracker.event('login')).toEqual(1);
|
|
* expect(eventTracker.event('login')).toEqual(2);
|
|
* }));
|
|
*
|
|
* it('saves to the tracking url', inject(function(eventTracker, $http) {
|
|
* postSpy = spyOn($http, 'post');
|
|
* eventTracker.event('login');
|
|
* eventTracker.save();
|
|
* expect(postSpy).toHaveBeenCalled();
|
|
* expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
|
|
* expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
|
|
* expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
|
|
* }));
|
|
* });
|
|
* </pre>
|
|
*/
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name AUTO.$provide#factory
|
|
* @methodOf AUTO.$provide
|
|
* @description
|
|
*
|
|
* Register a **service factory**, which will be called to return the service instance.
|
|
* This is short for registering a service where its provider consists of only a `$get` property,
|
|
* which is the given service factory function.
|
|
* You should use {@link AUTO.$provide#factory $provide.factory(getFn)} if you do not need to
|
|
* configure your service in a provider.
|
|
*
|
|
* @param {string} name The name of the instance.
|
|
* @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand
|
|
* for `$provide.provider(name, {$get: $getFn})`.
|
|
* @returns {Object} registered provider instance
|
|
*
|
|
* @example
|
|
* Here is an example of registering a service
|
|
* <pre>
|
|
* $provide.factory('ping', ['$http', function($http) {
|
|
* return function ping() {
|
|
* return $http.send('/ping');
|
|
* };
|
|
* }]);
|
|
* </pre>
|
|
* You would then inject and use this service like this:
|
|
* <pre>
|
|
* someModule.controller('Ctrl', ['ping', function(ping) {
|
|
* ping();
|
|
* }]);
|
|
* </pre>
|
|
*/
|
|
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name AUTO.$provide#service
|
|
* @methodOf AUTO.$provide
|
|
* @description
|
|
*
|
|
* Register a **service constructor**, which will be invoked with `new` to create the service
|
|
* instance.
|
|
* This is short for registering a service where its provider's `$get` property is the service
|
|
* constructor function that will be used to instantiate the service instance.
|
|
*
|
|
* You should use {@link AUTO.$provide#methods_service $provide.service(class)} if you define your service
|
|
* as a type/class.
|
|
*
|
|
* @param {string} name The name of the instance.
|
|
* @param {Function} constructor A class (constructor function) that will be instantiated.
|
|
* @returns {Object} registered provider instance
|
|
*
|
|
* @example
|
|
* Here is an example of registering a service using
|
|
* {@link AUTO.$provide#methods_service $provide.service(class)}.
|
|
* <pre>
|
|
* $provide.service('ping', ['$http', function($http) {
|
|
* var Ping = function() {
|
|
* this.$http = $http;
|
|
* };
|
|
*
|
|
* Ping.prototype.send = function() {
|
|
* return this.$http.get('/ping');
|
|
* };
|
|
*
|
|
* return Ping;
|
|
* }]);
|
|
* </pre>
|
|
* You would then inject and use this service like this:
|
|
* <pre>
|
|
* someModule.controller('Ctrl', ['ping', function(ping) {
|
|
* ping.send();
|
|
* }]);
|
|
* </pre>
|
|
*/
|
|
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name AUTO.$provide#value
|
|
* @methodOf AUTO.$provide
|
|
* @description
|
|
*
|
|
* Register a **value service** with the {@link AUTO.$injector $injector}, such as a string, a
|
|
* number, an array, an object or a function. This is short for registering a service where its
|
|
* provider's `$get` property is a factory function that takes no arguments and returns the **value
|
|
* service**.
|
|
*
|
|
* Value services are similar to constant services, except that they cannot be injected into a
|
|
* module configuration function (see {@link angular.Module#config}) but they can be overridden by
|
|
* an Angular
|
|
* {@link AUTO.$provide#decorator decorator}.
|
|
*
|
|
* @param {string} name The name of the instance.
|
|
* @param {*} value The value.
|
|
* @returns {Object} registered provider instance
|
|
*
|
|
* @example
|
|
* Here are some examples of creating value services.
|
|
* <pre>
|
|
* $provide.value('ADMIN_USER', 'admin');
|
|
*
|
|
* $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
|
|
*
|
|
* $provide.value('halfOf', function(value) {
|
|
* return value / 2;
|
|
* });
|
|
* </pre>
|
|
*/
|
|
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name AUTO.$provide#constant
|
|
* @methodOf AUTO.$provide
|
|
* @description
|
|
*
|
|
* Register a **constant service**, such as a string, a number, an array, an object or a function,
|
|
* with the {@link AUTO.$injector $injector}. Unlike {@link AUTO.$provide#value value} it can be
|
|
* injected into a module configuration function (see {@link angular.Module#config}) and it cannot
|
|
* be overridden by an Angular {@link AUTO.$provide#decorator decorator}.
|
|
*
|
|
* @param {string} name The name of the constant.
|
|
* @param {*} value The constant value.
|
|
* @returns {Object} registered instance
|
|
*
|
|
* @example
|
|
* Here a some examples of creating constants:
|
|
* <pre>
|
|
* $provide.constant('SHARD_HEIGHT', 306);
|
|
*
|
|
* $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
|
|
*
|
|
* $provide.constant('double', function(value) {
|
|
* return value * 2;
|
|
* });
|
|
* </pre>
|
|
*/
|
|
|
|
|
|
/**
|
|
* @ngdoc method
|
|
* @name AUTO.$provide#decorator
|
|
* @methodOf AUTO.$provide
|
|
* @description
|
|
*
|
|
* Register a **service decorator** with the {@link AUTO.$injector $injector}. A service decorator
|
|
* intercepts the creation of a service, allowing it to override or modify the behaviour of the
|
|
* service. The object returned by the decorator may be the original service, or a new service
|
|
* object which replaces or wraps and delegates to the original service.
|
|
*
|
|
* @param {string} name The name of the service to decorate.
|
|
* @param {function()} decorator This function will be invoked when the service needs to be
|
|
* instantiated and should return the decorated service instance. The function is called using
|
|
* the {@link AUTO.$injector#invoke injector.invoke} method and is therefore fully injectable.
|
|
* Local injection arguments:
|
|
*
|
|
* * `$delegate` - The original service instance, which can be monkey patched, configured,
|
|
* decorated or delegated to.
|
|
*
|
|
* @example
|
|
* Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
|
|
* calls to {@link ng.$log#error $log.warn()}.
|
|
* <pre>
|
|
* $provider.decorator('$log', ['$delegate', function($delegate) {
|
|
* $delegate.warn = $delegate.error;
|
|
* return $delegate;
|
|
* }]);
|
|
* </pre>
|
|
*/
|
|
|
|
|
|
function createInjector(modulesToLoad) {
|
|
var INSTANTIATING = {},
|
|
providerSuffix = 'Provider',
|
|
path = [],
|
|
loadedModules = new HashMap(),
|
|
providerCache = {
|
|
$provide: {
|
|
provider: supportObject(provider),
|
|
factory: supportObject(factory),
|
|
service: supportObject(service),
|
|
value: supportObject(value),
|
|
constant: supportObject(constant),
|
|
decorator: decorator
|
|
}
|
|
},
|
|
providerInjector = (providerCache.$injector =
|
|
createInternalInjector(providerCache, function() {
|
|
throw $injectorMinErr('unpr', "Unknown provider: {0}", 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 {
|
|
return delegate(key, value);
|
|
}
|
|
};
|
|
}
|
|
|
|
function provider(name, provider_) {
|
|
assertNotHasOwnProperty(name, 'service');
|
|
if (isFunction(provider_) || isArray(provider_)) {
|
|
provider_ = providerInjector.instantiate(provider_);
|
|
}
|
|
if (!provider_.$get) {
|
|
throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
|
|
}
|
|
return providerCache[name + providerSuffix] = provider_;
|
|
}
|
|
|
|
function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); }
|
|
|
|
function service(name, constructor) {
|
|
return factory(name, ['$injector', function($injector) {
|
|
return $injector.instantiate(constructor);
|
|
}]);
|
|
}
|
|
|
|
function value(name, val) { return factory(name, valueFn(val)); }
|
|
|
|
function constant(name, value) {
|
|
assertNotHasOwnProperty(name, 'constant');
|
|
providerCache[name] = value;
|
|
instanceCache[name] = 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 = [], moduleFn, invokeQueue, i, ii;
|
|
forEach(modulesToLoad, function(module) {
|
|
if (loadedModules.get(module)) return;
|
|
loadedModules.put(module, true);
|
|
|
|
try {
|
|
if (isString(module)) {
|
|
moduleFn = angularModule(module);
|
|
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
|
|
|
|
for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
|
|
var invokeArgs = invokeQueue[i],
|
|
provider = providerInjector.get(invokeArgs[0]);
|
|
|
|
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
|
|
}
|
|
} else if (isFunction(module)) {
|
|
runBlocks.push(providerInjector.invoke(module));
|
|
} else if (isArray(module)) {
|
|
runBlocks.push(providerInjector.invoke(module));
|
|
} else {
|
|
assertArgFn(module, 'module');
|
|
}
|
|
} catch (e) {
|
|
if (isArray(module)) {
|
|
module = module[module.length - 1];
|
|
}
|
|
if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
|
|
// Safari & FF's stack traces don't contain error.message content
|
|
// unlike those of Chrome and IE
|
|
// So if stack doesn't contain message, we create a new string that contains both.
|
|
// Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
|
|
/* jshint -W022 */
|
|
e = e.message + '\n' + e.stack;
|
|
}
|
|
throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
|
|
module, e.stack || e.message || e);
|
|
}
|
|
});
|
|
return runBlocks;
|
|
}
|
|
|
|
////////////////////////////////////
|
|
// internal Injector
|
|
////////////////////////////////////
|
|
|
|
function createInternalInjector(cache, factory) {
|
|
|
|
function getService(serviceName) {
|
|
if (cache.hasOwnProperty(serviceName)) {
|
|
if (cache[serviceName] === INSTANTIATING) {
|
|
throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- '));
|
|
}
|
|
return cache[serviceName];
|
|
} else {
|
|
try {
|
|
path.unshift(serviceName);
|
|
cache[serviceName] = INSTANTIATING;
|
|
return cache[serviceName] = factory(serviceName);
|
|
} catch (err) {
|
|
if (cache[serviceName] === INSTANTIATING) {
|
|
delete cache[serviceName];
|
|
}
|
|
throw err;
|
|
} finally {
|
|
path.shift();
|
|
}
|
|
}
|
|
}
|
|
|
|
function invoke(fn, self, locals){
|
|
var args = [],
|
|
$inject = annotate(fn),
|
|
length, i,
|
|
key;
|
|
|
|
for(i = 0, length = $inject.length; i < length; i++) {
|
|
key = $inject[i];
|
|
if (typeof key !== 'string') {
|
|
throw $injectorMinErr('itkn',
|
|
'Incorrect injection token! Expected service name as string, got {0}', key);
|
|
}
|
|
args.push(
|
|
locals && locals.hasOwnProperty(key)
|
|
? locals[key]
|
|
: getService(key)
|
|
);
|
|
}
|
|
if (!fn.$inject) {
|
|
// this means that we must be an array.
|
|
fn = fn[length];
|
|
}
|
|
|
|
// http://jsperf.com/angularjs-invoke-apply-vs-switch
|
|
// #5388
|
|
return fn.apply(self, args);
|
|
}
|
|
|
|
function instantiate(Type, locals) {
|
|
var Constructor = function() {},
|
|
instance, returnedValue;
|
|
|
|
// Check if Type is annotated and use just the given function at n-1 as parameter
|
|
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
|
|
Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
|
|
instance = new Constructor();
|
|
returnedValue = invoke(Type, instance, locals);
|
|
|
|
return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
|
|
}
|
|
|
|
return {
|
|
invoke: invoke,
|
|
instantiate: instantiate,
|
|
get: getService,
|
|
annotate: annotate,
|
|
has: function(name) {
|
|
return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|