vendor/assets/javascripts/angular-animate.js in angularjs-rails-1.2.0.rc1 vs vendor/assets/javascripts/angular-animate.js in angularjs-rails-1.2.0.rc2
- old
+ new
@@ -1,31 +1,26 @@
/**
- * @license AngularJS v1.2.0rc1
+ * @license AngularJS v1.2.0-rc.2
* (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
/**
* @ngdoc overview
* @name ngAnimate
* @description
*
- * ngAnimate
- * =========
+ * # ngAnimate
*
- * The ngAnimate module is an optional module that comes packed with AngularJS that can be included within an AngularJS
- * application to provide support for CSS and JavaScript animation hooks.
+ * `ngAnimate` is an optional module that provides CSS and JavaScript animation hooks.
*
- * To make use of animations with AngularJS, the `angular-animate.js` JavaScript file must be included into your application
- * and the `ngAnimate` module must be included as a dependency.
+ * {@installModule animate}
*
- * <pre>
- * angular.module('App', ['ngAnimate']);
- * </pre>
+ * # Usage
*
- * Then, to see animations in action, all that is required is to define the appropriate CSS classes
+ * To see animations in action, all that is required is to define the appropriate CSS classes
* or to register a JavaScript animation via the $animation service. The directives that support animation automatically are:
* `ngRepeat`, `ngInclude`, `ngSwitch`, `ngShow`, `ngHide` and `ngView`. Custom directives can take advantage of animation
* by using the `$animate` service.
*
* Below is a more detailed breakdown of the supported animation events provided by pre-existing ng directives:
@@ -51,11 +46,11 @@
* -webkit-transition:0.5s linear all;
* -moz-transition:0.5s linear all;
* -o-transition:0.5s linear all;
* transition:0.5s linear all;
* }
- *
+ *
* .slide.ng-enter { } /* starting animations for enter */
* .slide.ng-enter-active { } /* terminal animations for enter */
* .slide.ng-leave { } /* starting animations for leave */
* .slide.ng-leave-active { } /* terminal animations for leave */
* </style>
@@ -195,26 +190,28 @@
/**
* @ngdoc object
* @name ngAnimate.$animateProvider
* @description
*
- * The $AnimationProvider provider allows developers to register and access custom JavaScript animations directly inside
+ * The `$AnimationProvider` allows developers to register and access custom JavaScript animations directly inside
* of a module. When an animation is triggered, the $animate service will query the $animation function to find any
* animations that match the provided name value.
*
- * Please visit the {@link ngAnimate ngAnimate} module overview page learn more about how to use animations in your application.
+ * 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 NG_ANIMATE_STATE = '$$ngAnimateState';
var rootAnimateState = {running:true};
- $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout',
- function($delegate, $injector, $sniffer, $rootElement, $timeout) {
+ $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope',
+ function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope) {
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
function lookup(name) {
if (name) {
@@ -253,12 +250,14 @@
* as well as any CSS-defined animations against the CSS classes present on the element once the DOM operation is run.
*
* The `$animate` service is used behind the scenes with pre-existing directives and animation with these directives
* will work out of the box without any extra configuration.
*
- * Please visit the {@link ngAnimate ngAnimate} module overview page learn more about how to use animations in your application.
+ * 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.
+ *
*/
return {
/**
* @ngdoc function
* @name ngAnimate.$animate#enter
@@ -273,12 +272,12 @@
*
* | Animation Step | What the element class attribute looks like |
* |----------------------------------------------------------------------------------------------|-----------------------------------------------|
* | 1. $animate.enter(...) is called | class="my-animation" |
* | 2. element is inserted into the parent element or beside the after element | class="my-animation" |
- * | 3. the .ng-enter class is added to the element | class="my-animation ng-enter" |
- * | 4. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-enter" |
+ * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation" |
+ * | 4. the .ng-enter class is added to the element | class="my-animation ng-enter" |
* | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-enter" |
* | 6. the .ng-enter-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-enter ng-enter-active" |
* | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-enter ng-enter-active" |
* | 8. The animation ends and both CSS classes are removed from the element | class="my-animation" |
* | 9. The done() callback is fired (if provided) | class="my-animation" |
@@ -288,11 +287,15 @@
* @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the enter animation
* @param {function()=} done callback function that will be called once the animation is complete
*/
enter : function(element, parent, after, done) {
$delegate.enter(element, parent, after);
- performAnimation('enter', 'ng-enter', element, parent, after, done);
+ $rootScope.$$postDigest(function() {
+ performAnimation('enter', 'ng-enter', element, parent, after, function() {
+ done && $timeout(done, 0, false);
+ });
+ });
},
/**
* @ngdoc function
* @name ngAnimate.$animate#leave
@@ -306,12 +309,12 @@
* Below is a breakdown of each step that occurs during enter animation:
*
* | Animation Step | What the element class attribute looks like |
* |----------------------------------------------------------------------------------------------|----------------------------------------------|
* | 1. $animate.leave(...) is called | class="my-animation" |
- * | 2. the .ng-leave class is added to the element | class="my-animation ng-leave" |
- * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-leave" |
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation" |
+ * | 3. the .ng-leave class is added to the element | class="my-animation ng-leave" |
* | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-leave" |
* | 5. the .ng-leave-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-leave ng-leave-active |
* | 6. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-leave ng-leave-active |
* | 7. The animation ends and both CSS classes are removed from the element | class="my-animation" |
* | 8. The element is removed from the DOM | ... |
@@ -319,12 +322,14 @@
*
* @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
* @param {function()=} done callback function that will be called once the animation is complete
*/
leave : function(element, done) {
- performAnimation('leave', 'ng-leave', element, null, null, function() {
- $delegate.leave(element, done);
+ $rootScope.$$postDigest(function() {
+ performAnimation('leave', 'ng-leave', element, null, null, function() {
+ $delegate.leave(element, done);
+ });
});
},
/**
* @ngdoc function
@@ -341,12 +346,12 @@
*
* | Animation Step | What the element class attribute looks like |
* |----------------------------------------------------------------------------------------------|---------------------------------------------|
* | 1. $animate.move(...) is called | class="my-animation" |
* | 2. element is moved into the parent element or beside the after element | class="my-animation" |
- * | 3. the .ng-move class is added to the element | class="my-animation ng-move" |
- * | 4. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-move" |
+ * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation" |
+ * | 4. the .ng-move class is added to the element | class="my-animation ng-move" |
* | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-move" |
* | 6. the .ng-move-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-move ng-move-active" |
* | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-move ng-move-active" |
* | 8. The animation ends and both CSS classes are removed from the element | class="my-animation" |
* | 9. The done() callback is fired (if provided) | class="my-animation" |
@@ -356,30 +361,35 @@
* @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the move animation
* @param {function()=} done callback function that will be called once the animation is complete
*/
move : function(element, parent, after, done) {
$delegate.move(element, parent, after);
- performAnimation('move', 'ng-move', element, null, null, done);
+ $rootScope.$$postDigest(function() {
+ performAnimation('move', 'ng-move', element, null, null, function() {
+ done && $timeout(done, 0, false);
+ });
+ });
},
/**
* @ngdoc function
* @name ngAnimate.$animate#addClass
* @methodOf ngAnimate.$animate
*
* @description
* Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class.
* Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide
- * the animate service the setup and active CSS classes in order to trigger the animation.
+ * the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if no CSS transitions
+ * or keyframes are defined on the -add CSS class).
*
* Below is a breakdown of each step that occurs during addClass animation:
*
* | Animation Step | What the element class attribute looks like |
* |------------------------------------------------------------------------------------------------|---------------------------------------------|
* | 1. $animate.addClass(element, 'super') is called | class="" |
- * | 2. the .super-add class is added to the element | class="super-add" |
- * | 3. $animate runs any JavaScript-defined animations on the element | class="super-add" |
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="" |
+ * | 3. the .super-add class is added to the element | class="super-add" |
* | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="super-add" |
* | 5. the .super-add-active class is added (this triggers the CSS transition/animation) | class="super-add super-add-active" |
* | 6. $animate waits for X milliseconds for the animation to complete | class="super-add super-add-active" |
* | 7. The animation ends and both CSS classes are removed from the element | class="" |
* | 8. The super class is added to the element | class="super" |
@@ -401,19 +411,20 @@
* @methodOf ngAnimate.$animate
*
* @description
* Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value
* from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in
- * order to provide the animate service the setup and active CSS classes in order to trigger the animation.
+ * order to provide the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if
+ * no CSS transitions or keyframes are defined on the -remove CSS class).
*
* Below is a breakdown of each step that occurs during removeClass animation:
*
* | Animation Step | What the element class attribute looks like |
* |-----------------------------------------------------------------------------------------------|-------------------------------------------------|
* | 1. $animate.removeClass(element, 'super') is called | class="super" |
- * | 2. the .super-remove class is added to the element | class="super super-remove" |
- * | 3. $animate runs any JavaScript-defined animations on the element | class="super super-remove" |
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="super" |
+ * | 3. the .super-remove class is added to the element | class="super super-remove" |
* | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="super super-remove" |
* | 5. the .super-remove-active class is added (this triggers the CSS transition/animation) | class="super super-remove super-remove-active" |
* | 6. $animate waits for X milliseconds for the animation to complete | class="super super-remove super-remove-active" |
* | 7. The animation ends and both CSS all three classes are removed from the element | class="" |
* | 8. The done() callback is fired (if provided) | class="" |
@@ -494,27 +505,18 @@
running:true,
animations:animations,
done:done
});
- var baseClassName = className;
- if(event == 'addClass') {
- className = suffixClasses(className, '-add');
- } else if(event == 'removeClass') {
- className = suffixClasses(className, '-remove');
- }
-
- element.addClass(className);
-
forEach(animations, function(animation, index) {
var fn = function() {
progress(index);
};
if(animation.start) {
if(event == 'addClass' || event == 'removeClass') {
- animation.endFn = animation.start(element, baseClassName, fn);
+ animation.endFn = animation.start(element, className, fn);
} else {
animation.endFn = animation.start(element, fn);
}
} else {
fn();
@@ -538,109 +540,139 @@
}
function done() {
if(!done.hasBeenRun) {
done.hasBeenRun = true;
- element.removeClass(className);
element.removeData(NG_ANIMATE_STATE);
(onComplete || noop)();
}
}
}
}]);
$animateProvider.register('', ['$window','$sniffer', '$timeout', function($window, $sniffer, $timeout) {
var noop = angular.noop;
var forEach = angular.forEach;
+
+ //one day all browsers will have these properties
+ var w3cAnimationProp = 'animation';
+ var w3cTransitionProp = 'transition';
+
+ //but some still use vendor-prefixed styles
+ var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
+ var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
+
+ var durationKey = 'Duration',
+ delayKey = 'Delay',
+ propertyKey = 'Property',
+ animationIterationCountKey = 'IterationCount',
+ ELEMENT_NODE = 1;
+
function animate(element, className, done) {
if (!($sniffer.transitions || $sniffer.animations)) {
done();
- } else {
- var activeClassName = '';
- $timeout(startAnimation, 1, false);
-
- //this acts as the cancellation function in case
- //a new animation is triggered while another animation
- //is still going on (otherwise the active className
- //would still hang around until the timer is complete).
- return onEnd;
+ return;
}
-
- function parseMaxTime(str) {
- var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : [];
- forEach(values, function(value) {
- total = Math.max(parseFloat(value) || 0, total);
+ else if(['ng-enter','ng-leave','ng-move'].indexOf(className) == -1) {
+ var existingDuration = 0;
+ forEach(element, function(element) {
+ if (element.nodeType == ELEMENT_NODE) {
+ var elementStyles = $window.getComputedStyle(element) || {};
+ existingDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
+ parseMaxTime(elementStyles[vendorTransitionProp + durationKey]),
+ existingDuration);
+ }
});
- return total;
+ if(existingDuration > 0) {
+ done();
+ return;
+ }
}
- function startAnimation() {
- var duration = 0;
- forEach(className.split(' '), function(klass, i) {
- activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
- });
+ element.addClass(className);
- element.addClass(activeClassName);
+ //we want all the styles defined before and after
+ var duration = 0;
+ forEach(element, function(element) {
+ if (element.nodeType == ELEMENT_NODE) {
+ var elementStyles = $window.getComputedStyle(element) || {};
- //one day all browsers will have these properties
- var w3cAnimationProp = 'animation';
- var w3cTransitionProp = 'transition';
+ var transitionDelay = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + delayKey]),
+ parseMaxTime(elementStyles[vendorTransitionProp + delayKey]));
- //but some still use vendor-prefixed styles
- var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
- var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
+ var animationDelay = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + delayKey]),
+ parseMaxTime(elementStyles[vendorAnimationProp + delayKey]));
- var durationKey = 'Duration',
- delayKey = 'Delay',
- animationIterationCountKey = 'IterationCount';
+ var transitionDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
+ parseMaxTime(elementStyles[vendorTransitionProp + durationKey]));
- //we want all the styles defined before and after
- var ELEMENT_NODE = 1;
- forEach(element, function(element) {
- if (element.nodeType == ELEMENT_NODE) {
- var elementStyles = $window.getComputedStyle(element) || {};
+ var animationDuration = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + durationKey]),
+ parseMaxTime(elementStyles[vendorAnimationProp + durationKey]));
- var transitionDelay = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + delayKey]),
- parseMaxTime(elementStyles[vendorTransitionProp + delayKey]));
+ if(animationDuration > 0) {
+ animationDuration *= Math.max(parseInt(elementStyles[w3cAnimationProp + animationIterationCountKey]) || 0,
+ parseInt(elementStyles[vendorAnimationProp + animationIterationCountKey]) || 0,
+ 1);
+ }
- var animationDelay = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + delayKey]),
- parseMaxTime(elementStyles[vendorAnimationProp + delayKey]));
+ duration = Math.max(animationDelay + animationDuration,
+ transitionDelay + transitionDuration,
+ duration);
+ }
+ });
- var transitionDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
- parseMaxTime(elementStyles[vendorTransitionProp + durationKey]));
+ /* there is no point in performing a reflow if the animation
+ timeout is empty (this would cause a flicker bug normally
+ in the page */
+ if(duration > 0) {
+ var node = element[0];
- var animationDuration = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + durationKey]),
- parseMaxTime(elementStyles[vendorAnimationProp + durationKey]));
+ //temporarily disable the transition so that the enter styles
+ //don't animate twice (this is here to avoid a bug in Chrome/FF).
+ node.style[w3cTransitionProp + propertyKey] = 'none';
+ node.style[vendorTransitionProp + propertyKey] = 'none';
- if(animationDuration > 0) {
- animationDuration *= Math.max(parseInt(elementStyles[w3cAnimationProp + animationIterationCountKey]) || 0,
- parseInt(elementStyles[vendorAnimationProp + animationIterationCountKey]) || 0,
- 1);
- }
-
- duration = Math.max(animationDelay + animationDuration,
- transitionDelay + transitionDuration,
- duration);
- }
+ var activeClassName = '';
+ forEach(className.split(' '), function(klass, i) {
+ activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
});
+ //this triggers a reflow which allows for the transition animation to kick in
+ element.prop('clientWidth');
+ node.style[w3cTransitionProp + propertyKey] = '';
+ node.style[vendorTransitionProp + propertyKey] = '';
+ element.addClass(activeClassName);
+
$timeout(done, duration * 1000, false);
- }
- //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);
+ //this will automatically be called by $animate so
+ //there is no need to attach this internally to the
+ //timeout done method
+ return function onEnd(cancelled) {
+ element.removeClass(className);
+ element.removeClass(activeClassName);
- //only when the animation is cancelled is the done()
- //function not called for this animation therefore
- //this must be also called
- if(cancelled) {
- 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();
+ }
}
}
+ else {
+ element.removeClass(className);
+ done();
+ }
+
+ function parseMaxTime(str) {
+ var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : [];
+ forEach(values, function(value) {
+ total = Math.max(parseFloat(value) || 0, total);
+ });
+ return total;
+ }
}
return {
enter : function(element, done) {
return animate(element, 'ng-enter', done);
@@ -657,21 +689,20 @@
removeClass : function(element, className, done) {
return animate(element, suffixClasses(className, '-remove'), done);
}
};
+ function suffixClasses(classes, suffix) {
+ var className = '';
+ classes = angular.isArray(classes) ? classes : classes.split(/\s+/);
+ forEach(classes, function(klass, i) {
+ if(klass && klass.length > 0) {
+ className += (i > 0 ? ' ' : '') + klass + suffix;
+ }
+ });
+ return className;
+ }
}]);
-
- function suffixClasses(classes, suffix) {
- var className = '';
- classes = angular.isArray(classes) ? classes : classes.split(/\s+/);
- forEach(classes, function(klass, i) {
- if(klass && klass.length > 0) {
- className += (i > 0 ? ' ' : '') + klass + suffix;
- }
- });
- return className;
- }
}]);
})(window, window.angular);