diff options
| author | Misko Hevery | 2011-11-02 16:32:46 -0700 | 
|---|---|---|
| committer | Misko Hevery | 2011-11-14 16:39:33 -0800 | 
| commit | bd04316a89a0909e7a4e483839d573ce857f7622 (patch) | |
| tree | 0378c33976ba524b7090a3665cd7729638f1545e /src/service/scope.js | |
| parent | ed36b9da3be338fe9eb36f3eeea901d6f51cd768 (diff) | |
| download | angular.js-bd04316a89a0909e7a4e483839d573ce857f7622.tar.bz2 | |
refactor(services): migrate angular.service -> module
Diffstat (limited to 'src/service/scope.js')
| -rw-r--r-- | src/service/scope.js | 1182 | 
1 files changed, 592 insertions, 590 deletions
diff --git a/src/service/scope.js b/src/service/scope.js index 94c41041..f78ac448 100644 --- a/src/service/scope.js +++ b/src/service/scope.js @@ -24,631 +24,633 @@   * implemented in the same way as watch. Watch requires return of initialization function which   * are expensive to construct.   */ - -angularServiceInject('$rootScope', function($injector, $exceptionHandler){ -  /** -   * @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. -   */ - - -  Scope.prototype = { +function $RootScopeProvider(){ +  this.$get = ['$injector', '$exceptionHandler', +      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]); -        } -        $injector.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) { -                $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) { -        $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) { -            $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 $digest -        if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) { -          while(current !== target && !(next = current.$$nextSibling)) { -            current = current.$parent; -          } -        } -      } while ((current = next)); +    }; + +    // TODO(misko): remove this; +    var scope = new Scope(); +    scope.$service = $injector; +    return scope; + +    function compileToFn(exp, name) { +      var fn = isString(exp) +        ? expressionCompile(exp) +        : exp; +      assertArgFn(fn, name); +      return fn;      } -  }; - -  // TODO(misko): remove this; -  var scope = new Scope(); -  scope.$service = $injector; -  return scope; - -  function compileToFn(exp, name) { -    var fn = isString(exp) -      ? expressionCompile(exp) -      : exp; -    assertArgFn(fn, name); -    return fn; -  } -}, ['$injector', '$exceptionHandler']); +  }]; +}  | 
