Skip to content

Commit 5d0713f

Browse files
committed
wip
1 parent f4f1f43 commit 5d0713f

File tree

1 file changed

+127
-60
lines changed

1 file changed

+127
-60
lines changed

src/ngAnimate/animate.js

+127-60
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,120 @@ angular.module('ngAnimate', ['ng'])
347347
}
348348
}
349349

350+
function animationRunner(element, animationEvent, className) {
351+
var node = element[0];
352+
if(!node) {
353+
throw new Error;
354+
}
355+
356+
var setClassOperation = animationEvent == 'setClass';
357+
var isClassBased = setClassOperation ||
358+
animationEvent == 'addClass' ||
359+
animationEvent == 'removeClass';
360+
361+
var classNameAdd, classNameRemove;
362+
if(angular.isArray(className)) {
363+
classNameAdd = className[0];
364+
classNameRemove = className[1];
365+
className = classNameAdd + ' ' + classNameRemove;
366+
}
367+
368+
var currentClassName = element.attr('class');
369+
var classes = currentClassName + ' ' + className;
370+
if(isAnimatableClassName(classes)) {
371+
throw new Error;
372+
}
373+
374+
var detectedAnimations = lookup(classes);
375+
376+
var cancellations,
377+
before = [],
378+
after = [];
379+
380+
var animationLookup = (' ' + classes).replace(/\s+/g,'.');
381+
var matches = lookup(animationLookup);
382+
for(var i = 0; i < matches.length; i++) {
383+
var animationFactory = matches[i];
384+
var created = registerAnimation(animationFactory, animationEvent);
385+
if(!created && setClassOperation) {
386+
registerAnimation(animationFactory, 'addClass');
387+
registerAnimation(animationFactory, 'removeClass');
388+
}
389+
});
390+
391+
function registerAnimation(animationFactory, event) {
392+
var afterFn = animationFactory[event];
393+
var beforeFn = animationFactory[
394+
'before' + animationEvent.charAt(0).toUpperCase() + animationEvent.substr(1)];
395+
if(afterFn || beforeFn) {
396+
if(event == 'leave') {
397+
beforeFn = afterFn;
398+
afterFn = angular.noop;
399+
}
400+
after.push({
401+
event : event,
402+
fn : afterFn || angular.noop
403+
});
404+
before.push({
405+
event : event,
406+
fn : beforeFn || angular.noop
407+
});
408+
return true;
409+
}
410+
};
411+
412+
function run(animations, onAllComplete) {
413+
cancellations = [];
414+
var count = 0, total = animations.length;
415+
416+
function onComplete() {
417+
if(!cancellations) return;
418+
419+
cancellations[i]();
420+
if(++count < total) return;
421+
422+
cancellations = null;
423+
onAllComplete();
424+
};
425+
426+
angular.forEach(animations, function(animation) {
427+
var cancelFn;
428+
switch(animation.event) {
429+
case 'setClass':
430+
cancelFn = animation.fn(element, classNameAdd, classNameRemove, done);
431+
break;
432+
case 'addClass':
433+
case 'removeClass':
434+
cancelFn = animation.fn(element, className, done);
435+
break;
436+
default:
437+
cancelFn = animation.fn(element, done);
438+
break;
439+
}
440+
cancellations.push(cancelFn || angular.noop);
441+
});
442+
}
443+
444+
return {
445+
isClassBased :
446+
allowAnimations : function() {
447+
return true;
448+
},
449+
before : function(allCompleteFn) {
450+
run(before, allCompleteFn);
451+
},
452+
after : function(allCompleteFn) {
453+
run(before, allCompleteFn);
454+
},
455+
cancelAnmations : function() {
456+
angular.forEach(cancellation, function(cancelFn) {
457+
cancelFn(true);
458+
});
459+
cancellations = null;
460+
}
461+
};
462+
}
463+
350464
/**
351465
* @ngdoc service
352466
* @name $animate
@@ -622,22 +736,12 @@ angular.module('ngAnimate', ['ng'])
622736
*/
623737
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
624738

