angularjs-rails 1.2.13 → 1.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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