From 7067a8fb0b18d5b5489006e1960cee721a88b4d2 Mon Sep 17 00:00:00 2001 From: Matias Niemelä Date: Thu, 14 Nov 2013 15:36:07 -0500 Subject: fix($animate): ensure the DOM operation isn't run twice Depending on the animations placed on ngClass, the DOM operation may run twice causing a race condition between addClass and removeClass. Depending on what classes are removed and added via $compile this may cause all CSS classes to be removed accidentally from the element being animated. Closes #4949 --- src/ngAnimate/animate.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'src/ngAnimate') diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js index 02706c2b..3b94a651 100644 --- a/src/ngAnimate/animate.js +++ b/src/ngAnimate/animate.js @@ -564,7 +564,7 @@ angular.module('ngAnimate', ['ng']) //the animation if any matching animations are not found at all. //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found. if (animationsDisabled(element, parentElement) || matches.length === 0) { - domOperation(); + fireDOMOperation(); closeAnimation(); return; } @@ -597,7 +597,7 @@ angular.module('ngAnimate', ['ng']) //this would mean that an animation was not allowed so let the existing //animation do it's thing and close this one early if(animations.length === 0) { - domOperation(); + fireDOMOperation(); fireDoneCallbackAsync(); return; } @@ -617,7 +617,7 @@ angular.module('ngAnimate', ['ng']) //is so that the CSS classes present on the element can be properly examined. if((animationEvent == 'addClass' && element.hasClass(className)) || (animationEvent == 'removeClass' && !element.hasClass(className))) { - domOperation(); + fireDOMOperation(); fireDoneCallbackAsync(); return; } @@ -628,6 +628,7 @@ angular.module('ngAnimate', ['ng']) element.data(NG_ANIMATE_STATE, { running:true, + className:className, structural:!isClassBased, animations:animations, done:onBeforeAnimationsComplete @@ -638,7 +639,7 @@ angular.module('ngAnimate', ['ng']) invokeRegisteredAnimationFns(animations, 'before', onBeforeAnimationsComplete); function onBeforeAnimationsComplete(cancelled) { - domOperation(); + fireDOMOperation(); if(cancelled === true) { closeAnimation(); return; @@ -696,6 +697,15 @@ angular.module('ngAnimate', ['ng']) doneCallback && $timeout(doneCallback, 0, false); } + //it is less complicated to use a flag than managing and cancelling + //timeouts containing multiple callbacks. + function fireDOMOperation() { + if(!fireDOMOperation.hasBeenRun) { + fireDOMOperation.hasBeenRun = true; + domOperation(); + } + } + function closeAnimation() { if(!closeAnimation.hasBeenRun) { closeAnimation.hasBeenRun = true; -- cgit v1.2.3