625-
var classNameAdd, classNameRemove, setClassOperation = animationEvent == 'setClass';
626-
if(setClassOperation) {
627-
classNameAdd = className[0];
628-
classNameRemove = className[1];
629-
className = classNameAdd + ' ' + classNameRemove;
630-
}
631-
632-
var currentClassName, classes, node = element[0];
633-
if(node) {
634-
currentClassName = node.className;
635-
classes = currentClassName + ' ' + className;
636-
}
637-
638739
//transcluded directives may sometimes fire an animation using only comment nodes
639740
//best to catch this early on to prevent any animation operations from occurring
640-
if(!node || !isAnimatableClassName(classes)) {
741+
var runner;
742+
try {
743+
runner = animationRunner(element, animationEvent, className);
744+
} catch(e) {
641745
fireDOMOperation();
642746
fireBeforeCallbackAsync();
643747
fireAfterCallbackAsync();
@@ -648,73 +752,36 @@ angular.module('ngAnimate', ['ng'])
648752
var elementEvents = angular.element._data(node);
649753
elementEvents = elementEvents && elementEvents.events;
650754

651-
var animationLookup = (' ' + classes).replace(/\s+/g,'.');
652755
if (!parentElement) {
653756
parentElement = afterElement ? afterElement.parent() : element.parent();
654757
}
655758

656-
var matches = lookup(animationLookup);
657-
var isClassBased = animationEvent == 'addClass' ||
658-
animationEvent == 'removeClass' ||
659-
setClassOperation;
660759
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
661760

662761
var runningAnimations = ngAnimateState.active || {};
663762
var totalActiveAnimations = ngAnimateState.totalActive || 0;
664763
var lastAnimation = ngAnimateState.last;
665764

765+
//only allow animations if the currently running animation is not structural
766+
//or if there is no animation running at all
767+
var skipAnimations = runner.isClassBased ?
768+
!ngAnimateState.disabled && (!lastAnimation || lastAnimation.classBased) :
769+
true;
770+
666771
//skip the animation if animations are disabled, a parent is already being animated,
667772
//the element is not currently attached to the document body or then completely close
668773
//the animation if any matching animations are not found at all.
669774
//NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found.
670-
if (animationsDisabled(element, parentElement) || matches.length === 0) {
775+
if (skipAnimations ||
776+
!runner.allowAnimations() ||
777+
animationsDisabled(element, parentElement)) {
671778
fireDOMOperation();
672779
fireBeforeCallbackAsync();
673780
fireAfterCallbackAsync();
674781
closeAnimation();
675782
return;
676783
}
677784

678-
var animations = [];
679-
680-
//only add animations if the currently running animation is not structural
681-
//or if there is no animation running at all
682-
var allowAnimations = isClassBased ?
683-
!ngAnimateState.disabled && (!lastAnimation || lastAnimation.classBased) :
684-
true;
685-
686-
if(allowAnimations) {
687-
forEach(matches, function(animation) {
688-
//add the animation to the queue to if it is allowed to be cancelled
689-
if(!animation.allowCancel || animation.allowCancel(element, animationEvent, className)) {
690-
var beforeFn, afterFn = animation[animationEvent];
691-
692-
//Special case for a leave animation since there is no point in performing an
693-
//animation on a element node that has already been removed from the DOM
694-
if(animationEvent == 'leave') {
695-
beforeFn = afterFn;
696-
afterFn = null; //this must be falsy so that the animation is skipped for leave
697-
} else {
698-
beforeFn = animation['before' + animationEvent.charAt(0).toUpperCase() + animationEvent.substr(1)];
699-
}
700-
animations.push({
701-
before : beforeFn,
702-
after : afterFn
703-
});
704-
}
705-
});
706-
}
707-
708-
//this would mean that an animation was not allowed so let the existing
709-
//animation do it's thing and close this one early
710-
if(animations.length === 0) {
711-
fireDOMOperation();
712-
fireBeforeCallbackAsync();
713-
fireAfterCallbackAsync();
714-
fireDoneCallbackAsync();
715-
return;
716-
}
717-
718785
var skipAnimation = false;
719786
if(totalActiveAnimations > 0) {
720787
var animationsToCancel = [];

0 commit comments

Comments
 (0)