App-EventStreamr
view release on metacpan or search on metacpan
share/status/app/lib/angular/angular-animate.js view on Meta::CPAN
* <pre>
* var kids = parent.children();
*
* $animate.leave(kids[0]); //stagger index=0
* $animate.leave(kids[1]); //stagger index=1
* $animate.leave(kids[2]); //stagger index=2
* $animate.leave(kids[3]); //stagger index=3
* $animate.leave(kids[4]); //stagger index=4
*
* $timeout(function() {
* //stagger has reset itself
* $animate.leave(kids[5]); //stagger index=0
* $animate.leave(kids[6]); //stagger index=1
* }, 100, false);
* </pre>
*
* Stagger animations are currently only supported within CSS-defined animations.
*
* <h2>JavaScript-defined Animations</h2>
* In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations on browsers that do not
* yet support CSS transitions/animations, then you can make use of JavaScript animations defined inside of your AngularJS module.
*
* <pre>
* //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application.
* var ngModule = angular.module('YourApp', ['ngAnimate']);
* ngModule.animation('.my-crazy-animation', function() {
* return {
* enter: function(element, done) {
* //run the animation here and call done when the animation is complete
* return function(cancelled) {
* //this (optional) function will be called when the animation
* //completes or when the animation is cancelled (the cancelled
* //flag will be set to true if cancelled).
* };
* },
* leave: function(element, done) { },
* move: function(element, done) { },
*
* //animation that can be triggered before the class is added
* beforeAddClass: function(element, className, done) { },
*
* //animation that can be triggered after the class is added
* addClass: function(element, className, done) { },
*
* //animation that can be triggered before the class is removed
* beforeRemoveClass: function(element, className, done) { },
*
* //animation that can be triggered after the class is removed
* removeClass: function(element, className, done) { }
* };
* });
* </pre>
*
* JavaScript-defined animations are created with a CSS-like class selector and a collection of events which are set to run
* a javascript callback function. When an animation is triggered, $animate will look for a matching animation which fits
* the element's CSS class attribute value and then run the matching animation event function (if found).
* In other words, if the CSS classes present on the animated element match any of the JavaScript animations then the callback function will
* be executed. It should be also noted that only simple, single class selectors are allowed (compound class selectors are not supported).
*
* Within a JavaScript animation, an object containing various event callback animation functions is expected to be returned.
* As explained above, these callbacks are triggered based on the animation event. Therefore if an enter animation is run,
* and the JavaScript animation is found, then the enter callback will handle that animation (in addition to the CSS keyframe animation
* or transition code that is defined via a stylesheet).
*
*/
angular.module('ngAnimate', ['ng'])
/**
* @ngdoc object
* @name ngAnimate.$animateProvider
* @description
*
* The `$animateProvider` allows developers to register JavaScript animation event handlers directly inside of a module.
* When an animation is triggered, the $animate service will query the $animate service to find any animations that match
* the provided name value.
*
* Requires the {@link ngAnimate `ngAnimate`} module to be installed.
*
* Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
*
*/
.config(['$provide', '$animateProvider', function($provide, $animateProvider) {
var noop = angular.noop;
var forEach = angular.forEach;
var selectors = $animateProvider.$$selectors;
var ELEMENT_NODE = 1;
var NG_ANIMATE_STATE = '$$ngAnimateState';
var NG_ANIMATE_CLASS_NAME = 'ng-animate';
var rootAnimateState = {running: true};
function extractElementNode(element) {
for(var i = 0; i < element.length; i++) {
var elm = element[i];
if(elm.nodeType == ELEMENT_NODE) {
return elm;
}
}
}
function isMatchingElement(elm1, elm2) {
return extractElementNode(elm1) == extractElementNode(elm2);
}
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document',
function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document) {
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
// disable animations during bootstrap, but once we bootstrapped, wait again
// for another digest until enabling animations. The reason why we digest twice
// is because all structural animations (enter, leave and move) all perform a
// post digest operation before animating. If we only wait for a single digest
// to pass then the structural animation would render its animation on page load.
// (which is what we're trying to avoid when the application first boots up.)
$rootScope.$$postDigest(function() {
$rootScope.$$postDigest(function() {
rootAnimateState.running = false;
});
});
share/status/app/lib/angular/angular-animate.js view on Meta::CPAN
}
var animationLookup = (' ' + classes).replace(/\s+/g,'.');
if (!parentElement) {
parentElement = afterElement ? afterElement.parent() : element.parent();
}
var matches = lookup(animationLookup);
var isClassBased = animationEvent == 'addClass' || animationEvent == 'removeClass';
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
//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) {
fireDOMOperation();
closeAnimation();
return;
}
var animations = [];
//only add animations if the currently running animation is not structural
//or if there is no animation running at all
if(!ngAnimateState.running || !(isClassBased && ngAnimateState.structural)) {
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();
fireDoneCallbackAsync();
return;
}
//this value will be searched for class-based CSS className lookup. Therefore,
//we prefix and suffix the current className value with spaces to avoid substring
//lookups of className tokens
var futureClassName = ' ' + currentClassName + ' ';
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);
//if the class is removed during the reflow then it will revert the styles temporarily
//back to the base class CSS styling causing a jump-like effect to occur. This check
//here ensures that the domOperation is only performed after the reflow has commenced
if(ngAnimateState.beforeComplete) {
(ngAnimateState.done || noop)(true);
} else if(isClassBased && !ngAnimateState.structural) {
//class-based animations will compare element className values after cancelling the
//previous animation to see if the element properties already contain the final CSS
//class and if so then the animation will be skipped. Since the domOperation will
//be performed only after the reflow is complete then our element's className value
//will be invalid. Therefore the same string manipulation that would occur within the
//DOM operation will be performed below so that the class comparison is valid...
futureClassName = ngAnimateState.event == 'removeClass' ?
futureClassName.replace(ngAnimateState.className, '') :
futureClassName + ngAnimateState.className + ' ';
}
}
//There is no point in perform a class-based animation if the element already contains
//(on addClass) or doesn't contain (on removeClass) the className being animated.
//The reason why this is being called after the previous animations are cancelled
//is so that the CSS classes present on the element can be properly examined.
var classNameToken = ' ' + className + ' ';
if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) ||
(animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {
fireDOMOperation();
fireDoneCallbackAsync();
return;
}
//the ng-animate class does nothing, but it's here to allow for
//parent animations to find and cancel child animations when needed
element.addClass(NG_ANIMATE_CLASS_NAME);
element.data(NG_ANIMATE_STATE, {
running:true,
event:animationEvent,
className:className,
structural:!isClassBased,
animations:animations,
done:onBeforeAnimationsComplete
});
//first we run the before animations and when all of those are complete
//then we perform the DOM operation and run the next set of animations
invokeRegisteredAnimationFns(animations, 'before', onBeforeAnimationsComplete);
function onBeforeAnimationsComplete(cancelled) {
fireDOMOperation();
if(cancelled === true) {
closeAnimation();
return;
}
//set the done function to the final done function
//so that the DOM event won't be executed twice by accident
//if the after animation is cancelled as well
var data = element.data(NG_ANIMATE_STATE);
if(data) {
data.done = closeAnimation;
element.data(NG_ANIMATE_STATE, data);
}
invokeRegisteredAnimationFns(animations, 'after', closeAnimation);
}
function invokeRegisteredAnimationFns(animations, phase, allAnimationFnsComplete) {
var endFnName = phase + 'End';
forEach(animations, function(animation, index) {
var animationPhaseCompleted = function() {
progress(index, phase);
};
//there are no before functions for enter + move since the DOM
//operations happen before the performAnimation method fires
if(phase == 'before' && (animationEvent == 'enter' || animationEvent == 'move')) {
animationPhaseCompleted();
return;
}
if(animation[phase]) {
animation[endFnName] = isClassBased ?
animation[phase](element, className, animationPhaseCompleted) :
animation[phase](element, animationPhaseCompleted);
} else {
animationPhaseCompleted();
}
});
function progress(index, phase) {
var phaseCompletionFlag = phase + 'Complete';
var currentAnimation = animations[index];
currentAnimation[phaseCompletionFlag] = true;
(currentAnimation[endFnName] || noop)();
for(var i=0;i<animations.length;i++) {
if(!animations[i][phaseCompletionFlag]) return;
}
allAnimationFnsComplete();
}
}
function fireDoneCallbackAsync() {
doneCallback && $timeout(doneCallback, 0, false);
}
//it is less complicated to use a flag than managing and cancelling
//timeouts containing multiple callbacks.
function fireDOMOperation() {
if(!fireDOMOperation.hasBeenRun) {
fireDOMOperation.hasBeenRun = true;
domOperation();
}
}
function closeAnimation() {
if(!closeAnimation.hasBeenRun) {
closeAnimation.hasBeenRun = true;
var data = element.data(NG_ANIMATE_STATE);
if(data) {
/* only structural animations wait for reflow before removing an
animation, but class-based animations don't. An example of this
failing would be when a parent HTML tag has a ng-class attribute
causing ALL directives below to skip animations during the digest */
if(isClassBased) {
cleanup(element);
} else {
data.closeAnimationTimeout = $timeout(function() {
cleanup(element);
}, 0, false);
element.data(NG_ANIMATE_STATE, data);
}
}
fireDoneCallbackAsync();
}
}
}
function cancelChildAnimations(element) {
var node = extractElementNode(element);
forEach(node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME), function(element) {
element = angular.element(element);
var data = element.data(NG_ANIMATE_STATE);
if(data) {
cancelAnimations(data.animations);
cleanup(element);
}
});
}
function cancelAnimations(animations) {
var isCancelledFlag = true;
forEach(animations, function(animation) {
if(!animations.beforeComplete) {
(animation.beforeEnd || noop)(isCancelledFlag);
}
if(!animations.afterComplete) {
(animation.afterEnd || noop)(isCancelledFlag);
}
});
}
function cleanup(element) {
if(isMatchingElement(element, $rootElement)) {
if(!rootAnimateState.disabled) {
rootAnimateState.running = false;
rootAnimateState.structural = false;
}
( run in 1.021 second using v1.01-cache-2.11-cpan-63c85eba8c4 )