aboutsummaryrefslogtreecommitdiffstats
path: root/src/service
diff options
context:
space:
mode:
authorMisko Hevery2011-10-17 16:56:56 -0700
committerMisko Hevery2011-11-14 16:39:31 -0800
commit48697a2b86dbb12ea8de64cc5fece7caf68b321e (patch)
tree1fa50659f0bb5de2640dea2a2e5bb5628f2bb14a /src/service
parent93b777c916ccff243c5a6080bf5f39860ac7bf39 (diff)
downloadangular.js-48697a2b86dbb12ea8de64cc5fece7caf68b321e.tar.bz2
refactor(injector): turn scope into a service
- turn scope into a $rootScope service. - injector is now a starting point for creating angular application. - added inject() method which wraps jasmine its/beforeEach/afterEach, and which allows configuration and injection of services. - refactor tests to use inject() where possible BREAK: - removed angular.scope() method
Diffstat (limited to 'src/service')
-rw-r--r--src/service/cookies.js11
-rw-r--r--src/service/defer.js8
-rw-r--r--src/service/formFactory.js6
-rw-r--r--src/service/location.js14
-rw-r--r--src/service/route.js13
-rw-r--r--src/service/scope.js1199
-rw-r--r--src/service/xhr.bulk.js9
-rw-r--r--src/service/xhr.cache.js2
-rw-r--r--src/service/xhr.js7
9 files changed, 619 insertions, 650 deletions
diff --git a/src/service/cookies.js b/src/service/cookies.js
index a2ccee09..2cd2b9d6 100644
--- a/src/service/cookies.js
+++ b/src/service/cookies.js
@@ -13,9 +13,8 @@
*
* @example
*/
-angularServiceInject('$cookies', function($browser) {
- var rootScope = this,
- cookies = {},
+angularServiceInject('$cookies', function($rootScope, $browser) {
+ var cookies = {},
lastCookies = {},
lastBrowserCookies,
runEval = false;
@@ -27,7 +26,7 @@ angularServiceInject('$cookies', function($browser) {
lastBrowserCookies = currentCookies;
copy(currentCookies, lastCookies);
copy(currentCookies, cookies);
- if (runEval) rootScope.$apply();
+ if (runEval) $rootScope.$apply();
}
})();
@@ -36,7 +35,7 @@ angularServiceInject('$cookies', function($browser) {
//at the end of each eval, push cookies
//TODO: this should happen before the "delayed" watches fire, because if some cookies are not
// strings or browser refuses to store some cookies, we update the model in the push fn.
- this.$watch(push);
+ $rootScope.$watch(push);
return cookies;
@@ -90,4 +89,4 @@ angularServiceInject('$cookies', function($browser) {
}
}
}
-}, ['$browser']);
+}, ['$rootScope', '$browser']);
diff --git a/src/service/defer.js b/src/service/defer.js
index 42f80d25..07c98065 100644
--- a/src/service/defer.js
+++ b/src/service/defer.js
@@ -28,12 +28,10 @@
* @param {*} deferId Token returned by the `$defer` function.
* @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled.
*/
-angularServiceInject('$defer', function($browser) {
- var scope = this;
-
+angularServiceInject('$defer', function($rootScope, $browser) {
function defer(fn, delay) {
return $browser.defer(function() {
- scope.$apply(fn);
+ $rootScope.$apply(fn);
}, delay);
}
@@ -42,4 +40,4 @@ angularServiceInject('$defer', function($browser) {
};
return defer;
-}, ['$browser']);
+}, ['$rootScope', '$browser']);
diff --git a/src/service/formFactory.js b/src/service/formFactory.js
index e7ff42ff..fa6ad201 100644
--- a/src/service/formFactory.js
+++ b/src/service/formFactory.js
@@ -96,7 +96,7 @@
</doc:scenario>
</doc:example>
*/
-angularServiceInject('$formFactory', function() {
+angularServiceInject('$formFactory', function($rootScope) {
/**
@@ -109,7 +109,7 @@ angularServiceInject('$formFactory', function() {
* Each application ({@link guide/dev_guide.scopes.internals root scope}) gets a root form which
* is the top-level parent of all forms.
*/
- formFactory.rootForm = formFactory(this);
+ formFactory.rootForm = formFactory($rootScope);
/**
@@ -132,7 +132,7 @@ angularServiceInject('$formFactory', function() {
return (parent || formFactory.rootForm).$new(FormController);
}
-});
+}, ['$rootScope']);
function propertiesUpdate(widget) {
widget.$valid = !(widget.$invalid =
diff --git a/src/service/location.js b/src/service/location.js
index d1d34e67..c9b76122 100644
--- a/src/service/location.js
+++ b/src/service/location.js
@@ -419,8 +419,8 @@ function locationGetterSetter(property, preprocess) {
*
* For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular Services: Using $location}
*/
-angularServiceInject('$location', function($browser, $sniffer, $locationConfig, $document) {
- var scope = this, currentUrl,
+angularServiceInject('$location', function($rootScope, $browser, $sniffer, $locationConfig, $document) {
+ var currentUrl,
basePath = $browser.baseHref() || '/',
pathPrefix = pathPrefixFromBase(basePath),
hashPrefix = $locationConfig.hashPrefix || '',
@@ -464,7 +464,7 @@ angularServiceInject('$location', function($browser, $sniffer, $locationConfig,
href = href.indexOf(pathPrefix) === 0 ? href.substr(pathPrefix.length) : href;
currentUrl.url(href);
- scope.$apply();
+ $rootScope.$apply();
event.preventDefault();
// hack to work around FF6 bug 684208 when scenario runner clicks on links
window.angular['ff-684208-preventDefault'] = true;
@@ -482,16 +482,16 @@ angularServiceInject('$location', function($browser, $sniffer, $locationConfig,
$browser.onUrlChange(function(newUrl) {
if (currentUrl.absUrl() != newUrl) {
currentUrl.$$parse(newUrl);
- scope.$apply();
+ $rootScope.$apply();
}
});
// update browser
var changeCounter = 0;
- scope.$watch(function() {
+ $rootScope.$watch(function() {
if ($browser.url() != currentUrl.absUrl()) {
changeCounter++;
- scope.$evalAsync(function() {
+ $rootScope.$evalAsync(function() {
$browser.url(currentUrl.absUrl(), currentUrl.$$replace);
currentUrl.$$replace = false;
});
@@ -501,7 +501,7 @@ angularServiceInject('$location', function($browser, $sniffer, $locationConfig,
});
return currentUrl;
-}, ['$browser', '$sniffer', '$locationConfig', '$document']);
+}, ['$rootScope', '$browser', '$sniffer', '$locationConfig', '$document']);
angular.service('$locationConfig', function() {
diff --git a/src/service/route.js b/src/service/route.js
index ddc3df49..3918c251 100644
--- a/src/service/route.js
+++ b/src/service/route.js
@@ -62,7 +62,7 @@
</doc:scenario>
</doc:example>
*/
-angularServiceInject('$route', function($location, $routeParams) {
+angularServiceInject('$route', function($rootScope, $location, $routeParams) {
/**
* @ngdoc event
* @name angular.service.$route#$beforeRouteChange
@@ -112,8 +112,7 @@ angularServiceInject('$route', function($location, $routeParams) {
var routes = {},
matcher = switchRouteMatcher,
- parentScope = this,
- rootScope = this,
+ parentScope = $rootScope,
dirty = 0,
forceReload = false,
$route = {
@@ -220,7 +219,7 @@ angularServiceInject('$route', function($location, $routeParams) {
}
};
- this.$watch(function() { return dirty + $location.url(); }, updateRoute);
+ $rootScope.$watch(function() { return dirty + $location.url(); }, updateRoute);
return $route;
@@ -262,7 +261,7 @@ angularServiceInject('$route', function($location, $routeParams) {
last.scope && last.scope.$emit('$routeUpdate');
} else {
forceReload = false;
- rootScope.$broadcast('$beforeRouteChange', next, last);
+ $rootScope.$broadcast('$beforeRouteChange', next, last);
last && last.scope && last.scope.$destroy();
$route.current = next;
if (next) {
@@ -280,7 +279,7 @@ angularServiceInject('$route', function($location, $routeParams) {
next.scope = parentScope.$new(Controller);
}
}
- rootScope.$broadcast('$afterRouteChange', next, last);
+ $rootScope.$broadcast('$afterRouteChange', next, last);
}
}
@@ -323,4 +322,4 @@ angularServiceInject('$route', function($location, $routeParams) {
}
-}, ['$location', '$routeParams']);
+}, ['$rootScope', '$location', '$routeParams']);
diff --git a/src/service/scope.js b/src/service/scope.js
index c4b9513b..94c41041 100644
--- a/src/service/scope.js
+++ b/src/service/scope.js
@@ -25,655 +25,630 @@
* are expensive to construct.
*/
-
-function createScope(providers, instanceCache) {
- var scope = new Scope();
- (scope.$service = createInjector(scope, providers, instanceCache)).eager();
- return scope;
-}
-
-
-/**
- * @ngdoc function
- * @name angular.scope
- *
- * @description
- * A root scope can be created by calling {@link angular.scope angular.scope()}. Child scopes
- * are created using the {@link angular.scope.$new $new()} method.
- * (Most scopes are created automatically when compiled HTML template is executed.)
- *
- * Here is a simple scope snippet to show how you can interact with the scope.
- * <pre>
- var scope = angular.scope();
- scope.salutation = 'Hello';
- scope.name = 'World';
-
- expect(scope.greeting).toEqual(undefined);
-
- scope.$watch('name', function() {
- this.greeting = this.salutation + ' ' + this.name + '!';
- }); // initialize the watch
-
- expect(scope.greeting).toEqual(undefined);
- scope.name = 'Misko';
- // still old value, since watches have not been called yet
- expect(scope.greeting).toEqual(undefined);
-
- scope.$digest(); // fire all the watches
- expect(scope.greeting).toEqual('Hello Misko!');
- * </pre>
- *
- * # Inheritance
- * A scope can inherit from a parent scope, as in this example:
- * <pre>
- var parent = angular.scope();
- var child = parent.$new();
-
- parent.salutation = "Hello";
- child.name = "World";
- expect(child.salutation).toEqual('Hello');
-
- child.salutation = "Welcome";
- expect(child.salutation).toEqual('Welcome');
- expect(parent.salutation).toEqual('Hello');
- * </pre>
- *
- * # Dependency Injection
- * See {@link guide/dev_guide.di dependency injection}.
- *
- *
- * @param {Object.<string, function()>=} providers Map of service factory which need to be provided
- * for the current scope. Defaults to {@link angular.service}.
- * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
- * append/override services provided by `providers`. This is handy when unit-testing and having
- * the need to override a default service.
- * @returns {Object} Newly created scope.
- *
- */
-function Scope() {
- this.$id = nextUid();
- this.$$phase = this.$parent = this.$$watchers =
- this.$$nextSibling = this.$$prevSibling =
- this.$$childHead = this.$$childTail = null;
- this.$destructor = noop;
- this['this'] = this.$root = this;
- this.$$asyncQueue = [];
- this.$$listeners = {};
-}
-
-/**
- * @ngdoc property
- * @name angular.scope.$id
- * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for
- * debugging.
- */
-
-/**
- * @ngdoc property
- * @name angular.scope.$service
- * @function
- *
- * @description
- * Provides reference to an instance of {@link angular.injector injector} which can be used to
- * retrieve {@link angular.service services}. In general the use of this api is discouraged,
- * in favor of proper {@link guide/dev_guide.di dependency injection}.
- *
- * @returns {function} {@link angular.injector injector}
- */
-
-/**
- * @ngdoc property
- * @name angular.scope.$root
- * @returns {Scope} The root scope of the current scope hierarchy.
- */
-
-/**
- * @ngdoc property
- * @name angular.scope.$parent
- * @returns {Scope} The parent scope of the current scope.
- */
-
-
-Scope.prototype = {
+angularServiceInject('$rootScope', function($injector, $exceptionHandler){
/**
* @ngdoc function
- * @name angular.scope.$new
- * @function
+ * @name angular.scope
*
* @description
- * Creates a new child {@link angular.scope scope}. The new scope can optionally behave as a
- * controller. The parent scope will propagate the {@link angular.scope.$digest $digest()} and
- * {@link angular.scope.$digest $digest()} events. The scope can be removed from the scope
- * hierarchy using {@link angular.scope.$destroy $destroy()}.
- *
- * {@link angular.scope.$destroy $destroy()} must be called on a scope when it is desired for
- * the scope and its child scopes to be permanently detached from the parent and thus stop
- * participating in model change detection and listener notification by invoking.
- *
- * @param {function()=} Class Constructor function which the scope should be applied to the scope.
- * @param {...*} curryArguments Any additional arguments which are curried into the constructor.
- * See {@link guide/dev_guide.di dependency injection}.
- * @returns {Object} The newly created child scope.
+ * A root scope can be created by calling {@link angular.scope angular.scope()}. Child scopes
+ * are created using the {@link angular.scope.$new $new()} method.
+ * (Most scopes are created automatically when compiled HTML template is executed.)
*
- */
- $new: function(Class, curryArguments) {
- var Child = function() {}; // should be anonymous; This is so that when the minifier munges
- // the name it does not become random set of chars. These will then show up as class
- // name in the debugger.
- var child;
- Child.prototype = this;
- child = new Child();
- child['this'] = child;
- child.$$listeners = {};
- child.$parent = this;
- child.$id = nextUid();
- child.$$asyncQueue = [];
- child.$$phase = child.$$watchers =
- child.$$nextSibling = child.$$childHead = child.$$childTail = null;
- child.$$prevSibling = this.$$childTail;
- if (this.$$childHead) {
- this.$$childTail.$$nextSibling = child;
- this.$$childTail = child;
- } else {
- this.$$childHead = this.$$childTail = child;
- }
- // short circuit if we have no class
- if (Class) {
- // can't use forEach, we need speed!
- var ClassPrototype = Class.prototype;
- for(var key in ClassPrototype) {
- child[key] = bind(child, ClassPrototype[key]);
- }
- this.$service.invoke(child, Class, curryArguments);
- }
- return child;
- },
+ * Here is a simple scope snippet to show how you can interact with the scope.
+ * <pre>
+ var scope = angular.scope();
+ scope.salutation = 'Hello';
+ scope.name = 'World';
- /**
- * @ngdoc function
- * @name angular.scope.$watch
- * @function
- *
- * @description
- * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
- *
- * - The `watchExpression` is called on every call to {@link angular.scope.$digest $digest()} and
- * should return the value which will be watched. (Since {@link angular.scope.$digest $digest()}
- * reruns when it detects changes the `watchExpression` can execute multiple times per
- * {@link angular.scope.$digest $digest()} and should be idempotent.)
- * - The `listener` is called only when the value from the current `watchExpression` and the
- * previous call to `watchExpression' are not equal. The inequality is determined according to
- * {@link angular.equals} function. To save the value of the object for later comparison
- * {@link angular.copy} function is used. It also means that watching complex options will
- * have adverse memory and performance implications.
- * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This
- * is achieved by rerunning the watchers until no changes are detected. The rerun iteration
- * limit is 100 to prevent infinity loop deadlock.
- *
- *
- * If you want to be notified whenever {@link angular.scope.$digest $digest} is called,
- * you can register an `watchExpression` function with no `listener`. (Since `watchExpression`,
- * can execute multiple times per {@link angular.scope.$digest $digest} cycle when a change is
- * detected, be prepared for multiple calls to your listener.)
- *
- *
- * # Example
- <pre>
- var scope = angular.scope();
- scope.name = 'misko';
- scope.counter = 0;
-
- expect(scope.counter).toEqual(0);
- scope.$watch('name', function(scope, newValue, oldValue) { counter = counter + 1; });
- expect(scope.counter).toEqual(0);
-
- scope.$digest();
- // no variable change
- expect(scope.counter).toEqual(0);
-
- scope.name = 'adam';
- scope.$digest();
- expect(scope.counter).toEqual(1);
- </pre>
+ expect(scope.greeting).toEqual(undefined);
+
+ scope.$watch('name', function() {
+ this.greeting = this.salutation + ' ' + this.name + '!';
+ }); // initialize the watch
+
+ expect(scope.greeting).toEqual(undefined);
+ scope.name = 'Misko';
+ // still old value, since watches have not been called yet
+ expect(scope.greeting).toEqual(undefined);
+
+ scope.$digest(); // fire all the watches
+ expect(scope.greeting).toEqual('Hello Misko!');
+ * </pre>
*
+ * # Inheritance
+ * A scope can inherit from a parent scope, as in this example:
+ * <pre>
+ var parent = angular.scope();
+ var child = parent.$new();
+
+ parent.salutation = "Hello";
+ child.name = "World";
+ expect(child.salutation).toEqual('Hello');
+
+ child.salutation = "Welcome";
+ expect(child.salutation).toEqual('Welcome');
+ expect(parent.salutation).toEqual('Hello');
+ * </pre>
*
+ * # Dependency Injection
+ * See {@link guide/dev_guide.di dependency injection}.
*
- * @param {(function()|string)} watchExpression Expression that is evaluated on each
- * {@link angular.scope.$digest $digest} cycle. A change in the return value triggers a
- * call to the `listener`.
*
- * - `string`: Evaluated as {@link guide/dev_guide.expressions expression}
- * - `function(scope)`: called with current `scope` as a parameter.
- * @param {(function()|string)=} listener Callback called whenever the return value of
- * the `watchExpression` changes.
+ * @param {Object.<string, function()>=} providers Map of service factory which need to be provided
+ * for the current scope. Defaults to {@link angular.service}.
+ * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
+ * append/override services provided by `providers`. This is handy when unit-testing and having
+ * the need to override a default service.
+ * @returns {Object} Newly created scope.
*
- * - `string`: Evaluated as {@link guide/dev_guide.expressions expression}
- * - `function(scope, newValue, oldValue)`: called with current `scope` an previous and
- * current values as parameters.
- * @returns {function()} Returns a deregistration function for this listener.
*/
- $watch: function(watchExp, listener) {
- var scope = this,
- get = compileToFn(watchExp, 'watch'),
- listenFn = compileToFn(listener || noop, 'listener'),
- array = scope.$$watchers,
- watcher = {
- fn: listenFn,
- last: Number.NaN, // NaN !== NaN. We used this to force $watch to fire on first run.
- get: get,
- exp: watchExp
- };
-
- if (!array) {
- array = scope.$$watchers = [];
- }
- // we use unshift since we use a while loop in $digest for speed.
- // the while loop reads in reverse order.
- array.unshift(watcher);
-
- return function() {
- angularArray.remove(array, watcher);
- };
- },
+ function Scope() {
+ this.$id = nextUid();
+ this.$$phase = this.$parent = this.$$watchers =
+ this.$$nextSibling = this.$$prevSibling =
+ this.$$childHead = this.$$childTail = null;
+ this.$destructor = noop;
+ this['this'] = this.$root = this;
+ this.$$asyncQueue = [];
+ this.$$listeners = {};
+ }
/**
- * @ngdoc function
- * @name angular.scope.$digest
- * @function
- *
- * @description
- * Process all of the {@link angular.scope.$watch watchers} of the current scope and its children.
- * Because a {@link angular.scope.$watch watcher}'s listener can change the model, the
- * `$digest()` keeps calling the {@link angular.scope.$watch watchers} until no more listeners are
- * firing. This means that it is possible to get into an infinite loop. This function will throw
- * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 100.
- *
- * Usually you don't call `$digest()` directly in
- * {@link angular.directive.ng:controller controllers} or in {@link angular.directive directives}.
- * Instead a call to {@link angular.scope.$apply $apply()} (typically from within a
- * {@link angular.directive directive}) will force a `$digest()`.
- *
- * If you want to be notified whenever `$digest()` is called,
- * you can register a `watchExpression` function with {@link angular.scope.$watch $watch()}
- * with no `listener`.
- *
- * You may have a need to call `$digest()` from within unit-tests, to simulate the scope
- * life-cycle.
- *
- * # Example
- <pre>
- var scope = angular.scope();
- scope.name = 'misko';
- scope.counter = 0;
-
- expect(scope.counter).toEqual(0);
- scope.$digest('name', function(scope, newValue, oldValue) { counter = counter + 1; });
- expect(scope.counter).toEqual(0);
-
- scope.$digest();
- // no variable change
- expect(scope.counter).toEqual(0);
-
- scope.name = 'adam';
- scope.$digest();
- expect(scope.counter).toEqual(1);
- </pre>
- *
+ * @ngdoc property
+ * @name angular.scope.$id
+ * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for
+ * debugging.
*/
- $digest: function() {
- var watch, value, last,
- watchers,
- asyncQueue,
- length,
- dirty, ttl = 100,
- next, current, target = this,
- watchLog = [];
-
- if (target.$$phase) {
- throw Error(target.$$phase + ' already in progress');
- }
- do {
- dirty = false;
- current = target;
- do {
- current.$$phase = '$digest';
- asyncQueue = current.$$asyncQueue;
- while(asyncQueue.length) {
- try {
- current.$eval(asyncQueue.shift());
- } catch (e) {
- current.$service('$exceptionHandler')(e);
- }
+
+ Scope.prototype = {
+ /**
+ * @ngdoc function
+ * @name angular.scope.$new
+ * @function
+ *
+ * @description
+ * Creates a new child {@link angular.scope scope}. The new scope can optionally behave as a
+ * controller. The parent scope will propagate the {@link angular.scope.$digest $digest()} and
+ * {@link angular.scope.$digest $digest()} events. The scope can be removed from the scope
+ * hierarchy using {@link angular.scope.$destroy $destroy()}.
+ *
+ * {@link angular.scope.$destroy $destroy()} must be called on a scope when it is desired for
+ * the scope and its child scopes to be permanently detached from the parent and thus stop
+ * participating in model change detection and listener notification by invoking.
+ *
+ * @param {function()=} Class Constructor function which the scope should be applied to the scope.
+ * @param {...*} curryArguments Any additional arguments which are curried into the constructor.
+ * See {@link guide/dev_guide.di dependency injection}.
+ * @returns {Object} The newly created child scope.
+ *
+ */
+ $new: function(Class, curryArguments) {
+ var Child = function() {}; // should be anonymous; This is so that when the minifier munges
+ // the name it does not become random set of chars. These will then show up as class
+ // name in the debugger.
+ var child;
+ Child.prototype = this;
+ child = new Child();
+ child['this'] = child;
+ child.$$listeners = {};
+ child.$parent = this;
+ child.$id = nextUid();
+ child.$$asyncQueue = [];
+ child.$$phase = child.$$watchers =
+ child.$$nextSibling = child.$$childHead = child.$$childTail = null;
+ child.$$prevSibling = this.$$childTail;
+ if (this.$$childHead) {
+ this.$$childTail.$$nextSibling = child;
+ this.$$childTail = child;
+ } else {
+ this.$$childHead = this.$$childTail = child;
+ }
+ // short circuit if we have no class
+ if (Class) {
+ // can't use forEach, we need speed!
+ var ClassPrototype = Class.prototype;
+ for(var key in ClassPrototype) {
+ child[key] = bind(child, ClassPrototype[key]);
}
- if ((watchers = current.$$watchers)) {
- // process our watches
- length = watchers.length;
- while (length--) {
+ $injector.invoke(child, Class, curryArguments);
+ }
+ return child;
+ },
+
+ /**
+ * @ngdoc function
+ * @name angular.scope.$watch
+ * @function
+ *
+ * @description
+ * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
+ *
+ * - The `watchExpression` is called on every call to {@link angular.scope.$digest $digest()} and
+ * should return the value which will be watched. (Since {@link angular.scope.$digest $digest()}
+ * reruns when it detects changes the `watchExpression` can execute multiple times per
+ * {@link angular.scope.$digest $digest()} and should be idempotent.)
+ * - The `listener` is called only when the value from the current `watchExpression` and the
+ * previous call to `watchExpression' are not equal. The inequality is determined according to
+ * {@link angular.equals} function. To save the value of the object for later comparison
+ * {@link angular.copy} function is used. It also means that watching complex options will
+ * have adverse memory and performance implications.
+ * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This
+ * is achieved by rerunning the watchers until no changes are detected. The rerun iteration
+ * limit is 100 to prevent infinity loop deadlock.
+ *
+ *
+ * If you want to be notified whenever {@link angular.scope.$digest $digest} is called,
+ * you can register an `watchExpression` function with no `listener`. (Since `watchExpression`,
+ * can execute multiple times per {@link angular.scope.$digest $digest} cycle when a change is
+ * detected, be prepared for multiple calls to your listener.)
+ *
+ *
+ * # Example
+ <pre>
+ var scope = angular.scope();
+ scope.name = 'misko';
+ scope.counter = 0;
+
+ expect(scope.counter).toEqual(0);
+ scope.$watch('name', function(scope, newValue, oldValue) { counter = counter + 1; });
+ expect(scope.counter).toEqual(0);
+
+ scope.$digest();
+ // no variable change
+ expect(scope.counter).toEqual(0);
+
+ scope.name = 'adam';
+ scope.$digest();
+ expect(scope.counter).toEqual(1);
+ </pre>
+ *
+ *
+ *
+ * @param {(function()|string)} watchExpression Expression that is evaluated on each
+ * {@link angular.scope.$digest $digest} cycle. A change in the return value triggers a
+ * call to the `listener`.
+ *
+ * - `string`: Evaluated as {@link guide/dev_guide.expressions expression}
+ * - `function(scope)`: called with current `scope` as a parameter.
+ * @param {(function()|string)=} listener Callback called whenever the return value of
+ * the `watchExpression` changes.
+ *
+ * - `string`: Evaluated as {@link guide/dev_guide.expressions expression}
+ * - `function(scope, newValue, oldValue)`: called with current `scope` an previous and
+ * current values as parameters.
+ * @returns {function()} Returns a deregistration function for this listener.
+ */
+ $watch: function(watchExp, listener) {
+ var scope = this,
+ get = compileToFn(watchExp, 'watch'),
+ listenFn = compileToFn(listener || noop, 'listener'),
+ array = scope.$$watchers,
+ watcher = {
+ fn: listenFn,
+ last: Number.NaN, // NaN !== NaN. We used this to force $watch to fire on first run.
+ get: get,
+ exp: watchExp
+ };
+
+ if (!array) {
+ array = scope.$$watchers = [];
+ }
+ // we use unshift since we use a while loop in $digest for speed.
+ // the while loop reads in reverse order.
+ array.unshift(watcher);
+
+ return function() {
+ angularArray.remove(array, watcher);
+ };
+ },
+
+ /**
+ * @ngdoc function
+ * @name angular.scope.$digest
+ * @function
+ *
+ * @description
+ * Process all of the {@link angular.scope.$watch watchers} of the current scope and its children.
+ * Because a {@link angular.scope.$watch watcher}'s listener can change the model, the
+ * `$digest()` keeps calling the {@link angular.scope.$watch watchers} until no more listeners are
+ * firing. This means that it is possible to get into an infinite loop. This function will throw
+ * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 100.
+ *
+ * Usually you don't call `$digest()` directly in
+ * {@link angular.directive.ng:controller controllers} or in {@link angular.directive directives}.
+ * Instead a call to {@link angular.scope.$apply $apply()} (typically from within a
+ * {@link angular.directive directive}) will force a `$digest()`.
+ *
+ * If you want to be notified whenever `$digest()` is called,
+ * you can register a `watchExpression` function with {@link angular.scope.$watch $watch()}
+ * with no `listener`.
+ *
+ * You may have a need to call `$digest()` from within unit-tests, to simulate the scope
+ * life-cycle.
+ *
+ * # Example
+ <pre>
+ var scope = angular.scope();
+ scope.name = 'misko';
+ scope.counter = 0;
+
+ expect(scope.counter).toEqual(0);
+ scope.$digest('name', function(scope, newValue, oldValue) { counter = counter + 1; });
+ expect(scope.counter).toEqual(0);
+
+ scope.$digest();
+ // no variable change
+ expect(scope.counter).toEqual(0);
+
+ scope.name = 'adam';
+ scope.$digest();
+ expect(scope.counter).toEqual(1);
+ </pre>
+ *
+ */
+ $digest: function() {
+ var watch, value, last,
+ watchers,
+ asyncQueue,
+ length,
+ dirty, ttl = 100,
+ next, current, target = this,
+ watchLog = [];
+
+ if (target.$$phase) {
+ throw Error(target.$$phase + ' already in progress');
+ }
+ do {
+
+ dirty = false;
+ current = target;
+ do {
+ current.$$phase = '$digest';
+ asyncQueue = current.$$asyncQueue;
+ while(asyncQueue.length) {
try {
- watch = watchers[length];
- // Most common watches are on primitives, in which case we can short
- // circuit it with === operator, only when === fails do we use .equals
- if ((value = watch.get(current)) !== (last = watch.last) && !equals(value, last)) {
- dirty = true;
- watch.last = copy(value);
- watch.fn(current, value, last);
- if (ttl < 5) {
- if (!watchLog[4-ttl]) watchLog[4-ttl] = [];
- if (isFunction(watch.exp)) {
- watchLog[4-ttl].push('fn: ' + (watch.exp.name || watch.exp.toString()));
- } else {
- watchLog[4-ttl].push(watch.exp);
+ current.$eval(asyncQueue.shift());
+ } catch (e) {
+ current.$service('$exceptionHandler')(e);
+ }
+ }
+ if ((watchers = current.$$watchers)) {
+ // process our watches
+ length = watchers.length;
+ while (length--) {
+ try {
+ watch = watchers[length];
+ // Most common watches are on primitives, in which case we can short
+ // circuit it with === operator, only when === fails do we use .equals
+ if ((value = watch.get(current)) !== (last = watch.last) && !equals(value, last)) {
+ dirty = true;
+ watch.last = copy(value);
+ watch.fn(current, value, last);
+ if (ttl < 5) {
+ if (!watchLog[4-ttl]) watchLog[4-ttl] = [];
+ if (isFunction(watch.exp)) {
+ watchLog[4-ttl].push('fn: ' + (watch.exp.name || watch.exp.toString()));
+ } else {
+ watchLog[4-ttl].push(watch.exp);
+ }
}
}
+ } catch (e) {
+ $exceptionHandler(e);
}
- } catch (e) {
- current.$service('$exceptionHandler')(e);
}
}
+
+ current.$$phase = null;
+
+ // Insanity Warning: scope depth-first traversal
+ // yes, this code is a bit crazy, but it works and we have tests to prove it!
+ // this piece should be kept in sync with the traversal in $broadcast
+ if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
+ while(current !== target && !(next = current.$$nextSibling)) {
+ current = current.$parent;
+ }
+ }
+ } while ((current = next));
+
+ if(!(ttl--)) {
+ throw Error('100 $digest() iterations reached. Aborting!\n' +
+ 'Watchers fired in the last 5 iterations: ' + toJson(watchLog));
}
+ } while (dirty);
+ },
+
+ /**
+ * @ngdoc function
+ * @name angular.scope.$destroy
+ * @function
+ *
+ * @description
+ * Remove the current scope (and all of its children) from the parent scope. Removal implies
+ * that calls to {@link angular.scope.$digest $digest()} will no longer propagate to the current
+ * scope and its children. Removal also implies that the current scope is eligible for garbage
+ * collection.
+ *
+ * The destructing scope emits an `$destroy` {@link angular.scope.$emit event}.
+ *
+ * The `$destroy()` is usually used by directives such as
+ * {@link angular.widget.@ng:repeat ng:repeat} for managing the unrolling of the loop.
+ *
+ */
+ $destroy: function() {
+ if (this.$root == this) return; // we can't remove the root node;
+ this.$emit('$destroy');
+ var parent = this.$parent;
+
+ if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
+ if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
+ if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
+ if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
+ },
+
+ /**
+ * @ngdoc function
+ * @name angular.scope.$eval
+ * @function
+ *
+ * @description
+ * Executes the `expression` on the current scope returning the result. Any exceptions in the
+ * expression are propagated (uncaught). This is useful when evaluating engular expressions.
+ *
+ * # Example
+ <pre>
+ var scope = angular.scope();
+ scope.a = 1;
+ scope.b = 2;
+
+ expect(scope.$eval('a+b')).toEqual(3);
+ expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
+ </pre>
+ *
+ * @param {(string|function())=} expression An angular expression to be executed.
+ *
+ * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}.
+ * - `function(scope)`: execute the function with the current `scope` parameter.
+ *
+ * @returns {*} The result of evaluating the expression.
+ */
+ $eval: function(expr) {
+ var fn = isString(expr)
+ ? expressionCompile(expr)
+ : expr || noop;
+ return fn(this);
+ },
+
+ /**
+ * @ngdoc function
+ * @name angular.scope.$evalAsync
+ * @function
+ *
+ * @description
+ * Executes the expression on the current scope at a later point in time.
+ *
+ * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that:
+ *
+ * - it will execute in the current script execution context (before any DOM rendering).
+ * - at least one {@link angular.scope.$digest $digest cycle} will be performed after
+ * `expression` execution.
+ *
+ * Any exceptions from the execution of the expression are forwarded to the
+ * {@link angular.service.$exceptionHandler $exceptionHandler} service.
+ *
+ * @param {(string|function())=} expression An angular expression to be executed.
+ *
+ * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}.
+ * - `function(scope)`: execute the function with the current `scope` parameter.
+ *
+ */
+ $evalAsync: function(expr) {
+ this.$$asyncQueue.push(expr);
+ },
+
+ /**
+ * @ngdoc function
+ * @name angular.scope.$apply
+ * @function
+ *
+ * @description
+ * `$apply()` is used to execute an expression in angular from outside of the angular framework.
+ * (For example from browser DOM events, setTimeout, XHR or third party libraries).
+ * Because we are calling into the angular framework we need to perform proper scope life-cycle
+ * of {@link angular.service.$exceptionHandler exception handling},
+ * {@link angular.scope.$digest executing watches}.
+ *
+ * ## Life cycle
+ *
+ * # Pseudo-Code of `$apply()`
+ function $apply(expr) {
+ try {
+ return $eval(expr);
+ } catch (e) {
+ $exceptionHandler(e);
+ } finally {
+ $root.$digest();
+ }
+ }
+ *
+ *
+ * Scope's `$apply()` method transitions through the following stages:
+ *
+ * 1. The {@link guide/dev_guide.expressions expression} is executed using the
+ * {@link angular.scope.$eval $eval()} method.
+ * 2. Any exceptions from the execution of the expression are forwarded to the
+ * {@link angular.service.$exceptionHandler $exceptionHandler} service.
+ * 3. The {@link angular.scope.$watch watch} listeners are fired immediately after the expression
+ * was executed using the {@link angular.scope.$digest $digest()} method.
+ *
+ *
+ * @param {(string|function())=} exp An angular expression to be executed.
+ *
+ * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}.
+ * - `function(scope)`: execute the function with current `scope` parameter.
+ *
+ * @returns {*} The result of evaluating the expression.
+ */
+ $apply: function(expr) {
+ try {
+ return this.$eval(expr);
+ } catch (e) {
+ $exceptionHandler(e);
+ } finally {
+ this.$root.$digest();
+ }
+ },
+
+ /**
+ * @ngdoc function
+ * @name angular.scope.$on
+ * @function
+ *
+ * @description
+ * Listen on events of a given type. See {@link angular.scope.$emit $emit} for discussion of
+ * event life cycle.
+ *
+ * @param {string} name Event name to listen on.
+ * @param {function(event)} listener Function to call when the event is emitted.
+ * @returns {function()} Returns a deregistration function for this listener.
+ *
+ * The event listener function format is: `function(event)`. The `event` object passed into the
+ * listener has the following attributes
+ * - `targetScope` - {Scope}: the scope on which the event was `$emit`-ed or `$broadcast`-ed.
+ * - `currentScope` - {Scope}: the current scope which is handling the event.
+ * - `name` - {string}: Name of the event.
+ * - `cancel` - {function=}: calling `cancel` function will cancel further event propagation
+ * (available only for events that were `$emit`-ed).
+ */
+ $on: function(name, listener) {
+ var namedListeners = this.$$listeners[name];
+ if (!namedListeners) {
+ this.$$listeners[name] = namedListeners = [];
+ }
+ namedListeners.push(listener);
+
+ return function() {
+ angularArray.remove(namedListeners, listener);
+ };
+ },
+
+
+ /**
+ * @ngdoc function
+ * @name angular.scope.$emit
+ * @function
+ *
+ * @description
+ * Dispatches an event `name` upwards through the scope hierarchy notifying the
+ * registered {@link angular.scope.$on} listeners.
+ *
+ * The event life cycle starts at the scope on which `$emit` was called. All
+ * {@link angular.scope.$on listeners} listening for `name` event on this scope get notified.
+ * Afterwards, the event traverses upwards toward the root scope and calls all registered
+ * listeners along the way. The event will stop propagating if one of the listeners cancels it.
+ *
+ * Any exception emmited from the {@link angular.scope.$on listeners} will be passed
+ * onto the {@link angular.service.$exceptionHandler $exceptionHandler} service.
+ *
+ * @param {string} name Event name to emit.
+ * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
+ */
+ $emit: function(name, args) {
+ var empty = [],
+ namedListeners,
+ canceled = false,
+ scope = this,
+ event = {
+ name: name,
+ targetScope: scope,
+ cancel: function() {canceled = true;}
+ },
+ listenerArgs = concat([event], arguments, 1),
+ i, length;
- current.$$phase = null;
+ do {
+ namedListeners = scope.$$listeners[name] || empty;
+ event.currentScope = scope;
+ for (i=0, length=namedListeners.length; i<length; i++) {
+ try {
+ namedListeners[i].apply(null, listenerArgs);
+ if (canceled) return;
+ } catch (e) {
+ $exceptionHandler(e);
+ }
+ }
+ //traverse upwards
+ scope = scope.$parent;
+ } while (scope);
+ },
+
+
+ /**
+ * @ngdoc function
+ * @name angular.scope.$broadcast
+ * @function
+ *
+ * @description
+ * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
+ * registered {@link angular.scope.$on} listeners.
+ *
+ * The event life cycle starts at the scope on which `$broadcast` was called. All
+ * {@link angular.scope.$on listeners} listening for `name` event on this scope get notified.
+ * Afterwards, the event propagates to all direct and indirect scopes of the current scope and
+ * calls all registered listeners along the way. The event cannot be canceled.
+ *
+ * Any exception emmited from the {@link angular.scope.$on listeners} will be passed
+ * onto the {@link angular.service.$exceptionHandler $exceptionHandler} service.
+ *
+ * @param {string} name Event name to emit.
+ * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
+ */
+ $broadcast: function(name, args) {
+ var target = this,
+ current = target,
+ next = target,
+ event = { name: name,
+ targetScope: target },
+ listenerArgs = concat([event], arguments, 1);
+
+ //down while you can, then up and next sibling or up and next sibling until back at root
+ do {
+ current = next;
+ event.currentScope = current;
+ forEach(current.$$listeners[name], function(listener) {
+ try {
+ listener.apply(null, listenerArgs);
+ } catch(e) {
+ $exceptionHandler(e);
+ }
+ });
// Insanity Warning: scope depth-first traversal
// yes, this code is a bit crazy, but it works and we have tests to prove it!
- // this piece should be kept in sync with the traversal in $broadcast
+ // this piece should be kept in sync with the traversal in $digest
if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
while(current !== target && !(next = current.$$nextSibling)) {
current = current.$parent;
}
}
} while ((current = next));
-
- if(!(ttl--)) {
- throw Error('100 $digest() iterations reached. Aborting!\n' +
- 'Watchers fired in the last 5 iterations: ' + toJson(watchLog));
- }
- } while (dirty);
- },
-
- /**
- * @ngdoc function
- * @name angular.scope.$destroy
- * @function
- *
- * @description
- * Remove the current scope (and all of its children) from the parent scope. Removal implies
- * that calls to {@link angular.scope.$digest $digest()} will no longer propagate to the current
- * scope and its children. Removal also implies that the current scope is eligible for garbage
- * collection.
- *
- * The destructing scope emits an `$destroy` {@link angular.scope.$emit event}.
- *
- * The `$destroy()` is usually used by directives such as
- * {@link angular.widget.@ng:repeat ng:repeat} for managing the unrolling of the loop.
- *
- */
- $destroy: function() {
- if (this.$root == this) return; // we can't remove the root node;
- this.$emit('$destroy');
- var parent = this.$parent;
-
- if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
- if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
- if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
- if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
- },
-
- /**
- * @ngdoc function
- * @name angular.scope.$eval
- * @function
- *
- * @description
- * Executes the `expression` on the current scope returning the result. Any exceptions in the
- * expression are propagated (uncaught). This is useful when evaluating engular expressions.
- *
- * # Example
- <pre>
- var scope = angular.scope();
- scope.a = 1;
- scope.b = 2;
-
- expect(scope.$eval('a+b')).toEqual(3);
- expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
- </pre>
- *
- * @param {(string|function())=} expression An angular expression to be executed.
- *
- * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}.
- * - `function(scope)`: execute the function with the current `scope` parameter.
- *
- * @returns {*} The result of evaluating the expression.
- */
- $eval: function(expr) {
- var fn = isString(expr)
- ? expressionCompile(expr)
- : expr || noop;
- return fn(this);
- },
-
- /**
- * @ngdoc function
- * @name angular.scope.$evalAsync
- * @function
- *
- * @description
- * Executes the expression on the current scope at a later point in time.
- *
- * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that:
- *
- * - it will execute in the current script execution context (before any DOM rendering).
- * - at least one {@link angular.scope.$digest $digest cycle} will be performed after
- * `expression` execution.
- *
- * Any exceptions from the execution of the expression are forwarded to the
- * {@link angular.service.$exceptionHandler $exceptionHandler} service.
- *
- * @param {(string|function())=} expression An angular expression to be executed.
- *
- * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}.
- * - `function(scope)`: execute the function with the current `scope` parameter.
- *
- */
- $evalAsync: function(expr) {
- this.$$asyncQueue.push(expr);
- },
-
- /**
- * @ngdoc function
- * @name angular.scope.$apply
- * @function
- *
- * @description
- * `$apply()` is used to execute an expression in angular from outside of the angular framework.
- * (For example from browser DOM events, setTimeout, XHR or third party libraries).
- * Because we are calling into the angular framework we need to perform proper scope life-cycle
- * of {@link angular.service.$exceptionHandler exception handling},
- * {@link angular.scope.$digest executing watches}.
- *
- * ## Life cycle
- *
- * # Pseudo-Code of `$apply()`
- function $apply(expr) {
- try {
- return $eval(expr);
- } catch (e) {
- $exceptionHandler(e);
- } finally {
- $root.$digest();
- }
- }
- *
- *
- * Scope's `$apply()` method transitions through the following stages:
- *
- * 1. The {@link guide/dev_guide.expressions expression} is executed using the
- * {@link angular.scope.$eval $eval()} method.
- * 2. Any exceptions from the execution of the expression are forwarded to the
- * {@link angular.service.$exceptionHandler $exceptionHandler} service.
- * 3. The {@link angular.scope.$watch watch} listeners are fired immediately after the expression
- * was executed using the {@link angular.scope.$digest $digest()} method.
- *
- *
- * @param {(string|function())=} exp An angular expression to be executed.
- *
- * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}.
- * - `function(scope)`: execute the function with current `scope` parameter.
- *
- * @returns {*} The result of evaluating the expression.
- */
- $apply: function(expr) {
- try {
- return this.$eval(expr);
- } catch (e) {
- this.$service('$exceptionHandler')(e);
- } finally {
- this.$root.$digest();
}
- },
-
- /**
- * @ngdoc function
- * @name angular.scope.$on
- * @function
- *
- * @description
- * Listen on events of a given type. See {@link angular.scope.$emit $emit} for discussion of
- * event life cycle.
- *
- * @param {string} name Event name to listen on.
- * @param {function(event)} listener Function to call when the event is emitted.
- * @returns {function()} Returns a deregistration function for this listener.
- *
- * The event listener function format is: `function(event)`. The `event` object passed into the
- * listener has the following attributes
- * - `targetScope` - {Scope}: the scope on which the event was `$emit`-ed or `$broadcast`-ed.
- * - `currentScope` - {Scope}: the current scope which is handling the event.
- * - `name` - {string}: Name of the event.
- * - `cancel` - {function=}: calling `cancel` function will cancel further event propagation
- * (available only for events that were `$emit`-ed).
- */
- $on: function(name, listener) {
- var namedListeners = this.$$listeners[name];
- if (!namedListeners) {
- this.$$listeners[name] = namedListeners = [];
- }
- namedListeners.push(listener);
-
- return function() {
- angularArray.remove(namedListeners, listener);
- };
- },
-
-
- /**
- * @ngdoc function
- * @name angular.scope.$emit
- * @function
- *
- * @description
- * Dispatches an event `name` upwards through the scope hierarchy notifying the
- * registered {@link angular.scope.$on} listeners.
- *
- * The event life cycle starts at the scope on which `$emit` was called. All
- * {@link angular.scope.$on listeners} listening for `name` event on this scope get notified.
- * Afterwards, the event traverses upwards toward the root scope and calls all registered
- * listeners along the way. The event will stop propagating if one of the listeners cancels it.
- *
- * Any exception emmited from the {@link angular.scope.$on listeners} will be passed
- * onto the {@link angular.service.$exceptionHandler $exceptionHandler} service.
- *
- * @param {string} name Event name to emit.
- * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
- */
- $emit: function(name, args) {
- var empty = [],
- namedListeners,
- canceled = false,
- scope = this,
- event = {
- name: name,
- targetScope: scope,
- cancel: function() {canceled = true;}
- },
- listenerArgs = concat([event], arguments, 1),
- i, length;
-
- do {
- namedListeners = scope.$$listeners[name] || empty;
- event.currentScope = scope;
- for (i=0, length=namedListeners.length; i<length; i++) {
- try {
- namedListeners[i].apply(null, listenerArgs);
- if (canceled) return;
- } catch (e) {
- scope.$service('$exceptionHandler')(e);
- }
- }
- //traverse upwards
- scope = scope.$parent;
- } while (scope);
- },
+ };
+ // TODO(misko): remove this;
+ var scope = new Scope();
+ scope.$service = $injector;
+ return scope;
- /**
- * @ngdoc function
- * @name angular.scope.$broadcast
- * @function
- *
- * @description
- * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
- * registered {@link angular.scope.$on} listeners.
- *
- * The event life cycle starts at the scope on which `$broadcast` was called. All
- * {@link angular.scope.$on listeners} listening for `name` event on this scope get notified.
- * Afterwards, the event propagates to all direct and indirect scopes of the current scope and
- * calls all registered listeners along the way. The event cannot be canceled.
- *
- * Any exception emmited from the {@link angular.scope.$on listeners} will be passed
- * onto the {@link angular.service.$exceptionHandler $exceptionHandler} service.
- *
- * @param {string} name Event name to emit.
- * @param {...*} args Optional set of arguments which will be passed onto the event listeners.
- */
- $broadcast: function(name, args) {
- var target = this,
- current = target,
- next = target,
- event = { name: name,
- targetScope: target },
- listenerArgs = concat([event], arguments, 1);
-
- //down while you can, then up and next sibling or up and next sibling until back at root
- do {
- current = next;
- event.currentScope = current;
- forEach(current.$$listeners[name], function(listener) {
- try {
- listener.apply(null, listenerArgs);
- } catch(e) {
- current.$service('$exceptionHandler')(e);
- }
- });
-
- // Insanity Warning: scope depth-first traversal
- // yes, this code is a bit crazy, but it works and we have tests to prove it!
- // this piece should be kept in sync with the traversal in $digest
- if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
- while(current !== target && !(next = current.$$nextSibling)) {
- current = current.$parent;
- }
- }
- } while ((current = next));
+ function compileToFn(exp, name) {
+ var fn = isString(exp)
+ ? expressionCompile(exp)
+ : exp;
+ assertArgFn(fn, name);
+ return fn;
}
-};
-function compileToFn(exp, name) {
- var fn = isString(exp)
- ? expressionCompile(exp)
- : exp;
- assertArgFn(fn, name);
- return fn;
-}
+}, ['$injector', '$exceptionHandler']);
diff --git a/src/service/xhr.bulk.js b/src/service/xhr.bulk.js
index 33c9384b..bf5a1d95 100644
--- a/src/service/xhr.bulk.js
+++ b/src/service/xhr.bulk.js
@@ -11,9 +11,8 @@
*
* @example
*/
-angularServiceInject('$xhr.bulk', function($xhr, $error, $log){
- var requests = [],
- scope = this;
+angularServiceInject('$xhr.bulk', function($rootScope, $xhr, $error, $log){
+ var requests = [];
function bulkXHR(method, url, post, success, error) {
if (isFunction(post)) {
error = success;
@@ -82,6 +81,6 @@ angularServiceInject('$xhr.bulk', function($xhr, $error, $log){
}
});
};
- this.$watch(function() { bulkXHR.flush(); });
+ $rootScope.$watch(function() { bulkXHR.flush(); });
return bulkXHR;
-}, ['$xhr', '$xhr.error', '$log']);
+}, ['$rootScope', '$xhr', '$xhr.error', '$log']);
diff --git a/src/service/xhr.cache.js b/src/service/xhr.cache.js
index 630caa5b..335c481d 100644
--- a/src/service/xhr.cache.js
+++ b/src/service/xhr.cache.js
@@ -29,7 +29,7 @@
* @param {boolean=} [sync=false] in case of cache hit execute `success` synchronously.
*/
angularServiceInject('$xhr.cache', function($xhr, $defer, $error, $log) {
- var inflight = {}, self = this;
+ var inflight = {};
function cache(method, url, post, success, error, verifyCache, sync) {
if (isFunction(post)) {
if (!isFunction(success)) {
diff --git a/src/service/xhr.js b/src/service/xhr.js
index fe7d42d9..b2a5bdf2 100644
--- a/src/service/xhr.js
+++ b/src/service/xhr.js
@@ -171,8 +171,7 @@
</doc:scenario>
</doc:example>
*/
-angularServiceInject('$xhr', function($browser, $error, $log){
- var rootScope = this;
+angularServiceInject('$xhr', function($rootScope, $browser, $error, $log){
var xhrHeaderDefaults = {
common: {
"Accept": "application/json, text/plain, */*",
@@ -204,7 +203,7 @@ angularServiceInject('$xhr', function($browser, $error, $log){
response = fromJson(response, true);
}
}
- rootScope.$apply(function() {
+ $rootScope.$apply(function() {
if (200 <= code && code < 300) {
success(code, response);
} else if (isFunction(error)) {
@@ -226,4 +225,4 @@ angularServiceInject('$xhr', function($browser, $error, $log){
xhr.defaults = {headers: xhrHeaderDefaults};
return xhr;
-}, ['$browser', '$xhr.error', '$log']);
+}, ['$rootScope', '$browser', '$xhr.error', '$log']);