diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Angular.js | 7 | ||||
| -rw-r--r-- | src/AngularPublic.js | 6 | ||||
| -rw-r--r-- | src/Injector.js | 270 | ||||
| -rw-r--r-- | src/angular-mocks.js | 52 | ||||
| -rw-r--r-- | src/loader.js | 8 | ||||
| -rw-r--r-- | src/scenario/Application.js | 2 | ||||
| -rw-r--r-- | src/scenario/Runner.js | 2 | ||||
| -rw-r--r-- | src/scenario/dsl.js | 8 | ||||
| -rw-r--r-- | src/service/compiler.js | 12 | ||||
| -rw-r--r-- | src/service/scope.js | 4 | 
10 files changed, 226 insertions, 145 deletions
| diff --git a/src/Angular.js b/src/Angular.js index 74443bd1..0f79d363 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -128,7 +128,7 @@ var $$scope           = '$scope',     </pre>   *   * @param {Object|Array} obj Object to iterate over. - * @param {function()} iterator Iterator function. + * @param {Function} iterator Iterator function.   * @param {Object=} context Object to become context (`this`) for the iterator function.   * @returns {Object|Array} Reference to `obj`.   */ @@ -897,12 +897,14 @@ function angularInit(element, bootstrap) {   *   * @param {Element} element DOM element which is the root of angular application.   * @param {Array<String,function>=} modules an array of module declarations. See: {@link angular.module modules} + * @param {angular.module.auta.$injector} the injector;   */  function bootstrap(element, modules) {    element = jqLite(element);    modules = modules || [];    modules.unshift('ng'); -  createInjector(modules).invoke(null, +  var injector = createInjector(modules); +  injector.invoke(      ['$rootScope', '$compile', '$injector', function(scope, compile, injector){        scope.$apply(function() {          element.data('$injector', injector); @@ -910,6 +912,7 @@ function bootstrap(element, modules) {        });      }]    ); +  return injector;  }  function bindJQuery() { diff --git a/src/AngularPublic.js b/src/AngularPublic.js index ef3d3ccb..4973f574 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -30,7 +30,7 @@ function publishExternalAPI(angular){      'equals': equals,      'element': jqLite,      'forEach': forEach, -    'injector': function(){ return createInjector(arguments); }, +    'injector': createInjector,      'noop':noop,      'bind':bind,      'toJson': toJson, @@ -58,8 +58,8 @@ function publishExternalAPI(angular){      angularModule('ngLocale', []).service('$locale', $LocaleProvider);    } -  angularModule('ng', ['ngLocale'], ['$provide', '$injector', -    function ngModule($provide, $injector) { +  angularModule('ng', ['ngLocale'], ['$provide', +    function ngModule($provide) {      // TODO(misko): temporary services to get the compiler working;        $provide.value('$textMarkup', angularTextMarkup);        $provide.value('$attrMarkup', angularAttrMarkup); diff --git a/src/Injector.js b/src/Injector.js index 8326d645..c9901513 100644 --- a/src/Injector.js +++ b/src/Injector.js @@ -10,7 +10,7 @@   * dependency injection (see {@link guide/dev_guide.di dependency injection}).   * - * @param {<string, function()>} modules... A list of module functions or their aliases. See + * @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}.   * @@ -18,11 +18,11 @@   * Typical usage   * <pre>   *   // create an injector - *   var $injector = angular.injector('ng'); + *   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){ + *   $injector.invoke(function($rootScope, $compile, $document){   *     $compile($document)($rootScope);   *     $rootScope.$digest();   *   }); @@ -113,14 +113,26 @@ function inferInjectionArgs(fn) {  /**   * @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 {Object} self The `this` for the invoked method. - * @param {function} fn The function to invoke. The function arguments come form the function annotation. + * @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. @@ -238,29 +250,35 @@ function inferInjectionArgs(fn) {  function createInjector(modulesToLoad) { -  var cache = {}, -      providerSuffix = 'Provider', +  var 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 -  }); +      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); +          })); +    loadModules(modulesToLoad); -  return $injector; +  return instanceInjector;    //////////////////////////////////// +  // $provider +  ////////////////////////////////////    function supportObject(delegate) {      return function(key, value) { @@ -274,124 +292,146 @@ function createInjector(modulesToLoad) {    function service(name, provider) {      if (isFunction(provider)){ -      provider = instantiate(provider); +      provider = providerInjector.instantiate(provider);      }      if (!provider.$get) {        throw Error('Provider ' + name + ' must define $get factory method.');      } -    cache['#' + name + providerSuffix] = provider; +    providerCache[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; +  function decorator(serviceName, decorFn) { +    var origProvider = providerInjector.get(serviceName + providerSuffix), +        orig$get = origProvider.$get; +      origProvider.$get = function() { -      var origInstance = $injector.invoke(origProvider, orig$get); -      return $injector.invoke(null, decorFn, {$delegate: origInstance}); +      var origInstance = instanceInjector.invoke(orig$get, origProvider); +      return instanceInjector.invoke(decorFn, null, {$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); +  //////////////////////////////////// +  // Module Loading +  //////////////////////////////////// +  function loadModules(modulesToLoad){ +    forEach(modulesToLoad, function(module) { +      if (loadedModules.get(module)) return; +      loadedModules.put(module, true); +      if (isString(module)) { +        var moduleFn = angularModule(module); +        loadModules(moduleFn.requires); + +        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 { +          providerInjector.invoke(module); +        } catch (e) { +          if (e.message) e.message += ' from ' + module; +          throw e; +        } +      } else if (isArray(module)) { +        try { +          providerInjector.invoke(module); +        } catch (e) { +          if (e.message) e.message += ' from ' + String(module[module.length - 1]); +          throw e; +        }        } else { -        throw Error("Unknown provider for '" + path.join("' <- '") + "'."); +        assertArgFn(module, 'module');        } -    } 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'); -    } +  //////////////////////////////////// +  // internal Injector +  //////////////////////////////////// -    while(length--) { -      key = $inject[length]; -      args.unshift( -        locals && locals.hasOwnProperty(key) -        ? locals[key] -        : getService($inject[length], path) -      ); -    } +  function createInternalInjector(cache, factory) { -    // 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 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 instantiate(Type, locals){ -    var Constructor = function(){}, -        instance; -    Constructor.prototype = Type.prototype; -    instance = new Constructor(); -    return invoke(instance, Type, locals) || instance; -  } +    function invoke(fn, self, locals){ +      var args = [], +          $injectAnnotation, +          $injectAnnotationIndex, +          key; -  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); +      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'); +      } -        for(var invokeQueue = module.invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { -          var invokeArgs = invokeQueue[i], -              service = getService(invokeArgs[0]); +      while($injectAnnotationIndex--) { +        key = $injectAnnotation[$injectAnnotationIndex]; +        args.unshift(locals && locals.hasOwnProperty(key) ? locals[key] : getService(key)); +      } -          service[invokeArgs[1]].apply(service, invokeArgs[2]); -        } -      } else if (isFunction(module) || isArray(module)) { -        invoke(null, module); -      } else { -        assertArgFn(module, 'module'); +      // 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 +    };    }  } diff --git a/src/angular-mocks.js b/src/angular-mocks.js index f70731d3..d2dbb114 100644 --- a/src/angular-mocks.js +++ b/src/angular-mocks.js @@ -935,16 +935,56 @@ window.jstestdriver && (function(window) {   * @return a method   */  window.jasmine && (function(window) { -  window.inject = function () { + +  function getCurrentSpec() { +    return jasmine.getEnv().currentSpec; +  } + +  function isSpecRunning() { +    var spec = getCurrentSpec(); +    return spec && spec.queue.running; +  } + +  window.module = function() { +    var moduleFns = Array.prototype.slice.call(arguments, 0); +    var stack = Error('Declaration Location').stack; +    return isSpecRunning() ? workFn() : workFn; +    ///////////////////// +    function workFn() { +      var spec = getCurrentSpec(); +      if (spec.$injector) { +        throw Error('Injector already created, can not register a module!'); +      } else { +        var modules = spec.$modules || (spec.$modules = []); +        angular.forEach(moduleFns, function(module) { +          modules.push(module); +        }); +      } +    } +  }; +  window.inject = function() {      var blockFns = Array.prototype.slice.call(arguments, 0); -    return function() { -      var injector = this.$injector; +    var stack = Error('Declaration Location').stack; +    return isSpecRunning() ? workFn() : workFn; +    ///////////////////// +    function workFn() { +      var spec = getCurrentSpec(); +      var modules = spec.$modules || []; +      modules.unshift('ngMock'); +      modules.unshift('ng'); +      var injector = spec.$injector;        if (!injector) { -        injector = this.$injector = angular.injector('ng', 'ngMock'); +        injector = spec.$injector = angular.injector(modules);        } +      console.log('inject', modules)        for(var i = 0, ii = blockFns.length; i < ii; i++) { -        injector.invoke(this, blockFns[i]); +        try { +          injector.invoke(blockFns[i] || angular.noop, this); +        } catch (e) { +          if(e.stack) e.stack +=  '\n' + stack; +          throw e; +        }        } -    }; +    }    }  })(window); diff --git a/src/loader.js b/src/loader.js index ec30ad9a..3d5edd31 100644 --- a/src/loader.js +++ b/src/loader.js @@ -48,7 +48,7 @@ function setupModuleLoader(window) {       * Then you can load your module like this:       *       * <pre> -     * var injector = angular.injector('ng', 'MyModule') +     * var injector = angular.injector(['ng', 'MyModule'])       * </pre>       *       * @param {!string} name The name of the module to create or retrieve. @@ -67,13 +67,11 @@ function setupModuleLoader(window) {            throw Error('No module: ' + name);          } -        function init(fn) { -          invokeQueue.push(['$injector', 'invoke', [null, fn]]); -        } -          /** @type {!Array.<Array.<*>>} */          var invokeQueue = []; +        var init = invokeLater('$injector', 'invoke'); +          /** @type {angular.Module} */          var moduleInstance = {            /** diff --git a/src/scenario/Application.js b/src/scenario/Application.js index ba6bbea7..d3a70569 100644 --- a/src/scenario/Application.js +++ b/src/scenario/Application.js @@ -93,7 +93,7 @@ angular.scenario.Application.prototype.executeAction = function(action) {    angularInit($window.document, function(element) {      element = $window.angular.element(element);      var $injector = element.inheritedData('$injector'); -    $injector.invoke(null, function($browser){ +    $injector.invoke(function($browser){        $browser.notifyWhenNoOutstandingRequests(function() {          action.call(self, $window, _jQuery($window.document));        }); diff --git a/src/scenario/Runner.js b/src/scenario/Runner.js index aeb5196a..cfde1f64 100644 --- a/src/scenario/Runner.js +++ b/src/scenario/Runner.js @@ -163,7 +163,7 @@ angular.scenario.Runner.prototype.createSpecRunner_ = function(scope) {   */  angular.scenario.Runner.prototype.run = function(application) {    var self = this; -  var $root = angular.injector('ng').get('$rootScope'); +  var $root = angular.injector(['ng']).get('$rootScope');    angular.extend($root, this);    angular.forEach(angular.scenario.Runner.prototype, function(fn, name) {      $root[name] = angular.bind(self, fn); diff --git a/src/scenario/dsl.js b/src/scenario/dsl.js index 12cbdb21..bbe29948 100644 --- a/src/scenario/dsl.js +++ b/src/scenario/dsl.js @@ -103,25 +103,25 @@ angular.scenario.dsl('browser', function() {      api.url = function() {        return this.addFutureAction('$location.url()', function($window, $document, done) { -        done(null, $window.angular.injector('ng').get('$location').url()); +        done(null, $window.angular.injector(['ng']).get('$location').url());        });      };      api.path = function() {        return this.addFutureAction('$location.path()', function($window, $document, done) { -        done(null, $window.angular.injector('ng').get('$location').path()); +        done(null, $window.angular.injector(['ng']).get('$location').path());        });      };      api.search = function() {        return this.addFutureAction('$location.search()', function($window, $document, done) { -        done(null, $window.angular.injector('ng').get('$location').search()); +        done(null, $window.angular.injector(['ng']).get('$location').search());        });      };      api.hash = function() {        return this.addFutureAction('$location.hash()', function($window, $document, done) { -        done(null, $window.angular.injector('ng').get('$location').hash()); +        done(null, $window.angular.injector(['ng']).get('$location').hash());        });      }; diff --git a/src/service/compiler.js b/src/service/compiler.js index 8a0dca7d..727f7983 100644 --- a/src/service/compiler.js +++ b/src/service/compiler.js @@ -28,7 +28,7 @@ function $CompileProvider(){            forEach(this.linkFns, function(fn) {              try {                if (isArray(fn) || fn.$inject) { -                $injector.invoke(childScope, fn, locals); +                $injector.invoke(fn, childScope, locals);                } else {                  fn.call(childScope, element);                } @@ -97,7 +97,7 @@ function $CompileProvider(){         * that is a DOM clone of the original template.         *           <pre> -          angular.injector('ng').invoke(null, function($rootScope, $compile) { +          angular.injector(['ng']).invoke(function($rootScope, $compile) {              // Chose one:              // A: compile the entire window.document. @@ -143,8 +143,8 @@ function $CompileProvider(){         * - If you are not asking the linking function to clone the template, create the DOM element(s)         *   before you send them to the compiler and keep this reference around.         *   <pre> -       *     var $injector = angular.injector('ng'); -       *     var scope = $injector.invoke(null, function($rootScope, $compile){ +       *     var $injector = angular.injector(['ng']); +       *     var scope = $injector.invoke(function($rootScope, $compile){         *       var element = $compile('<p>{{total}}</p>')($rootScope);         *     });         *   </pre> @@ -277,7 +277,7 @@ function $CompileProvider(){              descend = false;              directives = false;              var parent = element.parent(); -            template.addLinkFn($injector.invoke(selfApi, widget, locals)); +            template.addLinkFn($injector.invoke(widget, selfApi, locals));              if (parent && parent[0]) {                element = jqLite(parent[0].childNodes[elementIndex]);              } @@ -310,7 +310,7 @@ function $CompileProvider(){                if (fn) {                  element.addClass('ng-directive');                  template.addLinkFn((isArray(fn) || fn.$inject) -                  ? $injector.invoke(selfApi, fn, {$value:value, $element: element}) +                  ? $injector.invoke(fn, selfApi, {$value:value, $element: element})                    : fn.call(selfApi, value, element));                }              }); diff --git a/src/service/scope.js b/src/service/scope.js index 2836b42e..a19bf83e 100644 --- a/src/service/scope.js +++ b/src/service/scope.js @@ -50,7 +50,7 @@ function $RootScopeProvider(){       *       * Here is a simple scope snippet to show how you can interact with the scope.       * <pre> -        angular.injector(function($rootScope) { +        angular.injector(['ng']).invoke(function($rootScope) {             var scope = $rootScope.$new();             scope.salutation = 'Hello';             scope.name = 'World'; @@ -168,7 +168,7 @@ function $RootScopeProvider(){            for(var key in ClassPrototype) {              child[key] = bind(child, ClassPrototype[key]);            } -          $injector.invoke(child, Class, curryArguments); +          $injector.invoke(Class, child, curryArguments);          }          return child;        }, | 
