'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.} 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 *
 *   // 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();
 *   });
 * 
*/ /** * @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: * *
 *   var $injector = angular.injector();
 *   expect($injector.get('$injector')).toBe($injector);
 *   expect($injector.invoke(function($injector){
 *     return $injector;
 *   }).toBe($injector);
 * 
* * # 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. * *
 *   // 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){}]);
 * 
* * ## 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. * *
 *   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!');
 *       });
 *     )};
 *
 *   });
 * 
*/ /** * @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 }; } }