Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
fix(core): ensure animate runner is the same with and without animations
Browse files Browse the repository at this point in the history
The $$AnimateRunner class is now the same for the core $animate service
and the ngAnimate $animate service. Previously, the core used a different
implementation that didn't match the ngAnimate behavior with regard
to callbacks.

Closes #13205
Closes #13347
  • Loading branch information
matsko authored and Narretz committed Nov 19, 2015
1 parent a5ff651 commit 546a277
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 235 deletions.
1 change: 1 addition & 0 deletions angularFiles.js
Expand Up @@ -14,6 +14,7 @@ var angularFiles = {

'src/ng/anchorScroll.js',
'src/ng/animate.js',
'src/ng/animateRunner.js',
'src/ng/animateCss.js',
'src/ng/browser.js',
'src/ng/cacheFactory.js',
Expand Down
6 changes: 4 additions & 2 deletions src/AngularPublic.js
Expand Up @@ -57,7 +57,8 @@
$AnimateProvider,
$CoreAnimateCssProvider,
$$CoreAnimateQueueProvider,
$$CoreAnimateRunnerProvider,
$$AnimateRunnerProvider,
$$AnimateAsyncRunFactoryProvider,
$BrowserProvider,
$CacheFactoryProvider,
$ControllerProvider,
Expand Down Expand Up @@ -218,7 +219,8 @@ function publishExternalAPI(angular) {
$animate: $AnimateProvider,
$animateCss: $CoreAnimateCssProvider,
$$animateQueue: $$CoreAnimateQueueProvider,
$$AnimateRunner: $$CoreAnimateRunnerProvider,
$$AnimateRunner: $$AnimateRunnerFactoryProvider,
$$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
$browser: $BrowserProvider,
$cacheFactory: $CacheFactoryProvider,
$controller: $ControllerProvider,
Expand Down
30 changes: 6 additions & 24 deletions src/ng/animate.js
Expand Up @@ -53,29 +53,6 @@ function prepareAnimateOptions(options) {
: {};
}

var $$CoreAnimateRunnerProvider = function() {
this.$get = ['$q', '$$rAF', function($q, $$rAF) {
function AnimateRunner() {}
AnimateRunner.all = noop;
AnimateRunner.chain = noop;
AnimateRunner.prototype = {
end: noop,
cancel: noop,
resume: noop,
pause: noop,
complete: noop,
then: function(pass, fail) {
return $q(function(resolve) {
$$rAF(function() {
resolve();
});
}).then(pass, fail);
}
};
return AnimateRunner;
}];
};

// this is prefixed with Core since it conflicts with
// the animateQueueProvider defined in ngAnimate/animateQueue.js
var $$CoreAnimateQueueProvider = function() {
Expand All @@ -101,7 +78,12 @@ var $$CoreAnimateQueueProvider = function() {
addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
}

return new $$AnimateRunner(); // jshint ignore:line
var runner = new $$AnimateRunner(); // jshint ignore:line

// since there are no animations to run the runner needs to be
// notified that the animation call is complete.
runner.complete();
return runner;
}
};

Expand Down
39 changes: 5 additions & 34 deletions src/ng/animateCss.js
Expand Up @@ -12,36 +12,7 @@
* Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
*/
var $CoreAnimateCssProvider = function() {
this.$get = ['$$rAF', '$q', function($$rAF, $q) {

var RAFPromise = function() {};
RAFPromise.prototype = {
done: function(cancel) {
this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
},
end: function() {
this.done();
},
cancel: function() {
this.done(true);
},
getPromise: function() {
if (!this.defer) {
this.defer = $q.defer();
}
return this.defer.promise;
},
then: function(f1,f2) {
return this.getPromise().then(f1,f2);
},
'catch': function(f1) {
return this.getPromise()['catch'](f1);
},
'finally': function(f1) {
return this.getPromise()['finally'](f1);
}
};

this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
return function(element, options) {
// there is no point in applying the styles since
// there is no animation that goes on at all in
Expand All @@ -55,24 +26,24 @@ var $CoreAnimateCssProvider = function() {
options.from = null;
}

var closed, runner = new RAFPromise();
var closed, runner = new $$AnimateRunner();
return {
start: run,
end: run
};

function run() {
$$rAF(function() {
close();
applyAnimationContents();
if (!closed) {
runner.done();
runner.complete();
}
closed = true;
});
return runner;
}

function close() {
function applyAnimationContents() {
if (options.addClass) {
element.addClass(options.addClass);
options.addClass = null;
Expand Down
170 changes: 170 additions & 0 deletions src/ng/animateRunner.js
@@ -0,0 +1,170 @@
'use strict';

var $$AnimateAsyncRunFactoryProvider = function() {
this.$get = ['$$rAF', function($$rAF) {
var waitQueue = [];

function waitForTick(fn) {
waitQueue.push(fn);
if (waitQueue.length > 1) return;
$$rAF(function() {
for (var i = 0; i < waitQueue.length; i++) {
waitQueue[i]();
}
waitQueue = [];
});
}

return function() {
var passed = false;
waitForTick(function() {
passed = true;
});
return function(callback) {
passed ? callback() : waitForTick(callback);
};
};
}];
};

var $$AnimateRunnerFactoryProvider = function() {
this.$get = ['$q', '$sniffer', '$$animateAsyncRun',
function($q, $sniffer, $$animateAsyncRun) {

var INITIAL_STATE = 0;
var DONE_PENDING_STATE = 1;
var DONE_COMPLETE_STATE = 2;

AnimateRunner.chain = function(chain, callback) {
var index = 0;

next();
function next() {
if (index === chain.length) {
callback(true);
return;
}

chain[index](function(response) {
if (response === false) {
callback(false);
return;
}
index++;
next();
});
}
};

AnimateRunner.all = function(runners, callback) {
var count = 0;
var status = true;
forEach(runners, function(runner) {
runner.done(onProgress);
});

function onProgress(response) {
status = status && response;
if (++count === runners.length) {
callback(status);
}
}
};

function AnimateRunner(host) {
this.setHost(host);

this._doneCallbacks = [];
this._runInAnimationFrame = $$animateAsyncRun();
this._state = 0;
}

AnimateRunner.prototype = {
setHost: function(host) {
this.host = host || {};
},

done: function(fn) {
if (this._state === DONE_COMPLETE_STATE) {
fn();
} else {
this._doneCallbacks.push(fn);
}
},

progress: noop,

getPromise: function() {
if (!this.promise) {
var self = this;
this.promise = $q(function(resolve, reject) {
self.done(function(status) {
status === false ? reject() : resolve();
});
});
}
return this.promise;
},

then: function(resolveHandler, rejectHandler) {
return this.getPromise().then(resolveHandler, rejectHandler);
},

'catch': function(handler) {
return this.getPromise()['catch'](handler);
},

'finally': function(handler) {
return this.getPromise()['finally'](handler);
},

pause: function() {
if (this.host.pause) {
this.host.pause();
}
},

resume: function() {
if (this.host.resume) {
this.host.resume();
}
},

end: function() {
if (this.host.end) {
this.host.end();
}
this._resolve(true);
},

cancel: function() {
if (this.host.cancel) {
this.host.cancel();
}
this._resolve(false);
},

complete: function(response) {
var self = this;
if (self._state === INITIAL_STATE) {
self._state = DONE_PENDING_STATE;
self._runInAnimationFrame(function() {
self._resolve(response);
});
}
},

_resolve: function(response) {
if (this._state !== DONE_COMPLETE_STATE) {
forEach(this._doneCallbacks, function(fn) {
fn(response);
});
this._doneCallbacks.length = 0;
this._state = DONE_COMPLETE_STATE;
}
}
};

return AnimateRunner;
}];
};

0 comments on commit 546a277

Please sign in to comment.