angular-gem 1.2.4 → 1.2.5

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