PageSourceSearch

https://app.menucontrol.com.br/bower_components/angular-animate/angular-animate.js

js menucontrol.com.br collected 2026-10-05 23:44:55 UTC 77,392 bytes, 1,689 lines download raw bytes

1/**
2 * @license AngularJS v1.2.24
3 * (c) 2010-2014 Google, Inc. http://angularjs.org
4 * License: MIT
5 */
6(function(window, angular, undefined) {'use strict';
7
8/* jshint maxlen: false */
9
10/**
11 * @ngdoc module
12 * @name ngAnimate
13 * @description
14 *
15 * # ngAnimate
16 *
17 * The `ngAnimate` module provides support for JavaScript, CSS3 transition and CSS3 keyframe animation hooks within existing core and custom directives.
18 *
19 *
20 * <div doc-module-components="ngAnimate"></div>
21 *
22 * # Usage
23 *
24 * To see animations in action, all that is required is to define the appropriate CSS classes
25 * or to register a JavaScript animation via the myModule.animation() function. The directives that support animation automatically are:
26 * `ngRepeat`, `ngInclude`, `ngIf`, `ngSwitch`, `ngShow`, `ngHide`, `ngView` and `ngClass`. Custom directives can take advantage of animation
27 * by using the `$animate` service.
28 *
29 * Below is a more detailed breakdown of the supported animation events provided by pre-existing ng directives:
30 *
31 * | Directive                                                 | Supported Animations                               |
32 * |---------------------------------------------------------- |----------------------------------------------------|
33 * | {@link ng.directive:ngRepeat#usage_animations ngRepeat}         | enter, leave and move                              |
34 * | {@link ngRoute.directive:ngView#usage_animations ngView}        | enter and leave                                    |
35 * | {@link ng.directive:ngInclude#usage_animations ngInclude}       | enter and leave                                    |
36 * | {@link ng.directive:ngSwitch#usage_animations ngSwitch}         | enter and leave                                    |
37 * | {@link ng.directive:ngIf#usage_animations ngIf}                 | enter and leave                                    |
38 * | {@link ng.directive:ngClass#usage_animations ngClass}           | add and remove                                     |
39 * | {@link ng.directive:ngShow#usage_animations ngShow & ngHide}    | add and remove (the ng-hide class value)           |
40 * | {@link ng.directive:form#usage_animations form}                 | add and remove (dirty, pristine, valid, invalid & all other validations)                |
41 * | {@link ng.directive:ngModel#usage_animations ngModel}           | add and remove (dirty, pristine, valid, invalid & all other validations)                |
42 *
43 * You can find out more information about animations upon visiting each directive page.
44 *
45 * Below is an example of how to apply animations to a directive that supports animation hooks:
46 *
47 * ```html
48 * <style type="text/css">
49 * .slide.ng-enter, .slide.ng-leave {
50 *   -webkit-transition:0.5s linear all;
51 *   transition:0.5s linear all;
52 * }
53 *
54 * .slide.ng-enter { }        /&#42; starting animations for enter &#42;/
55 * .slide.ng-enter.ng-enter-active { } /&#42; terminal animations for enter &#42;/
56 * .slide.ng-leave { }        /&#42; starting animations for leave &#42;/
57 * .slide.ng-leave.ng-leave-active { } /&#42; terminal animations for leave &#42;/
58 * </style>
59 *
60 * <!--
61 * the animate service will automatically add .ng-enter and .ng-leave to the element
62 * to trigger the CSS transition/animations
63 * -->
64 * <ANY class="slide" ng-include="..."></ANY>
65 * ```
66 *
67 * Keep in mind that, by default, if an animation is running, any child elements cannot be animated
68 * until the parent element's animation has completed. This blocking feature can be overridden by
69 * placing the `ng-animate-children` attribute on a parent container tag.
70 *
71 * ```html
72 * <div class="slide-animation" ng-if="on" ng-animate-children>
73 *   <div class="fade-animation" ng-if="on">
74 *     <div class="explode-animation" ng-if="on">
75 *        ...
76 *     </div>
77 *   </div>
78 * </div>
79 * ```
80 *
81 * When the `on` expression value changes and an animation is triggered then each of the elements within
82 * will all animate without the block being applied to child elements.
83 *
84 * <h2>CSS-defined Animations</h2>
85 * The animate service will automatically apply two CSS classes to the animated element and these two CSS classes
86 * are designed to contain the start and end CSS styling. Both CSS transitions and keyframe animations are supported
87 * and can be used to play along with this naming structure.
88 *
89 * The following code below demonstrates how to perform animations using **CSS transitions** with Angular:
90 *
91 * ```html
92 * <style type="text/css">
93 * /&#42;
94 *  The animate class is apart of the element and the ng-enter class
95 *  is attached to the element once the enter animation event is triggered
96 * &#42;/
97 * .reveal-animation.ng-enter {
98 *  -webkit-transition: 1s linear all; /&#42; Safari/Chrome &#42;/
99 *  transition: 1s linear all; /&#42; All other modern browsers and IE10+ &#42;/
100 *
101 *  /&#42; The animation preparation code &#42;/
102 *  opacity: 0;
103 * }
104 *
105 * /&#42;
106 *  Keep in mind that you want to combine both CSS
107 *  classes together to avoid any CSS-specificity
108 *  conflicts
109 * &#42;/
110 * .reveal-animation.ng-enter.ng-enter-active {
111 *  /&#42; The animation code itself &#42;/
112 *  opacity: 1;
113 * }
114 * </style>
115 *
116 * <div class="view-container">
117 *   <div ng-view class="reveal-animation"></div>
118 * </div>
119 * ```
120 *
121 * The following code below demonstrates how to perform animations using **CSS animations** with Angular:
122 *
123 * ```html
124 * <style type="text/css">
125 * .reveal-animation.ng-enter {
126 *   -webkit-animation: enter_sequence 1s linear; /&#42; Safari/Chrome &#42;/
127 *   animation: enter_sequence 1s linear; /&#42; IE10+ and Future Browsers &#42;/
128 * }
129 * @-webkit-keyframes enter_sequence {
130 *   from { opacity:0; }
131 *   to { opacity:1; }
132 * }
133 * @keyframes enter_sequence {
134 *   from { opacity:0; }
135 *   to { opacity:1; }
136 * }
137 * </style>
138 *
139 * <div class="view-container">
140 *   <div ng-view class="reveal-animation"></div>
141 * </div>
142 * ```
143 *
144 * Both CSS3 animations and transitions can be used together and the animate service will figure out the correct duration and delay timing.
145 *
146 * Upon DOM mutation, the event class is added first (something like `ng-enter`), then the browser prepares itself to add
147 * the active class (in this case `ng-enter-active`) which then triggers the animation. The animation module will automatically
148 * detect the CSS code to determine when the animation ends. Once the animation is over then both CSS classes will be
149 * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end
150 * immediately resulting in a DOM element that is at its final state. This final state is when the DOM element
151 * has no CSS transition/animation classes applied to it.
152 *
153 * <h3>CSS Staggering Animations</h3>
154 * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a
155 * curtain-like effect. The ngAnimate module, as of 1.2.0, supports staggering animations and the stagger effect can be
156 * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for
157 * the animation. The style property expected within the stagger class can either be a **transition-delay** or an
158 * **animation-delay** property (or both if your animation contains both transitions and keyframe animations).
159 *
160 * ```css
161 * .my-animation.ng-enter {
162 *   /&#42; standard transition code &#42;/
163 *   -webkit-transition: 1s linear all;
164 *   transition: 1s linear all;
165 *   opacity:0;
166 * }
167 * .my-animation.ng-enter-stagger {
168 *   /&#42; this will have a 100ms delay between each successive leave animation &#42;/
169 *   -webkit-transition-delay: 0.1s;
170 *   transition-delay: 0.1s;
171 *
172 *   /&#42; in case the stagger doesn't work then these two values
173 *    must be set to 0 to avoid an accidental CSS inheritance &#42;/
174 *   -webkit-transition-duration: 0s;
175 *   transition-duration: 0s;
176 * }
177 * .my-animation.ng-enter.ng-enter-active {
178 *   /&#42; standard transition styles &#42;/
179 *   opacity:1;
180 * }
181 * ```
182 *
183 * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations
184 * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this
185 * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation
186 * will also be reset if more than 10ms has passed after the last animation has been fired.
187 *
188 * The following code will issue the **ng-leave-stagger** event on the element provided:
189 *
190 * ```js
191 * var kids = parent.children();
192 *
193 * $animate.leave(kids[0]); //stagger index=0
194 * $animate.leave(kids[1]); //stagger index=1
195 * $animate.leave(kids[2]); //stagger index=2
196 * $animate.leave(kids[3]); //stagger index=3
197 * $animate.leave(kids[4]); //stagger index=4
198 *
199 * $timeout(function() {
200 *   //stagger has reset itself
201 *   $animate.leave(kids[5]); //stagger index=0
202 *   $animate.leave(kids[6]); //stagger index=1
203 * }, 100, false);
204 * ```
205 *
206 * Stagger animations are currently only supported within CSS-defined animations.
207 *
208 * <h2>JavaScript-defined Animations</h2>
209 * 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
210 * yet support CSS transitions/animations, then you can make use of JavaScript animations defined inside of your AngularJS module.
211 *
212 * ```js
213 * //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application.
214 * var ngModule = angular.module('YourApp', ['ngAnimate']);
215 * ngModule.animation('.my-crazy-animation', function() {
216 *   return {
217 *     enter: function(element, done) {
218 *       //run the animation here and call done when the animation is complete
219 *       return function(cancelled) {
220 *         //this (optional) function will be called when the animation
221 *         //completes or when the animation is cancelled (the cancelled
222 *         //flag will be set to true if cancelled).
223 *       };
224 *     },
225 *     leave: function(element, done) { },
226 *     move: function(element, done) { },
227 *
228 *     //animation that can be triggered before the class is added
229 *     beforeAddClass: function(element, className, done) { },
230 *
231 *     //animation that can be triggered after the class is added
232 *     addClass: function(element, className, done) { },
233 *
234 *     //animation that can be triggered before the class is removed
235 *     beforeRemoveClass: function(element, className, done) { },
236 *
237 *     //animation that can be triggered after the class is removed
238 *     removeClass: function(element, className, done) { }
239 *   };
240 * });
241 * ```
242 *
243 * JavaScript-defined animations are created with a CSS-like class selector and a collection of events which are set to run
244 * a javascript callback function. When an animation is triggered, $animate will look for a matching animation which fits
245 * the element's CSS class attribute value and then run the matching animation event function (if found).
246 * In other words, if the CSS classes present on the animated element match any of the JavaScript animations then the callback function will
247 * be executed. It should be also noted that only simple, single class selectors are allowed (compound class selectors are not supported).
248 *
249 * Within a JavaScript animation, an object containing various event callback animation functions is expected to be returned.
250 * As explained above, these callbacks are triggered based on the animation event. Therefore if an enter animation is run,
251 * and the JavaScript animation is found, then the enter callback will handle that animation (in addition to the CSS keyframe animation
252 * or transition code that is defined via a stylesheet).
253 *
254 */
255
256angular.module('ngAnimate', ['ng'])
257
258  /**
259   * @ngdoc provider
260   * @name $animateProvider
261   * @description
262   *
263   * The `$animateProvider` allows developers to register JavaScript animation event handlers directly inside of a module.
264   * When an animation is triggered, the $animate service will query the $animate service to find any animations that match
265   * the provided name value.
266   *
267   * Requires the {@link ngAnimate `ngAnimate`} module to be installed.
268   *
269   * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
270   *
271   */
272  .directive('ngAnimateChildren', function() {
273    var NG_ANIMATE_CHILDREN = '$$ngAnimateChildren';
274    return function(scope, element, attrs) {
275      var val = attrs.ngAnimateChildren;
276      if(angular.isString(val) && val.length === 0) { //empty attribute
277        element.data(NG_ANIMATE_CHILDREN, true);
278      } else {
279        scope.$watch(val, function(value) {
280          element.data(NG_ANIMATE_CHILDREN, !!value);
281        });
282      }
283    };
284  })
285
286  //this private service is only used within CSS-enabled animations
287  //IE8 + IE9 do not support rAF natively, but that is fine since they
288  //also don't support transitions and keyframes which means that the code
289  //below will never be used by the two browsers.
290  .factory('$$animateReflow', ['$$rAF', '$document', function($$rAF, $document) {
291    var bod = $document[0].body;
292    return function(fn) {
293      //the returned function acts as the cancellation function
294      return $$rAF(function() {
295        //the line below will force the browser to perform a repaint
296        //so that all the animated elements within the animation frame
297        //will be properly updated and drawn on screen. This is
298        //required to perform multi-class CSS based animations with
299        //Firefox. DO NOT REMOVE THIS LINE.
300        var a = bod.offsetWidth + 1;
301        fn();
302      });
303    };
304  }])
305
306  .config(['$provide', '$animateProvider', function($provide, $animateProvider) {
307    var noop = angular.noop;
308    var forEach = angular.forEach;
309    var selectors = $animateProvider.$$selectors;
310
311    var ELEMENT_NODE = 1;
312    var NG_ANIMATE_STATE = '$$ngAnimateState';
313    var NG_ANIMATE_CHILDREN = '$$ngAnimateChildren';
314    var NG_ANIMATE_CLASS_NAME = 'ng-animate';
315    var rootAnimateState = {running: true};
316
317    function extractElementNode(element) {
318      for(var i = 0; i < element.length; i++) {
319        var elm = element[i];
320        if(elm.nodeType == ELEMENT_NODE) {
321          return elm;
322        }
323      }
324    }
325
326    function prepareElement(element) {
327      return element && angular.element(element);
328    }
329
330    function stripCommentsFromElement(element) {
331      return angular.element(extractElementNode(element));
332    }
333
334    function isMatchingElement(elm1, elm2) {
335      return extractElementNode(elm1) == extractElementNode(elm2);
336    }
337
338    $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$$asyncCallback', '$rootScope', '$document',
339                            function($delegate,   $injector,   $sniffer,   $rootElement,   $$asyncCallback,    $rootScope,   $document) {
340
341      var globalAnimationCounter = 0;
342      $rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
343
344      // disable animations during bootstrap, but once we bootstrapped, wait again
345      // for another digest until enabling animations. The reason why we digest twice
346      // is because all structural animations (enter, leave and move) all perform a
347      // post digest operation before animating. If we only wait for a single digest
348      // to pass then the structural animation would render its animation on page load.
349      // (which is what we're trying to avoid when the application first boots up.)
350      $rootScope.$$postDigest(function() {
351        $rootScope.$$postDigest(function() {
352          rootAnimateState.running = false;
353        });
354      });
355
356      var classNameFilter = $animateProvider.classNameFilter();
357      var isAnimatableClassName = !classNameFilter
358              ? function() { return true; }
359              : function(className) {
360                return classNameFilter.test(className);
361              };
362
363      function blockElementAnimations(element) {
364        var data = element.data(NG_ANIMATE_STATE) || {};
365        data.running = true;
366        element.data(NG_ANIMATE_STATE, data);
367      }
368
369      function lookup(name) {
370        if (name) {
371          var matches = [],
372              flagMap = {},
373              classes = name.substr(1).split('.');
374
375          //the empty string value is the default animation
376          //operation which performs CSS transition and keyframe
377          //animations sniffing. This is always included for each
378          //element animation procedure if the browser supports
379          //transitions and/or keyframe animations. The default
380          //animation is added to the top of the list to prevent
381          //any previous animations from affecting the element styling
382          //prior to the element being animated.
383          if ($sniffer.transitions || $sniffer.animations) {
384            matches.push($injector.get(selectors['']));
385          }
386
387          for(var i=0; i < classes.length; i++) {
388            var klass = classes[i],
389                selectorFactoryName = selectors[klass];
390            if(selectorFactoryName && !flagMap[klass]) {
391              matches.push($injector.get(selectorFactoryName));
392              flagMap[klass] = true;
393            }
394          }
395          return matches;
396        }
397      }
398
399      function animationRunner(element, animationEvent, className) {
400        //transcluded directives may sometimes fire an animation using only comment nodes
401        //best to catch this early on to prevent any animation operations from occurring
402        var node = element[0];
403        if(!node) {
404          return;
405        }
406
407        var isSetClassOperation = animationEvent == 'setClass';
408        var isClassBased = isSetClassOperation ||
409                           animationEvent == 'addClass' ||
410                           animationEvent == 'removeClass';
411
412        var classNameAdd, classNameRemove;
413        if(angular.isArray(className)) {
414          classNameAdd = className[0];
415          classNameRemove = className[1];
416          className = classNameAdd + ' ' + classNameRemove;
417        }
418
419        var currentClassName = element.attr('class');
420        var classes = currentClassName + ' ' + className;
421        if(!isAnimatableClassName(classes)) {
422          return;
423        }
424
425        var beforeComplete = noop,
426            beforeCancel = [],
427            before = [],
428            afterComplete = noop,
429            afterCancel = [],
430            after = [];
431
432        var animationLookup = (' ' + classes).replace(/\s+/g,'.');
433        forEach(lookup(animationLookup), function(animationFactory) {
434          var created = registerAnimation(animationFactory, animationEvent);
435          if(!created && isSetClassOperation) {
436            registerAnimation(animationFactory, 'addClass');
437            registerAnimation(animationFactory, 'removeClass');
438          }
439        });
440
441        function registerAnimation(animationFactory, event) {
442          var afterFn = animationFactory[event];
443          var beforeFn = animationFactory['before' + event.charAt(0).toUpperCase() + event.substr(1)];
444          if(afterFn || beforeFn) {
445            if(event == 'leave') {
446              beforeFn = afterFn;
447              //when set as null then animation knows to skip this phase
448              afterFn = null;
449            }
450            after.push({
451              event : event, fn : afterFn
452            });
453            before.push({
454              event : event, fn : beforeFn
455            });
456            return true;
457          }
458        }
459
460        function run(fns, cancellations, allCompleteFn) {
461          var animations = [];
462          forEach(fns, function(animation) {
463            animation.fn && animations.push(animation);
464          });
465
466          var count = 0;
467          function afterAnimationComplete(index) {
468            if(cancellations) {
469              (cancellations[index] || noop)();
470              if(++count < animations.length) return;
471              cancellations = null;
472            }
473            allCompleteFn();
474          }
475
476          //The code below adds directly to the array in order to work with
477          //both sync and async animations. Sync animations are when the done()
478          //operation is called right away. DO NOT REFACTOR!
479          forEach(animations, function(animation, index) {
480            var progress = function() {
481              afterAnimationComplete(index);
482            };
483            switch(animation.event) {
484              case 'setClass':
485                cancellations.push(animation.fn(element, classNameAdd, classNameRemove, progress));
486                break;
487              case 'addClass':
488                cancellations.push(animation.fn(element, classNameAdd || className,     progress));
489                break;
490              case 'removeClass':
491                cancellations.push(animation.fn(element, classNameRemove || className,  progress));
492                break;
493              default:
494                cancellations.push(animation.fn(element, progress));
495                break;
496            }
497          });
498
499          if(cancellations && cancellations.length === 0) {
500            allCompleteFn();
501          }
502        }
503
504        return {
505          node : node,
506          event : animationEvent,
507          className : className,
508          isClassBased : isClassBased,
509          isSetClassOperation : isSetClassOperation,
510          before : function(allCompleteFn) {
511            beforeComplete = allCompleteFn;
512            run(before, beforeCancel, function() {
513              beforeComplete = noop;
514              allCompleteFn();
515            });
516          },
517          after : function(allCompleteFn) {
518            afterComplete = allCompleteFn;
519            run(after, afterCancel, function() {
520              afterComplete = noop;
521              allCompleteFn();
522            });
523          },
524          cancel : function() {
525            if(beforeCancel) {
526              forEach(beforeCancel, function(cancelFn) {
527                (cancelFn || noop)(true);
528              });
529              beforeComplete(true);
530            }
531            if(afterCancel) {
532              forEach(afterCancel, function(cancelFn) {
533                (cancelFn || noop)(true);
534              });
535              afterComplete(true);
536            }
537          }
538        };
539      }
540
541      /**
542       * @ngdoc service
543       * @name $animate
544       * @kind function
545       *
546       * @description
547       * The `$animate` service provides animation detection support while performing DOM operations (enter, leave and move) as well as during addClass and removeClass operations.
548       * When any of these operations are run, the $animate service
549       * will examine any JavaScript-defined animations (which are defined by using the $animateProvider provider object)
550       * as well as any CSS-defined animations against the CSS classes present on the element once the DOM operation is run.
551       *
552       * The `$animate` service is used behind the scenes with pre-existing directives and animation with these directives
553       * will work out of the box without any extra configuration.
554       *
555       * Requires the {@link ngAnimate `ngAnimate`} module to be installed.
556       *
557       * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
558       *
559       */
560      return {
561        /**
562         * @ngdoc method
563         * @name $animate#enter
564         * @kind function
565         *
566         * @description
567         * Appends the element to the parentElement element that resides in the document and then runs the enter animation. Once
568         * the animation is started, the following CSS classes will be present on the element for the duration of the animation:
569         *
570         * Below is a breakdown of each step that occurs during enter animation:
571         *
572         * | Animation Step                                                                               | What the element class attribute looks like |
573         * |----------------------------------------------------------------------------------------------|---------------------------------------------|
574         * | 1. $animate.enter(...) is called                                                             | class="my-animation"                        |
575         * | 2. element is inserted into the parentElement element or beside the afterElement element     | class="my-animation"                        |
576         * | 3. $animate runs any JavaScript-defined animations on the element                            | class="my-animation ng-animate"             |
577         * | 4. the .ng-enter class is added to the element                                               | class="my-animation ng-animate ng-enter"    |
578         * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay  | class="my-animation ng-animate ng-enter"    |
579         * | 6. $animate waits for 10ms (this performs a reflow)                                          | class="my-animation ng-animate ng-enter"    |
580         * | 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" |
581         * | 8. $animate waits for X milliseconds for the animation to complete                           | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" |
582         * | 9. The animation ends and all generated CSS classes are removed from the element             | class="my-animation"                        |
583         * | 10. The doneCallback() callback is fired (if provided)                                       | class="my-animation"                        |
584         *
585         * @param {DOMElement} element the element that will be the focus of the enter animation
586         * @param {DOMElement} parentElement the parent element of the element that will be the focus of the enter animation
587         * @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the enter animation
588         * @param {function()=} doneCallback the callback function that will be called once the animation is complete
589        */
590        enter : function(element, parentElement, afterElement, doneCallback) {
591          element = angular.element(element);
592          parentElement = prepareElement(parentElement);
593          afterElement = prepareElement(afterElement);
594
595          blockElementAnimations(element);
596          $delegate.enter(element, parentElement, afterElement);
597          $rootScope.$$postDigest(function() {
598            element = stripCommentsFromElement(element);
599            performAnimation('enter', 'ng-enter', element, parentElement, afterElement, noop, doneCallback);
600          });
601        },
602
603        /**
604         * @ngdoc method
605         * @name $animate#leave
606         * @kind function
607         *
608         * @description
609         * Runs the leave animation operation and, upon completion, removes the element from the DOM. Once
610         * the animation is started, the following CSS classes will be added for the duration of the animation:
611         *
612         * Below is a breakdown of each step that occurs during leave animation:
613         *
614         * | Animation Step                                                                               | What the element class attribute looks like |
615         * |----------------------------------------------------------------------------------------------|---------------------------------------------|
616         * | 1. $animate.leave(...) is called                                                             | class="my-animation"                        |
617         * | 2. $animate runs any JavaScript-defined animations on the element                            | class="my-animation ng-animate"             |
618         * | 3. the .ng-leave class is added to the element                                               | class="my-animation ng-animate ng-leave"    |
619         * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay  | class="my-animation ng-animate ng-leave"    |
620         * | 5. $animate waits for 10ms (this performs a reflow)                                          | class="my-animation ng-animate ng-leave"    |
621         * | 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" |
622         * | 7. $animate waits for X milliseconds for the animation to complete                           | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" |
623         * | 8. The animation ends and all generated CSS classes are removed from the element             | class="my-animation"                        |
624         * | 9. The element is removed from the DOM                                                       | ...                                         |
625         * | 10. The doneCallback() callback is fired (if provided)                                       | ...                                         |
626         *
627         * @param {DOMElement} element the element that will be the focus of the leave animation
628         * @param {function()=} doneCallback the callback function that will be called once the animation is complete
629        */
630        leave : function(element, doneCallback) {
631          element = angular.element(element);
632          cancelChildAnimations(element);
633          blockElementAnimations(element);
634          $rootScope.$$postDigest(function() {
635            performAnimation('leave', 'ng-leave', stripCommentsFromElement(element), null, null, function() {
636              $delegate.leave(element);
637            }, doneCallback);
638          });
639        },
640
641        /**
642         * @ngdoc method
643         * @name $animate#move
644         * @kind function
645         *
646         * @description
647         * Fires the move DOM operation. Just before the animation starts, the animate service will either append it into the parentElement container or
648         * add the element directly after the afterElement element if present. Then the move animation will be run. Once
649         * the animation is started, the following CSS classes will be added for the duration of the animation:
650         *
651         * Below is a breakdown of each step that occurs during move animation:
652         *
653         * | Animation Step                                                                               | What the element class attribute looks like |
654         * |----------------------------------------------------------------------------------------------|---------------------------------------------|
655         * | 1. $animate.move(...) is called                                                              | class="my-animation"                        |
656         * | 2. element is moved into the parentElement element or beside the afterElement element        | class="my-animation"                        |
657         * | 3. $animate runs any JavaScript-defined animations on the element                            | class="my-animation ng-animate"             |
658         * | 4. the .ng-move class is added to the element                                                | class="my-animation ng-animate ng-move"     |
659         * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay  | class="my-animation ng-animate ng-move"     |
660         * | 6. $animate waits for 10ms (this performs a reflow)                                          | class="my-animation ng-animate ng-move"     |
661         * | 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" |
662         * | 8. $animate waits for X milliseconds for the animation to complete                           | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" |
663         * | 9. The animation ends and all generated CSS classes are removed from the element             | class="my-animation"                        |
664         * | 10. The doneCallback() callback is fired (if provided)                                       | class="my-animation"                        |
665         *
666         * @param {DOMElement} element the element that will be the focus of the move animation
667         * @param {DOMElement} parentElement the parentElement element of the element that will be the focus of the move animation
668         * @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the move animation
669         * @param {function()=} doneCallback the callback function that will be called once the animation is complete
670        */
671        move : function(element, parentElement, afterElement, doneCallback) {
672          element = angular.element(element);
673          parentElement = prepareElement(parentElement);
674          afterElement = prepareElement(afterElement);
675
676          cancelChildAnimations(element);
677          blockElementAnimations(element);
678          $delegate.move(element, parentElement, afterElement);
679          $rootScope.$$postDigest(function() {
680            element = stripCommentsFromElement(element);
681            performAnimation('move', 'ng-move', element, parentElement, afterElement, noop, doneCallback);
682          });
683        },
684
685        /**
686         * @ngdoc method
687         * @name $animate#addClass
688         *
689         * @description
690         * Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class.
691         * Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide
692         * the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if no CSS transitions
693         * or keyframes are defined on the -add or base CSS class).
694         *
695         * Below is a breakdown of each step that occurs during addClass animation:
696         *
697         * | Animation Step                                                                                 | What the element class attribute looks like |
698         * |------------------------------------------------------------------------------------------------|---------------------------------------------|
699         * | 1. $animate.addClass(element, 'super') is called                                               | class="my-animation"                        |
700         * | 2. $animate runs any JavaScript-defined animations on the element                              | class="my-animation ng-animate"             |
701         * | 3. the .super-add class are added to the element                                               | class="my-animation ng-animate super-add"   |
702         * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay    | class="my-animation ng-animate super-add"   |
703         * | 5. $animate waits for 10ms (this performs a reflow)                                            | class="my-animation ng-animate super-add"   |
704         * | 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"          |
705         * | 7. $animate waits for X milliseconds for the animation to complete                             | class="my-animation super super-add super-add-active"  |
706         * | 8. The animation ends and all generated CSS classes are removed from the element               | class="my-animation super"                  |
707         * | 9. The super class is kept on the element                                                      | class="my-animation super"                  |
708         * | 10. The doneCallback() callback is fired (if provided)                                         | class="my-animation super"                  |
709         *
710         * @param {DOMElement} element the element that will be animated
711         * @param {string} className the CSS class that will be added to the element and then animated
712         * @param {function()=} doneCallback the callback function that will be called once the animation is complete
713        */
714        addClass : function(element, className, doneCallback) {
715          element = angular.element(element);
716          element = stripCommentsFromElement(element);
717          performAnimation('addClass', className, element, null, null, function() {
718            $delegate.addClass(element, className);
719          }, doneCallback);
720        },
721
722        /**
723         * @ngdoc method
724         * @name $animate#removeClass
725         *
726         * @description
727         * Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value
728         * from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in
729         * order to provide the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if
730         * no CSS transitions or keyframes are defined on the -remove or base CSS classes).
731         *
732         * Below is a breakdown of each step that occurs during removeClass animation:
733         *
734         * | Animation Step                                                                                | What the element class attribute looks like     |
735         * |-----------------------------------------------------------------------------------------------|---------------------------------------------|
736         * | 1. $animate.removeClass(element, 'super') is called                                           | class="my-animation super"                  |
737         * | 2. $animate runs any JavaScript-defined animations on the element                             | class="my-animation super ng-animate"       |
738         * | 3. the .super-remove class are added to the element                                           | class="my-animation super ng-animate super-remove"|
739         * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay   | class="my-animation super ng-animate super-remove"   |
740         * | 5. $animate waits for 10ms (this performs a reflow)                                           | class="my-animation super ng-animate super-remove"   |
741         * | 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"          |
742         * | 7. $animate waits for X milliseconds for the animation to complete                            | class="my-animation ng-animate ng-animate-active super-remove super-remove-active"   |
743         * | 8. The animation ends and all generated CSS classes are removed from the element              | class="my-animation"                        |
744         * | 9. The doneCallback() callback is fired (if provided)                                         | class="my-animation"                        |
745         *
746         *
747         * @param {DOMElement} element the element that will be animated
748         * @param {string} className the CSS class that will be animated and then removed from the element
749         * @param {function()=} doneCallback the callback function that will be called once the animation is complete
750        */
751        removeClass : function(element, className, doneCallback) {
752          element = angular.element(element);
753          element = stripCommentsFromElement(element);
754          performAnimation('removeClass', className, element, null, null, function() {
755            $delegate.removeClass(element, className);
756          }, doneCallback);
757        },
758
759          /**
760           *
761           * @ngdoc function
762           * @name $animate#setClass
763           * @function
764           * @description Adds and/or removes the given CSS classes to and from the element.
765           * Once complete, the done() callback will be fired (if provided).
766           * @param {DOMElement} element the element which will its CSS classes changed
767           *   removed from it
768           * @param {string} add the CSS classes which will be added to the element
769           * @param {string} remove the CSS class which will be removed from the element
770           * @param {Function=} done the callback function (if provided) that will be fired after the
771           *   CSS classes have been set on the element
772           */
773        setClass : function(element, add, remove, doneCallback) {
774          element = angular.element(element);
775          element = stripCommentsFromElement(element);
776          performAnimation('setClass', [add, remove], element, null, null, function() {
777            $delegate.setClass(element, add, remove);
778          }, doneCallback);
779        },
780
781        /**
782         * @ngdoc method
783         * @name $animate#enabled
784         * @kind function
785         *
786         * @param {boolean=} value If provided then set the animation on or off.
787         * @param {DOMElement=} element If provided then the element will be used to represent the enable/disable operation
788         * @return {boolean} Current animation state.
789         *
790         * @description
791         * Globally enables/disables animations.
792         *
793        */
794        enabled : function(value, element) {
795          switch(arguments.length) {
796            case 2:
797              if(value) {
798                cleanup(element);
799              } else {
800                var data = element.data(NG_ANIMATE_STATE) || {};
801                data.disabled = true;
802                element.data(NG_ANIMATE_STATE, data);
803              }
804            break;
805
806            case 1:
807              rootAnimateState.disabled = !value;
808            break;
809
810            default:
811              value = !rootAnimateState.disabled;
812            break;
813          }
814          return !!value;
815         }
816      };
817
818      /*
819        all animations call this shared animation triggering function internally.
820        The animationEvent variable refers to the JavaScript animation event that will be triggered
821        and the className value is the name of the animation that will be applied within the
822        CSS code. Element, parentElement and afterElement are provided DOM elements for the animation
823        and the onComplete callback will be fired once the animation is fully complete.
824      */
825      function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
826
827        var runner = animationRunner(element, animationEvent, className);
828        if(!runner) {
829          fireDOMOperation();
830          fireBeforeCallbackAsync();
831          fireAfterCallbackAsync();
832          closeAnimation();
833          return;
834        }
835
836        className = runner.className;
837        var elementEvents = angular.element._data(runner.node);
838        elementEvents = elementEvents && elementEvents.events;
839
840        if (!parentElement) {
841          parentElement = afterElement ? afterElement.parent() : element.parent();
842        }
843
844        var ngAnimateState  = element.data(NG_ANIMATE_STATE) || {};
845        var runningAnimations     = ngAnimateState.active || {};
846        var totalActiveAnimations = ngAnimateState.totalActive || 0;
847        var lastAnimation         = ngAnimateState.last;
848
849        //only allow animations if the currently running animation is not structural
850        //or if there is no animation running at all
851        var skipAnimations;
852        if (runner.isClassBased) {
853          skipAnimations = ngAnimateState.running ||
854                           ngAnimateState.disabled ||
855                           (lastAnimation && !lastAnimation.isClassBased);
856        }
857
858        //skip the animation if animations are disabled, a parent is already being animated,
859        //the element is not currently attached to the document body or then completely close
860        //the animation if any matching animations are not found at all.
861        //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case an animation was found.
862        if (skipAnimations || animationsDisabled(element, parentElement)) {
863          fireDOMOperation();
864          fireBeforeCallbackAsync();
865          fireAfterCallbackAsync();
866          closeAnimation();
867          return;
868        }
869
870        var skipAnimation = false;
871        if(totalActiveAnimations > 0) {
872          var animationsToCancel = [];
873          if(!runner.isClassBased) {
874            if(animationEvent == 'leave' && runningAnimations['ng-leave']) {
875              skipAnimation = true;
876            } else {
877              //cancel all animations when a structural animation takes place
878              for(var klass in runningAnimations) {
879                animationsToCancel.push(runningAnimations[klass]);
880                cleanup(element, klass);
881              }
882              runningAnimations = {};
883              totalActiveAnimations = 0;
884            }
885          } else if(lastAnimation.event == 'setClass') {
886            animationsToCancel.push(lastAnimation);
887            cleanup(element, className);
888          }
889          else if(runningAnimations[className]) {
890            var current = runningAnimations[className];
891            if(current.event == animationEvent) {
892              skipAnimation = true;
893            } else {
894              animationsToCancel.push(current);
895              cleanup(element, className);
896            }
897          }
898
899          if(animationsToCancel.length > 0) {
900            forEach(animationsToCancel, function(operation) {
901              operation.cancel();
902            });
903          }
904        }
905
906        if(runner.isClassBased && !runner.isSetClassOperation && !skipAnimation) {
907          skipAnimation = (animationEvent == 'addClass') == element.hasClass(className); //opposite of XOR
908        }
909
910        if(skipAnimation) {
911          fireDOMOperation();
912          fireBeforeCallbackAsync();
913          fireAfterCallbackAsync();
914          fireDoneCallbackAsync();
915          return;
916        }
917
918        if(animationEvent == 'leave') {
919          //there's no need to ever remove the listener since the element
920          //will be removed (destroyed) after the leave animation ends or
921          //is cancelled midway
922          element.one('$destroy', function(e) {
923            var element = angular.element(this);
924            var state = element.data(NG_ANIMATE_STATE);
925            if(state) {
926              var activeLeaveAnimation = state.active['ng-leave'];
927              if(activeLeaveAnimation) {
928                activeLeaveAnimation.cancel();
929                cleanup(element, 'ng-leave');
930              }
931            }
932          });
933        }
934
935        //the ng-animate class does nothing, but it's here to allow for
936        //parent animations to find and cancel child animations when needed
937        element.addClass(NG_ANIMATE_CLASS_NAME);
938
939        var localAnimationCount = globalAnimationCounter++;
940        totalActiveAnimations++;
941        runningAnimations[className] = runner;
942
943        element.data(NG_ANIMATE_STATE, {
944          last : runner,
945          active : runningAnimations,
946          index : localAnimationCount,
947          totalActive : totalActiveAnimations
948        });
949
950        //first we run the before animations and when all of those are complete
951        //then we perform the DOM operation and run the next set of animations
952        fireBeforeCallbackAsync();
953        runner.before(function(cancelled) {
954          var data = element.data(NG_ANIMATE_STATE);
955          cancelled = cancelled ||
956                        !data || !data.active[className] ||
957                        (runner.isClassBased && data.active[className].event != animationEvent);
958
959          fireDOMOperation();
960          if(cancelled === true) {
961            closeAnimation();
962          } else {
963            fireAfterCallbackAsync();
964            runner.after(closeAnimation);
965          }
966        });
967
968        function fireDOMCallback(animationPhase) {
969          var eventName = '$animate:' + animationPhase;
970          if(elementEvents && elementEvents[eventName] && elementEvents[eventName].length > 0) {
971            $$asyncCallback(function() {
972              element.triggerHandler(eventName, {
973                event : animationEvent,
974                className : className
975              });
976            });
977          }
978        }
979
980        function fireBeforeCallbackAsync() {
981          fireDOMCallback('before');
982        }
983
984        function fireAfterCallbackAsync() {
985          fireDOMCallback('after');
986        }
987
988        function fireDoneCallbackAsync() {
989          fireDOMCallback('close');
990          if(doneCallback) {
991            $$asyncCallback(function() {
992              doneCallback();
993            });
994          }
995        }
996
997        //it is less complicated to use a flag than managing and canceling
998        //timeouts containing multiple callbacks.
999        function fireDOMOperation() {
1000          if(!fireDOMOperation.hasBeenRun) {
1001            fireDOMOperation.hasBeenRun = true;
1002            domOperation();
1003          }
1004        }
1005
1006        function closeAnimation() {
1007          if(!closeAnimation.hasBeenRun) {
1008            closeAnimation.hasBeenRun = true;
1009            var data = element.data(NG_ANIMATE_STATE);
1010            if(data) {
1011              /* only structural animations wait for reflow before removing an
1012                 animation, but class-based animations don't. An example of this
1013                 failing would be when a parent HTML tag has a ng-class attribute
1014                 causing ALL directives below to skip animations during the digest */
1015              if(runner && runner.isClassBased) {
1016                cleanup(element, className);
1017              } else {
1018                $$asyncCallback(function() {
1019                  var data = element.data(NG_ANIMATE_STATE) || {};
1020                  if(localAnimationCount == data.index) {
1021                    cleanup(element, className, animationEvent);
1022                  }
1023                });
1024                element.data(NG_ANIMATE_STATE, data);
1025              }
1026            }
1027            fireDoneCallbackAsync();
1028          }
1029        }
1030      }
1031
1032      function cancelChildAnimations(element) {
1033        var node = extractElementNode(element);
1034        if (node) {
1035          var nodes = angular.isFunction(node.getElementsByClassName) ?
1036            node.getElementsByClassName(NG_ANIMATE_CLASS_NAME) :
1037            node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME);
1038          forEach(nodes, function(element) {
1039            element = angular.element(element);
1040            var data = element.data(NG_ANIMATE_STATE);
1041            if(data && data.active) {
1042              forEach(data.active, function(runner) {
1043                runner.cancel();
1044              });
1045            }
1046          });
1047        }
1048      }
1049
1050      function cleanup(element, className) {
1051        if(isMatchingElement(element, $rootElement)) {
1052          if(!rootAnimateState.disabled) {
1053            rootAnimateState.running = false;
1054            rootAnimateState.structural = false;
1055          }
1056        } else if(className) {
1057          var data = element.data(NG_ANIMATE_STATE) || {};
1058
1059          var removeAnimations = className === true;
1060          if(!removeAnimations && data.active && data.active[className]) {
1061            data.totalActive--;
1062            delete data.active[className];
1063          }
1064
1065          if(removeAnimations || !data.totalActive) {
1066            element.removeClass(NG_ANIMATE_CLASS_NAME);
1067            element.removeData(NG_ANIMATE_STATE);
1068          }
1069        }
1070      }
1071
1072      function animationsDisabled(element, parentElement) {
1073        if (rootAnimateState.disabled) {
1074          return true;
1075        }
1076
1077        if (isMatchingElement(element, $rootElement)) {
1078          return rootAnimateState.running;
1079        }
1080
1081        var allowChildAnimations, parentRunningAnimation, hasParent;
1082        do {
1083          //the element did not reach the root element which means that it
1084          //is not apart of the DOM. Therefore there is no reason to do
1085          //any animations on it
1086          if (parentElement.length === 0) break;
1087
1088          var isRoot = isMatchingElement(parentElement, $rootElement);
1089          var state = isRoot ? rootAnimateState : (parentElement.data(NG_ANIMATE_STATE) || {});
1090          if (state.disabled) {
1091            return true;
1092          }
1093
1094          //no matter what, for an animation to work it must reach the root element
1095          //this implies that the element is attached to the DOM when the animation is run
1096          if (isRoot) {
1097            hasParent = true;
1098          }
1099
1100          //once a flag is found that is strictly false then everything before
1101          //it will be discarded and all child animations will be restricted
1102          if (allowChildAnimations !== false) {
1103            var animateChildrenFlag = parentElement.data(NG_ANIMATE_CHILDREN);
1104            if(angular.isDefined(animateChildrenFlag)) {
1105              allowChildAnimations = animateChildrenFlag;
1106            }
1107          }
1108
1109          parentRunningAnimation = parentRunningAnimation ||
1110                                   state.running ||
1111                                   (state.last && !state.last.isClassBased);
1112        }
1113        while(parentElement = parentElement.parent());
1114
1115        return !hasParent || (!allowChildAnimations && parentRunningAnimation);
1116      }
1117    }]);
1118
1119    $animateProvider.register('', ['$window', '$sniffer', '$timeout', '$$animateReflow',
1120                           function($window,   $sniffer,   $timeout,   $$animateReflow) {
1121      // Detect proper transitionend/animationend event names.
1122      var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
1123
1124      // If unprefixed events are not supported but webkit-prefixed are, use the latter.
1125      // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
1126      // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
1127      // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
1128      // Register both events in case `window.onanimationend` is not supported because of that,
1129      // do the same for `transitionend` as Safari is likely to exhibit similar behavior.
1130      // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
1131      // therefore there is no reason to test anymore for other vendor prefixes: http://caniuse.com/#search=transition
1132      if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) {
1133        CSS_PREFIX = '-webkit-';
1134        TRANSITION_PROP = 'WebkitTransition';
1135        TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
1136      } else {
1137        TRANSITION_PROP = 'transition';
1138        TRANSITIONEND_EVENT = 'transitionend';
1139      }
1140
1141      if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) {
1142        CSS_PREFIX = '-webkit-';
1143        ANIMATION_PROP = 'WebkitAnimation';
1144        ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
1145      } else {
1146        ANIMATION_PROP = 'animation';
1147        ANIMATIONEND_EVENT = 'animationend';
1148      }
1149
1150      var DURATION_KEY = 'Duration';
1151      var PROPERTY_KEY = 'Property';
1152      var DELAY_KEY = 'Delay';
1153      var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
1154      var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey';
1155      var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data';
1156      var NG_ANIMATE_BLOCK_CLASS_NAME = 'ng-animate-block-transitions';
1157      var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
1158      var CLOSING_TIME_BUFFER = 1.5;
1159      var ONE_SECOND = 1000;
1160
1161      var lookupCache = {};
1162      var parentCounter = 0;
1163      var animationReflowQueue = [];
1164      var cancelAnimationReflow;
1165      function afterReflow(element, callback) {
1166        if(cancelAnimationReflow) {
1167          cancelAnimationReflow();
1168        }
1169        animationReflowQueue.push(callback);
1170        cancelAnimationReflow = $$animateReflow(function() {
1171          forEach(animationReflowQueue, function(fn) {
1172            fn();
1173          });
1174
1175          animationReflowQueue = [];
1176          cancelAnimationReflow = null;
1177          lookupCache = {};
1178        });
1179      }
1180
1181      var closingTimer = null;
1182      var closingTimestamp = 0;
1183      var animationElementQueue = [];
1184      function animationCloseHandler(element, totalTime) {
1185        var node = extractElementNode(element);
1186        element = angular.element(node);
1187
1188        //this item will be garbage collected by the closing
1189        //animation timeout
1190        animationElementQueue.push(element);
1191
1192        //but it may not need to cancel out the existing timeout
1193        //if the timestamp is less than the previous one
1194        var futureTimestamp = Date.now() + totalTime;
1195        if(futureTimestamp <= closingTimestamp) {
1196          return;
1197        }
1198
1199        $timeout.cancel(closingTimer);
1200
1201        closingTimestamp = futureTimestamp;
1202        closingTimer = $timeout(function() {
1203          closeAllAnimations(animationElementQueue);
1204          animationElementQueue = [];
1205        }, totalTime, false);
1206      }
1207
1208      function closeAllAnimations(elements) {
1209        forEach(elements, function(element) {
1210          var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
1211          if(elementData) {
1212            (elementData.closeAnimationFn || noop)();
1213          }
1214        });
1215      }
1216
1217      function getElementAnimationDetails(element, cacheKey) {
1218        var data = cacheKey ? lookupCache[cacheKey] : null;
1219        if(!data) {
1220          var transitionDuration = 0;
1221          var transitionDelay = 0;
1222          var animationDuration = 0;
1223          var animationDelay = 0;
1224          var transitionDelayStyle;
1225          var animationDelayStyle;
1226          var transitionDurationStyle;
1227          var transitionPropertyStyle;
1228
1229          //we want all the styles defined before and after
1230          forEach(element, function(element) {
1231            if (element.nodeType == ELEMENT_NODE) {
1232              var elementStyles = $window.getComputedStyle(element) || {};
1233
1234              transitionDurationStyle = elementStyles[TRANSITION_PROP + DURATION_KEY];
1235
1236              transitionDuration = Math.max(parseMaxTime(transitionDurationStyle), transitionDuration);
1237
1238              transitionPropertyStyle = elementStyles[TRANSITION_PROP + PROPERTY_KEY];
1239
1240              transitionDelayStyle = elementStyles[TRANSITION_PROP + DELAY_KEY];
1241
1242              transitionDelay  = Math.max(parseMaxTime(transitionDelayStyle), transitionDelay);
1243
1244              animationDelayStyle = elementStyles[ANIMATION_PROP + DELAY_KEY];
1245
1246              animationDelay   = Math.max(parseMaxTime(animationDelayStyle), animationDelay);
1247
1248              var aDuration  = parseMaxTime(elementStyles[ANIMATION_PROP + DURATION_KEY]);
1249
1250              if(aDuration > 0) {
1251                aDuration *= parseInt(elementStyles[ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY], 10) || 1;
1252              }
1253
1254              animationDuration = Math.max(aDuration, animationDuration);
1255            }
1256          });
1257          data = {
1258            total : 0,
1259            transitionPropertyStyle: transitionPropertyStyle,
1260            transitionDurationStyle: transitionDurationStyle,
1261            transitionDelayStyle: transitionDelayStyle,
1262            transitionDelay: transitionDelay,
1263            transitionDuration: transitionDuration,
1264            animationDelayStyle: animationDelayStyle,
1265            animationDelay: animationDelay,
1266            animationDuration: animationDuration
1267          };
1268          if(cacheKey) {
1269            lookupCache[cacheKey] = data;
1270          }
1271        }
1272        return data;
1273      }
1274
1275      function parseMaxTime(str) {
1276        var maxValue = 0;
1277        var values = angular.isString(str) ?
1278          str.split(/\s*,\s*/) :
1279          [];
1280        forEach(values, function(value) {
1281          maxValue = Math.max(parseFloat(value) || 0, maxValue);
1282        });
1283        return maxValue;
1284      }
1285
1286      function getCacheKey(element) {
1287        var parentElement = element.parent();
1288        var parentID = parentElement.data(NG_ANIMATE_PARENT_KEY);
1289        if(!parentID) {
1290          parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter);
1291          parentID = parentCounter;
1292        }
1293        return parentID + '-' + extractElementNode(element).getAttribute('class');
1294      }
1295
1296      function animateSetup(animationEvent, element, className, calculationDecorator) {
1297        var cacheKey = getCacheKey(element);
1298        var eventCacheKey = cacheKey + ' ' + className;
1299        var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
1300
1301        var stagger = {};
1302        if(itemIndex > 0) {
1303          var staggerClassName = className + '-stagger';
1304          var staggerCacheKey = cacheKey + ' ' + staggerClassName;
1305          var applyClasses = !lookupCache[staggerCacheKey];
1306
1307          applyClasses && element.addClass(staggerClassName);
1308
1309          stagger = getElementAnimationDetails(element, staggerCacheKey);
1310
1311          applyClasses && element.removeClass(staggerClassName);
1312        }
1313
1314        /* the animation itself may need to add/remove special CSS classes
1315         * before calculating the anmation styles */
1316        calculationDecorator = calculationDecorator ||
1317                               function(fn) { return fn(); };
1318
1319        element.addClass(className);
1320
1321        var formerData = element.data(NG_ANIMATE_CSS_DATA_KEY) || {};
1322
1323        var timings = calculationDecorator(function() {
1324          return getElementAnimationDetails(element, eventCacheKey);
1325        });
1326
1327        var transitionDuration = timings.transitionDuration;
1328        var animationDuration = timings.animationDuration;
1329        if(transitionDuration === 0 && animationDuration === 0) {
1330          element.removeClass(className);
1331          return false;
1332        }
1333
1334        element.data(NG_ANIMATE_CSS_DATA_KEY, {
1335          running : formerData.running || 0,
1336          itemIndex : itemIndex,
1337          stagger : stagger,
1338          timings : timings,
1339          closeAnimationFn : noop
1340        });
1341
1342        //temporarily disable the transition so that the enter styles
1343        //don't animate twice (this is here to avoid a bug in Chrome/FF).
1344        var isCurrentlyAnimating = formerData.running > 0 || animationEvent == 'setClass';
1345        if(transitionDuration > 0) {
1346          blockTransitions(element, className, isCurrentlyAnimating);
1347        }
1348
1349        //staggering keyframe animations work by adjusting the `animation-delay` CSS property
1350        //on the given element, however, the delay value can only calculated after the reflow
1351        //since by that time $animate knows how many elements are being animated. Therefore,
1352        //until the reflow occurs the element needs to be blocked (where the keyframe animation
1353        //is set to `none 0s`). This blocking mechanism should only be set for when a stagger
1354        //animation is detected and when the element item index is greater than 0.
1355        if(animationDuration > 0 && stagger.animationDelay > 0 && stagger.animationDuration === 0) {
1356          blockKeyframeAnimations(element);
1357        }
1358
1359        return true;
1360      }
1361
1362      function isStructuralAnimation(className) {
1363        return className == 'ng-enter' || className == 'ng-move' || className == 'ng-leave';
1364      }
1365
1366      function blockTransitions(element, className, isAnimating) {
1367        if(isStructuralAnimation(className) || !isAnimating) {
1368          extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
1369        } else {
1370          element.addClass(NG_ANIMATE_BLOCK_CLASS_NAME);
1371        }
1372      }
1373
1374      function blockKeyframeAnimations(element) {
1375        extractElementNode(element).style[ANIMATION_PROP] = 'none 0s';
1376      }
1377
1378      function unblockTransitions(element, className) {
1379        var prop = TRANSITION_PROP + PROPERTY_KEY;
1380        var node = extractElementNode(element);
1381        if(node.style[prop] && node.style[prop].length > 0) {
1382          node.style[prop] = '';
1383        }
1384        element.removeClass(NG_ANIMATE_BLOCK_CLASS_NAME);
1385      }
1386
1387      function unblockKeyframeAnimations(element) {
1388        var prop = ANIMATION_PROP;
1389        var node = extractElementNode(element);
1390        if(node.style[prop] && node.style[prop].length > 0) {
1391          node.style[prop] = '';
1392        }
1393      }
1394
1395      function animateRun(animationEvent, element, className, activeAnimationComplete) {
1396        var node = extractElementNode(element);
1397        var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
1398        if(node.getAttribute('class').indexOf(className) == -1 || !elementData) {
1399          activeAnimationComplete();
1400          return;
1401        }
1402
1403        var activeClassName = '';
1404        forEach(className.split(' '), function(klass, i) {
1405          activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
1406        });
1407
1408        var stagger = elementData.stagger;
1409        var timings = elementData.timings;
1410        var itemIndex = elementData.itemIndex;
1411        var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
1412        var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay);
1413        var maxDelayTime = maxDelay * ONE_SECOND;
1414
1415        var startTime = Date.now();
1416        var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
1417
1418        var style = '', appliedStyles = [];
1419        if(timings.transitionDuration > 0) {
1420          var propertyStyle = timings.transitionPropertyStyle;
1421          if(propertyStyle.indexOf('all') == -1) {
1422            style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ';';
1423            style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ';';
1424            appliedStyles.push(CSS_PREFIX + 'transition-property');
1425            appliedStyles.push(CSS_PREFIX + 'transition-duration');
1426          }
1427        }
1428
1429        if(itemIndex > 0) {
1430          if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
1431            var delayStyle = timings.transitionDelayStyle;
1432            style += CSS_PREFIX + 'transition-delay: ' +
1433                     prepareStaggerDelay(delayStyle, stagger.transitionDelay, itemIndex) + '; ';
1434            appliedStyles.push(CSS_PREFIX + 'transition-delay');
1435          }
1436
1437          if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
1438            style += CSS_PREFIX + 'animation-delay: ' +
1439                     prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, itemIndex) + '; ';
1440            appliedStyles.push(CSS_PREFIX + 'animation-delay');
1441          }
1442        }
1443
1444        if(appliedStyles.length > 0) {
1445          //the element being animated may sometimes contain comment nodes in
1446          //the jqLite object, so we're safe to use a single variable to house
1447          //the styles since there is always only one element being animated
1448          var oldStyle = node.getAttribute('style') || '';
1449          node.setAttribute('style', oldStyle + '; ' + style);
1450        }
1451
1452        element.on(css3AnimationEvents, onAnimationProgress);
1453        element.addClass(activeClassName);
1454        elementData.closeAnimationFn = function() {
1455          onEnd();
1456          activeAnimationComplete();
1457        };
1458
1459        var staggerTime       = itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0);
1460        var animationTime     = (maxDelay + maxDuration) * CLOSING_TIME_BUFFER;
1461        var totalTime         = (staggerTime + animationTime) * ONE_SECOND;
1462
1463        elementData.running++;
1464        animationCloseHandler(element, totalTime);
1465        return onEnd;
1466
1467        // This will automatically be called by $animate so
1468        // there is no need to attach this internally to the
1469        // timeout done method.
1470        function onEnd(cancelled) {
1471          element.off(css3AnimationEvents, onAnimationProgress);
1472          element.removeClass(activeClassName);
1473          animateClose(element, className);
1474          var node = extractElementNode(element);
1475          for (var i in appliedStyles) {
1476            node.style.removeProperty(appliedStyles[i]);
1477          }
1478        }
1479
1480        function onAnimationProgress(event) {
1481          event.stopPropagation();
1482          var ev = event.originalEvent || event;
1483          var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
1484
1485          /* Firefox (or possibly just Gecko) likes to not round values up
1486           * when a ms measurement is used for the animation */
1487          var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
1488
1489          /* $manualTimeStamp is a mocked timeStamp value which is set
1490           * within browserTrigger(). This is only here so that tests can
1491           * mock animations properly. Real events fallback to event.timeStamp,
1492           * or, if they don't, then a timeStamp is automatically created for them.
1493           * We're checking to see if the timeStamp surpasses the expected delay,
1494           * but we're using elapsedTime instead of the timeStamp on the 2nd
1495           * pre-condition since animations sometimes close off early */
1496          if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {
1497            activeAnimationComplete();
1498          }
1499        }
1500      }
1501
1502      function prepareStaggerDelay(delayStyle, staggerDelay, index) {
1503        var style = '';
1504        forEach(delayStyle.split(','), function(val, i) {
1505          style += (i > 0 ? ',' : '') +
1506                   (index * staggerDelay + parseInt(val, 10)) + 's';
1507        });
1508        return style;
1509      }
1510
1511      function animateBefore(animationEvent, element, className, calculationDecorator) {
1512        if(animateSetup(animationEvent, element, className, calculationDecorator)) {
1513          return function(cancelled) {
1514            cancelled && animateClose(element, className);
1515          };
1516        }
1517      }
1518
1519      function animateAfter(animationEvent, element, className, afterAnimationComplete) {
1520        if(element.data(NG_ANIMATE_CSS_DATA_KEY)) {
1521          return animateRun(animationEvent, element, className, afterAnimationComplete);
1522        } else {
1523          animateClose(element, className);
1524          afterAnimationComplete();
1525        }
1526      }
1527
1528      function animate(animationEvent, element, className, animationComplete) {
1529        //If the animateSetup function doesn't bother returning a
1530        //cancellation function then it means that there is no animation
1531        //to perform at all
1532        var preReflowCancellation = animateBefore(animationEvent, element, className);
1533        if(!preReflowCancellation) {
1534          animationComplete();
1535          return;
1536        }
1537
1538        //There are two cancellation functions: one is before the first
1539        //reflow animation and the second is during the active state
1540        //animation. The first function will take care of removing the
1541        //data from the element which will not make the 2nd animation
1542        //happen in the first place
1543        var cancel = preReflowCancellation;
1544        afterReflow(element, function() {
1545          unblockTransitions(element, className);
1546          unblockKeyframeAnimations(element);
1547          //once the reflow is complete then we point cancel to
1548          //the new cancellation function which will remove all of the
1549          //animation properties from the active animation
1550          cancel = animateAfter(animationEvent, element, className, animationComplete);
1551        });
1552
1553        return function(cancelled) {
1554          (cancel || noop)(cancelled);
1555        };
1556      }
1557
1558      function animateClose(element, className) {
1559        element.removeClass(className);
1560        var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
1561        if(data) {
1562          if(data.running) {
1563            data.running--;
1564          }
1565          if(!data.running || data.running === 0) {
1566            element.removeData(NG_ANIMATE_CSS_DATA_KEY);
1567          }
1568        }
1569      }
1570
1571      return {
1572        enter : function(element, animationCompleted) {
1573          return animate('enter', element, 'ng-enter', animationCompleted);
1574        },
1575
1576        leave : function(element, animationCompleted) {
1577          return animate('leave', element, 'ng-leave', animationCompleted);
1578        },
1579
1580        move : function(element, animationCompleted) {
1581          return animate('move', element, 'ng-move', animationCompleted);
1582        },
1583
1584        beforeSetClass : function(element, add, remove, animationCompleted) {
1585          var className = suffixClasses(remove, '-remove') + ' ' +
1586                          suffixClasses(add, '-add');
1587          var cancellationMethod = animateBefore('setClass', element, className, function(fn) {
1588            /* when classes are removed from an element then the transition style
1589             * that is applied is the transition defined on the element without the
1590             * CSS class being there. This is how CSS3 functions outside of ngAnimate.
1591             * http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */
1592            var klass = element.attr('class');
1593            element.removeClass(remove);
1594            element.addClass(add);
1595            var timings = fn();
1596            element.attr('class', klass);
1597            return timings;
1598          });
1599
1600          if(cancellationMethod) {
1601            afterReflow(element, function() {
1602              unblockTransitions(element, className);
1603              unblockKeyframeAnimations(element);
1604              animationCompleted();
1605            });
1606            return cancellationMethod;
1607          }
1608          animationCompleted();
1609        },
1610
1611        beforeAddClass : function(element, className, animationCompleted) {
1612          var cancellationMethod = animateBefore('addClass', element, suffixClasses(className, '-add'), function(fn) {
1613
1614            /* when a CSS class is added to an element then the transition style that
1615             * is applied is the transition defined on the element when the CSS class
1616             * is added at the time of the animation. This is how CSS3 functions
1617             * outside of ngAnimate. */
1618            element.addClass(className);
1619            var timings = fn();
1620            element.removeClass(className);
1621            return timings;
1622          });
1623
1624          if(cancellationMethod) {
1625            afterReflow(element, function() {
1626              unblockTransitions(element, className);
1627              unblockKeyframeAnimations(element);
1628              animationCompleted();
1629            });
1630            return cancellationMethod;
1631          }
1632          animationCompleted();
1633        },
1634
1635        setClass : function(element, add, remove, animationCompleted) {
1636          remove = suffixClasses(remove, '-remove');
1637          add = suffixClasses(add, '-add');
1638          var className = remove + ' ' + add;
1639          return animateAfter('setClass', element, className, animationCompleted);
1640        },
1641
1642        addClass : function(element, className, animationCompleted) {
1643          return animateAfter('addClass', element, suffixClasses(className, '-add'), animationCompleted);
1644        },
1645
1646        beforeRemoveClass : function(element, className, animationCompleted) {
1647          var cancellationMethod = animateBefore('removeClass', element, suffixClasses(className, '-remove'), function(fn) {
1648            /* when classes are removed from an element then the transition style
1649             * that is applied is the transition defined on the element without the
1650             * CSS class being there. This is how CSS3 functions outside of ngAnimate.
1651             * http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */
1652            var klass = element.attr('class');
1653            element.removeClass(className);
1654            var timings = fn();
1655            element.attr('class', klass);
1656            return timings;
1657          });
1658
1659          if(cancellationMethod) {
1660            afterReflow(element, function() {
1661              unblockTransitions(element, className);
1662              unblockKeyframeAnimations(element);
1663              animationCompleted();
1664            });
1665            return cancellationMethod;
1666          }
1667          animationCompleted();
1668        },
1669
1670        removeClass : function(element, className, animationCompleted) {
1671          return animateAfter('removeClass', element, suffixClasses(className, '-remove'), animationCompleted);
1672        }
1673      };
1674
1675      function suffixClasses(classes, suffix) {
1676        var className = '';
1677        classes = angular.isArray(classes) ? classes : classes.split(/\s+/);
1678        forEach(classes, function(klass, i) {
1679          if(klass && klass.length > 0) {
1680            className += (i > 0 ? ' ' : '') + klass + suffix;
1681          }
1682        });
1683        return className;
1684      }
1685    }]);
1686  }]);
1687
1688
1689})(window, window.angular);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.