From 71f25d7e8a70bd017d3589ae54efb19c4ba317f5 Mon Sep 17 00:00:00 2001 From: Bernhard Posselt Date: Thu, 28 May 2015 14:25:15 +0200 Subject: update js --- js/vendor/angular-animate/angular-animate.js | 221 ++++++++++++++++++++++----- 1 file changed, 179 insertions(+), 42 deletions(-) (limited to 'js/vendor/angular-animate/angular-animate.js') diff --git a/js/vendor/angular-animate/angular-animate.js b/js/vendor/angular-animate/angular-animate.js index 126be09b9..42b03e0b5 100644 --- a/js/vendor/angular-animate/angular-animate.js +++ b/js/vendor/angular-animate/angular-animate.js @@ -1,5 +1,5 @@ /** - * @license AngularJS v1.4.0-rc.2 + * @license AngularJS v1.4.0 * (c) 2010-2015 Google, Inc. http://angularjs.org * License: MIT */ @@ -256,6 +256,64 @@ function getDomNode(element) { return (element instanceof angular.element) ? element[0] : element; } +var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) { + var tickQueue = []; + var cancelFn; + + function scheduler(tasks) { + // we make a copy since RAFScheduler mutates the state + // of the passed in array variable and this would be difficult + // to track down on the outside code + tickQueue.push([].concat(tasks)); + nextTick(); + } + + /* waitUntilQuiet does two things: + * 1. It will run the FINAL `fn` value only when an uncancelled RAF has passed through + * 2. It will delay the next wave of tasks from running until the quiet `fn` has run. + * + * The motivation here is that animation code can request more time from the scheduler + * before the next wave runs. This allows for certain DOM properties such as classes to + * be resolved in time for the next animation to run. + */ + scheduler.waitUntilQuiet = function(fn) { + if (cancelFn) cancelFn(); + + cancelFn = $$rAF(function() { + cancelFn = null; + fn(); + nextTick(); + }); + }; + + return scheduler; + + function nextTick() { + if (!tickQueue.length) return; + + var updatedQueue = []; + for (var i = 0; i < tickQueue.length; i++) { + var innerQueue = tickQueue[i]; + runNextTask(innerQueue); + if (innerQueue.length) { + updatedQueue.push(innerQueue); + } + } + tickQueue = updatedQueue; + + if (!cancelFn) { + $$rAF(function() { + if (!cancelFn) nextTick(); + }); + } + } + + function runNextTask(tasks) { + var nextTask = tasks.shift(); + nextTask(); + } +}]; + var $$AnimateChildrenDirective = [function() { return function(scope, element, attrs) { var val = attrs.ngAnimateChildren; @@ -662,9 +720,9 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) { var gcsStaggerLookup = createLocalCacheLookup(); this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout', - '$document', '$sniffer', '$$rAF', + '$document', '$sniffer', '$$rAFScheduler', function($window, $$jqLite, $$AnimateRunner, $timeout, - $document, $sniffer, $$rAF) { + $document, $sniffer, $$rAFScheduler) { var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); @@ -722,15 +780,10 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) { } var bod = getDomNode($document).body; - var cancelLastRAFRequest; var rafWaitQueue = []; function waitUntilQuiet(callback) { - if (cancelLastRAFRequest) { - cancelLastRAFRequest(); //cancels the request - } rafWaitQueue.push(callback); - cancelLastRAFRequest = $$rAF(function() { - cancelLastRAFRequest = null; + $$rAFScheduler.waitUntilQuiet(function() { gcsLookup.flush(); gcsStaggerLookup.flush(); @@ -935,7 +988,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) { // we need to recalculate the delay value since we used a pre-emptive negative // delay value and the delay value is required for the final event checking. This // property will ensure that this will happen after the RAF phase has passed. - if (timings.transitionDuration > 0) { + if (options.duration == null && timings.transitionDuration > 0) { flags.recalculateTimingStyles = flags.recalculateTimingStyles || isFirst; } @@ -1452,18 +1505,20 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro var element = animationDetails.element; var options = animationDetails.options || {}; - options.structural = animationDetails.structural; - - // structural animations ensure that the CSS classes are always applied - // before the detection starts. - options.applyClassesEarly = options.structural; - - // we special case the leave animation since we want to ensure that - // the element is removed as soon as the animation is over. Otherwise - // a flicker might appear or the element may not be removed at all - options.event = animationDetails.event; - if (options.event === 'leave' && animationDetails.domOperation) { - options.onDone = animationDetails.domOperation; + if (animationDetails.structural) { + // structural animations ensure that the CSS classes are always applied + // before the detection starts. + options.structural = options.applyClassesEarly = true; + + // we special case the leave animation since we want to ensure that + // the element is removed as soon as the animation is over. Otherwise + // a flicker might appear or the element may not be removed at all + options.event = animationDetails.event; + if (options.event === 'leave') { + options.onDone = options.domOperation; + } + } else { + options.event = null; } var animator = $animateCss(element, options); @@ -1855,6 +1910,14 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { return currentAnimation.state === RUNNING_STATE && newAnimation.structural; }); + rules.cancel.push(function(element, newAnimation, currentAnimation) { + var nO = newAnimation.options; + var cO = currentAnimation.options; + + // if the exact same CSS class is added/removed then it's safe to cancel it + return (nO.addClass && nO.addClass === cO.removeClass) || (nO.removeClass && nO.removeClass === cO.addClass); + }); + this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap', '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', function($$rAF, $rootScope, $rootElement, $document, $$HashMap, @@ -2089,6 +2152,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { structural: isStructural, element: element, event: event, + close: close, options: options, runner: runner }; @@ -2108,8 +2172,17 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation); if (cancelAnimationFlag) { if (existingAnimation.state === RUNNING_STATE) { + // this will end the animation right away and it is safe + // to do so since the animation is already running and the + // runner callback code will run in async existingAnimation.runner.end(); + } else if (existingAnimation.structural) { + // this means that the animation is queued into a digest, but + // hasn't started yet. Therefore it is safe to run the close + // method which will call the runner methods in async. + existingAnimation.close(); } else { + // this will merge the existing animation options into this new follow-up animation mergeAnimationOptions(element, newAnimation.options, existingAnimation.options); } } else { @@ -2145,10 +2218,13 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { if (!isValidAnimation) { close(); + clearElementAnimationState(element); return runner; } - closeParentClassBasedAnimations(parent); + if (isStructural) { + closeParentClassBasedAnimations(parent); + } // the counter keeps track of cancelled animations var counter = (existingAnimation.counter || 0) + 1; @@ -2191,6 +2267,13 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { runner.end(); } + // in the event that the element animation was not cancelled or a follow-up animation + // isn't allowed to animate from here then we need to clear the state of the element + // so that any future animations won't read the expired animation data. + if (!isValidAnimation) { + clearElementAnimationState(element); + } + return; } @@ -2200,7 +2283,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { ? 'setClass' : animationDetails.event; - closeParentClassBasedAnimations(parentElement); + if (animationDetails.structural) { + closeParentClassBasedAnimations(parentElement); + } markElementAnimationState(element, RUNNING_STATE); var realRunner = $$animation(element, event, animationDetails.options); @@ -2542,12 +2627,16 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) { return element.data(RUNNER_STORAGE_KEY); } - this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', - function($$jqLite, $rootScope, $injector, $$AnimateRunner) { + this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$rAFScheduler', + function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$rAFScheduler) { var animationQueue = []; var applyAnimationClasses = applyAnimationClassesFactory($$jqLite); + var totalPendingClassBasedAnimations = 0; + var totalActiveClassBasedAnimations = 0; + var classBasedAnimationsQueue = []; + // TODO(matsko): document the signature in a better way return function(element, event, options) { options = prepareAnimationOptions(options); @@ -2576,12 +2665,19 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) { options.tempClasses = null; } + var classBasedIndex; + if (!isStructural) { + classBasedIndex = totalPendingClassBasedAnimations; + totalPendingClassBasedAnimations += 1; + } + animationQueue.push({ // this data is used by the postDigest code and passed into // the driver step function element: element, classes: classes, event: event, + classBasedIndex: classBasedIndex, structural: isStructural, options: options, beforeStart: beforeStart, @@ -2596,6 +2692,10 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) { if (animationQueue.length > 1) return runner; $rootScope.$$postDigest(function() { + totalActiveClassBasedAnimations = totalPendingClassBasedAnimations; + totalPendingClassBasedAnimations = 0; + classBasedAnimationsQueue.length = 0; + var animations = []; forEach(animationQueue, function(entry) { // the element was destroyed early on which removed the runner @@ -2610,23 +2710,58 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) { animationQueue.length = 0; forEach(groupAnimations(animations), function(animationEntry) { - // it's important that we apply the `ng-animate` CSS class and the - // temporary classes before we do any driver invoking since these - // CSS classes may be required for proper CSS detection. - animationEntry.beforeStart(); - - var operation = invokeFirstDriver(animationEntry); - var triggerAnimationStart = operation && operation.start; /// TODO(matsko): only recognize operation.start() - - var closeFn = animationEntry.close; - if (!triggerAnimationStart) { - closeFn(); + if (animationEntry.structural) { + triggerAnimationStart(); } else { - var animationRunner = triggerAnimationStart(); - animationRunner.done(function(status) { - closeFn(!status); + classBasedAnimationsQueue.push({ + node: getDomNode(animationEntry.element), + fn: triggerAnimationStart }); - updateAnimationRunners(animationEntry, animationRunner); + + if (animationEntry.classBasedIndex === totalActiveClassBasedAnimations - 1) { + // we need to sort each of the animations in order of parent to child + // relationships. This ensures that the child classes are applied at the + // right time. + classBasedAnimationsQueue = classBasedAnimationsQueue.sort(function(a,b) { + return b.node.contains(a.node); + }).map(function(entry) { + return entry.fn; + }); + + $$rAFScheduler(classBasedAnimationsQueue); + } + } + + function triggerAnimationStart() { + // it's important that we apply the `ng-animate` CSS class and the + // temporary classes before we do any driver invoking since these + // CSS classes may be required for proper CSS detection. + animationEntry.beforeStart(); + + var startAnimationFn, closeFn = animationEntry.close; + + // in the event that the element was removed before the digest runs or + // during the RAF sequencing then we should not trigger the animation. + var targetElement = animationEntry.anchors + ? (animationEntry.from.element || animationEntry.to.element) + : animationEntry.element; + + if (getRunner(targetElement)) { + var operation = invokeFirstDriver(animationEntry); + if (operation) { + startAnimationFn = operation.start; + } + } + + if (!startAnimationFn) { + closeFn(); + } else { + var animationRunner = startAnimationFn(); + animationRunner.done(function(status) { + closeFn(!status); + }); + updateAnimationRunners(animationEntry, animationRunner); + } } }); }); @@ -2698,7 +2833,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) { var lookupKey = from.animationID.toString(); if (!anchorGroups[lookupKey]) { var group = anchorGroups[lookupKey] = { - // TODO(matsko): double-check this code + structural: true, beforeStart: function() { fromAnimation.beforeStart(); toAnimation.beforeStart(); @@ -2816,6 +2951,7 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) { /* global angularAnimateModule: true, $$rAFMutexFactory, + $$rAFSchedulerFactory, $$AnimateChildrenDirective, $$AnimateRunnerFactory, $$AnimateQueueProvider, @@ -3555,6 +3691,7 @@ angular.module('ngAnimate', []) .directive('ngAnimateChildren', $$AnimateChildrenDirective) .factory('$$rAFMutex', $$rAFMutexFactory) + .factory('$$rAFScheduler', $$rAFSchedulerFactory) .factory('$$AnimateRunner', $$AnimateRunnerFactory) -- cgit v1.2.3