Compare commits
73 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 | |||
| 5cc5cc13b9 | |||
| d5c7ef0f78 | |||
| 84fd3a18a3 | |||
| fcf4393680 | |||
| 16301bed28 | |||
| 95be253fe5 | |||
| bf4b0dbd46 | |||
| 95d119ebb2 | |||
| e609239fab | |||
| 4224cd5182 | |||
| 906fdad0f9 | |||
| a8c1d9c978 | |||
| 8829a2a86d | |||
| 84467d8697 | |||
| 0e85ca9ddb | |||
| e7face4728 | |||
| a29bff1c98 | |||
| 95522cc11f | |||
| c5f69e3f64 | |||
| dd24c78373 | |||
| 36d37c0e38 | |||
| 24699ee8f0 | |||
| aa6a0e3fc6 | |||
| 8761ddc0e3 | |||
| 058842ad04 | |||
| 431bad0183 | |||
| 5850e61c82 | |||
| d2e4e49986 | |||
| 0da6cc9118 | |||
| cc60ba1f35 | |||
| 64d58a5b52 | |||
| 3bf4390339 | |||
| e7ac7aa43b | |||
| 37781ed145 | |||
| 19ba6510d0 | |||
| bc5ceee275 | |||
| 106af49258 |
+7
-4
@@ -5,7 +5,12 @@ node_js:
|
||||
env:
|
||||
matrix:
|
||||
- JOB=unit
|
||||
- JOB=e2e
|
||||
- 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
|
||||
@@ -16,9 +21,7 @@ before_script:
|
||||
- mkdir -p $LOGS_DIR
|
||||
- ./lib/sauce/sauce_connect_setup.sh
|
||||
- npm install -g grunt-cli
|
||||
- grunt bower
|
||||
- grunt bower
|
||||
- grunt package-without-bower
|
||||
- grunt package
|
||||
- ./scripts/travis/wait_for_browser_provider.sh
|
||||
|
||||
script:
|
||||
|
||||
+140
@@ -1,3 +1,119 @@
|
||||
<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)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:** retain CSS classes added in cloneAttachFn on asynchronous directives
|
||||
([5ed721b9](https://github.com/angular/angular.js/commit/5ed721b9b5e95ae08450e1ae9d5202e7f3f79295),
|
||||
[#5439](https://github.com/angular/angular.js/issues/5439), [#5617](https://github.com/angular/angular.js/issues/5617))
|
||||
- **$http:**
|
||||
- ignore xhr.responseType setter exception if value is "json"
|
||||
([24699ee8](https://github.com/angular/angular.js/commit/24699ee8f04c1f1459be1d36207e654421d58ff0),
|
||||
[#6115](https://github.com/angular/angular.js/issues/6115), [#6122](https://github.com/angular/angular.js/issues/6122))
|
||||
- update httpBackend to use ActiveXObject on IE8 if necessary
|
||||
([ef210e5e](https://github.com/angular/angular.js/commit/ef210e5e119db4f5bfc9d2428b19f9b335c4f976),
|
||||
[#5677](https://github.com/angular/angular.js/issues/5677), [#5679](https://github.com/angular/angular.js/issues/5679))
|
||||
- **$locale:** minor grammar amends for the locale `locale_lt`
|
||||
([95be253f](https://github.com/angular/angular.js/commit/95be253fe55d35336d425d3d600a36158fc3519d),
|
||||
[#6164](https://github.com/angular/angular.js/issues/6164))
|
||||
- **$q:** make $q.reject support `finally` and `catch`
|
||||
([074b0675](https://github.com/angular/angular.js/commit/074b0675a1f97dce07f520f1ae6198ed3c604000),
|
||||
[#6048](https://github.com/angular/angular.js/issues/6048), [#6076](https://github.com/angular/angular.js/issues/6076))
|
||||
- **docs:** clarify doc for "args" in $broadcast and $emit
|
||||
([caed2dfe](https://github.com/angular/angular.js/commit/caed2dfe4feeac5d19ecea2dbb1456b7fde21e6d),
|
||||
[#6047](https://github.com/angular/angular.js/issues/6047))
|
||||
- **filterFilter:** don't interpret dots in predicate object fields as paths
|
||||
([339a1658](https://github.com/angular/angular.js/commit/339a1658cd9bfa5e322a01c45aa0a1df67e3a842),
|
||||
[#6005](https://github.com/angular/angular.js/issues/6005), [#6009](https://github.com/angular/angular.js/issues/6009))
|
||||
- **http:** make jshint happy
|
||||
([6609e3da](https://github.com/angular/angular.js/commit/6609e3da76dd898cfe85f75f23ab2e39fee65fe5))
|
||||
- **jqLite:** trim HTML string in jqLite constructor
|
||||
([36d37c0e](https://github.com/angular/angular.js/commit/36d37c0e3880c774d20c014ade60d2331beefa15),
|
||||
[#6053](https://github.com/angular/angular.js/issues/6053))
|
||||
- **mocks:**
|
||||
- rename mock.animate to ngAnimateMock and ensure it contains all test helper code for ngAnimate
|
||||
([4224cd51](https://github.com/angular/angular.js/commit/4224cd5182bc93e4a210f75e0a4e4de7f3c544e8),
|
||||
[#5822](https://github.com/angular/angular.js/issues/5822), [#5917](https://github.com/angular/angular.js/issues/5917))
|
||||
- remove usage of $animate.flushNext in favour of queing
|
||||
([906fdad0](https://github.com/angular/angular.js/commit/906fdad0f95465842e336e057ea97d0633712189))
|
||||
- always call functions injected with `inject` with `this` set to the current spec
|
||||
([3bf43903](https://github.com/angular/angular.js/commit/3bf43903397c703aa2e9ba1e1a48dbc9e8286ee2),
|
||||
[#6102](https://github.com/angular/angular.js/issues/6102))
|
||||
- refactor currentSpec to work w/ Jasmine 2
|
||||
([95f0bf9b](https://github.com/angular/angular.js/commit/95f0bf9b526fda8964527c6d4aef1ad50a47f1f3),
|
||||
[#5662](https://github.com/angular/angular.js/issues/5662))
|
||||
- **ngMock:** return false from mock $interval.cancel() when no argument is supplied
|
||||
([dd24c783](https://github.com/angular/angular.js/commit/dd24c78373b5d24ecb3b9d19e61e1b3b6c74d155),
|
||||
[#6103](https://github.com/angular/angular.js/issues/6103))
|
||||
- **ngResource:**
|
||||
- don't filter "$"-prefixed properties from ngResource requests/responses
|
||||
([d2e4e499](https://github.com/angular/angular.js/commit/d2e4e499862aeca157dbe7a7422c465e7c79205e),
|
||||
[#5666](https://github.com/angular/angular.js/issues/5666), [#6080](https://github.com/angular/angular.js/issues/6080), [#6033](https://github.com/angular/angular.js/issues/6033))
|
||||
- don't append number to '$' in url param value when encoding URI
|
||||
([ce1f1f97](https://github.com/angular/angular.js/commit/ce1f1f97f0ebf77941b2bdaf5e8352d33786524d),
|
||||
[#6003](https://github.com/angular/angular.js/issues/6003), [#6004](https://github.com/angular/angular.js/issues/6004))
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
The animation mock module has been renamed from `mock.animate` to `ngAnimateMock`. In addition to the rename, animations will not block within test code even when ngAnimateMock is used. However, all function calls to $animate will be recorded into `$animate.queue` and are available within test code to assert animation calls. In addition, `$animate.triggerReflow()` is now only available when `ngAnimateMock` is used.
|
||||
|
||||
|
||||
<a name="1.2.11"></a>
|
||||
# 1.2.11 cryptocurrency-hyperdeflation (2014-02-03)
|
||||
|
||||
@@ -82,6 +198,30 @@
|
||||
([4ae3184c](https://github.com/angular/angular.js/commit/4ae3184c5915aac9aa00889aa2153c8e84c14966),
|
||||
[#4278](https://github.com/angular/angular.js/issues/4278), [#4225](https://github.com/angular/angular.js/issues/4225))
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$http:** due to [e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
|
||||
it is now necessary to seperately specify default HTTP headers for PUT, POST and PATCH requests, as these no longer share a single object.
|
||||
|
||||
To migrate your code, follow the example below:
|
||||
|
||||
Before:
|
||||
|
||||
// Will apply to POST, PUT and PATCH methods
|
||||
$httpProvider.defaults.headers.post = {
|
||||
"X-MY-CSRF-HEADER": "..."
|
||||
};
|
||||
|
||||
After:
|
||||
|
||||
// POST, PUT and PATCH default headers must be specified seperately,
|
||||
// as they do not share data.
|
||||
$httpProvider.defaults.headers.post =
|
||||
$httpProvider.defaults.headers.put =
|
||||
$httpProviders.defaults.headers.patch = {
|
||||
"X-MY-CSRF-HEADER": "..."
|
||||
};
|
||||
|
||||
<a name="1.2.8"></a>
|
||||
# 1.2.8 interdimensional-cartography (2014-01-10)
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
We'd love for you to contribute to our source code and to make AngularJS even better than it is
|
||||
today! Here are the guidelines we'd like you to follow:
|
||||
|
||||
## Code of Conduct
|
||||
Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct][coc].
|
||||
|
||||
## Got a Question or Problem?
|
||||
|
||||
If you have questions about how to use AngularJS, please direct these to the [Google Group][groups]
|
||||
@@ -258,5 +261,6 @@ You can find out more detailed information about contributing in the
|
||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
||||
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
+9
-16
@@ -4,18 +4,8 @@ var path = require('path');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
//grunt plugins
|
||||
grunt.loadNpmTasks('grunt-bump');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-ddescribe-iit');
|
||||
grunt.loadNpmTasks('grunt-jasmine-node');
|
||||
grunt.loadNpmTasks("grunt-jscs-checker");
|
||||
grunt.loadNpmTasks('grunt-merge-conflict');
|
||||
grunt.loadNpmTasks('grunt-parallel');
|
||||
grunt.loadNpmTasks('grunt-shell');
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.loadTasks('lib/grunt');
|
||||
|
||||
var NG_VERSION = util.getVersion();
|
||||
@@ -100,8 +90,10 @@ module.exports = function(grunt) {
|
||||
},
|
||||
|
||||
|
||||
runprotractor: {
|
||||
normal: 'protractor-conf.js'
|
||||
protractor: {
|
||||
normal: 'protractor-conf.js',
|
||||
jquery: 'protractor-jquery-conf.js',
|
||||
jenkins: 'protractor-jenkins-conf.js'
|
||||
},
|
||||
|
||||
|
||||
@@ -300,7 +292,9 @@ module.exports = function(grunt) {
|
||||
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['tests:modules']);
|
||||
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', 'runprotractor:normal']);
|
||||
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']);
|
||||
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
|
||||
@@ -308,7 +302,6 @@ module.exports = function(grunt) {
|
||||
grunt.registerTask('minify', ['bower','clean', 'build', 'minall']);
|
||||
grunt.registerTask('webserver', ['connect:devserver']);
|
||||
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('package-without-bower', ['clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+1
-1
@@ -7,6 +7,6 @@
|
||||
"components-font-awesome": "3.1.0",
|
||||
"bootstrap": "https://raw.github.com/twbs/bootstrap/v2.0.2/docs/assets/bootstrap.zip",
|
||||
"closure-compiler": "https://closure-compiler.googlecode.com/files/compiler-20130603.zip",
|
||||
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.2/assets/ng-closure-runner.zip"
|
||||
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
@name Developer Guide: E2E Testing
|
||||
@description
|
||||
|
||||
**If you're starting a new Angular project, you may want to look into
|
||||
using {@link https://github.com/angular/protractor Protractor}, as it is going to
|
||||
replace the current method of E2E Testing in the near future.**
|
||||
**Angular Scenario Runner is in maintenance mode - If you're starting a new Angular project,
|
||||
consider using {@link https://github.com/angular/protractor Protractor}.**
|
||||
|
||||
|
||||
|
||||
As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to
|
||||
verify the correctness of new features, catch bugs and notice regressions.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -110,6 +110,10 @@ prevent accidental access to the global state (a common source of subtle bugs).
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
it('should calculate expression in binding', function() {
|
||||
if (browser.params.browser = 'safari') {
|
||||
// Safari can't handle dialogs.
|
||||
return;
|
||||
};
|
||||
element(by.css('[ng-click="greet()"]')).click();
|
||||
|
||||
var alertDialog = browser.switchTo().alert();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -178,11 +178,11 @@ grunt --help
|
||||
|
||||
## Running the End-to-end Test Suite
|
||||
|
||||
Simply run:
|
||||
Angular's end to end tests are run with Protractor. Simply run:
|
||||
|
||||
```shell
|
||||
grunt test:e2e
|
||||
```
|
||||
|
||||
This will start the webserver and run the tests.
|
||||
This will start the webserver and run the tests on Chrome.
|
||||
|
||||
|
||||
@@ -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?
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ becoming an Angular expert.
|
||||
|
||||
1. Read the {@link guide/concepts conceptual overview}.<br/>Understand Angular's vocabulary and how all the Angular
|
||||
components work together.
|
||||
1. Do the {@link tutorial/ AngularJS Tutorial}.<br/>Walk end-to-end through building and application complete with tests
|
||||
1. Do the {@link tutorial/ AngularJS Tutorial}.<br/>Walk end-to-end through building an application complete with tests
|
||||
on top of a node.js web server. Covers every major AngularJS feature and show you how to set up your development
|
||||
environment.
|
||||
1. Download or clone the {@link https://github.com/angular/angular-seed Seed App project template}.<br/>Gives you a
|
||||
|
||||
@@ -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
-5
@@ -51,7 +51,7 @@ exports.ngVersions = function() {
|
||||
});
|
||||
|
||||
//match the future version of AngularJS that is set in the package.json file
|
||||
return expandVersions(sortVersionsNatrually(versions), exports.ngCurrentVersion().full);
|
||||
return expandVersions(sortVersionsNaturally(versions), exports.ngCurrentVersion().full);
|
||||
|
||||
function expandVersions(versions, latestVersion) {
|
||||
var RC_VERSION = /rc\d/;
|
||||
@@ -87,7 +87,7 @@ exports.ngVersions = function() {
|
||||
return expanded;
|
||||
};
|
||||
|
||||
function sortVersionsNatrually(versions) {
|
||||
function sortVersionsNaturally(versions) {
|
||||
var versionMap = {},
|
||||
NON_RC_RELEASE_NUMBER = 999;
|
||||
for(var i = versions.length - 1; i >= 0; i--) {
|
||||
@@ -1110,15 +1110,15 @@ function scenarios(docs){
|
||||
}
|
||||
}
|
||||
|
||||
function writeProtractorTest(doc){
|
||||
function writeProtractorTest(doc, pathPrefix){
|
||||
var lines = [];
|
||||
lines.push('describe("' + doc.section + '/' + doc.id + '", function() {');
|
||||
lines.push(' beforeEach(function() {');
|
||||
lines.push(' browser.get("index-nocache.html#!/' + doc.section + '/' + doc.id + '");');
|
||||
lines.push(' browser.get("' + pathPrefix + doc.section + '/' + doc.id + '");');
|
||||
lines.push(' });');
|
||||
lines.push('');
|
||||
doc.protractorTests.forEach(function(test){
|
||||
lines.push(indentCode(trim(test), 2));
|
||||
lines.push(indentCode(trim(test), 0));
|
||||
lines.push('');
|
||||
});
|
||||
lines.push('});');
|
||||
|
||||
+1
-2
@@ -1,11 +1,10 @@
|
||||
# i18n directory overview:
|
||||
|
||||
- closure/ - closure files we use for ruleset generation
|
||||
- locale/ - angular's locale ruleset files
|
||||
- src/ - source files
|
||||
- spec/ - spec files for stuff in src directory
|
||||
- generate.sh - runs src scripts on closure dir and stores output in locale dir
|
||||
- update-closure.sh - downloads the latest version of closure files from public svn repo
|
||||
- update-closure.sh - downloads the latest version of closure files from public git repo
|
||||
|
||||
The closure files (maintained by Shanjian Li (shanjian)) change very rarely, so we don't need to
|
||||
regenerate locale files very often.
|
||||
|
||||
+6
-1
@@ -1,7 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
BASE_DIR=`dirname $0`
|
||||
cd $BASE_DIR
|
||||
|
||||
./run-tests.sh
|
||||
|
||||
node src/closureSlurper.js
|
||||
|
||||
|
||||
../node_modules/.bin/jasmine-node spec/ --noColor && node src/closureSlurper.js
|
||||
|
||||
+2
-1
@@ -2,4 +2,5 @@
|
||||
|
||||
set -e
|
||||
PARENT_DIR="$(dirname "$0")"
|
||||
jasmine-node "$PARENT_DIR"/spec/
|
||||
|
||||
../node_modules/.bin/jasmine-node "$PARENT_DIR"/spec/
|
||||
|
||||
+28
-12
@@ -2,7 +2,7 @@
|
||||
'use strict';
|
||||
|
||||
var Q = require('q'),
|
||||
qfs = require('q-fs'),
|
||||
qfs = require('q-io/fs'),
|
||||
converter = require('./converter.js'),
|
||||
util = require('./util.js'),
|
||||
closureI18nExtractor = require('./closureI18nExtractor.js'),
|
||||
@@ -47,24 +47,40 @@ function extractPlurals() {
|
||||
|
||||
function writeLocaleFiles() {
|
||||
console.log('Final stage: Writing angular locale files to directory: %j', NG_LOCALE_DIR);
|
||||
var writePromises = [];
|
||||
var result = Q.defer();
|
||||
var localeIds = Object.keys(localeInfo);
|
||||
var num_files = 0;
|
||||
localeIds.forEach(function(localeID) {
|
||||
|
||||
console.log('Generated %j locale files.', localeIds.length);
|
||||
loop();
|
||||
return result.promise;
|
||||
|
||||
// Need to use a loop and not write the files in parallel,
|
||||
// as otherwise we will get the error EMFILE, which means
|
||||
// we have too many open files.
|
||||
function loop() {
|
||||
var nextPromise;
|
||||
if (localeIds.length) {
|
||||
nextPromise = process(localeIds.pop()) || Q.when();
|
||||
nextPromise.then(loop, result.reject);
|
||||
} else {
|
||||
result.resolve(num_files);
|
||||
}
|
||||
}
|
||||
|
||||
function process(localeID) {
|
||||
var content = closureI18nExtractor.outputLocale(localeInfo, localeID);
|
||||
if (!content) return;
|
||||
var correctedLocaleId = closureI18nExtractor.correctedLocaleId(localeID);
|
||||
var filename = NG_LOCALE_DIR + 'angular-locale_' + correctedLocaleId + '.js'
|
||||
writePromises.push(
|
||||
qfs.write(filename, content)
|
||||
.then(function () {
|
||||
console.log('Wrote ' + filename);
|
||||
++num_files;
|
||||
}));
|
||||
console.log('Writing ' + filename);
|
||||
});
|
||||
console.log('Generated %j locale files.', localeIds.length);
|
||||
return Q.all(writePromises).then(function() { return num_files });
|
||||
return qfs.write(filename, content)
|
||||
.then(function () {
|
||||
console.log('Wrote ' + filename);
|
||||
++num_files;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,8 +7,9 @@ cd $BASE_DIR
|
||||
|
||||
set -x # Trace commands as they're executed.
|
||||
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/currency.js > closure/currencySymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbols.js > closure/datetimeSymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbolsext.js > closure/datetimeSymbolsExt.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/numberformatsymbols.js > closure/numberSymbols.js
|
||||
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
|
||||
# use the github repo as it is more up to date than the svn repo
|
||||
curl https://closure-library.googlecode.com/git/closure/goog/i18n/currency.js > closure/currencySymbols.js
|
||||
curl https://closure-library.googlecode.com/git/closure/goog/i18n/datetimesymbols.js > closure/datetimeSymbols.js
|
||||
curl https://closure-library.googlecode.com/git/closure/goog/i18n/datetimesymbolsext.js > closure/datetimeSymbolsExt.js
|
||||
curl https://closure-library.googlecode.com/git/closure/goog/i18n/numberformatsymbols.js > closure/numberSymbols.js
|
||||
curl https://closure-library.googlecode.com/git/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
|
||||
|
||||
Binary file not shown.
+3
-8
@@ -13,12 +13,6 @@ then
|
||||
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh,/Users/jenkins/bin/ie8.sh,/Users/jenkins/bin/ie9.sh"
|
||||
fi
|
||||
|
||||
if [[ -z "$BROWSERS_E2E" ]]
|
||||
then
|
||||
BROWSERS_E2E="Chrome,Firefox,/Users/jenkins/bin/safari.sh"
|
||||
fi
|
||||
|
||||
|
||||
# CLEAN #
|
||||
rm -f angular.min.js.gzip.size
|
||||
rm -f angular.js.size
|
||||
@@ -28,6 +22,8 @@ rm -f angular.js.size
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
|
||||
mkdir -p test_out
|
||||
|
||||
# DOCS generator unit tests #
|
||||
grunt test:docgen --no-color
|
||||
|
||||
@@ -35,8 +31,7 @@ grunt test:docgen --no-color
|
||||
grunt test:unit --browsers $BROWSERS --reporters=dots,junit --no-colors --no-color
|
||||
|
||||
# END TO END TESTS #
|
||||
grunt test:e2e --browsers $BROWSERS_E2E --reporters=dots,junit --no-colors --no-color
|
||||
grunt test:protractor
|
||||
grunt test:ci-protractor
|
||||
|
||||
# Promises/A+ TESTS #
|
||||
grunt test:promises-aplus --no-color
|
||||
|
||||
@@ -65,7 +65,7 @@ module.exports = function(grunt) {
|
||||
util.updateWebdriver.call(util, this.async());
|
||||
});
|
||||
|
||||
grunt.registerMultiTask('runprotractor', 'Run Protractor integration tests', function() {
|
||||
grunt.registerMultiTask('protractor', 'Run Protractor integration tests', function() {
|
||||
util.startProtractor.call(util, this.data, this.async());
|
||||
});
|
||||
|
||||
|
||||
+81
-18
@@ -2,6 +2,7 @@ var fs = require('fs');
|
||||
var shell = require('shelljs');
|
||||
var grunt = require('grunt');
|
||||
var spawn = require('child_process').spawn;
|
||||
var semver = require('semver');
|
||||
var version;
|
||||
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
|
||||
|
||||
@@ -32,31 +33,82 @@ module.exports = {
|
||||
|
||||
getVersion: function(){
|
||||
if (version) return version;
|
||||
|
||||
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
|
||||
var match = package.version.match(/^([^\-]*)(?:\-(.+))?$/);
|
||||
var semver = match[1].split('.');
|
||||
try {
|
||||
|
||||
var fullVersion = match[1];
|
||||
var gitTag = getTagOfCurrentCommit();
|
||||
var semVerVersion, codeName, fullVersion;
|
||||
if (gitTag) {
|
||||
// tagged release
|
||||
fullVersion = semVerVersion = semver.valid(gitTag);
|
||||
codeName = getTaggedReleaseCodeName(gitTag);
|
||||
} else {
|
||||
// snapshot release
|
||||
semVerVersion = getSnapshotVersion();
|
||||
fullVersion = semVerVersion + '-' + getSnapshotSuffix();
|
||||
codeName = 'snapshot'
|
||||
}
|
||||
|
||||
if (match[2]) {
|
||||
fullVersion += '-';
|
||||
fullVersion += (match[2] == 'snapshot') ? getSnapshotSuffix() : match[2];
|
||||
var versionParts = semVerVersion.match(/(\d+)\.(\d+)\.(\d+)/);
|
||||
|
||||
version = {
|
||||
full: fullVersion,
|
||||
major: versionParts[1],
|
||||
minor: versionParts[2],
|
||||
dot: versionParts[3],
|
||||
codename: codeName,
|
||||
cdn: package.cdnVersion
|
||||
};
|
||||
|
||||
return version;
|
||||
|
||||
} catch (e) {
|
||||
grunt.fail.warn(e);
|
||||
}
|
||||
|
||||
version = {
|
||||
full: fullVersion,
|
||||
major: semver[0],
|
||||
minor: semver[1],
|
||||
dot: semver[2].replace(/rc\d+/, ''),
|
||||
codename: package.codename,
|
||||
cdn: package.cdnVersion
|
||||
};
|
||||
function getTagOfCurrentCommit() {
|
||||
var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
|
||||
var gitTagOutput = gitTagResult.output.trim();
|
||||
var branchVersionPattern = new RegExp(package.branchVersion.replace('.', '\\.').replace('*', '\\d+'));
|
||||
if (gitTagResult.code === 0 && gitTagOutput.match(branchVersionPattern)) {
|
||||
return gitTagOutput;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return version;
|
||||
function getTaggedReleaseCodeName(tagName) {
|
||||
var tagMessage = shell.exec('git cat-file -p '+ tagName +' | grep "codename"', {silent:true}).output;
|
||||
var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
|
||||
if (!codeName) {
|
||||
throw new Error("Could not extract release code name. The message of tag "+tagName+
|
||||
" must match '*codename(some release name)*'");
|
||||
}
|
||||
return codeName;
|
||||
}
|
||||
|
||||
function getSnapshotVersion() {
|
||||
var oldTags = shell.exec('git tag -l v'+package.branchVersion, {silent:true}).output.trim().split('\n');
|
||||
// ignore non semver versions.
|
||||
oldTags = oldTags.filter(function(version) {
|
||||
return version && semver.valid(version);
|
||||
});
|
||||
if (oldTags.length) {
|
||||
oldTags.sort(semver.compare);
|
||||
semVerVersion = oldTags[oldTags.length-1];
|
||||
if (semVerVersion.indexOf('-') !== -1) {
|
||||
semVerVersion = semver.inc(semVerVersion, 'prerelease');
|
||||
} else {
|
||||
semVerVersion = semver.inc(semVerVersion, 'patch');
|
||||
}
|
||||
} else {
|
||||
semVerVersion = semver.valid(package.branchVersion.replace(/\*/g, '0'));
|
||||
}
|
||||
return semVerVersion;
|
||||
}
|
||||
|
||||
function getSnapshotSuffix() {
|
||||
var jenkinsBuild = process.env.BUILD_NUMBER || 'local';
|
||||
var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER || 'local';
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
return 'build.'+jenkinsBuild+'+sha.'+hash;
|
||||
}
|
||||
@@ -84,7 +136,13 @@ module.exports = {
|
||||
},
|
||||
|
||||
|
||||
updateWebdriver: function(done){
|
||||
updateWebdriver: function(done){
|
||||
if (process.env.TRAVIS) {
|
||||
// Skip the webdriver-manager update on Travis, since the browsers will
|
||||
// be provided remotely.
|
||||
done();
|
||||
return;
|
||||
}
|
||||
var p = spawn('node', ['node_modules/protractor/bin/webdriver-manager', 'update']);
|
||||
p.stdout.pipe(process.stdout);
|
||||
p.stderr.pipe(process.stderr);
|
||||
@@ -99,11 +157,16 @@ module.exports = {
|
||||
var sauceKey = grunt.option('sauceKey');
|
||||
var tunnelIdentifier = grunt.option('capabilities.tunnel-identifier');
|
||||
var sauceBuild = grunt.option('capabilities.build');
|
||||
var browser = grunt.option('browser');
|
||||
var args = ['node_modules/protractor/bin/protractor', config];
|
||||
if (sauceUser) args.push('--sauceUser=' + sauceUser);
|
||||
if (sauceKey) args.push('--sauceKey=' + sauceKey);
|
||||
if (tunnelIdentifier) args.push('--capabilities.tunnel-identifier=' + tunnelIdentifier);
|
||||
if (sauceBuild) args.push('--capabilities.build=' + sauceBuild);
|
||||
if (browser) {
|
||||
args.push('--browser=' + browser);
|
||||
args.push('--params.browser=' + browser);
|
||||
}
|
||||
|
||||
|
||||
var p = spawn('node', args);
|
||||
|
||||
+17
-16
@@ -1,22 +1,29 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"version": "1.2.11",
|
||||
"cdnVersion": "1.2.10",
|
||||
"codename": "cryptocurrency-hyperdeflation",
|
||||
"branchVersion": "1.2.*",
|
||||
"cdnVersion": "1.2.12",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.2",
|
||||
"bower": "~1.2.2",
|
||||
"grunt-bump": "~0.0.13",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-contrib-compress": "~0.5.2",
|
||||
"grunt-contrib-connect": "~0.5.0",
|
||||
"grunt-contrib-compress": "~0.5.2",
|
||||
"grunt-contrib-copy": "~0.4.1",
|
||||
"grunt-contrib-jshint": "~0.7.2",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||
"grunt-jscs-checker": "~0.3.2",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"grunt-parallel": "~0.3.1",
|
||||
"grunt-shell": "~0.4.0",
|
||||
"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",
|
||||
@@ -29,20 +36,16 @@
|
||||
"karma-sauce-launcher": "0.2.0",
|
||||
"karma-script-launcher": "0.1.0",
|
||||
"karma-browserstack-launcher": "0.0.7",
|
||||
"protractor": "~0.17.0",
|
||||
"protractor": "~0.18.0",
|
||||
"yaml-js": "~0.0.8",
|
||||
"marked": "0.2.9",
|
||||
"rewire": "1.1.3",
|
||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||
"grunt-parallel": "~0.3.1",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"promises-aplus-tests": "~1.3.2",
|
||||
"grunt-shell": "~0.4.0",
|
||||
"semver": "~2.1.0",
|
||||
"lodash": "~2.1.0",
|
||||
"browserstacktunnel-wrapper": "~1.1.1",
|
||||
"grunt-jscs-checker": "~0.3.2"
|
||||
"grunt-jscs-checker": "~0.3.2",
|
||||
"jasmine-reporters": "~0.2.1"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
@@ -50,7 +53,5 @@
|
||||
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"grunt-contrib-jshint": "~0.7.2"
|
||||
}
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
+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,36 @@
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
|
||||
specs: [
|
||||
'build/docs/ptore2e/**/*.js',
|
||||
'test/e2e/docsAppE2E.js'
|
||||
],
|
||||
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
},
|
||||
|
||||
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);
|
||||
|
||||
require('jasmine-reporters');
|
||||
jasmine.getEnv().addReporter(
|
||||
new jasmine.JUnitXmlReporter('test_out/e2e-' + this.capabilities.browserName + '-', true, true));
|
||||
},
|
||||
|
||||
jasmineNodeOpts: {
|
||||
defaultTimeoutInterval: 30000,
|
||||
showColors: false
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "############################################"
|
||||
echo "## Remove "-snapshot" from version ########"
|
||||
echo "############################################"
|
||||
|
||||
ARG_DEFS=()
|
||||
|
||||
function run {
|
||||
cd ../..
|
||||
|
||||
replaceJsonProp "package.json" "version" "(.*)-snapshot" "\2"
|
||||
VERSION=$(readJsonProp "package.json" "version")
|
||||
|
||||
git add package.json
|
||||
git commit -m "chore(release): cut v$VERSION release"
|
||||
git tag -m "v$VERSION" v$VERSION
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "############################################"
|
||||
echo "## Increment version, add "-snapshot" and set version name ##"
|
||||
echo "############################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--next-version-type=(patch|minor|major)"
|
||||
"--next-version-name=(.+)"
|
||||
)
|
||||
|
||||
function run {
|
||||
cd ../..
|
||||
|
||||
grunt bump:$NEXT_VERSION_TYPE
|
||||
NEXT_VERSION=$(readJsonProp "package.json" "version")
|
||||
replaceJsonProp "package.json" "version" "(.*)" "\2-snapshot"
|
||||
replaceJsonProp "package.json" "codename" ".*" "$NEXT_VERSION_NAME"
|
||||
|
||||
git add package.json
|
||||
git commit -m "chore(release): start v$NEXT_VERSION ($NEXT_VERSION)"
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script for updating angular.js repo from current local build.
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update angular.js ###"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
"--next-version-type=(patch|minor|major)"
|
||||
"--next-version-name=(.+)"
|
||||
"[--no-test=(true|false)]"
|
||||
)
|
||||
|
||||
function init {
|
||||
cd ../..
|
||||
}
|
||||
|
||||
function prepare() {
|
||||
./scripts/angular.js/finalize-version.sh
|
||||
|
||||
# Build
|
||||
if [[ $NO_TEST == "true" ]]; then
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
else
|
||||
./jenkins_build.sh
|
||||
fi
|
||||
|
||||
./scripts/angular.js/initialize-new-version.sh --next-version-type=$NEXT_VERSION_TYPE --next-version-name=$NEXT_VERSION_NAME
|
||||
}
|
||||
|
||||
function publish() {
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
# push the commits to github
|
||||
git push origin $BRANCH
|
||||
# push the release tag
|
||||
git push origin v`cat build/version.txt`
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Tags a release
|
||||
# so that travis can do the actual release.
|
||||
|
||||
echo "#################################"
|
||||
echo "## Tag angular.js for a release #"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
"--commit-sha=(.*)"
|
||||
# the version number of the release.
|
||||
# e.g. 1.2.12 or 1.2.12-rc.1
|
||||
"--version-number=([0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?)"
|
||||
"--version-name=(.+)"
|
||||
)
|
||||
|
||||
function checkVersionNumber() {
|
||||
BRANCH_PATTERN=$(readJsonProp "package.json" "branchVersion")
|
||||
if [[ $VERSION_NUMBER != $BRANCH_PATTERN ]]; then
|
||||
echo "version-number needs to match $BRANCH_PATTERN on this branch"
|
||||
usage
|
||||
fi
|
||||
}
|
||||
|
||||
function init {
|
||||
cd ../..
|
||||
checkVersionNumber
|
||||
TAG_NAME="v$VERSION_NUMBER"
|
||||
}
|
||||
|
||||
function prepare() {
|
||||
git tag "$TAG_NAME" -m "chore(release): $TAG_NAME codename($VERSION_NAME)" "$COMMIT_SHA"
|
||||
}
|
||||
|
||||
function publish() {
|
||||
# push the tag to github
|
||||
git push origin $TAG_NAME
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
+38
-13
@@ -1,38 +1,63 @@
|
||||
#!/bin/bash
|
||||
|
||||
# tags the current commit as a release and publishes all artifacts to
|
||||
# the different repositories.
|
||||
# Note: This will also works if the commit is in the past!
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Cut release ################"
|
||||
echo "#### cut release ############"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--next-version-type=(patch|minor|major)"
|
||||
"--next-version-name=(.+)"
|
||||
# require the git dryrun flag so the script can't be run without
|
||||
# thinking about this!
|
||||
"--git-push-dryrun=(true|false)"
|
||||
"[--no-test=(true|false)]"
|
||||
# The sha to release. Needs to be the same as HEAD.
|
||||
# given as parameter to double check.
|
||||
"--commit-sha=(.*)"
|
||||
# the version number of the release.
|
||||
# e.g. 1.2.12 or 1.2.12-rc.1
|
||||
"--version-number=([0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?)"
|
||||
# the codename of the release
|
||||
"--version-name=(.+)"
|
||||
)
|
||||
|
||||
function init {
|
||||
NG_ARGS=("$@")
|
||||
if [[ $(git rev-parse --short HEAD) != $COMMIT_SHA ]]; then
|
||||
echo "HEAD is not at $COMMIT_SHA"
|
||||
usage
|
||||
fi
|
||||
|
||||
if [[ ! $VERBOSE ]]; then
|
||||
VERBOSE=false
|
||||
fi
|
||||
if [[ ! $NO_TEST ]]; then
|
||||
NO_TEST=false
|
||||
fi
|
||||
VERBOSE_ARG="--verbose=$VERBOSE"
|
||||
NO_TEST_ARG="--no_test=$NO_TEST"
|
||||
}
|
||||
|
||||
function build {
|
||||
cd ../..
|
||||
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
|
||||
cd $SCRIPT_DIR
|
||||
}
|
||||
|
||||
function phase {
|
||||
ACTION_ARG="--action=$1"
|
||||
../angular.js/publish.sh $ACTION_ARG $VERBOSE_ARG $NO_TEST_ARG \
|
||||
--next-version-type=$NEXT_VERSION_TYPE --next-version-name=$NEXT_VERSION_NAME
|
||||
../angular.js/tag-release.sh $ACTION_ARG $VERBOSE_ARG\
|
||||
--version-number=$VERSION_NUMBER --version-name=$VERSION_NAME\
|
||||
--commit-sha=$COMMIT_SHA
|
||||
|
||||
if [[ $1 == "prepare" ]]; then
|
||||
# The build requires the tag to be set already!
|
||||
build
|
||||
fi
|
||||
|
||||
../code.angularjs.org/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||
../bower/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||
../angular-seed/publish.sh $ACTION_ARG $VERBOSE_ARG $NO_TEST_ARG
|
||||
../angular-phonecat/publish.sh $ACTION_ARG $VERBOSE_ARG $NO_TEST_ARG
|
||||
../angular-seed/publish.sh $ACTION_ARG $VERBOSE_ARG --no-test=true
|
||||
../angular-phonecat/publish.sh $ACTION_ARG $VERBOSE_ARG --no-test=true
|
||||
}
|
||||
|
||||
function run {
|
||||
|
||||
@@ -10,10 +10,15 @@ if [ $JOB = "unit" ]; then
|
||||
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 \
|
||||
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
|
||||
--capabilities.build=$TRAVIS_BUILD_NUMBER \
|
||||
--browser=$BROWSER
|
||||
else
|
||||
echo "Unknown job type. Please set JOB=unit or JOB=e2e."
|
||||
echo "Unknown job type. Please set JOB=unit or JOB=e2e-*."
|
||||
fi
|
||||
|
||||
+6
-4
@@ -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
|
||||
@@ -192,7 +193,7 @@ function isArrayLike(obj) {
|
||||
* is the value of an object property or an array element and `key` is the object property key or
|
||||
* array element index. Specifying a `context` for the function is optional.
|
||||
*
|
||||
* It is worth nothing that `.forEach` does not iterate over inherited properties because it filters
|
||||
* It is worth noting that `.forEach` does not iterate over inherited properties because it filters
|
||||
* using the `hasOwnProperty` method.
|
||||
*
|
||||
<pre>
|
||||
@@ -201,7 +202,7 @@ function isArrayLike(obj) {
|
||||
angular.forEach(values, function(value, key){
|
||||
this.push(key + ': ' + value);
|
||||
}, log);
|
||||
expect(log).toEqual(['name: misko', 'gender:male']);
|
||||
expect(log).toEqual(['name: misko', 'gender: male']);
|
||||
</pre>
|
||||
*
|
||||
* @param {Object|Array} obj Object to iterate over.
|
||||
@@ -772,7 +773,7 @@ function shallowCopy(src, dst) {
|
||||
for(var key in src) {
|
||||
// shallowCopy is only ever called by $compile nodeLinkFn, which has control over src
|
||||
// so we don't need to worry about using our custom hasOwnProperty here
|
||||
if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') {
|
||||
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -589,7 +589,7 @@ function annotate(fn) {
|
||||
* Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
|
||||
* calls to {@link ng.$log#error $log.warn()}.
|
||||
* <pre>
|
||||
* $provider.decorator('$log', ['$delegate', function($delegate) {
|
||||
* $provide.decorator('$log', ['$delegate', function($delegate) {
|
||||
* $delegate.warn = $delegate.error;
|
||||
* return $delegate;
|
||||
* }]);
|
||||
|
||||
+13
-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; }
|
||||
|
||||
|
||||
@@ -175,6 +183,9 @@ function JQLite(element) {
|
||||
if (element instanceof JQLite) {
|
||||
return element;
|
||||
}
|
||||
if (isString(element)) {
|
||||
element = trim(element);
|
||||
}
|
||||
if (!(this instanceof JQLite)) {
|
||||
if (isString(element) && element.charAt(0) != '<') {
|
||||
throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}];
|
||||
|
||||
+46
-11
@@ -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,
|
||||
@@ -1196,7 +1205,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
hasTranscludeDirective = true;
|
||||
|
||||
// Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
|
||||
// This option should only be used by directives that know how to how to safely handle element transclusion,
|
||||
// This option should only be used by directives that know how to safely handle element transclusion,
|
||||
// where the transcluded nodes are added or replaced after linking.
|
||||
if (!directive.$$tlb) {
|
||||
assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
|
||||
@@ -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
|
||||
|
||||
@@ -59,7 +59,14 @@
|
||||
|
||||
element(by.id('link-3')).click();
|
||||
|
||||
expect(browser.driver.getCurrentUrl()).toMatch(/\/123$/);
|
||||
// At this point, we navigate away from an Angular page, so we need
|
||||
// to use browser.driver to get the base webdriver.
|
||||
|
||||
browser.wait(function() {
|
||||
return browser.driver.getCurrentUrl().then(function(url) {
|
||||
return url.match(/\/123$/);
|
||||
});
|
||||
}, 1000, 'page should navigate to /123');
|
||||
});
|
||||
|
||||
it('should execute ng-click but not reload when href empty string and name specified', function() {
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -955,11 +957,17 @@ var VALID_CLASS = 'ng-valid',
|
||||
</file>
|
||||
<file name="protractorTest.js">
|
||||
it('should data-bind and become invalid', function() {
|
||||
if (browser.params.browser = 'safari') {
|
||||
// SafariDriver can't handle contenteditable.
|
||||
return;
|
||||
};
|
||||
var contentEditable = element(by.css('.doc-example-live [contenteditable]'));
|
||||
|
||||
expect(contentEditable.getText()).toEqual('Change me!');
|
||||
|
||||
contentEditable.clear();
|
||||
// Firefox driver doesn't trigger the proper events on 'clear', so do this hack
|
||||
contentEditable.click();
|
||||
contentEditable.sendKeys(protractor.Key.chord(protractor.Key.COMMAND, "a"));
|
||||
contentEditable.sendKeys(protractor.Key.BACK_SPACE);
|
||||
|
||||
expect(contentEditable.getText()).toEqual('');
|
||||
@@ -1016,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;
|
||||
|
||||
@@ -133,7 +133,6 @@ function classDirective(name, selector) {
|
||||
expect(ps.get(1).getAttribute('class')).toBe('');
|
||||
element(by.model('style')).clear();
|
||||
element(by.model('style')).sendKeys('red');
|
||||
browser.debugger();
|
||||
expect(ps.get(1).getAttribute('class')).toBe('red');
|
||||
});
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -119,12 +119,21 @@
|
||||
});
|
||||
|
||||
it('should load template2.html', function() {
|
||||
if (browser.params.browser == 'firefox') {
|
||||
// Firefox can't handle using selects
|
||||
// See https://github.com/angular/protractor/issues/480
|
||||
return;
|
||||
}
|
||||
templateSelect.click();
|
||||
templateSelect.element.all(by.css('option')).get(2).click();
|
||||
expect(includeElem.getText()).toMatch(/Content of template2.html/);
|
||||
});
|
||||
|
||||
it('should change to blank', function() {
|
||||
if (browser.params.browser == 'firefox') {
|
||||
// Firefox can't handle using selects
|
||||
return;
|
||||
}
|
||||
templateSelect.click();
|
||||
templateSelect.element.all(by.css('option')).get(0).click();
|
||||
expect(includeElem.isPresent()).toBe(false);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('USD$1,234.56');
|
||||
});
|
||||
it('should update', function() {
|
||||
if (browser.params.browser == 'safari') {
|
||||
// Safari does not understand the minus key. See
|
||||
// https://github.com/angular/protractor/issues/481
|
||||
return;
|
||||
}
|
||||
element(by.model('amount')).clear();
|
||||
element(by.model('amount')).sendKeys('-1234');
|
||||
expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
|
||||
|
||||
+14
-1
@@ -107,7 +107,20 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
}
|
||||
|
||||
if (responseType) {
|
||||
xhr.responseType = responseType;
|
||||
try {
|
||||
xhr.responseType = responseType;
|
||||
} catch (e) {
|
||||
// WebKit added support for the json responseType value on 09/03/2013
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
|
||||
// known to throw when setting the value "json" as the response type. Other older
|
||||
// browsers implementing the responseType
|
||||
//
|
||||
// The json response type can be ignored if not supported, because JSON payloads are
|
||||
// parsed on the client-side regardless.
|
||||
if (responseType !== 'json') {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xhr.send(post || null);
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ function $LogProvider(){
|
||||
* @name ng.$logProvider#debugEnabled
|
||||
* @methodOf ng.$logProvider
|
||||
* @description
|
||||
* @param {string=} flag enable or disable debug level messages
|
||||
* @param {boolean=} flag enable or disable debug level messages
|
||||
* @returns {*} current value if used as getter or itself (chaining) if used as setter
|
||||
*/
|
||||
this.debugEnabled = function(flag) {
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@
|
||||
* constructed via `$q.reject`, the promise will be rejected instead.
|
||||
* - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
|
||||
* resolving it with a rejection constructed via `$q.reject`.
|
||||
* - `notify(value)` - provides updates on the status of the promises execution. This may be called
|
||||
* - `notify(value)` - provides updates on the status of the promise's execution. This may be called
|
||||
* multiple times before the promise is either resolved or rejected.
|
||||
*
|
||||
* **Properties**
|
||||
|
||||
+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
-2
@@ -17,7 +17,7 @@ $provide.value("$locale", {
|
||||
"\u0161e\u0161tadienis"
|
||||
],
|
||||
"MONTH": [
|
||||
"sausio",
|
||||
"sausis",
|
||||
"vasaris",
|
||||
"kovas",
|
||||
"balandis",
|
||||
@@ -41,7 +41,7 @@ $provide.value("$locale", {
|
||||
],
|
||||
"SHORTMONTH": [
|
||||
"Saus.",
|
||||
"Vas",
|
||||
"Vas.",
|
||||
"Kov.",
|
||||
"Bal.",
|
||||
"Geg.",
|
||||
|
||||
Vendored
+2
-2
@@ -17,7 +17,7 @@ $provide.value("$locale", {
|
||||
"\u0161e\u0161tadienis"
|
||||
],
|
||||
"MONTH": [
|
||||
"sausio",
|
||||
"sausis",
|
||||
"vasaris",
|
||||
"kovas",
|
||||
"balandis",
|
||||
@@ -41,7 +41,7 @@ $provide.value("$locale", {
|
||||
],
|
||||
"SHORTMONTH": [
|
||||
"Saus.",
|
||||
"Vas",
|
||||
"Vas.",
|
||||
"Kov.",
|
||||
"Bal.",
|
||||
"Geg.",
|
||||
|
||||
Vendored
+18
-47
@@ -504,6 +504,7 @@ angular.mock.$IntervalProvider = function() {
|
||||
};
|
||||
|
||||
$interval.cancel = function(promise) {
|
||||
if(!promise) return false;
|
||||
var fnIndex;
|
||||
|
||||
angular.forEach(repeatFns, function(fn, index) {
|
||||
@@ -756,70 +757,40 @@ angular.mock.TzDate = function (offset, timestamp) {
|
||||
angular.mock.TzDate.prototype = Date.prototype;
|
||||
/* jshint +W101 */
|
||||
|
||||
// TODO(matias): remove this IMMEDIATELY once we can properly detect the
|
||||
// presence of a registered module
|
||||
var animateLoaded;
|
||||
try {
|
||||
angular.module('ngAnimate');
|
||||
animateLoaded = true;
|
||||
} catch(e) {}
|
||||
angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
|
||||
|
||||
if(animateLoaded) {
|
||||
angular.module('ngAnimate').config(['$provide', function($provide) {
|
||||
.config(['$provide', function($provide) {
|
||||
var reflowQueue = [];
|
||||
|
||||
$provide.value('$$animateReflow', function(fn) {
|
||||
reflowQueue.push(fn);
|
||||
return angular.noop;
|
||||
});
|
||||
$provide.decorator('$animate', function($delegate) {
|
||||
$delegate.triggerReflow = function() {
|
||||
if(reflowQueue.length === 0) {
|
||||
throw new Error('No animation reflows present');
|
||||
}
|
||||
angular.forEach(reflowQueue, function(fn) {
|
||||
fn();
|
||||
});
|
||||
reflowQueue = [];
|
||||
};
|
||||
return $delegate;
|
||||
});
|
||||
}]);
|
||||
}
|
||||
|
||||
angular.mock.animate = angular.module('mock.animate', ['ng'])
|
||||
|
||||
.config(['$provide', function($provide) {
|
||||
|
||||
$provide.decorator('$animate', function($delegate) {
|
||||
var animate = {
|
||||
queue : [],
|
||||
enabled : $delegate.enabled,
|
||||
flushNext : function(name) {
|
||||
var tick = animate.queue.shift();
|
||||
|
||||
if (!tick) throw new Error('No animation to be flushed');
|
||||
if(tick.method !== name) {
|
||||
throw new Error('The next animation is not "' + name +
|
||||
'", but is "' + tick.method + '"');
|
||||
triggerReflow : function() {
|
||||
if(reflowQueue.length === 0) {
|
||||
throw new Error('No animation reflows present');
|
||||
}
|
||||
tick.fn();
|
||||
return tick;
|
||||
angular.forEach(reflowQueue, function(fn) {
|
||||
fn();
|
||||
});
|
||||
reflowQueue = [];
|
||||
}
|
||||
};
|
||||
|
||||
angular.forEach(['enter','leave','move','addClass','removeClass'], function(method) {
|
||||
angular.forEach(
|
||||
['enter','leave','move','addClass','removeClass','setClass'], function(method) {
|
||||
animate[method] = function() {
|
||||
var params = arguments;
|
||||
animate.queue.push({
|
||||
method : method,
|
||||
params : params,
|
||||
element : angular.isElement(params[0]) && params[0],
|
||||
parent : angular.isElement(params[1]) && params[1],
|
||||
after : angular.isElement(params[2]) && params[2],
|
||||
fn : function() {
|
||||
$delegate[method].apply($delegate, params);
|
||||
}
|
||||
event : method,
|
||||
element : arguments[0],
|
||||
args : arguments
|
||||
});
|
||||
$delegate[method].apply($delegate, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -2125,7 +2096,7 @@ if(window.jasmine || window.mocha) {
|
||||
window.inject = angular.mock.inject = function() {
|
||||
var blockFns = Array.prototype.slice.call(arguments, 0);
|
||||
var errorForStack = new Error('Declaration Location');
|
||||
return isSpecRunning() ? workFn() : workFn;
|
||||
return isSpecRunning() ? workFn.call(currentSpec) : workFn;
|
||||
/////////////////////
|
||||
function workFn() {
|
||||
var modules = currentSpec.$modules || [];
|
||||
|
||||
@@ -35,7 +35,7 @@ function shallowClearAndCopy(src, dst) {
|
||||
});
|
||||
|
||||
for (var key in src) {
|
||||
if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') {
|
||||
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
|
||||
+22
-1
@@ -190,7 +190,7 @@ describe('angular', function() {
|
||||
expect(copy.key).toBe(original.key);
|
||||
});
|
||||
|
||||
it('should not copy $$ properties nor prototype properties', function() {
|
||||
it('should omit "$$"-prefixed properties', function() {
|
||||
var original = {$$some: true, $$: true};
|
||||
var clone = {};
|
||||
|
||||
@@ -198,6 +198,27 @@ describe('angular', function() {
|
||||
expect(clone.$$some).toBeUndefined();
|
||||
expect(clone.$$).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should copy "$"-prefixed properties from copy', function() {
|
||||
var original = {$some: true};
|
||||
var clone = {};
|
||||
|
||||
expect(shallowCopy(original, clone)).toBe(clone);
|
||||
expect(clone.$some).toBe(original.$some);
|
||||
});
|
||||
|
||||
it('should omit properties from prototype chain', function() {
|
||||
var original, clone = {};
|
||||
function Func() {};
|
||||
Func.prototype.hello = "world";
|
||||
|
||||
original = new Func();
|
||||
original.goodbye = "world";
|
||||
|
||||
expect(shallowCopy(original, clone)).toBe(clone);
|
||||
expect(clone.hello).toBeUndefined();
|
||||
expect(clone.goodbye).toBe("world");
|
||||
});
|
||||
});
|
||||
|
||||
describe('elementHTML', function() {
|
||||
|
||||
@@ -30,7 +30,6 @@ describe('docs.angularjs.org', function () {
|
||||
browser.sleep(500);
|
||||
|
||||
var nameInput = element(by.input('user.name'));
|
||||
nameInput.click();
|
||||
nameInput.sendKeys('!!!');
|
||||
|
||||
var code = element(by.css('.doc-example-live tt'));
|
||||
|
||||
@@ -65,6 +65,17 @@ describe('jqLite', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should allow construction of html with leading whitespace', function() {
|
||||
var nodes = jqLite(' \n\r \r\n<div>1</div><span>2</span>');
|
||||
expect(nodes[0].parentNode).toBeDefined();
|
||||
expect(nodes[0].parentNode.nodeType).toBe(11); /** Document Fragment **/;
|
||||
expect(nodes[0].parentNode).toBe(nodes[1].parentNode);
|
||||
expect(nodes.length).toBe(2);
|
||||
expect(nodes[0].innerHTML).toBe('1');
|
||||
expect(nodes[1].innerHTML).toBe('2');
|
||||
});
|
||||
|
||||
|
||||
it('should allow creation of comment tags', function() {
|
||||
var nodes = jqLite('<!-- foo -->');
|
||||
expect(nodes.length).toBe(1);
|
||||
@@ -86,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() {
|
||||
|
||||
|
||||
+159
-9
@@ -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 "-->"',
|
||||
@@ -4490,7 +4638,7 @@ describe('$compile', function() {
|
||||
|
||||
describe('$animate animation hooks', function() {
|
||||
|
||||
beforeEach(module('mock.animate'));
|
||||
beforeEach(module('ngAnimateMock'));
|
||||
|
||||
it('should automatically fire the addClass and removeClass animation hooks',
|
||||
inject(function($compile, $animate, $rootScope) {
|
||||
@@ -4506,8 +4654,9 @@ describe('$compile', function() {
|
||||
$rootScope.val2 = 'rice';
|
||||
$rootScope.$digest();
|
||||
|
||||
data = $animate.flushNext('addClass');
|
||||
expect(data.params[1]).toBe('ice rice');
|
||||
data = $animate.queue.shift();
|
||||
expect(data.event).toBe('addClass');
|
||||
expect(data.args[1]).toBe('ice rice');
|
||||
|
||||
expect(element.hasClass('ice')).toBe(true);
|
||||
expect(element.hasClass('rice')).toBe(true);
|
||||
@@ -4516,10 +4665,10 @@ describe('$compile', function() {
|
||||
$rootScope.val2 = 'dice';
|
||||
$rootScope.$digest();
|
||||
|
||||
data = $animate.flushNext('removeClass');
|
||||
expect(data.params[1]).toBe('rice');
|
||||
data = $animate.flushNext('addClass');
|
||||
expect(data.params[1]).toBe('dice');
|
||||
data = $animate.queue.shift();
|
||||
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);
|
||||
@@ -4529,8 +4678,9 @@ describe('$compile', function() {
|
||||
$rootScope.val2 = '';
|
||||
$rootScope.$digest();
|
||||
|
||||
data = $animate.flushNext('removeClass');
|
||||
expect(data.params[1]).toBe('ice dice');
|
||||
data = $animate.queue.shift();
|
||||
expect(data.event).toBe('removeClass');
|
||||
expect(data.args[1]).toBe('ice dice');
|
||||
|
||||
expect(element.hasClass('ice')).toBe(false);
|
||||
expect(element.hasClass('dice')).toBe(false);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -309,7 +309,7 @@ describe('ngClass animations', function() {
|
||||
var body, element, $rootElement;
|
||||
|
||||
it("should avoid calling addClass accidentally when removeClass is going on", function() {
|
||||
module('mock.animate');
|
||||
module('ngAnimateMock');
|
||||
inject(function($compile, $rootScope, $animate, $timeout) {
|
||||
var element = angular.element('<div ng-class="val"></div>');
|
||||
var body = jqLite(document.body);
|
||||
@@ -320,23 +320,22 @@ describe('ngClass animations', function() {
|
||||
|
||||
$rootScope.val = 'one';
|
||||
$rootScope.$digest();
|
||||
$animate.flushNext('addClass');
|
||||
expect($animate.queue.shift().event).toBe('addClass');
|
||||
expect($animate.queue.length).toBe(0);
|
||||
|
||||
$rootScope.val = '';
|
||||
$rootScope.$digest();
|
||||
$animate.flushNext('removeClass'); //only removeClass is called
|
||||
expect($animate.queue.shift().event).toBe('removeClass'); //only removeClass is called
|
||||
expect($animate.queue.length).toBe(0);
|
||||
|
||||
$rootScope.val = 'one';
|
||||
$rootScope.$digest();
|
||||
$animate.flushNext('addClass');
|
||||
expect($animate.queue.shift().event).toBe('addClass');
|
||||
expect($animate.queue.length).toBe(0);
|
||||
|
||||
$rootScope.val = 'two';
|
||||
$rootScope.$digest();
|
||||
$animate.flushNext('removeClass');
|
||||
$animate.flushNext('addClass');
|
||||
expect($animate.queue.shift().event).toBe('setClass');
|
||||
expect($animate.queue.length).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -416,7 +415,7 @@ describe('ngClass animations', function() {
|
||||
});
|
||||
|
||||
it("should not remove classes if they're going to be added back right after", function() {
|
||||
module('mock.animate');
|
||||
module('ngAnimateMock');
|
||||
|
||||
inject(function($rootScope, $compile, $animate) {
|
||||
var className;
|
||||
@@ -430,16 +429,18 @@ describe('ngClass animations', function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
//this fires twice due to the class observer firing
|
||||
className = $animate.flushNext('addClass').params[1];
|
||||
expect(className).toBe('one two three');
|
||||
var item = $animate.queue.shift();
|
||||
expect(item.event).toBe('addClass');
|
||||
expect(item.args[1]).toBe('one two three');
|
||||
|
||||
expect($animate.queue.length).toBe(0);
|
||||
|
||||
$rootScope.three = false;
|
||||
$rootScope.$digest();
|
||||
|
||||
className = $animate.flushNext('removeClass').params[1];
|
||||
expect(className).toBe('three');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('removeClass');
|
||||
expect(item.args[1]).toBe('three');
|
||||
|
||||
expect($animate.queue.length).toBe(0);
|
||||
|
||||
@@ -447,11 +448,10 @@ describe('ngClass animations', function() {
|
||||
$rootScope.three = true;
|
||||
$rootScope.$digest();
|
||||
|
||||
className = $animate.flushNext('removeClass').params[1];
|
||||
expect(className).toBe('two');
|
||||
|
||||
className = $animate.flushNext('addClass').params[1];
|
||||
expect(className).toBe('three');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('setClass');
|
||||
expect(item.args[1]).toBe('three');
|
||||
expect(item.args[2]).toBe('two');
|
||||
|
||||
expect($animate.queue.length).toBe(0);
|
||||
});
|
||||
|
||||
@@ -210,7 +210,7 @@ describe('ngIf animations', function () {
|
||||
return element;
|
||||
}
|
||||
|
||||
beforeEach(module('mock.animate'));
|
||||
beforeEach(module('ngAnimateMock'));
|
||||
|
||||
beforeEach(module(function() {
|
||||
// we need to run animation on attached elements;
|
||||
@@ -245,8 +245,9 @@ describe('ngIf animations', function () {
|
||||
$rootScope.$digest();
|
||||
$scope.$apply('value = true');
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('Hi');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('Hi');
|
||||
|
||||
expect(element.children().length).toBe(1);
|
||||
}));
|
||||
@@ -262,14 +263,16 @@ describe('ngIf animations', function () {
|
||||
))($scope);
|
||||
$scope.$apply('value = true');
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('Hi');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('Hi');
|
||||
|
||||
$scope.$apply('value = false');
|
||||
expect(element.children().length).toBe(1);
|
||||
$scope.$apply('value = false');
|
||||
|
||||
item = $animate.flushNext('leave').element;
|
||||
expect(item.text()).toBe('Hi');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('leave');
|
||||
expect(item.element.text()).toBe('Hi');
|
||||
|
||||
expect(element.children().length).toBe(0);
|
||||
}));
|
||||
|
||||
@@ -353,7 +353,7 @@ describe('ngInclude', function() {
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(module(spyOnAnchorScroll(), 'mock.animate'));
|
||||
beforeEach(module(spyOnAnchorScroll(), 'ngAnimateMock'));
|
||||
beforeEach(inject(
|
||||
putIntoCache('template.html', 'CONTENT'),
|
||||
putIntoCache('another.html', 'CONTENT')));
|
||||
@@ -367,7 +367,7 @@ describe('ngInclude', function() {
|
||||
});
|
||||
|
||||
expect(autoScrollSpy).not.toHaveBeenCalled();
|
||||
$animate.flushNext('enter');
|
||||
expect($animate.queue.shift().event).toBe('enter');
|
||||
$timeout.flush();
|
||||
|
||||
expect(autoScrollSpy).toHaveBeenCalledOnce();
|
||||
@@ -384,7 +384,7 @@ describe('ngInclude', function() {
|
||||
$rootScope.value = true;
|
||||
});
|
||||
|
||||
$animate.flushNext('enter');
|
||||
expect($animate.queue.shift().event).toBe('enter');
|
||||
$timeout.flush();
|
||||
|
||||
$rootScope.$apply(function () {
|
||||
@@ -392,8 +392,8 @@ describe('ngInclude', function() {
|
||||
$rootScope.value = 'some-string';
|
||||
});
|
||||
|
||||
$animate.flushNext('leave');
|
||||
$animate.flushNext('enter');
|
||||
expect($animate.queue.shift().event).toBe('leave');
|
||||
expect($animate.queue.shift().event).toBe('enter');
|
||||
$timeout.flush();
|
||||
|
||||
$rootScope.$apply(function() {
|
||||
@@ -401,8 +401,8 @@ describe('ngInclude', function() {
|
||||
$rootScope.value = 100;
|
||||
});
|
||||
|
||||
$animate.flushNext('leave');
|
||||
$animate.flushNext('enter');
|
||||
expect($animate.queue.shift().event).toBe('leave');
|
||||
expect($animate.queue.shift().event).toBe('enter');
|
||||
$timeout.flush();
|
||||
|
||||
expect(autoScrollSpy).toHaveBeenCalled();
|
||||
@@ -418,7 +418,7 @@ describe('ngInclude', function() {
|
||||
$rootScope.tpl = 'template.html';
|
||||
});
|
||||
|
||||
$animate.flushNext('enter');
|
||||
expect($animate.queue.shift().event).toBe('enter');
|
||||
$timeout.flush();
|
||||
expect(autoScrollSpy).not.toHaveBeenCalled();
|
||||
}));
|
||||
@@ -434,7 +434,7 @@ describe('ngInclude', function() {
|
||||
$rootScope.value = false;
|
||||
});
|
||||
|
||||
$animate.flushNext('enter');
|
||||
expect($animate.queue.shift().event).toBe('enter');
|
||||
$timeout.flush();
|
||||
|
||||
$rootScope.$apply(function () {
|
||||
@@ -456,7 +456,7 @@ describe('ngInclude', function() {
|
||||
expect(autoScrollSpy).not.toHaveBeenCalled();
|
||||
|
||||
$rootScope.$apply("tpl = 'template.html'");
|
||||
$animate.flushNext('enter');
|
||||
expect($animate.queue.shift().event).toBe('enter');
|
||||
$timeout.flush();
|
||||
|
||||
expect(autoScrollSpy).toHaveBeenCalledOnce();
|
||||
@@ -589,7 +589,7 @@ describe('ngInclude animations', function() {
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
beforeEach(module('mock.animate'));
|
||||
beforeEach(module('ngAnimateMock'));
|
||||
|
||||
afterEach(function(){
|
||||
dealoc(element);
|
||||
@@ -608,8 +608,9 @@ describe('ngInclude animations', function() {
|
||||
))($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('data');
|
||||
var animation = $animate.queue.pop();
|
||||
expect(animation.event).toBe('enter');
|
||||
expect(animation.element.text()).toBe('data');
|
||||
}));
|
||||
|
||||
it('should fire off the leave animation',
|
||||
@@ -624,14 +625,16 @@ describe('ngInclude animations', function() {
|
||||
))($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('data');
|
||||
var animation = $animate.queue.shift();
|
||||
expect(animation.event).toBe('enter');
|
||||
expect(animation.element.text()).toBe('data');
|
||||
|
||||
$rootScope.tpl = '';
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('leave').element;
|
||||
expect(item.text()).toBe('data');
|
||||
animation = $animate.queue.shift();
|
||||
expect(animation.event).toBe('leave');
|
||||
expect(animation.element.text()).toBe('data');
|
||||
}));
|
||||
|
||||
it('should animate two separate ngInclude elements',
|
||||
@@ -647,14 +650,14 @@ describe('ngInclude animations', function() {
|
||||
))($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('one');
|
||||
var item1 = $animate.queue.shift().element;
|
||||
expect(item1.text()).toBe('one');
|
||||
|
||||
$rootScope.tpl = 'two';
|
||||
$rootScope.$digest();
|
||||
|
||||
var itemA = $animate.flushNext('leave').element;
|
||||
var itemB = $animate.flushNext('enter').element;
|
||||
var itemA = $animate.queue.shift().element;
|
||||
var itemB = $animate.queue.shift().element;
|
||||
expect(itemA.attr('ng-include')).toBe('tpl');
|
||||
expect(itemB.attr('ng-include')).toBe('tpl');
|
||||
expect(itemA).not.toEqual(itemB);
|
||||
|
||||
@@ -1149,7 +1149,7 @@ describe('ngRepeat animations', function() {
|
||||
return element;
|
||||
}
|
||||
|
||||
beforeEach(module('mock.animate'));
|
||||
beforeEach(module('ngAnimateMock'));
|
||||
|
||||
beforeEach(module(function() {
|
||||
// we need to run animation on attached elements;
|
||||
@@ -1182,14 +1182,17 @@ describe('ngRepeat animations', function() {
|
||||
$rootScope.items = ['1','2','3'];
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('1');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('1');
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('2');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('2');
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('3');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('3');
|
||||
}));
|
||||
|
||||
it('should fire off the leave animation',
|
||||
@@ -1207,20 +1210,24 @@ describe('ngRepeat animations', function() {
|
||||
$rootScope.items = ['1','2','3'];
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('1');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('1');
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('2');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('2');
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('3');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('3');
|
||||
|
||||
$rootScope.items = ['1','3'];
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('leave').element;
|
||||
expect(item.text()).toBe('2');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('leave');
|
||||
expect(item.element.text()).toBe('2');
|
||||
}));
|
||||
|
||||
it('should fire off the move animation',
|
||||
@@ -1239,23 +1246,28 @@ describe('ngRepeat animations', function() {
|
||||
$rootScope.items = ['1','2','3'];
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('1');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('1');
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('2');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('2');
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('3');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('3');
|
||||
|
||||
$rootScope.items = ['2','3','1'];
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('move').element;
|
||||
expect(item.text()).toBe('2');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('move');
|
||||
expect(item.element.text()).toBe('2');
|
||||
|
||||
item = $animate.flushNext('move').element;
|
||||
expect(item.text()).toBe('1');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('move');
|
||||
expect(item.element.text()).toBe('3');
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
@@ -73,7 +73,7 @@ describe('ngShow / ngHide animations', function() {
|
||||
body.removeAttr('ng-animation-running');
|
||||
});
|
||||
|
||||
beforeEach(module('mock.animate'));
|
||||
beforeEach(module('ngAnimateMock'));
|
||||
|
||||
beforeEach(module(function($animateProvider, $provide) {
|
||||
return function(_$rootElement_) {
|
||||
@@ -91,16 +91,18 @@ describe('ngShow / ngHide animations', function() {
|
||||
))($scope);
|
||||
$scope.$digest();
|
||||
|
||||
item = $animate.flushNext('removeClass').element;
|
||||
expect(item.text()).toBe('data');
|
||||
expect(item).toBeShown();
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('removeClass');
|
||||
expect(item.element.text()).toBe('data');
|
||||
expect(item.element).toBeShown();
|
||||
|
||||
$scope.on = false;
|
||||
$scope.$digest();
|
||||
|
||||
item = $animate.flushNext('addClass').element;
|
||||
expect(item.text()).toBe('data');
|
||||
expect(item).toBeHidden();
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('addClass');
|
||||
expect(item.element.text()).toBe('data');
|
||||
expect(item.element).toBeHidden();
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -114,16 +116,18 @@ describe('ngShow / ngHide animations', function() {
|
||||
))($scope);
|
||||
$scope.$digest();
|
||||
|
||||
item = $animate.flushNext('addClass').element;
|
||||
expect(item.text()).toBe('datum');
|
||||
expect(item).toBeHidden();
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('addClass');
|
||||
expect(item.element.text()).toBe('datum');
|
||||
expect(item.element).toBeHidden();
|
||||
|
||||
$scope.off = false;
|
||||
$scope.$digest();
|
||||
|
||||
item = $animate.flushNext('removeClass').element;
|
||||
expect(item.text()).toBe('datum');
|
||||
expect(item).toBeShown();
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('removeClass');
|
||||
expect(item.element.text()).toBe('datum');
|
||||
expect(item.element).toBeShown();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -223,7 +223,7 @@ describe('ngSwitch animations', function() {
|
||||
return element;
|
||||
}
|
||||
|
||||
beforeEach(module('mock.animate'));
|
||||
beforeEach(module('ngAnimateMock'));
|
||||
|
||||
beforeEach(module(function() {
|
||||
// we need to run animation on attached elements;
|
||||
@@ -255,8 +255,9 @@ describe('ngSwitch animations', function() {
|
||||
$scope.val = 'one';
|
||||
$scope.$digest();
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('one');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('one');
|
||||
}));
|
||||
|
||||
|
||||
@@ -276,17 +277,20 @@ describe('ngSwitch animations', function() {
|
||||
$scope.val = 'two';
|
||||
$scope.$digest();
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('two');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('two');
|
||||
|
||||
$scope.val = 'three';
|
||||
$scope.$digest();
|
||||
|
||||
item = $animate.flushNext('leave').element;
|
||||
expect(item.text()).toBe('two');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('leave');
|
||||
expect(item.element.text()).toBe('two');
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('three');
|
||||
item = $animate.queue.shift();
|
||||
expect(item.event).toBe('enter');
|
||||
expect(item.element.text()).toBe('three');
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
describe("ngAnimate", function() {
|
||||
|
||||
beforeEach(module('ngAnimate'));
|
||||
beforeEach(module('ngAnimateMock'));
|
||||
|
||||
|
||||
it("should disable animations on bootstrap for structural animations even after the first digest has passed", function() {
|
||||
@@ -481,7 +482,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
element.append(child);
|
||||
child.attr('style', 'width: 20px');
|
||||
|
||||
|
||||
$animate.addClass(child, 'ng-hide');
|
||||
$animate.leave(child);
|
||||
$rootScope.$digest();
|
||||
@@ -489,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);
|
||||
}));
|
||||
|
||||
@@ -522,7 +523,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
var completed = false;
|
||||
$animate.enter(child, element, null, function() {
|
||||
completed = true;
|
||||
completed = true;
|
||||
});
|
||||
$rootScope.$digest();
|
||||
|
||||
@@ -563,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);
|
||||
@@ -571,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();
|
||||
@@ -763,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);
|
||||
@@ -783,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);
|
||||
@@ -850,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',
|
||||
@@ -1074,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);
|
||||
@@ -1142,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',
|
||||
@@ -1234,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;' +
|
||||
@@ -1272,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',
|
||||
@@ -1471,6 +1471,8 @@ describe("ngAnimate", function() {
|
||||
|
||||
expect(flag).toBe(true);
|
||||
expect(element.parent().id).toBe(parent2.id);
|
||||
|
||||
dealoc(element);
|
||||
}));
|
||||
|
||||
|
||||
@@ -1542,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) {
|
||||
@@ -1570,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) {
|
||||
@@ -1604,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();
|
||||
@@ -2533,7 +2551,7 @@ describe("ngAnimate", function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
inject(function($compile, $rootScope, $animate, $timeout, $rootElement) {
|
||||
$animate.enabled(true);
|
||||
|
||||
@@ -2562,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 {
|
||||
@@ -2577,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)();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2594,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);
|
||||
@@ -2695,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) {
|
||||
@@ -2798,7 +2803,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'on');
|
||||
$animate.addClass(element, 'on');
|
||||
|
||||
expect(currentAnimation).toBe(null);
|
||||
expect(currentAnimation).toBe('addClass');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2827,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 {
|
||||
@@ -2856,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 {
|
||||
@@ -3075,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;' +
|
||||
@@ -3220,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;
|
||||
@@ -3242,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');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
Vendored
+24
@@ -509,6 +509,11 @@ describe('ngMock', function() {
|
||||
|
||||
it('should not throw a runtime exception when given an undefined promise',
|
||||
inject(function($interval) {
|
||||
var task1 = jasmine.createSpy('task1'),
|
||||
promise1;
|
||||
|
||||
promise1 = $interval(task1, 1000, 1);
|
||||
|
||||
expect($interval.cancel()).toBe(false);
|
||||
}));
|
||||
});
|
||||
@@ -864,6 +869,25 @@ describe('ngMock', function() {
|
||||
});
|
||||
|
||||
|
||||
describe('this', function() {
|
||||
|
||||
it('should set `this` to be the jasmine context', inject(function() {
|
||||
expect(this instanceof jasmine.Spec).toBe(true);
|
||||
}));
|
||||
|
||||
it('should set `this` to be the jasmine context when inlined in a test', function() {
|
||||
var tested = false;
|
||||
|
||||
inject(function() {
|
||||
expect(this instanceof jasmine.Spec).toBe(true);
|
||||
tested = true;
|
||||
});
|
||||
|
||||
expect(tested).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// We don't run the following tests on IE8.
|
||||
// IE8 throws "Object does not support this property or method." error,
|
||||
// when thrown from a function defined on window (which `inject` is).
|
||||
|
||||
@@ -94,6 +94,49 @@ describe("resource", function() {
|
||||
});
|
||||
|
||||
|
||||
describe('shallow copy', function() {
|
||||
it('should make a copy', function() {
|
||||
var original = {key:{}};
|
||||
var copy = shallowClearAndCopy(original);
|
||||
expect(copy).toEqual(original);
|
||||
expect(copy.key).toBe(original.key);
|
||||
});
|
||||
|
||||
|
||||
it('should omit "$$"-prefixed properties', function() {
|
||||
var original = {$$some: true, $$: true};
|
||||
var clone = {};
|
||||
|
||||
expect(shallowClearAndCopy(original, clone)).toBe(clone);
|
||||
expect(clone.$$some).toBeUndefined();
|
||||
expect(clone.$$).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
it('should copy "$"-prefixed properties from copy', function() {
|
||||
var original = {$some: true};
|
||||
var clone = {};
|
||||
|
||||
expect(shallowClearAndCopy(original, clone)).toBe(clone);
|
||||
expect(clone.$some).toBe(original.$some);
|
||||
});
|
||||
|
||||
|
||||
it('should omit properties from prototype chain', function() {
|
||||
var original, clone = {};
|
||||
function Func() {};
|
||||
Func.prototype.hello = "world";
|
||||
|
||||
original = new Func();
|
||||
original.goodbye = "world";
|
||||
|
||||
expect(shallowClearAndCopy(original, clone)).toBe(clone);
|
||||
expect(clone.hello).toBeUndefined();
|
||||
expect(clone.goodbye).toBe("world");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should default to empty parameters', function() {
|
||||
$httpBackend.expect('GET', 'URL').respond({});
|
||||
$resource('URL').query();
|
||||
|
||||
@@ -683,21 +683,22 @@ describe('ngView animations', function() {
|
||||
}));
|
||||
|
||||
describe('hooks', function() {
|
||||
beforeEach(module('mock.animate'));
|
||||
beforeEach(module('ngAnimate'));
|
||||
beforeEach(module('ngAnimateMock'));
|
||||
|
||||
it('should fire off the enter animation',
|
||||
inject(function($compile, $rootScope, $location, $animate) {
|
||||
inject(function($compile, $rootScope, $location, $timeout, $animate) {
|
||||
element = $compile(html('<div ng-view></div>'))($rootScope);
|
||||
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
|
||||
var item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('data');
|
||||
var animation = $animate.queue.pop();
|
||||
expect(animation.event).toBe('enter');
|
||||
}));
|
||||
|
||||
it('should fire off the leave animation',
|
||||
inject(function($compile, $rootScope, $location, $templateCache, $animate) {
|
||||
inject(function($compile, $rootScope, $location, $templateCache, $timeout, $animate) {
|
||||
|
||||
var item;
|
||||
$templateCache.put('/foo.html', [200, '<div>foo</div>', {}]);
|
||||
@@ -706,35 +707,40 @@ describe('ngView animations', function() {
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('foo');
|
||||
$timeout.flush();
|
||||
|
||||
$location.path('/');
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('leave').element;
|
||||
expect(item.text()).toBe('foo');
|
||||
var animation = $animate.queue.pop();
|
||||
expect(animation.event).toBe('leave');
|
||||
}));
|
||||
|
||||
it('should animate two separate ngView elements',
|
||||
inject(function($compile, $rootScope, $templateCache, $animate, $location) {
|
||||
inject(function($compile, $rootScope, $templateCache, $location, $animate) {
|
||||
var item;
|
||||
$rootScope.tpl = 'one';
|
||||
element = $compile(html('<div><div ng-view></div></div>'))($rootScope);
|
||||
element = $compile(html('<div ng-view></div>'))($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
expect(item.text()).toBe('data');
|
||||
//we don't care about the enter animation for the first element
|
||||
$animate.queue.pop();
|
||||
|
||||
$location.path('/bar');
|
||||
$rootScope.$digest();
|
||||
|
||||
var itemA = $animate.flushNext('enter').element;
|
||||
var animationB = $animate.queue.pop();
|
||||
expect(animationB.event).toBe('leave');
|
||||
var itemB = animationB.args[0];
|
||||
|
||||
var animationA = $animate.queue.pop();
|
||||
expect(animationA.event).toBe('enter');
|
||||
var itemA = animationA.args[0];
|
||||
|
||||
expect(itemA).not.toEqual(itemB);
|
||||
var itemB = $animate.flushNext('leave').element;
|
||||
}));
|
||||
|
||||
it('should render ngClass on ngView',
|
||||
@@ -749,17 +755,19 @@ describe('ngView animations', function() {
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
|
||||
item = $animate.flushNext('enter').element;
|
||||
//we don't care about the enter animation
|
||||
$animate.queue.shift();
|
||||
|
||||
$animate.flushNext('addClass').element;
|
||||
var animation = $animate.queue.shift();
|
||||
expect(animation.event).toBe('addClass');
|
||||
|
||||
var item = animation.element;
|
||||
expect(item.hasClass('classy')).toBe(true);
|
||||
|
||||
$rootScope.klass = 'boring';
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.flushNext('removeClass').element;
|
||||
$animate.flushNext('addClass').element;
|
||||
expect($animate.queue.shift().event).toBe('setClass');
|
||||
|
||||
expect(item.hasClass('classy')).toBe(false);
|
||||
expect(item.hasClass('boring')).toBe(true);
|
||||
@@ -767,69 +775,66 @@ describe('ngView animations', function() {
|
||||
$location.path('/bar');
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.flushNext('enter').element;
|
||||
item = $animate.flushNext('leave').element;
|
||||
//we don't care about the enter animation
|
||||
$animate.queue.shift();
|
||||
|
||||
$animate.flushNext('addClass').element;
|
||||
animation = $animate.queue.shift();
|
||||
item = animation.element;
|
||||
expect(animation.event).toBe('leave');
|
||||
|
||||
expect($animate.queue.shift().event).toBe('addClass');
|
||||
|
||||
expect(item.hasClass('boring')).toBe(true);
|
||||
}));
|
||||
});
|
||||
|
||||
it('should not double compile when the route changes', function() {
|
||||
it('should not double compile when the route changes', function() {
|
||||
|
||||
module('ngAnimate');
|
||||
module('mock.animate');
|
||||
var window;
|
||||
module(function($routeProvider, $animateProvider, $provide) {
|
||||
$routeProvider.when('/foo', {template: '<div ng-repeat="i in [1,2]">{{i}}</div>'});
|
||||
$routeProvider.when('/bar', {template: '<div ng-repeat="i in [3,4]">{{i}}</div>'});
|
||||
$animateProvider.register('.my-animation', function() {
|
||||
return {
|
||||
leave: function(element, done) {
|
||||
done();
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
var window;
|
||||
module(function($routeProvider, $animateProvider, $provide) {
|
||||
$routeProvider.when('/foo', {template: '<div ng-repeat="i in [1,2]">{{i}}</div>'});
|
||||
$routeProvider.when('/bar', {template: '<div ng-repeat="i in [3,4]">{{i}}</div>'});
|
||||
$animateProvider.register('.my-animation', function() {
|
||||
return {
|
||||
leave: function(element, done) {
|
||||
done();
|
||||
inject(function($rootScope, $compile, $location, $route, $timeout, $rootElement, $sniffer, $animate) {
|
||||
element = $compile(html('<div><ng:view onload="load()" class="my-animation"></ng:view></div>'))($rootScope);
|
||||
$animate.enabled(true);
|
||||
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($animate.queue.shift().event).toBe('enter'); //ngView
|
||||
expect($animate.queue.shift().event).toBe('enter'); //repeat 1
|
||||
expect($animate.queue.shift().event).toBe('enter'); //repeat 2
|
||||
|
||||
expect(element.text()).toEqual('12');
|
||||
|
||||
$location.path('/bar');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($animate.queue.shift().event).toBe('enter'); //ngView new
|
||||
expect($animate.queue.shift().event).toBe('leave'); //ngView old
|
||||
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($animate.queue.shift().event).toBe('enter'); //ngRepeat 3
|
||||
expect($animate.queue.shift().event).toBe('enter'); //ngRepeat 4
|
||||
|
||||
expect(element.text()).toEqual('34');
|
||||
|
||||
function n(text) {
|
||||
return text.replace(/\r\n/m, '').replace(/\r\n/m, '');
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
inject(function($rootScope, $compile, $location, $route, $timeout, $rootElement, $sniffer, $animate) {
|
||||
element = $compile(html('<div><ng:view onload="load()" class="my-animation"></ng:view></div>'))($rootScope);
|
||||
$animate.enabled(true);
|
||||
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.flushNext('enter'); //ngView
|
||||
$animate.flushNext('enter'); //repeat 1
|
||||
$animate.flushNext('enter'); //repeat 2
|
||||
|
||||
expect(element.text()).toEqual('12');
|
||||
|
||||
$location.path('/bar');
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.flushNext('enter'); //ngView new
|
||||
$animate.flushNext('leave'); //ngView old
|
||||
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(n(element.text())).toEqual(''); //this is midway during the animation
|
||||
|
||||
$animate.flushNext('enter'); //ngRepeat 3
|
||||
$animate.flushNext('enter'); //ngRepeat 4
|
||||
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.text()).toEqual('34');
|
||||
|
||||
function n(text) {
|
||||
return text.replace(/\r\n/m, '').replace(/\r\n/m, '');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('autoscroll', function () {
|
||||
var autoScrollSpy;
|
||||
@@ -857,7 +862,7 @@ describe('ngView animations', function() {
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(module(spyOnAnchorScroll(), 'mock.animate'));
|
||||
beforeEach(module(spyOnAnchorScroll(), 'ngAnimateMock'));
|
||||
beforeEach(inject(spyOnAnimateEnter()));
|
||||
|
||||
it('should call $anchorScroll if autoscroll attribute is present', inject(
|
||||
@@ -866,7 +871,7 @@ describe('ngView animations', function() {
|
||||
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
$animate.flushNext('enter');
|
||||
expect($animate.queue.shift().event).toBe('enter');
|
||||
$timeout.flush();
|
||||
|
||||
expect(autoScrollSpy).toHaveBeenCalledOnce();
|
||||
@@ -880,7 +885,7 @@ describe('ngView animations', function() {
|
||||
$rootScope.value = true;
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
$animate.flushNext('enter');
|
||||
expect($animate.queue.shift().event).toBe('enter');
|
||||
$timeout.flush();
|
||||
|
||||
expect(autoScrollSpy).toHaveBeenCalledOnce();
|
||||
@@ -893,7 +898,7 @@ describe('ngView animations', function() {
|
||||
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
$animate.flushNext('enter');
|
||||
expect($animate.queue.shift().event).toBe('enter');
|
||||
$timeout.flush();
|
||||
|
||||
expect(autoScrollSpy).not.toHaveBeenCalled();
|
||||
@@ -907,7 +912,7 @@ describe('ngView animations', function() {
|
||||
$rootScope.value = false;
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
$animate.flushNext('enter');
|
||||
expect($animate.queue.shift().event).toBe('enter');
|
||||
$timeout.flush();
|
||||
|
||||
expect(autoScrollSpy).not.toHaveBeenCalled();
|
||||
@@ -924,7 +929,7 @@ describe('ngView animations', function() {
|
||||
|
||||
expect(autoScrollSpy).not.toHaveBeenCalled();
|
||||
|
||||
$animate.flushNext('enter');
|
||||
expect($animate.queue.shift().event).toBe('enter');
|
||||
$timeout.flush();
|
||||
|
||||
expect($animate.enter).toHaveBeenCalledOnce();
|
||||
|
||||
Reference in New Issue
Block a user