Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 07ee29c563 | |||
| 9f5d0cf79f | |||
| 1413328e6a | |||
| 7d09bd30f9 | |||
| dde1b29497 | |||
| 4ae3184c59 | |||
| ed53100a0d | |||
| 6df598d9f5 | |||
| 4aa9df7a7a | |||
| 7d5d62dafe | |||
| 524650a40e | |||
| 02a45826f1 | |||
| e324c14907 | |||
| e1cfb1957f | |||
| 2a3586381f | |||
| 834d316829 | |||
| c61be8d0e6 | |||
| 465212835f | |||
| b3acddea37 | |||
| 308598795a | |||
| 2cd09c9f0e | |||
| 34fee06ca7 | |||
| c7a46d4b8a | |||
| de065f1961 | |||
| c3ab915d2e | |||
| f4a4f42abb | |||
| b2c84ccde3 | |||
| 2b344dbd20 | |||
| cde840fdf8 | |||
| f9656dab2d | |||
| a0d759c613 | |||
| 0d421f093d | |||
| 5f937e54df |
@@ -1,3 +1,39 @@
|
||||
<a name="1.2.9"></a>
|
||||
# 1.2.9 enchanted-articulacy (2014-01-15)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:**
|
||||
- ensure the final closing timeout respects staggering animations
|
||||
([ed53100a](https://github.com/angular/angular.js/commit/ed53100a0dbc9119d5dfc8b7248845d4f6989df2))
|
||||
- prevent race conditions for class-based animations when animating on the same CSS class
|
||||
([4aa9df7a](https://github.com/angular/angular.js/commit/4aa9df7a7ae533531dfae1e3eb9646245d6b5ff4),
|
||||
[#5588](https://github.com/angular/angular.js/issues/5588))
|
||||
- correctly detect and handle CSS transition changes during class addition and removal
|
||||
([7d5d62da](https://github.com/angular/angular.js/commit/7d5d62dafe11620082c79da35958f8014eeb008c))
|
||||
- avoid accidentally matching substrings when resolving the presence of className tokens
|
||||
([524650a4](https://github.com/angular/angular.js/commit/524650a40ed20f01571e5466475749874ee67288))
|
||||
- **$http:** ensure default headers PUT and POST are different objects
|
||||
([e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
|
||||
[#5742](https://github.com/angular/angular.js/issues/5742), [#5747](https://github.com/angular/angular.js/issues/5747), [#5764](https://github.com/angular/angular.js/issues/5764))
|
||||
- **$rootScope:** prevent infinite $digest by checking if asyncQueue is empty when decrementing ttl
|
||||
([2cd09c9f](https://github.com/angular/angular.js/commit/2cd09c9f0e7766bcd191662841b7b1ffc3b6dc3f),
|
||||
[#2622](https://github.com/angular/angular.js/issues/2622))
|
||||
- **$route:** update current route upon $route instantiation
|
||||
([2b344dbd](https://github.com/angular/angular.js/commit/2b344dbd20777fb1283b3a5bcf35a6ae8d09469d),
|
||||
[#4957](https://github.com/angular/angular.js/issues/4957))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$animate:**
|
||||
- provide support for DOM callbacks
|
||||
([dde1b294](https://github.com/angular/angular.js/commit/dde1b2949727c297e214c99960141bfad438d7a4))
|
||||
- use requestAnimationFrame instead of a timeout to issue a reflow
|
||||
([4ae3184c](https://github.com/angular/angular.js/commit/4ae3184c5915aac9aa00889aa2153c8e84c14966),
|
||||
[#4278](https://github.com/angular/angular.js/issues/4278), [#4225](https://github.com/angular/angular.js/issues/4225))
|
||||
|
||||
<a name="1.2.8"></a>
|
||||
# 1.2.8 interdimensional-cartography (2014-01-10)
|
||||
|
||||
|
||||
+1
-2
@@ -80,7 +80,7 @@ Before you submit your pull request consider the following guidelines:
|
||||
```
|
||||
|
||||
* Create your patch, including appropriate test cases.
|
||||
* Follow our Coding Rules
|
||||
* Follow our [Coding Rules](#coding-rules)
|
||||
* Commit your changes and create a descriptive commit message (the
|
||||
commit message is used to generate release notes, please check out our
|
||||
[commit message conventions](#commit-message-format) and our commit message presubmit hook
|
||||
@@ -259,5 +259,4 @@ You can find out more detailed information about contributing in the
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
@@ -63,7 +63,7 @@ This module is provided by default and contains the core components of AngularJS
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
The core filters available in the ng module are used to transform template data before it is renders within directives and expressions.
|
||||
The core filters available in the ng module are used to transform template data before it is rendered within directives and expressions.
|
||||
</p>
|
||||
<p>
|
||||
Some examples include:
|
||||
|
||||
@@ -184,7 +184,7 @@ The following graphic shows how everything works together after we introduced th
|
||||
# View independent business logic: Services
|
||||
|
||||
Right now, the `InvoiceController` contains all logic of our example. When the application grows it
|
||||
is a good practise to move view independent logic from the controller into a so called
|
||||
is a good practice to move view independent logic from the controller into a so called
|
||||
<a name="service">"{@link dev_guide.services service}"</a>, so it can be reused by other parts
|
||||
of the application as well. Later on, we could also change that service to load the exchange rates
|
||||
from the web, e.g. by calling the Yahoo Finance API, without changing the controller.
|
||||
|
||||
@@ -99,7 +99,7 @@ actual value is understood.
|
||||
|
||||
For example, if you want to display an account balance of 1000 dollars with the following binding
|
||||
containing currency filter: `{{ 1000 | currency }}`, and your app is currently in en-US locale.
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, her
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, their
|
||||
browser will specify the locale as ja, and the balance of '¥1000.00' will be shown instead. This
|
||||
will really upset your client.
|
||||
|
||||
|
||||
@@ -191,8 +191,8 @@ You can do this by issuing `npm install` into your terminal.
|
||||
To run the test, do the following:
|
||||
|
||||
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
|
||||
`./scripts/test.sh` to start the Karma server (the config file necessary to start the server
|
||||
is located at `./config/karma.conf.js`).
|
||||
`./scripts/test.sh` (if you are on Windows, run scripts\test.bat) to start the Karma server (the
|
||||
config file necessary to start the server is located at `./config/karma.conf.js`).
|
||||
|
||||
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
||||
the background. Karma will use this browser for test execution.
|
||||
@@ -206,7 +206,7 @@ is located at `./config/karma.conf.js`).
|
||||
|
||||
Yay! The test passed! Or not...
|
||||
|
||||
4. To rerun the tests, just change any of the source or test files. Karma will notice the change
|
||||
4. To rerun the tests, just change any of the source or test .js files. Karma will notice the change
|
||||
and will rerun the tests for you. Now isn't that sweet?
|
||||
|
||||
# Experiments
|
||||
@@ -223,7 +223,7 @@ is located at `./config/karma.conf.js`).
|
||||
|
||||
<p>Hello, {{name}}!</p>
|
||||
|
||||
Refresh your browser and verifies that it says "Hello, World!".
|
||||
Refresh your browser and verify that it says "Hello, World!".
|
||||
|
||||
* Create a repeater that constructs a simple table:
|
||||
|
||||
|
||||
@@ -88,8 +88,8 @@ phonecatApp.controller('PhoneListCtrl', function ($scope) {
|
||||
record. This property is used to order phones by age.
|
||||
|
||||
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
|
||||
not set the default value here, the model would stay uninitialized until our user picks an
|
||||
option from the drop down menu.
|
||||
not set a default value here, the `orderBy` filter would remain uninitialized until our
|
||||
user picked an option from the drop down menu.
|
||||
|
||||
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
|
||||
browser, "Newest" is selected in the drop down menu. This is because we set `orderProp` to `'age'`
|
||||
|
||||
@@ -170,6 +170,9 @@ describe('PhoneCat controllers', function() {
|
||||
describe('PhoneListCtrl', function(){
|
||||
var scope, ctrl, $httpBackend;
|
||||
|
||||
// Load our app module definition before each test.
|
||||
beforeEach(module('phonecatApp'));
|
||||
|
||||
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
|
||||
// This allows us to inject a service but then attach it to a variable
|
||||
// with the same name as the service.
|
||||
|
||||
@@ -5,11 +5,19 @@ var docsApp = {
|
||||
filter: {}
|
||||
};
|
||||
|
||||
docsApp.controller.DocsVersionsCtrl = ['$scope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $window, NG_VERSIONS, NG_VERSION) {
|
||||
docsApp.controller.DocsVersionsCtrl = ['$scope', '$rootScope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $rootScope, $window, NG_VERSIONS, NG_VERSION) {
|
||||
$scope.docs_versions = NG_VERSIONS;
|
||||
$scope.docs_version = NG_VERSIONS[0];
|
||||
|
||||
$scope.jumpToDocsVersion = function(version) {
|
||||
$window.location = version.url;
|
||||
var currentPagePath = '';
|
||||
|
||||
// preserve URL path when switching between doc versions
|
||||
if (angular.isObject($rootScope.currentPage) && $rootScope.currentPage.section && $rootScope.currentPage.id) {
|
||||
currentPagePath = '/' + $rootScope.currentPage.section + '/' + $rootScope.currentPage.id;
|
||||
}
|
||||
|
||||
$window.location = version.url + currentPagePath;
|
||||
};
|
||||
}];
|
||||
|
||||
@@ -645,7 +653,7 @@ docsApp.serviceFactory.sections = ['NG_PAGES', function sections(NG_PAGES) {
|
||||
}];
|
||||
|
||||
|
||||
docsApp.controller.DocsController = function($scope, $location, $window, $cookies, sections) {
|
||||
docsApp.controller.DocsController = function($scope, $rootScope, $location, $window, $cookies, sections) {
|
||||
$scope.fold = function(url) {
|
||||
if(url) {
|
||||
$scope.docs_fold = '/notes/' + url;
|
||||
@@ -736,9 +744,9 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
sectionName = SECTION_NAME[sectionId] || sectionId,
|
||||
page = sections.getPage(sectionId, partialId);
|
||||
|
||||
$scope.currentPage = sections.getPage(sectionId, partialId);
|
||||
$rootScope.currentPage = sections.getPage(sectionId, partialId);
|
||||
|
||||
if (!$scope.currentPage) {
|
||||
if (!$rootScope.currentPage) {
|
||||
$scope.partialTitle = 'Error: Page Not Found!';
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -10,12 +10,12 @@ module.exports = function(config) {
|
||||
|
||||
'build/angular.js',
|
||||
'build/angular-cookies.js',
|
||||
'build/angular-mocks.js',
|
||||
'build/angular-resource.js',
|
||||
'build/angular-touch.js',
|
||||
'build/angular-sanitize.js',
|
||||
'build/angular-route.js',
|
||||
'build/angular-animate.js',
|
||||
'build/angular-mocks.js',
|
||||
|
||||
'build/docs/components/lunr.js',
|
||||
'build/docs/components/google-code-prettify.js',
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"version": "1.2.8",
|
||||
"cdnVersion": "1.2.7",
|
||||
"codename": "interdimensional-cartography",
|
||||
"version": "1.2.9",
|
||||
"cdnVersion": "1.2.8",
|
||||
"codename": "enchanted-articulacy",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
|
||||
+2
-1
@@ -192,7 +192,8 @@ function isArrayLike(obj) {
|
||||
* is the value of an object property or an array element and `key` is the object property key or
|
||||
* array element index. Specifying a `context` for the function is optional.
|
||||
*
|
||||
* Note: this function was previously known as `angular.foreach`.
|
||||
* It is worth nothing that `.forEach` does not iterate over inherited properties because it filters
|
||||
* using the `hasOwnProperty` method.
|
||||
*
|
||||
<pre>
|
||||
var values = {name: 'misko', gender: 'male'};
|
||||
|
||||
+16
-11
@@ -474,7 +474,7 @@ function annotate(fn) {
|
||||
* constructor function that will be used to instantiate the service instance.
|
||||
*
|
||||
* You should use {@link AUTO.$provide#methods_service $provide.service(class)} if you define your service
|
||||
* as a type/class. This is common when using {@link http://coffeescript.org CoffeeScript}.
|
||||
* as a type/class.
|
||||
*
|
||||
* @param {string} name The name of the instance.
|
||||
* @param {Function} constructor A class (constructor function) that will be instantiated.
|
||||
@@ -482,20 +482,25 @@ function annotate(fn) {
|
||||
*
|
||||
* @example
|
||||
* Here is an example of registering a service using
|
||||
* {@link AUTO.$provide#methods_service $provide.service(class)} that is defined as a CoffeeScript class.
|
||||
* {@link AUTO.$provide#methods_service $provide.service(class)}.
|
||||
* <pre>
|
||||
* class Ping
|
||||
* constructor: (@$http) ->
|
||||
* send: () =>
|
||||
* @$http.get('/ping')
|
||||
*
|
||||
* $provide.service('ping', ['$http', Ping])
|
||||
* $provide.service('ping', ['$http', function($http) {
|
||||
* var Ping = function() {
|
||||
* this.$http = $http;
|
||||
* };
|
||||
*
|
||||
* Ping.prototype.send = function() {
|
||||
* return this.$http.get('/ping');
|
||||
* };
|
||||
*
|
||||
* return Ping;
|
||||
* }]);
|
||||
* </pre>
|
||||
* You would then inject and use this service like this:
|
||||
* <pre>
|
||||
* someModule.controller 'Ctrl', ['ping', (ping) ->
|
||||
* ping.send()
|
||||
* ]
|
||||
* someModule.controller('Ctrl', ['ping', function(ping) {
|
||||
* ping.send();
|
||||
* }]);
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
|
||||
+4
-4
@@ -111,9 +111,9 @@ function $HttpProvider() {
|
||||
common: {
|
||||
'Accept': 'application/json, text/plain, */*'
|
||||
},
|
||||
post: CONTENT_TYPE_APPLICATION_JSON,
|
||||
put: CONTENT_TYPE_APPLICATION_JSON,
|
||||
patch: CONTENT_TYPE_APPLICATION_JSON
|
||||
post: copy(CONTENT_TYPE_APPLICATION_JSON),
|
||||
put: copy(CONTENT_TYPE_APPLICATION_JSON),
|
||||
patch: copy(CONTENT_TYPE_APPLICATION_JSON)
|
||||
},
|
||||
|
||||
xsrfCookieName: 'XSRF-TOKEN',
|
||||
@@ -324,7 +324,7 @@ function $HttpProvider() {
|
||||
* to `push` or `unshift` a new transformation function into the transformation chain. You can
|
||||
* also decide to completely override any default transformations by assigning your
|
||||
* transformation functions to these properties directly without the array wrapper. These defaults
|
||||
* are again available on the $http factory at run-time, which may be useful if you have run-time
|
||||
* are again available on the $http factory at run-time, which may be useful if you have run-time
|
||||
* services you wish to be involved in your transformations.
|
||||
*
|
||||
* Similarly, to locally override the request/response transforms, augment the
|
||||
|
||||
+1
-1
@@ -632,7 +632,7 @@ function $RootScopeProvider(){
|
||||
|
||||
// `break traverseScopesLoop;` takes us to here
|
||||
|
||||
if(dirty && !(ttl--)) {
|
||||
if((dirty || asyncQueue.length) && !(ttl--)) {
|
||||
clearPhase();
|
||||
throw $rootScopeMinErr('infdig',
|
||||
'{0} $digest() iterations reached. Aborting!\n' +
|
||||
|
||||
+1
-1
@@ -321,7 +321,7 @@ function $SceDelegateProvider() {
|
||||
*
|
||||
* @param {*} value The result of a prior {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}
|
||||
* call or anything else.
|
||||
* @returns {*} The value the was originally provided to {@link ng.$sceDelegate#methods_trustAs
|
||||
* @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#methods_trustAs
|
||||
* `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
|
||||
* `value` unchanged.
|
||||
*/
|
||||
|
||||
+135
-32
@@ -248,6 +248,26 @@ angular.module('ngAnimate', ['ng'])
|
||||
* Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
|
||||
*
|
||||
*/
|
||||
.factory('$$animateReflow', ['$window', '$timeout', function($window, $timeout) {
|
||||
var requestAnimationFrame = $window.requestAnimationFrame ||
|
||||
$window.webkitRequestAnimationFrame ||
|
||||
function(fn) {
|
||||
return $timeout(fn, 10, false);
|
||||
};
|
||||
|
||||
var cancelAnimationFrame = $window.cancelAnimationFrame ||
|
||||
$window.webkitCancelAnimationFrame ||
|
||||
function(timer) {
|
||||
return $timeout.cancel(timer);
|
||||
};
|
||||
return function(fn) {
|
||||
var id = requestAnimationFrame(fn);
|
||||
return function() {
|
||||
cancelAnimationFrame(id);
|
||||
};
|
||||
};
|
||||
}])
|
||||
|
||||
.config(['$provide', '$animateProvider', function($provide, $animateProvider) {
|
||||
var noop = angular.noop;
|
||||
var forEach = angular.forEach;
|
||||
@@ -295,6 +315,10 @@ angular.module('ngAnimate', ['ng'])
|
||||
return classNameFilter.test(className);
|
||||
};
|
||||
|
||||
function async(fn) {
|
||||
return $timeout(fn, 0, false);
|
||||
}
|
||||
|
||||
function lookup(name) {
|
||||
if (name) {
|
||||
var matches = [],
|
||||
@@ -586,6 +610,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
//best to catch this early on to prevent any animation operations from occurring
|
||||
if(!node || !isAnimatableClassName(classes)) {
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
closeAnimation();
|
||||
return;
|
||||
}
|
||||
@@ -605,6 +631,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
//NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found.
|
||||
if (animationsDisabled(element, parentElement) || matches.length === 0) {
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
closeAnimation();
|
||||
return;
|
||||
}
|
||||
@@ -643,14 +671,17 @@ angular.module('ngAnimate', ['ng'])
|
||||
//animation do it's thing and close this one early
|
||||
if(animations.length === 0) {
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
fireDoneCallbackAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
var ONE_SPACE = ' ';
|
||||
//this value will be searched for class-based CSS className lookup. Therefore,
|
||||
//we prefix and suffix the current className value with spaces to avoid substring
|
||||
//lookups of className tokens
|
||||
var futureClassName = ' ' + currentClassName + ' ';
|
||||
var futureClassName = ONE_SPACE + currentClassName + ONE_SPACE;
|
||||
if(ngAnimateState.running) {
|
||||
//if an animation is currently running on the element then lets take the steps
|
||||
//to cancel that animation and fire any required callbacks
|
||||
@@ -658,12 +689,23 @@ angular.module('ngAnimate', ['ng'])
|
||||
cleanup(element);
|
||||
cancelAnimations(ngAnimateState.animations);
|
||||
|
||||
//in the event that the CSS is class is quickly added and removed back
|
||||
//then we don't want to wait until after the reflow to add/remove the CSS
|
||||
//class since both class animations may run into a race condition.
|
||||
//The code below will check to see if that is occurring and will
|
||||
//immediately remove the former class before the reflow so that the
|
||||
//animation can snap back to the original animation smoothly
|
||||
var isFullyClassBasedAnimation = isClassBased && !ngAnimateState.structural;
|
||||
var isRevertingClassAnimation = isFullyClassBasedAnimation &&
|
||||
ngAnimateState.className == className &&
|
||||
animationEvent != ngAnimateState.event;
|
||||
|
||||
//if the class is removed during the reflow then it will revert the styles temporarily
|
||||
//back to the base class CSS styling causing a jump-like effect to occur. This check
|
||||
//here ensures that the domOperation is only performed after the reflow has commenced
|
||||
if(ngAnimateState.beforeComplete) {
|
||||
if(ngAnimateState.beforeComplete || isRevertingClassAnimation) {
|
||||
(ngAnimateState.done || noop)(true);
|
||||
} else if(isClassBased && !ngAnimateState.structural) {
|
||||
} else if(isFullyClassBasedAnimation) {
|
||||
//class-based animations will compare element className values after cancelling the
|
||||
//previous animation to see if the element properties already contain the final CSS
|
||||
//class and if so then the animation will be skipped. Since the domOperation will
|
||||
@@ -671,8 +713,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
//will be invalid. Therefore the same string manipulation that would occur within the
|
||||
//DOM operation will be performed below so that the class comparison is valid...
|
||||
futureClassName = ngAnimateState.event == 'removeClass' ?
|
||||
futureClassName.replace(ngAnimateState.className, '') :
|
||||
futureClassName + ngAnimateState.className + ' ';
|
||||
futureClassName.replace(ONE_SPACE + ngAnimateState.className + ONE_SPACE, ONE_SPACE) :
|
||||
futureClassName + ngAnimateState.className + ONE_SPACE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,10 +722,12 @@ angular.module('ngAnimate', ['ng'])
|
||||
//(on addClass) or doesn't contain (on removeClass) the className being animated.
|
||||
//The reason why this is being called after the previous animations are cancelled
|
||||
//is so that the CSS classes present on the element can be properly examined.
|
||||
var classNameToken = ' ' + className + ' ';
|
||||
var classNameToken = ONE_SPACE + className + ONE_SPACE;
|
||||
if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) ||
|
||||
(animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
fireDoneCallbackAsync();
|
||||
return;
|
||||
}
|
||||
@@ -724,6 +768,10 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
|
||||
function invokeRegisteredAnimationFns(animations, phase, allAnimationFnsComplete) {
|
||||
phase == 'after' ?
|
||||
fireAfterCallbackAsync() :
|
||||
fireBeforeCallbackAsync();
|
||||
|
||||
var endFnName = phase + 'End';
|
||||
forEach(animations, function(animation, index) {
|
||||
var animationPhaseCompleted = function() {
|
||||
@@ -760,8 +808,27 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
}
|
||||
|
||||
function fireDOMCallback(animationPhase) {
|
||||
element.triggerHandler('$animate:' + animationPhase, {
|
||||
event : animationEvent,
|
||||
className : className
|
||||
});
|
||||
}
|
||||
|
||||
function fireBeforeCallbackAsync() {
|
||||
async(function() {
|
||||
fireDOMCallback('before');
|
||||
});
|
||||
}
|
||||
|
||||
function fireAfterCallbackAsync() {
|
||||
async(function() {
|
||||
fireDOMCallback('after');
|
||||
});
|
||||
}
|
||||
|
||||
function fireDoneCallbackAsync() {
|
||||
doneCallback && $timeout(doneCallback, 0, false);
|
||||
doneCallback && async(doneCallback);
|
||||
}
|
||||
|
||||
//it is less complicated to use a flag than managing and cancelling
|
||||
@@ -785,9 +852,9 @@ angular.module('ngAnimate', ['ng'])
|
||||
if(isClassBased) {
|
||||
cleanup(element);
|
||||
} else {
|
||||
data.closeAnimationTimeout = $timeout(function() {
|
||||
data.closeAnimationTimeout = async(function() {
|
||||
cleanup(element);
|
||||
}, 0, false);
|
||||
});
|
||||
element.data(NG_ANIMATE_STATE, data);
|
||||
}
|
||||
}
|
||||
@@ -811,10 +878,10 @@ angular.module('ngAnimate', ['ng'])
|
||||
function cancelAnimations(animations) {
|
||||
var isCancelledFlag = true;
|
||||
forEach(animations, function(animation) {
|
||||
if(!animations.beforeComplete) {
|
||||
if(!animation.beforeComplete) {
|
||||
(animation.beforeEnd || noop)(isCancelledFlag);
|
||||
}
|
||||
if(!animations.afterComplete) {
|
||||
if(!animation.afterComplete) {
|
||||
(animation.afterEnd || noop)(isCancelledFlag);
|
||||
}
|
||||
});
|
||||
@@ -860,7 +927,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
}]);
|
||||
|
||||
$animateProvider.register('', ['$window', '$sniffer', '$timeout', function($window, $sniffer, $timeout) {
|
||||
$animateProvider.register('', ['$window', '$sniffer', '$timeout', '$$animateReflow',
|
||||
function($window, $sniffer, $timeout, $$animateReflow) {
|
||||
// Detect proper transitionend/animationend event names.
|
||||
var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
|
||||
|
||||
@@ -905,11 +973,13 @@ angular.module('ngAnimate', ['ng'])
|
||||
var parentCounter = 0;
|
||||
var animationReflowQueue = [];
|
||||
var animationElementQueue = [];
|
||||
var animationTimer;
|
||||
var cancelAnimationReflow;
|
||||
var closingAnimationTime = 0;
|
||||
var timeOut = false;
|
||||
function afterReflow(element, callback) {
|
||||
$timeout.cancel(animationTimer);
|
||||
if(cancelAnimationReflow) {
|
||||
cancelAnimationReflow();
|
||||
}
|
||||
|
||||
animationReflowQueue.push(callback);
|
||||
|
||||
@@ -918,15 +988,19 @@ angular.module('ngAnimate', ['ng'])
|
||||
animationElementQueue.push(element);
|
||||
|
||||
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
closingAnimationTime = Math.max(closingAnimationTime,
|
||||
(elementData.maxDelay + elementData.maxDuration) * CLOSING_TIME_BUFFER * ONE_SECOND);
|
||||
|
||||
var stagger = elementData.stagger;
|
||||
var staggerTime = elementData.itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0);
|
||||
|
||||
var animationTime = (elementData.maxDelay + elementData.maxDuration) * CLOSING_TIME_BUFFER;
|
||||
closingAnimationTime = Math.max(closingAnimationTime, (staggerTime + animationTime) * ONE_SECOND);
|
||||
|
||||
//by placing a counter we can avoid an accidental
|
||||
//race condition which may close an animation when
|
||||
//a follow-up animation is midway in its animation
|
||||
elementData.animationCount = animationCounter;
|
||||
|
||||
animationTimer = $timeout(function() {
|
||||
cancelAnimationReflow = $$animateReflow(function() {
|
||||
forEach(animationReflowQueue, function(fn) {
|
||||
fn();
|
||||
});
|
||||
@@ -947,11 +1021,11 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
animationReflowQueue = [];
|
||||
animationElementQueue = [];
|
||||
animationTimer = null;
|
||||
cancelAnimationReflow = null;
|
||||
lookupCache = {};
|
||||
closingAnimationTime = 0;
|
||||
animationCounter++;
|
||||
}, 10, false);
|
||||
});
|
||||
}
|
||||
|
||||
function closeAllAnimations(elements, count) {
|
||||
@@ -1042,13 +1116,13 @@ angular.module('ngAnimate', ['ng'])
|
||||
return parentID + '-' + extractElementNode(element).className;
|
||||
}
|
||||
|
||||
function animateSetup(element, className) {
|
||||
function animateSetup(element, className, calculationDecorator) {
|
||||
var cacheKey = getCacheKey(element);
|
||||
var eventCacheKey = cacheKey + ' ' + className;
|
||||
var stagger = {};
|
||||
var ii = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
|
||||
var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
|
||||
|
||||
if(ii > 0) {
|
||||
if(itemIndex > 0) {
|
||||
var staggerClassName = className + '-stagger';
|
||||
var staggerCacheKey = cacheKey + ' ' + staggerClassName;
|
||||
var applyClasses = !lookupCache[staggerCacheKey];
|
||||
@@ -1060,9 +1134,16 @@ angular.module('ngAnimate', ['ng'])
|
||||
applyClasses && element.removeClass(staggerClassName);
|
||||
}
|
||||
|
||||
/* the animation itself may need to add/remove special CSS classes
|
||||
* before calculating the anmation styles */
|
||||
calculationDecorator = calculationDecorator ||
|
||||
function(fn) { return fn(); };
|
||||
|
||||
element.addClass(className);
|
||||
|
||||
var timings = getElementAnimationDetails(element, eventCacheKey);
|
||||
var timings = calculationDecorator(function() {
|
||||
return getElementAnimationDetails(element, eventCacheKey);
|
||||
});
|
||||
|
||||
/* there is no point in performing a reflow if the animation
|
||||
timeout is empty (this would cause a flicker bug normally
|
||||
@@ -1094,7 +1175,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
classes : className + ' ' + activeClassName,
|
||||
timings : timings,
|
||||
stagger : stagger,
|
||||
ii : ii
|
||||
itemIndex : itemIndex
|
||||
});
|
||||
|
||||
return true;
|
||||
@@ -1139,7 +1220,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * ONE_SECOND;
|
||||
var startTime = Date.now();
|
||||
var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
|
||||
var ii = elementData.ii;
|
||||
var itemIndex = elementData.itemIndex;
|
||||
|
||||
var style = '', appliedStyles = [];
|
||||
if(timings.transitionDuration > 0) {
|
||||
@@ -1152,17 +1233,17 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
}
|
||||
|
||||
if(ii > 0) {
|
||||
if(itemIndex > 0) {
|
||||
if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
|
||||
var delayStyle = timings.transitionDelayStyle;
|
||||
style += CSS_PREFIX + 'transition-delay: ' +
|
||||
prepareStaggerDelay(delayStyle, stagger.transitionDelay, ii) + '; ';
|
||||
prepareStaggerDelay(delayStyle, stagger.transitionDelay, itemIndex) + '; ';
|
||||
appliedStyles.push(CSS_PREFIX + 'transition-delay');
|
||||
}
|
||||
|
||||
if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
|
||||
style += CSS_PREFIX + 'animation-delay: ' +
|
||||
prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, ii) + '; ';
|
||||
prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, itemIndex) + '; ';
|
||||
appliedStyles.push(CSS_PREFIX + 'animation-delay');
|
||||
}
|
||||
}
|
||||
@@ -1227,8 +1308,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
return style;
|
||||
}
|
||||
|
||||
function animateBefore(element, className) {
|
||||
if(animateSetup(element, className)) {
|
||||
function animateBefore(element, className, calculationDecorator) {
|
||||
if(animateSetup(element, className, calculationDecorator)) {
|
||||
return function(cancelled) {
|
||||
cancelled && animateClose(element, className);
|
||||
};
|
||||
@@ -1323,7 +1404,18 @@ angular.module('ngAnimate', ['ng'])
|
||||
},
|
||||
|
||||
beforeAddClass : function(element, className, animationCompleted) {
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'));
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'), function(fn) {
|
||||
|
||||
/* when a CSS class is added to an element then the transition style that
|
||||
* is applied is the transition defined on the element when the CSS class
|
||||
* is added at the time of the animation. This is how CSS3 functions
|
||||
* outside of ngAnimate. */
|
||||
element.addClass(className);
|
||||
var timings = fn();
|
||||
element.removeClass(className);
|
||||
return timings;
|
||||
});
|
||||
|
||||
if(cancellationMethod) {
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element);
|
||||
@@ -1340,7 +1432,18 @@ angular.module('ngAnimate', ['ng'])
|
||||
},
|
||||
|
||||
beforeRemoveClass : function(element, className, animationCompleted) {
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'));
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'), function(fn) {
|
||||
/* when classes are removed from an element then the transition style
|
||||
* that is applied is the transition defined on the element without the
|
||||
* CSS class being there. This is how CSS3 functions outside of ngAnimate.
|
||||
* http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */
|
||||
var klass = element.attr('class');
|
||||
element.removeClass(className);
|
||||
var timings = fn();
|
||||
element.attr('class', klass);
|
||||
return timings;
|
||||
});
|
||||
|
||||
if(cancellationMethod) {
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element);
|
||||
|
||||
Vendored
+30
-1
@@ -756,6 +756,36 @@ angular.mock.TzDate = function (offset, timestamp) {
|
||||
angular.mock.TzDate.prototype = Date.prototype;
|
||||
/* jshint +W101 */
|
||||
|
||||
// TODO(matias): remove this IMMEDIATELY once we can properly detect the
|
||||
// presence of a registered module
|
||||
var animateLoaded;
|
||||
try {
|
||||
angular.module('ngAnimate');
|
||||
animateLoaded = true;
|
||||
} catch(e) {}
|
||||
|
||||
if(animateLoaded) {
|
||||
angular.module('ngAnimate').config(['$provide', function($provide) {
|
||||
var reflowQueue = [];
|
||||
$provide.value('$$animateReflow', function(fn) {
|
||||
reflowQueue.push(fn);
|
||||
return angular.noop;
|
||||
});
|
||||
$provide.decorator('$animate', function($delegate) {
|
||||
$delegate.triggerReflow = function() {
|
||||
if(reflowQueue.length === 0) {
|
||||
throw new Error('No animation reflows present');
|
||||
}
|
||||
angular.forEach(reflowQueue, function(fn) {
|
||||
fn();
|
||||
});
|
||||
reflowQueue = [];
|
||||
};
|
||||
return $delegate;
|
||||
});
|
||||
}]);
|
||||
}
|
||||
|
||||
angular.mock.animate = angular.module('mock.animate', ['ng'])
|
||||
|
||||
.config(['$provide', function($provide) {
|
||||
@@ -1913,7 +1943,6 @@ angular.mock.clearDataCache = function() {
|
||||
};
|
||||
|
||||
|
||||
|
||||
if(window.jasmine || window.mocha) {
|
||||
|
||||
var currentSpec = null,
|
||||
|
||||
@@ -26,6 +26,15 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
|
||||
*
|
||||
* @scope
|
||||
* @priority 400
|
||||
* @param {string=} onload Expression to evaluate whenever the view updates.
|
||||
*
|
||||
* @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
|
||||
* $anchorScroll} to scroll the viewport after the view is updated.
|
||||
*
|
||||
* - If the attribute is not set, disable scrolling.
|
||||
* - If the attribute is set without value, enable scrolling.
|
||||
* - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
|
||||
* as an expression yields a truthy value.
|
||||
* @example
|
||||
<example module="ngViewExample" deps="angular-route.js" animations="true">
|
||||
<file name="index.html">
|
||||
|
||||
@@ -1439,6 +1439,12 @@ describe('$http', function() {
|
||||
$http.get('/url');
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it('should have seperate opbjects for defaults PUT and POST', function() {
|
||||
expect($http.defaults.headers.post).not.toBe($http.defaults.headers.put);
|
||||
expect($http.defaults.headers.post).not.toBe($http.defaults.headers.patch);
|
||||
expect($http.defaults.headers.put).not.toBe($http.defaults.headers.patch);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -258,6 +258,31 @@ describe('Scope', function() {
|
||||
}));
|
||||
|
||||
|
||||
it('should prevent infinite loop when creating and resolving a promise in a watched expression', function() {
|
||||
module(function($rootScopeProvider) {
|
||||
$rootScopeProvider.digestTtl(10);
|
||||
});
|
||||
inject(function($rootScope, $q) {
|
||||
var d = $q.defer();
|
||||
|
||||
d.resolve('Hello, world.');
|
||||
$rootScope.$watch(function () {
|
||||
var $d2 = $q.defer();
|
||||
$d2.resolve('Goodbye.');
|
||||
$d2.promise.then(function () { });
|
||||
return d.promise;
|
||||
}, function () { return 0; });
|
||||
|
||||
expect(function() {
|
||||
$rootScope.$digest();
|
||||
}).toThrowMinErr('$rootScope', 'infdig', '10 $digest() iterations reached. Aborting!\n'+
|
||||
'Watchers fired in the last 5 iterations: []');
|
||||
|
||||
expect($rootScope.$$phase).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should not fire upon $watch registration on initial $digest', inject(function($rootScope) {
|
||||
var log = '';
|
||||
$rootScope.a = 1;
|
||||
|
||||
+343
-108
@@ -280,7 +280,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -298,7 +298,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-leave')).toBe(true);
|
||||
expect(child.hasClass('ng-leave-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -322,7 +322,7 @@ describe("ngAnimate", function() {
|
||||
$animate.move(child1, element, child2);
|
||||
$rootScope.$digest();
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(element.text()).toBe('21');
|
||||
}));
|
||||
@@ -336,7 +336,7 @@ describe("ngAnimate", function() {
|
||||
expect(child).toBeHidden();
|
||||
$animate.removeClass(child, 'ng-hide');
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-hide-remove')).toBe(true);
|
||||
expect(child.hasClass('ng-hide-remove-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -354,7 +354,7 @@ describe("ngAnimate", function() {
|
||||
expect(child).toBeShown();
|
||||
$animate.addClass(child, 'ng-hide');
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-hide-add')).toBe(true);
|
||||
expect(child.hasClass('ng-hide-add-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -374,7 +374,7 @@ describe("ngAnimate", function() {
|
||||
//enter
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(child.attr('class')).toContain('ng-enter');
|
||||
expect(child.attr('class')).toContain('ng-enter-active');
|
||||
@@ -385,7 +385,7 @@ describe("ngAnimate", function() {
|
||||
element.append(after);
|
||||
$animate.move(child, element, after);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(child.attr('class')).toContain('ng-move');
|
||||
expect(child.attr('class')).toContain('ng-move-active');
|
||||
@@ -394,14 +394,14 @@ describe("ngAnimate", function() {
|
||||
|
||||
//hide
|
||||
$animate.addClass(child, 'ng-hide');
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.attr('class')).toContain('ng-hide-add');
|
||||
expect(child.attr('class')).toContain('ng-hide-add-active');
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
|
||||
//show
|
||||
$animate.removeClass(child, 'ng-hide');
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.attr('class')).toContain('ng-hide-remove');
|
||||
expect(child.attr('class')).toContain('ng-hide-remove-active');
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -409,7 +409,7 @@ describe("ngAnimate", function() {
|
||||
//leave
|
||||
$animate.leave(child);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.attr('class')).toContain('ng-leave');
|
||||
expect(child.attr('class')).toContain('ng-leave-active');
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -435,7 +435,7 @@ describe("ngAnimate", function() {
|
||||
element.addClass('ng-hide');
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(element.text()).toBe('memento');
|
||||
}));
|
||||
@@ -455,7 +455,9 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.leave(child);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(child).toBeHidden(); //hides instantly
|
||||
|
||||
//lets change this to prove that done doesn't fire anymore for the previous hide() operation
|
||||
@@ -485,7 +487,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
//this is to verify that the existing style is appended with a semicolon automatically
|
||||
expect(child.attr('style')).toMatch(/width: 20px;.+?/i);
|
||||
@@ -504,6 +506,7 @@ describe("ngAnimate", function() {
|
||||
child.addClass('custom-delay ng-hide');
|
||||
$animate.removeClass(child, 'ng-hide');
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
$timeout.flush(2000);
|
||||
@@ -530,7 +533,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
expect(completed).toBe(false);
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
$timeout.flush();
|
||||
@@ -661,7 +664,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.animations) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'animationend', { timeStamp: Date.now() + 4000, elapsedTime: 4 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
@@ -686,7 +689,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.animations) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'animationend', { timeStamp: Date.now() + 6000, elapsedTime: 6 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
@@ -713,7 +716,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'animationend', { timeStamp : Date.now() + 20000, elapsedTime: 10 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
@@ -751,7 +754,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
|
||||
if($sniffer.animations) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-remove')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-remove-active')).toBe(true);
|
||||
}
|
||||
@@ -762,7 +765,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
|
||||
if($sniffer.animations) { //cleanup some pending animations
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-add')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-add-active')).toBe(true);
|
||||
browserTrigger(element,'animationend', { timeStamp: Date.now() + 2000, elapsedTime: 2 });
|
||||
@@ -806,7 +809,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).toMatch(/animation-delay: 0\.1\d*s/);
|
||||
@@ -823,7 +826,13 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
var expectFailure = true;
|
||||
try {
|
||||
$animate.triggerReflow();
|
||||
expectFailure = false;
|
||||
} catch(e) {}
|
||||
|
||||
expect(expectFailure).toBe(true);
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).not.toMatch(/animation-delay: 0\.1\d*s/);
|
||||
@@ -859,7 +868,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).toMatch(/animation-delay: 1\.1\d*s,\s*2\.1\d*s/);
|
||||
@@ -896,7 +905,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
@@ -920,7 +929,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
var now = Date.now();
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
|
||||
@@ -948,7 +957,6 @@ describe("ngAnimate", function() {
|
||||
|
||||
element.addClass('ng-hide');
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
$timeout.flush(0);
|
||||
expect(element).toBeShown();
|
||||
$animate.enabled(true);
|
||||
|
||||
@@ -957,7 +965,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
var now = Date.now();
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 3000, elapsedTime: 3 });
|
||||
@@ -985,7 +993,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
var now = Date.now();
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
|
||||
@@ -1014,7 +1022,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
if ($sniffer.transitions) {
|
||||
@@ -1039,7 +1047,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-remove')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-remove-active')).toBe(true);
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -1051,7 +1059,7 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element, 'ng-hide');
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-add')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-add-active')).toBe(true);
|
||||
}
|
||||
@@ -1092,7 +1100,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).toMatch(/transition-delay: 0\.1\d*s/);
|
||||
@@ -1109,7 +1117,14 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
var expectFailure = true;
|
||||
try {
|
||||
$animate.triggerReflow();
|
||||
expectFailure = false;
|
||||
} catch(e) {}
|
||||
|
||||
expect(expectFailure).toBe(true);
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).not.toMatch(/transition-delay: 0\.1\d*s/);
|
||||
@@ -1145,7 +1160,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toMatch(/transition-duration: 1\d*s,\s*3\d*s;/);
|
||||
expect(elements[0].attr('style')).not.toContain('transition-delay');
|
||||
@@ -1155,7 +1170,7 @@ describe("ngAnimate", function() {
|
||||
}));
|
||||
|
||||
|
||||
it("apply a closing timeout to close all pending transitions",
|
||||
it("should apply a closing timeout to close all pending transitions",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
@@ -1167,13 +1182,55 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.addClass(element, 'some-class');
|
||||
|
||||
$timeout.flush(10); //reflow
|
||||
$animate.triggerReflow(); //reflow
|
||||
expect(element.hasClass('some-class-add-active')).toBe(true);
|
||||
|
||||
$timeout.flush(7500); //closing timeout
|
||||
expect(element.hasClass('some-class-add-active')).toBe(false);
|
||||
}));
|
||||
|
||||
it("apply a closing timeout with respect to a staggering animation",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.entering-element.ng-enter',
|
||||
'-webkit-transition:5s linear all;' +
|
||||
'transition:5s linear all;');
|
||||
|
||||
ss.addRule('.entering-element.ng-enter-stagger',
|
||||
'-webkit-transition-delay:0.5s;' +
|
||||
'transition-delay:0.5s;');
|
||||
|
||||
element = $compile(html('<div></div>'))($rootScope);
|
||||
var kids = [];
|
||||
for(var i = 0; i < 5; i++) {
|
||||
kids.push(angular.element('<div class="entering-element"></div>'));
|
||||
$animate.enter(kids[i], element);
|
||||
}
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow(); //reflow
|
||||
expect(element.children().length).toBe(5);
|
||||
|
||||
for(var i = 0; i < 5; i++) {
|
||||
expect(kids[i].hasClass('ng-enter-active')).toBe(true);
|
||||
}
|
||||
|
||||
$timeout.flush(7500);
|
||||
|
||||
for(var i = 0; i < 5; i++) {
|
||||
expect(kids[i].hasClass('ng-enter-active')).toBe(true);
|
||||
}
|
||||
|
||||
//(stagger * index) + (duration + delay) * 150%
|
||||
$timeout.flush(9500); //0.5 * 4 + 5 * 1.5 = 9500;
|
||||
|
||||
for(var i = 0; i < 5; i++) {
|
||||
expect(kids[i].hasClass('ng-enter-active')).toBe(false);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
it("should not allow the closing animation to close off a successive animation midway",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
@@ -1189,12 +1246,12 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.addClass(element, 'some-class');
|
||||
|
||||
$timeout.flush(10); //reflow
|
||||
$animate.triggerReflow(); //reflow
|
||||
expect(element.hasClass('some-class-add-active')).toBe(true);
|
||||
|
||||
$animate.removeClass(element, 'some-class');
|
||||
|
||||
$timeout.flush(10); //second reflow
|
||||
$animate.triggerReflow(); //second reflow
|
||||
|
||||
$timeout.flush(7500); //closing timeout for the first animation
|
||||
expect(element.hasClass('some-class-remove-active')).toBe(true);
|
||||
@@ -1237,7 +1294,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
|
||||
@@ -1275,7 +1332,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('abc')).toBe(true);
|
||||
expect(element.hasClass('ng-enter')).toBe(true);
|
||||
expect(element.hasClass('ng-enter-active')).toBe(true);
|
||||
@@ -1289,7 +1346,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('xyz')).toBe(true);
|
||||
expect(element.hasClass('ng-enter')).toBe(true);
|
||||
expect(element.hasClass('ng-enter-active')).toBe(true);
|
||||
@@ -1317,7 +1374,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('one')).toBe(true);
|
||||
expect(element.hasClass('two')).toBe(true);
|
||||
expect(element.hasClass('ng-enter')).toBe(true);
|
||||
@@ -1439,6 +1496,68 @@ describe("ngAnimate", function() {
|
||||
expect(signature).toBe('AB');
|
||||
}));
|
||||
|
||||
it('should fire DOM callbacks on the element being animated',
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
if(!$sniffer.transitions) return;
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
ss.addRule('.klass-add', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
body.append($rootElement);
|
||||
|
||||
var steps = [];
|
||||
element.on('$animate:before', function(e, data) {
|
||||
steps.push(['before', data.className, data.event]);
|
||||
});
|
||||
|
||||
element.on('$animate:after', function(e, data) {
|
||||
steps.push(['after', data.className, data.event]);
|
||||
});
|
||||
|
||||
$animate.addClass(element, 'klass');
|
||||
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(steps.pop()).toEqual(['before', 'klass', 'addClass']);
|
||||
|
||||
$animate.triggerReflow();
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(steps.pop()).toEqual(['after', 'klass', 'addClass']);
|
||||
}));
|
||||
|
||||
it('should fire the DOM callbacks even if no animation is rendered',
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var parent = jqLite('<div></div>');
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(parent);
|
||||
body.append($rootElement);
|
||||
|
||||
var steps = [];
|
||||
element.on('$animate:before', function(e, data) {
|
||||
steps.push(['before', data.className, data.event]);
|
||||
});
|
||||
|
||||
element.on('$animate:after', function(e, data) {
|
||||
steps.push(['after', data.className, data.event]);
|
||||
});
|
||||
|
||||
$animate.enter(element, parent);
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(steps.shift()).toEqual(['before', 'ng-enter', 'enter']);
|
||||
expect(steps.shift()).toEqual(['after', 'ng-enter', 'enter']);
|
||||
}));
|
||||
|
||||
it("should fire a done callback when provided with no animation",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
@@ -1525,6 +1644,9 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
$animate.addClass(element, 'ng-hide'); //earlier animation cancelled
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
$timeout.flush();
|
||||
expect(signature).toBe('AB');
|
||||
}));
|
||||
@@ -1655,7 +1777,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
if($sniffer.transitions) {
|
||||
expect(element.hasClass('klass-add')).toBe(true);
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('klass')).toBe(true);
|
||||
expect(element.hasClass('klass-add-active')).toBe(true);
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 3000, elapsedTime: 3 });
|
||||
@@ -1670,7 +1792,7 @@ describe("ngAnimate", function() {
|
||||
if($sniffer.transitions) {
|
||||
expect(element.hasClass('klass-remove')).toBe(true);
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('klass')).toBe(false);
|
||||
expect(element.hasClass('klass-add')).toBe(false);
|
||||
expect(element.hasClass('klass-add-active')).toBe(false);
|
||||
@@ -1734,7 +1856,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('klass-add')).toBe(true);
|
||||
expect(element.hasClass('klass-add-active')).toBe(true);
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 11000, elapsedTime: 11 });
|
||||
@@ -1750,7 +1872,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('klass-remove')).toBe(true);
|
||||
expect(element.hasClass('klass-remove-active')).toBe(true);
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 11000, elapsedTime: 11 });
|
||||
@@ -1784,7 +1906,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('one-add')).toBe(true);
|
||||
expect(element.hasClass('two-add')).toBe(true);
|
||||
|
||||
@@ -1830,7 +1952,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('one-remove')).toBe(true);
|
||||
expect(element.hasClass('two-remove')).toBe(true);
|
||||
|
||||
@@ -1884,7 +2006,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -1908,7 +2030,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 9000, elapsedTime: 9 });
|
||||
@@ -1918,41 +2040,6 @@ describe("ngAnimate", function() {
|
||||
}));
|
||||
|
||||
|
||||
it("should not set the transition property flag if only CSS animations are used",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.animations) return;
|
||||
|
||||
ss.addRule('.sleek-animation.ng-enter', '-webkit-animation: my_animation 2s linear;' +
|
||||
'animation: my_animation 2s linear');
|
||||
|
||||
ss.addRule('.trans.ng-enter', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all');
|
||||
|
||||
var propertyKey = ($sniffer.vendorPrefix == 'Webkit' ? '-webkit-' : '') + 'transition-property';
|
||||
|
||||
var element = html($compile('<div>...</div>')($rootScope));
|
||||
var child = $compile('<div class="skeep-animation">...</div>')($rootScope);
|
||||
child.css(propertyKey,'background-color');
|
||||
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 2000, elapsedTime: 2 });
|
||||
|
||||
expect(child.css(propertyKey)).toBe('background-color');
|
||||
child.remove();
|
||||
|
||||
child = $compile('<div class="sleek-animation">...</div>')($rootScope);
|
||||
child.attr('class','trans');
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(child.css(propertyKey)).not.toBe('background-color');
|
||||
}));
|
||||
|
||||
|
||||
it("should skip animations if the browser does not support CSS3 transitions and CSS3 animations",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer) {
|
||||
|
||||
@@ -1997,9 +2084,8 @@ describe("ngAnimate", function() {
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.flush(10);
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
|
||||
@@ -2033,7 +2119,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
//this is added/removed right away otherwise
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
}
|
||||
@@ -2073,7 +2159,7 @@ describe("ngAnimate", function() {
|
||||
$animate.leave(child);
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter-active')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -2268,7 +2354,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
var empty = true;
|
||||
try {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
empty = false;
|
||||
}
|
||||
catch(e) {}
|
||||
@@ -2294,7 +2380,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
@@ -2354,7 +2440,7 @@ describe("ngAnimate", function() {
|
||||
expect(animationState).toBe('enter');
|
||||
if($sniffer.transitions) {
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
}
|
||||
|
||||
@@ -2371,7 +2457,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.addClass(child, 'something');
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(animationState).toBe('addClass');
|
||||
if($sniffer.transitions) {
|
||||
@@ -2390,7 +2476,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
|
||||
it("should wait until a queue of animations are complete before performing a reflow",
|
||||
inject(function($rootScope, $compile, $timeout,$sniffer) {
|
||||
inject(function($rootScope, $compile, $timeout, $sniffer, $animate) {
|
||||
|
||||
if(!$sniffer.transitions) return;
|
||||
|
||||
@@ -2402,7 +2488,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$rootScope.$digest();
|
||||
expect(element[0].querySelectorAll('.ng-enter-active').length).toBe(0);
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element[0].querySelectorAll('.ng-enter-active').length).toBe(5);
|
||||
|
||||
forEach(element.children(), function(kid) {
|
||||
@@ -2463,7 +2549,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
|
||||
it("should disable all child animations on structural animations until the first reflow has passed", function() {
|
||||
it("should disable all child animations on structural animations until the post animation timeout has passed", function() {
|
||||
var intercepted;
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.animated', function() {
|
||||
@@ -2577,7 +2663,6 @@ describe("ngAnimate", function() {
|
||||
$animate.enter(kid, element);
|
||||
}
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
//called three times since the classname is the same
|
||||
expect(count).toBe(2);
|
||||
@@ -2591,7 +2676,6 @@ describe("ngAnimate", function() {
|
||||
}
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
expect(count).toBe(20);
|
||||
});
|
||||
@@ -2659,12 +2743,51 @@ describe("ngAnimate", function() {
|
||||
expect(element.hasClass('green')).toBe(false);
|
||||
expect(element.hasClass('red')).toBe(false);
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(element.hasClass('green')).toBe(true);
|
||||
expect(element.hasClass('red')).toBe(true);
|
||||
}));
|
||||
|
||||
it("should avoid mixing up substring classes during add and remove operations", function() {
|
||||
var currentAnimation, currentFn;
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.on', function() {
|
||||
return {
|
||||
beforeAddClass : function(element, className, done) {
|
||||
currentAnimation = 'addClass';
|
||||
currentFn = done;
|
||||
return function(cancelled) {
|
||||
currentAnimation = cancelled ? null : currentAnimation;
|
||||
}
|
||||
},
|
||||
beforeRemoveClass : function(element, className, done) {
|
||||
currentAnimation = 'removeClass';
|
||||
currentFn = done;
|
||||
return function(cancelled) {
|
||||
currentAnimation = cancelled ? null : currentAnimation;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
var element = $compile('<div class="animation-enabled only"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$animate.addClass(element, 'on');
|
||||
expect(currentAnimation).toBe('addClass');
|
||||
currentFn();
|
||||
|
||||
currentAnimation = null;
|
||||
|
||||
$animate.removeClass(element, 'on');
|
||||
$animate.addClass(element, 'on');
|
||||
|
||||
expect(currentAnimation).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should enable and disable animations properly on the root element', function() {
|
||||
var count = 0;
|
||||
@@ -2770,17 +2893,17 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'base-class one two');
|
||||
|
||||
//still true since we're before the reflow
|
||||
expect(element.hasClass('base-class')).toBe(true);
|
||||
expect(element.hasClass('base-class')).toBe(false);
|
||||
|
||||
//this will cancel the remove animation
|
||||
$animate.addClass(element, 'base-class one two');
|
||||
|
||||
//the cancellation was a success and the class was added right away
|
||||
//since there was no successive animation for the after animation
|
||||
expect(element.hasClass('base-class')).toBe(true);
|
||||
expect(element.hasClass('base-class')).toBe(false);
|
||||
|
||||
//the reflow...
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
//the reflow DOM operation was commenced but it ran before so it
|
||||
//shouldn't run agaun
|
||||
@@ -2814,9 +2937,10 @@ describe("ngAnimate", function() {
|
||||
node._setAttribute(prop, val);
|
||||
};
|
||||
|
||||
expect(capturedProperty).toBe('none');
|
||||
$animate.addClass(element, 'trigger-class');
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(capturedProperty).not.toBe('none');
|
||||
}));
|
||||
@@ -2843,7 +2967,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
expect(node.style[animationKey]).toContain('none');
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(node.style[animationKey]).not.toContain('none');
|
||||
}));
|
||||
@@ -2888,7 +3012,7 @@ describe("ngAnimate", function() {
|
||||
expect(element[0].style[prop]).toContain('none');
|
||||
expect($window.getComputedStyle(element[0])[prop + 'Duration']).toBe('0s');
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2907,7 +3031,7 @@ describe("ngAnimate", function() {
|
||||
$animate.leave(element);
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 0.50999999991 });
|
||||
|
||||
@@ -2931,7 +3055,7 @@ describe("ngAnimate", function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $compile, $rootElement, $document, $timeout, $templateCache, $sniffer) {
|
||||
inject(function($rootScope, $compile, $rootElement, $document, $timeout, $templateCache, $sniffer, $animate) {
|
||||
if(!$sniffer.transitions) return;
|
||||
|
||||
$templateCache.put('item-template', 'item: #{{ item }} ');
|
||||
@@ -2950,7 +3074,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.tpl = 'item-template';
|
||||
$rootScope.items = [1,2,3];
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(capturedAnimation).toBe('enter');
|
||||
expect(element.text()).toContain('item: #1');
|
||||
@@ -2962,7 +3086,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$rootScope.items = [];
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(capturedAnimation).toBe('leave');
|
||||
});
|
||||
@@ -3017,5 +3141,116 @@ describe("ngAnimate", function() {
|
||||
expect(leaveDone).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should respect the most relevant CSS transition property if defined in multiple classes',
|
||||
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.base-class', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all;');
|
||||
|
||||
ss.addRule('.base-class.on', '-webkit-transition:5s linear all;' +
|
||||
'transition:5s linear all;');
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = $compile('<div class="base-class"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var ready = false;
|
||||
$animate.addClass(element, 'on', function() {
|
||||
ready = true;
|
||||
});
|
||||
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 1 });
|
||||
$timeout.flush(1);
|
||||
expect(ready).toBe(false);
|
||||
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 5 });
|
||||
$timeout.flush(1);
|
||||
expect(ready).toBe(true);
|
||||
|
||||
ready = false;
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
ready = true;
|
||||
});
|
||||
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 1 });
|
||||
$timeout.flush(1);
|
||||
expect(ready).toBe(true);
|
||||
}));
|
||||
|
||||
it('should not apply a transition upon removal of a class that has a transition',
|
||||
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.base-class.on', '-webkit-transition:5s linear all;' +
|
||||
'transition:5s linear all;');
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = $compile('<div class="base-class on"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var ready = false;
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
ready = true;
|
||||
});
|
||||
|
||||
$timeout.flush(1);
|
||||
expect(ready).toBe(true);
|
||||
}));
|
||||
|
||||
it('should avoid skip animations if the same CSS class is added / removed synchronously before the reflow kicks in',
|
||||
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.water-class', '-webkit-transition:2s linear all;' +
|
||||
'transition:2s linear all;');
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = $compile('<div class="water-class on"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var signature = '';
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
signature += 'A';
|
||||
});
|
||||
$animate.addClass(element, 'on', function() {
|
||||
signature += 'B';
|
||||
});
|
||||
|
||||
$timeout.flush(1);
|
||||
expect(signature).toBe('AB');
|
||||
|
||||
signature = '';
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
signature += 'A';
|
||||
});
|
||||
$animate.addClass(element, 'on', function() {
|
||||
signature += 'B';
|
||||
});
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
signature += 'C';
|
||||
});
|
||||
|
||||
$timeout.flush(1);
|
||||
expect(signature).toBe('AB');
|
||||
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2000 });
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(signature).toBe('ABC');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user