mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-22 21:25:47 +00:00
fix(ngAnimate): $timeout integration and cancel callbacks added
This commit is contained in:
parent
7d69d52acf
commit
15389b0e37
5 changed files with 425 additions and 431 deletions
|
|
@ -69,16 +69,16 @@ describe('Docs Annotations', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
module(function($provide, $animateProvider) {
|
module(function($provide, $animateProvider) {
|
||||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
$provide.value('$window', window = angular.mock.createMockWindow());
|
||||||
$animateProvider.register('.foldout', function($window) {
|
$animateProvider.register('.foldout', function($timeout) {
|
||||||
return {
|
return {
|
||||||
enter : function(element, done) {
|
enter : function(element, done) {
|
||||||
$window.setTimeout(done, 1000);
|
$timeout(done, 1000);
|
||||||
},
|
},
|
||||||
removeClass : function(element, className, done) {
|
removeClass : function(element, className, done) {
|
||||||
$window.setTimeout(done, 500);
|
$timeout(done, 500);
|
||||||
},
|
},
|
||||||
addClass : function(element, className, done) {
|
addClass : function(element, className, done) {
|
||||||
$window.setTimeout(done, 200);
|
$timeout(done, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -112,41 +112,46 @@ describe('Docs Annotations', function() {
|
||||||
expect(foldout.html()).toContain('loading');
|
expect(foldout.html()).toContain('loading');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should download a foldout HTML page and animate the contents', inject(function($httpBackend) {
|
it('should download a foldout HTML page and animate the contents', inject(function($httpBackend, $timeout) {
|
||||||
$httpBackend.expect('GET', url).respond('hello');
|
$httpBackend.expect('GET', url).respond('hello');
|
||||||
|
|
||||||
element.triggerHandler('click');
|
element.triggerHandler('click');
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
window.setTimeout.expect(1).process();
|
$timeout.flushNext(0);
|
||||||
window.setTimeout.expect(1000).process();
|
$timeout.flushNext(1);
|
||||||
|
$timeout.flushNext(0);
|
||||||
|
$timeout.flushNext(1000);
|
||||||
|
|
||||||
var kids = body.children();
|
var kids = body.children();
|
||||||
var foldout = angular.element(kids[kids.length-1]);
|
var foldout = angular.element(kids[kids.length-1]);
|
||||||
expect(foldout.text()).toContain('hello');
|
expect(foldout.text()).toContain('hello');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should hide then show when clicked again', inject(function($httpBackend) {
|
it('should hide then show when clicked again', inject(function($httpBackend, $timeout) {
|
||||||
$httpBackend.expect('GET', url).respond('hello');
|
$httpBackend.expect('GET', url).respond('hello');
|
||||||
|
|
||||||
//enter
|
//enter
|
||||||
element.triggerHandler('click');
|
element.triggerHandler('click');
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
window.setTimeout.expect(1).process();
|
$timeout.flushNext(0);
|
||||||
window.setTimeout.expect(1000).process();
|
$timeout.flushNext(1);
|
||||||
window.setTimeout.expect(0).process();
|
$timeout.flushNext(0);
|
||||||
|
$timeout.flushNext(1000);
|
||||||
|
|
||||||
//hide
|
//hide
|
||||||
element.triggerHandler('click');
|
element.triggerHandler('click');
|
||||||
window.setTimeout.expect(1).process();
|
$timeout.flushNext(1);
|
||||||
window.setTimeout.expect(200).process();
|
$timeout.flushNext(0);
|
||||||
window.setTimeout.expect(0).process();
|
$timeout.flushNext(200);
|
||||||
|
$timeout.flushNext(0);
|
||||||
|
|
||||||
//show
|
//show
|
||||||
element.triggerHandler('click');
|
element.triggerHandler('click');
|
||||||
window.setTimeout.expect(1).process();
|
$timeout.flushNext(1);
|
||||||
window.setTimeout.expect(500).process();
|
$timeout.flushNext(0);
|
||||||
window.setTimeout.expect(0).process();
|
$timeout.flushNext(500);
|
||||||
|
$timeout.flushNext(0);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||||
$provide.factory(name, factory);
|
$provide.factory(name, factory);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$get = function() {
|
this.$get = ['$timeout', function($timeout) {
|
||||||
return {
|
return {
|
||||||
enter : function(element, parent, after, done) {
|
enter : function(element, parent, after, done) {
|
||||||
var afterNode = after && after[after.length - 1];
|
var afterNode = after && after[after.length - 1];
|
||||||
|
|
@ -66,12 +66,12 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||||
forEach(element, function(node) {
|
forEach(element, function(node) {
|
||||||
parentNode.insertBefore(node, afterNextSibling);
|
parentNode.insertBefore(node, afterNextSibling);
|
||||||
});
|
});
|
||||||
(done || noop)();
|
$timeout(done || noop, 0, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
leave : function(element, done) {
|
leave : function(element, done) {
|
||||||
element.remove();
|
element.remove();
|
||||||
(done || noop)();
|
$timeout(done || noop, 0, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
move : function(element, parent, after, done) {
|
move : function(element, parent, after, done) {
|
||||||
|
|
@ -85,7 +85,7 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||||
className :
|
className :
|
||||||
isArray(className) ? className.join(' ') : '';
|
isArray(className) ? className.join(' ') : '';
|
||||||
element.addClass(className);
|
element.addClass(className);
|
||||||
(done || noop)();
|
$timeout(done || noop, 0, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
removeClass : function(element, className, done) {
|
removeClass : function(element, className, done) {
|
||||||
|
|
@ -93,10 +93,10 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||||
className :
|
className :
|
||||||
isArray(className) ? className.join(' ') : '';
|
isArray(className) ? className.join(' ') : '';
|
||||||
element.removeClass(className);
|
element.removeClass(className);
|
||||||
(done || noop)();
|
$timeout(done || noop, 0, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
enabled : noop
|
enabled : noop
|
||||||
};
|
};
|
||||||
};
|
}];
|
||||||
}];
|
}];
|
||||||
|
|
|
||||||
|
|
@ -203,15 +203,15 @@ angular.module('ngAnimate', ['ng'])
|
||||||
var NG_ANIMATE_STATE = '$$ngAnimateState';
|
var NG_ANIMATE_STATE = '$$ngAnimateState';
|
||||||
var rootAnimateState = {running:true};
|
var rootAnimateState = {running:true};
|
||||||
|
|
||||||
$provide.decorator('$animate', ['$delegate', '$injector', '$window', '$sniffer', '$rootElement',
|
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement',
|
||||||
function($delegate, $injector, $window, $sniffer, $rootElement) {
|
function($delegate, $injector, $sniffer, $rootElement) {
|
||||||
|
|
||||||
var noop = angular.noop;
|
var noop = angular.noop;
|
||||||
var forEach = angular.forEach;
|
var forEach = angular.forEach;
|
||||||
|
|
||||||
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
|
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
|
||||||
|
|
||||||
function lookup(name) {
|
function lookup(name) {
|
||||||
if (name) {
|
if (name) {
|
||||||
var classes = name.substr(1).split('.'),
|
var classes = name.substr(1).split('.'),
|
||||||
classMap = {};
|
classMap = {};
|
||||||
|
|
@ -241,7 +241,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
/**
|
/**
|
||||||
* @ngdoc object
|
* @ngdoc object
|
||||||
* @name ngAnimate.$animate
|
* @name ngAnimate.$animate
|
||||||
* @requires $window, $sniffer, $rootElement
|
* @requires $timeout, $sniffer, $rootElement
|
||||||
* @function
|
* @function
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
|
|
@ -444,80 +444,72 @@ angular.module('ngAnimate', ['ng'])
|
||||||
and the onComplete callback will be fired once the animation is fully complete.
|
and the onComplete callback will be fired once the animation is fully complete.
|
||||||
*/
|
*/
|
||||||
function performAnimation(event, className, element, parent, after, onComplete) {
|
function performAnimation(event, className, element, parent, after, onComplete) {
|
||||||
if(nothingToAnimate(className, element)) {
|
var classes = ((element.attr('class') || '') + ' ' + className),
|
||||||
|
animationLookup = (' ' + classes).replace(/\s+/g,'.'),
|
||||||
|
animations = [];
|
||||||
|
forEach(lookup(animationLookup), function(animation, index) {
|
||||||
|
animations.push({
|
||||||
|
start : animation[event]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!parent) {
|
||||||
|
parent = after ? after.parent() : element.parent();
|
||||||
|
}
|
||||||
|
var disabledAnimation = { running : true };
|
||||||
|
|
||||||
|
//skip the animation if animations are disabled, a parent is already being animated
|
||||||
|
//or the element is not currently attached to the document body.
|
||||||
|
if ((parent.inheritedData(NG_ANIMATE_STATE) || disabledAnimation).running) {
|
||||||
|
//avoid calling done() since there is no need to remove any
|
||||||
|
//data or className values since this happens earlier than that
|
||||||
(onComplete || noop)();
|
(onComplete || noop)();
|
||||||
} else {
|
return;
|
||||||
var classes = ((element.attr('class') || '') + ' ' + className),
|
}
|
||||||
animationLookup = (' ' + classes).replace(/\s+/g,'.'),
|
|
||||||
animations = [];
|
|
||||||
forEach(lookup(animationLookup), function(animation, index) {
|
|
||||||
animations.push({
|
|
||||||
start : animation[event],
|
|
||||||
done : false
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!parent) {
|
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
|
||||||
parent = after ? after.parent() : element.parent();
|
|
||||||
}
|
|
||||||
var disabledAnimation = { running : true };
|
|
||||||
|
|
||||||
//skip the animation if animations are disabled, a parent is already being animated
|
//if an animation is currently running on the element then lets take the steps
|
||||||
//or the element is not currently attached to the document body.
|
//to cancel that animation and fire any required callbacks
|
||||||
if ((parent.inheritedData(NG_ANIMATE_STATE) || disabledAnimation).running) {
|
if(ngAnimateState.running) {
|
||||||
//avoid calling done() since there is no need to remove any
|
cancelAnimations(ngAnimateState.animations);
|
||||||
//data or className values since this happens earlier than that
|
ngAnimateState.done();
|
||||||
(onComplete || noop)();
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var animationData = element.data(NG_ANIMATE_STATE) || {};
|
element.data(NG_ANIMATE_STATE, {
|
||||||
|
running:true,
|
||||||
|
animations:animations,
|
||||||
|
done:done
|
||||||
|
});
|
||||||
|
|
||||||
//if an animation is currently running on the element then lets take the steps
|
if(event == 'addClass') {
|
||||||
//to cancel that animation and fire any required callbacks
|
className = suffixClasses(className, '-add');
|
||||||
if(animationData.running) {
|
} else if(event == 'removeClass') {
|
||||||
cancelAnimations(animationData.animations);
|
className = suffixClasses(className, '-remove');
|
||||||
animationData.done();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
element.data(NG_ANIMATE_STATE, {
|
element.addClass(className);
|
||||||
running:true,
|
|
||||||
animations:animations,
|
|
||||||
done:done
|
|
||||||
});
|
|
||||||
|
|
||||||
if(event == 'addClass') {
|
forEach(animations, function(animation, index) {
|
||||||
className = suffixClasses(className, '-add');
|
var fn = function() {
|
||||||
} else if(event == 'removeClass') {
|
progress(index);
|
||||||
className = suffixClasses(className, '-remove');
|
};
|
||||||
}
|
|
||||||
|
|
||||||
element.addClass(className);
|
if(animation.start) {
|
||||||
|
if(event == 'addClass' || event == 'removeClass') {
|
||||||
forEach(animations, function(animation, index) {
|
animation.endFn = animation.start(element, className, fn);
|
||||||
var fn = function() {
|
|
||||||
progress(index);
|
|
||||||
};
|
|
||||||
|
|
||||||
if(animation.start) {
|
|
||||||
if(event == 'addClass' || event == 'removeClass') {
|
|
||||||
animation.cancel = animation.start(element, className, fn);
|
|
||||||
} else {
|
|
||||||
animation.cancel = animation.start(element, fn);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
fn();
|
animation.endFn = animation.start(element, fn);
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
}
|
fn();
|
||||||
|
}
|
||||||
function nothingToAnimate(className, element) {
|
});
|
||||||
return !(className && className.length > 0 && element.length > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancelAnimations(animations) {
|
function cancelAnimations(animations) {
|
||||||
|
var isCancelledFlag = true;
|
||||||
forEach(animations, function(animation) {
|
forEach(animations, function(animation) {
|
||||||
(animation.cancel || noop)(element);
|
(animation.endFn || noop)(isCancelledFlag);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -534,6 +526,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
|
|
||||||
function progress(index) {
|
function progress(index) {
|
||||||
animations[index].done = true;
|
animations[index].done = true;
|
||||||
|
(animations[index].endFn || noop)();
|
||||||
for(var i=0;i<animations.length;i++) {
|
for(var i=0;i<animations.length;i++) {
|
||||||
if(!animations[i].done) return;
|
if(!animations[i].done) return;
|
||||||
}
|
}
|
||||||
|
|
@ -552,7 +545,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
}]);
|
}]);
|
||||||
}])
|
}])
|
||||||
|
|
||||||
.animation('', ['$window','$sniffer', function($window, $sniffer) {
|
.animation('', ['$window','$sniffer', '$timeout', function($window, $sniffer, $timeout) {
|
||||||
return {
|
return {
|
||||||
enter : function(element, done) {
|
enter : function(element, done) {
|
||||||
return animate(element, 'ng-enter', done);
|
return animate(element, 'ng-enter', done);
|
||||||
|
|
@ -576,13 +569,13 @@ angular.module('ngAnimate', ['ng'])
|
||||||
done();
|
done();
|
||||||
} else {
|
} else {
|
||||||
var activeClassName = '';
|
var activeClassName = '';
|
||||||
$window.setTimeout(startAnimation, 1);
|
$timeout(startAnimation, 1, false);
|
||||||
|
|
||||||
//this acts as the cancellation function in case
|
//this acts as the cancellation function in case
|
||||||
//a new animation is triggered while another animation
|
//a new animation is triggered while another animation
|
||||||
//is still going on (otherwise the active className
|
//is still going on (otherwise the active className
|
||||||
//would still hang around until the timer is complete).
|
//would still hang around until the timer is complete).
|
||||||
return onComplete;
|
return onEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseMaxTime(str) {
|
function parseMaxTime(str) {
|
||||||
|
|
@ -643,12 +636,21 @@ angular.module('ngAnimate', ['ng'])
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$window.setTimeout(onComplete, duration * 1000);
|
$timeout(done, duration * 1000, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onComplete() {
|
//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);
|
element.removeClass(activeClassName);
|
||||||
done();
|
|
||||||
|
//only when the animation is cancelled is the done()
|
||||||
|
//function not called for this animation therefore
|
||||||
|
//this must be also called
|
||||||
|
if(cancelled) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -623,7 +623,7 @@ describe('ngView animations', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
inject(function($rootScope, $compile, $location, $route, $window, $rootElement, $sniffer, $animate) {
|
inject(function($rootScope, $compile, $location, $route, $timeout, $rootElement, $sniffer, $animate) {
|
||||||
element = $compile(html('<div><ng:view onload="load()" class="my-animation"></ng:view></div>'))($rootScope);
|
element = $compile(html('<div><ng:view onload="load()" class="my-animation"></ng:view></div>'))($rootScope);
|
||||||
$animate.enabled(true);
|
$animate.enabled(true);
|
||||||
|
|
||||||
|
|
@ -632,20 +632,12 @@ describe('ngView animations', function() {
|
||||||
|
|
||||||
$animate.process('enter'); //ngView
|
$animate.process('enter'); //ngView
|
||||||
|
|
||||||
if($sniffer.transitions) {
|
$timeout.flush();
|
||||||
$window.setTimeout.expect(1).process();
|
|
||||||
$window.setTimeout.expect(0).process();
|
|
||||||
}
|
|
||||||
|
|
||||||
$animate.process('enter'); //repeat 1
|
$animate.process('enter'); //repeat 1
|
||||||
$animate.process('enter'); //repeat 2
|
$animate.process('enter'); //repeat 2
|
||||||
|
|
||||||
if($sniffer.transitions) {
|
$timeout.flush();
|
||||||
$window.setTimeout.expect(1).process();
|
|
||||||
$window.setTimeout.expect(1).process();
|
|
||||||
$window.setTimeout.expect(0).process();
|
|
||||||
$window.setTimeout.expect(0).process();
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(element.text()).toEqual('12');
|
expect(element.text()).toEqual('12');
|
||||||
|
|
||||||
|
|
@ -653,29 +645,17 @@ describe('ngView animations', function() {
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
|
||||||
$animate.process('leave'); //ngView old
|
$animate.process('leave'); //ngView old
|
||||||
if($sniffer.transitions) {
|
$timeout.flush();
|
||||||
$window.setTimeout.expect(1).process();
|
|
||||||
$window.setTimeout.expect(0).process();
|
|
||||||
}
|
|
||||||
|
|
||||||
$animate.process('enter'); //ngView new
|
$animate.process('enter'); //ngView new
|
||||||
if($sniffer.transitions) {
|
$timeout.flush();
|
||||||
$window.setTimeout.expect(1).process();
|
|
||||||
$window.setTimeout.expect(0).process();
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(n(element.text())).toEqual(''); //this is midway during the animation
|
expect(n(element.text())).toEqual(''); //this is midway during the animation
|
||||||
|
|
||||||
$animate.process('enter'); //ngRepeat 3
|
$animate.process('enter'); //ngRepeat 3
|
||||||
$animate.process('enter'); //ngRepeat 4
|
$animate.process('enter'); //ngRepeat 4
|
||||||
|
|
||||||
|
$timeout.flush();
|
||||||
if($sniffer.transitions) {
|
|
||||||
$window.setTimeout.expect(1).process();
|
|
||||||
$window.setTimeout.expect(1).process();
|
|
||||||
$window.setTimeout.expect(0).process();
|
|
||||||
$window.setTimeout.expect(0).process();
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(element.text()).toEqual('34');
|
expect(element.text()).toEqual('34');
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue