angularjs-rails 1.2.0.rc2 → 1.2.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 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 {