rails-angularjs 1.4.3 → 1.4.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -1
  3. data/lib/rails-angularjs/version.rb +1 -1
  4. data/vendor/assets/javascripts/angular-animate.js +697 -545
  5. data/vendor/assets/javascripts/angular-animate.min.js +50 -47
  6. data/vendor/assets/javascripts/angular-animate.min.js.map +3 -3
  7. data/vendor/assets/javascripts/angular-aria.js +42 -37
  8. data/vendor/assets/javascripts/angular-aria.min.js +9 -8
  9. data/vendor/assets/javascripts/angular-aria.min.js.map +3 -3
  10. data/vendor/assets/javascripts/angular-cookies.js +3 -3
  11. data/vendor/assets/javascripts/angular-cookies.min.js +4 -4
  12. data/vendor/assets/javascripts/angular-cookies.min.js.map +2 -2
  13. data/vendor/assets/javascripts/angular-csp.css +21 -0
  14. data/vendor/assets/javascripts/angular-loader.js +34 -6
  15. data/vendor/assets/javascripts/angular-loader.min.js +2 -2
  16. data/vendor/assets/javascripts/angular-loader.min.js.map +1 -1
  17. data/vendor/assets/javascripts/angular-message-format.js +1 -1
  18. data/vendor/assets/javascripts/angular-message-format.min.js +1 -1
  19. data/vendor/assets/javascripts/angular-messages.js +9 -2
  20. data/vendor/assets/javascripts/angular-messages.min.js +7 -6
  21. data/vendor/assets/javascripts/angular-messages.min.js.map +3 -3
  22. data/vendor/assets/javascripts/angular-mocks.js +152 -39
  23. data/vendor/assets/javascripts/angular-resource.js +36 -11
  24. data/vendor/assets/javascripts/angular-resource.min.js +9 -8
  25. data/vendor/assets/javascripts/angular-resource.min.js.map +3 -3
  26. data/vendor/assets/javascripts/angular-route.js +1 -2
  27. data/vendor/assets/javascripts/angular-route.min.js +1 -1
  28. data/vendor/assets/javascripts/angular-route.min.js.map +1 -1
  29. data/vendor/assets/javascripts/angular-sanitize.js +2 -2
  30. data/vendor/assets/javascripts/angular-sanitize.min.js +1 -1
  31. data/vendor/assets/javascripts/angular-scenario.js +2497 -1191
  32. data/vendor/assets/javascripts/angular-touch.js +2 -3
  33. data/vendor/assets/javascripts/angular-touch.min.js +1 -1
  34. data/vendor/assets/javascripts/angular-touch.min.js.map +1 -1
  35. data/vendor/assets/javascripts/angular.js +2489 -1196
  36. data/vendor/assets/javascripts/angular.min.js +293 -285
  37. data/vendor/assets/javascripts/angular.min.js.map +3 -3
  38. metadata +4 -3
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.4.3
2
+ * @license AngularJS v1.4.9
3
3
  * (c) 2010-2015 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -7,6 +7,7 @@
7
7
 
8
8
  /* jshint ignore:start */
9
9
  var noop = angular.noop;
10
+ var copy = angular.copy;
10
11
  var extend = angular.extend;
11
12
  var jqLite = angular.element;
12
13
  var forEach = angular.forEach;
@@ -21,13 +22,62 @@ var isElement = angular.isElement;
21
22
  var ELEMENT_NODE = 1;
22
23
  var COMMENT_NODE = 8;
23
24
 
25
+ var ADD_CLASS_SUFFIX = '-add';
26
+ var REMOVE_CLASS_SUFFIX = '-remove';
27
+ var EVENT_CLASS_PREFIX = 'ng-';
28
+ var ACTIVE_CLASS_SUFFIX = '-active';
29
+
24
30
  var NG_ANIMATE_CLASSNAME = 'ng-animate';
25
31
  var NG_ANIMATE_CHILDREN_DATA = '$$ngAnimateChildren';
26
32
 
33
+ // Detect proper transitionend/animationend event names.
34
+ var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
35
+
36
+ // If unprefixed events are not supported but webkit-prefixed are, use the latter.
37
+ // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
38
+ // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
39
+ // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
40
+ // Register both events in case `window.onanimationend` is not supported because of that,
41
+ // do the same for `transitionend` as Safari is likely to exhibit similar behavior.
42
+ // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
43
+ // therefore there is no reason to test anymore for other vendor prefixes:
44
+ // http://caniuse.com/#search=transition
45
+ if (isUndefined(window.ontransitionend) && isDefined(window.onwebkittransitionend)) {
46
+ CSS_PREFIX = '-webkit-';
47
+ TRANSITION_PROP = 'WebkitTransition';
48
+ TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
49
+ } else {
50
+ TRANSITION_PROP = 'transition';
51
+ TRANSITIONEND_EVENT = 'transitionend';
52
+ }
53
+
54
+ if (isUndefined(window.onanimationend) && isDefined(window.onwebkitanimationend)) {
55
+ CSS_PREFIX = '-webkit-';
56
+ ANIMATION_PROP = 'WebkitAnimation';
57
+ ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
58
+ } else {
59
+ ANIMATION_PROP = 'animation';
60
+ ANIMATIONEND_EVENT = 'animationend';
61
+ }
62
+
63
+ var DURATION_KEY = 'Duration';
64
+ var PROPERTY_KEY = 'Property';
65
+ var DELAY_KEY = 'Delay';
66
+ var TIMING_KEY = 'TimingFunction';
67
+ var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
68
+ var ANIMATION_PLAYSTATE_KEY = 'PlayState';
69
+ var SAFE_FAST_FORWARD_DURATION_VALUE = 9999;
70
+
71
+ var ANIMATION_DELAY_PROP = ANIMATION_PROP + DELAY_KEY;
72
+ var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY;
73
+ var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY;
74
+ var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;
75
+
27
76
  var isPromiseLike = function(p) {
28
77
  return p && p.then ? true : false;
29
- }
78
+ };
30
79
 
