mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-06-10 15:28:51 +08:00
feat($animate): coalesce concurrent class-based animations within a digest loop
All class-based animation methods (addClass, removeClass and setClass) on $animate
are now processed after the next digest occurs. This fix prevents any sequencing
errors from occuring from excessive calls to $animate.addClass, $animate.remoteClass
or $animate.setClass.
BREAKING CHANGE
$animate.addClass, $animate.removeClass and $animate.setClass will no longer start the animation
right after being called in the directive code. The animation will only commence once a digest
has passed. This means that all animation-related testing code requires an extra digest to kick
off the animation.
```js
//before this fix
$animate.addClass(element, 'super');
expect(element).toHaveClass('super');
//now
$animate.addClass(element, 'super');
$rootScope.$digest();
expect(element).toHaveClass('super');
```
$animate will also tally the amount of times classes are added and removed and only animate
the left over classes once the digest kicks in. This means that for any directive code that
adds and removes the same CSS class on the same element then this may result in no animation
being triggered at all.
```js
$animate.addClass(element, 'klass');
$animate.removeClass(element, 'klass');
$rootScope.$digest();
//nothing happens...
```
This commit is contained in:
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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('<div class="animated"></div>')($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('<div style="height:200px"></div>'))($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('<div>1</div>'))($rootScope);
|
||||
|
||||
$animate.addClass(element, 'my-class');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.attr('style')).not.toMatch(/transition.*?:\s*none/);
|
||||
expect(element.hasClass('my-class')).toBe(false);
|
||||
expect(element.hasClass('my-class-add')).toBe(true);
|
||||
|
||||
$animate.triggerReflow();
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.attr('style')).not.toMatch(/transition.*?:\s*none/);
|
||||
expect(element.hasClass('my-class')).toBe(true);
|
||||
@@ -1626,6 +1684,7 @@ describe("ngAnimate", function() {
|
||||
element = $compile(html('<div class="animated-element">foo</div>'))($rootScope);
|
||||
|
||||
$animate.addClass(element, 'some-class');
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow(); //reflow
|
||||
expect(element.hasClass('some-class-add-active')).toBe(true);
|
||||
@@ -1792,11 +1851,13 @@ describe("ngAnimate", function() {
|
||||
element = $compile(html('<div>foo</div>'))($rootScope);
|
||||
|
||||
$animate.addClass(element, 'some-class');
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow(); //reflow
|
||||
expect(element.hasClass('some-class-add-active')).toBe(true);
|
||||
|
||||
$animate.removeClass(element, 'some-class');
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow(); //second reflow
|
||||
|
||||
@@ -2035,11 +2096,13 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element, 'on', function() {
|
||||
signature += 'A';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
signature += 'B';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
$animate.triggerCallbacks();
|
||||
@@ -2062,6 +2125,7 @@ describe("ngAnimate", function() {
|
||||
$animate.setClass(element, 'on', 'off', function() {
|
||||
signature += 'Z';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow();
|
||||
$animate.triggerCallbacks();
|
||||
@@ -2101,6 +2165,7 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element, 'klass', function() {
|
||||
steps.push(['done', 'klass', 'addClass']);
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerCallbacks();
|
||||
|
||||
@@ -2176,6 +2241,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'ng-hide', function() {
|
||||
flag = true;
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerCallbacks();
|
||||
expect(flag).toBe(true);
|
||||
@@ -2199,6 +2265,7 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element, 'ng-hide', function() {
|
||||
flag = true;
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
@@ -2222,6 +2289,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'ng-hide', function() {
|
||||
flag = true;
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerCallbacks();
|
||||
expect(flag).toBe(true);
|
||||
@@ -2245,9 +2313,11 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'ng-hide', function() {
|
||||
signature += 'A';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
$animate.addClass(element, 'ng-hide', function() {
|
||||
signature += 'B';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.addClass(element, 'ng-hide'); //earlier animation cancelled
|
||||
if($sniffer.transitions) {
|
||||
@@ -2292,6 +2362,7 @@ describe("ngAnimate", function() {
|
||||
//skipped animations
|
||||
captured = 'none';
|
||||
$animate.removeClass(element, 'some-class');
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('some-class')).toBe(false);
|
||||
expect(captured).toBe('none');
|
||||
|
||||
@@ -2299,18 +2370,21 @@ describe("ngAnimate", function() {
|
||||
|
||||
captured = 'nothing';
|
||||
$animate.addClass(element, 'some-class');
|
||||
$rootScope.$digest();
|
||||
expect(captured).toBe('nothing');
|
||||
expect(element.hasClass('some-class')).toBe(true);
|
||||
|
||||
//actual animations
|
||||
captured = 'none';
|
||||
$animate.removeClass(element, 'some-class');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('some-class')).toBe(false);
|
||||
expect(captured).toBe('removeClass-some-class');
|
||||
|
||||
captured = 'nothing';
|
||||
$animate.addClass(element, 'some-class');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('some-class')).toBe(true);
|
||||
expect(captured).toBe('addClass-some-class');
|
||||
@@ -2326,6 +2400,7 @@ describe("ngAnimate", function() {
|
||||
//skipped animations
|
||||
captured = 'none';
|
||||
$animate.removeClass(element[0], 'some-class');
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('some-class')).toBe(false);
|
||||
expect(captured).toBe('none');
|
||||
|
||||
@@ -2333,18 +2408,21 @@ describe("ngAnimate", function() {
|
||||
|
||||
captured = 'nothing';
|
||||
$animate.addClass(element[0], 'some-class');
|
||||
$rootScope.$digest();
|
||||
expect(captured).toBe('nothing');
|
||||
expect(element.hasClass('some-class')).toBe(true);
|
||||
|
||||
//actual animations
|
||||
captured = 'none';
|
||||
$animate.removeClass(element[0], 'some-class');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('some-class')).toBe(false);
|
||||
expect(captured).toBe('removeClass-some-class');
|
||||
|
||||
captured = 'nothing';
|
||||
$animate.addClass(element[0], 'some-class');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('some-class')).toBe(true);
|
||||
expect(captured).toBe('addClass-some-class');
|
||||
@@ -2359,11 +2437,13 @@ describe("ngAnimate", function() {
|
||||
var element = jqLite(parent.find('span'));
|
||||
|
||||
$animate.addClass(element,'klass');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(element.hasClass('klass')).toBe(true);
|
||||
|
||||
$animate.removeClass(element,'klass');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(element.hasClass('klass')).toBe(false);
|
||||
@@ -2385,6 +2465,7 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element,'klass', function() {
|
||||
signature += 'A';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(element.hasClass('klass')).toBe(true);
|
||||
@@ -2392,6 +2473,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element,'klass', function() {
|
||||
signature += 'B';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
$animate.triggerCallbacks();
|
||||
@@ -2418,6 +2500,7 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element,'klass', function() {
|
||||
signature += '1';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
expect(element.hasClass('klass-add')).toBe(true);
|
||||
@@ -2433,6 +2516,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element,'klass', function() {
|
||||
signature += '2';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
expect(element.hasClass('klass-remove')).toBe(true);
|
||||
@@ -2465,6 +2549,7 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element,'klassy', function() {
|
||||
signature += 'X';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
$timeout.flush(500);
|
||||
@@ -2474,6 +2559,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element,'klassy', function() {
|
||||
signature += 'Y';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
$timeout.flush(3000);
|
||||
@@ -2497,6 +2583,7 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element[0],'klassy', function() {
|
||||
signature += 'X';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
$timeout.flush(500);
|
||||
@@ -2506,6 +2593,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element[0],'klassy', function() {
|
||||
signature += 'Y';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
$timeout.flush(3000);
|
||||
@@ -2534,6 +2622,7 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element,'klass', function() {
|
||||
signature += 'd';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
@@ -2550,6 +2639,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element,'klass', function() {
|
||||
signature += 'b';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
@@ -2585,6 +2675,8 @@ describe("ngAnimate", function() {
|
||||
flag = true;
|
||||
});
|
||||
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('one-add')).toBe(true);
|
||||
@@ -2630,6 +2722,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element,'one two', function() {
|
||||
flag = true;
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
@@ -3004,14 +3097,17 @@ describe("ngAnimate", function() {
|
||||
var element = html($compile('<div class="classify"></div>')($rootScope));
|
||||
|
||||
$animate.addClass(element, 'super');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
expect(element.data('classify')).toBe('add-super');
|
||||
|
||||
$animate.removeClass(element, 'super');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
expect(element.data('classify')).toBe('remove-super');
|
||||
|
||||
$animate.addClass(element, 'superguy');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
expect(element.data('classify')).toBe('add-superguy');
|
||||
});
|
||||
@@ -3139,6 +3235,7 @@ describe("ngAnimate", function() {
|
||||
$animate.triggerCallbacks();
|
||||
|
||||
$animate.addClass(child, 'something');
|
||||
$rootScope.$digest();
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
@@ -3155,8 +3252,115 @@ describe("ngAnimate", function() {
|
||||
expect(child.hasClass('something-add-active')).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should coalesce all class-based animation calls together into a single animation', function() {
|
||||
var log = [];
|
||||
var track = function(name) {
|
||||
return function() {
|
||||
log.push({ name : name, className : arguments[1] });
|
||||
};
|
||||
};
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.animate', function() {
|
||||
return {
|
||||
addClass : track('addClass'),
|
||||
removeClass : track('removeClass')
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $animate, $compile, $rootElement, $document) {
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = $compile('<div class="animate three"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
$animate.addClass(element, 'one');
|
||||
$animate.addClass(element, 'two');
|
||||
$animate.removeClass(element, 'three');
|
||||
$animate.removeClass(element, 'four');
|
||||
$animate.setClass(element, 'four five', 'two');
|
||||
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(log.length).toBe(2);
|
||||
expect(log[0]).toEqual({ name : 'addClass', className : 'one five' });
|
||||
expect(log[1]).toEqual({ name : 'removeClass', className : 'three' });
|
||||
});
|
||||
});
|
||||
|
||||
it('should call class-based animation callbacks in the correct order when animations are skipped', function() {
|
||||
var continueAnimation;
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.animate', function() {
|
||||
return {
|
||||
addClass : function(element, className, done) {
|
||||
continueAnimation = done;
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $animate, $compile, $rootElement, $document) {
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = $compile('<div class="animate"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var log = '';
|
||||
$animate.addClass(element, 'one', function() {
|
||||
log += 'A';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.addClass(element, 'one', function() {
|
||||
log += 'B';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
$animate.triggerCallbacks();
|
||||
|
||||
$animate.triggerReflow();
|
||||
continueAnimation();
|
||||
$animate.triggerCallbacks();
|
||||
expect(log).toBe('BA');
|
||||
});
|
||||
});
|
||||
|
||||
it('should skip class-based animations when add class and remove class cancel each other out', function() {
|
||||
var spy = jasmine.createSpy();
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.animate', function() {
|
||||
return {
|
||||
addClass : spy,
|
||||
removeClass : spy,
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $animate, $compile) {
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = $compile('<div class="animate"></div>')($rootScope);
|
||||
|
||||
var count = 0;
|
||||
var callback = function() {
|
||||
count++;
|
||||
};
|
||||
|
||||
$animate.addClass(element, 'on', callback);
|
||||
$animate.addClass(element, 'on', callback);
|
||||
$animate.removeClass(element, 'on', callback);
|
||||
$animate.removeClass(element, 'on', callback);
|
||||
|
||||
$rootScope.$digest();
|
||||
$animate.triggerCallbacks();
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
expect(count).toBe(4);
|
||||
});
|
||||
});
|
||||
|
||||
it("should wait until a queue of animations are complete before performing a reflow",
|
||||
inject(function($rootScope, $compile, $timeout, $sniffer, $animate) {
|
||||
@@ -3217,6 +3421,7 @@ describe("ngAnimate", function() {
|
||||
$animate.enabled(true, element);
|
||||
|
||||
$animate.addClass(child, 'awesome');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
expect(childAnimated).toBe(true);
|
||||
|
||||
@@ -3224,6 +3429,7 @@ describe("ngAnimate", function() {
|
||||
$animate.enabled(false, element);
|
||||
|
||||
$animate.addClass(child, 'super');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
expect(childAnimated).toBe(false);
|
||||
|
||||
@@ -3283,6 +3489,7 @@ describe("ngAnimate", function() {
|
||||
continueAnimation();
|
||||
|
||||
$animate.addClass(child1, 'test');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
expect(child1.hasClass('test')).toBe(true);
|
||||
|
||||
@@ -3305,6 +3512,7 @@ describe("ngAnimate", function() {
|
||||
$animate.triggerCallbacks();
|
||||
|
||||
$animate.addClass(child2, 'testing');
|
||||
$rootScope.$digest();
|
||||
expect(intercepted).toBe('move');
|
||||
|
||||
continueAnimation();
|
||||
@@ -3445,9 +3653,11 @@ describe("ngAnimate", function() {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$animate.addClass(element, 'green');
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('green-add')).toBe(true);
|
||||
|
||||
$animate.addClass(element, 'red');
|
||||
$rootScope.$digest();
|
||||
expect(element.hasClass('red-add')).toBe(true);
|
||||
|
||||
expect(element.hasClass('green')).toBe(false);
|
||||
@@ -3529,13 +3739,17 @@ describe("ngAnimate", function() {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$animate.addClass(element, 'on');
|
||||
$rootScope.$digest();
|
||||
expect(currentAnimation).toBe('addClass');
|
||||
currentFn();
|
||||
|
||||
currentAnimation = null;
|
||||
|
||||
$animate.removeClass(element, 'on');
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.addClass(element, 'on');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(currentAnimation).toBe('addClass');
|
||||
});
|
||||
@@ -3557,11 +3771,13 @@ describe("ngAnimate", function() {
|
||||
|
||||
$rootElement.addClass('animated');
|
||||
$animate.addClass($rootElement, 'green');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(count).toBe(1);
|
||||
|
||||
$animate.addClass($rootElement, 'red');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(count).toBe(2);
|
||||
@@ -3592,6 +3808,8 @@ describe("ngAnimate", function() {
|
||||
$rootElement.append(element);
|
||||
|
||||
$animate.addClass(element, 'red');
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(steps).toEqual(['before','after']);
|
||||
@@ -3648,12 +3866,14 @@ describe("ngAnimate", function() {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$animate.removeClass(element, 'base-class one two');
|
||||
$rootScope.$digest();
|
||||
|
||||
//still true since we're before the reflow
|
||||
expect(element.hasClass('base-class')).toBe(true);
|
||||
|
||||
//this will cancel the remove animation
|
||||
$animate.addClass(element, 'base-class one two');
|
||||
$rootScope.$digest();
|
||||
|
||||
//the cancellation was a success and the class was removed right away
|
||||
expect(element.hasClass('base-class')).toBe(false);
|
||||
@@ -3694,6 +3914,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
expect(capturedProperty).toBe('none');
|
||||
$animate.addClass(element, 'trigger-class');
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow();
|
||||
|
||||
@@ -3719,6 +3940,7 @@ describe("ngAnimate", function() {
|
||||
var animationKey = $sniffer.vendorPrefix == 'Webkit' ? 'WebkitAnimation' : 'animation';
|
||||
|
||||
$animate.addClass(element, 'trigger-class');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(node.style[animationKey]).not.toContain('none');
|
||||
|
||||
@@ -3765,6 +3987,7 @@ describe("ngAnimate", function() {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$animate.addClass(element, 'some-klass');
|
||||
$rootScope.$digest();
|
||||
|
||||
var prop = $sniffer.vendorPrefix == 'Webkit' ? 'WebkitAnimation' : 'animation';
|
||||
|
||||
@@ -3967,6 +4190,7 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element, 'on', function() {
|
||||
ready = true;
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 1 });
|
||||
@@ -3981,6 +4205,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
ready = true;
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 1 });
|
||||
@@ -4006,6 +4231,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
ready = true;
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow();
|
||||
$animate.triggerCallbacks();
|
||||
@@ -4030,9 +4256,12 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
signature += 'A';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.addClass(element, 'on', function() {
|
||||
signature += 'B';
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow();
|
||||
$animate.triggerCallbacks();
|
||||
@@ -4066,7 +4295,10 @@ describe("ngAnimate", function() {
|
||||
expect(cancelReflowCallback).not.toHaveBeenCalled();
|
||||
|
||||
$animate.addClass(element, 'fast');
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.addClass(element, 'smooth');
|
||||
$rootScope.$digest();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(cancelReflowCallback).toHaveBeenCalled();
|
||||
|
||||
@@ -773,7 +773,10 @@ describe('ngView animations', function() {
|
||||
$rootScope.klass = 'boring';
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($animate.queue.shift().event).toBe('setClass');
|
||||
expect($animate.queue.shift().event).toBe('addClass');
|
||||
expect($animate.queue.shift().event).toBe('removeClass');
|
||||
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(item.hasClass('classy')).toBe(false);
|
||||
expect(item.hasClass('boring')).toBe(true);
|
||||
|
||||
Reference in New Issue
Block a user