Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 451bde1244 | |||
| 4f827f587b | |||
| 131410b61b | |||
| ca6b7d0fa2 | |||
| 40dc806e03 | |||
| 5e9835b4f2 | |||
| e0209169bf | |||
| b07afa0465 | |||
| c3b5e16d84 | |||
| 7f444a205e | |||
| 82213efff2 | |||
| ec59be67bc | |||
| 79e519feda | |||
| 7cf5544a9f | |||
| 030a9b8d33 | |||
| 310f129c1d | |||
| 0fc64ad8a1 | |||
| 12d1f5700d | |||
| 60e80509a8 | |||
| 3c12d36e73 | |||
| fd6bac7de5 | |||
| 6d525f06c0 | |||
| d7e6f1b192 | |||
| 5b9ff6cf48 | |||
| f09b6aa5b5 | |||
| 8b395ff325 | |||
| 6c9131ef10 | |||
| 99c5027bf2 | |||
| 90e60d2d54 | |||
| 928d000db7 | |||
| 42ec95ebae | |||
| 1028cfaa30 | |||
| 756c52d6c1 | |||
| c3e1a41d6f | |||
| 8dd4f14a04 | |||
| 7ba30fd2e7 | |||
| 5adea0ba64 | |||
| 2262ca6697 | |||
| cd74f74468 | |||
| fced1c0c16 | |||
| 1cdcddb5cc | |||
| 0e4d7cacad | |||
| 6a9ccacd62 | |||
| e591ddcb30 | |||
| cd0af8a771 | |||
| 51a7f9dc4a | |||
| 61eb426ab4 | |||
| 8ea8da4f11 | |||
| 07ee29c563 | |||
| 9f5d0cf79f | |||
| 1413328e6a | |||
| 7d09bd30f9 | |||
| dde1b29497 | |||
| 4ae3184c59 | |||
| ed53100a0d | |||
| 6df598d9f5 | |||
| 4aa9df7a7a | |||
| 7d5d62dafe | |||
| 524650a40e | |||
| 02a45826f1 | |||
| e324c14907 | |||
| e1cfb1957f | |||
| 2a3586381f | |||
| 834d316829 | |||
| c61be8d0e6 | |||
| 465212835f | |||
| b3acddea37 | |||
| 308598795a | |||
| 2cd09c9f0e | |||
| 34fee06ca7 | |||
| c7a46d4b8a | |||
| de065f1961 | |||
| c3ab915d2e | |||
| f4a4f42abb | |||
| b2c84ccde3 | |||
| 2b344dbd20 | |||
| cde840fdf8 | |||
| f9656dab2d | |||
| a0d759c613 | |||
| 0d421f093d | |||
| 5f937e54df |
@@ -1,3 +1,63 @@
|
||||
<a name="1.2.10"></a>
|
||||
# 1.2.10 augmented-serendipity (2014-01-24)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$parse:** do not use locals to resolve object properties
|
||||
([f09b6aa5](https://github.com/angular/angular.js/commit/f09b6aa5b58c090e3b8f8811fb7735e38d4b7623),
|
||||
[#5838](https://github.com/angular/angular.js/issues/5838), [#5862](https://github.com/angular/angular.js/issues/5862))
|
||||
- **a:** don't call preventDefault on click when a SVGAElement has an xlink:href attribute
|
||||
([e0209169](https://github.com/angular/angular.js/commit/e0209169bf1463465ad07484421620748a4d3908),
|
||||
[#5896](https://github.com/angular/angular.js/issues/5896), [#5897](https://github.com/angular/angular.js/issues/5897))
|
||||
- **input:** use Chromium's email validation regexp
|
||||
([79e519fe](https://github.com/angular/angular.js/commit/79e519fedaec54390a8bdacfb1926bfce57a1eb6),
|
||||
[#5899](https://github.com/angular/angular.js/issues/5899), [#5924](https://github.com/angular/angular.js/issues/5924))
|
||||
- **ngRoute:** pipe preceding route param no longer masks ? or * operator
|
||||
([fd6bac7d](https://github.com/angular/angular.js/commit/fd6bac7de56f728a89782dc80c78f7d5c21bbc65),
|
||||
[#5920](https://github.com/angular/angular.js/issues/5920))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$animate:** provide support for a close callback
|
||||
([ca6b7d0f](https://github.com/angular/angular.js/commit/ca6b7d0fa2e355ebd764230260758cee9a4ebe1e),
|
||||
[#5685](https://github.com/angular/angular.js/issues/5685), [#5053](https://github.com/angular/angular.js/issues/5053), [#4993](https://github.com/angular/angular.js/issues/4993))
|
||||
|
||||
|
||||
<a name="1.2.9"></a>
|
||||
# 1.2.9 enchanted-articulacy (2014-01-15)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:**
|
||||
- ensure the final closing timeout respects staggering animations
|
||||
([ed53100a](https://github.com/angular/angular.js/commit/ed53100a0dbc9119d5dfc8b7248845d4f6989df2))
|
||||
- prevent race conditions for class-based animations when animating on the same CSS class
|
||||
([4aa9df7a](https://github.com/angular/angular.js/commit/4aa9df7a7ae533531dfae1e3eb9646245d6b5ff4),
|
||||
[#5588](https://github.com/angular/angular.js/issues/5588))
|
||||
- correctly detect and handle CSS transition changes during class addition and removal
|
||||
([7d5d62da](https://github.com/angular/angular.js/commit/7d5d62dafe11620082c79da35958f8014eeb008c))
|
||||
- avoid accidentally matching substrings when resolving the presence of className tokens
|
||||
([524650a4](https://github.com/angular/angular.js/commit/524650a40ed20f01571e5466475749874ee67288))
|
||||
- **$http:** ensure default headers PUT and POST are different objects
|
||||
([e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
|
||||
[#5742](https://github.com/angular/angular.js/issues/5742), [#5747](https://github.com/angular/angular.js/issues/5747), [#5764](https://github.com/angular/angular.js/issues/5764))
|
||||
- **$rootScope:** prevent infinite $digest by checking if asyncQueue is empty when decrementing ttl
|
||||
([2cd09c9f](https://github.com/angular/angular.js/commit/2cd09c9f0e7766bcd191662841b7b1ffc3b6dc3f),
|
||||
[#2622](https://github.com/angular/angular.js/issues/2622))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$animate:**
|
||||
- provide support for DOM callbacks
|
||||
([dde1b294](https://github.com/angular/angular.js/commit/dde1b2949727c297e214c99960141bfad438d7a4))
|
||||
- use requestAnimationFrame instead of a timeout to issue a reflow
|
||||
([4ae3184c](https://github.com/angular/angular.js/commit/4ae3184c5915aac9aa00889aa2153c8e84c14966),
|
||||
[#4278](https://github.com/angular/angular.js/issues/4278), [#4225](https://github.com/angular/angular.js/issues/4225))
|
||||
|
||||
<a name="1.2.8"></a>
|
||||
# 1.2.8 interdimensional-cartography (2014-01-10)
|
||||
|
||||
|
||||
+1
-2
@@ -80,7 +80,7 @@ Before you submit your pull request consider the following guidelines:
|
||||
```
|
||||
|
||||
* Create your patch, including appropriate test cases.
|
||||
* Follow our Coding Rules
|
||||
* Follow our [Coding Rules](#coding-rules)
|
||||
* Commit your changes and create a descriptive commit message (the
|
||||
commit message is used to generate release notes, please check out our
|
||||
[commit message conventions](#commit-message-format) and our commit message presubmit hook
|
||||
@@ -259,5 +259,4 @@ You can find out more detailed information about contributing in the
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
Using AngularJS with the Closure Compiler
|
||||
=========================================
|
||||
|
||||
The Closure Compiler project contains externs definitions for AngularJS
|
||||
JavaScript in its `contrib/externs` directory.
|
||||
|
||||
The definitions contain externs for use with the Closure compiler (aka
|
||||
JSCompiler). Passing these files to the --externs parameter of a compiler
|
||||
pass allows using type annotations for AngularJS objects. For example,
|
||||
Angular's $scope objects can be annotated as:
|
||||
```js
|
||||
/** @type {angular.Scope} */
|
||||
var scope = $scope;
|
||||
```
|
||||
|
||||
This allows JSCompiler to type check accesses to scope, give warnings about
|
||||
missing methods or incorrect arguments, and also prevents renaming of property
|
||||
accesses with advanced compilation.
|
||||
|
||||
The externs are incomplete and maintained on an as-needed basis, but strive to
|
||||
be correct. Externs for individual modules should be added in separate files.
|
||||
|
||||
See https://developers.google.com/closure/compiler/
|
||||
@@ -7,7 +7,7 @@ syntax to express your application’s components clearly and succinctly. It au
|
||||
synchronizes data from your UI (view) with your JavaScript objects (model) through 2-way data
|
||||
binding. To help you structure your application better and make it easy to test, AngularJS teaches
|
||||
the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with
|
||||
server-side communication, taming async callbacks with promises and deferreds; and make client-side
|
||||
server-side communication, taming async callbacks with promises and deferreds; and makes client-side
|
||||
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
|
||||
it makes development fun!
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
This file contains externs for use with the Closure compiler (aka JSCompiler).
|
||||
Passing these files to the --externs parameter of a compiler pass allows using
|
||||
type annotations for AngularJS objects. For example, Angular's $scope objects
|
||||
can be annotated as:
|
||||
```js
|
||||
/** @type {angular.Scope} */
|
||||
var scope = $scope;
|
||||
```
|
||||
|
||||
This allows JSCompiler to type check accesses to scope, give warnings about
|
||||
missing methods or incorrect arguments, and also prevents renaming of property
|
||||
accesses with advanced compilation.
|
||||
|
||||
The externs are incomplete and maintained on an as-needed basis, but strive to
|
||||
be correct. Externs for individual modules should be added in separate files.
|
||||
|
||||
See https://developers.google.com/closure/compiler/
|
||||
Vendored
-1975
File diff suppressed because it is too large
Load Diff
@@ -5,8 +5,8 @@
|
||||
# AngularJS API Docs
|
||||
Welcome to the AngularJS API docs page. These pages contain the AngularJS reference materials for version <strong ng-bind="version"></strong>.
|
||||
|
||||
The documentation is organized into **modules** which contain various components of an AngularJS application.
|
||||
These components are directives, services, filters, providers, types, global APIs and testing mocks.
|
||||
The documentation is organized into **{@link guide/module modules}** which contain various components of an AngularJS application.
|
||||
These components are {@link guide/directive directives}, {@link guide/dev_guide.services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates types}, global APIs and testing mocks.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Angular Namespaces `$` and `$$`**
|
||||
@@ -63,7 +63,7 @@ This module is provided by default and contains the core components of AngularJS
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
The core filters available in the ng module are used to transform template data before it is renders within directives and expressions.
|
||||
The core filters available in the ng module are used to transform template data before it is rendered within directives and expressions.
|
||||
</p>
|
||||
<p>
|
||||
Some examples include:
|
||||
|
||||
@@ -21,19 +21,19 @@ Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
|
||||
<label>
|
||||
<input type="checkbox" ng-model="checked" style="float:left; margin-right:10px;"> Is Visible...
|
||||
</label>
|
||||
<div class="check-element animate-show-hide" ng-show="checked" style="clear:both;">
|
||||
<div class="check-element sample-show-hide" ng-show="checked" style="clear:both;">
|
||||
Visible...
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.animate-show-hide {
|
||||
.sample-show-hide {
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
background:white;
|
||||
}
|
||||
|
||||
.animate-show-hide.ng-hide-add, .animate-show-hide.ng-hide-remove {
|
||||
.sample-show-hide.ng-hide-add, .sample-show-hide.ng-hide-remove {
|
||||
-webkit-transition:all linear 0.5s;
|
||||
-moz-transition:all linear 0.5s;
|
||||
-o-transition:all linear 0.5s;
|
||||
@@ -41,13 +41,13 @@ Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
|
||||
display:block!important;
|
||||
}
|
||||
|
||||
.animate-show-hide.ng-hide-add.ng-hide-add-active,
|
||||
.animate-show-hide.ng-hide-remove {
|
||||
.sample-show-hide.ng-hide-add.ng-hide-add-active,
|
||||
.sample-show-hide.ng-hide-remove {
|
||||
opacity:0;
|
||||
}
|
||||
|
||||
.animate-show-hide.ng-hide-add,
|
||||
.animate-show-hide.ng-hide-remove.ng-hide-remove-active {
|
||||
.sample-show-hide.ng-hide-add,
|
||||
.sample-show-hide.ng-hide-remove.ng-hide-remove-active {
|
||||
opacity:1;
|
||||
}
|
||||
</file>
|
||||
|
||||
@@ -184,7 +184,7 @@ The following graphic shows how everything works together after we introduced th
|
||||
# View independent business logic: Services
|
||||
|
||||
Right now, the `InvoiceController` contains all logic of our example. When the application grows it
|
||||
is a good practise to move view independent logic from the controller into a so called
|
||||
is a good practice to move view independent logic from the controller into a so called
|
||||
<a name="service">"{@link dev_guide.services service}"</a>, so it can be reused by other parts
|
||||
of the application as well. Later on, we could also change that service to load the exchange rates
|
||||
from the web, e.g. by calling the Yahoo Finance API, without changing the controller.
|
||||
@@ -272,8 +272,8 @@ including the configuration of all modules that this module depends on.
|
||||
In the example above:
|
||||
The template contains the directive `ng-app="invoice2"`. This tells Angular
|
||||
to use the `invoice` module as the main module for the application.
|
||||
The code snippet `angular.module('invoice', ['finance'])` specifies that the `invoice` module depends on the
|
||||
`finance` module. By this, Angular uses the `InvoiceController` as well as the `currencyConverter` service.
|
||||
The code snippet `angular.module('invoice2', ['finance2'])` specifies that the `invoice2` module depends on the
|
||||
`finance2` module. By this, Angular uses the `InvoiceController` as well as the `currencyConverter` service.
|
||||
|
||||
Now that Angular knows of all the parts of the application, it needs to create them.
|
||||
In the previous section we saw that controllers are created using a factory function.
|
||||
|
||||
@@ -168,7 +168,7 @@ starts with capital letter and ends with "Ctrl" or "Controller".
|
||||
- Assigning a property to `$scope` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method)
|
||||
- The Controller methods and properties are available in the template (for the `<div>` element and
|
||||
and its children).
|
||||
its children).
|
||||
|
||||
## Spicy Arguments Example
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ of which depend on other services that are provided by the Angular framework:
|
||||
|
||||
function log() {
|
||||
if (messageQueue.length) {
|
||||
$log('batchLog messages: ', messageQueue);
|
||||
$log.log('batchLog messages: ', messageQueue);
|
||||
messageQueue = [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ The following also **matches** `ngModel`:
|
||||
|
||||
Angular **normalizes** an element's tag and attribute name to determine which elements match which
|
||||
directives. We typically refer to directives by their case-sensitive
|
||||
{@link http://en.wikipedia.org/wiki/CamelCase camelCase} **normalized** name (e.g. `ngModel`).
|
||||
{@link http://en.wikipedia.org/wiki/CamelCase camelCase} **normalized** name (e.g. `ngModel`).
|
||||
However, since HTML is case-insensitive, we refer to directives in the DOM by lower-case
|
||||
forms, typically using {@link http://en.wikipedia.org/wiki/Letter_case#Computers dash-delimited}
|
||||
attributes on DOM elements (e.g. `ng-model`).
|
||||
@@ -174,9 +174,9 @@ For example, we could fix the example above by instead writing:
|
||||
|
||||
## Creating Directives
|
||||
|
||||
First let's talk about the API for registering directives. Much like controllers, directives are
|
||||
registered on modules. To register a directive, you use the `module.directive` API.
|
||||
`module.directive` takes the
|
||||
First let's talk about the {@link api/ng.$compileProvider#methods_directive API for registering directives}. Much like
|
||||
controllers, directives are registered on modules. To register a directive, you use the
|
||||
`module.directive` API. `module.directive` takes the
|
||||
{@link guide/directive#creating-custom-directives_matching-directives normalized} directive name
|
||||
followed by a **factory function.** This factory function should return an object with the different
|
||||
options to tell `$compile` how the directive should behave when matched.
|
||||
@@ -527,7 +527,8 @@ where:
|
||||
|
||||
* `scope` is an Angular scope object.
|
||||
* `element` is the jqLite-wrapped element that this directive matches.
|
||||
* `attrs` is an object with the normalized attribute names and their corresponding values.
|
||||
* `attrs` is a hash object with key-value pairs of normalized attribute names and their
|
||||
corresponding attribute values.
|
||||
|
||||
In our `link` function, we want to update the displayed time once a second, or whenever a user
|
||||
changes the time formatting string that our directive binds to. We will use the `$interval` service
|
||||
@@ -734,13 +735,13 @@ own behavior to it.
|
||||
We want to run the function we pass by invoking it from the directive's scope, but have it run
|
||||
in the context of the scope where its registered.
|
||||
|
||||
We saw earlier how to use `=prop` in the `scope` option, but in the above example, we're using
|
||||
`&prop` instead. `&` bindings expose a function to an isolated scope allowing the isolated scope
|
||||
We saw earlier how to use `=attr` in the `scope` option, but in the above example, we're using
|
||||
`&attr` instead. `&` bindings expose a function to an isolated scope allowing the isolated scope
|
||||
to invoke it, but maintaining the original scope of the function. So when a user clicks the
|
||||
`x` in the dialog, it runs `Ctrl`'s `close` function.
|
||||
`x` in the dialog, it runs `Ctrl`'s `hideDialog` function.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** use `&prop` in the `scope` option when you want your directive
|
||||
**Best Practice:** use `&attr` in the `scope` option when you want your directive
|
||||
to expose an API for binding to behaviors.
|
||||
</div>
|
||||
|
||||
|
||||
@@ -121,3 +121,6 @@ text upper-case.
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
## Testing custom filters
|
||||
|
||||
See the {@link http://docs.angularjs.org/tutorial/step_09#test phonecat tutorial} for an example.
|
||||
|
||||
@@ -99,7 +99,7 @@ actual value is understood.
|
||||
|
||||
For example, if you want to display an account balance of 1000 dollars with the following binding
|
||||
containing currency filter: `{{ 1000 | currency }}`, and your app is currently in en-US locale.
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, her
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, their
|
||||
browser will specify the locale as ja, and the balance of '¥1000.00' will be shown instead. This
|
||||
will really upset your client.
|
||||
|
||||
|
||||
@@ -87,6 +87,16 @@ grunt package
|
||||
Administrator). This is because `grunt package` creates some symbolic links.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** If you're using Linux, and npm install fails with the message
|
||||
'Please try running this command again as root/Administrator.', you may need to globally install grunt and bower:
|
||||
<ul>
|
||||
<li>sudo npm install -g grunt-cli</li>
|
||||
<li>sudo npm install -g bower</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
The build output can be located under the `build` directory. It consists of the following files and
|
||||
directories:
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ directory.</p></li>
|
||||
</pre></li>
|
||||
<li><p>You will need an http server running on your system. Mac and Linux machines typically
|
||||
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
|
||||
to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
|
||||
to run a simple bundled http server: <code>node scripts/web-server.js</code>.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
@@ -106,8 +106,8 @@ directory.</p>
|
||||
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
|
||||
executed from the Windows command line.</li>
|
||||
<li><p>You need an http server running on your system, but if you don't already have one
|
||||
already installed, you can use <code>node</code> to run <code>scripts\web-server.js</code>, a simple
|
||||
bundled http server.</p></li>
|
||||
already installed, you can use <code>node</code> to run a simple
|
||||
bundled http server: <code>node scripts\web-server.js</code>.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ angular-seed, and run the application in the browser.
|
||||
You can now see the page in your browser. It's not very exciting, but that's OK.
|
||||
|
||||
The HTML page that displays "Nothing here yet!" was constructed with the HTML code shown below.
|
||||
The code contains some key Angular elements that we will need going forward.
|
||||
The code contains some key Angular elements that we will need as we progress.
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
@@ -104,7 +104,7 @@ __`app/index.html`:__
|
||||
|
||||
The `ng-app` attribute represents an Angular directive named `ngApp` (Angular uses
|
||||
`name-with-dashes` for its custom attributes and `camelCase` for the corresponding directives
|
||||
that implements them).
|
||||
which implement them).
|
||||
This directive is used to flag the html element that Angular should consider to be the root element
|
||||
of our application.
|
||||
This gives application developers the freedom to tell Angular if the entire html page or only a
|
||||
|
||||
@@ -191,8 +191,8 @@ You can do this by issuing `npm install` into your terminal.
|
||||
To run the test, do the following:
|
||||
|
||||
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
|
||||
`./scripts/test.sh` to start the Karma server (the config file necessary to start the server
|
||||
is located at `./config/karma.conf.js`).
|
||||
`./scripts/test.sh` (if you are on Windows, run scripts\test.bat) to start the Karma server (the
|
||||
config file necessary to start the server is located at `./config/karma.conf.js`).
|
||||
|
||||
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
||||
the background. Karma will use this browser for test execution.
|
||||
@@ -206,7 +206,7 @@ is located at `./config/karma.conf.js`).
|
||||
|
||||
Yay! The test passed! Or not...
|
||||
|
||||
4. To rerun the tests, just change any of the source or test files. Karma will notice the change
|
||||
4. To rerun the tests, just change any of the source or test .js files. Karma will notice the change
|
||||
and will rerun the tests for you. Now isn't that sweet?
|
||||
|
||||
# Experiments
|
||||
@@ -217,13 +217,13 @@ is located at `./config/karma.conf.js`).
|
||||
|
||||
* Create a new model property in the controller and bind to it from the template. For example:
|
||||
|
||||
$scope.name = "World"
|
||||
$scope.name = "World";
|
||||
|
||||
Then add a new binding to `index.html`:
|
||||
|
||||
<p>Hello, {{name}}!</p>
|
||||
|
||||
Refresh your browser and verifies that it says "Hello, World!".
|
||||
Refresh your browser and verify that it says "Hello, World!".
|
||||
|
||||
* Create a repeater that constructs a simple table:
|
||||
|
||||
|
||||
@@ -88,8 +88,8 @@ phonecatApp.controller('PhoneListCtrl', function ($scope) {
|
||||
record. This property is used to order phones by age.
|
||||
|
||||
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
|
||||
not set the default value here, the model would stay uninitialized until our user picks an
|
||||
option from the drop down menu.
|
||||
not set a default value here, the `orderBy` filter would remain uninitialized until our
|
||||
user picked an option from the drop down menu.
|
||||
|
||||
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
|
||||
browser, "Newest" is selected in the drop down menu. This is because we set `orderProp` to `'age'`
|
||||
|
||||
@@ -170,6 +170,9 @@ describe('PhoneCat controllers', function() {
|
||||
describe('PhoneListCtrl', function(){
|
||||
var scope, ctrl, $httpBackend;
|
||||
|
||||
// Load our app module definition before each test.
|
||||
beforeEach(module('phonecatApp'));
|
||||
|
||||
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
|
||||
// This allows us to inject a service but then attach it to a variable
|
||||
// with the same name as the service.
|
||||
|
||||
@@ -177,7 +177,7 @@ route into the layout template. This makes it a perfect fit for our `index.html`
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Note:** Starting with AngularJS version 1.2, `ngRoute` is in its own module and must be loaded by loading
|
||||
the `angular-route.js` file distributed with Angular. The easist way to load the file is to add a `<script>`
|
||||
the `angular-route.js` file distributed with Angular. The easiest way to load the file is to add a `<script>`
|
||||
tag to your `index.html` file as shown below.
|
||||
</div>
|
||||
|
||||
|
||||
@@ -340,45 +340,58 @@ Although we could do that, let's take the opportunity to learn how to create Jav
|
||||
|
||||
__`app/js/animations.js`.__
|
||||
<pre>
|
||||
angular.module('phonecatAnimations', ['ngAnimate'])
|
||||
var phonecatAnimations = angular.module('phonecatAnimations', ['ngAnimate']);
|
||||
|
||||
.animation('.phone', function() {
|
||||
return {
|
||||
addClass : function(element, className, done) {
|
||||
if(className != 'active') {
|
||||
return;
|
||||
}
|
||||
element.css({
|
||||
position: 'absolute',
|
||||
top: 500,
|
||||
left: 0,
|
||||
display: 'block'
|
||||
});
|
||||
jQuery(element).animate({
|
||||
top: 0
|
||||
}, done);
|
||||
phonecatAnimations.animation('.phone', function() {
|
||||
|
||||
return function(cancel) {
|
||||
if(cancel) element.stop();
|
||||
};
|
||||
},
|
||||
removeClass : function(element, className, done) {
|
||||
if(className != 'active') return;
|
||||
element.css({
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0
|
||||
});
|
||||
jQuery(element).animate({
|
||||
top: -500
|
||||
}, done);
|
||||
var animateUp = function(element, className, done) {
|
||||
if(className != 'active') {
|
||||
return;
|
||||
}
|
||||
element.css({
|
||||
position: 'absolute',
|
||||
top: 500,
|
||||
left: 0,
|
||||
display: 'block'
|
||||
});
|
||||
|
||||
return function(cancel) {
|
||||
if(cancel) element.stop();
|
||||
};
|
||||
jQuery(element).animate({
|
||||
top: 0
|
||||
}, done);
|
||||
|
||||
return function(cancel) {
|
||||
if(cancel) {
|
||||
element.stop();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
var animateDown = function(element, className, done) {
|
||||
if(className != 'active') {
|
||||
return;
|
||||
}
|
||||
element.css({
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0
|
||||
});
|
||||
|
||||
jQuery(element).animate({
|
||||
top: -500
|
||||
}, done);
|
||||
|
||||
return function(cancel) {
|
||||
if(cancel) {
|
||||
element.stop();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
addClass: animateUp,
|
||||
removeClass: animateDown
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
Note that we're using {@link http://jquery.com/ jQuery} to implement the animation. jQuery
|
||||
|
||||
@@ -1401,6 +1401,14 @@ function explainModuleInstallation(moduleName){
|
||||
modulePackage = 'angular-' + moduleName,
|
||||
modulePackageFile = modulePackage + '.js';
|
||||
|
||||
// Deal with inconsistent ngMock naming - doing it verbosely and explicitly here
|
||||
// rather than cleverly interweaving it in the previous lines to make it obvious
|
||||
// what is going on
|
||||
if ( moduleName == 'mock' ) {
|
||||
modulePackage = 'angular-mocks';
|
||||
modulePackageFile = modulePackage + '.js';
|
||||
}
|
||||
|
||||
return '<h1>Installation</h1>' +
|
||||
'<p>First include <code>' + modulePackageFile +'</code> in your HTML:</p><pre><code>' +
|
||||
' <script src="angular.js">\n' +
|
||||
|
||||
@@ -246,6 +246,10 @@ ul.events > li > h3 {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tutorial-nav li {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
@@ -5,11 +5,19 @@ var docsApp = {
|
||||
filter: {}
|
||||
};
|
||||
|
||||
docsApp.controller.DocsVersionsCtrl = ['$scope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $window, NG_VERSIONS, NG_VERSION) {
|
||||
docsApp.controller.DocsVersionsCtrl = ['$scope', '$rootScope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $rootScope, $window, NG_VERSIONS, NG_VERSION) {
|
||||
$scope.docs_versions = NG_VERSIONS;
|
||||
$scope.docs_version = NG_VERSIONS[0];
|
||||
|
||||
$scope.jumpToDocsVersion = function(version) {
|
||||
$window.location = version.url;
|
||||
var currentPagePath = '';
|
||||
|
||||
// preserve URL path when switching between doc versions
|
||||
if (angular.isObject($rootScope.currentPage) && $rootScope.currentPage.section && $rootScope.currentPage.id) {
|
||||
currentPagePath = '/' + $rootScope.currentPage.section + '/' + $rootScope.currentPage.id;
|
||||
}
|
||||
|
||||
$window.location = version.url + currentPagePath;
|
||||
};
|
||||
}];
|
||||
|
||||
@@ -137,11 +145,19 @@ docsApp.directive.docsSearchInput = ['$document',function($document) {
|
||||
var ESCAPE_KEY_KEYCODE = 27,
|
||||
FORWARD_SLASH_KEYCODE = 191;
|
||||
angular.element($document[0].body).bind('keydown', function(event) {
|
||||
var input = element[0];
|
||||
if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement != input) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
input.focus();
|
||||
if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement) {
|
||||
var activeElement = document.activeElement;
|
||||
var activeTagName = activeElement.nodeName.toLowerCase();
|
||||
var hasInputFocus = activeTagName == 'input' || activeTagName == 'select' ||
|
||||
activeTagName == 'option' || activeTagName == 'textarea' ||
|
||||
activeElement.hasAttribute('contenteditable');
|
||||
if(!hasInputFocus) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
var input = element[0];
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -645,7 +661,7 @@ docsApp.serviceFactory.sections = ['NG_PAGES', function sections(NG_PAGES) {
|
||||
}];
|
||||
|
||||
|
||||
docsApp.controller.DocsController = function($scope, $location, $window, $cookies, sections) {
|
||||
docsApp.controller.DocsController = function($scope, $rootScope, $location, $window, $cookies, sections) {
|
||||
$scope.fold = function(url) {
|
||||
if(url) {
|
||||
$scope.docs_fold = '/notes/' + url;
|
||||
@@ -736,9 +752,9 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
sectionName = SECTION_NAME[sectionId] || sectionId,
|
||||
page = sections.getPage(sectionId, partialId);
|
||||
|
||||
$scope.currentPage = sections.getPage(sectionId, partialId);
|
||||
$rootScope.currentPage = sections.getPage(sectionId, partialId);
|
||||
|
||||
if (!$scope.currentPage) {
|
||||
if (!$rootScope.currentPage) {
|
||||
$scope.partialTitle = 'Error: Page Not Found!';
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -10,12 +10,12 @@ module.exports = function(config) {
|
||||
|
||||
'build/angular.js',
|
||||
'build/angular-cookies.js',
|
||||
'build/angular-mocks.js',
|
||||
'build/angular-resource.js',
|
||||
'build/angular-touch.js',
|
||||
'build/angular-sanitize.js',
|
||||
'build/angular-route.js',
|
||||
'build/angular-animate.js',
|
||||
'build/angular-mocks.js',
|
||||
|
||||
'build/docs/components/lunr.js',
|
||||
'build/docs/components/google-code-prettify.js',
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"version": "1.2.8",
|
||||
"cdnVersion": "1.2.7",
|
||||
"codename": "interdimensional-cartography",
|
||||
"version": "1.2.10",
|
||||
"cdnVersion": "1.2.9",
|
||||
"codename": "augmented-serendipity",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
|
||||
@@ -14,6 +14,17 @@ exports.config = {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
+2
-1
@@ -192,7 +192,8 @@ function isArrayLike(obj) {
|
||||
* is the value of an object property or an array element and `key` is the object property key or
|
||||
* array element index. Specifying a `context` for the function is optional.
|
||||
*
|
||||
* Note: this function was previously known as `angular.foreach`.
|
||||
* It is worth nothing that `.forEach` does not iterate over inherited properties because it filters
|
||||
* using the `hasOwnProperty` method.
|
||||
*
|
||||
<pre>
|
||||
var values = {name: 'misko', gender: 'male'};
|
||||
|
||||
+19
-16
@@ -354,11 +354,9 @@ function annotate(fn) {
|
||||
* @param {(Object|function())} provider If the provider is:
|
||||
*
|
||||
* - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
|
||||
* {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be
|
||||
* created.
|
||||
* - `Constructor`: a new instance of the provider will be created using
|
||||
* {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as
|
||||
* `object`.
|
||||
* {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created.
|
||||
* - `Constructor`: a new instance of the provider will be created using
|
||||
* {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`.
|
||||
*
|
||||
* @returns {Object} registered provider instance
|
||||
|
||||
@@ -474,7 +472,7 @@ function annotate(fn) {
|
||||
* constructor function that will be used to instantiate the service instance.
|
||||
*
|
||||
* You should use {@link AUTO.$provide#methods_service $provide.service(class)} if you define your service
|
||||
* as a type/class. This is common when using {@link http://coffeescript.org CoffeeScript}.
|
||||
* as a type/class.
|
||||
*
|
||||
* @param {string} name The name of the instance.
|
||||
* @param {Function} constructor A class (constructor function) that will be instantiated.
|
||||
@@ -482,20 +480,25 @@ function annotate(fn) {
|
||||
*
|
||||
* @example
|
||||
* Here is an example of registering a service using
|
||||
* {@link AUTO.$provide#methods_service $provide.service(class)} that is defined as a CoffeeScript class.
|
||||
* {@link AUTO.$provide#methods_service $provide.service(class)}.
|
||||
* <pre>
|
||||
* class Ping
|
||||
* constructor: (@$http) ->
|
||||
* send: () =>
|
||||
* @$http.get('/ping')
|
||||
*
|
||||
* $provide.service('ping', ['$http', Ping])
|
||||
* $provide.service('ping', ['$http', function($http) {
|
||||
* var Ping = function() {
|
||||
* this.$http = $http;
|
||||
* };
|
||||
*
|
||||
* Ping.prototype.send = function() {
|
||||
* return this.$http.get('/ping');
|
||||
* };
|
||||
*
|
||||
* return Ping;
|
||||
* }]);
|
||||
* </pre>
|
||||
* You would then inject and use this service like this:
|
||||
* <pre>
|
||||
* someModule.controller 'Ctrl', ['ping', (ping) ->
|
||||
* ping.send()
|
||||
* ]
|
||||
* someModule.controller('Ctrl', ['ping', function(ping) {
|
||||
* ping.send();
|
||||
* }]);
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
|
||||
@@ -32,11 +32,14 @@ var htmlAnchorDirective = valueFn({
|
||||
element.append(document.createComment('IE fix'));
|
||||
}
|
||||
|
||||
if (!attr.href && !attr.name) {
|
||||
if (!attr.href && !attr.xlinkHref && !attr.name) {
|
||||
return function(scope, element) {
|
||||
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
|
||||
var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
|
||||
'xlink:href' : 'href';
|
||||
element.on('click', function(event){
|
||||
// if we have no href url, then don't navigate anywhere.
|
||||
if (!element.attr('href')) {
|
||||
if (!element.attr(href)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
|
||||
var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$/;
|
||||
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i;
|
||||
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
|
||||
|
||||
var inputType = {
|
||||
@@ -309,6 +309,8 @@ var inputType = {
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
* @param {string} ngValue Angular expression which sets the value to which the expression should
|
||||
* be set when selected.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
@@ -316,21 +318,26 @@ var inputType = {
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.color = 'blue';
|
||||
$scope.specialValue = {
|
||||
"id": "12345",
|
||||
"value": "green"
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<form name="myForm" ng-controller="Ctrl">
|
||||
<input type="radio" ng-model="color" value="red"> Red <br/>
|
||||
<input type="radio" ng-model="color" value="green"> Green <br/>
|
||||
<input type="radio" ng-model="color" ng-value="specialValue"> Green <br/>
|
||||
<input type="radio" ng-model="color" value="blue"> Blue <br/>
|
||||
<tt>color = {{color}}</tt><br/>
|
||||
<tt>color = {{color | json}}</tt><br/>
|
||||
</form>
|
||||
Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should change state', function() {
|
||||
expect(binding('color')).toEqual('blue');
|
||||
expect(binding('color')).toEqual('"blue"');
|
||||
|
||||
input('color').select('red');
|
||||
expect(binding('color')).toEqual('red');
|
||||
expect(binding('color')).toEqual('"red"');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
@@ -1188,7 +1195,10 @@ var ngModelDirective = function() {
|
||||
* @name ng.directive:ngChange
|
||||
*
|
||||
* @description
|
||||
* Evaluate given expression when user changes the input.
|
||||
* Evaluate the given expression when the user changes the input.
|
||||
* The expression is evaluated immediately, unlike the JavaScript onchange event
|
||||
* which only triggers at the end of a change (usually, when the user leaves the
|
||||
* form element or presses the return key).
|
||||
* The expression is not evaluated when the value change is coming from the model.
|
||||
*
|
||||
* Note, this directive requires `ngModel` to be present.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* an element is clicked.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
|
||||
* click. (Event object is available as `$event`)
|
||||
*
|
||||
@@ -65,6 +66,7 @@ forEach(
|
||||
* The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
|
||||
* a dblclick. (The Event object is available as `$event`)
|
||||
*
|
||||
@@ -88,6 +90,7 @@ forEach(
|
||||
* The ngMousedown directive allows you to specify custom behavior on mousedown event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
|
||||
* mousedown. (Event object is available as `$event`)
|
||||
*
|
||||
@@ -111,6 +114,7 @@ forEach(
|
||||
* Specify custom behavior on mouseup event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
|
||||
* mouseup. (Event object is available as `$event`)
|
||||
*
|
||||
@@ -133,6 +137,7 @@ forEach(
|
||||
* Specify custom behavior on mouseover event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
|
||||
* mouseover. (Event object is available as `$event`)
|
||||
*
|
||||
@@ -156,6 +161,7 @@ forEach(
|
||||
* Specify custom behavior on mouseenter event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
|
||||
* mouseenter. (Event object is available as `$event`)
|
||||
*
|
||||
@@ -179,6 +185,7 @@ forEach(
|
||||
* Specify custom behavior on mouseleave event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
|
||||
* mouseleave. (Event object is available as `$event`)
|
||||
*
|
||||
@@ -202,6 +209,7 @@ forEach(
|
||||
* Specify custom behavior on mousemove event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
|
||||
* mousemove. (Event object is available as `$event`)
|
||||
*
|
||||
@@ -225,6 +233,7 @@ forEach(
|
||||
* Specify custom behavior on keydown event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
|
||||
* keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
@@ -246,6 +255,7 @@ forEach(
|
||||
* Specify custom behavior on keyup event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
|
||||
* keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
@@ -292,6 +302,7 @@ forEach(
|
||||
* attribute**.
|
||||
*
|
||||
* @element form
|
||||
* @priority 0
|
||||
* @param {expression} ngSubmit {@link guide/expression Expression} to eval. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
@@ -341,6 +352,7 @@ forEach(
|
||||
* Specify custom behavior on focus event.
|
||||
*
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
|
||||
* focus. (Event object is available as `$event`)
|
||||
*
|
||||
@@ -356,6 +368,7 @@ forEach(
|
||||
* Specify custom behavior on blur event.
|
||||
*
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
|
||||
* blur. (Event object is available as `$event`)
|
||||
*
|
||||
@@ -371,6 +384,7 @@ forEach(
|
||||
* Specify custom behavior on copy event.
|
||||
*
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
|
||||
* copy. (Event object is available as `$event`)
|
||||
*
|
||||
@@ -391,6 +405,7 @@ forEach(
|
||||
* Specify custom behavior on cut event.
|
||||
*
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
|
||||
* cut. (Event object is available as `$event`)
|
||||
*
|
||||
@@ -411,6 +426,7 @@ forEach(
|
||||
* Specify custom behavior on paste event.
|
||||
*
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
|
||||
* paste. (Event object is available as `$event`)
|
||||
*
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
* should use {@link guide/controller controllers} rather than `ngInit`
|
||||
* to initialize values on a scope.
|
||||
* </div>
|
||||
* <div class="alert alert-warning">
|
||||
* **Note**: If you have assignment in `ngInit` along with {@link api/ng.$filter `$filter`}, make
|
||||
* sure you have parenthesis for correct precedence:
|
||||
* <pre class="prettyprint">
|
||||
* <div ng-init="test1 = (data | orderBy:'name')"></div>
|
||||
* </pre>
|
||||
* </div>
|
||||
*
|
||||
* @priority 450
|
||||
*
|
||||
|
||||
@@ -52,6 +52,11 @@
|
||||
*
|
||||
* Just remember to include the important flag so the CSS override will function.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** Here is a list of values that ngShow will consider as a falsy value (case insensitive):<br />
|
||||
* "f" / "0" / "false" / "no" / "n" / "[]"
|
||||
* </div>
|
||||
*
|
||||
* ## A note about animations with ngShow
|
||||
*
|
||||
* Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
|
||||
@@ -200,6 +205,11 @@ var ngShowDirective = ['$animate', function($animate) {
|
||||
* </pre>
|
||||
*
|
||||
* Just remember to include the important flag so the CSS override will function.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** Here is a list of values that ngHide will consider as a falsy value (case insensitive):<br />
|
||||
* "f" / "0" / "false" / "no" / "n" / "[]"
|
||||
* </div>
|
||||
*
|
||||
* ## A note about animations with ngHide
|
||||
*
|
||||
|
||||
@@ -19,14 +19,21 @@ var ngOptionsMinErr = minErr('ngOptions');
|
||||
* represented by the selected option will be bound to the model identified by the `ngModel`
|
||||
* directive.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** `ngModel` compares by reference, not value. This is important when binding to an
|
||||
* array of objects. See an example {@link http://jsfiddle.net/qWzTb/ in this jsfiddle}.
|
||||
* </div>
|
||||
*
|
||||
* Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
|
||||
* be nested into the `<select>` element. This element will then represent the `null` or "not selected"
|
||||
* option. See example below for demonstration.
|
||||
*
|
||||
* Note: `ngOptions` provides iterator facility for `<option>` element which should be used instead
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** `ngOptions` provides iterator facility for `<option>` element which should be used instead
|
||||
* of {@link ng.directive:ngRepeat ngRepeat} when you want the
|
||||
* `select` model to be bound to a non-string value. This is because an option element can only
|
||||
* be bound to string values at present.
|
||||
* </div>
|
||||
*
|
||||
* @param {string} ngModel Assignable angular expression to data-bind to.
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
@@ -430,7 +437,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
|
||||
// We now build up the list of options we need (we merge later)
|
||||
for (index = 0; length = keys.length, index < length; index++) {
|
||||
|
||||
|
||||
key = index;
|
||||
if (keyName) {
|
||||
key = keys[index];
|
||||
|
||||
+8
-25
@@ -111,9 +111,9 @@ function $HttpProvider() {
|
||||
common: {
|
||||
'Accept': 'application/json, text/plain, */*'
|
||||
},
|
||||
post: CONTENT_TYPE_APPLICATION_JSON,
|
||||
put: CONTENT_TYPE_APPLICATION_JSON,
|
||||
patch: CONTENT_TYPE_APPLICATION_JSON
|
||||
post: copy(CONTENT_TYPE_APPLICATION_JSON),
|
||||
put: copy(CONTENT_TYPE_APPLICATION_JSON),
|
||||
patch: copy(CONTENT_TYPE_APPLICATION_JSON)
|
||||
},
|
||||
|
||||
xsrfCookieName: 'XSRF-TOKEN',
|
||||
@@ -223,31 +223,14 @@ function $HttpProvider() {
|
||||
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be
|
||||
* called for such responses.
|
||||
*
|
||||
* # Calling $http from outside AngularJS
|
||||
* The `$http` service will not actually send the request until the next `$digest()` is
|
||||
* executed. Normally this is not an issue, since almost all the time your call to `$http` will
|
||||
* be from within a `$apply()` block.
|
||||
* If you are calling `$http` from outside Angular, then you should wrap it in a call to
|
||||
* `$apply` to cause a $digest to occur and also to handle errors in the block correctly.
|
||||
*
|
||||
* ```
|
||||
* $scope.$apply(function() {
|
||||
* $http(...);
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* # Writing Unit Tests that use $http
|
||||
* When unit testing you are mostly responsible for scheduling the `$digest` cycle. If you do
|
||||
* not trigger a `$digest` before calling `$httpBackend.flush()` then the request will not have
|
||||
* been made and `$httpBackend.expect(...)` expectations will fail. The solution is to run the
|
||||
* code that calls the `$http()` method inside a $apply block as explained in the previous
|
||||
* section.
|
||||
* When unit testing (using {@link api/ngMock ngMock}), it is necessary to call
|
||||
* {@link api/ngMock.$httpBackend#methods_flush $httpBackend.flush()} to flush each pending
|
||||
* request using trained responses.
|
||||
*
|
||||
* ```
|
||||
* $httpBackend.expectGET(...);
|
||||
* $scope.$apply(function() {
|
||||
* $http.get(...);
|
||||
* });
|
||||
* $http.get(...);
|
||||
* $httpBackend.flush();
|
||||
* ```
|
||||
*
|
||||
@@ -324,7 +307,7 @@ function $HttpProvider() {
|
||||
* to `push` or `unshift` a new transformation function into the transformation chain. You can
|
||||
* also decide to completely override any default transformations by assigning your
|
||||
* transformation functions to these properties directly without the array wrapper. These defaults
|
||||
* are again available on the $http factory at run-time, which may be useful if you have run-time
|
||||
* are again available on the $http factory at run-time, which may be useful if you have run-time
|
||||
* services you wish to be involved in your transformations.
|
||||
*
|
||||
* Similarly, to locally override the request/response transforms, augment the
|
||||
|
||||
+3
-3
@@ -24,7 +24,7 @@ function $IntervalProvider() {
|
||||
* In tests you can use {@link ngMock.$interval#methods_flush `$interval.flush(millis)`} to
|
||||
* move forward by `millis` milliseconds and trigger any functions scheduled to run in that
|
||||
* time.
|
||||
*
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note**: Intervals created by this service must be explicitly destroyed when you are finished
|
||||
* with them. In particular they are not automatically destroyed when a controller's scope or a
|
||||
@@ -137,8 +137,8 @@ function $IntervalProvider() {
|
||||
promise = deferred.promise,
|
||||
iteration = 0,
|
||||
skipApply = (isDefined(invokeApply) && !invokeApply);
|
||||
|
||||
count = isDefined(count) ? count : 0,
|
||||
|
||||
count = isDefined(count) ? count : 0;
|
||||
|
||||
promise.then(null, null, fn);
|
||||
|
||||
|
||||
+1
-1
@@ -707,7 +707,7 @@ Parser.prototype = {
|
||||
var getter = getterFn(field, this.options, this.text);
|
||||
|
||||
return extend(function(scope, locals, self) {
|
||||
return getter(self || object(scope, locals), locals);
|
||||
return getter(self || object(scope, locals));
|
||||
}, {
|
||||
assign: function(scope, value, locals) {
|
||||
return setter(object(scope, locals), field, value, parser.text, parser.options);
|
||||
|
||||
+3
-3
@@ -16,9 +16,9 @@
|
||||
* asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
|
||||
*
|
||||
* <pre>
|
||||
* // for the purpose of this example let's assume that variables `$q` and `scope` are
|
||||
* // available in the current lexical scope (they could have been injected or passed in).
|
||||
*
|
||||
* // for the purpose of this example let's assume that variables `$q`, `scope` and `okToGreet`
|
||||
* // are available in the current lexical scope (they could have been injected or passed in).
|
||||
*
|
||||
* function asyncGreet(name) {
|
||||
* var deferred = $q.defer();
|
||||
*
|
||||
|
||||
+1
-1
@@ -632,7 +632,7 @@ function $RootScopeProvider(){
|
||||
|
||||
// `break traverseScopesLoop;` takes us to here
|
||||
|
||||
if(dirty && !(ttl--)) {
|
||||
if((dirty || asyncQueue.length) && !(ttl--)) {
|
||||
clearPhase();
|
||||
throw $rootScopeMinErr('infdig',
|
||||
'{0} $digest() iterations reached. Aborting!\n' +
|
||||
|
||||
+8
-8
@@ -275,7 +275,7 @@ function $SceDelegateProvider() {
|
||||
*
|
||||
* @description
|
||||
* Returns an object that is trusted by angular for use in specified strict
|
||||
* contextual escaping contexts (such as ng-html-bind-unsafe, ng-include, any src
|
||||
* contextual escaping contexts (such as ng-bind-html, ng-include, any src
|
||||
* attribute interpolation, any dom event binding attribute interpolation
|
||||
* such as for onclick, etc.) that uses the provided value.
|
||||
* See {@link ng.$sce $sce} for enabling strict contextual escaping.
|
||||
@@ -321,7 +321,7 @@ function $SceDelegateProvider() {
|
||||
*
|
||||
* @param {*} value The result of a prior {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}
|
||||
* call or anything else.
|
||||
* @returns {*} The value the was originally provided to {@link ng.$sceDelegate#methods_trustAs
|
||||
* @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#methods_trustAs
|
||||
* `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
|
||||
* `value` unchanged.
|
||||
*/
|
||||
@@ -502,8 +502,8 @@ function $SceDelegateProvider() {
|
||||
* It's important to remember that SCE only applies to interpolation expressions.
|
||||
*
|
||||
* If your expressions are constant literals, they're automatically trusted and you don't need to
|
||||
* call `$sce.trustAs` on them. (e.g.
|
||||
* `<div ng-html-bind-unsafe="'<b>implicitly trusted</b>'"></div>`) just works.
|
||||
* call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
|
||||
* `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
|
||||
*
|
||||
* Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
|
||||
* through {@link ng.$sce#methods_getTrusted $sce.getTrusted}. SCE doesn't play a role here.
|
||||
@@ -563,7 +563,7 @@ function $SceDelegateProvider() {
|
||||
* matched against the **entire** *normalized / absolute URL* of the resource being tested
|
||||
* (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
|
||||
* present on the RegExp (such as multiline, global, ignoreCase) are ignored.
|
||||
* - If you are generating your Javascript from some other templating engine (not
|
||||
* - If you are generating your JavaScript from some other templating engine (not
|
||||
* recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
|
||||
* remember to escape your regular expression (and be aware that you might need more than
|
||||
* one level of escaping depending on your templating engine and the way you interpolated
|
||||
@@ -580,7 +580,7 @@ function $SceDelegateProvider() {
|
||||
* ## Show me an example using SCE.
|
||||
*
|
||||
* @example
|
||||
<example module="mySceApp">
|
||||
<example module="mySceApp" deps="angular-sanitize.js">
|
||||
<file name="index.html">
|
||||
<div ng-controller="myAppController as myCtrl">
|
||||
<i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
|
||||
@@ -805,8 +805,8 @@ function $SceProvider() {
|
||||
*
|
||||
* @description
|
||||
* Delegates to {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}. As such,
|
||||
* returns an objectthat is trusted by angular for use in specified strict contextual
|
||||
* escaping contexts (such as ng-html-bind-unsafe, ng-include, any src attribute
|
||||
* returns an object that is trusted by angular for use in specified strict contextual
|
||||
* escaping contexts (such as ng-bind-html, ng-include, any src attribute
|
||||
* interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
|
||||
* that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
|
||||
* escaping.
|
||||
|
||||
+138
-32
@@ -248,6 +248,26 @@ angular.module('ngAnimate', ['ng'])
|
||||
* Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
|
||||
*
|
||||
*/
|
||||
.factory('$$animateReflow', ['$window', '$timeout', function($window, $timeout) {
|
||||
var requestAnimationFrame = $window.requestAnimationFrame ||
|
||||
$window.webkitRequestAnimationFrame ||
|
||||
function(fn) {
|
||||
return $timeout(fn, 10, false);
|
||||
};
|
||||
|
||||
var cancelAnimationFrame = $window.cancelAnimationFrame ||
|
||||
$window.webkitCancelAnimationFrame ||
|
||||
function(timer) {
|
||||
return $timeout.cancel(timer);
|
||||
};
|
||||
return function(fn) {
|
||||
var id = requestAnimationFrame(fn);
|
||||
return function() {
|
||||
cancelAnimationFrame(id);
|
||||
};
|
||||
};
|
||||
}])
|
||||
|
||||
.config(['$provide', '$animateProvider', function($provide, $animateProvider) {
|
||||
var noop = angular.noop;
|
||||
var forEach = angular.forEach;
|
||||
@@ -295,6 +315,10 @@ angular.module('ngAnimate', ['ng'])
|
||||
return classNameFilter.test(className);
|
||||
};
|
||||
|
||||
function async(fn) {
|
||||
return $timeout(fn, 0, false);
|
||||
}
|
||||
|
||||
function lookup(name) {
|
||||
if (name) {
|
||||
var matches = [],
|
||||
@@ -586,6 +610,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
//best to catch this early on to prevent any animation operations from occurring
|
||||
if(!node || !isAnimatableClassName(classes)) {
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
closeAnimation();
|
||||
return;
|
||||
}
|
||||
@@ -605,6 +631,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
//NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found.
|
||||
if (animationsDisabled(element, parentElement) || matches.length === 0) {
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
closeAnimation();
|
||||
return;
|
||||
}
|
||||
@@ -643,14 +671,17 @@ angular.module('ngAnimate', ['ng'])
|
||||
//animation do it's thing and close this one early
|
||||
if(animations.length === 0) {
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
fireDoneCallbackAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
var ONE_SPACE = ' ';
|
||||
//this value will be searched for class-based CSS className lookup. Therefore,
|
||||
//we prefix and suffix the current className value with spaces to avoid substring
|
||||
//lookups of className tokens
|
||||
var futureClassName = ' ' + currentClassName + ' ';
|
||||
var futureClassName = ONE_SPACE + currentClassName + ONE_SPACE;
|
||||
if(ngAnimateState.running) {
|
||||
//if an animation is currently running on the element then lets take the steps
|
||||
//to cancel that animation and fire any required callbacks
|
||||
@@ -658,12 +689,23 @@ angular.module('ngAnimate', ['ng'])
|
||||
cleanup(element);
|
||||
cancelAnimations(ngAnimateState.animations);
|
||||
|
||||
//in the event that the CSS is class is quickly added and removed back
|
||||
//then we don't want to wait until after the reflow to add/remove the CSS
|
||||
//class since both class animations may run into a race condition.
|
||||
//The code below will check to see if that is occurring and will
|
||||
//immediately remove the former class before the reflow so that the
|
||||
//animation can snap back to the original animation smoothly
|
||||
var isFullyClassBasedAnimation = isClassBased && !ngAnimateState.structural;
|
||||
var isRevertingClassAnimation = isFullyClassBasedAnimation &&
|
||||
ngAnimateState.className == className &&
|
||||
animationEvent != ngAnimateState.event;
|
||||
|
||||
//if the class is removed during the reflow then it will revert the styles temporarily
|
||||
//back to the base class CSS styling causing a jump-like effect to occur. This check
|
||||
//here ensures that the domOperation is only performed after the reflow has commenced
|
||||
if(ngAnimateState.beforeComplete) {
|
||||
if(ngAnimateState.beforeComplete || isRevertingClassAnimation) {
|
||||
(ngAnimateState.done || noop)(true);
|
||||
} else if(isClassBased && !ngAnimateState.structural) {
|
||||
} else if(isFullyClassBasedAnimation) {
|
||||
//class-based animations will compare element className values after cancelling the
|
||||
//previous animation to see if the element properties already contain the final CSS
|
||||
//class and if so then the animation will be skipped. Since the domOperation will
|
||||
@@ -671,8 +713,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
//will be invalid. Therefore the same string manipulation that would occur within the
|
||||
//DOM operation will be performed below so that the class comparison is valid...
|
||||
futureClassName = ngAnimateState.event == 'removeClass' ?
|
||||
futureClassName.replace(ngAnimateState.className, '') :
|
||||
futureClassName + ngAnimateState.className + ' ';
|
||||
futureClassName.replace(ONE_SPACE + ngAnimateState.className + ONE_SPACE, ONE_SPACE) :
|
||||
futureClassName + ngAnimateState.className + ONE_SPACE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,10 +722,12 @@ angular.module('ngAnimate', ['ng'])
|
||||
//(on addClass) or doesn't contain (on removeClass) the className being animated.
|
||||
//The reason why this is being called after the previous animations are cancelled
|
||||
//is so that the CSS classes present on the element can be properly examined.
|
||||
var classNameToken = ' ' + className + ' ';
|
||||
var classNameToken = ONE_SPACE + className + ONE_SPACE;
|
||||
if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) ||
|
||||
(animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
fireDoneCallbackAsync();
|
||||
return;
|
||||
}
|
||||
@@ -724,6 +768,10 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
|
||||
function invokeRegisteredAnimationFns(animations, phase, allAnimationFnsComplete) {
|
||||
phase == 'after' ?
|
||||
fireAfterCallbackAsync() :
|
||||
fireBeforeCallbackAsync();
|
||||
|
||||
var endFnName = phase + 'End';
|
||||
forEach(animations, function(animation, index) {
|
||||
var animationPhaseCompleted = function() {
|
||||
@@ -760,8 +808,30 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
}
|
||||
|
||||
function fireDOMCallback(animationPhase) {
|
||||
element.triggerHandler('$animate:' + animationPhase, {
|
||||
event : animationEvent,
|
||||
className : className
|
||||
});
|
||||
}
|
||||
|
||||
function fireBeforeCallbackAsync() {
|
||||
async(function() {
|
||||
fireDOMCallback('before');
|
||||
});
|
||||
}
|
||||
|
||||
function fireAfterCallbackAsync() {
|
||||
async(function() {
|
||||
fireDOMCallback('after');
|
||||
});
|
||||
}
|
||||
|
||||
function fireDoneCallbackAsync() {
|
||||
doneCallback && $timeout(doneCallback, 0, false);
|
||||
async(function() {
|
||||
fireDOMCallback('close');
|
||||
doneCallback && doneCallback();
|
||||
});
|
||||
}
|
||||
|
||||
//it is less complicated to use a flag than managing and cancelling
|
||||
@@ -785,9 +855,9 @@ angular.module('ngAnimate', ['ng'])
|
||||
if(isClassBased) {
|
||||
cleanup(element);
|
||||
} else {
|
||||
data.closeAnimationTimeout = $timeout(function() {
|
||||
data.closeAnimationTimeout = async(function() {
|
||||
cleanup(element);
|
||||
}, 0, false);
|
||||
});
|
||||
element.data(NG_ANIMATE_STATE, data);
|
||||
}
|
||||
}
|
||||
@@ -811,10 +881,10 @@ angular.module('ngAnimate', ['ng'])
|
||||
function cancelAnimations(animations) {
|
||||
var isCancelledFlag = true;
|
||||
forEach(animations, function(animation) {
|
||||
if(!animations.beforeComplete) {
|
||||
if(!animation.beforeComplete) {
|
||||
(animation.beforeEnd || noop)(isCancelledFlag);
|
||||
}
|
||||
if(!animations.afterComplete) {
|
||||
if(!animation.afterComplete) {
|
||||
(animation.afterEnd || noop)(isCancelledFlag);
|
||||
}
|
||||
});
|
||||
@@ -860,7 +930,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
}]);
|
||||
|
||||
$animateProvider.register('', ['$window', '$sniffer', '$timeout', function($window, $sniffer, $timeout) {
|
||||
$animateProvider.register('', ['$window', '$sniffer', '$timeout', '$$animateReflow',
|
||||
function($window, $sniffer, $timeout, $$animateReflow) {
|
||||
// Detect proper transitionend/animationend event names.
|
||||
var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
|
||||
|
||||
@@ -905,11 +976,13 @@ angular.module('ngAnimate', ['ng'])
|
||||
var parentCounter = 0;
|
||||
var animationReflowQueue = [];
|
||||
var animationElementQueue = [];
|
||||
var animationTimer;
|
||||
var cancelAnimationReflow;
|
||||
var closingAnimationTime = 0;
|
||||
var timeOut = false;
|
||||
function afterReflow(element, callback) {
|
||||
$timeout.cancel(animationTimer);
|
||||
if(cancelAnimationReflow) {
|
||||
cancelAnimationReflow();
|
||||
}
|
||||
|
||||
animationReflowQueue.push(callback);
|
||||
|
||||
@@ -918,15 +991,19 @@ angular.module('ngAnimate', ['ng'])
|
||||
animationElementQueue.push(element);
|
||||
|
||||
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
closingAnimationTime = Math.max(closingAnimationTime,
|
||||
(elementData.maxDelay + elementData.maxDuration) * CLOSING_TIME_BUFFER * ONE_SECOND);
|
||||
|
||||
var stagger = elementData.stagger;
|
||||
var staggerTime = elementData.itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0);
|
||||
|
||||
var animationTime = (elementData.maxDelay + elementData.maxDuration) * CLOSING_TIME_BUFFER;
|
||||
closingAnimationTime = Math.max(closingAnimationTime, (staggerTime + animationTime) * ONE_SECOND);
|
||||
|
||||
//by placing a counter we can avoid an accidental
|
||||
//race condition which may close an animation when
|
||||
//a follow-up animation is midway in its animation
|
||||
elementData.animationCount = animationCounter;
|
||||
|
||||
animationTimer = $timeout(function() {
|
||||
cancelAnimationReflow = $$animateReflow(function() {
|
||||
forEach(animationReflowQueue, function(fn) {
|
||||
fn();
|
||||
});
|
||||
@@ -947,11 +1024,11 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
animationReflowQueue = [];
|
||||
animationElementQueue = [];
|
||||
animationTimer = null;
|
||||
cancelAnimationReflow = null;
|
||||
lookupCache = {};
|
||||
closingAnimationTime = 0;
|
||||
animationCounter++;
|
||||
}, 10, false);
|
||||
});
|
||||
}
|
||||
|
||||
function closeAllAnimations(elements, count) {
|
||||
@@ -1042,13 +1119,13 @@ angular.module('ngAnimate', ['ng'])
|
||||
return parentID + '-' + extractElementNode(element).className;
|
||||
}
|
||||
|
||||
function animateSetup(element, className) {
|
||||
function animateSetup(element, className, calculationDecorator) {
|
||||
var cacheKey = getCacheKey(element);
|
||||
var eventCacheKey = cacheKey + ' ' + className;
|
||||
var stagger = {};
|
||||
var ii = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
|
||||
var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
|
||||
|
||||
if(ii > 0) {
|
||||
if(itemIndex > 0) {
|
||||
var staggerClassName = className + '-stagger';
|
||||
var staggerCacheKey = cacheKey + ' ' + staggerClassName;
|
||||
var applyClasses = !lookupCache[staggerCacheKey];
|
||||
@@ -1060,9 +1137,16 @@ angular.module('ngAnimate', ['ng'])
|
||||
applyClasses && element.removeClass(staggerClassName);
|
||||
}
|
||||
|
||||
/* the animation itself may need to add/remove special CSS classes
|
||||
* before calculating the anmation styles */
|
||||
calculationDecorator = calculationDecorator ||
|
||||
function(fn) { return fn(); };
|
||||
|
||||
element.addClass(className);
|
||||
|
||||
var timings = getElementAnimationDetails(element, eventCacheKey);
|
||||
var timings = calculationDecorator(function() {
|
||||
return getElementAnimationDetails(element, eventCacheKey);
|
||||
});
|
||||
|
||||
/* there is no point in performing a reflow if the animation
|
||||
timeout is empty (this would cause a flicker bug normally
|
||||
@@ -1094,7 +1178,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
classes : className + ' ' + activeClassName,
|
||||
timings : timings,
|
||||
stagger : stagger,
|
||||
ii : ii
|
||||
itemIndex : itemIndex
|
||||
});
|
||||
|
||||
return true;
|
||||
@@ -1139,7 +1223,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * ONE_SECOND;
|
||||
var startTime = Date.now();
|
||||
var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
|
||||
var ii = elementData.ii;
|
||||
var itemIndex = elementData.itemIndex;
|
||||
|
||||
var style = '', appliedStyles = [];
|
||||
if(timings.transitionDuration > 0) {
|
||||
@@ -1152,17 +1236,17 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
}
|
||||
|
||||
if(ii > 0) {
|
||||
if(itemIndex > 0) {
|
||||
if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
|
||||
var delayStyle = timings.transitionDelayStyle;
|
||||
style += CSS_PREFIX + 'transition-delay: ' +
|
||||
prepareStaggerDelay(delayStyle, stagger.transitionDelay, ii) + '; ';
|
||||
prepareStaggerDelay(delayStyle, stagger.transitionDelay, itemIndex) + '; ';
|
||||
appliedStyles.push(CSS_PREFIX + 'transition-delay');
|
||||
}
|
||||
|
||||
if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
|
||||
style += CSS_PREFIX + 'animation-delay: ' +
|
||||
prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, ii) + '; ';
|
||||
prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, itemIndex) + '; ';
|
||||
appliedStyles.push(CSS_PREFIX + 'animation-delay');
|
||||
}
|
||||
}
|
||||
@@ -1227,8 +1311,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
return style;
|
||||
}
|
||||
|
||||
function animateBefore(element, className) {
|
||||
if(animateSetup(element, className)) {
|
||||
function animateBefore(element, className, calculationDecorator) {
|
||||
if(animateSetup(element, className, calculationDecorator)) {
|
||||
return function(cancelled) {
|
||||
cancelled && animateClose(element, className);
|
||||
};
|
||||
@@ -1323,7 +1407,18 @@ angular.module('ngAnimate', ['ng'])
|
||||
},
|
||||
|
||||
beforeAddClass : function(element, className, animationCompleted) {
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'));
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'), function(fn) {
|
||||
|
||||
/* when a CSS class is added to an element then the transition style that
|
||||
* is applied is the transition defined on the element when the CSS class
|
||||
* is added at the time of the animation. This is how CSS3 functions
|
||||
* outside of ngAnimate. */
|
||||
element.addClass(className);
|
||||
var timings = fn();
|
||||
element.removeClass(className);
|
||||
return timings;
|
||||
});
|
||||
|
||||
if(cancellationMethod) {
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element);
|
||||
@@ -1340,7 +1435,18 @@ angular.module('ngAnimate', ['ng'])
|
||||
},
|
||||
|
||||
beforeRemoveClass : function(element, className, animationCompleted) {
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'));
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'), function(fn) {
|
||||
/* when classes are removed from an element then the transition style
|
||||
* that is applied is the transition defined on the element without the
|
||||
* CSS class being there. This is how CSS3 functions outside of ngAnimate.
|
||||
* http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */
|
||||
var klass = element.attr('class');
|
||||
element.removeClass(className);
|
||||
var timings = fn();
|
||||
element.attr('class', klass);
|
||||
return timings;
|
||||
});
|
||||
|
||||
if(cancellationMethod) {
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element);
|
||||
|
||||
Vendored
+32
-3
@@ -379,7 +379,7 @@ angular.mock.$LogProvider = function() {
|
||||
*
|
||||
* @example
|
||||
* <pre>
|
||||
* $log.log('Some Error');
|
||||
* $log.error('Some Error');
|
||||
* var first = $log.error.logs.unshift();
|
||||
* </pre>
|
||||
*/
|
||||
@@ -756,6 +756,36 @@ angular.mock.TzDate = function (offset, timestamp) {
|
||||
angular.mock.TzDate.prototype = Date.prototype;
|
||||
/* jshint +W101 */
|
||||
|
||||
// TODO(matias): remove this IMMEDIATELY once we can properly detect the
|
||||
// presence of a registered module
|
||||
var animateLoaded;
|
||||
try {
|
||||
angular.module('ngAnimate');
|
||||
animateLoaded = true;
|
||||
} catch(e) {}
|
||||
|
||||
if(animateLoaded) {
|
||||
angular.module('ngAnimate').config(['$provide', function($provide) {
|
||||
var reflowQueue = [];
|
||||
$provide.value('$$animateReflow', function(fn) {
|
||||
reflowQueue.push(fn);
|
||||
return angular.noop;
|
||||
});
|
||||
$provide.decorator('$animate', function($delegate) {
|
||||
$delegate.triggerReflow = function() {
|
||||
if(reflowQueue.length === 0) {
|
||||
throw new Error('No animation reflows present');
|
||||
}
|
||||
angular.forEach(reflowQueue, function(fn) {
|
||||
fn();
|
||||
});
|
||||
reflowQueue = [];
|
||||
};
|
||||
return $delegate;
|
||||
});
|
||||
}]);
|
||||
}
|
||||
|
||||
angular.mock.animate = angular.module('mock.animate', ['ng'])
|
||||
|
||||
.config(['$provide', function($provide) {
|
||||
@@ -1699,7 +1729,7 @@ angular.mock.$RootElementProvider = function() {
|
||||
* In addition, ngMock also extends various core ng services such that they can be
|
||||
* inspected and controlled in a synchronous manner within test code.
|
||||
*
|
||||
* {@installModule mocks}
|
||||
* {@installModule mock}
|
||||
*
|
||||
* <div doc-module-components="ngMock"></div>
|
||||
*
|
||||
@@ -1913,7 +1943,6 @@ angular.mock.clearDataCache = function() {
|
||||
};
|
||||
|
||||
|
||||
|
||||
if(window.jasmine || window.mocha) {
|
||||
|
||||
var currentSpec = null,
|
||||
|
||||
@@ -26,6 +26,15 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
|
||||
*
|
||||
* @scope
|
||||
* @priority 400
|
||||
* @param {string=} onload Expression to evaluate whenever the view updates.
|
||||
*
|
||||
* @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
|
||||
* $anchorScroll} to scroll the viewport after the view is updated.
|
||||
*
|
||||
* - If the attribute is not set, disable scrolling.
|
||||
* - If the attribute is set without value, enable scrolling.
|
||||
* - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
|
||||
* as an expression yields a truthy value.
|
||||
* @example
|
||||
<example module="ngViewExample" deps="angular-route.js" animations="true">
|
||||
<file name="index.html">
|
||||
|
||||
@@ -185,7 +185,7 @@ function $RouteProvider(){
|
||||
|
||||
path = path
|
||||
.replace(/([().])/g, '\\$1')
|
||||
.replace(/(\/)?:(\w+)([\?|\*])?/g, function(_, slash, key, option){
|
||||
.replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option){
|
||||
var optional = option === '?' ? option : null;
|
||||
var star = option === '*' ? option : null;
|
||||
keys.push({ name: key, optional: !!optional });
|
||||
@@ -370,7 +370,7 @@ function $RouteProvider(){
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
* Broadcasted before a route change. At this point the route services starts
|
||||
* resolving all of the dependencies needed for the route change to occurs.
|
||||
* resolving all of the dependencies needed for the route change to occur.
|
||||
* Typically this involves fetching the view template as well as any dependencies
|
||||
* defined in `resolve` route property. Once all of the dependencies are resolved
|
||||
* `$routeChangeSuccess` is fired.
|
||||
|
||||
@@ -84,4 +84,71 @@ describe('a', function() {
|
||||
|
||||
expect(jq.prototype.on).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
if (isDefined(window.SVGElement)) {
|
||||
describe('SVGAElement', function() {
|
||||
it('should prevent default action to be executed when href is empty', function() {
|
||||
var orgLocation = document.location.href,
|
||||
preventDefaultCalled = false,
|
||||
event,
|
||||
child;
|
||||
|
||||
element = $compile('<svg><a xlink:href="">empty link</a></svg>')($rootScope);
|
||||
child = element.children('a');
|
||||
|
||||
if (msie < 9) {
|
||||
|
||||
event = document.createEventObject();
|
||||
expect(event.returnValue).not.toBeDefined();
|
||||
child[0].fireEvent('onclick', event);
|
||||
expect(event.returnValue).toEqual(false);
|
||||
|
||||
} else {
|
||||
|
||||
event = document.createEvent('MouseEvent');
|
||||
event.initMouseEvent(
|
||||
'click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
|
||||
event.preventDefaultOrg = event.preventDefault;
|
||||
event.preventDefault = function() {
|
||||
preventDefaultCalled = true;
|
||||
if (this.preventDefaultOrg) this.preventDefaultOrg();
|
||||
};
|
||||
|
||||
child[0].dispatchEvent(event);
|
||||
|
||||
expect(preventDefaultCalled).toEqual(true);
|
||||
}
|
||||
|
||||
expect(document.location.href).toEqual(orgLocation);
|
||||
});
|
||||
|
||||
|
||||
it('should not link and hookup an event if xlink:href is present at compile', function() {
|
||||
var jq = jQuery || jqLite;
|
||||
element = jq('<svg><a xlink:href="bobby">hello@you</a></svg>');
|
||||
var linker = $compile(element);
|
||||
|
||||
spyOn(jq.prototype, 'on');
|
||||
|
||||
linker($rootScope);
|
||||
|
||||
expect(jq.prototype.on).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should not link and hookup an event if name is present at compile', function() {
|
||||
var jq = jQuery || jqLite;
|
||||
element = jq('<svg><a name="bobby">hello@you</a></svg>');
|
||||
var linker = $compile(element);
|
||||
|
||||
spyOn(jq.prototype, 'on');
|
||||
|
||||
linker($rootScope);
|
||||
|
||||
expect(jq.prototype.on).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -944,7 +944,8 @@ describe('input', function() {
|
||||
it('should validate email', function() {
|
||||
expect(EMAIL_REGEXP.test('a@b.com')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('a@b.museum')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('a@B.c')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@B.c')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('a@.b.c')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1439,6 +1439,12 @@ describe('$http', function() {
|
||||
$http.get('/url');
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it('should have seperate opbjects for defaults PUT and POST', function() {
|
||||
expect($http.defaults.headers.post).not.toBe($http.defaults.headers.put);
|
||||
expect($http.defaults.headers.post).not.toBe($http.defaults.headers.patch);
|
||||
expect($http.defaults.headers.put).not.toBe($http.defaults.headers.patch);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+10
-1
@@ -358,7 +358,7 @@ describe('parser', function() {
|
||||
forEach([2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 42, 99], function(pathLength) {
|
||||
it('should resolve nested paths of length ' + pathLength, function() {
|
||||
// Create a nested object {x2: {x3: {x4: ... {x[n]: 42} ... }}}.
|
||||
var obj = 42;
|
||||
var obj = 42, locals = {};
|
||||
for (var i = pathLength; i >= 2; i--) {
|
||||
var newObj = {};
|
||||
newObj['x' + i] = obj;
|
||||
@@ -371,6 +371,8 @@ describe('parser', function() {
|
||||
path += '.x' + i;
|
||||
}
|
||||
expect(scope.$eval(path)).toBe(42);
|
||||
locals['x' + pathLength] = 'not 42'
|
||||
expect(scope.$eval(path, locals)).toBe(42);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -938,6 +940,13 @@ describe('parser', function() {
|
||||
expect($parse('a.b')({a: {b: 0}}, {a: null})).toEqual(undefined);
|
||||
expect($parse('a.b.c')({a: null}, {a: {b: {c: 1}}})).toEqual(1);
|
||||
}));
|
||||
|
||||
it('should not use locals to resolve object properties', inject(function($parse) {
|
||||
expect($parse('a[0].b')({a: [ {b : 'scope'} ]}, {b : 'locals'})).toBe('scope');
|
||||
expect($parse('a[0]["b"]')({a: [ {b : 'scope'} ]}, {b : 'locals'})).toBe('scope');
|
||||
expect($parse('a[0][0].b')({a: [[{b : 'scope'}]]}, {b : 'locals'})).toBe('scope');
|
||||
expect($parse('a[0].b.c')({a: [ {b: {c: 'scope'}}] }, {b : {c: 'locals'} })).toBe('scope');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('literal', function() {
|
||||
|
||||
@@ -258,6 +258,31 @@ describe('Scope', function() {
|
||||
}));
|
||||
|
||||
|
||||
it('should prevent infinite loop when creating and resolving a promise in a watched expression', function() {
|
||||
module(function($rootScopeProvider) {
|
||||
$rootScopeProvider.digestTtl(10);
|
||||
});
|
||||
inject(function($rootScope, $q) {
|
||||
var d = $q.defer();
|
||||
|
||||
d.resolve('Hello, world.');
|
||||
$rootScope.$watch(function () {
|
||||
var $d2 = $q.defer();
|
||||
$d2.resolve('Goodbye.');
|
||||
$d2.promise.then(function () { });
|
||||
return d.promise;
|
||||
}, function () { return 0; });
|
||||
|
||||
expect(function() {
|
||||
$rootScope.$digest();
|
||||
}).toThrowMinErr('$rootScope', 'infdig', '10 $digest() iterations reached. Aborting!\n'+
|
||||
'Watchers fired in the last 5 iterations: []');
|
||||
|
||||
expect($rootScope.$$phase).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should not fire upon $watch registration on initial $digest', inject(function($rootScope) {
|
||||
var log = '';
|
||||
$rootScope.a = 1;
|
||||
|
||||
+356
-108
@@ -280,7 +280,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -298,7 +298,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-leave')).toBe(true);
|
||||
expect(child.hasClass('ng-leave-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -322,7 +322,7 @@ describe("ngAnimate", function() {
|
||||
$animate.move(child1, element, child2);
|
||||
$rootScope.$digest();
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(element.text()).toBe('21');
|
||||
}));
|
||||
@@ -336,7 +336,7 @@ describe("ngAnimate", function() {
|
||||
expect(child).toBeHidden();
|
||||
$animate.removeClass(child, 'ng-hide');
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-hide-remove')).toBe(true);
|
||||
expect(child.hasClass('ng-hide-remove-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -354,7 +354,7 @@ describe("ngAnimate", function() {
|
||||
expect(child).toBeShown();
|
||||
$animate.addClass(child, 'ng-hide');
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-hide-add')).toBe(true);
|
||||
expect(child.hasClass('ng-hide-add-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -374,7 +374,7 @@ describe("ngAnimate", function() {
|
||||
//enter
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(child.attr('class')).toContain('ng-enter');
|
||||
expect(child.attr('class')).toContain('ng-enter-active');
|
||||
@@ -385,7 +385,7 @@ describe("ngAnimate", function() {
|
||||
element.append(after);
|
||||
$animate.move(child, element, after);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(child.attr('class')).toContain('ng-move');
|
||||
expect(child.attr('class')).toContain('ng-move-active');
|
||||
@@ -394,14 +394,14 @@ describe("ngAnimate", function() {
|
||||
|
||||
//hide
|
||||
$animate.addClass(child, 'ng-hide');
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.attr('class')).toContain('ng-hide-add');
|
||||
expect(child.attr('class')).toContain('ng-hide-add-active');
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
|
||||
//show
|
||||
$animate.removeClass(child, 'ng-hide');
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.attr('class')).toContain('ng-hide-remove');
|
||||
expect(child.attr('class')).toContain('ng-hide-remove-active');
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -409,7 +409,7 @@ describe("ngAnimate", function() {
|
||||
//leave
|
||||
$animate.leave(child);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.attr('class')).toContain('ng-leave');
|
||||
expect(child.attr('class')).toContain('ng-leave-active');
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -435,7 +435,7 @@ describe("ngAnimate", function() {
|
||||
element.addClass('ng-hide');
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(element.text()).toBe('memento');
|
||||
}));
|
||||
@@ -455,7 +455,9 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.leave(child);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(child).toBeHidden(); //hides instantly
|
||||
|
||||
//lets change this to prove that done doesn't fire anymore for the previous hide() operation
|
||||
@@ -485,7 +487,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
//this is to verify that the existing style is appended with a semicolon automatically
|
||||
expect(child.attr('style')).toMatch(/width: 20px;.+?/i);
|
||||
@@ -504,6 +506,7 @@ describe("ngAnimate", function() {
|
||||
child.addClass('custom-delay ng-hide');
|
||||
$animate.removeClass(child, 'ng-hide');
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
$timeout.flush(2000);
|
||||
@@ -530,7 +533,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
expect(completed).toBe(false);
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
$timeout.flush();
|
||||
@@ -661,7 +664,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.animations) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'animationend', { timeStamp: Date.now() + 4000, elapsedTime: 4 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
@@ -686,7 +689,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.animations) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'animationend', { timeStamp: Date.now() + 6000, elapsedTime: 6 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
@@ -713,7 +716,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'animationend', { timeStamp : Date.now() + 20000, elapsedTime: 10 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
@@ -751,7 +754,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
|
||||
if($sniffer.animations) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-remove')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-remove-active')).toBe(true);
|
||||
}
|
||||
@@ -762,7 +765,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
|
||||
if($sniffer.animations) { //cleanup some pending animations
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-add')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-add-active')).toBe(true);
|
||||
browserTrigger(element,'animationend', { timeStamp: Date.now() + 2000, elapsedTime: 2 });
|
||||
@@ -806,7 +809,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).toMatch(/animation-delay: 0\.1\d*s/);
|
||||
@@ -823,7 +826,13 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
var expectFailure = true;
|
||||
try {
|
||||
$animate.triggerReflow();
|
||||
expectFailure = false;
|
||||
} catch(e) {}
|
||||
|
||||
expect(expectFailure).toBe(true);
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).not.toMatch(/animation-delay: 0\.1\d*s/);
|
||||
@@ -859,7 +868,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).toMatch(/animation-delay: 1\.1\d*s,\s*2\.1\d*s/);
|
||||
@@ -896,7 +905,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
@@ -920,7 +929,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
var now = Date.now();
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
|
||||
@@ -948,7 +957,6 @@ describe("ngAnimate", function() {
|
||||
|
||||
element.addClass('ng-hide');
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
$timeout.flush(0);
|
||||
expect(element).toBeShown();
|
||||
$animate.enabled(true);
|
||||
|
||||
@@ -957,7 +965,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
var now = Date.now();
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 3000, elapsedTime: 3 });
|
||||
@@ -985,7 +993,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
var now = Date.now();
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
|
||||
@@ -1014,7 +1022,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
if ($sniffer.transitions) {
|
||||
@@ -1039,7 +1047,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-remove')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-remove-active')).toBe(true);
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -1051,7 +1059,7 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element, 'ng-hide');
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-add')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-add-active')).toBe(true);
|
||||
}
|
||||
@@ -1092,7 +1100,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).toMatch(/transition-delay: 0\.1\d*s/);
|
||||
@@ -1109,7 +1117,14 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
var expectFailure = true;
|
||||
try {
|
||||
$animate.triggerReflow();
|
||||
expectFailure = false;
|
||||
} catch(e) {}
|
||||
|
||||
expect(expectFailure).toBe(true);
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).not.toMatch(/transition-delay: 0\.1\d*s/);
|
||||
@@ -1145,7 +1160,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toMatch(/transition-duration: 1\d*s,\s*3\d*s;/);
|
||||
expect(elements[0].attr('style')).not.toContain('transition-delay');
|
||||
@@ -1155,7 +1170,7 @@ describe("ngAnimate", function() {
|
||||
}));
|
||||
|
||||
|
||||
it("apply a closing timeout to close all pending transitions",
|
||||
it("should apply a closing timeout to close all pending transitions",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
@@ -1167,13 +1182,55 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.addClass(element, 'some-class');
|
||||
|
||||
$timeout.flush(10); //reflow
|
||||
$animate.triggerReflow(); //reflow
|
||||
expect(element.hasClass('some-class-add-active')).toBe(true);
|
||||
|
||||
$timeout.flush(7500); //closing timeout
|
||||
expect(element.hasClass('some-class-add-active')).toBe(false);
|
||||
}));
|
||||
|
||||
it("apply a closing timeout with respect to a staggering animation",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.entering-element.ng-enter',
|
||||
'-webkit-transition:5s linear all;' +
|
||||
'transition:5s linear all;');
|
||||
|
||||
ss.addRule('.entering-element.ng-enter-stagger',
|
||||
'-webkit-transition-delay:0.5s;' +
|
||||
'transition-delay:0.5s;');
|
||||
|
||||
element = $compile(html('<div></div>'))($rootScope);
|
||||
var kids = [];
|
||||
for(var i = 0; i < 5; i++) {
|
||||
kids.push(angular.element('<div class="entering-element"></div>'));
|
||||
$animate.enter(kids[i], element);
|
||||
}
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow(); //reflow
|
||||
expect(element.children().length).toBe(5);
|
||||
|
||||
for(var i = 0; i < 5; i++) {
|
||||
expect(kids[i].hasClass('ng-enter-active')).toBe(true);
|
||||
}
|
||||
|
||||
$timeout.flush(7500);
|
||||
|
||||
for(var i = 0; i < 5; i++) {
|
||||
expect(kids[i].hasClass('ng-enter-active')).toBe(true);
|
||||
}
|
||||
|
||||
//(stagger * index) + (duration + delay) * 150%
|
||||
$timeout.flush(9500); //0.5 * 4 + 5 * 1.5 = 9500;
|
||||
|
||||
for(var i = 0; i < 5; i++) {
|
||||
expect(kids[i].hasClass('ng-enter-active')).toBe(false);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
it("should not allow the closing animation to close off a successive animation midway",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
@@ -1189,12 +1246,12 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.addClass(element, 'some-class');
|
||||
|
||||
$timeout.flush(10); //reflow
|
||||
$animate.triggerReflow(); //reflow
|
||||
expect(element.hasClass('some-class-add-active')).toBe(true);
|
||||
|
||||
$animate.removeClass(element, 'some-class');
|
||||
|
||||
$timeout.flush(10); //second reflow
|
||||
$animate.triggerReflow(); //second reflow
|
||||
|
||||
$timeout.flush(7500); //closing timeout for the first animation
|
||||
expect(element.hasClass('some-class-remove-active')).toBe(true);
|
||||
@@ -1237,7 +1294,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
|
||||
@@ -1275,7 +1332,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('abc')).toBe(true);
|
||||
expect(element.hasClass('ng-enter')).toBe(true);
|
||||
expect(element.hasClass('ng-enter-active')).toBe(true);
|
||||
@@ -1289,7 +1346,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('xyz')).toBe(true);
|
||||
expect(element.hasClass('ng-enter')).toBe(true);
|
||||
expect(element.hasClass('ng-enter-active')).toBe(true);
|
||||
@@ -1317,7 +1374,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('one')).toBe(true);
|
||||
expect(element.hasClass('two')).toBe(true);
|
||||
expect(element.hasClass('ng-enter')).toBe(true);
|
||||
@@ -1439,6 +1496,81 @@ describe("ngAnimate", function() {
|
||||
expect(signature).toBe('AB');
|
||||
}));
|
||||
|
||||
it('should fire DOM callbacks on the element being animated',
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
if(!$sniffer.transitions) return;
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
ss.addRule('.klass-add', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
body.append($rootElement);
|
||||
|
||||
var steps = [];
|
||||
element.on('$animate:before', function(e, data) {
|
||||
steps.push(['before', data.className, data.event]);
|
||||
});
|
||||
|
||||
element.on('$animate:after', function(e, data) {
|
||||
steps.push(['after', data.className, data.event]);
|
||||
});
|
||||
|
||||
element.on('$animate:close', function(e, data) {
|
||||
steps.push(['close', data.className, data.event]);
|
||||
});
|
||||
|
||||
$animate.addClass(element, 'klass', function() {
|
||||
steps.push(['done', 'klass', 'addClass']);
|
||||
});
|
||||
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(steps.pop()).toEqual(['before', 'klass', 'addClass']);
|
||||
|
||||
$animate.triggerReflow();
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(steps.pop()).toEqual(['after', 'klass', 'addClass']);
|
||||
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
$timeout.flush(1);
|
||||
|
||||
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) {
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var parent = jqLite('<div></div>');
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(parent);
|
||||
body.append($rootElement);
|
||||
|
||||
var steps = [];
|
||||
element.on('$animate:before', function(e, data) {
|
||||
steps.push(['before', data.className, data.event]);
|
||||
});
|
||||
|
||||
element.on('$animate:after', function(e, data) {
|
||||
steps.push(['after', data.className, data.event]);
|
||||
});
|
||||
|
||||
$animate.enter(element, parent);
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(steps.shift()).toEqual(['before', 'ng-enter', 'enter']);
|
||||
expect(steps.shift()).toEqual(['after', 'ng-enter', 'enter']);
|
||||
}));
|
||||
|
||||
it("should fire a done callback when provided with no animation",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
@@ -1525,6 +1657,9 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
$animate.addClass(element, 'ng-hide'); //earlier animation cancelled
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
$timeout.flush();
|
||||
expect(signature).toBe('AB');
|
||||
}));
|
||||
@@ -1655,7 +1790,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
if($sniffer.transitions) {
|
||||
expect(element.hasClass('klass-add')).toBe(true);
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('klass')).toBe(true);
|
||||
expect(element.hasClass('klass-add-active')).toBe(true);
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 3000, elapsedTime: 3 });
|
||||
@@ -1670,7 +1805,7 @@ describe("ngAnimate", function() {
|
||||
if($sniffer.transitions) {
|
||||
expect(element.hasClass('klass-remove')).toBe(true);
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('klass')).toBe(false);
|
||||
expect(element.hasClass('klass-add')).toBe(false);
|
||||
expect(element.hasClass('klass-add-active')).toBe(false);
|
||||
@@ -1734,7 +1869,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('klass-add')).toBe(true);
|
||||
expect(element.hasClass('klass-add-active')).toBe(true);
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 11000, elapsedTime: 11 });
|
||||
@@ -1750,7 +1885,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('klass-remove')).toBe(true);
|
||||
expect(element.hasClass('klass-remove-active')).toBe(true);
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 11000, elapsedTime: 11 });
|
||||
@@ -1784,7 +1919,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('one-add')).toBe(true);
|
||||
expect(element.hasClass('two-add')).toBe(true);
|
||||
|
||||
@@ -1830,7 +1965,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('one-remove')).toBe(true);
|
||||
expect(element.hasClass('two-remove')).toBe(true);
|
||||
|
||||
@@ -1884,7 +2019,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -1908,7 +2043,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 9000, elapsedTime: 9 });
|
||||
@@ -1918,41 +2053,6 @@ describe("ngAnimate", function() {
|
||||
}));
|
||||
|
||||
|
||||
it("should not set the transition property flag if only CSS animations are used",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.animations) return;
|
||||
|
||||
ss.addRule('.sleek-animation.ng-enter', '-webkit-animation: my_animation 2s linear;' +
|
||||
'animation: my_animation 2s linear');
|
||||
|
||||
ss.addRule('.trans.ng-enter', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all');
|
||||
|
||||
var propertyKey = ($sniffer.vendorPrefix == 'Webkit' ? '-webkit-' : '') + 'transition-property';
|
||||
|
||||
var element = html($compile('<div>...</div>')($rootScope));
|
||||
var child = $compile('<div class="skeep-animation">...</div>')($rootScope);
|
||||
child.css(propertyKey,'background-color');
|
||||
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 2000, elapsedTime: 2 });
|
||||
|
||||
expect(child.css(propertyKey)).toBe('background-color');
|
||||
child.remove();
|
||||
|
||||
child = $compile('<div class="sleek-animation">...</div>')($rootScope);
|
||||
child.attr('class','trans');
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(child.css(propertyKey)).not.toBe('background-color');
|
||||
}));
|
||||
|
||||
|
||||
it("should skip animations if the browser does not support CSS3 transitions and CSS3 animations",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer) {
|
||||
|
||||
@@ -1997,9 +2097,8 @@ describe("ngAnimate", function() {
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.flush(10);
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
|
||||
@@ -2033,7 +2132,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
//this is added/removed right away otherwise
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
}
|
||||
@@ -2073,7 +2172,7 @@ describe("ngAnimate", function() {
|
||||
$animate.leave(child);
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter-active')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -2268,7 +2367,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
var empty = true;
|
||||
try {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
empty = false;
|
||||
}
|
||||
catch(e) {}
|
||||
@@ -2294,7 +2393,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
@@ -2354,7 +2453,7 @@ describe("ngAnimate", function() {
|
||||
expect(animationState).toBe('enter');
|
||||
if($sniffer.transitions) {
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
}
|
||||
|
||||
@@ -2371,7 +2470,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.addClass(child, 'something');
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(animationState).toBe('addClass');
|
||||
if($sniffer.transitions) {
|
||||
@@ -2390,7 +2489,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
|
||||
it("should wait until a queue of animations are complete before performing a reflow",
|
||||
inject(function($rootScope, $compile, $timeout,$sniffer) {
|
||||
inject(function($rootScope, $compile, $timeout, $sniffer, $animate) {
|
||||
|
||||
if(!$sniffer.transitions) return;
|
||||
|
||||
@@ -2402,7 +2501,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$rootScope.$digest();
|
||||
expect(element[0].querySelectorAll('.ng-enter-active').length).toBe(0);
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element[0].querySelectorAll('.ng-enter-active').length).toBe(5);
|
||||
|
||||
forEach(element.children(), function(kid) {
|
||||
@@ -2463,7 +2562,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
|
||||
it("should disable all child animations on structural animations until the first reflow has passed", function() {
|
||||
it("should disable all child animations on structural animations until the post animation timeout has passed", function() {
|
||||
var intercepted;
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.animated', function() {
|
||||
@@ -2577,7 +2676,6 @@ describe("ngAnimate", function() {
|
||||
$animate.enter(kid, element);
|
||||
}
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
//called three times since the classname is the same
|
||||
expect(count).toBe(2);
|
||||
@@ -2591,7 +2689,6 @@ describe("ngAnimate", function() {
|
||||
}
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
expect(count).toBe(20);
|
||||
});
|
||||
@@ -2659,12 +2756,51 @@ describe("ngAnimate", function() {
|
||||
expect(element.hasClass('green')).toBe(false);
|
||||
expect(element.hasClass('red')).toBe(false);
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(element.hasClass('green')).toBe(true);
|
||||
expect(element.hasClass('red')).toBe(true);
|
||||
}));
|
||||
|
||||
it("should avoid mixing up substring classes during add and remove operations", function() {
|
||||
var currentAnimation, currentFn;
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.on', function() {
|
||||
return {
|
||||
beforeAddClass : function(element, className, done) {
|
||||
currentAnimation = 'addClass';
|
||||
currentFn = done;
|
||||
return function(cancelled) {
|
||||
currentAnimation = cancelled ? null : currentAnimation;
|
||||
}
|
||||
},
|
||||
beforeRemoveClass : function(element, className, done) {
|
||||
currentAnimation = 'removeClass';
|
||||
currentFn = done;
|
||||
return function(cancelled) {
|
||||
currentAnimation = cancelled ? null : currentAnimation;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
var element = $compile('<div class="animation-enabled only"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$animate.addClass(element, 'on');
|
||||
expect(currentAnimation).toBe('addClass');
|
||||
currentFn();
|
||||
|
||||
currentAnimation = null;
|
||||
|
||||
$animate.removeClass(element, 'on');
|
||||
$animate.addClass(element, 'on');
|
||||
|
||||
expect(currentAnimation).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should enable and disable animations properly on the root element', function() {
|
||||
var count = 0;
|
||||
@@ -2770,17 +2906,17 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'base-class one two');
|
||||
|
||||
//still true since we're before the reflow
|
||||
expect(element.hasClass('base-class')).toBe(true);
|
||||
expect(element.hasClass('base-class')).toBe(false);
|
||||
|
||||
//this will cancel the remove animation
|
||||
$animate.addClass(element, 'base-class one two');
|
||||
|
||||
//the cancellation was a success and the class was added right away
|
||||
//since there was no successive animation for the after animation
|
||||
expect(element.hasClass('base-class')).toBe(true);
|
||||
expect(element.hasClass('base-class')).toBe(false);
|
||||
|
||||
//the reflow...
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
//the reflow DOM operation was commenced but it ran before so it
|
||||
//shouldn't run agaun
|
||||
@@ -2814,9 +2950,10 @@ describe("ngAnimate", function() {
|
||||
node._setAttribute(prop, val);
|
||||
};
|
||||
|
||||
expect(capturedProperty).toBe('none');
|
||||
$animate.addClass(element, 'trigger-class');
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(capturedProperty).not.toBe('none');
|
||||
}));
|
||||
@@ -2843,7 +2980,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
expect(node.style[animationKey]).toContain('none');
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(node.style[animationKey]).not.toContain('none');
|
||||
}));
|
||||
@@ -2888,7 +3025,7 @@ describe("ngAnimate", function() {
|
||||
expect(element[0].style[prop]).toContain('none');
|
||||
expect($window.getComputedStyle(element[0])[prop + 'Duration']).toBe('0s');
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2907,7 +3044,7 @@ describe("ngAnimate", function() {
|
||||
$animate.leave(element);
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 0.50999999991 });
|
||||
|
||||
@@ -2931,7 +3068,7 @@ describe("ngAnimate", function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $compile, $rootElement, $document, $timeout, $templateCache, $sniffer) {
|
||||
inject(function($rootScope, $compile, $rootElement, $document, $timeout, $templateCache, $sniffer, $animate) {
|
||||
if(!$sniffer.transitions) return;
|
||||
|
||||
$templateCache.put('item-template', 'item: #{{ item }} ');
|
||||
@@ -2950,7 +3087,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.tpl = 'item-template';
|
||||
$rootScope.items = [1,2,3];
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(capturedAnimation).toBe('enter');
|
||||
expect(element.text()).toContain('item: #1');
|
||||
@@ -2962,7 +3099,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$rootScope.items = [];
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(capturedAnimation).toBe('leave');
|
||||
});
|
||||
@@ -3017,5 +3154,116 @@ describe("ngAnimate", function() {
|
||||
expect(leaveDone).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should respect the most relevant CSS transition property if defined in multiple classes',
|
||||
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.base-class', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all;');
|
||||
|
||||
ss.addRule('.base-class.on', '-webkit-transition:5s linear all;' +
|
||||
'transition:5s linear all;');
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = $compile('<div class="base-class"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var ready = false;
|
||||
$animate.addClass(element, 'on', function() {
|
||||
ready = true;
|
||||
});
|
||||
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 1 });
|
||||
$timeout.flush(1);
|
||||
expect(ready).toBe(false);
|
||||
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 5 });
|
||||
$timeout.flush(1);
|
||||
expect(ready).toBe(true);
|
||||
|
||||
ready = false;
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
ready = true;
|
||||
});
|
||||
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 1 });
|
||||
$timeout.flush(1);
|
||||
expect(ready).toBe(true);
|
||||
}));
|
||||
|
||||
it('should not apply a transition upon removal of a class that has a transition',
|
||||
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.base-class.on', '-webkit-transition:5s linear all;' +
|
||||
'transition:5s linear all;');
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = $compile('<div class="base-class on"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var ready = false;
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
ready = true;
|
||||
});
|
||||
|
||||
$timeout.flush(1);
|
||||
expect(ready).toBe(true);
|
||||
}));
|
||||
|
||||
it('should avoid skip animations if the same CSS class is added / removed synchronously before the reflow kicks in',
|
||||
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.water-class', '-webkit-transition:2s linear all;' +
|
||||
'transition:2s linear all;');
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = $compile('<div class="water-class on"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var signature = '';
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
signature += 'A';
|
||||
});
|
||||
$animate.addClass(element, 'on', function() {
|
||||
signature += 'B';
|
||||
});
|
||||
|
||||
$timeout.flush(1);
|
||||
expect(signature).toBe('AB');
|
||||
|
||||
signature = '';
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
signature += 'A';
|
||||
});
|
||||
$animate.addClass(element, 'on', function() {
|
||||
signature += 'B';
|
||||
});
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
signature += 'C';
|
||||
});
|
||||
|
||||
$timeout.flush(1);
|
||||
expect(signature).toBe('AB');
|
||||
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2000 });
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(signature).toBe('ABC');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user