80
+ var ngMinErr = angular.$$minErr('ng');
31
81
  function assertArg(arg, name, reason) {
32
82
  if (!arg) {
33
83
  throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
@@ -177,8 +227,21 @@ function mergeAnimationOptions(element, target, newOptions) {
177
227
  var toRemove = (target.removeClass || '') + ' ' + (newOptions.removeClass || '');
178
228
  var classes = resolveElementClasses(element.attr('class'), toAdd, toRemove);
179
229
 
230
+ if (newOptions.preparationClasses) {
231
+ target.preparationClasses = concatWithSpace(newOptions.preparationClasses, target.preparationClasses);
232
+ delete newOptions.preparationClasses;
233
+ }
234
+
235
+ // noop is basically when there is no callback; otherwise something has been set
236
+ var realDomOperation = target.domOperation !== noop ? target.domOperation : null;
237
+
180
238
  extend(target, newOptions);
181
239
 
240
+ // TODO(matsko or sreeramu): proper fix is to maintain all animation callback in array and call at last,but now only leave has the callback so no issue with this.
241
+ if (realDomOperation) {
242
+ target.domOperation = realDomOperation;
243
+ }
244
+
182
245
  if (classes.addClass) {
183
246
  target.addClass = classes.addClass;
184
247
  } else {
@@ -256,18 +319,75 @@ function getDomNode(element) {
256
319
  return (element instanceof angular.element) ? element[0] : element;
257
320
  }
258
321
 
322
+ function applyGeneratedPreparationClasses(element, event, options) {
323
+ var classes = '';
324
+ if (event) {
325
+ classes = pendClasses(event, EVENT_CLASS_PREFIX, true);
326
+ }
327
+ if (options.addClass) {
328
+ classes = concatWithSpace(classes, pendClasses(options.addClass, ADD_CLASS_SUFFIX));
329
+ }
330
+ if (options.removeClass) {
331
+ classes = concatWithSpace(classes, pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX));
332
+ }
333
+ if (classes.length) {
334
+ options.preparationClasses = classes;
335
+ element.addClass(classes);
336
+ }
337
+ }
338
+
339
+ function clearGeneratedClasses(element, options) {
340
+ if (options.preparationClasses) {
341
+ element.removeClass(options.preparationClasses);
342
+ options.preparationClasses = null;
343
+ }
344
+ if (options.activeClasses) {
345
+ element.removeClass(options.activeClasses);
346
+ options.activeClasses = null;
347
+ }
348
+ }
349
+
350
+ function blockTransitions(node, duration) {
351
+ // we use a negative delay value since it performs blocking
352
+ // yet it doesn't kill any existing transitions running on the
353
+ // same element which makes this safe for class-based animations
354
+ var value = duration ? '-' + duration + 's' : '';
355
+ applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]);
356
+ return [TRANSITION_DELAY_PROP, value];
357
+ }
358
+
359
+ function blockKeyframeAnimations(node, applyBlock) {
360
+ var value = applyBlock ? 'paused' : '';
361
+ var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY;
362
+ applyInlineStyle(node, [key, value]);
363
+ return [key, value];
364
+ }
365
+
366
+ function applyInlineStyle(node, styleTuple) {
367
+ var prop = styleTuple[0];
368
+ var value = styleTuple[1];
369
+ node.style[prop] = value;
370
+ }
371
+
372
+ function concatWithSpace(a,b) {
373
+ if (!a) return b;
374
+ if (!b) return a;
375
+ return a + ' ' + b;
376
+ }
377
+
259
378
  var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
260
- var tickQueue = [];
261
- var cancelFn;
379
+ var queue, cancelFn;
262
380
 
263
381
  function scheduler(tasks) {
264
382
  // we make a copy since RAFScheduler mutates the state
265
383
  // of the passed in array variable and this would be difficult
266
384
  // to track down on the outside code
267
- tickQueue.push([].concat(tasks));
385
+ queue = queue.concat(tasks);
268
386
  nextTick();
269
387
  }
270
388
 
389
+ queue = scheduler.queue = [];
390
+
271
391
  /* waitUntilQuiet does two things:
272
392
  * 1. It will run the FINAL `fn` value only when an uncancelled RAF has passed through
273
393
  * 2. It will delay the next wave of tasks from running until the quiet `fn` has run.
@@ -289,17 +409,12 @@ var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
289
409
  return scheduler;
290
410
 
291
411
  function nextTick() {
292
- if (!tickQueue.length) return;
412
+ if (!queue.length) return;
293
413
 
294
- var updatedQueue = [];
295
- for (var i = 0; i < tickQueue.length; i++) {
296
- var innerQueue = tickQueue[i];
297
- runNextTask(innerQueue);
298
- if (innerQueue.length) {
299
- updatedQueue.push(innerQueue);
300
- }
414
+ var items = queue.shift();
415
+ for (var i = 0; i < items.length; i++) {
416
+ items[i]();
301
417
  }
302
- tickQueue = updatedQueue;
303
418
 
304
419
  if (!cancelFn) {
305
420
  $$rAF(function() {
@@ -307,11 +422,6 @@ var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {
307
422
  });
308
423
  }
309
424
  }
310
-
311
- function runNextTask(tasks) {
312
- var nextTask = tasks.shift();
313
- nextTask();
314
- }
315
425
  }];
316
426
 
317
427
  var $$AnimateChildrenDirective = [function() {
@@ -328,6 +438,8 @@ var $$AnimateChildrenDirective = [function() {
328
438
  };
329
439
  }];
330
440
 
441
+ var ANIMATE_TIMER_KEY = '$$animateCss';
442
+
331
443
  /**
332
444
  * @ngdoc service
333
445
  * @name $animateCss
@@ -512,8 +624,10 @@ var $$AnimateChildrenDirective = [function() {
512
624
  *
513
625
  * * `event` - The DOM event (e.g. enter, leave, move). When used, a generated CSS class of `ng-EVENT` and `ng-EVENT-active` will be applied
514
626
  * to the element during the animation. Multiple events can be provided when spaces are used as a separator. (Note that this will not perform any DOM operation.)
627
+ * * `structural` - Indicates that the `ng-` prefix will be added to the event class. Setting to `false` or omitting will turn `ng-EVENT` and
628
+ * `ng-EVENT-active` in `EVENT` and `EVENT-active`. Unused if `event` is omitted.
515
629
  * * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both).
516
- * * `transition` - The raw CSS transition style that will be used (e.g. `1s linear all`).
630
+ * * `transitionStyle` - The raw CSS transition style that will be used (e.g. `1s linear all`).
517
631
  * * `keyframeStyle` - The raw CSS keyframe animation style that will be used (e.g. `1s my_animation linear`).
518
632
  * * `from` - The starting CSS styles (a key/value object) that will be applied at the start of the animation.
519
633
  * * `to` - The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition.
@@ -528,63 +642,23 @@ var $$AnimateChildrenDirective = [function() {
528
642
  * * `stagger` - A numeric time value representing the delay between successively animated elements
529
643
  * ({@link ngAnimate#css-staggering-animations Click here to learn how CSS-based staggering works in ngAnimate.})
530
644
  * * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a
531
- * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
532
- * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occuring on the classes being added and removed.)
645
+ * * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
646
+ * * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occuring on the classes being added and removed.)
647
+ * * `cleanupStyles` - Whether or not the provided `from` and `to` styles will be removed once
648
+ * the animation is closed. This is useful for when the styles are used purely for the sake of
649
+ * the animation and do not have a lasting visual effect on the element (e.g. a colapse and open animation).
650
+ * By default this value is set to `false`.
533
651
  *
534
652
  * @return {object} an object with start and end methods and details about the animation.
535
653
  *
536
654
  * * `start` - The method to start the animation. This will return a `Promise` when called.
537
655
  * * `end` - This method will cancel the animation and remove all applied CSS classes and styles.
538
656
  */
539
-
540
- // Detect proper transitionend/animationend event names.
541
- var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
542
-
543
- // If unprefixed events are not supported but webkit-prefixed are, use the latter.
544
- // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
545
- // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
546
- // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
547
- // Register both events in case `window.onanimationend` is not supported because of that,
548
- // do the same for `transitionend` as Safari is likely to exhibit similar behavior.
549
- // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
550
- // therefore there is no reason to test anymore for other vendor prefixes:
551
- // http://caniuse.com/#search=transition
552
- if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) {
553
- CSS_PREFIX = '-webkit-';
554
- TRANSITION_PROP = 'WebkitTransition';
555
- TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
556
- } else {
557
- TRANSITION_PROP = 'transition';
558
- TRANSITIONEND_EVENT = 'transitionend';
559
- }
560
-
561
- if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) {
562
- CSS_PREFIX = '-webkit-';
563
- ANIMATION_PROP = 'WebkitAnimation';
564
- ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
565
- } else {
566
- ANIMATION_PROP = 'animation';
567
- ANIMATIONEND_EVENT = 'animationend';
568
- }
569
-
570
- var DURATION_KEY = 'Duration';
571
- var PROPERTY_KEY = 'Property';
572
- var DELAY_KEY = 'Delay';
573
- var TIMING_KEY = 'TimingFunction';
574
- var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
575
- var ANIMATION_PLAYSTATE_KEY = 'PlayState';
576
- var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
577
- var CLOSING_TIME_BUFFER = 1.5;
578
657
  var ONE_SECOND = 1000;
579
658
  var BASE_TEN = 10;
580
659
 
581
- var SAFE_FAST_FORWARD_DURATION_VALUE = 9999;
582
-
583
- var ANIMATION_DELAY_PROP = ANIMATION_PROP + DELAY_KEY;
584
- var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY;
585
-
586
- var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY;
587
- var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;
660
+ var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
661
+ var CLOSING_TIME_BUFFER = 1.5;
588
662
 
589
663
  var DETECT_CSS_PROPERTIES = {
590
664
  transitionDuration: TRANSITION_DURATION_PROP,
@@ -602,6 +676,15 @@ var DETECT_STAGGER_CSS_PROPERTIES = {
602
676
  animationDelay: ANIMATION_DELAY_PROP
603
677
  };
604
678
 
679
+ function getCssKeyframeDurationStyle(duration) {
680
+ return [ANIMATION_DURATION_PROP, duration + 's'];
681
+ }
682
+
683
+ function getCssDelayStyle(delay, isKeyframeAnimation) {
684
+ var prop = isKeyframeAnimation ? ANIMATION_DELAY_PROP : TRANSITION_DELAY_PROP;
685
+ return [prop, delay + 's'];
686
+ }
687
+
605
688
  function computeCssStyles($window, element, properties) {
606
689
  var styles = Object.create(null);
607
690
  var detectedStyles = $window.getComputedStyle(element) || {};
@@ -658,37 +741,6 @@ function getCssTransitionDurationStyle(duration, applyOnlyDuration) {
658
741
  return [style, value];
659
742
  }
660
743
 
661
- function getCssKeyframeDurationStyle(duration) {
662
- return [ANIMATION_DURATION_PROP, duration + 's'];
663
- }
664
-
665
- function getCssDelayStyle(delay, isKeyframeAnimation) {
666
- var prop = isKeyframeAnimation ? ANIMATION_DELAY_PROP : TRANSITION_DELAY_PROP;
667
- return [prop, delay + 's'];
668
- }
669
-
670
- function blockTransitions(node, duration) {
671
- // we use a negative delay value since it performs blocking
672
- // yet it doesn't kill any existing transitions running on the
673
- // same element which makes this safe for class-based animations
674
- var value = duration ? '-' + duration + 's' : '';
675
- applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]);
676
- return [TRANSITION_DELAY_PROP, value];
677
- }
678
-
679
- function blockKeyframeAnimations(node, applyBlock) {
680
- var value = applyBlock ? 'paused' : '';
681
- var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY;
682
- applyInlineStyle(node, [key, value]);
683
- return [key, value];
684
- }
685
-
686
- function applyInlineStyle(node, styleTuple) {
687
- var prop = styleTuple[0];
688
- var value = styleTuple[1];
689
- node.style[prop] = value;
690
- }
691
-
692
744
  function createLocalCacheLookup() {
693
745
  var cache = Object.create(null);
694
746
  return {
@@ -716,14 +768,31 @@ function createLocalCacheLookup() {
716
768
  };
717
769
  }
718
770
 
771
+ // we do not reassign an already present style value since
772
+ // if we detect the style property value again we may be
773
+ // detecting styles that were added via the `from` styles.
774
+ // We make use of `isDefined` here since an empty string
775
+ // or null value (which is what getPropertyValue will return
776
+ // for a non-existing style) will still be marked as a valid
777
+ // value for the style (a falsy value implies that the style
778
+ // is to be removed at the end of the animation). If we had a simple
779
+ // "OR" statement then it would not be enough to catch that.
780
+ function registerRestorableStyles(backup, node, properties) {
781
+ forEach(properties, function(prop) {
782
+ backup[prop] = isDefined(backup[prop])
783
+ ? backup[prop]
784
+ : node.style.getPropertyValue(prop);
785
+ });
786
+ }
787
+
719
788
  var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
720
789
  var gcsLookup = createLocalCacheLookup();
721
790
  var gcsStaggerLookup = createLocalCacheLookup();
722
791
 
723
792
  this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout',
724
- '$document', '$sniffer', '$$rAFScheduler',
793
+ '$$forceReflow', '$sniffer', '$$rAFScheduler', '$$animateQueue',
725
794
  function($window, $$jqLite, $$AnimateRunner, $timeout,
726
- $document, $sniffer, $$rAFScheduler) {
795
+ $$forceReflow, $sniffer, $$rAFScheduler, $$animateQueue) {
727
796
 
728
797
  var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
729
798
 
@@ -780,7 +849,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
780
849
  return stagger || {};
781
850
  }
782
851
 
783
- var bod = getDomNode($document).body;
852
+ var cancelLastRAFRequest;
784
853
  var rafWaitQueue = [];
785
854
  function waitUntilQuiet(callback) {
786
855
  rafWaitQueue.push(callback);
@@ -788,27 +857,19 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
788
857
  gcsLookup.flush();
789
858
  gcsStaggerLookup.flush();
790
859
 
791
- //the line below will force the browser to perform a repaint so
792
- //that all the animated elements within the animation frame will
793
- //be properly updated and drawn on screen. This is required to
794
- //ensure that the preparation animation is properly flushed so that
795
- //the active state picks up from there. DO NOT REMOVE THIS LINE.
796
- //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
797
- //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
798
- //WILL TAKE YEARS AWAY FROM YOUR LIFE.
799
- var width = bod.offsetWidth + 1;
860
+ // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable.
861
+ // PLEASE EXAMINE THE `$$forceReflow` service to understand why.
862
+ var pageWidth = $$forceReflow();
800
863
 
801
864
  // we use a for loop to ensure that if the queue is changed
802
865
  // during this looping then it will consider new requests
803
866
  for (var i = 0; i < rafWaitQueue.length; i++) {
804
- rafWaitQueue[i](width);
867
+ rafWaitQueue[i](pageWidth);
805
868
  }
806
869
  rafWaitQueue.length = 0;
807
870
  });
