aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Angular.js7
-rw-r--r--src/AngularPublic.js6
-rw-r--r--src/Injector.js270
-rw-r--r--src/angular-mocks.js52
-rw-r--r--src/loader.js8
-rw-r--r--src/scenario/Application.js2
-rw-r--r--src/scenario/Runner.js2
-rw-r--r--src/scenario/dsl.js8
-rw-r--r--src/service/compiler.js12
-rw-r--r--src/service/scope.js4
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;
},