angularjs-rails 1.2.0.rc2 → 1.2.0.rc3

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: 60c24156d821e8f99fe877451cabfdb3da9a3840
4
- data.tar.gz: da2c799f76324b0b429dc3b3f9122a3223ef9ec5
3
+ metadata.gz: 5310c15961be67da6400b9e92357d2df624a877e
4
+ data.tar.gz: 7e245f7dbdd0374e0a878245d6222c8e960f1906
5
5
  SHA512:
6
- metadata.gz: cd335cd66257312b2975b59ff0e5d3f3e3cf4f58dd32ed144878d8f996634613431b7b1a6799195415368d1c69fe7df9b4440f422284868d114817e697a5aefd
7
- data.tar.gz: 65630c189cbf97e07f998e8db828927cb40338dc667de06ad27900fc4b79a8886f264e6dc53d7dc330aa688469f6bfcc0315a06f98e028d998755b848df07434
6
+ metadata.gz: 8a714cf4a158029a91236fce46489d04e94f07ed56bc7219ea7dc58199f42b7737d39c160dce3557c0d0e66c821c05fb8ff0e8f0b41e1948e7f61e29cb5593f7
7
+ data.tar.gz: 11c319302e864cd8e1f7b0ef977443ac14cecb7be116e5e617d2f9ebdacf7d2acf400eed06d57519c4d3ae7c6f57eaae1e884f955aef45ecd2d16803cec474fe
data/README.md CHANGED
@@ -14,7 +14,7 @@ Add the following directive to your JavaScript manifest file (application.js):
14
14
 
15
15
  If you desire to require (optional) Angular files, you may include them as well in your JavaScript manifest file (application.js). For example:
16
16
 
17
- //= require angular-bootstrap
17
+ //= require angular-animate
18
18
  //= require angular-resource
19
19
 
20
20
  To use the 'unstable' branch, add the following directive to your JavaScript manifest file (application.js):
@@ -1,5 +1,6 @@
1
1
  module AngularJS
2
2
  module Rails
3
- VERSION = "1.2.0.rc2"
3
+ VERSION = "1.2.0.rc3"
4
+ UNSTABLE_VERSION = "1.1.5"
4
5
  end
5
6
  end
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license AngularJS v1.2.0-rc.2
2
+ * @license AngularJS v1.2.0-rc.3
3
3
  * (c) 2010-2012 Google, Inc. http://angularjs.org
4
4
  * License: MIT
5
5
  */
@@ -32,7 +32,7 @@
32
32
  * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
33
33
  * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
