@@ -19,12 +19,16 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
1919 return element . data ( RUNNER_STORAGE_KEY ) ;
2020 }
2121
22- this . $get = [ '$$jqLite' , '$rootScope' , '$injector' , '$$AnimateRunner' ,
23- function ( $$jqLite , $rootScope , $injector , $$AnimateRunner ) {
22+ this . $get = [ '$$jqLite' , '$rootScope' , '$injector' , '$$AnimateRunner' , '$$rAFScheduler' ,
23+ function ( $$jqLite , $rootScope , $injector , $$AnimateRunner , $$rAFScheduler ) {
2424
2525 var animationQueue = [ ] ;
2626 var applyAnimationClasses = applyAnimationClassesFactory ( $$jqLite ) ;
2727
28+ var totalPendingClassBasedAnimations = 0 ;
29+ var totalActiveClassBasedAnimations = 0 ;
30+ var classBasedAnimationsQueue = [ ] ;
31+
2832 // TODO(matsko): document the signature in a better way
2933 return function ( element , event , options ) {
3034 options = prepareAnimationOptions ( options ) ;
@@ -53,12 +57,19 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
5357 options . tempClasses = null ;
5458 }
5559
60+ var classBasedIndex ;
61+ if ( ! isStructural ) {
62+ classBasedIndex = totalPendingClassBasedAnimations ;
63+ totalPendingClassBasedAnimations += 1 ;
64+ }
65+
5666 animationQueue . push ( {
5767 // this data is used by the postDigest code and passed into
5868 // the driver step function
5969 element : element ,
6070 classes : classes ,
6171 event : event ,
72+ classBasedIndex : classBasedIndex ,
6273 structural : isStructural ,
6374 options : options ,
6475 beforeStart : beforeStart ,
@@ -73,6 +84,10 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
7384 if ( animationQueue . length > 1 ) return runner ;
7485
7586 $rootScope . $$postDigest ( function ( ) {
87+ totalActiveClassBasedAnimations = totalPendingClassBasedAnimations ;
88+ totalPendingClassBasedAnimations = 0 ;
89+ classBasedAnimationsQueue . length = 0 ;
90+
7691 var animations = [ ] ;
7792 forEach ( animationQueue , function ( entry ) {
7893 // the element was destroyed early on which removed the runner
@@ -87,23 +102,58 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
87102 animationQueue . length = 0 ;
88103
89104 forEach ( groupAnimations ( animations ) , function ( animationEntry ) {
90- // it's important that we apply the `ng-animate` CSS class and the
91- // temporary classes before we do any driver invoking since these
92- // CSS classes may be required for proper CSS detection.
93- animationEntry . beforeStart ( ) ;
94-
95- var operation = invokeFirstDriver ( animationEntry ) ;
96- var triggerAnimationStart = operation && operation . start ; /// TODO(matsko): only recognize operation.start()
97-
98- var closeFn = animationEntry . close ;
99- if ( ! triggerAnimationStart ) {
100- closeFn ( ) ;
105+ if ( animationEntry . structural ) {
106+ triggerAnimationStart ( ) ;
101107 } else {
102- var animationRunner = triggerAnimationStart ( ) ;
103- animationRunner . done ( function ( status ) {
104- closeFn ( ! status ) ;
108+ classBasedAnimationsQueue . push ( {
109+ node : getDomNode ( animationEntry . element ) ,
110+ fn : triggerAnimationStart
105111 } ) ;
106- updateAnimationRunners ( animationEntry , animationRunner ) ;
112+
113+ if ( animationEntry . classBasedIndex === totalActiveClassBasedAnimations - 1 ) {
114+ // we need to sort each of the animations in order of parent to child
115+ // relationships. This ensures that the child classes are applied at the
116+ // right time.
117+ classBasedAnimationsQueue = classBasedAnimationsQueue . sort ( function ( a , b ) {
118+ return b . node . contains ( a . node ) ;
119+ } ) . map ( function ( entry ) {
120+ return entry . fn ;
121+ } ) ;
122+
123+ $$rAFScheduler ( classBasedAnimationsQueue ) ;
124+ }
125+ }
126+
127+ function triggerAnimationStart ( ) {
128+ // it's important that we apply the `ng-animate` CSS class and the
129+ // temporary classes before we do any driver invoking since these
130+ // CSS classes may be required for proper CSS detection.
131+ animationEntry . beforeStart ( ) ;
132+
133+ var startAnimationFn , closeFn = animationEntry . close ;
134+
135+ // in the event that the element was removed before the digest runs or
136+ // during the RAF sequencing then we should not trigger the animation.
137+ var targetElement = animationEntry . anchors
138+ ? ( animationEntry . from . element || animationEntry . to . element )
139+ : animationEntry . element ;
140+
141+ if ( getRunner ( targetElement ) ) {
142+ var operation = invokeFirstDriver ( animationEntry ) ;
143+ if ( operation ) {
144+ startAnimationFn = operation . start ;
145+ }
146+ }
147+
148+ if ( ! startAnimationFn ) {
149+ closeFn ( ) ;
150+ } else {
151+ var animationRunner = startAnimationFn ( ) ;
152+ animationRunner . done ( function ( status ) {
153+ closeFn ( ! status ) ;
154+ } ) ;
155+ updateAnimationRunners ( animationEntry , animationRunner ) ;
156+ }
107157 }
108158 } ) ;
109159 } ) ;
@@ -175,7 +225,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
175225 var lookupKey = from . animationID . toString ( ) ;
176226 if ( ! anchorGroups [ lookupKey ] ) {
177227 var group = anchorGroups [ lookupKey ] = {
178- // TODO(matsko): double-check this code
228+ structural : true ,
179229 beforeStart : function ( ) {
180230 fromAnimation . beforeStart ( ) ;
181231 toAnimation . beforeStart ( ) ;
0 commit comments