text: 'use strict';
  
  /**
   * DESIGN NOTES
   *
   * The design decisions behind the scope are heavily favored for speed and memory consumption.
   *
   * The typical use of scope is to watch the expressions, which most of the time return the same
   * value as last time so we optimize the operation.
   *
   * Closures construction is expensive in terms of speed as well as memory:
   *   - No closures, instead use prototypical inheritance for API
   *   - Internal state needs to be stored on scope directly, which means that private state is
   *     exposed as $$____ properties
   *
   * Loop operations are optimized by using while(count--) { ... }
   *   - this means that in order to keep the same order of execution as addition we have to add
   *     items to the array at the beginning (unshift) instead of at the end (push)
   *
   * Child scopes are created and removed often
   *   - Using an array would be slow since inserts in middle are expensive so we use linked list
   *
   * There are few watches then a lot of observers. This is why you don't want the observer to be
   * implemented in the same way as watch. Watch requires return of initialization function which
   * are expensive to construct.
   */
  
  
  /**
   * @ngdoc provider
   * @name $rootScopeProvider
   * @description
   *
   * Provider for the $rootScope service.
   */
  
  /**
   * @ngdoc method
   * @name $rootScopeProvider#digestTtl
   * @description
   *
   * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
   * assuming that the model is unstable.
   *
   * The current default is 10 iterations.
   *
   * In complex applications it's possible that the dependencies between `$watch`s will result in
   * several digest iterations. However if an application needs more than the default 10 digest
   * iterations for its model to stabilize then you should investigate what is causing the model to
   * continuously change during the digest.
   *
   * Increasing the TTL could have performance implications, so you should not change it without
   * proper justification.
   *
   * @param {number} limit The number of digest iterations.
   */
  
  
  /**
   * @ngdoc service
   * @name $rootScope
   * @description
   *
   * Every application has a single root {@link ng.$rootScope.Scope scope}.
   * All other scopes are descendant scopes of the root scope. Scopes provide separation
   * between the model and the view, via a mechanism for watching the model for changes.
   * They also provide an event emission/broadcast and subscription facility. See the
   * {@link guide/scope developer guide on scopes}.
   */
  function $RootScopeProvider(){
    var TTL = 10;
    var $rootScopeMinErr = minErr('$rootScope');
    var lastDirtyWatch = null;
  
    this.digestTtl = function(value) {
      if (arguments.length) {
        TTL = value;
      }
      return TTL;
    };
  
    this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
        function( $injector,   $exceptionHandler,   $parse,   $browser) {
  
      /**
       * @ngdoc type
       * @name $rootScope.Scope
       *
       * @description
       * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
       * {@link auto.$injector $injector}. Child scopes are created using the
       * {@link ng.$rootScope.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.
       * ```html
       * <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
       * ```
       *
       * # Inheritance
       * A scope can inherit from a parent scope, as in this example:
       * ```js
           var parent = $rootScope;
           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');
       * ```
       *
       *
       * @param {Object.<string, function()>=} providers Map of service factory which need to be
       *                                       provided for the current scope. Defaults to {@link ng}.
       * @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['this'] = this.$root =  this;
        this.$$destroyed = false;
        this.$$asyncQueue = [];
        this.$$postDigestQueue = [];
        this.$$listeners = {};
        this.$$listenerCount = {};
        this.$$isolateBindings = {};
      }
  
      /**
       * @ngdoc property
       * @name $rootScope.Scope#$id
       * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for
       *   debugging.
       */
  
  
      Scope.prototype = {
        constructor: Scope,
        /**
         * @ngdoc method
         * @name $rootScope.Scope#$new
         * @function
         *
         * @description
         * Creates a new child {@link ng.$rootScope.Scope scope}.
         *
         * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and
         * {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the
         * scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
         *
         * {@link ng.$rootScope.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 {boolean} isolate If true, then the scope does not prototypically inherit from the
         *         parent scope. The scope is isolated, as it can not see parent scope properties.
         *         When creating widgets, it is useful for the widget to not accidentally read parent
         *         state.
         *
         * @returns {Object} The newly created child scope.
         *
         */
        $new: function(isolate) {
          var ChildScope,
              child;
  
          if (isolate) {
            child = new Scope();
            child.$root = this.$root;
            // ensure that there is just one async queue per $rootScope and its children
            child.$$asyncQueue = this.$$asyncQueue;
            child.$$postDigestQueue = this.$$postDigestQueue;
          } else {
            // Only create a child scope class if somebody asks for one,
            // but cache it to allow the VM to optimize lookups.
            if (!this.$$childScopeClass) {
              this.$$childScopeClass = function() {
                this.$$watchers = this.$$nextSibling =
                    this.$$childHead = this.$$childTail = null;
                this.$$listeners = {};
                this.$$listenerCount = {};
                this.$id = nextUid();
                this.$$childScopeClass = null;
              };
              this.$$childScopeClass.prototype = this;
            }
            child = new this.$$childScopeClass();
          }
          child['this'] = child;
          child.$parent = this;
          child.$$prevSibling = this.$$childTail;
          if (this.$$childHead) {
            this.$$childTail.$$nextSibling = child;
            this.$$childTail = child;
          } else {
            this.$$childHead = this.$$childTail = child;
          }
          return child;
        },
  
        /**
         * @ngdoc method
         * @name $rootScope.Scope#$watch
         * @function
         *
         * @description
         * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
         *
         * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
         *   $digest()} and should return the value that will be watched. (Since
         *   {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
         *   `watchExpression` can execute multiple times per
         *   {@link ng.$rootScope.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 (with the exception of the initial run,
         *   see below). The inequality is determined according to
         *   {@link angular.equals} function. To save the value of the object for later comparison,
         *   the {@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 10 to prevent an infinite loop deadlock.
         *
         *
         * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
         * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
         * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
         * change is detected, be prepared for multiple calls to your listener.)
         *
         * After a watcher is registered with the scope, the `listener` fn is called asynchronously
         * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
         * watcher. In rare cases, this is undesirable because the listener is called when the result
         * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
         * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
         * listener was called due to initialization.
         *
         * The example below contains an illustration of using a function as your $watch listener
         *
         *
         * # Example
         * ```js
             // let's assume that scope was dependency injected as the $rootScope
             var scope = $rootScope;
             scope.name = 'misko';
             scope.counter = 0;
  
             expect(scope.counter).toEqual(0);
             scope.$watch('name', function(newValue, oldValue) {
               scope.counter = scope.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);
  
  
  
             // Using a listener function
             var food;
             scope.foodCounter = 0;
             expect(scope.foodCounter).toEqual(0);
             scope.$watch(
               // This is the listener function
               function() { return food; },
               // This is the change handler
               function(newValue, oldValue) {
                 if ( newValue !== oldValue ) {
                   // Only increment the counter if the value changed
                   scope.foodCounter = scope.foodCounter + 1;
                 }
               }
             );
             // No digest has been run so the counter will be zero
             expect(scope.foodCounter).toEqual(0);
  
             // Run the digest but since food has not changed count will still be zero
             scope.$digest();
             expect(scope.foodCounter).toEqual(0);
  
             // Update food and run digest.  Now the counter will increment
             food = 'cheeseburger';
             scope.$digest();
             expect(scope.foodCounter).toEqual(1);
  
         * ```
         *
         *
         *
         * @param {(function()|string)} watchExpression Expression that is evaluated on each
         *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
         *    a call to the `listener`.
         *
         *    - `string`: Evaluated as {@link guide/expression 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/expression expression}
         *    - `function(newValue, oldValue, scope)`: called with current and previous values as
         *      parameters.
         *
         * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
         *     comparing for reference equality.
         * @returns {function()} Returns a deregistration function for this listener.
         */
        $watch: function(watchExp, listener, objectEquality) {
          var scope = this,
              get = compileToFn(watchExp, 'watch'),
              array = scope.$$watchers,
              watcher = {
                fn: listener,
                last: initWatchVal,
                get: get,
                exp: watchExp,
                eq: !!objectEquality
              };
  
          lastDirtyWatch = null;
  
          // in the case user pass string, we need to compile it, do we really need this ?
          if (!isFunction(listener)) {
            var listenFn = compileToFn(listener || noop, 'listener');
            watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
          }
  
          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 deregisterWatch() {
            arrayRemove(array, watcher);
            lastDirtyWatch = null;
          };
        },
  
        /**
         * @ngdoc method
         * @name $rootScope.Scope#$watchGroup
         * @function
         *
         * @description
         * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
         * If any one expression in the collection changes the `listener` is executed.
         *
         * - The items in the `watchCollection` array are observed via standard $watch operation and are examined on every
         *   call to $digest() to see if any items changes.
         * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
         *
         * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
         * watched using {@link ng.$rootScope.Scope#$watch $watch()}
         *
         * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
         *    expression in `watchExpressions` changes
         *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
         *    those of `watchExpression`
         *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
         *    those of `watchExpression`
         *    The `scope` refers to the current scope.
         *
         * @returns {function()} Returns a de-registration function for all listeners.
         */
        $watchGroup: function(watchExpressions, listener) {
          var oldValues = new Array(watchExpressions.length);
          var newValues = new Array(watchExpressions.length);
          var deregisterFns = [];
          var changeCount = 0;
          var self = this;
          var unwatchFlags = new Array(watchExpressions.length);
          var unwatchCount = watchExpressions.length;
  
          forEach(watchExpressions, function (expr, i) {
            var exprFn = $parse(expr);
            deregisterFns.push(self.$watch(exprFn, function (value, oldValue) {
              newValues[i] = value;
              oldValues[i] = oldValue;
              changeCount++;
              if (unwatchFlags[i] && !exprFn.$$unwatch) unwatchCount++;
              if (!unwatchFlags[i] && exprFn.$$unwatch) unwatchCount--;
              unwatchFlags[i] = exprFn.$$unwatch;
            }));
          }, this);
  
          deregisterFns.push(self.$watch(watchGroupFn, function () {
            listener(newValues, oldValues, self);
            if (unwatchCount === 0) {
              watchGroupFn.$$unwatch = true;
            } else {
              watchGroupFn.$$unwatch = false;
            }
          }));
  
          return function deregisterWatchGroup() {
            forEach(deregisterFns, function (fn) {
              fn();
            });
          };
  
          function watchGroupFn() {return changeCount;}
        },
  
  
        /**
         * @ngdoc method
         * @name $rootScope.Scope#$watchCollection
         * @function
         *
         * @description
         * Shallow watches the properties of an object and fires whenever any of the properties change
         * (for arrays, this implies watching the array items; for object maps, this implies watching
         * the properties). If a change is detected, the `listener` callback is fired.
         *
         * - The `obj` collection is observed via standard $watch operation and is examined on every
         *   call to $digest() to see if any items have been added, removed, or moved.
         * - The `listener` is called whenever anything within the `obj` has changed. Examples include
         *   adding, removing, and moving items belonging to an object or array.
         *
         *
         * # Example
         * ```js
            $scope.names = ['igor', 'matias', 'misko', 'james'];
            $scope.dataCount = 4;
  
            $scope.$watchCollection('names', function(newNames, oldNames) {
              $scope.dataCount = newNames.length;
            });
  
            expect($scope.dataCount).toEqual(4);
            $scope.$digest();
  
            //still at 4 ... no changes
            expect($scope.dataCount).toEqual(4);
  
            $scope.names.pop();
            $scope.$digest();
  
            //now there's been a change
            expect($scope.dataCount).toEqual(3);
         * ```
         *
         *
         * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
         *    expression value should evaluate to an object or an array which is observed on each
         *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
         *    collection will trigger a call to the `listener`.
         *
         * @param {function(newCollection, oldCollection, scope)} listener a callback function called
         *    when a change is detected.
         *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
         *    - The `oldCollection` object is a copy of the former collection data.
         *      Due to performance considerations, the`oldCollection` value is computed only if the
         *      `listener` function declares two or more arguments.
         *    - The `scope` argument refers to the current scope.
         *
         * @returns {function()} Returns a de-registration function for this listener. When the
         *    de-registration function is executed, the internal watch operation is terminated.
         */
        $watchCollection: function(obj, listener) {
          var self = this;
          // the current value, updated on each dirty-check run
          var newValue;
          // a shallow copy of the newValue from the last dirty-check run,
          // updated to match newValue during dirty-check run
          var oldValue;
          // a shallow copy of the newValue from when the last change happened
          var veryOldValue;
          // only track veryOldValue if the listener is asking for it
          var trackVeryOldValue = (listener.length > 1);
          var changeDetected = 0;
          var objGetter = $parse(obj);
          var internalArray = [];
          var internalObject = {};
          var initRun = true;
          var oldLength = 0;
  
          function $watchCollectionWatch() {
            newValue = objGetter(self);
            var newLength, key;
  
            if (!isObject(newValue)) { // if primitive
              if (oldValue !== newValue) {
                oldValue = newValue;
                changeDetected++;
              }
            } else if (isArrayLike(newValue)) {
              if (oldValue !== internalArray) {
                // we are transitioning from something which was not an array into array.
                oldValue = internalArray;
                oldLength = oldValue.length = 0;
                changeDetected++;
              }
  
              newLength = newValue.length;
  
              if (oldLength !== newLength) {
                // if lengths do not match we need to trigger change notification
                changeDetected++;
                oldValue.length = oldLength = newLength;
              }
              // copy the items to oldValue and look for changes.
              for (var i = 0; i < newLength; i++) {
                var bothNaN = (oldValue[i] !== oldValue[i]) &&
                    (newValue[i] !== newValue[i]);
                if (!bothNaN && (oldValue[i] !== newValue[i])) {
                  changeDetected++;
                  oldValue[i] = newValue[i];
                }
              }
            } else {
              if (oldValue !== internalObject) {
                // we are transitioning from something which was not an object into object.
                oldValue = internalObject = {};
                oldLength = 0;
                changeDetected++;
              }
              // copy the items to oldValue and look for changes.
              newLength = 0;
              for (key in newValue) {
                if (newValue.hasOwnProperty(key)) {
                  newLength++;
                  if (oldValue.hasOwnProperty(key)) {
                    if (oldValue[key] !== newValue[key]) {
                      changeDetected++;
                      oldValue[key] = newValue[key];
                    }
                  } else {
                    oldLength++;
                    oldValue[key] = newValue[key];
                    changeDetected++;
                  }
                }
              }
              if (oldLength > newLength) {
                // we used to have more keys, need to find them and destroy them.
                changeDetected++;
                for(key in oldValue) {
                  if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) {
                    oldLength--;
                    delete oldValue[key];
                  }
                }
              }
            }
            $watchCollectionWatch.$$unwatch = objGetter.$$unwatch;
            return changeDetected;
          }
  
          function $watchCollectionAction() {
            if (initRun) {
              initRun = false;
              listener(newValue, newValue, self);
            } else {
              listener(newValue, veryOldValue, self);
            }
  
            // make a copy for the next time a collection is changed
            if (trackVeryOldValue) {
              if (!isObject(newValue)) {
                //primitive
                veryOldValue = newValue;
              } else if (isArrayLike(newValue)) {
                veryOldValue = new Array(newValue.length);
                for (var i = 0; i < newValue.length; i++) {
                  veryOldValue[i] = newValue[i];
                }
              } else { // if object
                veryOldValue = {};
                for (var key in newValue) {
                  if (hasOwnProperty.call(newValue, key)) {
                    veryOldValue[key] = newValue[key];
                  }
                }
              }
            }
          }
  
          return this.$watch($watchCollectionWatch, $watchCollectionAction);
        },
  
        /**
         * @ngdoc method
         * @name $rootScope.Scope#$digest
         * @function
         *
         * @description
         * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
         * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
         * the model, the `$digest()` keeps calling the {@link ng.$rootScope.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 10.
         *
         * Usually, you don't call `$digest()` directly in
         * {@link ng.directive:ngController controllers} or in
         * {@link ng.$compileProvider#directive directives}.
         * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
         * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
         *
         * If you want to be notified whenever `$digest()` is called,
         * you can register a `watchExpression` function with
         * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
         *
         * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
         *
         * # Example
         * ```js
             var scope = ...;
             scope.name = 'misko';
             scope.counter = 0;
  
             expect(scope.counter).toEqual(0);
             scope.$watch('name', function(newValue, oldValue) {
               scope.counter = scope.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);
         * ```
         *
         */
        $digest: function() {
          var watch, value, last,
              watchers,
              asyncQueue = this.$$asyncQueue,
              postDigestQueue = this.$$postDigestQueue,
              length,
              dirty, ttl = TTL,
              next, current, target = this,
              watchLog = [],
              stableWatchesCandidates = [],
              logIdx, logMsg, asyncTask;
  
          beginPhase('$digest');
  
          lastDirtyWatch = null;
  
          do { // "while dirty" loop
            dirty = false;
            current = target;
  
            while(asyncQueue.length) {
              try {
                asyncTask = asyncQueue.shift();
                asyncTask.scope.$eval(asyncTask.expression);
              } catch (e) {
                clearPhase();
                $exceptionHandler(e);
              }
              lastDirtyWatch = null;
            }
  
            traverseScopesLoop:
            do { // "traverse the scopes" loop
              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 (watch) {
                      if ((value = watch.get(current)) !== (last = watch.last) &&
                          !(watch.eq
                              ? equals(value, last)
                              : (typeof value == 'number' && typeof last == 'number'
                                 && isNaN(value) && isNaN(last)))) {
                        dirty = true;
                        lastDirtyWatch = watch;
                        watch.last = watch.eq ? copy(value) : value;
                        watch.fn(value, ((last === initWatchVal) ? value : last), current);
                        if (ttl < 5) {
                          logIdx = 4 - ttl;
                          if (!watchLog[logIdx]) watchLog[logIdx] = [];
                          logMsg = (isFunction(watch.exp))
                              ? 'fn: ' + (watch.exp.name || watch.exp.toString())
                              : watch.exp;
                          logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
                          watchLog[logIdx].push(logMsg);
                        }
                        if (watch.get.$$unwatch) stableWatchesCandidates.push({watch: watch, array: watchers});
                      } else if (watch === lastDirtyWatch) {
                        // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
                        // have already been tested.
                        dirty = false;
                        break traverseScopesLoop;
                      }
                    }
                  } catch (e) {
                    clearPhase();
                    $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
              if (!(next = (current.$$childHead ||
                  (current !== target && current.$$nextSibling)))) {
                while(current !== target && !(next = current.$$nextSibling)) {
                  current = current.$parent;
                }
              }
            } while ((current = next));
  
            // `break traverseScopesLoop;` takes us to here
  
            if((dirty || asyncQueue.length) && !(ttl--)) {
              clearPhase();
              throw $rootScopeMinErr('infdig',
                  '{0} $digest() iterations reached. Aborting!\n' +
                  'Watchers fired in the last 5 iterations: {1}',
                  TTL, toJson(watchLog));
            }
  
          } while (dirty || asyncQueue.length);
  
          clearPhase();
  
          while(postDigestQueue.length) {
            try {
              postDigestQueue.shift()();
            } catch (e) {
              $exceptionHandler(e);
            }
          }
  
          for (length = stableWatchesCandidates.length - 1; length >= 0; --length) {
            var candidate = stableWatchesCandidates[length];
            if (candidate.watch.get.$$unwatch) {
              arrayRemove(candidate.array, candidate.watch);
            }
          }
        },
  
  
        /**
         * @ngdoc event
         * @name $rootScope.Scope#$destroy
         * @eventType broadcast on scope being destroyed
         *
         * @description
         * Broadcasted when a scope and its children are being destroyed.
         *
         * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
         * clean up DOM bindings before an element is removed from the DOM.
         */
  
        /**
         * @ngdoc method
         * @name $rootScope.Scope#$destroy
         * @function
         *
         * @description
         * Removes the current scope (and all of its children) from the parent scope. Removal implies
         * that calls to {@link ng.$rootScope.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 `$destroy()` is usually used by directives such as
         * {@link ng.directive:ngRepeat ngRepeat} for managing the
         * unrolling of the loop.
         *
         * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
         * Application code can register a `$destroy` event handler that will give it a chance to
         * perform any necessary cleanup.
         *
         * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
         * clean up DOM bindings before an element is removed from the DOM.
         */
        $destroy: function() {
          // we can't destroy the root scope or a scope that has been already destroyed
          if (this.$$destroyed) return;
          var parent = this.$parent;
  
          this.$broadcast('$destroy');
          this.$$destroyed = true;
          if (this === $rootScope) return;
  
          forEach(this.$$listenerCount, bind(null, decrementListenerCount, this));
  
          // sever all the references to parent scopes (after this cleanup, the current scope should
          // not be retained by any of our references and should be eligible for garbage collection)
          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;
  
  
          // All of the code below is bogus code that works around V8's memory leak via optimized code
          // and inline caches.
          //
          // see:
          // - https://code.google.com/p/v8/issues/detail?id=2073#c26
          // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
          // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
  
          this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
              this.$$childTail = this.$root = null;
  
          // don't reset these to null in case some async task tries to register a listener/watch/task
          this.$$listeners = {};
          this.$$watchers = this.$$asyncQueue = this.$$postDigestQueue = [];
  
          // prevent NPEs since these methods have references to properties we nulled out
          this.$destroy = this.$digest = this.$apply = noop;
          this.$on = this.$watch = this.$watchGroup = function() { return noop; };
        },
  
        /**
         * @ngdoc method
         * @name $rootScope.Scope#$eval
         * @function
         *
         * @description
         * Executes the `expression` on the current scope and returns the result. Any exceptions in
         * the expression are propagated (uncaught). This is useful when evaluating Angular
         * expressions.
         *
         * # Example
         * ```js
             var scope = ng.$rootScope.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);
         * ```
         *
         * @param {(string|function())=} expression An angular expression to be executed.
         *
         *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
         *    - `function(scope)`: execute the function with the current `scope` parameter.
         *
         * @param {(object)=} locals Local variables object, useful for overriding values in scope.
         * @returns {*} The result of evaluating the expression.
         */
        $eval: function(expr, locals) {
          return $parse(expr)(this, locals);
        },
  
        /**
         * @ngdoc method
         * @name $rootScope.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 after the function that scheduled the evaluation (preferably before DOM
         *     rendering).
         *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
         *     `expression` execution.
         *
         * Any exceptions from the execution of the expression are forwarded to the
         * {@link ng.$exceptionHandler $exceptionHandler} service.
         *
         * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
         * will be scheduled. However, it is encouraged to always call code that changes the model
         * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
         *
         * @param {(string|function())=} expression An angular expression to be executed.
         *
         *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
         *    - `function(scope)`: execute the function with the current `scope` parameter.
         *
         */
        $evalAsync: function(expr) {
          // if we are outside of an $digest loop and this is the first time we are scheduling async
          // task also schedule async auto-flush
          if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) {
            $browser.defer(function() {
              if ($rootScope.$$asyncQueue.length) {
                $rootScope.$digest();
              }
            });
          }
  
          this.$$asyncQueue.push({scope: this, expression: expr});
        },
  
        $$postDigest : function(fn) {
          this.$$postDigestQueue.push(fn);
        },
  
        /**
         * @ngdoc method
         * @name $rootScope.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 ng.$exceptionHandler exception handling},
         * {@link ng.$rootScope.Scope#$digest executing watches}.
         *
         * ## Life cycle
         *
         * # Pseudo-Code of `$apply()`
         * ```js
             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/expression expression} is executed using the
         *    {@link ng.$rootScope.Scope#$eval $eval()} method.
         * 2. Any exceptions from the execution of the expression are forwarded to the
         *    {@link ng.$exceptionHandler $exceptionHandler} service.
         * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
         *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
         *
         *
         * @param {(string|function())=} exp An angular expression to be executed.
         *
         *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
         *    - `function(scope)`: execute the function with current `scope` parameter.
         *
         * @returns {*} The result of evaluating the expression.
         */
        $apply: function(expr) {
          try {
            beginPhase('$apply');
            return this.$eval(expr);
          } catch (e) {
            $exceptionHandler(e);
          } finally {
            clearPhase();
            try {
              $rootScope.$digest();
            } catch (e) {
              $exceptionHandler(e);
              throw e;
            }
          }
        },
  
        /**
         * @ngdoc method
         * @name $rootScope.Scope#$on
         * @function
         *
         * @description
         * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
         * discussion of event life cycle.
         *
         * The event listener function format is: `function(event, args...)`. 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 scope that is currently handling the event. Once the
         *     event propagates through the scope hierarchy, this property is set to null.
         *   - `name` - `{string}`: name of the event.
         *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
         *     further event propagation (available only for events that were `$emit`-ed).
         *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
         *     to true.
         *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
         *
         * @param {string} name Event name to listen on.
         * @param {function(event, ...args)} listener Function to call when the event is emitted.
         * @returns {function()} Returns a deregistration function for this listener.
         */
        $on: function(name, listener) {
          var namedListeners = this.$$listeners[name];
          if (!namedListeners) {
            this.$$listeners[name] = namedListeners = [];
          }
          namedListeners.push(listener);
  
          var current = this;
          do {
            if (!current.$$listenerCount[name]) {
              current.$$listenerCount[name] = 0;
            }
            current.$$listenerCount[name]++;
          } while ((current = current.$parent));
  
          var self = this;
          return function() {
            namedListeners[indexOf(namedListeners, listener)] = null;
            decrementListenerCount(self, 1, name);
          };
        },
  
  
        /**
         * @ngdoc method
         * @name $rootScope.Scope#$emit
         * @function
         *
         * @description
         * Dispatches an event `name` upwards through the scope hierarchy notifying the
         * registered {@link ng.$rootScope.Scope#$on} listeners.
         *
         * The event life cycle starts at the scope on which `$emit` was called. All
         * {@link ng.$rootScope.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 emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
         * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
         *
         * @param {string} name Event name to emit.
         * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
         * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
         */
        $emit: function(name, args) {
          var empty = [],
              namedListeners,
              scope = this,
              stopPropagation = false,
              event = {
                name: name,
                targetScope: scope,
                stopPropagation: function() {stopPropagation = true;},
                preventDefault: function() {
                  event.defaultPrevented = true;
                },
                defaultPrevented: false
              },
              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++) {
  
              // if listeners were deregistered, defragment the array
              if (!namedListeners[i]) {
                namedListeners.splice(i, 1);
                i--;
                length--;
                continue;
              }
              try {
                //allow all listeners attached to the current scope to run
                namedListeners[i].apply(null, listenerArgs);
              } catch (e) {
                $exceptionHandler(e);
              }
            }
            //if any listener on the current scope stops propagation, prevent bubbling
            if (stopPropagation) {
              event.currentScope = null;
              return event;
            }
            //traverse upwards
            scope = scope.$parent;
          } while (scope);
  
          event.currentScope = null;
  
          return event;
        },
  
  
        /**
         * @ngdoc method
         * @name $rootScope.Scope#$broadcast
         * @function
         *
         * @description
         * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
         * registered {@link ng.$rootScope.Scope#$on} listeners.
         *
         * The event life cycle starts at the scope on which `$broadcast` was called. All
         * {@link ng.$rootScope.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 emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
         * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
         *
         * @param {string} name Event name to broadcast.
         * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
         * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
         */
        $broadcast: function(name, args) {
          var target = this,
              current = target,
              next = target,
              event = {
                name: name,
                targetScope: target,
                preventDefault: function() {
                  event.defaultPrevented = true;
                },
                defaultPrevented: false
              },
              listenerArgs = concat([event], arguments, 1),
              listeners, i, length;
  
          //down while you can, then up and next sibling or up and next sibling until back at root
          while ((current = next)) {
            event.currentScope = current;
            listeners = current.$$listeners[name] || [];
            for (i=0, length = listeners.length; i<length; i++) {
              // if listeners were deregistered, defragment the array
              if (!listeners[i]) {
                listeners.splice(i, 1);
                i--;
                length--;
                continue;
              }
  
              try {
                listeners[i].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 $digest
            // (though it differs due to having the extra check for $$listenerCount)
            if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
                (current !== target && current.$$nextSibling)))) {
              while(current !== target && !(next = current.$$nextSibling)) {
                current = current.$parent;
              }
            }
          }
  
          event.currentScope = null;
          return event;
        }
      };
  
      var $rootScope = new Scope();
  
      return $rootScope;
  
  
      function beginPhase(phase) {
        if ($rootScope.$$phase) {
          throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
        }
  
        $rootScope.$$phase = phase;
      }
  
      function clearPhase() {
        $rootScope.$$phase = null;
      }
  
      function compileToFn(exp, name) {
        var fn = $parse(exp);
        assertArgFn(fn, name);
        return fn;
      }
  
      function decrementListenerCount(current, count, name) {
        do {
          current.$$listenerCount[name] -= count;
  
          if (current.$$listenerCount[name] === 0) {
            delete current.$$listenerCount[name];
          }
        } while ((current = current.$parent));
      }
  
      /**
       * function used as an initial value for watchers.
       * because it's unique we can easily tell it apart from other values
       */
      function initWatchVal() {}
    }];
  }
  