Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
matsko committed Feb 27, 2014
1 parent f4f1f43 commit 5d0713f
Showing 1 changed file with 127 additions and 60 deletions.
187 changes: 127 additions & 60 deletions src/ngAnimate/animate.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,120 @@ angular.module('ngAnimate', ['ng'])
}
}

function animationRunner(element, animationEvent, className) {
var node = element[0];
if(!node) {
throw new Error;
}

var setClassOperation = animationEvent == 'setClass';
var isClassBased = setClassOperation ||
animationEvent == 'addClass' ||
animationEvent == 'removeClass';

var classNameAdd, classNameRemove;
if(angular.isArray(className)) {
classNameAdd = className[0];
classNameRemove = className[1];
className = classNameAdd + ' ' + classNameRemove;
}

var currentClassName = element.attr('class');
var classes = currentClassName + ' ' + className;
if(isAnimatableClassName(classes)) {
throw new Error;
}

var detectedAnimations = lookup(classes);

var cancellations,
before = [],
after = [];

var animationLookup = (' ' + classes).replace(/\s+/g,'.');
var matches = lookup(animationLookup);
for(var i = 0; i < matches.length; i++) {
var animationFactory = matches[i];
var created = registerAnimation(animationFactory, animationEvent);
if(!created && setClassOperation) {
registerAnimation(animationFactory, 'addClass');
registerAnimation(animationFactory, 'removeClass');
}
});

function registerAnimation(animationFactory, event) {
var afterFn = animationFactory[event];
var beforeFn = animationFactory[
'before' + animationEvent.charAt(0).toUpperCase() + animationEvent.substr(1)];
if(afterFn || beforeFn) {
if(event == 'leave') {
beforeFn = afterFn;
afterFn = angular.noop;
}
after.push({
event : event,
fn : afterFn || angular.noop
});
before.push({
event : event,
fn : beforeFn || angular.noop
});
return true;
}
};

function run(animations, onAllComplete) {
cancellations = [];
var count = 0, total = animations.length;

function onComplete() {
if(!cancellations) return;

cancellations[i]();
if(++count < total) return;

cancellations = null;
onAllComplete();
};

angular.forEach(animations, function(animation) {
var cancelFn;
switch(animation.event) {
case 'setClass':
cancelFn = animation.fn(element, classNameAdd, classNameRemove, done);
break;
case 'addClass':
case 'removeClass':
cancelFn = animation.fn(element, className, done);
break;
default:
cancelFn = animation.fn(element, done);
break;
}
cancellations.push(cancelFn || angular.noop);
});
}

return {
isClassBased :
allowAnimations : function() {
return true;
},
before : function(allCompleteFn) {
run(before, allCompleteFn);
},
after : function(allCompleteFn) {
run(before, allCompleteFn);
},
cancelAnmations : function() {
angular.forEach(cancellation, function(cancelFn) {
cancelFn(true);
});
cancellations = null;
}
};
}

/**
* @ngdoc service
* @name $animate
Expand Down Expand Up @@ -622,22 +736,12 @@ angular.module('ngAnimate', ['ng'])
*/
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {

var classNameAdd, classNameRemove, setClassOperation = animationEvent == 'setClass';
if(setClassOperation) {
classNameAdd = className[0];
classNameRemove = className[1];
className = classNameAdd + ' ' + classNameRemove;
}

var currentClassName, classes, node = element[0];
if(node) {
currentClassName = node.className;
classes = currentClassName + ' ' + className;
}

//transcluded directives may sometimes fire an animation using only comment nodes
//best to catch this early on to prevent any animation operations from occurring
if(!node || !isAnimatableClassName(classes)) {
var runner;
try {
runner = animationRunner(element, animationEvent, className);
} catch(e) {
fireDOMOperation();
fireBeforeCallbackAsync();
fireAfterCallbackAsync();
Expand All @@ -648,73 +752,36 @@ angular.module('ngAnimate', ['ng'])
var elementEvents = angular.element._data(node);
elementEvents = elementEvents && elementEvents.events;

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

var matches = lookup(animationLookup);
var isClassBased = animationEvent == 'addClass' ||
animationEvent == 'removeClass' ||
setClassOperation;
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};

var runningAnimations = ngAnimateState.active || {};
var totalActiveAnimations = ngAnimateState.totalActive || 0;
var lastAnimation = ngAnimateState.last;

//only allow animations if the currently running animation is not structural
//or if there is no animation running at all
var skipAnimations = runner.isClassBased ?
!ngAnimateState.disabled && (!lastAnimation || lastAnimation.classBased) :
true;

//skip the animation if animations are disabled, a parent is already being animated,
//the element is not currently attached to the document body or then completely close
//the animation if any matching animations are not found at all.
//NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found.
if (animationsDisabled(element, parentElement) || matches.length === 0) {
if (skipAnimations ||
!runner.allowAnimations() ||
animationsDisabled(element, parentElement)) {
fireDOMOperation();
fireBeforeCallbackAsync();
fireAfterCallbackAsync();
closeAnimation();
return;
}

var animations = [];

//only add animations if the currently running animation is not structural
//or if there is no animation running at all
var allowAnimations = isClassBased ?
!ngAnimateState.disabled && (!lastAnimation || lastAnimation.classBased) :
true;

if(allowAnimations) {
forEach(matches, function(animation) {
//add the animation to the queue to if it is allowed to be cancelled
if(!animation.allowCancel || animation.allowCancel(element, animationEvent, className)) {
var beforeFn, afterFn = animation[animationEvent];

//Special case for a leave animation since there is no point in performing an
//animation on a element node that has already been removed from the DOM
if(animationEvent == 'leave') {
beforeFn = afterFn;
afterFn = null; //this must be falsy so that the animation is skipped for leave
} else {
beforeFn = animation['before' + animationEvent.charAt(0).toUpperCase() + animationEvent.substr(1)];
}
animations.push({
before : beforeFn,
after : afterFn
});
}
});
}

//this would mean that an animation was not allowed so let the existing
//animation do it's thing and close this one early
if(animations.length === 0) {
fireDOMOperation();
fireBeforeCallbackAsync();
fireAfterCallbackAsync();
fireDoneCallbackAsync();
return;
}

var skipAnimation = false;
if(totalActiveAnimations > 0) {
var animationsToCancel = [];
Expand Down

0 comments on commit 5d0713f

Please sign in to comment.