@@ -30,7 +30,7 @@ export class CssAnimator {
3030 * @param s collection of events to bind listeners to
3131 * @param fn callback that gets executed
3232 */
33- _addMultipleEventListener ( el : Element , s : string , fn : Function ) : void {
33+ _addMultipleEventListener ( el : Element , s : string , fn : Function ) : void {
3434 let evts = s . split ( ' ' ) ;
3535 for ( let i = 0 , ii = evts . length ; i < ii ; ++ i ) {
3636 el . addEventListener ( evts [ i ] , fn , false ) ;
@@ -44,7 +44,7 @@ export class CssAnimator {
4444 * @param s collection of events to remove
4545 * @param fn callback to remove
4646 */
47- _removeMultipleEventListener ( el : Element , s : string , fn : Function ) : void {
47+ _removeMultipleEventListener ( el : Element , s : string , fn : Function ) : void {
4848 let evts = s . split ( ' ' ) ;
4949 for ( let i = 0 , ii = evts . length ; i < ii ; ++ i ) {
5050 el . removeEventListener ( evts [ i ] , fn , false ) ;
@@ -136,7 +136,7 @@ export class CssAnimator {
136136 * @param element the element to be dispatched as event detail
137137 */
138138 _triggerDOMEvent ( eventType : string , element : Element ) : void {
139- let evt = DOM . createCustomEvent ( eventType , { bubbles : true , cancelable : true , detail : element } ) ;
139+ let evt = DOM . createCustomEvent ( eventType , { bubbles : true , cancelable : true , detail : element } ) ;
140140 DOM . dispatchEvent ( evt ) ;
141141 }
142142
@@ -153,13 +153,13 @@ export class CssAnimator {
153153 return false ;
154154 }
155155
156- if ( ! this . verifyKeyframesExist ) {
156+ if ( ! this . verifyKeyframesExist ) {
157157 return true ;
158158 }
159159
160160 const keyframesRuleType = window . CSSRule . KEYFRAMES_RULE ||
161- window . CSSRule . MOZ_KEYFRAMES_RULE ||
162- window . CSSRule . WEBKIT_KEYFRAMES_RULE ;
161+ window . CSSRule . MOZ_KEYFRAMES_RULE ||
162+ window . CSSRule . WEBKIT_KEYFRAMES_RULE ;
163163
164164 // loop through the stylesheets searching for the keyframes. no cache is
165165 // used in case of dynamic changes to the stylesheets.
@@ -206,7 +206,7 @@ export class CssAnimator {
206206 */
207207 animate ( element : Element | Array < Element > , className : string ) : Promise < boolean > {
208208 if ( Array . isArray ( element ) ) {
209- return Promise . all ( element . map ( ( el ) => {
209+ return Promise . all ( element . map ( ( el ) => {
210210 return this . _performSingleAnimate ( el , className ) ;
211211 } ) ) ;
212212 }
@@ -224,7 +224,7 @@ export class CssAnimator {
224224
225225 return animations . reduce ( ( p , anim ) => {
226226 return p . then ( ( ) => { return this . animate ( anim . element , anim . className ) ; } ) ;
227- } , Promise . resolve ( true ) ) . then ( ( ) => {
227+ } , Promise . resolve ( true ) ) . then ( ( ) => {
228228 this . _triggerDOMEvent ( animationEvent . sequenceDone , null ) ;
229229 } ) ;
230230 }
@@ -294,8 +294,8 @@ export class CssAnimator {
294294
295295 // Step 3.1.3 in case animation done animations are active, add the defined done class to the element
296296 if ( this . useAnimationDoneClasses &&
297- doneClass !== undefined &&
298- doneClass !== null ) {
297+ doneClass !== undefined &&
298+ doneClass !== null ) {
299299 classList . add ( doneClass ) ;
300300 }
301301
@@ -312,7 +312,7 @@ export class CssAnimator {
312312 const cleanupAnimation = ( ) => {
313313 // Step 5: if no animations scheduled cleanup animation classes
314314 const animationNames = this . _getElementAnimationNames ( element ) ;
315- if ( ! this . _animationChangeWithValidKeyframe ( animationNames , prevAnimationNames ) ) {
315+ if ( ! this . _animationChangeWithValidKeyframe ( animationNames , prevAnimationNames ) ) {
316316 classList . remove ( auClassActive ) ;
317317 classList . remove ( auClass ) ;
318318
@@ -326,7 +326,7 @@ export class CssAnimator {
326326 } ;
327327
328328 if ( parent !== null && parent !== undefined &&
329- ( parent . classList . contains ( 'au-stagger' ) || parent . classList . contains ( 'au-stagger-' + direction ) ) ) {
329+ ( parent . classList . contains ( 'au-stagger' ) || parent . classList . contains ( 'au-stagger-' + direction ) ) ) {
330330 const offset = + ( parent . getAttribute ( attrib ) || 0 ) ;
331331 parent . setAttribute ( attrib , offset + 1 ) ;
332332 const delay = this . _getElementAnimationDelay ( parent ) * offset ;
@@ -369,9 +369,10 @@ export class CssAnimator {
369369 * @returns Resolved when the animation is done
370370 */
371371 removeClass ( element : Element , className : string , suppressEvents : boolean = false ) : Promise < boolean > {
372- return new Promise ( ( resolve , reject ) => {
372+ return new Promise ( ( resolve , reject ) => {
373373 let classList = element . classList ;
374374
375+ // if neither the class exists on the element, nor is not currently being added, resolve immediately.
375376 if ( ! classList . contains ( className ) && ! classList . contains ( className + '-add' ) ) {
376377 resolve ( false ) ;
377378 return ;
@@ -381,6 +382,12 @@ export class CssAnimator {
381382 this . _triggerDOMEvent ( animationEvent . removeClassBegin , element ) ;
382383 }
383384
385+ // Step 1: If the 'addClass' animation is in progress, finish it prematurely.
386+ if ( classList . contains ( className + '-add' ) ) {
387+ classList . remove ( className + '-add' ) ;
388+ classList . add ( className ) ;
389+ }
390+
384391 // Step 2: Remove final className, so animation can start
385392 classList . remove ( className ) ;
386393 let prevAnimationNames = this . _getElementAnimationNames ( element ) ;
@@ -408,20 +415,28 @@ export class CssAnimator {
408415 // Step 3.1: Wait for animation to finish
409416 let animEnd ;
410417 this . _addMultipleEventListener ( element , 'webkitAnimationEnd animationend' , animEnd = ( evAnimEnd ) => {
411- if ( ! animHasStarted ) {
418+ if ( ! animHasStarted ) {
412419 return ;
413420 }
414421 if ( evAnimEnd . target !== element ) {
415422 return ;
416423 }
417424
418- // Step 3.1.0: Stop event propagation, bubbling will otherwise prevent parent animation
425+ // Step 3.1.0: Do nothing if a new addClass animation has started and ended the removeClass animation prematurely
426+ if ( ! element . classList . contains ( className + '-remove' ) ) {
427+ resolve ( true ) ;
428+ }
429+
430+ // Step 3.1.1: Stop event propagation, bubbling will otherwise prevent parent animation
419431 evAnimEnd . stopPropagation ( ) ;
420432
421- // Step 3.1.1 Remove -remove suffixed class
433+ // Step 3.1.2: Remove the class
434+ classList . remove ( className ) ;
435+
436+ // Step 3.1.3: Remove -remove suffixed class
422437 classList . remove ( className + '-remove' ) ;
423438
424- // Step 3.1.2 remove animationend listener
439+ // Step 3.1.4: remove animationend listener
425440 evAnimEnd . target . removeEventListener ( evAnimEnd . type , animEnd ) ;
426441
427442 this . isAnimating = false ;
@@ -439,7 +454,7 @@ export class CssAnimator {
439454
440455 // Step 5: if no animations happened cleanup animation classes and remove final class
441456 let animationNames = this . _getElementAnimationNames ( element ) ;
442- if ( ! this . _animationChangeWithValidKeyframe ( animationNames , prevAnimationNames ) ) {
457+ if ( ! this . _animationChangeWithValidKeyframe ( animationNames , prevAnimationNames ) ) {
443458 classList . remove ( className + '-remove' ) ;
444459 classList . remove ( className ) ;
445460
@@ -470,6 +485,12 @@ export class CssAnimator {
470485 this . _triggerDOMEvent ( animationEvent . addClassBegin , element ) ;
471486 }
472487
488+ // Step 1: If the 'removeClass' animation is in progress, finish it prematurely.
489+ if ( classList . contains ( className + '-remove' ) ) {
490+ classList . remove ( className + '-remove' ) ;
491+ classList . remove ( className ) ;
492+ }
493+
473494 // Step 2: setup event to check whether animations started
474495 let animStart ;
475496 let animHasStarted = false ;
@@ -493,23 +514,28 @@ export class CssAnimator {
493514 // Step 2.1: Wait for animation to finish
494515 let animEnd ;
495516 this . _addMultipleEventListener ( element , 'webkitAnimationEnd animationend' , animEnd = ( evAnimEnd ) => {
496- if ( ! animHasStarted ) {
517+ if ( ! animHasStarted ) {
497518 return ;
498519 }
499520 if ( evAnimEnd . target !== element ) {
500521 return ;
501522 }
502523
503- // Step 2.1.0: Stop event propagation, bubbling will otherwise prevent parent animation
524+ // Step 2.1.0: Do nothing if a new removeClass animation has started and ended the addClass animation prematurely
525+ if ( ! element . classList . contains ( className + '-add' ) ) {
526+ resolve ( true ) ;
527+ }
528+
529+ // Step 2.1.1: Stop event propagation, bubbling will otherwise prevent parent animation
504530 evAnimEnd . stopPropagation ( ) ;
505531
506- // Step 2.1.1 : Add final className
532+ // Step 2.1.2 : Add final className
507533 classList . add ( className ) ;
508534
509- // Step 2.1.2 Remove -add suffixed class
535+ // Step 2.1.3: Remove -add suffixed class
510536 classList . remove ( className + '-add' ) ;
511537
512- // Step 2.1.3 remove animationend listener
538+ // Step 2.1.4: remove animationend listener
513539 evAnimEnd . target . removeEventListener ( evAnimEnd . type , animEnd ) ;
514540
515541 this . isAnimating = false ;
@@ -528,7 +554,7 @@ export class CssAnimator {
528554
529555 // Step 4: if no animations happened cleanup animation classes and add final class
530556 let animationNames = this . _getElementAnimationNames ( element ) ;
531- if ( ! this . _animationChangeWithValidKeyframe ( animationNames , prevAnimationNames ) ) {
557+ if ( ! this . _animationChangeWithValidKeyframe ( animationNames , prevAnimationNames ) ) {
532558 classList . remove ( className + '-add' ) ;
533559 classList . add ( className ) ;
534560
0 commit comments