diff options
| -rw-r--r-- | src/ngAnimate/animate.js | 40 | ||||
| -rw-r--r-- | test/ngAnimate/animateSpec.js | 39 |
2 files changed, 59 insertions, 20 deletions
diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js index b1ea7d3d..99b42f38 100644 --- a/src/ngAnimate/animate.js +++ b/src/ngAnimate/animate.js @@ -268,6 +268,20 @@ angular.module('ngAnimate', ['ng']) }; }]) + .factory('$$asyncQueueBuffer', ['$timeout', function($timeout) { + var timer, queue = []; + return function(fn) { + $timeout.cancel(timer); + queue.push(fn); + timer = $timeout(function() { + for(var i = 0; i < queue.length; i++) { + queue[i](); + } + queue = []; + }, 0, false); + }; + }]) + .config(['$provide', '$animateProvider', function($provide, $animateProvider) { var noop = angular.noop; var forEach = angular.forEach; @@ -291,9 +305,10 @@ angular.module('ngAnimate', ['ng']) return extractElementNode(elm1) == extractElementNode(elm2); } - $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document', - function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document) { + $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$$asyncQueueBuffer', '$rootScope', '$document', + function($delegate, $injector, $sniffer, $rootElement, $$asyncQueueBuffer, $rootScope, $document) { + var globalAnimationCounter = 0; $rootElement.data(NG_ANIMATE_STATE, rootAnimateState); // disable animations during bootstrap, but once we bootstrapped, wait again @@ -315,10 +330,6 @@ angular.module('ngAnimate', ['ng']) return classNameFilter.test(className); }; - function async(fn) { - return $timeout(fn, 0, false); - } - function lookup(name) { if (name) { var matches = [], @@ -685,7 +696,6 @@ angular.module('ngAnimate', ['ng']) 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 - $timeout.cancel(ngAnimateState.closeAnimationTimeout); cleanup(element); cancelAnimations(ngAnimateState.animations); @@ -736,12 +746,15 @@ angular.module('ngAnimate', ['ng']) //parent animations to find and cancel child animations when needed element.addClass(NG_ANIMATE_CLASS_NAME); + var localAnimationCount = globalAnimationCounter++; + element.data(NG_ANIMATE_STATE, { running:true, event:animationEvent, className:className, structural:!isClassBased, animations:animations, + index:localAnimationCount, done:onBeforeAnimationsComplete }); @@ -816,19 +829,19 @@ angular.module('ngAnimate', ['ng']) } function fireBeforeCallbackAsync() { - async(function() { + $$asyncQueueBuffer(function() { fireDOMCallback('before'); }); } function fireAfterCallbackAsync() { - async(function() { + $$asyncQueueBuffer(function() { fireDOMCallback('after'); }); } function fireDoneCallbackAsync() { - async(function() { + $$asyncQueueBuffer(function() { fireDOMCallback('close'); doneCallback && doneCallback(); }); @@ -855,8 +868,11 @@ angular.module('ngAnimate', ['ng']) if(isClassBased) { cleanup(element); } else { - data.closeAnimationTimeout = async(function() { - cleanup(element); + $$asyncQueueBuffer(function() { + var data = element.data(NG_ANIMATE_STATE) || {}; + if(localAnimationCount == data.index) { + cleanup(element); + } }); element.data(NG_ANIMATE_STATE, data); } diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index d626d60e..41115e42 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -2563,8 +2563,9 @@ describe("ngAnimate", function() { }); - it("should disable all child animations on structural animations until the post animation timeout has passed", function() { - var intercepted; + it("should disable all child animations on structural animations until the post animation" + + "timeout has passed as well as all structural animations", function() { + var intercepted, continueAnimation; module(function($animateProvider) { $animateProvider.register('.animated', function() { return { @@ -2578,7 +2579,10 @@ describe("ngAnimate", function() { function ani(type) { return function(element, className, done) { intercepted = type; - (done || className)(); + continueAnimation = function() { + continueAnimation = angular.noop; + (done || className)(); + } } } }); @@ -2595,26 +2599,45 @@ describe("ngAnimate", function() { var child2 = $compile('<div class="child2 animated">...</div>')($rootScope); var container = $compile('<div class="container">...</div>')($rootScope); - jqLite($document[0].body).append($rootElement); + var body = angular.element($document[0].body); + body.append($rootElement); $rootElement.append(container); element.append(child1); element.append(child2); - $animate.move(element, null, container); + $animate.enter(element, container); $rootScope.$digest(); - expect(intercepted).toBe('move'); + expect(intercepted).toBe('enter'); + continueAnimation(); $animate.addClass(child1, 'test'); expect(child1.hasClass('test')).toBe(true); - expect(intercepted).toBe('move'); + expect(element.children().length).toBe(2); + + expect(intercepted).toBe('enter'); $animate.leave(child1); $rootScope.$digest(); + expect(element.children().length).toBe(1); + + expect(intercepted).toBe('enter'); + + $animate.move(element, null, container); + $rootScope.$digest(); + expect(intercepted).toBe('move'); - //reflow has passed + //flush the enter reflow + $timeout.flush(); + + $animate.addClass(child2, 'testing'); + expect(intercepted).toBe('move'); + + continueAnimation(); + + //flush the move reflow $timeout.flush(); $animate.leave(child2); |
