angular-gem 1.2.6 → 1.2.7

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