mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-28 23:28:16 +00:00
feat(ngAnimate): complete rewrite of animations
- ngAnimate directive is gone and was replaced with class based animations/transitions - support for triggering animations on css class additions and removals - done callback was added to all animation apis - $animation and $animator where merged into a single $animate service with api: - $animate.enter(element, parent, after, done); - $animate.leave(element, done); - $animate.move(element, parent, after, done); - $animate.addClass(element, className, done); - $animate.removeClass(element, className, done); BREAKING CHANGE: too many things changed, we'll write up a separate doc with migration instructions
This commit is contained in:
parent
11521a4cde
commit
81923f1e41
40 changed files with 3014 additions and 2244 deletions
|
|
@ -160,6 +160,10 @@ module.exports = function(grunt) {
|
||||||
dest: 'build/angular-resource.js',
|
dest: 'build/angular-resource.js',
|
||||||
src: util.wrap(['src/ngResource/resource.js'], 'module')
|
src: util.wrap(['src/ngResource/resource.js'], 'module')
|
||||||
},
|
},
|
||||||
|
animate: {
|
||||||
|
dest: 'build/angular-animate.js',
|
||||||
|
src: util.wrap(['src/ngAnimate/animate.js'], 'module')
|
||||||
|
},
|
||||||
route: {
|
route: {
|
||||||
dest: 'build/angular-route.js',
|
dest: 'build/angular-route.js',
|
||||||
src: util.wrap([
|
src: util.wrap([
|
||||||
|
|
@ -178,6 +182,7 @@ module.exports = function(grunt) {
|
||||||
|
|
||||||
min: {
|
min: {
|
||||||
angular: 'build/angular.js',
|
angular: 'build/angular.js',
|
||||||
|
animate: 'build/angular-animate.js',
|
||||||
cookies: 'build/angular-cookies.js',
|
cookies: 'build/angular-cookies.js',
|
||||||
loader: 'build/angular-loader.js',
|
loader: 'build/angular-loader.js',
|
||||||
mobile: 'build/angular-mobile.js',
|
mobile: 'build/angular-mobile.js',
|
||||||
|
|
|
||||||
5
angularFiles.js
vendored
5
angularFiles.js
vendored
|
|
@ -10,8 +10,7 @@ angularFiles = {
|
||||||
'src/auto/injector.js',
|
'src/auto/injector.js',
|
||||||
|
|
||||||
'src/ng/anchorScroll.js',
|
'src/ng/anchorScroll.js',
|
||||||
'src/ng/animation.js',
|
'src/ng/animate.js',
|
||||||
'src/ng/animator.js',
|
|
||||||
'src/ng/browser.js',
|
'src/ng/browser.js',
|
||||||
'src/ng/cacheFactory.js',
|
'src/ng/cacheFactory.js',
|
||||||
'src/ng/compile.js',
|
'src/ng/compile.js',
|
||||||
|
|
@ -66,6 +65,7 @@ angularFiles = {
|
||||||
],
|
],
|
||||||
|
|
||||||
'angularSrcModules': [
|
'angularSrcModules': [
|
||||||
|
'src/ngAnimate/animate.js',
|
||||||
'src/ngCookies/cookies.js',
|
'src/ngCookies/cookies.js',
|
||||||
'src/ngResource/resource.js',
|
'src/ngResource/resource.js',
|
||||||
'src/ngRoute/routeUtils.js',
|
'src/ngRoute/routeUtils.js',
|
||||||
|
|
@ -107,6 +107,7 @@ angularFiles = {
|
||||||
'test/*.js',
|
'test/*.js',
|
||||||
'test/auto/*.js',
|
'test/auto/*.js',
|
||||||
'test/ng/**/*.js',
|
'test/ng/**/*.js',
|
||||||
|
'test/ngAnimate/*.js',
|
||||||
'test/ngCookies/*.js',
|
'test/ngCookies/*.js',
|
||||||
'test/ngResource/*.js',
|
'test/ngResource/*.js',
|
||||||
'test/ngRoute/**/*.js',
|
'test/ngRoute/**/*.js',
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,7 @@
|
||||||
ng\:form {
|
ng\:form {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ng-hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,41 +67,33 @@ describe('Docs Annotations', function() {
|
||||||
|
|
||||||
var $scope, parent, element, url, window;
|
var $scope, parent, element, url, window;
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
module(function($provide, $animationProvider) {
|
module(function($provide, $animateProvider) {
|
||||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
$provide.value('$window', window = angular.mock.createMockWindow());
|
||||||
$animationProvider.register('foldout-enter', function($window) {
|
$animateProvider.register('.foldout', function($window) {
|
||||||
return {
|
return {
|
||||||
start : function(element, done) {
|
enter : function(element, done) {
|
||||||
$window.setTimeout(done, 1000);
|
$window.setTimeout(done, 1000);
|
||||||
}
|
},
|
||||||
}
|
show : function(element, done) {
|
||||||
});
|
|
||||||
$animationProvider.register('foldout-hide', function($window) {
|
|
||||||
return {
|
|
||||||
start : function(element, done) {
|
|
||||||
$window.setTimeout(done, 500);
|
$window.setTimeout(done, 500);
|
||||||
}
|
},
|
||||||
}
|
hide : function(element, done) {
|
||||||
});
|
|
||||||
$animationProvider.register('foldout-show', function($window) {
|
|
||||||
return {
|
|
||||||
start : function(element, done) {
|
|
||||||
$window.setTimeout(done, 200);
|
$window.setTimeout(done, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
inject(function($rootScope, $compile, $templateCache, $rootElement, $animator) {
|
inject(function($rootScope, $compile, $templateCache, $rootElement, $animate) {
|
||||||
$animator.enabled(true);
|
$animate.enabled(true);
|
||||||
url = '/page.html';
|
url = '/page.html';
|
||||||
$scope = $rootScope.$new();
|
$scope = $rootScope.$new();
|
||||||
parent = angular.element('<div class="parent"></div>');
|
parent = angular.element('<div class="parent"></div>');
|
||||||
element = angular.element('<div data-url="' + url + '" foldout></div>');
|
|
||||||
|
|
||||||
//we're injecting the element to the $rootElement since the changes in
|
//we're injecting the element to the $rootElement since the changes in
|
||||||
//$animator only detect and perform animations if the root element has
|
//$animate only detect and perform animations if the root element has
|
||||||
//animations enabled. If the element is not apart of the DOM
|
//animations enabled. If the element is not apart of the DOM
|
||||||
//then animations are skipped.
|
//then animations are skipped.
|
||||||
|
element = angular.element('<div data-url="' + url + '" class="foldout" foldout></div>');
|
||||||
parent.append(element);
|
parent.append(element);
|
||||||
$rootElement.append(parent);
|
$rootElement.append(parent);
|
||||||
body.append($rootElement);
|
body.append($rootElement);
|
||||||
|
|
@ -142,16 +134,19 @@ describe('Docs Annotations', function() {
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
window.setTimeout.expect(1).process();
|
window.setTimeout.expect(1).process();
|
||||||
window.setTimeout.expect(1000).process();
|
window.setTimeout.expect(1000).process();
|
||||||
|
window.setTimeout.expect(0).process();
|
||||||
|
|
||||||
//hide
|
//hide
|
||||||
element.triggerHandler('click');
|
element.triggerHandler('click');
|
||||||
window.setTimeout.expect(1).process();
|
window.setTimeout.expect(1).process();
|
||||||
window.setTimeout.expect(500).process();
|
window.setTimeout.expect(200).process();
|
||||||
|
window.setTimeout.expect(0).process();
|
||||||
|
|
||||||
//show
|
//show
|
||||||
element.triggerHandler('click');
|
element.triggerHandler('click');
|
||||||
window.setTimeout.expect(1).process();
|
window.setTimeout.expect(1).process();
|
||||||
window.setTimeout.expect(200).process();
|
window.setTimeout.expect(500).process();
|
||||||
|
window.setTimeout.expect(0).process();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
@ -160,7 +155,7 @@ describe('Docs Annotations', function() {
|
||||||
|
|
||||||
var window, $scope, ctrl;
|
var window, $scope, ctrl;
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
module(function($provide, $animationProvider) {
|
module(function($provide, $animateProvider) {
|
||||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
$provide.value('$window', window = angular.mock.createMockWindow());
|
||||||
});
|
});
|
||||||
inject(function($rootScope, $controller, $location, $cookies, sections) {
|
inject(function($rootScope, $controller, $location, $cookies, sections) {
|
||||||
|
|
|
||||||
|
|
@ -183,8 +183,8 @@ directive.ngEvalJavascript = ['getEmbeddedTemplate', function(getEmbeddedTemplat
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|
||||||
directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location', '$sniffer',
|
directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location', '$sniffer', '$animate',
|
||||||
function($templateCache, $browser, docsRootScope, $location, $sniffer) {
|
function($templateCache, $browser, docsRootScope, $location, $sniffer, $animate) {
|
||||||
return {
|
return {
|
||||||
terminal: true,
|
terminal: true,
|
||||||
link: function(scope, element, attrs) {
|
link: function(scope, element, attrs) {
|
||||||
|
|
@ -193,6 +193,7 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
||||||
deregisterEmbedRootScope;
|
deregisterEmbedRootScope;
|
||||||
|
|
||||||
modules.push(['$provide', function($provide) {
|
modules.push(['$provide', function($provide) {
|
||||||
|
$provide.value('$animate', $animate);
|
||||||
$provide.value('$templateCache', $templateCache);
|
$provide.value('$templateCache', $templateCache);
|
||||||
$provide.value('$anchorScroll', angular.noop);
|
$provide.value('$anchorScroll', angular.noop);
|
||||||
$provide.value('$browser', $browser);
|
$provide.value('$browser', $browser);
|
||||||
|
|
|
||||||
|
|
@ -335,12 +335,11 @@ directive.tabPane = function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
directive.foldout = ['$http', '$animator','$window', function($http, $animator, $window) {
|
directive.foldout = ['$http', '$animate','$window', function($http, $animate, $window) {
|
||||||
return {
|
return {
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
priority : 500,
|
priority : 500,
|
||||||
link: function(scope, element, attrs) {
|
link: function(scope, element, attrs) {
|
||||||
var animator = $animator(scope, { ngAnimate: "'foldout'" });
|
|
||||||
var container, loading, url = attrs.url;
|
var container, loading, url = attrs.url;
|
||||||
if(/\/build\//.test($window.location.href)) {
|
if(/\/build\//.test($window.location.href)) {
|
||||||
url = '/build/docs' + url;
|
url = '/build/docs' + url;
|
||||||
|
|
@ -353,7 +352,7 @@ directive.foldout = ['$http', '$animator','$window', function($http, $animator,
|
||||||
loading = true;
|
loading = true;
|
||||||
var par = element.parent();
|
var par = element.parent();
|
||||||
container = angular.element('<div class="foldout">loading...</div>');
|
container = angular.element('<div class="foldout">loading...</div>');
|
||||||
animator.enter(container, null, par);
|
$animate.enter(container, null, par);
|
||||||
|
|
||||||
$http.get(url, { cache : true }).success(function(html) {
|
$http.get(url, { cache : true }).success(function(html) {
|
||||||
loading = false;
|
loading = false;
|
||||||
|
|
@ -367,12 +366,12 @@ directive.foldout = ['$http', '$animator','$window', function($http, $animator,
|
||||||
//avoid showing the element if the user has already closed it
|
//avoid showing the element if the user has already closed it
|
||||||
if(container.css('display') == 'block') {
|
if(container.css('display') == 'block') {
|
||||||
container.css('display','none');
|
container.css('display','none');
|
||||||
animator.show(container);
|
$animate.show(container);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
container.css('display') == 'none' ? animator.show(container) : animator.hide(container);
|
container.hasClass('ng-hide') ? $animate.show(container) : $animate.hide(container);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ exports.Example.prototype.toHtmlTabs = function() {
|
||||||
|
|
||||||
exports.Example.prototype.toHtmlEmbed = function() {
|
exports.Example.prototype.toHtmlEmbed = function() {
|
||||||
var out = [];
|
var out = [];
|
||||||
out.push('<div class="well doc-example-live animator-container"');
|
out.push('<div class="well doc-example-live animate-container"');
|
||||||
if(this.animations) {
|
if(this.animations) {
|
||||||
out.push(" ng-class=\"{'animations-off':animationsOff == true}\"");
|
out.push(" ng-class=\"{'animations-off':animationsOff == true}\"");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -494,6 +494,19 @@ Doc.prototype = {
|
||||||
html_usage_parameters: function(dom) {
|
html_usage_parameters: function(dom) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var params = this.param ? this.param : [];
|
var params = this.param ? this.param : [];
|
||||||
|
if(this.animations) {
|
||||||
|
dom.h('Animations', this.animations, function(animations){
|
||||||
|
dom.html('<ul>');
|
||||||
|
var animations = animations.split("\n");
|
||||||
|
animations.forEach(function(ani) {
|
||||||
|
dom.html('<li>');
|
||||||
|
dom.text(ani);
|
||||||
|
dom.html('</li>');
|
||||||
|
});
|
||||||
|
dom.html('</ul>');
|
||||||
|
});
|
||||||
|
dom.html('<a href="api/ngAnimate.$animate">Click here</a> to learn more about the steps involved in the animation.');
|
||||||
|
}
|
||||||
if(params.length > 0) {
|
if(params.length > 0) {
|
||||||
dom.html('<h2 id="parameters">Parameters</h2>');
|
dom.html('<h2 id="parameters">Parameters</h2>');
|
||||||
dom.html('<table class="variables-matrix table table-bordered table-striped">');
|
dom.html('<table class="variables-matrix table table-bordered table-striped">');
|
||||||
|
|
@ -538,18 +551,6 @@ Doc.prototype = {
|
||||||
dom.html('</tbody>');
|
dom.html('</tbody>');
|
||||||
dom.html('</table>');
|
dom.html('</table>');
|
||||||
}
|
}
|
||||||
if(this.animations) {
|
|
||||||
dom.h('Animations', this.animations, function(animations){
|
|
||||||
dom.html('<ul>');
|
|
||||||
var animations = animations.split("\n");
|
|
||||||
animations.forEach(function(ani) {
|
|
||||||
dom.html('<li>');
|
|
||||||
dom.text(ani);
|
|
||||||
dom.html('</li>');
|
|
||||||
});
|
|
||||||
dom.html('</ul>');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
html_usage_returns: function(dom) {
|
html_usage_returns: function(dom) {
|
||||||
|
|
@ -665,48 +666,6 @@ Doc.prototype = {
|
||||||
dom.text('</' + element + '>');
|
dom.text('</' + element + '>');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if(self.animations) {
|
|
||||||
var animations = [], matches = self.animations.split("\n");
|
|
||||||
matches.forEach(function(ani) {
|
|
||||||
var name = ani.match(/^\s*(.+?)\s*-/)[1];
|
|
||||||
animations.push(name);
|
|
||||||
});
|
|
||||||
|
|
||||||
dom.html('with <span id="animations">animations</span>');
|
|
||||||
var comment;
|
|
||||||
if(animations.length == 1) {
|
|
||||||
comment = 'The ' + animations[0] + ' animation is supported';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var rhs = animations[animations.length-1];
|
|
||||||
var lhs = '';
|
|
||||||
for(var i=0;i<animations.length-1;i++) {
|
|
||||||
if(i>0) {
|
|
||||||
lhs += ', ';
|
|
||||||
}
|
|
||||||
lhs += animations[i];
|
|
||||||
}
|
|
||||||
comment = 'The ' + lhs + ' and ' + rhs + ' animations are supported';
|
|
||||||
}
|
|
||||||
var element = self.element || 'ANY';
|
|
||||||
dom.code(function() {
|
|
||||||
dom.text('//' + comment + "\n");
|
|
||||||
dom.text('<' + element + ' ');
|
|
||||||
dom.text(dashCase(self.shortName));
|
|
||||||
renderParams('\n ', '="', '"', true);
|
|
||||||
dom.text(' ng-animate="{');
|
|
||||||
animations.forEach(function(ani, index) {
|
|
||||||
if (index) {
|
|
||||||
dom.text(', ');
|
|
||||||
}
|
|
||||||
dom.text(ani + ': \'' + ani + '-animation\'');
|
|
||||||
});
|
|
||||||
dom.text('}">\n ...\n');
|
|
||||||
dom.text('</' + element + '>');
|
|
||||||
});
|
|
||||||
|
|
||||||
dom.html('<a href="api/ng.$animator#Methods">Click here</a> to learn more about the steps involved in the animation.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.html_usage_directiveInfo(dom);
|
self.html_usage_directiveInfo(dom);
|
||||||
self.html_usage_parameters(dom);
|
self.html_usage_parameters(dom);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.reveal {
|
.reveal.ng-enter {
|
||||||
-webkit-transition:1s linear all;
|
-webkit-transition:1s linear all;
|
||||||
-moz-transition:1s linear all;
|
-moz-transition:1s linear all;
|
||||||
-o-transition:1s linear all;
|
-o-transition:1s linear all;
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
opacity:0;
|
opacity:0;
|
||||||
}
|
}
|
||||||
.reveal.reveal-active {
|
.reveal.ng-enter.ng-enter-active {
|
||||||
opacity:1;
|
opacity:1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -15,48 +15,45 @@
|
||||||
overflow:hidden;
|
overflow:hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide-reveal {
|
.slide-reveal > .ng-enter {
|
||||||
-webkit-transition:0.5s linear all;
|
-webkit-transition:0.5s linear all;
|
||||||
-moz-transition:0.5s linear all;
|
-moz-transition:0.5s linear all;
|
||||||
-o-transition:0.5s linear all;
|
-o-transition:0.5s linear all;
|
||||||
transition:0.5s linear all;
|
transition:0.5s linear all;
|
||||||
opacity:0.5;
|
|
||||||
|
|
||||||
|
opacity:0.5;
|
||||||
position:relative;
|
position:relative;
|
||||||
opacity:0;
|
opacity:0;
|
||||||
top:10px;
|
top:10px;
|
||||||
}
|
}
|
||||||
.slide-reveal.slide-reveal-active {
|
.slide-reveal > .ng-enter.ng-enter-active {
|
||||||
top:0;
|
top:0;
|
||||||
opacity:1;
|
opacity:1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expand-enter {
|
.expand.ng-enter,
|
||||||
|
.expand.ng-leave {
|
||||||
-webkit-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
-webkit-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
||||||
-moz-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
-moz-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
||||||
-o-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
-o-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
||||||
transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
||||||
|
}
|
||||||
|
.expand.ng-enter {
|
||||||
opacity:0;
|
opacity:0;
|
||||||
line-height:0;
|
line-height:0;
|
||||||
height:0!important;
|
height:0!important;
|
||||||
}
|
}
|
||||||
.expand-enter.expand-enter-active {
|
.expand.ng-enter.expand.ng-enter-active {
|
||||||
opacity:1;
|
opacity:1;
|
||||||
line-height:20px;
|
line-height:20px;
|
||||||
height:20px!important;
|
height:20px!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expand-leave {
|
.expand.ng-leave {
|
||||||
-webkit-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
|
||||||
-moz-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
|
||||||
-o-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
|
||||||
transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
|
||||||
|
|
||||||
opacity:1;
|
opacity:1;
|
||||||
height:20px;
|
height:20px;
|
||||||
}
|
}
|
||||||
.expand-leave.expand-leave-active {
|
.expand.ng-leave.expand.ng-leave-active {
|
||||||
opacity:0;
|
opacity:0;
|
||||||
height:0;
|
height:0;
|
||||||
}
|
}
|
||||||
|
|
@ -73,32 +70,36 @@
|
||||||
padding:1em;
|
padding:1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animator-container.animations-off * {
|
.animate-container.animations-off * {
|
||||||
-webkit-transition: none;
|
-webkit-transition: none;
|
||||||
-moz-transition: none;
|
-moz-transition: none;
|
||||||
-o-transition: color 0 ease-in; /* opera is special :) */
|
-o-transition: color 0 ease-in; /* opera is special :) */
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.foldout-show, .foldout-enter, .foldout-hide {
|
.foldout.ng-enter,
|
||||||
|
.foldout.ng-hide-add,
|
||||||
|
.foldout.ng-hide-remove {
|
||||||
-webkit-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
-webkit-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
||||||
-moz-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
-moz-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
||||||
-o-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
-o-transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
||||||
transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
transition:0.3s cubic-bezier(0.250, 0.460, 0.450, 0.940) all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.foldout-show, .foldout-enter {
|
.foldout.ng-hide-remove,
|
||||||
|
.foldout.ng-enter {
|
||||||
opacity:0;
|
opacity:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.foldout-show.foldout-show-active, .foldout-hide.foldout-hide-active {
|
.foldout.ng-hide-remove.ng-hide-remove-active,
|
||||||
|
.foldout.ng-enter.ng-enter-active {
|
||||||
opacity:1;
|
opacity:1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.foldout-hide {
|
.foldout.ng-hide-add {
|
||||||
opacity:1;
|
opacity:1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.foldout-hide.foldout-hide-active {
|
.foldout.ng-hide-add.ng-hide-active {
|
||||||
opacity:0;
|
opacity:0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@
|
||||||
addTag('script', {src: path('angular-cookies.js') }, sync);
|
addTag('script', {src: path('angular-cookies.js') }, sync);
|
||||||
addTag('script', {src: path('angular-sanitize.js') }, sync);
|
addTag('script', {src: path('angular-sanitize.js') }, sync);
|
||||||
addTag('script', {src: path('angular-mobile.js') }, sync);
|
addTag('script', {src: path('angular-mobile.js') }, sync);
|
||||||
|
addTag('script', {src: path('angular-animate.js') }, sync);
|
||||||
addTag('script', {src: 'components/angular-bootstrap.js' }, sync);
|
addTag('script', {src: 'components/angular-bootstrap.js' }, sync);
|
||||||
addTag('script', {src: 'components/angular-bootstrap-prettify.js' }, sync);
|
addTag('script', {src: 'components/angular-bootstrap-prettify.js' }, sync);
|
||||||
addTag('script', {src: 'components/google-code-prettify.js' }, sync);
|
addTag('script', {src: 'components/google-code-prettify.js' }, sync);
|
||||||
|
|
@ -201,7 +202,7 @@
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div id="docs-fold-overlay" ng-show="docs_fold" ng-click="fold(null)"></div>
|
<div id="docs-fold-overlay" ng-show="docs_fold" ng-click="fold(null)"></div>
|
||||||
<div id="docs-fold" ng-show="docs_fold" ng-animate="'fold'">
|
<div class="foldout" id="docs-fold" ng-show="docs_fold">
|
||||||
<div id="docs-fold-close" ng-click="fold(null)">
|
<div id="docs-fold-close" ng-click="fold(null)">
|
||||||
<span class="icon-remove-sign"></span>
|
<span class="icon-remove-sign"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -283,21 +284,21 @@
|
||||||
<li class="nav-header section" ng-show="module.directives">
|
<li class="nav-header section" ng-show="module.directives">
|
||||||
<a href="{{URL.directive}}" class="guide">directive</a>
|
<a href="{{URL.directive}}" class="guide">directive</a>
|
||||||
</li>
|
</li>
|
||||||
<li ng-repeat="page in module.directives track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item">
|
<li ng-repeat="page in module.directives track by page.url" ng-class="navClass(page)" class="expand api-list-item">
|
||||||
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-header section" ng-show="module.filters">
|
<li class="nav-header section" ng-show="module.filters">
|
||||||
<a href="{{URL.filter}}" class="guide">filter</a>
|
<a href="{{URL.filter}}" class="guide">filter</a>
|
||||||
</li>
|
</li>
|
||||||
<li ng-repeat="page in module.filters track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item">
|
<li ng-repeat="page in module.filters track by page.url" ng-class="navClass(page)" class="expand api-list-item">
|
||||||
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-header section" ng-show="module.services">
|
<li class="nav-header section" ng-show="module.services">
|
||||||
<a href="{{URL.service}}" class="guide">service</a>
|
<a href="{{URL.service}}" class="guide">service</a>
|
||||||
</li>
|
</li>
|
||||||
<li ng-repeat="service in module.services track by service.instance.url" ng-animate="'expand'" ng-class="navClass(service.instance, service.provider)" class="api-list-item">
|
<li ng-repeat="service in module.services track by service.instance.url" ng-class="navClass(service.instance, service.provider)" class="api-list-item expand">
|
||||||
<a ng-show="service.provider" class="pull-right" href="{{service.provider.url}}" tabindex="2"><i class="icon-cog"></i></a>
|
<a ng-show="service.provider" class="pull-right" href="{{service.provider.url}}" tabindex="2"><i class="icon-cog"></i></a>
|
||||||
<a href="{{service.instance.url}}" tabindex="2">{{service.name}}</a>
|
<a href="{{service.instance.url}}" tabindex="2">{{service.name}}</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -305,7 +306,7 @@
|
||||||
<li class="nav-header section" ng-show="module.types">
|
<li class="nav-header section" ng-show="module.types">
|
||||||
<a href="{{URL.type}}" class="guide">Types</a>
|
<a href="{{URL.type}}" class="guide">Types</a>
|
||||||
</li>
|
</li>
|
||||||
<li ng-repeat="page in module.types track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item">
|
<li ng-repeat="page in module.types track by page.url" ng-class="navClass(page)" class="expand api-list-item">
|
||||||
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
@ -334,7 +335,7 @@
|
||||||
|
|
||||||
<div id="loading" ng-show="loading">Loading...</div>
|
<div id="loading" ng-show="loading">Loading...</div>
|
||||||
|
|
||||||
<div ng-hide="loading" ng-include src="currentPage.partialUrl" onload="afterPartialLoaded()" autoscroll class="content" ng-animate="{enter: 'slide-reveal'}" ></div>
|
<div ng-hide="loading" ng-include src="currentPage.partialUrl" onload="afterPartialLoaded()" autoscroll class="content slide-reveal"></div>
|
||||||
|
|
||||||
<div id="disqus" class="disqus">
|
<div id="disqus" class="disqus">
|
||||||
<h2>Discussion</h2>
|
<h2>Discussion</h2>
|
||||||
|
|
|
||||||
|
|
@ -803,7 +803,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
angular.module('docsApp', ['ngResource', 'ngRoute', 'ngCookies', 'ngSanitize', 'bootstrap', 'bootstrapPrettify', 'docsData']).
|
angular.module('docsApp', ['ngResource', 'ngRoute', 'ngCookies', 'ngSanitize', 'ngAnimate', 'bootstrap', 'bootstrapPrettify', 'docsData']).
|
||||||
config(function($locationProvider) {
|
config(function($locationProvider) {
|
||||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||||
}).
|
}).
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ module.exports = function(config) {
|
||||||
'build/angular-mobile.js',
|
'build/angular-mobile.js',
|
||||||
'build/angular-sanitize.js',
|
'build/angular-sanitize.js',
|
||||||
'build/angular-route.js',
|
'build/angular-route.js',
|
||||||
|
'build/angular-animate.js',
|
||||||
|
|
||||||
'build/docs/components/lunr.js',
|
'build/docs/components/lunr.js',
|
||||||
'build/docs/components/google-code-prettify.js',
|
'build/docs/components/google-code-prettify.js',
|
||||||
|
|
|
||||||
|
|
@ -1053,13 +1053,13 @@ function bootstrap(element, modules) {
|
||||||
}]);
|
}]);
|
||||||
modules.unshift('ng');
|
modules.unshift('ng');
|
||||||
var injector = createInjector(modules);
|
var injector = createInjector(modules);
|
||||||
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animator',
|
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate',
|
||||||
function(scope, element, compile, injector, animator) {
|
function(scope, element, compile, injector, animate) {
|
||||||
scope.$apply(function() {
|
scope.$apply(function() {
|
||||||
element.data('$injector', injector);
|
element.data('$injector', injector);
|
||||||
compile(element)(scope);
|
compile(element)(scope);
|
||||||
});
|
});
|
||||||
animator.enabled(true);
|
animate.enabled(true);
|
||||||
}]
|
}]
|
||||||
);
|
);
|
||||||
return injector;
|
return injector;
|
||||||
|
|
|
||||||
|
|
@ -106,8 +106,7 @@ function publishExternalAPI(angular){
|
||||||
directive(ngEventDirectives);
|
directive(ngEventDirectives);
|
||||||
$provide.provider({
|
$provide.provider({
|
||||||
$anchorScroll: $AnchorScrollProvider,
|
$anchorScroll: $AnchorScrollProvider,
|
||||||
$animation: $AnimationProvider,
|
$animate: $AnimateProvider,
|
||||||
$animator: $AnimatorProvider,
|
|
||||||
$browser: $BrowserProvider,
|
$browser: $BrowserProvider,
|
||||||
$cacheFactory: $CacheFactoryProvider,
|
$cacheFactory: $CacheFactoryProvider,
|
||||||
$controller: $ControllerProvider,
|
$controller: $ControllerProvider,
|
||||||
|
|
|
||||||
|
|
@ -173,24 +173,30 @@ function setupModuleLoader(window) {
|
||||||
* @param {Function} animationFactory Factory function for creating new instance of an animation.
|
* @param {Function} animationFactory Factory function for creating new instance of an animation.
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Defines an animation hook that can be later used with {@link ng.directive:ngAnimate ngAnimate}
|
* **NOTE**: animations are take effect only if the **ngAnimate** module is loaded.
|
||||||
* alongside {@link ng.directive:ngAnimate#Description common ng directives} as well as custom directives.
|
|
||||||
* <pre>
|
|
||||||
* module.animation('animation-name', function($inject1, $inject2) {
|
|
||||||
* return {
|
|
||||||
* //this gets called in preparation to setup an animation
|
|
||||||
* setup : function(element) { ... },
|
|
||||||
*
|
*
|
||||||
* //this gets called once the animation is run
|
*
|
||||||
* start : function(element, done, memo) { ... }
|
* Defines an animation hook that can be later used with {@link ngAnimate.$animate $animate} service and
|
||||||
|
* directives that use this service.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* module.animation('.animation-name', function($inject1, $inject2) {
|
||||||
|
* return {
|
||||||
|
* eventName : function(element, done) {
|
||||||
|
* //code to run the animation
|
||||||
|
* //once complete, then run done()
|
||||||
|
* return function cancellationFunction(element) {
|
||||||
|
* //code to cancel the animation
|
||||||
|
* }
|
||||||
|
* }
|
||||||
* }
|
* }
|
||||||
* })
|
* })
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* See {@link ng.$animationProvider#register $animationProvider.register()} and
|
* See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and
|
||||||
* {@link ng.directive:ngAnimate ngAnimate} for more information.
|
* {@link ngAnimate ngAnimate module} for more information.
|
||||||
*/
|
*/
|
||||||
animation: invokeLater('$animationProvider', 'register'),
|
animation: invokeLater('$animateProvider', 'register'),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc method
|
* @ngdoc method
|
||||||
|
|
|
||||||
112
src/ng/animate.js
Normal file
112
src/ng/animate.js
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc object
|
||||||
|
* @name ng.$animateProvider
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Default implementation of $animate that doesn't perform any animations, instead just synchronously performs DOM
|
||||||
|
* updates and calls done() callbacks.
|
||||||
|
*
|
||||||
|
* In order to enable animations the ngAnimate module has to be loaded.
|
||||||
|
*
|
||||||
|
* To see the functional implementation check out src/ngAnimate/animate.js
|
||||||
|
*/
|
||||||
|
var $AnimateProvider = ['$provide', function($provide) {
|
||||||
|
|
||||||
|
this.$$selectors = [];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name ng.$animateProvider#register
|
||||||
|
* @methodOf ng.$animateProvider
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Registers a new injectable animation factory function. The factory function produces the animation object which
|
||||||
|
* contains callback functions for each event that is expected to be animated.
|
||||||
|
*
|
||||||
|
* * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction` must be called once the
|
||||||
|
* element animation is complete. If a function is returned then the animation service will use this function to
|
||||||
|
* cancel the animation whenever a cancel event is triggered.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*<pre>
|
||||||
|
* return {
|
||||||
|
* eventFn : function(element, done) {
|
||||||
|
* //code to run the animation
|
||||||
|
* //once complete, then run done()
|
||||||
|
* return function cancellationFunction() {
|
||||||
|
* //code to cancel the animation
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*</pre>
|
||||||
|
*
|
||||||
|
* @param {string} name The name of the animation.
|
||||||
|
* @param {function} factory The factory function that will be executed to return the animation object.
|
||||||
|
*/
|
||||||
|
this.register = function(name, factory) {
|
||||||
|
var classes = name.substr(1).split('.');
|
||||||
|
name += '-animation';
|
||||||
|
this.$$selectors.push({
|
||||||
|
selectors : classes,
|
||||||
|
name : name
|
||||||
|
});
|
||||||
|
$provide.factory(name, factory);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$get = function() {
|
||||||
|
return {
|
||||||
|
enter : function(element, parent, after, done) {
|
||||||
|
var afterNode = after && after[after.length - 1];
|
||||||
|
var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
|
||||||
|
// IE does not like undefined so we have to pass null.
|
||||||
|
var afterNextSibling = (afterNode && afterNode.nextSibling) || null;
|
||||||
|
forEach(element, function(node) {
|
||||||
|
parentNode.insertBefore(node, afterNextSibling);
|
||||||
|
});
|
||||||
|
(done || noop)();
|
||||||
|
},
|
||||||
|
|
||||||
|
leave : function(element, done) {
|
||||||
|
element.remove();
|
||||||
|
(done || noop)();
|
||||||
|
},
|
||||||
|
|
||||||
|
move : function(element, parent, after, done) {
|
||||||
|
// Do not remove element before insert. Removing will cause data associated with the
|
||||||
|
// element to be dropped. Insert will implicitly do the remove.
|
||||||
|
this.enter(element, parent, after, done);
|
||||||
|
},
|
||||||
|
|
||||||
|
show : function(element, done) {
|
||||||
|
element.removeClass('ng-hide');
|
||||||
|
(done || noop)();
|
||||||
|
},
|
||||||
|
|
||||||
|
hide : function(element, done) {
|
||||||
|
element.addClass('ng-hide');
|
||||||
|
(done || noop)();
|
||||||
|
},
|
||||||
|
|
||||||
|
addClass : function(element, className, done) {
|
||||||
|
className = isString(className) ?
|
||||||
|
className :
|
||||||
|
isArray(className) ? className.join(' ') : '';
|
||||||
|
element.addClass(className);
|
||||||
|
(done || noop)();
|
||||||
|
},
|
||||||
|
|
||||||
|
removeClass : function(element, className, done) {
|
||||||
|
className = isString(className) ?
|
||||||
|
className :
|
||||||
|
isArray(className) ? className.join(' ') : '';
|
||||||
|
element.removeClass(className);
|
||||||
|
(done || noop)();
|
||||||
|
},
|
||||||
|
|
||||||
|
enabled : noop
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}];
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
/**
|
|
||||||
* @ngdoc object
|
|
||||||
* @name ng.$animationProvider
|
|
||||||
* @description
|
|
||||||
*
|
|
||||||
* The $AnimationProvider provider allows developers to register and access custom JavaScript animations directly inside
|
|
||||||
* of a module.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
$AnimationProvider.$inject = ['$provide'];
|
|
||||||
function $AnimationProvider($provide) {
|
|
||||||
var suffix = 'Animation';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name ng.$animation#register
|
|
||||||
* @methodOf ng.$animationProvider
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Registers a new injectable animation factory function. The factory function produces the animation object which
|
|
||||||
* has these two properties:
|
|
||||||
*
|
|
||||||
* * `setup`: `function(Element):*` A function which receives the starting state of the element. The purpose
|
|
||||||
* of this function is to get the element ready for animation. Optionally the function returns an memento which
|
|
||||||
* is passed to the `start` function.
|
|
||||||
* * `start`: `function(Element, doneFunction, *)` The element to animate, the `doneFunction` to be called on
|
|
||||||
* element animation completion, and an optional memento from the `setup` function.
|
|
||||||
*
|
|
||||||
* @param {string} name The name of the animation.
|
|
||||||
* @param {function} factory The factory function that will be executed to return the animation object.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
this.register = function(name, factory) {
|
|
||||||
$provide.factory(camelCase(name) + suffix, factory);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$get = ['$injector', function($injector) {
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name ng.$animation
|
|
||||||
* @function
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* The $animation service is used to retrieve any defined animation functions. When executed, the $animation service
|
|
||||||
* will return a object that contains the setup and start functions that were defined for the animation.
|
|
||||||
*
|
|
||||||
* @param {String} name Name of the animation function to retrieve. Animation functions are registered and stored
|
|
||||||
* inside of the AngularJS DI so a call to $animate('custom') is the same as injecting `customAnimation`
|
|
||||||
* via dependency injection.
|
|
||||||
* @return {Object} the animation object which contains the `setup` and `start` functions that perform the animation.
|
|
||||||
*/
|
|
||||||
return function $animation(name) {
|
|
||||||
if (name) {
|
|
||||||
var animationName = camelCase(name) + suffix;
|
|
||||||
if ($injector.has(animationName)) {
|
|
||||||
return $injector.get(animationName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
@ -1,446 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// NOTE: this is a pseudo directive.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc directive
|
|
||||||
* @name ng.directive:ngAnimate
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* The `ngAnimate` directive works as an attribute that is attached alongside pre-existing directives.
|
|
||||||
* It effects how the directive will perform DOM manipulation. This allows for complex animations to take place
|
|
||||||
* without burdening the directive which uses the animation with animation details. The built in directives
|
|
||||||
* `ngRepeat`, `ngInclude`, `ngSwitch`, `ngShow`, `ngHide` and `ngView` already accept `ngAnimate` directive.
|
|
||||||
* Custom directives can take advantage of animation through {@link ng.$animator $animator service}.
|
|
||||||
*
|
|
||||||
* Below is a more detailed breakdown of the supported callback events provided by pre-exisitng ng directives:
|
|
||||||
*
|
|
||||||
* | Directive | Supported Animations |
|
|
||||||
* |---------------------------------------------------------- |----------------------------------------------------|
|
|
||||||
* | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move |
|
|
||||||
* | {@link ngRoute.directive:ngView#animations ngView} | enter and leave |
|
|
||||||
* | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
|
|
||||||
* | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
|
|
||||||
* | {@link ng.directive:ngIf#animations ngIf} | enter and leave |
|
|
||||||
* | {@link ng.directive:ngShow#animations ngShow & ngHide} | show and hide |
|
|
||||||
*
|
|
||||||
* You can find out more information about animations upon visiting each directive page.
|
|
||||||
*
|
|
||||||
* Below is an example of a directive that makes use of the ngAnimate attribute:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* <!-- you can also use data-ng-animate, ng:animate or x-ng-animate as well -->
|
|
||||||
* <ANY ng-directive ng-animate="{event1: 'animation-name', event2: 'animation-name-2'}"></ANY>
|
|
||||||
*
|
|
||||||
* <!-- you can also use a short hand -->
|
|
||||||
* //!annotate="animation" ngAnimate|This *expands* to `{ enter: 'animation-enter', leave: 'animation-leave', ...}`</strong>
|
|
||||||
* <ANY ng-directive ng-animate=" 'animation' "></ANY>
|
|
||||||
*
|
|
||||||
* <!-- keep in mind that ng-animate can take expressions -->
|
|
||||||
* //!annotate="computeCurrentAnimation\(\)" Scope Function|This will be called each time the scope changes...
|
|
||||||
* <ANY ng-directive ng-animate=" computeCurrentAnimation() "></ANY>
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* The `event1` and `event2` attributes refer to the animation events specific to the directive that has been assigned.
|
|
||||||
*
|
|
||||||
* Keep in mind that if an animation is running, no child element of such animation can also be animated.
|
|
||||||
*
|
|
||||||
* <h2>CSS-defined Animations</h2>
|
|
||||||
* By default, ngAnimate attaches two CSS classes per animation event to the DOM element to achieve the animation.
|
|
||||||
* It is up to you, the developer, to ensure that the animations take place using cross-browser CSS3 transitions as
|
|
||||||
* well as CSS animations.
|
|
||||||
*
|
|
||||||
* The following code below demonstrates how to perform animations using **CSS transitions** with ngAnimate:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* <style type="text/css">
|
|
||||||
* /*
|
|
||||||
* The animate-enter CSS class is the event name that you
|
|
||||||
* have provided within the ngAnimate attribute.
|
|
||||||
* */
|
|
||||||
* .animate-enter {
|
|
||||||
* -webkit-transition: 1s linear all; /* Safari/Chrome */
|
|
||||||
* -moz-transition: 1s linear all; /* Firefox */
|
|
||||||
* -o-transition: 1s linear all; /* Opera */
|
|
||||||
* transition: 1s linear all; /* IE10+ and Future Browsers */
|
|
||||||
*
|
|
||||||
* /* The animation preparation code */
|
|
||||||
* opacity: 0;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* /*
|
|
||||||
* Keep in mind that you want to combine both CSS
|
|
||||||
* classes together to avoid any CSS-specificity
|
|
||||||
* conflicts
|
|
||||||
* */
|
|
||||||
* .animate-enter.animate-enter-active {
|
|
||||||
* /* The animation code itself */
|
|
||||||
* opacity: 1;
|
|
||||||
* }
|
|
||||||
* </style>
|
|
||||||
*
|
|
||||||
* <div ng-directive ng-animate="{enter: 'animate-enter'}"></div>
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* The following code below demonstrates how to perform animations using **CSS animations** with ngAnimate:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* <style type="text/css">
|
|
||||||
* .animate-enter {
|
|
||||||
* -webkit-animation: enter_sequence 1s linear; /* Safari/Chrome */
|
|
||||||
* -moz-animation: enter_sequence 1s linear; /* Firefox */
|
|
||||||
* -o-animation: enter_sequence 1s linear; /* Opera */
|
|
||||||
* animation: enter_sequence 1s linear; /* IE10+ and Future Browsers */
|
|
||||||
* }
|
|
||||||
* @-webkit-keyframes enter_sequence {
|
|
||||||
* from { opacity:0; }
|
|
||||||
* to { opacity:1; }
|
|
||||||
* }
|
|
||||||
* @-moz-keyframes enter_sequence {
|
|
||||||
* from { opacity:0; }
|
|
||||||
* to { opacity:1; }
|
|
||||||
* }
|
|
||||||
* @-o-keyframes enter_sequence {
|
|
||||||
* from { opacity:0; }
|
|
||||||
* to { opacity:1; }
|
|
||||||
* }
|
|
||||||
* @keyframes enter_sequence {
|
|
||||||
* from { opacity:0; }
|
|
||||||
* to { opacity:1; }
|
|
||||||
* }
|
|
||||||
* </style>
|
|
||||||
*
|
|
||||||
* <div ng-directive ng-animate="{enter: 'animate-enter'}"></div>
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* ngAnimate will first examine any CSS animation code and then fallback to using CSS transitions.
|
|
||||||
*
|
|
||||||
* Upon DOM mutation, the event class is added first, then the browser is allowed to reflow the content and then,
|
|
||||||
* the active class is added to trigger the animation. The ngAnimate directive will automatically extract the duration
|
|
||||||
* of the animation to determine when the animation ends. Once the animation is over then both CSS classes will be
|
|
||||||
* removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end
|
|
||||||
* immediately resulting in a DOM element that is at it's final state. This final state is when the DOM element
|
|
||||||
* has no CSS transition/animation classes surrounding it.
|
|
||||||
*
|
|
||||||
* <h2>JavaScript-defined Animations</h2>
|
|
||||||
* In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations to browsers that do not
|
|
||||||
* yet support them, then you can make use of JavaScript animations defined inside of your AngularJS module.
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* var ngModule = angular.module('YourApp', []);
|
|
||||||
* ngModule.animation('animate-enter', function() {
|
|
||||||
* return {
|
|
||||||
* setup : function(element) {
|
|
||||||
* //prepare the element for animation
|
|
||||||
* element.css({ 'opacity': 0 });
|
|
||||||
* var memo = "..."; //this value is passed to the start function
|
|
||||||
* return memo;
|
|
||||||
* },
|
|
||||||
* start : function(element, done, memo) {
|
|
||||||
* //start the animation
|
|
||||||
* element.animate({
|
|
||||||
* 'opacity' : 1
|
|
||||||
* }, function() {
|
|
||||||
* //call when the animation is complete
|
|
||||||
* done()
|
|
||||||
* });
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* });
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* As you can see, the JavaScript code follows a similar template to the CSS3 animations. Once defined, the animation
|
|
||||||
* can be used in the same way with the ngAnimate attribute. Keep in mind that, when using JavaScript-enabled
|
|
||||||
* animations, ngAnimate will also add in the same CSS classes that CSS-enabled animations do (even if you're not using
|
|
||||||
* CSS animations) to animated the element, but it will not attempt to find any CSS3 transition or animation duration/delay values.
|
|
||||||
* It will instead close off the animation once the provided done function is executed. So it's important that you
|
|
||||||
* make sure your animations remember to fire off the done function once the animations are complete.
|
|
||||||
*
|
|
||||||
* @param {expression} ngAnimate Used to configure the DOM manipulation animations.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
var $AnimatorProvider = function() {
|
|
||||||
var NG_ANIMATE_CONTROLLER = '$ngAnimateController';
|
|
||||||
var rootAnimateController = {running:true};
|
|
||||||
|
|
||||||
this.$get = ['$animation', '$window', '$sniffer', '$rootElement', '$rootScope',
|
|
||||||
function($animation, $window, $sniffer, $rootElement, $rootScope) {
|
|
||||||
$rootElement.data(NG_ANIMATE_CONTROLLER, rootAnimateController);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name ng.$animator
|
|
||||||
* @function
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* The $animator.create service provides the DOM manipulation API which is decorated with animations.
|
|
||||||
*
|
|
||||||
* @param {Scope} scope the scope for the ng-animate.
|
|
||||||
* @param {Attributes} attr the attributes object which contains the ngAnimate key / value pair. (The attributes are
|
|
||||||
* passed into the linking function of the directive using the `$animator`.)
|
|
||||||
* @return {object} the animator object which contains the enter, leave, move, show, hide and animate methods.
|
|
||||||
*/
|
|
||||||
var AnimatorService = function(scope, attrs) {
|
|
||||||
var animator = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name ng.animator#enter
|
|
||||||
* @methodOf ng.$animator
|
|
||||||
* @function
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Injects the element object into the DOM (inside of the parent element) and then runs the enter animation.
|
|
||||||
*
|
|
||||||
* @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
|
|
||||||
* @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the enter animation
|
|
||||||
* @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the enter animation
|
|
||||||
*/
|
|
||||||
animator.enter = animateActionFactory('enter', insert, noop);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name ng.animator#leave
|
|
||||||
* @methodOf ng.$animator
|
|
||||||
* @function
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Runs the leave animation operation and, upon completion, removes the element from the DOM.
|
|
||||||
*
|
|
||||||
* @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
|
|
||||||
* @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the leave animation
|
|
||||||
*/
|
|
||||||
animator.leave = animateActionFactory('leave', noop, remove);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name ng.animator#move
|
|
||||||
* @methodOf ng.$animator
|
|
||||||
* @function
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Fires the move DOM operation. Just before the animation starts, the animator will either append it into the parent container or
|
|
||||||
* add the element directly after the after element if present. Then the move animation will be run.
|
|
||||||
*
|
|
||||||
* @param {jQuery/jqLite element} element the element that will be the focus of the move animation
|
|
||||||
* @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the move animation
|
|
||||||
* @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the move animation
|
|
||||||
*/
|
|
||||||
animator.move = animateActionFactory('move', move, noop);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name ng.animator#show
|
|
||||||
* @methodOf ng.$animator
|
|
||||||
* @function
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Reveals the element by setting the CSS property `display` to `block` and then starts the show animation directly after.
|
|
||||||
*
|
|
||||||
* @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
|
|
||||||
*/
|
|
||||||
animator.show = animateActionFactory('show', show, noop);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name ng.animator#hide
|
|
||||||
* @methodOf ng.$animator
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Starts the hide animation first and sets the CSS `display` property to `none` upon completion.
|
|
||||||
*
|
|
||||||
* @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
|
|
||||||
*/
|
|
||||||
animator.hide = animateActionFactory('hide', noop, hide);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name ng.animator#animate
|
|
||||||
* @methodOf ng.$animator
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Triggers a custom animation event to be executed on the given element
|
|
||||||
*
|
|
||||||
* @param {string} event the name of the custom event
|
|
||||||
* @param {jQuery/jqLite element} element the element that will be animated
|
|
||||||
*/
|
|
||||||
animator.animate = function(event, element) {
|
|
||||||
animateActionFactory(event, noop, noop)(element);
|
|
||||||
}
|
|
||||||
return animator;
|
|
||||||
|
|
||||||
function animateActionFactory(type, beforeFn, afterFn) {
|
|
||||||
return function(element, parent, after) {
|
|
||||||
var ngAnimateValue = scope.$eval(attrs.ngAnimate);
|
|
||||||
var className = ngAnimateValue
|
|
||||||
? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type
|
|
||||||
: '';
|
|
||||||
var animationPolyfill = $animation(className);
|
|
||||||
var polyfillSetup = animationPolyfill && animationPolyfill.setup;
|
|
||||||
var polyfillStart = animationPolyfill && animationPolyfill.start;
|
|
||||||
var polyfillCancel = animationPolyfill && animationPolyfill.cancel;
|
|
||||||
|
|
||||||
if (!className) {
|
|
||||||
beforeFn(element, parent, after);
|
|
||||||
afterFn(element, parent, after);
|
|
||||||
} else {
|
|
||||||
var activeClassName = className + '-active';
|
|
||||||
|
|
||||||
if (!parent) {
|
|
||||||
parent = after ? after.parent() : element.parent();
|
|
||||||
}
|
|
||||||
var disabledAnimation = { running : true };
|
|
||||||
if ((!$sniffer.transitions && !polyfillSetup && !polyfillStart) ||
|
|
||||||
(parent.inheritedData(NG_ANIMATE_CONTROLLER) || disabledAnimation).running) {
|
|
||||||
beforeFn(element, parent, after);
|
|
||||||
afterFn(element, parent, after);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var animationData = element.data(NG_ANIMATE_CONTROLLER) || {};
|
|
||||||
if(animationData.running) {
|
|
||||||
(polyfillCancel || noop)(element);
|
|
||||||
animationData.done();
|
|
||||||
}
|
|
||||||
|
|
||||||
element.data(NG_ANIMATE_CONTROLLER, {running:true, done:done});
|
|
||||||
element.addClass(className);
|
|
||||||
beforeFn(element, parent, after);
|
|
||||||
if (element.length == 0) return done();
|
|
||||||
|
|
||||||
var memento = (polyfillSetup || noop)(element);
|
|
||||||
|
|
||||||
// $window.setTimeout(beginAnimation, 0); this was causing the element not to animate
|
|
||||||
// keep at 1 for animation dom rerender
|
|
||||||
$window.setTimeout(beginAnimation, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseMaxTime(str) {
|
|
||||||
var total = 0, values = isString(str) ? str.split(/\s*,\s*/) : [];
|
|
||||||
forEach(values, function(value) {
|
|
||||||
total = Math.max(parseFloat(value) || 0, total);
|
|
||||||
});
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
function beginAnimation() {
|
|
||||||
element.addClass(activeClassName);
|
|
||||||
if (polyfillStart) {
|
|
||||||
polyfillStart(element, done, memento);
|
|
||||||
} else if (isFunction($window.getComputedStyle)) {
|
|
||||||
//one day all browsers will have these properties
|
|
||||||
var w3cAnimationProp = 'animation';
|
|
||||||
var w3cTransitionProp = 'transition';
|
|
||||||
|
|
||||||
//but some still use vendor-prefixed styles
|
|
||||||
var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
|
|
||||||
var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
|
|
||||||
|
|
||||||
var durationKey = 'Duration',
|
|
||||||
delayKey = 'Delay',
|
|
||||||
animationIterationCountKey = 'IterationCount',
|
|
||||||
duration = 0;
|
|
||||||
|
|
||||||
//we want all the styles defined before and after
|
|
||||||
var ELEMENT_NODE = 1;
|
|
||||||
forEach(element, function(element) {
|
|
||||||
if (element.nodeType == ELEMENT_NODE) {
|
|
||||||
var elementStyles = $window.getComputedStyle(element) || {};
|
|
||||||
|
|
||||||
var transitionDelay = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + delayKey]),
|
|
||||||
parseMaxTime(elementStyles[vendorTransitionProp + delayKey]));
|
|
||||||
|
|
||||||
var animationDelay = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + delayKey]),
|
|
||||||
parseMaxTime(elementStyles[vendorAnimationProp + delayKey]));
|
|
||||||
|
|
||||||
var transitionDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
|
|
||||||
parseMaxTime(elementStyles[vendorTransitionProp + durationKey]));
|
|
||||||
|
|
||||||
var animationDuration = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + durationKey]),
|
|
||||||
parseMaxTime(elementStyles[vendorAnimationProp + durationKey]));
|
|
||||||
|
|
||||||
if(animationDuration > 0) {
|
|
||||||
animationDuration *= Math.max(parseInt(elementStyles[w3cAnimationProp + animationIterationCountKey]) || 0,
|
|
||||||
parseInt(elementStyles[vendorAnimationProp + animationIterationCountKey]) || 0,
|
|
||||||
1);
|
|
||||||
}
|
|
||||||
|
|
||||||
duration = Math.max(animationDelay + animationDuration,
|
|
||||||
transitionDelay + transitionDuration,
|
|
||||||
duration);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$window.setTimeout(done, duration * 1000);
|
|
||||||
} else {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function done() {
|
|
||||||
if(!done.run) {
|
|
||||||
done.run = true;
|
|
||||||
afterFn(element, parent, after);
|
|
||||||
element.removeClass(className);
|
|
||||||
element.removeClass(activeClassName);
|
|
||||||
element.removeData(NG_ANIMATE_CONTROLLER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function show(element) {
|
|
||||||
element.css('display', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
function hide(element) {
|
|
||||||
element.css('display', 'none');
|
|
||||||
}
|
|
||||||
|
|
||||||
function insert(element, parent, after) {
|
|
||||||
var afterNode = after && after[after.length - 1];
|
|
||||||
var parentNode = parent && parent[0] || afterNode && afterNode.parentNode;
|
|
||||||
var afterNextSibling = afterNode && afterNode.nextSibling;
|
|
||||||
forEach(element, function(node) {
|
|
||||||
if (afterNextSibling) {
|
|
||||||
parentNode.insertBefore(node, afterNextSibling);
|
|
||||||
} else {
|
|
||||||
parentNode.appendChild(node);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function remove(element) {
|
|
||||||
element.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
function move(element, parent, after) {
|
|
||||||
// Do not remove element before insert. Removing will cause data associated with the
|
|
||||||
// element to be dropped. Insert will implicitly do the remove.
|
|
||||||
insert(element, parent, after);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc function
|
|
||||||
* @name ng.animator#enabled
|
|
||||||
* @methodOf ng.$animator
|
|
||||||
* @function
|
|
||||||
*
|
|
||||||
* @param {Boolean=} If provided then set the animation on or off.
|
|
||||||
* @return {Boolean} Current animation state.
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* Globally enables/disables animations.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
AnimatorService.enabled = function(value) {
|
|
||||||
if (arguments.length) {
|
|
||||||
rootAnimateController.running = !value;
|
|
||||||
}
|
|
||||||
return !rootAnimateController.running;
|
|
||||||
};
|
|
||||||
|
|
||||||
return AnimatorService;
|
|
||||||
}];
|
|
||||||
};
|
|
||||||
|
|
@ -2,59 +2,72 @@
|
||||||
|
|
||||||
function classDirective(name, selector) {
|
function classDirective(name, selector) {
|
||||||
name = 'ngClass' + name;
|
name = 'ngClass' + name;
|
||||||
return ngDirective(function(scope, element, attr) {
|
return ['$animate', function($animate) {
|
||||||
var oldVal = undefined;
|
return {
|
||||||
|
restrict: 'AC',
|
||||||
|
link: function(scope, element, attr) {
|
||||||
|
var oldVal = undefined;
|
||||||
|
|
||||||
scope.$watch(attr[name], ngClassWatchAction, true);
|
scope.$watch(attr[name], ngClassWatchAction, true);
|
||||||
|
|
||||||
attr.$observe('class', function(value) {
|
attr.$observe('class', function(value) {
|
||||||
var ngClass = scope.$eval(attr[name]);
|
var ngClass = scope.$eval(attr[name]);
|
||||||
ngClassWatchAction(ngClass, ngClass);
|
ngClassWatchAction(ngClass, ngClass);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (name !== 'ngClass') {
|
if (name !== 'ngClass') {
|
||||||
scope.$watch('$index', function($index, old$index) {
|
scope.$watch('$index', function($index, old$index) {
|
||||||
var mod = $index & 1;
|
var mod = $index & 1;
|
||||||
if (mod !== old$index & 1) {
|
if (mod !== old$index & 1) {
|
||||||
if (mod === selector) {
|
if (mod === selector) {
|
||||||
addClass(scope.$eval(attr[name]));
|
addClass(scope.$eval(attr[name]));
|
||||||
} else {
|
} else {
|
||||||
removeClass(scope.$eval(attr[name]));
|
removeClass(scope.$eval(attr[name]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function ngClassWatchAction(newVal) {
|
||||||
|
if (selector === true || scope.$index % 2 === selector) {
|
||||||
|
if (oldVal && !equals(newVal,oldVal)) {
|
||||||
|
removeClass(oldVal);
|
||||||
|
}
|
||||||
|
addClass(newVal);
|
||||||
}
|
}
|
||||||
|
oldVal = copy(newVal);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function ngClassWatchAction(newVal) {
|
function removeClass(classVal) {
|
||||||
if (selector === true || scope.$index % 2 === selector) {
|
$animate.removeClass(element, flattenClasses(classVal));
|
||||||
if (oldVal && !equals(newVal,oldVal)) {
|
|
||||||
removeClass(oldVal);
|
|
||||||
}
|
}
|
||||||
addClass(newVal);
|
|
||||||
}
|
|
||||||
oldVal = copy(newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function removeClass(classVal) {
|
function addClass(classVal) {
|
||||||
if (isObject(classVal) && !isArray(classVal)) {
|
$animate.addClass(element, flattenClasses(classVal));
|
||||||
classVal = map(classVal, function(v, k) { if (v) return k });
|
}
|
||||||
}
|
|
||||||
element.removeClass(isArray(classVal) ? classVal.join(' ') : classVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
function flattenClasses(classVal) {
|
||||||
|
if(isArray(classVal)) {
|
||||||
|
return classVal.join(' ');
|
||||||
|
} else if (isObject(classVal)) {
|
||||||
|
var classes = [], i = 0;
|
||||||
|
forEach(classVal, function(v, k) {
|
||||||
|
if (v) {
|
||||||
|
classes.push(k);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return classes.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
function addClass(classVal) {
|
return classVal;
|
||||||
if (isObject(classVal) && !isArray(classVal)) {
|
};
|
||||||
classVal = map(classVal, function(v, k) { if (v) return k });
|
|
||||||
}
|
}
|
||||||
if (classVal) {
|
};
|
||||||
element.addClass(isArray(classVal) ? classVal.join(' ') : classVal);
|
}];
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -70,6 +83,10 @@ function classDirective(name, selector) {
|
||||||
* When the expression changes, the previously added classes are removed and only then the
|
* When the expression changes, the previously added classes are removed and only then the
|
||||||
* new classes are added.
|
* new classes are added.
|
||||||
*
|
*
|
||||||
|
* @animations
|
||||||
|
* add - happens just before the class is applied to the element
|
||||||
|
* remove - happens just before the class is removed from the element
|
||||||
|
*
|
||||||
* @element ANY
|
* @element ANY
|
||||||
* @param {expression} ngClass {@link guide/expression Expression} to eval. The result
|
* @param {expression} ngClass {@link guide/expression Expression} to eval. The result
|
||||||
* of the evaluation can be a string representing space delimited class
|
* of the evaluation can be a string representing space delimited class
|
||||||
|
|
@ -78,7 +95,7 @@ function classDirective(name, selector) {
|
||||||
* element.
|
* element.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
<example>
|
<example animations="true">
|
||||||
<file name="index.html">
|
<file name="index.html">
|
||||||
<input type="button" value="set" ng-click="myVar='my-class'">
|
<input type="button" value="set" ng-click="myVar='my-class'">
|
||||||
<input type="button" value="clear" ng-click="myVar=''">
|
<input type="button" value="clear" ng-click="myVar=''">
|
||||||
|
|
@ -86,8 +103,23 @@ function classDirective(name, selector) {
|
||||||
<span ng-class="myVar">Sample Text</span>
|
<span ng-class="myVar">Sample Text</span>
|
||||||
</file>
|
</file>
|
||||||
<file name="style.css">
|
<file name="style.css">
|
||||||
.my-class {
|
.my-class-add,
|
||||||
|
.my-class-remove {
|
||||||
|
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||||
|
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||||
|
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||||
|
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-class,
|
||||||
|
.my-class-add.my-class-add-active {
|
||||||
color: red;
|
color: red;
|
||||||
|
font-size:3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-class-remove.my-class-remove-active {
|
||||||
|
font-size:1.0em;
|
||||||
|
color:black;
|
||||||
}
|
}
|
||||||
</file>
|
</file>
|
||||||
<file name="scenario.js">
|
<file name="scenario.js">
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
* jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
|
* jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
|
||||||
* the added class will be lost because the original compiled state is used to regenerate the element.
|
* the added class will be lost because the original compiled state is used to regenerate the element.
|
||||||
*
|
*
|
||||||
* Additionally, you can provide animations via the ngAnimate attribute to animate the **enter**
|
* Additionally, you can provide animations via the ngAnimate module to animate the **enter**
|
||||||
* and **leave** effects.
|
* and **leave** effects.
|
||||||
*
|
*
|
||||||
* @animations
|
* @animations
|
||||||
|
|
@ -47,36 +47,32 @@
|
||||||
<file name="index.html">
|
<file name="index.html">
|
||||||
Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
|
Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
|
||||||
Show when checked:
|
Show when checked:
|
||||||
<span ng-if="checked" ng-animate="'example'">
|
<span ng-if="checked" class="example-if">
|
||||||
I'm removed when the checkbox is unchecked.
|
I'm removed when the checkbox is unchecked.
|
||||||
</span>
|
</span>
|
||||||
</file>
|
</file>
|
||||||
<file name="animations.css">
|
<file name="animations.css">
|
||||||
.example-leave, .example-enter {
|
.example-if.ng-enter,
|
||||||
|
.example-if.ng-leave {
|
||||||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||||
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||||
-ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
|
||||||
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-enter {
|
.example-if.ng-enter,
|
||||||
|
.example-if.ng-leave.ng-leave-active {
|
||||||
opacity:0;
|
opacity:0;
|
||||||
}
|
}
|
||||||
.example-enter.example-enter-active {
|
|
||||||
opacity:1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.example-leave {
|
.example-if.ng-enter.ng-enter-active,
|
||||||
|
.example-if.ng-leave {
|
||||||
opacity:1;
|
opacity:1;
|
||||||
}
|
}
|
||||||
.example-leave.example-leave-active {
|
|
||||||
opacity:0;
|
|
||||||
}
|
|
||||||
</file>
|
</file>
|
||||||
</example>
|
</example>
|
||||||
*/
|
*/
|
||||||
var ngIfDirective = ['$animator', function($animator) {
|
var ngIfDirective = ['$animate', function($animate) {
|
||||||
return {
|
return {
|
||||||
transclude: 'element',
|
transclude: 'element',
|
||||||
priority: 1000,
|
priority: 1000,
|
||||||
|
|
@ -84,11 +80,10 @@ var ngIfDirective = ['$animator', function($animator) {
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
compile: function (element, attr, transclude) {
|
compile: function (element, attr, transclude) {
|
||||||
return function ($scope, $element, $attr) {
|
return function ($scope, $element, $attr) {
|
||||||
var animate = $animator($scope, $attr);
|
|
||||||
var childElement, childScope;
|
var childElement, childScope;
|
||||||
$scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
|
$scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
|
||||||
if (childElement) {
|
if (childElement) {
|
||||||
animate.leave(childElement);
|
$animate.leave(childElement);
|
||||||
childElement = undefined;
|
childElement = undefined;
|
||||||
}
|
}
|
||||||
if (childScope) {
|
if (childScope) {
|
||||||
|
|
@ -99,7 +94,7 @@ var ngIfDirective = ['$animator', function($animator) {
|
||||||
childScope = $scope.$new();
|
childScope = $scope.$new();
|
||||||
transclude(childScope, function (clone) {
|
transclude(childScope, function (clone) {
|
||||||
childElement = clone;
|
childElement = clone;
|
||||||
animate.enter(clone, $element.parent(), $element);
|
$animate.enter(clone, $element.parent(), $element);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,6 @@
|
||||||
* (e.g. ngInclude won't work for cross-domain requests on all browsers and for `file://`
|
* (e.g. ngInclude won't work for cross-domain requests on all browsers and for `file://`
|
||||||
* access on some browsers)
|
* access on some browsers)
|
||||||
*
|
*
|
||||||
* Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter**
|
|
||||||
* and **leave** effects.
|
|
||||||
*
|
|
||||||
* @animations
|
* @animations
|
||||||
* enter - happens just after the ngInclude contents change and a new DOM element is created and injected into the ngInclude container
|
* enter - happens just after the ngInclude contents change and a new DOM element is created and injected into the ngInclude container
|
||||||
* leave - happens just after the ngInclude contents change and just before the former contents are removed from the DOM
|
* leave - happens just after the ngInclude contents change and just before the former contents are removed from the DOM
|
||||||
|
|
@ -143,8 +140,8 @@
|
||||||
* @description
|
* @description
|
||||||
* Emitted every time the ngInclude content is reloaded.
|
* Emitted every time the ngInclude content is reloaded.
|
||||||
*/
|
*/
|
||||||
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', '$animator', '$sce',
|
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', '$animate', '$sce',
|
||||||
function($http, $templateCache, $anchorScroll, $compile, $animator, $sce) {
|
function($http, $templateCache, $anchorScroll, $compile, $animate, $sce) {
|
||||||
return {
|
return {
|
||||||
restrict: 'ECA',
|
restrict: 'ECA',
|
||||||
terminal: true,
|
terminal: true,
|
||||||
|
|
@ -154,7 +151,6 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
|
||||||
autoScrollExp = attr.autoscroll;
|
autoScrollExp = attr.autoscroll;
|
||||||
|
|
||||||
return function(scope, element, attr) {
|
return function(scope, element, attr) {
|
||||||
var animate = $animator(scope, attr);
|
|
||||||
var changeCounter = 0,
|
var changeCounter = 0,
|
||||||
childScope;
|
childScope;
|
||||||
|
|
||||||
|
|
@ -163,7 +159,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
|
||||||
childScope.$destroy();
|
childScope.$destroy();
|
||||||
childScope = null;
|
childScope = null;
|
||||||
}
|
}
|
||||||
animate.leave(element.contents(), element);
|
$animate.leave(element.contents());
|
||||||
};
|
};
|
||||||
|
|
||||||
scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
|
scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
|
||||||
|
|
@ -175,11 +171,11 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
|
||||||
|
|
||||||
if (childScope) childScope.$destroy();
|
if (childScope) childScope.$destroy();
|
||||||
childScope = scope.$new();
|
childScope = scope.$new();
|
||||||
animate.leave(element.contents(), element);
|
$animate.leave(element.contents());
|
||||||
|
|
||||||
var contents = jqLite('<div/>').html(response).contents();
|
var contents = jqLite('<div/>').html(response).contents();
|
||||||
|
|
||||||
animate.enter(contents, element);
|
$animate.enter(contents, element);
|
||||||
$compile(contents)(childScope);
|
$compile(contents)(childScope);
|
||||||
|
|
||||||
if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
|
if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,6 @@
|
||||||
* | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
|
* | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
|
||||||
* | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
|
* | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
|
||||||
*
|
*
|
||||||
* Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter**,
|
|
||||||
* **leave** and **move** effects.
|
|
||||||
*
|
|
||||||
*
|
*
|
||||||
* # Special repeat start and end points
|
* # Special repeat start and end points
|
||||||
* To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
|
* To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
|
||||||
|
|
@ -131,46 +128,40 @@
|
||||||
I have {{friends.length}} friends. They are:
|
I have {{friends.length}} friends. They are:
|
||||||
<input type="search" ng-model="q" placeholder="filter friends..." />
|
<input type="search" ng-model="q" placeholder="filter friends..." />
|
||||||
<ul>
|
<ul>
|
||||||
<li ng-repeat="friend in friends | filter:q"
|
<li class="animate-repeat" ng-repeat="friend in friends | filter:q">
|
||||||
ng-animate="{enter: 'example-repeat-enter',
|
|
||||||
leave: 'example-repeat-leave',
|
|
||||||
move: 'example-repeat-move'}">
|
|
||||||
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
|
[{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</file>
|
</file>
|
||||||
<file name="animations.css">
|
<file name="animations.css">
|
||||||
.example-repeat-enter,
|
.animate-repeat {
|
||||||
.example-repeat-leave,
|
|
||||||
.example-repeat-move {
|
|
||||||
-webkit-transition:all linear 0.5s;
|
-webkit-transition:all linear 0.5s;
|
||||||
-moz-transition:all linear 0.5s;
|
-moz-transition:all linear 0.5s;
|
||||||
-ms-transition:all linear 0.5s;
|
|
||||||
-o-transition:all linear 0.5s;
|
-o-transition:all linear 0.5s;
|
||||||
transition:all linear 0.5s;
|
transition:all linear 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-repeat-enter {
|
.animate-repeat.ng-enter {
|
||||||
line-height:0;
|
line-height:0;
|
||||||
opacity:0;
|
opacity:0;
|
||||||
}
|
}
|
||||||
.example-repeat-enter.example-repeat-enter-active {
|
.animate-repeat.ng-enter.ng-enter-active {
|
||||||
line-height:20px;
|
line-height:20px;
|
||||||
opacity:1;
|
opacity:1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-repeat-leave {
|
.animate-repeat.ng-leave {
|
||||||
opacity:1;
|
opacity:1;
|
||||||
line-height:20px;
|
line-height:20px;
|
||||||
}
|
}
|
||||||
.example-repeat-leave.example-repeat-leave-active {
|
.animate-repeat.ng-leave.ng-leave-active {
|
||||||
opacity:0;
|
opacity:0;
|
||||||
line-height:0;
|
line-height:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-repeat-move { }
|
.animate-repeat.ng-move { }
|
||||||
.example-repeat-move.example-repeat-move-active { }
|
.animate-repeat.ng-move.ng-move-active { }
|
||||||
</file>
|
</file>
|
||||||
<file name="scenario.js">
|
<file name="scenario.js">
|
||||||
it('should render initial data set', function() {
|
it('should render initial data set', function() {
|
||||||
|
|
@ -195,7 +186,7 @@
|
||||||
</file>
|
</file>
|
||||||
</example>
|
</example>
|
||||||
*/
|
*/
|
||||||
var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
|
var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||||
var NG_REMOVED = '$$NG_REMOVED';
|
var NG_REMOVED = '$$NG_REMOVED';
|
||||||
var ngRepeatMinErr = minErr('ngRepeat');
|
var ngRepeatMinErr = minErr('ngRepeat');
|
||||||
return {
|
return {
|
||||||
|
|
@ -204,7 +195,6 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
|
||||||
terminal: true,
|
terminal: true,
|
||||||
compile: function(element, attr, linker) {
|
compile: function(element, attr, linker) {
|
||||||
return function($scope, $element, $attr){
|
return function($scope, $element, $attr){
|
||||||
var animate = $animator($scope, $attr);
|
|
||||||
var expression = $attr.ngRepeat;
|
var expression = $attr.ngRepeat;
|
||||||
var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
|
var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
|
||||||
trackByExp, trackByExpGetter, trackByIdFn, trackByIdArrayFn, trackByIdObjFn, lhs, rhs, valueIdentifier, keyIdentifier,
|
trackByExp, trackByExpGetter, trackByIdFn, trackByIdArrayFn, trackByIdObjFn, lhs, rhs, valueIdentifier, keyIdentifier,
|
||||||
|
|
@ -316,7 +306,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
|
||||||
for (key in lastBlockMap) {
|
for (key in lastBlockMap) {
|
||||||
if (lastBlockMap.hasOwnProperty(key)) {
|
if (lastBlockMap.hasOwnProperty(key)) {
|
||||||
block = lastBlockMap[key];
|
block = lastBlockMap[key];
|
||||||
animate.leave(block.elements);
|
$animate.leave(block.elements);
|
||||||
forEach(block.elements, function(element) { element[NG_REMOVED] = true});
|
forEach(block.elements, function(element) { element[NG_REMOVED] = true});
|
||||||
block.scope.$destroy();
|
block.scope.$destroy();
|
||||||
}
|
}
|
||||||
|
|
@ -342,7 +332,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
|
||||||
// do nothing
|
// do nothing
|
||||||
} else {
|
} else {
|
||||||
// existing item which got moved
|
// existing item which got moved
|
||||||
animate.move(block.elements, null, jqLite(previousNode));
|
$animate.move(block.elements, null, jqLite(previousNode));
|
||||||
}
|
}
|
||||||
previousNode = block.endNode;
|
previousNode = block.endNode;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -360,7 +350,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) {
|
||||||
|
|
||||||
if (!block.startNode) {
|
if (!block.startNode) {
|
||||||
linker(childScope, function(clone) {
|
linker(childScope, function(clone) {
|
||||||
animate.enter(clone, null, jqLite(previousNode));
|
$animate.enter(clone, null, jqLite(previousNode));
|
||||||
previousNode = clone;
|
previousNode = clone;
|
||||||
block.scope = childScope;
|
block.scope = childScope;
|
||||||
block.startNode = clone[0];
|
block.startNode = clone[0];
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@
|
||||||
* With ngHide this is the reverse whereas true values cause the element itself to become
|
* With ngHide this is the reverse whereas true values cause the element itself to become
|
||||||
* hidden.
|
* hidden.
|
||||||
*
|
*
|
||||||
* Additionally, you can also provide animations via the ngAnimate attribute to animate the **show**
|
|
||||||
* and **hide** effects.
|
|
||||||
*
|
*
|
||||||
* @animations
|
* @animations
|
||||||
* show - happens after the ngShow expression evaluates to a truthy value and the contents are set to visible
|
* show - happens after the ngShow expression evaluates to a truthy value and the contents are set to visible
|
||||||
|
|
@ -29,36 +27,37 @@
|
||||||
Click me: <input type="checkbox" ng-model="checked"><br/>
|
Click me: <input type="checkbox" ng-model="checked"><br/>
|
||||||
<div>
|
<div>
|
||||||
Show:
|
Show:
|
||||||
<span class="check-element"
|
<span class="check-element example-show-hide" ng-show="checked">
|
||||||
ng-show="checked"
|
|
||||||
ng-animate="{show: 'example-show', hide: 'example-hide'}">
|
|
||||||
<span class="icon-thumbs-up"></span> I show up when your checkbox is checked.
|
<span class="icon-thumbs-up"></span> I show up when your checkbox is checked.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Hide:
|
Hide:
|
||||||
<span class="check-element"
|
<span class="check-element example-show-hide" ng-hide="checked">
|
||||||
ng-hide="checked"
|
|
||||||
ng-animate="{show: 'example-show', hide: 'example-hide'}">
|
|
||||||
<span class="icon-thumbs-down"></span> I hide when your checkbox is checked.
|
<span class="icon-thumbs-down"></span> I hide when your checkbox is checked.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</file>
|
</file>
|
||||||
<file name="animations.css">
|
<file name="animations.css">
|
||||||
.example-show, .example-hide {
|
.example-show-hide {
|
||||||
-webkit-transition:all linear 0.5s;
|
-webkit-transition:all linear 0.5s;
|
||||||
-moz-transition:all linear 0.5s;
|
-moz-transition:all linear 0.5s;
|
||||||
-ms-transition:all linear 0.5s;
|
-ms-transition:all linear 0.5s;
|
||||||
-o-transition:all linear 0.5s;
|
-o-transition:all linear 0.5s;
|
||||||
transition:all linear 0.5s;
|
transition:all linear 0.5s;
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
.example-show-hide.ng-hide {
|
||||||
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-show {
|
.example-show-hide.ng-hide-remove {
|
||||||
|
display:block;
|
||||||
line-height:0;
|
line-height:0;
|
||||||
opacity:0;
|
opacity:0;
|
||||||
padding:0 10px;
|
padding:0 10px;
|
||||||
}
|
}
|
||||||
.example-show-active.example-show-active {
|
.example-show-hide.ng-hide-remove.ng-hide-remove-active {
|
||||||
line-height:20px;
|
line-height:20px;
|
||||||
opacity:1;
|
opacity:1;
|
||||||
padding:10px;
|
padding:10px;
|
||||||
|
|
@ -66,14 +65,14 @@
|
||||||
background:white;
|
background:white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-hide {
|
.example-show-hide.ng-hide-add {
|
||||||
line-height:20px;
|
line-height:20px;
|
||||||
opacity:1;
|
opacity:1;
|
||||||
padding:10px;
|
padding:10px;
|
||||||
border:1px solid black;
|
border:1px solid black;
|
||||||
background:white;
|
background:white;
|
||||||
}
|
}
|
||||||
.example-hide-active.example-hide-active {
|
.example-show-hide.ng-hide-add.ng-hide-add-active {
|
||||||
line-height:0;
|
line-height:0;
|
||||||
opacity:0;
|
opacity:0;
|
||||||
padding:0 10px;
|
padding:0 10px;
|
||||||
|
|
@ -98,12 +97,10 @@
|
||||||
</file>
|
</file>
|
||||||
</example>
|
</example>
|
||||||
*/
|
*/
|
||||||
//TODO(misko): refactor to remove element from the DOM
|
var ngShowDirective = ['$animate', function($animate) {
|
||||||
var ngShowDirective = ['$animator', function($animator) {
|
|
||||||
return function(scope, element, attr) {
|
return function(scope, element, attr) {
|
||||||
var animate = $animator(scope, attr);
|
|
||||||
scope.$watch(attr.ngShow, function ngShowWatchAction(value){
|
scope.$watch(attr.ngShow, function ngShowWatchAction(value){
|
||||||
animate[toBoolean(value) ? 'show' : 'hide'](element);
|
$animate[toBoolean(value) ? 'show' : 'hide'](element);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}];
|
}];
|
||||||
|
|
@ -121,9 +118,6 @@ var ngShowDirective = ['$animator', function($animator) {
|
||||||
* With ngHide this is the reverse whereas true values cause the element itself to become
|
* With ngHide this is the reverse whereas true values cause the element itself to become
|
||||||
* hidden.
|
* hidden.
|
||||||
*
|
*
|
||||||
* Additionally, you can also provide animations via the ngAnimate attribute to animate the **show**
|
|
||||||
* and **hide** effects.
|
|
||||||
*
|
|
||||||
* @animations
|
* @animations
|
||||||
* show - happens after the ngHide expression evaluates to a non truthy value and the contents are set to visible
|
* show - happens after the ngHide expression evaluates to a non truthy value and the contents are set to visible
|
||||||
* hide - happens after the ngHide expression evaluates to a truthy value and just before the contents are set to hidden
|
* hide - happens after the ngHide expression evaluates to a truthy value and just before the contents are set to hidden
|
||||||
|
|
@ -138,36 +132,36 @@ var ngShowDirective = ['$animator', function($animator) {
|
||||||
Click me: <input type="checkbox" ng-model="checked"><br/>
|
Click me: <input type="checkbox" ng-model="checked"><br/>
|
||||||
<div>
|
<div>
|
||||||
Show:
|
Show:
|
||||||
<span class="check-element"
|
<span class="check-element example-show-hide" ng-show="checked">
|
||||||
ng-show="checked"
|
|
||||||
ng-animate="{show: 'example-show', hide: 'example-hide'}">
|
|
||||||
<span class="icon-thumbs-up"></span> I show up when your checkbox is checked.
|
<span class="icon-thumbs-up"></span> I show up when your checkbox is checked.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Hide:
|
Hide:
|
||||||
<span class="check-element"
|
<span class="check-element example-show-hide" ng-hide="checked">
|
||||||
ng-hide="checked"
|
|
||||||
ng-animate="{show: 'example-show', hide: 'example-hide'}">
|
|
||||||
<span class="icon-thumbs-down"></span> I hide when your checkbox is checked.
|
<span class="icon-thumbs-down"></span> I hide when your checkbox is checked.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</file>
|
</file>
|
||||||
<file name="animations.css">
|
<file name="animations.css">
|
||||||
.example-show, .example-hide {
|
.example-show-hide {
|
||||||
-webkit-transition:all linear 0.5s;
|
-webkit-transition:all linear 0.5s;
|
||||||
-moz-transition:all linear 0.5s;
|
-moz-transition:all linear 0.5s;
|
||||||
-ms-transition:all linear 0.5s;
|
|
||||||
-o-transition:all linear 0.5s;
|
-o-transition:all linear 0.5s;
|
||||||
transition:all linear 0.5s;
|
transition:all linear 0.5s;
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
.example-show-hide.ng-hide {
|
||||||
|
display:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-show {
|
.example-show-hide.ng-hide-remove {
|
||||||
|
display:block;
|
||||||
line-height:0;
|
line-height:0;
|
||||||
opacity:0;
|
opacity:0;
|
||||||
padding:0 10px;
|
padding:0 10px;
|
||||||
}
|
}
|
||||||
.example-show.example-show-active {
|
.example-show-hide.ng-hide-remove.ng-hide-remove-active {
|
||||||
line-height:20px;
|
line-height:20px;
|
||||||
opacity:1;
|
opacity:1;
|
||||||
padding:10px;
|
padding:10px;
|
||||||
|
|
@ -175,14 +169,14 @@ var ngShowDirective = ['$animator', function($animator) {
|
||||||
background:white;
|
background:white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-hide {
|
.example-show-hide.ng-hide-add {
|
||||||
line-height:20px;
|
line-height:20px;
|
||||||
opacity:1;
|
opacity:1;
|
||||||
padding:10px;
|
padding:10px;
|
||||||
border:1px solid black;
|
border:1px solid black;
|
||||||
background:white;
|
background:white;
|
||||||
}
|
}
|
||||||
.example-hide.example-hide-active {
|
.example-show-hide.ng-hide-add.ng-hide-add-active {
|
||||||
line-height:0;
|
line-height:0;
|
||||||
opacity:0;
|
opacity:0;
|
||||||
padding:0 10px;
|
padding:0 10px;
|
||||||
|
|
@ -207,12 +201,10 @@ var ngShowDirective = ['$animator', function($animator) {
|
||||||
</file>
|
</file>
|
||||||
</example>
|
</example>
|
||||||
*/
|
*/
|
||||||
//TODO(misko): refactor to remove element from the DOM
|
var ngHideDirective = ['$animate', function($animate) {
|
||||||
var ngHideDirective = ['$animator', function($animator) {
|
|
||||||
return function(scope, element, attr) {
|
return function(scope, element, attr) {
|
||||||
var animate = $animator(scope, attr);
|
|
||||||
scope.$watch(attr.ngHide, function ngHideWatchAction(value){
|
scope.$watch(attr.ngHide, function ngHideWatchAction(value){
|
||||||
animate[toBoolean(value) ? 'hide' : 'show'](element);
|
$animate[toBoolean(value) ? 'hide' : 'show'](element);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}];
|
}];
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,6 @@
|
||||||
* expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
|
* expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
|
||||||
* attribute is displayed.
|
* attribute is displayed.
|
||||||
*
|
*
|
||||||
* Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter**
|
|
||||||
* and **leave** effects.
|
|
||||||
*
|
|
||||||
* @animations
|
* @animations
|
||||||
* enter - happens after the ngSwtich contents change and the matched child element is placed inside the container
|
* enter - happens after the ngSwtich contents change and the matched child element is placed inside the container
|
||||||
* leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
|
* leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
|
||||||
|
|
@ -55,9 +52,8 @@
|
||||||
<tt>selection={{selection}}</tt>
|
<tt>selection={{selection}}</tt>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div
|
<div
|
||||||
class="example-animate-container"
|
class="example-animate-container animate-switch"
|
||||||
ng-switch on="selection"
|
ng-switch on="selection">
|
||||||
ng-animate="{enter: 'example-enter', leave: 'example-leave'}">
|
|
||||||
<div ng-switch-when="settings">Settings Div</div>
|
<div ng-switch-when="settings">Settings Div</div>
|
||||||
<div ng-switch-when="home">Home Span</div>
|
<div ng-switch-when="home">Home Span</div>
|
||||||
<div ng-switch-default>default</div>
|
<div ng-switch-default>default</div>
|
||||||
|
|
@ -71,10 +67,9 @@
|
||||||
}
|
}
|
||||||
</file>
|
</file>
|
||||||
<file name="animations.css">
|
<file name="animations.css">
|
||||||
.example-leave, .example-enter {
|
.animate-switch > * {
|
||||||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||||
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||||
-ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
|
||||||
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||||
|
|
||||||
|
|
@ -90,17 +85,17 @@
|
||||||
padding:10px;
|
padding:10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-enter {
|
.animate-switch > .ng-enter {
|
||||||
top:-50px;
|
top:-50px;
|
||||||
}
|
}
|
||||||
.example-enter.example-enter-active {
|
.animate-switch > .ng-enter.ng-enter-active {
|
||||||
top:0;
|
top:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-leave {
|
.animate-switch > .ng-leave {
|
||||||
top:0;
|
top:0;
|
||||||
}
|
}
|
||||||
.example-leave.example-leave-active {
|
.animate-switch > .ng-leave.ng-leave-active {
|
||||||
top:50px;
|
top:50px;
|
||||||
}
|
}
|
||||||
</file>
|
</file>
|
||||||
|
|
@ -119,7 +114,7 @@
|
||||||
</file>
|
</file>
|
||||||
</example>
|
</example>
|
||||||
*/
|
*/
|
||||||
var ngSwitchDirective = ['$animator', function($animator) {
|
var ngSwitchDirective = ['$animate', function($animate) {
|
||||||
return {
|
return {
|
||||||
restrict: 'EA',
|
restrict: 'EA',
|
||||||
require: 'ngSwitch',
|
require: 'ngSwitch',
|
||||||
|
|
@ -129,7 +124,6 @@ var ngSwitchDirective = ['$animator', function($animator) {
|
||||||
this.cases = {};
|
this.cases = {};
|
||||||
}],
|
}],
|
||||||
link: function(scope, element, attr, ngSwitchController) {
|
link: function(scope, element, attr, ngSwitchController) {
|
||||||
var animate = $animator(scope, attr);
|
|
||||||
var watchExpr = attr.ngSwitch || attr.on,
|
var watchExpr = attr.ngSwitch || attr.on,
|
||||||
selectedTranscludes,
|
selectedTranscludes,
|
||||||
selectedElements,
|
selectedElements,
|
||||||
|
|
@ -138,7 +132,7 @@ var ngSwitchDirective = ['$animator', function($animator) {
|
||||||
scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
|
scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
|
||||||
for (var i= 0, ii=selectedScopes.length; i<ii; i++) {
|
for (var i= 0, ii=selectedScopes.length; i<ii; i++) {
|
||||||
selectedScopes[i].$destroy();
|
selectedScopes[i].$destroy();
|
||||||
animate.leave(selectedElements[i]);
|
$animate.leave(selectedElements[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedElements = [];
|
selectedElements = [];
|
||||||
|
|
@ -153,7 +147,7 @@ var ngSwitchDirective = ['$animator', function($animator) {
|
||||||
var anchor = selectedTransclude.element;
|
var anchor = selectedTransclude.element;
|
||||||
|
|
||||||
selectedElements.push(caseElement);
|
selectedElements.push(caseElement);
|
||||||
animate.enter(caseElement, anchor.parent(), anchor);
|
$animate.enter(caseElement, anchor.parent(), anchor);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
714
src/ngAnimate/animate.js
Normal file
714
src/ngAnimate/animate.js
Normal file
|
|
@ -0,0 +1,714 @@
|
||||||
|
/**
|
||||||
|
* @ngdoc overview
|
||||||
|
* @name ngAnimate
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* ngAnimate
|
||||||
|
* =========
|
||||||
|
*
|
||||||
|
* The ngAnimate module is an optional module that comes packed with AngularJS that can be included within an AngularJS
|
||||||
|
* application to provide support for CSS and JavaScript animation hooks.
|
||||||
|
*
|
||||||
|
* To make use of animations with AngularJS, the `angular-animate.js` JavaScript file must be included into your application
|
||||||
|
* and the `ngAnimate` module must be included as a dependency.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* angular.module('App', ['ngAnimate']);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Then, to see animations in action, all that is required is to define the appropriate CSS classes
|
||||||
|
* or to register a JavaScript animation via the $animation service. The directives that support animation automatically are:
|
||||||
|
* `ngRepeat`, `ngInclude`, `ngSwitch`, `ngShow`, `ngHide` and `ngView`. Custom directives can take advantage of animation
|
||||||
|
* by using the `$animate` service.
|
||||||
|
*
|
||||||
|
* Below is a more detailed breakdown of the supported animation events provided by pre-existing ng directives:
|
||||||
|
*
|
||||||
|
* | Directive | Supported Animations |
|
||||||
|
* |========================================================== |====================================================|
|
||||||
|
* | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move |
|
||||||
|
* | {@link ngRoute.directive:ngView#animations ngView} | enter and leave |
|
||||||
|
* | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
|
||||||
|
* | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
|
||||||
|
* | {@link ng.directive:ngIf#animations ngIf} | enter and leave |
|
||||||
|
* | {@link ng.directive:ngShow#animations ngShow & ngHide} | show and hide |
|
||||||
|
* | {@link ng.directive:ngShow#animations ngClass} | add and remove |
|
||||||
|
*
|
||||||
|
* You can find out more information about animations upon visiting each directive page.
|
||||||
|
*
|
||||||
|
* Below is an example of how to apply animations to a directive that supports animation hooks:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <style type="text/css">
|
||||||
|
* .slide.ng-enter > div,
|
||||||
|
* .slide.ng-leave > div {
|
||||||
|
* -webkit-transition:0.5s linear all;
|
||||||
|
* -moz-transition:0.5s linear all;
|
||||||
|
* -o-transition:0.5s linear all;
|
||||||
|
* transition:0.5s linear all;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* .slide > .ng-enter { } /* starting animations for enter */
|
||||||
|
* .slide > .ng-enter-active { } /* terminal animations for enter */
|
||||||
|
* .slide > .ng-leave { } /* starting animations for leave */
|
||||||
|
* .slide > .ng-leave-active { } /* terminal animations for leave */
|
||||||
|
* </style>
|
||||||
|
*
|
||||||
|
* <!--
|
||||||
|
* the animate service will automatically add .ng-enter and .ng-leave to the element
|
||||||
|
* to trigger the CSS animations
|
||||||
|
* -->
|
||||||
|
* <ANY class="slide" ng-include="..."></ANY>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Keep in mind that if an animation is running, any child elements cannot be animated until the parent element's
|
||||||
|
* animation has completed.
|
||||||
|
*
|
||||||
|
* <h2>CSS-defined Animations</h2>
|
||||||
|
* The animate service will automatically apply two CSS classes to the animated element and these two CSS classes
|
||||||
|
* are designed to contain the start and end CSS styling. Both CSS transitions and keyframe animations are supported
|
||||||
|
* and can be used to play along with this naming structure.
|
||||||
|
*
|
||||||
|
* The following code below demonstrates how to perform animations using **CSS transitions** with Angular:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <style type="text/css">
|
||||||
|
* /*
|
||||||
|
* The animate class is apart of the element and the ng-enter class
|
||||||
|
* is attached to the element once the enter animation event is triggered
|
||||||
|
* */
|
||||||
|
* .reveal-animation.ng-enter {
|
||||||
|
* -webkit-transition: 1s linear all; /* Safari/Chrome */
|
||||||
|
* -moz-transition: 1s linear all; /* Firefox */
|
||||||
|
* -o-transition: 1s linear all; /* Opera */
|
||||||
|
* transition: 1s linear all; /* IE10+ and Future Browsers */
|
||||||
|
*
|
||||||
|
* /* The animation preparation code */
|
||||||
|
* opacity: 0;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* /*
|
||||||
|
* Keep in mind that you want to combine both CSS
|
||||||
|
* classes together to avoid any CSS-specificity
|
||||||
|
* conflicts
|
||||||
|
* */
|
||||||
|
* .reveal-animation.ng-enter.ng-enter-active {
|
||||||
|
* /* The animation code itself */
|
||||||
|
* opacity: 1;
|
||||||
|
* }
|
||||||
|
* </style>
|
||||||
|
*
|
||||||
|
* <div class="view-container">
|
||||||
|
* <div ng-view class="reveal-animation"></div>
|
||||||
|
* </div>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The following code below demonstrates how to perform animations using **CSS animations** with Angular:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <style type="text/css">
|
||||||
|
* .reveal-animation.ng-enter {
|
||||||
|
* -webkit-animation: enter_sequence 1s linear; /* Safari/Chrome */
|
||||||
|
* -moz-animation: enter_sequence 1s linear; /* Firefox */
|
||||||
|
* -o-animation: enter_sequence 1s linear; /* Opera */
|
||||||
|
* animation: enter_sequence 1s linear; /* IE10+ and Future Browsers */
|
||||||
|
* }
|
||||||
|
* @-webkit-keyframes enter_sequence {
|
||||||
|
* from { opacity:0; }
|
||||||
|
* to { opacity:1; }
|
||||||
|
* }
|
||||||
|
* @-moz-keyframes enter_sequence {
|
||||||
|
* from { opacity:0; }
|
||||||
|
* to { opacity:1; }
|
||||||
|
* }
|
||||||
|
* @-o-keyframes enter_sequence {
|
||||||
|
* from { opacity:0; }
|
||||||
|
* to { opacity:1; }
|
||||||
|
* }
|
||||||
|
* @keyframes enter_sequence {
|
||||||
|
* from { opacity:0; }
|
||||||
|
* to { opacity:1; }
|
||||||
|
* }
|
||||||
|
* </style>
|
||||||
|
*
|
||||||
|
* <div class="view-container">
|
||||||
|
* <div ng-view class="reveal-animation"></div>
|
||||||
|
* </div>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Both CSS3 animations and transitions can be used together and the animate service will figure out the correct duration and delay timing.
|
||||||
|
*
|
||||||
|
* Upon DOM mutation, the event class is added first (something like `ng-enter`), then the browser prepares itself to add
|
||||||
|
* the active class (in this case `ng-enter-active`) which then triggers the animation. The animation module will automatically
|
||||||
|
* detect the CSS code to determine when the animation ends. Once the animation is over then both CSS classes will be
|
||||||
|
* removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end
|
||||||
|
* immediately resulting in a DOM element that is at its final state. This final state is when the DOM element
|
||||||
|
* has no CSS transition/animation classes applied to it.
|
||||||
|
*
|
||||||
|
* <h2>JavaScript-defined Animations</h2>
|
||||||
|
* In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations on browsers that do not
|
||||||
|
* yet support CSS transitions/animations, then you can make use of JavaScript animations defined inside of your AngularJS module.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application.
|
||||||
|
* var ngModule = angular.module('YourApp', []);
|
||||||
|
* ngModule.animation('.my-crazy-animation', function() {
|
||||||
|
* return {
|
||||||
|
* enter: function(element, done) {
|
||||||
|
* //run the animation
|
||||||
|
* //!annotate Cancel Animation|This function (if provided) will perform the cancellation of the animation when another is triggered
|
||||||
|
* return function(element, done) {
|
||||||
|
* //cancel the animation
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* leave: function(element, done) { },
|
||||||
|
* move: function(element, done) { },
|
||||||
|
* show: function(element, done) { },
|
||||||
|
* hide: function(element, done) { },
|
||||||
|
* addClass: function(element, className, done) { },
|
||||||
|
* removeClass: function(element, className, done) { },
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* JavaScript-defined animations are created with a CSS-like class selector and a collection of events which are set to run
|
||||||
|
* a javascript callback function. When an animation is triggered, $animate will look for a matching animation which fits
|
||||||
|
* the element's CSS class attribute value and then run the matching animation event function (if found).
|
||||||
|
* In other words, if the CSS classes present on the animated element match any of the JavaScript animations then the callback function
|
||||||
|
* be executed. It should be also noted that only simple or compound class selectors are allowed.
|
||||||
|
*
|
||||||
|
* Within a JavaScript animation, an object containing various event callback animation functions is expected to be returned.
|
||||||
|
* As explained above, these callbacks are triggered based on the animation event. Therefore if an enter animation is run,
|
||||||
|
* and the JavaScript animation is found, then the enter callback will handle that animation (in addition to the CSS keyframe animation
|
||||||
|
* or transition code that is defined via a stylesheet).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
angular.module('ngAnimate', ['ng'])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc object
|
||||||
|
* @name ngAnimate.$animateProvider
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* The $AnimationProvider provider allows developers to register and access custom JavaScript animations directly inside
|
||||||
|
* of a module. When an animation is triggered, the $animate service will query the $animation function to find any
|
||||||
|
* animations that match the provided name value.
|
||||||
|
*
|
||||||
|
* Please visit the {@link ngAnimate ngAnimate} module overview page learn more about how to use animations in your application.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
.config(['$provide', '$animateProvider', function($provide, $animateProvider) {
|
||||||
|
var selectors = $animateProvider.$$selectors;
|
||||||
|
|
||||||
|
var NG_ANIMATE_STATE = '$$ngAnimateState';
|
||||||
|
var rootAnimateState = {running:true};
|
||||||
|
|
||||||
|
$provide.decorator('$animate', ['$delegate', '$injector', '$window', '$sniffer', '$rootElement',
|
||||||
|
function($delegate, $injector, $window, $sniffer, $rootElement) {
|
||||||
|
|
||||||
|
var noop = angular.noop;
|
||||||
|
var forEach = angular.forEach;
|
||||||
|
|
||||||
|
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
|
||||||
|
|
||||||
|
function lookup(name) {
|
||||||
|
if (name) {
|
||||||
|
var classes = name.substr(1).split('.'),
|
||||||
|
classMap = {};
|
||||||
|
|
||||||
|
for (var i = 0, ii = classes.length; i < ii; i++) {
|
||||||
|
classMap[classes[i]] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var matches = [];
|
||||||
|
for (var i = 0, ii = selectors.length; i < ii; i++) {
|
||||||
|
var selectorFactory = selectors[i];
|
||||||
|
var found = true;
|
||||||
|
for(var j = 0, jj = selectorFactory.selectors.length; j < jj; j++) {
|
||||||
|
var klass = selectorFactory.selectors[j];
|
||||||
|
if(klass.length > 0) {
|
||||||
|
found = found && classMap[klass];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(found) {
|
||||||
|
matches.push($injector.get(selectorFactory.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc object
|
||||||
|
* @name ngAnimate.$animate
|
||||||
|
* @requires $window, $sniffer, $rootElement
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* The `$animate` service provides animation detection support while performing DOM operations (enter, leave and move)
|
||||||
|
* as well as during addClass and removeClass operations. When any of these operations are run, the $animate service
|
||||||
|
* will examine any JavaScript-defined animations (which are defined by using the $animateProvider provider object)
|
||||||
|
* as well as any CSS-defined animations against the CSS classes present on the element once the DOM operation is run.
|
||||||
|
*
|
||||||
|
* The `$animate` service is used behind the scenes with pre-existing directives and animation with these directives
|
||||||
|
* will work out of the box without any extra configuration.
|
||||||
|
*
|
||||||
|
* Please visit the {@link ngAnimate ngAnimate} module overview page learn more about how to use animations in your application.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name ngAnimate.$animate#enter
|
||||||
|
* @methodOf ngAnimate.$animate
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Appends the element to the parent element that resides in the document and then runs the enter animation. Once
|
||||||
|
* the animation is started, the following CSS classes will be present on the element for the duration of the animation:
|
||||||
|
* <pre>
|
||||||
|
* .ng-enter
|
||||||
|
* .ng-enter-active
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Once the animation is complete then the done callback, if provided, will be also fired.
|
||||||
|
*
|
||||||
|
* @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
|
||||||
|
* @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the enter animation
|
||||||
|
* @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the enter animation
|
||||||
|
* @param {function()=} done callback function that will be called once the animation is complete
|
||||||
|
*/
|
||||||
|
enter : function(element, parent, after, done) {
|
||||||
|
$delegate.enter(element, parent, after);
|
||||||
|
performAnimation('enter', 'ng-enter', element, parent, after, done);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name ngAnimate.$animate#leave
|
||||||
|
* @methodOf ngAnimate.$animate
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Runs the leave animation operation and, upon completion, removes the element from the DOM. Once
|
||||||
|
* the animation is started, the following CSS classes will be added for the duration of the animation:
|
||||||
|
* <pre>
|
||||||
|
* .ng-leave
|
||||||
|
* .ng-leave-active
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Once the animation is complete then the done callback, if provided, will be also fired.
|
||||||
|
*
|
||||||
|
* @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
|
||||||
|
* @param {function()=} done callback function that will be called once the animation is complete
|
||||||
|
*/
|
||||||
|
leave : function(element, done) {
|
||||||
|
performAnimation('leave', 'ng-leave', element, null, null, function() {
|
||||||
|
$delegate.leave(element, done);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name ngAnimate.$animate#move
|
||||||
|
* @methodOf ngAnimate.$animate
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Fires the move DOM operation. Just before the animation starts, the animate service will either append it into the parent container or
|
||||||
|
* add the element directly after the after element if present. Then the move animation will be run. Once
|
||||||
|
* the animation is started, the following CSS classes will be added for the duration of the animation:
|
||||||
|
* <pre>
|
||||||
|
* .ng-move
|
||||||
|
* .ng-move-active
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Once the animation is complete then the done callback, if provided, will be also fired.
|
||||||
|
*
|
||||||
|
* @param {jQuery/jqLite element} element the element that will be the focus of the move animation
|
||||||
|
* @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the move animation
|
||||||
|
* @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the move animation
|
||||||
|
* @param {function()=} done callback function that will be called once the animation is complete
|
||||||
|
*/
|
||||||
|
move : function(element, parent, after, done) {
|
||||||
|
$delegate.move(element, parent, after);
|
||||||
|
performAnimation('move', 'ng-move', element, null, null, done);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name ngAnimate.$animate#show
|
||||||
|
* @methodOf ngAnimate.$animate
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Reveals the element by removing the `ng-hide` class thus performing an animation in the process. During
|
||||||
|
* this animation the CSS classes present on the element will be:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* .ng-hide //already on the element if hidden
|
||||||
|
* .ng-hide-remove
|
||||||
|
* .ng-hide-remove-active
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Once the animation is complete then all three CSS classes will be removed from the element.
|
||||||
|
* The done callback, if provided, will be also fired once the animation is complete.
|
||||||
|
*
|
||||||
|
* @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
|
||||||
|
* @param {function()=} done callback function that will be called once the animation is complete
|
||||||
|
*/
|
||||||
|
show : function(element, done) {
|
||||||
|
performAnimation('show', 'ng-hide-remove', element, null, null, function() {
|
||||||
|
$delegate.show(element, done);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name ngAnimate.$animate#hide
|
||||||
|
* @methodOf ngAnimate.$animate
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Sets the element to hidden by adding the `ng-hide` class it. However, before the class is applied
|
||||||
|
* the following CSS classes will be added temporarily to trigger any animation code:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* .ng-hide-add
|
||||||
|
* .ng-hide-add-active
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Once the animation is complete then both CSS classes will be removed and `ng-hide` will be added to the element.
|
||||||
|
* The done callback, if provided, will be also fired once the animation is complete.
|
||||||
|
*
|
||||||
|
* @param {jQuery/jqLite element} element the element that will be rendered visible or hidden
|
||||||
|
* @param {function()=} done callback function that will be called once the animation is complete
|
||||||
|
*/
|
||||||
|
hide : function(element, done) {
|
||||||
|
performAnimation('hide', 'ng-hide-add', element, null, null, function() {
|
||||||
|
$delegate.hide(element, done);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name ngAnimate.$animate#addClass
|
||||||
|
* @methodOf ngAnimate.$animate
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class.
|
||||||
|
* Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide
|
||||||
|
* the animate service the setup and active CSS classes in order to trigger the animation.
|
||||||
|
*
|
||||||
|
* For example, upon execution of:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* $animate.addClass(element, 'super');
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The generated CSS class values present on element will look like:
|
||||||
|
* <pre>
|
||||||
|
* .super-add
|
||||||
|
* .super-add-active
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* And upon completion, the generated animation CSS classes will be removed from the element, but the className
|
||||||
|
* value will be attached to the element. In this case, based on the previous example, the resulting CSS class for the element
|
||||||
|
* will look like so:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* .super
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Once this is complete, then the done callback, if provided, will be fired.
|
||||||
|
*
|
||||||
|
* @param {jQuery/jqLite element} element the element that will be animated
|
||||||
|
* @param {string} className the CSS class that will be animated and then attached to the element
|
||||||
|
* @param {function()=} done callback function that will be called once the animation is complete
|
||||||
|
*/
|
||||||
|
addClass : function(element, className, done) {
|
||||||
|
performAnimation('addClass', className, element, null, null, function() {
|
||||||
|
$delegate.addClass(element, className, done);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name ngAnimate.$animate#removeClass
|
||||||
|
* @methodOf ngAnimate.$animate
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value
|
||||||
|
* from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in
|
||||||
|
* order to provide the animate service the setup and active CSS classes in order to trigger the animation.
|
||||||
|
*
|
||||||
|
* For example, upon the execution of:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* $animate.removeClass(element, 'super');
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The CSS class values present on element during the animation will look like:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* .super //this was here from before
|
||||||
|
* .super-remove
|
||||||
|
* .super-remove-active
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* And upon completion, the generated animation CSS classes will be removed from the element as well as the
|
||||||
|
* className value that was provided (in this case {@type super} will be removed). Once that is complete, then, if provided,
|
||||||
|
* the done callback will be fired.
|
||||||
|
*
|
||||||
|
* @param {jQuery/jqLite element} element the element that will be animated
|
||||||
|
* @param {string} className the CSS class that will be animated and then removed from the element
|
||||||
|
* @param {function()=} done callback function that will be called once the animation is complete
|
||||||
|
*/
|
||||||
|
removeClass : function(element, className, done) {
|
||||||
|
performAnimation('removeClass', className, element, null, null, function() {
|
||||||
|
$delegate.removeClass(element, className, done);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name ngAnimate.$animate#enabled
|
||||||
|
* @methodOf ngAnimate.$animate
|
||||||
|
* @function
|
||||||
|
*
|
||||||
|
* @param {boolean=} If provided then set the animation on or off.
|
||||||
|
* @return {boolean} Current animation state.
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Globally enables/disables animations.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
enabled : function(value) {
|
||||||
|
if (arguments.length) {
|
||||||
|
rootAnimateState.running = !value;
|
||||||
|
}
|
||||||
|
return !rootAnimateState.running;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
all animations call this shared animation triggering function internally.
|
||||||
|
The event variable refers to the JavaScript animation event that will be triggered
|
||||||
|
and the className value is the name of the animation that will be applied within the
|
||||||
|
CSS code. Element, parent and after are provided DOM elements for the animation
|
||||||
|
and the onComplete callback will be fired once the animation is fully complete.
|
||||||
|
*/
|
||||||
|
function performAnimation(event, className, element, parent, after, onComplete) {
|
||||||
|
if(nothingToAnimate(className, element)) {
|
||||||
|
(onComplete || noop)();
|
||||||
|
} else {
|
||||||
|
var classes = ((element.attr('class') || '') + ' ' + className),
|
||||||
|
animationLookup = (' ' + classes).replace(/\s+/g,'.'),
|
||||||
|
animations = [];
|
||||||
|
forEach(lookup(animationLookup), function(animation, index) {
|
||||||
|
animations.push({
|
||||||
|
start : animation[event],
|
||||||
|
done : false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!parent) {
|
||||||
|
parent = after ? after.parent() : element.parent();
|
||||||
|
}
|
||||||
|
var disabledAnimation = { running : true };
|
||||||
|
|
||||||
|
//skip the animation if animations are disabled, a parent is already being animated
|
||||||
|
//or the element is not currently attached to the document body.
|
||||||
|
if ((parent.inheritedData(NG_ANIMATE_STATE) || disabledAnimation).running) {
|
||||||
|
//avoid calling done() since there is no need to remove any
|
||||||
|
//data or className values since this happens earlier than that
|
||||||
|
(onComplete || noop)();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var animationData = element.data(NG_ANIMATE_STATE) || {};
|
||||||
|
|
||||||
|
//if an animation is currently running on the element then lets take the steps
|
||||||
|
//to cancel that animation and fire any required callbacks
|
||||||
|
if(animationData.running) {
|
||||||
|
cancelAnimations(animationData.animations);
|
||||||
|
animationData.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
element.data(NG_ANIMATE_STATE, {
|
||||||
|
running:true,
|
||||||
|
animations:animations,
|
||||||
|
done:done
|
||||||
|
});
|
||||||
|
|
||||||
|
if(event == 'addClass') {
|
||||||
|
className = suffixClasses(className, '-add');
|
||||||
|
} else if(event == 'removeClass') {
|
||||||
|
className = suffixClasses(className, '-remove');
|
||||||
|
}
|
||||||
|
|
||||||
|
element.addClass(className);
|
||||||
|
|
||||||
|
forEach(animations, function(animation, index) {
|
||||||
|
var fn = function() {
|
||||||
|
progress(index);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(animation.start) {
|
||||||
|
if(event == 'addClass' || event == 'removeClass') {
|
||||||
|
animation.cancel = animation.start(element, className, fn);
|
||||||
|
} else {
|
||||||
|
animation.cancel = animation.start(element, fn);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function nothingToAnimate(className, element) {
|
||||||
|
return !(className && className.length > 0 && element.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelAnimations(animations) {
|
||||||
|
forEach(animations, function(animation) {
|
||||||
|
(animation.cancel || noop)(element);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function suffixClasses(classes, suffix) {
|
||||||
|
var className = '';
|
||||||
|
classes = angular.isArray(classes) ? classes : classes.split(/\s+/);
|
||||||
|
forEach(classes, function(klass, i) {
|
||||||
|
if(klass && klass.length > 0) {
|
||||||
|
className += (i > 0 ? ' ' : '') + klass + suffix;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
function progress(index) {
|
||||||
|
animations[index].done = true;
|
||||||
|
for(var i=0;i<animations.length;i++) {
|
||||||
|
if(!animations[i].done) return;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
function done() {
|
||||||
|
if(!done.hasBeenRun) {
|
||||||
|
done.hasBeenRun = true;
|
||||||
|
element.removeClass(className);
|
||||||
|
element.removeData(NG_ANIMATE_STATE);
|
||||||
|
(onComplete || noop)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
}])
|
||||||
|
|
||||||
|
.animation('', ['$window','$sniffer', function($window, $sniffer) {
|
||||||
|
return {
|
||||||
|
enter : function(element, done) {
|
||||||
|
return animate(element, 'ng-enter', done);
|
||||||
|
},
|
||||||
|
leave : function(element, done) {
|
||||||
|
return animate(element, 'ng-leave', done);
|
||||||
|
},
|
||||||
|
move : function(element, done) {
|
||||||
|
return animate(element, 'ng-move', done);
|
||||||
|
},
|
||||||
|
show : function(element, done) {
|
||||||
|
return animate(element, 'ng-hide-remove', done);
|
||||||
|
},
|
||||||
|
hide : function(element, done) {
|
||||||
|
return animate(element, 'ng-hide-add', done);
|
||||||
|
},
|
||||||
|
addClass : function(element, className, done) {
|
||||||
|
return animate(element, className, done);
|
||||||
|
},
|
||||||
|
removeClass : function(element, className, done) {
|
||||||
|
return animate(element, className, done);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function animate(element, className, done) {
|
||||||
|
if (!($sniffer.transitions || $sniffer.animations)) {
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
var activeClassName = '';
|
||||||
|
$window.setTimeout(startAnimation, 1);
|
||||||
|
|
||||||
|
//this acts as the cancellation function in case
|
||||||
|
//a new animation is triggered while another animation
|
||||||
|
//is still going on (otherwise the active className
|
||||||
|
//would still hang around until the timer is complete).
|
||||||
|
return onComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseMaxTime(str) {
|
||||||
|
var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : [];
|
||||||
|
forEach(values, function(value) {
|
||||||
|
total = Math.max(parseFloat(value) || 0, total);
|
||||||
|
});
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startAnimation() {
|
||||||
|
var duration = 0;
|
||||||
|
forEach(className.split(' '), function(klass, i) {
|
||||||
|
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
|
||||||
|
});
|
||||||
|
|
||||||
|
element.addClass(activeClassName);
|
||||||
|
|
||||||
|
//one day all browsers will have these properties
|
||||||
|
var w3cAnimationProp = 'animation';
|
||||||
|
var w3cTransitionProp = 'transition';
|
||||||
|
|
||||||
|
//but some still use vendor-prefixed styles
|
||||||
|
var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
|
||||||
|
var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
|
||||||
|
|
||||||
|
var durationKey = 'Duration',
|
||||||
|
delayKey = 'Delay',
|
||||||
|
animationIterationCountKey = 'IterationCount';
|
||||||
|
|
||||||
|
//we want all the styles defined before and after
|
||||||
|
var ELEMENT_NODE = 1;
|
||||||
|
forEach(element, function(element) {
|
||||||
|
if (element.nodeType == ELEMENT_NODE) {
|
||||||
|
var elementStyles = $window.getComputedStyle(element) || {};
|
||||||
|
|
||||||
|
var transitionDelay = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + delayKey]),
|
||||||
|
parseMaxTime(elementStyles[vendorTransitionProp + delayKey]));
|
||||||
|
|
||||||
|
var animationDelay = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + delayKey]),
|
||||||
|
parseMaxTime(elementStyles[vendorAnimationProp + delayKey]));
|
||||||
|
|
||||||
|
var transitionDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
|
||||||
|
parseMaxTime(elementStyles[vendorTransitionProp + durationKey]));
|
||||||
|
|
||||||
|
var animationDuration = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + durationKey]),
|
||||||
|
parseMaxTime(elementStyles[vendorAnimationProp + durationKey]));
|
||||||
|
|
||||||
|
if(animationDuration > 0) {
|
||||||
|
animationDuration *= Math.max(parseInt(elementStyles[w3cAnimationProp + animationIterationCountKey]) || 0,
|
||||||
|
parseInt(elementStyles[vendorAnimationProp + animationIterationCountKey]) || 0,
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
|
||||||
|
duration = Math.max(animationDelay + animationDuration,
|
||||||
|
transitionDelay + transitionDuration,
|
||||||
|
duration);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$window.setTimeout(onComplete, duration * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onComplete() {
|
||||||
|
element.removeClass(activeClassName);
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}]);
|
||||||
37
src/ngMock/angular-mocks.js
vendored
37
src/ngMock/angular-mocks.js
vendored
|
|
@ -627,6 +627,43 @@ angular.mock.$LogProvider = function() {
|
||||||
angular.mock.TzDate.prototype = Date.prototype;
|
angular.mock.TzDate.prototype = Date.prototype;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
angular.mock.animate = angular.module('mock.animate', ['ng'])
|
||||||
|
|
||||||
|
.config(['$provide', function($provide) {
|
||||||
|
|
||||||
|
$provide.decorator('$animate', function($delegate) {
|
||||||
|
var animate = {
|
||||||
|
queue : [],
|
||||||
|
enabled : $delegate.enabled,
|
||||||
|
process : function(name) {
|
||||||
|
var tick = animate.queue.shift();
|
||||||
|
expect(tick.method).toBe(name);
|
||||||
|
tick.fn();
|
||||||
|
return tick;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
forEach(['enter','leave','move','show','hide','addClass','removeClass'], function(method) {
|
||||||
|
animate[method] = function() {
|
||||||
|
var params = arguments;
|
||||||
|
animate.queue.push({
|
||||||
|
method : method,
|
||||||
|
params : params,
|
||||||
|
element : angular.isElement(params[0]) && params[0],
|
||||||
|
parent : angular.isElement(params[1]) && params[1],
|
||||||
|
after : angular.isElement(params[2]) && params[2],
|
||||||
|
fn : function() {
|
||||||
|
$delegate[method].apply($delegate, params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return animate;
|
||||||
|
});
|
||||||
|
|
||||||
|
}]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc function
|
* @ngdoc function
|
||||||
* @name angular.mock.createMockWindow
|
* @name angular.mock.createMockWindow
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,6 @@ ngRouteModule.directive('ngView', ngViewFactory);
|
||||||
* Every time the current route changes, the included view changes with it according to the
|
* Every time the current route changes, the included view changes with it according to the
|
||||||
* configuration of the `$route` service.
|
* configuration of the `$route` service.
|
||||||
*
|
*
|
||||||
* Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter**
|
|
||||||
* and **leave** effects.
|
|
||||||
*
|
|
||||||
* @animations
|
* @animations
|
||||||
* enter - happens just after the ngView contents are changed (when the new view DOM element is inserted into the DOM)
|
* enter - happens just after the ngView contents are changed (when the new view DOM element is inserted into the DOM)
|
||||||
* leave - happens just after the current ngView contents change and just before the former contents are removed from the DOM
|
* leave - happens just after the current ngView contents change and just before the former contents are removed from the DOM
|
||||||
|
|
@ -35,8 +32,8 @@ ngRouteModule.directive('ngView', ngViewFactory);
|
||||||
|
|
||||||
<div
|
<div
|
||||||
ng-view
|
ng-view
|
||||||
class="example-animate-container"
|
class="example-$animate-container"
|
||||||
ng-animate="{enter: 'example-enter', leave: 'example-leave'}"></div>
|
ng-$animate="{enter: 'example-enter', leave: 'example-leave'}"></div>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<pre>$location.path() = {{main.$location.path()}}</pre>
|
<pre>$location.path() = {{main.$location.path()}}</pre>
|
||||||
|
|
@ -71,12 +68,12 @@ ngRouteModule.directive('ngView', ngViewFactory);
|
||||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-animate-container {
|
.example-$animate-container {
|
||||||
position:relative;
|
position:relative;
|
||||||
height:100px;
|
height:100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-animate-container > * {
|
.example-$animate-container > * {
|
||||||
display:block;
|
display:block;
|
||||||
width:100%;
|
width:100%;
|
||||||
border-left:1px solid black;
|
border-left:1px solid black;
|
||||||
|
|
@ -162,15 +159,14 @@ ngRouteModule.directive('ngView', ngViewFactory);
|
||||||
* @description
|
* @description
|
||||||
* Emitted every time the ngView content is reloaded.
|
* Emitted every time the ngView content is reloaded.
|
||||||
*/
|
*/
|
||||||
ngViewFactory.$inject = ['$route', '$anchorScroll', '$compile', '$controller', '$animator'];
|
ngViewFactory.$inject = ['$route', '$anchorScroll', '$compile', '$controller', '$animate'];
|
||||||
function ngViewFactory( $route, $anchorScroll, $compile, $controller, $animator) {
|
function ngViewFactory( $route, $anchorScroll, $compile, $controller, $animate) {
|
||||||
return {
|
return {
|
||||||
restrict: 'ECA',
|
restrict: 'ECA',
|
||||||
terminal: true,
|
terminal: true,
|
||||||
link: function(scope, element, attr) {
|
link: function(scope, element, attr) {
|
||||||
var lastScope,
|
var lastScope,
|
||||||
onloadExp = attr.onload || '',
|
onloadExp = attr.onload || '';
|
||||||
animate = $animator(scope, attr);
|
|
||||||
|
|
||||||
scope.$on('$routeChangeSuccess', update);
|
scope.$on('$routeChangeSuccess', update);
|
||||||
update();
|
update();
|
||||||
|
|
@ -184,7 +180,7 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearContent() {
|
function clearContent() {
|
||||||
animate.leave(element.contents(), element);
|
$animate.leave(element.contents());
|
||||||
destroyLastScope();
|
destroyLastScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,7 +191,7 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
|
||||||
if (template) {
|
if (template) {
|
||||||
clearContent();
|
clearContent();
|
||||||
var enterElements = jqLite('<div></div>').html(template).contents();
|
var enterElements = jqLite('<div></div>').html(template).contents();
|
||||||
animate.enter(enterElements, element);
|
$animate.enter(enterElements, element);
|
||||||
|
|
||||||
var link = $compile(enterElements),
|
var link = $compile(enterElements),
|
||||||
current = $route.current,
|
current = $route.current,
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,23 @@ beforeEach(function() {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isNgElementHidden(element) {
|
||||||
|
return angular.element(element).hasClass('ng-hide');
|
||||||
|
};
|
||||||
|
|
||||||
this.addMatchers({
|
this.addMatchers({
|
||||||
toBeInvalid: cssMatcher('ng-invalid', 'ng-valid'),
|
toBeInvalid: cssMatcher('ng-invalid', 'ng-valid'),
|
||||||
toBeValid: cssMatcher('ng-valid', 'ng-invalid'),
|
toBeValid: cssMatcher('ng-valid', 'ng-invalid'),
|
||||||
toBeDirty: cssMatcher('ng-dirty', 'ng-pristine'),
|
toBeDirty: cssMatcher('ng-dirty', 'ng-pristine'),
|
||||||
toBePristine: cssMatcher('ng-pristine', 'ng-dirty'),
|
toBePristine: cssMatcher('ng-pristine', 'ng-dirty'),
|
||||||
|
toBeShown: function() {
|
||||||
|
this.message = valueFn("Expected element to not have 'ng-hide' class");
|
||||||
|
return !isNgElementHidden(this.actual);
|
||||||
|
},
|
||||||
|
toBeHidden: function() {
|
||||||
|
this.message = valueFn("Expected element to have 'ng-hide' class");
|
||||||
|
return isNgElementHidden(this.actual);
|
||||||
|
},
|
||||||
|
|
||||||
toEqual: function(expected) {
|
toEqual: function(expected) {
|
||||||
if (this.actual && this.actual.$$log) {
|
if (this.actual && this.actual.$$log) {
|
||||||
|
|
|
||||||
53
test/ng/animateSpec.js
Normal file
53
test/ng/animateSpec.js
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
describe("$animate", function() {
|
||||||
|
|
||||||
|
describe("without animation", function() {
|
||||||
|
beforeEach(inject(function($compile, _$rootElement_, $rootScope) {
|
||||||
|
element = $compile('<div></div>')($rootScope);
|
||||||
|
$rootElement = _$rootElement_;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("should add element at the start of enter animation", inject(function($animate, $compile, $rootScope) {
|
||||||
|
var child = $compile('<div></div>')($rootScope);
|
||||||
|
expect(element.contents().length).toBe(0);
|
||||||
|
$animate.enter(child, element);
|
||||||
|
expect(element.contents().length).toBe(1);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("should remove the element at the end of leave animation", inject(function($animate, $compile, $rootScope) {
|
||||||
|
var child = $compile('<div></div>')($rootScope);
|
||||||
|
element.append(child);
|
||||||
|
expect(element.contents().length).toBe(1);
|
||||||
|
$animate.leave(child);
|
||||||
|
expect(element.contents().length).toBe(0);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("should reorder the move animation", inject(function($animate, $compile, $rootScope) {
|
||||||
|
var child1 = $compile('<div>1</div>')($rootScope);
|
||||||
|
var child2 = $compile('<div>2</div>')($rootScope);
|
||||||
|
element.append(child1);
|
||||||
|
element.append(child2);
|
||||||
|
expect(element.text()).toBe('12');
|
||||||
|
$animate.move(child1, element, child2);
|
||||||
|
expect(element.text()).toBe('21');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("should animate the show animation event", inject(function($animate) {
|
||||||
|
element.addClass('ng-hide');
|
||||||
|
$animate.show(element);
|
||||||
|
expect(element).toBeShown();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("should animate the hide animation event", inject(function($animate) {
|
||||||
|
expect(element).toBeShown();
|
||||||
|
$animate.hide(element);
|
||||||
|
expect(element).toBeHidden();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("should still perform DOM operations even if animations are disabled", inject(function($animate) {
|
||||||
|
$animate.enabled(false);
|
||||||
|
expect(element).toBeShown();
|
||||||
|
$animate.hide(element);
|
||||||
|
expect(element).toBeHidden();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
describe('$animation', function() {
|
|
||||||
|
|
||||||
it('should allow animation registration', function() {
|
|
||||||
var noopCustom = function(){};
|
|
||||||
module(function($animationProvider) {
|
|
||||||
$animationProvider.register('noop-custom', valueFn(noopCustom));
|
|
||||||
});
|
|
||||||
inject(function($animation) {
|
|
||||||
expect($animation('noop-custom')).toBe(noopCustom);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
@ -1,773 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
describe("$animator", function() {
|
|
||||||
|
|
||||||
var body, element, $rootElement;
|
|
||||||
|
|
||||||
function html(html) {
|
|
||||||
body.append($rootElement);
|
|
||||||
$rootElement.html(html);
|
|
||||||
element = $rootElement.children().eq(0);
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
// we need to run animation on attached elements;
|
|
||||||
body = jqLite(document.body);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function(){
|
|
||||||
dealoc(body);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("enable / disable", function() {
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
module(function($animationProvider, $provide) {
|
|
||||||
$provide.value('$window', angular.mock.createMockWindow());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should disable and enable the animations", function() {
|
|
||||||
var initialState = null;
|
|
||||||
var animator;
|
|
||||||
|
|
||||||
angular.bootstrap(body, [function() {
|
|
||||||
return function($animator) {
|
|
||||||
animator = $animator;
|
|
||||||
initialState = $animator.enabled();
|
|
||||||
}
|
|
||||||
}]);
|
|
||||||
|
|
||||||
expect(initialState).toBe(false);
|
|
||||||
|
|
||||||
expect(animator.enabled()).toBe(true);
|
|
||||||
|
|
||||||
expect(animator.enabled(0)).toBe(false);
|
|
||||||
expect(animator.enabled()).toBe(false);
|
|
||||||
|
|
||||||
expect(animator.enabled(1)).toBe(true);
|
|
||||||
expect(animator.enabled()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("without animation", function() {
|
|
||||||
var window, animator;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
module(function($animationProvider, $provide) {
|
|
||||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
|
||||||
})
|
|
||||||
inject(function($animator, $compile, $rootScope, _$rootElement_) {
|
|
||||||
animator = $animator($rootScope, {});
|
|
||||||
element = $compile('<div></div>')($rootScope);
|
|
||||||
$rootElement = _$rootElement_;
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should add element at the start of enter animation", inject(function($animator, $compile, $rootScope) {
|
|
||||||
var child = $compile('<div></div>')($rootScope);
|
|
||||||
expect(element.contents().length).toBe(0);
|
|
||||||
animator.enter(child, element);
|
|
||||||
expect(element.contents().length).toBe(1);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should remove the element at the end of leave animation", inject(function($animator, $compile, $rootScope) {
|
|
||||||
var child = $compile('<div></div>')($rootScope);
|
|
||||||
element.append(child);
|
|
||||||
expect(element.contents().length).toBe(1);
|
|
||||||
animator.leave(child, element);
|
|
||||||
expect(element.contents().length).toBe(0);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should reorder the move animation", inject(function($animator, $compile, $rootScope) {
|
|
||||||
var child1 = $compile('<div>1</div>')($rootScope);
|
|
||||||
var child2 = $compile('<div>2</div>')($rootScope);
|
|
||||||
element.append(child1);
|
|
||||||
element.append(child2);
|
|
||||||
expect(element.text()).toBe('12');
|
|
||||||
animator.move(child1, element, child2);
|
|
||||||
expect(element.text()).toBe('21');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should animate the show animation event", inject(function() {
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
animator.show(element);
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should animate the hide animation event", inject(function() {
|
|
||||||
element.css('display','block');
|
|
||||||
expect(element.css('display')).toBe('block');
|
|
||||||
animator.hide(element);
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should still perform DOM operations even if animations are disabled", inject(function($animator) {
|
|
||||||
$animator.enabled(false);
|
|
||||||
element.css('display','block');
|
|
||||||
expect(element.css('display')).toBe('block');
|
|
||||||
animator.hide(element);
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("with polyfill", function() {
|
|
||||||
|
|
||||||
var child, after, window, animator;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
module(function($animationProvider, $provide) {
|
|
||||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
|
||||||
$animationProvider.register('custom', function() {
|
|
||||||
return {
|
|
||||||
start: function(element, done) {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$animationProvider.register('custom-delay', function() {
|
|
||||||
return {
|
|
||||||
start: function(element, done) {
|
|
||||||
window.setTimeout(done, 2000);
|
|
||||||
},
|
|
||||||
cancel : function(element) {
|
|
||||||
element.addClass('animation-cancelled');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$animationProvider.register('setup-memo', function() {
|
|
||||||
return {
|
|
||||||
setup: function(element) {
|
|
||||||
return "memento";
|
|
||||||
},
|
|
||||||
start: function(element, done, memento) {
|
|
||||||
element.text(memento);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
inject(function($animator, $compile, $rootScope, $rootElement) {
|
|
||||||
element = $compile('<div></div>')($rootScope);
|
|
||||||
child = $compile('<div></div>')($rootScope);
|
|
||||||
after = $compile('<div></div>')($rootScope);
|
|
||||||
$rootElement.append(element);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should animate the enter animation event", inject(function($animator, $rootScope) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{enter: \'custom\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(element.contents().length).toBe(0);
|
|
||||||
animator.enter(child, element);
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should animate the leave animation event", inject(function($animator, $rootScope) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{leave: \'custom\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
element.append(child);
|
|
||||||
expect(element.contents().length).toBe(1);
|
|
||||||
animator.leave(child, element);
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(element.contents().length).toBe(0);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should animate the move animation event", inject(function($animator, $compile, $rootScope) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{move: \'custom\'}'
|
|
||||||
});
|
|
||||||
$rootScope.$digest();
|
|
||||||
var child1 = $compile('<div>1</div>')($rootScope);
|
|
||||||
var child2 = $compile('<div>2</div>')($rootScope);
|
|
||||||
element.append(child1);
|
|
||||||
element.append(child2);
|
|
||||||
expect(element.text()).toBe('12');
|
|
||||||
animator.move(child1, element, child2);
|
|
||||||
expect(element.text()).toBe('21');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should animate the show animation event", inject(function($animator, $rootScope) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'custom\'}'
|
|
||||||
});
|
|
||||||
$rootScope.$digest();
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
animator.show(element);
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should animate the hide animation event", inject(function($animator, $rootScope) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{hide: \'custom\'}'
|
|
||||||
});
|
|
||||||
$rootScope.$digest();
|
|
||||||
element.css('display','block');
|
|
||||||
expect(element.css('display')).toBe('block');
|
|
||||||
animator.hide(element);
|
|
||||||
expect(element.css('display')).toBe('block');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should assign the ngAnimate string to all events if a string is given",
|
|
||||||
inject(function($animator, $sniffer, $rootScope) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
if (!$sniffer.transitions) return;
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '"custom"'
|
|
||||||
});
|
|
||||||
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
//enter
|
|
||||||
animator.enter(child, element);
|
|
||||||
expect(child.attr('class')).toContain('custom-enter');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(child.attr('class')).toContain('custom-enter-active');
|
|
||||||
window.setTimeout.expect(0).process();
|
|
||||||
|
|
||||||
//leave
|
|
||||||
element.append(after);
|
|
||||||
animator.move(child, element, after);
|
|
||||||
expect(child.attr('class')).toContain('custom-move');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(child.attr('class')).toContain('custom-move-active');
|
|
||||||
window.setTimeout.expect(0).process();
|
|
||||||
|
|
||||||
//hide
|
|
||||||
animator.hide(child);
|
|
||||||
expect(child.attr('class')).toContain('custom-hide');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(child.attr('class')).toContain('custom-hide-active');
|
|
||||||
window.setTimeout.expect(0).process();
|
|
||||||
|
|
||||||
//show
|
|
||||||
animator.show(child);
|
|
||||||
expect(child.attr('class')).toContain('custom-show');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(child.attr('class')).toContain('custom-show-active');
|
|
||||||
window.setTimeout.expect(0).process();
|
|
||||||
|
|
||||||
//leave
|
|
||||||
animator.leave(child);
|
|
||||||
expect(child.attr('class')).toContain('custom-leave');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(child.attr('class')).toContain('custom-leave-active');
|
|
||||||
window.setTimeout.expect(0).process();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should run polyfillSetup and return the memento", inject(function($animator, $rootScope) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'setup-memo\'}'
|
|
||||||
});
|
|
||||||
$rootScope.$digest();
|
|
||||||
expect(element.text()).toEqual('');
|
|
||||||
animator.show(element);
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(element.text()).toBe('memento');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should not run if animations are disabled", inject(function($animator, $rootScope) {
|
|
||||||
$animator.enabled(false);
|
|
||||||
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'setup-memo\'}'
|
|
||||||
});
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
element.text('123');
|
|
||||||
expect(element.text()).toBe('123');
|
|
||||||
animator.show(element);
|
|
||||||
expect(element.text()).toBe('123');
|
|
||||||
|
|
||||||
$animator.enabled(true);
|
|
||||||
|
|
||||||
animator.show(element);
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(element.text()).toBe('memento');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should only call done() once and right away if another animation takes place in between",
|
|
||||||
inject(function($animator, $rootScope) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{hide: \'custom-delay\', leave: \'custom-delay\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
element.append(child);
|
|
||||||
|
|
||||||
child.css('display','block');
|
|
||||||
animator.hide(child);
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(child.css('display')).toBe('block');
|
|
||||||
|
|
||||||
animator.leave(child);
|
|
||||||
expect(child.css('display')).toBe('none'); //hides instantly
|
|
||||||
|
|
||||||
//lets change this to prove that done doesn't fire anymore for the previous hide() operation
|
|
||||||
child.css('display','block');
|
|
||||||
|
|
||||||
window.setTimeout.expect(2000).process();
|
|
||||||
expect(child.css('display')).toBe('block'); //doesn't run the done() method to hide it
|
|
||||||
|
|
||||||
expect(element.children().length).toBe(1); //still animating
|
|
||||||
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(2000).process();
|
|
||||||
expect(element.children().length).toBe(0);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should call the cancel callback when another animation is called on the same element",
|
|
||||||
inject(function($animator, $rootScope) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{hide: \'custom-delay\', show: \'custom-delay\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
child.css('display','none');
|
|
||||||
element.data('foo', 'bar');
|
|
||||||
animator.show(element);
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
|
|
||||||
animator.hide(element);
|
|
||||||
|
|
||||||
expect(element.hasClass('animation-cancelled')).toBe(true);
|
|
||||||
expect(element.data('foo')).toEqual('bar');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should NOT clobber all data on an element when animation is finished",
|
|
||||||
inject(function($animator, $rootScope) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{hide: \'custom-delay\', show: \'custom-delay\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
child.css('display','none');
|
|
||||||
element.data('foo', 'bar');
|
|
||||||
|
|
||||||
animator.show(element);
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
|
|
||||||
animator.hide(element);
|
|
||||||
|
|
||||||
expect(element.data('foo')).toEqual('bar');
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
it("should properly animate custom animation events", inject(function($animator, $rootScope) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{custom: \'setup-memo\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
element.text('123');
|
|
||||||
animator.animate('custom',element);
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(element.text()).toBe('memento');
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("with CSS3", function() {
|
|
||||||
var window, animator, prefix, vendorPrefix;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
module(function($animationProvider, $provide) {
|
|
||||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
|
||||||
return function($sniffer, _$rootElement_, $animator) {
|
|
||||||
vendorPrefix = '-' + $sniffer.vendorPrefix.toLowerCase() + '-';
|
|
||||||
$rootElement = _$rootElement_;
|
|
||||||
$animator.enabled(true);
|
|
||||||
};
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should properly animate custom animations for specific animation events",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
|
|
||||||
$animator.enabled(true);
|
|
||||||
var element = $compile(html('<div></div>'))($rootScope);
|
|
||||||
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{custom: \'special\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
animator.animate('custom',element);
|
|
||||||
if($sniffer.transitions) {
|
|
||||||
expect(element.hasClass('special')).toBe(true);
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(element.hasClass('special-active')).toBe(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should not animate custom animations if not specifically defined",
|
|
||||||
inject(function($animator, $rootScope, $compile) {
|
|
||||||
|
|
||||||
$animator.enabled(true);
|
|
||||||
var element = $compile(html('<div></div>'))($rootScope);
|
|
||||||
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{custom: \'special\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
animator.animate('custom1',element);
|
|
||||||
expect(element.hasClass('special')).toBe(false);
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should properly animate custom animations for general animation events",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
|
|
||||||
$animator.enabled(true);
|
|
||||||
var element = $compile(html('<div></div>'))($rootScope);
|
|
||||||
|
|
||||||
animator = $animator($rootScope, {
|
|
||||||
ngAnimate : "'special'"
|
|
||||||
});
|
|
||||||
|
|
||||||
animator.animate('custom',element);
|
|
||||||
if($sniffer.transitions) {
|
|
||||||
expect(element.hasClass('special-custom')).toBe(true);
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(element.hasClass('special-custom-active')).toBe(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe("Animations", function() {
|
|
||||||
it("should properly detect and make use of CSS Animations",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
var style = 'animation: some_animation 4s linear 0s 1 alternate;' +
|
|
||||||
vendorPrefix + 'animation: some_animation 4s linear 0s 1 alternate;';
|
|
||||||
element = $compile(html('<div style="' + style + '">1</div>'))($rootScope);
|
|
||||||
var animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'inline-show\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
|
|
||||||
animator.show(element);
|
|
||||||
if ($sniffer.animations) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(4000).process();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should properly detect and make use of CSS Animations with multiple iterations",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
var style = 'animation-duration: 2s;' +
|
|
||||||
'animation-iteration-count: 3;' +
|
|
||||||
vendorPrefix + 'animation-duration: 2s;' +
|
|
||||||
vendorPrefix + 'animation-iteration-count: 3;';
|
|
||||||
element = $compile(html('<div style="' + style + '">1</div>'))($rootScope);
|
|
||||||
var animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'inline-show\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
|
|
||||||
animator.show(element);
|
|
||||||
if ($sniffer.animations) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(6000).process();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should fallback to the animation duration if an infinite iteration is provided",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
var style = 'animation-duration: 2s;' +
|
|
||||||
'animation-iteration-count: infinite;' +
|
|
||||||
vendorPrefix + 'animation-duration: 2s;' +
|
|
||||||
vendorPrefix + 'animation-iteration-count: infinite;';
|
|
||||||
element = $compile(html('<div style="' + style + '">1</div>'))($rootScope);
|
|
||||||
var animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'inline-show\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
|
|
||||||
animator.show(element);
|
|
||||||
if ($sniffer.animations) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(2000).process();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should consider the animation delay is provided",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
var style = 'animation-duration: 2s;' +
|
|
||||||
'animation-delay: 10s;' +
|
|
||||||
'animation-iteration-count: 5;' +
|
|
||||||
vendorPrefix + 'animation-duration: 2s;' +
|
|
||||||
vendorPrefix + 'animation-delay: 10s;' +
|
|
||||||
vendorPrefix + 'animation-iteration-count: 5;';
|
|
||||||
element = $compile(html('<div style="' + style + '">1</div>'))($rootScope);
|
|
||||||
var animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'inline-show\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
|
|
||||||
animator.show(element);
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(20000).process();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should skip animations if disabled and run when enabled",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
$animator.enabled(false);
|
|
||||||
var style = 'animation: some_animation 2s linear 0s 1 alternate;' +
|
|
||||||
vendorPrefix + 'animation: some_animation 2s linear 0s 1 alternate;'
|
|
||||||
|
|
||||||
element = $compile(html('<div style="' + style + '">1</div>'))($rootScope);
|
|
||||||
var animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'inline-show\'}'
|
|
||||||
});
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
animator.show(element);
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should finish the previous animation when a new animation is started",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
var style = 'animation: some_animation 2s linear 0s 1 alternate;' +
|
|
||||||
vendorPrefix + 'animation: some_animation 2s linear 0s 1 alternate;'
|
|
||||||
|
|
||||||
element = $compile(html('<div style="' + style + '">1</div>'))($rootScope);
|
|
||||||
var animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'show\', hide: \'hide\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
animator.show(element);
|
|
||||||
if($sniffer.animations) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(element.hasClass('show')).toBe(true);
|
|
||||||
expect(element.hasClass('show-active')).toBe(true);
|
|
||||||
}
|
|
||||||
else { //animation is skipped
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
animator.hide(element);
|
|
||||||
if(!$sniffer.animations) {
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
expect(element.hasClass('show')).toBe(false);
|
|
||||||
expect(element.hasClass('show-active')).toBe(false);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Transitions", function() {
|
|
||||||
it("should skip transitions if disabled and run when enabled",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
$animator.enabled(false);
|
|
||||||
element = $compile(html('<div style="' + vendorPrefix + 'transition: 1s linear all">1</div>'))($rootScope);
|
|
||||||
var animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'inline-show\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
animator.show(element);
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
|
|
||||||
$animator.enabled(true);
|
|
||||||
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
|
|
||||||
animator.show(element);
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should skip animations if disabled and run when enabled picking the longest specified duration",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
element = $compile(html('<div style="' + vendorPrefix + 'transition-duration: 1s, 2000ms, 1s; ' + vendorPrefix + 'transition-property: height, left, opacity">foo</div>'))($rootScope);
|
|
||||||
var animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'inline-show\'}'
|
|
||||||
});
|
|
||||||
element.css('display','none');
|
|
||||||
animator.show(element);
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(2000).process();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should skip animations if disabled and run when enabled picking the longest specified duration/delay combination",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
$animator.enabled(false);
|
|
||||||
element = $compile(html('<div style="' + vendorPrefix +
|
|
||||||
'transition-duration: 1s, 0s, 1s; ' + vendorPrefix +
|
|
||||||
'transition-delay: 2s, 1000ms, 2s; ' + vendorPrefix +
|
|
||||||
'transition-property: height, left, opacity">foo</div>'))($rootScope);
|
|
||||||
|
|
||||||
var animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'inline-show\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
animator.show(element);
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
|
|
||||||
$animator.enabled(true);
|
|
||||||
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
|
|
||||||
animator.show(element);
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(3000).process();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should select the highest duration and delay",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
var styles = 'transition:1s linear all 2s;' +
|
|
||||||
vendorPrefix + 'transition:1s linear all 2s;' +
|
|
||||||
'animation:my_ani 10s 1s;' +
|
|
||||||
vendorPrefix + 'animation:my_ani 10s 1s;';
|
|
||||||
|
|
||||||
element = $compile(html('<div style="' + styles + '">foo</div>'))($rootScope);
|
|
||||||
|
|
||||||
var animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'inline-show\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
|
|
||||||
animator.show(element);
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(11000).process();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should finish the previous transition when a new animation is started",
|
|
||||||
inject(function($animator, $rootScope, $compile, $sniffer) {
|
|
||||||
var style = 'transition: 1s linear all;' +
|
|
||||||
vendorPrefix + 'transition: 1s linear all;'
|
|
||||||
|
|
||||||
element = $compile(html('<div style="' + style + '">1</div>'))($rootScope);
|
|
||||||
var animator = $animator($rootScope, {
|
|
||||||
ngAnimate : '{show: \'show\', hide: \'hide\'}'
|
|
||||||
});
|
|
||||||
|
|
||||||
animator.show(element);
|
|
||||||
if($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(element.hasClass('show')).toBe(true);
|
|
||||||
expect(element.hasClass('show-active')).toBe(true);
|
|
||||||
}
|
|
||||||
else { //animation is skipped
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
animator.hide(element);
|
|
||||||
if(!$sniffer.transitions) {
|
|
||||||
expect(window.setTimeout.queue.length).toBe(0);
|
|
||||||
}
|
|
||||||
expect(element.hasClass('show')).toBe(false);
|
|
||||||
expect(element.hasClass('show-active')).toBe(false);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('anmation evaluation', function () {
|
|
||||||
it('should re-evaluate the animation expression on each animation', inject(function($animator, $rootScope) {
|
|
||||||
var parent = jqLite('<div><span></span></div>');
|
|
||||||
var element = parent.find('span');
|
|
||||||
|
|
||||||
$rootScope.animationFn = function () { throw new Error('too early'); };
|
|
||||||
var animate = $animator($rootScope, { ngAnimate: 'animationFn()' });
|
|
||||||
var log = '';
|
|
||||||
|
|
||||||
$rootScope.animationFn = function () { log = 'abc' };
|
|
||||||
animate.enter(element, parent);
|
|
||||||
expect(log).toEqual('abc');
|
|
||||||
|
|
||||||
$rootScope.animationFn = function () { log = 'xyz' };
|
|
||||||
animate.enter(element, parent);
|
|
||||||
expect(log).toEqual('xyz');
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should throw an error when an invalid ng-animate syntax is provided", inject(function($animator, $rootScope) {
|
|
||||||
expect(function() {
|
|
||||||
var animate = $animator($rootScope, { ngAnimate: ':' });
|
|
||||||
animate.enter();
|
|
||||||
}).toThrow("[$parse:syntax] Syntax Error: Token ':' not a primary expression at column 1 of the expression [:] starting at [:].");
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
@ -3096,8 +3096,8 @@ describe('$compile', function() {
|
||||||
'</div>')($rootScope);
|
'</div>')($rootScope);
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
var spans = element.find('span');
|
var spans = element.find('span');
|
||||||
expect(spans.eq(0).css('display')).toBe('none');
|
expect(spans.eq(0)).toBeHidden();
|
||||||
expect(spans.eq(1).css('display')).toBe('none');
|
expect(spans.eq(1)).toBeHidden();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -3216,10 +3216,10 @@ describe('$compile', function() {
|
||||||
'</div>')($rootScope);
|
'</div>')($rootScope);
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
var spans = element.find('span');
|
var spans = element.find('span');
|
||||||
expect(spans.eq(0).css('display')).toBe('none');
|
expect(spans.eq(0)).toBeHidden();
|
||||||
expect(spans.eq(1).css('display')).toBe('none');
|
expect(spans.eq(1)).toBeHidden();
|
||||||
expect(spans.eq(2).css('display')).toBe('none');
|
expect(spans.eq(2)).toBeHidden();
|
||||||
expect(spans.eq(3).css('display')).toBe('none');
|
expect(spans.eq(3)).toBeHidden();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,7 @@ describe('ngIf', function () {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngIf ngAnimate', function () {
|
describe('ngIf animations', function () {
|
||||||
var vendorPrefix, window;
|
|
||||||
var body, element, $rootElement;
|
var body, element, $rootElement;
|
||||||
|
|
||||||
function html(html) {
|
function html(html) {
|
||||||
|
|
@ -85,6 +84,8 @@ describe('ngIf ngAnimate', function () {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beforeEach(module('mock.animate'));
|
||||||
|
|
||||||
beforeEach(module(function() {
|
beforeEach(module(function() {
|
||||||
// we need to run animation on attached elements;
|
// we need to run animation on attached elements;
|
||||||
return function(_$rootElement_) {
|
return function(_$rootElement_) {
|
||||||
|
|
@ -99,97 +100,52 @@ describe('ngIf ngAnimate', function () {
|
||||||
dealoc(element);
|
dealoc(element);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(module(function($animationProvider, $provide) {
|
beforeEach(module(function($animateProvider, $provide) {
|
||||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
return function($animate) {
|
||||||
return function($sniffer, $animator) {
|
$animate.enabled(true);
|
||||||
vendorPrefix = '-' + $sniffer.vendorPrefix + '-';
|
|
||||||
$animator.enabled(true);
|
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should fire off the enter animation + add and remove the css classes',
|
it('should fire off the enter animation',
|
||||||
inject(function($compile, $rootScope, $sniffer) {
|
inject(function($compile, $rootScope, $animate) {
|
||||||
|
var item;
|
||||||
var $scope = $rootScope.$new();
|
var $scope = $rootScope.$new();
|
||||||
var style = vendorPrefix + 'transition: 1s linear all';
|
|
||||||
element = $compile(html(
|
element = $compile(html(
|
||||||
'<div>' +
|
'<div>' +
|
||||||
'<div ng-if="value" style="' + style + '" ng-animate="{enter: \'custom-enter\', leave: \'custom-leave\'}"><div>Hi</div></div>' +
|
'<div ng-if="value"><div>Hi</div></div>' +
|
||||||
'</div>'
|
'</div>'
|
||||||
))($scope);
|
))($scope);
|
||||||
|
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
$scope.$apply('value = true');
|
$scope.$apply('value = true');
|
||||||
|
|
||||||
|
item = $animate.process('enter').element;
|
||||||
|
expect(item.text()).toBe('Hi');
|
||||||
|
|
||||||
expect(element.children().length).toBe(1);
|
expect(element.children().length).toBe(1);
|
||||||
var first = element.children()[0];
|
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
expect(first.className).toContain('custom-enter');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(first.className).toContain('custom-enter-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(first.className).not.toContain('custom-enter');
|
|
||||||
expect(first.className).not.toContain('custom-enter-active');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should fire off the leave animation + add and remove the css classes',
|
it('should fire off the leave animation',
|
||||||
inject(function ($compile, $rootScope, $sniffer) {
|
inject(function ($compile, $rootScope, $animate) {
|
||||||
|
var item;
|
||||||
var $scope = $rootScope.$new();
|
var $scope = $rootScope.$new();
|
||||||
var style = vendorPrefix + 'transition: 1s linear all';
|
|
||||||
element = $compile(html(
|
element = $compile(html(
|
||||||
'<div>' +
|
'<div>' +
|
||||||
'<div ng-if="value" style="' + style + '" ng-animate="{enter: \'custom-enter\', leave: \'custom-leave\'}"><div>Hi</div></div>' +
|
'<div ng-if="value"><div>Hi</div></div>' +
|
||||||
'</div>'
|
'</div>'
|
||||||
))($scope);
|
))($scope);
|
||||||
$scope.$apply('value = true');
|
$scope.$apply('value = true');
|
||||||
|
|
||||||
expect(element.children().length).toBe(1);
|
item = $animate.process('enter').element;
|
||||||
var first = element.children()[0];
|
expect(item.text()).toBe('Hi');
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.$apply('value = false');
|
$scope.$apply('value = false');
|
||||||
expect(element.children().length).toBe($sniffer.transitions ? 1 : 0);
|
expect(element.children().length).toBe(1);
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
item = $animate.process('leave').element;
|
||||||
expect(first.className).toContain('custom-leave');
|
expect(item.text()).toBe('Hi');
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(first.className).toContain('custom-leave-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(element.children().length).toBe(0);
|
expect(element.children().length).toBe(0);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should catch and use the correct duration for animation',
|
|
||||||
inject(function ($compile, $rootScope, $sniffer) {
|
|
||||||
var $scope = $rootScope.$new();
|
|
||||||
var style = vendorPrefix + 'transition: 0.5s linear all';
|
|
||||||
element = $compile(html(
|
|
||||||
'<div>' +
|
|
||||||
'<div ng-if="value" style="' + style + '" ng-animate="{enter: \'custom-enter\', leave: \'custom-leave\'}"><div>Hi</div></div>' +
|
|
||||||
'</div>'
|
|
||||||
))($scope);
|
|
||||||
$scope.$apply('value = true');
|
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(500).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -341,8 +341,7 @@ describe('ngInclude', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngInclude ngAnimate', function() {
|
describe('ngInclude animations', function() {
|
||||||
var vendorPrefix, window;
|
|
||||||
var body, element, $rootElement;
|
var body, element, $rootElement;
|
||||||
|
|
||||||
function html(html) {
|
function html(html) {
|
||||||
|
|
@ -351,11 +350,6 @@ describe('ngInclude ngAnimate', function() {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyCSS(element, cssProp, cssValue) {
|
|
||||||
element.css(cssProp, cssValue);
|
|
||||||
element.css(vendorPrefix + cssProp, cssValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(module(function() {
|
beforeEach(module(function() {
|
||||||
// we need to run animation on attached elements;
|
// we need to run animation on attached elements;
|
||||||
return function(_$rootElement_) {
|
return function(_$rootElement_) {
|
||||||
|
|
@ -370,107 +364,51 @@ describe('ngInclude ngAnimate', function() {
|
||||||
dealoc(element);
|
dealoc(element);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(module(function($animationProvider, $provide) {
|
beforeEach(module('mock.animate'));
|
||||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
|
||||||
return function($sniffer, $animator) {
|
|
||||||
vendorPrefix = '-' + $sniffer.vendorPrefix + '-';
|
|
||||||
$animator.enabled(true);
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
|
|
||||||
afterEach(function(){
|
afterEach(function(){
|
||||||
dealoc(element);
|
dealoc(element);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fire off the enter animation + add and remove the css classes',
|
it('should fire off the enter animation',
|
||||||
inject(function($compile, $rootScope, $templateCache, $sniffer) {
|
inject(function($compile, $rootScope, $templateCache, $animate) {
|
||||||
|
var item;
|
||||||
|
|
||||||
$templateCache.put('enter', [200, '<div>data</div>', {}]);
|
$templateCache.put('enter', [200, '<div>data</div>', {}]);
|
||||||
$rootScope.tpl = 'enter';
|
$rootScope.tpl = 'enter';
|
||||||
element = $compile(html(
|
element = $compile(html(
|
||||||
'<div ' +
|
'<div ' +
|
||||||
'ng-include="tpl" ' +
|
'ng-include="tpl">' +
|
||||||
'ng-animate="{enter: \'custom-enter\'}">' +
|
|
||||||
'</div>'
|
'</div>'
|
||||||
))($rootScope);
|
))($rootScope);
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
|
||||||
//if we add the custom css stuff here then it will get picked up before the animation takes place
|
item = $animate.process('leave').element;
|
||||||
var child = jqLite(element.children()[0]);
|
item = $animate.process('enter').element;
|
||||||
applyCSS(child, 'transition', '1s linear all');
|
expect(item.text()).toBe('data');
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
expect(child.attr('class')).toContain('custom-enter');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
|
|
||||||
expect(child.attr('class')).toContain('custom-enter-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(child.attr('class')).not.toContain('custom-enter');
|
|
||||||
expect(child.attr('class')).not.toContain('custom-enter-active');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should fire off the leave animation + add and remove the css classes',
|
it('should fire off the leave animation',
|
||||||
inject(function($compile, $rootScope, $templateCache, $sniffer) {
|
inject(function($compile, $rootScope, $templateCache, $animate) {
|
||||||
|
var item;
|
||||||
$templateCache.put('enter', [200, '<div>data</div>', {}]);
|
$templateCache.put('enter', [200, '<div>data</div>', {}]);
|
||||||
$rootScope.tpl = 'enter';
|
$rootScope.tpl = 'enter';
|
||||||
element = $compile(html(
|
element = $compile(html(
|
||||||
'<div ' +
|
'<div ' +
|
||||||
'ng-include="tpl" ' +
|
'ng-include="tpl">' +
|
||||||
'ng-animate="{leave: \'custom-leave\'}">' +
|
|
||||||
'</div>'
|
'</div>'
|
||||||
))($rootScope);
|
))($rootScope);
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
|
||||||
//if we add the custom css stuff here then it will get picked up before the animation takes place
|
item = $animate.process('leave').element;
|
||||||
var child = jqLite(element.children()[0]);
|
item = $animate.process('enter').element;
|
||||||
applyCSS(child, 'transition', '1s linear all');
|
expect(item.text()).toBe('data');
|
||||||
|
|
||||||
$rootScope.tpl = '';
|
$rootScope.tpl = '';
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
item = $animate.process('leave').element;
|
||||||
expect(child.attr('class')).toContain('custom-leave');
|
expect(item.text()).toBe('data');
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
|
|
||||||
expect(child.attr('class')).toContain('custom-leave-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(child.attr('class')).not.toContain('custom-leave');
|
|
||||||
expect(child.attr('class')).not.toContain('custom-leave-active');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should catch and use the correct duration for animation',
|
|
||||||
inject(function($compile, $rootScope, $templateCache, $sniffer) {
|
|
||||||
$templateCache.put('enter', [200, '<div>data</div>', {}]);
|
|
||||||
$rootScope.tpl = 'enter';
|
|
||||||
element = $compile(html(
|
|
||||||
'<div ' +
|
|
||||||
'ng-include="tpl" ' +
|
|
||||||
'ng-animate="{enter: \'custom-enter\'}">' +
|
|
||||||
'</div>'
|
|
||||||
))($rootScope);
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
//if we add the custom css stuff here then it will get picked up before the animation takes place
|
|
||||||
var child = jqLite(element.children()[0]);
|
|
||||||
applyCSS(child, 'transition', '0.5s linear all');
|
|
||||||
|
|
||||||
$rootScope.tpl = 'enter';
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(500).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -822,212 +822,6 @@ describe('ngRepeat', function() {
|
||||||
expect(newLis[2]).toEqual(lis[1]);
|
expect(newLis[2]).toEqual(lis[1]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('ngRepeat ngAnimate', function() {
|
|
||||||
var vendorPrefix, window;
|
|
||||||
var body, element, $rootElement;
|
|
||||||
|
|
||||||
function html(html) {
|
|
||||||
$rootElement.html(html);
|
|
||||||
element = $rootElement.children().eq(0);
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyCSS(element, cssProp, cssValue) {
|
|
||||||
element.css(cssProp, cssValue);
|
|
||||||
element.css(vendorPrefix + cssProp, cssValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(module(function() {
|
|
||||||
// we need to run animation on attached elements;
|
|
||||||
return function(_$rootElement_) {
|
|
||||||
$rootElement = _$rootElement_;
|
|
||||||
body = jqLite(document.body);
|
|
||||||
body.append($rootElement);
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
|
|
||||||
afterEach(function(){
|
|
||||||
dealoc(body);
|
|
||||||
dealoc(element);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(module(function($animationProvider, $provide) {
|
|
||||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
|
||||||
return function($sniffer, $animator) {
|
|
||||||
vendorPrefix = '-' + $sniffer.vendorPrefix + '-';
|
|
||||||
$animator.enabled(true);
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fire off the enter animation + add and remove the css classes',
|
|
||||||
inject(function($compile, $rootScope, $sniffer) {
|
|
||||||
|
|
||||||
element = $compile(html(
|
|
||||||
'<div><div ' +
|
|
||||||
'ng-repeat="item in items" ' +
|
|
||||||
'ng-animate="{enter: \'custom-enter\'}">' +
|
|
||||||
'{{ item }}' +
|
|
||||||
'</div></div>'
|
|
||||||
))($rootScope);
|
|
||||||
|
|
||||||
$rootScope.$digest(); // re-enable the animations;
|
|
||||||
|
|
||||||
$rootScope.items = ['1','2','3'];
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
//if we add the custom css stuff here then it will get picked up before the animation takes place
|
|
||||||
var kids = element.children();
|
|
||||||
for(var i=0;i<kids.length;i++) {
|
|
||||||
kids[i] = jqLite(kids[i]);
|
|
||||||
applyCSS(kids[i], 'transition', '1s linear all');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
angular.forEach(kids, function(kid) {
|
|
||||||
expect(kid.attr('class')).toContain('custom-enter');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
});
|
|
||||||
|
|
||||||
angular.forEach(kids, function(kid) {
|
|
||||||
expect(kid.attr('class')).toContain('custom-enter-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
angular.forEach(kids, function(kid) {
|
|
||||||
expect(kid.attr('class')).not.toContain('custom-enter');
|
|
||||||
expect(kid.attr('class')).not.toContain('custom-enter-active');
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fire off the leave animation + add and remove the css classes',
|
|
||||||
inject(function($compile, $rootScope, $sniffer) {
|
|
||||||
|
|
||||||
element = $compile(html(
|
|
||||||
'<div><div ' +
|
|
||||||
'ng-repeat="item in items" ' +
|
|
||||||
'ng-animate="{leave: \'custom-leave\'}">' +
|
|
||||||
'{{ item }}' +
|
|
||||||
'</div></div>'
|
|
||||||
))($rootScope);
|
|
||||||
|
|
||||||
$rootScope.items = ['1','2','3'];
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
//if we add the custom css stuff here then it will get picked up before the animation takes place
|
|
||||||
var kids = element.children();
|
|
||||||
for(var i=0;i<kids.length;i++) {
|
|
||||||
kids[i] = jqLite(kids[i]);
|
|
||||||
applyCSS(kids[i], 'transition', '1s linear all');
|
|
||||||
}
|
|
||||||
|
|
||||||
$rootScope.items = ['1','3'];
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
//the last element gets pushed down when it animates
|
|
||||||
var kid = jqLite(element.children()[1]);
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
expect(kid.attr('class')).toContain('custom-leave');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(kid.attr('class')).toContain('custom-leave-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(kid.attr('class')).not.toContain('custom-leave');
|
|
||||||
expect(kid.attr('class')).not.toContain('custom-leave-active');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fire off the move animation + add and remove the css classes',
|
|
||||||
inject(function($compile, $rootScope, $sniffer) {
|
|
||||||
element = $compile(html(
|
|
||||||
'<div>' +
|
|
||||||
'<div ng-repeat="item in items" ng-animate="{move: \'custom-move\'}">' +
|
|
||||||
'{{ item }}' +
|
|
||||||
'</div>' +
|
|
||||||
'</div>'
|
|
||||||
))($rootScope);
|
|
||||||
|
|
||||||
$rootScope.items = ['1','2','3'];
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
//if we add the custom css stuff here then it will get picked up before the animation takes place
|
|
||||||
var kids = element.children();
|
|
||||||
for(var i=0;i<kids.length;i++) {
|
|
||||||
kids[i] = jqLite(kids[i]);
|
|
||||||
applyCSS(kids[i], 'transition', '1s linear all');
|
|
||||||
}
|
|
||||||
|
|
||||||
$rootScope.items = ['2','3','1'];
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
//the last element gets pushed down when it animates
|
|
||||||
kids = element.children();
|
|
||||||
var first = jqLite(kids[0]);
|
|
||||||
var left = jqLite(kids[1]);
|
|
||||||
var right = jqLite(kids[2]);
|
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
expect(first.attr('class')).toContain('custom-move');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(left.attr('class')).toContain('custom-move');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
|
|
||||||
expect(first.attr('class')).toContain('custom-move-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
expect(left.attr('class')).toContain('custom-move-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(first.attr('class')).not.toContain('custom-move');
|
|
||||||
expect(first.attr('class')).not.toContain('custom-move-active');
|
|
||||||
expect(left.attr('class')).not.toContain('custom-move');
|
|
||||||
expect(left.attr('class')).not.toContain('custom-move-active');
|
|
||||||
expect(right.attr('class')).not.toContain('custom-move');
|
|
||||||
expect(right.attr('class')).not.toContain('custom-move-active');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should catch and use the correct duration for animation',
|
|
||||||
inject(function($compile, $rootScope, $sniffer) {
|
|
||||||
|
|
||||||
element = $compile(html(
|
|
||||||
'<div><div ' +
|
|
||||||
'ng-repeat="item in items" ' +
|
|
||||||
'ng-animate="{enter: \'custom-enter\'}">' +
|
|
||||||
'{{ item }}' +
|
|
||||||
'</div></div>'
|
|
||||||
))($rootScope);
|
|
||||||
|
|
||||||
$rootScope.$digest(); // re-enable the animations;
|
|
||||||
|
|
||||||
$rootScope.items = ['a','b'];
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
//if we add the custom css stuff here then it will get picked up before the animation takes place
|
|
||||||
var kids = element.children();
|
|
||||||
var first = jqLite(kids[0]);
|
|
||||||
var second = jqLite(kids[1]);
|
|
||||||
var cssProp = 'transition';
|
|
||||||
var cssValue = '0.5s linear all';
|
|
||||||
applyCSS(first, cssProp, cssValue);
|
|
||||||
applyCSS(second, cssProp, cssValue);
|
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(500).process();
|
|
||||||
window.setTimeout.expect(500).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should grow multi-node repeater', inject(function($compile, $rootScope) {
|
it('should grow multi-node repeater', inject(function($compile, $rootScope) {
|
||||||
$rootScope.show = false;
|
$rootScope.show = false;
|
||||||
|
|
@ -1050,3 +844,123 @@ describe('ngRepeat ngAnimate', function() {
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ngRepeat animations', function() {
|
||||||
|
var body, element, $rootElement;
|
||||||
|
|
||||||
|
function html(html) {
|
||||||
|
$rootElement.html(html);
|
||||||
|
element = $rootElement.children().eq(0);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(module('mock.animate'));
|
||||||
|
|
||||||
|
beforeEach(module(function() {
|
||||||
|
// we need to run animation on attached elements;
|
||||||
|
return function(_$rootElement_) {
|
||||||
|
$rootElement = _$rootElement_;
|
||||||
|
body = jqLite(document.body);
|
||||||
|
body.append($rootElement);
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
|
afterEach(function(){
|
||||||
|
dealoc(body);
|
||||||
|
dealoc(element);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fire off the enter animation',
|
||||||
|
inject(function($compile, $rootScope, $animate) {
|
||||||
|
|
||||||
|
var item;
|
||||||
|
|
||||||
|
element = $compile(html(
|
||||||
|
'<div><div ' +
|
||||||
|
'ng-repeat="item in items">' +
|
||||||
|
'{{ item }}' +
|
||||||
|
'</div></div>'
|
||||||
|
))($rootScope);
|
||||||
|
|
||||||
|
$rootScope.$digest(); // re-enable the animations;
|
||||||
|
|
||||||
|
$rootScope.items = ['1','2','3'];
|
||||||
|
$rootScope.$digest();
|
||||||
|
|
||||||
|
item = $animate.process('enter').element;
|
||||||
|
expect(item.text()).toBe('1');
|
||||||
|
|
||||||
|
item = $animate.process('enter').element;
|
||||||
|
expect(item.text()).toBe('2');
|
||||||
|
|
||||||
|
item = $animate.process('enter').element;
|
||||||
|
expect(item.text()).toBe('3');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should fire off the leave animation',
|
||||||
|
inject(function($compile, $rootScope, $animate) {
|
||||||
|
|
||||||
|
var item;
|
||||||
|
|
||||||
|
element = $compile(html(
|
||||||
|
'<div><div ' +
|
||||||
|
'ng-repeat="item in items">' +
|
||||||
|
'{{ item }}' +
|
||||||
|
'</div></div>'
|
||||||
|
))($rootScope);
|
||||||
|
|
||||||
|
$rootScope.items = ['1','2','3'];
|
||||||
|
$rootScope.$digest();
|
||||||
|
|
||||||
|
item = $animate.process('enter').element;
|
||||||
|
expect(item.text()).toBe('1');
|
||||||
|
|
||||||
|
item = $animate.process('enter').element;
|
||||||
|
expect(item.text()).toBe('2');
|
||||||
|
|
||||||
|
item = $animate.process('enter').element;
|
||||||
|
expect(item.text()).toBe('3');
|
||||||
|
|
||||||
|
$rootScope.items = ['1','3'];
|
||||||
|
$rootScope.$digest();
|
||||||
|
|
||||||
|
item = $animate.process('leave').element;
|
||||||
|
expect(item.text()).toBe('2');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should fire off the move animation',
|
||||||
|
inject(function($compile, $rootScope, $animate) {
|
||||||
|
|
||||||
|
var item;
|
||||||
|
|
||||||
|
element = $compile(html(
|
||||||
|
'<div>' +
|
||||||
|
'<div ng-repeat="item in items">' +
|
||||||
|
'{{ item }}' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>'
|
||||||
|
))($rootScope);
|
||||||
|
|
||||||
|
$rootScope.items = ['1','2','3'];
|
||||||
|
$rootScope.$digest();
|
||||||
|
|
||||||
|
item = $animate.process('enter').element;
|
||||||
|
expect(item.text()).toBe('1');
|
||||||
|
|
||||||
|
item = $animate.process('enter').element;
|
||||||
|
expect(item.text()).toBe('2');
|
||||||
|
|
||||||
|
item = $animate.process('enter').element;
|
||||||
|
expect(item.text()).toBe('3');
|
||||||
|
|
||||||
|
$rootScope.items = ['2','3','1'];
|
||||||
|
$rootScope.$digest();
|
||||||
|
|
||||||
|
item = $animate.process('move').element;
|
||||||
|
expect(item.text()).toBe('2');
|
||||||
|
|
||||||
|
item = $animate.process('move').element;
|
||||||
|
expect(item.text()).toBe('1');
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -13,20 +13,20 @@ describe('ngShow / ngHide', function() {
|
||||||
element = jqLite('<div ng-show="exp"></div>');
|
element = jqLite('<div ng-show="exp"></div>');
|
||||||
element = $compile(element)($rootScope);
|
element = $compile(element)($rootScope);
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(isCssVisible(element)).toEqual(false);
|
expect(element).toBeHidden();
|
||||||
$rootScope.exp = true;
|
$rootScope.exp = true;
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(isCssVisible(element)).toEqual(true);
|
expect(element).toBeShown();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
it('should make hidden element visible', inject(function($rootScope, $compile) {
|
it('should make hidden element visible', inject(function($rootScope, $compile) {
|
||||||
element = jqLite('<div style="display: none" ng-show="exp"></div>');
|
element = jqLite('<div class="ng-hide" ng-show="exp"></div>');
|
||||||
element = $compile(element)($rootScope);
|
element = $compile(element)($rootScope);
|
||||||
expect(isCssVisible(element)).toBe(false);
|
expect(element).toBeHidden();
|
||||||
$rootScope.exp = true;
|
$rootScope.exp = true;
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(isCssVisible(element)).toBe(true);
|
expect(element).toBeShown();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -34,17 +34,15 @@ describe('ngShow / ngHide', function() {
|
||||||
it('should hide an element', inject(function($rootScope, $compile) {
|
it('should hide an element', inject(function($rootScope, $compile) {
|
||||||
element = jqLite('<div ng-hide="exp"></div>');
|
element = jqLite('<div ng-hide="exp"></div>');
|
||||||
element = $compile(element)($rootScope);
|
element = $compile(element)($rootScope);
|
||||||
expect(isCssVisible(element)).toBe(true);
|
expect(element).toBeShown();
|
||||||
$rootScope.exp = true;
|
$rootScope.exp = true;
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
expect(isCssVisible(element)).toBe(false);
|
expect(element).toBeHidden();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngShow / ngHide - ngAnimate', function() {
|
describe('ngShow / ngHide animations', function() {
|
||||||
var window;
|
|
||||||
var vendorPrefix;
|
|
||||||
var body, element, $rootElement;
|
var body, element, $rootElement;
|
||||||
|
|
||||||
function html(html) {
|
function html(html) {
|
||||||
|
|
@ -65,152 +63,57 @@ describe('ngShow / ngHide - ngAnimate', function() {
|
||||||
body.removeAttr('ng-animation-running');
|
body.removeAttr('ng-animation-running');
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(module(function($animationProvider, $provide) {
|
beforeEach(module('mock.animate'));
|
||||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
|
||||||
return function($sniffer, _$rootElement_, $animator) {
|
beforeEach(module(function($animateProvider, $provide) {
|
||||||
vendorPrefix = '-' + $sniffer.vendorPrefix + '-';
|
return function(_$rootElement_) {
|
||||||
$rootElement = _$rootElement_;
|
$rootElement = _$rootElement_;
|
||||||
$animator.enabled(true);
|
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('ngShow', function() {
|
describe('ngShow', function() {
|
||||||
it('should fire off the animator.show and animator.hide animation', inject(function($compile, $rootScope, $sniffer) {
|
it('should fire off the $animate.show and $animate.hide animation', inject(function($compile, $rootScope, $animate) {
|
||||||
|
var item;
|
||||||
var $scope = $rootScope.$new();
|
var $scope = $rootScope.$new();
|
||||||
$scope.on = true;
|
$scope.on = true;
|
||||||
element = $compile(html(
|
element = $compile(html(
|
||||||
'<div ' +
|
'<div ng-show="on">data</div>'
|
||||||
'style="'+vendorPrefix+'transition: 1s linear all"' +
|
|
||||||
'ng-show="on" ' +
|
|
||||||
'ng-animate="{show: \'custom-show\', hide: \'custom-hide\', animateFirst: true}">' +
|
|
||||||
'</div>'
|
|
||||||
))($scope);
|
))($scope);
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
item = $animate.process('show').element;
|
||||||
expect(element.attr('class')).toContain('custom-show');
|
expect(item.text()).toBe('data');
|
||||||
window.setTimeout.expect(1).process();
|
expect(item).toBeShown();
|
||||||
|
|
||||||
expect(element.attr('class')).toContain('custom-show-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(element.attr('class')).not.toContain('custom-show-active');
|
|
||||||
expect(element.attr('class')).not.toContain('custom-show');
|
|
||||||
|
|
||||||
$scope.on = false;
|
$scope.on = false;
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
if ($sniffer.transitions) {
|
|
||||||
expect(element.attr('class')).toContain('custom-hide');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
expect(element.attr('class')).toContain('custom-hide-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(element.attr('class')).not.toContain('custom-hide-active');
|
item = $animate.process('hide').element;
|
||||||
expect(element.attr('class')).not.toContain('custom-hide');
|
expect(item.text()).toBe('data');
|
||||||
|
expect(item).toBeHidden();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should skip animation if parent animation running', function() {
|
|
||||||
var fired = false;
|
|
||||||
inject(function($animator, $compile, $rootScope, $sniffer) {
|
|
||||||
$animator.enabled(true);
|
|
||||||
$rootScope.$digest();
|
|
||||||
$rootScope.val = true;
|
|
||||||
var element = $compile(html('<div ng-show="val" ng-animate="\'animation\'">123</div>'))($rootScope);
|
|
||||||
$rootElement.controller('ngAnimate').running = true;
|
|
||||||
element.css('display','none');
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
|
|
||||||
$rootScope.$digest();
|
|
||||||
expect(element[0].style.display).toBe('');
|
|
||||||
expect(fired).toBe(false);
|
|
||||||
|
|
||||||
$rootElement.controller('ngAnimate').running = false;
|
|
||||||
$rootScope.val = false;
|
|
||||||
$rootScope.$digest();
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(0).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
expect(element[0].style.display).toBe('none');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngHide', function() {
|
describe('ngHide', function() {
|
||||||
it('should fire off the animator.show and animator.hide animation', inject(function($compile, $rootScope, $sniffer) {
|
it('should fire off the $animate.show and $animate.hide animation', inject(function($compile, $rootScope, $animate) {
|
||||||
|
var item;
|
||||||
var $scope = $rootScope.$new();
|
var $scope = $rootScope.$new();
|
||||||
$scope.off = true;
|
$scope.off = true;
|
||||||
element = $compile(html(
|
element = $compile(html(
|
||||||
'<div ' +
|
'<div ng-hide="off">datum</div>'
|
||||||
'style="'+vendorPrefix+'transition: 1s linear all"' +
|
|
||||||
'ng-hide="off" ' +
|
|
||||||
'ng-animate="{show: \'custom-show\', hide: \'custom-hide\', animateFirst: true}">' +
|
|
||||||
'</div>'
|
|
||||||
))($scope);
|
))($scope);
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
item = $animate.process('hide').element;
|
||||||
expect(element.attr('class')).toContain('custom-hide');
|
expect(item.text()).toBe('datum');
|
||||||
window.setTimeout.expect(1).process();
|
expect(item).toBeHidden();
|
||||||
|
|
||||||
expect(element.attr('class')).toContain('custom-hide-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(element.attr('class')).not.toContain('custom-hide-active');
|
|
||||||
expect(element.attr('class')).not.toContain('custom-hide');
|
|
||||||
|
|
||||||
$scope.off = false;
|
$scope.off = false;
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
item = $animate.process('show').element;
|
||||||
expect(element.attr('class')).toContain('custom-show');
|
expect(item.text()).toBe('datum');
|
||||||
window.setTimeout.expect(1).process();
|
expect(item).toBeShown();
|
||||||
expect(element.attr('class')).toContain('custom-show-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(element.attr('class')).not.toContain('custom-show-active');
|
|
||||||
expect(element.attr('class')).not.toContain('custom-show');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should disable animation when parent animation is running', function() {
|
|
||||||
var fired = false;
|
|
||||||
module(function($animationProvider) {
|
|
||||||
$animationProvider.register('destructive-animation', function() {
|
|
||||||
return {
|
|
||||||
setup : function() {},
|
|
||||||
start : function(element, done) {
|
|
||||||
fired = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
inject(function($compile, $rootScope) {
|
|
||||||
$rootScope.val = false;
|
|
||||||
var element = $compile(html('<div ng-hide="val" ng-animate="{ hide:\'destructive-animation\' }">123</div>'))($rootScope);
|
|
||||||
$rootElement.controller('ngAnimate').running = true;
|
|
||||||
element.css('display','block');
|
|
||||||
expect(element.css('display')).toBe('block');
|
|
||||||
|
|
||||||
$rootScope.val = true;
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
expect(element.css('display')).toBe('none');
|
|
||||||
expect(fired).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -214,8 +214,7 @@ describe('ngSwitch', function() {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngSwitch ngAnimate', function() {
|
describe('ngSwitch animations', function() {
|
||||||
var vendorPrefix, window;
|
|
||||||
var body, element, $rootElement;
|
var body, element, $rootElement;
|
||||||
|
|
||||||
function html(html) {
|
function html(html) {
|
||||||
|
|
@ -224,6 +223,8 @@ describe('ngSwitch ngAnimate', function() {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beforeEach(module('mock.animate'));
|
||||||
|
|
||||||
beforeEach(module(function() {
|
beforeEach(module(function() {
|
||||||
// we need to run animation on attached elements;
|
// we need to run animation on attached elements;
|
||||||
return function(_$rootElement_) {
|
return function(_$rootElement_) {
|
||||||
|
|
@ -238,23 +239,15 @@ describe('ngSwitch ngAnimate', function() {
|
||||||
dealoc(element);
|
dealoc(element);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(module(function($animationProvider, $provide) {
|
it('should fire off the enter animation',
|
||||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
inject(function($compile, $rootScope, $animate) {
|
||||||
return function($sniffer, $animator) {
|
var item;
|
||||||
vendorPrefix = '-' + $sniffer.vendorPrefix + '-';
|
|
||||||
$animator.enabled(true);
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fire off the enter animation + set and remove the classes',
|
|
||||||
inject(function($compile, $rootScope, $sniffer) {
|
|
||||||
var $scope = $rootScope.$new();
|
var $scope = $rootScope.$new();
|
||||||
var style = vendorPrefix + 'transition: 1s linear all';
|
|
||||||
element = $compile(html(
|
element = $compile(html(
|
||||||
'<div ng-switch on="val" ng-animate="{enter: \'cool-enter\', leave: \'cool-leave\'}">' +
|
'<div ng-switch on="val">' +
|
||||||
'<div ng-switch-when="one" style="' + style + '">one</div>' +
|
'<div ng-switch-when="one">one</div>' +
|
||||||
'<div ng-switch-when="two" style="' + style + '">two</div>' +
|
'<div ng-switch-when="two">two</div>' +
|
||||||
'<div ng-switch-when="three" style="' + style + '">three</div>' +
|
'<div ng-switch-when="three">three</div>' +
|
||||||
'</div>'
|
'</div>'
|
||||||
))($scope);
|
))($scope);
|
||||||
|
|
||||||
|
|
@ -262,33 +255,20 @@ describe('ngSwitch ngAnimate', function() {
|
||||||
$scope.val = 'one';
|
$scope.val = 'one';
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
|
|
||||||
expect(element.children().length).toBe(1);
|
item = $animate.process('enter').element;
|
||||||
var first = element.children()[0];
|
expect(item.text()).toBe('one');
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
expect(first.className).toContain('cool-enter');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
|
|
||||||
expect(first.className).toContain('cool-enter-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(first.className).not.toContain('cool-enter');
|
|
||||||
expect(first.className).not.toContain('cool-enter-active');
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
it('should fire off the leave animation + set and remove the classes',
|
it('should fire off the leave animation',
|
||||||
inject(function($compile, $rootScope, $sniffer) {
|
inject(function($compile, $rootScope, $animate) {
|
||||||
|
var item;
|
||||||
var $scope = $rootScope.$new();
|
var $scope = $rootScope.$new();
|
||||||
var style = vendorPrefix + 'transition: 1s linear all';
|
|
||||||
element = $compile(html(
|
element = $compile(html(
|
||||||
'<div ng-switch on="val" ng-animate="{enter: \'cool-enter\', leave: \'cool-leave\'}">' +
|
'<div ng-switch on="val">' +
|
||||||
'<div ng-switch-when="one" style="' + style + '">one</div>' +
|
'<div ng-switch-when="one">one</div>' +
|
||||||
'<div ng-switch-when="two" style="' + style + '">two</div>' +
|
'<div ng-switch-when="two">two</div>' +
|
||||||
'<div ng-switch-when="three" style="' + style + '">three</div>' +
|
'<div ng-switch-when="three">three</div>' +
|
||||||
'</div>'
|
'</div>'
|
||||||
))($scope);
|
))($scope);
|
||||||
|
|
||||||
|
|
@ -296,59 +276,17 @@ describe('ngSwitch ngAnimate', function() {
|
||||||
$scope.val = 'two';
|
$scope.val = 'two';
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
item = $animate.process('enter').element;
|
||||||
window.setTimeout.expect(1).process();
|
expect(item.text()).toBe('two');
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.val = 'three';
|
$scope.val = 'three';
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
|
|
||||||
expect(element.children().length).toBe($sniffer.transitions ? 2 : 1);
|
item = $animate.process('leave').element;
|
||||||
var first = element.children()[0];
|
expect(item.text()).toBe('two');
|
||||||
|
|
||||||
|
item = $animate.process('enter').element;
|
||||||
if ($sniffer.transitions) {
|
expect(item.text()).toBe('three');
|
||||||
expect(first.className).toContain('cool-leave');
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
expect(first.className).toContain('cool-leave-active');
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
window.setTimeout.expect(1000).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(first.className).not.toContain('cool-leave');
|
|
||||||
expect(first.className).not.toContain('cool-leave-active');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should catch and use the correct duration for animation',
|
|
||||||
inject(function($compile, $rootScope, $sniffer) {
|
|
||||||
element = $compile(html(
|
|
||||||
'<div ng-switch on="val" ng-animate="{enter: \'cool-enter\', leave: \'cool-leave\'}">' +
|
|
||||||
'<div ng-switch-when="one" style="' + vendorPrefix + 'transition: 0.5s linear all">one</div>' +
|
|
||||||
'</div>'
|
|
||||||
))($rootScope);
|
|
||||||
|
|
||||||
$rootScope.$digest(); // re-enable the animations;
|
|
||||||
$rootScope.val = 'one';
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect(500).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
1524
test/ngAnimate/animateSpec.js
Normal file
1524
test/ngAnimate/animateSpec.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -7,9 +7,9 @@ describe('ngView', function() {
|
||||||
|
|
||||||
beforeEach(module(function($provide) {
|
beforeEach(module(function($provide) {
|
||||||
$provide.value('$window', angular.mock.createMockWindow());
|
$provide.value('$window', angular.mock.createMockWindow());
|
||||||
return function($rootScope, $compile, $animator) {
|
return function($rootScope, $compile, $animate) {
|
||||||
element = $compile('<ng:view onload="load()"></ng:view>')($rootScope);
|
element = $compile('<ng:view onload="load()"></ng:view>')($rootScope);
|
||||||
$animator.enabled(true);
|
$animate.enabled(true);
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -509,8 +509,7 @@ describe('ngView', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ngAnimate ', function() {
|
describe('animations', function() {
|
||||||
var window, vendorPrefix;
|
|
||||||
var body, element, $rootElement;
|
var body, element, $rootElement;
|
||||||
|
|
||||||
function html(html) {
|
function html(html) {
|
||||||
|
|
@ -520,11 +519,6 @@ describe('ngView', function() {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyCSS(element, cssProp, cssValue) {
|
|
||||||
element.css(cssProp, cssValue);
|
|
||||||
element.css(vendorPrefix + cssProp, cssValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(module(function() {
|
beforeEach(module(function() {
|
||||||
// we need to run animation on attached elements;
|
// we need to run animation on attached elements;
|
||||||
return function(_$rootElement_) {
|
return function(_$rootElement_) {
|
||||||
|
|
@ -540,128 +534,131 @@ describe('ngView', function() {
|
||||||
|
|
||||||
|
|
||||||
beforeEach(module(function($provide, $routeProvider) {
|
beforeEach(module(function($provide, $routeProvider) {
|
||||||
$provide.value('$window', window = angular.mock.createMockWindow());
|
|
||||||
$routeProvider.when('/foo', {controller: noop, templateUrl: '/foo.html'});
|
$routeProvider.when('/foo', {controller: noop, templateUrl: '/foo.html'});
|
||||||
return function($sniffer, $templateCache, $animator) {
|
return function($templateCache) {
|
||||||
vendorPrefix = '-' + $sniffer.vendorPrefix + '-';
|
|
||||||
$templateCache.put('/foo.html', [200, '<div>data</div>', {}]);
|
$templateCache.put('/foo.html', [200, '<div>data</div>', {}]);
|
||||||
$animator.enabled(true);
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should fire off the enter animation + add and remove the css classes',
|
describe('hooks', function() {
|
||||||
inject(function($compile, $rootScope, $sniffer, $location) {
|
beforeEach(module('mock.animate'));
|
||||||
element = $compile(html('<div ng-view ng-animate="{enter: \'custom-enter\'}"></div>'))($rootScope);
|
|
||||||
|
|
||||||
$location.path('/foo');
|
it('should fire off the enter animation',
|
||||||
$rootScope.$digest();
|
inject(function($compile, $rootScope, $location, $animate) {
|
||||||
|
var item;
|
||||||
|
element = $compile(html('<div ng-view></div>'))($rootScope);
|
||||||
|
|
||||||
//if we add the custom css stuff here then it will get picked up before the animation takes place
|
$location.path('/foo');
|
||||||
var child = jqLite(element.children()[0]);
|
$rootScope.$digest();
|
||||||
applyCSS(child, 'transition', '1s linear all');
|
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
item = $animate.process('leave').element;
|
||||||
expect(child.attr('class')).toContain('custom-enter');
|
item = $animate.process('leave').element;
|
||||||
window.setTimeout.expect(1).process();
|
item = $animate.process('leave').element;
|
||||||
|
|
||||||
expect(child.attr('class')).toContain('custom-enter-active');
|
item = $animate.process('enter').element;
|
||||||
window.setTimeout.expect(1000).process();
|
expect(item.text()).toBe('data');
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(child.attr('class')).not.toContain('custom-enter');
|
item = $animate.process('leave').element;
|
||||||
expect(child.attr('class')).not.toContain('custom-enter-active');
|
item = $animate.process('enter').element;
|
||||||
}));
|
expect(item.text()).toBe('data');
|
||||||
|
}));
|
||||||
|
|
||||||
it('should fire off the leave animation + add and remove the css classes',
|
it('should fire off the leave animation',
|
||||||
inject(function($compile, $rootScope, $sniffer, $location, $templateCache) {
|
inject(function($compile, $rootScope, $location, $templateCache, $animate) {
|
||||||
$templateCache.put('/foo.html', [200, '<div>foo</div>', {}]);
|
|
||||||
element = $compile(html('<div ng-view ng-animate="{leave: \'custom-leave\'}"></div>'))($rootScope);
|
|
||||||
|
|
||||||
$location.path('/foo');
|
var item;
|
||||||
$rootScope.$digest();
|
$templateCache.put('/foo.html', [200, '<div>foo</div>', {}]);
|
||||||
|
element = $compile(html('<div ng-view></div>'))($rootScope);
|
||||||
|
|
||||||
//if we add the custom css stuff here then it will get picked up before the animation takes place
|
$location.path('/foo');
|
||||||
var child = jqLite(element.children()[0]);
|
$rootScope.$digest();
|
||||||
applyCSS(child, 'transition', '1s linear all');
|
|
||||||
|
|
||||||
$location.path('/');
|
item = $animate.process('leave').element;
|
||||||
$rootScope.$digest();
|
item = $animate.process('leave').element;
|
||||||
|
item = $animate.process('leave').element;
|
||||||
|
|
||||||
if ($sniffer.transitions) {
|
item = $animate.process('enter').element;
|
||||||
expect(child.attr('class')).toContain('custom-leave');
|
expect(item.text()).toBe('foo');
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
|
|
||||||
expect(child.attr('class')).toContain('custom-leave-active');
|
item = $animate.process('leave').element;
|
||||||
window.setTimeout.expect(1000).process();
|
item = $animate.process('enter').element;
|
||||||
} else {
|
expect(item.text()).toBe('foo');
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(child.attr('class')).not.toContain('custom-leave');
|
$location.path('/');
|
||||||
expect(child.attr('class')).not.toContain('custom-leave-active');
|
$rootScope.$digest();
|
||||||
}));
|
|
||||||
|
|
||||||
it('should catch and use the correct duration for animations',
|
item = $animate.process('leave').element;
|
||||||
inject(function($compile, $rootScope, $sniffer, $location, $templateCache) {
|
expect(item.text()).toBe('foo');
|
||||||
$templateCache.put('/foo.html', [200, '<div>foo</div>', {}]);
|
|
||||||
element = $compile(html(
|
|
||||||
'<div ' +
|
|
||||||
'ng-view ' +
|
|
||||||
'ng-animate="{enter: \'customEnter\'}">' +
|
|
||||||
'</div>'
|
|
||||||
))($rootScope);
|
|
||||||
|
|
||||||
$location.path('/foo');
|
|
||||||
$rootScope.$digest();
|
|
||||||
|
|
||||||
//if we add the custom css stuff here then it will get picked up before the animation takes place
|
|
||||||
var child = jqLite(element.children()[0]);
|
|
||||||
applyCSS(child, 'transition', '0.5s linear all');
|
|
||||||
|
|
||||||
if($sniffer.transitions) {
|
|
||||||
window.setTimeout.expect(1).process();
|
|
||||||
window.setTimeout.expect($sniffer.transitions ? 500 : 0).process();
|
|
||||||
} else {
|
|
||||||
expect(window.setTimeout.queue).toEqual([]);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
item = $animate.process('leave').element;
|
||||||
|
expect(item.text()).toBe('foo');
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
it('should not double compile when route changes', function() {
|
it('should not double compile when route changes', function() {
|
||||||
module(function($routeProvider, $animationProvider, $provide) {
|
module('ngAnimate');
|
||||||
|
module('mock.animate');
|
||||||
|
module(function($routeProvider, $animateProvider, $provide) {
|
||||||
$routeProvider.when('/foo', {template: '<div ng-repeat="i in [1,2]">{{i}}</div>'});
|
$routeProvider.when('/foo', {template: '<div ng-repeat="i in [1,2]">{{i}}</div>'});
|
||||||
$routeProvider.when('/bar', {template: '<div ng-repeat="i in [3,4]">{{i}}</div>'});
|
$routeProvider.when('/bar', {template: '<div ng-repeat="i in [3,4]">{{i}}</div>'});
|
||||||
$animationProvider.register('my-animation-leave', function() {
|
$animateProvider.register('.my-animation', function() {
|
||||||
return {
|
return {
|
||||||
start: function(element, done) {
|
leave: function(element, done) {
|
||||||
|
dump('yes');
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
inject(function($rootScope, $compile, $location, $route, $window, $rootElement, $sniffer) {
|
inject(function($rootScope, $compile, $location, $route, $window, $rootElement, $sniffer, $animate) {
|
||||||
element = $compile(html('<ng:view onload="load()" ng-animate="\'my-animation\'"></ng:view>'))($rootScope);
|
if (!$sniffer.transitions) return;
|
||||||
|
|
||||||
|
element = $compile(html('<ng:view onload="load()"></ng:view>'))($rootScope);
|
||||||
|
|
||||||
$location.path('/foo');
|
$location.path('/foo');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
if ($sniffer.transitions) {
|
|
||||||
$window.setTimeout.expect(1).process();
|
$animate.process('leave');
|
||||||
$window.setTimeout.expect(0).process();
|
$animate.process('leave');
|
||||||
}
|
$animate.process('leave');
|
||||||
|
$animate.process('enter');
|
||||||
|
$animate.process('leave');
|
||||||
|
$animate.process('enter');
|
||||||
|
$animate.process('enter');
|
||||||
|
$animate.process('enter');
|
||||||
|
$animate.process('enter');
|
||||||
|
$animate.process('enter');
|
||||||
|
$window.setTimeout.expect(1).process();
|
||||||
|
$window.setTimeout.expect(1).process();
|
||||||
|
$window.setTimeout.expect(1).process();
|
||||||
|
$window.setTimeout.expect(0).process();
|
||||||
|
$window.setTimeout.expect(0).process();
|
||||||
|
$window.setTimeout.expect(0).process();
|
||||||
|
|
||||||
expect(element.text()).toEqual('12');
|
expect(element.text()).toEqual('12');
|
||||||
|
|
||||||
$location.path('/bar');
|
$location.path('/bar');
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
|
$animate.process('leave');
|
||||||
|
$animate.process('enter');
|
||||||
|
$animate.process('leave');
|
||||||
|
$animate.process('enter');
|
||||||
|
$animate.process('enter');
|
||||||
|
$animate.process('enter');
|
||||||
|
$animate.process('enter');
|
||||||
|
$animate.process('enter');
|
||||||
expect(n(element.text())).toEqual('1234');
|
expect(n(element.text())).toEqual('1234');
|
||||||
if ($sniffer.transitions) {
|
|
||||||
$window.setTimeout.expect(1).process();
|
$window.setTimeout.expect(1).process();
|
||||||
$window.setTimeout.expect(1).process();
|
$window.setTimeout.expect(1).process();
|
||||||
} else {
|
$window.setTimeout.expect(1).process();
|
||||||
$window.setTimeout.expect(1).process();
|
$window.setTimeout.expect(1).process();
|
||||||
}
|
$window.setTimeout.expect(0).process();
|
||||||
|
$window.setTimeout.expect(0).process();
|
||||||
|
$window.setTimeout.expect(0).process();
|
||||||
|
$window.setTimeout.expect(0).process();
|
||||||
|
|
||||||
expect(element.text()).toEqual('34');
|
expect(element.text()).toEqual('34');
|
||||||
|
|
||||||
function n(text) {
|
function n(text) {
|
||||||
|
|
|
||||||
|
|
@ -233,7 +233,7 @@ function sortedHtml(element, showNgClass) {
|
||||||
*/
|
*/
|
||||||
function isCssVisible(node) {
|
function isCssVisible(node) {
|
||||||
var display = node.css('display');
|
var display = node.css('display');
|
||||||
return display != 'none';
|
return !node.hasClass('ng-hide') && display != 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertHidden(node) {
|
function assertHidden(node) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue