angular-gem 1.2.1 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module AngularGem
2
- VERSION = "1.2.1"
2
+ VERSION = "1.2.2"
3
3
  end
@@ -0,0 +1,1283 @@
1
+ /**
2
+ * @license AngularJS v1.2.2
3
+ * (c) 2010-2012 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 overview
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
+ * {@installModule animate}
20
+ *
21
+ * <div doc-module-components="ngAnimate"></div>
22
+ *
23
+ * # Usage
24
+ *
25
+ * To see animations in action, all that is required is to define the appropriate CSS classes
26
+ * or to register a JavaScript animation via the myModule.animation() function. The directives that support animation automatically are:
27
+ * `ngRepeat`, `ngInclude`, `ngIf`, `ngSwitch`, `ngShow`, `ngHide`, `ngView` and `ngClass`. Custom directives can take advantage of animation
28
+ * by using the `$animate` service.
29
+ *
30
+ * Below is a more detailed breakdown of the supported animation events provided by pre-existing ng directives:
31
+ *
32
+ * | Directive | Supported Animations |
33
+ * |---------------------------------------------------------- |----------------------------------------------------|
34
+ * | {@link ng.directive:ngRepeat#usage_animations ngRepeat} | enter, leave and move |
35
+ * | {@link ngRoute.directive:ngView#usage_animations ngView} | enter and leave |
36
+ * | {@link ng.directive:ngInclude#usage_animations ngInclude} | enter and leave |
37
+ * | {@link ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave |
38
+ * | {@link ng.directive:ngIf#usage_animations ngIf} | enter and leave |
39
+ * | {@link ng.directive:ngClass#usage_animations ngClass} | add and remove |
40
+ * | {@link ng.directive:ngShow#usage_animations ngShow & ngHide} | add and remove (the ng-hide class value) |
41
+ *
42
+ * You can find out more information about animations upon visiting each directive page.
43
+ *
44
+ * Below is an example of how to apply animations to a directive that supports animation hooks:
45
+ *
46
+ * <pre>
47
+ * <style type="text/css">
48
+ * .slide.ng-enter, .slide.ng-leave {
49
+ * -webkit-transition:0.5s linear all;
50
+ * transition:0.5s linear all;
51
+ * }
52
+ *
53
+ * .slide.ng-enter { } /&#42; starting animations for enter &#42;/
54
+ * .slide.ng-enter-active { } /&#42; terminal animations for enter &#42;/
55
+ * .slide.ng-leave { } /&#42; starting animations for leave &#42;/
56
+ * .slide.ng-leave-active { } /&#42; terminal animations for leave &#42;/
57
+ * </style>
58
+ *
59
+ * <!--
60
+ * the animate service will automatically add .ng-enter and .ng-leave to the element
61
+ * to trigger the CSS transition/animations
62
+ * -->
63
+ * <ANY class="slide" ng-include="..."></ANY>
64
+ * </pre>
65
+ *
66
+ * Keep in mind that if an animation is running, any child elements cannot be animated until the parent element's
67
+ * animation has completed.
68
+ *
69
+ * <h2>CSS-defined Animations</h2>
70
+ * The animate service will automatically apply two CSS classes to the animated element and these two CSS classes
71
+ * are designed to contain the start and end CSS styling. Both CSS transitions and keyframe animations are supported
72
+ * and can be used to play along with this naming structure.
73
+ *
74
+ * The following code below demonstrates how to perform animations using **CSS transitions** with Angular:
75
+ *
76
+ * <pre>
77
+ * <style type="text/css">
78
+ * /&#42;
79
+ * The animate class is apart of the element and the ng-enter class
80
+ * is attached to the element once the enter animation event is triggered
81
+ * &#42;/
82
+ * .reveal-animation.ng-enter {
83
+ * -webkit-transition: 1s linear all; /&#42; Safari/Chrome &#42;/
84
+ * transition: 1s linear all; /&#42; All other modern browsers and IE10+ &#42;/
85
+ *
86
+ * /&#42; The animation preparation code &#42;/
87
+ * opacity: 0;
88
+ * }
89
+ *
90
+ * /&#42;
91
+ * Keep in mind that you want to combine both CSS
92
+ * classes together to avoid any CSS-specificity
93
+ * conflicts
94
+ * &#42;/
95
+ * .reveal-animation.ng-enter.ng-enter-active {
96
+ * /&#42; The animation code itself &#42;/
97
+ * opacity: 1;
98
+ * }
99
+ * </style>
100
+ *
101
+ * <div class="view-container">
102
+ * <div ng-view class="reveal-animation"></div>
103
+ * </div>
104
+ * </pre>
105
+ *
106
+ * The following code below demonstrates how to perform animations using **CSS animations** with Angular:
107
+ *
108
+ * <pre>
109
+ * <style type="text/css">
110
+ * .reveal-animation.ng-enter {
111
+ * -webkit-animation: enter_sequence 1s linear; /&#42; Safari/Chrome &#42;/
112
+ * animation: enter_sequence 1s linear; /&#42; IE10+ and Future Browsers &#42;/
113
+ * }
114
+ * &#64-webkit-keyframes enter_sequence {
115
+ * from { opacity:0; }
116
+ * to { opacity:1; }
117
+ * }
118
+ * &#64keyframes enter_sequence {
119
+ * from { opacity:0; }
120
+ * to { opacity:1; }
121
+ * }
122
+ * </style>
123
+ *
124
+ * <div class="view-container">
125
+ * <div ng-view class="reveal-animation"></div>
126
+ * </div>
127
+ * </pre>
128
+ *
129
+ * Both CSS3 animations and transitions can be used together and the animate service will figure out the correct duration and delay timing.
130
+ *
131
+ * Upon DOM mutation, the event class is added first (something like `ng-enter`), then the browser prepares itself to add
132
+ * the active class (in this case `ng-enter-active`) which then triggers the animation. The animation module will automatically
133
+ * detect the CSS code to determine when the animation ends. Once the animation is over then both CSS classes will be
134
+ * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end
135
+ * immediately resulting in a DOM element that is at its final state. This final state is when the DOM element
136
+ * has no CSS transition/animation classes applied to it.
137
+ *
138
+ * <h3>CSS Staggering Animations</h3>
139
+ * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a
140
+ * curtain-like effect. The ngAnimate module, as of 1.2.0, supports staggering animations and the stagger effect can be
141
+ * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for
142
+ * the animation. The style property expected within the stagger class can either be a **transition-delay** or an
143
+ * **animation-delay** property (or both if your animation contains both transitions and keyframe animations).
144
+ *
145
+ * <pre>
146
+ * .my-animation.ng-enter {
147
+ * /&#42; standard transition code &#42;/
148
+ * -webkit-transition: 1s linear all;
149
+ * transition: 1s linear all;
150
+ * opacity:0;
151
+ * }
152
+ * .my-animation.ng-enter-stagger {
153
+ * /&#42; this will have a 100ms delay between each successive leave animation &#42;/
154
+ * -webkit-transition-delay: 0.1s;
155
+ * transition-delay: 0.1s;
156
+ *
157
+ * /&#42; in case the stagger doesn't work then these two values
158
+ * must be set to 0 to avoid an accidental CSS inheritance &#42;/
159
+ * -webkit-transition-duration: 0s;
160
+ * transition-duration: 0s;
161
+ * }
162
+ * .my-animation.ng-enter.ng-enter-active {
163
+ * /&#42; standard transition styles &#42;/
164
+ * opacity:1;
165
+ * }
166
+ * </pre>
167
+ *
168
+ * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations
169
+ * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this
170
+ * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation
171
+ * will also be reset if more than 10ms has passed after the last animation has been fired.
172
+ *
173
+ * The following code will issue the **ng-leave-stagger** event on the element provided:
174
+ *
175
+ * <pre>
176
+ * var kids = parent.children();
177
+ *
178
+ * $animate.leave(kids[0]); //stagger index=0
179
+ * $animate.leave(kids[1]); //stagger index=1
180
+ * $animate.leave(kids[2]); //stagger index=2
181
+ * $animate.leave(kids[3]); //stagger index=3
182
+ * $animate.leave(kids[4]); //stagger index=4
183
+ *
184
+ * $timeout(function() {
185
+ * //stagger has reset itself
186
+ * $animate.leave(kids[5]); //stagger index=0
187
+ * $animate.leave(kids[6]); //stagger index=1
188
+ * }, 100, false);
189
+ * </pre>
190
+ *
191
+ * Stagger animations are currently only supported within CSS-defined animations.
192
+ *
193
+ * <h2>JavaScript-defined Animations</h2>
194
+ * 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
195
+ * yet support CSS transitions/animations, then you can make use of JavaScript animations defined inside of your AngularJS module.
196
+ *
197
+ * <pre>
198
+ * //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application.
199
+ * var ngModule = angular.module('YourApp', []);
200
+ * ngModule.animation('.my-crazy-animation', function() {
201
+ * return {
202
+ * enter: function(element, done) {
203
+ * //run the animation here and call done when the animation is complete
204
+ * return function(cancelled) {
205
+ * //this (optional) function will be called when the animation
206
+ * //completes or when the animation is cancelled (the cancelled
207
+ * //flag will be set to true if cancelled).
208
+ * }
209
+ * }
210
+ * leave: function(element, done) { },
211
+ * move: function(element, done) { },
212
+ *
213
+ * //animation that can be triggered before the class is added
214
+ * beforeAddClass: function(element, className, done) { },
215
+ *
216
+ * //animation that can be triggered after the class is added
217
+ * addClass: function(element, className, done) { },
218
+ *
219
+ * //animation that can be triggered before the class is removed
220
+ * beforeRemoveClass: function(element, className, done) { },
221
+ *
222
+ * //animation that can be triggered after the class is removed
223
+ * removeClass: function(element, className, done) { }
224
+ * }
225
+ * });
226
+ * </pre>
227
+ *
228
+ * JavaScript-defined animations are created with a CSS-like class selector and a collection of events which are set to run
229
+ * a javascript callback function. When an animation is triggered, $animate will look for a matching animation which fits
230
+ * the element's CSS class attribute value and then run the matching animation event function (if found).
231
+ * In other words, if the CSS classes present on the animated element match any of the JavaScript animations then the callback function will
232
+ * be executed. It should be also noted that only simple, single class selectors are allowed (compound class selectors are not supported).
233
+ *
234
+ * Within a JavaScript animation, an object containing various event callback animation functions is expected to be returned.
235
+ * As explained above, these callbacks are triggered based on the animation event. Therefore if an enter animation is run,
236
+ * and the JavaScript animation is found, then the enter callback will handle that animation (in addition to the CSS keyframe animation
237
+ * or transition code that is defined via a stylesheet).
238
+ *
239
+ */
240
+
241
+ angular.module('ngAnimate', ['ng'])
242
+
243
+ /**
244
+ * @ngdoc object
245
+ * @name ngAnimate.$animateProvider
246
+ * @description
247
+ *
248
+ * The `$animateProvider` allows developers to register JavaScript animation event handlers directly inside of a module.
249
+ * When an animation is triggered, the $animate service will query the $animate service to find any animations that match
250
+ * the provided name value.
251
+ *
252
+ * Requires the {@link ngAnimate `ngAnimate`} module to be installed.
253
+ *
254
+ * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
255
+ *
256
+ */
257
+ .config(['$provide', '$animateProvider', function($provide, $animateProvider) {
258
+ var noop = angular.noop;
259
+ var forEach = angular.forEach;
260
+ var selectors = $animateProvider.$$selectors;
261
+
262
+ var ELEMENT_NODE = 1;
263
+ var NG_ANIMATE_STATE = '$$ngAnimateState';
264
+ var NG_ANIMATE_CLASS_NAME = 'ng-animate';
265
+ var rootAnimateState = {running: true};
266
+
267
+ $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document',
268
+ function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document) {
269
+
270
+ $rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
271
+
272
+ // disable animations during bootstrap, but once we bootstrapped, enable animations
273
+ $rootScope.$$postDigest(function() {
274
+ rootAnimateState.running = false;
275
+ });
276
+
277
+ function lookup(name) {
278
+ if (name) {
279
+ var matches = [],
280
+ flagMap = {},
281
+ classes = name.substr(1).split('.');
282
+
283
+ //the empty string value is the default animation
284
+ //operation which performs CSS transition and keyframe
285
+ //animations sniffing. This is always included for each
286
+ //element animation procedure if the browser supports
287
+ //transitions and/or keyframe animations
288
+ if ($sniffer.transitions || $sniffer.animations) {
289
+ classes.push('');
290
+ }
291
+
292
+ for(var i=0; i < classes.length; i++) {
293
+ var klass = classes[i],
294
+ selectorFactoryName = selectors[klass];
295
+ if(selectorFactoryName && !flagMap[klass]) {
296
+ matches.push($injector.get(selectorFactoryName));
297
+ flagMap[klass] = true;
298
+ }
299
+ }
300
+ return matches;
301
+ }
302
+ }
303
+
304
+ /**
305
+ * @ngdoc object
306
+ * @name ngAnimate.$animate
307
+ * @function
308
+ *
309
+ * @description
310
+ * The `$animate` service provides animation detection support while performing DOM operations (enter, leave and move) as well as during addClass and removeClass operations.
311
+ * When any of these operations are run, the $animate service
312
+ * will examine any JavaScript-defined animations (which are defined by using the $animateProvider provider object)
313
+ * as well as any CSS-defined animations against the CSS classes present on the element once the DOM operation is run.
314
+ *
315
+ * The `$animate` service is used behind the scenes with pre-existing directives and animation with these directives
316
+ * will work out of the box without any extra configuration.
317
+ *
318
+ * Requires the {@link ngAnimate `ngAnimate`} module to be installed.
319
+ *
320
+ * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
321
+ *
322
+ */
323
+ return {
324
+ /**
325
+ * @ngdoc function
326
+ * @name ngAnimate.$animate#enter
327
+ * @methodOf ngAnimate.$animate
328
+ * @function
329
+ *
330
+ * @description
331
+ * Appends the element to the parentElement element that resides in the document and then runs the enter animation. Once
332
+ * the animation is started, the following CSS classes will be present on the element for the duration of the animation:
333
+ *
334
+ * Below is a breakdown of each step that occurs during enter animation:
335
+ *
336
+ * | Animation Step | What the element class attribute looks like |
337
+ * |----------------------------------------------------------------------------------------------|---------------------------------------------|
338
+ * | 1. $animate.enter(...) is called | class="my-animation" |
339
+ * | 2. element is inserted into the parentElement element or beside the afterElement element | class="my-animation" |
340
+ * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
341
+ * | 4. the .ng-enter class is added to the element | class="my-animation ng-animate ng-enter" |
342
+ * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-enter" |
343
+ * | 6. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-enter" |
344
+ * | 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" |
345
+ * | 8. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" |
346
+ * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
347
+ * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" |
348
+ *
349
+ * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
350
+ * @param {jQuery/jqLite element} parentElement the parent element of the element that will be the focus of the enter animation
351
+ * @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
352
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
353
+ */
354
+ enter : function(element, parentElement, afterElement, doneCallback) {
355
+ this.enabled(false, element);
356
+ $delegate.enter(element, parentElement, afterElement);
357
+ $rootScope.$$postDigest(function() {
358
+ performAnimation('enter', 'ng-enter', element, parentElement, afterElement, noop, doneCallback);
359
+ });
360
+ },
361
+
362
+ /**
363
+ * @ngdoc function
364
+ * @name ngAnimate.$animate#leave
365
+ * @methodOf ngAnimate.$animate
366
+ * @function
367
+ *
368
+ * @description
369
+ * Runs the leave animation operation and, upon completion, removes the element from the DOM. Once
370
+ * the animation is started, the following CSS classes will be added for the duration of the animation:
371
+ *
372
+ * Below is a breakdown of each step that occurs during enter animation:
373
+ *
374
+ * | Animation Step | What the element class attribute looks like |
375
+ * |----------------------------------------------------------------------------------------------|---------------------------------------------|
376
+ * | 1. $animate.leave(...) is called | class="my-animation" |
377
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
378
+ * | 3. the .ng-leave class is added to the element | class="my-animation ng-animate ng-leave" |
379
+ * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-leave" |
380
+ * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-leave" |
381
+ * | 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" |
382
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" |
383
+ * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
384
+ * | 9. The element is removed from the DOM | ... |
385
+ * | 10. The doneCallback() callback is fired (if provided) | ... |
386
+ *
387
+ * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
388
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
389
+ */
390
+ leave : function(element, doneCallback) {
391
+ cancelChildAnimations(element);
392
+ this.enabled(false, element);
393
+ $rootScope.$$postDigest(function() {
394
+ performAnimation('leave', 'ng-leave', element, null, null, function() {
395
+ $delegate.leave(element);
396
+ }, doneCallback);
397
+ });
398
+ },
399
+
400
+ /**
401
+ * @ngdoc function
402
+ * @name ngAnimate.$animate#move
403
+ * @methodOf ngAnimate.$animate
404
+ * @function
405
+ *
406
+ * @description
407
+ * Fires the move DOM operation. Just before the animation starts, the animate service will either append it into the parentElement container or
408
+ * add the element directly after the afterElement element if present. Then the move animation will be run. Once
409
+ * the animation is started, the following CSS classes will be added for the duration of the animation:
410
+ *
411
+ * Below is a breakdown of each step that occurs during move animation:
412
+ *
413
+ * | Animation Step | What the element class attribute looks like |
414
+ * |----------------------------------------------------------------------------------------------|---------------------------------------------|
415
+ * | 1. $animate.move(...) is called | class="my-animation" |
416
+ * | 2. element is moved into the parentElement element or beside the afterElement element | class="my-animation" |
417
+ * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
418
+ * | 4. the .ng-move class is added to the element | class="my-animation ng-animate ng-move" |
419
+ * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-move" |
420
+ * | 6. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-move" |
421
+ * | 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" |
422
+ * | 8. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" |
423
+ * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
424
+ * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" |
425
+ *
426
+ * @param {jQuery/jqLite element} element the element that will be the focus of the move animation
427
+ * @param {jQuery/jqLite element} parentElement the parentElement element of the element that will be the focus of the move animation
428
+ * @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
429
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
430
+ */
431
+ move : function(element, parentElement, afterElement, doneCallback) {
432
+ cancelChildAnimations(element);
433
+ this.enabled(false, element);
434
+ $delegate.move(element, parentElement, afterElement);
435
+ $rootScope.$$postDigest(function() {
436
+ performAnimation('move', 'ng-move', element, parentElement, afterElement, noop, doneCallback);
437
+ });
438
+ },
439
+
440
+ /**
441
+ * @ngdoc function
442
+ * @name ngAnimate.$animate#addClass
443
+ * @methodOf ngAnimate.$animate
444
+ *
445
+ * @description
446
+ * Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class.
447
+ * Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide
448
+ * the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if no CSS transitions
449
+ * or keyframes are defined on the -add or base CSS class).
450
+ *
451
+ * Below is a breakdown of each step that occurs during addClass animation:
452
+ *
453
+ * | Animation Step | What the element class attribute looks like |
454
+ * |------------------------------------------------------------------------------------------------|---------------------------------------------|
455
+ * | 1. $animate.addClass(element, 'super') is called | class="my-animation" |
456
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
457
+ * | 3. the .super-add class are added to the element | class="my-animation ng-animate super-add" |
458
+ * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super-add" |
459
+ * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate super-add" |
460
+ * | 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" |
461
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation super-add super-add-active" |
462
+ * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation super" |
463
+ * | 9. The super class is kept on the element | class="my-animation super" |
464
+ * | 10. The doneCallback() callback is fired (if provided) | class="my-animation super" |
465
+ *
466
+ * @param {jQuery/jqLite element} element the element that will be animated
467
+ * @param {string} className the CSS class that will be added to the element and then animated
468
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
469
+ */
470
+ addClass : function(element, className, doneCallback) {
471
+ performAnimation('addClass', className, element, null, null, function() {
472
+ $delegate.addClass(element, className);
473
+ }, doneCallback);
474
+ },
475
+
476
+ /**
477
+ * @ngdoc function
478
+ * @name ngAnimate.$animate#removeClass
479
+ * @methodOf ngAnimate.$animate
480
+ *
481
+ * @description
482
+ * Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value
483
+ * from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in
484
+ * order to provide the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if
485
+ * no CSS transitions or keyframes are defined on the -remove or base CSS classes).
486
+ *
487
+ * Below is a breakdown of each step that occurs during removeClass animation:
488
+ *
489
+ * | Animation Step | What the element class attribute looks like |
490
+ * |-----------------------------------------------------------------------------------------------|---------------------------------------------|
491
+ * | 1. $animate.removeClass(element, 'super') is called | class="my-animation super" |
492
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation super ng-animate" |
493
+ * | 3. the .super-remove class are added to the element | class="my-animation super ng-animate super-remove"|
494
+ * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation super ng-animate super-remove" |
495
+ * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation super ng-animate super-remove" |
496
+ * | 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" |
497
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active super-remove super-remove-active" |
498
+ * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
499
+ * | 9. The doneCallback() callback is fired (if provided) | class="my-animation" |
500
+ *
501
+ *
502
+ * @param {jQuery/jqLite element} element the element that will be animated
503
+ * @param {string} className the CSS class that will be animated and then removed from the element
504
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
505
+ */
506
+ removeClass : function(element, className, doneCallback) {
507
+ performAnimation('removeClass', className, element, null, null, function() {
508
+ $delegate.removeClass(element, className);
509
+ }, doneCallback);
510
+ },
511
+
512
+ /**
513
+ * @ngdoc function
514
+ * @name ngAnimate.$animate#enabled
515
+ * @methodOf ngAnimate.$animate
516
+ * @function
517
+ *
518
+ * @param {boolean=} value If provided then set the animation on or off.
519
+ * @param {jQuery/jqLite element=} element If provided then the element will be used to represent the enable/disable operation
520
+ * @return {boolean} Current animation state.
521
+ *
522
+ * @description
523
+ * Globally enables/disables animations.
524
+ *
525
+ */
526
+ enabled : function(value, element) {
527
+ switch(arguments.length) {
528
+ case 2:
529
+ if(value) {
530
+ cleanup(element);
531
+ } else {
532
+ var data = element.data(NG_ANIMATE_STATE) || {};
533
+ data.disabled = true;
534
+ element.data(NG_ANIMATE_STATE, data);
535
+ }
536
+ break;
537
+
538
+ case 1:
539
+ rootAnimateState.disabled = !value;
540
+ break;
541
+
542
+ default:
543
+ value = !rootAnimateState.disabled;
544
+ break;
545
+ }
546
+ return !!value;
547
+ }
548
+ };
549
+
550
+ /*
551
+ all animations call this shared animation triggering function internally.
552
+ The animationEvent variable refers to the JavaScript animation event that will be triggered
553
+ and the className value is the name of the animation that will be applied within the
554
+ CSS code. Element, parentElement and afterElement are provided DOM elements for the animation
555
+ and the onComplete callback will be fired once the animation is fully complete.
556
+ */
557
+ function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
558
+ var currentClassName = element.attr('class') || '';
559
+ var classes = currentClassName + ' ' + className;
560
+ var animationLookup = (' ' + classes).replace(/\s+/g,'.');
561
+ if (!parentElement) {
562
+ parentElement = afterElement ? afterElement.parent() : element.parent();
563
+ }
564
+
565
+ var matches = lookup(animationLookup);
566
+ var isClassBased = animationEvent == 'addClass' || animationEvent == 'removeClass';
567
+ var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
568
+
569
+ //skip the animation if animations are disabled, a parent is already being animated,
570
+ //the element is not currently attached to the document body or then completely close
571
+ //the animation if any matching animations are not found at all.
572
+ //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found.
573
+ if (animationsDisabled(element, parentElement) || matches.length === 0) {
574
+ fireDOMOperation();
575
+ closeAnimation();
576
+ return;
577
+ }
578
+
579
+ var animations = [];
580
+ //only add animations if the currently running animation is not structural
581
+ //or if there is no animation running at all
582
+ if(!ngAnimateState.running || !(isClassBased && ngAnimateState.structural)) {
583
+ forEach(matches, function(animation) {
584
+ //add the animation to the queue to if it is allowed to be cancelled
585
+ if(!animation.allowCancel || animation.allowCancel(element, animationEvent, className)) {
586
+ var beforeFn, afterFn = animation[animationEvent];
587
+
588
+ //Special case for a leave animation since there is no point in performing an
589
+ //animation on a element node that has already been removed from the DOM
590
+ if(animationEvent == 'leave') {
591
+ beforeFn = afterFn;
592
+ afterFn = null; //this must be falsy so that the animation is skipped for leave
593
+ } else {
594
+ beforeFn = animation['before' + animationEvent.charAt(0).toUpperCase() + animationEvent.substr(1)];
595
+ }
596
+ animations.push({
597
+ before : beforeFn,
598
+ after : afterFn
599
+ });
600
+ }
601
+ });
602
+ }
603
+
604
+ //this would mean that an animation was not allowed so let the existing
605
+ //animation do it's thing and close this one early
606
+ if(animations.length === 0) {
607
+ fireDOMOperation();
608
+ fireDoneCallbackAsync();
609
+ return;
610
+ }
611
+
612
+ //this value will be searched for class-based CSS className lookup. Therefore,
613
+ //we prefix and suffix the current className value with spaces to avoid substring
614
+ //lookups of className tokens
615
+ var futureClassName = ' ' + currentClassName + ' ';
616
+ if(ngAnimateState.running) {
617
+ //if an animation is currently running on the element then lets take the steps
618
+ //to cancel that animation and fire any required callbacks
619
+ $timeout.cancel(ngAnimateState.closeAnimationTimeout);
620
+ cleanup(element);
621
+ cancelAnimations(ngAnimateState.animations);
622
+
623
+ //if the class is removed during the reflow then it will revert the styles temporarily
624
+ //back to the base class CSS styling causing a jump-like effect to occur. This check
625
+ //here ensures that the domOperation is only performed after the reflow has commenced
626
+ if(ngAnimateState.beforeComplete) {
627
+ (ngAnimateState.done || noop)(true);
628
+ } else if(isClassBased && !ngAnimateState.structural) {
629
+ //class-based animations will compare element className values after cancelling the
630
+ //previous animation to see if the element properties already contain the final CSS
631
+ //class and if so then the animation will be skipped. Since the domOperation will
632
+ //be performed only after the reflow is complete then our element's className value
633
+ //will be invalid. Therefore the same string manipulation that would occur within the
634
+ //DOM operation will be performed below so that the class comparison is valid...
635
+ futureClassName = ngAnimateState.event == 'removeClass' ?
636
+ futureClassName.replace(ngAnimateState.className, '') :
637
+ futureClassName + ngAnimateState.className + ' ';
638
+ }
639
+ }
640
+
641
+ //There is no point in perform a class-based animation if the element already contains
642
+ //(on addClass) or doesn't contain (on removeClass) the className being animated.
643
+ //The reason why this is being called after the previous animations are cancelled
644
+ //is so that the CSS classes present on the element can be properly examined.
645
+ var classNameToken = ' ' + className + ' ';
646
+ if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) ||
647
+ (animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {
648
+ fireDOMOperation();
649
+ fireDoneCallbackAsync();
650
+ return;
651
+ }
652
+
653
+ //the ng-animate class does nothing, but it's here to allow for
654
+ //parent animations to find and cancel child animations when needed
655
+ element.addClass(NG_ANIMATE_CLASS_NAME);
656
+
657
+ element.data(NG_ANIMATE_STATE, {
658
+ running:true,
659
+ event:animationEvent,
660
+ className:className,
661
+ structural:!isClassBased,
662
+ animations:animations,
663
+ done:onBeforeAnimationsComplete
664
+ });
665
+
666
+ //first we run the before animations and when all of those are complete
667
+ //then we perform the DOM operation and run the next set of animations
668
+ invokeRegisteredAnimationFns(animations, 'before', onBeforeAnimationsComplete);
669
+
670
+ function onBeforeAnimationsComplete(cancelled) {
671
+ fireDOMOperation();
672
+ if(cancelled === true) {
673
+ closeAnimation();
674
+ return;
675
+ }
676
+
677
+ //set the done function to the final done function
678
+ //so that the DOM event won't be executed twice by accident
679
+ //if the after animation is cancelled as well
680
+ var data = element.data(NG_ANIMATE_STATE);
681
+ if(data) {
682
+ data.done = closeAnimation;
683
+ element.data(NG_ANIMATE_STATE, data);
684
+ }
685
+ invokeRegisteredAnimationFns(animations, 'after', closeAnimation);
686
+ }
687
+
688
+ function invokeRegisteredAnimationFns(animations, phase, allAnimationFnsComplete) {
689
+ var endFnName = phase + 'End';
690
+ forEach(animations, function(animation, index) {
691
+ var animationPhaseCompleted = function() {
692
+ progress(index, phase);
693
+ };
694
+
695
+ //there are no before functions for enter + move since the DOM
696
+ //operations happen before the performAnimation method fires
697
+ if(phase == 'before' && (animationEvent == 'enter' || animationEvent == 'move')) {
698
+ animationPhaseCompleted();
699
+ return;
700
+ }
701
+
702
+ if(animation[phase]) {
703
+ animation[endFnName] = isClassBased ?
704
+ animation[phase](element, className, animationPhaseCompleted) :
705
+ animation[phase](element, animationPhaseCompleted);
706
+ } else {
707
+ animationPhaseCompleted();
708
+ }
709
+ });
710
+
711
+ function progress(index, phase) {
712
+ var phaseCompletionFlag = phase + 'Complete';
713
+ var currentAnimation = animations[index];
714
+ currentAnimation[phaseCompletionFlag] = true;
715
+ (currentAnimation[endFnName] || noop)();
716
+
717
+ for(var i=0;i<animations.length;i++) {
718
+ if(!animations[i][phaseCompletionFlag]) return;
719
+ }
720
+
721
+ allAnimationFnsComplete();
722
+ }
723
+ }
724
+
725
+ function fireDoneCallbackAsync() {
726
+ doneCallback && $timeout(doneCallback, 0, false);
727
+ }
728
+
729
+ //it is less complicated to use a flag than managing and cancelling
730
+ //timeouts containing multiple callbacks.
731
+ function fireDOMOperation() {
732
+ if(!fireDOMOperation.hasBeenRun) {
733
+ fireDOMOperation.hasBeenRun = true;
734
+ domOperation();
735
+ }
736
+ }
737
+
738
+ function closeAnimation() {
739
+ if(!closeAnimation.hasBeenRun) {
740
+ closeAnimation.hasBeenRun = true;
741
+ var data = element.data(NG_ANIMATE_STATE);
742
+ if(data) {
743
+ /* only structural animations wait for reflow before removing an
744
+ animation, but class-based animations don't. An example of this
745
+ failing would be when a parent HTML tag has a ng-class attribute
746
+ causing ALL directives below to skip animations during the digest */
747
+ if(isClassBased) {
748
+ cleanup(element);
749
+ } else {
750
+ data.closeAnimationTimeout = $timeout(function() {
751
+ cleanup(element);
752
+ }, 0, false);
753
+ element.data(NG_ANIMATE_STATE, data);
754
+ }
755
+ }
756
+ fireDoneCallbackAsync();
757
+ }
758
+ }
759
+ }
760
+
761
+ function cancelChildAnimations(element) {
762
+ var node = element[0];
763
+ if(node.nodeType != ELEMENT_NODE) {
764
+ return;
765
+ }
766
+
767
+ forEach(node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME), function(element) {
768
+ element = angular.element(element);
769
+ var data = element.data(NG_ANIMATE_STATE);
770
+ if(data) {
771
+ cancelAnimations(data.animations);
772
+ cleanup(element);
773
+ }
774
+ });
775
+ }
776
+
777
+ function cancelAnimations(animations) {
778
+ var isCancelledFlag = true;
779
+ forEach(animations, function(animation) {
780
+ if(!animations.beforeComplete) {
781
+ (animation.beforeEnd || noop)(isCancelledFlag);
782
+ }
783
+ if(!animations.afterComplete) {
784
+ (animation.afterEnd || noop)(isCancelledFlag);
785
+ }
786
+ });
787
+ }
788
+
789
+ function cleanup(element) {
790
+ if(element[0] == $rootElement[0]) {
791
+ if(!rootAnimateState.disabled) {
792
+ rootAnimateState.running = false;
793
+ rootAnimateState.structural = false;
794
+ }
795
+ } else {
796
+ element.removeClass(NG_ANIMATE_CLASS_NAME);
797
+ element.removeData(NG_ANIMATE_STATE);
798
+ }
799
+ }
800
+
801
+ function animationsDisabled(element, parentElement) {
802
+ if (rootAnimateState.disabled) return true;
803
+
804
+ if(element[0] == $rootElement[0]) {
805
+ return rootAnimateState.disabled || rootAnimateState.running;
806
+ }
807
+
808
+ do {
809
+ //the element did not reach the root element which means that it
810
+ //is not apart of the DOM. Therefore there is no reason to do
811
+ //any animations on it
812
+ if(parentElement.length === 0) break;
813
+
814
+ var isRoot = parentElement[0] == $rootElement[0];
815
+ var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE);
816
+ var result = state && (!!state.disabled || !!state.running);
817
+ if(isRoot || result) {
818
+ return result;
819
+ }
820
+
821
+ if(isRoot) return true;
822
+ }
823
+ while(parentElement = parentElement.parent());
824
+
825
+ return true;
826
+ }
827
+ }]);
828
+
829
+ $animateProvider.register('', ['$window', '$sniffer', '$timeout', function($window, $sniffer, $timeout) {
830
+ // Detect proper transitionend/animationend event names.
831
+ var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
832
+
833
+ // If unprefixed events are not supported but webkit-prefixed are, use the latter.
834
+ // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
835
+ // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
836
+ // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
837
+ // Register both events in case `window.onanimationend` is not supported because of that,
838
+ // do the same for `transitionend` as Safari is likely to exhibit similar behavior.
839
+ // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
840
+ // therefore there is no reason to test anymore for other vendor prefixes: http://caniuse.com/#search=transition
841
+ if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) {
842
+ CSS_PREFIX = '-webkit-';
843
+ TRANSITION_PROP = 'WebkitTransition';
844
+ TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
845
+ } else {
846
+ TRANSITION_PROP = 'transition';
847
+ TRANSITIONEND_EVENT = 'transitionend';
848
+ }
849
+
850
+ if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) {
851
+ CSS_PREFIX = '-webkit-';
852
+ ANIMATION_PROP = 'WebkitAnimation';
853
+ ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
854
+ } else {
855
+ ANIMATION_PROP = 'animation';
856
+ ANIMATIONEND_EVENT = 'animationend';
857
+ }
858
+
859
+ var DURATION_KEY = 'Duration';
860
+ var PROPERTY_KEY = 'Property';
861
+ var DELAY_KEY = 'Delay';
862
+ var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
863
+ var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey';
864
+ var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data';
865
+ var NG_ANIMATE_FALLBACK_CLASS_NAME = 'ng-animate-start';
866
+ var NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME = 'ng-animate-active';
867
+
868
+ var lookupCache = {};
869
+ var parentCounter = 0;
870
+
871
+ var animationReflowQueue = [], animationTimer, timeOut = false;
872
+ function afterReflow(callback) {
873
+ animationReflowQueue.push(callback);
874
+ $timeout.cancel(animationTimer);
875
+ animationTimer = $timeout(function() {
876
+ forEach(animationReflowQueue, function(fn) {
877
+ fn();
878
+ });
879
+ animationReflowQueue = [];
880
+ animationTimer = null;
881
+ lookupCache = {};
882
+ }, 10, false);
883
+ }
884
+
885
+ function getElementAnimationDetails(element, cacheKey) {
886
+ var data = cacheKey ? lookupCache[cacheKey] : null;
887
+ if(!data) {
888
+ var transitionDuration = 0;
889
+ var transitionDelay = 0;
890
+ var animationDuration = 0;
891
+ var animationDelay = 0;
892
+ var transitionDelayStyle;
893
+ var animationDelayStyle;
894
+ var transitionDurationStyle;
895
+ var transitionPropertyStyle;
896
+
897
+ //we want all the styles defined before and after
898
+ forEach(element, function(element) {
899
+ if (element.nodeType == ELEMENT_NODE) {
900
+ var elementStyles = $window.getComputedStyle(element) || {};
901
+
902
+ transitionDurationStyle = elementStyles[TRANSITION_PROP + DURATION_KEY];
903
+
904
+ transitionDuration = Math.max(parseMaxTime(transitionDurationStyle), transitionDuration);
905
+
906
+ transitionPropertyStyle = elementStyles[TRANSITION_PROP + PROPERTY_KEY];
907
+
908
+ transitionDelayStyle = elementStyles[TRANSITION_PROP + DELAY_KEY];
909
+
910
+ transitionDelay = Math.max(parseMaxTime(transitionDelayStyle), transitionDelay);
911
+
912
+ animationDelayStyle = elementStyles[ANIMATION_PROP + DELAY_KEY];
913
+
914
+ animationDelay = Math.max(parseMaxTime(animationDelayStyle), animationDelay);
915
+
916
+ var aDuration = parseMaxTime(elementStyles[ANIMATION_PROP + DURATION_KEY]);
917
+
918
+ if(aDuration > 0) {
919
+ aDuration *= parseInt(elementStyles[ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY], 10) || 1;
920
+ }
921
+
922
+ animationDuration = Math.max(aDuration, animationDuration);
923
+ }
924
+ });
925
+ data = {
926
+ total : 0,
927
+ transitionPropertyStyle: transitionPropertyStyle,
928
+ transitionDurationStyle: transitionDurationStyle,
929
+ transitionDelayStyle: transitionDelayStyle,
930
+ transitionDelay: transitionDelay,
931
+ transitionDuration: transitionDuration,
932
+ animationDelayStyle: animationDelayStyle,
933
+ animationDelay: animationDelay,
934
+ animationDuration: animationDuration
935
+ };
936
+ if(cacheKey) {
937
+ lookupCache[cacheKey] = data;
938
+ }
939
+ }
940
+ return data;
941
+ }
942
+
943
+ function parseMaxTime(str) {
944
+ var maxValue = 0;
945
+ var values = angular.isString(str) ?
946
+ str.split(/\s*,\s*/) :
947
+ [];
948
+ forEach(values, function(value) {
949
+ maxValue = Math.max(parseFloat(value) || 0, maxValue);
950
+ });
951
+ return maxValue;
952
+ }
953
+
954
+ function getCacheKey(element) {
955
+ var parentElement = element.parent();
956
+ var parentID = parentElement.data(NG_ANIMATE_PARENT_KEY);
957
+ if(!parentID) {
958
+ parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter);
959
+ parentID = parentCounter;
960
+ }
961
+ return parentID + '-' + element[0].className;
962
+ }
963
+
964
+ function animateSetup(element, className) {
965
+ var cacheKey = getCacheKey(element);
966
+ var eventCacheKey = cacheKey + ' ' + className;
967
+ var stagger = {};
968
+ var ii = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
969
+
970
+ if(ii > 0) {
971
+ var staggerClassName = className + '-stagger';
972
+ var staggerCacheKey = cacheKey + ' ' + staggerClassName;
973
+ var applyClasses = !lookupCache[staggerCacheKey];
974
+
975
+ applyClasses && element.addClass(staggerClassName);
976
+
977
+ stagger = getElementAnimationDetails(element, staggerCacheKey);
978
+
979
+ applyClasses && element.removeClass(staggerClassName);
980
+ }
981
+
982
+ element.addClass(className);
983
+
984
+ var timings = getElementAnimationDetails(element, eventCacheKey);
985
+
986
+ /* there is no point in performing a reflow if the animation
987
+ timeout is empty (this would cause a flicker bug normally
988
+ in the page. There is also no point in performing an animation
989
+ that only has a delay and no duration */
990
+ var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
991
+ if(maxDuration === 0) {
992
+ element.removeClass(className);
993
+ return false;
994
+ }
995
+
996
+ var node = element[0];
997
+ //temporarily disable the transition so that the enter styles
998
+ //don't animate twice (this is here to avoid a bug in Chrome/FF).
999
+ var activeClassName = '';
1000
+ if(timings.transitionDuration > 0) {
1001
+ element.addClass(NG_ANIMATE_FALLBACK_CLASS_NAME);
1002
+ activeClassName += NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME + ' ';
1003
+ blockTransitions(element);
1004
+ } else {
1005
+ blockKeyframeAnimations(element);
1006
+ }
1007
+
1008
+ forEach(className.split(' '), function(klass, i) {
1009
+ activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
1010
+ });
1011
+
1012
+ element.data(NG_ANIMATE_CSS_DATA_KEY, {
1013
+ className : className,
1014
+ activeClassName : activeClassName,
1015
+ maxDuration : maxDuration,
1016
+ classes : className + ' ' + activeClassName,
1017
+ timings : timings,
1018
+ stagger : stagger,
1019
+ ii : ii
1020
+ });
1021
+
1022
+ return true;
1023
+ }
1024
+
1025
+ function blockTransitions(element) {
1026
+ element[0].style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
1027
+ }
1028
+
1029
+ function blockKeyframeAnimations(element) {
1030
+ element[0].style[ANIMATION_PROP] = 'none 0s';
1031
+ }
1032
+
1033
+ function unblockTransitions(element) {
1034
+ var node = element[0], prop = TRANSITION_PROP + PROPERTY_KEY;
1035
+ if(node.style[prop] && node.style[prop].length > 0) {
1036
+ node.style[prop] = '';
1037
+ }
1038
+ }
1039
+
1040
+ function unblockKeyframeAnimations(element) {
1041
+ element[0].style[ANIMATION_PROP] = '';
1042
+ }
1043
+
1044
+ function animateRun(element, className, activeAnimationComplete) {
1045
+ var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
1046
+ if(!element.hasClass(className) || !data) {
1047
+ activeAnimationComplete();
1048
+ return;
1049
+ }
1050
+
1051
+ var node = element[0];
1052
+ var timings = data.timings;
1053
+ var stagger = data.stagger;
1054
+ var maxDuration = data.maxDuration;
1055
+ var activeClassName = data.activeClassName;
1056
+ var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * 1000;
1057
+ var startTime = Date.now();
1058
+ var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
1059
+ var ii = data.ii;
1060
+
1061
+ var applyFallbackStyle, style = '', appliedStyles = [];
1062
+ if(timings.transitionDuration > 0) {
1063
+ var propertyStyle = timings.transitionPropertyStyle;
1064
+ if(propertyStyle.indexOf('all') == -1) {
1065
+ applyFallbackStyle = true;
1066
+ var fallbackProperty = $sniffer.msie ? '-ms-zoom' : 'border-spacing';
1067
+ style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ', ' + fallbackProperty + '; ';
1068
+ style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ', ' + timings.transitionDuration + 's; ';
1069
+ appliedStyles.push(CSS_PREFIX + 'transition-property');
1070
+ appliedStyles.push(CSS_PREFIX + 'transition-duration');
1071
+ }
1072
+ } else {
1073
+ unblockKeyframeAnimations(element);
1074
+ }
1075
+
1076
+ if(ii > 0) {
1077
+ if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
1078
+ var delayStyle = timings.transitionDelayStyle;
1079
+ if(applyFallbackStyle) {
1080
+ delayStyle += ', ' + timings.transitionDelay + 's';
1081
+ }
1082
+
1083
+ style += CSS_PREFIX + 'transition-delay: ' +
1084
+ prepareStaggerDelay(delayStyle, stagger.transitionDelay, ii) + '; ';
1085
+ appliedStyles.push(CSS_PREFIX + 'transition-delay');
1086
+ }
1087
+
1088
+ if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
1089
+ style += CSS_PREFIX + 'animation-delay: ' +
1090
+ prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, ii) + '; ';
1091
+ appliedStyles.push(CSS_PREFIX + 'animation-delay');
1092
+ }
1093
+ }
1094
+
1095
+ if(appliedStyles.length > 0) {
1096
+ var oldStyle = node.getAttribute('style') || '';
1097
+ node.setAttribute('style', oldStyle + ' ' + style);
1098
+ }
1099
+
1100
+ element.on(css3AnimationEvents, onAnimationProgress);
1101
+ element.addClass(activeClassName);
1102
+
1103
+ // This will automatically be called by $animate so
1104
+ // there is no need to attach this internally to the
1105
+ // timeout done method.
1106
+ return function onEnd(cancelled) {
1107
+ element.off(css3AnimationEvents, onAnimationProgress);
1108
+ element.removeClass(activeClassName);
1109
+ animateClose(element, className);
1110
+ for (var i in appliedStyles) {
1111
+ node.style.removeProperty(appliedStyles[i]);
1112
+ }
1113
+ };
1114
+
1115
+ function onAnimationProgress(event) {
1116
+ event.stopPropagation();
1117
+ var ev = event.originalEvent || event;
1118
+ var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
1119
+ /* $manualTimeStamp is a mocked timeStamp value which is set
1120
+ * within browserTrigger(). This is only here so that tests can
1121
+ * mock animations properly. Real events fallback to event.timeStamp,
1122
+ * or, if they don't, then a timeStamp is automatically created for them.
1123
+ * We're checking to see if the timeStamp surpasses the expected delay,
1124
+ * but we're using elapsedTime instead of the timeStamp on the 2nd
1125
+ * pre-condition since animations sometimes close off early */
1126
+ if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && ev.elapsedTime >= maxDuration) {
1127
+ activeAnimationComplete();
1128
+ }
1129
+ }
1130
+ }
1131
+
1132
+ function prepareStaggerDelay(delayStyle, staggerDelay, index) {
1133
+ var style = '';
1134
+ forEach(delayStyle.split(','), function(val, i) {
1135
+ style += (i > 0 ? ',' : '') +
1136
+ (index * staggerDelay + parseInt(val, 10)) + 's';
1137
+ });
1138
+ return style;
1139
+ }
1140
+
1141
+ function animateBefore(element, className) {
1142
+ if(animateSetup(element, className)) {
1143
+ return function(cancelled) {
1144
+ cancelled && animateClose(element, className);
1145
+ };
1146
+ }
1147
+ }
1148
+
1149
+ function animateAfter(element, className, afterAnimationComplete) {
1150
+ if(element.data(NG_ANIMATE_CSS_DATA_KEY)) {
1151
+ return animateRun(element, className, afterAnimationComplete);
1152
+ } else {
1153
+ animateClose(element, className);
1154
+ afterAnimationComplete();
1155
+ }
1156
+ }
1157
+
1158
+ function animate(element, className, animationComplete) {
1159
+ //If the animateSetup function doesn't bother returning a
1160
+ //cancellation function then it means that there is no animation
1161
+ //to perform at all
1162
+ var preReflowCancellation = animateBefore(element, className);
1163
+ if(!preReflowCancellation) {
1164
+ animationComplete();
1165
+ return;
1166
+ }
1167
+
1168
+ //There are two cancellation functions: one is before the first
1169
+ //reflow animation and the second is during the active state
1170
+ //animation. The first function will take care of removing the
1171
+ //data from the element which will not make the 2nd animation
1172
+ //happen in the first place
1173
+ var cancel = preReflowCancellation;
1174
+ afterReflow(function() {
1175
+ unblockTransitions(element);
1176
+ //once the reflow is complete then we point cancel to
1177
+ //the new cancellation function which will remove all of the
1178
+ //animation properties from the active animation
1179
+ cancel = animateAfter(element, className, animationComplete);
1180
+ });
1181
+
1182
+ return function(cancelled) {
1183
+ (cancel || noop)(cancelled);
1184
+ };
1185
+ }
1186
+
1187
+ function animateClose(element, className) {
1188
+ element.removeClass(className);
1189
+ element.removeClass(NG_ANIMATE_FALLBACK_CLASS_NAME);
1190
+ element.removeData(NG_ANIMATE_CSS_DATA_KEY);
1191
+ }
1192
+
1193
+ return {
1194
+ allowCancel : function(element, animationEvent, className) {
1195
+ //always cancel the current animation if it is a
1196
+ //structural animation
1197
+ var oldClasses = (element.data(NG_ANIMATE_CSS_DATA_KEY) || {}).classes;
1198
+ if(!oldClasses || ['enter','leave','move'].indexOf(animationEvent) >= 0) {
1199
+ return true;
1200
+ }
1201
+
1202
+ var parentElement = element.parent();
1203
+ var clone = angular.element(element[0].cloneNode());
1204
+
1205
+ //make the element super hidden and override any CSS style values
1206
+ clone.attr('style','position:absolute; top:-9999px; left:-9999px');
1207
+ clone.removeAttr('id');
1208
+ clone.html('');
1209
+
1210
+ forEach(oldClasses.split(' '), function(klass) {
1211
+ clone.removeClass(klass);
1212
+ });
1213
+
1214
+ var suffix = animationEvent == 'addClass' ? '-add' : '-remove';
1215
+ clone.addClass(suffixClasses(className, suffix));
1216
+ parentElement.append(clone);
1217
+
1218
+ var timings = getElementAnimationDetails(clone);
1219
+ clone.remove();
1220
+
1221
+ return Math.max(timings.transitionDuration, timings.animationDuration) > 0;
1222
+ },
1223
+
1224
+ enter : function(element, animationCompleted) {
1225
+ return animate(element, 'ng-enter', animationCompleted);
1226
+ },
1227
+
1228
+ leave : function(element, animationCompleted) {
1229
+ return animate(element, 'ng-leave', animationCompleted);
1230
+ },
1231
+
1232
+ move : function(element, animationCompleted) {
1233
+ return animate(element, 'ng-move', animationCompleted);
1234
+ },
1235
+
1236
+ beforeAddClass : function(element, className, animationCompleted) {
1237
+ var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'));
1238
+ if(cancellationMethod) {
1239
+ afterReflow(function() {
1240
+ unblockTransitions(element);
1241
+ animationCompleted();
1242
+ });
1243
+ return cancellationMethod;
1244
+ }
1245
+ animationCompleted();
1246
+ },
1247
+
1248
+ addClass : function(element, className, animationCompleted) {
1249
+ return animateAfter(element, suffixClasses(className, '-add'), animationCompleted);
1250
+ },
1251
+
1252
+ beforeRemoveClass : function(element, className, animationCompleted) {
1253
+ var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'));
1254
+ if(cancellationMethod) {
1255
+ afterReflow(function() {
1256
+ unblockTransitions(element);
1257
+ animationCompleted();
1258
+ });
1259
+ return cancellationMethod;
1260
+ }
1261
+ animationCompleted();
1262
+ },
1263
+
1264
+ removeClass : function(element, className, animationCompleted) {
1265
+ return animateAfter(element, suffixClasses(className, '-remove'), animationCompleted);
1266
+ }
1267
+ };
1268
+
1269
+ function suffixClasses(classes, suffix) {
1270
+ var className = '';
1271
+ classes = angular.isArray(classes) ? classes : classes.split(/\s+/);
1272
+ forEach(classes, function(klass, i) {
1273
+ if(klass && klass.length > 0) {
1274
+ className += (i > 0 ? ' ' : '') + klass + suffix;
1275
+ }
1276
+ });
1277
+ return className;
1278
+ }
1279
+ }]);
1280
+ }]);
1281
+
1282
+
1283
+ })(window, window.angular);