Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c086f831fb | |||
| fab2d3c92f | |||
| 4f84f6b3e4 | |||
| cf5e463abd | |||
| f288b8f010 | |||
| b7e4e92014 | |||
| 31c450bcee | |||
| a9fcb0d0fc | |||
| a3846ab837 | |||
| 1953b0bed9 | |||
| 2b73027136 | |||
| 1079105443 | |||
| d119e36302 | |||
| 98b2f8ef18 | |||
| e7ab857ddb | |||
| 46cba2e05d | |||
| 72894f0dd2 | |||
| 760f49de10 | |||
| 686b13bf60 | |||
| 56cc7bcc98 | |||
| b4eed8ad94 | |||
| 08793a690a | |||
| ef4bf8c77c | |||
| b6ab826c4b | |||
| 71f974b459 | |||
| a68624444a | |||
| 945fc1a4bc | |||
| ec900cabfc | |||
| f99fe799e2 | |||
| e7338d3f27 | |||
| 2dfbc083c5 | |||
| 27613fd500 | |||
| e645f7cae1 | |||
| ad275b2265 | |||
| 600e6218fe | |||
| 5218c7bbdc |
+6
-3
@@ -5,9 +5,12 @@ node_js:
|
||||
env:
|
||||
matrix:
|
||||
- JOB=unit
|
||||
- JOB=e2e-chrome
|
||||
- JOB=e2e-firefox
|
||||
- JOB=e2e-safari
|
||||
- JOB=e2e BROWSER=chrome JQVERSION=jqlite
|
||||
- JOB=e2e BROWSER=firefox JQVERSION=jqlite
|
||||
- JOB=e2e BROWSER=safari JQVERSION=jqlite
|
||||
- JOB=e2e BROWSER=chrome JQVERSION=jquery
|
||||
- JOB=e2e BROWSER=firefox JQVERSION=jquery
|
||||
- JOB=e2e BROWSER=safari JQVERSION=jquery
|
||||
global:
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
|
||||
@@ -1,3 +1,58 @@
|
||||
<a name="1.2.13"></a>
|
||||
# 1.2.13 romantic-transclusion (2014-02-14)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:** ensure $animate doesn't break natural CSS transitions
|
||||
([4f84f6b3](https://github.com/angular/angular.js/commit/4f84f6b3e4210ae1eb14728a46d43dd961700a0c),
|
||||
[#6019](https://github.com/angular/angular.js/issues/6019))
|
||||
- **$compile:**
|
||||
- ensure element transclusion directives are linked with comment element
|
||||
([e7338d3f](https://github.com/angular/angular.js/commit/e7338d3f27e8824196136a18e1c3e0fcf51a0e28),
|
||||
[#6006](https://github.com/angular/angular.js/issues/6006), [#6101](https://github.com/angular/angular.js/issues/6101))
|
||||
- support templates with table content root nodes
|
||||
([e7338d3f](https://github.com/angular/angular.js/commit/31c450bcee53d0a3827b7e0a611e9013b2496506),
|
||||
[#2848](https://github.com/angular/angular.js/issues/2848), [#1459](https://github.com/angular/angular.js/issues/1459), [#3647](https://github.com/angular/angular.js/issues/3647), [#3241](https://github.com/angular/angular.js/issues/3241))
|
||||
- **input:**
|
||||
- don't apply textInput to `<input type="file">`
|
||||
([a9fcb0d0](https://github.com/angular/angular.js/commit/a9fcb0d0fc6456f80501b8820d02b04d7c15b6d6),
|
||||
[#6247](https://github.com/angular/angular.js/issues/6247), [#6231](https://github.com/angular/angular.js/issues/6231))
|
||||
- setViewValue on compositionend
|
||||
([2b730271](https://github.com/angular/angular.js/commit/2b7302713674506fdbcdc396c38f18dcb90dee8c),
|
||||
[#6058](https://github.com/angular/angular.js/issues/6058), [#5433](https://github.com/angular/angular.js/issues/5433))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **filterFilter:** support deeply nested predicate objects
|
||||
([b4eed8ad](https://github.com/angular/angular.js/commit/b4eed8ad94ce9719540462c1ee969dfd3c6b2355),
|
||||
[#6215](https://github.com/angular/angular.js/issues/6215))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$animate:**
|
||||
- due to [4f84f6b3](https://github.com/angular/angular.js/commit/4f84f6b3e4210ae1eb14728a46d43dd961700a0c),
|
||||
ngClass and {{ class }} will now call the `setClass`
|
||||
animation callback instead of addClass / removeClass when both a
|
||||
addClass/removeClass operation is being executed on the element during the animation.
|
||||
|
||||
Please include the setClass animation callback as well as addClass and removeClass within
|
||||
your JS animations to work with ngClass and {{ class }} directives.
|
||||
|
||||
|
||||
- due to [cf5e463a](https://github.com/angular/angular.js/commit/cf5e463abd2c23f62e9c2e6361e6c53048c8910e),
|
||||
Both the `$animate:before` and `$animate:after` DOM events must be now
|
||||
registered prior to the $animate operation taking place. The `$animate:close` event
|
||||
can be registered anytime afterwards.
|
||||
|
||||
DOM callbacks used to fired for each and every animation operation that occurs within the
|
||||
$animate service provided in the ngAnimate module. This may end up slowing down an
|
||||
application if 100s of elements are being inserted into the page. Therefore after this
|
||||
change callbacks are only fired if registered on the element being animated.
|
||||
|
||||
|
||||
<a name="1.2.12"></a>
|
||||
# 1.2.12 cauliflower-eradication (2014-02-07)
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ module.exports = function(grunt) {
|
||||
|
||||
protractor: {
|
||||
normal: 'protractor-conf.js',
|
||||
jquery: 'protractor-jquery-conf.js',
|
||||
jenkins: 'protractor-jenkins-conf.js'
|
||||
},
|
||||
|
||||
@@ -292,6 +293,7 @@ module.exports = function(grunt) {
|
||||
grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
|
||||
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['tests:jqlite', 'tests:jquery', 'tests:modules']);
|
||||
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'protractor:normal']);
|
||||
grunt.registerTask('test:jq-protractor', 'Run the end to end tests against jquery with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'protractor:jquery']);
|
||||
grunt.registerTask('test:ci-protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'protractor:jenkins']);
|
||||
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
|
||||
grunt.registerTask('test:docgen', ['jasmine_node']);
|
||||
|
||||
@@ -9,3 +9,8 @@
|
||||
ng\:form {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ng-animate-block-transitions {
|
||||
transition:0s all!important;
|
||||
-webkit-transition:0s all!important;
|
||||
}
|
||||
|
||||
@@ -54,18 +54,18 @@ Try out the Live Preview above, and then let's walk through the example and desc
|
||||
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-databinding1.png">
|
||||
|
||||
This looks like normal HTML, with some new markup. In Angular, a file like this is called a
|
||||
<a name="template">"{@link templates template}"</a>. When Angular starts your application, it parses and
|
||||
<a name="template">"{@link templates template}"</a>. When Angular starts your application, it parses and
|
||||
processes this new markup from the template using the so called <a name="compiler">"{@link compiler compiler}"</a>.
|
||||
The loaded, transformed and rendered DOM is then called the <a name="view">"view"</a>.
|
||||
|
||||
The first kind of new markup are the so called <a name="directive">"{@link directive directives}"</a>.
|
||||
They apply special behavior to attributes or elements in the HTML. In the example above we use the
|
||||
They apply special behavior to attributes or elements in the HTML. In the example above we use the
|
||||
{@link api/ng.directive:ngApp `ng-app`} attribute, which is linked to a directive that automatically
|
||||
initializes our application. Angular also defines a directive for the {@link api/ng.directive:input `input`}
|
||||
element that adds extra behavior to the element. E.g. it is able to automatically validate that the entered
|
||||
text is non empty by evaluating the `required` attribute.
|
||||
element that adds extra behavior to the element. E.g. it is able to automatically validate that the entered
|
||||
text is non empty by evaluating the `required` attribute.
|
||||
The {@link api/ng.directive:ngModel `ng-model`} directive stores/updates
|
||||
the value of the input field into/from a variable and shows the validation state of the input field by
|
||||
the value of the input field into/from a variable and shows the validation state of the input field by
|
||||
adding css classes. In the example we use these css classes to mark an empty input field with a red border.
|
||||
|
||||
<div class="alert alert-info">
|
||||
@@ -120,7 +120,7 @@ different currencies and also pay the invoice.
|
||||
return this.convertCurrency(this.qty * this.cost, this.inCurr, outCurr);
|
||||
};
|
||||
this.convertCurrency = function convertCurrency(amount, inCurr, outCurr) {
|
||||
return amount * this.usdToForeignRates[outCurr] * 1 / this.usdToForeignRates[inCurr];
|
||||
return amount * this.usdToForeignRates[outCurr] / this.usdToForeignRates[inCurr];
|
||||
};
|
||||
this.pay = function pay() {
|
||||
window.alert("Thanks!");
|
||||
@@ -195,20 +195,20 @@ Let's refactor our example and move the currency conversion into a service in an
|
||||
<file name="finance2.js">
|
||||
angular.module('finance2', [])
|
||||
.factory('currencyConverter', function() {
|
||||
var currencies = ['USD', 'EUR', 'CNY'],
|
||||
usdToForeignRates = {
|
||||
var currencies = ['USD', 'EUR', 'CNY'];
|
||||
var usdToForeignRates = {
|
||||
USD: 1,
|
||||
EUR: 0.74,
|
||||
CNY: 6.09
|
||||
};
|
||||
var convert = function (amount, inCurr, outCurr) {
|
||||
return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
|
||||
}
|
||||
|
||||
return {
|
||||
currencies: currencies,
|
||||
convert: convert
|
||||
};
|
||||
|
||||
function convert(amount, inCurr, outCurr) {
|
||||
return amount * usdToForeignRates[outCurr] * 1 / usdToForeignRates[inCurr];
|
||||
}
|
||||
});
|
||||
</file>
|
||||
<file name="invoice2.js">
|
||||
@@ -325,21 +325,15 @@ The following example shows how this is done with Angular:
|
||||
var YAHOO_FINANCE_URL_PATTERN =
|
||||
'http://query.yahooapis.com/v1/public/yql?q=select * from '+
|
||||
'yahoo.finance.xchange where pair in ("PAIRS")&format=json&'+
|
||||
'env=store://datatables.org/alltableswithkeys&callback=JSON_CALLBACK',
|
||||
currencies = ['USD', 'EUR', 'CNY'],
|
||||
usdToForeignRates = {};
|
||||
refresh();
|
||||
return {
|
||||
currencies: currencies,
|
||||
convert: convert,
|
||||
refresh: refresh
|
||||
};
|
||||
'env=store://datatables.org/alltableswithkeys&callback=JSON_CALLBACK';
|
||||
var currencies = ['USD', 'EUR', 'CNY'];
|
||||
var usdToForeignRates = {};
|
||||
|
||||
function convert(amount, inCurr, outCurr) {
|
||||
return amount * usdToForeignRates[outCurr] * 1 / usdToForeignRates[inCurr];
|
||||
var convert = function (amount, inCurr, outCurr) {
|
||||
return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
var refresh = function() {
|
||||
var url = YAHOO_FINANCE_URL_PATTERN.
|
||||
replace('PAIRS', 'USD' + currencies.join('","USD'));
|
||||
return $http.jsonp(url).success(function(data) {
|
||||
@@ -351,6 +345,14 @@ The following example shows how this is done with Angular:
|
||||
usdToForeignRates = newUsdToForeignRates;
|
||||
});
|
||||
}
|
||||
|
||||
refresh();
|
||||
|
||||
return {
|
||||
currencies: currencies,
|
||||
convert: convert,
|
||||
refresh: refresh
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
<file name="index.html">
|
||||
|
||||
@@ -160,7 +160,7 @@ encoded.
|
||||
|
||||
`$location` service has two configuration modes which control the format of the URL in the browser
|
||||
address bar: **Hashbang mode** (the default) and the **HTML5 mode** which is based on using the
|
||||
HTML5 {@link http://www.w3.org/TR/html5/history.html History API}. Applications use the same API in
|
||||
HTML5 {@link http://www.w3.org/TR/html5/browsers.html#history History API}. Applications use the same API in
|
||||
both modes and the `$location` service will work with appropriate URL segments and browser APIs to
|
||||
facilitate the browser URL change and history management.
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ Everything you need to know about AngularJS
|
||||
|
||||
* {@link tutorial/index Official AngularJS Tutorial}
|
||||
* [10 Reasons Why You Should Use AngularJS](http://www.sitepoint.com/10-reasons-use-angularjs/)
|
||||
* [10 Reasons Why Developers Should Learn AngularJS](http://wintellect.com/blogs/jlikness/10-reasons-web-developers-should-learn-angularjs)
|
||||
* [Design Principles of AngularJS (video)](https://www.youtube.com/watch?v=HCR7i5F5L8c)
|
||||
* [Fundamentals in 60 Minutes (video)](http://www.youtube.com/watch?v=i9MHigUZKEM)
|
||||
* [For folks with jQuery background](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background)
|
||||
@@ -87,7 +88,7 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
* **FireBase:** [AngularFire](http://angularfire.com/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
|
||||
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/resources/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
|
||||
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
|
||||
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
|
||||
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
|
||||
* **Rails: **[Tutorial](http://coderberry.me/blog/2013/04/22/angularjs-on-rails-4-part-1/), [AngularJS with Rails4](https://shellycloud.com/blog/2013/10/how-to-integrate-angularjs-with-rails-4), [angularjs-rails](https://github.com/hiravgandhi/angularjs-rails)
|
||||
* **PHP: **[Building a RESTful web service](http://blog.brunoscopelliti.com/building-a-restful-web-service-with-angularjs-and-php-more-power-with-resource), [End to End with Laravel 4 (video)](http://www.youtube.com/watch?v=hqAyiqUs93c)
|
||||
|
||||
@@ -113,6 +114,7 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
|
||||
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
|
||||
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html)
|
||||
[WintellectNOW (4 lessons)](http://www.wintellectnow.com/Course/Detail/mastering-angularjs)
|
||||
* **Paid onsite:**
|
||||
[angularbootcamp.com](http://angularbootcamp.com/)
|
||||
|
||||
|
||||
@@ -333,7 +333,7 @@ information.
|
||||
|
||||
Dirty checking the scope for property changes is a common operation in Angular and for this reason
|
||||
the dirty checking function must be efficient. Care should be taken that the dirty checking
|
||||
function does not do any DOM access, as DOM access is orders of magnitude slower then property
|
||||
function does not do any DOM access, as DOM access is orders of magnitude slower than property
|
||||
access on JavaScript object.
|
||||
|
||||
## Integration with the browser event loop
|
||||
|
||||
@@ -100,7 +100,7 @@ Watch the July 17, 2012 talk
|
||||
|
||||
### How is Angular licensed?
|
||||
|
||||
The MIT License.
|
||||
The {@link https://github.com/angular/angular.js/blob/master/LICENSE MIT License}.
|
||||
|
||||
### Can I download and use the Angular logo artwork?
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ describe('PhoneCat controllers', function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
beforeEach(module('phonecatApp'));
|
||||
beforeEach(module('phonecatServices'));
|
||||
|
||||
|
||||
|
||||
@@ -57,7 +57,10 @@ writer.makeDir('build/docs/', true).then(function() {
|
||||
fileFutures.push(writer.output('partials/' + doc.section + '/' + id + '.html', doc.html()));
|
||||
// If it has a sample Protractor test, output that as well.
|
||||
if (doc.protractorTests.length) {
|
||||
fileFutures.push(writer.output('ptore2e/' + doc.section + '/' + id + '_test.js', ngdoc.writeProtractorTest(doc)));
|
||||
fileFutures.push(writer.output('ptore2e/' + doc.section + '/' + id + '.jquery_test.js',
|
||||
ngdoc.writeProtractorTest(doc, 'index-jq-nocache.html#!/')));
|
||||
fileFutures.push(writer.output('ptore2e/' + doc.section + '/' + id + '.jqlite_test.js',
|
||||
ngdoc.writeProtractorTest(doc, 'index-nocache.html#!/')));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+5
-16
@@ -1110,28 +1110,17 @@ function scenarios(docs){
|
||||
}
|
||||
}
|
||||
|
||||
function writeProtractorTest(doc){
|
||||
function writeProtractorTest(doc, pathPrefix){
|
||||
var lines = [];
|
||||
lines.push('describe("' + doc.section + '/' + doc.id + '", function() {');
|
||||
lines.push(' describe("angular+jqLite", function() {')
|
||||
lines.push(' beforeEach(function() {');
|
||||
lines.push(' browser.get("index-nocache.html#!/' + doc.section + '/' + doc.id + '");');
|
||||
lines.push(' });');
|
||||
lines.push(' beforeEach(function() {');
|
||||
lines.push(' browser.get("' + pathPrefix + doc.section + '/' + doc.id + '");');
|
||||
lines.push(' });');
|
||||
lines.push('');
|
||||
doc.protractorTests.forEach(function(test){
|
||||
lines.push(indentCode(trim(test), 4));
|
||||
lines.push(indentCode(trim(test), 0));
|
||||
lines.push('');
|
||||
});
|
||||
lines.push(' });');
|
||||
lines.push(' describe("angular+jQuery", function() {')
|
||||
lines.push(' beforeEach(function() {');
|
||||
lines.push(' browser.get("index-jq-nocache.html#!/' + doc.section + '/' + doc.id + '");');
|
||||
lines.push(' });');
|
||||
doc.protractorTests.forEach(function(test){
|
||||
lines.push(indentCode(trim(test), 4));
|
||||
lines.push('');
|
||||
});
|
||||
lines.push(' });');
|
||||
lines.push('});');
|
||||
lines.push('');
|
||||
return lines.join('\n');
|
||||
|
||||
Binary file not shown.
@@ -32,8 +32,6 @@ grunt test:unit --browsers $BROWSERS --reporters=dots,junit --no-colors --no-col
|
||||
|
||||
# END TO END TESTS #
|
||||
grunt test:ci-protractor
|
||||
grunt test:ci-protractor --browser safari
|
||||
grunt test:ci-protractor --browser firefox
|
||||
|
||||
# Promises/A+ TESTS #
|
||||
grunt test:promises-aplus --no-color
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"branchVersion": "1.2.*",
|
||||
"cdnVersion": "1.2.11",
|
||||
"cdnVersion": "1.2.12",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
@@ -23,7 +23,7 @@
|
||||
"load-grunt-tasks": "~0.3.0",
|
||||
"bower": "~1.2.2",
|
||||
"jasmine-node": "~1.11.0",
|
||||
"q": "~0.9.2",
|
||||
"q": "~1.0.0",
|
||||
"q-io": "~1.10.6",
|
||||
"qq": "~0.3.5",
|
||||
"shelljs": "~0.2.6",
|
||||
|
||||
+3
-2
@@ -2,12 +2,13 @@ exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
|
||||
specs: [
|
||||
'build/docs/ptore2e/**/*.js',
|
||||
'build/docs/ptore2e/**/*jqlite_test.js',
|
||||
'test/e2e/docsAppE2E.js'
|
||||
],
|
||||
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
'browserName': 'chrome',
|
||||
'name': 'Angular E2E: jqlite'
|
||||
},
|
||||
|
||||
baseUrl: 'http://localhost:8000/build/docs/',
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
|
||||
specs: [
|
||||
'build/docs/ptore2e/**/*jquery_test.js',
|
||||
'test/e2e/docsAppE2E.js'
|
||||
],
|
||||
|
||||
capabilities: {
|
||||
'browserName': 'chrome',
|
||||
'name': 'Angular E2E: jquery'
|
||||
},
|
||||
|
||||
baseUrl: 'http://localhost:8000/build/docs/',
|
||||
|
||||
framework: 'jasmine',
|
||||
|
||||
onPrepare: function() {
|
||||
// Disable animations so e2e tests run more quickly
|
||||
var disableNgAnimate = function() {
|
||||
angular.module('disableNgAnimate', []).run(function($animate) {
|
||||
$animate.enabled(false);
|
||||
});
|
||||
};
|
||||
|
||||
browser.addMockModule('disableNgAnimate', disableNgAnimate);
|
||||
},
|
||||
|
||||
jasmineNodeOpts: {
|
||||
defaultTimeoutInterval: 30000
|
||||
}
|
||||
};
|
||||
@@ -3,15 +3,18 @@
|
||||
set -e
|
||||
|
||||
export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||
export BROWSER=${JOB#*-}
|
||||
|
||||
if [ $JOB = "unit" ]; then
|
||||
grunt ci-checks
|
||||
grunt test:docgen
|
||||
grunt test:promises-aplus
|
||||
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_8,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
|
||||
elif [[ $JOB == e2e* ]]; then
|
||||
grunt test:protractor --sauceUser $SAUCE_USERNAME \
|
||||
elif [ $JOB = "e2e" ]; then
|
||||
export GRUNT_TARGET="test:protractor"
|
||||
if [ $JQVERSION = "jquery" ]; then
|
||||
GRUNT_TARGET="test:jq-protractor"
|
||||
fi
|
||||
grunt $GRUNT_TARGET --sauceUser $SAUCE_USERNAME \
|
||||
--sauceKey $SAUCE_ACCESS_KEY \
|
||||
--capabilities.tunnel-identifier=$TRAVIS_JOB_NUMBER \
|
||||
--capabilities.build=$TRAVIS_BUILD_NUMBER \
|
||||
|
||||
+3
-1
@@ -81,6 +81,7 @@
|
||||
-assertNotHasOwnProperty,
|
||||
-getter,
|
||||
-getBlockElements,
|
||||
-hasOwnProperty,
|
||||
|
||||
*/
|
||||
|
||||
@@ -96,7 +97,7 @@
|
||||
* @returns {string} Lowercased string.
|
||||
*/
|
||||
var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;};
|
||||
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
@@ -1131,6 +1132,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
<file name="index.html">
|
||||
<div ng-controller="ngAppDemoController">
|
||||
I can add: {{a}} + {{b}} = {{ a+b }}
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
|
||||
|
||||
+10
-2
@@ -40,7 +40,7 @@
|
||||
* - [`after()`](http://api.jquery.com/after/)
|
||||
* - [`append()`](http://api.jquery.com/append/)
|
||||
* - [`attr()`](http://api.jquery.com/attr/)
|
||||
* - [`bind()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
|
||||
* - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
|
||||
* - [`children()`](http://api.jquery.com/children/) - Does not support selectors
|
||||
* - [`clone()`](http://api.jquery.com/clone/)
|
||||
* - [`contents()`](http://api.jquery.com/contents/)
|
||||
@@ -67,7 +67,7 @@
|
||||
* - [`text()`](http://api.jquery.com/text/)
|
||||
* - [`toggleClass()`](http://api.jquery.com/toggleClass/)
|
||||
* - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
|
||||
* - [`unbind()`](http://api.jquery.com/off/) - Does not support namespaces
|
||||
* - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
|
||||
* - [`val()`](http://api.jquery.com/val/)
|
||||
* - [`wrap()`](http://api.jquery.com/wrap/)
|
||||
*
|
||||
@@ -107,6 +107,14 @@ var jqCache = JQLite.cache = {},
|
||||
? function(element, type, fn) {element.removeEventListener(type, fn, false); }
|
||||
: function(element, type, fn) {element.detachEvent('on' + type, fn); });
|
||||
|
||||
/*
|
||||
* !!! This is an undocumented "private" function !!!
|
||||
*/
|
||||
var jqData = JQLite._data = function(node) {
|
||||
//jQuery always returns an object on cache miss
|
||||
return this.cache[node[this.expando]] || {};
|
||||
};
|
||||
|
||||
function jqNextId() { return ++jqId; }
|
||||
|
||||
|
||||
|
||||
@@ -222,6 +222,29 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
done && $timeout(done, 0, false);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @ngdoc function
|
||||
* @name ng.$animate#setClass
|
||||
* @methodOf ng.$animate
|
||||
* @function
|
||||
* @description Adds and/or removes the given CSS classes to and from the element.
|
||||
* Once complete, the done() callback will be fired (if provided).
|
||||
* @param {jQuery/jqLite element} element the element which will it's CSS classes changed
|
||||
* removed from it
|
||||
* @param {string} add the CSS classes which will be added to the element
|
||||
* @param {string} remove the CSS class which will be removed from the element
|
||||
* @param {function=} done the callback function (if provided) that will be fired after the
|
||||
* CSS classes have been set on the element
|
||||
*/
|
||||
setClass : function(element, add, remove, done) {
|
||||
forEach(element, function (element) {
|
||||
jqLiteAddClass(element, add);
|
||||
jqLiteRemoveClass(element, remove);
|
||||
});
|
||||
done && $timeout(done, 0, false);
|
||||
},
|
||||
|
||||
enabled : noop
|
||||
};
|
||||
}];
|
||||
|
||||
+45
-10
@@ -502,7 +502,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
var hasDirectives = {},
|
||||
Suffix = 'Directive',
|
||||
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/;
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
||||
TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|tbody)(\s+[^>]*)?>/i;
|
||||
|
||||
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
|
||||
// The assumption is that future DOM event attribute names will begin with
|
||||
@@ -689,8 +690,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
* @param {string} oldClasses The former CSS className value
|
||||
*/
|
||||
$updateClass : function(newClasses, oldClasses) {
|
||||
this.$removeClass(tokenDifference(oldClasses, newClasses));
|
||||
this.$addClass(tokenDifference(newClasses, oldClasses));
|
||||
var toAdd = tokenDifference(newClasses, oldClasses);
|
||||
var toRemove = tokenDifference(oldClasses, newClasses);
|
||||
|
||||
if(toAdd.length === 0) {
|
||||
$animate.removeClass(this.$$element, toRemove);
|
||||
} else if(toRemove.length === 0) {
|
||||
$animate.addClass(this.$$element, toAdd);
|
||||
} else {
|
||||
$animate.setClass(this.$$element, toAdd, toRemove);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1142,7 +1151,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
templateDirective = previousCompileContext.templateDirective,
|
||||
nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
|
||||
hasTranscludeDirective = false,
|
||||
hasElementTranscludeDirective = false,
|
||||
hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
|
||||
$compileNode = templateAttrs.$$element = jqLite(compileNode),
|
||||
directive,
|
||||
directiveName,
|
||||
@@ -1243,9 +1252,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
if (directive.replace) {
|
||||
replaceDirective = directive;
|
||||
$template = jqLite('<div>' +
|
||||
trim(directiveValue) +
|
||||
'</div>').contents();
|
||||
$template = directiveTemplateContents(directiveValue);
|
||||
compileNode = $template[0];
|
||||
|
||||
if ($template.length != 1 || compileNode.nodeType !== 1) {
|
||||
@@ -1316,6 +1323,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
|
||||
nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn;
|
||||
previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
|
||||
|
||||
// might be normal or delayed nodeLinkFn depending on if templateUrl is present
|
||||
return nodeLinkFn;
|
||||
@@ -1643,6 +1651,28 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
|
||||
function directiveTemplateContents(template) {
|
||||
var type;
|
||||
template = trim(template);
|
||||
if ((type = TABLE_CONTENT_REGEXP.exec(template))) {
|
||||
type = type[1].toLowerCase();
|
||||
var table = jqLite('<table>' + template + '</table>'),
|
||||
tbody = table.children('tbody'),
|
||||
leaf = /(td|th)/.test(type) && table.find('tr');
|
||||
if (tbody.length && type !== 'tbody') {
|
||||
table = tbody;
|
||||
}
|
||||
if (leaf && leaf.length) {
|
||||
table = leaf;
|
||||
}
|
||||
return table.contents();
|
||||
}
|
||||
return jqLite('<div>' +
|
||||
template +
|
||||
'</div>').contents();
|
||||
}
|
||||
|
||||
|
||||
function compileTemplateUrl(directives, $compileNode, tAttrs,
|
||||
$rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
|
||||
var linkQueue = [],
|
||||
@@ -1667,7 +1697,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
content = denormalizeTemplate(content);
|
||||
|
||||
if (origAsyncDirective.replace) {
|
||||
$template = jqLite('<div>' + trim(content) + '</div>').contents();
|
||||
$template = directiveTemplateContents(content);
|
||||
compileNode = $template[0];
|
||||
|
||||
if ($template.length != 1 || compileNode.nodeType !== 1) {
|
||||
@@ -1712,8 +1742,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
|
||||
var oldClasses = beforeTemplateLinkNode.className;
|
||||
// it was cloned therefore we have to clone as well.
|
||||
linkNode = jqLiteClone(compileNode);
|
||||
|
||||
if (!(previousCompileContext.hasElementTranscludeDirective &&
|
||||
origAsyncDirective.replace)) {
|
||||
// it was cloned therefore we have to clone as well.
|
||||
linkNode = jqLiteClone(compileNode);
|
||||
}
|
||||
|
||||
replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
|
||||
|
||||
// Copy in CSS classes from original node
|
||||
|
||||
@@ -424,7 +424,8 @@ var inputType = {
|
||||
'hidden': noop,
|
||||
'button': noop,
|
||||
'submit': noop,
|
||||
'reset': noop
|
||||
'reset': noop,
|
||||
'file': noop
|
||||
};
|
||||
|
||||
// A helper function to call $setValidity and return the value / undefined,
|
||||
@@ -447,6 +448,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
|
||||
element.on('compositionend', function() {
|
||||
composing = false;
|
||||
listener();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1022,6 +1024,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
* You can override this for input directives whose concept of being empty is different to the
|
||||
* default. The `checkboxInputType` directive does this because in its case a value of `false`
|
||||
* implies empty.
|
||||
*
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is empty.
|
||||
*/
|
||||
this.$isEmpty = function(value) {
|
||||
return isUndefined(value) || value === '' || value === null || value !== value;
|
||||
|
||||
@@ -298,8 +298,8 @@ forEach(
|
||||
* Enables binding angular expressions to onsubmit events.
|
||||
*
|
||||
* Additionally it prevents the default action (which for form means sending the request to the
|
||||
* server and reloading the current page) **but only if the form does not contain an `action`
|
||||
* attribute**.
|
||||
* server and reloading the current page), but only if the form does not contain `action`,
|
||||
* `data-action`, or `x-action` attributes.
|
||||
*
|
||||
* @element form
|
||||
* @priority 0
|
||||
|
||||
@@ -56,23 +56,16 @@
|
||||
*
|
||||
*/
|
||||
var ngTranscludeDirective = ngDirective({
|
||||
controller: ['$element', '$transclude', function($element, $transclude) {
|
||||
link: function($scope, $element, $attrs, controller, $transclude) {
|
||||
if (!$transclude) {
|
||||
throw minErr('ngTransclude')('orphan',
|
||||
'Illegal use of ngTransclude directive in the template! ' +
|
||||
'No parent directive that requires a transclusion found. ' +
|
||||
'Element: {0}',
|
||||
startingTag($element));
|
||||
'Illegal use of ngTransclude directive in the template! ' +
|
||||
'No parent directive that requires a transclusion found. ' +
|
||||
'Element: {0}',
|
||||
startingTag($element));
|
||||
}
|
||||
|
||||
// remember the transclusion fn but call it during linking so that we don't process transclusion before directives on
|
||||
// the parent element even when the transclusion replaces the current element. (we can't use priority here because
|
||||
// that applies only to compile fns and not controllers
|
||||
this.$transclude = $transclude;
|
||||
}],
|
||||
|
||||
link: function($scope, $element, $attrs, controller) {
|
||||
controller.$transclude(function(clone) {
|
||||
|
||||
$transclude(function(clone) {
|
||||
$element.empty();
|
||||
$element.append(clone);
|
||||
});
|
||||
|
||||
@@ -136,6 +136,15 @@ function filterFilter() {
|
||||
};
|
||||
} else {
|
||||
comparator = function(obj, text) {
|
||||
if (obj && text && typeof obj === 'object' && typeof text === 'object') {
|
||||
for (var objKey in obj) {
|
||||
if (objKey.charAt(0) !== '$' && hasOwnProperty.call(obj, objKey) &&
|
||||
comparator(obj[objKey], text[objKey])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
text = (''+text).toLowerCase();
|
||||
return (''+obj).toLowerCase().indexOf(text) > -1;
|
||||
};
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
return;
|
||||
}
|
||||
element(by.model('amount')).clear();
|
||||
element(by.model('amount')).sendKeys('-1234'); expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
|
||||
element(by.model('amount')).sendKeys('-1234');
|
||||
expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
|
||||
expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('(USD$1,234.00)');
|
||||
});
|
||||
</doc:protractor>
|
||||
|
||||
@@ -227,7 +227,7 @@ function $InterpolateProvider() {
|
||||
* @description
|
||||
* Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
|
||||
*
|
||||
* Use {@link ng.$interpolateProvider#endSymbol $interpolateProvider#endSymbol} to change
|
||||
* Use {@link ng.$interpolateProvider#methods_endSymbol $interpolateProvider#endSymbol} to change
|
||||
* the symbol.
|
||||
*
|
||||
* @returns {string} start symbol.
|
||||
|
||||
+1
-1
@@ -574,7 +574,7 @@ function $LocationProvider(){
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
* Broadcasted before a URL will change. This change can be prevented by calling
|
||||
* `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
|
||||
* `preventDefault` method of the event. See {@link ng.$rootScope.Scope#methods_$on} for more
|
||||
* details about event object. Upon successful change
|
||||
* {@link ng.$location#events_$locationChangeSuccess $locationChangeSuccess} is fired.
|
||||
*
|
||||
|
||||
+329
-226
@@ -248,7 +248,9 @@ 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) {
|
||||
.factory('$$animateReflow', ['$window', '$timeout', '$document',
|
||||
function($window, $timeout, $document) {
|
||||
var bod = $document[0].body;
|
||||
var requestAnimationFrame = $window.requestAnimationFrame ||
|
||||
$window.webkitRequestAnimationFrame ||
|
||||
function(fn) {
|
||||
@@ -261,13 +263,30 @@ angular.module('ngAnimate', ['ng'])
|
||||
return $timeout.cancel(timer);
|
||||
};
|
||||
return function(fn) {
|
||||
var id = requestAnimationFrame(fn);
|
||||
var id = requestAnimationFrame(function() {
|
||||
var a = bod.offsetWidth + 1;
|
||||
fn();
|
||||
});
|
||||
return function() {
|
||||
cancelAnimationFrame(id);
|
||||
};
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('$$asyncQueueBuffer', ['$timeout', function($timeout) {
|
||||
var timer, queue = [];
|
||||
return function(fn) {
|
||||
$timeout.cancel(timer);
|
||||
queue.push(fn);
|
||||
timer = $timeout(function() {
|
||||
for(var i = 0; i < queue.length; i++) {
|
||||
queue[i]();
|
||||
}
|
||||
queue = [];
|
||||
}, 0, false);
|
||||
};
|
||||
}])
|
||||
|
||||
.config(['$provide', '$animateProvider', function($provide, $animateProvider) {
|
||||
var noop = angular.noop;
|
||||
var forEach = angular.forEach;
|
||||
@@ -287,13 +306,18 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
}
|
||||
|
||||
function stripCommentsFromElement(element) {
|
||||
return angular.element(extractElementNode(element));
|
||||
}
|
||||
|
||||
function isMatchingElement(elm1, elm2) {
|
||||
return extractElementNode(elm1) == extractElementNode(elm2);
|
||||
}
|
||||
|
||||
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document',
|
||||
function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document) {
|
||||
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$$asyncQueueBuffer', '$rootScope', '$document',
|
||||
function($delegate, $injector, $sniffer, $rootElement, $$asyncQueueBuffer, $rootScope, $document) {
|
||||
|
||||
var globalAnimationCounter = 0;
|
||||
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
|
||||
|
||||
// disable animations during bootstrap, but once we bootstrapped, wait again
|
||||
@@ -315,10 +339,6 @@ angular.module('ngAnimate', ['ng'])
|
||||
return classNameFilter.test(className);
|
||||
};
|
||||
|
||||
function async(fn) {
|
||||
return $timeout(fn, 0, false);
|
||||
}
|
||||
|
||||
function lookup(name) {
|
||||
if (name) {
|
||||
var matches = [],
|
||||
@@ -400,6 +420,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
this.enabled(false, element);
|
||||
$delegate.enter(element, parentElement, afterElement);
|
||||
$rootScope.$$postDigest(function() {
|
||||
element = stripCommentsFromElement(element);
|
||||
performAnimation('enter', 'ng-enter', element, parentElement, afterElement, noop, doneCallback);
|
||||
});
|
||||
},
|
||||
@@ -436,6 +457,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
cancelChildAnimations(element);
|
||||
this.enabled(false, element);
|
||||
$rootScope.$$postDigest(function() {
|
||||
element = stripCommentsFromElement(element);
|
||||
performAnimation('leave', 'ng-leave', element, null, null, function() {
|
||||
$delegate.leave(element);
|
||||
}, doneCallback);
|
||||
@@ -478,6 +500,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
this.enabled(false, element);
|
||||
$delegate.move(element, parentElement, afterElement);
|
||||
$rootScope.$$postDigest(function() {
|
||||
element = stripCommentsFromElement(element);
|
||||
performAnimation('move', 'ng-move', element, parentElement, afterElement, noop, doneCallback);
|
||||
});
|
||||
},
|
||||
@@ -513,6 +536,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
* @param {function()=} doneCallback the callback function that will be called once the animation is complete
|
||||
*/
|
||||
addClass : function(element, className, doneCallback) {
|
||||
element = stripCommentsFromElement(element);
|
||||
performAnimation('addClass', className, element, null, null, function() {
|
||||
$delegate.addClass(element, className);
|
||||
}, doneCallback);
|
||||
@@ -549,11 +573,34 @@ angular.module('ngAnimate', ['ng'])
|
||||
* @param {function()=} doneCallback the callback function that will be called once the animation is complete
|
||||
*/
|
||||
removeClass : function(element, className, doneCallback) {
|
||||
element = stripCommentsFromElement(element);
|
||||
performAnimation('removeClass', className, element, null, null, function() {
|
||||
$delegate.removeClass(element, className);
|
||||
}, doneCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @ngdoc function
|
||||
* @name ng.$animate#setClass
|
||||
* @methodOf ng.$animate
|
||||
* @function
|
||||
* @description Adds and/or removes the given CSS classes to and from the element.
|
||||
* Once complete, the done() callback will be fired (if provided).
|
||||
* @param {jQuery/jqLite element} element the element which will it's CSS classes changed
|
||||
* removed from it
|
||||
* @param {string} add the CSS classes which will be added to the element
|
||||
* @param {string} remove the CSS class which will be removed from the element
|
||||
* @param {function=} done the callback function (if provided) that will be fired after the
|
||||
* CSS classes have been set on the element
|
||||
*/
|
||||
setClass : function(element, add, remove, doneCallback) {
|
||||
element = stripCommentsFromElement(element);
|
||||
performAnimation('setClass', [add, remove], element, null, null, function() {
|
||||
$delegate.setClass(element, add, remove);
|
||||
}, doneCallback);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ngAnimate.$animate#enabled
|
||||
@@ -600,7 +647,15 @@ angular.module('ngAnimate', ['ng'])
|
||||
and the onComplete callback will be fired once the animation is fully complete.
|
||||
*/
|
||||
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
|
||||
var currentClassName, classes, node = extractElementNode(element);
|
||||
|
||||
var classNameAdd, classNameRemove, setClassOperation = animationEvent == 'setClass';
|
||||
if(setClassOperation) {
|
||||
classNameAdd = className[0];
|
||||
classNameRemove = className[1];
|
||||
className = classNameAdd + ' ' + classNameRemove;
|
||||
}
|
||||
|
||||
var currentClassName, classes, node = element[0];
|
||||
if(node) {
|
||||
currentClassName = node.className;
|
||||
classes = currentClassName + ' ' + className;
|
||||
@@ -612,18 +667,27 @@ angular.module('ngAnimate', ['ng'])
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
closeAnimation();
|
||||
fireDoneCallbackAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
var elementEvents = angular.element._data(node);
|
||||
elementEvents = elementEvents && elementEvents.events;
|
||||
|
||||
var animationLookup = (' ' + classes).replace(/\s+/g,'.');
|
||||
if (!parentElement) {
|
||||
parentElement = afterElement ? afterElement.parent() : element.parent();
|
||||
}
|
||||
|
||||
var matches = lookup(animationLookup);
|
||||
var isClassBased = animationEvent == 'addClass' || animationEvent == 'removeClass';
|
||||
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
|
||||
var matches = lookup(animationLookup);
|
||||
var isClassBased = animationEvent == 'addClass' ||
|
||||
animationEvent == 'removeClass' ||
|
||||
setClassOperation;
|
||||
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
|
||||
|
||||
var runningAnimations = ngAnimateState.active || {};
|
||||
var totalActiveAnimations = ngAnimateState.totalActive || 0;
|
||||
var lastAnimation = ngAnimateState.last;
|
||||
|
||||
//skip the animation if animations are disabled, a parent is already being animated,
|
||||
//the element is not currently attached to the document body or then completely close
|
||||
@@ -642,7 +706,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
//only add animations if the currently running animation is not structural
|
||||
//or if there is no animation running at all
|
||||
var allowAnimations = isClassBased ?
|
||||
!ngAnimateState.disabled && (!ngAnimateState.running || !ngAnimateState.structural) :
|
||||
!ngAnimateState.disabled && (!lastAnimation || lastAnimation.classBased) :
|
||||
true;
|
||||
|
||||
if(allowAnimations) {
|
||||
@@ -677,55 +741,48 @@ angular.module('ngAnimate', ['ng'])
|
||||
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 = 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
|
||||
$timeout.cancel(ngAnimateState.closeAnimationTimeout);
|
||||
cleanup(element);
|
||||
cancelAnimations(ngAnimateState.animations);
|
||||
var skipAnimation = false;
|
||||
if(totalActiveAnimations > 0) {
|
||||
var animationsToCancel = [];
|
||||
if(!isClassBased) {
|
||||
if(animationEvent == 'leave' && runningAnimations['ng-leave']) {
|
||||
skipAnimation = true;
|
||||
} else {
|
||||
//cancel all animations when a structural animation takes place
|
||||
for(var klass in runningAnimations) {
|
||||
animationsToCancel.push(runningAnimations[klass]);
|
||||
cleanup(element, klass);
|
||||
}
|
||||
runningAnimations = {};
|
||||
totalActiveAnimations = 0;
|
||||
}
|
||||
} else if(lastAnimation.event == 'setClass') {
|
||||
animationsToCancel.push(lastAnimation);
|
||||
cleanup(element, className);
|
||||
}
|
||||
else if(runningAnimations[className]) {
|
||||
var current = runningAnimations[className];
|
||||
if(current.event == animationEvent) {
|
||||
skipAnimation = true;
|
||||
} else {
|
||||
animationsToCancel.push(current);
|
||||
cleanup(element, className);
|
||||
}
|
||||
}
|
||||
|
||||
//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 || isRevertingClassAnimation) {
|
||||
(ngAnimateState.done || noop)(true);
|
||||
} 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
|
||||
//be performed only after the reflow is complete then our element's className value
|
||||
//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(ONE_SPACE + ngAnimateState.className + ONE_SPACE, ONE_SPACE) :
|
||||
futureClassName + ngAnimateState.className + ONE_SPACE;
|
||||
if(animationsToCancel.length > 0) {
|
||||
angular.forEach(animationsToCancel, function(operation) {
|
||||
(operation.done || noop)(true);
|
||||
cancelAnimations(operation.animations);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//There is no point in perform a class-based animation if the element already contains
|
||||
//(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 = ONE_SPACE + className + ONE_SPACE;
|
||||
if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) ||
|
||||
(animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {
|
||||
fireDOMOperation();
|
||||
if(isClassBased && !setClassOperation && !skipAnimation) {
|
||||
skipAnimation = (animationEvent == 'addClass') == element.hasClass(className); //opposite of XOR
|
||||
}
|
||||
|
||||
if(skipAnimation) {
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
fireDoneCallbackAsync();
|
||||
@@ -736,13 +793,22 @@ angular.module('ngAnimate', ['ng'])
|
||||
//parent animations to find and cancel child animations when needed
|
||||
element.addClass(NG_ANIMATE_CLASS_NAME);
|
||||
|
||||
element.data(NG_ANIMATE_STATE, {
|
||||
running:true,
|
||||
event:animationEvent,
|
||||
className:className,
|
||||
structural:!isClassBased,
|
||||
animations:animations,
|
||||
var localAnimationCount = globalAnimationCounter++;
|
||||
lastAnimation = {
|
||||
classBased : isClassBased,
|
||||
event : animationEvent,
|
||||
animations : animations,
|
||||
done:onBeforeAnimationsComplete
|
||||
};
|
||||
|
||||
totalActiveAnimations++;
|
||||
runningAnimations[className] = lastAnimation;
|
||||
|
||||
element.data(NG_ANIMATE_STATE, {
|
||||
last : lastAnimation,
|
||||
active : runningAnimations,
|
||||
index : localAnimationCount,
|
||||
totalActive : totalActiveAnimations
|
||||
});
|
||||
|
||||
//first we run the before animations and when all of those are complete
|
||||
@@ -750,6 +816,11 @@ angular.module('ngAnimate', ['ng'])
|
||||
invokeRegisteredAnimationFns(animations, 'before', onBeforeAnimationsComplete);
|
||||
|
||||
function onBeforeAnimationsComplete(cancelled) {
|
||||
var data = element.data(NG_ANIMATE_STATE);
|
||||
cancelled = cancelled ||
|
||||
!data || !data.active[className] ||
|
||||
(isClassBased && data.active[className].event != animationEvent);
|
||||
|
||||
fireDOMOperation();
|
||||
if(cancelled === true) {
|
||||
closeAnimation();
|
||||
@@ -759,11 +830,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
//set the done function to the final done function
|
||||
//so that the DOM event won't be executed twice by accident
|
||||
//if the after animation is cancelled as well
|
||||
var data = element.data(NG_ANIMATE_STATE);
|
||||
if(data) {
|
||||
data.done = closeAnimation;
|
||||
element.data(NG_ANIMATE_STATE, data);
|
||||
}
|
||||
var currentAnimation = data.active[className];
|
||||
currentAnimation.done = closeAnimation;
|
||||
invokeRegisteredAnimationFns(animations, 'after', closeAnimation);
|
||||
}
|
||||
|
||||
@@ -786,9 +854,13 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
|
||||
if(animation[phase]) {
|
||||
animation[endFnName] = isClassBased ?
|
||||
animation[phase](element, className, animationPhaseCompleted) :
|
||||
animation[phase](element, animationPhaseCompleted);
|
||||
if(setClassOperation) {
|
||||
animation[endFnName] = animation[phase](element, classNameAdd, classNameRemove, animationPhaseCompleted);
|
||||
} else {
|
||||
animation[endFnName] = isClassBased ?
|
||||
animation[phase](element, className, animationPhaseCompleted) :
|
||||
animation[phase](element, animationPhaseCompleted);
|
||||
}
|
||||
} else {
|
||||
animationPhaseCompleted();
|
||||
}
|
||||
@@ -809,29 +881,32 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
|
||||
function fireDOMCallback(animationPhase) {
|
||||
element.triggerHandler('$animate:' + animationPhase, {
|
||||
event : animationEvent,
|
||||
className : className
|
||||
});
|
||||
var eventName = '$animate:' + animationPhase;
|
||||
if(elementEvents && elementEvents[eventName] && elementEvents[eventName].length > 0) {
|
||||
$$asyncQueueBuffer(function() {
|
||||
element.triggerHandler(eventName, {
|
||||
event : animationEvent,
|
||||
className : className
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function fireBeforeCallbackAsync() {
|
||||
async(function() {
|
||||
fireDOMCallback('before');
|
||||
});
|
||||
fireDOMCallback('before');
|
||||
}
|
||||
|
||||
function fireAfterCallbackAsync() {
|
||||
async(function() {
|
||||
fireDOMCallback('after');
|
||||
});
|
||||
fireDOMCallback('after');
|
||||
}
|
||||
|
||||
function fireDoneCallbackAsync() {
|
||||
async(function() {
|
||||
fireDOMCallback('close');
|
||||
doneCallback && doneCallback();
|
||||
});
|
||||
fireDOMCallback('close');
|
||||
if(doneCallback) {
|
||||
$$asyncQueueBuffer(function() {
|
||||
doneCallback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//it is less complicated to use a flag than managing and cancelling
|
||||
@@ -853,10 +928,13 @@ angular.module('ngAnimate', ['ng'])
|
||||
failing would be when a parent HTML tag has a ng-class attribute
|
||||
causing ALL directives below to skip animations during the digest */
|
||||
if(isClassBased) {
|
||||
cleanup(element);
|
||||
cleanup(element, className);
|
||||
} else {
|
||||
data.closeAnimationTimeout = async(function() {
|
||||
cleanup(element);
|
||||
$$asyncQueueBuffer(function() {
|
||||
var data = element.data(NG_ANIMATE_STATE) || {};
|
||||
if(localAnimationCount == data.index) {
|
||||
cleanup(element, className, animationEvent);
|
||||
}
|
||||
});
|
||||
element.data(NG_ANIMATE_STATE, data);
|
||||
}
|
||||
@@ -871,9 +949,11 @@ angular.module('ngAnimate', ['ng'])
|
||||
forEach(node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME), function(element) {
|
||||
element = angular.element(element);
|
||||
var data = element.data(NG_ANIMATE_STATE);
|
||||
if(data) {
|
||||
cancelAnimations(data.animations);
|
||||
cleanup(element);
|
||||
if(data && data.active) {
|
||||
angular.forEach(data.active, function(operation) {
|
||||
(operation.done || noop)(true);
|
||||
cancelAnimations(operation.animations);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -890,15 +970,27 @@ angular.module('ngAnimate', ['ng'])
|
||||
});
|
||||
}
|
||||
|
||||
function cleanup(element) {
|
||||
function cleanup(element, className) {
|
||||
if(isMatchingElement(element, $rootElement)) {
|
||||
if(!rootAnimateState.disabled) {
|
||||
rootAnimateState.running = false;
|
||||
rootAnimateState.structural = false;
|
||||
}
|
||||
} else {
|
||||
element.removeClass(NG_ANIMATE_CLASS_NAME);
|
||||
element.removeData(NG_ANIMATE_STATE);
|
||||
} else if(className) {
|
||||
var data = element.data(NG_ANIMATE_STATE) || {};
|
||||
|
||||
var removeAnimations = className === true;
|
||||
if(!removeAnimations) {
|
||||
if(data.active && data.active[className]) {
|
||||
data.totalActive--;
|
||||
delete data.active[className];
|
||||
}
|
||||
}
|
||||
|
||||
if(removeAnimations || !data.totalActive) {
|
||||
element.removeClass(NG_ANIMATE_CLASS_NAME);
|
||||
element.removeData(NG_ANIMATE_STATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -917,7 +1009,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
var isRoot = isMatchingElement(parentElement, $rootElement);
|
||||
var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE);
|
||||
var result = state && (!!state.disabled || !!state.running);
|
||||
var result = state && (!!state.disabled || state.running || state.totalActive > 0);
|
||||
if(isRoot || result) {
|
||||
return result;
|
||||
}
|
||||
@@ -967,74 +1059,57 @@ angular.module('ngAnimate', ['ng'])
|
||||
var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
|
||||
var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey';
|
||||
var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data';
|
||||
var NG_ANIMATE_BLOCK_CLASS_NAME = 'ng-animate-block-transitions';
|
||||
var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
|
||||
var CLOSING_TIME_BUFFER = 1.5;
|
||||
var ONE_SECOND = 1000;
|
||||
|
||||
var animationCounter = 0;
|
||||
var lookupCache = {};
|
||||
var parentCounter = 0;
|
||||
var animationReflowQueue = [];
|
||||
var animationElementQueue = [];
|
||||
var cancelAnimationReflow;
|
||||
var closingAnimationTime = 0;
|
||||
var timeOut = false;
|
||||
function afterReflow(element, callback) {
|
||||
if(cancelAnimationReflow) {
|
||||
cancelAnimationReflow();
|
||||
}
|
||||
|
||||
animationReflowQueue.push(callback);
|
||||
|
||||
var node = extractElementNode(element);
|
||||
element = angular.element(node);
|
||||
animationElementQueue.push(element);
|
||||
|
||||
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
|
||||
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;
|
||||
|
||||
cancelAnimationReflow = $$animateReflow(function() {
|
||||
forEach(animationReflowQueue, function(fn) {
|
||||
fn();
|
||||
});
|
||||
|
||||
//copy the list of elements so that successive
|
||||
//animations won't conflict if they're added before
|
||||
//the closing animation timeout has run
|
||||
var elementQueueSnapshot = [];
|
||||
var animationCounterSnapshot = animationCounter;
|
||||
forEach(animationElementQueue, function(elm) {
|
||||
elementQueueSnapshot.push(elm);
|
||||
});
|
||||
|
||||
$timeout(function() {
|
||||
closeAllAnimations(elementQueueSnapshot, animationCounterSnapshot);
|
||||
elementQueueSnapshot = null;
|
||||
}, closingAnimationTime, false);
|
||||
|
||||
animationReflowQueue = [];
|
||||
animationElementQueue = [];
|
||||
cancelAnimationReflow = null;
|
||||
lookupCache = {};
|
||||
closingAnimationTime = 0;
|
||||
animationCounter++;
|
||||
});
|
||||
}
|
||||
|
||||
function closeAllAnimations(elements, count) {
|
||||
var closingTimer = null;
|
||||
var closingTimestamp = 0;
|
||||
var animationElementQueue = [];
|
||||
function animationCloseHandler(element, totalTime) {
|
||||
var futureTimestamp = Date.now() + (totalTime * 1000);
|
||||
if(futureTimestamp <= closingTimestamp) {
|
||||
return;
|
||||
}
|
||||
|
||||
$timeout.cancel(closingTimer);
|
||||
|
||||
var node = extractElementNode(element);
|
||||
element = angular.element(node);
|
||||
animationElementQueue.push(element);
|
||||
|
||||
closingTimestamp = futureTimestamp;
|
||||
closingTimer = $timeout(function() {
|
||||
closeAllAnimations(animationElementQueue);
|
||||
animationElementQueue = [];
|
||||
}, totalTime, false);
|
||||
}
|
||||
|
||||
function closeAllAnimations(elements) {
|
||||
forEach(elements, function(element) {
|
||||
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
if(elementData && elementData.animationCount == count) {
|
||||
if(elementData) {
|
||||
(elementData.closeAnimationFn || noop)();
|
||||
}
|
||||
});
|
||||
@@ -1119,12 +1194,12 @@ angular.module('ngAnimate', ['ng'])
|
||||
return parentID + '-' + extractElementNode(element).className;
|
||||
}
|
||||
|
||||
function animateSetup(element, className, calculationDecorator) {
|
||||
function animateSetup(animationEvent, element, className, calculationDecorator) {
|
||||
var cacheKey = getCacheKey(element);
|
||||
var eventCacheKey = cacheKey + ' ' + className;
|
||||
var stagger = {};
|
||||
var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
|
||||
|
||||
var stagger = {};
|
||||
if(itemIndex > 0) {
|
||||
var staggerClassName = className + '-stagger';
|
||||
var staggerCacheKey = cacheKey + ' ' + staggerClassName;
|
||||
@@ -1144,60 +1219,63 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
element.addClass(className);
|
||||
|
||||
var formerData = element.data(NG_ANIMATE_CSS_DATA_KEY) || {};
|
||||
|
||||
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
|
||||
in the page. There is also no point in performing an animation
|
||||
that only has a delay and no duration */
|
||||
var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay);
|
||||
var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
|
||||
if(maxDuration === 0) {
|
||||
var transitionDuration = timings.transitionDuration;
|
||||
var animationDuration = timings.animationDuration;
|
||||
if(transitionDuration === 0 && animationDuration === 0) {
|
||||
element.removeClass(className);
|
||||
return false;
|
||||
}
|
||||
|
||||
element.data(NG_ANIMATE_CSS_DATA_KEY, {
|
||||
running : formerData.running || 0,
|
||||
itemIndex : itemIndex,
|
||||
stagger : stagger,
|
||||
timings : timings,
|
||||
closeAnimationFn : angular.noop
|
||||
});
|
||||
|
||||
//temporarily disable the transition so that the enter styles
|
||||
//don't animate twice (this is here to avoid a bug in Chrome/FF).
|
||||
var activeClassName = '';
|
||||
timings.transitionDuration > 0 ?
|
||||
blockTransitions(element) :
|
||||
var isCurrentlyAnimating = formerData.running > 0 || animationEvent == 'setClass';
|
||||
if(transitionDuration > 0) {
|
||||
blockTransitions(element, className, isCurrentlyAnimating);
|
||||
}
|
||||
if(animationDuration > 0) {
|
||||
blockKeyframeAnimations(element);
|
||||
|
||||
forEach(className.split(' '), function(klass, i) {
|
||||
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
|
||||
});
|
||||
|
||||
element.data(NG_ANIMATE_CSS_DATA_KEY, {
|
||||
className : className,
|
||||
activeClassName : activeClassName,
|
||||
maxDuration : maxDuration,
|
||||
maxDelay : maxDelay,
|
||||
classes : className + ' ' + activeClassName,
|
||||
timings : timings,
|
||||
stagger : stagger,
|
||||
itemIndex : itemIndex
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function blockTransitions(element) {
|
||||
extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
|
||||
function isStructuralAnimation(className) {
|
||||
return className == 'ng-enter' || className == 'ng-move' || className == 'ng-leave';
|
||||
}
|
||||
|
||||
function blockTransitions(element, className, isAnimating) {
|
||||
if(isStructuralAnimation(className) || !isAnimating) {
|
||||
extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
|
||||
} else {
|
||||
element.addClass(NG_ANIMATE_BLOCK_CLASS_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
function blockKeyframeAnimations(element) {
|
||||
extractElementNode(element).style[ANIMATION_PROP] = 'none 0s';
|
||||
}
|
||||
|
||||
function unblockTransitions(element) {
|
||||
function unblockTransitions(element, className) {
|
||||
var prop = TRANSITION_PROP + PROPERTY_KEY;
|
||||
var node = extractElementNode(element);
|
||||
if(node.style[prop] && node.style[prop].length > 0) {
|
||||
node.style[prop] = '';
|
||||
}
|
||||
element.removeClass(NG_ANIMATE_BLOCK_CLASS_NAME);
|
||||
}
|
||||
|
||||
function unblockKeyframeAnimations(element) {
|
||||
@@ -1208,22 +1286,28 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
}
|
||||
|
||||
function animateRun(element, className, activeAnimationComplete) {
|
||||
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
function animateRun(animationEvent, element, className, activeAnimationComplete) {
|
||||
var node = extractElementNode(element);
|
||||
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
if(node.className.indexOf(className) == -1 || !elementData) {
|
||||
activeAnimationComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
var timings = elementData.timings;
|
||||
var activeClassName = '';
|
||||
forEach(className.split(' '), function(klass, i) {
|
||||
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
|
||||
});
|
||||
|
||||
var stagger = elementData.stagger;
|
||||
var maxDuration = elementData.maxDuration;
|
||||
var activeClassName = elementData.activeClassName;
|
||||
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * ONE_SECOND;
|
||||
var timings = elementData.timings;
|
||||
var itemIndex = elementData.itemIndex;
|
||||
var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
|
||||
var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay);
|
||||
var maxDelayTime = maxDelay * ONE_SECOND;
|
||||
|
||||
var startTime = Date.now();
|
||||
var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
|
||||
var itemIndex = elementData.itemIndex;
|
||||
|
||||
var style = '', appliedStyles = [];
|
||||
if(timings.transitionDuration > 0) {
|
||||
@@ -1265,6 +1349,13 @@ angular.module('ngAnimate', ['ng'])
|
||||
onEnd();
|
||||
activeAnimationComplete();
|
||||
};
|
||||
|
||||
var staggerTime = itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0);
|
||||
var animationTime = (maxDelay + maxDuration) * CLOSING_TIME_BUFFER;
|
||||
var totalTime = (staggerTime + animationTime) * ONE_SECOND;
|
||||
|
||||
elementData.running++;
|
||||
animationCloseHandler(element, totalTime);
|
||||
return onEnd;
|
||||
|
||||
// This will automatically be called by $animate so
|
||||
@@ -1284,7 +1375,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
event.stopPropagation();
|
||||
var ev = event.originalEvent || event;
|
||||
var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
|
||||
|
||||
|
||||
/* Firefox (or possibly just Gecko) likes to not round values up
|
||||
* when a ms measurement is used for the animation */
|
||||
var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
|
||||
@@ -1311,28 +1402,28 @@ angular.module('ngAnimate', ['ng'])
|
||||
return style;
|
||||
}
|
||||
|
||||
function animateBefore(element, className, calculationDecorator) {
|
||||
if(animateSetup(element, className, calculationDecorator)) {
|
||||
function animateBefore(animationEvent, element, className, calculationDecorator) {
|
||||
if(animateSetup(animationEvent, element, className, calculationDecorator)) {
|
||||
return function(cancelled) {
|
||||
cancelled && animateClose(element, className);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function animateAfter(element, className, afterAnimationComplete) {
|
||||
function animateAfter(animationEvent, element, className, afterAnimationComplete) {
|
||||
if(element.data(NG_ANIMATE_CSS_DATA_KEY)) {
|
||||
return animateRun(element, className, afterAnimationComplete);
|
||||
return animateRun(animationEvent, element, className, afterAnimationComplete);
|
||||
} else {
|
||||
animateClose(element, className);
|
||||
afterAnimationComplete();
|
||||
}
|
||||
}
|
||||
|
||||
function animate(element, className, animationComplete) {
|
||||
function animate(animationEvent, element, className, animationComplete) {
|
||||
//If the animateSetup function doesn't bother returning a
|
||||
//cancellation function then it means that there is no animation
|
||||
//to perform at all
|
||||
var preReflowCancellation = animateBefore(element, className);
|
||||
var preReflowCancellation = animateBefore(animationEvent, element, className);
|
||||
if(!preReflowCancellation) {
|
||||
animationComplete();
|
||||
return;
|
||||
@@ -1345,12 +1436,12 @@ angular.module('ngAnimate', ['ng'])
|
||||
//happen in the first place
|
||||
var cancel = preReflowCancellation;
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element);
|
||||
unblockTransitions(element, className);
|
||||
unblockKeyframeAnimations(element);
|
||||
//once the reflow is complete then we point cancel to
|
||||
//the new cancellation function which will remove all of the
|
||||
//animation properties from the active animation
|
||||
cancel = animateAfter(element, className, animationComplete);
|
||||
cancel = animateAfter(animationEvent, element, className, animationComplete);
|
||||
});
|
||||
|
||||
return function(cancelled) {
|
||||
@@ -1360,54 +1451,59 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
function animateClose(element, className) {
|
||||
element.removeClass(className);
|
||||
element.removeData(NG_ANIMATE_CSS_DATA_KEY);
|
||||
var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
if(data) {
|
||||
if(data.running) {
|
||||
data.running--;
|
||||
}
|
||||
if(!data.running || data.running === 0) {
|
||||
element.removeData(NG_ANIMATE_CSS_DATA_KEY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
allowCancel : function(element, animationEvent, className) {
|
||||
//always cancel the current animation if it is a
|
||||
//structural animation
|
||||
var oldClasses = (element.data(NG_ANIMATE_CSS_DATA_KEY) || {}).classes;
|
||||
if(!oldClasses || ['enter','leave','move'].indexOf(animationEvent) >= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var parentElement = element.parent();
|
||||
var clone = angular.element(extractElementNode(element).cloneNode());
|
||||
|
||||
//make the element super hidden and override any CSS style values
|
||||
clone.attr('style','position:absolute; top:-9999px; left:-9999px');
|
||||
clone.removeAttr('id');
|
||||
clone.empty();
|
||||
|
||||
forEach(oldClasses.split(' '), function(klass) {
|
||||
clone.removeClass(klass);
|
||||
});
|
||||
|
||||
var suffix = animationEvent == 'addClass' ? '-add' : '-remove';
|
||||
clone.addClass(suffixClasses(className, suffix));
|
||||
parentElement.append(clone);
|
||||
|
||||
var timings = getElementAnimationDetails(clone);
|
||||
clone.remove();
|
||||
|
||||
return Math.max(timings.transitionDuration, timings.animationDuration) > 0;
|
||||
},
|
||||
|
||||
enter : function(element, animationCompleted) {
|
||||
return animate(element, 'ng-enter', animationCompleted);
|
||||
return animate('enter', element, 'ng-enter', animationCompleted);
|
||||
},
|
||||
|
||||
leave : function(element, animationCompleted) {
|
||||
return animate(element, 'ng-leave', animationCompleted);
|
||||
return animate('leave', element, 'ng-leave', animationCompleted);
|
||||
},
|
||||
|
||||
move : function(element, animationCompleted) {
|
||||
return animate(element, 'ng-move', animationCompleted);
|
||||
return animate('move', element, 'ng-move', animationCompleted);
|
||||
},
|
||||
|
||||
beforeSetClass : function(element, add, remove, animationCompleted) {
|
||||
var className = suffixClasses(remove, '-remove') + ' ' +
|
||||
suffixClasses(add, '-add');
|
||||
var cancellationMethod = animateBefore('setClass', element, className, 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(remove);
|
||||
element.addClass(add);
|
||||
var timings = fn();
|
||||
element.attr('class', klass);
|
||||
return timings;
|
||||
});
|
||||
|
||||
if(cancellationMethod) {
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element, className);
|
||||
unblockKeyframeAnimations(element);
|
||||
animationCompleted();
|
||||
});
|
||||
return cancellationMethod;
|
||||
}
|
||||
animationCompleted();
|
||||
},
|
||||
|
||||
beforeAddClass : function(element, className, animationCompleted) {
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'), function(fn) {
|
||||
var cancellationMethod = animateBefore('addClass', 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
|
||||
@@ -1421,7 +1517,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
if(cancellationMethod) {
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element);
|
||||
unblockTransitions(element, className);
|
||||
unblockKeyframeAnimations(element);
|
||||
animationCompleted();
|
||||
});
|
||||
@@ -1430,12 +1526,19 @@ angular.module('ngAnimate', ['ng'])
|
||||
animationCompleted();
|
||||
},
|
||||
|
||||
setClass : function(element, add, remove, animationCompleted) {
|
||||
remove = suffixClasses(remove, '-remove');
|
||||
add = suffixClasses(add, '-add');
|
||||
var className = remove + ' ' + add;
|
||||
return animateAfter('setClass', element, className, animationCompleted);
|
||||
},
|
||||
|
||||
addClass : function(element, className, animationCompleted) {
|
||||
return animateAfter(element, suffixClasses(className, '-add'), animationCompleted);
|
||||
return animateAfter('addClass', element, suffixClasses(className, '-add'), animationCompleted);
|
||||
},
|
||||
|
||||
beforeRemoveClass : function(element, className, animationCompleted) {
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'), function(fn) {
|
||||
var cancellationMethod = animateBefore('removeClass', 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.
|
||||
@@ -1449,7 +1552,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
if(cancellationMethod) {
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element);
|
||||
unblockTransitions(element, className);
|
||||
unblockKeyframeAnimations(element);
|
||||
animationCompleted();
|
||||
});
|
||||
@@ -1459,7 +1562,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
},
|
||||
|
||||
removeClass : function(element, className, animationCompleted) {
|
||||
return animateAfter(element, suffixClasses(className, '-remove'), animationCompleted);
|
||||
return animateAfter('removeClass', element, suffixClasses(className, '-remove'), animationCompleted);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Vendored
+2
-1
@@ -782,7 +782,8 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
|
||||
}
|
||||
};
|
||||
|
||||
angular.forEach(['enter','leave','move','addClass','removeClass'], function(method) {
|
||||
angular.forEach(
|
||||
['enter','leave','move','addClass','removeClass','setClass'], function(method) {
|
||||
animate[method] = function() {
|
||||
animate.queue.push({
|
||||
event : method,
|
||||
|
||||
@@ -97,6 +97,23 @@ describe('jqLite', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('_data', function() {
|
||||
it('should provide access to the data present on the element', function() {
|
||||
var element = jqLite('<i>foo</i>');
|
||||
var data = ['value'];
|
||||
element.data('val', data);
|
||||
expect(angular.element._data(element[0]).data.val).toBe(data);
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
it('should provide access to the events present on the element', function() {
|
||||
var element = jqLite('<i>foo</i>');
|
||||
expect(angular.element._data(element[0]).events).toBeUndefined();
|
||||
|
||||
element.on('click', function() { });
|
||||
expect(angular.element._data(element[0]).events.click).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('inheritedData', function() {
|
||||
|
||||
|
||||
+150
-4
@@ -517,6 +517,22 @@ describe('$compile', function() {
|
||||
expect(element).toBe(attr.$$element);
|
||||
}
|
||||
}));
|
||||
directive('replaceWithTr', valueFn({
|
||||
replace: true,
|
||||
template: '<tr><td>TR</td></tr>'
|
||||
}));
|
||||
directive('replaceWithTd', valueFn({
|
||||
replace: true,
|
||||
template: '<td>TD</td>'
|
||||
}));
|
||||
directive('replaceWithTh', valueFn({
|
||||
replace: true,
|
||||
template: '<th>TH</th>'
|
||||
}));
|
||||
directive('replaceWithTbody', valueFn({
|
||||
replace: true,
|
||||
template: '<tbody><tr><td>TD</td></tr></tbody>'
|
||||
}));
|
||||
}));
|
||||
|
||||
|
||||
@@ -680,6 +696,34 @@ describe('$compile', function() {
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('should support templates with root <tr> tags', inject(function($compile, $rootScope) {
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-tr></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
expect(nodeName_(element)).toMatch(/tr/i);
|
||||
}));
|
||||
|
||||
it('should support templates with root <td> tags', inject(function($compile, $rootScope) {
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-td></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
expect(nodeName_(element)).toMatch(/td/i);
|
||||
}));
|
||||
|
||||
it('should support templates with root <th> tags', inject(function($compile, $rootScope) {
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-th></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
expect(nodeName_(element)).toMatch(/th/i);
|
||||
}));
|
||||
|
||||
it('should support templates with root <tbody> tags', inject(function($compile, $rootScope) {
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-tbody></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
expect(nodeName_(element)).toMatch(/tbody/i);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -776,6 +820,23 @@ describe('$compile', function() {
|
||||
replace: true,
|
||||
template: '<span>Hello, {{name}}!</span>'
|
||||
}));
|
||||
|
||||
directive('replaceWithTr', valueFn({
|
||||
replace: true,
|
||||
templateUrl: 'tr.html'
|
||||
}));
|
||||
directive('replaceWithTd', valueFn({
|
||||
replace: true,
|
||||
templateUrl: 'td.html'
|
||||
}));
|
||||
directive('replaceWithTh', valueFn({
|
||||
replace: true,
|
||||
templateUrl: 'th.html'
|
||||
}));
|
||||
directive('replaceWithTbody', valueFn({
|
||||
replace: true,
|
||||
templateUrl: 'tbody.html'
|
||||
}));
|
||||
}
|
||||
));
|
||||
|
||||
@@ -1411,6 +1472,42 @@ describe('$compile', function() {
|
||||
expect(element.html()).toContain('i = 1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should support templates with root <tr> tags', inject(function($compile, $rootScope, $templateCache) {
|
||||
$templateCache.put('tr.html', '<tr><td>TR</td></tr>');
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-tr></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
$rootScope.$digest();
|
||||
expect(nodeName_(element)).toMatch(/tr/i);
|
||||
}));
|
||||
|
||||
it('should support templates with root <td> tags', inject(function($compile, $rootScope, $templateCache) {
|
||||
$templateCache.put('td.html', '<td>TD</td>');
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-td></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
$rootScope.$digest();
|
||||
expect(nodeName_(element)).toMatch(/td/i);
|
||||
}));
|
||||
|
||||
it('should support templates with root <th> tags', inject(function($compile, $rootScope, $templateCache) {
|
||||
$templateCache.put('th.html', '<th>TH</th>');
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-th></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
$rootScope.$digest();
|
||||
expect(nodeName_(element)).toMatch(/th/i);
|
||||
}));
|
||||
|
||||
it('should support templates with root <tbody> tags', inject(function($compile, $rootScope, $templateCache) {
|
||||
$templateCache.put('tbody.html', '<tbody><tr><td>TD</td></tr></tbody>');
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-tbody></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
$rootScope.$digest();
|
||||
expect(nodeName_(element)).toMatch(/tbody/i);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -3972,6 +4069,57 @@ describe('$compile', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// issue #6006
|
||||
it('should link directive with $element as a comment node', function() {
|
||||
module(function($provide) {
|
||||
directive('innerAgain', function(log) {
|
||||
return {
|
||||
transclude: 'element',
|
||||
link: function(scope, element, attr, controllers, transclude) {
|
||||
log('innerAgain:'+lowercase(nodeName_(element))+':'+trim(element[0].data));
|
||||
transclude(scope, function(clone) {
|
||||
element.parent().append(clone);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
directive('inner', function(log) {
|
||||
return {
|
||||
replace: true,
|
||||
templateUrl: 'inner.html',
|
||||
link: function(scope, element) {
|
||||
log('inner:'+lowercase(nodeName_(element))+':'+trim(element[0].data));
|
||||
}
|
||||
};
|
||||
});
|
||||
directive('outer', function(log) {
|
||||
return {
|
||||
transclude: 'element',
|
||||
link: function(scope, element, attrs, controllers, transclude) {
|
||||
log('outer:'+lowercase(nodeName_(element))+':'+trim(element[0].data));
|
||||
transclude(scope, function(clone) {
|
||||
element.parent().append(clone);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function(log, $compile, $rootScope, $templateCache) {
|
||||
$templateCache.put('inner.html', '<div inner-again><p>Content</p></div>');
|
||||
element = $compile('<div><div outer><div inner></div></div></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
var child = element.children();
|
||||
|
||||
expect(log.toArray()).toEqual([
|
||||
"outer:#comment:outer:",
|
||||
"innerAgain:#comment:innerAgain:",
|
||||
"inner:#comment:innerAgain:"]);
|
||||
expect(child.length).toBe(1);
|
||||
expect(child.contents().length).toBe(2);
|
||||
expect(lowercase(nodeName_(child.contents().eq(0)))).toBe('#comment');
|
||||
expect(lowercase(nodeName_(child.contents().eq(1)))).toBe('div');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should safely create transclude comment node and not break with "-->"',
|
||||
@@ -4518,11 +4666,9 @@ describe('$compile', function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
data = $animate.queue.shift();
|
||||
expect(data.event).toBe('removeClass');
|
||||
expect(data.args[1]).toBe('rice');
|
||||
data = $animate.queue.shift();
|
||||
expect(data.event).toBe('addClass');
|
||||
expect(data.event).toBe('setClass');
|
||||
expect(data.args[1]).toBe('dice');
|
||||
expect(data.args[2]).toBe('rice');
|
||||
|
||||
expect(element.hasClass('ice')).toBe(true);
|
||||
expect(element.hasClass('dice')).toBe(true);
|
||||
|
||||
@@ -509,6 +509,17 @@ describe('input', function() {
|
||||
});
|
||||
}
|
||||
|
||||
it('should update the model on "compositionend"', function() {
|
||||
compileInput('<input type="text" ng-model="name" name="alias" />');
|
||||
if (!(msie < 9)) {
|
||||
browserTrigger(inputElm, 'compositionstart');
|
||||
changeInputValueTo('caitp');
|
||||
expect(scope.name).toBeUndefined();
|
||||
browserTrigger(inputElm, 'compositionend');
|
||||
expect(scope.name).toEqual('caitp');
|
||||
}
|
||||
});
|
||||
|
||||
describe('"change" event', function() {
|
||||
function assertBrowserSupportsChangeEvent(inputEventSupported) {
|
||||
// Force browser to report a lack of an 'input' event
|
||||
|
||||
@@ -335,8 +335,7 @@ describe('ngClass animations', function() {
|
||||
|
||||
$rootScope.val = 'two';
|
||||
$rootScope.$digest();
|
||||
expect($animate.queue.shift().event).toBe('removeClass');
|
||||
expect($animate.queue.shift().event).toBe('addClass');
|
||||
expect($animate.queue.shift().event).toBe('setClass');
|
||||
expect($animate.queue.length).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -450,12 +449,9 @@ describe('ngClass animations', function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('removeClass');
|
||||
expect(item.args[1]).toBe('two');
|
||||
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('addClass');
|
||||
expect(item.event).toBe('setClass');
|
||||
expect(item.args[1]).toBe('three');
|
||||
expect(item.args[2]).toBe('two');
|
||||
|
||||
expect($animate.queue.length).toBe(0);
|
||||
});
|
||||
|
||||
@@ -70,6 +70,17 @@ describe('Filter: filter', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should support deep predicate objects', function() {
|
||||
var items = [{person: {name: 'John'}},
|
||||
{person: {name: 'Rita'}},
|
||||
{person: {name: 'Billy'}},
|
||||
{person: {name: 'Joan'}}];
|
||||
expect(filter(items, {person: {name: 'Jo'}}).length).toBe(2);
|
||||
expect(filter(items, {person: {name: 'Jo'}})).toEqual([
|
||||
{person: {name: 'John'}}, {person: {name: 'Joan'}}]);
|
||||
});
|
||||
|
||||
|
||||
it('should match any properties for given "$" property', function() {
|
||||
var items = [{first: 'tom', last: 'hevery'},
|
||||
{first: 'adam', last: 'hevery', alias: 'tom', done: false},
|
||||
|
||||
@@ -482,7 +482,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
element.append(child);
|
||||
child.attr('style', 'width: 20px');
|
||||
|
||||
|
||||
$animate.addClass(child, 'ng-hide');
|
||||
$animate.leave(child);
|
||||
$rootScope.$digest();
|
||||
@@ -490,11 +490,11 @@ describe("ngAnimate", function() {
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
|
||||
//this is to verify that the existing style is appended with a semicolon automatically
|
||||
expect(child.attr('style')).toMatch(/width: 20px;.+?/i);
|
||||
//this is to verify that the existing style is appended with a semicolon automatically
|
||||
expect(child.attr('style')).toMatch(/width: 20px;.*?/i);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
|
||||
|
||||
expect(child.attr('style')).toMatch(/width: 20px/i);
|
||||
}));
|
||||
|
||||
@@ -523,7 +523,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
var completed = false;
|
||||
$animate.enter(child, element, null, function() {
|
||||
completed = true;
|
||||
completed = true;
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
@@ -564,7 +564,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
it("should fire the cancel/end function with the correct flag in the parameters",
|
||||
it("should not apply a cancellation when addClass is done multiple times",
|
||||
inject(function($animate, $rootScope, $sniffer, $timeout) {
|
||||
|
||||
element.append(child);
|
||||
@@ -572,7 +572,7 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(child, 'custom-delay');
|
||||
$animate.addClass(child, 'custom-long-delay');
|
||||
|
||||
expect(child.hasClass('animation-cancelled')).toBe(true);
|
||||
expect(child.hasClass('animation-cancelled')).toBe(false);
|
||||
expect(child.hasClass('animation-ended')).toBe(false);
|
||||
|
||||
$timeout.flush();
|
||||
@@ -764,7 +764,6 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element, 'ng-hide');
|
||||
expect(element.hasClass('ng-hide-remove')).toBe(false); //added right away
|
||||
|
||||
|
||||
if($sniffer.animations) { //cleanup some pending animations
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-add')).toBe(true);
|
||||
@@ -784,19 +783,19 @@ describe("ngAnimate", function() {
|
||||
$animate.enabled(true);
|
||||
|
||||
ss.addRule('.real-animation.ng-enter, .real-animation.ng-leave, .real-animation-fake.ng-enter, .real-animation-fake.ng-leave',
|
||||
'-webkit-animation:1s my_animation;' +
|
||||
'-webkit-animation:1s my_animation;' +
|
||||
'animation:1s my_animation;');
|
||||
|
||||
ss.addRule('.real-animation.ng-enter-stagger, .real-animation.ng-leave-stagger',
|
||||
'-webkit-animation-delay:0.1s;' +
|
||||
'-webkit-animation-duration:0s;' +
|
||||
'animation-delay:0.1s;' +
|
||||
'animation-delay:0.1s;' +
|
||||
'animation-duration:0s;');
|
||||
|
||||
ss.addRule('.fake-animation.ng-enter-stagger, .fake-animation.ng-leave-stagger',
|
||||
'-webkit-animation-delay:0.1s;' +
|
||||
'-webkit-animation-duration:1s;' +
|
||||
'animation-delay:0.1s;' +
|
||||
'animation-delay:0.1s;' +
|
||||
'animation-duration:1s;');
|
||||
|
||||
var container = $compile(html('<div></div>'))($rootScope);
|
||||
@@ -851,7 +850,7 @@ describe("ngAnimate", function() {
|
||||
$animate.enabled(true);
|
||||
|
||||
ss.addRule('.stagger-animation.ng-enter, .stagger-animation.ng-leave',
|
||||
'-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
|
||||
'-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
|
||||
'animation:my_animation 1s 1s, your_animation 1s 2s;');
|
||||
|
||||
ss.addRule('.stagger-animation.ng-enter-stagger, .stagger-animation.ng-leave-stagger',
|
||||
@@ -1075,19 +1074,19 @@ describe("ngAnimate", function() {
|
||||
$animate.enabled(true);
|
||||
|
||||
ss.addRule('.real-animation.ng-enter, .real-animation.ng-leave, .real-animation-fake.ng-enter, .real-animation-fake.ng-leave',
|
||||
'-webkit-transition:1s linear all;' +
|
||||
'-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all;');
|
||||
|
||||
ss.addRule('.real-animation.ng-enter-stagger, .real-animation.ng-leave-stagger',
|
||||
'-webkit-transition-delay:0.1s;' +
|
||||
'-webkit-transition-duration:0s;' +
|
||||
'transition-delay:0.1s;' +
|
||||
'transition-delay:0.1s;' +
|
||||
'transition-duration:0s;');
|
||||
|
||||
ss.addRule('.fake-animation.ng-enter-stagger, .fake-animation.ng-leave-stagger',
|
||||
'-webkit-transition-delay:0.1s;' +
|
||||
'-webkit-transition-duration:1s;' +
|
||||
'transition-delay:0.1s;' +
|
||||
'transition-delay:0.1s;' +
|
||||
'transition-duration:1s;');
|
||||
|
||||
var container = $compile(html('<div></div>'))($rootScope);
|
||||
@@ -1143,7 +1142,7 @@ describe("ngAnimate", function() {
|
||||
$animate.enabled(true);
|
||||
|
||||
ss.addRule('.stagger-animation.ng-enter, .ani.ng-leave',
|
||||
'-webkit-transition:1s linear color 2s, 3s linear font-size 4s;' +
|
||||
'-webkit-transition:1s linear color 2s, 3s linear font-size 4s;' +
|
||||
'transition:1s linear color 2s, 3s linear font-size 4s;');
|
||||
|
||||
ss.addRule('.stagger-animation.ng-enter-stagger, .ani.ng-leave-stagger',
|
||||
@@ -1235,7 +1234,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
it("should not allow the closing animation to close off a successive animation midway",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.some-class-add', '-webkit-transition:5s linear all;' +
|
||||
@@ -1273,9 +1272,9 @@ describe("ngAnimate", function() {
|
||||
$animate.enabled(true);
|
||||
|
||||
ss.addRule('.stagger-animation.ng-enter, .stagger-animation.ng-leave',
|
||||
'-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
|
||||
'-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
|
||||
'animation:my_animation 1s 1s, your_animation 1s 2s;' +
|
||||
'-webkit-transition:1s linear all 1s;' +
|
||||
'-webkit-transition:1s linear all 1s;' +
|
||||
'transition:1s linear all 1s;');
|
||||
|
||||
ss.addRule('.stagger-animation.ng-enter-stagger, .stagger-animation.ng-leave-stagger',
|
||||
@@ -1472,6 +1471,8 @@ describe("ngAnimate", function() {
|
||||
|
||||
expect(flag).toBe(true);
|
||||
expect(element.parent().id).toBe(parent2.id);
|
||||
|
||||
dealoc(element);
|
||||
}));
|
||||
|
||||
|
||||
@@ -1543,7 +1544,7 @@ describe("ngAnimate", function() {
|
||||
expect(steps.shift()).toEqual(['close', 'klass', 'addClass']);
|
||||
|
||||
expect(steps.shift()).toEqual(['done', 'klass', 'addClass']);
|
||||
}));
|
||||
}));
|
||||
|
||||
it('should fire the DOM callbacks even if no animation is rendered',
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
@@ -1571,7 +1572,22 @@ describe("ngAnimate", function() {
|
||||
|
||||
expect(steps.shift()).toEqual(['before', 'ng-enter', 'enter']);
|
||||
expect(steps.shift()).toEqual(['after', 'ng-enter', 'enter']);
|
||||
}));
|
||||
}));
|
||||
|
||||
it('should not fire DOM callbacks on the element being animated unless registered',
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
body.append($rootElement);
|
||||
|
||||
$animate.addClass(element, 'class');
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.verifyNoPendingTasks();
|
||||
}));
|
||||
|
||||
it("should fire a done callback when provided with no animation",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
@@ -1605,11 +1621,12 @@ describe("ngAnimate", function() {
|
||||
var element = parent.find('span');
|
||||
|
||||
var flag = false;
|
||||
$animate.removeClass(element, 'ng-hide', function() {
|
||||
$animate.addClass(element, 'ng-hide', function() {
|
||||
flag = true;
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
$timeout.flush();
|
||||
@@ -2534,7 +2551,7 @@ describe("ngAnimate", function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
inject(function($compile, $rootScope, $animate, $timeout, $rootElement) {
|
||||
$animate.enabled(true);
|
||||
|
||||
@@ -2563,8 +2580,9 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
|
||||
it("should disable all child animations on structural animations until the post animation timeout has passed", function() {
|
||||
var intercepted;
|
||||
it("should disable all child animations on structural animations until the post animation" +
|
||||
"timeout has passed as well as all structural animations", function() {
|
||||
var intercepted, continueAnimation;
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.animated', function() {
|
||||
return {
|
||||
@@ -2578,7 +2596,10 @@ describe("ngAnimate", function() {
|
||||
function ani(type) {
|
||||
return function(element, className, done) {
|
||||
intercepted = type;
|
||||
(done || className)();
|
||||
continueAnimation = function() {
|
||||
continueAnimation = angular.noop;
|
||||
(done || className)();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2595,26 +2616,45 @@ describe("ngAnimate", function() {
|
||||
var child2 = $compile('<div class="child2 animated">...</div>')($rootScope);
|
||||
var container = $compile('<div class="container">...</div>')($rootScope);
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
var body = angular.element($document[0].body);
|
||||
body.append($rootElement);
|
||||
$rootElement.append(container);
|
||||
element.append(child1);
|
||||
element.append(child2);
|
||||
|
||||
$animate.enter(element, container);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(intercepted).toBe('enter');
|
||||
continueAnimation();
|
||||
|
||||
$animate.addClass(child1, 'test');
|
||||
expect(child1.hasClass('test')).toBe(true);
|
||||
|
||||
expect(element.children().length).toBe(2);
|
||||
|
||||
expect(intercepted).toBe('enter');
|
||||
$animate.leave(child1);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.children().length).toBe(1);
|
||||
|
||||
expect(intercepted).toBe('enter');
|
||||
|
||||
$animate.move(element, null, container);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(intercepted).toBe('move');
|
||||
|
||||
$animate.addClass(child1, 'test');
|
||||
expect(child1.hasClass('test')).toBe(true);
|
||||
|
||||
expect(intercepted).toBe('move');
|
||||
$animate.leave(child1);
|
||||
$rootScope.$digest();
|
||||
//flush the enter reflow
|
||||
$timeout.flush();
|
||||
|
||||
$animate.addClass(child2, 'testing');
|
||||
expect(intercepted).toBe('move');
|
||||
|
||||
//reflow has passed
|
||||
continueAnimation();
|
||||
|
||||
//flush the move reflow
|
||||
$timeout.flush();
|
||||
|
||||
$animate.leave(child2);
|
||||
@@ -2696,42 +2736,6 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
|
||||
it("should cancel an ongoing class-based animation only if the new class contains transition/animation CSS code",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.green-add', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all;');
|
||||
|
||||
ss.addRule('.blue-add', 'background:blue;');
|
||||
|
||||
ss.addRule('.red-add', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all;');
|
||||
|
||||
ss.addRule('.yellow-add', '-webkit-animation: some_animation 4s linear 1s 2 alternate;' +
|
||||
'animation: some_animation 4s linear 1s 2 alternate;');
|
||||
|
||||
var element = $compile('<div></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$animate.addClass(element, 'green');
|
||||
expect(element.hasClass('green-add')).toBe(true);
|
||||
|
||||
$animate.addClass(element, 'blue');
|
||||
expect(element.hasClass('blue')).toBe(true);
|
||||
expect(element.hasClass('green-add')).toBe(true); //not cancelled
|
||||
|
||||
$animate.addClass(element, 'red');
|
||||
expect(element.hasClass('green-add')).toBe(false);
|
||||
expect(element.hasClass('red-add')).toBe(true);
|
||||
|
||||
$animate.addClass(element, 'yellow');
|
||||
expect(element.hasClass('red-add')).toBe(false);
|
||||
expect(element.hasClass('yellow-add')).toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
it("should cancel and perform the dom operation only after the reflow has run",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
@@ -2799,7 +2803,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'on');
|
||||
$animate.addClass(element, 'on');
|
||||
|
||||
expect(currentAnimation).toBe(null);
|
||||
expect(currentAnimation).toBe('addClass');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2828,7 +2832,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
|
||||
it('should perform pre and post animations', function() {
|
||||
var steps = [];
|
||||
var steps = [];
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.class-animate', function() {
|
||||
return {
|
||||
@@ -2857,7 +2861,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
|
||||
it('should treat the leave event always as a before event and discard the beforeLeave function', function() {
|
||||
var parentID, steps = [];
|
||||
var parentID, steps = [];
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.animate', function() {
|
||||
return {
|
||||
@@ -3076,7 +3080,7 @@ describe("ngAnimate", function() {
|
||||
var element = $compile('<div>' +
|
||||
' <div ng-repeat="item in items"' +
|
||||
' ng-include="tpl"' +
|
||||
' class="special"></div>' +
|
||||
' class="special"></div>' +
|
||||
'</div>')($rootScope);
|
||||
|
||||
ss.addRule('.special', '-webkit-transition:1s linear all;' +
|
||||
@@ -3221,7 +3225,7 @@ describe("ngAnimate", function() {
|
||||
expect(ready).toBe(true);
|
||||
}));
|
||||
|
||||
it('should avoid skip animations if the same CSS class is added / removed synchronously before the reflow kicks in',
|
||||
it('should immediately close the former animation if the same CSS class is added/removed',
|
||||
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
@@ -3243,28 +3247,15 @@ describe("ngAnimate", 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();
|
||||
|
||||
$timeout.flush(1);
|
||||
expect(signature).toBe('A');
|
||||
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2000 });
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(signature).toBe('ABC');
|
||||
expect(signature).toBe('AB');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -767,8 +767,7 @@ describe('ngView animations', function() {
|
||||
$rootScope.klass = 'boring';
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($animate.queue.shift().event).toBe('removeClass');
|
||||
expect($animate.queue.shift().event).toBe('addClass');
|
||||
expect($animate.queue.shift().event).toBe('setClass');
|
||||
|
||||
expect(item.hasClass('classy')).toBe(false);
|
||||
expect(item.hasClass('boring')).toBe(true);
|
||||
|
||||
Reference in New Issue
Block a user