Compare commits

...

73 Commits

Author SHA1 Message Date
Brian Ford c086f831fb docs(changelog): release notes for 1.2.13 2014-02-14 16:41:02 -08:00
Igor Minar fab2d3c92f style(animate): remove ws 2014-02-14 16:31:57 -08:00
Matias Niemelä 4f84f6b3e4 fix($animate): ensure $animate doesn't break natural CSS transitions
BREAKING CHANGE: ngClass and {{ class }} will now call the `setClass`
animation callback instead of addClass / removeClass when both a
addClass/removeClass operation is being executed on the element during the animation.

Please include the setClass animation callback as well as addClass and removeClass within
your JS animations to work with ngClass and {{ class }} directives.

Closes #6019
2014-02-14 16:30:48 -08:00
Matias Niemelä cf5e463abd pref($animate): only trigger DOM callbacks if registered on the element being animated
BREAKING CHANGE: Both the `$animate:before` and `$animate:after` DOM events must be now
registered prior to the $animate operation taking place. The `$animate:close` event
can be registered anytime afterwards.

DOM callbacks used to fired for each and every animation operation that occurs within the
$animate service provided in the ngAnimate module. This may end up slowing down an
application if 100s of elements are being inserted into the page. Therefore after this
change callbacks are only fired if registered on the element being animated.
2014-02-14 16:28:56 -08:00
Matias Niemelä f288b8f010 pref($animate): group all asynchronous requests into one shared buffer 2014-02-14 16:28:34 -08:00
Matias Niemelä b7e4e92014 chore(jqLite): expose the _data lookup function to angular.element 2014-02-14 15:49:08 -08:00
Caitlin Potter 31c450bcee fix($compile) support templates with table content root nodes
If the first element in a template is a <tr>, <th>, <td>, or <tbody> tag,
the HTML compiler will ensure that the template is wrapped in a <table>
element so that the table content is not discarded.

Closes #2848
Closes #1459
Closes #3647
Closes #3241
2014-02-14 14:42:55 -05:00
Tim Whitbeck a9fcb0d0fc fix(input): don't apply textInput to <input type="file">
textInput shouldn't be applied to file inputs to ease writing of custom file input directives.

This change prevents file inputs from instantiating the text input parser/formatter pipelines.

Closes #6247
Closes #6231
2014-02-13 16:43:18 -05:00
Naomi Black a3846ab837 Merge pull request #6245 from brianhall/master
Google logo in AngularJS.exports to vector format.
2014-02-13 10:35:05 -08:00
Brian Hall 1953b0bed9 docs(logo): change logo to vector format in .eps file
Browser: Other
Component: docs
Regression: no

Closes issue #6092
2014-02-13 09:51:11 -08:00
Caitlin Potter 2b73027136 fix(input): setViewValue on compositionend
Because of a4e6d962, model is not updated on input/change between the
compositionstart and compositionend events. Unfortunately, the compositionend
event does not always happen prior to an input/change event.

This changeset calls the listener function to update the model after a
compositionend event is received.

Closes #6058
Closes #5433
2014-02-12 20:28:13 -05:00
Caitlin Potter 1079105443 docs($interpolate): fix link to $interpolateProvider#endSymbol
The markup here was missing the methods_ prefix and behaved incorrectly.

Closes #5802
2014-02-12 14:41:37 -05:00
Caitlin Potter d119e36302 docs($location): fix link to $rootScope.Scope.$on
Previously missing the methods_ prefix.

Closes #5798
2014-02-12 14:39:41 -05:00
James Kyle 98b2f8ef18 docs(currencyFilter): added missing line break in currency doc ptor test
Closes #6229
2014-02-12 10:05:35 -05:00
Stéphane Reynaud e7ab857ddb docs(guide/$location): correct link to HTML5 draft section 5.5 (history api)
Previous link url is no longer served, responds with bad link (error 404). This change corrects the
URL to point to section 5.5 of the draft. The old URL appears to have been removed from service in
2012.

Corrects the link to "History API"

Closes #6225
2014-02-12 08:26:21 -05:00
Mathieu Tricoire 46cba2e05d docs(input): document NgModelController.$isEmpty parameters / return value
Closes #6224
2014-02-12 08:19:24 -05:00
Jason Schapiro 72894f0dd2 docs(tutorial): inject phonecapApp module into unit test
When I was reading this doc I was thinking "but what about phonecatApp?" and when I looked in the
file from the step-11 branch there it is. Should be reflected in the docs as well

Closes #6209
2014-02-11 21:29:47 -05:00
Caitlin Potter 760f49de10 chore(dependencies): upgrade kriskowal/q to version ~1.0.0
CI builds on travis occasionally freak out because of the recursive use of process.nextTick, which
has been deprecated in Node relatively recently, to be replaced with setImmediate. Unfortunately,
this change does not resolve the issue. However, it does not hurt, either.

Closes #6161
2014-02-11 18:54:38 -05:00
Jesse Palmer 686b13bf60 docs(core): add closing tag to ngApp directive example
added missing closing tag to ngApp example.

Closes #6066
2014-02-11 18:42:16 -05:00
Evgeniy Tkachenko 56cc7bcc98 docs(jqLite): link to jQuery.fn.bind/unbind docs rather than jQuery.fn.on/off docs
Сorrect link.

Closes #6171
2014-02-11 18:23:25 -05:00
Caitlin Potter b4eed8ad94 feat(filterFilter): support deeply nested predicate objects
Due to 339a165, it became impossible to filter nested properties of an object using the filterFilter.
A proposed solution to this was to enable the use of nested predicate objects. This change enables the
use of these nested predicate objects.

Example:

```html
<div ng-repeat="it in items | filter:{ address: { country: 'Canuckistan'}}"></div>
```

Or

```js
$filter('filter')(items, { address: { country: 'Canuckistan' } });
```

Closes #6215
Related to #6009
2014-02-11 17:08:41 -05:00
Daniel Tabuenca 08793a690a refactor(ngTransclude): use transclusion function passed in to link
Since we now pass in the transclusion function directly to the link function, we no longer need
the old scheme whereby we saved the transclude function injected into the controller for later
use in during linking.

Additionally, this change may aid in correcting a memory leak of detached DOM nodes (see #6181
for details).

This commit removes the controller and simplifies ngTransclude.

Closes #5375
Closes #6181
2014-02-11 14:57:56 -05:00
Stéphane Reynaud ef4bf8c77c docs(guide/index): replace "shold" to "should"
Replace "shold" to "should"

Closes #6216
2014-02-11 11:46:36 -05:00
Igor Minar b6ab826c4b style(guide): remove ws 2014-02-10 17:09:35 -08:00
Jeremy Likness 71f974b459 docs(guide): add new resource links
Added a link to 10 reasons to use and online courses for Angular

Closes #6194
2014-02-10 17:09:34 -08:00
James Wagoner a68624444a docs(ngSubmit): ngSubmit also works with the data-action/x-action attributes
The documentation states only the "action" attribute triggers this, which is incorrect. When using
the attribute "data-action" (as for AJAX control, attempting to bypass the "action" attribute but
still make it obvious what its for), Angular thinks this is also classified as "action" and
continues with the page submission.

Closes #6196
2014-02-10 19:35:22 -05:00
Igor Minar 945fc1a4bc style(guide/concepts): remove ws 2014-02-10 16:19:10 -08:00
Sequoia McDowell ec900cabfc docs(guide/concepts): removing confusing use of hoisting
Closes #6207
2014-02-10 16:19:10 -08:00
Mark Miyashita f99fe799e2 docs(faq): add link to MIT license
Closes #6197
2014-02-10 15:58:34 -08:00
Caitlin Potter e7338d3f27 fix($compile): ensure element transclusion directives are linked with comment element
This corrects a complicated compiler issue, described in detail below:

Previously, if an element transclusion directive contained an asynchronous directive whose template
contained another element transclusion directive, the inner element transclusion directive would be
linked with the element, rather than the expected comment node.

An example manifestation of this bug would look like so:

```html
<div ng-repeat="i in [1,2,3,4,5]">
  <div my-directive>
  </div>
</div>
```

`my-directive` would be a replace directive, and its template would contain another element
transclusion directive, like so:

```html
<div ng-if="true">{{i}}</div>
```

ngIf would be linked with this template content, rather than the comment node, and the template element
would be attached to the DOM, rather than the comment. As a result, this caused ng-if to duplicate the
template when its expression evaluated to true.

Closes #6006
Closes #6101
2014-02-10 18:41:28 -05:00
Sequoia McDowell 2dfbc083c5 docs(concepts): Remove pointless * 1s
Closes #6206
2014-02-10 15:15:30 -08:00
Victor Berchet 27613fd500 docs(guide/scope): fix a typo
Signed-off-by: Caitlin Potter <caitpotter88@gmail.com>

Closes #6202
2014-02-10 18:09:03 -05:00
Julie e645f7cae1 refactor(testing): split travis end to end tests into separate jobs for jquery and jqlite
Closes #6159
2014-02-07 20:41:39 -08:00
Julie ad275b2265 refactor(doc): separate end to end tests into jquery and jqlite files 2014-02-07 20:41:11 -08:00
Julie 600e6218fe chore(testing): switch Jenkins to test e2e only on chrome
End to end tests will continue to be run on Safari and Firefox on Travis.

Closes #6187
2014-02-07 20:23:26 -08:00
jenkins 5218c7bbdc chore(release): update cdn version 2014-02-07 14:38:15 -08:00
Matias Niemelä 5cc5cc13b9 docs(changelog): release notes for 1.2.12 2014-02-07 17:00:28 -05:00
Igor Minar d5c7ef0f78 revert: refactor(mocks): simplify the inject implementation
This reverts commit 64d58a5b52.

For some weird reason this is causing regressions at Google.
I'm not sure why and I'm running out of time to investigate, so I'm taking
a safe route here and reverting the commit since it's just a refactoring.
2014-02-07 12:14:32 -08:00
Brian Ford 84fd3a18a3 docs(contributing): add code of conduct 2014-02-07 11:16:17 -08:00
Tobias Bosch fcf4393680 chore(build): Update closure i18n integration
Use git repo as source and use q-io instead of q-fs
2014-02-06 17:53:37 -08:00
Julie 16301bed28 chore(testing): de-flake a ngHref test for navigating away from the Angular page 2014-02-06 17:03:39 -08:00
Kasparas Galdikas 95be253fe5 fix($locale): Minor grammar amends for locale_lt
Closes #6164
2014-02-06 16:11:29 -08:00
asif22 bf4b0dbd46 docs(misc): fix typo in "getting started" docs
changed "building and application" to "building an application"

Closes #6156
2014-02-06 11:15:13 -05:00
sunderls 95d119ebb2 docs(injector): correct typo in example
$provide misused into $provider

maybe this should be corrected I think

Closes #6146
2014-02-06 10:44:31 -05:00
Igor Minar e609239fab chore(travis): remove double bower install to test if it's still needed
We did this due to travis-ci/travis-ci#1293 but since it's possible that this hack is not needed, I'm removing it.

If it turns out that we do need it still then we should ping the travis issue and revert this commit
2014-02-06 02:31:23 -08:00
Matias Niemelä 4224cd5182 fix(mocks): rename mock.animate to ngAnimateMock and ensure it contains all test helper code for ngAnimate
Closes #5822
Closes #5917
2014-02-06 01:22:14 -05:00
Matias Niemelä 906fdad0f9 fix(mocks): remove usage of $animate.flushNext in favour of queing
The flushNext method of testing is difficult and highly coupled with the behavior
of ngAnimate's $animate workflow. It is much better instead to just queue all
$animate animation calls into a queue collection which is available on the $animate
service when mock.animate is included as a module within test code.
2014-02-06 01:21:41 -05:00
Franziskus Domig a8c1d9c978 docs(angular.forEach): add missing space in test
Closes #6130
2014-02-05 16:25:03 -08:00
Julie 8829a2a86d chore(testing): fix Jenkins breakage due to test directory already being present 2014-02-05 16:13:06 -08:00
Julie 84467d8697 refactor(testing): run end to end tests on separate browsers in parallel 2014-02-05 15:40:16 -08:00
Julie 0e85ca9ddb chore(testing): run end to end tests on firefox and safari as well as chrome
Update the Travis and Jenkins configs to run protractor tests on Safari and Firefox as well,
and make the Travis tests run output XML and turn off color.

Fix tests which were failing in Firefox due to clear() not working as expected.

Fix tests which were failing in Safari due to SafariDriver not understanding the minus key,
and disable tests which SafariDriver has no support for.
2014-02-05 15:39:59 -08:00
Julie e7face4728 chore(end2end): remove old references to the scenario runner and update to point to protractor 2014-02-05 15:39:46 -08:00
Tobias Bosch a29bff1c98 chore(build): remove MINERR_ASSET from source map
The actual change happened in ng-closure-runner.
The change here just includes the new version.

Closes #4675
2014-02-05 15:34:56 -08:00
Hopiu 95522cc11f docs(forEach): correct spelling error
Closes #6124
2014-02-05 09:58:26 -05:00
Nicolas Leger c5f69e3f64 chore(ngdoc): fix misspellling of Naturally in sortVersionsNatrually
Corrects "sortVersionsNatrually" method name in `ngdoc.js` in "sortVersionsNaturally"
2014-02-04 16:43:39 -08:00
Daniel Luxemburg dd24c78373 fix(ngMock): return false from mock $interval.cancel() when no argument is supplied
Closes #6103.
Closed #6099.
2014-02-04 16:41:25 -08:00
Caitlin Potter 36d37c0e38 fix(jqLite): trim HTML string in jqLite constructor
jQuery will construct DOM nodes containing leading whitespace. Prior to this change, jqLite would
throw a nosel minErr due to the first character of the string not being '<'. This change corrects
this behaviour by trimming the element string in jqLite constructor before testing for '<'.

Closes #6053
2014-02-04 16:39:52 -08:00
Caitlin Potter 24699ee8f0 fix($http): ignore xhr.responseType setter exception if value is "json"
WebKit added support for the json responseType value on 09/03/2013
https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are known to throw when
setting the value "json" as the response type. Other older browsers implementing the responseType.
Other browsers with infrequent update cycles may also be affected.

The json responseType value can be ignored if not supported, because JSON payloads are parsed on the
client-side regardless.

Closes #6115
Closes #6122
2014-02-04 19:34:31 -05:00
GiffenGood aa6a0e3fc6 docs(log.js): param debugEnabled is a boolean and not a string 2014-02-04 16:26:36 -08:00
Tobias Bosch 8761ddc0e3 chore(release): be able to release any commit
The version information is now stored only in the tags.
By this we are able to release commits in the past, which
have already been tested, so we don't need a code freeze
or run tests any more. This is also the first step for
letting Travis do the releases in the future.

The package.json now contains the new
property 'branchVersion' that defines which tags are
valid on this branch.

Closes #6116
2014-02-04 16:20:22 -08:00
Caitlin Potter 058842ad04 revert: "fix($http): ignore xhr.responseType setter exception if value is "json""
This reverts commit 431bad0183.
2014-02-04 19:09:53 -05:00
Caitlin Potter 431bad0183 fix($http): ignore xhr.responseType setter exception if value is "json"
WebKit added support for the json responseType value on 09/03/2013
https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are known to throw when
setting the value "json" as the response type. Other older browsers implementing the responseType.
Other browsers with infrequent update cycles may also be affected.

The json responseType value can be ignored if not supported, because JSON payloads are parsed on the
client-side regardless.

Closes #6115
Closes #6122
2014-02-04 18:46:54 -05:00
Caitlin Potter 5850e61c82 docs(CHANGELOG): add breaking change notice for 1.2.9 $http default headers change
Add a mention for the slightly breaking change introduced in 1.2.9.

Closes #6022
2014-02-04 10:59:44 -05:00
Thomas Belin d2e4e49986 fix(ngResource): don't filter "$"-prefixed properties from ngResource requests/responses
ngResource no longer filters properties prefixed with a single "$" character from requests or
responses, correcting a regression introduced in 1.2.6 (cb29632a) which caused shallowCopy and
shallowClearAndCopy to ignore properties prefixed with a single "$".

Closes #5666
Closes #6080
Closes #6033
2014-02-04 10:51:24 -05:00
Kamil Pekala 0da6cc9118 docs($compile): fixed syntax error.
"how to" was written twice in a row.

Closes #6110
2014-02-04 08:45:48 -05:00
John Kurlak cc60ba1f35 docs($q): fixed grammatical error
"Promises" should be of the possessive form.

Closes #6082
2014-02-04 00:00:31 -08:00
Igor Minar 64d58a5b52 refactor(mocks): simplify the implementation 2014-02-03 22:41:18 -08:00
Wes Alvaro 3bf4390339 fix(mocks): always call functions injected with inject with this set to the current spec
Currently when a function is injected inside of a test we set the context to undefined which
is a bug.

