|
|
|
|
@@ -248,7 +248,9 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
* Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
.factory('$$animateReflow', ['$window', '$timeout', function($window, $timeout) {
|
|
|
|
|
.factory('$$animateReflow', ['$window', '$timeout', '$document',
|
|
|
|
|
function($window, $timeout, $document) {
|
|
|
|
|
var bod = $document[0].body;
|
|
|
|
|
var requestAnimationFrame = $window.requestAnimationFrame ||
|
|
|
|
|
$window.webkitRequestAnimationFrame ||
|
|
|
|
|
function(fn) {
|
|
|
|
|
@@ -261,7 +263,10 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
return $timeout.cancel(timer);
|
|
|
|
|
};
|
|
|
|
|
return function(fn) {
|
|
|
|
|
var id = requestAnimationFrame(fn);
|
|
|
|
|
var id = requestAnimationFrame(function() {
|
|
|
|
|
var a = bod.offsetWidth + 1;
|
|
|
|
|
fn();
|
|
|
|
|
});
|
|
|
|
|
return function() {
|
|
|
|
|
cancelAnimationFrame(id);
|
|
|
|
|
};
|
|
|
|
|
@@ -301,6 +306,10 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function stripCommentsFromElement(element) {
|
|
|
|
|
return angular.element(extractElementNode(element));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isMatchingElement(elm1, elm2) {
|
|
|
|
|
return extractElementNode(elm1) == extractElementNode(elm2);
|
|
|
|
|
}
|
|
|
|
|
@@ -411,6 +420,7 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
this.enabled(false, element);
|
|
|
|
|
$delegate.enter(element, parentElement, afterElement);
|
|
|
|
|
$rootScope.$$postDigest(function() {
|
|
|
|
|
element = stripCommentsFromElement(element);
|
|
|
|
|
performAnimation('enter', 'ng-enter', element, parentElement, afterElement, noop, doneCallback);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
@@ -447,6 +457,7 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
cancelChildAnimations(element);
|
|
|
|
|
this.enabled(false, element);
|
|
|
|
|
$rootScope.$$postDigest(function() {
|
|
|
|
|
element = stripCommentsFromElement(element);
|
|
|
|
|
performAnimation('leave', 'ng-leave', element, null, null, function() {
|
|
|
|
|
$delegate.leave(element);
|
|
|
|
|
}, doneCallback);
|
|
|
|
|
@@ -489,6 +500,7 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
this.enabled(false, element);
|
|
|
|
|
$delegate.move(element, parentElement, afterElement);
|
|
|
|
|
$rootScope.$$postDigest(function() {
|
|
|
|
|
element = stripCommentsFromElement(element);
|
|
|
|
|
performAnimation('move', 'ng-move', element, parentElement, afterElement, noop, doneCallback);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
@@ -524,6 +536,7 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
* @param {function()=} doneCallback the callback function that will be called once the animation is complete
|
|
|
|
|
*/
|
|
|
|
|
addClass : function(element, className, doneCallback) {
|
|
|
|
|
element = stripCommentsFromElement(element);
|
|
|
|
|
performAnimation('addClass', className, element, null, null, function() {
|
|
|
|
|
$delegate.addClass(element, className);
|
|
|
|
|
}, doneCallback);
|
|
|
|
|
@@ -560,11 +573,34 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
* @param {function()=} doneCallback the callback function that will be called once the animation is complete
|
|
|
|
|
*/
|
|
|
|
|
removeClass : function(element, className, doneCallback) {
|
|
|
|
|
element = stripCommentsFromElement(element);
|
|
|
|
|
performAnimation('removeClass', className, element, null, null, function() {
|
|
|
|
|
$delegate.removeClass(element, className);
|
|
|
|
|
}, doneCallback);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @ngdoc function
|
|
|
|
|
* @name ng.$animate#setClass
|
|
|
|
|
* @methodOf ng.$animate
|
|
|
|
|
* @function
|
|
|
|
|
* @description Adds and/or removes the given CSS classes to and from the element.
|
|
|
|
|
* Once complete, the done() callback will be fired (if provided).
|
|
|
|
|
* @param {jQuery/jqLite element} element the element which will it's CSS classes changed
|
|
|
|
|
* removed from it
|
|
|
|
|
* @param {string} add the CSS classes which will be added to the element
|
|
|
|
|
* @param {string} remove the CSS class which will be removed from the element
|
|
|
|
|
* @param {function=} done the callback function (if provided) that will be fired after the
|
|
|
|
|
* CSS classes have been set on the element
|
|
|
|
|
*/
|
|
|
|
|
setClass : function(element, add, remove, doneCallback) {
|
|
|
|
|
element = stripCommentsFromElement(element);
|
|
|
|
|
performAnimation('setClass', [add, remove], element, null, null, function() {
|
|
|
|
|
$delegate.setClass(element, add, remove);
|
|
|
|
|
}, doneCallback);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @ngdoc function
|
|
|
|
|
* @name ngAnimate.$animate#enabled
|
|
|
|
|
@@ -611,7 +647,15 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
and the onComplete callback will be fired once the animation is fully complete.
|
|
|
|
|
*/
|
|
|
|
|
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
|
|
|
|
|
var currentClassName, classes, node = extractElementNode(element);
|
|
|
|
|
|
|
|
|
|
var classNameAdd, classNameRemove, setClassOperation = animationEvent == 'setClass';
|
|
|
|
|
if(setClassOperation) {
|
|
|
|
|
classNameAdd = className[0];
|
|
|
|
|
classNameRemove = className[1];
|
|
|
|
|
className = classNameAdd + ' ' + classNameRemove;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var currentClassName, classes, node = element[0];
|
|
|
|
|
if(node) {
|
|
|
|
|
currentClassName = node.className;
|
|
|
|
|
classes = currentClassName + ' ' + className;
|
|
|
|
|
@@ -623,7 +667,7 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
fireDOMOperation();
|
|
|
|
|
fireBeforeCallbackAsync();
|
|
|
|
|
fireAfterCallbackAsync();
|
|
|
|
|
closeAnimation();
|
|
|
|
|
fireDoneCallbackAsync();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -635,9 +679,15 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
parentElement = afterElement ? afterElement.parent() : element.parent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var matches = lookup(animationLookup);
|
|
|
|
|
var isClassBased = animationEvent == 'addClass' || animationEvent == 'removeClass';
|
|
|
|
|
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
|
|
|
|
|
var matches = lookup(animationLookup);
|
|
|
|
|
var isClassBased = animationEvent == 'addClass' ||
|
|
|
|
|
animationEvent == 'removeClass' ||
|
|
|
|
|
setClassOperation;
|
|
|
|
|
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
|
|
|
|
|
|
|
|
|
|
var runningAnimations = ngAnimateState.active || {};
|
|
|
|
|
var totalActiveAnimations = ngAnimateState.totalActive || 0;
|
|
|
|
|
var lastAnimation = ngAnimateState.last;
|
|
|
|
|
|
|
|
|
|
//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
|
|
|
|
|
@@ -656,7 +706,7 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
//only add animations if the currently running animation is not structural
|
|
|
|
|
//or if there is no animation running at all
|
|
|
|
|
var allowAnimations = isClassBased ?
|
|
|
|
|
!ngAnimateState.disabled && (!ngAnimateState.running || !ngAnimateState.structural) :
|
|
|
|
|
!ngAnimateState.disabled && (!lastAnimation || lastAnimation.classBased) :
|
|
|
|
|
true;
|
|
|
|
|
|
|
|
|
|
if(allowAnimations) {
|
|
|
|
|
@@ -691,54 +741,48 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ONE_SPACE = ' ';
|
|
|
|
|
//this value will be searched for class-based CSS className lookup. Therefore,
|
|
|
|
|
//we prefix and suffix the current className value with spaces to avoid substring
|
|
|
|
|
//lookups of className tokens
|
|
|
|
|
var futureClassName = ONE_SPACE + currentClassName + ONE_SPACE;
|
|
|
|
|
if(ngAnimateState.running) {
|
|
|
|
|
//if an animation is currently running on the element then lets take the steps
|
|
|
|
|
//to cancel that animation and fire any required callbacks
|
|
|
|
|
cleanup(element);
|
|
|
|
|
cancelAnimations(ngAnimateState.animations);
|
|
|
|
|
var skipAnimation = false;
|
|
|
|
|
if(totalActiveAnimations > 0) {
|
|
|
|
|
var animationsToCancel = [];
|
|
|
|
|
if(!isClassBased) {
|
|
|
|
|
if(animationEvent == 'leave' && runningAnimations['ng-leave']) {
|
|
|
|
|
skipAnimation = true;
|
|
|
|
|
} else {
|
|
|
|
|
//cancel all animations when a structural animation takes place
|
|
|
|
|
for(var klass in runningAnimations) {
|
|
|
|
|
animationsToCancel.push(runningAnimations[klass]);
|
|
|
|
|
cleanup(element, klass);
|
|
|
|
|
}
|
|
|
|
|
runningAnimations = {};
|
|
|
|
|
totalActiveAnimations = 0;
|
|
|
|
|
}
|
|
|
|
|
} else if(lastAnimation.event == 'setClass') {
|
|
|
|
|
animationsToCancel.push(lastAnimation);
|
|
|
|
|
cleanup(element, className);
|
|
|
|
|
}
|
|
|
|
|
else if(runningAnimations[className]) {
|
|
|
|
|
var current = runningAnimations[className];
|
|
|
|
|
if(current.event == animationEvent) {
|
|
|
|
|
skipAnimation = true;
|
|
|
|
|
} else {
|
|
|
|
|
animationsToCancel.push(current);
|
|
|
|
|
cleanup(element, className);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//in the event that the CSS is class is quickly added and removed back
|
|
|
|
|
//then we don't want to wait until after the reflow to add/remove the CSS
|
|
|
|
|
//class since both class animations may run into a race condition.
|
|
|
|
|
//The code below will check to see if that is occurring and will
|
|
|
|
|
//immediately remove the former class before the reflow so that the
|
|
|
|
|
//animation can snap back to the original animation smoothly
|
|
|
|
|
var isFullyClassBasedAnimation = isClassBased && !ngAnimateState.structural;
|
|
|
|
|
var isRevertingClassAnimation = isFullyClassBasedAnimation &&
|
|
|
|
|
ngAnimateState.className == className &&
|
|
|
|
|
animationEvent != ngAnimateState.event;
|
|
|
|
|
|
|
|
|
|
//if the class is removed during the reflow then it will revert the styles temporarily
|
|
|
|
|
//back to the base class CSS styling causing a jump-like effect to occur. This check
|
|
|
|
|
//here ensures that the domOperation is only performed after the reflow has commenced
|
|
|
|
|
if(ngAnimateState.beforeComplete || isRevertingClassAnimation) {
|
|
|
|
|
(ngAnimateState.done || noop)(true);
|
|
|
|
|
} else if(isFullyClassBasedAnimation) {
|
|
|
|
|
//class-based animations will compare element className values after cancelling the
|
|
|
|
|
//previous animation to see if the element properties already contain the final CSS
|
|
|
|
|
//class and if so then the animation will be skipped. Since the domOperation will
|
|
|
|
|
//be performed only after the reflow is complete then our element's className value
|
|
|
|
|
//will be invalid. Therefore the same string manipulation that would occur within the
|
|
|
|
|
//DOM operation will be performed below so that the class comparison is valid...
|
|
|
|
|
futureClassName = ngAnimateState.event == 'removeClass' ?
|
|
|
|
|
futureClassName.replace(ONE_SPACE + ngAnimateState.className + ONE_SPACE, ONE_SPACE) :
|
|
|
|
|
futureClassName + ngAnimateState.className + ONE_SPACE;
|
|
|
|
|
if(animationsToCancel.length > 0) {
|
|
|
|
|
angular.forEach(animationsToCancel, function(operation) {
|
|
|
|
|
(operation.done || noop)(true);
|
|
|
|
|
cancelAnimations(operation.animations);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//There is no point in perform a class-based animation if the element already contains
|
|
|
|
|
//(on addClass) or doesn't contain (on removeClass) the className being animated.
|
|
|
|
|
//The reason why this is being called after the previous animations are cancelled
|
|
|
|
|
//is so that the CSS classes present on the element can be properly examined.
|
|
|
|
|
var classNameToken = ONE_SPACE + className + ONE_SPACE;
|
|
|
|
|
if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) ||
|
|
|
|
|
(animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {
|
|
|
|
|
fireDOMOperation();
|
|
|
|
|
if(isClassBased && !setClassOperation && !skipAnimation) {
|
|
|
|
|
skipAnimation = (animationEvent == 'addClass') == element.hasClass(className); //opposite of XOR
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(skipAnimation) {
|
|
|
|
|
fireBeforeCallbackAsync();
|
|
|
|
|
fireAfterCallbackAsync();
|
|
|
|
|
fireDoneCallbackAsync();
|
|
|
|
|
@@ -750,15 +794,21 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
element.addClass(NG_ANIMATE_CLASS_NAME);
|
|
|
|
|
|
|
|
|
|
var localAnimationCount = globalAnimationCounter++;
|
|
|
|
|
lastAnimation = {
|
|
|
|
|
classBased : isClassBased,
|
|
|
|
|
event : animationEvent,
|
|
|
|
|
animations : animations,
|
|
|
|
|
done:onBeforeAnimationsComplete
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
totalActiveAnimations++;
|
|
|
|
|
runningAnimations[className] = lastAnimation;
|
|
|
|
|
|
|
|
|
|
element.data(NG_ANIMATE_STATE, {
|
|
|
|
|
running:true,
|
|
|
|
|
event:animationEvent,
|
|
|
|
|
className:className,
|
|
|
|
|
structural:!isClassBased,
|
|
|
|
|
animations:animations,
|
|
|
|
|
index:localAnimationCount,
|
|
|
|
|
done:onBeforeAnimationsComplete
|
|
|
|
|
last : lastAnimation,
|
|
|
|
|
active : runningAnimations,
|
|
|
|
|
index : localAnimationCount,
|
|
|
|
|
totalActive : totalActiveAnimations
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//first we run the before animations and when all of those are complete
|
|
|
|
|
@@ -766,6 +816,11 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
invokeRegisteredAnimationFns(animations, 'before', onBeforeAnimationsComplete);
|
|
|
|
|
|
|
|
|
|
function onBeforeAnimationsComplete(cancelled) {
|
|
|
|
|
var data = element.data(NG_ANIMATE_STATE);
|
|
|
|
|
cancelled = cancelled ||
|
|
|
|
|
!data || !data.active[className] ||
|
|
|
|
|
(isClassBased && data.active[className].event != animationEvent);
|
|
|
|
|
|
|
|
|
|
fireDOMOperation();
|
|
|
|
|
if(cancelled === true) {
|
|
|
|
|
closeAnimation();
|
|
|
|
|
@@ -775,11 +830,8 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
//set the done function to the final done function
|
|
|
|
|
//so that the DOM event won't be executed twice by accident
|
|
|
|
|
//if the after animation is cancelled as well
|
|
|
|
|
var data = element.data(NG_ANIMATE_STATE);
|
|
|
|
|
if(data) {
|
|
|
|
|
data.done = closeAnimation;
|
|
|
|
|
element.data(NG_ANIMATE_STATE, data);
|
|
|
|
|
}
|
|
|
|
|
var currentAnimation = data.active[className];
|
|
|
|
|
currentAnimation.done = closeAnimation;
|
|
|
|
|
invokeRegisteredAnimationFns(animations, 'after', closeAnimation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -802,9 +854,13 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(animation[phase]) {
|
|
|
|
|
animation[endFnName] = isClassBased ?
|
|
|
|
|
animation[phase](element, className, animationPhaseCompleted) :
|
|
|
|
|
animation[phase](element, animationPhaseCompleted);
|
|
|
|
|
if(setClassOperation) {
|
|
|
|
|
animation[endFnName] = animation[phase](element, classNameAdd, classNameRemove, animationPhaseCompleted);
|
|
|
|
|
} else {
|
|
|
|
|
animation[endFnName] = isClassBased ?
|
|
|
|
|
animation[phase](element, className, animationPhaseCompleted) :
|
|
|
|
|
animation[phase](element, animationPhaseCompleted);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
animationPhaseCompleted();
|
|
|
|
|
}
|
|
|
|
|
@@ -872,12 +928,12 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
failing would be when a parent HTML tag has a ng-class attribute
|
|
|
|
|
causing ALL directives below to skip animations during the digest */
|
|
|
|
|
if(isClassBased) {
|
|
|
|
|
cleanup(element);
|
|
|
|
|
cleanup(element, className);
|
|
|
|
|
} else {
|
|
|
|
|
$$asyncQueueBuffer(function() {
|
|
|
|
|
var data = element.data(NG_ANIMATE_STATE) || {};
|
|
|
|
|
if(localAnimationCount == data.index) {
|
|
|
|
|
cleanup(element);
|
|
|
|
|
cleanup(element, className, animationEvent);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
element.data(NG_ANIMATE_STATE, data);
|
|
|
|
|
@@ -893,9 +949,11 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
forEach(node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME), function(element) {
|
|
|
|
|
element = angular.element(element);
|
|
|
|
|
var data = element.data(NG_ANIMATE_STATE);
|
|
|
|
|
if(data) {
|
|
|
|
|
cancelAnimations(data.animations);
|
|
|
|
|
cleanup(element);
|
|
|
|
|
if(data && data.active) {
|
|
|
|
|
angular.forEach(data.active, function(operation) {
|
|
|
|
|
(operation.done || noop)(true);
|
|
|
|
|
cancelAnimations(operation.animations);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
@@ -912,15 +970,27 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function cleanup(element) {
|
|
|
|
|
function cleanup(element, className) {
|
|
|
|
|
if(isMatchingElement(element, $rootElement)) {
|
|
|
|
|
if(!rootAnimateState.disabled) {
|
|
|
|
|
rootAnimateState.running = false;
|
|
|
|
|
rootAnimateState.structural = false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
element.removeClass(NG_ANIMATE_CLASS_NAME);
|
|
|
|
|
element.removeData(NG_ANIMATE_STATE);
|
|
|
|
|
} else if(className) {
|
|
|
|
|
var data = element.data(NG_ANIMATE_STATE) || {};
|
|
|
|
|
|
|
|
|
|
var removeAnimations = className === true;
|
|
|
|
|
if(!removeAnimations) {
|
|
|
|
|
if(data.active && data.active[className]) {
|
|
|
|
|
data.totalActive--;
|
|
|
|
|
delete data.active[className];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(removeAnimations || !data.totalActive) {
|
|
|
|
|
element.removeClass(NG_ANIMATE_CLASS_NAME);
|
|
|
|
|
element.removeData(NG_ANIMATE_STATE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -939,7 +1009,7 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
|
|
|
|
|
var isRoot = isMatchingElement(parentElement, $rootElement);
|
|
|
|
|
var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE);
|
|
|
|
|
var result = state && (!!state.disabled || !!state.running);
|
|
|
|
|
var result = state && (!!state.disabled || state.running || state.totalActive > 0);
|
|
|
|
|
if(isRoot || result) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
@@ -989,74 +1059,57 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
|
|
|
|
|
var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey';
|
|
|
|
|
var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data';
|
|
|
|
|
var NG_ANIMATE_BLOCK_CLASS_NAME = 'ng-animate-block-transitions';
|
|
|
|
|
var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
|
|
|
|
|
var CLOSING_TIME_BUFFER = 1.5;
|
|
|
|
|
var ONE_SECOND = 1000;
|
|
|
|
|
|
|
|
|
|
var animationCounter = 0;
|
|
|
|
|
var lookupCache = {};
|
|
|
|
|
var parentCounter = 0;
|
|
|
|
|
var animationReflowQueue = [];
|
|
|
|
|
var animationElementQueue = [];
|
|
|
|
|
var cancelAnimationReflow;
|
|
|
|
|
var closingAnimationTime = 0;
|
|
|
|
|
var timeOut = false;
|
|
|
|
|
function afterReflow(element, callback) {
|
|
|
|
|
if(cancelAnimationReflow) {
|
|
|
|
|
cancelAnimationReflow();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
animationReflowQueue.push(callback);
|
|
|
|
|
|
|
|
|
|
var node = extractElementNode(element);
|
|
|
|
|
element = angular.element(node);
|
|
|
|
|
animationElementQueue.push(element);
|
|
|
|
|
|
|
|
|
|
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
|
|
|
|
|
|
|
|
|
var stagger = elementData.stagger;
|
|
|
|
|
var staggerTime = elementData.itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0);
|
|
|
|
|
|
|
|
|
|
var animationTime = (elementData.maxDelay + elementData.maxDuration) * CLOSING_TIME_BUFFER;
|
|
|
|
|
closingAnimationTime = Math.max(closingAnimationTime, (staggerTime + animationTime) * ONE_SECOND);
|
|
|
|
|
|
|
|
|
|
//by placing a counter we can avoid an accidental
|
|
|
|
|
//race condition which may close an animation when
|
|
|
|
|
//a follow-up animation is midway in its animation
|
|
|
|
|
elementData.animationCount = animationCounter;
|
|
|
|
|
|
|
|
|
|
cancelAnimationReflow = $$animateReflow(function() {
|
|
|
|
|
forEach(animationReflowQueue, function(fn) {
|
|
|
|
|
fn();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//copy the list of elements so that successive
|
|
|
|
|
//animations won't conflict if they're added before
|
|
|
|
|
//the closing animation timeout has run
|
|
|
|
|
var elementQueueSnapshot = [];
|
|
|
|
|
var animationCounterSnapshot = animationCounter;
|
|
|
|
|
forEach(animationElementQueue, function(elm) {
|
|
|
|
|
elementQueueSnapshot.push(elm);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$timeout(function() {
|
|
|
|
|
closeAllAnimations(elementQueueSnapshot, animationCounterSnapshot);
|
|
|
|
|
elementQueueSnapshot = null;
|
|
|
|
|
}, closingAnimationTime, false);
|
|
|
|
|
|
|
|
|
|
animationReflowQueue = [];
|
|
|
|
|
animationElementQueue = [];
|
|
|
|
|
cancelAnimationReflow = null;
|
|
|
|
|
lookupCache = {};
|
|
|
|
|
closingAnimationTime = 0;
|
|
|
|
|
animationCounter++;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeAllAnimations(elements, count) {
|
|
|
|
|
var closingTimer = null;
|
|
|
|
|
var closingTimestamp = 0;
|
|
|
|
|
var animationElementQueue = [];
|
|
|
|
|
function animationCloseHandler(element, totalTime) {
|
|
|
|
|
var futureTimestamp = Date.now() + (totalTime * 1000);
|
|
|
|
|
if(futureTimestamp <= closingTimestamp) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$timeout.cancel(closingTimer);
|
|
|
|
|
|
|
|
|
|
var node = extractElementNode(element);
|
|
|
|
|
element = angular.element(node);
|
|
|
|
|
animationElementQueue.push(element);
|
|
|
|
|
|
|
|
|
|
closingTimestamp = futureTimestamp;
|
|
|
|
|
closingTimer = $timeout(function() {
|
|
|
|
|
closeAllAnimations(animationElementQueue);
|
|
|
|
|
animationElementQueue = [];
|
|
|
|
|
}, totalTime, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeAllAnimations(elements) {
|
|
|
|
|
forEach(elements, function(element) {
|
|
|
|
|
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
|
|
|
|
if(elementData && elementData.animationCount == count) {
|
|
|
|
|
if(elementData) {
|
|
|
|
|
(elementData.closeAnimationFn || noop)();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
@@ -1141,12 +1194,12 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
return parentID + '-' + extractElementNode(element).className;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function animateSetup(element, className, calculationDecorator) {
|
|
|
|
|
function animateSetup(animationEvent, element, className, calculationDecorator) {
|
|
|
|
|
var cacheKey = getCacheKey(element);
|
|
|
|
|
var eventCacheKey = cacheKey + ' ' + className;
|
|
|
|
|
var stagger = {};
|
|
|
|
|
var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
|
|
|
|
|
|
|
|
|
|
var stagger = {};
|
|
|
|
|
if(itemIndex > 0) {
|
|
|
|
|
var staggerClassName = className + '-stagger';
|
|
|
|
|
var staggerCacheKey = cacheKey + ' ' + staggerClassName;
|
|
|
|
|
@@ -1166,60 +1219,63 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
|
|
|
|
|
element.addClass(className);
|
|
|
|
|
|
|
|
|
|
var formerData = element.data(NG_ANIMATE_CSS_DATA_KEY) || {};
|
|
|
|
|
|
|
|
|
|
var timings = calculationDecorator(function() {
|
|
|
|
|
return getElementAnimationDetails(element, eventCacheKey);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* there is no point in performing a reflow if the animation
|
|
|
|
|
timeout is empty (this would cause a flicker bug normally
|
|
|
|
|
in the page. There is also no point in performing an animation
|
|
|
|
|
that only has a delay and no duration */
|
|
|
|
|
var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay);
|
|
|
|
|
var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
|
|
|
|
|
if(maxDuration === 0) {
|
|
|
|
|
var transitionDuration = timings.transitionDuration;
|
|
|
|
|
var animationDuration = timings.animationDuration;
|
|
|
|
|
if(transitionDuration === 0 && animationDuration === 0) {
|
|
|
|
|
element.removeClass(className);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
element.data(NG_ANIMATE_CSS_DATA_KEY, {
|
|
|
|
|
running : formerData.running || 0,
|
|
|
|
|
itemIndex : itemIndex,
|
|
|
|
|
stagger : stagger,
|
|
|
|
|
timings : timings,
|
|
|
|
|
closeAnimationFn : angular.noop
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//temporarily disable the transition so that the enter styles
|
|
|
|
|
//don't animate twice (this is here to avoid a bug in Chrome/FF).
|
|
|
|
|
var activeClassName = '';
|
|
|
|
|
timings.transitionDuration > 0 ?
|
|
|
|
|
blockTransitions(element) :
|
|
|
|
|
var isCurrentlyAnimating = formerData.running > 0 || animationEvent == 'setClass';
|
|
|
|
|
if(transitionDuration > 0) {
|
|
|
|
|
blockTransitions(element, className, isCurrentlyAnimating);
|
|
|
|
|
}
|
|
|
|
|
if(animationDuration > 0) {
|
|
|
|
|
blockKeyframeAnimations(element);
|
|
|
|
|
|
|
|
|
|
forEach(className.split(' '), function(klass, i) {
|
|
|
|
|
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
element.data(NG_ANIMATE_CSS_DATA_KEY, {
|
|
|
|
|
className : className,
|
|
|
|
|
activeClassName : activeClassName,
|
|
|
|
|
maxDuration : maxDuration,
|
|
|
|
|
maxDelay : maxDelay,
|
|
|
|
|
classes : className + ' ' + activeClassName,
|
|
|
|
|
timings : timings,
|
|
|
|
|
stagger : stagger,
|
|
|
|
|
itemIndex : itemIndex
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function blockTransitions(element) {
|
|
|
|
|
extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
|
|
|
|
|
function isStructuralAnimation(className) {
|
|
|
|
|
return className == 'ng-enter' || className == 'ng-move' || className == 'ng-leave';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function blockTransitions(element, className, isAnimating) {
|
|
|
|
|
if(isStructuralAnimation(className) || !isAnimating) {
|
|
|
|
|
extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
|
|
|
|
|
} else {
|
|
|
|
|
element.addClass(NG_ANIMATE_BLOCK_CLASS_NAME);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function blockKeyframeAnimations(element) {
|
|
|
|
|
extractElementNode(element).style[ANIMATION_PROP] = 'none 0s';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function unblockTransitions(element) {
|
|
|
|
|
function unblockTransitions(element, className) {
|
|
|
|
|
var prop = TRANSITION_PROP + PROPERTY_KEY;
|
|
|
|
|
var node = extractElementNode(element);
|
|
|
|
|
if(node.style[prop] && node.style[prop].length > 0) {
|
|
|
|
|
node.style[prop] = '';
|
|
|
|
|
}
|
|
|
|
|
element.removeClass(NG_ANIMATE_BLOCK_CLASS_NAME);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function unblockKeyframeAnimations(element) {
|
|
|
|
|
@@ -1230,22 +1286,28 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function animateRun(element, className, activeAnimationComplete) {
|
|
|
|
|
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
|
|
|
|
function animateRun(animationEvent, element, className, activeAnimationComplete) {
|
|
|
|
|
var node = extractElementNode(element);
|
|
|
|
|
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
|
|
|
|
if(node.className.indexOf(className) == -1 || !elementData) {
|
|
|
|
|
activeAnimationComplete();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var timings = elementData.timings;
|
|
|
|
|
var activeClassName = '';
|
|
|
|
|
forEach(className.split(' '), function(klass, i) {
|
|
|
|
|
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var stagger = elementData.stagger;
|
|
|
|
|
var maxDuration = elementData.maxDuration;
|
|
|
|
|
var activeClassName = elementData.activeClassName;
|
|
|
|
|
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * ONE_SECOND;
|
|
|
|
|
var timings = elementData.timings;
|
|
|
|
|
var itemIndex = elementData.itemIndex;
|
|
|
|
|
var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
|
|
|
|
|
var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay);
|
|
|
|
|
var maxDelayTime = maxDelay * ONE_SECOND;
|
|
|
|
|
|
|
|
|
|
var startTime = Date.now();
|
|
|
|
|
var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
|
|
|
|
|
var itemIndex = elementData.itemIndex;
|
|
|
|
|
|
|
|
|
|
var style = '', appliedStyles = [];
|
|
|
|
|
if(timings.transitionDuration > 0) {
|
|
|
|
|
@@ -1287,6 +1349,13 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
onEnd();
|
|
|
|
|
activeAnimationComplete();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var staggerTime = itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0);
|
|
|
|
|
var animationTime = (maxDelay + maxDuration) * CLOSING_TIME_BUFFER;
|
|
|
|
|
var totalTime = (staggerTime + animationTime) * ONE_SECOND;
|
|
|
|
|
|
|
|
|
|
elementData.running++;
|
|
|
|
|
animationCloseHandler(element, totalTime);
|
|
|
|
|
return onEnd;
|
|
|
|
|
|
|
|
|
|
// This will automatically be called by $animate so
|
|
|
|
|
@@ -1333,28 +1402,28 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function animateBefore(element, className, calculationDecorator) {
|
|
|
|
|
if(animateSetup(element, className, calculationDecorator)) {
|
|
|
|
|
function animateBefore(animationEvent, element, className, calculationDecorator) {
|
|
|
|
|
if(animateSetup(animationEvent, element, className, calculationDecorator)) {
|
|
|
|
|
return function(cancelled) {
|
|
|
|
|
cancelled && animateClose(element, className);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function animateAfter(element, className, afterAnimationComplete) {
|
|
|
|
|
function animateAfter(animationEvent, element, className, afterAnimationComplete) {
|
|
|
|
|
if(element.data(NG_ANIMATE_CSS_DATA_KEY)) {
|
|
|
|
|
return animateRun(element, className, afterAnimationComplete);
|
|
|
|
|
return animateRun(animationEvent, element, className, afterAnimationComplete);
|
|
|
|
|
} else {
|
|
|
|
|
animateClose(element, className);
|
|
|
|
|
afterAnimationComplete();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function animate(element, className, animationComplete) {
|
|
|
|
|
function animate(animationEvent, element, className, animationComplete) {
|
|
|
|
|
//If the animateSetup function doesn't bother returning a
|
|
|
|
|
//cancellation function then it means that there is no animation
|
|
|
|
|
//to perform at all
|
|
|
|
|
var preReflowCancellation = animateBefore(element, className);
|
|
|
|
|
var preReflowCancellation = animateBefore(animationEvent, element, className);
|
|
|
|
|
if(!preReflowCancellation) {
|
|
|
|
|
animationComplete();
|
|
|
|
|
return;
|
|
|
|
|
@@ -1367,12 +1436,12 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
//happen in the first place
|
|
|
|
|
var cancel = preReflowCancellation;
|
|
|
|
|
afterReflow(element, function() {
|
|
|
|
|
unblockTransitions(element);
|
|
|
|
|
unblockTransitions(element, className);
|
|
|
|
|
unblockKeyframeAnimations(element);
|
|
|
|
|
//once the reflow is complete then we point cancel to
|
|
|
|
|
//the new cancellation function which will remove all of the
|
|
|
|
|
//animation properties from the active animation
|
|
|
|
|
cancel = animateAfter(element, className, animationComplete);
|
|
|
|
|
cancel = animateAfter(animationEvent, element, className, animationComplete);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return function(cancelled) {
|
|
|
|
|
@@ -1382,54 +1451,59 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
|
|
|
|
|
function animateClose(element, className) {
|
|
|
|
|
element.removeClass(className);
|
|
|
|
|
element.removeData(NG_ANIMATE_CSS_DATA_KEY);
|
|
|
|
|
var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
|
|
|
|
if(data) {
|
|
|
|
|
if(data.running) {
|
|
|
|
|
data.running--;
|
|
|
|
|
}
|
|
|
|
|
if(!data.running || data.running === 0) {
|
|
|
|
|
element.removeData(NG_ANIMATE_CSS_DATA_KEY);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
allowCancel : function(element, animationEvent, className) {
|
|
|
|
|
//always cancel the current animation if it is a
|
|
|
|
|
//structural animation
|
|
|
|
|
var oldClasses = (element.data(NG_ANIMATE_CSS_DATA_KEY) || {}).classes;
|
|
|
|
|
if(!oldClasses || ['enter','leave','move'].indexOf(animationEvent) >= 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var parentElement = element.parent();
|
|
|
|
|
var clone = angular.element(extractElementNode(element).cloneNode());
|
|
|
|
|
|
|
|
|
|
//make the element super hidden and override any CSS style values
|
|
|
|
|
clone.attr('style','position:absolute; top:-9999px; left:-9999px');
|
|
|
|
|
clone.removeAttr('id');
|
|
|
|
|
clone.empty();
|
|
|
|
|
|
|
|
|
|
forEach(oldClasses.split(' '), function(klass) {
|
|
|
|
|
clone.removeClass(klass);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var suffix = animationEvent == 'addClass' ? '-add' : '-remove';
|
|
|
|
|
clone.addClass(suffixClasses(className, suffix));
|
|
|
|
|
parentElement.append(clone);
|
|
|
|
|
|
|
|
|
|
var timings = getElementAnimationDetails(clone);
|
|
|
|
|
clone.remove();
|
|
|
|
|
|
|
|
|
|
return Math.max(timings.transitionDuration, timings.animationDuration) > 0;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
enter : function(element, animationCompleted) {
|
|
|
|
|
return animate(element, 'ng-enter', animationCompleted);
|
|
|
|
|
return animate('enter', element, 'ng-enter', animationCompleted);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
leave : function(element, animationCompleted) {
|
|
|
|
|
return animate(element, 'ng-leave', animationCompleted);
|
|
|
|
|
return animate('leave', element, 'ng-leave', animationCompleted);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
move : function(element, animationCompleted) {
|
|
|
|
|
return animate(element, 'ng-move', animationCompleted);
|
|
|
|
|
return animate('move', element, 'ng-move', animationCompleted);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
beforeSetClass : function(element, add, remove, animationCompleted) {
|
|
|
|
|
var className = suffixClasses(remove, '-remove') + ' ' +
|
|
|
|
|
suffixClasses(add, '-add');
|
|
|
|
|
var cancellationMethod = animateBefore('setClass', element, className, function(fn) {
|
|
|
|
|
/* when classes are removed from an element then the transition style
|
|
|
|
|
* that is applied is the transition defined on the element without the
|
|
|
|
|
* CSS class being there. This is how CSS3 functions outside of ngAnimate.
|
|
|
|
|
* http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */
|
|
|
|
|
var klass = element.attr('class');
|
|
|
|
|
element.removeClass(remove);
|
|
|
|
|
element.addClass(add);
|
|
|
|
|
var timings = fn();
|
|
|
|
|
element.attr('class', klass);
|
|
|
|
|
return timings;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if(cancellationMethod) {
|
|
|
|
|
afterReflow(element, function() {
|
|
|
|
|
unblockTransitions(element, className);
|
|
|
|
|
unblockKeyframeAnimations(element);
|
|
|
|
|
animationCompleted();
|
|
|
|
|
});
|
|
|
|
|
return cancellationMethod;
|
|
|
|
|
}
|
|
|
|
|
animationCompleted();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
beforeAddClass : function(element, className, animationCompleted) {
|
|
|
|
|
var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'), function(fn) {
|
|
|
|
|
var cancellationMethod = animateBefore('addClass', element, suffixClasses(className, '-add'), function(fn) {
|
|
|
|
|
|
|
|
|
|
/* when a CSS class is added to an element then the transition style that
|
|
|
|
|
* is applied is the transition defined on the element when the CSS class
|
|
|
|
|
@@ -1443,7 +1517,7 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
|
|
|
|
|
if(cancellationMethod) {
|
|
|
|
|
afterReflow(element, function() {
|
|
|
|
|
unblockTransitions(element);
|
|
|
|
|
unblockTransitions(element, className);
|
|
|
|
|
unblockKeyframeAnimations(element);
|
|
|
|
|
animationCompleted();
|
|
|
|
|
});
|
|
|
|
|
@@ -1452,12 +1526,19 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
animationCompleted();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
setClass : function(element, add, remove, animationCompleted) {
|
|
|
|
|
remove = suffixClasses(remove, '-remove');
|
|
|
|
|
add = suffixClasses(add, '-add');
|
|
|
|
|
var className = remove + ' ' + add;
|
|
|
|
|
return animateAfter('setClass', element, className, animationCompleted);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
addClass : function(element, className, animationCompleted) {
|
|
|
|
|
return animateAfter(element, suffixClasses(className, '-add'), animationCompleted);
|
|
|
|
|
return animateAfter('addClass', element, suffixClasses(className, '-add'), animationCompleted);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
beforeRemoveClass : function(element, className, animationCompleted) {
|
|
|
|
|
var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'), function(fn) {
|
|
|
|
|
var cancellationMethod = animateBefore('removeClass', element, suffixClasses(className, '-remove'), function(fn) {
|
|
|
|
|
/* when classes are removed from an element then the transition style
|
|
|
|
|
* that is applied is the transition defined on the element without the
|
|
|
|
|
* CSS class being there. This is how CSS3 functions outside of ngAnimate.
|
|
|
|
|
@@ -1471,7 +1552,7 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
|
|
|
|
|
if(cancellationMethod) {
|
|
|
|
|
afterReflow(element, function() {
|
|
|
|
|
unblockTransitions(element);
|
|
|
|
|
unblockTransitions(element, className);
|
|
|
|
|
unblockKeyframeAnimations(element);
|
|
|
|
|
animationCompleted();
|
|
|
|
|
});
|
|
|
|
|
@@ -1481,7 +1562,7 @@ angular.module('ngAnimate', ['ng'])
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
removeClass : function(element, className, animationCompleted) {
|
|
|
|
|
return animateAfter(element, suffixClasses(className, '-remove'), animationCompleted);
|
|
|
|
|
return animateAfter('removeClass', element, suffixClasses(className, '-remove'), animationCompleted);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|