34
34
  * | {@link ng.directive:ngIf#animations ngIf} | enter and leave |
35
- * | {@link ng.directive:ngShow#animations ngClass} | add and remove |
35
+ * | {@link ng.directive:ngClass#animations ngClass} | add and remove |
36
36
  * | {@link ng.directive:ngShow#animations ngShow & ngHide} | add and remove (the ng-hide class value) |
37
37
  *
38
38
  * You can find out more information about animations upon visiting each directive page.
@@ -207,6 +207,7 @@ angular.module('ngAnimate', ['ng'])
207
207
  var selectors = $animateProvider.$$selectors;
208
208
 
209
209
  var NG_ANIMATE_STATE = '$$ngAnimateState';
210
+ var NG_ANIMATE_CLASS_NAME = 'ng-animate';
210
211
  var rootAnimateState = {running:true};
211
212
  $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope',
212
213
  function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope) {
@@ -222,8 +223,11 @@ angular.module('ngAnimate', ['ng'])
222
223
  //the empty string value is the default animation
223
224
  //operation which performs CSS transition and keyframe
224
225
  //animations sniffing. This is always included for each
225
- //element animation procedure
226
- classes.push('');
226
+ //element animation procedure if the browser supports
227
+ //transitions and/or keyframe animations
228
+ if ($sniffer.transitions || $sniffer.animations) {
229
+ classes.push('');
230
+ }
227
231
 
228
232
  for(var i=0; i < classes.length; i++) {
229
233
  var klass = classes[i],
@@ -288,6 +292,7 @@ angular.module('ngAnimate', ['ng'])
288
292
  * @param {function()=} done callback function that will be called once the animation is complete
289
293
  */
290
294
  enter : function(element, parent, after, done) {
295
+ this.enabled(false, element);
291
296
  $delegate.enter(element, parent, after);
292
297
  $rootScope.$$postDigest(function() {
293
298
  performAnimation('enter', 'ng-enter', element, parent, after, function() {
@@ -324,6 +329,8 @@ angular.module('ngAnimate', ['ng'])
324
329
  * @param {function()=} done callback function that will be called once the animation is complete
325
330
  */
326
331
  leave : function(element, done) {
332
+ cancelChildAnimations(element);
333
+ this.enabled(false, element);
327
334
  $rootScope.$$postDigest(function() {
328
335
  performAnimation('leave', 'ng-leave', element, null, null, function() {
329
336
  $delegate.leave(element, done);
@@ -362,6 +369,8 @@ angular.module('ngAnimate', ['ng'])
362
369
  * @param {function()=} done callback function that will be called once the animation is complete
363
370
  */
364
371
  move : function(element, parent, after, done) {
372
+ cancelChildAnimations(element);
373
+ this.enabled(false, element);
365
374
  $delegate.move(element, parent, after);
366
375
  $rootScope.$$postDigest(function() {
367
376
  performAnimation('move', 'ng-move', element, null, null, function() {
@@ -452,12 +461,30 @@ angular.module('ngAnimate', ['ng'])
452
461
  * Globally enables/disables animations.
453
462
  *
454
463
  */
455
- enabled : function(value) {
456
- if (arguments.length) {
457
- rootAnimateState.running = !value;
464
+ enabled : function(value, element) {
465
+ switch(arguments.length) {
466
+ case 2:
467
+ if(value) {
468
+ cleanup(element);
469
+ }
470
+ else {
471
+ var data = element.data(NG_ANIMATE_STATE) || {};
472
+ data.structural = true;
473
+ data.running = true;
474
+ element.data(NG_ANIMATE_STATE, data);
475
+ }
476
+ break;
477
+
478
+ case 1:
479
+ rootAnimateState.running = !value;
480
+ break;
481
+
482
+ default:
483
+ value = !rootAnimateState.running
484
+ break;
458
485
  }
459
- return !rootAnimateState.running;
460
- }
486
+ return !!value;
487
+ }
461
488
  };
462
489
 
463
490
  /*
@@ -484,52 +511,52 @@ angular.module('ngAnimate', ['ng'])
484
511
 
485
512
  //skip the animation if animations are disabled, a parent is already being animated
486
513
  //or the element is not currently attached to the document body.
487
- if ((parent.inheritedData(NG_ANIMATE_STATE) || disabledAnimation).running) {
488
- //avoid calling done() since there is no need to remove any
489
- //data or className values since this happens earlier than that
490
- //and also use a timeout so that it won't be asynchronous
491
- $timeout(onComplete || noop, 0, false);
514
+ if ((parent.inheritedData(NG_ANIMATE_STATE) || disabledAnimation).running || animations.length == 0) {
515
+ done();
492
516
  return;
493
517
  }
494
518
 
495
519
  var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
496
520
 
497
- //if an animation is currently running on the element then lets take the steps
498
- //to cancel that animation and fire any required callbacks
521
+ var isClassBased = event == 'addClass' || event == 'removeClass';
499
522
  if(ngAnimateState.running) {
523
+ if(isClassBased && ngAnimateState.structural) {
524
+ onComplete && onComplete();
525
+ return;
526
+ }
527
+
528
+ //if an animation is currently running on the element then lets take the steps
529
+ //to cancel that animation and fire any required callbacks
530
+ $timeout.cancel(ngAnimateState.flagTimer);
500
531
  cancelAnimations(ngAnimateState.animations);
501
- ngAnimateState.done();
532
+ (ngAnimateState.done || noop)();
502
533
  }
503
534
 
504
535
  element.data(NG_ANIMATE_STATE, {
505
536
  running:true,
537
+ structural:!isClassBased,
506
538
  animations:animations,
507
539
  done:done
508
540
  });
509
541
 
542
+ //the ng-animate class does nothing, but it's here to allow for
543
+ //parent animations to find and cancel child animations when needed
544
+ element.addClass(NG_ANIMATE_CLASS_NAME);
545
+
510
546
  forEach(animations, function(animation, index) {
511
547
  var fn = function() {
512
548
  progress(index);
513
549
  };
514
550
 
515
551
  if(animation.start) {
516
- if(event == 'addClass' || event == 'removeClass') {
517
- animation.endFn = animation.start(element, className, fn);
518
- } else {
519
- animation.endFn = animation.start(element, fn);
520
- }
552
+ animation.endFn = isClassBased ?
553
+ animation.start(element, className, fn) :
554
+ animation.start(element, fn);
521
555
  } else {
522
556
  fn();
523
557
  }
524
558
  });
525
559
 
526
- function cancelAnimations(animations) {
527
- var isCancelledFlag = true;
528
- forEach(animations, function(animation) {
529
- (animation.endFn || noop)(isCancelledFlag);
530
- });
531
- }
532
-
533
560
  function progress(index) {
534
561
  animations[index].done = true;
535
562
  (animations[index].endFn || noop)();
@@ -542,118 +569,218 @@ angular.module('ngAnimate', ['ng'])
542
569
  function done() {
543
570
  if(!done.hasBeenRun) {
544
571
  done.hasBeenRun = true;
545
- element.removeData(NG_ANIMATE_STATE);
572
+ var data = element.data(NG_ANIMATE_STATE);
573
+ if(data) {
574
+ /* only structural animations wait for reflow before removing an
575
+ animation, but class-based animations don't. An example of this
576
+ failing would be when a parent HTML tag has a ng-class attribute
577
+ causing ALL directives below to skip animations during the digest */
578
+ if(isClassBased) {
579
+ cleanup(element);
580
+ } else {
581
+ data.flagTimer = $timeout(function() {
582
+ cleanup(element);
583
+ }, 0, false);
584
+ element.data(NG_ANIMATE_STATE, data);
585
+ }
586
+ }
546
587
  (onComplete || noop)();
547
588
  }
548
589
  }
549
590
  }
591
+
592
+ function cancelChildAnimations(element) {
593
+ angular.forEach(element[0].querySelectorAll('.' + NG_ANIMATE_CLASS_NAME), function(element) {
594
+ element = angular.element(element);
595
+ var data = element.data(NG_ANIMATE_STATE);
596
+ if(data) {
597
+ cancelAnimations(data.animations);
598
+ cleanup(element);
599
+ }
600
+ });
601
+ }
602
+
603
+ function cancelAnimations(animations) {
604
+ var isCancelledFlag = true;
605
+ forEach(animations, function(animation) {
606
+ (animation.endFn || noop)(isCancelledFlag);
607
+ });
608
+ }
609
+
610
+ function cleanup(element) {
611
+ element.removeClass(NG_ANIMATE_CLASS_NAME);
612
+ element.removeData(NG_ANIMATE_STATE);
613
+ }
550
614
  }]);
551
615
 
552
- $animateProvider.register('', ['$window','$sniffer', '$timeout', function($window, $sniffer, $timeout) {
553
- var noop = angular.noop;
616
+ $animateProvider.register('', ['$window', '$sniffer', '$timeout', function($window, $sniffer, $timeout) {
554
617
  var forEach = angular.forEach;
555
618
 
556
- //one day all browsers will have these properties
557
- var w3cAnimationProp = 'animation';
558
- var w3cTransitionProp = 'transition';
619
+ // Detect proper transitionend/animationend event names.
620
+ var transitionProp, transitionendEvent, animationProp, animationendEvent;
621
+
622
+ // If unprefixed events are not supported but webkit-prefixed are, use the latter.
623
+ // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.
624
+ // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`
625
+ // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.
626
+ // Register both events in case `window.onanimationend` is not supported because of that,
627
+ // do the same for `transitionend` as Safari is likely to exhibit similar behavior.
628
+ // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
629
+ // therefore there is no reason to test anymore for other vendor prefixes: http://caniuse.com/#search=transition
630
+ if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) {
631
+ transitionProp = 'WebkitTransition';
632
+ transitionendEvent = 'webkitTransitionEnd transitionend';
633
+ } else {
634
+ transitionProp = 'transition';
635
+ transitionendEvent = 'transitionend';
636
+ }
559
637
 
560
- //but some still use vendor-prefixed styles
561
- var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
562
- var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
638
+ if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) {
639
+ animationProp = 'WebkitAnimation';
640
+ animationendEvent = 'webkitAnimationEnd animationend';
641
+ } else {
642
+ animationProp = 'animation';
643
+ animationendEvent = 'animationend';
644
+ }
563
645
 
564
646
  var durationKey = 'Duration',
565
- delayKey = 'Delay',
566
647
  propertyKey = 'Property',
648
+ delayKey = 'Delay',
567
649
  animationIterationCountKey = 'IterationCount',
568
650
  ELEMENT_NODE = 1;
569
651
 
570
- function animate(element, className, done) {
571
- if (!($sniffer.transitions || $sniffer.animations)) {
572
- done();
573
- return;
574
- }
575
- else if(['ng-enter','ng-leave','ng-move'].indexOf(className) == -1) {
576
- var existingDuration = 0;
652
+ var NG_ANIMATE_PARENT_KEY = '$ngAnimateKey';
653
+ var lookupCache = {};
654
+ var parentCounter = 0;
655
+
656
+ var animationReflowQueue = [], animationTimer, timeOut = false;
657
+ function afterReflow(callback) {
658
+ animationReflowQueue.push(callback);
659
+ $timeout.cancel(animationTimer);
660
+ animationTimer = $timeout(function() {
661
+ angular.forEach(animationReflowQueue, function(fn) {
662
+ fn();
663
+ });
664
+ animationReflowQueue = [];
665
+ animationTimer = null;
666
+ lookupCache = {};
667
+ }, 10, false);
668
+ }
669
+
670
+ function getElementAnimationDetails(element, cacheKey, onlyCheckTransition) {
671
+ var data = lookupCache[cacheKey];
672
+ if(!data) {
673
+ var transitionDuration = 0, transitionDelay = 0,
674
+ animationDuration = 0, animationDelay = 0;
675
+
676
+ //we want all the styles defined before and after
577
677
  forEach(element, function(element) {
578
678
  if (element.nodeType == ELEMENT_NODE) {
579
679
  var elementStyles = $window.getComputedStyle(element) || {};
580
- existingDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
581
- parseMaxTime(elementStyles[vendorTransitionProp + durationKey]),
582
- existingDuration);
680
+
681
+ transitionDuration = Math.max(parseMaxTime(elementStyles[transitionProp + durationKey]), transitionDuration);
682
+
683
+ if(!onlyCheckTransition) {
684
+ transitionDelay = Math.max(parseMaxTime(elementStyles[transitionProp + delayKey]), transitionDelay);
685
+
686
+ animationDelay = Math.max(parseMaxTime(elementStyles[animationProp + delayKey]), animationDelay);
687
+
688
+ var aDuration = parseMaxTime(elementStyles[animationProp + durationKey]);
689
+
690
+ if(aDuration > 0) {
691
+ aDuration *= parseInt(elementStyles[animationProp + animationIterationCountKey]) || 1;
692
+ }
693
+
694
+ animationDuration = Math.max(aDuration, animationDuration);
695
+ }
583
696
  }
584
697
  });
585
- if(existingDuration > 0) {
586
- done();
587
- return;
588
- }
698
+ data = {
699
+ transitionDelay : transitionDelay,
700
+ animationDelay : animationDelay,
701
+ transitionDuration : transitionDuration,
702
+ animationDuration : animationDuration
703
+ };
704
+ lookupCache[cacheKey] = data;
589
705
  }
706
+ return data;
707
+ }
590
708
 
591
- element.addClass(className);
592
-
593
- //we want all the styles defined before and after
594
- var duration = 0;
595
- forEach(element, function(element) {
596
- if (element.nodeType == ELEMENT_NODE) {
597
- var elementStyles = $window.getComputedStyle(element) || {};
709
+ function parseMaxTime(str) {
710
+ var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : [];
711
+ forEach(values, function(value) {
712
+ total = Math.max(parseFloat(value) || 0, total);
713
+ });
714
+ return total;
715
+ }
598
716
 
599
- var transitionDelay = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + delayKey]),
600
- parseMaxTime(elementStyles[vendorTransitionProp + delayKey]));
717
+ function getCacheKey(element) {
718
+ var parent = element.parent();
719
+ var parentID = parent.data(NG_ANIMATE_PARENT_KEY);
720
+ if(!parentID) {
721
+ parent.data(NG_ANIMATE_PARENT_KEY, ++parentCounter);
722
+ parentID = parentCounter;
723
+ }
724
+ return parentID + '-' + element[0].className;
725
+ }
601
726
 
602
- var animationDelay = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + delayKey]),
603
- parseMaxTime(elementStyles[vendorAnimationProp + delayKey]));
727
+ function animate(element, className, done) {
604
728
 
605
- var transitionDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
606
- parseMaxTime(elementStyles[vendorTransitionProp + durationKey]));
729
+ var cacheKey = getCacheKey(element);
730
+ if(getElementAnimationDetails(element, cacheKey, true).transitionDuration > 0) {
607
731
 
608
- var animationDuration = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + durationKey]),
609
- parseMaxTime(elementStyles[vendorAnimationProp + durationKey]));
732
+ done();
733
+ return;
734
+ }
610
735
 
611
- if(animationDuration > 0) {
612
- animationDuration *= Math.max(parseInt(elementStyles[w3cAnimationProp + animationIterationCountKey]) || 0,
613
- parseInt(elementStyles[vendorAnimationProp + animationIterationCountKey]) || 0,
614
- 1);
615
- }
736
+ element.addClass(className);
616
737
 
617
- duration = Math.max(animationDelay + animationDuration,
618
- transitionDelay + transitionDuration,
619
- duration);
620
- }
621
- });
738
+ var timings = getElementAnimationDetails(element, cacheKey + ' ' + className);
622
739
 
623
740
  /* there is no point in performing a reflow if the animation
624
741
  timeout is empty (this would cause a flicker bug normally
625
- in the page */
626
- if(duration > 0) {
627
- var node = element[0];
742
+ in the page. There is also no point in performing an animation
743
+ that only has a delay and no duration */
744
+ var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
745
+ if(maxDuration > 0) {
746
+ var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * 1000,
747
+ startTime = Date.now(),
748
+ node = element[0];
628
749
 
629
750
  //temporarily disable the transition so that the enter styles
630
751
  //don't animate twice (this is here to avoid a bug in Chrome/FF).
631
- node.style[w3cTransitionProp + propertyKey] = 'none';
632
- node.style[vendorTransitionProp + propertyKey] = 'none';
752
+ if(timings.transitionDuration > 0) {
753
+ node.style[transitionProp + propertyKey] = 'none';
754
+ }
633
755
 
634
756
  var activeClassName = '';
635
757
  forEach(className.split(' '), function(klass, i) {
636
758
  activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
637
759
  });
638
760
 
639
- //this triggers a reflow which allows for the transition animation to kick in
640
- element.prop('clientWidth');
641
- node.style[w3cTransitionProp + propertyKey] = '';
642
- node.style[vendorTransitionProp + propertyKey] = '';
643
- element.addClass(activeClassName);
761
+ // This triggers a reflow which allows for the transition animation to kick in.
762
+ var css3AnimationEvents = animationendEvent + ' ' + transitionendEvent;
763
+
764
+ afterReflow(function() {
765
+ if(timings.transitionDuration > 0) {
766
+ node.style[transitionProp + propertyKey] = '';
767
+ }
768
+ element.addClass(activeClassName);
769
+ });
644
770
 
645
- $timeout(done, duration * 1000, false);
771
+ element.on(css3AnimationEvents, onAnimationProgress);
646
772
 
647
- //this will automatically be called by $animate so
648
- //there is no need to attach this internally to the
649
- //timeout done method
773
+ // This will automatically be called by $animate so
774
+ // there is no need to attach this internally to the
775
+ // timeout done method.
650
776
  return function onEnd(cancelled) {
777
+ element.off(css3AnimationEvents, onAnimationProgress);
651
778
  element.removeClass(className);
652
779
  element.removeClass(activeClassName);
653
780
 
654
- //only when the animation is cancelled is the done()
655
- //function not called for this animation therefore
656
- //this must be also called
781
+ // Only when the animation is cancelled is the done()
782
+ // function not called for this animation therefore
783
+ // this must be also called.
657
784
  if(cancelled) {
658
785
  done();
659
786
  }
@@ -664,13 +791,22 @@ angular.module('ngAnimate', ['ng'])
664
791
  done();
665
792
  }
666
793
 
667
- function parseMaxTime(str) {
668
- var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : [];
669
- forEach(values, function(value) {
670
- total = Math.max(parseFloat(value) || 0, total);
671
- });
672
- return total;
794
+ function onAnimationProgress(event) {
795
+ event.stopPropagation();
796
+ var ev = event.originalEvent || event;
797
+ var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
798
+ /* $manualTimeStamp is a mocked timeStamp value which is set
799
+ * within browserTrigger(). This is only here so that tests can
800
+ * mock animations properly. Real events fallback to event.timeStamp,
801
+ * or, if they don't, then a timeStamp is automatically created for them.
802
+ * We're checking to see if the timeStamp surpasses the expected delay,
803
+ * but we're using elapsedTime instead of the timeStamp on the 2nd
804
+ * pre-condition since animations sometimes close off early */
805
+ if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && ev.elapsedTime >= maxDuration) {
806
+ done();
807
+ }
673
808
  }
809
+
674
810
  }
675
811
 
676
812
  return {