Closes #6102
2014-02-03 22:25:30 -08:00
gdi2290 e7ac7aa43b chore(Gruntfile.js, package.json): use load-grunt-tasks and move grunt-contrib-jshint into devDependencies
Closes #6085
2014-02-03 19:25:47 -05:00
PatrickJS 37781ed145 style(License): update copyright year
Closes #6090
2014-02-03 19:19:29 -05:00
Igor Minar 19ba6510d0 chore(ngClass): remove debugger statement from an e2e test 2014-02-03 11:43:14 -08:00
jenkins bc5ceee275 chore(release): update cdn version 2014-02-03 10:03:06 -08:00
jenkins 106af49258 chore(release): start v1.2.12 (1.2.12) 2014-02-03 09:41:17 -08:00
77 changed files with 1619 additions and 825 deletions
+7 -4
View File
@@ -5,7 +5,12 @@ node_js:
env:
matrix:
- JOB=unit
- JOB=e2e
- JOB=e2e BROWSER=chrome JQVERSION=jqlite
- JOB=e2e BROWSER=firefox JQVERSION=jqlite
- JOB=e2e BROWSER=safari JQVERSION=jqlite
- JOB=e2e BROWSER=chrome JQVERSION=jquery
- JOB=e2e BROWSER=firefox JQVERSION=jquery
- JOB=e2e BROWSER=safari JQVERSION=jquery
global:
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
@@ -16,9 +21,7 @@ before_script:
- mkdir -p $LOGS_DIR
- ./lib/sauce/sauce_connect_setup.sh
- npm install -g grunt-cli
- grunt bower
- grunt bower
- grunt package-without-bower
- grunt package
- ./scripts/travis/wait_for_browser_provider.sh
script:
+140
View File
@@ -1,3 +1,119 @@
<a name="1.2.13"></a>
# 1.2.13 romantic-transclusion (2014-02-14)
## Bug Fixes
- **$animate:** ensure $animate doesn't break natural CSS transitions
([4f84f6b3](https://github.com/angular/angular.js/commit/4f84f6b3e4210ae1eb14728a46d43dd961700a0c),
[#6019](https://github.com/angular/angular.js/issues/6019))
- **$compile:**
- ensure element transclusion directives are linked with comment element
([e7338d3f](https://github.com/angular/angular.js/commit/e7338d3f27e8824196136a18e1c3e0fcf51a0e28),
[#6006](https://github.com/angular/angular.js/issues/6006), [#6101](https://github.com/angular/angular.js/issues/6101))
- support templates with table content root nodes
([e7338d3f](https://github.com/angular/angular.js/commit/31c450bcee53d0a3827b7e0a611e9013b2496506),
[#2848](https://github.com/angular/angular.js/issues/2848), [#1459](https://github.com/angular/angular.js/issues/1459), [#3647](https://github.com/angular/angular.js/issues/3647), [#3241](https://github.com/angular/angular.js/issues/3241))
- **input:**
- don't apply textInput to `<input type="file">`
([a9fcb0d0](https://github.com/angular/angular.js/commit/a9fcb0d0fc6456f80501b8820d02b04d7c15b6d6),
[#6247](https://github.com/angular/angular.js/issues/6247), [#6231](https://github.com/angular/angular.js/issues/6231))
- setViewValue on compositionend
([2b730271](https://github.com/angular/angular.js/commit/2b7302713674506fdbcdc396c38f18dcb90dee8c),
[#6058](https://github.com/angular/angular.js/issues/6058), [#5433](https://github.com/angular/angular.js/issues/5433))
## Features
- **filterFilter:** support deeply nested predicate objects
([b4eed8ad](https://github.com/angular/angular.js/commit/b4eed8ad94ce9719540462c1ee969dfd3c6b2355),
[#6215](https://github.com/angular/angular.js/issues/6215))
## Breaking Changes
- **$animate:**
- due to [4f84f6b3](https://github.com/angular/angular.js/commit/4f84f6b3e4210ae1eb14728a46d43dd961700a0c),
ngClass and {{ class }} will now call the `setClass`
animation callback instead of addClass / removeClass when both a
addClass/removeClass operation is being executed on the element during the animation.
Please include the setClass animation callback as well as addClass and removeClass within
your JS animations to work with ngClass and {{ class }} directives.
- due to [cf5e463a](https://github.com/angular/angular.js/commit/cf5e463abd2c23f62e9c2e6361e6c53048c8910e),
Both the `$animate:before` and `$animate:after` DOM events must be now
registered prior to the $animate operation taking place. The `$animate:close` event
can be registered anytime afterwards.
DOM callbacks used to fired for each and every animation operation that occurs within the
$animate service provided in the ngAnimate module. This may end up slowing down an
application if 100s of elements are being inserted into the page. Therefore after this
change callbacks are only fired if registered on the element being animated.
<a name="1.2.12"></a>
# 1.2.12 cauliflower-eradication (2014-02-07)
## Bug Fixes
- **$compile:** retain CSS classes added in cloneAttachFn on asynchronous directives
([5ed721b9](https://github.com/angular/angular.js/commit/5ed721b9b5e95ae08450e1ae9d5202e7f3f79295),
[#5439](https://github.com/angular/angular.js/issues/5439), [#5617](https://github.com/angular/angular.js/issues/5617))
- **$http:**
- ignore xhr.responseType setter exception if value is "json"
([24699ee8](https://github.com/angular/angular.js/commit/24699ee8f04c1f1459be1d36207e654421d58ff0),
[#6115](https://github.com/angular/angular.js/issues/6115), [#6122](https://github.com/angular/angular.js/issues/6122))
- update httpBackend to use ActiveXObject on IE8 if necessary
([ef210e5e](https://github.com/angular/angular.js/commit/ef210e5e119db4f5bfc9d2428b19f9b335c4f976),
[#5677](https://github.com/angular/angular.js/issues/5677), [#5679](https://github.com/angular/angular.js/issues/5679))
- **$locale:** minor grammar amends for the locale `locale_lt`
([95be253f](https://github.com/angular/angular.js/commit/95be253fe55d35336d425d3d600a36158fc3519d),
[#6164](https://github.com/angular/angular.js/issues/6164))
- **$q:** make $q.reject support `finally` and `catch`
([074b0675](https://github.com/angular/angular.js/commit/074b0675a1f97dce07f520f1ae6198ed3c604000),
[#6048](https://github.com/angular/angular.js/issues/6048), [#6076](https://github.com/angular/angular.js/issues/6076))
- **docs:** clarify doc for "args" in $broadcast and $emit
([caed2dfe](https://github.com/angular/angular.js/commit/caed2dfe4feeac5d19ecea2dbb1456b7fde21e6d),
[#6047](https://github.com/angular/angular.js/issues/6047))
- **filterFilter:** don't interpret dots in predicate object fields as paths
([339a1658](https://github.com/angular/angular.js/commit/339a1658cd9bfa5e322a01c45aa0a1df67e3a842),
[#6005](https://github.com/angular/angular.js/issues/6005), [#6009](https://github.com/angular/angular.js/issues/6009))
- **http:** make jshint happy
([6609e3da](https://github.com/angular/angular.js/commit/6609e3da76dd898cfe85f75f23ab2e39fee65fe5))
- **jqLite:** trim HTML string in jqLite constructor
([36d37c0e](https://github.com/angular/angular.js/commit/36d37c0e3880c774d20c014ade60d2331beefa15),
[#6053](https://github.com/angular/angular.js/issues/6053))
- **mocks:**
- rename mock.animate to ngAnimateMock and ensure it contains all test helper code for ngAnimate
([4224cd51](https://github.com/angular/angular.js/commit/4224cd5182bc93e4a210f75e0a4e4de7f3c544e8),
[#5822](https://github.com/angular/angular.js/issues/5822), [#5917](https://github.com/angular/angular.js/issues/5917))
- remove usage of $animate.flushNext in favour of queing
([906fdad0](https://github.com/angular/angular.js/commit/906fdad0f95465842e336e057ea97d0633712189))
- always call functions injected with `inject` with `this` set to the current spec
([3bf43903](https://github.com/angular/angular.js/commit/3bf43903397c703aa2e9ba1e1a48dbc9e8286ee2),
[#6102](https://github.com/angular/angular.js/issues/6102))
- refactor currentSpec to work w/ Jasmine 2
([95f0bf9b](https://github.com/angular/angular.js/commit/95f0bf9b526fda8964527c6d4aef1ad50a47f1f3),
[#5662](https://github.com/angular/angular.js/issues/5662))
- **ngMock:** return false from mock $interval.cancel() when no argument is supplied
([dd24c783](https://github.com/angular/angular.js/commit/dd24c78373b5d24ecb3b9d19e61e1b3b6c74d155),
[#6103](https://github.com/angular/angular.js/issues/6103))
- **ngResource:**
- don't filter "$"-prefixed properties from ngResource requests/responses
([d2e4e499](https://github.com/angular/angular.js/commit/d2e4e499862aeca157dbe7a7422c465e7c79205e),
[#5666](https://github.com/angular/angular.js/issues/5666), [#6080](https://github.com/angular/angular.js/issues/6080), [#6033](https://github.com/angular/angular.js/issues/6033))
- don't append number to '$' in url param value when encoding URI
([ce1f1f97](https://github.com/angular/angular.js/commit/ce1f1f97f0ebf77941b2bdaf5e8352d33786524d),
[#6003](https://github.com/angular/angular.js/issues/6003), [#6004](https://github.com/angular/angular.js/issues/6004))
## Breaking Changes
The animation mock module has been renamed from `mock.animate` to `ngAnimateMock`. In addition to the rename, animations will not block within test code even when ngAnimateMock is used. However, all function calls to $animate will be recorded into `$animate.queue` and are available within test code to assert animation calls. In addition, `$animate.triggerReflow()` is now only available when `ngAnimateMock` is used.
<a name="1.2.11"></a>
# 1.2.11 cryptocurrency-hyperdeflation (2014-02-03)
@@ -82,6 +198,30 @@
([4ae3184c](https://github.com/angular/angular.js/commit/4ae3184c5915aac9aa00889aa2153c8e84c14966),
[#4278](https://github.com/angular/angular.js/issues/4278), [#4225](https://github.com/angular/angular.js/issues/4225))
## Breaking Changes
- **$http:** due to [e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
it is now necessary to seperately specify default HTTP headers for PUT, POST and PATCH requests, as these no longer share a single object.
To migrate your code, follow the example below:
Before:
// Will apply to POST, PUT and PATCH methods
$httpProvider.defaults.headers.post = {
"X-MY-CSRF-HEADER": "..."
};
After:
// POST, PUT and PATCH default headers must be specified seperately,
// as they do not share data.
$httpProvider.defaults.headers.post =
$httpProvider.defaults.headers.put =
$httpProviders.defaults.headers.patch = {
"X-MY-CSRF-HEADER": "..."
};
<a name="1.2.8"></a>
# 1.2.8 interdimensional-cartography (2014-01-10)
+4
View File
@@ -3,6 +3,9 @@
We'd love for you to contribute to our source code and to make AngularJS even better than it is
today! Here are the guidelines we'd like you to follow:
## Code of Conduct
Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct][coc].
## Got a Question or Problem?
If you have questions about how to use AngularJS, please direct these to the [Google Group][groups]
@@ -258,5 +261,6 @@ You can find out more detailed information about contributing in the
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/CONTRIBUTING.md?pixel)](https://github.com/igrigorik/ga-beacon)
+9 -16
View File
@@ -4,18 +4,8 @@ var path = require('path');
module.exports = function(grunt) {
//grunt plugins
grunt.loadNpmTasks('grunt-bump');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-ddescribe-iit');
grunt.loadNpmTasks('grunt-jasmine-node');
grunt.loadNpmTasks("grunt-jscs-checker");
grunt.loadNpmTasks('grunt-merge-conflict');
grunt.loadNpmTasks('grunt-parallel');
grunt.loadNpmTasks('grunt-shell');
require('load-grunt-tasks')(grunt);
grunt.loadTasks('lib/grunt');
var NG_VERSION = util.getVersion();
@@ -100,8 +90,10 @@ module.exports = function(grunt) {
},
runprotractor: {
normal: 'protractor-conf.js'
protractor: {
normal: 'protractor-conf.js',
jquery: 'protractor-jquery-conf.js',
jenkins: 'protractor-jenkins-conf.js'
},
@@ -300,7 +292,9 @@ module.exports = function(grunt) {
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['tests:modules']);
grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['tests:jqlite', 'tests:jquery', 'tests:modules']);
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'runprotractor:normal']);
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'protractor:normal']);
grunt.registerTask('test:jq-protractor', 'Run the end to end tests against jquery with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'protractor:jquery']);
grunt.registerTask('test:ci-protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'protractor:jenkins']);
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
grunt.registerTask('test:docgen', ['jasmine_node']);
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
@@ -308,7 +302,6 @@ module.exports = function(grunt) {
grunt.registerTask('minify', ['bower','clean', 'build', 'minall']);
grunt.registerTask('webserver', ['connect:devserver']);
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('package-without-bower', ['clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
grunt.registerTask('default', ['package']);
};
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1 -1
View File
@@ -7,6 +7,6 @@
"components-font-awesome": "3.1.0",
"bootstrap": "https://raw.github.com/twbs/bootstrap/v2.0.2/docs/assets/bootstrap.zip",
"closure-compiler": "https://closure-compiler.googlecode.com/files/compiler-20130603.zip",
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.2/assets/ng-closure-runner.zip"
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip"
}
}
+5
View File
@@ -9,3 +9,8 @@
ng\:form {
display: block;
}
.ng-animate-block-transitions {
transition:0s all!important;
-webkit-transition:0s all!important;
}
+26 -24
View File
@@ -54,18 +54,18 @@ Try out the Live Preview above, and then let's walk through the example and desc
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-databinding1.png">
This looks like normal HTML, with some new markup. In Angular, a file like this is called a
<a name="template">"{@link templates template}"</a>. When Angular starts your application, it parses and
<a name="template">"{@link templates template}"</a>. When Angular starts your application, it parses and
processes this new markup from the template using the so called <a name="compiler">"{@link compiler compiler}"</a>.
The loaded, transformed and rendered DOM is then called the <a name="view">"view"</a>.
The first kind of new markup are the so called <a name="directive">"{@link directive directives}"</a>.
They apply special behavior to attributes or elements in the HTML. In the example above we use the
They apply special behavior to attributes or elements in the HTML. In the example above we use the
{@link api/ng.directive:ngApp `ng-app`} attribute, which is linked to a directive that automatically
initializes our application. Angular also defines a directive for the {@link api/ng.directive:input `input`}
element that adds extra behavior to the element. E.g. it is able to automatically validate that the entered
text is non empty by evaluating the `required` attribute.
element that adds extra behavior to the element. E.g. it is able to automatically validate that the entered
text is non empty by evaluating the `required` attribute.
The {@link api/ng.directive:ngModel `ng-model`} directive stores/updates
the value of the input field into/from a variable and shows the validation state of the input field by
the value of the input field into/from a variable and shows the validation state of the input field by
adding css classes. In the example we use these css classes to mark an empty input field with a red border.
<div class="alert alert-info">
@@ -120,7 +120,7 @@ different currencies and also pay the invoice.
return this.convertCurrency(this.qty * this.cost, this.inCurr, outCurr);
};
this.convertCurrency = function convertCurrency(amount, inCurr, outCurr) {
return amount * this.usdToForeignRates[outCurr] * 1 / this.usdToForeignRates[inCurr];
return amount * this.usdToForeignRates[outCurr] / this.usdToForeignRates[inCurr];
};
this.pay = function pay() {
window.alert("Thanks!");
@@ -195,20 +195,20 @@ Let's refactor our example and move the currency conversion into a service in an
<file name="finance2.js">
angular.module('finance2', [])
.factory('currencyConverter', function() {
var currencies = ['USD', 'EUR', 'CNY'],
usdToForeignRates = {
var currencies = ['USD', 'EUR', 'CNY'];
var usdToForeignRates = {
USD: 1,
EUR: 0.74,
CNY: 6.09
};
var convert = function (amount, inCurr, outCurr) {
return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
}
return {
currencies: currencies,
convert: convert
};
function convert(amount, inCurr, outCurr) {
return amount * usdToForeignRates[outCurr] * 1 / usdToForeignRates[inCurr];
}
});
</file>
<file name="invoice2.js">
@@ -325,21 +325,15 @@ The following example shows how this is done with Angular:
var YAHOO_FINANCE_URL_PATTERN =
'http://query.yahooapis.com/v1/public/yql?q=select * from '+
'yahoo.finance.xchange where pair in ("PAIRS")&format=json&'+
'env=store://datatables.org/alltableswithkeys&callback=JSON_CALLBACK',
currencies = ['USD', 'EUR', 'CNY'],
usdToForeignRates = {};
refresh();
return {
currencies: currencies,
convert: convert,
refresh: refresh
};
'env=store://datatables.org/alltableswithkeys&callback=JSON_CALLBACK';
var currencies = ['USD', 'EUR', 'CNY'];
var usdToForeignRates = {};
function convert(amount, inCurr, outCurr) {
return amount * usdToForeignRates[outCurr] * 1 / usdToForeignRates[inCurr];
var convert = function (amount, inCurr, outCurr) {
return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
}
function refresh() {
var refresh = function() {
var url = YAHOO_FINANCE_URL_PATTERN.
replace('PAIRS', 'USD' + currencies.join('","USD'));
return $http.jsonp(url).success(function(data) {
@@ -351,6 +345,14 @@ The following example shows how this is done with Angular:
usdToForeignRates = newUsdToForeignRates;
});
}
refresh();
return {
currencies: currencies,
convert: convert,
refresh: refresh
};
}]);
</file>
<file name="index.html">
@@ -3,9 +3,10 @@
@name Developer Guide: E2E Testing
@description
**If you're starting a new Angular project, you may want to look into
using {@link https://github.com/angular/protractor Protractor}, as it is going to
replace the current method of E2E Testing in the near future.**
**Angular Scenario Runner is in maintenance mode - If you're starting a new Angular project,
consider using {@link https://github.com/angular/protractor Protractor}.**
As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to
verify the correctness of new features, catch bugs and notice regressions.
@@ -160,7 +160,7 @@ encoded.
`$location` service has two configuration modes which control the format of the URL in the browser
address bar: **Hashbang mode** (the default) and the **HTML5 mode** which is based on using the
HTML5 {@link http://www.w3.org/TR/html5/history.html History API}. Applications use the same API in
HTML5 {@link http://www.w3.org/TR/html5/browsers.html#history History API}. Applications use the same API in
both modes and the `$location` service will work with appropriate URL segments and browser APIs to
facilitate the browser URL change and history management.
+4
View File
@@ -110,6 +110,10 @@ prevent accidental access to the global state (a common source of subtle bugs).
</doc:source>
<doc:protractor>
it('should calculate expression in binding', function() {
if (browser.params.browser = 'safari') {
// Safari can't handle dialogs.
return;
};
element(by.css('[ng-click="greet()"]')).click();
var alertDialog = browser.switchTo().alert();
+3 -1
View File
@@ -13,6 +13,7 @@ Everything you need to know about AngularJS
* {@link tutorial/index Official AngularJS Tutorial}
* [10 Reasons Why You Should Use AngularJS](http://www.sitepoint.com/10-reasons-use-angularjs/)
* [10 Reasons Why Developers Should Learn AngularJS](http://wintellect.com/blogs/jlikness/10-reasons-web-developers-should-learn-angularjs)
* [Design Principles of AngularJS (video)](https://www.youtube.com/watch?v=HCR7i5F5L8c)
* [Fundamentals in 60 Minutes (video)](http://www.youtube.com/watch?v=i9MHigUZKEM)
* [For folks with jQuery background](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background)
@@ -87,7 +88,7 @@ This is a short list of libraries with specific support and documentation for wo
* **FireBase:** [AngularFire](http://angularfire.com/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/resources/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
* **Rails: **[Tutorial](http://coderberry.me/blog/2013/04/22/angularjs-on-rails-4-part-1/), [AngularJS with Rails4](https://shellycloud.com/blog/2013/10/how-to-integrate-angularjs-with-rails-4), [angularjs-rails](https://github.com/hiravgandhi/angularjs-rails)
* **PHP: **[Building a RESTful web service](http://blog.brunoscopelliti.com/building-a-restful-web-service-with-angularjs-and-php-more-power-with-resource), [End to End with Laravel 4 (video)](http://www.youtube.com/watch?v=hqAyiqUs93c)
@@ -113,6 +114,7 @@ This is a short list of libraries with specific support and documentation for wo
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html)
[WintellectNOW (4 lessons)](http://www.wintellectnow.com/Course/Detail/mastering-angularjs)
* **Paid onsite:**
[angularbootcamp.com](http://angularbootcamp.com/)
+1 -1
View File
@@ -333,7 +333,7 @@ information.
Dirty checking the scope for property changes is a common operation in Angular and for this reason
the dirty checking function must be efficient. Care should be taken that the dirty checking
function does not do any DOM access, as DOM access is orders of magnitude slower then property
function does not do any DOM access, as DOM access is orders of magnitude slower than property
access on JavaScript object.
## Integration with the browser event loop
+2 -2
View File
@@ -178,11 +178,11 @@ grunt --help
## Running the End-to-end Test Suite
Simply run:
Angular's end to end tests are run with Protractor. Simply run:
```shell
grunt test:e2e
```
This will start the webserver and run the tests.
This will start the webserver and run the tests on Chrome.
+1 -1
View File
@@ -100,7 +100,7 @@ Watch the July 17, 2012 talk
### How is Angular licensed?
The MIT License.
The {@link https://github.com/angular/angular.js/blob/master/LICENSE MIT License}.
### Can I download and use the Angular logo artwork?
+1 -1
View File
@@ -7,7 +7,7 @@ becoming an Angular expert.
1. Read the {@link guide/concepts conceptual overview}.<br/>Understand Angular's vocabulary and how all the Angular
components work together.
1. Do the {@link tutorial/ AngularJS Tutorial}.<br/>Walk end-to-end through building and application complete with tests
1. Do the {@link tutorial/ AngularJS Tutorial}.<br/>Walk end-to-end through building an application complete with tests
on top of a node.js web server. Covers every major AngularJS feature and show you how to set up your development
environment.
1. Download or clone the {@link https://github.com/angular/angular-seed Seed App project template}.<br/>Gives you a
+1 -1
View File
@@ -148,7 +148,7 @@ describe('PhoneCat controllers', function() {
});
});
beforeEach(module('phonecatApp'));
beforeEach(module('phonecatServices'));
+4 -1
View File
@@ -57,7 +57,10 @@ writer.makeDir('build/docs/', true).then(function() {
fileFutures.push(writer.output('partials/' + doc.section + '/' + id + '.html', doc.html()));
// If it has a sample Protractor test, output that as well.
if (doc.protractorTests.length) {
fileFutures.push(writer.output('ptore2e/' + doc.section + '/' + id + '_test.js', ngdoc.writeProtractorTest(doc)));
fileFutures.push(writer.output('ptore2e/' + doc.section + '/' + id + '.jquery_test.js',
ngdoc.writeProtractorTest(doc, 'index-jq-nocache.html#!/')));
fileFutures.push(writer.output('ptore2e/' + doc.section + '/' + id + '.jqlite_test.js',
ngdoc.writeProtractorTest(doc, 'index-nocache.html#!/')));
}
});
+5 -5
View File
@@ -51,7 +51,7 @@ exports.ngVersions = function() {
});
//match the future version of AngularJS that is set in the package.json file
return expandVersions(sortVersionsNatrually(versions), exports.ngCurrentVersion().full);
return expandVersions(sortVersionsNaturally(versions), exports.ngCurrentVersion().full);
function expandVersions(versions, latestVersion) {
var RC_VERSION = /rc\d/;
@@ -87,7 +87,7 @@ exports.ngVersions = function() {
return expanded;
};
function sortVersionsNatrually(versions) {
function sortVersionsNaturally(versions) {
var versionMap = {},
NON_RC_RELEASE_NUMBER = 999;
for(var i = versions.length - 1; i >= 0; i--) {
@@ -1110,15 +1110,15 @@ function scenarios(docs){
}
}
function writeProtractorTest(doc){
function writeProtractorTest(doc, pathPrefix){
var lines = [];
lines.push('describe("' + doc.section + '/' + doc.id + '", function() {');
lines.push(' beforeEach(function() {');
lines.push(' browser.get("index-nocache.html#!/' + doc.section + '/' + doc.id + '");');
lines.push(' browser.get("' + pathPrefix + doc.section + '/' + doc.id + '");');
lines.push(' });');
lines.push('');
doc.protractorTests.forEach(function(test){
lines.push(indentCode(trim(test), 2));
lines.push(indentCode(trim(test), 0));
lines.push('');
});
lines.push('});');
+1 -2
View File
@@ -1,11 +1,10 @@
# i18n directory overview:
- closure/ - closure files we use for ruleset generation
- locale/ - angular's locale ruleset files
- src/ - source files
- spec/ - spec files for stuff in src directory
- generate.sh - runs src scripts on closure dir and stores output in locale dir
- update-closure.sh - downloads the latest version of closure files from public svn repo
- update-closure.sh - downloads the latest version of closure files from public git repo
The closure files (maintained by Shanjian Li (shanjian)) change very rarely, so we don't need to
regenerate locale files very often.
+6 -1
View File
@@ -1,7 +1,12 @@
#!/bin/bash
set -e
BASE_DIR=`dirname $0`
cd $BASE_DIR
./run-tests.sh
node src/closureSlurper.js
../node_modules/.bin/jasmine-node spec/ --noColor && node src/closureSlurper.js
+2 -1
View File
@@ -2,4 +2,5 @@
set -e
PARENT_DIR="$(dirname "$0")"
jasmine-node "$PARENT_DIR"/spec/
../node_modules/.bin/jasmine-node "$PARENT_DIR"/spec/
+28 -12
View File
@@ -2,7 +2,7 @@
'use strict';
var Q = require('q'),
qfs = require('q-fs'),
qfs = require('q-io/fs'),
converter = require('./converter.js'),
util = require('./util.js'),
closureI18nExtractor = require('./closureI18nExtractor.js'),
@@ -47,24 +47,40 @@ function extractPlurals() {
function writeLocaleFiles() {
console.log('Final stage: Writing angular locale files to directory: %j', NG_LOCALE_DIR);
var writePromises = [];
var result = Q.defer();
var localeIds = Object.keys(localeInfo);
var num_files = 0;
localeIds.forEach(function(localeID) {
console.log('Generated %j locale files.', localeIds.length);
loop();
return result.promise;
// Need to use a loop and not write the files in parallel,
// as otherwise we will get the error EMFILE, which means
// we have too many open files.
function loop() {
var nextPromise;
if (localeIds.length) {
nextPromise = process(localeIds.pop()) || Q.when();
nextPromise.then(loop, result.reject);
} else {
result.resolve(num_files);
}
}
function process(localeID) {
var content = closureI18nExtractor.outputLocale(localeInfo, localeID);
if (!content) return;
var correctedLocaleId = closureI18nExtractor.correctedLocaleId(localeID);
var filename = NG_LOCALE_DIR + 'angular-locale_' + correctedLocaleId + '.js'
writePromises.push(
qfs.write(filename, content)
.then(function () {
console.log('Wrote ' + filename);
++num_files;
}));
console.log('Writing ' + filename);
});
console.log('Generated %j locale files.', localeIds.length);
return Q.all(writePromises).then(function() { return num_files });
return qfs.write(filename, content)
.then(function () {
console.log('Wrote ' + filename);
++num_files;
});
}
}
/**
+6 -5
View File
@@ -7,8 +7,9 @@ cd $BASE_DIR
set -x # Trace commands as they're executed.
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/currency.js > closure/currencySymbols.js
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbols.js > closure/datetimeSymbols.js
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbolsext.js > closure/datetimeSymbolsExt.js
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/numberformatsymbols.js > closure/numberSymbols.js
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
# use the github repo as it is more up to date than the svn repo
curl https://closure-library.googlecode.com/git/closure/goog/i18n/currency.js > closure/currencySymbols.js
curl https://closure-library.googlecode.com/git/closure/goog/i18n/datetimesymbols.js > closure/datetimeSymbols.js
curl https://closure-library.googlecode.com/git/closure/goog/i18n/datetimesymbolsext.js > closure/datetimeSymbolsExt.js
curl https://closure-library.googlecode.com/git/closure/goog/i18n/numberformatsymbols.js > closure/numberSymbols.js
curl https://closure-library.googlecode.com/git/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
Binary file not shown.
+3 -8
View File
@@ -13,12 +13,6 @@ then
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh,/Users/jenkins/bin/ie8.sh,/Users/jenkins/bin/ie9.sh"
fi
if [[ -z "$BROWSERS_E2E" ]]
then
BROWSERS_E2E="Chrome,Firefox,/Users/jenkins/bin/safari.sh"
fi
# CLEAN #
rm -f angular.min.js.gzip.size
rm -f angular.js.size
@@ -28,6 +22,8 @@ rm -f angular.js.size
npm install --color false
grunt ci-checks package --no-color
mkdir -p test_out
# DOCS generator unit tests #
grunt test:docgen --no-color
@@ -35,8 +31,7 @@ grunt test:docgen --no-color
grunt test:unit --browsers $BROWSERS --reporters=dots,junit --no-colors --no-color
# END TO END TESTS #
grunt test:e2e --browsers $BROWSERS_E2E --reporters=dots,junit --no-colors --no-color
grunt test:protractor
grunt test:ci-protractor
# Promises/A+ TESTS #
grunt test:promises-aplus --no-color
+1 -1
View File
@@ -65,7 +65,7 @@ module.exports = function(grunt) {
util.updateWebdriver.call(util, this.async());
});
grunt.registerMultiTask('runprotractor', 'Run Protractor integration tests', function() {
grunt.registerMultiTask('protractor', 'Run Protractor integration tests', function() {
util.startProtractor.call(util, this.data, this.async());
});
+81 -18
View File
@@ -2,6 +2,7 @@ var fs = require('fs');
var shell = require('shelljs');
var grunt = require('grunt');
var spawn = require('child_process').spawn;
var semver = require('semver');
var version;
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
@@ -32,31 +33,82 @@ module.exports = {
getVersion: function(){
if (version) return version;
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
var match = package.version.match(/^([^\-]*)(?:\-(.+))?$/);
var semver = match[1].split('.');
try {
var fullVersion = match[1];
var gitTag = getTagOfCurrentCommit();
var semVerVersion, codeName, fullVersion;
if (gitTag) {
// tagged release
fullVersion = semVerVersion = semver.valid(gitTag);
codeName = getTaggedReleaseCodeName(gitTag);
} else {
// snapshot release
semVerVersion = getSnapshotVersion();
fullVersion = semVerVersion + '-' + getSnapshotSuffix();
codeName = 'snapshot'
}
if (match[2]) {
fullVersion += '-';
fullVersion += (match[2] == 'snapshot') ? getSnapshotSuffix() : match[2];
var versionParts = semVerVersion.match(/(\d+)\.(\d+)\.(\d+)/);
version = {
full: fullVersion,
major: versionParts[1],
minor: versionParts[2],
dot: versionParts[3],
codename: codeName,
cdn: package.cdnVersion
};
return version;
} catch (e) {
grunt.fail.warn(e);
}
version = {
full: fullVersion,
major: semver[0],
minor: semver[1],
dot: semver[2].replace(/rc\d+/, ''),
codename: package.codename,
cdn: package.cdnVersion
};
function getTagOfCurrentCommit() {
var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
var gitTagOutput = gitTagResult.output.trim();
var branchVersionPattern = new RegExp(package.branchVersion.replace('.', '\\.').replace('*', '\\d+'));
if (gitTagResult.code === 0 && gitTagOutput.match(branchVersionPattern)) {
return gitTagOutput;
} else {
return null;
}
}
return version;
function getTaggedReleaseCodeName(tagName) {
var tagMessage = shell.exec('git cat-file -p '+ tagName +' | grep "codename"', {silent:true}).output;
var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
if (!codeName) {
throw new Error("Could not extract release code name. The message of tag "+tagName+
" must match '*codename(some release name)*'");
}
return codeName;
}
function getSnapshotVersion() {
var oldTags = shell.exec('git tag -l v'+package.branchVersion, {silent:true}).output.trim().split('\n');
// ignore non semver versions.
oldTags = oldTags.filter(function(version) {
return version && semver.valid(version);
});
if (oldTags.length) {
oldTags.sort(semver.compare);
semVerVersion = oldTags[oldTags.length-1];
if (semVerVersion.indexOf('-') !== -1) {
semVerVersion = semver.inc(semVerVersion, 'prerelease');
} else {
semVerVersion = semver.inc(semVerVersion, 'patch');
}
} else {
semVerVersion = semver.valid(package.branchVersion.replace(/\*/g, '0'));
}
return semVerVersion;
}
function getSnapshotSuffix() {
var jenkinsBuild = process.env.BUILD_NUMBER || 'local';
var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER || 'local';
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
return 'build.'+jenkinsBuild+'+sha.'+hash;
}
@@ -84,7 +136,13 @@ module.exports = {
},
updateWebdriver: function(done){
updateWebdriver: function(done){
if (process.env.TRAVIS) {
// Skip the webdriver-manager update on Travis, since the browsers will
// be provided remotely.
done();
return;
}
var p = spawn('node', ['node_modules/protractor/bin/webdriver-manager', 'update']);
p.stdout.pipe(process.stdout);
p.stderr.pipe(process.stderr);
@@ -99,11 +157,16 @@ module.exports = {
var sauceKey = grunt.option('sauceKey');
var tunnelIdentifier = grunt.option('capabilities.tunnel-identifier');
var sauceBuild = grunt.option('capabilities.build');
var browser = grunt.option('browser');
var args = ['node_modules/protractor/bin/protractor', config];
if (sauceUser) args.push('--sauceUser=' + sauceUser);
if (sauceKey) args.push('--sauceKey=' + sauceKey);
if (tunnelIdentifier) args.push('--capabilities.tunnel-identifier=' + tunnelIdentifier);
if (sauceBuild) args.push('--capabilities.build=' + sauceBuild);
if (browser) {
args.push('--browser=' + browser);
args.push('--params.browser=' + browser);
}
var p = spawn('node', args);
+17 -16
View File
@@ -1,22 +1,29 @@
{
"name": "angularjs",
"version": "1.2.11",
"cdnVersion": "1.2.10",
"codename": "cryptocurrency-hyperdeflation",
"branchVersion": "1.2.*",
"cdnVersion": "1.2.12",
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.js.git"
},
"devDependencies": {
"grunt": "~0.4.2",
"bower": "~1.2.2",
"grunt-bump": "~0.0.13",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-compress": "~0.5.2",
"grunt-contrib-connect": "~0.5.0",
"grunt-contrib-compress": "~0.5.2",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-jshint": "~0.7.2",
"grunt-ddescribe-iit": "~0.0.1",
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
"grunt-jscs-checker": "~0.3.2",
"grunt-merge-conflict": "~0.0.1",
"grunt-parallel": "~0.3.1",
"grunt-shell": "~0.4.0",
"load-grunt-tasks": "~0.3.0",
"bower": "~1.2.2",
"jasmine-node": "~1.11.0",
"q": "~0.9.2",
"q": "~1.0.0",
"q-io": "~1.10.6",
"qq": "~0.3.5",
"shelljs": "~0.2.6",
@@ -29,20 +36,16 @@
"karma-sauce-launcher": "0.2.0",
"karma-script-launcher": "0.1.0",
"karma-browserstack-launcher": "0.0.7",
"protractor": "~0.17.0",
"protractor": "~0.18.0",
"yaml-js": "~0.0.8",
"marked": "0.2.9",
"rewire": "1.1.3",
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
"grunt-parallel": "~0.3.1",
"grunt-ddescribe-iit": "~0.0.1",
"grunt-merge-conflict": "~0.0.1",
"promises-aplus-tests": "~1.3.2",
"grunt-shell": "~0.4.0",
"semver": "~2.1.0",
"lodash": "~2.1.0",
"browserstacktunnel-wrapper": "~1.1.1",
"grunt-jscs-checker": "~0.3.2"
"grunt-jscs-checker": "~0.3.2",
"jasmine-reporters": "~0.2.1"
},
"licenses": [
{
@@ -50,7 +53,5 @@
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
}
],
"dependencies": {
"grunt-contrib-jshint": "~0.7.2"
}
"dependencies": {}
}
+3 -2
View File
@@ -2,12 +2,13 @@ exports.config = {
allScriptsTimeout: 11000,
specs: [
'build/docs/ptore2e/**/*.js',
'build/docs/ptore2e/**/*jqlite_test.js',
'test/e2e/docsAppE2E.js'
],
capabilities: {
'browserName': 'chrome'
'browserName': 'chrome',
'name': 'Angular E2E: jqlite'
},
baseUrl: 'http://localhost:8000/build/docs/',
+36
View File
@@ -0,0 +1,36 @@
exports.config = {
allScriptsTimeout: 11000,
specs: [
'build/docs/ptore2e/**/*.js',
'test/e2e/docsAppE2E.js'
],
capabilities: {
'browserName': 'chrome'
},
baseUrl: 'http://localhost:8000/build/docs/',
framework: 'jasmine',
onPrepare: function() {
// Disable animations so e2e tests run more quickly
var disableNgAnimate = function() {
angular.module('disableNgAnimate', []).run(function($animate) {
$animate.enabled(false);
});
};
browser.addMockModule('disableNgAnimate', disableNgAnimate);
require('jasmine-reporters');
jasmine.getEnv().addReporter(
new jasmine.JUnitXmlReporter('test_out/e2e-' + this.capabilities.browserName + '-', true, true));
},
jasmineNodeOpts: {
defaultTimeoutInterval: 30000,
showColors: false
}
};
+32
View File
@@ -0,0 +1,32 @@
exports.config = {
allScriptsTimeout: 11000,
specs: [
'build/docs/ptore2e/**/*jquery_test.js',
'test/e2e/docsAppE2E.js'
],
capabilities: {
'browserName': 'chrome',
'name': 'Angular E2E: jquery'
},
baseUrl: 'http://localhost:8000/build/docs/',
framework: 'jasmine',
onPrepare: function() {
// Disable animations so e2e tests run more quickly
var disableNgAnimate = function() {
angular.module('disableNgAnimate', []).run(function($animate) {
$animate.enabled(false);
});
};
browser.addMockModule('disableNgAnimate', disableNgAnimate);
},
jasmineNodeOpts: {
defaultTimeoutInterval: 30000
}
};
-20
View File
@@ -1,20 +0,0 @@
#!/bin/bash
echo "############################################"
echo "## Remove "-snapshot" from version ########"
echo "############################################"
ARG_DEFS=()
function run {
cd ../..
replaceJsonProp "package.json" "version" "(.*)-snapshot" "\2"
VERSION=$(readJsonProp "package.json" "version")
git add package.json
git commit -m "chore(release): cut v$VERSION release"
git tag -m "v$VERSION" v$VERSION
}
source $(dirname $0)/../utils.inc
@@ -1,24 +0,0 @@
#!/bin/bash
echo "############################################"
echo "## Increment version, add "-snapshot" and set version name ##"
echo "############################################"
ARG_DEFS=(
"--next-version-type=(patch|minor|major)"
"--next-version-name=(.+)"
)
function run {
cd ../..
grunt bump:$NEXT_VERSION_TYPE
NEXT_VERSION=$(readJsonProp "package.json" "version")
replaceJsonProp "package.json" "version" "(.*)" "\2-snapshot"
replaceJsonProp "package.json" "codename" ".*" "$NEXT_VERSION_NAME"
git add package.json
git commit -m "chore(release): start v$NEXT_VERSION ($NEXT_VERSION)"
}
source $(dirname $0)/../utils.inc
-42
View File
@@ -1,42 +0,0 @@
#!/bin/bash
# Script for updating angular.js repo from current local build.
echo "#################################"
echo "## Update angular.js ###"
echo "#################################"
ARG_DEFS=(
"--action=(prepare|publish)"
"--next-version-type=(patch|minor|major)"
"--next-version-name=(.+)"
"[--no-test=(true|false)]"
)
function init {
cd ../..
}
function prepare() {
./scripts/angular.js/finalize-version.sh
# Build
if [[ $NO_TEST == "true" ]]; then
npm install --color false
grunt ci-checks package --no-color
else
./jenkins_build.sh
fi
./scripts/angular.js/initialize-new-version.sh --next-version-type=$NEXT_VERSION_TYPE --next-version-name=$NEXT_VERSION_NAME
}
function publish() {
BRANCH=$(git rev-parse --abbrev-ref HEAD)
# push the commits to github
git push origin $BRANCH
# push the release tag
git push origin v`cat build/version.txt`
}
source $(dirname $0)/../utils.inc
+42
View File
@@ -0,0 +1,42 @@
#!/bin/bash
# Tags a release
# so that travis can do the actual release.
echo "#################################"
echo "## Tag angular.js for a release #"
echo "#################################"
ARG_DEFS=(
"--action=(prepare|publish)"
"--commit-sha=(.*)"
# the version number of the release.
# e.g. 1.2.12 or 1.2.12-rc.1
"--version-number=([0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?)"
"--version-name=(.+)"
)
function checkVersionNumber() {
BRANCH_PATTERN=$(readJsonProp "package.json" "branchVersion")
if [[ $VERSION_NUMBER != $BRANCH_PATTERN ]]; then
echo "version-number needs to match $BRANCH_PATTERN on this branch"
usage
fi
}
function init {
cd ../..
checkVersionNumber
TAG_NAME="v$VERSION_NUMBER"
}
function prepare() {
git tag "$TAG_NAME" -m "chore(release): $TAG_NAME codename($VERSION_NAME)" "$COMMIT_SHA"
}
function publish() {
# push the tag to github
git push origin $TAG_NAME
}
source $(dirname $0)/../utils.inc
+38 -13
View File
@@ -1,38 +1,63 @@
#!/bin/bash
# tags the current commit as a release and publishes all artifacts to
# the different repositories.
# Note: This will also works if the commit is in the past!
echo "#################################"
echo "#### Cut release ################"
echo "#### cut release ############"
echo "#################################"
ARG_DEFS=(
"--next-version-type=(patch|minor|major)"
"--next-version-name=(.+)"
# require the git dryrun flag so the script can't be run without
# thinking about this!
"--git-push-dryrun=(true|false)"
"[--no-test=(true|false)]"
# The sha to release. Needs to be the same as HEAD.
# given as parameter to double check.
"--commit-sha=(.*)"
# the version number of the release.
# e.g. 1.2.12 or 1.2.12-rc.1
"--version-number=([0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?)"
# the codename of the release
"--version-name=(.+)"
)
function init {
NG_ARGS=("$@")
if [[ $(git rev-parse --short HEAD) != $COMMIT_SHA ]]; then
echo "HEAD is not at $COMMIT_SHA"
usage
fi
if [[ ! $VERBOSE ]]; then
VERBOSE=false
fi
if [[ ! $NO_TEST ]]; then
NO_TEST=false
fi
VERBOSE_ARG="--verbose=$VERBOSE"
NO_TEST_ARG="--no_test=$NO_TEST"
}
function build {
cd ../..
npm install --color false
grunt ci-checks package --no-color
cd $SCRIPT_DIR
}
function phase {
ACTION_ARG="--action=$1"
../angular.js/publish.sh $ACTION_ARG $VERBOSE_ARG $NO_TEST_ARG \
--next-version-type=$NEXT_VERSION_TYPE --next-version-name=$NEXT_VERSION_NAME
../angular.js/tag-release.sh $ACTION_ARG $VERBOSE_ARG\
--version-number=$VERSION_NUMBER --version-name=$VERSION_NAME\
--commit-sha=$COMMIT_SHA
if [[ $1 == "prepare" ]]; then
# The build requires the tag to be set already!
build
fi
../code.angularjs.org/publish.sh $ACTION_ARG $VERBOSE_ARG
../bower/publish.sh $ACTION_ARG $VERBOSE_ARG
../angular-seed/publish.sh $ACTION_ARG $VERBOSE_ARG $NO_TEST_ARG
../angular-phonecat/publish.sh $ACTION_ARG $VERBOSE_ARG $NO_TEST_ARG
../angular-seed/publish.sh $ACTION_ARG $VERBOSE_ARG --no-test=true
../angular-phonecat/publish.sh $ACTION_ARG $VERBOSE_ARG --no-test=true
}
function run {
+8 -3
View File
@@ -10,10 +10,15 @@ if [ $JOB = "unit" ]; then
grunt test:promises-aplus
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_8,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
elif [ $JOB = "e2e" ]; then
grunt test:protractor --sauceUser $SAUCE_USERNAME \
export GRUNT_TARGET="test:protractor"
if [ $JQVERSION = "jquery" ]; then
GRUNT_TARGET="test:jq-protractor"
fi
grunt $GRUNT_TARGET --sauceUser $SAUCE_USERNAME \
--sauceKey $SAUCE_ACCESS_KEY \
--capabilities.tunnel-identifier=$TRAVIS_JOB_NUMBER \
--capabilities.build=$TRAVIS_BUILD_NUMBER
--capabilities.build=$TRAVIS_BUILD_NUMBER \
--browser=$BROWSER
else
echo "Unknown job type. Please set JOB=unit or JOB=e2e."
echo "Unknown job type. Please set JOB=unit or JOB=e2e-*."
fi
+6 -4
View File
@@ -81,6 +81,7 @@
-assertNotHasOwnProperty,
-getter,
-getBlockElements,
-hasOwnProperty,
*/
@@ -96,7 +97,7 @@
* @returns {string} Lowercased string.
*/
var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;};
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* @ngdoc function
@@ -192,7 +193,7 @@ function isArrayLike(obj) {
* is the value of an object property or an array element and `key` is the object property key or
* array element index. Specifying a `context` for the function is optional.
*
* It is worth nothing that `.forEach` does not iterate over inherited properties because it filters
* It is worth noting that `.forEach` does not iterate over inherited properties because it filters
* using the `hasOwnProperty` method.
*
<pre>
@@ -201,7 +202,7 @@ function isArrayLike(obj) {
angular.forEach(values, function(value, key){
this.push(key + ': ' + value);
}, log);
expect(log).toEqual(['name: misko', 'gender:male']);
expect(log).toEqual(['name: misko', 'gender: male']);
</pre>
*
* @param {Object|Array} obj Object to iterate over.
@@ -772,7 +773,7 @@ function shallowCopy(src, dst) {
for(var key in src) {
// shallowCopy is only ever called by $compile nodeLinkFn, which has control over src
// so we don't need to worry about using our custom hasOwnProperty here
if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') {
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
dst[key] = src[key];
}
}
@@ -1131,6 +1132,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
<file name="index.html">
<div ng-controller="ngAppDemoController">
I can add: {{a}} + {{b}} = {{ a+b }}
</div>
</file>
<file name="script.js">
angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
+1 -1
View File
@@ -589,7 +589,7 @@ function annotate(fn) {
* Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
* calls to {@link ng.$log#error $log.warn()}.
* <pre>
* $provider.decorator('$log', ['$delegate', function($delegate) {
* $provide.decorator('$log', ['$delegate', function($delegate) {
* $delegate.warn = $delegate.error;
* return $delegate;
* }]);
+13 -2
View File
@@ -40,7 +40,7 @@
* - [`after()`](http://api.jquery.com/after/)
* - [`append()`](http://api.jquery.com/append/)
* - [`attr()`](http://api.jquery.com/attr/)
* - [`bind()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
* - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
* - [`children()`](http://api.jquery.com/children/) - Does not support selectors
* - [`clone()`](http://api.jquery.com/clone/)
* - [`contents()`](http://api.jquery.com/contents/)
@@ -67,7 +67,7 @@
* - [`text()`](http://api.jquery.com/text/)
* - [`toggleClass()`](http://api.jquery.com/toggleClass/)
* - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
* - [`unbind()`](http://api.jquery.com/off/) - Does not support namespaces
* - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
* - [`val()`](http://api.jquery.com/val/)
* - [`wrap()`](http://api.jquery.com/wrap/)
*
@@ -107,6 +107,14 @@ var jqCache = JQLite.cache = {},
? function(element, type, fn) {element.removeEventListener(type, fn, false); }
: function(element, type, fn) {element.detachEvent('on' + type, fn); });
/*
* !!! This is an undocumented "private" function !!!
*/
var jqData = JQLite._data = function(node) {
//jQuery always returns an object on cache miss
return this.cache[node[this.expando]] || {};
};
function jqNextId() { return ++jqId; }
@@ -175,6 +183,9 @@ function JQLite(element) {
if (element instanceof JQLite) {
return element;
}
if (isString(element)) {
element = trim(element);
}
if (!(this instanceof JQLite)) {
if (isString(element) && element.charAt(0) != '<') {
throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
+23
View File
@@ -222,6 +222,29 @@ var $AnimateProvider = ['$provide', function($provide) {
done && $timeout(done, 0, false);
},
/**
*
* @ngdoc function
* @name ng.$animate#setClass
* @methodOf ng.$animate
* @function
* @description Adds and/or removes the given CSS classes to and from the element.
* Once complete, the done() callback will be fired (if provided).
* @param {jQuery/jqLite element} element the element which will it's CSS classes changed
* removed from it
* @param {string} add the CSS classes which will be added to the element
* @param {string} remove the CSS class which will be removed from the element
* @param {function=} done the callback function (if provided) that will be fired after the
* CSS classes have been set on the element
*/
setClass : function(element, add, remove, done) {
forEach(element, function (element) {
jqLiteAddClass(element, add);
jqLiteRemoveClass(element, remove);
});
done && $timeout(done, 0, false);
},
enabled : noop
};
}];
+46 -11
View File
@@ -502,7 +502,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var hasDirectives = {},
Suffix = 'Directive',
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/;
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|tbody)(\s+[^>]*)?>/i;
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
// The assumption is that future DOM event attribute names will begin with
@@ -689,8 +690,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* @param {string} oldClasses The former CSS className value
*/
$updateClass : function(newClasses, oldClasses) {
this.$removeClass(tokenDifference(oldClasses, newClasses));
this.$addClass(tokenDifference(newClasses, oldClasses));
var toAdd = tokenDifference(newClasses, oldClasses);
var toRemove = tokenDifference(oldClasses, newClasses);
if(toAdd.length === 0) {
$animate.removeClass(this.$$element, toRemove);
} else if(toRemove.length === 0) {
$animate.addClass(this.$$element, toAdd);
} else {
$animate.setClass(this.$$element, toAdd, toRemove);
}
},
/**
@@ -1142,7 +1151,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
templateDirective = previousCompileContext.templateDirective,
nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
hasTranscludeDirective = false,
hasElementTranscludeDirective = false,
hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
$compileNode = templateAttrs.$$element = jqLite(compileNode),
directive,
directiveName,
@@ -1196,7 +1205,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
hasTranscludeDirective = true;
// Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
// This option should only be used by directives that know how to how to safely handle element transclusion,
// This option should only be used by directives that know how to safely handle element transclusion,
// where the transcluded nodes are added or replaced after linking.
if (!directive.$$tlb) {
assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
@@ -1243,9 +1252,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (directive.replace) {
replaceDirective = directive;
$template = jqLite('<div>' +
trim(directiveValue) +
'</div>').contents();
$template = directiveTemplateContents(directiveValue);
compileNode = $template[0];
if ($template.length != 1 || compileNode.nodeType !== 1) {
@@ -1316,6 +1323,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn;
previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
// might be normal or delayed nodeLinkFn depending on if templateUrl is present
return nodeLinkFn;
@@ -1643,6 +1651,28 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
function directiveTemplateContents(template) {
var type;
template = trim(template);
if ((type = TABLE_CONTENT_REGEXP.exec(template))) {
type = type[1].toLowerCase();
var table = jqLite('<table>' + template + '</table>'),
tbody = table.children('tbody'),
leaf = /(td|th)/.test(type) && table.find('tr');
if (tbody.length && type !== 'tbody') {
table = tbody;
}
if (leaf && leaf.length) {
table = leaf;
}
return table.contents();
}
return jqLite('<div>' +
template +
'</div>').contents();
}
function compileTemplateUrl(directives, $compileNode, tAttrs,
$rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
var linkQueue = [],
@@ -1667,7 +1697,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
content = denormalizeTemplate(content);
if (origAsyncDirective.replace) {
$template = jqLite('<div>' + trim(content) + '</div>').contents();
$template = directiveTemplateContents(content);
compileNode = $template[0];
if ($template.length != 1 || compileNode.nodeType !== 1) {
@@ -1712,8 +1742,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
var oldClasses = beforeTemplateLinkNode.className;
// it was cloned therefore we have to clone as well.
linkNode = jqLiteClone(compileNode);
if (!(previousCompileContext.hasElementTranscludeDirective &&
origAsyncDirective.replace)) {
// it was cloned therefore we have to clone as well.
linkNode = jqLiteClone(compileNode);
}
replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
// Copy in CSS classes from original node
+8 -1
View File
@@ -59,7 +59,14 @@
element(by.id('link-3')).click();
expect(browser.driver.getCurrentUrl()).toMatch(/\/123$/);
// At this point, we navigate away from an Angular page, so we need
// to use browser.driver to get the base webdriver.
browser.wait(function() {
return browser.driver.getCurrentUrl().then(function(url) {
return url.match(/\/123$/);
});
}, 1000, 'page should navigate to /123');
});
it('should execute ng-click but not reload when href empty string and name specified', function() {
+13 -2
View File
@@ -424,7 +424,8 @@ var inputType = {
'hidden': noop,
'button': noop,
'submit': noop,
'reset': noop
'reset': noop,
'file': noop
};
// A helper function to call $setValidity and return the value / undefined,
@@ -447,6 +448,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
element.on('compositionend', function() {
composing = false;
listener();
});
}
@@ -955,11 +957,17 @@ var VALID_CLASS = 'ng-valid',
</file>
<file name="protractorTest.js">
it('should data-bind and become invalid', function() {
if (browser.params.browser = 'safari') {
// SafariDriver can't handle contenteditable.
return;
};
var contentEditable = element(by.css('.doc-example-live [contenteditable]'));
expect(contentEditable.getText()).toEqual('Change me!');
contentEditable.clear();
// Firefox driver doesn't trigger the proper events on 'clear', so do this hack
contentEditable.click();
contentEditable.sendKeys(protractor.Key.chord(protractor.Key.COMMAND, "a"));
contentEditable.sendKeys(protractor.Key.BACK_SPACE);
expect(contentEditable.getText()).toEqual('');
@@ -1016,6 +1024,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* You can override this for input directives whose concept of being empty is different to the
* default. The `checkboxInputType` directive does this because in its case a value of `false`
* implies empty.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is empty.
*/
this.$isEmpty = function(value) {
return isUndefined(value) || value === '' || value === null || value !== value;
-1
View File
@@ -133,7 +133,6 @@ function classDirective(name, selector) {
expect(ps.get(1).getAttribute('class')).toBe('');
element(by.model('style')).clear();
element(by.model('style')).sendKeys('red');
browser.debugger();
expect(ps.get(1).getAttribute('class')).toBe('red');
});
+2 -2
View File
@@ -298,8 +298,8 @@ forEach(
* Enables binding angular expressions to onsubmit events.
*
* Additionally it prevents the default action (which for form means sending the request to the
* server and reloading the current page) **but only if the form does not contain an `action`
* attribute**.
* server and reloading the current page), but only if the form does not contain `action`,
* `data-action`, or `x-action` attributes.
*
* @element form
* @priority 0
+9
View File
@@ -119,12 +119,21 @@
});
it('should load template2.html', function() {
if (browser.params.browser == 'firefox') {
// Firefox can't handle using selects
// See https://github.com/angular/protractor/issues/480
return;
}
templateSelect.click();
templateSelect.element.all(by.css('option')).get(2).click();
expect(includeElem.getText()).toMatch(/Content of template2.html/);
});
it('should change to blank', function() {
if (browser.params.browser == 'firefox') {
// Firefox can't handle using selects
return;
}
templateSelect.click();
templateSelect.element.all(by.css('option')).get(0).click();
expect(includeElem.isPresent()).toBe(false);
+7 -14
View File
@@ -56,23 +56,16 @@
*
*/
var ngTranscludeDirective = ngDirective({
controller: ['$element', '$transclude', function($element, $transclude) {
link: function($scope, $element, $attrs, controller, $transclude) {
if (!$transclude) {
throw minErr('ngTransclude')('orphan',
'Illegal use of ngTransclude directive in the template! ' +
'No parent directive that requires a transclusion found. ' +
'Element: {0}',
startingTag($element));
'Illegal use of ngTransclude directive in the template! ' +
'No parent directive that requires a transclusion found. ' +
'Element: {0}',
startingTag($element));
}
// remember the transclusion fn but call it during linking so that we don't process transclusion before directives on
// the parent element even when the transclusion replaces the current element. (we can't use priority here because
// that applies only to compile fns and not controllers
this.$transclude = $transclude;
}],
link: function($scope, $element, $attrs, controller) {
controller.$transclude(function(clone) {
$transclude(function(clone) {
$element.empty();
$element.append(clone);
});
+9
View File
@@ -136,6 +136,15 @@ function filterFilter() {
};
} else {
comparator = function(obj, text) {
if (obj && text && typeof obj === 'object' && typeof text === 'object') {
for (var objKey in obj) {
if (objKey.charAt(0) !== '$' && hasOwnProperty.call(obj, objKey) &&
comparator(obj[objKey], text[objKey])) {
return true;
}
}
return false;
}
text = (''+text).toLowerCase();
return (''+obj).toLowerCase().indexOf(text) > -1;
};
+5
View File
@@ -34,6 +34,11 @@
expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('USD$1,234.56');
});
it('should update', function() {
if (browser.params.browser == 'safari') {
// Safari does not understand the minus key. See
// https://github.com/angular/protractor/issues/481
return;
}
element(by.model('amount')).clear();
element(by.model('amount')).sendKeys('-1234');
expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
+14 -1
View File
@@ -107,7 +107,20 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
}
if (responseType) {
xhr.responseType = responseType;
try {
xhr.responseType = responseType;
} catch (e) {
// WebKit added support for the json responseType value on 09/03/2013
// https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
// known to throw when setting the value "json" as the response type. Other older
// browsers implementing the responseType
//
// The json response type can be ignored if not supported, because JSON payloads are
// parsed on the client-side regardless.
if (responseType !== 'json') {
throw e;
}
}
}
xhr.send(post || null);
+1 -1
View File
@@ -227,7 +227,7 @@ function $InterpolateProvider() {
* @description
* Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
*
* Use {@link ng.$interpolateProvider#endSymbol $interpolateProvider#endSymbol} to change
* Use {@link ng.$interpolateProvider#methods_endSymbol $interpolateProvider#endSymbol} to change
* the symbol.
*
* @returns {string} start symbol.
+1 -1
View File
@@ -574,7 +574,7 @@ function $LocationProvider(){
* @eventType broadcast on root scope
* @description
* Broadcasted before a URL will change. This change can be prevented by calling
* `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
* `preventDefault` method of the event. See {@link ng.$rootScope.Scope#methods_$on} for more
* details about event object. Upon successful change
* {@link ng.$location#events_$locationChangeSuccess $locationChangeSuccess} is fired.
*
+1 -1
View File
@@ -51,7 +51,7 @@ function $LogProvider(){
* @name ng.$logProvider#debugEnabled
* @methodOf ng.$logProvider
* @description
* @param {string=} flag enable or disable debug level messages
* @param {boolean=} flag enable or disable debug level messages
* @returns {*} current value if used as getter or itself (chaining) if used as setter
*/
this.debugEnabled = function(flag) {
+1 -1
View File
@@ -73,7 +73,7 @@
* constructed via `$q.reject`, the promise will be rejected instead.
* - `reject(reason)` rejects the derived promise with the `reason`. This is equivalent to
* resolving it with a rejection constructed via `$q.reject`.
* - `notify(value)` - provides updates on the status of the promises execution. This may be called
* - `notify(value)` - provides updates on the status of the promise's execution. This may be called
* multiple times before the promise is either resolved or rejected.
*
* **Properties**
+329 -226
View File
@@ -248,7 +248,9 @@ angular.module('ngAnimate', ['ng'])
* Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
*
*/
.factory('$$animateReflow', ['$window', '$timeout', function($window, $timeout) {
.factory('$$animateReflow', ['$window', '$timeout', '$document',
function($window, $timeout, $document) {
var bod = $document[0].body;
var requestAnimationFrame = $window.requestAnimationFrame ||
$window.webkitRequestAnimationFrame ||
function(fn) {
@@ -261,13 +263,30 @@ angular.module('ngAnimate', ['ng'])
return $timeout.cancel(timer);
};
return function(fn) {
var id = requestAnimationFrame(fn);
var id = requestAnimationFrame(function() {
var a = bod.offsetWidth + 1;
fn();
});
return function() {
cancelAnimationFrame(id);
};
};
}])
.factory('$$asyncQueueBuffer', ['$timeout', function($timeout) {
var timer, queue = [];
return function(fn) {
$timeout.cancel(timer);
queue.push(fn);
timer = $timeout(function() {
for(var i = 0; i < queue.length; i++) {
queue[i]();
}
queue = [];
}, 0, false);
};
}])
.config(['$provide', '$animateProvider', function($provide, $animateProvider) {
var noop = angular.noop;
var forEach = angular.forEach;
@@ -287,13 +306,18 @@ angular.module('ngAnimate', ['ng'])
}
}
function stripCommentsFromElement(element) {
return angular.element(extractElementNode(element));
}
function isMatchingElement(elm1, elm2) {
return extractElementNode(elm1) == extractElementNode(elm2);
}
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document',
function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document) {
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$$asyncQueueBuffer', '$rootScope', '$document',
function($delegate, $injector, $sniffer, $rootElement, $$asyncQueueBuffer, $rootScope, $document) {
var globalAnimationCounter = 0;
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
// disable animations during bootstrap, but once we bootstrapped, wait again
@@ -315,10 +339,6 @@ angular.module('ngAnimate', ['ng'])
return classNameFilter.test(className);
};
function async(fn) {
return $timeout(fn, 0, false);
}
function lookup(name) {
if (name) {
var matches = [],
@@ -400,6 +420,7 @@ angular.module('ngAnimate', ['ng'])
this.enabled(false, element);
$delegate.enter(element, parentElement, afterElement);
$rootScope.$$postDigest(function() {
element = stripCommentsFromElement(element);
performAnimation('enter', 'ng-enter', element, parentElement, afterElement, noop, doneCallback);
});
},
@@ -436,6 +457,7 @@ angular.module('ngAnimate', ['ng'])
cancelChildAnimations(element);
this.enabled(false, element);
$rootScope.$$postDigest(function() {
element = stripCommentsFromElement(element);
performAnimation('leave', 'ng-leave', element, null, null, function() {
$delegate.leave(element);
}, doneCallback);
@@ -478,6 +500,7 @@ angular.module('ngAnimate', ['ng'])
this.enabled(false, element);
$delegate.move(element, parentElement, afterElement);
$rootScope.$$postDigest(function() {
element = stripCommentsFromElement(element);
performAnimation('move', 'ng-move', element, parentElement, afterElement, noop, doneCallback);
});
},
@@ -513,6 +536,7 @@ angular.module('ngAnimate', ['ng'])
* @param {function()=} doneCallback the callback function that will be called once the animation is complete
*/
addClass : function(element, className, doneCallback) {
element = stripCommentsFromElement(element);
performAnimation('addClass', className, element, null, null, function() {
$delegate.addClass(element, className);
}, doneCallback);
@@ -549,11 +573,34 @@ angular.module('ngAnimate', ['ng'])
* @param {function()=} doneCallback the callback function that will be called once the animation is complete
*/
removeClass : function(element, className, doneCallback) {
element = stripCommentsFromElement(element);
performAnimation('removeClass', className, element, null, null, function() {
$delegate.removeClass(element, className);
}, doneCallback);
},
/**
*
* @ngdoc function
* @name ng.$animate#setClass
* @methodOf ng.$animate
* @function
* @description Adds and/or removes the given CSS classes to and from the element.
* Once complete, the done() callback will be fired (if provided).
* @param {jQuery/jqLite element} element the element which will it's CSS classes changed
* removed from it
* @param {string} add the CSS classes which will be added to the element
* @param {string} remove the CSS class which will be removed from the element
* @param {function=} done the callback function (if provided) that will be fired after the
* CSS classes have been set on the element
*/
setClass : function(element, add, remove, doneCallback) {
element = stripCommentsFromElement(element);
performAnimation('setClass', [add, remove], element, null, null, function() {
$delegate.setClass(element, add, remove);
}, doneCallback);
},
/**
* @ngdoc function
* @name ngAnimate.$animate#enabled
@@ -600,7 +647,15 @@ angular.module('ngAnimate', ['ng'])
and the onComplete callback will be fired once the animation is fully complete.
*/
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
var currentClassName, classes, node = extractElementNode(element);
var classNameAdd, classNameRemove, setClassOperation = animationEvent == 'setClass';
if(setClassOperation) {
classNameAdd = className[0];
classNameRemove = className[1];
className = classNameAdd + ' ' + classNameRemove;
}
var currentClassName, classes, node = element[0];
if(node) {
currentClassName = node.className;
classes = currentClassName + ' ' + className;
@@ -612,18 +667,27 @@ angular.module('ngAnimate', ['ng'])
fireDOMOperation();
fireBeforeCallbackAsync();
fireAfterCallbackAsync();
closeAnimation();
fireDoneCallbackAsync();
return;
}
var elementEvents = angular.element._data(node);
elementEvents = elementEvents && elementEvents.events;
var animationLookup = (' ' + classes).replace(/\s+/g,'.');
if (!parentElement) {
parentElement = afterElement ? afterElement.parent() : element.parent();
}
var matches = lookup(animationLookup);
var isClassBased = animationEvent == 'addClass' || animationEvent == 'removeClass';
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
var matches = lookup(animationLookup);
var isClassBased = animationEvent == 'addClass' ||
animationEvent == 'removeClass' ||
setClassOperation;
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
var runningAnimations = ngAnimateState.active || {};
var totalActiveAnimations = ngAnimateState.totalActive || 0;
var lastAnimation = ngAnimateState.last;
//skip the animation if animations are disabled, a parent is already being animated,
//the element is not currently attached to the document body or then completely close
@@ -642,7 +706,7 @@ angular.module('ngAnimate', ['ng'])
//only add animations if the currently running animation is not structural
//or if there is no animation running at all
var allowAnimations = isClassBased ?
!ngAnimateState.disabled && (!ngAnimateState.running || !ngAnimateState.structural) :
!ngAnimateState.disabled && (!lastAnimation || lastAnimation.classBased) :
true;
if(allowAnimations) {
@@ -677,55 +741,48 @@ angular.module('ngAnimate', ['ng'])
return;
}
var ONE_SPACE = ' ';
//this value will be searched for class-based CSS className lookup. Therefore,
//we prefix and suffix the current className value with spaces to avoid substring
//lookups of className tokens
var futureClassName = ONE_SPACE + currentClassName + ONE_SPACE;
if(ngAnimateState.running) {
//if an animation is currently running on the element then lets take the steps
//to cancel that animation and fire any required callbacks
$timeout.cancel(ngAnimateState.closeAnimationTimeout);
cleanup(element);
cancelAnimations(ngAnimateState.animations);
var skipAnimation = false;
if(totalActiveAnimations > 0) {
var animationsToCancel = [];
if(!isClassBased) {
if(animationEvent == 'leave' && runningAnimations['ng-leave']) {
skipAnimation = true;
} else {
//cancel all animations when a structural animation takes place
for(var klass in runningAnimations) {
animationsToCancel.push(runningAnimations[klass]);
cleanup(element, klass);
}
runningAnimations = {};
totalActiveAnimations = 0;
}
} else if(lastAnimation.event == 'setClass') {
animationsToCancel.push(lastAnimation);
cleanup(element, className);
}
else if(runningAnimations[className]) {
var current = runningAnimations[className];
if(current.event == animationEvent) {
skipAnimation = true;
} else {
animationsToCancel.push(current);
cleanup(element, className);
}
}
//in the event that the CSS is class is quickly added and removed back
//then we don't want to wait until after the reflow to add/remove the CSS
//class since both class animations may run into a race condition.
//The code below will check to see if that is occurring and will
//immediately remove the former class before the reflow so that the
//animation can snap back to the original animation smoothly
var isFullyClassBasedAnimation = isClassBased && !ngAnimateState.structural;
var isRevertingClassAnimation = isFullyClassBasedAnimation &&
ngAnimateState.className == className &&
animationEvent != ngAnimateState.event;
//if the class is removed during the reflow then it will revert the styles temporarily
//back to the base class CSS styling causing a jump-like effect to occur. This check
//here ensures that the domOperation is only performed after the reflow has commenced
if(ngAnimateState.beforeComplete || isRevertingClassAnimation) {
(ngAnimateState.done || noop)(true);
} else if(isFullyClassBasedAnimation) {
//class-based animations will compare element className values after cancelling the
//previous animation to see if the element properties already contain the final CSS
//class and if so then the animation will be skipped. Since the domOperation will
//be performed only after the reflow is complete then our element's className value
//will be invalid. Therefore the same string manipulation that would occur within the
//DOM operation will be performed below so that the class comparison is valid...
futureClassName = ngAnimateState.event == 'removeClass' ?
futureClassName.replace(ONE_SPACE + ngAnimateState.className + ONE_SPACE, ONE_SPACE) :
futureClassName + ngAnimateState.className + ONE_SPACE;
if(animationsToCancel.length > 0) {
angular.forEach(animationsToCancel, function(operation) {
(operation.done || noop)(true);
cancelAnimations(operation.animations);
});
}
}
//There is no point in perform a class-based animation if the element already contains
//(on addClass) or doesn't contain (on removeClass) the className being animated.
//The reason why this is being called after the previous animations are cancelled
//is so that the CSS classes present on the element can be properly examined.
var classNameToken = ONE_SPACE + className + ONE_SPACE;
if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) ||
(animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {
fireDOMOperation();
if(isClassBased && !setClassOperation && !skipAnimation) {
skipAnimation = (animationEvent == 'addClass') == element.hasClass(className); //opposite of XOR
}
if(skipAnimation) {
fireBeforeCallbackAsync();
fireAfterCallbackAsync();
fireDoneCallbackAsync();
@@ -736,13 +793,22 @@ angular.module('ngAnimate', ['ng'])
//parent animations to find and cancel child animations when needed
element.addClass(NG_ANIMATE_CLASS_NAME);
element.data(NG_ANIMATE_STATE, {
running:true,
event:animationEvent,
className:className,
structural:!isClassBased,
animations:animations,
var localAnimationCount = globalAnimationCounter++;
lastAnimation = {
classBased : isClassBased,
event : animationEvent,
animations : animations,
done:onBeforeAnimationsComplete
};
totalActiveAnimations++;
runningAnimations[className] = lastAnimation;
element.data(NG_ANIMATE_STATE, {
last : lastAnimation,
active : runningAnimations,
index : localAnimationCount,
totalActive : totalActiveAnimations
});
//first we run the before animations and when all of those are complete
@@ -750,6 +816,11 @@ angular.module('ngAnimate', ['ng'])
invokeRegisteredAnimationFns(animations, 'before', onBeforeAnimationsComplete);
function onBeforeAnimationsComplete(cancelled) {
var data = element.data(NG_ANIMATE_STATE);
cancelled = cancelled ||
!data || !data.active[className] ||
(isClassBased && data.active[className].event != animationEvent);
fireDOMOperation();
if(cancelled === true) {
closeAnimation();
@@ -759,11 +830,8 @@ angular.module('ngAnimate', ['ng'])
//set the done function to the final done function
//so that the DOM event won't be executed twice by accident
//if the after animation is cancelled as well
var data = element.data(NG_ANIMATE_STATE);
if(data) {
data.done = closeAnimation;
element.data(NG_ANIMATE_STATE, data);
}
var currentAnimation = data.active[className];
currentAnimation.done = closeAnimation;
invokeRegisteredAnimationFns(animations, 'after', closeAnimation);
}
@@ -786,9 +854,13 @@ angular.module('ngAnimate', ['ng'])
}
if(animation[phase]) {
animation[endFnName] = isClassBased ?
animation[phase](element, className, animationPhaseCompleted) :
animation[phase](element, animationPhaseCompleted);
if(setClassOperation) {
animation[endFnName] = animation[phase](element, classNameAdd, classNameRemove, animationPhaseCompleted);
} else {
animation[endFnName] = isClassBased ?
animation[phase](element, className, animationPhaseCompleted) :
animation[phase](element, animationPhaseCompleted);
}
} else {
animationPhaseCompleted();
}
@@ -809,29 +881,32 @@ angular.module('ngAnimate', ['ng'])
}
function fireDOMCallback(animationPhase) {
element.triggerHandler('$animate:' + animationPhase, {
event : animationEvent,
className : className
});
var eventName = '$animate:' + animationPhase;
if(elementEvents && elementEvents[eventName] && elementEvents[eventName].length > 0) {
$$asyncQueueBuffer(function() {
element.triggerHandler(eventName, {
event : animationEvent,
className : className
});
});
}
}
function fireBeforeCallbackAsync() {
async(function() {
fireDOMCallback('before');
});
fireDOMCallback('before');
}
function fireAfterCallbackAsync() {
async(function() {
fireDOMCallback('after');
});
fireDOMCallback('after');
}
function fireDoneCallbackAsync() {
async(function() {
fireDOMCallback('close');
doneCallback && doneCallback();
});
fireDOMCallback('close');
if(doneCallback) {
$$asyncQueueBuffer(function() {
doneCallback();
});
}
}
//it is less complicated to use a flag than managing and cancelling
@@ -853,10 +928,13 @@ angular.module('ngAnimate', ['ng'])
failing would be when a parent HTML tag has a ng-class attribute
causing ALL directives below to skip animations during the digest */
if(isClassBased) {
cleanup(element);
cleanup(element, className);
} else {
data.closeAnimationTimeout = async(function() {
cleanup(element);
$$asyncQueueBuffer(function() {
var data = element.data(NG_ANIMATE_STATE) || {};
if(localAnimationCount == data.index) {
cleanup(element, className, animationEvent);
}
});
element.data(NG_ANIMATE_STATE, data);
}
@@ -871,9 +949,11 @@ angular.module('ngAnimate', ['ng'])
forEach(node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME), function(element) {
element = angular.element(element);
var data = element.data(NG_ANIMATE_STATE);
if(data) {
cancelAnimations(data.animations);
cleanup(element);
if(data && data.active) {
angular.forEach(data.active, function(operation) {
(operation.done || noop)(true);
cancelAnimations(operation.animations);
});
}
});
}
@@ -890,15 +970,27 @@ angular.module('ngAnimate', ['ng'])
});
}
function cleanup(element) {
function cleanup(element, className) {
if(isMatchingElement(element, $rootElement)) {
if(!rootAnimateState.disabled) {
rootAnimateState.running = false;
rootAnimateState.structural = false;
}
} else {
element.removeClass(NG_ANIMATE_CLASS_NAME);
element.removeData(NG_ANIMATE_STATE);
} else if(className) {
var data = element.data(NG_ANIMATE_STATE) || {};
var removeAnimations = className === true;
if(!removeAnimations) {
if(data.active && data.active[className]) {
data.totalActive--;
delete data.active[className];
}
}
if(removeAnimations || !data.totalActive) {
element.removeClass(NG_ANIMATE_CLASS_NAME);
element.removeData(NG_ANIMATE_STATE);
}
}
}
@@ -917,7 +1009,7 @@ angular.module('ngAnimate', ['ng'])
var isRoot = isMatchingElement(parentElement, $rootElement);
var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE);
var result = state && (!!state.disabled || !!state.running);
var result = state && (!!state.disabled || state.running || state.totalActive > 0);
if(isRoot || result) {
return result;
}
@@ -967,74 +1059,57 @@ angular.module('ngAnimate', ['ng'])
var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey';
var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data';
var NG_ANIMATE_BLOCK_CLASS_NAME = 'ng-animate-block-transitions';
var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
var CLOSING_TIME_BUFFER = 1.5;
var ONE_SECOND = 1000;
var animationCounter = 0;
var lookupCache = {};
var parentCounter = 0;
var animationReflowQueue = [];
var animationElementQueue = [];
var cancelAnimationReflow;
var closingAnimationTime = 0;
var timeOut = false;
function afterReflow(element, callback) {
if(cancelAnimationReflow) {
cancelAnimationReflow();
}
animationReflowQueue.push(callback);
var node = extractElementNode(element);
element = angular.element(node);
animationElementQueue.push(element);
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
var stagger = elementData.stagger;
var staggerTime = elementData.itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0);
var animationTime = (elementData.maxDelay + elementData.maxDuration) * CLOSING_TIME_BUFFER;
closingAnimationTime = Math.max(closingAnimationTime, (staggerTime + animationTime) * ONE_SECOND);
//by placing a counter we can avoid an accidental
//race condition which may close an animation when
//a follow-up animation is midway in its animation
elementData.animationCount = animationCounter;
cancelAnimationReflow = $$animateReflow(function() {
forEach(animationReflowQueue, function(fn) {
fn();
});
//copy the list of elements so that successive
//animations won't conflict if they're added before
//the closing animation timeout has run
var elementQueueSnapshot = [];
var animationCounterSnapshot = animationCounter;
forEach(animationElementQueue, function(elm) {
elementQueueSnapshot.push(elm);
});
$timeout(function() {
closeAllAnimations(elementQueueSnapshot, animationCounterSnapshot);
elementQueueSnapshot = null;
}, closingAnimationTime, false);
animationReflowQueue = [];
animationElementQueue = [];
cancelAnimationReflow = null;
lookupCache = {};
closingAnimationTime = 0;
animationCounter++;
});
}
function closeAllAnimations(elements, count) {
var closingTimer = null;
var closingTimestamp = 0;
var animationElementQueue = [];
function animationCloseHandler(element, totalTime) {
var futureTimestamp = Date.now() + (totalTime * 1000);
if(futureTimestamp <= closingTimestamp) {
return;
}
$timeout.cancel(closingTimer);
var node = extractElementNode(element);
element = angular.element(node);
animationElementQueue.push(element);
closingTimestamp = futureTimestamp;
closingTimer = $timeout(function() {
closeAllAnimations(animationElementQueue);
animationElementQueue = [];
}, totalTime, false);
}
function closeAllAnimations(elements) {
forEach(elements, function(element) {
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
if(elementData && elementData.animationCount == count) {
if(elementData) {
(elementData.closeAnimationFn || noop)();
}
});
@@ -1119,12 +1194,12 @@ angular.module('ngAnimate', ['ng'])
return parentID + '-' + extractElementNode(element).className;
}
function animateSetup(element, className, calculationDecorator) {
function animateSetup(animationEvent, element, className, calculationDecorator) {
var cacheKey = getCacheKey(element);
var eventCacheKey = cacheKey + ' ' + className;
var stagger = {};
var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
var stagger = {};
if(itemIndex > 0) {
var staggerClassName = className + '-stagger';
var staggerCacheKey = cacheKey + ' ' + staggerClassName;
@@ -1144,60 +1219,63 @@ angular.module('ngAnimate', ['ng'])
element.addClass(className);
var formerData = element.data(NG_ANIMATE_CSS_DATA_KEY) || {};
var timings = calculationDecorator(function() {
return getElementAnimationDetails(element, eventCacheKey);
});
/* there is no point in performing a reflow if the animation
timeout is empty (this would cause a flicker bug normally
in the page. There is also no point in performing an animation
that only has a delay and no duration */
var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay);
var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
if(maxDuration === 0) {
var transitionDuration = timings.transitionDuration;
var animationDuration = timings.animationDuration;
if(transitionDuration === 0 && animationDuration === 0) {
element.removeClass(className);
return false;
}
element.data(NG_ANIMATE_CSS_DATA_KEY, {
running : formerData.running || 0,
itemIndex : itemIndex,
stagger : stagger,
timings : timings,
closeAnimationFn : angular.noop
});
//temporarily disable the transition so that the enter styles
//don't animate twice (this is here to avoid a bug in Chrome/FF).
var activeClassName = '';
timings.transitionDuration > 0 ?
blockTransitions(element) :
var isCurrentlyAnimating = formerData.running > 0 || animationEvent == 'setClass';
if(transitionDuration > 0) {
blockTransitions(element, className, isCurrentlyAnimating);
}
if(animationDuration > 0) {
blockKeyframeAnimations(element);
forEach(className.split(' '), function(klass, i) {
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
});
element.data(NG_ANIMATE_CSS_DATA_KEY, {
className : className,
activeClassName : activeClassName,
maxDuration : maxDuration,
maxDelay : maxDelay,
classes : className + ' ' + activeClassName,
timings : timings,
stagger : stagger,
itemIndex : itemIndex
});
}
return true;
}
function blockTransitions(element) {
extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
function isStructuralAnimation(className) {
return className == 'ng-enter' || className == 'ng-move' || className == 'ng-leave';
}
function blockTransitions(element, className, isAnimating) {
if(isStructuralAnimation(className) || !isAnimating) {
extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none';
} else {
element.addClass(NG_ANIMATE_BLOCK_CLASS_NAME);
}
}
function blockKeyframeAnimations(element) {
extractElementNode(element).style[ANIMATION_PROP] = 'none 0s';
}
function unblockTransitions(element) {
function unblockTransitions(element, className) {
var prop = TRANSITION_PROP + PROPERTY_KEY;
var node = extractElementNode(element);
if(node.style[prop] && node.style[prop].length > 0) {
node.style[prop] = '';
}
element.removeClass(NG_ANIMATE_BLOCK_CLASS_NAME);
}
function unblockKeyframeAnimations(element) {
@@ -1208,22 +1286,28 @@ angular.module('ngAnimate', ['ng'])
}
}
function animateRun(element, className, activeAnimationComplete) {
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
function animateRun(animationEvent, element, className, activeAnimationComplete) {
var node = extractElementNode(element);
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
if(node.className.indexOf(className) == -1 || !elementData) {
activeAnimationComplete();
return;
}
var timings = elementData.timings;
var activeClassName = '';
forEach(className.split(' '), function(klass, i) {
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
});
var stagger = elementData.stagger;
var maxDuration = elementData.maxDuration;
var activeClassName = elementData.activeClassName;
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * ONE_SECOND;
var timings = elementData.timings;
var itemIndex = elementData.itemIndex;
var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay);
var maxDelayTime = maxDelay * ONE_SECOND;
var startTime = Date.now();
var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
var itemIndex = elementData.itemIndex;
var style = '', appliedStyles = [];
if(timings.transitionDuration > 0) {
@@ -1265,6 +1349,13 @@ angular.module('ngAnimate', ['ng'])
onEnd();
activeAnimationComplete();
};
var staggerTime = itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0);
var animationTime = (maxDelay + maxDuration) * CLOSING_TIME_BUFFER;
var totalTime = (staggerTime + animationTime) * ONE_SECOND;
elementData.running++;
animationCloseHandler(element, totalTime);
return onEnd;
// This will automatically be called by $animate so
@@ -1284,7 +1375,7 @@ angular.module('ngAnimate', ['ng'])
event.stopPropagation();
var ev = event.originalEvent || event;
var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
/* Firefox (or possibly just Gecko) likes to not round values up
* when a ms measurement is used for the animation */
var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES));
@@ -1311,28 +1402,28 @@ angular.module('ngAnimate', ['ng'])
return style;
}
function animateBefore(element, className, calculationDecorator) {
if(animateSetup(element, className, calculationDecorator)) {
function animateBefore(animationEvent, element, className, calculationDecorator) {
if(animateSetup(animationEvent, element, className, calculationDecorator)) {
return function(cancelled) {
cancelled && animateClose(element, className);
};
}
}
function animateAfter(element, className, afterAnimationComplete) {
function animateAfter(animationEvent, element, className, afterAnimationComplete) {
if(element.data(NG_ANIMATE_CSS_DATA_KEY)) {
return animateRun(element, className, afterAnimationComplete);
return animateRun(animationEvent, element, className, afterAnimationComplete);
} else {
animateClose(element, className);
afterAnimationComplete();
}
}
function animate(element, className, animationComplete) {
function animate(animationEvent, element, className, animationComplete) {
//If the animateSetup function doesn't bother returning a
//cancellation function then it means that there is no animation
//to perform at all
var preReflowCancellation = animateBefore(element, className);
var preReflowCancellation = animateBefore(animationEvent, element, className);
if(!preReflowCancellation) {
animationComplete();
return;
@@ -1345,12 +1436,12 @@ angular.module('ngAnimate', ['ng'])
//happen in the first place
var cancel = preReflowCancellation;
afterReflow(element, function() {
unblockTransitions(element);
unblockTransitions(element, className);
unblockKeyframeAnimations(element);
//once the reflow is complete then we point cancel to
//the new cancellation function which will remove all of the
//animation properties from the active animation
cancel = animateAfter(element, className, animationComplete);
cancel = animateAfter(animationEvent, element, className, animationComplete);
});
return function(cancelled) {
@@ -1360,54 +1451,59 @@ angular.module('ngAnimate', ['ng'])
function animateClose(element, className) {
element.removeClass(className);
element.removeData(NG_ANIMATE_CSS_DATA_KEY);
var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
if(data) {
if(data.running) {
data.running--;
}
if(!data.running || data.running === 0) {
element.removeData(NG_ANIMATE_CSS_DATA_KEY);
}
}
}
return {
allowCancel : function(element, animationEvent, className) {
//always cancel the current animation if it is a
//structural animation
var oldClasses = (element.data(NG_ANIMATE_CSS_DATA_KEY) || {}).classes;
if(!oldClasses || ['enter','leave','move'].indexOf(animationEvent) >= 0) {
return true;
}
var parentElement = element.parent();
var clone = angular.element(extractElementNode(element).cloneNode());
//make the element super hidden and override any CSS style values
clone.attr('style','position:absolute; top:-9999px; left:-9999px');
clone.removeAttr('id');
clone.empty();
forEach(oldClasses.split(' '), function(klass) {
clone.removeClass(klass);
});
var suffix = animationEvent == 'addClass' ? '-add' : '-remove';
clone.addClass(suffixClasses(className, suffix));
parentElement.append(clone);
var timings = getElementAnimationDetails(clone);
clone.remove();
return Math.max(timings.transitionDuration, timings.animationDuration) > 0;
},
enter : function(element, animationCompleted) {
return animate(element, 'ng-enter', animationCompleted);
return animate('enter', element, 'ng-enter', animationCompleted);
},
leave : function(element, animationCompleted) {
return animate(element, 'ng-leave', animationCompleted);
return animate('leave', element, 'ng-leave', animationCompleted);
},
move : function(element, animationCompleted) {
return animate(element, 'ng-move', animationCompleted);
return animate('move', element, 'ng-move', animationCompleted);
},
beforeSetClass : function(element, add, remove, animationCompleted) {
var className = suffixClasses(remove, '-remove') + ' ' +
suffixClasses(add, '-add');
var cancellationMethod = animateBefore('setClass', element, className, function(fn) {
/* when classes are removed from an element then the transition style
* that is applied is the transition defined on the element without the
* CSS class being there. This is how CSS3 functions outside of ngAnimate.
* http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */
var klass = element.attr('class');
element.removeClass(remove);
element.addClass(add);
var timings = fn();
element.attr('class', klass);
return timings;
});
if(cancellationMethod) {
afterReflow(element, function() {
unblockTransitions(element, className);
unblockKeyframeAnimations(element);
animationCompleted();
});
return cancellationMethod;
}
animationCompleted();
},
beforeAddClass : function(element, className, animationCompleted) {
var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'), function(fn) {
var cancellationMethod = animateBefore('addClass', element, suffixClasses(className, '-add'), function(fn) {
/* when a CSS class is added to an element then the transition style that
* is applied is the transition defined on the element when the CSS class
@@ -1421,7 +1517,7 @@ angular.module('ngAnimate', ['ng'])
if(cancellationMethod) {
afterReflow(element, function() {
unblockTransitions(element);
unblockTransitions(element, className);
unblockKeyframeAnimations(element);
animationCompleted();
});
@@ -1430,12 +1526,19 @@ angular.module('ngAnimate', ['ng'])
animationCompleted();
},
setClass : function(element, add, remove, animationCompleted) {
remove = suffixClasses(remove, '-remove');
add = suffixClasses(add, '-add');
var className = remove + ' ' + add;
return animateAfter('setClass', element, className, animationCompleted);
},
addClass : function(element, className, animationCompleted) {
return animateAfter(element, suffixClasses(className, '-add'), animationCompleted);
return animateAfter('addClass', element, suffixClasses(className, '-add'), animationCompleted);
},
beforeRemoveClass : function(element, className, animationCompleted) {
var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'), function(fn) {
var cancellationMethod = animateBefore('removeClass', element, suffixClasses(className, '-remove'), function(fn) {
/* when classes are removed from an element then the transition style
* that is applied is the transition defined on the element without the
* CSS class being there. This is how CSS3 functions outside of ngAnimate.
@@ -1449,7 +1552,7 @@ angular.module('ngAnimate', ['ng'])
if(cancellationMethod) {
afterReflow(element, function() {
unblockTransitions(element);
unblockTransitions(element, className);
unblockKeyframeAnimations(element);
animationCompleted();
});
@@ -1459,7 +1562,7 @@ angular.module('ngAnimate', ['ng'])
},
removeClass : function(element, className, animationCompleted) {
return animateAfter(element, suffixClasses(className, '-remove'), animationCompleted);
return animateAfter('removeClass', element, suffixClasses(className, '-remove'), animationCompleted);
}
};
+2 -2
View File
@@ -17,7 +17,7 @@ $provide.value("$locale", {
"\u0161e\u0161tadienis"
],
"MONTH": [
"sausio",
"sausis",
"vasaris",
"kovas",
"balandis",
@@ -41,7 +41,7 @@ $provide.value("$locale", {
],
"SHORTMONTH": [
"Saus.",
"Vas",
"Vas.",
"Kov.",
"Bal.",
"Geg.",
+2 -2
View File
@@ -17,7 +17,7 @@ $provide.value("$locale", {
"\u0161e\u0161tadienis"
],
"MONTH": [
"sausio",
"sausis",
"vasaris",
"kovas",
"balandis",
@@ -41,7 +41,7 @@ $provide.value("$locale", {
],
"SHORTMONTH": [
"Saus.",
"Vas",
"Vas.",
"Kov.",
"Bal.",
"Geg.",
+18 -47
View File
@@ -504,6 +504,7 @@ angular.mock.$IntervalProvider = function() {
};
$interval.cancel = function(promise) {
if(!promise) return false;
var fnIndex;
angular.forEach(repeatFns, function(fn, index) {
@@ -756,70 +757,40 @@ angular.mock.TzDate = function (offset, timestamp) {
angular.mock.TzDate.prototype = Date.prototype;
/* jshint +W101 */
// TODO(matias): remove this IMMEDIATELY once we can properly detect the
// presence of a registered module
var animateLoaded;
try {
angular.module('ngAnimate');
animateLoaded = true;
} catch(e) {}
angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
if(animateLoaded) {
angular.module('ngAnimate').config(['$provide', function($provide) {
.config(['$provide', function($provide) {
var reflowQueue = [];
$provide.value('$$animateReflow', function(fn) {
reflowQueue.push(fn);
return angular.noop;
});
$provide.decorator('$animate', function($delegate) {
$delegate.triggerReflow = function() {
if(reflowQueue.length === 0) {
throw new Error('No animation reflows present');
}
angular.forEach(reflowQueue, function(fn) {
fn();
});
reflowQueue = [];
};
return $delegate;
});
}]);
}
angular.mock.animate = angular.module('mock.animate', ['ng'])
.config(['$provide', function($provide) {
$provide.decorator('$animate', function($delegate) {
var animate = {
queue : [],
enabled : $delegate.enabled,
flushNext : function(name) {
var tick = animate.queue.shift();
if (!tick) throw new Error('No animation to be flushed');
if(tick.method !== name) {
throw new Error('The next animation is not "' + name +
'", but is "' + tick.method + '"');
triggerReflow : function() {
if(reflowQueue.length === 0) {
throw new Error('No animation reflows present');
}
tick.fn();
return tick;
angular.forEach(reflowQueue, function(fn) {
fn();
});
reflowQueue = [];
}
};
angular.forEach(['enter','leave','move','addClass','removeClass'], function(method) {
angular.forEach(
['enter','leave','move','addClass','removeClass','setClass'], function(method) {
animate[method] = function() {
var params = arguments;
animate.queue.push({
method : method,
params : params,
element : angular.isElement(params[0]) && params[0],
parent : angular.isElement(params[1]) && params[1],
after : angular.isElement(params[2]) && params[2],
fn : function() {
$delegate[method].apply($delegate, params);
}
event : method,
element : arguments[0],
args : arguments
});
$delegate[method].apply($delegate, arguments);
};
});
@@ -2125,7 +2096,7 @@ if(window.jasmine || window.mocha) {
window.inject = angular.mock.inject = function() {
var blockFns = Array.prototype.slice.call(arguments, 0);
var errorForStack = new Error('Declaration Location');
return isSpecRunning() ? workFn() : workFn;
return isSpecRunning() ? workFn.call(currentSpec) : workFn;
/////////////////////
function workFn() {
var modules = currentSpec.$modules || [];
+1 -1
View File
@@ -35,7 +35,7 @@ function shallowClearAndCopy(src, dst) {
});
for (var key in src) {
if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') {
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
dst[key] = src[key];
}
}
+22 -1
View File
@@ -190,7 +190,7 @@ describe('angular', function() {
expect(copy.key).toBe(original.key);
});
it('should not copy $$ properties nor prototype properties', function() {
it('should omit "$$"-prefixed properties', function() {
var original = {$$some: true, $$: true};
var clone = {};
@@ -198,6 +198,27 @@ describe('angular', function() {
expect(clone.$$some).toBeUndefined();
expect(clone.$$).toBeUndefined();
});
it('should copy "$"-prefixed properties from copy', function() {
var original = {$some: true};
var clone = {};
expect(shallowCopy(original, clone)).toBe(clone);
expect(clone.$some).toBe(original.$some);
});
it('should omit properties from prototype chain', function() {
var original, clone = {};
function Func() {};
Func.prototype.hello = "world";
original = new Func();
original.goodbye = "world";
expect(shallowCopy(original, clone)).toBe(clone);
expect(clone.hello).toBeUndefined();
expect(clone.goodbye).toBe("world");
});
});
describe('elementHTML', function() {
-1
View File
@@ -30,7 +30,6 @@ describe('docs.angularjs.org', function () {
browser.sleep(500);
var nameInput = element(by.input('user.name'));
nameInput.click();
nameInput.sendKeys('!!!');
var code = element(by.css('.doc-example-live tt'));
+28
View File
@@ -65,6 +65,17 @@ describe('jqLite', function() {
});
it('should allow construction of html with leading whitespace', function() {
var nodes = jqLite(' \n\r \r\n<div>1</div><span>2</span>');
expect(nodes[0].parentNode).toBeDefined();
expect(nodes[0].parentNode.nodeType).toBe(11); /** Document Fragment **/;
expect(nodes[0].parentNode).toBe(nodes[1].parentNode);
expect(nodes.length).toBe(2);
expect(nodes[0].innerHTML).toBe('1');
expect(nodes[1].innerHTML).toBe('2');
});
it('should allow creation of comment tags', function() {
var nodes = jqLite('<!-- foo -->');
expect(nodes.length).toBe(1);
@@ -86,6 +97,23 @@ describe('jqLite', function() {
});
});
describe('_data', function() {
it('should provide access to the data present on the element', function() {
var element = jqLite('<i>foo</i>');
var data = ['value'];
element.data('val', data);
expect(angular.element._data(element[0]).data.val).toBe(data);
dealoc(element);
});
it('should provide access to the events present on the element', function() {
var element = jqLite('<i>foo</i>');
expect(angular.element._data(element[0]).events).toBeUndefined();
element.on('click', function() { });
expect(angular.element._data(element[0]).events.click).toBeDefined();
});
});
describe('inheritedData', function() {
+159 -9
View File
@@ -517,6 +517,22 @@ describe('$compile', function() {
expect(element).toBe(attr.$$element);
}
}));
directive('replaceWithTr', valueFn({
replace: true,
template: '<tr><td>TR</td></tr>'
}));
directive('replaceWithTd', valueFn({
replace: true,
template: '<td>TD</td>'
}));
directive('replaceWithTh', valueFn({
replace: true,
template: '<th>TH</th>'
}));
directive('replaceWithTbody', valueFn({
replace: true,
template: '<tbody><tr><td>TD</td></tr></tbody>'
}));
}));
@@ -680,6 +696,34 @@ describe('$compile', function() {
}).not.toThrow();
});
});
it('should support templates with root <tr> tags', inject(function($compile, $rootScope) {
expect(function() {
element = $compile('<div replace-with-tr></div>')($rootScope);
}).not.toThrow();
expect(nodeName_(element)).toMatch(/tr/i);
}));
it('should support templates with root <td> tags', inject(function($compile, $rootScope) {
expect(function() {
element = $compile('<div replace-with-td></div>')($rootScope);
}).not.toThrow();
expect(nodeName_(element)).toMatch(/td/i);
}));
it('should support templates with root <th> tags', inject(function($compile, $rootScope) {
expect(function() {
element = $compile('<div replace-with-th></div>')($rootScope);
}).not.toThrow();
expect(nodeName_(element)).toMatch(/th/i);
}));
it('should support templates with root <tbody> tags', inject(function($compile, $rootScope) {
expect(function() {
element = $compile('<div replace-with-tbody></div>')($rootScope);
}).not.toThrow();
expect(nodeName_(element)).toMatch(/tbody/i);
}));
});
@@ -776,6 +820,23 @@ describe('$compile', function() {
replace: true,
template: '<span>Hello, {{name}}!</span>'
}));
directive('replaceWithTr', valueFn({
replace: true,
templateUrl: 'tr.html'
}));
directive('replaceWithTd', valueFn({
replace: true,
templateUrl: 'td.html'
}));
directive('replaceWithTh', valueFn({
replace: true,
templateUrl: 'th.html'
}));
directive('replaceWithTbody', valueFn({
replace: true,
templateUrl: 'tbody.html'
}));
}
));
@@ -1411,6 +1472,42 @@ describe('$compile', function() {
expect(element.html()).toContain('i = 1');
});
});
it('should support templates with root <tr> tags', inject(function($compile, $rootScope, $templateCache) {
$templateCache.put('tr.html', '<tr><td>TR</td></tr>');
expect(function() {
element = $compile('<div replace-with-tr></div>')($rootScope);
}).not.toThrow();
$rootScope.$digest();
expect(nodeName_(element)).toMatch(/tr/i);
}));
it('should support templates with root <td> tags', inject(function($compile, $rootScope, $templateCache) {
$templateCache.put('td.html', '<td>TD</td>');
expect(function() {
element = $compile('<div replace-with-td></div>')($rootScope);
}).not.toThrow();
$rootScope.$digest();
expect(nodeName_(element)).toMatch(/td/i);
}));
it('should support templates with root <th> tags', inject(function($compile, $rootScope, $templateCache) {
$templateCache.put('th.html', '<th>TH</th>');
expect(function() {
element = $compile('<div replace-with-th></div>')($rootScope);
}).not.toThrow();
$rootScope.$digest();
expect(nodeName_(element)).toMatch(/th/i);
}));
it('should support templates with root <tbody> tags', inject(function($compile, $rootScope, $templateCache) {
$templateCache.put('tbody.html', '<tbody><tr><td>TD</td></tr></tbody>');
expect(function() {
element = $compile('<div replace-with-tbody></div>')($rootScope);
}).not.toThrow();
$rootScope.$digest();
expect(nodeName_(element)).toMatch(/tbody/i);
}));
});
@@ -3972,6 +4069,57 @@ describe('$compile', function() {
});
});
// issue #6006
it('should link directive with $element as a comment node', function() {
module(function($provide) {
directive('innerAgain', function(log) {
return {
transclude: 'element',
link: function(scope, element, attr, controllers, transclude) {
log('innerAgain:'+lowercase(nodeName_(element))+':'+trim(element[0].data));
transclude(scope, function(clone) {
element.parent().append(clone);
});
}
};
});
directive('inner', function(log) {
return {
replace: true,
templateUrl: 'inner.html',
link: function(scope, element) {
log('inner:'+lowercase(nodeName_(element))+':'+trim(element[0].data));
}
};
});
directive('outer', function(log) {
return {
transclude: 'element',
link: function(scope, element, attrs, controllers, transclude) {
log('outer:'+lowercase(nodeName_(element))+':'+trim(element[0].data));
transclude(scope, function(clone) {
element.parent().append(clone);
});
}
};
});
});
inject(function(log, $compile, $rootScope, $templateCache) {
$templateCache.put('inner.html', '<div inner-again><p>Content</p></div>');
element = $compile('<div><div outer><div inner></div></div></div>')($rootScope);
$rootScope.$digest();
var child = element.children();
expect(log.toArray()).toEqual([
"outer:#comment:outer:",
"innerAgain:#comment:innerAgain:",
"inner:#comment:innerAgain:"]);
expect(child.length).toBe(1);
expect(child.contents().length).toBe(2);
expect(lowercase(nodeName_(child.contents().eq(0)))).toBe('#comment');
expect(lowercase(nodeName_(child.contents().eq(1)))).toBe('div');
});
});
});
it('should safely create transclude comment node and not break with "-->"',
@@ -4490,7 +4638,7 @@ describe('$compile', function() {
describe('$animate animation hooks', function() {
beforeEach(module('mock.animate'));
beforeEach(module('ngAnimateMock'));
it('should automatically fire the addClass and removeClass animation hooks',
inject(function($compile, $animate, $rootScope) {
@@ -4506,8 +4654,9 @@ describe('$compile', function() {
$rootScope.val2 = 'rice';
$rootScope.$digest();
data = $animate.flushNext('addClass');
expect(data.params[1]).toBe('ice rice');
data = $animate.queue.shift();
expect(data.event).toBe('addClass');
expect(data.args[1]).toBe('ice rice');
expect(element.hasClass('ice')).toBe(true);
expect(element.hasClass('rice')).toBe(true);
@@ -4516,10 +4665,10 @@ describe('$compile', function() {
$rootScope.val2 = 'dice';
$rootScope.$digest();
data = $animate.flushNext('removeClass');
expect(data.params[1]).toBe('rice');
data = $animate.flushNext('addClass');
expect(data.params[1]).toBe('dice');
data = $animate.queue.shift();
expect(data.event).toBe('setClass');
expect(data.args[1]).toBe('dice');
expect(data.args[2]).toBe('rice');
expect(element.hasClass('ice')).toBe(true);
expect(element.hasClass('dice')).toBe(true);
@@ -4529,8 +4678,9 @@ describe('$compile', function() {
$rootScope.val2 = '';
$rootScope.$digest();
data = $animate.flushNext('removeClass');
expect(data.params[1]).toBe('ice dice');
data = $animate.queue.shift();
expect(data.event).toBe('removeClass');
expect(data.args[1]).toBe('ice dice');
expect(element.hasClass('ice')).toBe(false);
expect(element.hasClass('dice')).toBe(false);
+11
View File
@@ -509,6 +509,17 @@ describe('input', function() {
});
}
it('should update the model on "compositionend"', function() {
compileInput('<input type="text" ng-model="name" name="alias" />');
if (!(msie < 9)) {
browserTrigger(inputElm, 'compositionstart');
changeInputValueTo('caitp');
expect(scope.name).toBeUndefined();
browserTrigger(inputElm, 'compositionend');
expect(scope.name).toEqual('caitp');
}
});
describe('"change" event', function() {
function assertBrowserSupportsChangeEvent(inputEventSupported) {
// Force browser to report a lack of an 'input' event
+16 -16
View File
@@ -309,7 +309,7 @@ describe('ngClass animations', function() {
var body, element, $rootElement;
it("should avoid calling addClass accidentally when removeClass is going on", function() {
module('mock.animate');
module('ngAnimateMock');
inject(function($compile, $rootScope, $animate, $timeout) {
var element = angular.element('<div ng-class="val"></div>');
var body = jqLite(document.body);
@@ -320,23 +320,22 @@ describe('ngClass animations', function() {
$rootScope.val = 'one';
$rootScope.$digest();
$animate.flushNext('addClass');
expect($animate.queue.shift().event).toBe('addClass');
expect($animate.queue.length).toBe(0);
$rootScope.val = '';
$rootScope.$digest();
$animate.flushNext('removeClass'); //only removeClass is called
expect($animate.queue.shift().event).toBe('removeClass'); //only removeClass is called
expect($animate.queue.length).toBe(0);
$rootScope.val = 'one';
$rootScope.$digest();
$animate.flushNext('addClass');
expect($animate.queue.shift().event).toBe('addClass');
expect($animate.queue.length).toBe(0);
$rootScope.val = 'two';
$rootScope.$digest();
$animate.flushNext('removeClass');
$animate.flushNext('addClass');
expect($animate.queue.shift().event).toBe('setClass');
expect($animate.queue.length).toBe(0);
});
});
@@ -416,7 +415,7 @@ describe('ngClass animations', function() {
});
it("should not remove classes if they're going to be added back right after", function() {
module('mock.animate');
module('ngAnimateMock');
inject(function($rootScope, $compile, $animate) {
var className;
@@ -430,16 +429,18 @@ describe('ngClass animations', function() {
$rootScope.$digest();
//this fires twice due to the class observer firing
className = $animate.flushNext('addClass').params[1];
expect(className).toBe('one two three');
var item = $animate.queue.shift();
expect(item.event).toBe('addClass');
expect(item.args[1]).toBe('one two three');
expect($animate.queue.length).toBe(0);
$rootScope.three = false;
$rootScope.$digest();
className = $animate.flushNext('removeClass').params[1];
expect(className).toBe('three');
item = $animate.queue.shift();
expect(item.event).toBe('removeClass');
expect(item.args[1]).toBe('three');
expect($animate.queue.length).toBe(0);
@@ -447,11 +448,10 @@ describe('ngClass animations', function() {
$rootScope.three = true;
$rootScope.$digest();
className = $animate.flushNext('removeClass').params[1];
expect(className).toBe('two');
className = $animate.flushNext('addClass').params[1];
expect(className).toBe('three');
item = $animate.queue.shift();
expect(item.event).toBe('setClass');
expect(item.args[1]).toBe('three');
expect(item.args[2]).toBe('two');
expect($animate.queue.length).toBe(0);
});
+11 -8
View File
@@ -210,7 +210,7 @@ describe('ngIf animations', function () {
return element;
}
beforeEach(module('mock.animate'));
beforeEach(module('ngAnimateMock'));
beforeEach(module(function() {
// we need to run animation on attached elements;
@@ -245,8 +245,9 @@ describe('ngIf animations', function () {
$rootScope.$digest();
$scope.$apply('value = true');
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('Hi');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('Hi');
expect(element.children().length).toBe(1);
}));
@@ -262,14 +263,16 @@ describe('ngIf animations', function () {
))($scope);
$scope.$apply('value = true');
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('Hi');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('Hi');
$scope.$apply('value = false');
expect(element.children().length).toBe(1);
$scope.$apply('value = false');
item = $animate.flushNext('leave').element;
expect(item.text()).toBe('Hi');
item = $animate.queue.shift();
expect(item.event).toBe('leave');
expect(item.element.text()).toBe('Hi');
expect(element.children().length).toBe(0);
}));
+24 -21
View File
@@ -353,7 +353,7 @@ describe('ngInclude', function() {
};
}
beforeEach(module(spyOnAnchorScroll(), 'mock.animate'));
beforeEach(module(spyOnAnchorScroll(), 'ngAnimateMock'));
beforeEach(inject(
putIntoCache('template.html', 'CONTENT'),
putIntoCache('another.html', 'CONTENT')));
@@ -367,7 +367,7 @@ describe('ngInclude', function() {
});
expect(autoScrollSpy).not.toHaveBeenCalled();
$animate.flushNext('enter');
expect($animate.queue.shift().event).toBe('enter');
$timeout.flush();
expect(autoScrollSpy).toHaveBeenCalledOnce();
@@ -384,7 +384,7 @@ describe('ngInclude', function() {
$rootScope.value = true;
});
$animate.flushNext('enter');
expect($animate.queue.shift().event).toBe('enter');
$timeout.flush();
$rootScope.$apply(function () {
@@ -392,8 +392,8 @@ describe('ngInclude', function() {
$rootScope.value = 'some-string';
});
$animate.flushNext('leave');
$animate.flushNext('enter');
expect($animate.queue.shift().event).toBe('leave');
expect($animate.queue.shift().event).toBe('enter');
$timeout.flush();
$rootScope.$apply(function() {
@@ -401,8 +401,8 @@ describe('ngInclude', function() {
$rootScope.value = 100;
});
$animate.flushNext('leave');
$animate.flushNext('enter');
expect($animate.queue.shift().event).toBe('leave');
expect($animate.queue.shift().event).toBe('enter');
$timeout.flush();
expect(autoScrollSpy).toHaveBeenCalled();
@@ -418,7 +418,7 @@ describe('ngInclude', function() {
$rootScope.tpl = 'template.html';
});
$animate.flushNext('enter');
expect($animate.queue.shift().event).toBe('enter');
$timeout.flush();
expect(autoScrollSpy).not.toHaveBeenCalled();
}));
@@ -434,7 +434,7 @@ describe('ngInclude', function() {
$rootScope.value = false;
});
$animate.flushNext('enter');
expect($animate.queue.shift().event).toBe('enter');
$timeout.flush();
$rootScope.$apply(function () {
@@ -456,7 +456,7 @@ describe('ngInclude', function() {
expect(autoScrollSpy).not.toHaveBeenCalled();
$rootScope.$apply("tpl = 'template.html'");
$animate.flushNext('enter');
expect($animate.queue.shift().event).toBe('enter');
$timeout.flush();
expect(autoScrollSpy).toHaveBeenCalledOnce();
@@ -589,7 +589,7 @@ describe('ngInclude animations', function() {
dealoc(element);
});
beforeEach(module('mock.animate'));
beforeEach(module('ngAnimateMock'));
afterEach(function(){
dealoc(element);
@@ -608,8 +608,9 @@ describe('ngInclude animations', function() {
))($rootScope);
$rootScope.$digest();
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('data');
var animation = $animate.queue.pop();
expect(animation.event).toBe('enter');
expect(animation.element.text()).toBe('data');
}));
it('should fire off the leave animation',
@@ -624,14 +625,16 @@ describe('ngInclude animations', function() {
))($rootScope);
$rootScope.$digest();
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('data');
var animation = $animate.queue.shift();
expect(animation.event).toBe('enter');
expect(animation.element.text()).toBe('data');
$rootScope.tpl = '';
$rootScope.$digest();
item = $animate.flushNext('leave').element;
expect(item.text()).toBe('data');
animation = $animate.queue.shift();
expect(animation.event).toBe('leave');
expect(animation.element.text()).toBe('data');
}));
it('should animate two separate ngInclude elements',
@@ -647,14 +650,14 @@ describe('ngInclude animations', function() {
))($rootScope);
$rootScope.$digest();
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('one');
var item1 = $animate.queue.shift().element;
expect(item1.text()).toBe('one');
$rootScope.tpl = 'two';
$rootScope.$digest();
var itemA = $animate.flushNext('leave').element;
var itemB = $animate.flushNext('enter').element;
var itemA = $animate.queue.shift().element;
var itemB = $animate.queue.shift().element;
expect(itemA.attr('ng-include')).toBe('tpl');
expect(itemB.attr('ng-include')).toBe('tpl');
expect(itemA).not.toEqual(itemB);
+37 -25
View File
@@ -1149,7 +1149,7 @@ describe('ngRepeat animations', function() {
return element;
}
beforeEach(module('mock.animate'));
beforeEach(module('ngAnimateMock'));
beforeEach(module(function() {
// we need to run animation on attached elements;
@@ -1182,14 +1182,17 @@ describe('ngRepeat animations', function() {
$rootScope.items = ['1','2','3'];
$rootScope.$digest();
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('1');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('1');
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('2');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('2');
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('3');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('3');
}));
it('should fire off the leave animation',
@@ -1207,20 +1210,24 @@ describe('ngRepeat animations', function() {
$rootScope.items = ['1','2','3'];
$rootScope.$digest();
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('1');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('1');
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('2');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('2');
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('3');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('3');
$rootScope.items = ['1','3'];
$rootScope.$digest();
item = $animate.flushNext('leave').element;
expect(item.text()).toBe('2');
item = $animate.queue.shift();
expect(item.event).toBe('leave');
expect(item.element.text()).toBe('2');
}));
it('should fire off the move animation',
@@ -1239,23 +1246,28 @@ describe('ngRepeat animations', function() {
$rootScope.items = ['1','2','3'];
$rootScope.$digest();
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('1');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('1');
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('2');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('2');
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('3');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('3');
$rootScope.items = ['2','3','1'];
$rootScope.$digest();
item = $animate.flushNext('move').element;
expect(item.text()).toBe('2');
item = $animate.queue.shift();
expect(item.event).toBe('move');
expect(item.element.text()).toBe('2');
item = $animate.flushNext('move').element;
expect(item.text()).toBe('1');
item = $animate.queue.shift();
expect(item.event).toBe('move');
expect(item.element.text()).toBe('3');
}));
});
+17 -13
View File
@@ -73,7 +73,7 @@ describe('ngShow / ngHide animations', function() {
body.removeAttr('ng-animation-running');
});
beforeEach(module('mock.animate'));
beforeEach(module('ngAnimateMock'));
beforeEach(module(function($animateProvider, $provide) {
return function(_$rootElement_) {
@@ -91,16 +91,18 @@ describe('ngShow / ngHide animations', function() {
))($scope);
$scope.$digest();
item = $animate.flushNext('removeClass').element;
expect(item.text()).toBe('data');
expect(item).toBeShown();
item = $animate.queue.shift();
expect(item.event).toBe('removeClass');
expect(item.element.text()).toBe('data');
expect(item.element).toBeShown();
$scope.on = false;
$scope.$digest();
item = $animate.flushNext('addClass').element;
expect(item.text()).toBe('data');
expect(item).toBeHidden();
item = $animate.queue.shift();
expect(item.event).toBe('addClass');
expect(item.element.text()).toBe('data');
expect(item.element).toBeHidden();
}));
});
@@ -114,16 +116,18 @@ describe('ngShow / ngHide animations', function() {
))($scope);
$scope.$digest();
item = $animate.flushNext('addClass').element;
expect(item.text()).toBe('datum');
expect(item).toBeHidden();
item = $animate.queue.shift();
expect(item.event).toBe('addClass');
expect(item.element.text()).toBe('datum');
expect(item.element).toBeHidden();
$scope.off = false;
$scope.$digest();
item = $animate.flushNext('removeClass').element;
expect(item.text()).toBe('datum');
expect(item).toBeShown();
item = $animate.queue.shift();
expect(item.event).toBe('removeClass');
expect(item.element.text()).toBe('datum');
expect(item.element).toBeShown();
}));
});
});
+13 -9
View File
@@ -223,7 +223,7 @@ describe('ngSwitch animations', function() {
return element;
}
beforeEach(module('mock.animate'));
beforeEach(module('ngAnimateMock'));
beforeEach(module(function() {
// we need to run animation on attached elements;
@@ -255,8 +255,9 @@ describe('ngSwitch animations', function() {
$scope.val = 'one';
$scope.$digest();
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('one');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('one');
}));
@@ -276,17 +277,20 @@ describe('ngSwitch animations', function() {
$scope.val = 'two';
$scope.$digest();
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('two');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('two');
$scope.val = 'three';
$scope.$digest();
item = $animate.flushNext('leave').element;
expect(item.text()).toBe('two');
item = $animate.queue.shift();
expect(item.event).toBe('leave');
expect(item.element.text()).toBe('two');
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('three');
item = $animate.queue.shift();
expect(item.event).toBe('enter');
expect(item.element.text()).toBe('three');
}));
});
+11
View File
@@ -70,6 +70,17 @@ describe('Filter: filter', function() {
});
it('should support deep predicate objects', function() {
var items = [{person: {name: 'John'}},
{person: {name: 'Rita'}},
{person: {name: 'Billy'}},
{person: {name: 'Joan'}}];
expect(filter(items, {person: {name: 'Jo'}}).length).toBe(2);
expect(filter(items, {person: {name: 'Jo'}})).toEqual([
{person: {name: 'John'}}, {person: {name: 'Joan'}}]);
});
it('should match any properties for given "$" property', function() {
var items = [{first: 'tom', last: 'hevery'},
{first: 'adam', last: 'hevery', alias: 'tom', done: false},
+85 -93
View File
@@ -3,6 +3,7 @@
describe("ngAnimate", function() {
beforeEach(module('ngAnimate'));
beforeEach(module('ngAnimateMock'));
it("should disable animations on bootstrap for structural animations even after the first digest has passed", function() {
@@ -481,7 +482,7 @@ describe("ngAnimate", function() {
element.append(child);
child.attr('style', 'width: 20px');
$animate.addClass(child, 'ng-hide');
$animate.leave(child);
$rootScope.$digest();
@@ -489,11 +490,11 @@ describe("ngAnimate", function() {
if($sniffer.transitions) {
$animate.triggerReflow();
//this is to verify that the existing style is appended with a semicolon automatically
expect(child.attr('style')).toMatch(/width: 20px;.+?/i);
//this is to verify that the existing style is appended with a semicolon automatically
expect(child.attr('style')).toMatch(/width: 20px;.*?/i);
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
}
expect(child.attr('style')).toMatch(/width: 20px/i);
}));
@@ -522,7 +523,7 @@ describe("ngAnimate", function() {
var completed = false;
$animate.enter(child, element, null, function() {
completed = true;
completed = true;
});
$rootScope.$digest();
@@ -563,7 +564,7 @@ describe("ngAnimate", function() {
});
});
it("should fire the cancel/end function with the correct flag in the parameters",
it("should not apply a cancellation when addClass is done multiple times",
inject(function($animate, $rootScope, $sniffer, $timeout) {
element.append(child);
@@ -571,7 +572,7 @@ describe("ngAnimate", function() {
$animate.addClass(child, 'custom-delay');
$animate.addClass(child, 'custom-long-delay');
expect(child.hasClass('animation-cancelled')).toBe(true);
expect(child.hasClass('animation-cancelled')).toBe(false);
expect(child.hasClass('animation-ended')).toBe(false);
$timeout.flush();
@@ -763,7 +764,6 @@ describe("ngAnimate", function() {
$animate.addClass(element, 'ng-hide');
expect(element.hasClass('ng-hide-remove')).toBe(false); //added right away
if($sniffer.animations) { //cleanup some pending animations
$animate.triggerReflow();
expect(element.hasClass('ng-hide-add')).toBe(true);
@@ -783,19 +783,19 @@ describe("ngAnimate", function() {
$animate.enabled(true);
ss.addRule('.real-animation.ng-enter, .real-animation.ng-leave, .real-animation-fake.ng-enter, .real-animation-fake.ng-leave',
'-webkit-animation:1s my_animation;' +
'-webkit-animation:1s my_animation;' +
'animation:1s my_animation;');
ss.addRule('.real-animation.ng-enter-stagger, .real-animation.ng-leave-stagger',
'-webkit-animation-delay:0.1s;' +
'-webkit-animation-duration:0s;' +
'animation-delay:0.1s;' +
'animation-delay:0.1s;' +
'animation-duration:0s;');
ss.addRule('.fake-animation.ng-enter-stagger, .fake-animation.ng-leave-stagger',
'-webkit-animation-delay:0.1s;' +
'-webkit-animation-duration:1s;' +
'animation-delay:0.1s;' +
'animation-delay:0.1s;' +
'animation-duration:1s;');
var container = $compile(html('<div></div>'))($rootScope);
@@ -850,7 +850,7 @@ describe("ngAnimate", function() {
$animate.enabled(true);
ss.addRule('.stagger-animation.ng-enter, .stagger-animation.ng-leave',
'-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
'-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
'animation:my_animation 1s 1s, your_animation 1s 2s;');
ss.addRule('.stagger-animation.ng-enter-stagger, .stagger-animation.ng-leave-stagger',
@@ -1074,19 +1074,19 @@ describe("ngAnimate", function() {
$animate.enabled(true);
ss.addRule('.real-animation.ng-enter, .real-animation.ng-leave, .real-animation-fake.ng-enter, .real-animation-fake.ng-leave',
'-webkit-transition:1s linear all;' +
'-webkit-transition:1s linear all;' +
'transition:1s linear all;');
ss.addRule('.real-animation.ng-enter-stagger, .real-animation.ng-leave-stagger',
'-webkit-transition-delay:0.1s;' +
'-webkit-transition-duration:0s;' +
'transition-delay:0.1s;' +
'transition-delay:0.1s;' +
'transition-duration:0s;');
ss.addRule('.fake-animation.ng-enter-stagger, .fake-animation.ng-leave-stagger',
'-webkit-transition-delay:0.1s;' +
'-webkit-transition-duration:1s;' +
'transition-delay:0.1s;' +
'transition-delay:0.1s;' +
'transition-duration:1s;');
var container = $compile(html('<div></div>'))($rootScope);
@@ -1142,7 +1142,7 @@ describe("ngAnimate", function() {
$animate.enabled(true);
ss.addRule('.stagger-animation.ng-enter, .ani.ng-leave',
'-webkit-transition:1s linear color 2s, 3s linear font-size 4s;' +
'-webkit-transition:1s linear color 2s, 3s linear font-size 4s;' +
'transition:1s linear color 2s, 3s linear font-size 4s;');
ss.addRule('.stagger-animation.ng-enter-stagger, .ani.ng-leave-stagger',
@@ -1234,7 +1234,7 @@ describe("ngAnimate", function() {
it("should not allow the closing animation to close off a successive animation midway",
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
if (!$sniffer.transitions) return;
ss.addRule('.some-class-add', '-webkit-transition:5s linear all;' +
@@ -1272,9 +1272,9 @@ describe("ngAnimate", function() {
$animate.enabled(true);
ss.addRule('.stagger-animation.ng-enter, .stagger-animation.ng-leave',
'-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
'-webkit-animation:my_animation 1s 1s, your_animation 1s 2s;' +
'animation:my_animation 1s 1s, your_animation 1s 2s;' +
'-webkit-transition:1s linear all 1s;' +
'-webkit-transition:1s linear all 1s;' +
'transition:1s linear all 1s;');
ss.addRule('.stagger-animation.ng-enter-stagger, .stagger-animation.ng-leave-stagger',
@@ -1471,6 +1471,8 @@ describe("ngAnimate", function() {
expect(flag).toBe(true);
expect(element.parent().id).toBe(parent2.id);
dealoc(element);
}));
@@ -1542,7 +1544,7 @@ describe("ngAnimate", function() {
expect(steps.shift()).toEqual(['close', 'klass', 'addClass']);
expect(steps.shift()).toEqual(['done', 'klass', 'addClass']);
}));
}));
it('should fire the DOM callbacks even if no animation is rendered',
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
@@ -1570,7 +1572,22 @@ describe("ngAnimate", function() {
expect(steps.shift()).toEqual(['before', 'ng-enter', 'enter']);
expect(steps.shift()).toEqual(['after', 'ng-enter', 'enter']);
}));
}));
it('should not fire DOM callbacks on the element being animated unless registered',
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
$animate.enabled(true);
var element = jqLite('<div></div>');
$rootElement.append(element);
body.append($rootElement);
$animate.addClass(element, 'class');
$rootScope.$digest();
$timeout.verifyNoPendingTasks();
}));
it("should fire a done callback when provided with no animation",
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
@@ -1604,11 +1621,12 @@ describe("ngAnimate", function() {
var element = parent.find('span');
var flag = false;
$animate.removeClass(element, 'ng-hide', function() {
$animate.addClass(element, 'ng-hide', function() {
flag = true;
});
if($sniffer.transitions) {
$animate.triggerReflow();
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
}
$timeout.flush();
@@ -2533,7 +2551,7 @@ describe("ngAnimate", function() {
}
});
});
inject(function($compile, $rootScope, $animate, $timeout, $rootElement) {
$animate.enabled(true);
@@ -2562,8 +2580,9 @@ describe("ngAnimate", function() {
});
it("should disable all child animations on structural animations until the post animation timeout has passed", function() {
var intercepted;
it("should disable all child animations on structural animations until the post animation" +
"timeout has passed as well as all structural animations", function() {
var intercepted, continueAnimation;
module(function($animateProvider) {
$animateProvider.register('.animated', function() {
return {
@@ -2577,7 +2596,10 @@ describe("ngAnimate", function() {
function ani(type) {
return function(element, className, done) {
intercepted = type;
(done || className)();
continueAnimation = function() {
continueAnimation = angular.noop;
(done || className)();
}
}
}
});
@@ -2594,26 +2616,45 @@ describe("ngAnimate", function() {
var child2 = $compile('<div class="child2 animated">...</div>')($rootScope);
var container = $compile('<div class="container">...</div>')($rootScope);
jqLite($document[0].body).append($rootElement);
var body = angular.element($document[0].body);
body.append($rootElement);
$rootElement.append(container);
element.append(child1);
element.append(child2);
$animate.enter(element, container);
$rootScope.$digest();
expect(intercepted).toBe('enter');
continueAnimation();
$animate.addClass(child1, 'test');
expect(child1.hasClass('test')).toBe(true);
expect(element.children().length).toBe(2);
expect(intercepted).toBe('enter');
$animate.leave(child1);
$rootScope.$digest();
expect(element.children().length).toBe(1);
expect(intercepted).toBe('enter');
$animate.move(element, null, container);
$rootScope.$digest();
expect(intercepted).toBe('move');
$animate.addClass(child1, 'test');
expect(child1.hasClass('test')).toBe(true);
expect(intercepted).toBe('move');
$animate.leave(child1);
$rootScope.$digest();
//flush the enter reflow
$timeout.flush();
$animate.addClass(child2, 'testing');
expect(intercepted).toBe('move');
//reflow has passed
continueAnimation();
//flush the move reflow
$timeout.flush();
$animate.leave(child2);
@@ -2695,42 +2736,6 @@ describe("ngAnimate", function() {
});
it("should cancel an ongoing class-based animation only if the new class contains transition/animation CSS code",
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
if (!$sniffer.transitions) return;
ss.addRule('.green-add', '-webkit-transition:1s linear all;' +
'transition:1s linear all;');
ss.addRule('.blue-add', 'background:blue;');
ss.addRule('.red-add', '-webkit-transition:1s linear all;' +
'transition:1s linear all;');
ss.addRule('.yellow-add', '-webkit-animation: some_animation 4s linear 1s 2 alternate;' +
'animation: some_animation 4s linear 1s 2 alternate;');
var element = $compile('<div></div>')($rootScope);
$rootElement.append(element);
jqLite($document[0].body).append($rootElement);
$animate.addClass(element, 'green');
expect(element.hasClass('green-add')).toBe(true);
$animate.addClass(element, 'blue');
expect(element.hasClass('blue')).toBe(true);
expect(element.hasClass('green-add')).toBe(true); //not cancelled
$animate.addClass(element, 'red');
expect(element.hasClass('green-add')).toBe(false);
expect(element.hasClass('red-add')).toBe(true);
$animate.addClass(element, 'yellow');
expect(element.hasClass('red-add')).toBe(false);
expect(element.hasClass('yellow-add')).toBe(true);
}));
it("should cancel and perform the dom operation only after the reflow has run",
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
@@ -2798,7 +2803,7 @@ describe("ngAnimate", function() {
$animate.removeClass(element, 'on');
$animate.addClass(element, 'on');
expect(currentAnimation).toBe(null);
expect(currentAnimation).toBe('addClass');
});
});
@@ -2827,7 +2832,7 @@ describe("ngAnimate", function() {
it('should perform pre and post animations', function() {
var steps = [];
var steps = [];
module(function($animateProvider) {
$animateProvider.register('.class-animate', function() {
return {
@@ -2856,7 +2861,7 @@ describe("ngAnimate", function() {
it('should treat the leave event always as a before event and discard the beforeLeave function', function() {
var parentID, steps = [];
var parentID, steps = [];
module(function($animateProvider) {
$animateProvider.register('.animate', function() {
return {
@@ -3075,7 +3080,7 @@ describe("ngAnimate", function() {
var element = $compile('<div>' +
' <div ng-repeat="item in items"' +
' ng-include="tpl"' +
' class="special"></div>' +
' class="special"></div>' +
'</div>')($rootScope);
ss.addRule('.special', '-webkit-transition:1s linear all;' +
@@ -3220,7 +3225,7 @@ describe("ngAnimate", function() {
expect(ready).toBe(true);
}));
it('should avoid skip animations if the same CSS class is added / removed synchronously before the reflow kicks in',
it('should immediately close the former animation if the same CSS class is added/removed',
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
if (!$sniffer.transitions) return;
@@ -3242,28 +3247,15 @@ describe("ngAnimate", function() {
signature += 'B';
});
$timeout.flush(1);
expect(signature).toBe('AB');
signature = '';
$animate.removeClass(element, 'on', function() {
signature += 'A';
});
$animate.addClass(element, 'on', function() {
signature += 'B';
});
$animate.removeClass(element, 'on', function() {
signature += 'C';
});
$timeout.flush(1);
expect(signature).toBe('AB');
$animate.triggerReflow();
$timeout.flush(1);
expect(signature).toBe('A');
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2000 });
$timeout.flush(1);
expect(signature).toBe('ABC');
expect(signature).toBe('AB');
}));
});
});
+24
View File
@@ -509,6 +509,11 @@ describe('ngMock', function() {
it('should not throw a runtime exception when given an undefined promise',
inject(function($interval) {
var task1 = jasmine.createSpy('task1'),
promise1;
promise1 = $interval(task1, 1000, 1);
expect($interval.cancel()).toBe(false);
}));
});
@@ -864,6 +869,25 @@ describe('ngMock', function() {
});
describe('this', function() {
it('should set `this` to be the jasmine context', inject(function() {
expect(this instanceof jasmine.Spec).toBe(true);
}));
it('should set `this` to be the jasmine context when inlined in a test', function() {
var tested = false;
inject(function() {
expect(this instanceof jasmine.Spec).toBe(true);
tested = true;
});
expect(tested).toBe(true);
});
});
// We don't run the following tests on IE8.
// IE8 throws "Object does not support this property or method." error,
// when thrown from a function defined on window (which `inject` is).
+43
View File
@@ -94,6 +94,49 @@ describe("resource", function() {
});
describe('shallow copy', function() {
it('should make a copy', function() {
var original = {key:{}};
var copy = shallowClearAndCopy(original);
expect(copy).toEqual(original);
expect(copy.key).toBe(original.key);
});
it('should omit "$$"-prefixed properties', function() {
var original = {$$some: true, $$: true};
var clone = {};
expect(shallowClearAndCopy(original, clone)).toBe(clone);
expect(clone.$$some).toBeUndefined();
expect(clone.$$).toBeUndefined();
});
it('should copy "$"-prefixed properties from copy', function() {
var original = {$some: true};
var clone = {};
expect(shallowClearAndCopy(original, clone)).toBe(clone);
expect(clone.$some).toBe(original.$some);
});
it('should omit properties from prototype chain', function() {
var original, clone = {};
function Func() {};
Func.prototype.hello = "world";
original = new Func();
original.goodbye = "world";
expect(shallowClearAndCopy(original, clone)).toBe(clone);
expect(clone.hello).toBeUndefined();
expect(clone.goodbye).toBe("world");
});
});
it('should default to empty parameters', function() {
$httpBackend.expect('GET', 'URL').respond({});
$resource('URL').query();
+82 -77
View File
@@ -683,21 +683,22 @@ describe('ngView animations', function() {
}));
describe('hooks', function() {
beforeEach(module('mock.animate'));
beforeEach(module('ngAnimate'));
beforeEach(module('ngAnimateMock'));
it('should fire off the enter animation',
inject(function($compile, $rootScope, $location, $animate) {
inject(function($compile, $rootScope, $location, $timeout, $animate) {
element = $compile(html('<div ng-view></div>'))($rootScope);
$location.path('/foo');
$rootScope.$digest();
var item = $animate.flushNext('enter').element;
expect(item.text()).toBe('data');
var animation = $animate.queue.pop();
expect(animation.event).toBe('enter');
}));
it('should fire off the leave animation',
inject(function($compile, $rootScope, $location, $templateCache, $animate) {
inject(function($compile, $rootScope, $location, $templateCache, $timeout, $animate) {
var item;
$templateCache.put('/foo.html', [200, '<div>foo</div>', {}]);
@@ -706,35 +707,40 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('foo');
$timeout.flush();
$location.path('/');
$rootScope.$digest();
item = $animate.flushNext('leave').element;
expect(item.text()).toBe('foo');
var animation = $animate.queue.pop();
expect(animation.event).toBe('leave');
}));
it('should animate two separate ngView elements',
inject(function($compile, $rootScope, $templateCache, $animate, $location) {
inject(function($compile, $rootScope, $templateCache, $location, $animate) {
var item;
$rootScope.tpl = 'one';
element = $compile(html('<div><div ng-view></div></div>'))($rootScope);
element = $compile(html('<div ng-view></div>'))($rootScope);
$rootScope.$digest();
$location.path('/foo');
$rootScope.$digest();
item = $animate.flushNext('enter').element;
expect(item.text()).toBe('data');
//we don't care about the enter animation for the first element
$animate.queue.pop();
$location.path('/bar');
$rootScope.$digest();
var itemA = $animate.flushNext('enter').element;
var animationB = $animate.queue.pop();
expect(animationB.event).toBe('leave');
var itemB = animationB.args[0];
var animationA = $animate.queue.pop();
expect(animationA.event).toBe('enter');
var itemA = animationA.args[0];
expect(itemA).not.toEqual(itemB);
var itemB = $animate.flushNext('leave').element;
}));
it('should render ngClass on ngView',
@@ -749,17 +755,19 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
item = $animate.flushNext('enter').element;
//we don't care about the enter animation
$animate.queue.shift();
$animate.flushNext('addClass').element;
var animation = $animate.queue.shift();
expect(animation.event).toBe('addClass');
var item = animation.element;
expect(item.hasClass('classy')).toBe(true);
$rootScope.klass = 'boring';
$rootScope.$digest();
$animate.flushNext('removeClass').element;
$animate.flushNext('addClass').element;
expect($animate.queue.shift().event).toBe('setClass');
expect(item.hasClass('classy')).toBe(false);
expect(item.hasClass('boring')).toBe(true);
@@ -767,69 +775,66 @@ describe('ngView animations', function() {
$location.path('/bar');
$rootScope.$digest();
$animate.flushNext('enter').element;
item = $animate.flushNext('leave').element;
//we don't care about the enter animation
$animate.queue.shift();
$animate.flushNext('addClass').element;
animation = $animate.queue.shift();
item = animation.element;
expect(animation.event).toBe('leave');
expect($animate.queue.shift().event).toBe('addClass');
expect(item.hasClass('boring')).toBe(true);
}));
});
it('should not double compile when the route changes', function() {
it('should not double compile when the route changes', function() {
module('ngAnimate');
module('mock.animate');
var window;
module(function($routeProvider, $animateProvider, $provide) {
$routeProvider.when('/foo', {template: '<div ng-repeat="i in [1,2]">{{i}}</div>'});
$routeProvider.when('/bar', {template: '<div ng-repeat="i in [3,4]">{{i}}</div>'});
$animateProvider.register('.my-animation', function() {
return {
leave: function(element, done) {
done();
}
};
});
});
var window;
module(function($routeProvider, $animateProvider, $provide) {
$routeProvider.when('/foo', {template: '<div ng-repeat="i in [1,2]">{{i}}</div>'});
$routeProvider.when('/bar', {template: '<div ng-repeat="i in [3,4]">{{i}}</div>'});
$animateProvider.register('.my-animation', function() {
return {
leave: function(element, done) {
done();
inject(function($rootScope, $compile, $location, $route, $timeout, $rootElement, $sniffer, $animate) {
element = $compile(html('<div><ng:view onload="load()" class="my-animation"></ng:view></div>'))($rootScope);
$animate.enabled(true);
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter'); //ngView
expect($animate.queue.shift().event).toBe('enter'); //repeat 1
expect($animate.queue.shift().event).toBe('enter'); //repeat 2
expect(element.text()).toEqual('12');
$location.path('/bar');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter'); //ngView new
expect($animate.queue.shift().event).toBe('leave'); //ngView old
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter'); //ngRepeat 3
expect($animate.queue.shift().event).toBe('enter'); //ngRepeat 4
expect(element.text()).toEqual('34');
function n(text) {
return text.replace(/\r\n/m, '').replace(/\r\n/m, '');
}
};
});
});
});
inject(function($rootScope, $compile, $location, $route, $timeout, $rootElement, $sniffer, $animate) {
element = $compile(html('<div><ng:view onload="load()" class="my-animation"></ng:view></div>'))($rootScope);
$animate.enabled(true);
$location.path('/foo');
$rootScope.$digest();
$animate.flushNext('enter'); //ngView
$animate.flushNext('enter'); //repeat 1
$animate.flushNext('enter'); //repeat 2
expect(element.text()).toEqual('12');
$location.path('/bar');
$rootScope.$digest();
$animate.flushNext('enter'); //ngView new
$animate.flushNext('leave'); //ngView old
$rootScope.$digest();
expect(n(element.text())).toEqual(''); //this is midway during the animation
$animate.flushNext('enter'); //ngRepeat 3
$animate.flushNext('enter'); //ngRepeat 4
$rootScope.$digest();
expect(element.text()).toEqual('34');
function n(text) {
return text.replace(/\r\n/m, '').replace(/\r\n/m, '');
}
});
});
describe('autoscroll', function () {
var autoScrollSpy;
@@ -857,7 +862,7 @@ describe('ngView animations', function() {
};
}
beforeEach(module(spyOnAnchorScroll(), 'mock.animate'));
beforeEach(module(spyOnAnchorScroll(), 'ngAnimateMock'));
beforeEach(inject(spyOnAnimateEnter()));
it('should call $anchorScroll if autoscroll attribute is present', inject(
@@ -866,7 +871,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
$animate.flushNext('enter');
expect($animate.queue.shift().event).toBe('enter');
$timeout.flush();
expect(autoScrollSpy).toHaveBeenCalledOnce();
@@ -880,7 +885,7 @@ describe('ngView animations', function() {
$rootScope.value = true;
$location.path('/foo');
$rootScope.$digest();
$animate.flushNext('enter');
expect($animate.queue.shift().event).toBe('enter');
$timeout.flush();
expect(autoScrollSpy).toHaveBeenCalledOnce();
@@ -893,7 +898,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
$animate.flushNext('enter');
expect($animate.queue.shift().event).toBe('enter');
$timeout.flush();
expect(autoScrollSpy).not.toHaveBeenCalled();
@@ -907,7 +912,7 @@ describe('ngView animations', function() {
$rootScope.value = false;
$location.path('/foo');
$rootScope.$digest();
$animate.flushNext('enter');
expect($animate.queue.shift().event).toBe('enter');
$timeout.flush();
expect(autoScrollSpy).not.toHaveBeenCalled();
@@ -924,7 +929,7 @@ describe('ngView animations', function() {
expect(autoScrollSpy).not.toHaveBeenCalled();
$animate.flushNext('enter');
expect($animate.queue.shift().event).toBe('enter');
$timeout.flush();
expect($animate.enter).toHaveBeenCalledOnce();