diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js index a55067bc8721..c43401ec32b3 100644 --- a/src/ngAnimate/animate.js +++ b/src/ngAnimate/animate.js @@ -489,6 +489,7 @@ angular.module('ngAnimate', ['ng']) done:done }); + var baseClassName = className; if(event == 'addClass') { className = suffixClasses(className, '-add'); } else if(event == 'removeClass') { @@ -504,7 +505,7 @@ angular.module('ngAnimate', ['ng']) if(animation.start) { if(event == 'addClass' || event == 'removeClass') { - animation.endFn = animation.start(element, className, fn); + animation.endFn = animation.start(element, baseClassName, fn); } else { animation.endFn = animation.start(element, fn); } @@ -520,17 +521,6 @@ angular.module('ngAnimate', ['ng']) }); } - function suffixClasses(classes, suffix) { - var className = ''; - classes = angular.isArray(classes) ? classes : classes.split(/\s+/); - forEach(classes, function(klass, i) { - if(klass && klass.length > 0) { - className += (i > 0 ? ' ' : '') + klass + suffix; - } - }); - return className; - } - function progress(index) { animations[index].done = true; (animations[index].endFn || noop)(); @@ -550,117 +540,128 @@ angular.module('ngAnimate', ['ng']) } } }]); - }]) - .animation('', ['$window','$sniffer', '$timeout', function($window, $sniffer, $timeout) { - var noop = angular.noop; - var forEach = angular.forEach; - function animate(element, className, done) { - if (!($sniffer.transitions || $sniffer.animations)) { - done(); - } else { - var activeClassName = ''; - $timeout(startAnimation, 1, false); - - //this acts as the cancellation function in case - //a new animation is triggered while another animation - //is still going on (otherwise the active className - //would still hang around until the timer is complete). - return onEnd; - } + $animateProvider.register('', ['$window','$sniffer', '$timeout', function($window, $sniffer, $timeout) { + var noop = angular.noop; + var forEach = angular.forEach; + function animate(element, className, done) { + if (!($sniffer.transitions || $sniffer.animations)) { + done(); + } else { + var activeClassName = ''; + $timeout(startAnimation, 1, false); + + //this acts as the cancellation function in case + //a new animation is triggered while another animation + //is still going on (otherwise the active className + //would still hang around until the timer is complete). + return onEnd; + } - function parseMaxTime(str) { - var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : []; - forEach(values, function(value) { - total = Math.max(parseFloat(value) || 0, total); - }); - return total; - } + function parseMaxTime(str) { + var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : []; + forEach(values, function(value) { + total = Math.max(parseFloat(value) || 0, total); + }); + return total; + } - function startAnimation() { - var duration = 0; - forEach(className.split(' '), function(klass, i) { - activeClassName += (i > 0 ? ' ' : '') + klass + '-active'; - }); + function startAnimation() { + var duration = 0; + forEach(className.split(' '), function(klass, i) { + activeClassName += (i > 0 ? ' ' : '') + klass + '-active'; + }); - element.addClass(activeClassName); + element.addClass(activeClassName); - //one day all browsers will have these properties - var w3cAnimationProp = 'animation'; - var w3cTransitionProp = 'transition'; + //one day all browsers will have these properties + var w3cAnimationProp = 'animation'; + var w3cTransitionProp = 'transition'; - //but some still use vendor-prefixed styles - var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation'; - var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition'; + //but some still use vendor-prefixed styles + var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation'; + var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition'; - var durationKey = 'Duration', - delayKey = 'Delay', - animationIterationCountKey = 'IterationCount'; + var durationKey = 'Duration', + delayKey = 'Delay', + animationIterationCountKey = 'IterationCount'; - //we want all the styles defined before and after - var ELEMENT_NODE = 1; - forEach(element, function(element) { - if (element.nodeType == ELEMENT_NODE) { - var elementStyles = $window.getComputedStyle(element) || {}; + //we want all the styles defined before and after + var ELEMENT_NODE = 1; + forEach(element, function(element) { + if (element.nodeType == ELEMENT_NODE) { + var elementStyles = $window.getComputedStyle(element) || {}; - var transitionDelay = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + delayKey]), - parseMaxTime(elementStyles[vendorTransitionProp + delayKey])); + var transitionDelay = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + delayKey]), + parseMaxTime(elementStyles[vendorTransitionProp + delayKey])); - var animationDelay = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + delayKey]), - parseMaxTime(elementStyles[vendorAnimationProp + delayKey])); + var animationDelay = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + delayKey]), + parseMaxTime(elementStyles[vendorAnimationProp + delayKey])); - var transitionDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]), - parseMaxTime(elementStyles[vendorTransitionProp + durationKey])); + var transitionDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]), + parseMaxTime(elementStyles[vendorTransitionProp + durationKey])); - var animationDuration = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + durationKey]), - parseMaxTime(elementStyles[vendorAnimationProp + durationKey])); + var animationDuration = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + durationKey]), + parseMaxTime(elementStyles[vendorAnimationProp + durationKey])); - if(animationDuration > 0) { - animationDuration *= Math.max(parseInt(elementStyles[w3cAnimationProp + animationIterationCountKey]) || 0, - parseInt(elementStyles[vendorAnimationProp + animationIterationCountKey]) || 0, - 1); + if(animationDuration > 0) { + animationDuration *= Math.max(parseInt(elementStyles[w3cAnimationProp + animationIterationCountKey]) || 0, + parseInt(elementStyles[vendorAnimationProp + animationIterationCountKey]) || 0, + 1); + } + + duration = Math.max(animationDelay + animationDuration, + transitionDelay + transitionDuration, + duration); } + }); - duration = Math.max(animationDelay + animationDuration, - transitionDelay + transitionDuration, - duration); - } - }); + $timeout(done, duration * 1000, false); + } - $timeout(done, duration * 1000, false); + //this will automatically be called by $animate so + //there is no need to attach this internally to the + //timeout done method + function onEnd(cancelled) { + element.removeClass(activeClassName); + + //only when the animation is cancelled is the done() + //function not called for this animation therefore + //this must be also called + if(cancelled) { + done(); + } + } } - //this will automatically be called by $animate so - //there is no need to attach this internally to the - //timeout done method - function onEnd(cancelled) { - element.removeClass(activeClassName); - - //only when the animation is cancelled is the done() - //function not called for this animation therefore - //this must be also called - if(cancelled) { - done(); + return { + enter : function(element, done) { + return animate(element, 'ng-enter', done); + }, + leave : function(element, done) { + return animate(element, 'ng-leave', done); + }, + move : function(element, done) { + return animate(element, 'ng-move', done); + }, + addClass : function(element, className, done) { + return animate(element, suffixClasses(className, '-add'), done); + }, + removeClass : function(element, className, done) { + return animate(element, suffixClasses(className, '-remove'), done); } - } - } + }; - return { - enter : function(element, done) { - return animate(element, 'ng-enter', done); - }, - leave : function(element, done) { - return animate(element, 'ng-leave', done); - }, - move : function(element, done) { - return animate(element, 'ng-move', done); - }, - addClass : function(element, className, done) { - return animate(element, className, done); - }, - removeClass : function(element, className, done) { - return animate(element, className, done); - } - }; + }]); + function suffixClasses(classes, suffix) { + var className = ''; + classes = angular.isArray(classes) ? classes : classes.split(/\s+/); + forEach(classes, function(klass, i) { + if(klass && klass.length > 0) { + className += (i > 0 ? ' ' : '') + klass + suffix; + } + }); + return className; + } }]); diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index e5ade9f34df2..30bf6ba7502f 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -1513,4 +1513,36 @@ describe("ngAnimate", function() { }); }); + it("should provide the correct CSS class to the addClass and removeClass callbacks within a JS animation", function() { + module(function($animateProvider) { + $animateProvider.register('.classify', function($timeout) { + return { + removeClass : function(element, className, done) { + element.data('classify','remove-' + className); + done(); + }, + addClass : function(element, className, done) { + element.data('classify','add-' + className); + done(); + } + } + }); + }) + inject(function($compile, $rootScope, $animate, $timeout) { + var element = html($compile('
')($rootScope)); + + $animate.addClass(element, 'super'); + expect(element.data('classify')).toBe('add-super'); + $timeout.flush(); + + $animate.removeClass(element, 'super'); + expect(element.data('classify')).toBe('remove-super'); + $timeout.flush(); + + $animate.addClass(element, 'superguy'); + expect(element.data('classify')).toBe('add-superguy'); + $timeout.flush(); + }); + }); + });