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; }, |