808
871
  }
809
872
 
810
- return init;
811
-
812
873
  function computeTimings(node, className, cacheKey) {
813
874
  var timings = computeCachedCssStyles(node, className, cacheKey, DETECT_CSS_PROPERTIES);
814
875
  var aD = timings.animationDelay;
@@ -823,14 +884,24 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
823
884
  return timings;
824
885
  }
825
886
 
826
- function init(element, options) {
887
+ return function init(element, initialOptions) {
888
+ // all of the animation functions should create
889
+ // a copy of the options data, however, if a
890
+ // parent service has already created a copy then
891
+ // we should stick to using that
892
+ var options = initialOptions || {};
893
+ if (!options.$$prepared) {
894
+ options = prepareAnimationOptions(copy(options));
895
+ }
896
+
897
+ var restoreStyles = {};
827
898
  var node = getDomNode(element);
828
- if (!node || !node.parentNode) {
899
+ if (!node
900
+ || !node.parentNode
901
+ || !$$animateQueue.enabled()) {
829
902
  return closeAndReturnNoopAnimator();
830
903
  }
831
904
 
832
- options = prepareAnimationOptions(options);
833
-
834
905
  var temporaryStyles = [];
835
906
  var classes = element.attr('class');
836
907
  var styles = packageStyles(options);
@@ -843,6 +914,8 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
843
914
  var maxDelayTime;
844
915
  var maxDuration;
845
916
  var maxDurationTime;
917
+ var startTime;
918
+ var events = [];
846
919
 
847
920
  if (options.duration === 0 || (!$sniffer.animations && !$sniffer.transitions)) {
848
921
  return closeAndReturnNoopAnimator();
@@ -857,20 +930,20 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
857
930
  var addRemoveClassName = '';
858
931
 
859
932
  if (isStructural) {
860
- structuralClassName = pendClasses(method, 'ng-', true);
933
+ structuralClassName = pendClasses(method, EVENT_CLASS_PREFIX, true);
861
934
  } else if (method) {
862
935
  structuralClassName = method;
863
936
  }
864
937
 
865
938
  if (options.addClass) {
866
- addRemoveClassName += pendClasses(options.addClass, '-add');
939
+ addRemoveClassName += pendClasses(options.addClass, ADD_CLASS_SUFFIX);
867
940
  }
868
941
 
869
942
  if (options.removeClass) {
870
943
  if (addRemoveClassName.length) {
871
944
  addRemoveClassName += ' ';
872
945
  }
873
- addRemoveClassName += pendClasses(options.removeClass, '-remove');
946
+ addRemoveClassName += pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX);
874
947
  }
875
948
 
876
949
  // there may be a situation where a structural animation is combined together
@@ -881,12 +954,11 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
881
954
  // there actually is a detected transition or keyframe animation
882
955
  if (options.applyClassesEarly && addRemoveClassName.length) {
883
956
  applyAnimationClasses(element, options);
884
- addRemoveClassName = '';
885
957
  }
886
958
 
887
- var setupClasses = [structuralClassName, addRemoveClassName].join(' ').trim();
888
- var fullClassName = classes + ' ' + setupClasses;
889
- var activeClasses = pendClasses(setupClasses, '-active');
959
+ var preparationClasses = [structuralClassName, addRemoveClassName].join(' ').trim();
960
+ var fullClassName = classes + ' ' + preparationClasses;
961
+ var activeClasses = pendClasses(preparationClasses, ACTIVE_CLASS_SUFFIX);
890
962
  var hasToStyles = styles.to && Object.keys(styles.to).length > 0;
891
963
  var containsKeyframeAnimation = (options.keyframeStyle || '').length > 0;
892
964
 
@@ -895,7 +967,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
895
967
  // unless there a is raw keyframe value that is applied to the element.
896
968
  if (!containsKeyframeAnimation
897
969
  && !hasToStyles
898
- && !setupClasses) {
970
+ && !preparationClasses) {
899
971
  return closeAndReturnNoopAnimator();
900
972
  }
901
973
 
@@ -910,10 +982,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
910
982
  };
911
983
  } else {
912
984
  cacheKey = gcsHashFn(node, fullClassName);
913
- stagger = computeCachedCssStaggerStyles(node, setupClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES);
985
+ stagger = computeCachedCssStaggerStyles(node, preparationClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES);
914
986
  }
915
987
 
916
- $$jqLite.addClass(element, setupClasses);
988
+ if (!options.$$skipPreparationClasses) {
989
+ $$jqLite.addClass(element, preparationClasses);
990
+ }
917
991
 
918
992
  var applyOnlyDuration;
919
993
 
@@ -952,7 +1026,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
952
1026
  // transition delay to allow for the transition to naturally do it's thing. The beauty here is
953
1027
  // that if there is no transition defined then nothing will happen and this will also allow
954
1028
  // other transitions to be stacked on top of each other without any chopping them out.
955
- if (isFirst) {
1029
+ if (isFirst && !options.skipBlocking) {
956
1030
  blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);
957
1031
  }
958
1032
 
@@ -994,6 +1068,23 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
994
1068
  return closeAndReturnNoopAnimator();
995
1069
  }
996
1070
 
1071
+ if (options.delay != null) {
1072
+ var delayStyle;
1073
+ if (typeof options.delay !== "boolean") {
1074
+ delayStyle = parseFloat(options.delay);
1075
+ // number in options.delay means we have to recalculate the delay for the closing timeout
1076
+ maxDelay = Math.max(delayStyle, 0);
1077
+ }
1078
+
1079
+ if (flags.applyTransitionDelay) {
1080
+ temporaryStyles.push(getCssDelayStyle(delayStyle));
1081
+ }
1082
+
1083
+ if (flags.applyAnimationDelay) {
1084
+ temporaryStyles.push(getCssDelayStyle(delayStyle, true));
1085
+ }
1086
+ }
1087
+
997
1088
  // we need to recalculate the delay value since we used a pre-emptive negative
998
1089
  // delay value and the delay value is required for the final event checking. This
999
1090
  // property will ensure that this will happen after the RAF phase has passed.
@@ -1010,12 +1101,18 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1010
1101
  stagger.animationDuration === 0;
1011
1102
  }
1012
1103
 
1013
- applyAnimationFromStyles(element, options);
1014
- if (!flags.blockTransition) {
1015
- blockTransitions(node, false);
1104
+ if (options.from) {
1105
+ if (options.cleanupStyles) {
1106
+ registerRestorableStyles(restoreStyles, node, Object.keys(options.from));
1107
+ }
1108
+ applyAnimationFromStyles(element, options);
1016
1109
  }
1017
1110
 
1018
- applyBlocking(maxDuration);
1111
+ if (flags.blockTransition || flags.blockKeyframeAnimation) {
1112
+ applyBlocking(maxDuration);
1113
+ } else if (!options.skipBlocking) {
1114
+ blockTransitions(node, false);
1115
+ }
1019
1116
 
1020
1117
  // TODO(matsko): for 1.5 change this code to have an animator object for better debugging
1021
1118
  return {
@@ -1058,7 +1155,9 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1058
1155
  animationClosed = true;
1059
1156
  animationPaused = false;
1060
1157
 
1061
- $$jqLite.removeClass(element, setupClasses);
1158
+ if (!options.$$skipPreparationClasses) {
1159
+ $$jqLite.removeClass(element, preparationClasses);
1160
+ }
1062
1161
  $$jqLite.removeClass(element, activeClasses);
1063
1162
 
1064
1163
  blockKeyframeAnimations(node, false);
@@ -1074,6 +1173,13 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1074
1173
  applyAnimationClasses(element, options);
1075
1174
  applyAnimationStyles(element, options);
1076
1175
 
1176
+ if (Object.keys(restoreStyles).length) {
1177
+ forEach(restoreStyles, function(value, prop) {
1178
+ value ? node.style.setProperty(prop, value)
1179
+ : node.style.removeProperty(prop);
1180
+ });
1181
+ }
1182
+
1077
1183
  // the reason why we have this option is to allow a synchronous closing callback
1078
1184
  // that is fired as SOON as the animation ends (when the CSS is removed) or if
1079
1185
  // the animation never takes off at all. A good example is a leave animation since
@@ -1083,6 +1189,11 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1083
1189
  options.onDone();
1084
1190
  }
1085
1191
 
1192
+ if (events && events.length) {
1193
+ // Remove the transitionend / animationend listener(s)
1194
+ element.off(events.join(' '), onAnimationProgress);
1195
+ }
1196
+
1086
1197
  // if the preparation function fails then the promise is not setup
1087
1198
  if (runner) {
1088
1199
  runner.complete(!rejected);
@@ -1105,6 +1216,8 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1105
1216
  cancel: cancelFn
1106
1217
  });
1107
1218
 
1219
+ // should flush the cache animation
1220
+ waitUntilQuiet(noop);
1108
1221
  close();
1109
1222
 
1110
1223
  return {
@@ -1116,6 +1229,33 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1116
1229
  };
1117
1230
  }
1118
1231
 
