angular-gem 1.2.2 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1294 @@
1
+ /**
2
+ * @license AngularJS v1.2.3
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 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, wait again
273
+ // for another digest until enabling animations. The reason why we digest twice
274
+ // is because all structural animations (enter, leave and move) all perform a
275
+ // post digest operation before animating. If we only wait for a single digest
276
+ // to pass then the structural animation would render its animation on page load.
277
+ // (which is what we're trying to avoid when the application first boots up.)
278
+ $rootScope.$$postDigest(function() {
279
+ $rootScope.$$postDigest(function() {
280
+ rootAnimateState.running = false;
281
+ });
282
+ });
283
+
284
+ function lookup(name) {
285
+ if (name) {
286
+ var matches = [],
287
+ flagMap = {},
288
+ classes = name.substr(1).split('.');
289
+
290
+ //the empty string value is the default animation
291
+ //operation which performs CSS transition and keyframe
292
+ //animations sniffing. This is always included for each
293
+ //element animation procedure if the browser supports
294
+ //transitions and/or keyframe animations
295
+ if ($sniffer.transitions || $sniffer.animations) {
296
+ classes.push('');
297
+ }
298
+
299
+ for(var i=0; i < classes.length; i++) {
300
+ var klass = classes[i],
301
+ selectorFactoryName = selectors[klass];
302
+ if(selectorFactoryName && !flagMap[klass]) {
303
+ matches.push($injector.get(selectorFactoryName));
304
+ flagMap[klass] = true;
305
+ }
306
+ }
307
+ return matches;
308
+ }
309
+ }
310
+
311
+ /**
312
+ * @ngdoc object
313
+ * @name ngAnimate.$animate
314
+ * @function
315
+ *
316
+ * @description
317
+ * The `$animate` service provides animation detection support while performing DOM operations (enter, leave and move) as well as during addClass and removeClass operations.
318
+ * When any of these operations are run, the $animate service
319
+ * will examine any JavaScript-defined animations (which are defined by using the $animateProvider provider object)
320
+ * as well as any CSS-defined animations against the CSS classes present on the element once the DOM operation is run.
321
+ *
322
+ * The `$animate` service is used behind the scenes with pre-existing directives and animation with these directives
323
+ * will work out of the box without any extra configuration.
324
+ *
325
+ * Requires the {@link ngAnimate `ngAnimate`} module to be installed.
326
+ *
327
+ * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
328
+ *
329
+ */
330
+ return {
331
+ /**
332
+ * @ngdoc function
333
+ * @name ngAnimate.$animate#enter
334
+ * @methodOf ngAnimate.$animate
335
+ * @function
336
+ *
337
+ * @description
338
+ * Appends the element to the parentElement element that resides in the document and then runs the enter animation. Once
339
+ * the animation is started, the following CSS classes will be present on the element for the duration of the animation:
340
+ *
341
+ * Below is a breakdown of each step that occurs during enter animation:
342
+ *
343
+ * | Animation Step | What the element class attribute looks like |
344
+ * |----------------------------------------------------------------------------------------------|---------------------------------------------|
345
+ * | 1. $animate.enter(...) is called | class="my-animation" |
346
+ * | 2. element is inserted into the parentElement element or beside the afterElement element | class="my-animation" |
347
+ * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
348
+ * | 4. the .ng-enter class is added to the element | class="my-animation ng-animate ng-enter" |
349
+ * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-enter" |
350
+ * | 6. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-enter" |
351
+ * | 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" |
352
+ * | 8. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-enter ng-enter-active" |
353
+ * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
354
+ * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" |
355
+ *
356
+ * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
357
+ * @param {jQuery/jqLite element} parentElement the parent element of the element that will be the focus of the enter animation
358
+ * @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
359
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
360
+ */
361
+ enter : function(element, parentElement, afterElement, doneCallback) {
362
+ this.enabled(false, element);
363
+ $delegate.enter(element, parentElement, afterElement);
364
+ $rootScope.$$postDigest(function() {
365
+ performAnimation('enter', 'ng-enter', element, parentElement, afterElement, noop, doneCallback);
366
+ });
367
+ },
368
+
369
+ /**
370
+ * @ngdoc function
371
+ * @name ngAnimate.$animate#leave
372
+ * @methodOf ngAnimate.$animate
373
+ * @function
374
+ *
375
+ * @description
376
+ * Runs the leave animation operation and, upon completion, removes the element from the DOM. Once
377
+ * the animation is started, the following CSS classes will be added for the duration of the animation:
378
+ *
379
+ * Below is a breakdown of each step that occurs during enter animation:
380
+ *
381
+ * | Animation Step | What the element class attribute looks like |
382
+ * |----------------------------------------------------------------------------------------------|---------------------------------------------|
383
+ * | 1. $animate.leave(...) is called | class="my-animation" |
384
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
385
+ * | 3. the .ng-leave class is added to the element | class="my-animation ng-animate ng-leave" |
386
+ * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-leave" |
387
+ * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-leave" |
388
+ * | 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" |
389
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-leave ng-leave-active" |
390
+ * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
391
+ * | 9. The element is removed from the DOM | ... |
392
+ * | 10. The doneCallback() callback is fired (if provided) | ... |
393
+ *
394
+ * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
395
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
396
+ */
397
+ leave : function(element, doneCallback) {
398
+ cancelChildAnimations(element);
399
+ this.enabled(false, element);
400
+ $rootScope.$$postDigest(function() {
401
+ performAnimation('leave', 'ng-leave', element, null, null, function() {
402
+ $delegate.leave(element);
403
+ }, doneCallback);
404
+ });
405
+ },
406
+
407
+ /**
408
+ * @ngdoc function
409
+ * @name ngAnimate.$animate#move
410
+ * @methodOf ngAnimate.$animate
411
+ * @function
412
+ *
413
+ * @description
414
+ * Fires the move DOM operation. Just before the animation starts, the animate service will either append it into the parentElement container or
415
+ * add the element directly after the afterElement element if present. Then the move animation will be run. Once
416
+ * the animation is started, the following CSS classes will be added for the duration of the animation:
417
+ *
418
+ * Below is a breakdown of each step that occurs during move animation:
419
+ *
420
+ * | Animation Step | What the element class attribute looks like |
421
+ * |----------------------------------------------------------------------------------------------|---------------------------------------------|
422
+ * | 1. $animate.move(...) is called | class="my-animation" |
423
+ * | 2. element is moved into the parentElement element or beside the afterElement element | class="my-animation" |
424
+ * | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
425
+ * | 4. the .ng-move class is added to the element | class="my-animation ng-animate ng-move" |
426
+ * | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-move" |
427
+ * | 6. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate ng-move" |
428
+ * | 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" |
429
+ * | 8. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active ng-move ng-move-active" |
430
+ * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
431
+ * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" |
432
+ *
433
+ * @param {jQuery/jqLite element} element the element that will be the focus of the move animation
434
+ * @param {jQuery/jqLite element} parentElement the parentElement element of the element that will be the focus of the move animation
435
+ * @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
436
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
437
+ */
438
+ move : function(element, parentElement, afterElement, doneCallback) {
439
+ cancelChildAnimations(element);
440
+ this.enabled(false, element);
441
+ $delegate.move(element, parentElement, afterElement);
442
+ $rootScope.$$postDigest(function() {
443
+ performAnimation('move', 'ng-move', element, parentElement, afterElement, noop, doneCallback);
444
+ });
445
+ },
446
+
447
+ /**
448
+ * @ngdoc function
449
+ * @name ngAnimate.$animate#addClass
450
+ * @methodOf ngAnimate.$animate
451
+ *
452
+ * @description
453
+ * Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class.
454
+ * Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide
455
+ * the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if no CSS transitions
456
+ * or keyframes are defined on the -add or base CSS class).
457
+ *
458
+ * Below is a breakdown of each step that occurs during addClass animation:
459
+ *
460
+ * | Animation Step | What the element class attribute looks like |
461
+ * |------------------------------------------------------------------------------------------------|---------------------------------------------|
462
+ * | 1. $animate.addClass(element, 'super') is called | class="my-animation" |
463
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation ng-animate" |
464
+ * | 3. the .super-add class are added to the element | class="my-animation ng-animate super-add" |
465
+ * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super-add" |
466
+ * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate super-add" |
467
+ * | 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" |
468
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation super-add super-add-active" |
469
+ * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation super" |
470
+ * | 9. The super class is kept on the element | class="my-animation super" |
471
+ * | 10. The doneCallback() callback is fired (if provided) | class="my-animation super" |
472
+ *
473
+ * @param {jQuery/jqLite element} element the element that will be animated
474
+ * @param {string} className the CSS class that will be added to the element and then animated
475
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
476
+ */
477
+ addClass : function(element, className, doneCallback) {
478
+ performAnimation('addClass', className, element, null, null, function() {
479
+ $delegate.addClass(element, className);
480
+ }, doneCallback);
481
+ },
482
+
483
+ /**
484
+ * @ngdoc function
485
+ * @name ngAnimate.$animate#removeClass
486
+ * @methodOf ngAnimate.$animate
487
+ *
488
+ * @description
489
+ * Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value
490
+ * from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in
491
+ * order to provide the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if
492
+ * no CSS transitions or keyframes are defined on the -remove or base CSS classes).
493
+ *
494
+ * Below is a breakdown of each step that occurs during removeClass animation:
495
+ *
496
+ * | Animation Step | What the element class attribute looks like |
497
+ * |-----------------------------------------------------------------------------------------------|---------------------------------------------|
498
+ * | 1. $animate.removeClass(element, 'super') is called | class="my-animation super" |
499
+ * | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation super ng-animate" |
500
+ * | 3. the .super-remove class are added to the element | class="my-animation super ng-animate super-remove"|
501
+ * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation super ng-animate super-remove" |
502
+ * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation super ng-animate super-remove" |
503
+ * | 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" |
504
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-animate ng-animate-active super-remove super-remove-active" |
505
+ * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
506
+ * | 9. The doneCallback() callback is fired (if provided) | class="my-animation" |
507
+ *
508
+ *
509
+ * @param {jQuery/jqLite element} element the element that will be animated
510
+ * @param {string} className the CSS class that will be animated and then removed from the element
511
+ * @param {function()=} doneCallback the callback function that will be called once the animation is complete
512
+ */
513
+ removeClass : function(element, className, doneCallback) {
514
+ performAnimation('removeClass', className, element, null, null, function() {
515
+ $delegate.removeClass(element, className);
516
+ }, doneCallback);
517
+ },
518
+
519
+ /**
520
+ * @ngdoc function
521
+ * @name ngAnimate.$animate#enabled
522
+ * @methodOf ngAnimate.$animate
523
+ * @function
524
+ *
525
+ * @param {boolean=} value If provided then set the animation on or off.
526
+ * @param {jQuery/jqLite element=} element If provided then the element will be used to represent the enable/disable operation
527
+ * @return {boolean} Current animation state.
528
+ *
529
+ * @description
530
+ * Globally enables/disables animations.
531
+ *
532
+ */
533
+ enabled : function(value, element) {
534
+ switch(arguments.length) {
535
+ case 2:
536
+ if(value) {
537
+ cleanup(element);
538
+ } else {
539
+ var data = element.data(NG_ANIMATE_STATE) || {};
540
+ data.disabled = true;
541
+ element.data(NG_ANIMATE_STATE, data);
542
+ }
543
+ break;
544
+
545
+ case 1:
546
+ rootAnimateState.disabled = !value;
547
+ break;
548
+
549
+ default:
550
+ value = !rootAnimateState.disabled;
551
+ break;
552
+ }
553
+ return !!value;
554
+ }
555
+ };
556
+
557
+ /*
558
+ all animations call this shared animation triggering function internally.
559
+ The animationEvent variable refers to the JavaScript animation event that will be triggered
560
+ and the className value is the name of the animation that will be applied within the
561
+ CSS code. Element, parentElement and afterElement are provided DOM elements for the animation
562
+ and the onComplete callback will be fired once the animation is fully complete.
563
+ */
564
+ function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
565
+ var currentClassName = element.attr('class') || '';
566
+ var classes = currentClassName + ' ' + className;
567
+ var animationLookup = (' ' + classes).replace(/\s+/g,'.');
568
+ if (!parentElement) {
569
+ parentElement = afterElement ? afterElement.parent() : element.parent();
570
+ }
571
+
572
+ var matches = lookup(animationLookup);
573
+ var isClassBased = animationEvent == 'addClass' || animationEvent == 'removeClass';
574
+ var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
575
+
576
+ //skip the animation if animations are disabled, a parent is already being animated,
577
+ //the element is not currently attached to the document body or then completely close
578
+ //the animation if any matching animations are not found at all.
579
+ //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found.
580
+ if (animationsDisabled(element, parentElement) || matches.length === 0) {
581
+ fireDOMOperation();
582
+ closeAnimation();
583
+ return;
584
+ }
585
+
586
+ var animations = [];
587
+ //only add animations if the currently running animation is not structural
588
+ //or if there is no animation running at all
589
+ if(!ngAnimateState.running || !(isClassBased && ngAnimateState.structural)) {
590
+ forEach(matches, function(animation) {
591
+ //add the animation to the queue to if it is allowed to be cancelled
592
+ if(!animation.allowCancel || animation.allowCancel(element, animationEvent, className)) {
593
+ var beforeFn, afterFn = animation[animationEvent];
594
+
595
+ //Special case for a leave animation since there is no point in performing an
596
+ //animation on a element node that has already been removed from the DOM
597
+ if(animationEvent == 'leave') {
598
+ beforeFn = afterFn;
599
+ afterFn = null; //this must be falsy so that the animation is skipped for leave
600
+ } else {
601
+ beforeFn = animation['before' + animationEvent.charAt(0).toUpperCase() + animationEvent.substr(1)];
602
+ }
603
+ animations.push({
604
+ before : beforeFn,
605
+ after : afterFn
606
+ });
607
+ }
608
+ });
609
+ }
610
+
611
+ //this would mean that an animation was not allowed so let the existing
612
+ //animation do it's thing and close this one early
613
+ if(animations.length === 0) {
614
+ fireDOMOperation();
615
+ fireDoneCallbackAsync();
616
+ return;
617
+ }
618
+
619
+ //this value will be searched for class-based CSS className lookup. Therefore,
620
+ //we prefix and suffix the current className value with spaces to avoid substring
621
+ //lookups of className tokens
622
+ var futureClassName = ' ' + currentClassName + ' ';
623
+ if(ngAnimateState.running) {
624
+ //if an animation is currently running on the element then lets take the steps
625
+ //to cancel that animation and fire any required callbacks
626
+ $timeout.cancel(ngAnimateState.closeAnimationTimeout);
627
+ cleanup(element);
628
+ cancelAnimations(ngAnimateState.animations);
629
+
630
+ //if the class is removed during the reflow then it will revert the styles temporarily
631
+ //back to the base class CSS styling causing a jump-like effect to occur. This check
632
+ //here ensures that the domOperation is only performed after the reflow has commenced
633
+ if(ngAnimateState.beforeComplete) {
634
+ (ngAnimateState.done || noop)(true);
635
+ } else if(isClassBased && !ngAnimateState.structural) {
636
+ //class-based animations will compare element className values after cancelling the
637
+ //previous animation to see if the element properties already contain the final CSS
638
+ //class and if so then the animation will be skipped. Since the domOperation will
639
+ //be performed only after the reflow is complete then our element's className value
640
+ //will be invalid. Therefore the same string manipulation that would occur within the
641
+ //DOM operation will be performed below so that the class comparison is valid...
642
+ futureClassName = ngAnimateState.event == 'removeClass' ?
643
+ futureClassName.replace(ngAnimateState.className, '') :
644
+ futureClassName + ngAnimateState.className + ' ';
645
+ }
646
+ }
647
+
648
+ //There is no point in perform a class-based animation if the element already contains
649
+ //(on addClass) or doesn't contain (on removeClass) the className being animated.
650
+ //The reason why this is being called after the previous animations are cancelled
651
+ //is so that the CSS classes present on the element can be properly examined.
652
+ var classNameToken = ' ' + className + ' ';
653
+ if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) ||
654
+ (animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {
655
+ fireDOMOperation();
656
+ fireDoneCallbackAsync();
657
+ return;
658
+ }
659
+
660
+ //the ng-animate class does nothing, but it's here to allow for
661
+ //parent animations to find and cancel child animations when needed
662
+ element.addClass(NG_ANIMATE_CLASS_NAME);
663
+
664
+ element.data(NG_ANIMATE_STATE, {
665
+ running:true,
666
+ event:animationEvent,
667
+ className:className,
668
+ structural:!isClassBased,
669
+ animations:animations,
670
+ done:onBeforeAnimationsComplete
671
+ });
672
+
673
+ //first we run the before animations and when all of those are complete
674
+ //then we perform the DOM operation and run the next set of animations
675
+ invokeRegisteredAnimationFns(animations, 'before', onBeforeAnimationsComplete);
676
+
677
+ function onBeforeAnimationsComplete(cancelled) {
678
+ fireDOMOperation();
679
+ if(cancelled === true) {
680
+ closeAnimation();
681
+ return;
682
+ }
683
+
684
+ //set the done function to the final done function
685
+ //so that the DOM event won't be executed twice by accident
686
+ //if the after animation is cancelled as well
687
+ var data = element.data(NG_ANIMATE_STATE);
688
+ if(data) {
689
+ data.done = closeAnimation;
690
+ element.data(NG_ANIMATE_STATE, data);
691
+ }
692
+ invokeRegisteredAnimationFns(animations, 'after', closeAnimation);
693
+ }
694
+
695
+ function invokeRegisteredAnimationFns(animations, phase, allAnimationFnsComplete) {
696
+ var endFnName = phase + 'End';
697
+ forEach(animations, function(animation, index) {
698
+ var animationPhaseCompleted = function() {
699
+ progress(index, phase);
700
+ };
701
+
702
+ //there are no before functions for enter + move since the DOM
703
+ //operations happen before the performAnimation method fires
704
+ if(phase == 'before' && (animationEvent == 'enter' || animationEvent == 'move')) {
705
+ animationPhaseCompleted();
706
+ return;
707
+ }
708
+
709
+ if(animation[phase]) {
710
+ animation[endFnName] = isClassBased ?
711
+ animation[phase](element, className, animationPhaseCompleted) :
712
+ animation[phase](element, animationPhaseCompleted);
713
+ } else {
714
+ animationPhaseCompleted();
715
+ }
716
+ });
717
+
718
+ function progress(index, phase) {
719
+ var phaseCompletionFlag = phase + 'Complete';
720
+ var currentAnimation = animations[index];
721
+ currentAnimation[phaseCompletionFlag] = true;
722
+ (currentAnimation[endFnName] || noop)();
723
+
724
+ for(var i=0;i<animations.length;i++) {
725
+ if(!animations[i][phaseCompletionFlag]) return;
726
+ }
727
+
728
+ allAnimationFnsComplete();
729
+ }
730
+ }
731
+
732
+ function fireDoneCallbackAsync() {
733
+ doneCallback && $timeout(doneCallback, 0, false);
734
+ }
735
+
736
+ //it is less complicated to use a flag than managing and cancelling
737
+ //timeouts containing multiple callbacks.
738
+ function fireDOMOperation() {
739
+ if(!fireDOMOperation.hasBeenRun) {
740
+ fireDOMOperation.hasBeenRun = true;
741
+ domOperation();
742
+ }
743
+ }
744
+
745
+ function closeAnimation() {
746
+ if(!closeAnimation.hasBeenRun) {
747
+ closeAnimation.hasBeenRun = true;
748
+ var data = element.data(NG_ANIMATE_STATE);
749
+ if(data) {
750
+ /* only structural animations wait for reflow before removing an
751
+ animation, but class-based animations don't. An example of this
752
+ failing would be when a parent HTML tag has a ng-class attribute
753
+ causing ALL directives below to skip animations during the digest */
754
+ if(isClassBased) {
755
+ cleanup(element);
756
+ } else {
757
+ data.closeAnimationTimeout = $timeout(function() {
758
+ cleanup(element);
759
+ }, 0, false);
760
+ element.data(NG_ANIMATE_STATE, data);
761
+ }
762
+ }
763
+ fireDoneCallbackAsync();
764
+ }
765
+ }
766
+ }
767
+
768
+ function cancelChildAnimations(element) {
769
+ var node = element[0];
770
+ if(node.nodeType != ELEMENT_NODE) {
771
+ return;
772
+ }
773
+
774
+ forEach(node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME), function(element) {
775
+ element = angular.element(element);
776
+ var data = element.data(NG_ANIMATE_STATE);
777
+ if(data) {
778
+ cancelAnimations(data.animations);
779
+ cleanup(element);
780
+ }
781
+ });
782
+ }
783
+
784
+ function cancelAnimations(animations) {
785
+ var isCancelledFlag = true;
786
+ forEach(animations, function(animation) {
787
+ if(!animations.beforeComplete) {
788
+ (animation.beforeEnd || noop)(isCancelledFlag);
789
+ }
790
+ if(!animations.afterComplete) {
791
+ (animation.afterEnd || noop)(isCancelledFlag);
792
+ }
793
+ });
794
+ }
795
+
796
+ function cleanup(element) {
797
+ if(element[0] == $rootElement[0]) {
798
+ if(!rootAnimateState.disabled) {
799
+ rootAnimateState.running = false;
800
+ rootAnimateState.structural = false;
801
+ }
802
+ } else {
803
+ element.removeClass(NG_ANIMATE_CLASS_NAME);
804
+ element.removeData(NG_ANIMATE_STATE);
805
+ }
806
+ }
807
+
808
+ function animationsDisabled(element, parentElement) {
809
+ if (rootAnimateState.disabled) return true;
810
+
811
+ if(element[0] == $rootElement[0]) {
812
+ return rootAnimateState.disabled || rootAnimateState.running;
813
+ }
814
+
815
+ do {
816
+ //the element did not reach the root element which means that it
817
+ //is not apart of the DOM. Therefore there is no reason to do
818
+ //any animations on it
819
+ if(parentElement.length === 0) break;
820
+
821
+ var isRoot = parentElement[0] == $rootElement[0];
822
+ var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE);
823
+ var result = state && (!!state.disabled || !!state.running);
824
+ if(isRoot || result) {
825
+ return result;
826
+ }
827
+
828
+ if(isRoot) return true;
829
+ }
830
+ while(parentElement = parentElement.parent());
831
+
832
+ return true;
833
+ }
834
+ }]);
835
+
836
+ $animateProvider.register('', ['$window', '$sniffer', '$timeout', function($window, $sniffer, $timeout) {
837
+ // Detect proper transitionend/animationend event names.
838
+ var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
839
+
840
+ // If unprefixed events are not supported but webkit-prefixed are, use the latter.
841
+ // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
842
+ // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
843
+ // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
844
+ // Register both events in case `window.onanimationend` is not supported because of that,
845
+ // do the same for `transitionend` as Safari is likely to exhibit similar behavior.
846
+ // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
847
+ // therefore there is no reason to test anymore for other vendor prefixes: http://caniuse.com/#search=transition
848
+ if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) {
849
+ CSS_PREFIX = '-webkit-';
850
+ TRANSITION_PROP = 'WebkitTransition';
851
+ TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
852
+ } else {
853
+ TRANSITION_PROP = 'transition';
854
+ TRANSITIONEND_EVENT = 'transitionend';
855
+ }
856
+
857
+ if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) {
858
+ CSS_PREFIX = '-webkit-';
859
+ ANIMATION_PROP = 'WebkitAnimation';
860
+ ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
861
+ } else {
862
+ ANIMATION_PROP = 'animation';
863
+ ANIMATIONEND_EVENT = 'animationend';
864
+ }
865
+
866
+ var DURATION_KEY = 'Duration';
867
+ var PROPERTY_KEY = 'Property';
868
+ var DELAY_KEY = 'Delay';
869
+ var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
870
+ var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey';
871
+ var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data';
872
+ var NG_ANIMATE_FALLBACK_CLASS_NAME = 'ng-animate-start';
873
+ var NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME = 'ng-animate-active';
874
+
875
+ var lookupCache = {};
876
+ var parentCounter = 0;
877
+
878
+ var animationReflowQueue = [], animationTimer, timeOut = false;
879
+ function afterReflow(callback) {
880
+ animationReflowQueue.push(callback);
881
+ $timeout.cancel(animationTimer);
882
+ animationTimer = $timeout(function() {
883
+ forEach(animationReflowQueue, function(fn) {
884
+ fn();
885
+ });
886
+ animationReflowQueue = [];
887
+ animationTimer = null;
888
+ lookupCache = {};
889
+ }, 10, false);
890
+ }
891
+
892
+ function getElementAnimationDetails(element, cacheKey) {
893
+ var data = cacheKey ? lookupCache[cacheKey] : null;
894
+ if(!data) {
895
+ var transitionDuration = 0;
896
+ var transitionDelay = 0;
897
+ var animationDuration = 0;
898
+ var animationDelay = 0;
899
+ var transitionDelayStyle;
900
+ var animationDelayStyle;
901
+ var transitionDurationStyle;
902
+ var transitionPropertyStyle;
903
+
904
+ //we want all the styles defined before and after
905
+ forEach(element, function(element) {
906
+ if (element.nodeType == ELEMENT_NODE) {
907
+ var elementStyles = $window.getComputedStyle(element) || {};
908
+
909
+ transitionDurationStyle = elementStyles[TRANSITION_PROP + DURATION_KEY];
910
+
911
+ transitionDuration = Math.max(parseMaxTime(transitionDurationStyle), transitionDuration);
912
+
913
+ transitionPropertyStyle = elementStyles[TRANSITION_PROP + PROPERTY_KEY];
914
+
915
+ transitionDelayStyle = elementStyles[TRANSITION_PROP + DELAY_KEY];
916
+
917
+ transitionDelay = Math.max(parseMaxTime(transitionDelayStyle), transitionDelay);
918
+
919
+ animationDelayStyle = elementStyles[ANIMATION_PROP + DELAY_KEY];
920
+
921
+ animationDelay = Math.max(parseMaxTime(animationDelayStyle), animationDelay);
922
+
923
+ var aDuration = parseMaxTime(elementStyles[ANIMATION_PROP + DURATION_KEY]);
924
+
925
+ if(aDuration > 0) {
926
+ aDuration *= parseInt(elementStyles[ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY], 10) || 1;
927
+ }
928
+
929
+ animationDuration = Math.max(aDuration, animationDuration);
930
+ }
931
+ });
932
+ data = {
933
+ total : 0,
934
+ transitionPropertyStyle: transitionPropertyStyle,
935
+ transitionDurationStyle: transitionDurationStyle,
936
+ transitionDelayStyle: transitionDelayStyle,
937
+ transitionDelay: transitionDelay,
938
+ transitionDuration: transitionDuration,
939
+ animationDelayStyle: animationDelayStyle,
940
+ animationDelay: animationDelay,
941
+ animationDuration: animationDuration
942
+ };
943
+ if(cacheKey) {
944
+ lookupCache[cacheKey] = data;
945
+ }
946
+ }
947
+ return data;
948
+ }
949
+
950
+ function parseMaxTime(str) {
951
+ var maxValue = 0;
952
+ var values = angular.isString(str) ?
953
+ str.split(/\s*,\s*/) :
954
+ [];
955
+ forEach(values, function(value) {
956
+ maxValue = Math.max(parseFloat(value) || 0, maxValue);
957
+ });
958
+ return maxValue;
959
+ }
960
+
961
+ function getCacheKey(element) {
962
+ var parentElement = element.parent();
963
+ var parentID = parentElement.data(NG_ANIMATE_PARENT_KEY);
964
+ if(!parentID) {
965
+ parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter);
966
+ parentID = parentCounter;
967
+ }
968
+ return parentID + '-' + element[0].className;
969
+ }
970
+
971
+ function animateSetup(element, className) {
972
+ var cacheKey = getCacheKey(element);
973
+ var eventCacheKey = cacheKey + ' ' + className;
974
+ var stagger = {};
975
+ var ii = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
976
+
977
+ if(ii > 0) {
978
+ var staggerClassName = className + '-stagger';
979
+ var staggerCacheKey = cacheKey + ' ' + staggerClassName;
980
+ var applyClasses = !lookupCache[staggerCacheKey];
981
+
982
+ applyClasses && element.addClass(staggerClassName);
983
+
984
+ stagger = getElementAnimationDetails(element, staggerCacheKey);
985
+
986
+ applyClasses && element.removeClass(staggerClassName);
987
+ }
988
+
989
+ element.addClass(className);
990
+
991
+ var timings = getElementAnimationDetails(element, eventCacheKey);
992
+
993
+ /* there is no point in performing a reflow if the animation
994
+ timeout is empty (this would cause a flicker bug normally
995
+ in the page. There is also no point in performing an animation
996
+ that only has a delay and no duration */
997
+ var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
998
+ if(maxDuration === 0) {
999
+ element.removeClass(className);
1000
+ return false;
1001
+ }
1002
+
1003
+ var node = element[0];
1004
+ //temporarily disable the transition so that the enter styles
1005
+ //don't animate twice (this is here to avoid a bug in Chrome/FF).
1006
+ var activeClassName = '';
1007
+ if(timings.transitionDuration > 0) {
1008
+ element.addClass(NG_ANIMATE_FALLBACK_CLASS_NAME);
1009
+ activeClassName += NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME + ' ';
1010
+ blockTransitions(element);
1011
+ } else {
1012
+ blockKeyframeAnimations(element);
1013
+ }
1014
+
1015
+ forEach(className.split(' '), function(klass, i) {
1016
+ activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
1017
+ });
1018
+
1019
+ element.data(NG_ANIMATE_CSS_DATA_KEY, {
1020
+ className : className,
1021
+ activeClassName : activeClassName,
1022
+ maxDuration : maxDuration,
1023
+ classes : className + ' ' + activeClassName,
1024
+ timings : timings,
1025
+ stagger : stagger,
1026
+ ii : ii
1027
+ });
1028
+
1029
+ return true;
1030
+ }
1031
+
1032
+ function blockTransitions(element) {
1033
+ element[0].style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
1034
+ }
1035
+
1036
+ function blockKeyframeAnimations(element) {
1037
+ element[0].style[ANIMATION_PROP] = 'none 0s';
1038
+ }
1039
+
1040
+ function unblockTransitions(element) {
1041
+ var node = element[0], prop = TRANSITION_PROP + PROPERTY_KEY;
1042
+ if(node.style[prop] && node.style[prop].length > 0) {
1043
+ node.style[prop] = '';
1044
+ }
1045
+ }
1046
+
1047
+ function unblockKeyframeAnimations(element) {
1048
+ var node = element[0], prop = ANIMATION_PROP;
1049
+ if(node.style[prop] && node.style[prop].length > 0) {
1050
+ element[0].style[prop] = '';
1051
+ }
1052
+ }
1053
+
1054
+ function animateRun(element, className, activeAnimationComplete) {
1055
+ var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
1056
+ if(!element.hasClass(className) || !data) {
1057
+ activeAnimationComplete();
1058
+ return;
1059
+ }
1060
+
1061
+ var node = element[0];
1062
+ var timings = data.timings;
1063
+ var stagger = data.stagger;
1064
+ var maxDuration = data.maxDuration;
1065
+ var activeClassName = data.activeClassName;
1066
+ var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * 1000;
1067
+ var startTime = Date.now();
1068
+ var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
1069
+ var ii = data.ii;
1070
+
1071
+ var applyFallbackStyle, style = '', appliedStyles = [];
1072
+ if(timings.transitionDuration > 0) {
1073
+ var propertyStyle = timings.transitionPropertyStyle;
1074
+ if(propertyStyle.indexOf('all') == -1) {
1075
+ applyFallbackStyle = true;
1076
+ var fallbackProperty = $sniffer.msie ? '-ms-zoom' : 'border-spacing';
1077
+ style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ', ' + fallbackProperty + '; ';
1078
+ style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ', ' + timings.transitionDuration + 's; ';
1079
+ appliedStyles.push(CSS_PREFIX + 'transition-property');
1080
+ appliedStyles.push(CSS_PREFIX + 'transition-duration');
1081
+ }
1082
+ }
1083
+
1084
+ if(ii > 0) {
1085
+ if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
1086
+ var delayStyle = timings.transitionDelayStyle;
1087
+ if(applyFallbackStyle) {
1088
+ delayStyle += ', ' + timings.transitionDelay + 's';
1089
+ }
1090
+
1091
+ style += CSS_PREFIX + 'transition-delay: ' +
1092
+ prepareStaggerDelay(delayStyle, stagger.transitionDelay, ii) + '; ';
1093
+ appliedStyles.push(CSS_PREFIX + 'transition-delay');
1094
+ }
1095
+
1096
+ if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
1097
+ style += CSS_PREFIX + 'animation-delay: ' +
1098
+ prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, ii) + '; ';
1099
+ appliedStyles.push(CSS_PREFIX + 'animation-delay');
1100
+ }
1101
+ }
1102
+
1103
+ if(appliedStyles.length > 0) {
1104
+ var oldStyle = node.getAttribute('style') || '';
1105
+ node.setAttribute('style', oldStyle + ' ' + style);
1106
+ }
1107
+
1108
+ element.on(css3AnimationEvents, onAnimationProgress);
1109
+ element.addClass(activeClassName);
1110
+
1111
+ // This will automatically be called by $animate so
1112
+ // there is no need to attach this internally to the
1113
+ // timeout done method.
1114
+ return function onEnd(cancelled) {
1115
+ element.off(css3AnimationEvents, onAnimationProgress);
1116
+ element.removeClass(activeClassName);
1117
+ animateClose(element, className);
1118
+ for (var i in appliedStyles) {
1119
+ node.style.removeProperty(appliedStyles[i]);
1120
+ }
1121
+ };
1122
+
1123
+ function onAnimationProgress(event) {
1124
+ event.stopPropagation();
1125
+ var ev = event.originalEvent || event;
1126
+ var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
1127
+ /* $manualTimeStamp is a mocked timeStamp value which is set
1128
+ * within browserTrigger(). This is only here so that tests can
1129
+ * mock animations properly. Real events fallback to event.timeStamp,
1130
+ * or, if they don't, then a timeStamp is automatically created for them.
1131
+ * We're checking to see if the timeStamp surpasses the expected delay,
1132
+ * but we're using elapsedTime instead of the timeStamp on the 2nd
1133
+ * pre-condition since animations sometimes close off early */
1134
+ if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && ev.elapsedTime >= maxDuration) {
1135
+ activeAnimationComplete();
1136
+ }
1137
+ }
1138
+ }
1139
+
1140
+ function prepareStaggerDelay(delayStyle, staggerDelay, index) {
1141
+ var style = '';
1142
+ forEach(delayStyle.split(','), function(val, i) {
1143
+ style += (i > 0 ? ',' : '') +
1144
+ (index * staggerDelay + parseInt(val, 10)) + 's';
1145
+ });
1146
+ return style;
1147
+ }
1148
+
1149
+ function animateBefore(element, className) {
1150
+ if(animateSetup(element, className)) {
1151
+ return function(cancelled) {
1152
+ cancelled && animateClose(element, className);
1153
+ };
1154
+ }
1155
+ }
1156
+
1157
+ function animateAfter(element, className, afterAnimationComplete) {
1158
+ if(element.data(NG_ANIMATE_CSS_DATA_KEY)) {
1159
+ return animateRun(element, className, afterAnimationComplete);
1160
+ } else {
1161
+ animateClose(element, className);
1162
+ afterAnimationComplete();
1163
+ }
1164
+ }
1165
+
1166
+ function animate(element, className, animationComplete) {
1167
+ //If the animateSetup function doesn't bother returning a
1168
+ //cancellation function then it means that there is no animation
1169
+ //to perform at all
1170
+ var preReflowCancellation = animateBefore(element, className);
1171
+ if(!preReflowCancellation) {
1172
+ animationComplete();
1173
+ return;
1174
+ }
1175
+
1176
+ //There are two cancellation functions: one is before the first
1177
+ //reflow animation and the second is during the active state
1178
+ //animation. The first function will take care of removing the
1179
+ //data from the element which will not make the 2nd animation
1180
+ //happen in the first place
1181
+ var cancel = preReflowCancellation;
1182
+ afterReflow(function() {
1183
+ unblockTransitions(element);
1184
+ unblockKeyframeAnimations(element);
1185
+ //once the reflow is complete then we point cancel to
1186
+ //the new cancellation function which will remove all of the
1187
+ //animation properties from the active animation
1188
+ cancel = animateAfter(element, className, animationComplete);
1189
+ });
1190
+
1191
+ return function(cancelled) {
1192
+ (cancel || noop)(cancelled);
1193
+ };
1194
+ }
1195
+
1196
+ function animateClose(element, className) {
1197
+ element.removeClass(className);
1198
+ element.removeClass(NG_ANIMATE_FALLBACK_CLASS_NAME);
1199
+ element.removeData(NG_ANIMATE_CSS_DATA_KEY);
1200
+ }
1201
+
1202
+ return {
1203
+ allowCancel : function(element, animationEvent, className) {
1204
+ //always cancel the current animation if it is a
1205
+ //structural animation
1206
+ var oldClasses = (element.data(NG_ANIMATE_CSS_DATA_KEY) || {}).classes;
1207
+ if(!oldClasses || ['enter','leave','move'].indexOf(animationEvent) >= 0) {
1208
+ return true;
1209
+ }
1210
+
1211
+ var parentElement = element.parent();
1212
+ var clone = angular.element(element[0].cloneNode());
1213
+
1214
+ //make the element super hidden and override any CSS style values
1215
+ clone.attr('style','position:absolute; top:-9999px; left:-9999px');
1216
+ clone.removeAttr('id');
1217
+ clone.html('');
1218
+
1219
+ forEach(oldClasses.split(' '), function(klass) {
1220
+ clone.removeClass(klass);
1221
+ });
1222
+
1223
+ var suffix = animationEvent == 'addClass' ? '-add' : '-remove';
1224
+ clone.addClass(suffixClasses(className, suffix));
1225
+ parentElement.append(clone);
1226
+
1227
+ var timings = getElementAnimationDetails(clone);
1228
+ clone.remove();
1229
+
1230
+ return Math.max(timings.transitionDuration, timings.animationDuration) > 0;
1231
+ },
1232
+
1233
+ enter : function(element, animationCompleted) {
1234
+ return animate(element, 'ng-enter', animationCompleted);
1235
+ },
1236
+
1237
+ leave : function(element, animationCompleted) {
1238
+ return animate(element, 'ng-leave', animationCompleted);
1239
+ },
1240
+
1241
+ move : function(element, animationCompleted) {
1242
+ return animate(element, 'ng-move', animationCompleted);
1243
+ },
1244
+
1245
+ beforeAddClass : function(element, className, animationCompleted) {
1246
+ var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'));
1247
+ if(cancellationMethod) {
1248
+ afterReflow(function() {
1249
+ unblockTransitions(element);
1250
+ unblockKeyframeAnimations(element);
1251
+ animationCompleted();
1252
+ });
1253
+ return cancellationMethod;
1254
+ }
1255
+ animationCompleted();
1256
+ },
1257
+
1258
+ addClass : function(element, className, animationCompleted) {
1259
+ return animateAfter(element, suffixClasses(className, '-add'), animationCompleted);
1260
+ },
1261
+
1262
+ beforeRemoveClass : function(element, className, animationCompleted) {
1263
+ var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'));
1264
+ if(cancellationMethod) {
1265
+ afterReflow(function() {
1266
+ unblockTransitions(element);
1267
+ unblockKeyframeAnimations(element);
1268
+ animationCompleted();
1269
+ });
1270
+ return cancellationMethod;
1271
+ }
1272
+ animationCompleted();
1273
+ },
1274
+
1275
+ removeClass : function(element, className, animationCompleted) {
1276
+ return animateAfter(element, suffixClasses(className, '-remove'), animationCompleted);
1277
+ }
1278
+ };
1279
+
1280
+ function suffixClasses(classes, suffix) {
1281
+ var className = '';
1282
+ classes = angular.isArray(classes) ? classes : classes.split(/\s+/);
1283
+ forEach(classes, function(klass, i) {
1284
+ if(klass && klass.length > 0) {
1285
+ className += (i > 0 ? ' ' : '') + klass + suffix;
1286
+ }
1287
+ });
1288
+ return className;
1289
+ }
1290
+ }]);
1291
+ }]);
1292
+
1293
+
1294
+ })(window, window.angular);