summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxime Vidori <maxime.vidori@gmail.com>2014-08-18 17:48:49 +0200
committerMaxime Vidori <maxime.vidori@gmail.com>2014-08-18 17:48:49 +0200
commit91b9d3197f67213e4e35e9a35ba7196b898731e7 (patch)
tree5e5e96e946d6ba7791f333980a5a4efdef127b83
downloadxstatic-angular-animate-91b9d3197f67213e4e35e9a35ba7196b898731e7.tar.gz
Version 1.2.1 of Angular-Animate
-rw-r--r--.gitignore9
-rw-r--r--MANIFEST.in8
-rw-r--r--README.txt13
-rw-r--r--setup.py27
-rw-r--r--xstatic/__init__.py1
-rw-r--r--xstatic/pkg/__init__.py1
-rw-r--r--xstatic/pkg/angular_animate/__init__.py49
-rwxr-xr-xxstatic/pkg/angular_animate/data/angular-animate.js1226
8 files changed, 1334 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b3085b8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+*.pyc
+*.sw?
+*.sqlite3
+.DS_STORE
+*.egg-info
+.venv
+.tox
+build
+dist
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..69d6cea
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,8 @@
+include README.txt
+recursive-include xstatic/pkg/angular_animate *
+
+global-exclude *.pyc
+global-exclude *.pyo
+global-exclude *.orig
+global-exclude *.rej
+
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..9e5fc6a
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,13 @@
+XStatic-Angular-Animate
+-----------------------
+
+Angular-Animate JavaScript library packaged for setuptools (easy_install) / pip.
+
+This package is intended to be used by **any** project that needs these files.
+
+It intentionally does **not** provide any extra code except some metadata
+**nor** has any extra requirements. You MAY use some minimal support code from
+the XStatic base package, if you like.
+
+You can find more info about the xstatic packaging way in the package `XStatic`.
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..5f4913f
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,27 @@
+from xstatic.pkg import angular_animate as xs
+
+# The README.txt file should be written in reST so that PyPI can use
+# it to generate your project's PyPI page.
+long_description = open('README.txt').read()
+
+from setuptools import setup, find_packages
+
+setup(
+ name=xs.PACKAGE_NAME,
+ version=xs.PACKAGE_VERSION,
+ description=xs.DESCRIPTION,
+ long_description=long_description,
+ classifiers=xs.CLASSIFIERS,
+ keywords=xs.KEYWORDS,
+ maintainer=xs.MAINTAINER,
+ maintainer_email=xs.MAINTAINER_EMAIL,
+ license=xs.LICENSE,
+ url=xs.HOMEPAGE,
+ platforms=xs.PLATFORMS,
+ packages=find_packages(),
+ namespace_packages=['xstatic', 'xstatic.pkg', ],
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=[], # nothing! :)
+ # if you like, you MAY use the 'XStatic' package.
+)
diff --git a/xstatic/__init__.py b/xstatic/__init__.py
new file mode 100644
index 0000000..de40ea7
--- /dev/null
+++ b/xstatic/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/xstatic/pkg/__init__.py b/xstatic/pkg/__init__.py
new file mode 100644
index 0000000..de40ea7
--- /dev/null
+++ b/xstatic/pkg/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/xstatic/pkg/angular_animate/__init__.py b/xstatic/pkg/angular_animate/__init__.py
new file mode 100644
index 0000000..a66d9cc
--- /dev/null
+++ b/xstatic/pkg/angular_animate/__init__.py
@@ -0,0 +1,49 @@
+"""
+XStatic resource package
+
+See package 'XStatic' for documentation and basic tools.
+"""
+
+DISPLAY_NAME = 'Angular-Animate' # official name, upper/lowercase allowed, no spaces
+PACKAGE_NAME = 'XStatic-%s' % DISPLAY_NAME # name used for PyPi
+
+NAME = __name__.split('.')[-1] # package name (e.g. 'foo' or 'foo_bar')
+ # please use a all-lowercase valid python
+ # package name
+
+VERSION = '1.2.1' # version of the packaged files, please use the upstream
+ # version number
+BUILD = '1' # our package build number, so we can release new builds
+ # with fixes for xstatic stuff.
+PACKAGE_VERSION = VERSION + '.' + BUILD # version used for PyPi
+
+DESCRIPTION = "%s %s (XStatic packaging standard)" % (DISPLAY_NAME, VERSION)
+
+PLATFORMS = 'any'
+CLASSIFIERS = []
+KEYWORDS = '%s xstatic' % NAME
+
+# XStatic-* package maintainer:
+MAINTAINER = 'Maxime Vidori'
+MAINTAINER_EMAIL = 'maxime.vidori@enovance.com'
+
+# this refers to the project homepage of the stuff we packaged:
+HOMEPAGE = 'https://angularjs.org/'
+
+# this refers to all files:
+LICENSE = '(same as %s)' % DISPLAY_NAME
+
+from os.path import join, dirname
+BASE_DIR = join(dirname(__file__), 'data')
+# linux package maintainers just can point to their file locations like this:
+#BASE_DIR = '/usr/share/javascript/angular_cookies'
+
+LOCATIONS = {
+ # CDN locations (if no public CDN exists, use an empty dict)
+ # if value is a string, it is a base location, just append relative
+ # path/filename. if value is a dict, do another lookup using the
+ # relative path/filename you want.
+ # your relative path/filenames should usually be without version
+ # information, because either the base dir/url is exactly for this
+ # version or the mapping will care for accessing this version.
+}
diff --git a/xstatic/pkg/angular_animate/data/angular-animate.js b/xstatic/pkg/angular_animate/data/angular-animate.js
new file mode 100755
index 0000000..737ac0e
--- /dev/null
+++ b/xstatic/pkg/angular_animate/data/angular-animate.js
@@ -0,0 +1,1226 @@
+/**
+ * @license AngularJS v1.2.1
+ * (c) 2010-2012 Google, Inc. http://angularjs.org
+ * License: MIT
+ */
+(function(window, angular, undefined) {'use strict';
+
+/* jshint maxlen: false */
+
+/**
+ * @ngdoc overview
+ * @name ngAnimate
+ * @description
+ *
+ * # ngAnimate
+ *
+ * The `ngAnimate` module provides support for JavaScript, CSS3 transition and CSS3 keyframe animation hooks within existing core and custom directives.
+ *
+ * {@installModule animate}
+ *
+ * <div doc-module-components="ngAnimate"></div>
+ *
+ * # Usage
+ *
+ * To see animations in action, all that is required is to define the appropriate CSS classes
+ * or to register a JavaScript animation via the myModule.animation() function. The directives that support animation automatically are:
+ * `ngRepeat`, `ngInclude`, `ngIf`, `ngSwitch`, `ngShow`, `ngHide`, `ngView` and `ngClass`. 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:
+ *
+ * | Directive | Supported Animations |
+ * |---------------------------------------------------------- |----------------------------------------------------|
+ * | {@link ng.directive:ngRepeat#usage_animations ngRepeat} | enter, leave and move |
+ * | {@link ngRoute.directive:ngView#usage_animations ngView} | enter and leave |
+ * | {@link ng.directive:ngInclude#usage_animations ngInclude} | enter and leave |
+ * | {@link ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave |
+ * | {@link ng.directive:ngIf#usage_animations ngIf} | enter and leave |
+ * | {@link ng.directive:ngClass#usage_animations ngClass} | add and remove |
+ * | {@link ng.directive:ngShow#usage_animations ngShow & ngHide} | add and remove (the ng-hide class value) |
+ *
+ * You can find out more information about animations upon visiting each directive page.
+ *
+ * Below is an example of how to apply animations to a directive that supports animation hooks:
+ *
+ * <pre>
+ * <style type="text/css">
+ * .slide.ng-enter, .slide.ng-leave {
+ * -webkit-transition:0.5s linear all;
+ * transition:0.5s linear all;
+ * }
+ *
+ * .slide.ng-enter { } /&#42; starting animations for enter &#42;/
+ * .slide.ng-enter-active { } /&#42; terminal animations for enter &#42;/
+ * .slide.ng-leave { } /&#42; starting animations for leave &#42;/
+ * .slide.ng-leave-active { } /&#42; terminal animations for leave &#42;/
+ * </style>
+ *
+ * <!--
+ * the animate service will automatically add .ng-enter and .ng-leave to the element
+ * to trigger the CSS transition/animations
+ * -->
+ * <ANY class="slide" ng-include="..."></ANY>
+ * </pre>
+ *
+ * Keep in mind that if an animation is running, any child elements cannot be animated until the parent element's
+ * animation has completed.
+ *
+ * <h2>CSS-defined Animations</h2>
+ * The animate service will automatically apply two CSS classes to the animated element and these two CSS classes
+ * are designed to contain the start and end CSS styling. Both CSS transitions and keyframe animations are supported
+ * and can be used to play along with this naming structure.
+ *
+ * The following code below demonstrates how to perform animations using **CSS transitions** with Angular:
+ *
+ * <pre>
+ * <style type="text/css">
+ * /&#42;
+ * The animate class is apart of the element and the ng-enter class
+ * is attached to the element once the enter animation event is triggered
+ * &#42;/
+ * .reveal-animation.ng-enter {
+ * -webkit-transition: 1s linear all; /&#42; Safari/Chrome &#42;/
+ * transition: 1s linear all; /&#42; All other modern browsers and IE10+ &#42;/
+ *
+ * /&#42; The animation preparation code &#42;/
+ * opacity: 0;
+ * }
+ *
+ * /&#42;
+ * Keep in mind that you want to combine both CSS
+ * classes together to avoid any CSS-specificity
+ * conflicts
+ * &#42;/
+ * .reveal-animation.ng-enter.ng-enter-active {
+ * /&#42; The animation code itself &#42;/
+ * opacity: 1;
+ * }
+ * </style>
+ *
+ * <div class="view-container">
+ * <div ng-view class="reveal-animation"></div>
+ * </div>
+ * </pre>
+ *
+ * The following code below demonstrates how to perform animations using **CSS animations** with Angular:
+ *
+ * <pre>
+ * <style type="text/css">
+ * .reveal-animation.ng-enter {
+ * -webkit-animation: enter_sequence 1s linear; /&#42; Safari/Chrome &#42;/
+ * animation: enter_sequence 1s linear; /&#42; IE10+ and Future Browsers &#42;/
+ * }
+ * &#64-webkit-keyframes enter_sequence {
+ * from { opacity:0; }
+ * to { opacity:1; }
+ * }
+ * &#64keyframes enter_sequence {
+ * from { opacity:0; }
+ * to { opacity:1; }
+ * }
+ * </style>
+ *
+ * <div class="view-container">
+ * <div ng-view class="reveal-animation"></div>
+ * </div>
+ * </pre>
+ *
+ * Both CSS3 animations and transitions can be used together and the animate service will figure out the correct duration and delay timing.
+ *
+ * Upon DOM mutation, the event class is added first (something like `ng-enter`), then the browser prepares itself to add
+ * the active class (in this case `ng-enter-active`) which then triggers the animation. The animation module will automatically
+ * detect the CSS code to determine when the animation ends. Once the animation is over then both CSS classes will be
+ * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end
+ * immediately resulting in a DOM element that is at its final state. This final state is when the DOM element
+ * has no CSS transition/animation classes applied to it.
+ *
+ * <h3>CSS Staggering Animations</h3>
+ * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a
+ * curtain-like effect. The ngAnimate module, as of 1.2.0, supports staggering animations and the stagger effect can be
+ * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for
+ * the animation. The style property expected within the stagger class can either be a **transition-delay** or an
+ * **animation-delay** property (or both if your animation contains both transitions and keyframe animations).
+ *
+ * <pre>
+ * .my-animation.ng-enter {
+ * /&#42; standard transition code &#42;/
+ * -webkit-transition: 1s linear all;
+ * transition: 1s linear all;
+ * opacity:0;
+ * }
+ * .my-animation.ng-enter-stagger {
+ * /&#42; this will have a 100ms delay between each successive leave animation &#42;/
+ * -webkit-transition-delay: 0.1s;
+ * transition-delay: 0.1s;
+ *
+ * /&#42; in case the stagger doesn't work then these two values
+ * must be set to 0 to avoid an accidental CSS inheritance &#42;/
+ * -webkit-transition-duration: 0s;
+ * transition-duration: 0s;
+ * }
+ * .my-animation.ng-enter.ng-enter-active {
+ * /&#42; standard transition styles &#42;/
+ * opacity:1;
+ * }
+ * </pre>
+ *
+ * Staggering animations work by default in ngRepeat (so long as the CSS class is defiend). Outside of ngRepeat, to use staggering animations
+ * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this
+ * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation
+ * will also be reset if more than 10ms has passed after the last animation has been fired.
+ *
+ * The following code will issue the **ng-leave-stagger** event on the element provided:
+ *
+ * <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', []);
+ * 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
+ * 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};
+
+ $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, enable animations
+ $rootScope.$$postDigest(function() {
+ rootAnimateState.running = false;
+ });
+
+ function lookup(name) {
+ if (name) {
+ var matches = [],
+ flagMap = {},
+ classes = name.substr(1).split('.');
+
+ //the empty string value is the default animation
+ //operation which performs CSS transition and keyframe
+ //animations sniffing. This is always included for each
+ //element animation procedure if the browser supports
+ //transitions and/or keyframe animations
+ if ($sniffer.transitions || $sniffer.animations) {
+ classes.push('');
+ }
+
+ for(var i=0; i < classes.length; i++) {
+ var klass = classes[i],
+ selectorFactoryName = selectors[klass];
+ if(selectorFactoryName && !flagMap[klass]) {
+ matches.push($injector.get(selectorFactoryName));
+ flagMap[klass] = true;
+ }
+ }
+ return matches;
+ }
+ }
+
+ /**
+ * @ngdoc object
+ * @name ngAnimate.$animate
+ * @function
+ *
+ * @description
+ * The `$animate` service provides animation detection support while performing DOM operations (enter, leave and move) as well as during addClass and removeClass operations.
+ * When any of these operations are run, the $animate service
+ * will examine any JavaScript-defined animations (which are defined by using the $animateProvider provider object)
+ * 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.
+ *
+ * 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
+ * @methodOf ngAnimate.$animate
+ * @function
+ *
+ * @description
+ * Appends the element to the parentElement element that resides in the document and then runs the enter animation. Once
+ * the animation is started, the following CSS classes will be present on the element for the duration of the animation:
+ *
+ * Below is a breakdown of each step that occurs during enter animation:
+ *
+ * | Animation Step | What the element class attribute looks like |
+ * |----------------------------------------------------------------------------------------------|---------------------------------------------|
+ * | 1. $animate.enter(...) is called | class="my-animation" |
+ * | 2. element is inserted into the parentElement element or beside the afterElement element | class="my-animation" |
+ * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
+ * | 4. the .ng-enter class is added to the element | class="my-animation ng-animate ng-enter" |
+ * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-enter" |
+ * | 6. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-enter" |
+ * | 7. the .ng-enter-active and .ng-animate-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" |
+ * | 8. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" |
+ * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
+ * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" |
+ *
+ * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
+ * @param {jQuery/jqLite element} parentElement the parent element of the element that will be the focus of the enter animation
+ * @param {jQuery/jqLite element} afterElement the sibling element (which is the previous element) of the element that will be the focus of the enter animation
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
+ */
+ enter : function(element, parentElement, afterElement, doneCallback) {
+ this.enabled(false, element);
+ $delegate.enter(element, parentElement, afterElement);
+ $rootScope.$$postDigest(function() {
+ performAnimation('enter', 'ng-enter', element, parentElement, afterElement, noop, doneCallback);
+ });
+ },
+
+ /**
+ * @ngdoc function
+ * @name ngAnimate.$animate#leave
+ * @methodOf ngAnimate.$animate
+ * @function
+ *
+ * @description
+ * Runs the leave animation operation and, upon completion, removes the element from the DOM. Once
+ * the animation is started, the following CSS classes will be added for the duration of the animation:
+ *
+ * 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. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
+ * | 3. the .ng-leave class is added to the element | class="my-animation ng-animate ng-leave" |
+ * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-leave" |
+ * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-leave" |
+ * | 6. the .ng-leave-active and .ng-animate-active classes is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" |
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" |
+ * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
+ * | 9. The element is removed from the DOM | ... |
+ * | 10. The doneCallback() callback is fired (if provided) | ... |
+ *
+ * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
+ */
+ leave : function(element, doneCallback) {
+ cancelChildAnimations(element);
+ this.enabled(false, element);
+ $rootScope.$$postDigest(function() {
+ performAnimation('leave', 'ng-leave', element, null, null, function() {
+ $delegate.leave(element);
+ }, doneCallback);
+ });
+ },
+
+ /**
+ * @ngdoc function
+ * @name ngAnimate.$animate#move
+ * @methodOf ngAnimate.$animate
+ * @function
+ *
+ * @description
+ * Fires the move DOM operation. Just before the animation starts, the animate service will either append it into the parentElement container or
+ * add the element directly after the afterElement element if present. Then the move animation will be run. Once
+ * the animation is started, the following CSS classes will be added for the duration of the animation:
+ *
+ * Below is a breakdown of each step that occurs during move animation:
+ *
+ * | Animation Step | What the element class attribute looks like |
+ * |----------------------------------------------------------------------------------------------|---------------------------------------------|
+ * | 1. $animate.move(...) is called | class="my-animation" |
+ * | 2. element is moved into the parentElement element or beside the afterElement element | class="my-animation" |
+ * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
+ * | 4. the .ng-move class is added to the element | class="my-animation ng-animate ng-move" |
+ * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-move" |
+ * | 6. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-move" |
+ * | 7. the .ng-move-active and .ng-animate-active classes is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" |
+ * | 8. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" |
+ * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
+ * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" |
+ *
+ * @param {jQuery/jqLite element} element the element that will be the focus of the move animation
+ * @param {jQuery/jqLite element} parentElement the parentElement element of the element that will be the focus of the move animation
+ * @param {jQuery/jqLite element} afterElement the sibling element (which is the previous element) of the element that will be the focus of the move animation
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
+ */
+ move : function(element, parentElement, afterElement, doneCallback) {
+ cancelChildAnimations(element);
+ this.enabled(false, element);
+ $delegate.move(element, parentElement, afterElement);
+ $rootScope.$$postDigest(function() {
+ performAnimation('move', 'ng-move', element, parentElement, afterElement, noop, doneCallback);
+ });
+ },
+
+ /**
+ * @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 (this will be skipped if no CSS transitions
+ * or keyframes are defined on the -add or base 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="my-animation" |
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
+ * | 3. the .super-add class are added to the element | class="my-animation ng-animate super-add" |
+ * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super-add" |
+ * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate super-add" |
+ * | 6. the .super, .super-add-active and .ng-animate-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active super super-add super-add-active" |
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation super-add super-add-active" |
+ * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation super" |
+ * | 9. The super class is kept on the element | class="my-animation super" |
+ * | 10. The doneCallback() callback is fired (if provided) | class="my-animation super" |
+ *
+ * @param {jQuery/jqLite element} element the element that will be animated
+ * @param {string} className the CSS class that will be added to the element and then animated
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
+ */
+ addClass : function(element, className, doneCallback) {
+ performAnimation('addClass', className, element, null, null, function() {
+ $delegate.addClass(element, className);
+ }, doneCallback);
+ },
+
+ /**
+ * @ngdoc function
+ * @name ngAnimate.$animate#removeClass
+ * @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 (this will be skipped if
+ * no CSS transitions or keyframes are defined on the -remove or base CSS classes).
+ *
+ * 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="my-animation super" |
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation super ng-animate" |
+ * | 3. the .super-remove class are added to the element | class="my-animation super ng-animate super-remove"|
+ * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation super ng-animate super-remove" |
+ * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation super ng-animate super-remove" |
+ * | 6. the .super-remove-active and .ng-animate-active classes are added and .super is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-animate-active super-remove super-remove-active" |
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active super-remove super-remove-active" |
+ * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
+ * | 9. The doneCallback() callback is fired (if provided) | class="my-animation" |
+ *
+ *
+ * @param {jQuery/jqLite element} element the element that will be animated
+ * @param {string} className the CSS class that will be animated and then removed from the element
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
+ */
+ removeClass : function(element, className, doneCallback) {
+ performAnimation('removeClass', className, element, null, null, function() {
+ $delegate.removeClass(element, className);
+ }, doneCallback);
+ },
+
+ /**
+ * @ngdoc function
+ * @name ngAnimate.$animate#enabled
+ * @methodOf ngAnimate.$animate
+ * @function
+ *
+ * @param {boolean=} value If provided then set the animation on or off.
+ * @return {boolean} Current animation state.
+ *
+ * @description
+ * Globally enables/disables animations.
+ *
+ */
+ enabled : function(value, element) {
+ switch(arguments.length) {
+ case 2:
+ if(value) {
+ cleanup(element);
+ } else {
+ var data = element.data(NG_ANIMATE_STATE) || {};
+ data.disabled = true;
+ element.data(NG_ANIMATE_STATE, data);
+ }
+ break;
+
+ case 1:
+ rootAnimateState.disabled = !value;
+ break;
+
+ default:
+ value = !rootAnimateState.disabled;
+ break;
+ }
+ return !!value;
+ }
+ };
+
+ /*
+ all animations call this shared animation triggering function internally.
+ The animationEvent variable refers to the JavaScript animation event that will be triggered
+ and the className value is the name of the animation that will be applied within the
+ CSS code. Element, parentElement and afterElement are provided DOM elements for the animation
+ and the onComplete callback will be fired once the animation is fully complete.
+ */
+ function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
+ var classes = (element.attr('class') || '') + ' ' + className;
+ 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) {
+ domOperation();
+ 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) {
+ domOperation();
+ fireDoneCallbackAsync();
+ return;
+ }
+
+ 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);
+ (ngAnimateState.done || noop)(true);
+ }
+
+ //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.
+ if((animationEvent == 'addClass' && element.hasClass(className)) ||
+ (animationEvent == 'removeClass' && !element.hasClass(className))) {
+ domOperation();
+ 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,
+ 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) {
+ domOperation();
+ 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);
+ }
+
+ 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 = element[0];
+ if(node.nodeType != ELEMENT_NODE) {
+ return;
+ }
+
+ 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(element[0] == $rootElement[0]) {
+ if(!rootAnimateState.disabled) {
+ rootAnimateState.running = false;
+ rootAnimateState.structural = false;
+ }
+ } else {
+ element.removeClass(NG_ANIMATE_CLASS_NAME);
+ element.removeData(NG_ANIMATE_STATE);
+ }
+ }
+
+ function animationsDisabled(element, parentElement) {
+ if (rootAnimateState.disabled) return true;
+
+ if(element[0] == $rootElement[0]) {
+ return rootAnimateState.disabled || rootAnimateState.running;
+ }
+
+ do {
+ //the element did not reach the root element which means that it
+ //is not apart of the DOM. Therefore there is no reason to do
+ //any animations on it
+ if(parentElement.length === 0) break;
+
+ var isRoot = parentElement[0] == $rootElement[0];
+ var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE);
+ var result = state && (!!state.disabled || !!state.running);
+ if(isRoot || result) {
+ return result;
+ }
+
+ if(isRoot) return true;
+ }
+ while(parentElement = parentElement.parent());
+
+ return true;
+ }
+ }]);
+
+ $animateProvider.register('', ['$window', '$sniffer', '$timeout', function($window, $sniffer, $timeout) {
+ // Detect proper transitionend/animationend event names.
+ var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
+
+ // If unprefixed events are not supported but webkit-prefixed are, use the latter.
+ // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
+ // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
+ // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
+ // Register both events in case `window.onanimationend` is not supported because of that,
+ // do the same for `transitionend` as Safari is likely to exhibit similar behavior.
+ // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
+ // therefore there is no reason to test anymore for other vendor prefixes: http://caniuse.com/#search=transition
+ if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) {
+ CSS_PREFIX = '-webkit-';
+ TRANSITION_PROP = 'WebkitTransition';
+ TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
+ } else {
+ TRANSITION_PROP = 'transition';
+ TRANSITIONEND_EVENT = 'transitionend';
+ }
+
+ if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) {
+ CSS_PREFIX = '-webkit-';
+ ANIMATION_PROP = 'WebkitAnimation';
+ ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
+ } else {
+ ANIMATION_PROP = 'animation';
+ ANIMATIONEND_EVENT = 'animationend';
+ }
+
+ var DURATION_KEY = 'Duration';
+ var PROPERTY_KEY = 'Property';
+ var DELAY_KEY = 'Delay';
+ var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
+ var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey';
+ var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data';
+ var NG_ANIMATE_FALLBACK_CLASS_NAME = 'ng-animate-start';
+ var NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME = 'ng-animate-active';
+
+ var lookupCache = {};
+ var parentCounter = 0;
+
+ var animationReflowQueue = [], animationTimer, timeOut = false;
+ function afterReflow(callback) {
+ animationReflowQueue.push(callback);
+ $timeout.cancel(animationTimer);
+ animationTimer = $timeout(function() {
+ forEach(animationReflowQueue, function(fn) {
+ fn();
+ });
+ animationReflowQueue = [];
+ animationTimer = null;
+ lookupCache = {};
+ }, 10, false);
+ }
+
+ function applyStyle(node, style) {
+ var oldStyle = node.getAttribute('style') || '';
+ var newStyle = (oldStyle.length > 0 ? '; ' : '') + style;
+ node.setAttribute('style', newStyle);
+ return oldStyle;
+ }
+
+ function getElementAnimationDetails(element, cacheKey) {
+ var data = cacheKey ? lookupCache[cacheKey] : null;
+ if(!data) {
+ var transitionDuration = 0;
+ var transitionDelay = 0;
+ var animationDuration = 0;
+ var animationDelay = 0;
+ var transitionDelayStyle;
+ var animationDelayStyle;
+ var transitionDurationStyle;
+ var transitionPropertyStyle;
+
+ //we want all the styles defined before and after
+ forEach(element, function(element) {
+ if (element.nodeType == ELEMENT_NODE) {
+ var elementStyles = $window.getComputedStyle(element) || {};
+
+ transitionDurationStyle = elementStyles[TRANSITION_PROP + DURATION_KEY];
+
+ transitionDuration = Math.max(parseMaxTime(transitionDurationStyle), transitionDuration);
+
+ transitionPropertyStyle = elementStyles[TRANSITION_PROP + PROPERTY_KEY];
+
+ transitionDelayStyle = elementStyles[TRANSITION_PROP + DELAY_KEY];
+
+ transitionDelay = Math.max(parseMaxTime(transitionDelayStyle), transitionDelay);
+
+ animationDelayStyle = elementStyles[ANIMATION_PROP + DELAY_KEY];
+
+ animationDelay = Math.max(parseMaxTime(animationDelayStyle), animationDelay);
+
+ var aDuration = parseMaxTime(elementStyles[ANIMATION_PROP + DURATION_KEY]);
+
+ if(aDuration > 0) {
+ aDuration *= parseInt(elementStyles[ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY], 10) || 1;
+ }
+
+ animationDuration = Math.max(aDuration, animationDuration);
+ }
+ });
+ data = {
+ total : 0,
+ transitionPropertyStyle: transitionPropertyStyle,
+ transitionDurationStyle: transitionDurationStyle,
+ transitionDelayStyle: transitionDelayStyle,
+ transitionDelay: transitionDelay,
+ transitionDuration: transitionDuration,
+ animationDelayStyle: animationDelayStyle,
+ animationDelay: animationDelay,
+ animationDuration: animationDuration
+ };
+ if(cacheKey) {
+ lookupCache[cacheKey] = data;
+ }
+ }
+ return data;
+ }
+
+ function parseMaxTime(str) {
+ var maxValue = 0;
+ var values = angular.isString(str) ?
+ str.split(/\s*,\s*/) :
+ [];
+ forEach(values, function(value) {
+ maxValue = Math.max(parseFloat(value) || 0, maxValue);
+ });
+ return maxValue;
+ }
+
+ function getCacheKey(element) {
+ var parentElement = element.parent();
+ var parentID = parentElement.data(NG_ANIMATE_PARENT_KEY);
+ if(!parentID) {
+ parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter);
+ parentID = parentCounter;
+ }
+ return parentID + '-' + element[0].className;
+ }
+
+ function animateSetup(element, className) {
+ var cacheKey = getCacheKey(element);
+ var eventCacheKey = cacheKey + ' ' + className;
+ var stagger = {};
+ var ii = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
+
+ if(ii > 0) {
+ var staggerClassName = className + '-stagger';
+ var staggerCacheKey = cacheKey + ' ' + staggerClassName;
+ var applyClasses = !lookupCache[staggerCacheKey];
+
+ applyClasses && element.addClass(staggerClassName);
+
+ stagger = getElementAnimationDetails(element, staggerCacheKey);
+
+ applyClasses && element.removeClass(staggerClassName);
+ }
+
+ element.addClass(className);
+
+ var timings = getElementAnimationDetails(element, eventCacheKey);
+
+ /* there is no point in performing a reflow if the animation
+ timeout is empty (this would cause a flicker bug normally
+ in the page. There is also no point in performing an animation
+ that only has a delay and no duration */
+ var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
+ if(maxDuration === 0) {
+ element.removeClass(className);
+ return false;
+ }
+
+ var node = element[0];
+ //temporarily disable the transition so that the enter styles
+ //don't animate twice (this is here to avoid a bug in Chrome/FF).
+ var activeClassName = '';
+ if(timings.transitionDuration > 0) {
+ element.addClass(NG_ANIMATE_FALLBACK_CLASS_NAME);
+ activeClassName += NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME + ' ';
+ node.style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
+ }
+
+ forEach(className.split(' '), function(klass, i) {
+ activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
+ });
+
+ element.data(NG_ANIMATE_CSS_DATA_KEY, {
+ className : className,
+ activeClassName : activeClassName,
+ maxDuration : maxDuration,
+ classes : className + ' ' + activeClassName,
+ timings : timings,
+ stagger : stagger,
+ ii : ii
+ });
+
+ return true;
+ }
+
+ function animateRun(element, className, activeAnimationComplete) {
+ var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
+ if(!element.hasClass(className) || !data) {
+ activeAnimationComplete();
+ return;
+ }
+
+ var node = element[0];
+ var timings = data.timings;
+ var stagger = data.stagger;
+ var maxDuration = data.maxDuration;
+ var activeClassName = data.activeClassName;
+ var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * 1000;
+ var startTime = Date.now();
+ var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
+ var formerStyle;
+ var ii = data.ii;
+
+ var applyFallbackStyle, style = '';
+ if(timings.transitionDuration > 0) {
+ node.style[TRANSITION_PROP + PROPERTY_KEY] = '';
+
+ var propertyStyle = timings.transitionPropertyStyle;
+ if(propertyStyle.indexOf('all') == -1) {
+ applyFallbackStyle = true;
+ var fallbackProperty = $sniffer.msie ? '-ms-zoom' : 'clip';
+ style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ', ' + fallbackProperty + '; ';
+ style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ', ' + timings.transitionDuration + 's; ';
+ }
+ }
+
+ if(ii > 0) {
+ if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
+ var delayStyle = timings.transitionDelayStyle;
+ if(applyFallbackStyle) {
+ delayStyle += ', ' + timings.transitionDelay + 's';
+ }
+
+ style += CSS_PREFIX + 'transition-delay: ' +
+ prepareStaggerDelay(delayStyle, stagger.transitionDelay, ii) + '; ';
+ }
+
+ if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
+ style += CSS_PREFIX + 'animation-delay: ' +
+ prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, ii) + '; ';
+ }
+ }
+
+ if(style.length > 0) {
+ formerStyle = applyStyle(node, style);
+ }
+
+ element.on(css3AnimationEvents, onAnimationProgress);
+ element.addClass(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.off(css3AnimationEvents, onAnimationProgress);
+ element.removeClass(activeClassName);
+ animateClose(element, className);
+ if(formerStyle != null) {
+ formerStyle.length > 0 ?
+ node.setAttribute('style', formerStyle) :
+ node.removeAttribute('style');
+ }
+ };
+
+ function onAnimationProgress(event) {
+ event.stopPropagation();
+ var ev = event.originalEvent || event;
+ var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
+ /* $manualTimeStamp is a mocked timeStamp value which is set
+ * within browserTrigger(). This is only here so that tests can
+ * mock animations properly. Real events fallback to event.timeStamp,
+ * or, if they don't, then a timeStamp is automatically created for them.
+ * We're checking to see if the timeStamp surpasses the expected delay,
+ * but we're using elapsedTime instead of the timeStamp on the 2nd
+ * pre-condition since animations sometimes close off early */
+ if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && ev.elapsedTime >= maxDuration) {
+ activeAnimationComplete();
+ }
+ }
+ }
+
+ function prepareStaggerDelay(delayStyle, staggerDelay, index) {
+ var style = '';
+ forEach(delayStyle.split(','), function(val, i) {
+ style += (i > 0 ? ',' : '') +
+ (index * staggerDelay + parseInt(val, 10)) + 's';
+ });
+ return style;
+ }
+
+ function animateBefore(element, className) {
+ if(animateSetup(element, className)) {
+ return function(cancelled) {
+ cancelled && animateClose(element, className);
+ };
+ }
+ }
+
+ function animateAfter(element, className, afterAnimationComplete) {
+ if(element.data(NG_ANIMATE_CSS_DATA_KEY)) {
+ return animateRun(element, className, afterAnimationComplete);
+ } else {
+ animateClose(element, className);
+ afterAnimationComplete();
+ }
+ }
+
+ function animate(element, className, animationComplete) {
+ //If the animateSetup function doesn't bother returning a
+ //cancellation function then it means that there is no animation
+ //to perform at all
+ var preReflowCancellation = animateBefore(element, className);
+ if(!preReflowCancellation) {
+ animationComplete();
+ return;
+ }
+
+ //There are two cancellation functions: one is before the first
+ //reflow animation and the second is during the active state
+ //animation. The first function will take care of removing the
+ //data from the element which will not make the 2nd animation
+ //happen in the first place
+ var cancel = preReflowCancellation;
+ afterReflow(function() {
+ //once the reflow is complete then we point cancel to
+ //the new cancellation function which will remove all of the
+ //animation properties from the active animation
+ cancel = animateAfter(element, className, animationComplete);
+ });
+
+ return function(cancelled) {
+ (cancel || noop)(cancelled);
+ };
+ }
+
+ function animateClose(element, className) {
+ element.removeClass(className);
+ element.removeClass(NG_ANIMATE_FALLBACK_CLASS_NAME);
+ element.removeData(NG_ANIMATE_CSS_DATA_KEY);
+ }
+
+ return {
+ allowCancel : function(element, animationEvent, className) {
+ //always cancel the current animation if it is a
+ //structural animation
+ var oldClasses = (element.data(NG_ANIMATE_CSS_DATA_KEY) || {}).classes;
+ if(!oldClasses || ['enter','leave','move'].indexOf(animationEvent) >= 0) {
+ return true;
+ }
+
+ var parentElement = element.parent();
+ var clone = angular.element(element[0].cloneNode());
+
+ //make the element super hidden and override any CSS style values
+ clone.attr('style','position:absolute; top:-9999px; left:-9999px');
+ clone.removeAttr('id');
+ clone.html('');
+
+ forEach(oldClasses.split(' '), function(klass) {
+ clone.removeClass(klass);
+ });
+
+ var suffix = animationEvent == 'addClass' ? '-add' : '-remove';
+ clone.addClass(suffixClasses(className, suffix));
+ parentElement.append(clone);
+
+ var timings = getElementAnimationDetails(clone);
+ clone.remove();
+
+ return Math.max(timings.transitionDuration, timings.animationDuration) > 0;
+ },
+
+ enter : function(element, animationCompleted) {
+ return animate(element, 'ng-enter', animationCompleted);
+ },
+
+ leave : function(element, animationCompleted) {
+ return animate(element, 'ng-leave', animationCompleted);
+ },
+
+ move : function(element, animationCompleted) {
+ return animate(element, 'ng-move', animationCompleted);
+ },
+
+ beforeAddClass : function(element, className, animationCompleted) {
+ var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'));
+ if(cancellationMethod) {
+ afterReflow(animationCompleted);
+ return cancellationMethod;
+ }
+ animationCompleted();
+ },
+
+ addClass : function(element, className, animationCompleted) {
+ return animateAfter(element, suffixClasses(className, '-add'), animationCompleted);
+ },
+
+ beforeRemoveClass : function(element, className, animationCompleted) {
+ var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'));
+ if(cancellationMethod) {
+ afterReflow(animationCompleted);
+ return cancellationMethod;
+ }
+ animationCompleted();
+ },
+
+ removeClass : function(element, className, animationCompleted) {
+ return animateAfter(element, suffixClasses(className, '-remove'), animationCompleted);
+ }
+ };
+
+ 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);