@@ -99,6 +99,7 @@ function Scope() {
99
99
this . $destructor = noop ;
100
100
this [ 'this' ] = this . $root = this ;
101
101
this . $$asyncQueue = [ ] ;
102
+ this . $$listeners = { } ;
102
103
}
103
104
104
105
/**
@@ -155,8 +156,8 @@ Scope.prototype = {
155
156
* the scope and its child scopes to be permanently detached from the parent and thus stop
156
157
* participating in model change detection and listener notification by invoking.
157
158
*
158
- * @param {function()= } constructor Constructor function which the scope should behave as .
159
- * @param {curryArguments= } ... Any additional arguments which are curried into the constructor.
159
+ * @param {function()= } Class Constructor function which the scope should be applied to the scope .
160
+ * @param {...* } curryArguments Any additional arguments which are curried into the constructor.
160
161
* See {@link guide/dev_guide.di dependency injection}.
161
162
* @returns {Object } The newly created child scope.
162
163
*
@@ -169,6 +170,7 @@ Scope.prototype = {
169
170
Child . prototype = this ;
170
171
child = new Child ( ) ;
171
172
child [ 'this' ] = child ;
173
+ child . $$listeners = { } ;
172
174
child . $parent = this ;
173
175
child . $id = nextUid ( ) ;
174
176
child . $$asyncQueue = [ ] ;
@@ -392,12 +394,15 @@ Scope.prototype = {
392
394
* scope and its children. Removal also implies that the current scope is eligible for garbage
393
395
* collection.
394
396
*
397
+ * The destructing scope emits an `$destroy` {@link angular.scope.$emit event}.
398
+ *
395
399
* The `$destroy()` is usually used by directives such as
396
400
* {@link angular.widget.@ng:repeat ng:repeat} for managing the unrolling of the loop.
397
401
*
398
402
*/
399
403
$destroy : function ( ) {
400
404
if ( this . $root == this ) return ; // we can't remove the root node;
405
+ this . $emit ( '$destroy' ) ;
401
406
var parent = this . $parent ;
402
407
403
408
if ( parent . $$childHead == this ) parent . $$childHead = this . $$nextSibling ;
@@ -413,7 +418,7 @@ Scope.prototype = {
413
418
* @function
414
419
*
415
420
* @description
416
- * Executes the expression on the current scope returning the result. Any exceptions in the
421
+ * Executes the ` expression` on the current scope returning the result. Any exceptions in the
417
422
* expression are propagated (uncaught). This is useful when evaluating engular expressions.
418
423
*
419
424
* # Example
@@ -520,9 +525,173 @@ Scope.prototype = {
520
525
} finally {
521
526
this . $root . $digest ( ) ;
522
527
}
528
+ } ,
529
+
530
+ /**
531
+ * @workInProgress
532
+ * @ngdoc function
533
+ * @name angular.scope.$on
534
+ * @function
535
+ *
536
+ * @description
537
+ * Listen on events of a given type. See {@link angular.scope.$emit $emit} for discussion of
538
+ * event life cycle.
539
+ *
540
+ * @param {string } name Event name to listen on.
541
+ * @param {function(event) } listener Function to call when the event is emitted.
542
+ *
543
+ * The event listener function format is: `function(event)`. The `event` object passed into the
544
+ * listener has the following attributes
545
+ * - `targetScope` - {Scope}: the scope on which the event was `$emit`-ed or `$broadcast`-ed.
546
+ * - `currentScope` - {Scope}: the current scope which is handling the event.
547
+ * - `name` - {string}: Name of the event.
548
+ * - `cancel` - {function=}: calling `cancel` function will cancel further event propagation
549
+ * (available only for events that were `$emit`-ed).
550
+ */
551
+ $on : function ( name , listener ) {
552
+ var namedListeners = this . $$listeners [ name ] ;
553
+ if ( ! namedListeners ) {
554
+ this . $$listeners [ name ] = namedListeners = [ ] ;
555
+ }
556
+ namedListeners . push ( listener ) ;
557
+ } ,
558
+
559
+ /**
560
+ * @workInProgress
561
+ * @ngdoc function
562
+ * @name angular.scope.$removeListener
563
+ * @function
564
+ *
565
+ * @description
566
+ * Remove the on listener registered by {@link angular.scope.$on $on}.
567
+ *
568
+ * @param {string } name Event name to remove on.
569
+ * @param {function } listener Function to remove.
570
+ */
571
+ $removeListener : function ( name , listener ) {
572
+ var namedListeners = this . $$listeners [ name ] ;
573
+ var i ;
574
+ if ( namedListeners ) {
575
+ i = namedListeners . indexOf ( listener ) ;
576
+ namedListeners . splice ( i , 1 ) ;
577
+ }
578
+ } ,
579
+
580
+ /**
581
+ * @workInProgress
582
+ * @ngdoc function
583
+ * @name angular.scope.$emit
584
+ * @function
585
+ *
586
+ * @description
587
+ * Dispatches an event `name` upwards through the scope hierarchy notifying the
588
+ * registered {@link angular.scope.$on} listeners.
589
+ *
590
+ * The event life cycle starts at the scope on which `$emit` was called. All
591
+ * {@link angular.scope.$on listeners} listening for `name` event on this scope get notified.
592
+ * Afterwards, the event traverses upwards toward the root scope and calls all registered
593
+ * listeners along the way. The event will stop propagating if one of the listeners cancels it.
594
+ *
595
+ * Any exception emmited from the {@link angular.scope.$on listeners} will be passed
596
+ * onto the {@link angular.service.$exceptionHandler $exceptionHandler} service.
597
+ *
598
+ * @param {string } name Event name to emit.
599
+ * @param {...* } args Optional set of arguments which will be passed onto the event listeners.
600
+ */
601
+ $emit : function ( name , args ) {
602
+ var empty = [ ] ,
603
+ namedListeners ,
604
+ canceled = false ,
605
+ scope = this ,
606
+ event = {
607
+ name : name ,
608
+ targetScope : scope ,
609
+ cancel : function ( ) { canceled = true ; }
610
+ } ,
611
+ listenerArgs = concat ( [ event ] , arguments , 1 ) ,
612
+ i , length ;
613
+
614
+ do {
615
+ namedListeners = scope . $$listeners [ name ] || empty ;
616
+ event . currentScope = scope ;
617
+ for ( i = 0 , length = namedListeners . length ; i < length ; i ++ ) {
618
+ try {
619
+ namedListeners [ i ] . apply ( null , listenerArgs ) ;
620
+ if ( canceled ) return ;
621
+ } catch ( e ) {
622
+ scope . $service ( '$exceptionHandler' ) ( e ) ;
623
+ }
624
+ }
625
+ //traverse upwards
626
+ scope = scope . $parent ;
627
+ } while ( scope ) ;
628
+ } ,
629
+
630
+
631
+ /**
632
+ * @workInProgress
633
+ * @ngdoc function
634
+ * @name angular.scope.$broadcast
635
+ * @function
636
+ *
637
+ * @description
638
+ * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
639
+ * registered {@link angular.scope.$on} listeners.
640
+ *
641
+ * The event life cycle starts at the scope on which `$broadcast` was called. All
642
+ * {@link angular.scope.$on listeners} listening for `name` event on this scope get notified.
643
+ * Afterwards, the event propagates to all direct and indirect scopes of the current scope and
644
+ * calls all registered listeners along the way. The event cannot be canceled.
645
+ *
646
+ * Any exception emmited from the {@link angular.scope.$on listeners} will be passed
647
+ * onto the {@link angular.service.$exceptionHandler $exceptionHandler} service.
648
+ *
649
+ * @param {string } name Event name to emit.
650
+ * @param {...* } args Optional set of arguments which will be passed onto the event listeners.
651
+ */
652
+ $broadcast : function ( name , args ) {
653
+ var targetScope = this ,
654
+ currentScope = targetScope ,
655
+ nextScope = targetScope ,
656
+ event = { name : name ,
657
+ targetScope : targetScope } ,
658
+ listenerArgs = concat ( [ event ] , arguments , 1 ) ;
659
+
660
+ //down while you can, then up and next sibling or up and next sibling until back at root
661
+ do {
662
+ currentScope = nextScope ;
663
+ event . currentScope = currentScope ;
664
+ forEach ( currentScope . $$listeners [ name ] , function ( listener ) {
665
+ try {
666
+ listener . apply ( null , listenerArgs ) ;
667
+ } catch ( e ) {
668
+ currentScope . $service ( '$exceptionHandler' ) ( e ) ;
669
+ }
670
+ } ) ;
671
+
672
+ // down or to the right!
673
+ nextScope = currentScope . $$childHead || currentScope . $$nextSibling ;
674
+
675
+ if ( nextScope ) {
676
+ // found child or sibling
677
+ continue ;
678
+ }
679
+
680
+ // we have to restore nextScope and go up!
681
+ nextScope = currentScope ;
682
+
683
+ while ( ! nextScope . $$nextSibling && ( nextScope != targetScope ) ) {
684
+ nextScope = nextScope . $parent ;
685
+ }
686
+
687
+ if ( nextScope != targetScope ) {
688
+ nextScope = nextScope . $$nextSibling ;
689
+ }
690
+ } while ( nextScope != targetScope ) ;
523
691
}
524
692
} ;
525
693
694
+
526
695
function compileToFn ( exp , name ) {
527
696
var fn = isString ( exp )
528
697
? expressionCompile ( exp )
0 commit comments