'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 {} 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(null, 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(null, 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#invoke * @methodOf angular.module.AUTO.$injector * * @description * Invoke the method and supply the method arguments from the `$injector`. * * @param {Object} self The `this` for the invoked method. * @param {function} fn The function to invoke. The function arguments come form the function annotation. * @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(inject(function($provide) {
 *       $provide.service('greet', GreetProvider);
 *     });
 *
 *     it('should greet', inject(function(greet) {
 *       expect(greet('angular')).toEqual('Hello angular!');
 *     }));
 *
 *     it('should allow configuration of salutation', inject(
 *       function(greetProvider) {
 *         greetProvider.salutation('Ahoj');
 *       },
 *       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 cache = {}, providerSuffix = 'Provider', path = [], $injector, loadedModules = new HashMap(); value('$injector', $injector = { get: getService, invoke: invoke, instantiate: instantiate }); value('$provide', { service: supportObject(service), factory: supportObject(factory), value: supportObject(value), decorator: decorator }); loadModules(modulesToLoad); return $injector; //////////////////////////////////// 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 = instantiate(provider); } if (!provider.$get) { throw Error('Provider ' + name + ' must define $get factory method.'); } cache['#' + name + providerSuffix] = provider; } function factory(name, factoryFn) { service(name, { $get:factoryFn }); } function value(name, value) { factory(name, valueFn(value)); } function decorator(name, decorFn) { var origProvider = cache['#' + name + providerSuffix]; if (!origProvider) throw Error("Can't find provider for: " + name); if (cache['#' + name]) throw Error("Service " + name + " already instantiated, can't decorate!"); var orig$get = origProvider.$get; origProvider.$get = function() { var origInstance = $injector.invoke(origProvider, orig$get); return $injector.invoke(null, decorFn, {$delegate: origInstance}); }; } function getService(value) { if (typeof value !== 'string') { throw Error('Service name expected'); } var instanceKey = '#' + value, instance = cache[instanceKey]; if (instance !== undefined || cache.hasOwnProperty(instanceKey)) { return instance; } try { path.unshift(value); var providerKey = instanceKey + providerSuffix, provider = cache[providerKey]; if (provider) { return cache[instanceKey] = invoke(provider, provider.$get); } else { throw Error("Unknown provider for '" + path.join("' <- '") + "'."); } } finally { path.shift(); } } function invoke(self, fn, locals){ var args = [], $inject, length, key; if (typeof fn == 'function') { $inject = inferInjectionArgs(fn); length = $inject.length; } else { if (isArray(fn)) { $inject = fn; length = $inject.length; fn = $inject[--length]; } assertArgFn(fn, 'fn'); } while(length--) { key = $inject[length]; args.unshift( locals && locals.hasOwnProperty(key) ? locals[key] : getService($inject[length], path) ); } // 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(instance, Type, locals) || instance; } function loadModules(modulesToLoad){ forEach(modulesToLoad, function(module) { if (loadedModules.get(module)) return; loadedModules.put(module, true); if (isString(module)) { module = angularModule(module); loadModules(module.requires); for(var invokeQueue = module.invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { var invokeArgs = invokeQueue[i], service = getService(invokeArgs[0]); service[invokeArgs[1]].apply(service, invokeArgs[2]); } } else if (isFunction(module) || isArray(module)) { invoke(null, module); } else { assertArgFn(module, 'module'); } }); } }