diff --git a/src/ng/animate.js b/src/ng/animate.js index f404eccc..e8cca8fc 100644 --- a/src/ng/animate.js +++ b/src/ng/animate.js @@ -234,10 +234,8 @@ var $AnimateProvider = ['$provide', function($provide) { * CSS classes have been set on the element */ setClass : function(element, add, remove, done) { - forEach(element, function (element) { - jqLiteAddClass(element, add); - jqLiteRemoveClass(element, remove); - }); + this.addClass(element, add); + this.removeClass(element, remove); async(done); return noop; }, diff --git a/src/ng/compile.js b/src/ng/compile.js index 4d9c1f68..5caa4145 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -742,14 +742,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { */ $updateClass : function(newClasses, oldClasses) { var toAdd = tokenDifference(newClasses, oldClasses); - var toRemove = tokenDifference(oldClasses, newClasses); - - if(toAdd.length === 0) { - $animate.removeClass(this.$$element, toRemove); - } else if(toRemove.length === 0) { + if (toAdd && toAdd.length) { $animate.addClass(this.$$element, toAdd); - } else { - $animate.setClass(this.$$element, toAdd, toRemove); + } + + var toRemove = tokenDifference(oldClasses, newClasses); + if (toRemove && toRemove.length) { + $animate.removeClass(this.$$element, toRemove); } }, diff --git a/src/ng/directive/ngClass.js b/src/ng/directive/ngClass.js index c9550187..6fd96481 100644 --- a/src/ng/directive/ngClass.js +++ b/src/ng/directive/ngClass.js @@ -56,15 +56,13 @@ function classDirective(name, selector) { function updateClasses (oldClasses, newClasses) { var toAdd = arrayDifference(newClasses, oldClasses); var toRemove = arrayDifference(oldClasses, newClasses); - toRemove = digestClassCounts(toRemove, -1); toAdd = digestClassCounts(toAdd, 1); - - if (toAdd.length === 0) { - $animate.removeClass(element, toRemove); - } else if (toRemove.length === 0) { + toRemove = digestClassCounts(toRemove, -1); + if (toAdd && toAdd.length) { $animate.addClass(element, toAdd); - } else { - $animate.setClass(element, toAdd, toRemove); + } + if (toRemove && toRemove.length) { + $animate.removeClass(element, toRemove); } } diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js index 0208a1ae..a9359410 100644 --- a/src/ngAnimate/animate.js +++ b/src/ngAnimate/animate.js @@ -366,6 +366,7 @@ angular.module('ngAnimate', ['ng']) var noop = angular.noop; var forEach = angular.forEach; var selectors = $animateProvider.$$selectors; + var isArray = angular.isArray; var ELEMENT_NODE = 1; var NG_ANIMATE_STATE = '$$ngAnimateState'; @@ -419,10 +420,14 @@ angular.module('ngAnimate', ['ng']) return classNameFilter.test(className); }; - function blockElementAnimations(element) { + function classBasedAnimationsBlocked(element, setter) { var data = element.data(NG_ANIMATE_STATE) || {}; - data.running = true; - element.data(NG_ANIMATE_STATE, data); + if (setter) { + data.running = true; + data.structural = true; + element.data(NG_ANIMATE_STATE, data); + } + return data.disabled || (data.running && data.structural); } function runAnimationPostDigest(fn) { @@ -435,6 +440,60 @@ angular.module('ngAnimate', ['ng']) }; } + function resolveElementClasses(element, cache, runningAnimations) { + runningAnimations = runningAnimations || {}; + var map = {}; + + forEach(cache.add, function(className) { + if (className && className.length) { + map[className] = map[className] || 0; + map[className]++; + } + }); + + forEach(cache.remove, function(className) { + if (className && className.length) { + map[className] = map[className] || 0; + map[className]--; + } + }); + + var lookup = []; + forEach(runningAnimations, function(data, selector) { + forEach(selector.split(' '), function(s) { + lookup[s]=data; + }); + }); + + var toAdd = [], toRemove = []; + forEach(map, function(status, className) { + var hasClass = element.hasClass(className); + var matchingAnimation = lookup[className] || {}; + + // When addClass and removeClass is called then $animate will check to + // see if addClass and removeClass cancel each other out. When there are + // more calls to removeClass than addClass then the count falls below 0 + // and then the removeClass animation will be allowed. Otherwise if the + // count is above 0 then that means an addClass animation will commence. + // Once an animation is allowed then the code will also check to see if + // there exists any on-going animation that is already adding or remvoing + // the matching CSS class. + if (status < 0) { + //does it have the class or will it have the class + if(hasClass || matchingAnimation.event == 'addClass') { + toRemove.push(className); + } + } else if (status > 0) { + //is the class missing or will it be removed? + if(!hasClass || matchingAnimation.event == 'removeClass') { + toAdd.push(className); + } + } + }); + + return (toAdd.length + toRemove.length) > 0 && [toAdd.join(' '), toRemove.join(' ')]; + } + function lookup(name) { if (name) { var matches = [], @@ -473,18 +532,27 @@ angular.module('ngAnimate', ['ng']) return; } + var classNameAdd; + var classNameRemove; + if (isArray(className)) { + classNameAdd = className[0]; + classNameRemove = className[1]; + if (!classNameAdd) { + className = classNameRemove; + animationEvent = 'removeClass'; + } else if(!classNameRemove) { + className = classNameAdd; + animationEvent = 'addClass'; + } else { + className = classNameAdd + ' ' + classNameRemove; + } + } + var isSetClassOperation = animationEvent == 'setClass'; var isClassBased = isSetClassOperation || animationEvent == 'addClass' || animationEvent == 'removeClass'; - var classNameAdd, classNameRemove; - if (angular.isArray(className)) { - classNameAdd = className[0]; - classNameRemove = className[1]; - className = classNameAdd + ' ' + classNameRemove; - } - var currentClassName = element.attr('class'); var classes = currentClassName + ' ' + className; if (!isAnimatableClassName(classes)) { @@ -665,7 +733,7 @@ angular.module('ngAnimate', ['ng']) parentElement = prepareElement(parentElement); afterElement = prepareElement(afterElement); - blockElementAnimations(element); + classBasedAnimationsBlocked(element, true); $delegate.enter(element, parentElement, afterElement); return runAnimationPostDigest(function() { return performAnimation('enter', 'ng-enter', stripCommentsFromElement(element), parentElement, afterElement, noop, doneCallback); @@ -707,7 +775,7 @@ angular.module('ngAnimate', ['ng']) element = angular.element(element); cancelChildAnimations(element); - blockElementAnimations(element); + classBasedAnimationsBlocked(element, true); this.enabled(false, element); return runAnimationPostDigest(function() { return performAnimation('leave', 'ng-leave', stripCommentsFromElement(element), null, null, function() { @@ -756,7 +824,7 @@ angular.module('ngAnimate', ['ng']) afterElement = prepareElement(afterElement); cancelChildAnimations(element); - blockElementAnimations(element); + classBasedAnimationsBlocked(element, true); $delegate.move(element, parentElement, afterElement); return runAnimationPostDigest(function() { return performAnimation('move', 'ng-move', stripCommentsFromElement(element), parentElement, afterElement, noop, doneCallback); @@ -794,11 +862,7 @@ angular.module('ngAnimate', ['ng']) * @return {function} the animation cancellation function */ addClass : function(element, className, doneCallback) { - element = angular.element(element); - element = stripCommentsFromElement(element); - return performAnimation('addClass', className, element, null, null, function() { - $delegate.addClass(element, className); - }, doneCallback); + return this.setClass(element, className, [], doneCallback); }, /** @@ -832,11 +896,7 @@ angular.module('ngAnimate', ['ng']) * @return {function} the animation cancellation function */ removeClass : function(element, className, doneCallback) { - element = angular.element(element); - element = stripCommentsFromElement(element); - return performAnimation('removeClass', className, element, null, null, function() { - $delegate.removeClass(element, className); - }, doneCallback); + return this.setClass(element, [], className, doneCallback); }, /** @@ -868,11 +928,54 @@ angular.module('ngAnimate', ['ng']) * @return {function} the animation cancellation function */ setClass : function(element, add, remove, doneCallback) { + var STORAGE_KEY = '$$animateClasses'; element = angular.element(element); element = stripCommentsFromElement(element); - return performAnimation('setClass', [add, remove], element, null, null, function() { - $delegate.setClass(element, add, remove); - }, doneCallback); + + if (classBasedAnimationsBlocked(element)) { + return $delegate.setClass(element, add, remove, doneCallback); + } + + add = isArray(add) ? add : add.split(' '); + remove = isArray(remove) ? remove : remove.split(' '); + doneCallback = doneCallback || noop; + + var cache = element.data(STORAGE_KEY); + if (cache) { + cache.callbacks.push(doneCallback); + cache.add = cache.add.concat(add); + cache.remove = cache.remove.concat(remove); + + //the digest cycle will combine all the animations into one function + return; + } else { + element.data(STORAGE_KEY, cache = { + callbacks : [doneCallback], + add : add, + remove : remove + }); + } + + return runAnimationPostDigest(function() { + var cache = element.data(STORAGE_KEY); + var callbacks = cache.callbacks; + + element.removeData(STORAGE_KEY); + + var state = element.data(NG_ANIMATE_STATE) || {}; + var classes = resolveElementClasses(element, cache, state.active); + return !classes + ? $$asyncCallback(onComplete) + : performAnimation('setClass', classes, element, null, null, function() { + $delegate.setClass(element, classes[0], classes[1]); + }, onComplete); + + function onComplete() { + forEach(callbacks, function(fn) { + fn(); + }); + } + }); }, /** @@ -931,6 +1034,7 @@ angular.module('ngAnimate', ['ng']) return noopCancel; } + animationEvent = runner.event; className = runner.className; var elementEvents = angular.element._data(runner.node); elementEvents = elementEvents && elementEvents.events; @@ -939,25 +1043,11 @@ angular.module('ngAnimate', ['ng']) parentElement = afterElement ? afterElement.parent() : element.parent(); } - var ngAnimateState = element.data(NG_ANIMATE_STATE) || {}; - var runningAnimations = ngAnimateState.active || {}; - var totalActiveAnimations = ngAnimateState.totalActive || 0; - var lastAnimation = ngAnimateState.last; - - //only allow animations if the currently running animation is not structural - //or if there is no animation running at all - var skipAnimations; - if (runner.isClassBased) { - skipAnimations = ngAnimateState.running || - ngAnimateState.disabled || - (lastAnimation && !lastAnimation.isClassBased); - } - //skip the animation if animations are disabled, a parent is already being animated, //the element is not currently attached to the document body or then completely close //the animation if any matching animations are not found at all. //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case an animation was found. - if (skipAnimations || animationsDisabled(element, parentElement)) { + if (animationsDisabled(element, parentElement)) { fireDOMOperation(); fireBeforeCallbackAsync(); fireAfterCallbackAsync(); @@ -965,6 +1055,10 @@ angular.module('ngAnimate', ['ng']) return noopCancel; } + var ngAnimateState = element.data(NG_ANIMATE_STATE) || {}; + var runningAnimations = ngAnimateState.active || {}; + var totalActiveAnimations = ngAnimateState.totalActive || 0; + var lastAnimation = ngAnimateState.last; var skipAnimation = false; if (totalActiveAnimations > 0) { var animationsToCancel = []; @@ -1000,9 +1094,6 @@ angular.module('ngAnimate', ['ng']) } } - runningAnimations = ngAnimateState.active || {}; - totalActiveAnimations = ngAnimateState.totalActive || 0; - if (runner.isClassBased && !runner.isSetClassOperation && !skipAnimation) { skipAnimation = (animationEvent == 'addClass') == element.hasClass(className); //opposite of XOR } @@ -1015,6 +1106,9 @@ angular.module('ngAnimate', ['ng']) return noopCancel; } + runningAnimations = ngAnimateState.active || {}; + totalActiveAnimations = ngAnimateState.totalActive || 0; + if (animationEvent == 'leave') { //there's no need to ever remove the listener since the element //will be removed (destroyed) after the leave animation ends or @@ -1708,7 +1802,7 @@ angular.module('ngAnimate', ['ng']) function suffixClasses(classes, suffix) { var className = ''; - classes = angular.isArray(classes) ? classes : classes.split(/\s+/); + classes = isArray(classes) ? classes : classes.split(/\s+/); forEach(classes, function(klass, i) { if (klass && klass.length > 0) { className += (i > 0 ? ' ' : '') + klass + suffix; diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 04f6ad99..a011ead5 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -6194,9 +6194,12 @@ describe('$compile', function() { $rootScope.$digest(); data = $animate.queue.shift(); - expect(data.event).toBe('setClass'); + expect(data.event).toBe('addClass'); expect(data.args[1]).toBe('dice'); - expect(data.args[2]).toBe('rice'); + + data = $animate.queue.shift(); + expect(data.event).toBe('removeClass'); + expect(data.args[1]).toBe('rice'); expect(element.hasClass('ice')).toBe(true); expect(element.hasClass('dice')).toBe(true); diff --git a/test/ng/directive/ngClassSpec.js b/test/ng/directive/ngClassSpec.js index e789d141..9d4c92b3 100644 --- a/test/ng/directive/ngClassSpec.js +++ b/test/ng/directive/ngClassSpec.js @@ -391,7 +391,8 @@ describe('ngClass animations', function() { $rootScope.val = 'two'; $rootScope.$digest(); - expect($animate.queue.shift().event).toBe('setClass'); + expect($animate.queue.shift().event).toBe('addClass'); + expect($animate.queue.shift().event).toBe('removeClass'); expect($animate.queue.length).toBe(0); }); }); @@ -506,9 +507,12 @@ describe('ngClass animations', function() { $rootScope.$digest(); item = $animate.queue.shift(); - expect(item.event).toBe('setClass'); + expect(item.event).toBe('addClass'); expect(item.args[1]).toBe('three'); - expect(item.args[2]).toBe('two'); + + item = $animate.queue.shift(); + expect(item.event).toBe('removeClass'); + expect(item.args[1]).toBe('two'); expect($animate.queue.length).toBe(0); }); diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index e674a08b..926368f8 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -112,12 +112,14 @@ describe("ngAnimate", function() { angular.element(document.body).append($rootElement); $animate.addClass(elm1, 'klass'); + $rootScope.$digest(); $animate.triggerReflow(); expect(count).toBe(1); $animate.enabled(false); $animate.addClass(elm1, 'klass2'); + $rootScope.$digest(); $animate.triggerReflow(); expect(count).toBe(1); @@ -126,18 +128,21 @@ describe("ngAnimate", function() { elm1.append(elm2); $animate.addClass(elm2, 'klass'); + $rootScope.$digest(); $animate.triggerReflow(); expect(count).toBe(2); $animate.enabled(false, elm1); $animate.addClass(elm2, 'klass2'); + $rootScope.$digest(); $animate.triggerReflow(); expect(count).toBe(2); var root = angular.element($rootElement[0]); $rootElement.addClass('animated'); $animate.addClass(root, 'klass2'); + $rootScope.$digest(); $animate.triggerReflow(); expect(count).toBe(3); }); @@ -162,6 +167,7 @@ describe("ngAnimate", function() { var elm1 = $compile('
')($rootScope); $animate.addClass(elm1, 'klass2'); + $rootScope.$digest(); expect(count).toBe(0); }); }); @@ -193,6 +199,7 @@ describe("ngAnimate", function() { expect(captured).toBe(false); $animate.addClass(element, 'red'); + $rootScope.$digest(); $animate.triggerReflow(); expect(captured).toBe(true); @@ -200,6 +207,7 @@ describe("ngAnimate", function() { $animate.enabled(false); $animate.addClass(element, 'blue'); + $rootScope.$digest(); $animate.triggerReflow(); expect(captured).toBe(false); @@ -395,6 +403,7 @@ describe("ngAnimate", function() { child.addClass('ng-hide'); expect(child).toBeHidden(); $animate.removeClass(child, 'ng-hide'); + $rootScope.$digest(); if($sniffer.transitions) { $animate.triggerReflow(); expect(child.hasClass('ng-hide-remove')).toBe(true); @@ -412,6 +421,7 @@ describe("ngAnimate", function() { $rootScope.$digest(); expect(child).toBeShown(); $animate.addClass(child, 'ng-hide'); + $rootScope.$digest(); if($sniffer.transitions) { $animate.triggerReflow(); expect(child.hasClass('ng-hide-add')).toBe(true); @@ -450,6 +460,7 @@ describe("ngAnimate", function() { inject(function($animate, $rootScope, $sniffer, $timeout) { child.attr('class','classify no'); $animate.setClass(child, 'yes', 'no'); + $rootScope.$digest(); $animate.triggerReflow(); expect(child.hasClass('yes')).toBe(true); @@ -488,6 +499,7 @@ describe("ngAnimate", function() { inject(function($animate, $rootScope, $sniffer, $timeout) { child.attr('class','classify no'); $animate.setClass(child[0], 'yes', 'no'); + $rootScope.$digest(); $animate.triggerReflow(); expect(child.hasClass('yes')).toBe(true); @@ -529,6 +541,7 @@ describe("ngAnimate", function() { inject(function($animate, $rootScope, $sniffer, $timeout) { child.attr('class','classify no'); $animate.setClass(child, 'yes', 'no'); + $rootScope.$digest(); $animate.triggerReflow(); expect(child.hasClass('yes')).toBe(true); @@ -568,6 +581,7 @@ describe("ngAnimate", function() { //hide $animate.addClass(child, 'ng-hide'); + $rootScope.$digest(); $animate.triggerReflow(); expect(child.attr('class')).toContain('ng-hide-add'); expect(child.attr('class')).toContain('ng-hide-add-active'); @@ -575,6 +589,7 @@ describe("ngAnimate", function() { //show $animate.removeClass(child, 'ng-hide'); + $rootScope.$digest(); $animate.triggerReflow(); expect(child.attr('class')).toContain('ng-hide-remove'); expect(child.attr('class')).toContain('ng-hide-remove-active'); @@ -645,6 +660,7 @@ describe("ngAnimate", function() { //addClass fn = $animate.addClass(child, 'ng-hide'); + $rootScope.$digest(); $animate.triggerReflow(); expect(captures.addClass).toBeUndefined(); @@ -654,6 +670,7 @@ describe("ngAnimate", function() { //removeClass fn = $animate.removeClass(child, 'ng-hide'); + $rootScope.$digest(); $animate.triggerReflow(); expect(captures.removeClass).toBeUndefined(); @@ -664,6 +681,7 @@ describe("ngAnimate", function() { //setClass child.addClass('red'); fn = $animate.setClass(child, 'blue', 'red'); + $rootScope.$digest(); $animate.triggerReflow(); expect(captures.setClass).toBeUndefined(); @@ -695,12 +713,14 @@ describe("ngAnimate", function() { element.text('123'); expect(element.text()).toBe('123'); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); expect(element.text()).toBe('123'); $animate.enabled(true); element.addClass('ng-hide'); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); if($sniffer.transitions) { $animate.triggerReflow(); } @@ -716,6 +736,7 @@ describe("ngAnimate", function() { expect(element).toBeShown(); $animate.addClass(child, 'ng-hide'); + $rootScope.$digest(); if($sniffer.transitions) { expect(child).toBeShown(); } @@ -750,6 +771,8 @@ describe("ngAnimate", function() { child.attr('style', 'width: 20px'); $animate.addClass(child, 'ng-hide'); + $rootScope.$digest(); + $animate.leave(child); $rootScope.$digest(); @@ -772,6 +795,7 @@ describe("ngAnimate", function() { child.addClass('custom-delay ng-hide'); $animate.removeClass(child, 'ng-hide'); + $rootScope.$digest(); if($sniffer.transitions) { $animate.triggerReflow(); browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 }); @@ -805,12 +829,14 @@ describe("ngAnimate", function() { inject(function($animate, $rootScope, $sniffer, $timeout) { $animate.addClass(element, 'hide'); + $rootScope.$digest(); expect(element).toHaveClass('ng-animate'); $animate.triggerReflow(); $animate.removeClass(element, 'hide'); + $rootScope.$digest(); expect(addClassDoneSpy).toHaveBeenCalled(); $animate.triggerReflow(); @@ -836,6 +862,7 @@ describe("ngAnimate", function() { expect(completed).toBe(false); $animate.addClass(child, 'green'); + $rootScope.$digest(); expect(element.hasClass('green')); expect(completed).toBe(false); @@ -865,6 +892,7 @@ describe("ngAnimate", function() { $animate.enabled(false, element); $animate.addClass(element, 'capture'); + $rootScope.$digest(); expect(element.hasClass('capture')).toBe(true); expect(capture).not.toBe(true); }); @@ -876,7 +904,11 @@ describe("ngAnimate", function() { element.append(child); $animate.addClass(child, 'custom-delay'); + $rootScope.$digest(); + $animate.addClass(child, 'custom-long-delay'); + $rootScope.$digest(); + $animate.triggerReflow(); expect(child.hasClass('animation-cancelled')).toBe(false); @@ -888,13 +920,17 @@ describe("ngAnimate", function() { it("should NOT clobber all data on an element when animation is finished", - inject(function($animate) { + inject(function($animate, $rootScope) { child.css('display','none'); element.data('foo', 'bar'); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); + $animate.addClass(element, 'ng-hide'); + $rootScope.$digest(); + expect(element.data('foo')).toEqual('bar'); })); @@ -903,6 +939,7 @@ describe("ngAnimate", function() { inject(function($animate, $rootScope, $compile, $sniffer, $timeout) { $animate.addClass(element, 'custom-delay custom-long-delay'); + $rootScope.$digest(); $animate.triggerReflow(); $timeout.flush(2000); $timeout.flush(20000); @@ -926,6 +963,7 @@ describe("ngAnimate", function() { $rootScope.$digest(); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); if($sniffer.transitions) { browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 }); @@ -1004,6 +1042,7 @@ describe("ngAnimate", function() { expect(element).toBeHidden(); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); if ($sniffer.animations) { $animate.triggerReflow(); browserTrigger(element,'animationend', { timeStamp: Date.now() + 4000, elapsedTime: 4 }); @@ -1029,6 +1068,7 @@ describe("ngAnimate", function() { expect(element).toBeHidden(); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); if ($sniffer.animations) { $animate.triggerReflow(); browserTrigger(element,'animationend', { timeStamp: Date.now() + 6000, elapsedTime: 6 }); @@ -1056,6 +1096,7 @@ describe("ngAnimate", function() { expect(element).toBeHidden(); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); if ($sniffer.transitions) { $animate.triggerReflow(); browserTrigger(element,'animationend', { timeStamp : Date.now() + 20000, elapsedTime: 10 }); @@ -1077,6 +1118,7 @@ describe("ngAnimate", function() { element.addClass('ng-hide'); expect(element).toBeHidden(); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); expect(element).toBeShown(); })); @@ -1093,6 +1135,7 @@ describe("ngAnimate", function() { element.addClass('custom'); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); if($sniffer.animations) { $animate.triggerReflow(); @@ -1102,6 +1145,8 @@ describe("ngAnimate", function() { element.removeClass('ng-hide'); $animate.addClass(element, 'ng-hide'); + $rootScope.$digest(); + expect(element.hasClass('ng-hide-remove')).toBe(false); //added right away if($sniffer.animations) { //cleanup some pending animations @@ -1279,6 +1324,7 @@ describe("ngAnimate", function() { element.addClass('ng-hide'); expect(element).toBeHidden(); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); expect(element).toBeShown(); $animate.enabled(true); @@ -1287,6 +1333,7 @@ describe("ngAnimate", function() { expect(element).toBeHidden(); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); if ($sniffer.transitions) { $animate.triggerReflow(); browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 }); @@ -1310,6 +1357,7 @@ describe("ngAnimate", function() { element.addClass('ng-hide'); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); if ($sniffer.transitions) { $animate.triggerReflow(); @@ -1340,6 +1388,8 @@ describe("ngAnimate", function() { element.addClass('ng-hide'); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); + expect(element).toBeShown(); $animate.enabled(true); @@ -1347,6 +1397,7 @@ describe("ngAnimate", function() { expect(element).toBeHidden(); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); if ($sniffer.transitions) { $animate.triggerReflow(); var now = Date.now(); @@ -1376,6 +1427,7 @@ describe("ngAnimate", function() { element.addClass('ng-hide'); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); $animate.triggerReflow(); @@ -1399,6 +1451,7 @@ describe("ngAnimate", function() { ss.addRule('.on', style); element = $compile(html(''))($rootScope); $animate.addClass(element, 'on'); + $rootScope.$digest(); $animate.triggerReflow(); @@ -1424,6 +1477,7 @@ describe("ngAnimate", function() { expect(element).toBeHidden(); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); if ($sniffer.transitions) { $animate.triggerReflow(); } @@ -1449,6 +1503,7 @@ describe("ngAnimate", function() { element.addClass('ng-hide'); $animate.removeClass(element, 'ng-hide'); + $rootScope.$digest(); if($sniffer.transitions) { $animate.triggerReflow(); @@ -1461,6 +1516,7 @@ describe("ngAnimate", function() { expect(element).toBeShown(); $animate.addClass(element, 'ng-hide'); + $rootScope.$digest(); if($sniffer.transitions) { $animate.triggerReflow(); @@ -1505,12 +1561,14 @@ describe("ngAnimate", function() { element = $compile(html('