angularjs-rails 1.2.13 → 1.2.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7cf133a5ca9c6e3c2a8cfa50a1d6120f352076eb
4
- data.tar.gz: 1a63dfc6b63423e8bb8287c02b5f8c696402bd81
3
+ metadata.gz: 4b6ecf5eea62ec6e6a1bf705921fe554bb40f748
4
+ data.tar.gz: 9d5fbe9e03c5671e105153b0a89102e109ef8465
5
5
  SHA512:
6
- metadata.gz: 2709f14653020e8703dd307a0dbaf76f9d678aa84c802595f3e41d1b1c56850a389a64c59c0b4b7cb32983a9a4cb594b9f4d5e5c7cf0a326815fc89e96f537d1
7
- data.tar.gz: b59ed59cc7f21285bb5639585465f2a0fc16e5048a4f531188947dad9969ab2672273413ba9851a9d02865f73207f3a85d1947c0f9471e2c43f45c36e094c8b5
6
+ metadata.gz: e4192934a9c7d3f16635428efd4ad0e8594cb1565fc8a75de53efa7699f2e1468c4b3fa07934772e8321522638cfbf7dd8fbf1124647e0976df5ccd2a7600fbe
7
+ data.tar.gz: e906b90edff483cd3576bb7313a0d45c4aeaad8e2ce75b4cd3c892a6c8956632197ba62470656550e5f4445e2ff77d97b1be3d0df30f5f478354b9fc8e3c4e79
@@ -1,6 +1,6 @@
1
1
  module AngularJS
2
2
  module Rails
3
- VERSION = "1.2.13"
3
+ VERSION = "1.2.14"
4
4
  UNSTABLE_VERSION = "1.1.5"
5
5
  end
6
6
  end
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.2.13
2
+ * @license AngularJS v1.2.14
3
3
  * (c) 2010-2014 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -8,7 +8,7 @@
8
8
  /* jshint maxlen: false */
9
9
 
10
10
  /**
11
- * @ngdoc overview
11
+ * @ngdoc module
12
12
  * @name ngAnimate
13
13
  * @description
14
14
  *
@@ -16,7 +16,6 @@
16
16
  *
17
17
  * The `ngAnimate` module provides support for JavaScript, CSS3 transition and CSS3 keyframe animation hooks within existing core and custom directives.
18
18
  *
19
- * {@installModule animate}
20
19
  *
21
20
  * <div doc-module-components="ngAnimate"></div>
22
21
  *
@@ -38,12 +37,14 @@
38
37
  * | {@link ng.directive:ngIf#usage_animations ngIf} | enter and leave |
39
38
  * | {@link ng.directive:ngClass#usage_animations ngClass} | add and remove |
40
39
  * | {@link ng.directive:ngShow#usage_animations ngShow & ngHide} | add and remove (the ng-hide class value) |
40
+ * | {@link ng.directive:form#usage_animations form} | add and remove (dirty, pristine, valid, invalid & all other validations) |
41
+ * | {@link ng.directive:ngModel#usage_animations ngModel} | add and remove (dirty, pristine, valid, invalid & all other validations) |
41
42
  *
42
43
  * You can find out more information about animations upon visiting each directive page.
43
44
  *
44
45
  * Below is an example of how to apply animations to a directive that supports animation hooks:
45
46
  *
46
- * <pre>
47
+ * ```html
47
48
  * <style type="text/css">
48
49
  * .slide.ng-enter, .slide.ng-leave {
49
50
  * -webkit-transition:0.5s linear all;
@@ -61,7 +62,7 @@
61
62
  * to trigger the CSS transition/animations
62
63
  * -->
63
64
  * <ANY class="slide" ng-include="..."></ANY>
64
- * </pre>
65
+ * ```
65
66
  *
66
67
  * Keep in mind that if an animation is running, any child elements cannot be animated until the parent element's
67
68
  * animation has completed.
@@ -73,7 +74,7 @@
73
74
  *
74
75
  * The following code below demonstrates how to perform animations using **CSS transitions** with Angular:
75
76
  *
76
- * <pre>
77
+ * ```html
77
78
  * <style type="text/css">
78
79
  * /&#42;
79
80
  * The animate class is apart of the element and the ng-enter class
@@ -101,11 +102,11 @@
101
102
  * <div class="view-container">
102
103
  * <div ng-view class="reveal-animation"></div>
103
104
  * </div>
104
- * </pre>
105
+ * ```
105
106
  *
106
107
  * The following code below demonstrates how to perform animations using **CSS animations** with Angular:
107
108
  *
108
- * <pre>
109
+ * ```html
109
110
  * <style type="text/css">
110
111
  * .reveal-animation.ng-enter {
111
112
  * -webkit-animation: enter_sequence 1s linear; /&#42; Safari/Chrome &#42;/
@@ -124,7 +125,7 @@
124
125
  * <div class="view-container">
125
126
  * <div ng-view class="reveal-animation"></div>
126
127
  * </div>
127
- * </pre>
128
+ * ```
128
129
  *
129
130
  * Both CSS3 animations and transitions can be used together and the animate service will figure out the correct duration and delay timing.
130
131
  *
@@ -142,7 +143,7 @@
142
143
  * the animation. The style property expected within the stagger class can either be a **transition-delay** or an
143
144
  * **animation-delay** property (or both if your animation contains both transitions and keyframe animations).
144
145
  *
145
- * <pre>
146
+ * ```css
146
147
  * .my-animation.ng-enter {
147
148
  * /&#42; standard transition code &#42;/
148
149
  * -webkit-transition: 1s linear all;
@@ -163,7 +164,7 @@
163
164
  * /&#42; standard transition styles &#42;/
164
165
  * opacity:1;
165
166
  * }
166
- * </pre>
167
+ * ```
167
168
  *
168
169
  * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations
169
170
  * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this
@@ -172,7 +173,7 @@
172
173
  *
173
174
  * The following code will issue the **ng-leave-stagger** event on the element provided:
174
175
  *
175
- * <pre>
176
+ * ```js
176
177
  * var kids = parent.children();
177
178
  *
178
179
  * $animate.leave(kids[0]); //stagger index=0
@@ -186,7 +187,7 @@
186
187
  * $animate.leave(kids[5]); //stagger index=0
187
188
  * $animate.leave(kids[6]); //stagger index=1
188
189
  * }, 100, false);
189
- * </pre>
190
+ * ```
190
191
  *
191
192
  * Stagger animations are currently only supported within CSS-defined animations.
192
193
  *
@@ -194,7 +195,7 @@
194
195
  * 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
196
  * yet support CSS transitions/animations, then you can make use of JavaScript animations defined inside of your AngularJS module.
196
197
  *
197
- * <pre>
198
+ * ```js
198
199
  * //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application.
199
200
  * var ngModule = angular.module('YourApp', ['ngAnimate']);
200
201
  * ngModule.animation('.my-crazy-animation', function() {
@@ -223,7 +224,7 @@
223
224
  * removeClass: function(element, className, done) { }
224
225
  * };
225
226
  * });
226
- * </pre>
227
+ * ```
227
228
  *
228
229
  * JavaScript-defined animations are created with a CSS-like class selector and a collection of events which are set to run
229
230
  * a javascript callback function. When an animation is triggered, $animate will look for a matching animation which fits
@@ -241,8 +242,8 @@
241
242
  angular.module('ngAnimate', ['ng'])
242
243
 
243
244
  /**
244
- * @ngdoc object
245
- * @name ngAnimate.$animateProvider
245
+ * @ngdoc provider
246
+ * @name $animateProvider
246
247
  * @description
247
248
  *
248
249
  * The `$animateProvider` allows developers to register JavaScript animation event handlers directly inside of a module.
@@ -254,42 +255,24 @@ angular.module('ngAnimate', ['ng'])
254
255
  * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
255
256
  *
256
257
  */