1232
+ function onAnimationProgress(event) {
1233
+ event.stopPropagation();
1234
+ var ev = event.originalEvent || event;
1235
+
1236
+ // we now always use `Date.now()` due to the recent changes with
1237
+ // event.timeStamp in Firefox, Webkit and Chrome (see #13494 for more info)
1238
+ var timeStamp = ev.$manualTimeStamp || Date.now();
1239
+
1240
+ /* Firefox (or possibly just Gecko) likes to not round values up
1241
+ * when a ms measurement is used for the animation */
1242
+ var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
1243
+
1244
+ /* $manualTimeStamp is a mocked timeStamp value which is set
1245
+ * within browserTrigger(). This is only here so that tests can
1246
+ * mock animations properly. Real events fallback to event.timeStamp,
1247
+ * or, if they don't, then a timeStamp is automatically created for them.
1248
+ * We're checking to see if the timeStamp surpasses the expected delay,
1249
+ * but we're using elapsedTime instead of the timeStamp on the 2nd
1250
+ * pre-condition since animationPauseds sometimes close off early */
1251
+ if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {
1252
+ // we set this flag to ensure that if the transition is paused then, when resumed,
1253
+ // the animation will automatically close itself since transitions cannot be paused.
1254
+ animationCompleted = true;
1255
+ close();
1256
+ }
1257
+ }
1258
+
1119
1259
  function start() {
1120
1260
  if (animationClosed) return;
1121
1261
  if (!node.parentNode) {
@@ -1123,8 +1263,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1123
1263
  return;
1124
1264
  }
1125
1265
 
1126
- var startTime, events = [];
1127
-
1128
1266
  // even though we only pause keyframe animations here the pause flag
1129
1267
  // will still happen when transitions are used. Only the transition will
1130
1268
  // not be paused since that is not possible. If the animation ends when
@@ -1185,7 +1323,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1185
1323
  $$jqLite.addClass(element, activeClasses);
1186
1324
 
1187
1325
  if (flags.recalculateTimingStyles) {
1188
- fullClassName = node.className + ' ' + setupClasses;
1326
+ fullClassName = node.className + ' ' + preparationClasses;
1189
1327
  cacheKey = gcsHashFn(node, fullClassName);
1190
1328
 
1191
1329
  timings = computeTimings(node, fullClassName, cacheKey);
@@ -1202,27 +1340,16 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1202
1340
  flags.hasAnimations = timings.animationDuration > 0;
1203
1341
  }
1204
1342
 
1205
- if (flags.applyTransitionDelay || flags.applyAnimationDelay) {
1343
+ if (flags.applyAnimationDelay) {
1206
1344
  relativeDelay = typeof options.delay !== "boolean" && truthyTimingValue(options.delay)
1207
1345
  ? parseFloat(options.delay)
1208
1346
  : relativeDelay;
1209
1347
 
1210
1348
  maxDelay = Math.max(relativeDelay, 0);
1211
-
1212
- var delayStyle;
1213
- if (flags.applyTransitionDelay) {
1214
- timings.transitionDelay = relativeDelay;
1215
- delayStyle = getCssDelayStyle(relativeDelay);
1216
- temporaryStyles.push(delayStyle);
1217
- node.style[delayStyle[0]] = delayStyle[1];
1218
- }
1219
-
1220
- if (flags.applyAnimationDelay) {
1221
- timings.animationDelay = relativeDelay;
1222
- delayStyle = getCssDelayStyle(relativeDelay, true);
1223
- temporaryStyles.push(delayStyle);
1224
- node.style[delayStyle[0]] = delayStyle[1];
1225
- }
1349
+ timings.animationDelay = relativeDelay;
1350
+ delayStyle = getCssDelayStyle(relativeDelay, true);
1351
+ temporaryStyles.push(delayStyle);
1352
+ node.style[delayStyle[0]] = delayStyle[1];
1226
1353
  }
1227
1354
 
1228
1355
  maxDelayTime = maxDelay * ONE_SECOND;
@@ -1251,44 +1378,58 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
1251
1378
  }
1252
1379
 
1253
1380
  startTime = Date.now();
1254
- element.on(events.join(' '), onAnimationProgress);
1255
- $timeout(onAnimationExpired, maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime);
1381
+ var timerTime = maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime;
1382
+ var endTime = startTime + timerTime;
1383
+
1384
+ var animationsData = element.data(ANIMATE_TIMER_KEY) || [];
1385
+ var setupFallbackTimer = true;
1386
+ if (animationsData.length) {
1387
+ var currentTimerData = animationsData[0];
1388
+ setupFallbackTimer = endTime > currentTimerData.expectedEndTime;
1389
+ if (setupFallbackTimer) {
1390
+ $timeout.cancel(currentTimerData.timer);
1391
+ } else {
1392
+ animationsData.push(close);
1393
+ }
1394
+ }
1256
1395
 
1257
- applyAnimationToStyles(element, options);
1258
- }
1396
+ if (setupFallbackTimer) {
1397
+ var timer = $timeout(onAnimationExpired, timerTime, false);
1398
+ animationsData[0] = {
1399
+ timer: timer,
1400
+ expectedEndTime: endTime
1401
+ };
1402
+ animationsData.push(close);
1403
+ element.data(ANIMATE_TIMER_KEY, animationsData);
1404
+ }
1259
1405
 
1260
- function onAnimationExpired() {
1261
- // although an expired animation is a failed animation, getting to
1262
- // this outcome is very easy if the CSS code screws up. Therefore we
1263
- // should still continue normally as if the animation completed correctly.
1264
- close();
1406
+ if (events.length) {
1407
+ element.on(events.join(' '), onAnimationProgress);
1408
+ }
1409
+
1410
+ if (options.to) {
1411
+ if (options.cleanupStyles) {
1412
+ registerRestorableStyles(restoreStyles, node, Object.keys(options.to));
1413
+ }
1414
+ applyAnimationToStyles(element, options);
1415
+ }
1265
1416
  }
1266
1417
 
1267
- function onAnimationProgress(event) {
1268
- event.stopPropagation();
1269
- var ev = event.originalEvent || event;
1270
- var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
1271
-
1272
- /* Firefox (or possibly just Gecko) likes to not round values up
1273
- * when a ms measurement is used for the animation */
1274
- var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
1275
-
1276
- /* $manualTimeStamp is a mocked timeStamp value which is set
1277
- * within browserTrigger(). This is only here so that tests can
1278
- * mock animations properly. Real events fallback to event.timeStamp,
1279
- * or, if they don't, then a timeStamp is automatically created for them.
1280
- * We're checking to see if the timeStamp surpasses the expected delay,
1281
- * but we're using elapsedTime instead of the timeStamp on the 2nd
1282
- * pre-condition since animations sometimes close off early */
1283
- if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {
1284
- // we set this flag to ensure that if the transition is paused then, when resumed,
1285
- // the animation will automatically close itself since transitions cannot be paused.
1286
- animationCompleted = true;
1287
- close();
1418
+ function onAnimationExpired() {
1419
+ var animationsData = element.data(ANIMATE_TIMER_KEY);
1420
+
1421
+ // this will be false in the event that the element was
1422
+ // removed from the DOM (via a leave animation or something
1423
+ // similar)
1424
+ if (animationsData) {
1425
+ for (var i = 1; i < animationsData.length; i++) {
1426
+ animationsData[i]();
1427
+ }
1428
+ element.removeData(ANIMATE_TIMER_KEY);
1288
1429
  }
1289
1430
  }
1290
1431
  }
1291
- }
1432
+ };
1292
1433
  }];
1293
1434
  }];
1294
1435
 
@@ -1301,16 +1442,27 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
1301
1442
  var NG_OUT_ANCHOR_CLASS_NAME = 'ng-anchor-out';
1302
1443
  var NG_IN_ANCHOR_CLASS_NAME = 'ng-anchor-in';
1303
1444
 
