(function (window, angular, undefined){ 'use strict'; angular.module('ngAnimate', ['ng'] ).factory('$$animateReflow', ['$window', '$timeout', function ($window, $timeout){ var requestAnimationFrame = $window.requestAnimationFrame || $window.webkitRequestAnimationFrame || function (fn){ return $timeout(fn, 10, false ); } ; var cancelAnimationFrame = $window.cancelAnimationFrame || $window.webkitCancelAnimationFrame || function (timer){ return $timeout.cancel(timer); } ; return function (fn){ var id = requestAnimationFrame(fn); return function (){ cancelAnimationFrame(id); } ; } ; } ] ).config(['$provide', '$animateProvider', function ($provide, $animateProvider){ var noop = angular.noop; var forEach = angular.forEach; var selectors = $animateProvider.$$selectors; var ELEMENT_NODE = 1; var NG_ANIMATE_STATE = '$$ngAnimateState'; var NG_ANIMATE_CLASS_NAME = 'ng-animate'; var rootAnimateState = { running: true } ; function extractElementNode(element){ for (var i = 0; i < _AN_Read_length('length', element); i++ ){ var elm = element[i]; if (elm.nodeType == ELEMENT_NODE) { return elm; } } } function isMatchingElement(elm1, elm2){ return extractElementNode(elm1) == extractElementNode(elm2); } $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document', function ($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document){ $rootElement.data(NG_ANIMATE_STATE, rootAnimateState); $rootScope.$$postDigest(function (){ $rootScope.$$postDigest(function (){ rootAnimateState.running = false ; } ); } ); var classNameFilter = $animateProvider.classNameFilter(); var isAnimatableClassName = !classNameFilter? function (){ return true ; } : function (className){ return classNameFilter.test(className); } ; function async(fn){ return $timeout(fn, 0, false ); } function lookup(name){ if (name) { var matches = [] , flagMap = { } , classes = name.substr(1).split('.'); if ($sniffer.transitions || $sniffer.animations) { classes.push(''); } for (var i = 0; i < _AN_Read_length('length', classes); i++ ){ var klass = classes[i], selectorFactoryName = selectors[klass]; if (selectorFactoryName && !flagMap[klass]) { matches.push($injector.get(selectorFactoryName)); flagMap[klass] = true ; } } return matches; } } return { enter: function (element, parentElement, afterElement, doneCallback){ this.enabled(false , element); $delegate.enter(element, parentElement, afterElement); $rootScope.$$postDigest(function (){ performAnimation('enter', 'ng-enter', element, parentElement, afterElement, noop, doneCallback); } ); } , leave: function (element, doneCallback){ cancelChildAnimations(element); this.enabled(false , element); $rootScope.$$postDigest(function (){ performAnimation('leave', 'ng-leave', element, null , null , function (){ $delegate.leave(element); } , doneCallback); } ); } , move: function (element, parentElement, afterElement, doneCallback){ cancelChildAnimations(element); this.enabled(false , element); $delegate.move(element, parentElement, afterElement); $rootScope.$$postDigest(function (){ performAnimation('move', 'ng-move', element, parentElement, afterElement, noop, doneCallback); } ); } , addClass: function (element, className, doneCallback){ performAnimation('addClass', className, element, null , null , function (){ $delegate.addClass(element, className); } , doneCallback); } , removeClass: function (element, className, doneCallback){ performAnimation('removeClass', className, element, null , null , function (){ $delegate.removeClass(element, className); } , doneCallback); } , enabled: function (value, element){ switch (arguments.length){ case 2: if (value) { cleanup(element); } else { var data = element.data(NG_ANIMATE_STATE) || { } ; data.disabled = true ; element.data(NG_ANIMATE_STATE, data); } break ; case 1: rootAnimateState.disabled = !value; break ; default : { value = !rootAnimateState.disabled; break ; } } return !!value; } } ; function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback){ var currentClassName, classes, node = extractElementNode(element); if (node) { currentClassName = node.className; classes = currentClassName + ' ' + className; } if (!node || !isAnimatableClassName(classes)) { fireDOMOperation(); fireBeforeCallbackAsync(); fireAfterCallbackAsync(); closeAnimation(); return ; } var animationLookup = _AN_Call_replace('replace', (' ' + classes), /\s+/g, '.'); if (!parentElement) { parentElement = afterElement? afterElement.parent(): element.parent(); } var matches = lookup(animationLookup); var isClassBased = animationEvent == 'addClass' || animationEvent == 'removeClass'; var ngAnimateState = element.data(NG_ANIMATE_STATE) || { } ; if (animationsDisabled(element, parentElement) || _AN_Read_length('length', matches) === 0) { fireDOMOperation(); fireBeforeCallbackAsync(); fireAfterCallbackAsync(); closeAnimation(); return ; } var animations = [] ; var allowAnimations = isClassBased? !ngAnimateState.disabled && (!ngAnimateState.running || !ngAnimateState.structural): true ; if (allowAnimations) { forEach(matches, function (animation){ if (!animation.allowCancel || animation.allowCancel(element, animationEvent, className)) { var beforeFn, afterFn = animation[animationEvent]; if (animationEvent == 'leave') { beforeFn = afterFn; afterFn = null ; } else { beforeFn = animation['before' + animationEvent.charAt(0).toUpperCase() + animationEvent.substr(1)]; } animations.push({ before: beforeFn, after: afterFn} ); } } ); } if (_AN_Read_length('length', animations) === 0) { fireDOMOperation(); fireBeforeCallbackAsync(); fireAfterCallbackAsync(); fireDoneCallbackAsync(); return ; } var ONE_SPACE = ' '; var futureClassName = ONE_SPACE + currentClassName + ONE_SPACE; if (ngAnimateState.running) { $timeout.cancel(ngAnimateState.closeAnimationTimeout); cleanup(element); cancelAnimations(ngAnimateState.animations); var isFullyClassBasedAnimation = isClassBased && !ngAnimateState.structural; var isRevertingClassAnimation = isFullyClassBasedAnimation && ngAnimateState.className == className && animationEvent != ngAnimateState.event; if (ngAnimateState.beforeComplete || isRevertingClassAnimation) { (ngAnimateState.done || noop)(true ); } else if (isFullyClassBasedAnimation) { futureClassName = ngAnimateState.event == 'removeClass'? _AN_Call_replace('replace', futureClassName, ONE_SPACE + ngAnimateState.className + ONE_SPACE, ONE_SPACE): futureClassName + ngAnimateState.className + ONE_SPACE; } } var classNameToken = ONE_SPACE + className + ONE_SPACE; if ((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) || (animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) { fireDOMOperation(); fireBeforeCallbackAsync(); fireAfterCallbackAsync(); fireDoneCallbackAsync(); return ; } element.addClass(NG_ANIMATE_CLASS_NAME); element.data(NG_ANIMATE_STATE, { running: true , event: animationEvent, className: className, structural: !isClassBased, animations: animations, done: onBeforeAnimationsComplete} ); invokeRegisteredAnimationFns(animations, 'before', onBeforeAnimationsComplete); function onBeforeAnimationsComplete(cancelled){ fireDOMOperation(); if (cancelled === true ) { closeAnimation(); return ; } var data = element.data(NG_ANIMATE_STATE); if (data) { data.done = closeAnimation; element.data(NG_ANIMATE_STATE, data); } invokeRegisteredAnimationFns(animations, 'after', closeAnimation); } function invokeRegisteredAnimationFns(animations, phase, allAnimationFnsComplete){ phase == 'after'? fireAfterCallbackAsync(): fireBeforeCallbackAsync(); var endFnName = phase + 'End'; forEach(animations, function (animation, index){ var animationPhaseCompleted = function (){ progress(index, phase); } ; if (phase == 'before' && (animationEvent == 'enter' || animationEvent == 'move')) { animationPhaseCompleted(); return ; } if (animation[phase]) { animation[endFnName] = isClassBased? animation[phase](element, className, animationPhaseCompleted): animation[phase](element, animationPhaseCompleted); } else { animationPhaseCompleted(); } } ); function progress(index, phase){ var phaseCompletionFlag = phase + 'Complete'; var currentAnimation = animations[index]; currentAnimation[phaseCompletionFlag] = true ; (currentAnimation[endFnName] || noop)(); for (var i = 0; i < _AN_Read_length('length', animations); i++ ){ if (!animations[i][phaseCompletionFlag]) return ; } allAnimationFnsComplete(); } } function fireDOMCallback(animationPhase){ element.triggerHandler('$animate:' + animationPhase, { event: animationEvent, className: className} ); } function fireBeforeCallbackAsync(){ async(function (){ fireDOMCallback('before'); } ); } function fireAfterCallbackAsync(){ async(function (){ fireDOMCallback('after'); } ); } function fireDoneCallbackAsync(){ doneCallback && async(doneCallback); } function fireDOMOperation(){ if (!fireDOMOperation.hasBeenRun) { fireDOMOperation.hasBeenRun = true ; domOperation(); } } function closeAnimation(){ if (!closeAnimation.hasBeenRun) { closeAnimation.hasBeenRun = true ; var data = element.data(NG_ANIMATE_STATE); if (data) { if (isClassBased) { cleanup(element); } else { data.closeAnimationTimeout = async(function (){ cleanup(element); } ); element.data(NG_ANIMATE_STATE, data); } } fireDoneCallbackAsync(); } } } function cancelChildAnimations(element){ var node = extractElementNode(element); 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); } } ); } function cancelAnimations(animations){ var isCancelledFlag = true ; forEach(animations, function (animation){ if (!animation.beforeComplete) { (animation.beforeEnd || noop)(isCancelledFlag); } if (!animation.afterComplete) { (animation.afterEnd || noop)(isCancelledFlag); } } ); } function cleanup(element){ 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); } } function animationsDisabled(element, parentElement){ if (rootAnimateState.disabled) return true ; if (isMatchingElement(element, $rootElement)) { return rootAnimateState.disabled || rootAnimateState.running; } do { if (_AN_Read_length('length', parentElement) === 0) break ; var isRoot = isMatchingElement(parentElement, $rootElement); var state = isRoot? rootAnimateState: parentElement.data(NG_ANIMATE_STATE); var result = state && (!!state.disabled || !!state.running); if (isRoot || result) { return result; } if (isRoot) return true ; } while(parentElement = parentElement.parent())return true ; } } ] ); $animateProvider.register('', ['$window', '$sniffer', '$timeout', '$$animateReflow', function ($window, $sniffer, $timeout, $$animateReflow){ var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT; if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) { CSS_PREFIX = '-webkit-'; TRANSITION_PROP = 'WebkitTransition'; TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend'; } else { TRANSITION_PROP = 'transition'; TRANSITIONEND_EVENT = 'transitionend'; } if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) { CSS_PREFIX = '-webkit-'; ANIMATION_PROP = 'WebkitAnimation'; ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend'; } else { ANIMATION_PROP = 'animation'; ANIMATIONEND_EVENT = 'animationend'; } var DURATION_KEY = 'Duration'; var PROPERTY_KEY = 'Property'; var DELAY_KEY = 'Delay'; var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount'; var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey'; var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data'; 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); elementData.animationCount = animationCounter; cancelAnimationReflow = $$animateReflow(function (){ forEach(animationReflowQueue, function (fn){ fn(); } ); 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){ forEach(elements, function (element){ var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY); if (elementData && elementData.animationCount == count) { (elementData.closeAnimationFn || noop)(); } } ); } function getElementAnimationDetails(element, cacheKey){ var data = cacheKey? lookupCache[cacheKey]: null ; if (!data) { var transitionDuration = 0; var transitionDelay = 0; var animationDuration = 0; var animationDelay = 0; var transitionDelayStyle; var animationDelayStyle; var transitionDurationStyle; var transitionPropertyStyle; forEach(element, function (element){ if (element.nodeType == ELEMENT_NODE) { var elementStyles = $window.getComputedStyle(element) || { } ; transitionDurationStyle = elementStyles[TRANSITION_PROP + DURATION_KEY]; transitionDuration = Math.max(parseMaxTime(transitionDurationStyle), transitionDuration); transitionPropertyStyle = elementStyles[TRANSITION_PROP + PROPERTY_KEY]; transitionDelayStyle = elementStyles[TRANSITION_PROP + DELAY_KEY]; transitionDelay = Math.max(parseMaxTime(transitionDelayStyle), transitionDelay); animationDelayStyle = elementStyles[ANIMATION_PROP + DELAY_KEY]; animationDelay = Math.max(parseMaxTime(animationDelayStyle), animationDelay); var aDuration = parseMaxTime(elementStyles[ANIMATION_PROP + DURATION_KEY]); if (aDuration > 0) { aDuration *= parseInt(elementStyles[ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY], 10) || 1; } animationDuration = Math.max(aDuration, animationDuration); } } ); data = { total: 0, transitionPropertyStyle: transitionPropertyStyle, transitionDurationStyle: transitionDurationStyle, transitionDelayStyle: transitionDelayStyle, transitionDelay: transitionDelay, transitionDuration: transitionDuration, animationDelayStyle: animationDelayStyle, animationDelay: animationDelay, animationDuration: animationDuration} ; if (cacheKey) { lookupCache[cacheKey] = data; } } return data; } function parseMaxTime(str){ var maxValue = 0; var values = angular.isString(str)? str.split(/\s*,\s*/): [] ; forEach(values, function (value){ maxValue = Math.max(parseFloat(value) || 0, maxValue); } ); return maxValue; } function getCacheKey(element){ var parentElement = element.parent(); var parentID = parentElement.data(NG_ANIMATE_PARENT_KEY); if (!parentID) { parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter); parentID = parentCounter; } return parentID + '-' + extractElementNode(element).className; } function animateSetup(element, className, calculationDecorator){ var cacheKey = getCacheKey(element); var eventCacheKey = cacheKey + ' ' + className; var stagger = { } ; var itemIndex = lookupCache[eventCacheKey]? ++lookupCache[eventCacheKey].total: 0; if (itemIndex > 0) { var staggerClassName = className + '-stagger'; var staggerCacheKey = cacheKey + ' ' + staggerClassName; var applyClasses = !lookupCache[staggerCacheKey]; applyClasses && element.addClass(staggerClassName); stagger = getElementAnimationDetails(element, staggerCacheKey); applyClasses && element.removeClass(staggerClassName); } calculationDecorator = calculationDecorator || function (fn){ return fn(); } ; element.addClass(className); var timings = calculationDecorator(function (){ return getElementAnimationDetails(element, eventCacheKey); } ); var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay); var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration); if (maxDuration === 0) { element.removeClass(className); return false ; } var activeClassName = ''; timings.transitionDuration > 0? blockTransitions(element): 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 blockKeyframeAnimations(element){ extractElementNode(element).style[ANIMATION_PROP] = 'none 0s'; } function unblockTransitions(element){ var prop = TRANSITION_PROP + PROPERTY_KEY; var node = extractElementNode(element); if (node.style[prop] && _AN_Read_length('length', node.style[prop]) > 0) { node.style[prop] = ''; } } function unblockKeyframeAnimations(element){ var prop = ANIMATION_PROP; var node = extractElementNode(element); if (node.style[prop] && _AN_Read_length('length', node.style[prop]) > 0) { node.style[prop] = ''; } } function animateRun(element, className, activeAnimationComplete){ var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY); var node = extractElementNode(element); if (node.className.indexOf(className) == -1 || !elementData) { activeAnimationComplete(); return ; } var timings = elementData.timings; var stagger = elementData.stagger; var maxDuration = elementData.maxDuration; var activeClassName = elementData.activeClassName; var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * ONE_SECOND; var startTime = Date.now(); var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT; var itemIndex = elementData.itemIndex; var style = '', appliedStyles = [] ; if (timings.transitionDuration > 0) { var propertyStyle = timings.transitionPropertyStyle; if (propertyStyle.indexOf('all') == -1) { style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ';'; style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ';'; appliedStyles.push(CSS_PREFIX + 'transition-property'); appliedStyles.push(CSS_PREFIX + 'transition-duration'); } } if (itemIndex > 0) { if (stagger.transitionDelay > 0 && stagger.transitionDuration === 0) { var delayStyle = timings.transitionDelayStyle; style += CSS_PREFIX + 'transition-delay: ' + prepareStaggerDelay(delayStyle, stagger.transitionDelay, itemIndex) + '; '; appliedStyles.push(CSS_PREFIX + 'transition-delay'); } if (stagger.animationDelay > 0 && stagger.animationDuration === 0) { style += CSS_PREFIX + 'animation-delay: ' + prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, itemIndex) + '; '; appliedStyles.push(CSS_PREFIX + 'animation-delay'); } } if (_AN_Read_length('length', appliedStyles) > 0) { var oldStyle = _AN_Call_getattribute('getAttribute', node, 'style') || ''; _AN_Call_setattribute('setAttribute', node, 'style', oldStyle + ' ' + style); } element.on(css3AnimationEvents, onAnimationProgress); element.addClass(activeClassName); elementData.closeAnimationFn = function (){ onEnd(); activeAnimationComplete(); } ; return onEnd; function onEnd(cancelled){ element.off(css3AnimationEvents, onAnimationProgress); element.removeClass(activeClassName); animateClose(element, className); var node = extractElementNode(element); for (var i in appliedStyles){ node.style.removeProperty(appliedStyles[i]); } } function onAnimationProgress(event){ event.stopPropagation(); var ev = event.originalEvent || event; var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now(); var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES)); if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) { activeAnimationComplete(); } } } function prepareStaggerDelay(delayStyle, staggerDelay, index){ var style = ''; forEach(delayStyle.split(','), function (val, i){ style += (i > 0? ',': '') + (index * staggerDelay + parseInt(val, 10)) + 's'; } ); return style; } function animateBefore(element, className, calculationDecorator){ if (animateSetup(element, className, calculationDecorator)) { return function (cancelled){ cancelled && animateClose(element, className); } ; } } function animateAfter(element, className, afterAnimationComplete){ if (element.data(NG_ANIMATE_CSS_DATA_KEY)) { return animateRun(element, className, afterAnimationComplete); } else { animateClose(element, className); afterAnimationComplete(); } } function animate(element, className, animationComplete){ var preReflowCancellation = animateBefore(element, className); if (!preReflowCancellation) { animationComplete(); return ; } var cancel = preReflowCancellation; afterReflow(element, function (){ unblockTransitions(element); unblockKeyframeAnimations(element); cancel = animateAfter(element, className, animationComplete); } ); return function (cancelled){ (cancel || noop)(cancelled); } ; } function animateClose(element, className){ element.removeClass(className); element.removeData(NG_ANIMATE_CSS_DATA_KEY); } return { allowCancel: function (element, animationEvent, className){ 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()); 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); } , leave: function (element, animationCompleted){ return animate(element, 'ng-leave', animationCompleted); } , move: function (element, animationCompleted){ return animate(element, 'ng-move', animationCompleted); } , beforeAddClass: function (element, className, animationCompleted){ var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'), function (fn){ element.addClass(className); var timings = fn(); element.removeClass(className); return timings; } ); if (cancellationMethod) { afterReflow(element, function (){ unblockTransitions(element); unblockKeyframeAnimations(element); animationCompleted(); } ); return cancellationMethod; } animationCompleted(); } , addClass: function (element, className, animationCompleted){ return animateAfter(element, suffixClasses(className, '-add'), animationCompleted); } , beforeRemoveClass: function (element, className, animationCompleted){ var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'), function (fn){ var klass = element.attr('class'); element.removeClass(className); var timings = fn(); element.attr('class', klass); return timings; } ); if (cancellationMethod) { afterReflow(element, function (){ unblockTransitions(element); unblockKeyframeAnimations(element); animationCompleted(); } ); return cancellationMethod; } animationCompleted(); } , removeClass: function (element, className, animationCompleted){ return animateAfter(element, suffixClasses(className, '-remove'), animationCompleted); } } ; function suffixClasses(classes, suffix){ var className = ''; classes = angular.isArray(classes)? classes: classes.split(/\s+/); forEach(classes, function (klass, i){ if (klass && _AN_Read_length('length', klass) > 0) { className += (i > 0? ' ': '') + klass + suffix; } } ); return className; } } ] ); } ] ); } )(window, window.angular);