257
- .factory('$$animateReflow', ['$window', '$timeout', '$document',
258
- function($window, $timeout, $document) {
258
+
259
+ //this private service is only used within CSS-enabled animations
260
+ //IE8 + IE9 do not support rAF natively, but that is fine since they
261
+ //also don't support transitions and keyframes which means that the code
262
+ //below will never be used by the two browsers.
263
+ .factory('$$animateReflow', ['$$rAF', '$document', function($$rAF, $document) {
259
264
  var bod = $document[0].body;
260
- var requestAnimationFrame = $window.requestAnimationFrame ||
261
- $window.webkitRequestAnimationFrame ||
262
- function(fn) {
263
- return $timeout(fn, 10, false);
264
- };
265
-
266
- var cancelAnimationFrame = $window.cancelAnimationFrame ||
267
- $window.webkitCancelAnimationFrame ||
268
- function(timer) {
269
- return $timeout.cancel(timer);
270
- };
271
265
  return function(fn) {
272
- var id = requestAnimationFrame(function() {
266
+ //the returned function acts as the cancellation function
267
+ return $$rAF(function() {
268
+ //the line below will force the browser to perform a repaint
269
+ //so that all the animated elements within the animation frame
270
+ //will be properly updated and drawn on screen. This is
271
+ //required to perform multi-class CSS based animations with
272
+ //Firefox. DO NOT REMOVE THIS LINE.
273
273
  var a = bod.offsetWidth + 1;
274
274
  fn();
275
275
  });
276
- return function() {
277
- cancelAnimationFrame(id);
278
- };
279
- };
280
- }])
281
-
282
- .factory('$$asyncQueueBuffer', ['$timeout', function($timeout) {
283
- var timer, queue = [];
284
- return function(fn) {
285
- $timeout.cancel(timer);
286
- queue.push(fn);
287
- timer = $timeout(function() {
288
- for(var i = 0; i < queue.length; i++) {
289
- queue[i]();
290
- }
291
- queue = [];
292
- }, 0, false);
293
276
  };
294
277
  }])
295
278
 
@@ -320,8 +303,8 @@ angular.module('ngAnimate', ['ng'])
320
303
  return extractElementNode(elm1) == extractElementNode(elm2);
321
304
  }
322
305
 
323
- $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$$asyncQueueBuffer', '$rootScope', '$document',
324
- function($delegate, $injector, $sniffer, $rootElement, $$asyncQueueBuffer, $rootScope, $document) {
306
+ $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$$asyncCallback', '$rootScope', '$document',
307
+ function($delegate, $injector, $sniffer, $rootElement, $$asyncCallback, $rootScope, $document) {
325
308
 
326
309
  var globalAnimationCounter = 0;
327
310
  $rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
@@ -372,9 +355,151 @@ angular.module('ngAnimate', ['ng'])
372
355
  }
373
356
  }
374
357
 
358
+ function animationRunner(element, animationEvent, className) {
359
+ //transcluded directives may sometimes fire an animation using only comment nodes
360
+ //best to catch this early on to prevent any animation operations from occurring
361
+ var node = element[0];
362
+ if(!node) {
363
+ return;
364
+ }
365
+
366
+ var isSetClassOperation = animationEvent == 'setClass';
367
+ var isClassBased = isSetClassOperation ||
368
+ animationEvent == 'addClass' ||
369
+ animationEvent == 'removeClass';
370
+
371
+ var classNameAdd, classNameRemove;
372
+ if(angular.isArray(className)) {
373
+ classNameAdd = className[0];
374
+ classNameRemove = className[1];
375
+ className = classNameAdd + ' ' + classNameRemove;
376
+ }
377
+
378
+ var currentClassName = element.attr('class');
379
+ var classes = currentClassName + ' ' + className;
380
+ if(!isAnimatableClassName(classes)) {
381
+ return;
382
+ }
383
+
384
+ var beforeComplete = noop,
385
+ beforeCancel = [],
386
+ before = [],
387
+ afterComplete = noop,
388
+ afterCancel = [],
389
+ after = [];
390
+
391
+ var animationLookup = (' ' + classes).replace(/\s+/g,'.');
392
+ forEach(lookup(animationLookup), function(animationFactory) {
393
+ var created = registerAnimation(animationFactory, animationEvent);
394
+ if(!created && isSetClassOperation) {
395
+ registerAnimation(animationFactory, 'addClass');
396
+ registerAnimation(animationFactory, 'removeClass');
397
+ }
398
+ });
399
+
400
+ function registerAnimation(animationFactory, event) {
401
+ var afterFn = animationFactory[event];
402
+ var beforeFn = animationFactory['before' + event.charAt(0).toUpperCase() + event.substr(1)];
403
+ if(afterFn || beforeFn) {
404
+ if(event == 'leave') {
405
+ beforeFn = afterFn;
406
+ //when set as null then animation knows to skip this phase
407
+ afterFn = null;
408
+ }
409
+ after.push({
410
+ event : event, fn : afterFn
411
+ });
412
+ before.push({
413
+ event : event, fn : beforeFn
414
+ });
415
+ return true;
416
+ }
417
+ }
418
+
419
+ function run(fns, cancellations, allCompleteFn) {
420
+ var animations = [];
421
+ forEach(fns, function(animation) {
422
+ animation.fn && animations.push(animation);
423
+ });
424
+
425
+ var count = 0;
426
+ function afterAnimationComplete(index) {
427
+ if(cancellations) {
428
+ (cancellations[index] || noop)();
429
+ if(++count < animations.length) return;
430
+ cancellations = null;
431
+ }
432
+ allCompleteFn();
433
+ }
434
+
435
+ //The code below adds directly to the array in order to work with
436
+ //both sync and async animations. Sync animations are when the done()
437
+ //operation is called right away. DO NOT REFACTOR!
438
+ forEach(animations, function(animation, index) {
439
+ var progress = function() {
440
+ afterAnimationComplete(index);
441
+ };
442
+ switch(animation.event) {
443
+ case 'setClass':
444
+ cancellations.push(animation.fn(element, classNameAdd, classNameRemove, progress));
445
+ break;
446
+ case 'addClass':
447
+ cancellations.push(animation.fn(element, classNameAdd || className, progress));
448
+ break;
449
+ case 'removeClass':
450
+ cancellations.push(animation.fn(element, classNameRemove || className, progress));
451
+ break;
452
+ default:
453
+ cancellations.push(animation.fn(element, progress));
454
+ break;
455
+ }
456
+ });
457
+
458
+ if(cancellations && cancellations.length === 0) {
459
+ allCompleteFn();
460
+ }
461
+ }
462
+
463
+ return {
464
+ node : node,
465
+ event : animationEvent,
466
+ className : className,
467
+ isClassBased : isClassBased,
468
+ isSetClassOperation : isSetClassOperation,
469
+ before : function(allCompleteFn) {
470
+ beforeComplete = allCompleteFn;
471
+ run(before, beforeCancel, function() {
472
+ beforeComplete = noop;
473
+ allCompleteFn();
474
+ });
475
+ },
476
+ after : function(allCompleteFn) {
477
+ afterComplete = allCompleteFn;
478
+ run(after, afterCancel, function() {
479
+ afterComplete = noop;
480
+ allCompleteFn();
481
+ });
482
+ },
483
+ cancel : function() {
484
+ if(beforeCancel) {
485
+ forEach(beforeCancel, function(cancelFn) {
486
+ (cancelFn || noop)(true);
487
+ });
488
+ beforeComplete(true);
489
+ }
490
+ if(afterCancel) {
491
+ forEach(afterCancel, function(cancelFn) {
492
+ (cancelFn || noop)(true);
493
+ });
494
+ afterComplete(true);
495
+ }
496
+ }
497
+ };
498
+ }
499
+
375
500
  /**
376
- * @ngdoc object
377
- * @name ngAnimate.$animate
501
+ * @ngdoc service
502
+ * @name $animate
378
503
  * @function
379
504
  *
380
505
  * @description
@@ -393,9 +518,8 @@ angular.module('ngAnimate', ['ng'])
393
518
  */
394
519
  return {
395
520
  /**
396
- * @ngdoc function
397
- * @name ngAnimate.$animate#enter
398
- * @methodOf ngAnimate.$animate
521
+ * @ngdoc method
522
+ * @name $animate#enter
399
523
  * @function
400
524
  *
401
525
  * @description
@@ -417,9 +541,9 @@ angular.module('ngAnimate', ['ng'])
417
541
  * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
418
542
  * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" |
419
543
  *
420
- * @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
421
- * @param {jQuery/jqLite element} parentElement the parent element of the element that will be the focus of the enter animation
422
- * @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
544
+ * @param {DOMElement} element the element that will be the focus of the enter animation
545
+ * @param {DOMElement} parentElement the parent element of the element that will be the focus of the enter animation
546
+ * @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the enter animation
423
547
  * @param {function()=} doneCallback the callback function that will be called once the animation is complete
424
548
  */
425
549
  enter : function(element, parentElement, afterElement, doneCallback) {
@@ -432,9 +556,8 @@ angular.module('ngAnimate', ['ng'])
432
556
  },
433
557
 
434
558
  /**
435
- * @ngdoc function
436
- * @name ngAnimate.$animate#leave
437
- * @methodOf ngAnimate.$animate
559
+ * @ngdoc method
560
+ * @name $animate#leave
438
561
  * @function
439
562
  *
440
563
  * @description
@@ -456,24 +579,22 @@ angular.module('ngAnimate', ['ng'])
456
579
  * | 9. The element is removed from the DOM | ... |
457
580
  * | 10. The doneCallback() callback is fired (if provided) | ... |
458
581
  *
459
- * @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
582
+ * @param {DOMElement} element the element that will be the focus of the leave animation
460
583
  * @param {function()=} doneCallback the callback function that will be called once the animation is complete
461
584
  */
462
585
  leave : function(element, doneCallback) {
463
586
  cancelChildAnimations(element);
464
587
  this.enabled(false, element);
465
588
  $rootScope.$$postDigest(function() {
466
- element = stripCommentsFromElement(element);
467
- performAnimation('leave', 'ng-leave', element, null, null, function() {
589
+ performAnimation('leave', 'ng-leave', stripCommentsFromElement(element), null, null, function() {
468
590
  $delegate.leave(element);
469
591
  }, doneCallback);
470
592
  });
471
593
  },
472
594
 
473
595
  /**
474
- * @ngdoc function
475
- * @name ngAnimate.$animate#move
476
- * @methodOf ngAnimate.$animate
596
+ * @ngdoc method
597
+ * @name $animate#move
477
598
  * @function
478
599
  *
479
600
  * @description
@@ -496,9 +617,9 @@ angular.module('ngAnimate', ['ng'])
496
617
  * | 9. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
497
618
  * | 10. The doneCallback() callback is fired (if provided) | class="my-animation" |
498
619
  *
499
- * @param {jQuery/jqLite element} element the element that will be the focus of the move animation
500
- * @param {jQuery/jqLite element} parentElement the parentElement element of the element that will be the focus of the move animation
501
- * @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
620
+ * @param {DOMElement} element the element that will be the focus of the move animation
621
+ * @param {DOMElement} parentElement the parentElement element of the element that will be the focus of the move animation
622
+ * @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the move animation
502
623
  * @param {function()=} doneCallback the callback function that will be called once the animation is complete
503
624
  */
504
625
  move : function(element, parentElement, afterElement, doneCallback) {
@@ -512,9 +633,8 @@ angular.module('ngAnimate', ['ng'])
512
633
  },
513
634
 
514
635
  /**
515
- * @ngdoc function
516
- * @name ngAnimate.$animate#addClass
517
- * @methodOf ngAnimate.$animate
636
+ * @ngdoc method
637
+ * @name $animate#addClass
518
638
  *
519
639
  * @description
520
640
  * Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class.
@@ -532,12 +652,12 @@ angular.module('ngAnimate', ['ng'])
532
652
  * | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super-add" |
533
653
  * | 5. $animate waits for 10ms (this performs a reflow) | class="my-animation ng-animate super-add" |
534
654
  * | 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" |
535
- * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation super-add super-add-active" |
655
+ * | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation super super-add super-add-active" |
536
656
  * | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation super" |
537
657
  * | 9. The super class is kept on the element | class="my-animation super" |
538
658
  * | 10. The doneCallback() callback is fired (if provided) | class="my-animation super" |
539
659
  *
540
- * @param {jQuery/jqLite element} element the element that will be animated
660
+ * @param {DOMElement} element the element that will be animated
541
661
  * @param {string} className the CSS class that will be added to the element and then animated
542
662
  * @param {function()=} doneCallback the callback function that will be called once the animation is complete
543
663
  */
@@ -549,9 +669,8 @@ angular.module('ngAnimate', ['ng'])
549
669
  },
550
670
 
551
671
  /**
552
- * @ngdoc function
553
- * @name ngAnimate.$animate#removeClass
554
- * @methodOf ngAnimate.$animate
672
+ * @ngdoc method
673
+ * @name $animate#removeClass
555
674
  *
556
675
  * @description
557
676
  * Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value
@@ -574,7 +693,7 @@ angular.module('ngAnimate', ['ng'])
574
693
  * | 9. The doneCallback() callback is fired (if provided) | class="my-animation" |
575
694
  *
576
695
  *
577
- * @param {jQuery/jqLite element} element the element that will be animated
696
+ * @param {DOMElement} element the element that will be animated
578
697
  * @param {string} className the CSS class that will be animated and then removed from the element
579
698
  * @param {function()=} doneCallback the callback function that will be called once the animation is complete
580
699
  */
@@ -593,11 +712,11 @@ angular.module('ngAnimate', ['ng'])
593
712
  * @function
594
713
  * @description Adds and/or removes the given CSS classes to and from the element.
595
714
  * Once complete, the done() callback will be fired (if provided).
596
- * @param {jQuery/jqLite element} element the element which will it's CSS classes changed
715
+ * @param {DOMElement} element the element which will it's CSS classes changed
597
716
  * removed from it
598
717
  * @param {string} add the CSS classes which will be added to the element
599
718
  * @param {string} remove the CSS class which will be removed from the element
600
- * @param {function=} done the callback function (if provided) that will be fired after the
719
+ * @param {Function=} done the callback function (if provided) that will be fired after the
601
720
  * CSS classes have been set on the element
602
721
  */
603
722
  setClass : function(element, add, remove, doneCallback) {
@@ -608,13 +727,12 @@ angular.module('ngAnimate', ['ng'])
608
727
  },
609
728
 
610
729
  /**
611
- * @ngdoc function
612
- * @name ngAnimate.$animate#enabled
613
- * @methodOf ngAnimate.$animate
730
+ * @ngdoc method
731
+ * @name $animate#enabled
614
732
  * @function
615
733
  *
616
734
  * @param {boolean=} value If provided then set the animation on or off.
617
- * @param {jQuery/jqLite element=} element If provided then the element will be used to represent the enable/disable operation
735
+ * @param {DOMElement=} element If provided then the element will be used to represent the enable/disable operation
618
736
  * @return {boolean} Current animation state.
619
737
  *
620
738
  * @description
@@ -654,22 +772,8 @@ angular.module('ngAnimate', ['ng'])
654
772
  */
655
773
  function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
656
774
 
657
- var classNameAdd, classNameRemove, setClassOperation = animationEvent == 'setClass';
658
- if(setClassOperation) {
659
- classNameAdd = className[0];
660
- classNameRemove = className[1];
661
- className = classNameAdd + ' ' + classNameRemove;
662
- }
663
-
664
- var currentClassName, classes, node = element[0];
665
- if(node) {
666
- currentClassName = node.className;
667
- classes = currentClassName + ' ' + className;
668
- }
669
-
670
- //transcluded directives may sometimes fire an animation using only comment nodes
671
- //best to catch this early on to prevent any animation operations from occurring
672
- if(!node || !isAnimatableClassName(classes)) {
775
+ var runner = animationRunner(element, animationEvent, className);
776
+ if(!runner) {
673
777
  fireDOMOperation();
674
778
  fireBeforeCallbackAsync();
675
779
  fireAfterCallbackAsync();
@@ -677,29 +781,30 @@ angular.module('ngAnimate', ['ng'])
677
781
  return;
678
782
  }
679
783
 
680
- var elementEvents = angular.element._data(node);
784
+ className = runner.className;
785
+ var elementEvents = angular.element._data(runner.node);
681
786
  elementEvents = elementEvents && elementEvents.events;
682
787
 
683
- var animationLookup = (' ' + classes).replace(/\s+/g,'.');
684
788
  if (!parentElement) {
685
789
  parentElement = afterElement ? afterElement.parent() : element.parent();
686
790
  }
687
791
 
688
- var matches = lookup(animationLookup);
689
- var isClassBased = animationEvent == 'addClass' ||
690
- animationEvent == 'removeClass' ||
691
- setClassOperation;
692
792
  var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
693
-
694
793
  var runningAnimations = ngAnimateState.active || {};
695
794
  var totalActiveAnimations = ngAnimateState.totalActive || 0;
696
795
  var lastAnimation = ngAnimateState.last;
697
796
 
797
+ //only allow animations if the currently running animation is not structural
798
+ //or if there is no animation running at all
799
+ var skipAnimations = runner.isClassBased ?
800
+ ngAnimateState.disabled || (lastAnimation && !lastAnimation.isClassBased) :
801
+ false;
802
+
698
803
  //skip the animation if animations are disabled, a parent is already being animated,
699
804
  //the element is not currently attached to the document body or then completely close
700
805
  //the animation if any matching animations are not found at all.
701
- //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found.
702
- if (animationsDisabled(element, parentElement) || matches.length === 0) {
806
+ //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case an animation was found.
807
+ if (skipAnimations || animationsDisabled(element, parentElement)) {
703
808
  fireDOMOperation();
704
809
  fireBeforeCallbackAsync();
705
810
  fireAfterCallbackAsync();
@@ -707,50 +812,10 @@ angular.module('ngAnimate', ['ng'])
707
812
  return;
708
813
  }
709
814
 
710
- var animations = [];
711
-
712
- //only add animations if the currently running animation is not structural
713
- //or if there is no animation running at all
714
- var allowAnimations = isClassBased ?
715
- !ngAnimateState.disabled && (!lastAnimation || lastAnimation.classBased) :
716
- true;
717
-
718
- if(allowAnimations) {
719
- forEach(matches, function(animation) {
720
- //add the animation to the queue to if it is allowed to be cancelled
721
- if(!animation.allowCancel || animation.allowCancel(element, animationEvent, className)) {
722
- var beforeFn, afterFn = animation[animationEvent];
723
-
724
- //Special case for a leave animation since there is no point in performing an
725
- //animation on a element node that has already been removed from the DOM
726
- if(animationEvent == 'leave') {
727
- beforeFn = afterFn;
728
- afterFn = null; //this must be falsy so that the animation is skipped for leave
729
- } else {
730
- beforeFn = animation['before' + animationEvent.charAt(0).toUpperCase() + animationEvent.substr(1)];
731
- }
732
- animations.push({
733
- before : beforeFn,
734
- after : afterFn
735
- });
736
- }
737
- });
738
- }
739
-
740
- //this would mean that an animation was not allowed so let the existing
741
- //animation do it's thing and close this one early
742
- if(animations.length === 0) {
743
- fireDOMOperation();
744
- fireBeforeCallbackAsync();
745
- fireAfterCallbackAsync();
746
- fireDoneCallbackAsync();
747
- return;
748
- }
749
-
750
815
  var skipAnimation = false;
751
816
  if(totalActiveAnimations > 0) {
752
817
  var animationsToCancel = [];
753
- if(!isClassBased) {
818
+ if(!runner.isClassBased) {
754
819
  if(animationEvent == 'leave' && runningAnimations['ng-leave']) {
755
820
  skipAnimation = true;
756
821
  } else {
@@ -777,14 +842,13 @@ angular.module('ngAnimate', ['ng'])
777
842
  }
778
843
 
779
844
  if(animationsToCancel.length > 0) {
780
- angular.forEach(animationsToCancel, function(operation) {
781
- (operation.done || noop)(true);
782
- cancelAnimations(operation.animations);
845
+ forEach(animationsToCancel, function(operation) {
846
+ operation.cancel();
783
847
  });
784
848
  }
785
849
  }
786
850
 
787
- if(isClassBased && !setClassOperation && !skipAnimation) {
851
+ if(runner.isClassBased && !runner.isSetClassOperation && !skipAnimation) {
788
852
  skipAnimation = (animationEvent == 'addClass') == element.hasClass(className); //opposite of XOR
789
853
  }
790
854
 
@@ -795,23 +859,33 @@ angular.module('ngAnimate', ['ng'])
795
859
  return;
796
860
  }
797
861
 
862
+ if(animationEvent == 'leave') {
863
+ //there's no need to ever remove the listener since the element
864
+ //will be removed (destroyed) after the leave animation ends or
865
+ //is cancelled midway
866
+ element.one('$destroy', function(e) {
867
+ var element = angular.element(this);
868
+ var state = element.data(NG_ANIMATE_STATE);
869
+ if(state) {
870
+ var activeLeaveAnimation = state.active['ng-leave'];
871
+ if(activeLeaveAnimation) {
872
+ activeLeaveAnimation.cancel();
873
+ cleanup(element, 'ng-leave');
874
+ }
875
+ }
876
+ });
877
+ }
878
+
798
879
  //the ng-animate class does nothing, but it's here to allow for
799
880
  //parent animations to find and cancel child animations when needed
800
881
  element.addClass(NG_ANIMATE_CLASS_NAME);
801
882
 
802
883
  var localAnimationCount = globalAnimationCounter++;
803
- lastAnimation = {
804
- classBased : isClassBased,
805
- event : animationEvent,
806
- animations : animations,
807
- done:onBeforeAnimationsComplete
808
- };
809
-
810
884
  totalActiveAnimations++;
811
- runningAnimations[className] = lastAnimation;
885
+ runningAnimations[className] = runner;
812
886
 
813
887
  element.data(NG_ANIMATE_STATE, {
814
- last : lastAnimation,
888
+ last : runner,
815
889
  active : runningAnimations,
816
890
  index : localAnimationCount,
817
891
  totalActive : totalActiveAnimations
@@ -819,77 +893,26 @@ angular.module('ngAnimate', ['ng'])
819
893
 
820
894
  //first we run the before animations and when all of those are complete
821
895
  //then we perform the DOM operation and run the next set of animations
822
- invokeRegisteredAnimationFns(animations, 'before', onBeforeAnimationsComplete);
823
-
824
- function onBeforeAnimationsComplete(cancelled) {
896
+ fireBeforeCallbackAsync();
897
+ runner.before(function(cancelled) {
825
898
  var data = element.data(NG_ANIMATE_STATE);
826
899
  cancelled = cancelled ||
827
- !data || !data.active[className] ||
828
- (isClassBased && data.active[className].event != animationEvent);
900
+ !data || !data.active[className] ||
901
+ (runner.isClassBased && data.active[className].event != animationEvent);
829
902
 
830
903
  fireDOMOperation();
831
904
  if(cancelled === true) {
832
905
  closeAnimation();
833
- return;
834
- }
835
-
836
- //set the done function to the final done function
837
- //so that the DOM event won't be executed twice by accident
838
- //if the after animation is cancelled as well
839
- var currentAnimation = data.active[className];
840
- currentAnimation.done = closeAnimation;
841
- invokeRegisteredAnimationFns(animations, 'after', closeAnimation);
842
- }
843
-
844
- function invokeRegisteredAnimationFns(animations, phase, allAnimationFnsComplete) {
845
- phase == 'after' ?
846
- fireAfterCallbackAsync() :
847
- fireBeforeCallbackAsync();
848
-
849
- var endFnName = phase + 'End';
850
- forEach(animations, function(animation, index) {
851
- var animationPhaseCompleted = function() {
852
- progress(index, phase);
853
- };
854
-
855
- //there are no before functions for enter + move since the DOM
856
- //operations happen before the performAnimation method fires
857
- if(phase == 'before' && (animationEvent == 'enter' || animationEvent == 'move')) {
858
- animationPhaseCompleted();
859
- return;
860
- }
861
-
862
- if(animation[phase]) {
863
- if(setClassOperation) {
864
- animation[endFnName] = animation[phase](element, classNameAdd, classNameRemove, animationPhaseCompleted);
865
- } else {
866
- animation[endFnName] = isClassBased ?
867
- animation[phase](element, className, animationPhaseCompleted) :
868
- animation[phase](element, animationPhaseCompleted);
869
- }
870
- } else {
871
- animationPhaseCompleted();
872
- }
873
- });
874
-
875
- function progress(index, phase) {
876
- var phaseCompletionFlag = phase + 'Complete';
877
- var currentAnimation = animations[index];
878
- currentAnimation[phaseCompletionFlag] = true;
879
- (currentAnimation[endFnName] || noop)();
880
-
881
- for(var i=0;i<animations.length;i++) {
882
- if(!animations[i][phaseCompletionFlag]) return;
883
- }
884
-
885
- allAnimationFnsComplete();
906
+ } else {
907
+ fireAfterCallbackAsync();
908
+ runner.after(closeAnimation);
886
909
  }
887
- }
910
+ });
888
911
 
889
912
  function fireDOMCallback(animationPhase) {
890
913
  var eventName = '$animate:' + animationPhase;
891
914
  if(elementEvents && elementEvents[eventName] && elementEvents[eventName].length > 0) {
892
- $$asyncQueueBuffer(function() {
915
+ $$asyncCallback(function() {
893
916
  element.triggerHandler(eventName, {
894
917
  event : animationEvent,
895
918
  className : className
@@ -909,13 +932,13 @@ angular.module('ngAnimate', ['ng'])
909
932
  function fireDoneCallbackAsync() {
910
933
  fireDOMCallback('close');
911
934
  if(doneCallback) {
912
- $$asyncQueueBuffer(function() {
935
+ $$asyncCallback(function() {
913
936
  doneCallback();
914
937
  });
915
938
  }
916
939
  }
917
940
 
918
- //it is less complicated to use a flag than managing and cancelling
941
+ //it is less complicated to use a flag than managing and canceling
919
942
  //timeouts containing multiple callbacks.
920
943
  function fireDOMOperation() {
921
944
  if(!fireDOMOperation.hasBeenRun) {
@@ -933,10 +956,10 @@ angular.module('ngAnimate', ['ng'])
933
956
  animation, but class-based animations don't. An example of this
934
957
  failing would be when a parent HTML tag has a ng-class attribute
935
958
  causing ALL directives below to skip animations during the digest */
936
- if(isClassBased) {
959
+ if(runner.isClassBased) {
937
960
  cleanup(element, className);
938
961
  } else {
939
- $$asyncQueueBuffer(function() {
962
+ $$asyncCallback(function() {
940
963
  var data = element.data(NG_ANIMATE_STATE) || {};
941
964
  if(localAnimationCount == data.index) {
942
965
  cleanup(element, className, animationEvent);
@@ -952,28 +975,20 @@ angular.module('ngAnimate', ['ng'])
952
975
 
953
976
  function cancelChildAnimations(element) {
954
977
  var node = extractElementNode(element);
955
- forEach(node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME), function(element) {
956
- element = angular.element(element);
957
- var data = element.data(NG_ANIMATE_STATE);
958
- if(data && data.active) {
959
- angular.forEach(data.active, function(operation) {
960
- (operation.done || noop)(true);
961
- cancelAnimations(operation.animations);
962
- });
963
- }
964
- });
965
- }
966
-
967
- function cancelAnimations(animations) {
968
- var isCancelledFlag = true;
969
- forEach(animations, function(animation) {
970
- if(!animation.beforeComplete) {
971
- (animation.beforeEnd || noop)(isCancelledFlag);
972
- }
973
- if(!animation.afterComplete) {
974
- (animation.afterEnd || noop)(isCancelledFlag);
975
- }
976
- });
978
+ if (node) {
979
+ var nodes = angular.isFunction(node.getElementsByClassName) ?
980
+ node.getElementsByClassName(NG_ANIMATE_CLASS_NAME) :
981
+ node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME);
982
+ forEach(nodes, function(element) {
983
+ element = angular.element(element);
984
+ var data = element.data(NG_ANIMATE_STATE);
985
+ if(data && data.active) {
986
+ forEach(data.active, function(runner) {
987
+ runner.cancel();
988
+ });
989
+ }
990
+ });
991
+ }
977
992
  }
978
993
 
979
994
  function cleanup(element, className) {
@@ -986,11 +1001,9 @@ angular.module('ngAnimate', ['ng'])
986
1001
  var data = element.data(NG_ANIMATE_STATE) || {};
987
1002
 
988
1003
  var removeAnimations = className === true;
989
- if(!removeAnimations) {
990
- if(data.active && data.active[className]) {
991
- data.totalActive--;
992
- delete data.active[className];
993
- }
1004
+ if(!removeAnimations && data.active && data.active[className]) {
1005
+ data.totalActive--;
1006
+ delete data.active[className];
994
1007
  }
995
1008
 
996
1009
  if(removeAnimations || !data.totalActive) {
@@ -1094,6 +1107,15 @@ angular.module('ngAnimate', ['ng'])
1094
1107
  var closingTimestamp = 0;
1095
1108
  var animationElementQueue = [];
1096
1109
  function animationCloseHandler(element, totalTime) {
1110
+ var node = extractElementNode(element);
1111
+ element = angular.element(node);
1112
+
1113
+ //this item will be garbage collected by the closing
1114
+ //animation timeout
1115
+ animationElementQueue.push(element);
1116
+
1117
+ //but it may not need to cancel out the existing timeout
1118
+ //if the timestamp is less than the previous one
1097
1119
  var futureTimestamp = Date.now() + (totalTime * 1000);
1098
1120
  if(futureTimestamp <= closingTimestamp) {
1099
1121
  return;
@@ -1101,10 +1123,6 @@ angular.module('ngAnimate', ['ng'])
1101
1123
 
1102
1124
  $timeout.cancel(closingTimer);
1103
1125
 
1104
- var node = extractElementNode(element);
1105
- element = angular.element(node);
1106
- animationElementQueue.push(element);
1107
-
1108
1126
  closingTimestamp = futureTimestamp;
1109
1127
  closingTimer = $timeout(function() {
1110
1128
  closeAllAnimations(animationElementQueue);
@@ -1243,7 +1261,7 @@ angular.module('ngAnimate', ['ng'])
1243
1261
  itemIndex : itemIndex,
1244
1262
  stagger : stagger,
1245
1263
  timings : timings,
1246
- closeAnimationFn : angular.noop
1264
+ closeAnimationFn : noop
1247
1265
  });
1248
1266
 
1249
1267
  //temporarily disable the transition so that the enter styles
@@ -1252,7 +1270,14 @@ angular.module('ngAnimate', ['ng'])
1252
1270
  if(transitionDuration > 0) {
1253
1271
  blockTransitions(element, className, isCurrentlyAnimating);
1254
1272
  }
1255
- if(animationDuration > 0) {
1273
+
1274
+ //staggering keyframe animations work by adjusting the `animation-delay` CSS property
1275
+ //on the given element, however, the delay value can only calculated after the reflow
1276
+ //since by that time $animate knows how many elements are being animated. Therefore,
1277
+ //until the reflow occurs the element needs to be blocked (where the keyframe animation
1278
+ //is set to `none 0s`). This blocking mechanism should only be set for when a stagger
1279
+ //animation is detected and when the element item index is greater than 0.
1280
+ if(animationDuration > 0 && stagger.animationDelay > 0 && stagger.animationDuration === 0) {
1256
1281
  blockKeyframeAnimations(element);
1257
1282
  }
1258
1283