1304
- this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$document', '$sniffer',
1305
- function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $document, $sniffer) {
1445
+ function isDocumentFragment(node) {
1446
+ return node.parentNode && node.parentNode.nodeType === 11;
1447
+ }
1448
+
1449
+ this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$sniffer', '$$jqLite', '$document',
1450
+ function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $sniffer, $$jqLite, $document) {
1306
1451
 
1307
1452
  // only browsers that support these properties can render animations
1308
1453
  if (!$sniffer.animations && !$sniffer.transitions) return noop;
1309
1454
 
1310
- var bodyNode = getDomNode($document).body;
1455
+ var bodyNode = $document[0].body;
1311
1456
  var rootNode = getDomNode($rootElement);
1312
1457
 
1313
- var rootBodyElement = jqLite(bodyNode.parentNode === rootNode ? bodyNode : rootNode);
1458
+ var rootBodyElement = jqLite(
1459
+ // this is to avoid using something that exists outside of the body
1460
+ // we also special case the doc fragement case because our unit test code
1461
+ // appends the $rootElement to the body after the app has been bootstrapped
1462
+ isDocumentFragment(rootNode) || bodyNode.contains(rootNode) ? rootNode : bodyNode
1463
+ );
1464
+
1465
+ var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
1314
1466
 
1315
1467
  return function initDriverFn(animationDetails) {
1316
1468
  return animationDetails.from && animationDetails.to
@@ -1462,8 +1614,8 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
1462
1614
  }
1463
1615
 
1464
1616
  function prepareFromToAnchorAnimation(from, to, classes, anchors) {
1465
- var fromAnimation = prepareRegularAnimation(from);
1466
- var toAnimation = prepareRegularAnimation(to);
1617
+ var fromAnimation = prepareRegularAnimation(from, noop);
1618
+ var toAnimation = prepareRegularAnimation(to, noop);
1467
1619
 
1468
1620
  var anchorAnimations = [];
1469
1621
  forEach(anchors, function(anchor) {
@@ -1519,19 +1671,23 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
1519
1671
  var options = animationDetails.options || {};
1520
1672
 
1521
1673
  if (animationDetails.structural) {
1522
- // structural animations ensure that the CSS classes are always applied
1523
- // before the detection starts.
1524
- options.structural = options.applyClassesEarly = true;
1674
+ options.event = animationDetails.event;
1675
+ options.structural = true;
1676
+ options.applyClassesEarly = true;
1525
1677
 
1526
1678
  // we special case the leave animation since we want to ensure that
1527
1679
  // the element is removed as soon as the animation is over. Otherwise
1528
1680
  // a flicker might appear or the element may not be removed at all
1529
- options.event = animationDetails.event;
1530
- if (options.event === 'leave') {
1681
+ if (animationDetails.event === 'leave') {
1531
1682
  options.onDone = options.domOperation;
1532
1683
  }
1533
- } else {
1534
- options.event = null;
1684
+ }
1685
+
1686
+ // We assign the preparationClasses as the actual animation event since
1687
+ // the internals of $animateCss will just suffix the event token values
1688
+ // with `-active` to trigger the animation.
1689
+ if (options.preparationClasses) {
1690
+ options.event = concatWithSpace(options.event, options.preparationClasses);
1535
1691
  }
1536
1692
 
1537
1693
  var animator = $animateCss(element, options);
@@ -1550,12 +1706,14 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
1550
1706
  // by the time...
1551
1707
 
1552
1708
  var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
1553
- this.$get = ['$injector', '$$AnimateRunner', '$$rAFMutex', '$$jqLite',
1554
- function($injector, $$AnimateRunner, $$rAFMutex, $$jqLite) {
1709
+ this.$get = ['$injector', '$$AnimateRunner', '$$jqLite',
1710
+ function($injector, $$AnimateRunner, $$jqLite) {
1555
1711
 
1556
1712
  var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
1557
1713
  // $animateJs(element, 'enter');
1558
1714
  return function(element, event, classes, options) {
1715
+ var animationClosed = false;
1716
+
1559
1717
  // the `classes` argument is optional and if it is not used
1560
1718
  // then the classes will be resolved from the element's className
1561
1719
  // property as well as options.addClass/options.removeClass.
@@ -1608,8 +1766,32 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
1608
1766
  applyAnimationClasses(element, options);
1609
1767
  }
1610
1768
 
1769
+ function close() {
1770
+ animationClosed = true;
1771
+ applyOptions();
1772
+ applyAnimationStyles(element, options);
1773
+ }
1774
+
1775
+ var runner;
1776
+
1611
1777
  return {
1778
+ $$willAnimate: true,
1779
+ end: function() {
1780
+ if (runner) {
1781
+ runner.end();
1782
+ } else {
1783
+ close();
1784
+ runner = new $$AnimateRunner();
1785
+ runner.complete(true);
1786
+ }
1787
+ return runner;
1788
+ },
1612
1789
  start: function() {
1790
+ if (runner) {
1791
+ return runner;
1792
+ }
1793
+
1794
+ runner = new $$AnimateRunner();
1613
1795
  var closeActiveAnimations;
1614
1796
  var chain = [];
1615
1797
 
@@ -1634,8 +1816,7 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
1634
1816
  });
1635
1817
  }
1636
1818
 
1637
- var animationClosed = false;
1638
- var runner = new $$AnimateRunner({
1819
+ runner.setHost({
1639
1820
  end: function() {
1640
1821
  endAnimations();
1641
1822
  },
@@ -1648,9 +1829,7 @@ var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {
1648
1829
  return runner;
1649
1830
 
1650
1831
  function onComplete(success) {
1651
- animationClosed = true;
1652
- applyOptions();
1653
- applyAnimationStyles(element, options);
1832
+ close(success);
1654
1833
  runner.complete(success);
1655
1834
  }
1656
1835
 
@@ -1870,6 +2049,7 @@ var NG_ANIMATE_PIN_DATA = '$ngAnimatePin';
1870
2049
  var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
1871
2050
  var PRE_DIGEST_STATE = 1;
1872
2051
  var RUNNING_STATE = 2;
2052
+ var ONE_SPACE = ' ';
1873
2053
 
1874
2054
  var rules = this.rules = {
1875
2055
  skip: [],
@@ -1877,6 +2057,29 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
1877
2057
  join: []
1878
2058
  };
1879
2059
 
2060
+ function makeTruthyCssClassMap(classString) {
2061
+ if (!classString) {
2062
+ return null;
2063
+ }
2064
+
2065
+ var keys = classString.split(ONE_SPACE);
2066
+ var map = Object.create(null);
2067
+
2068
+ forEach(keys, function(key) {
2069
+ map[key] = true;
2070
+ });
2071
+ return map;
2072
+ }
2073
+
2074
+ function hasMatchingClasses(newClassString, currentClassString) {
2075
+ if (newClassString && currentClassString) {
2076
+ var currentClassMap = makeTruthyCssClassMap(currentClassString);
2077
+ return newClassString.split(ONE_SPACE).some(function(className) {
2078
+ return currentClassMap[className];
2079
+ });
2080
+ }
2081
+ }
2082
+
1880
2083
  function isAllowed(ruleType, element, currentAnimation, previousAnimation) {
1881
2084
  return rules[ruleType].some(function(fn) {
1882
2085
  return fn(element, currentAnimation, previousAnimation);
@@ -1908,8 +2111,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
1908
2111
  });
1909
2112
 
1910
2113
  rules.skip.push(function(element, newAnimation, currentAnimation) {
1911
- // if there is a current animation then skip the class-based animation
1912
- return currentAnimation.structural && !newAnimation.structural;
2114
+ // if there is an ongoing current animation then don't even bother running the class-based animation
2115
+ return currentAnimation.structural && currentAnimation.state === RUNNING_STATE && !newAnimation.structural;
1913
2116
  });
1914
2117
 
1915
2118
  rules.cancel.push(function(element, newAnimation, currentAnimation) {
@@ -1924,23 +2127,48 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
1924
2127
  });
1925
2128
 
1926
2129
  rules.cancel.push(function(element, newAnimation, currentAnimation) {
1927
- var nO = newAnimation.options;
1928
- var cO = currentAnimation.options;
1929
2130
 
1930
- // if the exact same CSS class is added/removed then it's safe to cancel it
1931
- return (nO.addClass && nO.addClass === cO.removeClass) || (nO.removeClass && nO.removeClass === cO.addClass);
2131
+
2132
+ var nA = newAnimation.options.addClass;
2133
+ var nR = newAnimation.options.removeClass;
2134
+ var cA = currentAnimation.options.addClass;
2135
+ var cR = currentAnimation.options.removeClass;
2136
+
2137
+ // early detection to save the global CPU shortage :)
2138
+ if ((isUndefined(nA) && isUndefined(nR)) || (isUndefined(cA) && isUndefined(cR))) {
2139
+ return false;
2140
+ }
2141
+
2142
+ return (hasMatchingClasses(nA, cR)) || (hasMatchingClasses(nR, cA));
1932
2143
  });
1933
2144
 
1934
2145
  this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
1935
- '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite',
2146
+ '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow',
1936
2147
  function($$rAF, $rootScope, $rootElement, $document, $$HashMap,
1937
- $$animation, $$AnimateRunner, $templateRequest, $$jqLite) {
2148
+ $$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow) {
1938
2149
 
1939
2150
  var activeAnimationsLookup = new $$HashMap();
1940
2151
  var disabledElementsLookup = new $$HashMap();
1941
-
1942
2152
  var animationsEnabled = null;
1943
2153
 
2154
+ function postDigestTaskFactory() {
2155
+ var postDigestCalled = false;
2156
+ return function(fn) {
2157
+ // we only issue a call to postDigest before
2158
+ // it has first passed. This prevents any callbacks
2159
+ // from not firing once the animation has completed
2160
+ // since it will be out of the digest cycle.
2161
+ if (postDigestCalled) {
2162
+ fn();
2163
+ } else {
2164
+ $rootScope.$$postDigest(function() {
2165
+ postDigestCalled = true;
2166
+ fn();
2167
+ });
2168
+ }
2169
+ };
2170
+ }
2171
+
1944
2172
  // Wait until all directive and route-related templates are downloaded and
1945
2173
  // compiled. The $templateRequest.totalPendingRequests variable keeps track of
1946
2174
  // all of the remote templates being currently downloaded. If there are no
@@ -1970,8 +2198,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
1970
2198
  }
1971
2199
  );
1972
2200
 
1973
- var bodyElement = jqLite($document[0].body);
1974
-
1975
2201
  var callbackRegistry = {};
1976
2202
 
1977
2203
  // remember that the classNameFilter is set during the provider/config
@@ -1989,14 +2215,24 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
1989
2215
  return mergeAnimationOptions(element, options, {});
1990
2216
  }
1991
2217
 
1992
- function findCallbacks(element, event) {
2218
+ // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2219
+ var contains = Node.prototype.contains || function(arg) {
2220
+ // jshint bitwise: false
2221
+ return this === arg || !!(this.compareDocumentPosition(arg) & 16);
2222
+ // jshint bitwise: true
2223
+ };
2224
+
2225
+ function findCallbacks(parent, element, event) {
1993
2226
  var targetNode = getDomNode(element);
2227
+ var targetParentNode = getDomNode(parent);
1994
2228
 
1995
2229
  var matches = [];
1996
2230
  var entries = callbackRegistry[event];
1997
2231
  if (entries) {
1998
2232
  forEach(entries, function(entry) {
1999
- if (entry.node.contains(targetNode)) {
2233
+ if (contains.call(entry.node, targetNode)) {
2234
+ matches.push(entry.callback);
2235
+ } else if (event === 'leave' && contains.call(entry.node, targetParentNode)) {
2000
2236
  matches.push(entry.callback);
2001
2237
  }
2002
2238
  });
@@ -2005,14 +2241,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2005
2241
  return matches;
2006
2242
  }
2007
2243
 
2008
- function triggerCallback(event, element, phase, data) {
2009
- $$rAF(function() {
2010
- forEach(findCallbacks(element, event), function(callback) {
2011
- callback(element, phase, data);
2012
- });
2013
- });
2014
- }
2015
-
2016
2244
  return {
2017
2245
  on: function(event, container, callback) {
2018
2246
  var node = extractElementNode(container);
@@ -2079,12 +2307,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2079
2307
  bool = !recordExists;
2080
2308
  } else {
2081
2309
  // (element, bool) - Element setter
2082
- bool = !!bool;
2083
- if (!bool) {
2084
- disabledElementsLookup.put(node, true);
2085
- } else if (recordExists) {
2086
- disabledElementsLookup.remove(node);
2087
- }
2310
+ disabledElementsLookup.put(node, !bool);
2088
2311
  }
2089
2312
  }
2090
2313
  }
@@ -2093,7 +2316,12 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2093
2316
  }
2094
2317
  };
2095
2318
 
2096
- function queueAnimation(element, event, options) {
2319
+ function queueAnimation(element, event, initialOptions) {
2320
+ // we always make a copy of the options since
2321
+ // there should never be any side effects on
2322
+ // the input data when running `$animateCss`.
2323
+ var options = copy(initialOptions);
2324
+
2097
2325
  var node, parent;
2098
2326
  element = stripCommentsFromElement(element);
2099
2327
  if (element) {
@@ -2107,22 +2335,25 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2107
2335
  // These methods will become available after the digest has passed
2108
2336
  var runner = new $$AnimateRunner();
2109
2337
 
2110
- // there are situations where a directive issues an animation for
2111
- // a jqLite wrapper that contains only comment nodes... If this
2112
- // happens then there is no way we can perform an animation
2113
- if (!node) {
2114
- close();
2115
- return runner;
2116
- }
2338
+ // this is used to trigger callbacks in postDigest mode
2339
+ var runInNextPostDigestOrNow = postDigestTaskFactory();
2117
2340
 
2118
2341
  if (isArray(options.addClass)) {
2119
2342
  options.addClass = options.addClass.join(' ');
2120
2343
  }
2121
2344
 
2345
+ if (options.addClass && !isString(options.addClass)) {
2346
+ options.addClass = null;
2347
+ }
2348
+
2122
2349
  if (isArray(options.removeClass)) {
2123
2350
  options.removeClass = options.removeClass.join(' ');
2124
2351
  }
2125
2352
 
2353
+ if (options.removeClass && !isString(options.removeClass)) {
2354
+ options.removeClass = null;
2355
+ }
2356
+
2126
2357
  if (options.from && !isObject(options.from)) {
2127
2358
  options.from = null;
2128
2359
  }
@@ -2131,6 +2362,14 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2131
2362
  options.to = null;
2132
2363
  }
2133
2364
 
2365
+ // there are situations where a directive issues an animation for
2366
+ // a jqLite wrapper that contains only comment nodes... If this
2367
+ // happens then there is no way we can perform an animation
2368
+ if (!node) {
2369
+ close();
2370
+ return runner;
2371
+ }
2372
+
2134
2373
  var className = [node.className, options.addClass, options.removeClass].join(' ');
2135
2374
  if (!isAnimatableClassName(className)) {
2136
2375
  close();
@@ -2142,7 +2381,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2142
2381
  // this is a hard disable of all animations for the application or on
2143
2382
  // the element itself, therefore there is no need to continue further
2144
2383
  // past this point if not enabled
2145
- var skipAnimations = !animationsEnabled || disabledElementsLookup.get(node);
2384
+ // Animations are also disabled if the document is currently hidden (page is not visible
2385
+ // to the user), because browsers slow down or do not flush calls to requestAnimationFrame
2386
+ var skipAnimations = !animationsEnabled || $document[0].hidden || disabledElementsLookup.get(node);
2146
2387
  var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {};
2147
2388
  var hasExistingAnimation = !!existingAnimation.state;
2148
2389
 
@@ -2195,8 +2436,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2195
2436
  // method which will call the runner methods in async.
2196
2437
  existingAnimation.close();
2197
2438
  } else {
2198
- // this will merge the existing animation options into this new follow-up animation
2199
- mergeAnimationOptions(element, newAnimation.options, existingAnimation.options);
2439
+ // this will merge the new animation options into existing animation options
2440
+ mergeAnimationOptions(element, existingAnimation.options, newAnimation.options);
2441
+ return existingAnimation.runner;
2200
2442
  }
2201
2443
  } else {
2202
2444
  // a joined animation means that this animation will take over the existing one
@@ -2207,9 +2449,14 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2207
2449
  if (existingAnimation.state === RUNNING_STATE) {
2208
2450
  normalizeAnimationOptions(element, options);
2209
2451
  } else {
2452
+ applyGeneratedPreparationClasses(element, isStructural ? event : null, options);
2453
+
2210
2454
  event = newAnimation.event = existingAnimation.event;
2211
2455
  options = mergeAnimationOptions(element, existingAnimation.options, newAnimation.options);
2212
- return runner;
2456
+
2457
+ //we return the same runner since only the option values of this animation will
2458
+ //be fed into the `existingAnimation`.
2459
+ return existingAnimation.runner;
2213
2460
  }
2214
2461
  }
2215
2462
  }
@@ -2235,10 +2482,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2235
2482
  return runner;
2236
2483
  }
2237
2484
 
2238
- if (isStructural) {
2239
- closeParentClassBasedAnimations(parent);
2240
- }
2241
-
2242
2485
  // the counter keeps track of cancelled animations
2243
2486
  var counter = (existingAnimation.counter || 0) + 1;
2244
2487
  newAnimation.counter = counter;
@@ -2296,12 +2539,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2296
2539
  ? 'setClass'
2297
2540
  : animationDetails.event;
2298
2541
 
2299
- if (animationDetails.structural) {
2300
- closeParentClassBasedAnimations(parentElement);
2301
- }
2302
-
2303
2542
  markElementAnimationState(element, RUNNING_STATE);
2304
2543
  var realRunner = $$animation(element, event, animationDetails.options);
2544
+
2305
2545
  realRunner.done(function(status) {
2306
2546
  close(!status);
2307
2547
  var animationDetails = activeAnimationsLookup.get(node);
@@ -2320,11 +2560,25 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2320
2560
  return runner;
2321
2561
 
2322
2562
  function notifyProgress(runner, event, phase, data) {
2323
- triggerCallback(event, element, phase, data);
2563
+ runInNextPostDigestOrNow(function() {
2564
+ var callbacks = findCallbacks(parent, element, event);
2565
+ if (callbacks.length) {
2566
+ // do not optimize this call here to RAF because
2567
+ // we don't know how heavy the callback code here will
2568
+ // be and if this code is buffered then this can
2569
+ // lead to a performance regression.
2570
+ $$rAF(function() {
2571
+ forEach(callbacks, function(callback) {
2572
+ callback(element, phase, data);
2573
+ });
2574
+ });
2575
+ }
2576
+ });
2324
2577
  runner.progress(event, phase, data);
2325
2578
  }
2326
2579
 
2327
2580
  function close(reject) { // jshint ignore:line
2581
+ clearGeneratedClasses(element, options);
2328
2582
  applyAnimationClasses(element, options);
2329
2583
  applyAnimationStyles(element, options);
2330
2584
  options.domOperation();
@@ -2338,15 +2592,15 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2338
2592
  forEach(children, function(child) {
2339
2593
  var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME));
2340
2594
  var animationDetails = activeAnimationsLookup.get(child);
2341
- switch (state) {
2342
- case RUNNING_STATE:
2343
- animationDetails.runner.end();
2344
- /* falls through */
2345
- case PRE_DIGEST_STATE:
2346
- if (animationDetails) {
2595
+ if (animationDetails) {
2596
+ switch (state) {
2597
+ case RUNNING_STATE:
2598
+ animationDetails.runner.end();
2599
+ /* falls through */
2600
+ case PRE_DIGEST_STATE:
2347
2601
  activeAnimationsLookup.remove(child);
2348
- }
2349
- break;
2602
+ break;
2603
+ }
2350
2604
  }
2351
2605
  });
2352
2606
  }
@@ -2361,38 +2615,20 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2361
2615
  return getDomNode(nodeOrElmA) === getDomNode(nodeOrElmB);
2362
2616
  }
2363
2617
 
2364
- function closeParentClassBasedAnimations(startingElement) {
2365
- var parentNode = getDomNode(startingElement);
2366
- do {
2367
- if (!parentNode || parentNode.nodeType !== ELEMENT_NODE) break;
2368
-
2369
- var animationDetails = activeAnimationsLookup.get(parentNode);
2370
- if (animationDetails) {
2371
- examineParentAnimation(parentNode, animationDetails);
2372
- }
2373
-
2374
- parentNode = parentNode.parentNode;
2375
- } while (true);
2376
-
2377
- // since animations are detected from CSS classes, we need to flush all parent
2378
- // class-based animations so that the parent classes are all present for child
2379
- // animations to properly function (otherwise any CSS selectors may not work)
2380
- function examineParentAnimation(node, animationDetails) {
2381
- // enter/leave/move always have priority
2382
- if (animationDetails.structural || !hasAnimationClasses(animationDetails.options)) return;
2383
-
2384
- if (animationDetails.state === RUNNING_STATE) {
2385
- animationDetails.runner.end();
2386
- }
2387
- clearElementAnimationState(node);
2388
- }
2389
- }
2390
-
2618
+ /**
2619
+ * This fn returns false if any of the following is true:
2620
+ * a) animations on any parent element are disabled, and animations on the element aren't explicitly allowed
2621
+ * b) a parent element has an ongoing structural animation, and animateChildren is false
2622
+ * c) the element is not a child of the body
2623
+ * d) the element is not a child of the $rootElement
2624
+ */
2391
2625
  function areAnimationsAllowed(element, parentElement, event) {
2392
- var bodyElementDetected = false;
2393
- var rootElementDetected = false;
2626
+ var bodyElement = jqLite($document[0].body);
2627
+ var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';
2628
+ var rootElementDetected = isMatchingElement(element, $rootElement);
2394
2629
  var parentAnimationDetected = false;
2395
2630
  var animateChildren;
2631
+ var elementDisabled = disabledElementsLookup.get(getDomNode(element));
2396
2632
 
2397
2633
  var parentHost = element.data(NG_ANIMATE_PIN_DATA);
2398
2634
  if (parentHost) {
@@ -2417,7 +2653,18 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2417
2653
  // therefore we can't allow any animations to take place
2418
2654
  // but if a parent animation is class-based then that's ok
2419
2655
  if (!parentAnimationDetected) {
2420
- parentAnimationDetected = details.structural || disabledElementsLookup.get(parentNode);
2656
+ var parentElementDisabled = disabledElementsLookup.get(parentNode);
2657
+
2658
+ if (parentElementDisabled === true && elementDisabled !== false) {
2659
+ // disable animations if the user hasn't explicitly enabled animations on the
2660
+ // current element
2661
+ elementDisabled = true;
2662
+ // element is disabled via parent element, no need to check anything else
2663
+ break;
2664
+ } else if (parentElementDisabled === false) {
2665
+ elementDisabled = false;
2666
+ }
2667
+ parentAnimationDetected = details.structural;
2421
2668
  }
2422
2669
 
2423
2670
  if (isUndefined(animateChildren) || animateChildren === true) {
@@ -2430,28 +2677,32 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2430
2677
  // there is no need to continue traversing at this point
2431
2678
  if (parentAnimationDetected && animateChildren === false) break;
2432
2679
 
2433
- if (!rootElementDetected) {
2434
- // angular doesn't want to attempt to animate elements outside of the application
2435
- // therefore we need to ensure that the rootElement is an ancestor of the current element
2436
- rootElementDetected = isMatchingElement(parentElement, $rootElement);
2437
- if (!rootElementDetected) {
2438
- parentHost = parentElement.data(NG_ANIMATE_PIN_DATA);
2439
- if (parentHost) {
2440
- parentElement = parentHost;
2441
- }
2442
- }
2443
- }
2444
-
2445
2680
  if (!bodyElementDetected) {
2446
- // we also need to ensure that the element is or will be apart of the body element
2681
+ // we also need to ensure that the element is or will be a part of the body element
2447
2682
  // otherwise it is pointless to even issue an animation to be rendered
2448
2683
  bodyElementDetected = isMatchingElement(parentElement, bodyElement);
2449
2684
  }
2450
2685
 
2686
+ if (bodyElementDetected && rootElementDetected) {
2687
+ // If both body and root have been found, any other checks are pointless,
2688
+ // as no animation data should live outside the application
2689
+ break;
2690
+ }
2691
+
2692
+ if (!rootElementDetected) {
2693
+ // If no rootElement is detected, check if the parentElement is pinned to another element
2694
+ parentHost = parentElement.data(NG_ANIMATE_PIN_DATA);
2695
+ if (parentHost) {
2696
+ // The pin target element becomes the next parent element
2697
+ parentElement = parentHost;
2698
+ continue;
2699
+ }
2700
+ }
2701
+
2451
2702
  parentElement = parentElement.parent();
2452
2703
  }
2453
2704
 
2454
- var allowAnimation = !parentAnimationDetected || animateChildren;
2705
+ var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true;
2455
2706
  return allowAnimation && rootElementDetected && bodyElementDetected;
2456
2707
  }
2457
2708
 
@@ -2471,184 +2722,112 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
2471
2722
  }];
2472
2723
  }];
2473
2724
 
2474
- var $$rAFMutexFactory = ['$$rAF', function($$rAF) {
2475
- return function() {
2476
- var passed = false;
2477
- $$rAF(function() {
2478
- passed = true;
2479
- });
2480
- return function(fn) {
2481
- passed ? fn() : $$rAF(fn);
2482
- };
2483
- };
2484
- }];
2485
-
2486
- var $$AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAFMutex) {
2487
- var INITIAL_STATE = 0;
2488
- var DONE_PENDING_STATE = 1;
2489
- var DONE_COMPLETE_STATE = 2;
2490
-
2491
- AnimateRunner.chain = function(chain, callback) {
2492
- var index = 0;
2493
-
2494
- next();
2495
- function next() {
2496
- if (index === chain.length) {
2497
- callback(true);
2498
- return;
2499
- }
2500
-
2501
- chain[index](function(response) {
2502
- if (response === false) {
2503
- callback(false);
2504
- return;
2505
- }
2506
- index++;
2507
- next();
2508
- });
2509
- }
2510
- };
2725
+ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2726
+ var NG_ANIMATE_REF_ATTR = 'ng-animate-ref';
2511
2727
 
2512
- AnimateRunner.all = function(runners, callback) {
2513
- var count = 0;
2514
- var status = true;
2515
- forEach(runners, function(runner) {
2516
- runner.done(onProgress);
2517
- });
2728
+ var drivers = this.drivers = [];
2518
2729
 
2519
- function onProgress(response) {
2520
- status = status && response;
2521
- if (++count === runners.length) {
2522
- callback(status);
2523
- }
2524
- }
2525
- };
2730
+ var RUNNER_STORAGE_KEY = '$$animationRunner';
2526
2731
 
2527
- function AnimateRunner(host) {
2528
- this.setHost(host);
2732
+ function setRunner(element, runner) {
2733
+ element.data(RUNNER_STORAGE_KEY, runner);
2734
+ }
2529
2735
 
2530
- this._doneCallbacks = [];
2531
- this._runInAnimationFrame = $$rAFMutex();
2532
- this._state = 0;
2736
+ function removeRunner(element) {
2737
+ element.removeData(RUNNER_STORAGE_KEY);
2533
2738
  }
2534
2739
 
2535
- AnimateRunner.prototype = {
2536
- setHost: function(host) {
2537
- this.host = host || {};
2538
- },
2740
+ function getRunner(element) {
2741
+ return element.data(RUNNER_STORAGE_KEY);
2742
+ }
2539
2743
 
2540
- done: function(fn) {
2541
- if (this._state === DONE_COMPLETE_STATE) {
2542
- fn();
2543
- } else {
2544
- this._doneCallbacks.push(fn);
2545
- }
2546
- },
2744
+ this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler',
2745
+ function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$HashMap, $$rAFScheduler) {
2547
2746
 
2548
- progress: noop,
2747
+ var animationQueue = [];
2748
+ var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
2549
2749
 
2550
- getPromise: function() {
2551
- if (!this.promise) {
2552
- var self = this;
2553
- this.promise = $q(function(resolve, reject) {
2554
- self.done(function(status) {
2555
- status === false ? reject() : resolve();
2556
- });
2750
+ function sortAnimations(animations) {
2751
+ var tree = { children: [] };
2752
+ var i, lookup = new $$HashMap();
2753
+
2754
+ // this is done first beforehand so that the hashmap
2755
+ // is filled with a list of the elements that will be animated
2756
+ for (i = 0; i < animations.length; i++) {
2757
+ var animation = animations[i];
2758
+ lookup.put(animation.domNode, animations[i] = {
2759
+ domNode: animation.domNode,
2760
+ fn: animation.fn,
2761
+ children: []
2557
2762
  });
2558
2763
  }
2559
- return this.promise;
2560
- },
2561
-
2562
- then: function(resolveHandler, rejectHandler) {
2563
- return this.getPromise().then(resolveHandler, rejectHandler);
2564
- },
2565
-
2566
- 'catch': function(handler) {
2567
- return this.getPromise()['catch'](handler);
2568
- },
2569
-
2570
- 'finally': function(handler) {
2571
- return this.getPromise()['finally'](handler);
2572
- },
2573
2764
 
2574
- pause: function() {
2575
- if (this.host.pause) {
2576
- this.host.pause();
2765
+ for (i = 0; i < animations.length; i++) {
2766
+ processNode(animations[i]);
2577
2767
  }
2578
- },
2579
2768
 
2580
- resume: function() {
2581
- if (this.host.resume) {
2582
- this.host.resume();
2583
- }
2584
- },
2769
+ return flatten(tree);
2585
2770
 
2586
- end: function() {
2587
- if (this.host.end) {
2588
- this.host.end();
2589
- }
2590
- this._resolve(true);
2591
- },
2771
+ function processNode(entry) {
2772
+ if (entry.processed) return entry;
2773
+ entry.processed = true;
2592
2774
 
2593
- cancel: function() {
2594
- if (this.host.cancel) {
2595
- this.host.cancel();
2596
- }
2597
- this._resolve(false);
2598
- },
2775
+ var elementNode = entry.domNode;
2776
+ var parentNode = elementNode.parentNode;
2777
+ lookup.put(elementNode, entry);
2599
2778
 
2600
- complete: function(response) {
2601
- var self = this;
2602
- if (self._state === INITIAL_STATE) {
2603
- self._state = DONE_PENDING_STATE;
2604
- self._runInAnimationFrame(function() {
2605
- self._resolve(response);
2606
- });
2607
- }
2608
- },
2779
+ var parentEntry;
2780
+ while (parentNode) {
2781
+ parentEntry = lookup.get(parentNode);
2782
+ if (parentEntry) {
2783
+ if (!parentEntry.processed) {
2784
+ parentEntry = processNode(parentEntry);
2785
+ }
2786
+ break;
2787
+ }
2788
+ parentNode = parentNode.parentNode;
2789
+ }
2609
2790
 
2610
- _resolve: function(response) {
2611
- if (this._state !== DONE_COMPLETE_STATE) {
2612
- forEach(this._doneCallbacks, function(fn) {
2613
- fn(response);
2614
- });
2615
- this._doneCallbacks.length = 0;
2616
- this._state = DONE_COMPLETE_STATE;
2791
+ (parentEntry || tree).children.push(entry);
2792
+ return entry;
2617
2793
  }
2618
- }
2619
- };
2620
-
2621
- return AnimateRunner;
2622
- }];
2623
-
2624
- var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2625
- var NG_ANIMATE_REF_ATTR = 'ng-animate-ref';
2626
2794
 
2627
- var drivers = this.drivers = [];
2628
-
2629
- var RUNNER_STORAGE_KEY = '$$animationRunner';
2630
-
2631
- function setRunner(element, runner) {
2632
- element.data(RUNNER_STORAGE_KEY, runner);
2633
- }
2795
+ function flatten(tree) {
2796
+ var result = [];
2797
+ var queue = [];
2798
+ var i;
2634
2799
 
2635
- function removeRunner(element) {
2636
- element.removeData(RUNNER_STORAGE_KEY);
2637
- }
2638
-
2639
- function getRunner(element) {
2640
- return element.data(RUNNER_STORAGE_KEY);
2641
- }
2800
+ for (i = 0; i < tree.children.length; i++) {
2801
+ queue.push(tree.children[i]);
2802
+ }
2642
2803
 
2643
- this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$rAFScheduler',
2644
- function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$rAFScheduler) {
2804
+ var remainingLevelEntries = queue.length;
2805
+ var nextLevelEntries = 0;
2806
+ var row = [];
2807
+
2808
+ for (i = 0; i < queue.length; i++) {
2809
+ var entry = queue[i];
2810
+ if (remainingLevelEntries <= 0) {
2811
+ remainingLevelEntries = nextLevelEntries;
2812
+ nextLevelEntries = 0;
2813
+ result.push(row);
2814
+ row = [];
2815
+ }
2816
+ row.push(entry.fn);
2817
+ entry.children.forEach(function(childEntry) {
2818
+ nextLevelEntries++;
2819
+ queue.push(childEntry);
2820
+ });
2821
+ remainingLevelEntries--;
2822
+ }
2645
2823
 
2646
- var animationQueue = [];
2647
- var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
2824
+ if (row.length) {
2825
+ result.push(row);
2826
+ }
2648
2827
 
2649
- var totalPendingClassBasedAnimations = 0;
2650
- var totalActiveClassBasedAnimations = 0;
2651
- var classBasedAnimationsQueue = [];
2828
+ return result;
2829
+ }
2830
+ }
2652
2831
 
2653
2832
  // TODO(matsko): document the signature in a better way
2654
2833
  return function(element, event, options) {
@@ -2678,19 +2857,12 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2678
2857
  options.tempClasses = null;
2679
2858
  }
2680
2859
 
2681
- var classBasedIndex;
2682
- if (!isStructural) {
2683
- classBasedIndex = totalPendingClassBasedAnimations;
2684
- totalPendingClassBasedAnimations += 1;
2685
- }
2686
-
2687
2860
  animationQueue.push({
2688
2861
  // this data is used by the postDigest code and passed into
2689
2862
  // the driver step function
2690
2863
  element: element,
2691
2864
  classes: classes,
2692
2865
  event: event,
2693
- classBasedIndex: classBasedIndex,
2694
2866
  structural: isStructural,
2695
2867
  options: options,
2696
2868
  beforeStart: beforeStart,
@@ -2705,10 +2877,6 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2705
2877
  if (animationQueue.length > 1) return runner;
2706
2878
 
2707
2879
  $rootScope.$$postDigest(function() {
2708
- totalActiveClassBasedAnimations = totalPendingClassBasedAnimations;
2709
- totalPendingClassBasedAnimations = 0;
2710
- classBasedAnimationsQueue.length = 0;
2711
-
2712
2880
  var animations = [];
2713
2881
  forEach(animationQueue, function(entry) {
2714
2882
  // the element was destroyed early on which removed the runner
@@ -2716,67 +2884,58 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2716
2884
  // at all and it already has been closed due to destruction.
2717
2885
  if (getRunner(entry.element)) {
2718
2886
  animations.push(entry);
2887
+ } else {
2888
+ entry.close();
2719
2889
  }
2720
2890
  });
2721
2891
 
2722
2892
  // now any future animations will be in another postDigest
2723
2893
  animationQueue.length = 0;
2724
2894
 
2725
- forEach(groupAnimations(animations), function(animationEntry) {
2726
- if (animationEntry.structural) {
2727
- triggerAnimationStart();
2728
- } else {
2729
- classBasedAnimationsQueue.push({
2730
- node: getDomNode(animationEntry.element),
2731
- fn: triggerAnimationStart
2732
- });
2733
-
2734
- if (animationEntry.classBasedIndex === totalActiveClassBasedAnimations - 1) {
2735
- // we need to sort each of the animations in order of parent to child
2736
- // relationships. This ensures that the child classes are applied at the
2737
- // right time.
2738
- classBasedAnimationsQueue = classBasedAnimationsQueue.sort(function(a,b) {
2739
- return b.node.contains(a.node);
2740
- }).map(function(entry) {
2741
- return entry.fn;
2742
- });
2743
-
2744
- $$rAFScheduler(classBasedAnimationsQueue);
2745
- }
2746
- }
2747
-
2748
- function triggerAnimationStart() {
2749
- // it's important that we apply the `ng-animate` CSS class and the
2750
- // temporary classes before we do any driver invoking since these
2751
- // CSS classes may be required for proper CSS detection.
2752
- animationEntry.beforeStart();
2753
-
2754
- var startAnimationFn, closeFn = animationEntry.close;
2755
-
2756
- // in the event that the element was removed before the digest runs or
2757
- // during the RAF sequencing then we should not trigger the animation.
2758
- var targetElement = animationEntry.anchors
2759
- ? (animationEntry.from.element || animationEntry.to.element)
2760
- : animationEntry.element;
2761
-
2762
- if (getRunner(targetElement) && getDomNode(targetElement).parentNode) {
2763
- var operation = invokeFirstDriver(animationEntry);
2764
- if (operation) {
2765
- startAnimationFn = operation.start;
2895
+ var groupedAnimations = groupAnimations(animations);
2896
+ var toBeSortedAnimations = [];
2897
+
2898
+ forEach(groupedAnimations, function(animationEntry) {
2899
+ toBeSortedAnimations.push({
2900
+ domNode: getDomNode(animationEntry.from ? animationEntry.from.element : animationEntry.element),
2901
+ fn: function triggerAnimationStart() {
2902
+ // it's important that we apply the `ng-animate` CSS class and the
2903
+ // temporary classes before we do any driver invoking since these
2904
+ // CSS classes may be required for proper CSS detection.
2905
+ animationEntry.beforeStart();
2906
+
2907
+ var startAnimationFn, closeFn = animationEntry.close;
2908
+
2909
+ // in the event that the element was removed before the digest runs or
2910
+ // during the RAF sequencing then we should not trigger the animation.
2911
+ var targetElement = animationEntry.anchors
2912
+ ? (animationEntry.from.element || animationEntry.to.element)
2913
+ : animationEntry.element;
2914
+
2915
+ if (getRunner(targetElement)) {
2916
+ var operation = invokeFirstDriver(animationEntry);
2917
+ if (operation) {
2918
+ startAnimationFn = operation.start;
2919
+ }
2766
2920
  }
2767
- }
2768
2921
 
2769
- if (!startAnimationFn) {
2770
- closeFn();
2771
- } else {
2772
- var animationRunner = startAnimationFn();
2773
- animationRunner.done(function(status) {
2774
- closeFn(!status);
2775
- });
2776
- updateAnimationRunners(animationEntry, animationRunner);
2922
+ if (!startAnimationFn) {
2923
+ closeFn();
2924
+ } else {
2925
+ var animationRunner = startAnimationFn();
2926
+ animationRunner.done(function(status) {
2927
+ closeFn(!status);
2928
+ });
2929
+ updateAnimationRunners(animationEntry, animationRunner);
2930
+ }
2777
2931
  }
2778
- }
2932
+ });
2779
2933
  });
2934
+
2935
+ // we need to sort each of the animations in order of parent to child
2936
+ // relationships. This ensures that the child classes are applied at the
2937
+ // right time.
2938
+ $$rAFScheduler(sortAnimations(toBeSortedAnimations));
2780
2939
  });
2781
2940
 
2782
2941
  return runner;
@@ -2963,10 +3122,9 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2963
3122
 
2964
3123
  /* global angularAnimateModule: true,
2965
3124
 
2966
- $$rAFMutexFactory,
3125
+ $$AnimateAsyncRunFactory,
2967
3126
  $$rAFSchedulerFactory,
2968
3127
  $$AnimateChildrenDirective,
2969
- $$AnimateRunnerFactory,
2970
3128
  $$AnimateQueueProvider,
2971
3129
  $$AnimationProvider,
2972
3130
  $AnimateCssProvider,
@@ -2981,7 +3139,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
2981
3139
  * @description
2982
3140
  *
2983
3141
  * The `ngAnimate` module provides support for CSS-based animations (keyframes and transitions) as well as JavaScript-based animations via
2984
- * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` then the animation hooks are enabled for an Angular app.
3142
+ * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` the animation hooks are enabled for an Angular app.
2985
3143
  *
2986
3144
  * <div doc-module-components="ngAnimate"></div>
2987
3145
  *
@@ -3014,7 +3172,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3014
3172
  * CSS-based animations with ngAnimate are unique since they require no JavaScript code at all. By using a CSS class that we reference between our HTML
3015
3173
  * and CSS code we can create an animation that will be picked up by Angular when an the underlying directive performs an operation.
3016
3174
  *
3017
- * The example below shows how an `enter` animation can be made possible on a element using `ng-if`:
3175
+ * The example below shows how an `enter` animation can be made possible on an element using `ng-if`:
3018
3176
  *
3019
3177
  * ```html
3020
3178
  * <div ng-if="bool" class="fade">
@@ -3149,8 +3307,8 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3149
3307
  * /&#42; this will have a 100ms delay between each successive leave animation &#42;/
3150
3308
  * transition-delay: 0.1s;
3151
3309
  *
3152
- * /&#42; in case the stagger doesn't work then the duration value
3153
- * must be set to 0 to avoid an accidental CSS inheritance &#42;/
3310
+ * /&#42; As of 1.4.4, this must always be set: it signals ngAnimate
3311
+ * to not accidentally inherit a delay property from another CSS class &#42;/
3154
3312
  * transition-duration: 0s;
3155
3313
  * }
3156
3314
  * .my-animation.ng-enter.ng-enter-active {
@@ -3251,7 +3409,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3251
3409
  * jQuery(element).fadeOut(1000, doneFn);
3252
3410
  * }
3253
3411
  * }
3254
- * }]
3412
+ * }]);
3255
3413
  * ```
3256
3414
  *
3257
3415
  * The nice thing about JS-based animations is that we can inject other services and make use of advanced animation libraries such as
@@ -3282,7 +3440,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3282
3440
  * // do some cool animation and call the doneFn
3283
3441
  * }
3284
3442
  * }
3285
- * }]
3443
+ * }]);
3286
3444
  * ```
3287
3445
  *
3288
3446
  * ## CSS + JS Animations Together
@@ -3304,7 +3462,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3304
3462
  * jQuery(element).slideIn(1000, doneFn);
3305
3463
  * }
3306
3464
  * }
3307
- * }]
3465
+ * }]);
3308
3466
  * ```
3309
3467
  *
3310
3468
  * ```css
@@ -3324,16 +3482,15 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3324
3482
  * ```js
3325
3483
  * myModule.animation('.slide', ['$animateCss', function($animateCss) {
3326
3484
  * return {
3327
- * enter: function(element, doneFn) {
3485
+ * enter: function(element) {
3328
3486
  * // this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`.
3329
- * var runner = $animateCss(element, {
3487
+ * return $animateCss(element, {
3330
3488
  * event: 'enter',
3331
3489
  * structural: true
3332
- * }).start();
3333
- * runner.done(doneFn);
3490
+ * });
3334
3491
  * }
3335
3492
  * }
3336
- * }]
3493
+ * }]);
3337
3494
  * ```
3338
3495
  *
3339
3496
  * The nice thing here is that we can save bandwidth by sticking to our CSS-based animation code and we don't need to rely on a 3rd-party animation framework.
@@ -3345,18 +3502,17 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3345
3502
  * ```js
3346
3503
  * myModule.animation('.slide', ['$animateCss', function($animateCss) {
3347
3504
  * return {
3348
- * enter: function(element, doneFn) {
3349
- * var runner = $animateCss(element, {
3505
+ * enter: function(element) {
3506
+ * return $animateCss(element, {
3350
3507
  * event: 'enter',
3508
+ * structural: true,
3351
3509
  * addClass: 'maroon-setting',
3352
3510
  * from: { height:0 },
3353
3511
  * to: { height: 200 }
3354
- * }).start();
3355
- *
3356
- * runner.done(doneFn);
3512
+ * });
3357
3513
  * }
3358
3514
  * }
3359
- * }]
3515
+ * }]);
3360
3516
  * ```
3361
3517
  *
3362
3518
  * Now we can fill in the rest via our transition CSS code:
@@ -3698,16 +3854,12 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
3698
3854
  * @description
3699
3855
  * The ngAnimate `$animate` service documentation is the same for the core `$animate` service.
3700
3856
  *
3701
- * Click here {@link ng.$animate $animate to learn more about animations with `$animate`}.
3857
+ * Click here {@link ng.$animate to learn more about animations with `$animate`}.
3702
3858
  */
3703
3859
  angular.module('ngAnimate', [])
3704
3860
  .directive('ngAnimateChildren', $$AnimateChildrenDirective)
3705
-
3706
- .factory('$$rAFMutex', $$rAFMutexFactory)
3707
3861
  .factory('$$rAFScheduler', $$rAFSchedulerFactory)
3708
3862
 
3709
- .factory('$$AnimateRunner', $$AnimateRunnerFactory)
3710
-
3711
3863
  .provider('$$animateQueue', $$AnimateQueueProvider)
3712
3864
  .provider('$$animation', $$AnimationProvider)
3713
3865