Compare commits
427 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 39eecd136f | |||
| 260cfe6dc9 | |||
| 455af41a19 | |||
| f3c8aa2790 | |||
| e2312e3efd | |||
| a460ce4665 | |||
| d06431e530 | |||
| 34ec0d9629 | |||
| 4ac23c0ac5 | |||
| 507cf31ff9 | |||
| 91414918d7 | |||
| 52c21fe51d | |||
| 2f0a50b526 | |||
| 4a80a2e8bc | |||
| 446fac84d9 | |||
| 43f72066e1 | |||
| a909ed1a5c | |||
| 1c6edd416b | |||
| 8bda5ec735 | |||
| 23395ce07d | |||
| c81c9e7cf4 | |||
| 344b4bcb95 | |||
| 34ba294f76 | |||
| 50ecc370b9 | |||
| 622c421696 | |||
| db1180f3b3 | |||
| ca5b27bd0b | |||
| 3d158f629b | |||
| 8aecf46ad2 | |||
| 2072641537 | |||
| 63154c3d1a | |||
| 37cb9ad436 | |||
| 5a3504abdc | |||
| 0dfc1dfebf | |||
| e9c406b246 | |||
| 79b6d55792 | |||
| 8f94b5b277 | |||
| b11120be0a | |||
| bfba95ce46 | |||
| 310f80e78e | |||
| acfda1022d | |||
| 04d4d93e5b | |||
| 8348365df9 | |||
| 16bcdcb61d | |||
| adcfa74327 | |||
| d641901be6 | |||
| eae0a1121f | |||
| 173c9063e7 | |||
| b461551b81 | |||
| 63492a0261 | |||
| adb0e1746b | |||
| 3be79cd6a1 | |||
| e4c6e01791 | |||
| fb76d96009 | |||
| f322c4f3c3 | |||
| 081f6ec7f2 | |||
| 9bb6a30417 | |||
| 97e97d1eb7 | |||
| 1ab4e44443 | |||
| 99eeec358c | |||
| ce8a7525cc | |||
| 5df2e5ce29 | |||
| ef03dfc4a4 | |||
| 4ff9c027b0 | |||
| 20b8ece444 | |||
| 6406e3b01d | |||
| 234053fc9a | |||
| a277bcf0f7 | |||
| 7985416d39 | |||
| dc3013e848 | |||
| 0f2926db38 | |||
| bd59335eba | |||
| 6aa111b333 | |||
| 42b5ce99fb | |||
| 2cb1989d12 | |||
| 1137d91abd | |||
| 288e4e33c3 | |||
| d98c5f03a4 | |||
| 8d20b04f1c | |||
| 56c3666fe5 | |||
| cd21216ff7 | |||
| 3ffdf380c5 | |||
| db5e0ffe12 | |||
| 92e4801d88 | |||
| 6a92e9111f | |||
| ba6d37756e | |||
| 683bd92f56 | |||
| 7700e2df09 | |||
| 3fb809e412 | |||
| 52ea4110d3 | |||
| a3a7afd3aa | |||
| 20bc37fbc8 | |||
| 7761b6c3b0 | |||
| b77e14beea | |||
| a1f461e429 | |||
| 13587193a5 | |||
| a1ff35850c | |||
| 95b3e1ce6c | |||
| b78b12976a | |||
| a31c082de6 | |||
| 5afd54514d | |||
| 72b96ef57a | |||
| 90975db5f9 | |||
| 25bc531802 | |||
| 4e1b36c216 | |||
| d4fa331308 | |||
| a801df719e | |||
| cb74999b17 | |||
| 2764536e8f | |||
| 8ccc0547a8 | |||
| dd14e0c44d | |||
| 7e24590fa3 | |||
| 8b6360338d | |||
| d91cf16796 | |||
| f31c5a3924 | |||
| 16ccac91d0 | |||
| 91b080e6e6 | |||
| feeb19787c | |||
| 98c2db7f9c | |||
| 06aa52efff | |||
| fabc6ab5b0 | |||
| 93c7251f5f | |||
| e1def1b8fe | |||
| e5cab951f4 | |||
| f7eab8d8fe | |||
| 495d40d802 | |||
| d28ae2126e | |||
| 8955cfb646 | |||
| 776972ed9c | |||
| 1358b3ca9b | |||
| e020b8993e | |||
| b2b896f949 | |||
| 3c5827b6f5 | |||
| 0e03644dad | |||
| 112024271b | |||
| bca0a1f786 | |||
| 959f2bbb2d | |||
| 6a47c0d75d | |||
| b0248b7894 | |||
| 6fd41e7f59 | |||
| 3297bbd188 | |||
| 8863836cd4 | |||
| b8fb0c4573 | |||
| 17b700a339 | |||
| 811b20e3b9 | |||
| 5ded3d3e73 | |||
| 986647a968 | |||
| 7b51243be5 | |||
| 798fb18542 | |||
| 99d601a048 | |||
| a6e9174a27 | |||
| dec8a0eb72 | |||
| 98776487a0 | |||
| b3ef5e0852 | |||
| 04efdd5bfa | |||
| 9f5d76e16b | |||
| 525be5b7d4 | |||
| 042e0f1f0a | |||
| 03872983a4 | |||
| 62f79e820f | |||
| e5c26e92cc | |||
| 4bcb307abc | |||
| 0e729e1dd5 | |||
| 9bb184d181 | |||
| a7a053f5be | |||
| 9630159444 | |||
| e3be5d6efa | |||
| c2173c1298 | |||
| 0b94e8a8bf | |||
| 983374c574 | |||
| 05d3ed0d9b | |||
| 1b25f80cd2 | |||
| 6cdbda7cf1 | |||
| c9e6cf9be0 | |||
| c7ed8a33af | |||
| 6988667e5e | |||
| e57cf13d5d | |||
| e4e5677fbd | |||
| 8b6b428271 | |||
| 8f0b482596 | |||
| 6428ed5bb5 | |||
| bc41ad8aa8 | |||
| 6858caf251 | |||
| 20604e7fc4 | |||
| b7b06d8477 | |||
| de9777d819 | |||
| 1b06f33f30 | |||
| ca6e266869 | |||
| 23c4ae522a | |||
| 3112f8e910 | |||
| 21ab82906e | |||
| 1dd206ef85 | |||
| da5db4b1b3 | |||
| 3694390c90 | |||
| 36a3c81177 | |||
| b0e7d548d0 | |||
| e4bb838795 | |||
| 0ea535035a | |||
| e9aba90f7f | |||
| f50b0cb393 | |||
| 8b3bec7e07 | |||
| 6752337629 | |||
| 48ad7486d9 | |||
| 341b834229 | |||
| 7a668cdd7d | |||
| 5a674f3bb9 | |||
| e94b37e20e | |||
| f5ebcbacf8 | |||
| 0812af49bd | |||
| c3ae6ed78e | |||
| c3a2691115 | |||
| 6976d6d8d8 | |||
| b75c0d8d05 | |||
| df6fade6e6 | |||
| 983b059812 | |||
| 25e8c5927c | |||
| 6628b4f1e5 | |||
| 6e18b50a5b | |||
| 77419cf19f | |||
| 193153c3d3 | |||
| 6a0686d434 | |||
| 08c9a5e9e7 | |||
| a72c12bd70 | |||
| 73e38658c4 | |||
| b641181b93 | |||
| 7ffb2d3c17 | |||
| 023b777a56 | |||
| 0c9480de8c | |||
| 0bc275461c | |||
| 0b5ecc64f0 | |||
| b183eae7ae | |||
| 7ddbc9aa35 | |||
| b2a937d425 | |||
| 4473b81cda | |||
| 98528be311 | |||
| 9190d4c3ad | |||
| d9ec9951e2 | |||
| fedafdc677 | |||
| 26c36bb4d1 | |||
| c7a2028ab3 | |||
| 596af70101 | |||
| 96c73a0672 | |||
| 1537651c8c | |||
| d59aeb4e0b | |||
| 551a33db56 | |||
| cd91640146 | |||
| ab5824ee12 | |||
| 5c50723535 | |||
| 546a277d65 | |||
| a5ff651a59 | |||
| ccd47ec904 | |||
| 2e23a3cdbc | |||
| ca7f4a387c | |||
| 898a3fd3b9 | |||
| 9473371343 | |||
| e5275590db | |||
| 78297d252d | |||
| e5e0884eaf | |||
| 551d1c20cf | |||
| f5aa207960 | |||
| 4412fe238f | |||
| 8088284f66 | |||
| 25f1bbaad1 | |||
| bd7b217729 | |||
| 50557a6cd3 | |||
| 1c13a4f45d | |||
| 1a98c0ee34 | |||
| b837fc3116 | |||
| aff74ec87b | |||
| 077ee37942 | |||
| 0efef2385f | |||
| 7da22e6685 | |||
| 92bdd7627f | |||
| 39ebb06baf | |||
| 9cf6b197ab | |||
| 4971ef12d4 | |||
| 7c792f4cc9 | |||
| 4daafd3dbe | |||
| fe11265fdc | |||
| 75292a6cb5 | |||
| 4ff6c85792 | |||
| e26bc2370b | |||
| 33713deeb8 | |||
| 17715fa366 | |||
| 96288d02d3 | |||
| 8be98e4fdf | |||
| cf83b4f445 | |||
| 15bfea8339 | |||
| 9b90c32f31 | |||
| f4bf744516 | |||
| 0d96995fcc | |||
| 2a85a634f8 | |||
| 00d2b2c4cf | |||
| e688f07023 | |||
| 8e2f7d37e4 | |||
| f5bc3ed9b4 | |||
| 11d60af3dc | |||
| 865f6065e7 | |||
| 91c0b364af | |||
| fce07f55e5 | |||
| c8768d12f2 | |||
| af6342d6fb | |||
| a179757fad | |||
| 35eada68c4 | |||
| bfad2a4f4a | |||
| 54e816552f | |||
| 4fc734665e | |||
| b8736e65b0 | |||
| b9bed7d9da | |||
| b2fc39d2dd | |||
| 7c0731edb2 | |||
| 8fe781fbe7 | |||
| 23932a26ff | |||
| 3ca4ca463c | |||
| 395f3ec638 | |||
| 964a901bd8 | |||
| a995ee17ee | |||
| 794d1c1ebe | |||
| 662fb282c1 | |||
| ffb6b2fb56 | |||
| 941c1c35f1 | |||
| 29a05984fe | |||
| 4038aabffa | |||
| 773efd0812 | |||
| 80881949fc | |||
| 2c8d87e064 | |||
| 33c67ce785 | |||
| ad4296d966 | |||
| 469b14a525 | |||
| 1caf0b6bee | |||
| 9f716dd590 | |||
| 914a934b6f | |||
| a4ada8ba9c | |||
| 40c974ab14 | |||
| 06f002b161 | |||
| 8226ff8b8e | |||
| d67e999dfb | |||
| a8e03b3a90 | |||
| 6f3e26c404 | |||
| 1c75ea613d | |||
| c8e1db2050 | |||
| 74ed28665d | |||
| 4ad0ca130d | |||
| b51dd3010d | |||
| 6e6f31943c | |||
| 66fee7e22a | |||
| beea571660 | |||
| 0c8a9a0e1a | |||
| fd83d3724a | |||
| 2fcfd75a14 | |||
| 2d40507547 | |||
| ecf9304811 | |||
| b9ab88776b | |||
| 00bf218304 | |||
| 95fbf168d1 | |||
| 51a27c0f1a | |||
| f02811f0bb | |||
| bcf78ebb18 | |||
| 3050dd1b47 | |||
| 1efdb4745a | |||
| d73f7dff45 | |||
| f047ad2628 | |||
| 049d3def80 | |||
| 00778aa239 | |||
| 6b123a0419 | |||
| 4079eea6b3 | |||
| d277ac2eb8 | |||
| 9deb123d04 | |||
| 1c2d2e8ba0 | |||
| 144bcc84ab | |||
| 693021c449 | |||
| dc818e1165 | |||
| e51174bf13 | |||
| 7f3f3dd3eb | |||
| d077966ff1 | |||
| 0df4ff800a | |||
| 6208b76afa | |||
| 70dac5ae82 | |||
| aafbd94439 | |||
| e732f8e579 | |||
| 9f67da6252 | |||
| b3a3c6a72e | |||
| e8cdabe129 | |||
| 3af71bee75 | |||
| f2724b2bbc | |||
| 38500669f6 | |||
| 862c9d8bb2 | |||
| 240d5896ec | |||
| 64ef084b91 | |||
| 8b27c3f064 | |||
| 8366622bed | |||
| d3de0066b0 | |||
| b7e5133b2e | |||
| 42c97c5db5 | |||
| e1f4f23f78 | |||
| 630280c7fb | |||
| 496a67c10e | |||
| 7dcfe5e03e | |||
| 9e83b8355e | |||
| 03726f7fbd | |||
| 75893ae9e7 | |||
| d9ca245917 | |||
| 3259aabdf3 | |||
| befeeb3689 | |||
| 79577c5d31 | |||
| b71d7c3f3c | |||
| fa4c7b7f1d | |||
| 652b83eb22 | |||
| 20cf7d5e3a | |||
| ded2518756 | |||
| b366f0352a | |||
| 76c2491a31 | |||
| ef3df93afe | |||
| 106f90aafa | |||
| 86e8088b38 | |||
| a654bdfed9 | |||
| 2c22c57e58 | |||
| a4dfa4d061 | |||
| bc0d8c4eea | |||
| 9b84fcad76 | |||
| f33ce173c9 | |||
| 181fc567d8 | |||
| 94207f8fb6 | |||
| 35a21532b7 | |||
| 1c97a6057b | |||
| 658a865c5b | |||
| f8cf28a788 |
+19
-23
@@ -1,7 +1,7 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '0.10'
|
||||
- '4.2'
|
||||
|
||||
cache:
|
||||
directories:
|
||||
@@ -15,28 +15,26 @@ branches:
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- JOB=ci-checks
|
||||
- JOB=unit BROWSER_PROVIDER=saucelabs
|
||||
- JOB=docs-e2e BROWSER_PROVIDER=saucelabs
|
||||
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=saucelabs
|
||||
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=saucelabs
|
||||
- JOB=unit BROWSER_PROVIDER=browserstack
|
||||
- JOB=docs-e2e BROWSER_PROVIDER=browserstack
|
||||
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack
|
||||
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack
|
||||
global:
|
||||
- CXX=g++-4.8 # node 4 likes the G++ v4.8 compiler
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- BROWSER_STACK_USERNAME=VojtaJina
|
||||
- BROWSER_STACK_ACCESS_KEY=QCQJ1ZpWXpBkSwEdD8ev
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
- BROWSER_PROVIDER_READY_FILE=/tmp/browsersprovider-tunnel-ready
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- env: "JOB=unit BROWSER_PROVIDER=browserstack"
|
||||
- env: "JOB=docs-e2e BROWSER_PROVIDER=browserstack"
|
||||
- env: "JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack"
|
||||
- env: "JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack"
|
||||
# node 4 likes the G++ v4.8 compiler
|
||||
# see https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
|
||||
install:
|
||||
# Check the size of caches
|
||||
@@ -46,27 +44,25 @@ install:
|
||||
- npm config set spin false
|
||||
# Log HTTP requests
|
||||
- npm config set loglevel http
|
||||
- npm install -g npm@2.5
|
||||
# Instal npm dependecies and ensure that npm cache is not stale
|
||||
- scripts/npm/install-dependencies.sh
|
||||
#- npm install -g npm@2.5
|
||||
# Install npm dependencies and ensure that npm cache is not stale
|
||||
- npm install
|
||||
|
||||
before_script:
|
||||
- mkdir -p $LOGS_DIR
|
||||
- ./scripts/travis/start_browser_provider.sh
|
||||
- npm install -g grunt-cli
|
||||
- grunt package
|
||||
- ./scripts/travis/wait_for_browser_provider.sh
|
||||
- ./scripts/travis/before_build.sh
|
||||
|
||||
script:
|
||||
- ./scripts/travis/build.sh
|
||||
|
||||
after_script:
|
||||
- ./scripts/travis/tear_down_browser_provider.sh
|
||||
- ./scripts/travis/print_logs.sh
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/d2120f3f2bb39a4531b2
|
||||
on_success: change # options: [always|never|change] default: always
|
||||
- http://104.197.9.155:8484/hubot/travis/activity #hubot-server
|
||||
on_success: always # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: false # default: false
|
||||
on_start: always # default: false
|
||||
|
||||
+1170
-59
File diff suppressed because it is too large
Load Diff
+10
-2
@@ -71,7 +71,7 @@ chances of your issue being dealt with quickly:
|
||||
* **Angular Version(s)** - is it a regression?
|
||||
* **Browsers and Operating System** - is this a problem with all browsers or only IE8?
|
||||
* **Reproduce the Error** - provide a live example (using [Plunker][plunker] or
|
||||
[JSFiddle][jsfiddle]) or a unambiguous set of steps.
|
||||
[JSFiddle][jsfiddle]) or an unambiguous set of steps.
|
||||
* **Related Issues** - has a similar issue been reported before?
|
||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
||||
causing the problem (line of code or commit)
|
||||
@@ -123,13 +123,19 @@ Before you submit your pull request consider the following guidelines:
|
||||
* If we suggest changes then:
|
||||
* Make the required updates.
|
||||
* Re-run the Angular test suite to ensure tests are still passing.
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
* Commit your changes to your branch (e.g. `my-fix-branch`).
|
||||
* Push the changes to your GitHub repository (this will update your Pull Request).
|
||||
|
||||
If the PR gets too outdated we may ask you to rebase and force push to update the PR:
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
git push origin my-fix-branch -f
|
||||
```
|
||||
|
||||
*WARNING. Squashing or reverting commits and forced push thereafter may remove GitHub comments
|
||||
on code that were previously made by you and others in your commits.*
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
#### After your pull request is merged
|
||||
@@ -187,6 +193,8 @@ We have very precise rules over how our git commit messages can be formatted. T
|
||||
readable messages** that are easy to follow when looking through the **project history**. But also,
|
||||
we use the git commit messages to **generate the AngularJS change log**.
|
||||
|
||||
The commit message formatting can be added using a typical git workflow or through the use of a CLI wizard ([Commitizen](https://github.com/commitizen/cz-cli)). To use the wizard, run `npm run commit` in your terminal after staging your changes in git.
|
||||
|
||||
### Commit Message Format
|
||||
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
|
||||
format that includes a **type**, a **scope** and a **subject**:
|
||||
|
||||
+14
-12
@@ -162,7 +162,7 @@ module.exports = function(grunt) {
|
||||
'!src/angular.bind.js' // we ignore this file since contains an early return statement
|
||||
],
|
||||
options: {
|
||||
config: ".jscsrc"
|
||||
config: '.jscsrc'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -231,9 +231,9 @@ module.exports = function(grunt) {
|
||||
dest: 'build/angular-aria.js',
|
||||
src: util.wrap(files['angularModules']['ngAria'], 'module')
|
||||
},
|
||||
"promises-aplus-adapter": {
|
||||
'promises-aplus-adapter': {
|
||||
dest:'tmp/promises-aplus-adapter++.js',
|
||||
src:['src/ng/q.js','lib/promises-aplus/promises-aplus-test-adapter.js']
|
||||
src:['src/ng/q.js', 'lib/promises-aplus/promises-aplus-test-adapter.js']
|
||||
}
|
||||
},
|
||||
|
||||
@@ -253,7 +253,7 @@ module.exports = function(grunt) {
|
||||
},
|
||||
|
||||
|
||||
"ddescribe-iit": {
|
||||
'ddescribe-iit': {
|
||||
files: [
|
||||
'src/**/*.js',
|
||||
'test/**/*.js',
|
||||
@@ -274,7 +274,7 @@ module.exports = function(grunt) {
|
||||
}
|
||||
},
|
||||
|
||||
"merge-conflict": {
|
||||
'merge-conflict': {
|
||||
files: [
|
||||
'src/**/*',
|
||||
'test/**/*',
|
||||
@@ -304,11 +304,11 @@ module.exports = function(grunt) {
|
||||
},
|
||||
|
||||
shell: {
|
||||
"npm-install": {
|
||||
command: path.normalize('scripts/npm/install-dependencies.sh')
|
||||
'npm-install': {
|
||||
command: 'node scripts/npm/check-node-modules.js'
|
||||
},
|
||||
|
||||
"promises-aplus-tests": {
|
||||
'promises-aplus-tests': {
|
||||
options: {
|
||||
stdout: false,
|
||||
stderr: true,
|
||||
@@ -339,8 +339,10 @@ module.exports = function(grunt) {
|
||||
grunt.task.run('shell:npm-install');
|
||||
}
|
||||
|
||||
|
||||
|
||||
//alias tasks
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package', 'test:unit', 'test:promises-aplus', 'tests:docs', 'test:protractor']);
|
||||
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
|
||||
grunt.registerTask('test:jquery', 'Run the jQuery unit tests with Karma', ['tests:jquery']);
|
||||
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['build', 'tests:modules']);
|
||||
@@ -350,11 +352,11 @@ module.exports = function(grunt) {
|
||||
grunt.registerTask('test:travis-protractor', 'Run the end to end tests with Protractor for Travis CI builds', ['connect:testserver', 'protractor:travis']);
|
||||
grunt.registerTask('test:ci-protractor', 'Run the end to end tests with Protractor for Jenkins CI builds', ['webdriver', 'connect:testserver', 'protractor:jenkins']);
|
||||
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
|
||||
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
|
||||
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter', 'shell:promises-aplus-tests']);
|
||||
|
||||
grunt.registerTask('minify', ['bower','clean', 'build', 'minall']);
|
||||
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', ['bower', 'validate-angular-files', 'clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
Copyright (c) 2010-2016 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
|
||||
|
||||
@@ -21,7 +21,7 @@ piece of cake. Best of all?? It makes development fun!
|
||||
|
||||
Building AngularJS
|
||||
---------
|
||||
[Once you have your environment setup](http://docs.angularjs.org/misc/contribute) just run:
|
||||
[Once you have your environment set up](http://docs.angularjs.org/misc/contribute) just run:
|
||||
|
||||
grunt package
|
||||
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ The following is done automatically so you don't have to worry about it:
|
||||
This process based on the idea of minimizing user pain
|
||||
[from this blog post](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html).
|
||||
|
||||
1. Open the list of [non triaged issues](https://github.com/angular/angular.js/issues?direction=desc&milestone=none&page=1&sort=created&state=open)
|
||||
1. Open the list of [non triaged issues](https://github.com/angular/angular.js/issues?q=is%3Aopen+sort%3Acreated-desc+no%3Amilestone)
|
||||
* Sort by submit date, with the newest issues first
|
||||
* You don't have to do issues in order; feel free to pick and choose issues as you please.
|
||||
* You can triage older issues as well
|
||||
|
||||
Vendored
+4
-3
@@ -14,6 +14,7 @@ var angularFiles = {
|
||||
|
||||
'src/ng/anchorScroll.js',
|
||||
'src/ng/animate.js',
|
||||
'src/ng/animateRunner.js',
|
||||
'src/ng/animateCss.js',
|
||||
'src/ng/browser.js',
|
||||
'src/ng/cacheFactory.js',
|
||||
@@ -33,6 +34,7 @@ var angularFiles = {
|
||||
'src/ng/q.js',
|
||||
'src/ng/raf.js',
|
||||
'src/ng/rootScope.js',
|
||||
'src/ng/rootElement.js',
|
||||
'src/ng/sanitizeUri.js',
|
||||
'src/ng/sce.js',
|
||||
'src/ng/sniffer.js',
|
||||
@@ -84,7 +86,7 @@ var angularFiles = {
|
||||
],
|
||||
|
||||
'angularLoader': [
|
||||
'stringify.js',
|
||||
'src/stringify.js',
|
||||
'src/minErr.js',
|
||||
'src/loader.js'
|
||||
],
|
||||
@@ -92,7 +94,6 @@ var angularFiles = {
|
||||
'angularModules': {
|
||||
'ngAnimate': [
|
||||
'src/ngAnimate/shared.js',
|
||||
'src/ngAnimate/body.js',
|
||||
'src/ngAnimate/rafScheduler.js',
|
||||
'src/ngAnimate/animateChildrenDirective.js',
|
||||
'src/ngAnimate/animateCss.js',
|
||||
@@ -100,8 +101,8 @@ var angularFiles = {
|
||||
'src/ngAnimate/animateJs.js',
|
||||
'src/ngAnimate/animateJsDriver.js',
|
||||
'src/ngAnimate/animateQueue.js',
|
||||
'src/ngAnimate/animateRunner.js',
|
||||
'src/ngAnimate/animation.js',
|
||||
'src/ngAnimate/ngAnimateSwap.js',
|
||||
'src/ngAnimate/module.js'
|
||||
],
|
||||
'ngCookies': [
|
||||
|
||||
@@ -8,20 +8,20 @@
|
||||
Large table rendered with AngularJS
|
||||
</p>
|
||||
|
||||
<div>none: <input type="radio" ng-model="benchmarkType" value="none"></div>
|
||||
<div>baseline binding: <input type="radio" ng-model="benchmarkType" value="baselineBinding"></div>
|
||||
<div>baseline interpolation: <input type="radio" ng-model="benchmarkType" value="baselineInterpolation"></div>
|
||||
<div>ngBind: <input type="radio" ng-model="benchmarkType" value="ngBind"></div>
|
||||
<div>ngBindOnce: <input type="radio" ng-model="benchmarkType" value="ngBindOnce"></div>
|
||||
<div>interpolation: <input type="radio" ng-model="benchmarkType" value="interpolation"></div>
|
||||
<div>interpolation + bind-once: <input type="radio" ng-model="benchmarkType" value="bindOnceInterpolation"></div>
|
||||
<div>attribute interpolation: <input type="radio" ng-model="benchmarkType" value="interpolationAttr"></div>
|
||||
<div>ngBind + fnInvocation: <input type="radio" ng-model="benchmarkType" value="ngBindFn"></div>
|
||||
<div>interpolation + fnInvocation: <input type="radio" ng-model="benchmarkType" value="interpolationFn"></div>
|
||||
<div>ngBind + filter: <input type="radio" ng-model="benchmarkType" value="ngBindFilter"></div>
|
||||
<div>interpolation + filter: <input type="radio" ng-model="benchmarkType" value="interpolationFilter"></div>
|
||||
<div>ngModel (const name): <input type="radio" ng-model="benchmarkType" value="ngModelConstName"></div>
|
||||
<div>ngModel (interp name): <input type="radio" ng-model="benchmarkType" value="ngModelInterpName"></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="none">none: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="baselineBinding">baseline binding: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="baselineInterpolation">baseline interpolation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngBind">ngBind: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngBindOnce">ngBindOnce: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="interpolation">interpolation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="bindOnceInterpolation">interpolation + bind-once: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="interpolationAttr">attribute interpolation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngBindFn">ngBind + fnInvocation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="interpolationFn">interpolation + fnInvocation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngBindFilter">ngBind + filter: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="interpolationFilter">interpolation + filter: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngModelConstName">ngModel (const name): </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngModelInterpName">ngModel (interp name): </label></div>
|
||||
|
||||
<ng-switch on="benchmarkType">
|
||||
<baseline-binding-table ng-switch-when="baselineBinding">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "AngularJS",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"jquery": "2.1.1",
|
||||
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
|
||||
|
||||
@@ -124,7 +124,7 @@ h1,h2,h3,h4,h5,h6 {
|
||||
font-size:1.2em;
|
||||
padding:0;
|
||||
margin:0;
|
||||
border-bottom:1px soild #aaa;
|
||||
border-bottom:1px solid #aaa;
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
@@ -315,8 +315,13 @@ iframe.example {
|
||||
color:white;
|
||||
}
|
||||
|
||||
.search-results-group .search-results {
|
||||
padding: 0 5px 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.search-results-frame > .search-results-group:first-child > .search-results {
|
||||
border-right:1px solid #050505;
|
||||
border-right:1px solid #222;
|
||||
}
|
||||
|
||||
.search-results-group.col-group-api { width:30%; }
|
||||
@@ -325,10 +330,57 @@ iframe.example {
|
||||
.search-results-group.col-group-misc,
|
||||
.search-results-group.col-group-error { width:15%; float: right; }
|
||||
|
||||
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
|
||||
.search-results-group.col-group-api .search-results {
|
||||
-moz-column-count: 2;
|
||||
-ms-column-count: 2;
|
||||
-webkit-column-count: 2;
|
||||
column-count: 2;
|
||||
/* Prevent bullets in the second column from being hidden in Chrome and IE */
|
||||
-webkit-column-gap: 2em;
|
||||
-ms-column-gap: 2em;
|
||||
column-gap: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.search-results-group .search-result {
|
||||
word-wrap: break-word;
|
||||
-webkit-hyphens: auto;
|
||||
-moz-hyphens: auto;
|
||||
-ms-hyphens: auto;
|
||||
hyphens: auto;
|
||||
-ms-column-break-inside: avoid;
|
||||
-webkit-column-break-inside: avoid;
|
||||
-moz-column-break-inside: avoid; /* Unsupported */
|
||||
column-break-inside: avoid;
|
||||
text-indent: -0.65em; /* Make sure line wrapped words are aligned vertically */
|
||||
}
|
||||
|
||||
@supports (-moz-column-count: 2) {
|
||||
.search-results-group .search-result {
|
||||
/* Prevents column breaks inside words in FF, but has adverse effects in IE11 and Chrome */
|
||||
overflow: hidden;
|
||||
padding-left: 1em; /* In FF the list item bullet is otherwise hidden */
|
||||
margin-left: -1em; /* offset the padding left */
|
||||
}
|
||||
}
|
||||
|
||||
.search-result:before {
|
||||
content: "\002D\00A0"; /* Dash and non-breaking space as List item type */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-results-group.col-group-api .search-result {
|
||||
width:48%;
|
||||
display:inline-block;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
|
||||
.search-results-group.col-group-api .search-result {
|
||||
width:auto;
|
||||
display: list-item;
|
||||
}
|
||||
}
|
||||
|
||||
.search-close {
|
||||
@@ -589,6 +641,12 @@ ul.events > li {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.table > tbody > tr.head > td,
|
||||
.table > tbody > tr.head > th {
|
||||
border-bottom: 2px solid #ddd;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 769px) and (max-width: 991px) {
|
||||
.main-body-grid {
|
||||
margin-top: 160px;
|
||||
@@ -682,6 +740,11 @@ ul.events > li {
|
||||
padding-bottom:60px;
|
||||
text-align:left;
|
||||
}
|
||||
|
||||
.search-results-frame > .search-results-group:first-child > .search-results {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.search-results-group {
|
||||
float:none!important;
|
||||
display:block!important;
|
||||
@@ -689,14 +752,42 @@ ul.events > li {
|
||||
border:0!important;
|
||||
padding:0!important;
|
||||
}
|
||||
|
||||
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
|
||||
.search-results-group .search-results {
|
||||
-moz-column-count: 2;
|
||||
-ms-column-count: 2;
|
||||
-webkit-column-count: 2;
|
||||
column-count: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.search-results-group .search-result {
|
||||
display:inline-block!important;
|
||||
padding:0 5px;
|
||||
width:auto!important;
|
||||
text-indent: initial;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.search-results-group .search-result:after {
|
||||
content:", ";
|
||||
}
|
||||
|
||||
.search-results-group .search-result:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
|
||||
.search-results-group .search-result {
|
||||
display: list-item !important;
|
||||
}
|
||||
|
||||
.search-results-group .search-result:after {
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
padding-bottom:0px;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/* global importScripts, onmessage: true, postMessage, lunr */
|
||||
|
||||
// Load up the lunr library
|
||||
importScripts('../components/lunr.js-0.4.2/lunr.min.js');
|
||||
importScripts('../components/lunr.js-0.5.12/lunr.min.js');
|
||||
|
||||
// Create the lunr index - the docs should be an array of object, each object containing
|
||||
// the path and search terms for a page
|
||||
|
||||
@@ -24,7 +24,26 @@ angular.module('examples', [])
|
||||
|
||||
|
||||
.factory('openPlunkr', ['formPostData', '$http', '$q', function(formPostData, $http, $q) {
|
||||
return function(exampleFolder, clickEvent) {
|
||||
|
||||
var COPYRIGHT = 'Copyright ' + (new Date()).getFullYear() + ' Google Inc. All Rights Reserved.\n'
|
||||
+ 'Use of this source code is governed by an MIT-style license that\n'
|
||||
+ 'can be found in the LICENSE file at http://angular.io/license';
|
||||
var COPYRIGHT_JS_CSS = '\n\n/*\n' + COPYRIGHT + '\n*/';
|
||||
var COPYRIGHT_HTML = '\n\n<!-- \n' + COPYRIGHT + '\n-->';
|
||||
function getCopyright(filename) {
|
||||
switch (filename.substr(filename.lastIndexOf('.'))) {
|
||||
case '.html':
|
||||
return COPYRIGHT_HTML;
|
||||
case '.js':
|
||||
case '.css':
|
||||
return COPYRIGHT_JS_CSS;
|
||||
case '.md':
|
||||
return COPYRIGHT;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
return function(exampleFolder, clickEvent) {
|
||||
|
||||
var exampleName = 'AngularJS Example';
|
||||
var newWindow = clickEvent.ctrlKey || clickEvent.metaKey;
|
||||
@@ -67,7 +86,7 @@ angular.module('examples', [])
|
||||
var postData = {};
|
||||
|
||||
angular.forEach(files, function(file) {
|
||||
postData['files[' + file.name + ']'] = file.content;
|
||||
postData['files[' + file.name + ']'] = file.content + getCopyright(file.name);
|
||||
});
|
||||
|
||||
postData['tags[0]'] = "angularjs";
|
||||
|
||||
@@ -11,7 +11,15 @@ angular.module('search', [])
|
||||
var MIN_SEARCH_LENGTH = 2;
|
||||
if(q.length >= MIN_SEARCH_LENGTH) {
|
||||
docsSearch(q).then(function(hits) {
|
||||
var results = {};
|
||||
// Make sure the areas are always in the same order
|
||||
var results = {
|
||||
api: [],
|
||||
guide: [],
|
||||
tutorial: [],
|
||||
error: [],
|
||||
misc: []
|
||||
};
|
||||
|
||||
angular.forEach(hits, function(hit) {
|
||||
var area = hit.area;
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"name": "AngularJS-docs-app",
|
||||
"dependencies": {
|
||||
"jquery": "2.1.1",
|
||||
"lunr.js": "0.4.3",
|
||||
"lunr.js": "0.5.12",
|
||||
"open-sans-fontface": "1.0.4",
|
||||
"google-code-prettify": "1.0.1",
|
||||
"bootstrap": "3.1.1"
|
||||
|
||||
@@ -170,4 +170,8 @@ module.exports = new Package('angularjs', [
|
||||
jqueryDeployment,
|
||||
productionDeployment
|
||||
];
|
||||
})
|
||||
|
||||
.config(function(generateKeywordsProcessor) {
|
||||
generateKeywordsProcessor.docTypesToIgnore = ['componentGroup'];
|
||||
});
|
||||
|
||||
@@ -16,9 +16,11 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
|
||||
ignoreWordsFile: undefined,
|
||||
areasToSearch: ['api', 'guide', 'misc', 'error', 'tutorial'],
|
||||
propertiesToIgnore: [],
|
||||
docTypesToIgnore: [],
|
||||
$validate: {
|
||||
ignoreWordsFile: { },
|
||||
areasToSearch: { presence: true },
|
||||
docTypesToIgnore: { },
|
||||
propertiesToIgnore: { }
|
||||
},
|
||||
$runAfter: ['memberDocsProcessor'],
|
||||
@@ -28,6 +30,7 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
|
||||
// Keywords to ignore
|
||||
var wordsToIgnore = [];
|
||||
var propertiesToIgnore;
|
||||
var docTypesToIgnore;
|
||||
var areasToSearch;
|
||||
|
||||
// Keywords start with "ng:" or one of $, _ or a letter
|
||||
@@ -47,6 +50,8 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
|
||||
areasToSearch = _.indexBy(this.areasToSearch);
|
||||
propertiesToIgnore = _.indexBy(this.propertiesToIgnore);
|
||||
log.debug('Properties to ignore', propertiesToIgnore);
|
||||
docTypesToIgnore = _.indexBy(this.docTypesToIgnore);
|
||||
log.debug('Doc types to ignore', docTypesToIgnore);
|
||||
|
||||
var ignoreWordsMap = _.indexBy(wordsToIgnore);
|
||||
|
||||
@@ -78,34 +83,36 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
|
||||
|
||||
// We are only interested in docs that live in the right area
|
||||
docs = _.filter(docs, function(doc) { return areasToSearch[doc.area]; });
|
||||
docs = _.filter(docs, function(doc) { return !docTypesToIgnore[doc.docType]; });
|
||||
|
||||
_.forEach(docs, function(doc) {
|
||||
|
||||
var words = [];
|
||||
var keywordMap = _.clone(ignoreWordsMap);
|
||||
var members = [];
|
||||
var membersMap = {};
|
||||
|
||||
// Search each top level property of the document for search terms
|
||||
_.forEach(doc, function(value, key) {
|
||||
var words = [];
|
||||
var keywordMap = _.clone(ignoreWordsMap);
|
||||
var members = [];
|
||||
var membersMap = {};
|
||||
|
||||
if ( _.isString(value) && !propertiesToIgnore[key] ) {
|
||||
extractWords(value, words, keywordMap);
|
||||
}
|
||||
// Search each top level property of the document for search terms
|
||||
_.forEach(doc, function(value, key) {
|
||||
|
||||
if ( key === 'methods' || key === 'properties' || key === 'events' ) {
|
||||
_.forEach(value, function(member) {
|
||||
extractWords(member.name, members, membersMap);
|
||||
});
|
||||
}
|
||||
});
|
||||
if ( _.isString(value) && !propertiesToIgnore[key] ) {
|
||||
extractWords(value, words, keywordMap);
|
||||
}
|
||||
|
||||
if ( key === 'methods' || key === 'properties' || key === 'events' ) {
|
||||
_.forEach(value, function(member) {
|
||||
extractWords(member.name, members, membersMap);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
doc.searchTerms = {
|
||||
titleWords: extractTitleWords(doc.name),
|
||||
keywords: _.sortBy(words).join(' '),
|
||||
members: _.sortBy(members).join(' ')
|
||||
};
|
||||
doc.searchTerms = {
|
||||
titleWords: extractTitleWords(doc.name),
|
||||
keywords: _.sortBy(words).join(' '),
|
||||
members: _.sortBy(members).join(' ')
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -147,13 +147,13 @@
|
||||
<div class="search-results-container" ng-show="hasResults">
|
||||
<div class="container">
|
||||
<div class="search-results-frame">
|
||||
<div ng-repeat="(key, value) in results" class="search-results-group" ng-class="colClassName + ' col-group-' + key">
|
||||
<div ng-repeat="(key, value) in results track by key" class="search-results-group" ng-class="colClassName + ' col-group-' + key" ng-show="value.length > 0">
|
||||
<h4 class="search-results-group-heading">{{ key }}</h4>
|
||||
<div class="search-results">
|
||||
<div ng-repeat="item in value" class="search-result">
|
||||
- <a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="search-results">
|
||||
<!-- Do not insert a line break between li and a. Chrome will insert an actual line-break, which breaks the list item view.
|
||||
TODO: use a html minifier instead -->
|
||||
<li ng-repeat="item in value" class="search-result"><a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<a href="" ng-click="hideResults()" class="search-close">
|
||||
@@ -220,7 +220,7 @@
|
||||
<p class="pull-right"><a back-to-top>Back to top</a></p>
|
||||
|
||||
<p>
|
||||
Super-powered by Google ©2010-2015
|
||||
Super-powered by Google ©2010-2016
|
||||
( <a id="version"
|
||||
ng-href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#{{versionNumber}}"
|
||||
ng-bind-template="v{{version}}" title="Changelog of this version of Angular JS">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
|
||||
{# Be aware that we need these extra new lines here or marked will not realize that the <div>
|
||||
is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
|
||||
<div>
|
||||
@@ -24,5 +24,5 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
|
||||
{# Be aware that we need these extra new lines here or marked will not realize that the <div>
|
||||
above is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
|
||||
@@ -8,7 +8,7 @@ but the required directive controller is not present on the current DOM element
|
||||
|
||||
To resolve this error ensure that there is no typo in the required controller name and that the required directive controller is present on the current element.
|
||||
|
||||
If the required controller is expected to be on a ancestor element, make sure that you prefix the controller name in the `require` definition with `^`.
|
||||
If the required controller is expected to be on an ancestor element, make sure that you prefix the controller name in the `require` definition with `^`.
|
||||
|
||||
If the required controller is optionally requested, use `?` or `^?` to specify that.
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
This error occurs when a module fails to load due to some exception. The error
|
||||
message above should provide additional context.
|
||||
|
||||
A common reason why the module fails to load is that you've forgotten to
|
||||
include the file with the defined module or that the file couldn't be loaded.
|
||||
|
||||
### Using `ngRoute`
|
||||
|
||||
In AngularJS `1.2.0` and later, `ngRoute` has been moved to its own module.
|
||||
@@ -24,4 +27,4 @@ angular.module('ng').filter('tel', function (){});
|
||||
|
||||
Instead create your own module and add it as a dependency to your application's top-level module.
|
||||
See [#9692](https://github.com/angular/angular.js/issues/9692) and
|
||||
[#7709](https://github.com/angular/angular.js/issues/7709) for more information
|
||||
[#7709](https://github.com/angular/angular.js/issues/7709) for more information
|
||||
|
||||
@@ -81,3 +81,6 @@ angular.module('myModule', [])
|
||||
// a scope object cannot be injected into a service.
|
||||
}]);
|
||||
```
|
||||
|
||||
If you encounter this error only with minified code, consider using `ngStrictDi` (see
|
||||
{@link ng.directive:ngApp ngApp}) to provoke the error with the non-minified source.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@ngdoc error
|
||||
@name $location:nobase
|
||||
@fullName $location in HTML5 mode requires a <base> tag to be present!
|
||||
@fullName $location in HTML5 mode requires a `<base>` tag to be present!
|
||||
@description
|
||||
|
||||
If you configure {@link ng.$location `$location`} to use
|
||||
@@ -15,7 +15,7 @@ $locationProvider.html5Mode({
|
||||
});
|
||||
```
|
||||
|
||||
Note that removing the requirement for a <base> tag will have adverse side effects when resolving
|
||||
Note that removing the requirement for a `<base>` tag will have adverse side effects when resolving
|
||||
relative paths with `$location` in IE9.
|
||||
|
||||
The base URL is then used to resolve all relative URLs throughout the application regardless of the
|
||||
|
||||
@@ -14,3 +14,32 @@ perform this check - it's up to the developer to not expose such sensitive and p
|
||||
directly on the scope chain.
|
||||
|
||||
To resolve this error, avoid Window access.
|
||||
|
||||
### Common CoffeeScript Issue
|
||||
|
||||
Be aware that if you are using CoffeeScript, it automatically returns the value of the last statement in a
|
||||
function. So for instance
|
||||
|
||||
```coffeescript
|
||||
scope.foo = ->
|
||||
window.open 'https://example.com'
|
||||
```
|
||||
|
||||
compiles to something like
|
||||
|
||||
```js
|
||||
scope.foo = function() {
|
||||
return window.open('https://example.com');
|
||||
};
|
||||
```
|
||||
|
||||
You can see that this function will return the result of calling `window.open`, which is a `Window`
|
||||
object.
|
||||
|
||||
You can avoid this by explicitly returning something else from the function:
|
||||
|
||||
```coffeescript
|
||||
scope.foo = ->
|
||||
window.open 'https://example.com'
|
||||
return true;
|
||||
```
|
||||
|
||||
@@ -100,7 +100,7 @@ To resolve this type of issue, either fix the api to be always synchronous or as
|
||||
your callback handler to always run asynchronously by using the `$timeout` service.
|
||||
|
||||
```
|
||||
function MyController($scope, thirdPartyComponent) {
|
||||
function MyController($scope, $timeout, thirdPartyComponent) {
|
||||
thirdPartyComponent.getData(function(someData) {
|
||||
$timeout(function() {
|
||||
$scope.someData = someData;
|
||||
@@ -161,7 +161,7 @@ In this second scenario, we are already inside a `$digest` when the ngFocus dire
|
||||
call to `$apply()`, causing this error to be thrown.
|
||||
|
||||
It is possible to workaround this problem by moving the call to set the focus outside of the digest,
|
||||
by using `$timeout(fn, 0, false)`, where the `false` value tells Angular not to wrap this `fn` in a
|
||||
by using `$timeout(fn, 0, false)`, where the `false` value tells Angular not to wrap this `fn` in an
|
||||
`$apply` block:
|
||||
|
||||
```
|
||||
@@ -200,7 +200,7 @@ the top of the call stack.
|
||||
Once you have identified this call you work your way up the stack to see what the problem is.
|
||||
|
||||
* If the second call was made in your application code then you should look at why this code has been
|
||||
called from within a `$apply`/`$digest`. It may be a simple oversight or maybe it fits with the
|
||||
called from within an `$apply`/`$digest`. It may be a simple oversight or maybe it fits with the
|
||||
sync/async scenario described earlier.
|
||||
|
||||
* If the second call was made inside an Angular directive then it is likely that it matches the second
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $sanitize:badparse
|
||||
@fullName Parsing Error while Sanitizing
|
||||
@description
|
||||
|
||||
This error occurs when the HTML string passed to '$sanitize' can't be parsed by the sanitizer.
|
||||
The error contains part of the html string that can't be parsed.
|
||||
|
||||
The parser is more strict than a typical browser parser, so it's possible that some obscure input would produce this error despite the string being recognized as valid HTML by a browser.
|
||||
|
||||
If a valid html code results in this error, please file a bug.
|
||||
@@ -0,0 +1,10 @@
|
||||
@ngdoc error
|
||||
@name $sanitize:noinert
|
||||
@fullName Can't create an inert html document
|
||||
@description
|
||||
|
||||
This error occurs when `$sanitize` sanitizer determines that `document.implementation.createHTMLDocument ` api is not supported by the current browser.
|
||||
|
||||
This api is necessary for safe parsing of HTML strings into DOM trees and without it the sanitizer can't sanitize the input.
|
||||
|
||||
The api is present in all supported browsers including IE 9.0, so the presence of this error usually indicates that Angular's `$sanitize` is being used on an unsupported platform.
|
||||
@@ -0,0 +1,13 @@
|
||||
@ngdoc error
|
||||
@name $sanitize:uinput
|
||||
@fullName Failed to sanitize html because the input is unstable
|
||||
@description
|
||||
|
||||
This error occurs when `$sanitize` sanitizer tries to check the input for possible mXSS payload and the verification
|
||||
errors due to the input mutating indefinitely. This could be a sign that the payload contains code exploiting an mXSS
|
||||
vulnerability in the browser.
|
||||
|
||||
mXSS attack exploit browser bugs that cause some browsers parse a certain html strings into DOM, which once serialized
|
||||
doesn't match the original input. These browser bugs can be exploited by attackers to create payload which looks
|
||||
harmless to sanitizers, but due to mutations caused by the browser are turned into dangerous code once processed after
|
||||
sanitization.
|
||||
@@ -0,0 +1,16 @@
|
||||
@ngdoc error
|
||||
@name linky:notstring
|
||||
@fullName Not a string
|
||||
@description
|
||||
|
||||
This error occurs when {@link ngSanitize.linky linky} is used with a non-empty, non-string value:
|
||||
```html
|
||||
<div ng-bind-html="42 | linky"></div>
|
||||
```
|
||||
|
||||
`linky` is supposed to be used with string values only, and therefore assumes that several methods
|
||||
(such as `.match()`) are available on the passed in value.
|
||||
The value can be initialized asynchronously and therefore null or undefined won't throw this error.
|
||||
|
||||
If you want to pass non-string values to `linky` (e.g. Objects whose `.toString()` should be
|
||||
utilized), you need to manually convert them to strings.
|
||||
@@ -0,0 +1,28 @@
|
||||
@ngdoc error
|
||||
@name ngModel:nopromise
|
||||
@fullName No promise
|
||||
@description
|
||||
|
||||
The return value of an async validator, must always be a promise. If you want to return a
|
||||
non-promise value, you can convert it to a promise using {@link ng.$q#resolve `$q.resolve()`} or
|
||||
{@link ng.$q#reject `$q.reject()`}.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
.directive('asyncValidator', function($q) {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elem, attrs, ngModel) {
|
||||
ngModel.$asyncValidators.myAsyncValidation = function(modelValue, viewValue) {
|
||||
if (/* I don't need to hit the backend API */) {
|
||||
return $q.resolve(); // to mark as valid or
|
||||
// return $q.reject(); // to mark as invalid
|
||||
} else {
|
||||
// ...send a request to the backend and return a promise
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
})
|
||||
```
|
||||
@@ -0,0 +1,52 @@
|
||||
@ngdoc error
|
||||
@name orderBy:notarray
|
||||
@fullName Value is not array-like
|
||||
@description
|
||||
|
||||
This error occurs when {@link ng.orderBy orderBy} is not passed an array-like value:
|
||||
```html
|
||||
<div ng-repeat="(key, value) in myObj | orderBy:someProp">
|
||||
{{ key }} : {{ value }}
|
||||
</div>
|
||||
```
|
||||
|
||||
`orderBy` must be used with an array-like value so a subset of items can be returned.
|
||||
The array can be initialized asynchronously and therefore `null` or `undefined` won't throw this error.
|
||||
|
||||
To use `orderBy` to order the properties of an object, you can create your own array based on that object:
|
||||
```js
|
||||
angular.module('aModule', [])
|
||||
.controller('aController', function($scope) {
|
||||
var myObj = {
|
||||
one: {id: 1, name: 'Some thing'},
|
||||
two: {id: 2, name: 'Another thing'},
|
||||
three: {id: 3, name: 'A third thing'}
|
||||
};
|
||||
|
||||
$scope.arrFromMyObj = Object.keys(myObj).map(function(key) {
|
||||
return myObj[key];
|
||||
});
|
||||
});
|
||||
```
|
||||
That can be used as:
|
||||
```html
|
||||
<label>
|
||||
Order by:
|
||||
<select ng-model="orderProp" ng-options="prop for prop in ['id', 'name']"></select>
|
||||
</label>
|
||||
<div ng-repeat="item in arrFromMyObj | orderBy:orderProp">
|
||||
[{{ item.id }}] {{ item.name }}
|
||||
</div>
|
||||
```
|
||||
|
||||
You could as well convert the object to an array using a filter such as
|
||||
[toArrayFilter](https://github.com/petebacondarwin/angular-toArrayFilter):
|
||||
```html
|
||||
<label>
|
||||
Order by:
|
||||
<select ng-model="orderProp" ng-options="prop for prop in ['id', 'name']"></select>
|
||||
</label>
|
||||
<div ng-repeat="item in myObj | toArray:false | orderBy:orderProp">
|
||||
[{{ item.id }}] {{ item.name }}
|
||||
</div>
|
||||
```
|
||||
@@ -356,15 +356,15 @@ legacy browsers and hashbang links in modern browser:
|
||||
|
||||
### Example
|
||||
|
||||
Here you can see two `$location` instances, both in **Html5 mode**, but on different browsers, so
|
||||
that you can see the differences. These `$location` services are connected to a fake browsers. Each
|
||||
input represents the address bar of the browser.
|
||||
Here you can see two `$location` instances that show the difference between **Html5 mode** and **Html5 Fallback mode**.
|
||||
Note that to simulate different levels of browser support, the `$location` instances are connected to
|
||||
a fakeBrowser service, which you don't have to set up in actual projects.
|
||||
|
||||
Note that when you type hashbang url into first browser (or vice versa) it doesn't rewrite /
|
||||
Note that when you type hashbang url into the first browser (or vice versa) it doesn't rewrite /
|
||||
redirect to regular / hashbang url, as this conversion happens only during parsing the initial URL
|
||||
= on page reload.
|
||||
|
||||
In these examples we use `<base href="/base/index.html" />`
|
||||
In these examples we use `<base href="/base/index.html" />`. The inputs represent the address bar of the browser.
|
||||
|
||||
#### Browser in HTML5 mode
|
||||
<example module="html5-mode" name="location-html5-mode">
|
||||
@@ -389,6 +389,7 @@ In these examples we use `<base href="/base/index.html" />`
|
||||
<file name="app.js">
|
||||
angular.module('html5-mode', ['fake-browser', 'address-bar'])
|
||||
|
||||
// Configure the fakeBrowser. Do not set these values in actual projects.
|
||||
.constant('initUrl', 'http://www.example.com/base/path?a=b#h')
|
||||
.constant('baseHref', '/base/index.html')
|
||||
.value('$sniffer', { history: true })
|
||||
@@ -538,6 +539,7 @@ In these examples we use `<base href="/base/index.html" />`
|
||||
<file name="app.js">
|
||||
angular.module('hashbang-mode', ['fake-browser', 'address-bar'])
|
||||
|
||||
// Configure the fakeBrowser. Do not set these values in actual projects.
|
||||
.constant('initUrl', 'http://www.example.com/base/index.html#!/path?a=b#h')
|
||||
.constant('baseHref', '/base/index.html')
|
||||
.value('$sniffer', { history: false })
|
||||
@@ -769,8 +771,8 @@ then uses the information it obtains to compose hashbang URLs (such as
|
||||
</tr>
|
||||
|
||||
<tr class="head">
|
||||
<td>Navigation outside the app</td>
|
||||
<td>Use lower level API</td>
|
||||
<th>Navigation outside the app</td>
|
||||
<th>Use lower level API</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@@ -784,8 +786,8 @@ then uses the information it obtains to compose hashbang URLs (such as
|
||||
</tr>
|
||||
|
||||
<tr class="head">
|
||||
<td>Read access</td>
|
||||
<td>Change to</td>
|
||||
<th>Read access</td>
|
||||
<th>Change to</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
||||
@@ -33,6 +33,9 @@ Currently, ngAria interfaces with the following directives:
|
||||
|
||||
* {@link guide/accessibility#ngmodel ngModel}
|
||||
* {@link guide/accessibility#ngdisabled ngDisabled}
|
||||
* {@link guide/accessibility#ngrequired ngRequired}
|
||||
* {@link guide/accessibility#ngvaluechecked ngChecked}
|
||||
* {@link guide/accessibility#ngvaluechecked ngValue}
|
||||
* {@link guide/accessibility#ngshow ngShow}
|
||||
* {@link guide/accessibility#nghide ngHide}
|
||||
* {@link guide/accessibility#ngclick ngClick}
|
||||
@@ -41,7 +44,7 @@ Currently, ngAria interfaces with the following directives:
|
||||
|
||||
<h2 id="ngmodel">ngModel</h2>
|
||||
|
||||
Much of ngAria's heavy lifting happens in the {@link ngModel ngModel}
|
||||
Much of ngAria's heavy lifting happens in the {@link ng.ngModel ngModel}
|
||||
directive. For elements using ngModel, special attention is paid by ngAria if that element also
|
||||
has a role or type of `checkbox`, `radio`, `range` or `textbox`.
|
||||
|
||||
@@ -134,35 +137,75 @@ attributes (if they have not been explicitly specified by the developer):
|
||||
|
||||
ngAria will also add `tabIndex`, ensuring custom elements with these roles will be reachable from
|
||||
the keyboard. It is still up to **you** as a developer to **ensure custom controls will be
|
||||
accessible**. As a rule, any time you create a widget involving user interaction, be sure to test
|
||||
accessible**. As a rule, any time you create a widget involving user interaction, be sure to test
|
||||
it with your keyboard and at least one mobile and desktop screen reader.
|
||||
|
||||
<h2 id="ngvaluechecked">ngValue and ngChecked</h2>
|
||||
|
||||
To ease the transition between native inputs and custom controls, ngAria now supports
|
||||
{@link ng.ngValue ngValue} and {@link ng.ngChecked ngChecked}.
|
||||
The original directives were created for native inputs only, so ngAria extends
|
||||
support to custom elements by managing `aria-checked` for accessibility.
|
||||
|
||||
###Example
|
||||
|
||||
```html
|
||||
<custom-checkbox ng-checked="val"></custom-checkbox>
|
||||
<custom-radio-button ng-value="val"></custom-radio-button>
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```html
|
||||
<custom-checkbox ng-checked="val" aria-checked="true"></custom-checkbox>
|
||||
<custom-radio-button ng-value="val" aria-checked="true"></custom-radio-button>
|
||||
```
|
||||
|
||||
<h2 id="ngdisabled">ngDisabled</h2>
|
||||
|
||||
The `disabled` attribute is only valid for certain elements such as `button`, `input` and
|
||||
`textarea`. To properly disable custom element directives such as `<md-checkbox>` or `<taco-tab>`,
|
||||
using ngAria with [ngDisabled](https://docs.angularjs.org/api/ng/directive/ngDisabled) will also
|
||||
using ngAria with {@link ng.ngDisabled ngDisabled} will also
|
||||
add `aria-disabled`. This tells assistive technologies when a non-native input is disabled, helping
|
||||
custom controls to be more accessible.
|
||||
|
||||
###Example
|
||||
|
||||
```html
|
||||
<md-checkbox ng-disabled="disabled">
|
||||
<md-checkbox ng-disabled="disabled"></md-checkbox>
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```html
|
||||
<md-checkbox disabled aria-disabled="true">
|
||||
<md-checkbox disabled aria-disabled="true"></md-checkbox>
|
||||
```
|
||||
|
||||
>You can check whether a control is legitimately disabled for a screen reader by visiting
|
||||
[chrome://accessibility](chrome://accessibility) and inspecting [the accessibility tree](http://www.paciellogroup.com/blog/2015/01/the-browser-accessibility-tree/).
|
||||
|
||||
<h2 id="ngrequired">ngRequired</h2>
|
||||
|
||||
The boolean `required` attribute is only valid for native form controls such as `input` and
|
||||
`textarea`. To properly indicate custom element directives such as `<md-checkbox>` or `<custom-input>`
|
||||
as required, using ngAria with {@link ng.ngRequired ngRequired} will also add
|
||||
`aria-required`. This tells accessibility APIs when a custom control is required.
|
||||
|
||||
###Example
|
||||
|
||||
```html
|
||||
<md-checkbox ng-required="val"></md-checkbox>
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```html
|
||||
<md-checkbox ng-required="val" aria-required="true"></md-checkbox>
|
||||
```
|
||||
|
||||
<h2 id="ngshow">ngShow</h2>
|
||||
|
||||
>The [ngShow](https://docs.angularjs.org/api/ng/directive/ngShow) directive shows or hides the
|
||||
>The {@link ng.ngShow ngShow} directive shows or hides the
|
||||
given HTML element based on the expression provided to the `ngShow` attribute. The element is
|
||||
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
|
||||
|
||||
@@ -199,7 +242,7 @@ Becomes:
|
||||
|
||||
<h2 id="nghide">ngHide</h2>
|
||||
|
||||
>The [ngHide](https://docs.angularjs.org/api/ng/directive/ngHide) directive shows or hides the
|
||||
>The {@link ng.ngHide ngHide} directive shows or hides the
|
||||
given HTML element based on the expression provided to the `ngHide` attribute. The element is
|
||||
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
|
||||
|
||||
@@ -208,7 +251,7 @@ The default CSS for `ngHide`, the inverse method to `ngShow`, makes ngAria redun
|
||||
`display: none`. See explanation for {@link guide/accessibility#ngshow ngShow} when overriding the default CSS.
|
||||
|
||||
<h2><span id="ngclick">ngClick</span> and <span id="ngdblclick">ngDblclick</span></h2>
|
||||
If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex="0"` to any element not in
|
||||
If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex="0"` to any element not in
|
||||
a node blacklist:
|
||||
|
||||
* Button
|
||||
@@ -218,14 +261,14 @@ a node blacklist:
|
||||
* Select
|
||||
* Details/Summary
|
||||
|
||||
To fix widespread accessibility problems with `ng-click` on `div` elements, ngAria will
|
||||
To fix widespread accessibility problems with `ng-click` on `div` elements, ngAria will
|
||||
dynamically bind a keypress event by default as long as the element isn't in the node blacklist.
|
||||
You can turn this functionality on or off with the `bindKeypress` configuration option.
|
||||
You can turn this functionality on or off with the `bindKeypress` configuration option.
|
||||
|
||||
ngAria will also add the `button` role to communicate to users of assistive technologies. This can
|
||||
be disabled with the `bindRoleForClick` configuration option.
|
||||
|
||||
For `ng-dblclick`, you must still manually add `ng-keypress` and a role to non-interactive elements
|
||||
For `ng-dblclick`, you must still manually add `ng-keypress` and a role to non-interactive elements
|
||||
such as `div` or `taco-button` to enable keyboard access.
|
||||
|
||||
<h3>Example</h3>
|
||||
|
||||
@@ -274,6 +274,37 @@ myModule.directive('my-directive', ['$animate', function($animate) {
|
||||
}]);
|
||||
```
|
||||
|
||||
## Preventing flicker before an animation starts
|
||||
|
||||
When nesting elements with structural animations such as `ngIf` into elements that have class-based
|
||||
animations such as `ngClass`, it sometimes happens that before the actual animation starts, there is a brief flicker or flash of content
|
||||
where the animated element is briefly visible.
|
||||
|
||||
To prevent this, you can apply styles to the `ng-[event]-prepare` class, which is added as soon as an animation is initialized,
|
||||
but removed before the actual animation starts (after waiting for a $digest). This class is only added for *structural*
|
||||
animations (`enter`, `move`, and `leave`).
|
||||
|
||||
Here's an example where you might see flickering:
|
||||
|
||||
```html
|
||||
<div ng-class="{red: myProp}">
|
||||
<div ng-class="{blue: myProp}">
|
||||
<div class="message" ng-if="myProp"></div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
It is possible that during the `enter` event, the `.message` div will be briefly visible before it starts animating.
|
||||
In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts:
|
||||
|
||||
```css
|
||||
.message.ng-enter-prepare {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Other animation styles ... */
|
||||
```
|
||||
|
||||
## More about animations
|
||||
|
||||
For a full breakdown of each method available on `$animate`, see the {@link ng.$animate API documentation}.
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
@ngdoc overview
|
||||
@name Components
|
||||
@sortOrder 305
|
||||
@description
|
||||
|
||||
# Understanding Components
|
||||
|
||||
In Angular, a Component is a special kind of {@link guide/directive directive} that uses a simpler
|
||||
configuration which is suitable for a component-based application structure.
|
||||
|
||||
This makes it easier to write an app in a way that's similar to using Web Components or using Angular
|
||||
2's style of application architecture.
|
||||
|
||||
Advantages of Components:
|
||||
- simpler configuration than plain directives
|
||||
- promote sane defaults and best practices
|
||||
- optimized for component-based architecture
|
||||
- writing component directives will make it easier to upgrade to Angular 2
|
||||
|
||||
When not to use Components:
|
||||
|
||||
- for directives that rely on DOM manipulation, adding event listeners etc, because the compile
|
||||
and link functions are unavailable
|
||||
- when you need advanced directive definition options like priority, terminal, multi-element
|
||||
- when you want a directive that is triggered by an attribute or CSS class, rather than an element
|
||||
|
||||
## Creating and configuring a Component
|
||||
|
||||
Components can be registered using the `.component()` method of an Angular module (returned by {@link module `angular.module()`}). The method takes two arguments:
|
||||
|
||||
* The name of the Component (as string).
|
||||
* The Component config object (note that, unlike the `.directive()` method, this method does **not** take a factory function.
|
||||
|
||||
<example name="heroComponentSimple" module="heroApp">
|
||||
<file name="index.js">
|
||||
angular.module('heroApp', []).controller('mainCtrl', function() {
|
||||
this.hero = {
|
||||
name: 'Spawn'
|
||||
};
|
||||
});
|
||||
</file>
|
||||
<file name="heroDetail.js">
|
||||
|
||||
function HeroDetailController() {
|
||||
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('heroDetail', {
|
||||
templateUrl: 'heroDetail.html',
|
||||
controller: HeroDetailController,
|
||||
bindings: {
|
||||
hero: '='
|
||||
}
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<!-- components match only elements -->
|
||||
<div ng-controller="mainCtrl as ctrl">
|
||||
<b>Hero</b><br>
|
||||
<hero-detail hero="ctrl.hero"></hero-detail>
|
||||
</div>
|
||||
</file>
|
||||
<file name="heroDetail.html">
|
||||
<span>Name: {{$ctrl.hero.name}}</span>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
It's also possible to add components via {@link $compileProvider#component} in a module's config phase.
|
||||
|
||||
### Comparison between Directive definition and Component definition
|
||||
|
||||
| | Directive | Component |
|
||||
|-------------------|----------------------|-----------------|
|
||||
| bindings | No | Yes (binds to controller) |
|
||||
| bindToController | Yes (default: false) | No (use bindings instead) |
|
||||
| compile function | Yes | No |
|
||||
| controller | Yes | Yes (default `function() {}`) |
|
||||
| controllerAs | Yes (default: false) | Yes (default: `$ctrl`) |
|
||||
| link functions | Yes | No |
|
||||
| multiElement | Yes | No |
|
||||
| priority | Yes | No |
|
||||
| require | Yes | Yes |
|
||||
| restrict | Yes | No (restricted to elements only) |
|
||||
| scope | Yes (default: false) | No (scope is always isolate) |
|
||||
| template | Yes | Yes, injectable |
|
||||
| templateNamespace | Yes | No |
|
||||
| templateUrl | Yes | Yes, injectable |
|
||||
| terminal | Yes | No |
|
||||
| transclude | Yes (default: false) | Yes (default: false) |
|
||||
|
||||
|
||||
## Component-based application architecture
|
||||
|
||||
As already mentioned, the component helper makes it easier to structure your application with
|
||||
a component-based architecture. But what makes a component beyond the options that
|
||||
the component helper has?
|
||||
|
||||
- **Components only control their own View and Data:**
|
||||
Components should never modify any data or DOM that is out of their own scope. Normally, in Angular
|
||||
it is possible to modify data anywhere in the application through scope inheritance and watches. This
|
||||
is practical, but can also lead to problems when it is not clear which part of the application is
|
||||
responsible for modifying the data. That is why component directives use an isolate scope, so a whole
|
||||
class of scope manipulation is not possible.
|
||||
|
||||
- **Components have a well-defined public API - Inputs and Outputs:**
|
||||
However, scope isolation only goes so far, because Angular uses two-way binding. So if you pass
|
||||
an object to a component like this - `bindings: {item: '='}`, and modify one of its properties, the
|
||||
change will be reflected in the parent component. For components however, only the component that owns
|
||||
the data should modify it, to make it easy to reason about what data is changed, and when. For that reason,
|
||||
components should follow a few simple conventions:
|
||||
|
||||
- Inputs are realized with `@` and `=` bindings
|
||||
```js
|
||||
bindings: {
|
||||
hero: `=`,
|
||||
}
|
||||
```
|
||||
- Outputs are realized with `&` bindings, which function as callbacks to component events
|
||||
```js
|
||||
bindings: {
|
||||
onDelete: '&',
|
||||
onUpdate: '&'
|
||||
}
|
||||
```
|
||||
- Instead of manipulating Input Data, the component calls the correct Output Event with the changed data.
|
||||
For a deletion, that means the component doesn't delete the `hero` itself, but sends it back to
|
||||
the owner component via the correct event.
|
||||
```html
|
||||
<button ng-click="$ctrl.onDelete({hero: hero})">Delete</button>
|
||||
```
|
||||
- That way, the parent component can decide what to do with the event (e.g. delete an item or update the properties)
|
||||
```js
|
||||
ctrl.deleteHero(hero) {
|
||||
$http.delete(...).then(function() {
|
||||
var idx = ctrl.list.indexOf(hero);
|
||||
if (idx >= 0) {
|
||||
ctrl.list.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- **An application is a tree of components:**
|
||||
Ideally, the whole application should be a tree of components that implement clearly defined inputs
|
||||
and outputs, and minimize two-way data binding. That way, it's easier to predict when data changes and what the state
|
||||
of a component is.
|
||||
|
||||
## Example of a component tree
|
||||
|
||||
The following example expands on the simple component example and incorporates the concepts we introduced
|
||||
above:
|
||||
|
||||
Instead of an ngController, we now have a heroList component that holds the data of
|
||||
different heroes, and creates a heroDetail for each of them.
|
||||
|
||||
The heroDetail component now contains the following new functionality:
|
||||
- a delete button that calls the bound onDelete function of the heroList component
|
||||
- an input to change the hero location, in the form of a reusable editableField component. Instead
|
||||
of manipulating the hero object itself, it sends the changes upwards to the heroDetail, which sends
|
||||
it upwards to the heroList component, which updates it.
|
||||
|
||||
<example name="heroComponentTree" module="heroApp">
|
||||
<file name="index.js">
|
||||
var mode = angular.module('heroApp', []);
|
||||
</file>
|
||||
|
||||
<file name="heroList.js">
|
||||
function HeroListController($scope, $element, $attrs) {
|
||||
var ctrl = this;
|
||||
|
||||
// This would be loaded by $http etc.
|
||||
ctrl.list = [
|
||||
{
|
||||
name: 'Superman',
|
||||
location: ''
|
||||
},
|
||||
{
|
||||
name: 'Batman',
|
||||
location: 'Wayne Manor'
|
||||
}
|
||||
];
|
||||
|
||||
ctrl.updateHero = function(hero, prop, value) {
|
||||
hero[prop] = value;
|
||||
};
|
||||
|
||||
ctrl.deleteHero = function(hero) {
|
||||
var idx = ctrl.list.indexOf(hero);
|
||||
if (idx >= 0) {
|
||||
ctrl.list.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('heroList', {
|
||||
templateUrl: 'heroList.html',
|
||||
controller: HeroListController
|
||||
});
|
||||
|
||||
</file>
|
||||
|
||||
<file name="heroDetail.js">
|
||||
function HeroDetailController($scope, $element, $attrs) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.update = function(prop, value) {
|
||||
ctrl.onUpdate({hero: ctrl.hero, prop: prop, value: value});
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('heroDetail', {
|
||||
templateUrl: 'heroDetail.html',
|
||||
controller: HeroDetailController,
|
||||
bindings: {
|
||||
hero: '=',
|
||||
onDelete: '&',
|
||||
onUpdate: '&'
|
||||
}
|
||||
});
|
||||
</file>
|
||||
|
||||
<file name="editableField.js">
|
||||
|
||||
function EditableFieldController($scope, $element, $attrs) {
|
||||
var ctrl = this;
|
||||
this.editMode = false;
|
||||
|
||||
this.handleModeChange = function() {
|
||||
if (ctrl.editMode) {
|
||||
ctrl.onUpdate({value: ctrl.fieldValue});
|
||||
ctrl.fieldValueCopy = ctrl.fieldValue;
|
||||
}
|
||||
ctrl.editMode = !ctrl.editMode;
|
||||
};
|
||||
|
||||
this.reset = function() {
|
||||
ctrl.fieldValue = ctrl.fieldValueCopy;
|
||||
};
|
||||
|
||||
this.$onInit = function() {
|
||||
// Make a copy of the initial value to be able to reset it later
|
||||
this.fieldValueCopy = this.fieldValue;
|
||||
|
||||
// Set a default fieldType
|
||||
if (!this.fieldType) {
|
||||
this.fieldType = 'text';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('editableField', {
|
||||
templateUrl: 'editableField.html',
|
||||
controller: EditableFieldController,
|
||||
bindings: {
|
||||
fieldValue: '@',
|
||||
fieldType: '@',
|
||||
onUpdate: '&'
|
||||
}
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<hero-list></hero-list>
|
||||
</file>
|
||||
<file name="heroList.html">
|
||||
<b>Heroes</b><br>
|
||||
<hero-detail ng-repeat="hero in $ctrl.list" hero="hero" on-delete="$ctrl.deleteHero(hero)" on-update="$ctrl.updateHero(hero, prop, value)"></hero-detail>
|
||||
</file>
|
||||
<file name="heroDetail.html">
|
||||
<hr>
|
||||
<div>
|
||||
Name: {{$ctrl.hero.name}}<br>
|
||||
Location: <editable-field field-value="{{$ctrl.hero.location}}" field-type="text" on-update="$ctrl.update('location', value)"></editable-field><br>
|
||||
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
|
||||
</div>
|
||||
</file>
|
||||
<file name="editableField.html">
|
||||
<span ng-switch="$ctrl.editMode">
|
||||
<input ng-switch-when="true" type="text" ng-model="$ctrl.fieldValue">
|
||||
<span ng-switch-default>{{$ctrl.fieldValue}}</span>
|
||||
</span>
|
||||
<button ng-click="$ctrl.handleModeChange()">{{$ctrl.editMode ? 'Save' : 'Edit'}}</button>
|
||||
<button ng-if="$ctrl.editMode" ng-click="$ctrl.reset()">Reset</button>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
## Components as route templates
|
||||
Components are also useful as route templates (e.g. when using {@link ngRoute ngRoute}). In a component-based
|
||||
application, every view is a component:
|
||||
|
||||
```js
|
||||
var myMod = angular.module('myMod', ['ngRoute']);
|
||||
myMod.component('home', {
|
||||
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
|
||||
controller: function() {
|
||||
this.user = {name: 'world'};
|
||||
}
|
||||
});
|
||||
myMod.config(function($routeProvider) {
|
||||
$routeProvider.when('/', {
|
||||
template: '<home></home>'
|
||||
});
|
||||
});
|
||||
```
|
||||
<br />
|
||||
When using {@link ngRoute.$routeProvider $routeProvider}, you can often avoid some
|
||||
boilerplate, by assigning the resolved dependencies directly to the route scope:
|
||||
```js
|
||||
var myMod = angular.module('myMod', ['ngRoute']);
|
||||
myMod.component('home', {
|
||||
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
|
||||
bindings: {user: '='}
|
||||
});
|
||||
myMod.config(function($routeProvider) {
|
||||
$routeProvider.when('/', {
|
||||
template: '<home user="$resolve.user"></home>',
|
||||
resolve: {user: function($http) { return $http.get('...'); }}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Intercomponent Communication
|
||||
|
||||
Directives can require the controllers of other directives to enable communication
|
||||
between each other. This can be achieved in a component by providing an
|
||||
object mapping for the `require` property. Here is a tab pane example built
|
||||
from components:
|
||||
|
||||
<example module="docsTabsExample">
|
||||
<file name="script.js">
|
||||
angular.module('docsTabsExample', [])
|
||||
.component('myTabs', {
|
||||
transclude: true,
|
||||
controller: function() {
|
||||
var panes = this.panes = [];
|
||||
this.select = function(pane) {
|
||||
angular.forEach(panes, function(pane) {
|
||||
pane.selected = false;
|
||||
});
|
||||
pane.selected = true;
|
||||
};
|
||||
this.addPane = function(pane) {
|
||||
if (panes.length === 0) {
|
||||
this.select(pane);
|
||||
}
|
||||
panes.push(pane);
|
||||
};
|
||||
},
|
||||
templateUrl: 'my-tabs.html'
|
||||
})
|
||||
.component('myPane', {
|
||||
transclude: true,
|
||||
require: {tabsCtrl: '^myTabs'},
|
||||
bindings: {
|
||||
title: '@'
|
||||
},
|
||||
controller: function() {
|
||||
this.$onInit = function() {
|
||||
this.tabsCtrl.addPane(this);
|
||||
console.log(this);
|
||||
};
|
||||
},
|
||||
templateUrl: 'my-pane.html'
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<my-tabs>
|
||||
<my-pane title="Hello">
|
||||
<h4>Hello</h4>
|
||||
<p>Lorem ipsum dolor sit amet</p>
|
||||
</my-pane>
|
||||
<my-pane title="World">
|
||||
<h4>World</h4>
|
||||
<em>Mauris elementum elementum enim at suscipit.</em>
|
||||
<p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p>
|
||||
</my-pane>
|
||||
</my-tabs>
|
||||
</file>
|
||||
<file name="my-tabs.html">
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs">
|
||||
<li ng-repeat="pane in $ctrl.panes" ng-class="{active:pane.selected}">
|
||||
<a href="" ng-click="$ctrl.select(pane)">{{pane.title}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" ng-transclude></div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="my-pane.html">
|
||||
<div class="tab-pane" ng-show="$ctrl.selected" ng-transclude></div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
# Unit-testing Component Controllers
|
||||
|
||||
The easiest way to unit-test a component controller is by using the {@link ngMock.$componentController $componentController}
|
||||
that is included in {@link ngMock}. The advantage of this method is that you do not have
|
||||
to create any DOM elements. The following example shows how to do this for the `heroDetail` component
|
||||
from above.
|
||||
|
||||
The examples use the [Jasmine](http://jasmine.github.io/) testing framework.
|
||||
|
||||
**Controller Test:**
|
||||
```js
|
||||
describe('component: heroDetail', function() {
|
||||
var component, scope, hero;
|
||||
|
||||
beforeEach(module('simpleComponent'));
|
||||
|
||||
beforeEach(inject(function($rootScope, $componentController) {
|
||||
scope = $rootScope.$new();
|
||||
hero = {name: 'Wolverine'};
|
||||
}));
|
||||
|
||||
it('should set the default values of the hero', function() {
|
||||
// It's necessary to always pass the scope in the locals, so that the controller instance can be bound to it
|
||||
component = $componentController('heroDetail', {$scope: scope});
|
||||
|
||||
expect(component.hero).toEqual({
|
||||
name: undefined,
|
||||
location: 'unknown'
|
||||
});
|
||||
});
|
||||
|
||||
it('should assign the name bindings to the hero object', function() {
|
||||
// Here we are passing actual bindings to the component
|
||||
|
||||
component = $componentController('heroDetail',
|
||||
{$scope: scope},
|
||||
{hero: hero}
|
||||
);
|
||||
expect(component.hero.name).toBe('Wolverine');
|
||||
});
|
||||
|
||||
it('should call the onDelete binding when a hero is deleted', function() {
|
||||
component = $componentController('heroDetail',
|
||||
{$scope: scope},
|
||||
{hero: hero, onDelete: jasmine.createSpy('deleteSpy')}
|
||||
);
|
||||
|
||||
component.onDelete({hero: component.hero});
|
||||
expect(spy('deleteSpy')).toHaveBeenCalledWith(component.hero);
|
||||
});
|
||||
|
||||
});
|
||||
```
|
||||
@@ -76,7 +76,7 @@ stores/updates the value of the input field into/from a variable.
|
||||
The second kind of new markup are the double curly braces `{{ expression | filter }}`:
|
||||
When the compiler encounters this markup, it will replace it with the evaluated value of the markup.
|
||||
An <a name="expression">{@link expression expression}</a> in a template is a JavaScript-like code snippet that allows
|
||||
to read and write variables. Note that those variables are not global variables.
|
||||
Angular to read and write variables. Note that those variables are not global variables.
|
||||
Just like variables in a JavaScript function live in a scope,
|
||||
Angular provides a <a name="scope">{@link scope scope}</a> for the variables accessible to expressions.
|
||||
The values that are stored in variables on the scope are referred to as the <a name="model"></a>*model*
|
||||
@@ -334,9 +334,9 @@ The following example shows how this is done with Angular:
|
||||
var refresh = function() {
|
||||
var url = YAHOO_FINANCE_URL_PATTERN.
|
||||
replace('PAIRS', 'USD' + currencies.join('","USD'));
|
||||
return $http.jsonp(url).success(function(data) {
|
||||
return $http.jsonp(url).then(function(response) {
|
||||
var newUsdToForeignRates = {};
|
||||
angular.forEach(data.query.results.rate, function(rate) {
|
||||
angular.forEach(response.data.query.results.rate, function(rate) {
|
||||
var currency = rate.id.substring(3,6);
|
||||
newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);
|
||||
});
|
||||
@@ -348,8 +348,7 @@ The following example shows how this is done with Angular:
|
||||
|
||||
return {
|
||||
currencies: currencies,
|
||||
convert: convert,
|
||||
refresh: refresh
|
||||
convert: convert
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
|
||||
@@ -43,8 +43,7 @@ mirrors the process of compiling source code in
|
||||
Before we can write a directive, we need to know how Angular's {@link guide/compiler HTML compiler}
|
||||
determines when to use a given directive.
|
||||
|
||||
Similar to the terminology used when an [element **matches** a selector]
|
||||
(https://developer.mozilla.org/en-US/docs/Web/API/Element.matches), we say an element **matches** a
|
||||
Similar to the terminology used when an [element **matches** a selector](https://developer.mozilla.org/en-US/docs/Web/API/Element.matches), we say an element **matches** a
|
||||
directive when the directive is part of its declaration.
|
||||
|
||||
In the following example, we say that the `<input>` element **matches** the `ngModel` directive
|
||||
@@ -100,8 +99,13 @@ For example, the following forms are all equivalent and match the {@link ngBind}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should show off bindings', function() {
|
||||
expect(element(by.css('div[ng-controller="Controller"] span[ng-bind]')).getText())
|
||||
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)');
|
||||
var containerElm = element(by.css('div[ng-controller="Controller"]'));
|
||||
var nameBindings = containerElm.all(by.binding('name'));
|
||||
|
||||
expect(nameBindings.count()).toBe(5);
|
||||
nameBindings.each(function(elem) {
|
||||
expect(elem.getText()).toEqual('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)');
|
||||
});
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
@@ -142,63 +146,6 @@ directives when possible.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
### Text and attribute bindings
|
||||
|
||||
During the compilation process the {@link ng.$compile compiler} matches text and attributes
|
||||
using the {@link ng.$interpolate $interpolate} service to see if they contain embedded
|
||||
expressions. These expressions are registered as {@link ng.$rootScope.Scope#$watch watches}
|
||||
and will update as part of normal {@link ng.$rootScope.Scope#$digest digest} cycle. An
|
||||
example of interpolation is shown below:
|
||||
|
||||
```html
|
||||
<a ng-href="img/{{username}}.jpg">Hello {{username}}!</a>
|
||||
```
|
||||
|
||||
|
||||
### `ngAttr` attribute bindings
|
||||
|
||||
Web browsers are sometimes picky about what values they consider valid for attributes.
|
||||
|
||||
For example, considering this template:
|
||||
|
||||
```html
|
||||
<svg>
|
||||
<circle cx="{{cx}}"></circle>
|
||||
</svg>
|
||||
```
|
||||
|
||||
We would expect Angular to be able to bind to this, but when we check the console we see
|
||||
something like `Error: Invalid value for attribute cx="{{cx}}"`. Because of the SVG DOM API's
|
||||
restrictions, you cannot simply write `cx="{{cx}}"`.
|
||||
|
||||
With `ng-attr-cx` you can work around this problem.
|
||||
|
||||
If an attribute with a binding is prefixed with the `ngAttr` prefix (denormalized as `ng-attr-`)
|
||||
then during the binding it will be applied to the corresponding unprefixed attribute. This allows
|
||||
you to bind to attributes that would otherwise be eagerly processed by browsers
|
||||
(e.g. an SVG element's `circle[cx]` attributes). When using `ngAttr`, the `allOrNothing` flag of
|
||||
{@link ng.$interpolate $interpolate} is used, so if any expression in the interpolated string
|
||||
results in `undefined`, the attribute is removed and not added to the element.
|
||||
|
||||
For example, we could fix the example above by instead writing:
|
||||
|
||||
```html
|
||||
<svg>
|
||||
<circle ng-attr-cx="{{cx}}"></circle>
|
||||
</svg>
|
||||
```
|
||||
|
||||
If one wants to modify a camelcased attribute (SVG elements have valid camelcased attributes), such as `viewBox` on the `svg` element, one can use underscores to denote that the attribute to bind to is naturally camelcased.
|
||||
|
||||
For example, to bind to `viewBox`, we can write:
|
||||
|
||||
```html
|
||||
<svg ng-attr-view_box="{{viewBox}}">
|
||||
</svg>
|
||||
```
|
||||
|
||||
|
||||
## Creating Directives
|
||||
|
||||
First let's talk about the {@link ng.$compileProvider#directive API for registering directives}. Much like
|
||||
@@ -356,6 +303,7 @@ The `restrict` option is typically set to:
|
||||
* `'A'` - only matches attribute name
|
||||
* `'E'` - only matches element name
|
||||
* `'C'` - only matches class name
|
||||
* `'M'` - only matches comment
|
||||
|
||||
These restrictions can all be combined as needed:
|
||||
|
||||
@@ -459,7 +407,7 @@ This is clearly not a great solution.
|
||||
|
||||
What we want to be able to do is separate the scope inside a directive from the scope
|
||||
outside, and then map the outer scope to a directive's inner scope. We can do this by creating what
|
||||
we call an **isolate scope**. To do this, we can use a directive's `scope` option:
|
||||
we call an **isolate scope**. To do this, we can use a {@link $compile#-scope- directive's `scope`} option:
|
||||
|
||||
<example module="docsIsolateScopeDirective">
|
||||
<file name="script.js">
|
||||
@@ -588,14 +536,24 @@ want to reuse throughout your app.
|
||||
In this example we will build a directive that displays the current time.
|
||||
Once a second, it updates the DOM to reflect the current time.
|
||||
|
||||
Directives that want to modify the DOM typically use the `link` option.
|
||||
`link` takes a function with the following signature, `function link(scope, element, attrs) { ... }`
|
||||
where:
|
||||
Directives that want to modify the DOM typically use the `link` option to register DOM listeners
|
||||
as well as update the DOM. It is executed after the template has been cloned and is where
|
||||
directive logic will be put.
|
||||
|
||||
`link` takes a function with the following signature,
|
||||
`function link(scope, element, attrs, controller, transcludeFn) { ... }`, where:
|
||||
|
||||
* `scope` is an Angular scope object.
|
||||
* `element` is the jqLite-wrapped element that this directive matches.
|
||||
* `attrs` is a hash object with key-value pairs of normalized attribute names and their
|
||||
corresponding attribute values.
|
||||
* `controller` is the directive's required controller instance(s) or its own controller (if any).
|
||||
The exact value depends on the directive's require property.
|
||||
* `transcludeFn` is a transclude linking function pre-bound to the correct transclusion scope.
|
||||
|
||||
<div class="alert alert-info">
|
||||
For more details on the `link` option refer to the {@link ng.$compile#-link- `$compile` API} page.
|
||||
</div>
|
||||
|
||||
In our `link` function, we want to update the displayed time once a second, or whenever a user
|
||||
changes the time formatting string that our directive binds to. We will use the `$interval` service
|
||||
@@ -689,6 +647,7 @@ To do this, we need to use the `transclude` option.
|
||||
return {
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {},
|
||||
templateUrl: 'my-dialog.html'
|
||||
};
|
||||
});
|
||||
@@ -699,8 +658,7 @@ To do this, we need to use the `transclude` option.
|
||||
</div>
|
||||
</file>
|
||||
<file name="my-dialog.html">
|
||||
<div class="alert" ng-transclude>
|
||||
</div>
|
||||
<div class="alert" ng-transclude></div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
@@ -722,7 +680,7 @@ that redefines `name` as `Jeff`. What do you think the `{{name}}` binding will r
|
||||
transclude: true,
|
||||
scope: {},
|
||||
templateUrl: 'my-dialog.html',
|
||||
link: function (scope, element) {
|
||||
link: function (scope) {
|
||||
scope.name = 'Jeff';
|
||||
}
|
||||
};
|
||||
@@ -734,8 +692,7 @@ that redefines `name` as `Jeff`. What do you think the `{{name}}` binding will r
|
||||
</div>
|
||||
</file>
|
||||
<file name="my-dialog.html">
|
||||
<div class="alert" ng-transclude>
|
||||
</div>
|
||||
<div class="alert" ng-transclude></div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
@@ -746,7 +703,7 @@ The `transclude` option changes the way scopes are nested. It makes it so that t
|
||||
transcluded directive have whatever scope is outside the directive, rather than whatever scope is on
|
||||
the inside. In doing so, it gives the contents access to the outside scope.
|
||||
|
||||
Note that if the directive did not create its own scope, then `scope` in `scope.name = 'Jeff';` would
|
||||
Note that if the directive did not create its own scope, then `scope` in `scope.name = 'Jeff'` would
|
||||
reference the outside scope and we would see `Jeff` in the output.
|
||||
|
||||
This behavior makes sense for a directive that wraps some content, because otherwise you'd have to
|
||||
@@ -819,9 +776,9 @@ function.
|
||||
|
||||
Often it's desirable to pass data from the isolate scope via an expression to the
|
||||
parent scope, this can be done by passing a map of local variable names and values into the expression
|
||||
wrapper fn. For example, the hideDialog function takes a message to display when the dialog is hidden.
|
||||
This is specified in the directive by calling `close({message: 'closing for now'})`. Then the local
|
||||
variable `message` will be available within the `on-close` expression.
|
||||
wrapper function. For example, the `hideDialog` function takes a message to display when the dialog
|
||||
is hidden. This is specified in the directive by calling `close({message: 'closing for now'})`.
|
||||
Then the local variable `message` will be available within the `on-close` expression.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** use `&attr` in the `scope` option when you want your directive
|
||||
@@ -880,7 +837,7 @@ element?
|
||||
}]);
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<span my-draggable>Drag ME</span>
|
||||
<span my-draggable>Drag Me</span>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
@@ -903,7 +860,7 @@ to which tab is active.
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {},
|
||||
controller: function($scope) {
|
||||
controller: ['$scope', function($scope) {
|
||||
var panes = $scope.panes = [];
|
||||
|
||||
$scope.select = function(pane) {
|
||||
@@ -919,13 +876,13 @@ to which tab is active.
|
||||
}
|
||||
panes.push(pane);
|
||||
};
|
||||
},
|
||||
}],
|
||||
templateUrl: 'my-tabs.html'
|
||||
};
|
||||
})
|
||||
.directive('myPane', function() {
|
||||
return {
|
||||
require: '^myTabs',
|
||||
require: '^^myTabs',
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {
|
||||
@@ -941,11 +898,9 @@ to which tab is active.
|
||||
<file name="index.html">
|
||||
<my-tabs>
|
||||
<my-pane title="Hello">
|
||||
<h4>Hello</h4>
|
||||
<p>Lorem ipsum dolor sit amet</p>
|
||||
</my-pane>
|
||||
<my-pane title="World">
|
||||
<h4>World</h4>
|
||||
<em>Mauris elementum elementum enim at suscipit.</em>
|
||||
<p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p>
|
||||
</my-pane>
|
||||
@@ -962,22 +917,25 @@ to which tab is active.
|
||||
</div>
|
||||
</file>
|
||||
<file name="my-pane.html">
|
||||
<div class="tab-pane" ng-show="selected" ng-transclude>
|
||||
<div class="tab-pane" ng-show="selected">
|
||||
<h4>{{title}}</h4>
|
||||
<div ng-transclude></div>
|
||||
</div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
The `myPane` directive has a `require` option with value `^myTabs`. When a directive uses this
|
||||
option, `$compile` will throw an error unless the specified controller is found. The `^` prefix
|
||||
means that this directive searches for the controller on its parents (without the `^` prefix, the
|
||||
directive would look for the controller on just its own element).
|
||||
The `myPane` directive has a `require` option with value `^^myTabs`. When a directive uses this
|
||||
option, `$compile` will throw an error unless the specified controller is found. The `^^` prefix
|
||||
means that this directive searches for the controller on its parents. (A `^` prefix would make the
|
||||
directive look for the controller on its own element or its parents; without any prefix, the
|
||||
directive would look on its own element only.)
|
||||
|
||||
So where does this `myTabs` controller come from? Directives can specify controllers using
|
||||
the unsurprisingly named `controller` option. As you can see, the `myTabs` directive uses this
|
||||
option. Just like `ngController`, this option attaches a controller to the template of the directive.
|
||||
|
||||
If it is necessary to reference the controller or any functions bound to the controller's scope in
|
||||
the template, you can use the option `controllerAs` to specify the name of the controller as an alias.
|
||||
If it is necessary to reference the controller or any functions bound to the controller from the
|
||||
template, you can use the option `controllerAs` to specify the name of the controller as an alias.
|
||||
The directive needs to define a scope for this configuration to be used. This is particularly useful
|
||||
in the case when the directive is used as a component.
|
||||
|
||||
@@ -992,7 +950,7 @@ The corresponding parameter being sent to the `link` function will also be an ar
|
||||
angular.module('docsTabsExample', [])
|
||||
.directive('myPane', function() {
|
||||
return {
|
||||
require: ['^myTabs', '^ngModel'],
|
||||
require: ['^^myTabs', 'ngModel'],
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {
|
||||
@@ -1028,4 +986,3 @@ available in the {@link guide/compiler compiler guide}.
|
||||
|
||||
The {@link ng.$compile `$compile` API} page has a comprehensive list of directive options for
|
||||
reference.
|
||||
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
|
||||
# Angular Expressions
|
||||
|
||||
Angular expressions are JavaScript-like code snippets that are usually placed in bindings such as
|
||||
`{{ expression }}`.
|
||||
Angular expressions are JavaScript-like code snippets that are mainly placed in
|
||||
interpolation bindings such as `<span title="{{ attrBinding }}">{{ textBinding }}</span>`,
|
||||
but also used directly in directive attributes such as `ng-click="functionExpression()"`.
|
||||
|
||||
For example, these are valid expressions in Angular:
|
||||
|
||||
@@ -35,7 +36,9 @@ Angular expressions are like JavaScript expressions with the following differenc
|
||||
* **No RegExp Creation With Literal Notation:** You cannot create regular expressions
|
||||
in an Angular expression.
|
||||
|
||||
* **No Comma And Void Operators:** You cannot use `,` or `void` in an Angular expression.
|
||||
* **No Object Creation With New Operator:** You cannot use `new` operator in an Angular expression.
|
||||
|
||||
* **No Comma And Void Operators:** You cannot use `,` or `void` operators in an Angular expression.
|
||||
|
||||
* **Filters:** You can use {@link guide/filter filters} within expressions to format data before
|
||||
displaying it.
|
||||
@@ -113,6 +116,9 @@ This restriction is intentional. It prevents accidental access to the global sta
|
||||
Instead use services like `$window` and `$location` in functions called from expressions. Such services
|
||||
provide mockable access to globals.
|
||||
|
||||
It is possible to access the context object using the identifier `this` and the locals object using the
|
||||
identifier `$locals`.
|
||||
|
||||
<example module="expressionExample">
|
||||
<file name="index.html">
|
||||
<div class="example2" ng-controller="ExampleController">
|
||||
@@ -280,7 +286,7 @@ result is a non-undefined value (see value stabilization algorithm below).
|
||||
</example>
|
||||
|
||||
|
||||
### Why this feature
|
||||
### Reasons for using one-time binding
|
||||
|
||||
The main purpose of one-time binding expression is to provide a way to create a binding
|
||||
that gets deregistered and frees up resources once the binding is stabilized.
|
||||
|
||||
@@ -32,10 +32,13 @@ E.g. the markup `{{ 1234 | number:2 }}` formats the number 1234 with 2 decimal p
|
||||
|
||||
## Using filters in controllers, services, and directives
|
||||
|
||||
You can also use filters in controllers, services, and directives. For this, inject a dependency
|
||||
with the name `<filterName>Filter` to your controller/service/directive. E.g. using the dependency
|
||||
`numberFilter` will inject the number filter. The injected argument is a function that takes the
|
||||
value to format as first argument and filter parameters starting with the second argument.
|
||||
You can also use filters in controllers, services, and directives.
|
||||
|
||||
<div class="alert alert-info">
|
||||
For this, inject a dependency with the name `<filterName>Filter` into your controller/service/directive.
|
||||
E.g. a filter called `number` is injected by using the dependency `numberFilter`. The injected argument
|
||||
is a function that takes the value to format as first argument, and filter parameters starting with the second argument.
|
||||
</div>
|
||||
|
||||
The example below uses the filter called {@link ng.filter:filter `filter`}.
|
||||
This filter reduces arrays into sub arrays based on
|
||||
@@ -108,6 +111,7 @@ text upper-case.
|
||||
No filter: {{greeting}}<br>
|
||||
Reverse: {{greeting|reverse}}<br>
|
||||
Reverse + uppercase: {{greeting|reverse:true}}<br>
|
||||
Reverse, filtered in controller: {{filteredGreeting}}<br>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
@@ -127,8 +131,9 @@ text upper-case.
|
||||
return out;
|
||||
};
|
||||
})
|
||||
.controller('MyController', ['$scope', function($scope) {
|
||||
.controller('MyController', ['$scope', 'reverseFilter', function($scope, reverseFilter) {
|
||||
$scope.greeting = 'hello';
|
||||
$scope.filteredGreeting = reverseFilter($scope.greeting);
|
||||
}]);
|
||||
</file>
|
||||
</example>
|
||||
|
||||
@@ -383,7 +383,7 @@ In the following example we create two directives:
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
var usernames = ['Jim', 'John', 'Jill', 'Jackie'];
|
||||
var usernames = ['Jim', 'John', 'Jill', 'Jackie'];
|
||||
|
||||
ctrl.$asyncValidators.username = function(modelValue, viewValue) {
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
@ngdoc overview
|
||||
@name Interpolation
|
||||
@sortOrder 275
|
||||
@description
|
||||
|
||||
# Interpolation and data-binding
|
||||
|
||||
Interpolation markup with embedded {@link guide/expression expressions} is used by Angular to
|
||||
provide data-binding to text nodes and attribute values.
|
||||
|
||||
An example of interpolation is shown below:
|
||||
|
||||
```html
|
||||
<a ng-href="img/{{username}}.jpg">Hello {{username}}!</a>
|
||||
```
|
||||
|
||||
### How text and attribute bindings work
|
||||
|
||||
During the compilation process the {@link ng.$compile compiler} uses the {@link ng.$interpolate $interpolate}
|
||||
service to see if text nodes and element attributes contain interpolation markup with embedded expressions.
|
||||
|
||||
If that is the case, the compiler adds an interpolateDirective to the node and
|
||||
registers {@link ng.$rootScope.Scope#$watch watches} on the computed interpolation function,
|
||||
which will update the corresponding text nodes or attribute values as part of the
|
||||
normal {@link ng.$rootScope.Scope#$digest digest} cycle.
|
||||
|
||||
Note that the interpolateDirective has a priority of 100 and sets up the watch in the preLink function.
|
||||
|
||||
### Binding to boolean attributes
|
||||
|
||||
Attributes such as `disabled` are called `boolean` attributes, because their presence means `true` and
|
||||
their absence means `false`. We cannot use normal attribute bindings with them, because the HTML
|
||||
specification does not require browsers to preserve the values of boolean attributes. This means that
|
||||
if we put an Angular interpolation expression into such an attribute then the binding information
|
||||
would be lost, because the browser ignores the attribute value.
|
||||
|
||||
In the following example, the interpolation information would be ignored and the browser would simply
|
||||
interpret the attribute as present, meaning that the button would always be disabled.
|
||||
|
||||
```html
|
||||
Disabled: <input type="checkbox" ng-model="isDisabled" />
|
||||
<button disabled="{{isDisabled}}">Disabled</button>
|
||||
```
|
||||
|
||||
For this reason, Angular provides special `ng`-prefixed directives for the following boolean attributes:
|
||||
{@link ngDisabled `disabled`}, {@link ngRequired `required`}, {@link ngSelected `selected`},
|
||||
{@link ngChecked `checked`}, {@link ngReadonly `readOnly`} , and {@link ngOpen `open`}.
|
||||
|
||||
These directives take an expression inside the attribute, and set the corresponding boolean attribute
|
||||
to true when the expression evaluates to truthy.
|
||||
|
||||
```html
|
||||
Disabled: <input type="checkbox" ng-model="isDisabled" />
|
||||
<button ng-disabled="isDisabled">Disabled</button>
|
||||
```
|
||||
|
||||
### `ngAttr` for binding to arbitrary attributes
|
||||
|
||||
Web browsers are sometimes picky about what values they consider valid for attributes.
|
||||
|
||||
For example, considering this template:
|
||||
|
||||
```html
|
||||
<svg>
|
||||
<circle cx="{{cx}}"></circle>
|
||||
</svg>
|
||||
```
|
||||
|
||||
We would expect Angular to be able to bind to this, but when we check the console we see
|
||||
something like `Error: Invalid value for attribute cx="{{cx}}"`. Because of the SVG DOM API's
|
||||
restrictions, you cannot simply write `cx="{{cx}}"`.
|
||||
|
||||
With `ng-attr-cx` you can work around this problem.
|
||||
|
||||
If an attribute with a binding is prefixed with the `ngAttr` prefix (denormalized as `ng-attr-`)
|
||||
then during the binding it will be applied to the corresponding unprefixed attribute. This allows
|
||||
you to bind to attributes that would otherwise be eagerly processed by browsers
|
||||
(e.g. an SVG element's `circle[cx]` attributes). When using `ngAttr`, the `allOrNothing` flag of
|
||||
{@link ng.$interpolate $interpolate} is used, so if any expression in the interpolated string
|
||||
results in `undefined`, the attribute is removed and not added to the element.
|
||||
|
||||
For example, we could fix the example above by instead writing:
|
||||
|
||||
```html
|
||||
<svg>
|
||||
<circle ng-attr-cx="{{cx}}"></circle>
|
||||
</svg>
|
||||
```
|
||||
|
||||
If one wants to modify a camelcased attribute (SVG elements have valid camelcased attributes),
|
||||
such as `viewBox` on the `svg` element, one can use underscores to denote that the attribute to bind
|
||||
to is naturally camelcased.
|
||||
|
||||
For example, to bind to `viewBox`, we can write:
|
||||
|
||||
```html
|
||||
<svg ng-attr-view_box="{{viewBox}}">
|
||||
</svg>
|
||||
```
|
||||
|
||||
The following attributes are also known to cause problems when used with normal bindings:
|
||||
|
||||
- **size** in `<select>` elements (see [Github issue 1619](https://github.com/angular/angular.js/issues/1619))
|
||||
- **placeholder** in `<textarea>` in Internet Explorer 10/11 (see [Github issue 5025](https://github.com/angular/angular.js/issues/5025))
|
||||
|
||||
|
||||
### Embedding interpolation markup inside expressions
|
||||
|
||||
Angular directives take either expressions or interpolation markup with embedded expressions. So the
|
||||
following example which embeds interpolation inside an expression is a bad practice:
|
||||
|
||||
```html
|
||||
<div ng-show="form{{$index}}.$invalid"></div>
|
||||
```
|
||||
|
||||
You should instead delegate the computation of complex expressions to the scope, like this:
|
||||
|
||||
```html
|
||||
<div ng-show="getForm($index).$invalid"></div>
|
||||
```
|
||||
|
||||
```js
|
||||
function getForm() {
|
||||
return $scope['form' + $index];
|
||||
}
|
||||
```
|
||||
|
||||
You can also access the `scope` with `this` in your templates:
|
||||
|
||||
```html
|
||||
<div ng-show="this['form' + $index].$invalid"></div>
|
||||
```
|
||||
|
||||
#### Why mixing interpolation and expressions is bad practice:
|
||||
|
||||
- It increases the complexity of the markup
|
||||
- There is no guarantee that it works for every directive, because interpolation itself is a directive.
|
||||
If another directive accesses attribute data before interpolation has run, it will get the raw
|
||||
interpolation markup and not data.
|
||||
- It impacts performance, as interpolation adds another watcher to the scope.
|
||||
- Since this is not recommended usage, we do not test for this, and changes to
|
||||
Angular core may break your code.
|
||||
@@ -3,17 +3,225 @@
|
||||
@sortOrder 550
|
||||
@description
|
||||
|
||||
# Migrating an App to a newer version
|
||||
|
||||
Minor version releases in AngularJS introduce several breaking changes that may require changes to your
|
||||
application's source code; for instance from 1.0 to 1.2 and from 1.2 to 1.3.
|
||||
|
||||
Although we try to avoid breaking changes, there are some cases where it is unavoidable.
|
||||
Although we try to avoid breaking changes, there are some cases where it is unavoidable:
|
||||
|
||||
* AngularJS has undergone thorough security reviews to make applications safer by default,
|
||||
which drives many of these changes.
|
||||
* Several new features, especially animations, would not be possible without a few changes.
|
||||
* Finally, some outstanding bugs were best fixed by changing an existing API.
|
||||
|
||||
# Migrating from 1.3 to 1.4
|
||||
<hr />
|
||||
|
||||
## Contents
|
||||
|
||||
<ul class="nav nav-list">
|
||||
<li>{@link guide/migration#migrating-from-1-4-to-1-5 Migrating from 1.4 to 1.5}</li>
|
||||
<li>{@link guide/migration#migrating-from-1-3-to-1-4 Migrating from 1.3 to 1.4}</li>
|
||||
<li>{@link guide/migration#migrating-from-1-2-to-1-3 Migrating from 1.2 to 1.3}</li>
|
||||
<li>{@link guide/migration#migrating-from-1-0-to-1-2 Migrating from 1.0 to 1.2}</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Migrating from 1.4 to 1.5
|
||||
|
||||
Angular 1.5 takes a big step towards preparing developers for a smoother transition to Angular 2 in
|
||||
the future. Architecturing your applications using components, multi-slot transclusion, one-way
|
||||
bindings in isolate scopes, using lifecycle hooks in directive controllers and relying on native ES6
|
||||
features (such as classes and arrow functions) are now all possible with Angular 1.5.
|
||||
|
||||
|
||||
This release includes numerous bug and security fixes, as well as performance improvements to core
|
||||
services, directives, filters and helper functions. Existing applications can start enjoying the
|
||||
benefits of such changes in `$compile`, `$parse`, `$animate`, `$animateCss`, `$sanitize`, `ngOptions`,
|
||||
`currencyFilter`, `numberFilter`, `copy()` (to name but a few) without any change in code.
|
||||
|
||||
New features have been added to more than a dozen services, directives and filters across 8 modules.
|
||||
Among them, a few stand out:
|
||||
|
||||
* `angular.component()`: Introducing "components", a special sort of directive that are easy to
|
||||
configure and promote best practices (plus can bring Angular 1 applications closer to Angular 2's
|
||||
style of architecture).
|
||||
* Multi-slot transclusion: Enabling the design of more powerful and complex UI elements with a much
|
||||
simpler configuration and reduced boilerplate.
|
||||
* `$onInit` lifecycle hook: Introducing a new lifecycle hook for directive controllers, called after
|
||||
all required controllers have been constructed. This enables access to required controllers from
|
||||
a directive's controller, without having to rely on the linking function.
|
||||
* `ngAnimateSwap`: A new directive in `ngAnimate`, making it super easy to create rotating
|
||||
banner-like components.
|
||||
* Testing helpers: New helper functions in `ngMock`, simplifying testing for animations, component
|
||||
controllers and routing.
|
||||
|
||||
Also, notable is the improved support for ES6 features, such as classes and arrow functions. These
|
||||
features are now more reliably detected and correctly handled within the core.
|
||||
|
||||
|
||||
All this goodness doesn't come without a price, though. Below is a list of breaking changes (grouped
|
||||
by module) that need to be taken into account while migrating from 1.4. Fortunately, the majority of
|
||||
them should have a pretty low impact on most applications.
|
||||
|
||||
|
||||
### Core
|
||||
|
||||
We tried to keep the breaking changes inside the core components to a bare minimum. Still, a few of
|
||||
them were unavoidable.
|
||||
|
||||
#### Services (`$parse`)
|
||||
|
||||
Due to [0ea53503](https://github.com/angular/angular.js/commit/0ea535035a3a1a992948490c3533bffb83235052),
|
||||
a new special property, `$locals`, will be available for accessing the locals from an expression.
|
||||
This is a breaking change, only if a `$locals` property does already exist (and needs to be
|
||||
referenced) either on the `scope` or on the `locals` object. Your expressions should be changed to
|
||||
access such existing properties as `this.$locals` and `$locals.$locals` respectively.
|
||||
|
||||
|
||||
#### Directives (`ngOptions`)
|
||||
|
||||
A fair amount of work has been put into the `ngOptions` directive, fixing bugs and corner-cases and
|
||||
neutralizing browser quirks. A couple of breaking changes were made in the process:
|
||||
|
||||
Due to [b71d7c3f](https://github.com/angular/angular.js/commit/b71d7c3f3c04e65b02d88b33c22dd90ae3cdfc27),
|
||||
falsy values (`''`, `0`, `false` and `null`) are properly recognized as option group identifiers for
|
||||
options passed to `ngOptions`. Previously, all of these values were ignored and the option was not
|
||||
assigned to any group. `undefined` is still interpreted as "no group".
|
||||
If you have options with falsy group indentifiers that should still not be assigned to any group,
|
||||
then you must filter the values before passing them to `ngOptions`, converting falsy values to
|
||||
`undefined`.
|
||||
|
||||
Due to [ded25187](https://github.com/angular/angular.js/commit/ded2518756d4409fdfda0d4af243f2125bea01b5),
|
||||
`ngOptions` now explicitly requires `ngModel` on the same element, thus an error will be thrown if
|
||||
`ngModel` is not found. Previously, `ngOptions` would silently fail, which could lead to
|
||||
hard-to-debug errors.
|
||||
This is not expected to have any significant impact on applications, since `ngOptions` didn't work
|
||||
without `ngModel` before either. The main difference is that now it will fail with a more
|
||||
informative error message.
|
||||
|
||||
|
||||
#### Filters (`orderBy`)
|
||||
|
||||
Due to [2a85a634](https://github.com/angular/angular.js/commit/2a85a634f86c84f15b411ce009a3515fca7ba580),
|
||||
passing a non-array-like value (other than `undefined` or `null`) through the `orderBy` filter will
|
||||
throw an error. Previously, the input was returned unchanged, which could lead to hard-to-spot bugs
|
||||
and was not consistent with other filters (e.g. `filter`).
|
||||
Objects considered array-like include: arrays, array subclasses, strings, NodeLists,
|
||||
jqLite/jQuery collections
|
||||
|
||||
|
||||
### ngMessages (`ngMessage`)
|
||||
|
||||
Due to [4971ef12](https://github.com/angular/angular.js/commit/4971ef12d4c2c268cb8d26f90385dc96eba19db8),
|
||||
the `ngMessage` directive is now compiled with a priority of 1, which means directives on the same
|
||||
element as `ngMessage` with a priority lower than 1 will be applied when `ngMessage` calls its
|
||||
`$transclude` function. Previously, they were applied during the initial compile phase and were
|
||||
passed the comment element created by the transclusion of `ngMessage`.
|
||||
If you have custom directives that relied on the previous behavior, you need to give them a priority
|
||||
of 1 or greater.
|
||||
|
||||
|
||||
### ngResource (`$resource`)
|
||||
|
||||
The `$resource` service underwent a minor internal refactoring to finally solve a long-standing bug
|
||||
preventing requests from being cancelled using promises. Due to the nature of `$resource`'s
|
||||
configuration, it was not possible to follow the `$http` convention. A new `$cancelRequest()` method
|
||||
was introduced instead.
|
||||
|
||||
Due to [98528be3](https://github.com/angular/angular.js/commit/98528be311b48269ba0e15ba4e3e2ad9b89693a9),
|
||||
using a promise as `timeout` in `$resource` is no longer supported and will log a warning. This is
|
||||
hardly expected to affect the behavior of your application, since a promise as `timeout` didn't work
|
||||
before either, but it will now warn you explicitly when trying to pass one.
|
||||
If you need to be able to cancel pending requests, you can now use the new `$cancelRequest()` that
|
||||
will be available on `$resource` instances.
|
||||
|
||||
|
||||
### ngRoute (`ngView`)
|
||||
|
||||
Due to [983b0598](https://github.com/angular/angular.js/commit/983b0598121a8c5a3a51a30120e114d7e3085d4d),
|
||||
a new property will be available on the scope of the route, allowing easy access to the route's
|
||||
resolved values from the view's template. The default name for this property is `$resolve`. This is
|
||||
a breaking change, only if a `$resolve` property is already available on the scope, in which case
|
||||
the existing property will be hidden or overwritten.
|
||||
To fix this, you should choose a custom name for this property, that does not collide with other
|
||||
properties on the scope, by specifying the `resolveAs` property on the route.
|
||||
|
||||
|
||||
### ngSanitize (`$sanitize`, `linky`)
|
||||
|
||||
The HTML sanitizer has been re-implemented using inert documents, increasing security, fixing some
|
||||
corner-cases that were difficult to handle and reducing its size by about 20% (in terms of loc). In
|
||||
order to make it more secure by default, a couple of breaking changes have been introduced:
|
||||
|
||||
Due to [181fc567](https://github.com/angular/angular.js/commit/181fc567d873df065f1e84af7225deb70a8d2eb9),
|
||||
SVG support in `$sanitize` is now an opt-in feature (i.e. disabled by default), as it could make
|
||||
an application vulnerable to click-hijacking attacks. If your application relies on it, you can
|
||||
still turn it on with `$sanitizeProvider.enableSvg(true)`, but you extra precautions need to be
|
||||
taken in order to keep your application secure. Read the documentation for more information about
|
||||
the dangers and ways to mitigate them.
|
||||
|
||||
Due to [7a668cdd](https://github.com/angular/angular.js/commit/7a668cdd7d08a7016883eb3c671cbcd586223ae8),
|
||||
the `$sanitize` service will now remove instances of the `<use>` tag from the content passed to it.
|
||||
This element is used to import external SVG resources, which is a security risk as the `$sanitize`
|
||||
service does not have access to the resource in order to sanitize it.
|
||||
|
||||
Similarly, due to [234053fc](https://github.com/angular/angular.js/commit/234053fc9ad90e0d05be7e8359c6af66be94c094),
|
||||
the `$sanitize` service will now also remove instances of the `usemap` attribute from any elements
|
||||
passed to it. This attribute is used to reference another element by `name` or `id`. Since the
|
||||
`name` and `id` attributes are already blacklisted, a sanitized `usemap` attribute could only
|
||||
reference unsanitized content, which is a security risk.
|
||||
|
||||
Due to [98c2db7f](https://github.com/angular/angular.js/commit/98c2db7f9c2d078a408576e722407d518c7ee10a),
|
||||
passing a non-string value (other than `undefined` or `null`) through the `linky` filter will throw
|
||||
an error. This is not expected to have any significant impact on applications, since the input was
|
||||
always assumed to be of type 'string', so passing non-string values never worked correctly anyway.
|
||||
The main difference is that now it will fail faster and with a more informative error message.
|
||||
|
||||
|
||||
## ngTouch (`ngClick`)
|
||||
|
||||
Due to [0dfc1dfe](https://github.com/angular/angular.js/commit/0dfc1dfebf26af7f951f301c4e3848ac46f05d7f),
|
||||
the `ngClick` override directive from the `ngTouch` module is **deprecated and disabled by default**.
|
||||
This means that on touch-based devices, users might now experience a 300ms delay before a click
|
||||
event is fired.
|
||||
|
||||
If you rely on this directive, you can still enable it using
|
||||
`$touchProvider.ngClickOverrideEnabled()`:
|
||||
|
||||
```js
|
||||
angular.module('myApp').config(function($touchProvider) {
|
||||
$touchProvider.ngClickOverrideEnabled(true);
|
||||
});
|
||||
```
|
||||
|
||||
Going forward, we recommend using [FastClick](https://github.com/ftlabs/fastclick) or perhaps one of
|
||||
the [Angular 3rd party touch-related modules](http://ngmodules.org/tags/touch) that provide similar
|
||||
functionality.
|
||||
|
||||
Also note that modern browsers already remove the 300ms delay under some circumstances:
|
||||
|
||||
- **Chrome and Firefox for Android** remove the 300ms delay when the well-known
|
||||
`<meta name="viewport" content="width=device-width">` is set.
|
||||
- **Internet Explorer** removes the delay, when the `touch-action` css property is set to `none` or
|
||||
`manipulation`.
|
||||
- Since **iOS 8, Safari** removes the delay on so-called "slow taps".
|
||||
|
||||
For more info on the topic, you can take a look at this
|
||||
[article by Telerik](http://developer.telerik.com/featured/300-ms-click-delay-ios-8/).
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** This change does **not** affect the `ngSwipe` directive.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Migrating from 1.3 to 1.4
|
||||
|
||||
Angular 1.4 fixes major animation issues and introduces a new API for `ngCookies`. Further, there
|
||||
are changes to `ngMessages`, `$compile`, `ngRepeat`, `ngOptions `and some fixes to core filters:
|
||||
@@ -37,7 +245,7 @@ relatively straightforward otherwise.
|
||||
|
||||
|
||||
|
||||
## Animation (`ngAnimate`)
|
||||
### Animation (`ngAnimate`)
|
||||
|
||||
Animations in 1.4 have been refactored internally, but the API has stayed much the same. There are, however,
|
||||
some breaking changes that need to be addressed when upgrading to 1.4.
|
||||
@@ -136,9 +344,9 @@ class based animations (animations triggered via ngClass) in order to ensure tha
|
||||
|
||||
|
||||
|
||||
## Forms (`ngMessages`, `ngOptions`, `select`)
|
||||
### Forms (`ngMessages`, `ngOptions`, `select`)
|
||||
|
||||
### ngMessages
|
||||
#### ngMessages
|
||||
The ngMessages module has also been subject to an internal refactor to allow it to be more flexible
|
||||
and compatible with dynamic message data. The `ngMessage` directive now supports a new attribute
|
||||
called `ng-message-exp` which will evaluate an expression and will keep track of that expression
|
||||
@@ -170,7 +378,26 @@ other inline messages situated as children within the `ngMessages` container dir
|
||||
Depending on where the `ngMessagesInclude` directive is placed it will be prioritized inline with the other messages
|
||||
before and after it.
|
||||
|
||||
### ngOptions
|
||||
Also due to [c9a4421f](https://github.com/angular/angular.js/commit/c9a4421fc3c97448527eadef1f42eb2f487ec2e0),
|
||||
it is no longer possible to use interpolation inside the `ngMessages` attribute expression. This technique
|
||||
is generally not recommended, and can easily break when a directive implementation changes. In cases
|
||||
where a simple expression is not possible, you can delegate accessing the object to a function:
|
||||
|
||||
```html
|
||||
<div ng-messages="ctrl.form['field_{{$index}}'].$error">...</div>
|
||||
```
|
||||
would become
|
||||
```html
|
||||
<div ng-messages="ctrl.getMessages($index)">...</div>
|
||||
```
|
||||
where `ctrl.getMessages()`
|
||||
```javascript
|
||||
ctrl.getMessages = function($index) {
|
||||
return ctrl.form['field_' + $index].$error;
|
||||
}
|
||||
```
|
||||
|
||||
#### ngOptions
|
||||
|
||||
The `ngOptions` directive has also been refactored and as a result some long-standing bugs
|
||||
have been fixed. The breaking changes are comparatively minor and should not affect most applications.
|
||||
@@ -189,8 +416,12 @@ But in practice this is not what people want and so this change iterates over pr
|
||||
in the order they are returned by Object.keys(obj), which is almost always the order
|
||||
in which the properties were defined.
|
||||
|
||||
Also due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
|
||||
setting the ngOptions attribute expression after the element is compiled, will no longer trigger the ngOptions behavior.
|
||||
This worked previously because the ngOptions logic was part of the select directive, while
|
||||
it is now implemented in the ngOptions directive itself.
|
||||
|
||||
### select
|
||||
#### select
|
||||
|
||||
Due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
|
||||
the `select` directive will now use strict comparison of the `ngModel` scope value against `option`
|
||||
@@ -221,9 +452,9 @@ ngModelCtrl.$formatters.push(function(value) {
|
||||
});
|
||||
```
|
||||
|
||||
## Templating (`ngRepeat`, `$compile`)
|
||||
### Templating (`ngRepeat`, `$compile`)
|
||||
|
||||
### ngRepeat
|
||||
#### ngRepeat
|
||||
|
||||
Due to [c260e738](https://github.com/angular/angular.js/commit/c260e7386391877625eda086480de73e8a0ba921),
|
||||
previously, the order of items when using ngRepeat to iterate over object properties was guaranteed to be consistent
|
||||
@@ -242,7 +473,7 @@ https://github.com/petebacondarwin/angular-toArrayFilter
|
||||
or some other mechanism, and then sort them manually in the order you need.
|
||||
|
||||
|
||||
### $compile
|
||||
#### $compile
|
||||
|
||||
Due to [6a38dbfd](https://github.com/angular/angular.js/commit/6a38dbfd3c34c8f9efff503d17eb3cbeb666d422),
|
||||
previously, '&' expressions would always set up a function in the isolate scope. Now, if the binding
|
||||
@@ -253,7 +484,7 @@ returning an object from a controller constructor function will now override the
|
||||
controllerAs method will no longer get the this reference, but the returned object.
|
||||
|
||||
|
||||
## Cookies (`ngCookies`)
|
||||
### Cookies (`ngCookies`)
|
||||
|
||||
Due to [38fbe3ee](https://github.com/angular/angular.js/commit/38fbe3ee8370fc449b82d80df07b5c2ed2cd5fbe),
|
||||
`$cookies` will no longer expose properties that represent the current browser cookie
|
||||
@@ -292,7 +523,7 @@ delegates calls.
|
||||
|
||||
|
||||
|
||||
## Server Requests (`$http`)
|
||||
### Server Requests (`$http`)
|
||||
|
||||
Due to [5da1256](https://github.com/angular/angular.js/commit/5da1256fc2812d5b28fb0af0de81256054856369),
|
||||
`transformRequest` functions can no longer modify request headers.
|
||||
@@ -325,9 +556,9 @@ $http.get(url, {
|
||||
|
||||
|
||||
|
||||
## Filters (`filter`, `limitTo`)
|
||||
### Filters (`filter`, `limitTo`)
|
||||
|
||||
### `filter` filter
|
||||
#### `filter` filter
|
||||
Due to [cea8e751](https://github.com/angular/angular.js/commit/cea8e75144e6910b806b63a6ec2a6d118316fddd),
|
||||
the `filter` filter will throw an error when used with a non-array. Beforehand it would silently
|
||||
return an empty array.
|
||||
@@ -335,7 +566,7 @@ return an empty array.
|
||||
If necessary, this can be worked around by converting an object to an array,
|
||||
using a filter such as https://github.com/petebacondarwin/angular-toArrayFilter.
|
||||
|
||||
### `limitTo` filter
|
||||
#### `limitTo` filter
|
||||
Due to [a3c3bf33](https://github.com/angular/angular.js/commit/a3c3bf3332e5685dc319c46faef882cb6ac246e1),
|
||||
the limitTo filter has changed behavior when the provided limit value is invalid.
|
||||
Now, instead of returning empty object/array, it returns unchanged input.
|
||||
@@ -344,9 +575,9 @@ Now, instead of returning empty object/array, it returns unchanged input.
|
||||
|
||||
|
||||
|
||||
# Migrating from 1.2 to 1.3
|
||||
## Migrating from 1.2 to 1.3
|
||||
|
||||
## Controllers
|
||||
### Controllers
|
||||
|
||||
Due to [3f2232b5](https://github.com/angular/angular.js/commit/3f2232b5a181512fac23775b1df4a6ebda67d018),
|
||||
`$controller` will no longer look for controllers on `window`.
|
||||
@@ -384,7 +615,7 @@ angular.module('myModule').config(['$controllerProvider', function($controllerPr
|
||||
}]);
|
||||
```
|
||||
|
||||
## Angular Expression Parsing (`$parse` + `$interpolate`)
|
||||
### Angular Expression Parsing (`$parse` + `$interpolate`)
|
||||
|
||||
- due to [77ada4c8](https://github.com/angular/angular.js/commit/77ada4c82d6b8fc6d977c26f3cdb48c2f5fbe5a5),
|
||||
|
||||
@@ -442,7 +673,7 @@ expression parser; there are six of them: false, null, undefined, NaN, 0 and "".
|
||||
|
||||
|
||||
|
||||
## Miscellaneous Angular helpers
|
||||
### Miscellaneous Angular helpers
|
||||
|
||||
- **Angular.copy:** due to [b59b04f9](https://github.com/angular/angular.js/commit/b59b04f98a0b59eead53f6a53391ce1bbcbe9b57),
|
||||
|
||||
@@ -473,15 +704,14 @@ This change also makes our forEach behave more like Array#forEach.
|
||||
|
||||
|
||||
- **angular.toJson:** due to [c054288c](https://github.com/angular/angular.js/commit/c054288c9722875e3595e6e6162193e0fb67a251),
|
||||
|
||||
If you expected `toJson` to strip these types of properties before, you will have to
|
||||
manually do this yourself now.
|
||||
`toJson()` will no longer strip properties starting with a single `$`. If you relied on
|
||||
`toJson()`'s stripping these types of properties before, you will have to do it manually now.
|
||||
It will still strip properties starting with `$$` though.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## jqLite / JQuery
|
||||
### jqLite / JQuery
|
||||
|
||||
- **jqLite:** due to [a196c8bc](https://github.com/angular/angular.js/commit/a196c8bca82a28c08896d31f1863cf4ecd11401c),
|
||||
previously it was possible to set jqLite data on Text/Comment
|
||||
@@ -497,7 +727,7 @@ jQuery. We don't expect that app code actually depends on this accidental featur
|
||||
|
||||
|
||||
|
||||
## Angular HTML Compiler (`$compile`)
|
||||
### Angular HTML Compiler (`$compile`)
|
||||
|
||||
|
||||
- due to [2ee29c5d](https://github.com/angular/angular.js/commit/2ee29c5da81ffacdc1cabb438f5d125d5e116cb9),
|
||||
@@ -561,10 +791,40 @@ After:
|
||||
};
|
||||
});
|
||||
|
||||
- due to [531a8de7](https://github.com/angular/angular.js/commit/531a8de72c439d8ddd064874bf364c00cedabb11),
|
||||
`$observe` no longer registers on undefined attributes. For example, if you were using `$observe` on
|
||||
an absent optional attribute to set a default value, the following would not work anymore:
|
||||
|
||||
```html
|
||||
<my-dir></my-dir>
|
||||
```
|
||||
|
||||
```js
|
||||
// Link function for directive myDir
|
||||
link: function(scope, element, attr) {
|
||||
attr.$observe('myAttr', function(newVal) {
|
||||
scope.myValue = newVal ? newVal : 'myDefaultValue';
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Instead, check if the attribute is set before registering the observer:
|
||||
|
||||
```js
|
||||
link: function(scope, element, attr) {
|
||||
if (attr.myAttr) {
|
||||
// register the observer
|
||||
} else {
|
||||
// set the default
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Forms, Inputs and ngModel
|
||||
|
||||
|
||||
### Forms, Inputs and ngModel
|
||||
|
||||
- due to [1be9bb9d](https://github.com/angular/angular.js/commit/1be9bb9d3527e0758350c4f7417a4228d8571440),
|
||||
|
||||
@@ -633,11 +893,18 @@ $scope.resetWithCancel = function (e) {
|
||||
[#5864](https://github.com/angular/angular.js/issues/5864))
|
||||
|
||||
|
||||
- {@link input[checkbox] `input[checkbox]`} now supports constant expressions in `ngTrueValue` and
|
||||
`ngFalseValue`, making it now possible to e.g. use boolean and integer values. Previously, these attributes would
|
||||
always be treated as strings, whereas they are now parsed as expressions, and will throw if an expression
|
||||
is non-constant. To convert non-constant strings into constant expressions, simply wrap them in an
|
||||
extra pair of quotes, like so:
|
||||
|
||||
`<input type="checkbox" ng-model="..." ng-true-value="'truthyValue'">`
|
||||
|
||||
See [c90cefe1614](https://github.com/angular/angular.js/commit/c90cefe16142d973a123e945fc9058e8a874c357)
|
||||
|
||||
|
||||
|
||||
|
||||
## Scopes and Digests (`$scope`)
|
||||
### Scopes and Digests (`$scope`)
|
||||
|
||||
- due to [8c6a8171](https://github.com/angular/angular.js/commit/8c6a8171f9bdaa5cdabc0cc3f7d3ce10af7b434d),
|
||||
Scope#$id is now of type number rather than string. Since the
|
||||
@@ -657,7 +924,7 @@ anyone.
|
||||
|
||||
|
||||
|
||||
## Server Requests (`$http`, `$resource`)
|
||||
### Server Requests (`$http`, `$resource`)
|
||||
- **$http:** due to [ad4336f9](https://github.com/angular/angular.js/commit/ad4336f9359a073e272930f8f9bcd36587a8648f),
|
||||
|
||||
|
||||
@@ -724,7 +991,7 @@ More details on the new interceptors API (which has been around as of v1.1.4) ca
|
||||
|
||||
|
||||
|
||||
## Modules and Injector (`$inject`)
|
||||
### Modules and Injector (`$inject`)
|
||||
|
||||
- due to [c0b4e2db](https://github.com/angular/angular.js/commit/c0b4e2db9cbc8bc3164cedc4646145d3ab72536e),
|
||||
|
||||
@@ -766,7 +1033,7 @@ app. This is no longer possible within a single module.
|
||||
|
||||
|
||||
|
||||
## Animation (`ngAnimate`)
|
||||
### Animation (`ngAnimate`)
|
||||
|
||||
|
||||
- due to [1cb8584e](https://github.com/angular/angular.js/commit/1cb8584e8490ecdb1b410a8846c4478c6c2c0e53),
|
||||
@@ -819,7 +1086,7 @@ After:
|
||||
Please view the documentation for ngAnimate for more info.
|
||||
|
||||
|
||||
## Testing
|
||||
### Testing
|
||||
|
||||
- due to [85880a64](https://github.com/angular/angular.js/commit/85880a64900fa22a61feb926bf52de0965332ca5), some deprecated features of
|
||||
Protractor tests no longer work.
|
||||
@@ -858,7 +1125,7 @@ or simply use:
|
||||
var el = element(by.repeater('foo in foos').row(2))
|
||||
|
||||
|
||||
## Internet Explorer 8
|
||||
### Internet Explorer 8
|
||||
|
||||
- due to [eaa1d00b](https://github.com/angular/angular.js/commit/eaa1d00b24008f590b95ad099241b4003688cdda),
|
||||
As communicated before, IE8 is no longer supported.
|
||||
@@ -868,7 +1135,7 @@ or simply use:
|
||||
|
||||
|
||||
|
||||
# Migrating from 1.0 to 1.2
|
||||
## Migrating from 1.0 to 1.2
|
||||
|
||||
|
||||
<div class="alert alert-warning">
|
||||
@@ -911,7 +1178,7 @@ below should still apply, but you may want to consult the
|
||||
</ul>
|
||||
|
||||
|
||||
## ngRoute has been moved into its own module
|
||||
### ngRoute has been moved into its own module
|
||||
|
||||
Just like `ngResource`, `ngRoute` is now its own module.
|
||||
|
||||
@@ -942,7 +1209,7 @@ var myApp = angular.module('myApp', ['ngRoute', 'someOtherModule']);
|
||||
See [5599b55b](https://github.com/angular/angular.js/commit/5599b55b04788c2e327d7551a4a699d75516dd21).
|
||||
|
||||
|
||||
## Templates no longer automatically unwrap promises
|
||||
### Templates no longer automatically unwrap promises
|
||||
|
||||
`$parse` and templates in general will no longer automatically unwrap promises.
|
||||
|
||||
@@ -976,7 +1243,7 @@ See [5dc35b52](https://github.com/angular/angular.js/commit/5dc35b527b3c99f6544b
|
||||
[b6a37d11](https://github.com/angular/angular.js/commit/b6a37d112b3e1478f4d14a5f82faabf700443748).
|
||||
|
||||
|
||||
## Syntax for named wildcard parameters changed in `$route`
|
||||
### Syntax for named wildcard parameters changed in `$route`
|
||||
|
||||
To migrate the code, follow the example below. Here, `*highlight` becomes `:highlight*`
|
||||
|
||||
@@ -997,7 +1264,7 @@ $routeProvider.when('/Book1/:book/Chapter/:chapter/:highlight*/edit',
|
||||
See [04cebcc1](https://github.com/angular/angular.js/commit/04cebcc133c8b433a3ac5f72ed19f3631778142b).
|
||||
|
||||
|
||||
## You can only bind one expression to `*[src]`, `*[ng-src]` or `action`
|
||||
### You can only bind one expression to `*[src]`, `*[ng-src]` or `action`
|
||||
|
||||
With the exception of `<a>` and `<img>` elements, you cannot bind more than one expression to the
|
||||
`src` or `action` attribute of elements.
|
||||
@@ -1072,7 +1339,7 @@ scope.getIframeSrc = function() {
|
||||
See [38deedd6](https://github.com/angular/angular.js/commit/38deedd6e3d806eb8262bb43f26d47245f6c2739).
|
||||
|
||||
|
||||
## Interpolations inside DOM event handlers are now disallowed
|
||||
### Interpolations inside DOM event handlers are now disallowed
|
||||
|
||||
DOM event handlers execute arbitrary Javascript code. Using an interpolation for such handlers
|
||||
means that the interpolated value is a JS string that is evaluated. Storing or generating such
|
||||
@@ -1099,7 +1366,7 @@ HTML: <div ng-click="foo()">
|
||||
See [39841f2e](https://github.com/angular/angular.js/commit/39841f2ec9b17b3b2920fd1eb548d444251f4f56).
|
||||
|
||||
|
||||
## Directives cannot end with -start or -end
|
||||
### Directives cannot end with -start or -end
|
||||
|
||||
This change was necessary to enable multi-element directives. The best fix is to rename existing
|
||||
directives so that they don't end with these suffixes.
|
||||
@@ -1107,7 +1374,7 @@ directives so that they don't end with these suffixes.
|
||||
See [e46100f7](https://github.com/angular/angular.js/commit/e46100f7097d9a8f174bdb9e15d4c6098395c3f2).
|
||||
|
||||
|
||||
## In $q, promise.always has been renamed promise.finally
|
||||
### In $q, promise.always has been renamed promise.finally
|
||||
|
||||
The reason for this change is to align `$q` with the [Q promise
|
||||
library](https://github.com/kriskowal/q), despite the fact that this makes it a bit more difficult
|
||||
@@ -1139,7 +1406,7 @@ $http.get('/foo')['finally'](doSomething);
|
||||
See [f078762d](https://github.com/angular/angular.js/commit/f078762d48d0d5d9796dcdf2cb0241198677582c).
|
||||
|
||||
|
||||
## ngMobile is now ngTouch
|
||||
### ngMobile is now ngTouch
|
||||
|
||||
Many touch-enabled devices are not mobile devices, so we decided to rename this module to better
|
||||
reflect its concerns.
|
||||
@@ -1150,7 +1417,7 @@ To migrate, replace all references to `ngMobile` with `ngTouch` and `angular-mob
|
||||
See [94ec84e7](https://github.com/angular/angular.js/commit/94ec84e7b9c89358dc00e4039009af9e287bbd05).
|
||||
|
||||
|
||||
## resource.$then has been removed
|
||||
### resource.$then has been removed
|
||||
|
||||
Resource instances do not have a `$then` function anymore. Use the `$promise.then` instead.
|
||||
|
||||
@@ -1169,7 +1436,7 @@ Resource.query().$promise.then(callback);
|
||||
See [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d).
|
||||
|
||||
|
||||
## Resource methods return the promise
|
||||
### Resource methods return the promise
|
||||
|
||||
Methods of a resource instance return the promise rather than the instance itself.
|
||||
|
||||
@@ -1189,7 +1456,7 @@ resource.chaining = true;
|
||||
See [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d).
|
||||
|
||||
|
||||
## Resource promises are resolved with the resource instance
|
||||
### Resource promises are resolved with the resource instance
|
||||
|
||||
On success, the resource promise is resolved with the resource instance rather than HTTP response object.
|
||||
|
||||
@@ -1220,7 +1487,7 @@ var Resource = $resource('/url', {}, {
|
||||
See [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d).
|
||||
|
||||
|
||||
## $location.search supports multiple keys
|
||||
### $location.search supports multiple keys
|
||||
|
||||
{@link ng.$location#search `$location.search`} now supports multiple keys with the
|
||||
same value provided that the values are stored in an array.
|
||||
@@ -1237,7 +1504,7 @@ passing it to `$location`.
|
||||
See [80739409](https://github.com/angular/angular.js/commit/807394095b991357225a03d5fed81fea5c9a1abe).
|
||||
|
||||
|
||||
## ngBindHtmlUnsafe has been removed and replaced by ngBindHtml
|
||||
### ngBindHtmlUnsafe has been removed and replaced by ngBindHtml
|
||||
|
||||
`ngBindHtml` provides `ngBindHtmlUnsafe` like
|
||||
behavior (evaluate an expression and innerHTML the result into the DOM) when bound to the result
|
||||
@@ -1253,7 +1520,7 @@ trusted.
|
||||
See [dae69473](https://github.com/angular/angular.js/commit/dae694739b9581bea5dbc53522ec00d87b26ae55).
|
||||
|
||||
|
||||
## Form names that are expressions are evaluated
|
||||
### Form names that are expressions are evaluated
|
||||
|
||||
If you have form names that will evaluate as an expression:
|
||||
|
||||
@@ -1285,7 +1552,7 @@ Supporting the previous behavior offers no benefit.
|
||||
See [8ea802a1](https://github.com/angular/angular.js/commit/8ea802a1d23ad8ecacab892a3a451a308d9c39d7).
|
||||
|
||||
|
||||
## hasOwnProperty disallowed as an input name
|
||||
### hasOwnProperty disallowed as an input name
|
||||
|
||||
Inputs with name equal to `hasOwnProperty` are not allowed inside form or ngForm directives.
|
||||
|
||||
@@ -1296,7 +1563,7 @@ and bad practice. To migrate, change your input name.
|
||||
See [7a586e5c](https://github.com/angular/angular.js/commit/7a586e5c19f3d1ecc3fefef084ce992072ee7f60).
|
||||
|
||||
|
||||
## Directives: Order of postLink functions reversed
|
||||
### Directives: Order of postLink functions reversed
|
||||
|
||||
The order of postLink fn is now mirror opposite of the order in which corresponding preLinking and compile functions execute.
|
||||
|
||||
@@ -1352,7 +1619,7 @@ attribute interpolation directive was adjusted.
|
||||
See [31f190d4](https://github.com/angular/angular.js/commit/31f190d4d53921d32253ba80d9ebe57d6c1de82b).
|
||||
|
||||
|
||||
## Directive priority
|
||||
### Directive priority
|
||||
|
||||
the priority of ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView has changed. This could affect directives that explicitly specify their priority.
|
||||
|
||||
@@ -1370,7 +1637,7 @@ ngView | 1000 | 400
|
||||
See [b7af76b4](https://github.com/angular/angular.js/commit/b7af76b4c5aa77648cc1bfd49935b48583419023).
|
||||
|
||||
|
||||
## ngScenario
|
||||
### ngScenario
|
||||
|
||||
browserTrigger now uses an eventData object instead of direct parameters for mouse events.
|
||||
To migrate, place the `keys`,`x` and `y` parameters inside of an object and place that as the
|
||||
@@ -1379,7 +1646,7 @@ third parameter for the browserTrigger function.
|
||||
See [28f56a38](https://github.com/angular/angular.js/commit/28f56a383e9d1ff378e3568a3039e941c7ffb1d8).
|
||||
|
||||
|
||||
## ngInclude and ngView replace its entire element on update
|
||||
### ngInclude and ngView replace its entire element on update
|
||||
|
||||
Previously `ngInclude` and `ngView` only updated its element's content. Now these directives will
|
||||
recreate the element every time a new content is included.
|
||||
@@ -1391,7 +1658,7 @@ See [7d69d52a](https://github.com/angular/angular.js/commit/7d69d52acff8578e0f7d
|
||||
[aa2133ad](https://github.com/angular/angular.js/commit/aa2133ad818d2e5c27cbd3933061797096356c8a).
|
||||
|
||||
|
||||
## URLs are now sanitized against a whitelist
|
||||
### URLs are now sanitized against a whitelist
|
||||
|
||||
A whitelist configured via `$compileProvider` can be used to configure what URLs are considered safe.
|
||||
By default all common protocol prefixes are whitelisted including `data:` URIs with mime types `image/*`.
|
||||
@@ -1401,7 +1668,7 @@ See [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d612868
|
||||
[3e39ac7e](https://github.com/angular/angular.js/commit/3e39ac7e1b10d4812a44dad2f959a93361cd823b).
|
||||
|
||||
|
||||
## Isolate scope only exposed to directives with `scope` property
|
||||
### Isolate scope only exposed to directives with `scope` property
|
||||
|
||||
If you declare a scope option on a directive, that directive will have an
|
||||
[isolate scope](https://github.com/angular/angular.js/wiki/Understanding-Scopes). In Angular 1.0, if a
|
||||
@@ -1474,7 +1741,7 @@ See [909cabd3](https://github.com/angular/angular.js/commit/909cabd36d779598763c
|
||||
[#2500](https://github.com/angular/angular.js/issues/2500).
|
||||
|
||||
|
||||
## Change to interpolation priority
|
||||
### Change to interpolation priority
|
||||
|
||||
Previously, the interpolation priority was `-100` in 1.2.0-rc.2, and `100` before 1.2.0-rc.2.
|
||||
Before this change the binding was setup in the post-linking phase.
|
||||
@@ -1487,7 +1754,7 @@ See [79223eae](https://github.com/angular/angular.js/commit/79223eae502283889334
|
||||
[#4528](https://github.com/angular/angular.js/issues/4528), and
|
||||
[#4649](https://github.com/angular/angular.js/issues/4649)
|
||||
|
||||
## Underscore-prefixed/suffixed properties are non-bindable
|
||||
### Underscore-prefixed/suffixed properties are non-bindable
|
||||
|
||||
<div class="alert alert-info">
|
||||
<p>**Reverted**: This breaking change has been reverted in 1.2.1, and so can be ignored if you're using **version 1.2.1 or higher**</p>
|
||||
@@ -1526,7 +1793,7 @@ are actually needed by the expressions.
|
||||
See [3d6a89e8](https://github.com/angular/angular.js/commit/3d6a89e8888b14ae5cb5640464e12b7811853c7e).
|
||||
|
||||
|
||||
## You cannot bind to select[multiple]
|
||||
### You cannot bind to select[multiple]
|
||||
|
||||
Switching between `select[single]` and `select[multiple]` has always been odd due to browser quirks.
|
||||
This feature never worked with two-way data-binding so it's not expected that anyone is using it.
|
||||
@@ -1536,7 +1803,7 @@ If you are interested in properly adding this feature, please submit a pull requ
|
||||
See [d87fa004](https://github.com/angular/angular.js/commit/d87fa0042375b025b98c40bff05e5f42c00af114).
|
||||
|
||||
|
||||
## Uncommon region-specific local files were removed from i18n
|
||||
### Uncommon region-specific local files were removed from i18n
|
||||
|
||||
AngularJS uses the Google Closure library's locale files. The following locales were removed from
|
||||
Closure, so Angular is not able to continue to support them:
|
||||
@@ -1552,7 +1819,7 @@ load and use your copy of the locale file provided that you maintain it yourself
|
||||
|
||||
See [6382e21f](https://github.com/angular/angular.js/commit/6382e21fb28541a2484ac1a241d41cf9fbbe9d2c).
|
||||
|
||||
## Services can now return functions
|
||||
### Services can now return functions
|
||||
|
||||
Previously, the service constructor only returned objects regardless of whether a function was returned.
|
||||
|
||||
|
||||
@@ -75,9 +75,8 @@ that you break your application to multiple modules like this:
|
||||
* And an application level module which depends on the above modules and contains any
|
||||
initialization code.
|
||||
|
||||
We've also
|
||||
[written a document](http://angularjs.blogspot.com/2014/02/an-angularjs-style-guide-and-best.html)
|
||||
on how we organize large apps at Google.
|
||||
You can find a community
|
||||
[style guide](https://github.com/johnpapa/angular-styleguide) to help yourself when application grows.
|
||||
|
||||
The above is a suggestion. Tailor it to your needs.
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ and {@link angular.reloadWithDebugInfo `angular.reloadWithDebugInfo`}.
|
||||
|
||||
## Strict DI Mode
|
||||
|
||||
Using strict di mode in your production application will throw errors when a injectable
|
||||
Using strict di mode in your production application will throw errors when an injectable
|
||||
function is not
|
||||
{@link di#dependency-annotation annotated explicitly}. Strict di mode is intended to help
|
||||
you make sure that your code will work when minified. However, it also will force you to
|
||||
|
||||
@@ -391,7 +391,7 @@ implementing custom event callbacks, or when working with third-party library ca
|
||||
5. The {@link ng.$rootScope.Scope#$watch $watch} list is a set of expressions
|
||||
which may have changed since last iteration. If a change is detected then the `$watch`
|
||||
function is called which typically updates the DOM with the new value.
|
||||
6. Once the Angular {@link ng.$rootScope.Scope#$digest $digest} loop finishes
|
||||
6. Once the Angular {@link ng.$rootScope.Scope#$digest $digest} loop finishes,
|
||||
the execution leaves the Angular and JavaScript context. This is followed by the browser
|
||||
re-rendering the DOM to reflect any changes.
|
||||
|
||||
@@ -419,4 +419,4 @@ user enters text into the text field.
|
||||
which in turn updates the DOM.
|
||||
6. Angular exits the execution context, which in turn exits the `keydown` event and with it
|
||||
the JavaScript execution context.
|
||||
7. The browser re-renders the view with update text.
|
||||
7. The browser re-renders the view with the updated text.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
comes with almost no help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
Angular which makes testing your Angular applications easy. So there is no excuse for not testing.
|
||||
Angular which make testing your Angular applications easy. With Angular, there is no excuse for not testing.
|
||||
|
||||
## Separation of Concerns
|
||||
|
||||
@@ -20,13 +20,13 @@ related pieces such as the DOM elements, or making any XHR calls to fetch the da
|
||||
|
||||
While this may seem obvious it can be very difficult to call an individual function on a
|
||||
typical project. The reason is that the developers often mix concerns resulting in a
|
||||
piece of code which does everything. It makes an XHR request, it sorts the response data and then it
|
||||
piece of code which does everything. It makes an XHR request, it sorts the response data, and then it
|
||||
manipulates the DOM.
|
||||
|
||||
With Angular we try to make it easy for you to do the right thing, and so we
|
||||
provide dependency injection for your XHR requests, which can be mocked, and we provide abstractions which
|
||||
allow you to test your model without having to resort to manipulating the DOM. The test can then
|
||||
assert that the data has been sorted without having to create or look at the state of the DOM or
|
||||
With Angular, we try to make it easy for you to do the right thing. For your XHR requests, we
|
||||
provide dependency injection, so your requests can be simulated. For the DOM, we abstract it, so you can
|
||||
test your model without having to manipulate the DOM directly. Your tests can then
|
||||
assert that the data has been sorted without having to create or look at the state of the DOM or to
|
||||
wait for any XHR requests to return data. The individual sort function can be tested in isolation.
|
||||
|
||||
## With great power comes great responsibility
|
||||
@@ -359,7 +359,7 @@ element, to which it can then insert the transcluded content into its template.
|
||||
|
||||
Before compilation:
|
||||
```html
|
||||
<div translude-directive>
|
||||
<div transclude-directive>
|
||||
Some transcluded content
|
||||
</div>
|
||||
```
|
||||
|
||||
@@ -46,7 +46,7 @@ Download the version you want and have fun.
|
||||
Each directory under <http://code.angularjs.org/> includes the following set of files:
|
||||
|
||||
* __`angular.js`__ — This file is non-obfuscated, non-minified, and human-readable by
|
||||
opening it it any editor or browser. In order to get better error messages during development, you
|
||||
opening it in any editor or browser. In order to get better error messages during development, you
|
||||
should always use this non-minified angular script.
|
||||
|
||||
* __`angular.min.js`__ — This is a minified and obfuscated version of
|
||||
|
||||
@@ -61,7 +61,7 @@ a few git commands.
|
||||
|
||||
### Install Git
|
||||
|
||||
You can download and install Git from http://git-scm.com/download. Once installed you should have
|
||||
You can download and install Git from http://git-scm.com/download. Once installed, you should have
|
||||
access to the `git` command line tool. The main commands that you will need to use are:
|
||||
|
||||
- `git clone ...` : clone a remote repository onto your local machine
|
||||
@@ -123,7 +123,7 @@ npm --version
|
||||
</a>.
|
||||
</div>
|
||||
|
||||
Once you have Node.js installed on your machine you can download the tool dependencies by running:
|
||||
Once you have Node.js installed on your machine, you can download the tool dependencies by running:
|
||||
|
||||
```
|
||||
npm install
|
||||
@@ -198,7 +198,7 @@ http://localhost:8000/app/index.html
|
||||
```
|
||||
|
||||
<div class="alert alert-info">
|
||||
To serve the web app on a different ip address or port, edit the "start" script within package.json.
|
||||
To serve the web app on a different IP address or port, edit the "start" script within package.json.
|
||||
You can use `-a` to set the address and `-p` to set the port.
|
||||
</div>
|
||||
|
||||
@@ -246,6 +246,15 @@ npm run update-webdriver
|
||||
|
||||
*(You should only need to do this once.)*
|
||||
|
||||
You will need to have Java present on your dev machine to allow the Selenium standalone to be started.
|
||||
Check if you already have java installed by opening a terminal/command line window and typing
|
||||
'''
|
||||
java -version
|
||||
'''
|
||||
If java is already installed and exists in the PATH then you will be shown the version installed,
|
||||
if, however you receive a message that "java is not recognized as an internal command or external
|
||||
command" you will need to install [java].
|
||||
|
||||
Since Protractor works by interacting with a running application, we need to start our web server:
|
||||
|
||||
```
|
||||
@@ -280,3 +289,4 @@ Now that you have set up your local machine, let's get started with the tutorial
|
||||
[bower]: http://bower.io/
|
||||
[http-server]: https://github.com/nodeapps/http-server
|
||||
[karma]: https://github.com/karma-runner/karma
|
||||
[java]: https://www.java.com/en/download/help/download_options.xml
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
<ul doc-tutorial-nav="0"></ul>
|
||||
|
||||
|
||||
You are now ready to build the AngularJS phonecat app. In this step, you will become familiar
|
||||
with the most important source code files, learn how to start the development servers bundled with
|
||||
In this step of the tutorial, you will become familiar with the most important source code files of
|
||||
the AngularJS phonecat app. You will also learn how to start the development servers bundled with
|
||||
angular-seed, and run the application in the browser.
|
||||
|
||||
Before you continue, make sure you have set up your development environment and installed all necessary
|
||||
dependencies, as described in {@link index#get-started Get Started}.
|
||||
|
||||
In `angular-phonecat` directory, run this command:
|
||||
In the `angular-phonecat` directory, run this command:
|
||||
|
||||
```
|
||||
git checkout -f step-0
|
||||
|
||||
@@ -61,7 +61,7 @@ by the value of the expressions.
|
||||
We have added a new directive, called `ng-controller`, which attaches a `PhoneListCtrl`
|
||||
__controller__ to the <body> tag. At this point:
|
||||
|
||||
* The expressions in curly braces (`{{phone.name}}` and `{{phone.snippet}}` denote
|
||||
* The expressions in curly braces (`{{phone.name}}` and `{{phone.snippet}}`) denote
|
||||
bindings, which are referring to our application model, which is set up in our `PhoneListCtrl`
|
||||
controller.
|
||||
|
||||
@@ -132,6 +132,8 @@ The "Angular way" of separating controller from the view, makes it easy to test
|
||||
developed. If our controller is available on the global namespace then we could simply instantiate it
|
||||
with a mock `scope` object:
|
||||
|
||||
__`test/e2e/scenarios.js`:__
|
||||
|
||||
```js
|
||||
describe('PhoneListCtrl', function(){
|
||||
|
||||
@@ -195,8 +197,19 @@ to ensure that Karma and its necessary plugins are installed. You can do this by
|
||||
|
||||
To run the tests, and then watch the files for changes: `npm test`.
|
||||
|
||||
* Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
||||
the background. Karma will use this browser for test execution.
|
||||
* Karma will start new instances of Chrome and Firefox browsers automatically. Just ignore them and
|
||||
let them run in the background. Karma will use these browsers for test execution.
|
||||
* If you only have one of the browsers installed on your machine (either Chrome or Firefox), make
|
||||
sure to update the karma configuration file before running the test. Locate the configuration file
|
||||
in `test/karma.conf.js`, then update the `browsers` property.
|
||||
|
||||
E.g. if you only have Chrome installed:
|
||||
<pre>
|
||||
...
|
||||
browsers: ['Chrome'],
|
||||
...
|
||||
</pre>
|
||||
|
||||
* You should see the following or similar output in the terminal:
|
||||
|
||||
<pre>
|
||||
@@ -250,7 +263,7 @@ browser is limited, which results in your karma tests running extremely slow.
|
||||
<tr><th>row number</th></tr>
|
||||
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
Extra points: try and make an 8x8 table using an additional `ng-repeat`.
|
||||
|
||||
* Make the unit test fail by changing `expect(scope.phones.length).toBe(3)` to instead use `toBe(4)`.
|
||||
|
||||
@@ -65,7 +65,7 @@ phonecatApp.controller('PhoneListCtrl', function ($scope, $http) {
|
||||
`$http` makes an HTTP GET request to our web server, asking for `phones/phones.json` (the url is
|
||||
relative to our `index.html` file). The server responds by providing the data in the json file.
|
||||
(The response might just as well have been dynamically generated by a backend server. To the
|
||||
browser and our app they both look the same. For the sake of simplicity we used a json file in this
|
||||
browser and our app, they both look the same. For the sake of simplicity, we used a json file in this
|
||||
tutorial.)
|
||||
|
||||
The `$http` service returns a {@link ng.$q promise object} with a `success`
|
||||
@@ -114,7 +114,7 @@ as strings, which will not get minified. There are two ways to provide these inj
|
||||
|
||||
* Create a `$inject` property on the controller function which holds an array of strings.
|
||||
Each string in the array is the name of the service to inject for the corresponding parameter.
|
||||
In our example we would write:
|
||||
In our example, we would write:
|
||||
|
||||
```js
|
||||
function PhoneListCtrl($scope, $http) {...}
|
||||
|
||||
@@ -43,7 +43,7 @@ __`app/index.html`:__
|
||||
...
|
||||
<ul class="phones">
|
||||
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp" class="thumbnail">
|
||||
<a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}"></a>
|
||||
<a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}" alt="{{phone.name}}"></a>
|
||||
<a href="#/phones/{{phone.id}}">{{phone.name}}</a>
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
@@ -59,8 +59,8 @@ the element attribute.
|
||||
We also added phone images next to each record using an image tag with the {@link
|
||||
ng.directive:ngSrc ngSrc} directive. That directive prevents the
|
||||
browser from treating the Angular `{{ expression }}` markup literally, and initiating a request to
|
||||
invalid URL `http://localhost:8000/app/{{phone.imageUrl}}`, which it would have done if we had only
|
||||
specified an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`).
|
||||
an invalid URL `http://localhost:8000/app/{{phone.imageUrl}}`, which it would have done if we had
|
||||
only specified an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`).
|
||||
Using the `ngSrc` directive prevents the browser from making an http request to an invalid location.
|
||||
|
||||
|
||||
|
||||
@@ -37,12 +37,12 @@ We are using [Bower][bower] to install client-side dependencies. This step upda
|
||||
"angular-mocks": "1.4.x",
|
||||
"jquery": "~2.1.1",
|
||||
"bootstrap": "~3.1.1",
|
||||
"angular-route": "~1.4.0"
|
||||
"angular-route": "1.4.x"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The new dependency `"angular-route": "~1.4.0"` tells bower to install a version of the
|
||||
The new dependency `"angular-route": "1.4.x"` tells bower to install a version of the
|
||||
angular-route component that is compatible with version 1.4.x. We must tell bower to download
|
||||
and install this dependency.
|
||||
|
||||
@@ -53,6 +53,18 @@ preconfigured npm to run bower install for us:
|
||||
npm install
|
||||
```
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Warning:** If a new version of Angular has been released since you last ran `npm install`, then you may have a
|
||||
problem with the `bower install` due to a conflict between the versions of angular.js that need to
|
||||
be installed. If you get this then simply delete your `app/bower_components` folder before running
|
||||
`npm install`.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Note:** If you have bower installed globally then you can run `bower install` but for this project we have
|
||||
preconfigured `npm install` to run bower for us.
|
||||
</div>
|
||||
|
||||
|
||||
## Multiple Views, Routing and Layout Template
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ phone in the phone list.
|
||||
* When you click on a phone on the list, the phone details page with phone-specific information
|
||||
is displayed.
|
||||
|
||||
To implement the phone details view we used {@link ng.$http $http} to fetch our data, and we
|
||||
fleshed out the `phone-detail.html` view template.
|
||||
To implement the phone details view we are going to use {@link ng.$http $http} to fetch our data,
|
||||
and then flesh out the `phone-detail.html` view template.
|
||||
|
||||
<div doc-tutorial-reset="8"></div>
|
||||
|
||||
|
||||
@@ -32,17 +32,18 @@ We are using [Bower][bower] to install client side dependencies. This step upda
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"angular": "~1.3.0",
|
||||
"angular-mocks": "~1.3.0",
|
||||
"angular": "1.4.x",
|
||||
"angular-mocks": "1.4.x",
|
||||
"jquery": "~2.1.1",
|
||||
"bootstrap": "~3.1.1",
|
||||
"angular-route": "~1.3.0",
|
||||
"angular-resource": "~1.3.0"
|
||||
"angular-route": "1.4.x",
|
||||
"angular-resource": "1.4.x"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The new dependency `"angular-resource": "~1.3.0"` tells bower to install a version of the
|
||||
angular-resource component that is compatible with version 1.3.x. We must ask bower to download
|
||||
The new dependency `"angular-resource": "1.4.x"` tells bower to install a version of the
|
||||
angular-resource component that is compatible with version 1.4.x. We must ask bower to download
|
||||
and install this dependency. We can do this by running:
|
||||
|
||||
```
|
||||
|
||||
@@ -36,20 +36,20 @@ We are using [Bower][bower] to install client side dependencies. This step upda
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"angular": "~1.3.0",
|
||||
"angular-mocks": "~1.3.0",
|
||||
"bootstrap": "~3.1.1",
|
||||
"angular-route": "~1.3.0",
|
||||
"angular-resource": "~1.3.0",
|
||||
"angular": "1.4.x",
|
||||
"angular-mocks": "1.4.x",
|
||||
"jquery": "~2.1.1",
|
||||
"angular-animate": "~1.3.0"
|
||||
"bootstrap": "~3.1.1",
|
||||
"angular-route": "1.4.x",
|
||||
"angular-resource": "1.4.x",
|
||||
"angular-animate": "1.4.x"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* `"angular-animate": "~1.3.0"` tells bower to install a version of the
|
||||
angular-animate component that is compatible with version 1.3.x.
|
||||
* `"jquery": "2.1.1"` tells bower to install the 2.1.1 version of jQuery. Note that this is not an
|
||||
* `"angular-animate": "1.4.x"` tells bower to install a version of the
|
||||
angular-animate component that is compatible with version 1.4.x.
|
||||
* `"jquery": "~2.1.1"` tells bower to install the 2.1.1 version of jQuery. Note that this is not an
|
||||
Angular library, it is the standard jQuery library. We can use bower to install a wide range of 3rd
|
||||
party libraries.
|
||||
|
||||
@@ -111,7 +111,7 @@ __`app/index.html`.__
|
||||
```
|
||||
|
||||
<div class="alert alert-error">
|
||||
**Important:** Be sure to use jQuery version 2.1 or newer when using Angular 1.3; jQuery 1.x is
|
||||
**Important:** Be sure to use jQuery version 2.1 or newer when using Angular 1.4; jQuery 1.x is
|
||||
not officially supported.
|
||||
Be sure to load jQuery before all AngularJS scripts, otherwise AngularJS won't detect jQuery and
|
||||
animations will not work as expected.
|
||||
@@ -239,9 +239,9 @@ The name of the starting class is the name of the event that is fired (like `ent
|
||||
The active class name is the same as the starting class's but with an `-active` suffix.
|
||||
This two-class CSS naming convention allows the developer to craft an animation, beginning to end.
|
||||
|
||||
In our example above, elements expand from a height of **0** to **120 pixels** when items are added or moved,
|
||||
around and collapsing the items before removing them from the list.
|
||||
There's also a nice fade-in and fade-out effect that also occurs at the same time. All of this is handled
|
||||
In our example above, elements are expanded from a height of **0** to **120 pixels** when they're added to the
|
||||
list and are collapsed back down to **0 pixels** before being removed from the list.
|
||||
There's also a nice fade-in and fade-out effect that occurs at the same time. All of this is handled
|
||||
by the CSS transition declarations at the top of the example code above.
|
||||
|
||||
Although most modern browsers have good support for [CSS transitions](http://caniuse.com/#feat=css-transitions)
|
||||
@@ -357,10 +357,10 @@ For more on CSS animations, see the
|
||||
## Animating `ngClass` with JavaScript
|
||||
|
||||
Let's add another animation to our application. Switching to our `phone-detail.html` page,
|
||||
we see that we have a nice thumbnail swapper. By clicking on the thumbnails listed on the page,
|
||||
we see that we have a nice thumbnail swapper. By hovering over the thumbnails listed on the page,
|
||||
the profile phone image changes. But how can we change this around to add animations?
|
||||
|
||||
Let's think about it first. Basically, when you click on a thumbnail image, you're changing the
|
||||
Let's think about it first. Basically, when you hover over a thumbnail image, you're changing the
|
||||
state of the profile image to reflect the newly selected thumbnail image.
|
||||
The best way to specify state changes within HTML is to use classes.
|
||||
Much like before, how we used a CSS class to specify an animation, this time the animation will
|
||||
@@ -369,7 +369,7 @@ occur whenever the CSS class itself changes.
|
||||
Whenever a new phone thumbnail is selected, the state changes and the `.active` CSS class is added
|
||||
to the matching profile image and the animation plays.
|
||||
|
||||
Let's get started and tweak our HTML code on the `phone-detail.html` page first. Notice that we
|
||||
Let's get started and tweak our HTML code on the `phone-detail.html` page first. Notice that we
|
||||
have changed the way we display our large image:
|
||||
|
||||
__`app/partials/phone-detail.html`.__
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* using the --for_closure flag.
|
||||
* File generated from CLDR ver. 27.0.1
|
||||
*
|
||||
* This file coveres those locales that are not covered in
|
||||
* This file covers those locales that are not covered in
|
||||
* "numberformatsymbols.js".
|
||||
*
|
||||
* Before checkin, this file could have been manually edited. This is
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ set -e
|
||||
BASE_DIR=`dirname $0`
|
||||
cd $BASE_DIR
|
||||
|
||||
./run-tests.sh
|
||||
npm run test-i18n
|
||||
|
||||
node src/closureSlurper.js
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
PARENT_DIR="$(dirname "$0")"
|
||||
|
||||
../node_modules/.bin/jasmine-node "$PARENT_DIR"/spec/
|
||||
@@ -4,6 +4,7 @@ findLocaleId = closureI18nExtractor.findLocaleId;
|
||||
extractNumberSymbols = closureI18nExtractor.extractNumberSymbols;
|
||||
extractCurrencySymbols = closureI18nExtractor.extractCurrencySymbols;
|
||||
extractDateTimeSymbols = closureI18nExtractor.extractDateTimeSymbols;
|
||||
outputLocale = closureI18nExtractor.outputLocale;
|
||||
|
||||
|
||||
function newTestLocaleInfo() {
|
||||
@@ -11,6 +12,8 @@ function newTestLocaleInfo() {
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||
'octobre', 'novembre', 'décembre'],
|
||||
STANDALONEMONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||
'octobre', 'novembre', 'décembre'],
|
||||
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
|
||||
'nov.', 'déc.'],
|
||||
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
|
||||
@@ -70,7 +73,7 @@ describe("findLocaleId", function() {
|
||||
it("should throw an error otherwise", function() {
|
||||
expect(function() {
|
||||
findLocaleId("str", "otherwise")
|
||||
}).toThrow("unknown type in findLocaleId: otherwise");
|
||||
}).toThrowError("unknown type in findLocaleId: otherwise");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -129,7 +132,10 @@ describe("extractCurrencySymbols", function() {
|
||||
].join('\n');
|
||||
|
||||
var localeInfo = {};
|
||||
expect(extractCurrencySymbols(CONTENT)).toEqual({
|
||||
var currencySymbols = extractCurrencySymbols(CONTENT);
|
||||
expect(currencySymbols.GBP).toEqual([2, '£', 'GB£']);
|
||||
expect(currencySymbols.AOA).toEqual([2, 'Kz', 'Kz']);
|
||||
expect(currencySymbols).toEqual({
|
||||
'GBP':[2, '£', 'GB£'],
|
||||
'AOA':[2, 'Kz', 'Kz']
|
||||
});
|
||||
@@ -140,69 +146,71 @@ describe("extractCurrencySymbols", function() {
|
||||
describe("extractDateTimeSymbols", function() {
|
||||
it("should extract date time data", function() {
|
||||
var CONTENT = [
|
||||
"goog.i18n.DateTimeSymbols_fr_CA = {",
|
||||
" ERAS: ['av. J.-C.', 'ap. J.-C.'],",
|
||||
" ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],",
|
||||
" NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],",
|
||||
" STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',",
|
||||
" 'N', 'D'],",
|
||||
" MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',",
|
||||
" 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" STANDALONEMONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',",
|
||||
" 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',",
|
||||
" 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" STANDALONESHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',",
|
||||
" 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',",
|
||||
" 'samedi'],",
|
||||
" STANDALONEWEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi',",
|
||||
" 'vendredi', 'samedi'],",
|
||||
" SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],",
|
||||
" STANDALONESHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.',",
|
||||
" 'sam.'],",
|
||||
" NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],",
|
||||
" QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],",
|
||||
" AMPMS: ['AM', 'PM'],",
|
||||
" DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'yyyy-MM-dd', 'yy-MM-dd'],",
|
||||
" TIMEFORMATS: ['HH \\'h\\' mm \\'min\\' ss \\'s\\' zzzz', 'HH:mm:ss z',",
|
||||
" 'HH:mm:ss', 'HH:mm'],",
|
||||
" FIRSTDAYOFWEEK: 6,",
|
||||
" WEEKENDRANGE: [5, 6],",
|
||||
" FIRSTWEEKCUTOFFDAY: 2",
|
||||
"};"
|
||||
"goog.i18n.DateTimeSymbols_fr_CA = {",
|
||||
" ERAS: ['av. J.-C.', 'ap. J.-C.'],",
|
||||
" ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],",
|
||||
" NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],",
|
||||
" STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',",
|
||||
" 'N', 'D'],",
|
||||
" MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',",
|
||||
" 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" STANDALONEMONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',",
|
||||
" 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
|
||||
" SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',",
|
||||
" 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" STANDALONESHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',",
|
||||
" 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
|
||||
" WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',",
|
||||
" 'samedi'],",
|
||||
" STANDALONEWEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi',",
|
||||
" 'vendredi', 'samedi'],",
|
||||
" SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],",
|
||||
" STANDALONESHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.',",
|
||||
" 'sam.'],",
|
||||
" NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
|
||||
" SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],",
|
||||
" QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],",
|
||||
" AMPMS: ['AM', 'PM'],",
|
||||
" DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'yyyy-MM-dd', 'yy-MM-dd'],",
|
||||
" TIMEFORMATS: ['HH \\'h\\' mm \\'min\\' ss \\'s\\' zzzz', 'HH:mm:ss z',",
|
||||
" 'HH:mm:ss', 'HH:mm'],",
|
||||
" FIRSTDAYOFWEEK: 6,",
|
||||
" WEEKENDRANGE: [5, 6],",
|
||||
" FIRSTWEEKCUTOFFDAY: 2",
|
||||
"};"
|
||||
].join('\n');
|
||||
var localeInfo = {};
|
||||
var expectedLocaleInfo = {
|
||||
fr_CA: {
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||
'octobre', 'novembre', 'décembre'],
|
||||
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
|
||||
'nov.', 'déc.'],
|
||||
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
|
||||
SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
|
||||
FIRSTDAYOFWEEK: 6,
|
||||
WEEKENDRANGE: [5, 6],
|
||||
AMPMS: ['AM', 'PM'],
|
||||
ERAS: ['av. J.-C.', 'ap. J.-C.'],
|
||||
ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
|
||||
medium: 'yyyy-MM-dd HH:mm:ss',
|
||||
short: 'yy-MM-dd HH:mm',
|
||||
fullDate: 'EEEE d MMMM y',
|
||||
longDate: 'd MMMM y',
|
||||
mediumDate: 'yyyy-MM-dd',
|
||||
shortDate: 'yy-MM-dd',
|
||||
mediumTime: 'HH:mm:ss',
|
||||
shortTime: 'HH:mm'
|
||||
}
|
||||
var localeInfo = {};
|
||||
var expectedLocaleInfo = {
|
||||
fr_CA: {
|
||||
DATETIME_FORMATS: {
|
||||
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
|
||||
'octobre', 'novembre', 'décembre'],
|
||||
STANDALONEMONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
|
||||
'août', 'septembre', 'octobre', 'novembre', 'décembre'],
|
||||
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
|
||||
'nov.', 'déc.'],
|
||||
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
|
||||
SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
|
||||
FIRSTDAYOFWEEK: 6,
|
||||
WEEKENDRANGE: [5, 6],
|
||||
AMPMS: ['AM', 'PM'],
|
||||
ERAS: ['av. J.-C.', 'ap. J.-C.'],
|
||||
ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],
|
||||
medium: 'yyyy-MM-dd HH:mm:ss',
|
||||
short: 'yy-MM-dd HH:mm',
|
||||
fullDate: 'EEEE d MMMM y',
|
||||
longDate: 'd MMMM y',
|
||||
mediumDate: 'yyyy-MM-dd',
|
||||
shortDate: 'yy-MM-dd',
|
||||
mediumTime: 'HH:mm:ss',
|
||||
shortTime: 'HH:mm'
|
||||
}
|
||||
};
|
||||
extractDateTimeSymbols(CONTENT, localeInfo);
|
||||
expect(localeInfo).toEqual(expectedLocaleInfo);
|
||||
})
|
||||
}
|
||||
};
|
||||
extractDateTimeSymbols(CONTENT, localeInfo);
|
||||
expect(localeInfo).toEqual(expectedLocaleInfo);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pluralExtractor", function() {
|
||||
@@ -268,3 +276,10 @@ describe("serializeContent", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("outputLocale", function() {
|
||||
it("should render the correct locale ids", function() {
|
||||
var output = outputLocale(newTestLocaleInfo(), 'fr_CA');
|
||||
expect(output).toContain('"id": "fr-ca"');
|
||||
expect(output).toContain('"localeID": "fr_CA"');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,6 +27,7 @@ describe("convertNumberData", function() {
|
||||
describe("convertDatetimeData", function() {
|
||||
var convert = converter.convertDatetimeData,
|
||||
dataObj = { MONTHS: ['Enero', 'Pebrero'],
|
||||
STANDALONEMONTHS: ['Enero', 'Pebrero'],
|
||||
SHORTMONTHS: ['Ene', 'Peb'],
|
||||
WEEKDAYS: ['Linggo', 'Lunes'],
|
||||
SHORTWEEKDAYS: ['Lin', 'Lun'],
|
||||
@@ -37,6 +38,7 @@ describe("convertDatetimeData", function() {
|
||||
it('should convert empty datetime obj', function() {
|
||||
var processedData = convert(dataObj);
|
||||
expect(processedData.MONTH).toEqual(['Enero', 'Pebrero']);
|
||||
expect(processedData.STANDALONEMONTH).toEqual(['Enero', 'Pebrero']);
|
||||
expect(processedData.SHORTMONTH).toEqual(['Ene', 'Peb']);
|
||||
expect(processedData.DAY).toEqual(['Linggo', 'Lunes']);
|
||||
expect(processedData.SHORTDAY).toEqual(['Lin', 'Lun']);
|
||||
|
||||
@@ -50,10 +50,10 @@ function extractNumberSymbols(content, localeInfo, currencySymbols) {
|
||||
function extractCurrencySymbols(content) {
|
||||
//eval script in the current context so that we get access to all the symbols
|
||||
eval(content.toString());
|
||||
var currencySymbols = goog.i18n.currency.CurrencyInfo;
|
||||
currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
|
||||
// var currencySymbols = goog.i18n.currency.CurrencyInfo;
|
||||
// currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
|
||||
|
||||
return currencySymbols;
|
||||
return Object.assign({}, goog.i18n.currency.CurrencyInfoTier2, goog.i18n.currency.CurrencyInfo);
|
||||
}
|
||||
|
||||
function extractDateTimeSymbols(content, localeInfo) {
|
||||
@@ -79,7 +79,7 @@ function pluralExtractor(content, localeInfo) {
|
||||
goog.LOCALE = localeIds[i].match(/[^_]+/)[0];
|
||||
try {
|
||||
eval(contentText);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
console.log("Error in eval(contentText): " + e.stack);
|
||||
}
|
||||
if (!goog.i18n.pluralRules.select) {
|
||||
@@ -133,7 +133,7 @@ function canonicalizeForJsonStringify(unused_key, object) {
|
||||
|
||||
function serializeContent(localeObj) {
|
||||
return JSON.stringify(localeObj, canonicalizeForJsonStringify, ' ')
|
||||
.replace(new RegExp('[\\u007f-\\uffff]', 'g'), function(c) { return '\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4); })
|
||||
.replace(new RegExp('[\\u007f-\\uffff]', 'g'), function(c) { return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4); })
|
||||
.replace(/"@@|@@"/g, '');
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ function outputLocale(localeInfo, localeID) {
|
||||
if (!localeObj.DATETIME_FORMATS) {
|
||||
localeObj.DATETIME_FORMATS = fallBackObj.DATETIME_FORMATS;
|
||||
}
|
||||
localeObj.localeID = localeID;
|
||||
localeObj.id = correctedLocaleId(localeID);
|
||||
|
||||
var getDecimals = [
|
||||
@@ -201,10 +202,11 @@ function outputLocale(localeInfo, localeID) {
|
||||
DATETIME_FORMATS: localeObj.DATETIME_FORMATS,
|
||||
NUMBER_FORMATS: localeObj.NUMBER_FORMATS,
|
||||
pluralCat: localeObj.pluralCat,
|
||||
id: localeObj.id
|
||||
id: localeObj.id,
|
||||
localeID: localeID
|
||||
};
|
||||
|
||||
var content = serializeContent(localeInfo[localeID]);
|
||||
var content = serializeContent(localeObj);
|
||||
if (content.indexOf('getVF(') < 0) {
|
||||
getVF = '';
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ function convertDatetimeData(dataObj) {
|
||||
|
||||
datetimeFormats.MONTH = dataObj.MONTHS;
|
||||
datetimeFormats.SHORTMONTH = dataObj.SHORTMONTHS;
|
||||
datetimeFormats.STANDALONEMONTH = dataObj.STANDALONEMONTHS;
|
||||
datetimeFormats.DAY = dataObj.WEEKDAYS;
|
||||
datetimeFormats.SHORTDAY = dataObj.SHORTWEEKDAYS;
|
||||
datetimeFormats.AMPMS = dataObj.AMPMS;
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ var PATTERN_SEP = ';',
|
||||
DIGIT = '#';
|
||||
|
||||
/**
|
||||
* main funciton for parser
|
||||
* main function for parser
|
||||
* @param str {string} pattern to be parsed (e.g. #,##0.###).
|
||||
*/
|
||||
function parsePattern(pattern) {
|
||||
|
||||
@@ -2142,7 +2142,7 @@ queue}</string>
|
||||
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr
|
||||
|
||||
\f0\fs22 \cf2 $scope\
|
||||
name='Wold'}</string>
|
||||
name='World'}</string>
|
||||
<key>VerticalPad</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
|
||||
@@ -37,7 +37,7 @@ module.exports = function(config, specificOptions) {
|
||||
'SL_Chrome': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: '43'
|
||||
version: '45'
|
||||
},
|
||||
'SL_Firefox': {
|
||||
base: 'SauceLabs',
|
||||
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
|
||||
echo "Shutting down Browserstack tunnel"
|
||||
echo "TODO: implement me"
|
||||
exit 1
|
||||
@@ -0,0 +1,58 @@
|
||||
'use strict';
|
||||
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var glob = require("glob");
|
||||
var _ = require('lodash');
|
||||
var files = require('../../angularFiles').files;
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
grunt.registerTask('validate-angular-files', function() {
|
||||
var combinedFiles = _.clone(files.angularModules);
|
||||
combinedFiles.ng = files.angularSrc;
|
||||
combinedFiles.angularLoader = files.angularLoader;
|
||||
|
||||
var errorsDetected = false;
|
||||
var directories = [];
|
||||
var detectedFiles = {};
|
||||
|
||||
for (var section in combinedFiles) {
|
||||
var sectionFiles = combinedFiles[section];
|
||||
|
||||
if (section != 'angularLoader') {
|
||||
directories.push('src/' + section);
|
||||
}
|
||||
|
||||
grunt.log.debug('Validating ' + sectionFiles.length + ' files from the "' + section + '" module.');
|
||||
|
||||
sectionFiles.forEach(function(file) {
|
||||
detectedFiles[file] = true;
|
||||
|
||||
if (!fs.existsSync(file)) {
|
||||
grunt.log.error(file + ' does not exist in the local file structure.');
|
||||
errorsDetected = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
directories.forEach(function(directory) {
|
||||
glob.sync(directory + '/**/*').forEach(function(filePath) {
|
||||
if (!fs.lstatSync(filePath).isDirectory()) {
|
||||
var fileName = path.basename(filePath);
|
||||
var isHiddenFile = fileName[0] == '.';
|
||||
if (!isHiddenFile && !detectedFiles[filePath]) {
|
||||
grunt.log.error(filePath + ' exists in the local file structure but isn\'t used by any module.');
|
||||
errorsDetected = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (errorsDetected) {
|
||||
throw new Error('Not all files were properly detected in the local file structure.');
|
||||
} else {
|
||||
grunt.log.ok('All files were detected successfully!');
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,309 +0,0 @@
|
||||
/*
|
||||
* HTML Parser By John Resig (ejohn.org)
|
||||
* Original code by Erik Arvidsson, Mozilla Public License
|
||||
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
|
||||
*
|
||||
* // Use like so:
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
* // or to get an XML string:
|
||||
* HTMLtoXML(htmlString);
|
||||
*
|
||||
* // or to get an XML DOM Document
|
||||
* HTMLtoDOM(htmlString);
|
||||
*
|
||||
* // or to inject into an existing document/DOM node
|
||||
* HTMLtoDOM(htmlString, document);
|
||||
* HTMLtoDOM(htmlString, document.body);
|
||||
*
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
var startTag = /^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
|
||||
endTag = /^<\/(\w+)[^>]*>/,
|
||||
attr = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
|
||||
|
||||
// Empty Elements - HTML 4.01
|
||||
var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed");
|
||||
|
||||
// Block Elements - HTML 4.01
|
||||
var block = makeMap("address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul");
|
||||
|
||||
// Inline Elements - HTML 4.01
|
||||
var inline = makeMap("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");
|
||||
|
||||
// Elements that you can, intentionally, leave open
|
||||
// (and which close themselves)
|
||||
var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");
|
||||
|
||||
// Attributes that have their values filled in disabled="disabled"
|
||||
var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
|
||||
|
||||
// Special Elements (can contain anything)
|
||||
var special = makeMap("script,style");
|
||||
|
||||
var htmlParser = this.htmlParser = function( html, handler ) {
|
||||
var index, chars, match, stack = [], last = html;
|
||||
stack.last = function(){
|
||||
return this[ this.length - 1 ];
|
||||
};
|
||||
|
||||
while ( html ) {
|
||||
chars = true;
|
||||
|
||||
// Make sure we're not in a script or style element
|
||||
if ( !stack.last() || !special[ stack.last() ] ) {
|
||||
|
||||
// Comment
|
||||
if ( html.indexOf("<!--") == 0 ) {
|
||||
index = html.indexOf("-->");
|
||||
|
||||
if ( index >= 0 ) {
|
||||
if ( handler.comment )
|
||||
handler.comment( html.substring( 4, index ) );
|
||||
html = html.substring( index + 3 );
|
||||
chars = false;
|
||||
}
|
||||
|
||||
// end tag
|
||||
} else if ( html.indexOf("</") == 0 ) {
|
||||
match = html.match( endTag );
|
||||
|
||||
if ( match ) {
|
||||
html = html.substring( match[0].length );
|
||||
match[0].replace( endTag, parseEndTag );
|
||||
chars = false;
|
||||
}
|
||||
|
||||
// start tag
|
||||
} else if ( html.indexOf("<") == 0 ) {
|
||||
match = html.match( startTag );
|
||||
|
||||
if ( match ) {
|
||||
html = html.substring( match[0].length );
|
||||
match[0].replace( startTag, parseStartTag );
|
||||
chars = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( chars ) {
|
||||
index = html.indexOf("<");
|
||||
|
||||
var text = index < 0 ? html : html.substring( 0, index );
|
||||
html = index < 0 ? "" : html.substring( index );
|
||||
|
||||
if ( handler.chars )
|
||||
handler.chars( text );
|
||||
}
|
||||
|
||||
} else {
|
||||
html = html.replace(new RegExp("(.*)<\/" + stack.last() + "[^>]*>"), function(all, text){
|
||||
text = text.replace(/<!--(.*?)-->/g, "$1")
|
||||
.replace(/<!\[CDATA\[(.*?)]]>/g, "$1");
|
||||
|
||||
if ( handler.chars )
|
||||
handler.chars( text );
|
||||
|
||||
return "";
|
||||
});
|
||||
|
||||
parseEndTag( "", stack.last() );
|
||||
}
|
||||
|
||||
if ( html == last )
|
||||
throw "Parse Error: " + html;
|
||||
last = html;
|
||||
}
|
||||
|
||||
// Clean up any remaining tags
|
||||
parseEndTag();
|
||||
|
||||
function parseStartTag( tag, tagName, rest, unary ) {
|
||||
if ( block[ tagName ] ) {
|
||||
while ( stack.last() && inline[ stack.last() ] ) {
|
||||
parseEndTag( "", stack.last() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( closeSelf[ tagName ] && stack.last() == tagName ) {
|
||||
parseEndTag( "", tagName );
|
||||
}
|
||||
|
||||
unary = empty[ tagName ] || !!unary;
|
||||
|
||||
if ( !unary )
|
||||
stack.push( tagName );
|
||||
|
||||
if ( handler.start ) {
|
||||
var attrs = [];
|
||||
|
||||
rest.replace(attr, function(match, name) {
|
||||
var value = arguments[2] ? arguments[2] :
|
||||
arguments[3] ? arguments[3] :
|
||||
arguments[4] ? arguments[4] :
|
||||
fillAttrs[name] ? name : "";
|
||||
|
||||
attrs.push({
|
||||
name: name,
|
||||
value: value,
|
||||
escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //"
|
||||
});
|
||||
});
|
||||
|
||||
if ( handler.start )
|
||||
handler.start( tagName, attrs, unary );
|
||||
}
|
||||
}
|
||||
|
||||
function parseEndTag( tag, tagName ) {
|
||||
// If no tag name is provided, clean shop
|
||||
if ( !tagName )
|
||||
var pos = 0;
|
||||
|
||||
// Find the closest opened tag of the same type
|
||||
else
|
||||
for ( var pos = stack.length - 1; pos >= 0; pos-- )
|
||||
if ( stack[ pos ] == tagName )
|
||||
break;
|
||||
|
||||
if ( pos >= 0 ) {
|
||||
// Close all the open elements, up the stack
|
||||
for ( var i = stack.length - 1; i >= pos; i-- )
|
||||
if ( handler.end )
|
||||
handler.end( stack[ i ] );
|
||||
|
||||
// Remove the open elements from the stack
|
||||
stack.length = pos;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.HTMLtoXML = function( html ) {
|
||||
var results = "";
|
||||
|
||||
htmlParser(html, {
|
||||
start: function( tag, attrs, unary ) {
|
||||
results += "<" + tag;
|
||||
|
||||
for ( var i = 0; i < attrs.length; i++ )
|
||||
results += " " + attrs[i].name + '="' + attrs[i].escaped + '"';
|
||||
|
||||
results += (unary ? "/" : "") + ">";
|
||||
},
|
||||
end: function( tag ) {
|
||||
results += "</" + tag + ">";
|
||||
},
|
||||
chars: function( text ) {
|
||||
results += text;
|
||||
},
|
||||
comment: function( text ) {
|
||||
results += "<!--" + text + "-->";
|
||||
}
|
||||
});
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
this.HTMLtoDOM = function( html, doc ) {
|
||||
// There can be only one of these elements
|
||||
var one = makeMap("html,head,body,title");
|
||||
|
||||
// Enforce a structure for the document
|
||||
var structure = {
|
||||
link: "head",
|
||||
base: "head"
|
||||
};
|
||||
|
||||
if ( !doc ) {
|
||||
if ( typeof DOMDocument != "undefined" )
|
||||
doc = new DOMDocument();
|
||||
else if ( typeof document != "undefined" && document.implementation && document.implementation.createDocument )
|
||||
doc = document.implementation.createDocument("", "", null);
|
||||
else if ( typeof ActiveX != "undefined" )
|
||||
doc = new ActiveXObject("Msxml.DOMDocument");
|
||||
|
||||
} else
|
||||
doc = doc.ownerDocument ||
|
||||
doc.getOwnerDocument && doc.getOwnerDocument() ||
|
||||
doc;
|
||||
|
||||
var elems = [],
|
||||
documentElement = doc.documentElement ||
|
||||
doc.getDocumentElement && doc.getDocumentElement();
|
||||
|
||||
// If we're dealing with an empty document then we
|
||||
// need to pre-populate it with the HTML document structure
|
||||
if ( !documentElement && doc.createElement ) (function(){
|
||||
var html = doc.createElement("html");
|
||||
var head = doc.createElement("head");
|
||||
head.appendChild( doc.createElement("title") );
|
||||
html.appendChild( head );
|
||||
html.appendChild( doc.createElement("body") );
|
||||
doc.appendChild( html );
|
||||
})();
|
||||
|
||||
// Find all the unique elements
|
||||
if ( doc.getElementsByTagName )
|
||||
for ( var i in one )
|
||||
one[ i ] = doc.getElementsByTagName( i )[0];
|
||||
|
||||
// If we're working with a document, inject contents into
|
||||
// the body element
|
||||
var curParentNode = one.body;
|
||||
|
||||
htmlParser( html, {
|
||||
start: function( tagName, attrs, unary ) {
|
||||
// If it's a pre-built element, then we can ignore
|
||||
// its construction
|
||||
if ( one[ tagName ] ) {
|
||||
curParentNode = one[ tagName ];
|
||||
return;
|
||||
}
|
||||
|
||||
var elem = doc.createElement( tagName );
|
||||
|
||||
for ( var attr in attrs )
|
||||
elem.setAttribute( attrs[ attr ].name, attrs[ attr ].value );
|
||||
|
||||
if ( structure[ tagName ] && typeof one[ structure[ tagName ] ] != "boolean" )
|
||||
one[ structure[ tagName ] ].appendChild( elem );
|
||||
|
||||
else if ( curParentNode && curParentNode.appendChild )
|
||||
curParentNode.appendChild( elem );
|
||||
|
||||
if ( !unary ) {
|
||||
elems.push( elem );
|
||||
curParentNode = elem;
|
||||
}
|
||||
},
|
||||
end: function( tag ) {
|
||||
elems.length -= 1;
|
||||
|
||||
// Init the new parentNode
|
||||
curParentNode = elems[ elems.length - 1 ];
|
||||
},
|
||||
chars: function( text ) {
|
||||
curParentNode.appendChild( doc.createTextNode( text ) );
|
||||
},
|
||||
comment: function( text ) {
|
||||
// create comment node
|
||||
}
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
|
||||
function makeMap(str){
|
||||
var obj = {}, items = str.split(",");
|
||||
for ( var i = 0; i < items.length; i++ )
|
||||
obj[ items[i] ] = true;
|
||||
return obj;
|
||||
}
|
||||
})();
|
||||
@@ -11,8 +11,7 @@ set -e
|
||||
# Curl and run this script as part of your .travis.yml before_script section:
|
||||
# before_script:
|
||||
# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash
|
||||
|
||||
CONNECT_URL="https://saucelabs.com/downloads/sc-4.3.7-linux.tar.gz"
|
||||
CONNECT_URL="https://saucelabs.com/downloads/sc-4.3.13-linux.tar.gz"
|
||||
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
|
||||
CONNECT_DOWNLOAD="sc-4.3.7-linux.tar.gz"
|
||||
|
||||
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
|
||||
echo "Shutting down Sauce Connect tunnel"
|
||||
|
||||
killall sc
|
||||
|
||||
while [[ -n `ps -ef | grep "sauce-connect-" | grep -v "grep"` ]]; do
|
||||
printf "."
|
||||
sleep .5
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Sauce Connect tunnel has been shut down"
|
||||
@@ -178,7 +178,7 @@ var getSnapshotVersion = function() {
|
||||
// last release was a non beta release. Increment the patch level to
|
||||
// indicate the next release that we will be doing.
|
||||
// E.g. last release was 1.3.0, then the snapshot will be
|
||||
// 1.3.1-build.1, which is lesser than 1.3.1 accorind the semver!
|
||||
// 1.3.1-build.1, which is lesser than 1.3.1 according to the semver!
|
||||
|
||||
// If the last release was a beta release we don't update the
|
||||
// beta number by purpose, as otherwise the semver comparison
|
||||
|
||||
+5240
-911
File diff suppressed because it is too large
Load Diff
Generated
+8311
-1615
File diff suppressed because it is too large
Load Diff
+31
-17
@@ -1,18 +1,24 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"license": "MIT",
|
||||
"branchVersion": "^1.4.0-beta.0",
|
||||
"branchPattern": "1.4.*",
|
||||
"distTag": "latest",
|
||||
"branchVersion": "^1.5.0-beta.2",
|
||||
"branchPattern": "1.5.*",
|
||||
"distTag": "beta",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": "~0.10",
|
||||
"node": "<5",
|
||||
"npm": "~2.5"
|
||||
},
|
||||
"engineStrict": true,
|
||||
"scripts": {
|
||||
"preinstall": "node scripts/npm/check-node-modules.js --purge",
|
||||
"postinstall": "node scripts/npm/copy-npm-shrinkwrap.js",
|
||||
"commit": "git-cz",
|
||||
"test-i18n": "jasmine-node i18n/spec"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-benchpress": "0.x.x",
|
||||
"benchmark": "1.x.x",
|
||||
@@ -20,9 +26,12 @@
|
||||
"browserstacktunnel-wrapper": "~1.3.1",
|
||||
"canonical-path": "0.0.2",
|
||||
"cheerio": "^0.17.0",
|
||||
"commitizen": "^2.3.0",
|
||||
"cz-conventional-changelog": "1.1.4",
|
||||
"dgeni": "^0.4.0",
|
||||
"dgeni-packages": "^0.10.0",
|
||||
"dgeni-packages": "^0.11.0",
|
||||
"event-stream": "~3.1.0",
|
||||
"glob": "^6.0.1",
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-bump": "~0.0.13",
|
||||
"grunt-contrib-clean": "~0.6.0",
|
||||
@@ -32,7 +41,7 @@
|
||||
"grunt-contrib-jshint": "~0.10.0",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||
"grunt-jscs": "~1.2.0",
|
||||
"grunt-jscs": "^2.1.0",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"grunt-shell": "~1.1.1",
|
||||
"gulp": "~3.8.0",
|
||||
@@ -43,18 +52,18 @@
|
||||
"gulp-sourcemaps": "^1.2.2",
|
||||
"gulp-uglify": "^1.0.1",
|
||||
"gulp-util": "^3.0.1",
|
||||
"jasmine-node": "~1.14.5",
|
||||
"jasmine-node": "^2.0.0",
|
||||
"jasmine-reporters": "~1.0.1",
|
||||
"jshint-stylish": "~1.0.0",
|
||||
"karma": "0.12.32",
|
||||
"karma-browserstack-launcher": "0.1.2",
|
||||
"karma-chrome-launcher": "0.1.5",
|
||||
"karma-firefox-launcher": "0.1.3",
|
||||
"karma-jasmine": "0.1.5",
|
||||
"karma-junit-reporter": "0.2.2",
|
||||
"karma-ng-scenario": "0.1.0",
|
||||
"karma-sauce-launcher": "0.2.10",
|
||||
"karma-script-launcher": "0.1.0",
|
||||
"karma": "^0.13.19",
|
||||
"karma-browserstack-launcher": "^0.1.8",
|
||||
"karma-chrome-launcher": "^0.2.2",
|
||||
"karma-firefox-launcher": "^0.1.7",
|
||||
"karma-jasmine": "^0.1.6",
|
||||
"karma-junit-reporter": "^0.3.8",
|
||||
"karma-ng-scenario": "^0.1.0",
|
||||
"karma-sauce-launcher": "^0.3.0",
|
||||
"karma-script-launcher": "^0.1.0",
|
||||
"load-grunt-tasks": "~0.6.0",
|
||||
"lodash": "~2.4.1",
|
||||
"marked": "~0.3.0",
|
||||
@@ -76,5 +85,10 @@
|
||||
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"dependencies": {}
|
||||
"dependencies": {},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-conventional-changelog"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ config.specs = [
|
||||
];
|
||||
|
||||
config.capabilities = {
|
||||
browserName: 'chrome',
|
||||
browserName: 'chrome'
|
||||
};
|
||||
|
||||
exports.config = config;
|
||||
|
||||
@@ -14,8 +14,9 @@ function init {
|
||||
TMP_DIR=$(resolveDir ../../tmp)
|
||||
BUILD_DIR=$(resolveDir ../../build)
|
||||
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||
PROJECT_DIR=$(resolveDir ../..)
|
||||
# get the npm dist-tag from a custom property (distTag) in package.json
|
||||
DIST_TAG=$(readJsonProp "package.json" "distTag")
|
||||
DIST_TAG=$(readJsonProp "$PROJECT_DIR/package.json" "distTag")
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ echo "#################################"
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
|
||||
scripts/jenkins/set-node-version.sh
|
||||
|
||||
# This is the default set of browsers to use on the CI server unless overridden via env variable
|
||||
if [[ -z "$BROWSERS" ]]
|
||||
then
|
||||
@@ -19,6 +21,7 @@ rm -f angular.js.size
|
||||
|
||||
|
||||
# BUILD #
|
||||
npm install -g grunt-cli
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
|
||||
@@ -4,9 +4,7 @@ echo "#################################"
|
||||
echo "#### Update master ##############"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"[--no-test=(true|false)]"
|
||||
)
|
||||
ARG_DEFS=()
|
||||
|
||||
function init {
|
||||
if [[ ! $VERBOSE ]]; then
|
||||
@@ -17,14 +15,7 @@ function init {
|
||||
|
||||
function build {
|
||||
cd ../..
|
||||
|
||||
if [[ $NO_TEST == "true" ]]; then
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
else
|
||||
./jenkins_build.sh
|
||||
fi
|
||||
|
||||
scripts/jenkins/build.sh
|
||||
cd $SCRIPT_DIR
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,10 @@ function init {
|
||||
}
|
||||
|
||||
function build {
|
||||
./set-node-version.sh
|
||||
cd ../..
|
||||
|
||||
npm install -g grunt-cli
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
|
||||
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Install nvm for this shell
|
||||
source ~/.nvm/nvm.sh
|
||||
|
||||
# Use node.js at 4.2.x
|
||||
nvm install 4.2
|
||||
@@ -0,0 +1,74 @@
|
||||
// Implementation based on:
|
||||
// https://github.com/angular/angular/blob/3b9c08676a4c921bbfa847802e08566fb601ba7a/tools/npm/check-node-modules.js
|
||||
'use strict';
|
||||
|
||||
// Imports
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
// Constants
|
||||
var PROJECT_ROOT = path.join(__dirname, '../../');
|
||||
var NODE_MODULES_DIR = 'node_modules';
|
||||
var NPM_SHRINKWRAP_FILE = 'npm-shrinkwrap.json';
|
||||
var NPM_SHRINKWRAP_CACHED_FILE = NODE_MODULES_DIR + '/npm-shrinkwrap.cached.json';
|
||||
|
||||
// Run
|
||||
_main();
|
||||
|
||||
// Functions - Definitions
|
||||
function _main() {
|
||||
var purgeIfStale = process.argv.indexOf('--purge') !== -1;
|
||||
|
||||
process.chdir(PROJECT_ROOT);
|
||||
checkNodeModules(purgeIfStale);
|
||||
}
|
||||
|
||||
function checkNodeModules(purgeIfStale) {
|
||||
var nodeModulesOk = compareMarkerFiles(NPM_SHRINKWRAP_FILE, NPM_SHRINKWRAP_CACHED_FILE);
|
||||
|
||||
if (nodeModulesOk) {
|
||||
console.log(':-) npm dependencies are looking good!');
|
||||
} else if (purgeIfStale) {
|
||||
console.log(':-( npm dependencies are stale or in an unknown state!');
|
||||
console.log(' Purging \'' + NODE_MODULES_DIR + '\'...');
|
||||
deleteDirSync(NODE_MODULES_DIR);
|
||||
} else {
|
||||
var separator = new Array(81).join('!');
|
||||
|
||||
console.warn(separator);
|
||||
console.warn(':-( npm dependencies are stale or in an unknown state!');
|
||||
console.warn('You can rebuild the dependencies by running `npm install`.');
|
||||
console.warn(separator);
|
||||
}
|
||||
|
||||
return nodeModulesOk;
|
||||
}
|
||||
|
||||
function compareMarkerFiles(markerFilePath, cachedMarkerFilePath) {
|
||||
if (!fs.existsSync(cachedMarkerFilePath)) return false;
|
||||
|
||||
var opts = {encoding: 'utf-8'};
|
||||
var markerContent = fs.readFileSync(markerFilePath, opts);
|
||||
var cachedMarkerContent = fs.readFileSync(cachedMarkerFilePath, opts);
|
||||
|
||||
return markerContent === cachedMarkerContent;
|
||||
}
|
||||
|
||||
// Custom implementation of `rm -rf` that works consistently across OSes
|
||||
function deleteDirSync(path) {
|
||||
if (fs.existsSync(path)) {
|
||||
fs.readdirSync(path).forEach(deleteDirOrFileSync);
|
||||
fs.rmdirSync(path);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
function deleteDirOrFileSync(subpath) {
|
||||
var curPath = path + '/' + subpath;
|
||||
|
||||
if (fs.lstatSync(curPath).isDirectory()) {
|
||||
deleteDirSync(curPath);
|
||||
} else {
|
||||
fs.unlinkSync(curPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
|
||||
// Imports
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
// Constants
|
||||
var PROJECT_ROOT = path.join(__dirname, '../../');
|
||||
var NODE_MODULES_DIR = 'node_modules';
|
||||
var NPM_SHRINKWRAP_FILE = 'npm-shrinkwrap.json';
|
||||
var NPM_SHRINKWRAP_CACHED_FILE = NODE_MODULES_DIR + '/npm-shrinkwrap.cached.json';
|
||||
|
||||
// Run
|
||||
_main();
|
||||
|
||||
// Functions - Definitions
|
||||
function _main() {
|
||||
process.chdir(PROJECT_ROOT);
|
||||
copyFile(NPM_SHRINKWRAP_FILE, NPM_SHRINKWRAP_CACHED_FILE, onCopied);
|
||||
}
|
||||
|
||||
// Implementation based on:
|
||||
// https://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js#answer-21995878
|
||||
function copyFile(srcPath, dstPath, callback) {
|
||||
var callbackCalled = false;
|
||||
|
||||
if (!fs.existsSync(srcPath)) {
|
||||
done(new Error('Missing source file: ' + srcPath));
|
||||
return;
|
||||
}
|
||||
|
||||
var rs = fs.createReadStream(srcPath);
|
||||
rs.on('error', done);
|
||||
|
||||
var ws = fs.createWriteStream(dstPath);
|
||||
ws.on('error', done);
|
||||
ws.on('finish', done);
|
||||
|
||||
rs.pipe(ws);
|
||||
|
||||
// Helpers
|
||||
function done(err) {
|
||||
if (callback && !callbackCalled) {
|
||||
callbackCalled = true;
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onCopied(err) {
|
||||
if (err) {
|
||||
var separator = new Array(81).join('!');
|
||||
|
||||
console.error(separator);
|
||||
console.error(
|
||||
'Failed to copy `' + NPM_SHRINKWRAP_FILE + '` to `' + NPM_SHRINKWRAP_CACHED_FILE + '`:');
|
||||
console.error(err);
|
||||
console.error(separator);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
SHRINKWRAP_FILE=npm-shrinkwrap.json
|
||||
SHRINKWRAP_CACHED_FILE=node_modules/npm-shrinkwrap.cached.json
|
||||
|
||||
if diff -q $SHRINKWRAP_FILE $SHRINKWRAP_CACHED_FILE; then
|
||||
echo 'No shrinkwrap changes detected. npm install will be skipped...';
|
||||
else
|
||||
echo 'Blowing away node_modules and reinstalling npm dependencies...'
|
||||
rm -rf node_modules
|
||||
npm install
|
||||
cp $SHRINKWRAP_FILE $SHRINKWRAP_CACHED_FILE
|
||||
echo 'npm install successful!'
|
||||
fi
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
mkdir -p $LOGS_DIR
|
||||
|
||||
if [ $JOB != "ci-checks" ]; then
|
||||
echo "start_browser_provider"
|
||||
./scripts/travis/start_browser_provider.sh
|
||||
fi
|
||||
|
||||
npm install -g grunt-cli
|
||||
|
||||
if [ $JOB != "ci-checks" ]; then
|
||||
grunt package
|
||||
echo "wait_for_browser_provider"
|
||||
./scripts/travis/wait_for_browser_provider.sh
|
||||
fi
|
||||
@@ -5,7 +5,9 @@ set -e
|
||||
export BROWSER_STACK_ACCESS_KEY=`echo $BROWSER_STACK_ACCESS_KEY | rev`
|
||||
export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||
|
||||
if [ $JOB = "unit" ]; then
|
||||
if [ $JOB = "ci-checks" ]; then
|
||||
grunt ci-checks
|
||||
elif [ $JOB = "unit" ]; then
|
||||
if [ "$BROWSER_PROVIDER" == "browserstack" ]; then
|
||||
BROWSERS="BS_Chrome,BS_Safari,BS_Firefox,BS_IE_9,BS_IE_10,BS_IE_11,BS_iOS"
|
||||
else
|
||||
@@ -14,7 +16,6 @@ if [ $JOB = "unit" ]; then
|
||||
|
||||
grunt test:promises-aplus
|
||||
grunt test:unit --browsers $BROWSERS --reporters dots
|
||||
grunt ci-checks
|
||||
grunt tests:docs --browsers $BROWSERS --reporters dots
|
||||
elif [ $JOB = "docs-e2e" ]; then
|
||||
grunt test:travis-protractor --specs "docs/app/e2e/**/*.scenario.js"
|
||||
@@ -31,5 +32,5 @@ elif [ $JOB = "e2e" ]; then
|
||||
export TARGET_SPECS="test/e2e/tests/**/*.js,$TARGET_SPECS"
|
||||
grunt test:travis-protractor --specs "$TARGET_SPECS"
|
||||
else
|
||||
echo "Unknown job type. Please set JOB=unit or JOB=e2e-*."
|
||||
echo "Unknown job type. Please set JOB=ci-checks, JOB=unit or JOB=e2e-*."
|
||||
fi
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
# Has to be run from project root directory.
|
||||
|
||||
./lib/${BROWSER_PROVIDER}/teardown_tunnel.sh
|
||||
+1
-2
@@ -94,8 +94,6 @@
|
||||
"VALIDITY_STATE_PROPERTY": false,
|
||||
"reloadWithDebugInfo": false,
|
||||
|
||||
"skipDestroyOnNextJQueryCleanData": true,
|
||||
|
||||
"NODE_TYPE_ELEMENT": false,
|
||||
"NODE_TYPE_ATTRIBUTE": false,
|
||||
"NODE_TYPE_TEXT": false,
|
||||
@@ -140,6 +138,7 @@
|
||||
"jqLiteInheritedData": false,
|
||||
"jqLiteBuildFragment": false,
|
||||
"jqLiteParseHTML": false,
|
||||
"jqLiteWrapNode": false,
|
||||
"getBooleanAttrName": false,
|
||||
"getAliasedAttrName": false,
|
||||
"createEventHandler": false,
|
||||
|
||||
+187
-158
@@ -119,29 +119,9 @@ var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
|
||||
// This is used so that it's possible for internal tests to create mock ValidityStates.
|
||||
var VALIDITY_STATE_PROPERTY = 'validity';
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.lowercase
|
||||
* @module ng
|
||||
* @kind function
|
||||
*
|
||||
* @description Converts the specified string to lowercase.
|
||||
* @param {string} string String to be converted to lowercase.
|
||||
* @returns {string} Lowercased string.
|
||||
*/
|
||||
var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.uppercase
|
||||
* @module ng
|
||||
* @kind function
|
||||
*
|
||||
* @description Converts the specified string to uppercase.
|
||||
* @param {string} string String to be converted to uppercase.
|
||||
* @returns {string} Uppercased string.
|
||||
*/
|
||||
var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
|
||||
var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
|
||||
|
||||
|
||||
@@ -161,7 +141,7 @@ var manualUppercase = function(s) {
|
||||
|
||||
// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
|
||||
// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
|
||||
// with correct but slower alternatives.
|
||||
// with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
|
||||
if ('i' !== 'I'.toLowerCase()) {
|
||||
lowercase = manualLowercase;
|
||||
uppercase = manualUppercase;
|
||||
@@ -198,20 +178,25 @@ msie = document.documentMode;
|
||||
* String ...)
|
||||
*/
|
||||
function isArrayLike(obj) {
|
||||
if (obj == null || isWindow(obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// `null`, `undefined` and `window` are not array-like
|
||||
if (obj == null || isWindow(obj)) return false;
|
||||
|
||||
// arrays, strings and jQuery/jqLite objects are array like
|
||||
// * jqLite is either the jQuery or jqLite constructor function
|
||||
// * we have to check the existence of jqLite first as this method is called
|
||||
// via the forEach method when constructing the jqLite object in the first place
|
||||
if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
|
||||
|
||||
// Support: iOS 8.2 (not reproducible in simulator)
|
||||
// "length" in obj used to prevent JIT error (gh-11508)
|
||||
var length = "length" in Object(obj) && obj.length;
|
||||
|
||||
if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
|
||||
return true;
|
||||
}
|
||||
// NodeList objects (with `item` method) and
|
||||
// other objects with suitable length characteristics are array-like
|
||||
return isNumber(length) &&
|
||||
(length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
|
||||
|
||||
return isString(obj) || isArray(obj) || length === 0 ||
|
||||
typeof length === 'number' && length > 0 && (length - 1) in obj;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,7 +293,7 @@ function forEachSorted(obj, iterator, context) {
|
||||
* @returns {function(*, string)}
|
||||
*/
|
||||
function reverseParams(iteratorFn) {
|
||||
return function(value, key) { iteratorFn(key, value); };
|
||||
return function(value, key) {iteratorFn(key, value);};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,6 +341,10 @@ function baseExtend(dst, objs, deep) {
|
||||
dst[key] = new Date(src.valueOf());
|
||||
} else if (isRegExp(src)) {
|
||||
dst[key] = new RegExp(src);
|
||||
} else if (src.nodeName) {
|
||||
dst[key] = src.cloneNode(true);
|
||||
} else if (isElement(src)) {
|
||||
dst[key] = src.clone();
|
||||
} else {
|
||||
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
|
||||
baseExtend(dst[key], [src], true);
|
||||
@@ -471,7 +460,7 @@ identity.$inject = [];
|
||||
function valueFn(value) {return function() {return value;};}
|
||||
|
||||
function hasCustomToString(obj) {
|
||||
return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
|
||||
return isFunction(obj.toString) && obj.toString !== toString;
|
||||
}
|
||||
|
||||
|
||||
@@ -670,9 +659,13 @@ function isPromiseLike(obj) {
|
||||
}
|
||||
|
||||
|
||||
var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/;
|
||||
var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
|
||||
function isTypedArray(value) {
|
||||
return TYPED_ARRAY_REGEXP.test(toString.call(value));
|
||||
return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
|
||||
}
|
||||
|
||||
function isArrayBuffer(obj) {
|
||||
return toString.call(obj) === '[object ArrayBuffer]';
|
||||
}
|
||||
|
||||
|
||||
@@ -712,7 +705,7 @@ function isElement(node) {
|
||||
* @returns {object} in the form of {key1:true, key2:true, ...}
|
||||
*/
|
||||
function makeMap(str) {
|
||||
var obj = {}, items = str.split(","), i;
|
||||
var obj = {}, items = str.split(','), i;
|
||||
for (i = 0; i < items.length; i++) {
|
||||
obj[items[i]] = true;
|
||||
}
|
||||
@@ -794,100 +787,138 @@ function arrayRemove(array, value) {
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
function copy(source, destination, stackSource, stackDest) {
|
||||
if (isWindow(source) || isScope(source)) {
|
||||
throw ngMinErr('cpws',
|
||||
"Can't copy! Making copies of Window or Scope instances is not supported.");
|
||||
}
|
||||
if (isTypedArray(destination)) {
|
||||
throw ngMinErr('cpta',
|
||||
"Can't copy! TypedArray destination cannot be mutated.");
|
||||
}
|
||||
function copy(source, destination) {
|
||||
var stackSource = [];
|
||||
var stackDest = [];
|
||||
|
||||
if (!destination) {
|
||||
destination = source;
|
||||
if (isObject(source)) {
|
||||
var index;
|
||||
if (stackSource && (index = stackSource.indexOf(source)) !== -1) {
|
||||
return stackDest[index];
|
||||
}
|
||||
|
||||
// TypedArray, Date and RegExp have specific copy functionality and must be
|
||||
// pushed onto the stack before returning.
|
||||
// Array and other objects create the base object and recurse to copy child
|
||||
// objects. The array/object will be pushed onto the stack when recursed.
|
||||
if (isArray(source)) {
|
||||
return copy(source, [], stackSource, stackDest);
|
||||
} else if (isTypedArray(source)) {
|
||||
destination = new source.constructor(source);
|
||||
} else if (isDate(source)) {
|
||||
destination = new Date(source.getTime());
|
||||
} else if (isRegExp(source)) {
|
||||
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
|
||||
destination.lastIndex = source.lastIndex;
|
||||
} else if (isFunction(source.cloneNode)) {
|
||||
destination = source.cloneNode(true);
|
||||
} else {
|
||||
var emptyObject = Object.create(getPrototypeOf(source));
|
||||
return copy(source, emptyObject, stackSource, stackDest);
|
||||
}
|
||||
|
||||
if (stackDest) {
|
||||
stackSource.push(source);
|
||||
stackDest.push(destination);
|
||||
}
|
||||
if (destination) {
|
||||
if (isTypedArray(destination) || isArrayBuffer(destination)) {
|
||||
throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
|
||||
}
|
||||
} else {
|
||||
if (source === destination) throw ngMinErr('cpi',
|
||||
"Can't copy! Source and destination are identical.");
|
||||
|
||||
stackSource = stackSource || [];
|
||||
stackDest = stackDest || [];
|
||||
|
||||
if (isObject(source)) {
|
||||
stackSource.push(source);
|
||||
stackDest.push(destination);
|
||||
if (source === destination) {
|
||||
throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
|
||||
}
|
||||
|
||||
// Empty the destination object
|
||||
if (isArray(destination)) {
|
||||
destination.length = 0;
|
||||
} else {
|
||||
forEach(destination, function(value, key) {
|
||||
if (key !== '$$hashKey') {
|
||||
delete destination[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
stackSource.push(source);
|
||||
stackDest.push(destination);
|
||||
return copyRecurse(source, destination);
|
||||
}
|
||||
|
||||
return copyElement(source);
|
||||
|
||||
function copyRecurse(source, destination) {
|
||||
var h = destination.$$hashKey;
|
||||
var result, key;
|
||||
if (isArray(source)) {
|
||||
destination.length = 0;
|
||||
for (var i = 0; i < source.length; i++) {
|
||||
destination.push(copy(source[i], null, stackSource, stackDest));
|
||||
for (var i = 0, ii = source.length; i < ii; i++) {
|
||||
destination.push(copyElement(source[i]));
|
||||
}
|
||||
} else if (isBlankObject(source)) {
|
||||
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
|
||||
for (key in source) {
|
||||
destination[key] = copyElement(source[key]);
|
||||
}
|
||||
} else if (source && typeof source.hasOwnProperty === 'function') {
|
||||
// Slow path, which must rely on hasOwnProperty
|
||||
for (key in source) {
|
||||
if (source.hasOwnProperty(key)) {
|
||||
destination[key] = copyElement(source[key]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var h = destination.$$hashKey;
|
||||
if (isArray(destination)) {
|
||||
destination.length = 0;
|
||||
} else {
|
||||
forEach(destination, function(value, key) {
|
||||
delete destination[key];
|
||||
});
|
||||
}
|
||||
if (isBlankObject(source)) {
|
||||
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
|
||||
for (key in source) {
|
||||
destination[key] = copy(source[key], null, stackSource, stackDest);
|
||||
}
|
||||
} else if (source && typeof source.hasOwnProperty === 'function') {
|
||||
// Slow path, which must rely on hasOwnProperty
|
||||
for (key in source) {
|
||||
if (source.hasOwnProperty(key)) {
|
||||
destination[key] = copy(source[key], null, stackSource, stackDest);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Slowest path --- hasOwnProperty can't be called as a method
|
||||
for (key in source) {
|
||||
if (hasOwnProperty.call(source, key)) {
|
||||
destination[key] = copy(source[key], null, stackSource, stackDest);
|
||||
}
|
||||
// Slowest path --- hasOwnProperty can't be called as a method
|
||||
for (key in source) {
|
||||
if (hasOwnProperty.call(source, key)) {
|
||||
destination[key] = copyElement(source[key]);
|
||||
}
|
||||
}
|
||||
setHashKey(destination,h);
|
||||
}
|
||||
setHashKey(destination, h);
|
||||
return destination;
|
||||
}
|
||||
|
||||
function copyElement(source) {
|
||||
// Simple values
|
||||
if (!isObject(source)) {
|
||||
return source;
|
||||
}
|
||||
|
||||
// Already copied values
|
||||
var index = stackSource.indexOf(source);
|
||||
if (index !== -1) {
|
||||
return stackDest[index];
|
||||
}
|
||||
|
||||
if (isWindow(source) || isScope(source)) {
|
||||
throw ngMinErr('cpws',
|
||||
"Can't copy! Making copies of Window or Scope instances is not supported.");
|
||||
}
|
||||
|
||||
var needsRecurse = false;
|
||||
var destination = copyType(source);
|
||||
|
||||
if (destination === undefined) {
|
||||
destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
|
||||
needsRecurse = true;
|
||||
}
|
||||
|
||||
stackSource.push(source);
|
||||
stackDest.push(destination);
|
||||
|
||||
return needsRecurse
|
||||
? copyRecurse(source, destination)
|
||||
: destination;
|
||||
}
|
||||
|
||||
function copyType(source) {
|
||||
switch (toString.call(source)) {
|
||||
case '[object Int8Array]':
|
||||
case '[object Int16Array]':
|
||||
case '[object Int32Array]':
|
||||
case '[object Float32Array]':
|
||||
case '[object Float64Array]':
|
||||
case '[object Uint8Array]':
|
||||
case '[object Uint8ClampedArray]':
|
||||
case '[object Uint16Array]':
|
||||
case '[object Uint32Array]':
|
||||
return new source.constructor(copyElement(source.buffer));
|
||||
|
||||
case '[object ArrayBuffer]':
|
||||
//Support: IE10
|
||||
if (!source.slice) {
|
||||
var copied = new ArrayBuffer(source.byteLength);
|
||||
new Uint8Array(copied).set(new Uint8Array(source));
|
||||
return copied;
|
||||
}
|
||||
return source.slice(0);
|
||||
|
||||
case '[object Boolean]':
|
||||
case '[object Number]':
|
||||
case '[object String]':
|
||||
case '[object Date]':
|
||||
return new source.constructor(source.valueOf());
|
||||
|
||||
case '[object RegExp]':
|
||||
var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
|
||||
re.lastIndex = source.lastIndex;
|
||||
return re;
|
||||
}
|
||||
|
||||
if (isFunction(source.cloneNode)) {
|
||||
return source.cloneNode(true);
|
||||
}
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -950,38 +981,37 @@ function equals(o1, o2) {
|
||||
if (o1 === null || o2 === null) return false;
|
||||
if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
|
||||
var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
|
||||
if (t1 == t2) {
|
||||
if (t1 == 'object') {
|
||||
if (isArray(o1)) {
|
||||
if (!isArray(o2)) return false;
|
||||
if ((length = o1.length) == o2.length) {
|
||||
for (key = 0; key < length; key++) {
|
||||
if (!equals(o1[key], o2[key])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if (isDate(o1)) {
|
||||
if (!isDate(o2)) return false;
|
||||
return equals(o1.getTime(), o2.getTime());
|
||||
} else if (isRegExp(o1)) {
|
||||
return isRegExp(o2) ? o1.toString() == o2.toString() : false;
|
||||
} else {
|
||||
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
|
||||
isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
|
||||
keySet = createMap();
|
||||
for (key in o1) {
|
||||
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
|
||||
if (t1 == t2 && t1 == 'object') {
|
||||
if (isArray(o1)) {
|
||||
if (!isArray(o2)) return false;
|
||||
if ((length = o1.length) == o2.length) {
|
||||
for (key = 0; key < length; key++) {
|
||||
if (!equals(o1[key], o2[key])) return false;
|
||||
keySet[key] = true;
|
||||
}
|
||||
for (key in o2) {
|
||||
if (!(key in keySet) &&
|
||||
key.charAt(0) !== '$' &&
|
||||
isDefined(o2[key]) &&
|
||||
!isFunction(o2[key])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if (isDate(o1)) {
|
||||
if (!isDate(o2)) return false;
|
||||
return equals(o1.getTime(), o2.getTime());
|
||||
} else if (isRegExp(o1)) {
|
||||
if (!isRegExp(o2)) return false;
|
||||
return o1.toString() == o2.toString();
|
||||
} else {
|
||||
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
|
||||
isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
|
||||
keySet = createMap();
|
||||
for (key in o1) {
|
||||
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
|
||||
if (!equals(o1[key], o2[key])) return false;
|
||||
keySet[key] = true;
|
||||
}
|
||||
for (key in o2) {
|
||||
if (!(key in keySet) &&
|
||||
key.charAt(0) !== '$' &&
|
||||
isDefined(o2[key]) &&
|
||||
!isFunction(o2[key])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -1158,7 +1188,7 @@ function toJsonReplacer(key, value) {
|
||||
* @returns {string|undefined} JSON-ified string representing `obj`.
|
||||
*/
|
||||
function toJson(obj, pretty) {
|
||||
if (typeof obj === 'undefined') return undefined;
|
||||
if (isUndefined(obj)) return undefined;
|
||||
if (!isNumber(pretty)) {
|
||||
pretty = pretty ? 2 : null;
|
||||
}
|
||||
@@ -1185,7 +1215,10 @@ function fromJson(json) {
|
||||
}
|
||||
|
||||
|
||||
var ALL_COLONS = /:/g;
|
||||
function timezoneToOffset(timezone, fallback) {
|
||||
// IE/Edge do not "understand" colon (`:`) in timezone
|
||||
timezone = timezone.replace(ALL_COLONS, '');
|
||||
var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
|
||||
return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
|
||||
}
|
||||
@@ -1200,8 +1233,9 @@ function addDateMinutes(date, minutes) {
|
||||
|
||||
function convertTimezoneToLocal(date, timezone, reverse) {
|
||||
reverse = reverse ? -1 : 1;
|
||||
var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
|
||||
return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
|
||||
var dateTimezoneOffset = date.getTimezoneOffset();
|
||||
var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
|
||||
return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
|
||||
}
|
||||
|
||||
|
||||
@@ -1220,7 +1254,7 @@ function startingTag(element) {
|
||||
return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
|
||||
elemHtml.
|
||||
match(/^(<[^>]+>)/)[1].
|
||||
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
|
||||
replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
|
||||
} catch (e) {
|
||||
return lowercase(elemHtml);
|
||||
}
|
||||
@@ -1663,7 +1697,6 @@ function snake_case(name, separator) {
|
||||
}
|
||||
|
||||
var bindJQueryFired = false;
|
||||
var skipDestroyOnNextJQueryCleanData;
|
||||
function bindJQuery() {
|
||||
var originalCleanData;
|
||||
|
||||
@@ -1697,15 +1730,11 @@ function bindJQuery() {
|
||||
originalCleanData = jQuery.cleanData;
|
||||
jQuery.cleanData = function(elems) {
|
||||
var events;
|
||||
if (!skipDestroyOnNextJQueryCleanData) {
|
||||
for (var i = 0, elem; (elem = elems[i]) != null; i++) {
|
||||
events = jQuery._data(elem, "events");
|
||||
if (events && events.$destroy) {
|
||||
jQuery(elem).triggerHandler('$destroy');
|
||||
}
|
||||
for (var i = 0, elem; (elem = elems[i]) != null; i++) {
|
||||
events = jQuery._data(elem, "events");
|
||||
if (events && events.$destroy) {
|
||||
jQuery(elem).triggerHandler('$destroy');
|
||||
}
|
||||
} else {
|
||||
skipDestroyOnNextJQueryCleanData = false;
|
||||
}
|
||||
originalCleanData(elems);
|
||||
};
|
||||
|
||||
@@ -56,11 +56,14 @@
|
||||
$AnchorScrollProvider,
|
||||
$AnimateProvider,
|
||||
$CoreAnimateCssProvider,
|
||||
$$CoreAnimateJsProvider,
|
||||
$$CoreAnimateQueueProvider,
|
||||
$$CoreAnimateRunnerProvider,
|
||||
$$AnimateRunnerFactoryProvider,
|
||||
$$AnimateAsyncRunFactoryProvider,
|
||||
$BrowserProvider,
|
||||
$CacheFactoryProvider,
|
||||
$ControllerProvider,
|
||||
$DateProvider,
|
||||
$DocumentProvider,
|
||||
$ExceptionHandlerProvider,
|
||||
$FilterProvider,
|
||||
@@ -72,6 +75,7 @@
|
||||
$HttpParamSerializerProvider,
|
||||
$HttpParamSerializerJQLikeProvider,
|
||||
$HttpBackendProvider,
|
||||
$xhrFactoryProvider,
|
||||
$LocationProvider,
|
||||
$LogProvider,
|
||||
$ParseProvider,
|
||||
@@ -215,8 +219,10 @@ function publishExternalAPI(angular) {
|
||||
$anchorScroll: $AnchorScrollProvider,
|
||||
$animate: $AnimateProvider,
|
||||
$animateCss: $CoreAnimateCssProvider,
|
||||
$$animateJs: $$CoreAnimateJsProvider,
|
||||
$$animateQueue: $$CoreAnimateQueueProvider,
|
||||
$$AnimateRunner: $$CoreAnimateRunnerProvider,
|
||||
$$AnimateRunner: $$AnimateRunnerFactoryProvider,
|
||||
$$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
|
||||
$browser: $BrowserProvider,
|
||||
$cacheFactory: $CacheFactoryProvider,
|
||||
$controller: $ControllerProvider,
|
||||
@@ -230,6 +236,7 @@ function publishExternalAPI(angular) {
|
||||
$httpParamSerializer: $HttpParamSerializerProvider,
|
||||
$httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
|
||||
$httpBackend: $HttpBackendProvider,
|
||||
$xhrFactory: $xhrFactoryProvider,
|
||||
$location: $LocationProvider,
|
||||
$log: $LogProvider,
|
||||
$parse: $ParseProvider,
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, document, undefined) {
|
||||
|
||||
+79
-39
@@ -62,17 +62,23 @@
|
||||
* Implicit module which gets automatically added to each {@link auto.$injector $injector}.
|
||||
*/
|
||||
|
||||
var ARROW_ARG = /^([^\(]+?)=>/;
|
||||
var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
|
||||
var FN_ARG_SPLIT = /,/;
|
||||
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
|
||||
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
||||
var $injectorMinErr = minErr('$injector');
|
||||
|
||||
function extractArgs(fn) {
|
||||
var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
|
||||
args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
|
||||
return args;
|
||||
}
|
||||
|
||||
function anonFn(fn) {
|
||||
// For anonymous functions, showing at the very least the function signature can help in
|
||||
// debugging.
|
||||
var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
|
||||
args = fnText.match(FN_ARGS);
|
||||
var args = extractArgs(fn);
|
||||
if (args) {
|
||||
return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
|
||||
}
|
||||
@@ -81,7 +87,6 @@ function anonFn(fn) {
|
||||
|
||||
function annotate(fn, strictDi, name) {
|
||||
var $inject,
|
||||
fnText,
|
||||
argDecl,
|
||||
last;
|
||||
|
||||
@@ -96,8 +101,7 @@ function annotate(fn, strictDi, name) {
|
||||
throw $injectorMinErr('strictdi',
|
||||
'{0} is not using explicit annotation and cannot be invoked in strict mode', name);
|
||||
}
|
||||
fnText = fn.toString().replace(STRIP_COMMENTS, '');
|
||||
argDecl = fnText.match(FN_ARGS);
|
||||
argDecl = extractArgs(fn);
|
||||
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
|
||||
arg.replace(FN_ARG, function(all, underscore, name) {
|
||||
$inject.push(name);
|
||||
@@ -487,8 +491,20 @@ function annotate(fn, strictDi, name) {
|
||||
*
|
||||
* Register a **service constructor**, which will be invoked with `new` to create the service
|
||||
* instance.
|
||||
* This is short for registering a service where its provider's `$get` property is the service
|
||||
* constructor function that will be used to instantiate the service instance.
|
||||
* This is short for registering a service where its provider's `$get` property is a factory
|
||||
* function that returns an instance instantiated by the injector from the service constructor
|
||||
* function.
|
||||
*
|
||||
* Internally it looks a bit like this:
|
||||
*
|
||||
* ```
|
||||
* {
|
||||
* $get: function() {
|
||||
* return $injector.instantiate(constructor);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* You should use {@link auto.$provide#service $provide.service(class)} if you define your service
|
||||
* as a type/class.
|
||||
@@ -589,7 +605,7 @@ function annotate(fn, strictDi, name) {
|
||||
* @description
|
||||
*
|
||||
* Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
|
||||
* intercepts the creation of a service, allowing it to override or modify the behaviour of the
|
||||
* intercepts the creation of a service, allowing it to override or modify the behavior of the
|
||||
* service. The object returned by the decorator may be the original service, or a new service
|
||||
* object which replaces or wraps and delegates to the original service.
|
||||
*
|
||||
@@ -638,14 +654,19 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
|
||||
})),
|
||||
instanceCache = {},
|
||||
instanceInjector = (instanceCache.$injector =
|
||||
protoInstanceInjector =
|
||||
createInternalInjector(instanceCache, function(serviceName, caller) {
|
||||
var provider = providerInjector.get(serviceName + providerSuffix, caller);
|
||||
return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
|
||||
}));
|
||||
return instanceInjector.invoke(
|
||||
provider.$get, provider, undefined, serviceName);
|
||||
}),
|
||||
instanceInjector = protoInstanceInjector;
|
||||
|
||||
|
||||
forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
|
||||
providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
|
||||
var runBlocks = loadModules(modulesToLoad);
|
||||
instanceInjector = protoInstanceInjector.get('$injector');
|
||||
instanceInjector.strictDi = strictDi;
|
||||
forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
|
||||
|
||||
return instanceInjector;
|
||||
|
||||
@@ -795,48 +816,67 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function injectionArgs(fn, locals, serviceName) {
|
||||
var args = [],
|
||||
$inject = createInjector.$$annotate(fn, strictDi, serviceName);
|
||||
|
||||
for (var i = 0, length = $inject.length; i < length; i++) {
|
||||
var key = $inject[i];
|
||||
if (typeof key !== 'string') {
|
||||
throw $injectorMinErr('itkn',
|
||||
'Incorrect injection token! Expected service name as string, got {0}', key);
|
||||
}
|
||||
args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
|
||||
getService(key, serviceName));
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
function isClass(func) {
|
||||
// IE 9-11 do not support classes and IE9 leaks with the code below.
|
||||
if (msie <= 11) {
|
||||
return false;
|
||||
}
|
||||
// Workaround for MS Edge.
|
||||
// Check https://connect.microsoft.com/IE/Feedback/Details/2211653
|
||||
return typeof func === 'function'
|
||||
&& /^(?:class\s|constructor\()/.test(Function.prototype.toString.call(func));
|
||||
}
|
||||
|
||||
function invoke(fn, self, locals, serviceName) {
|
||||
if (typeof locals === 'string') {
|
||||
serviceName = locals;
|
||||
locals = null;
|
||||
}
|
||||
|
||||
var args = [],
|
||||
$inject = createInjector.$$annotate(fn, strictDi, serviceName),
|
||||
length, i,
|
||||
key;
|
||||
|
||||
for (i = 0, length = $inject.length; i < length; i++) {
|
||||
key = $inject[i];
|
||||
if (typeof key !== 'string') {
|
||||
throw $injectorMinErr('itkn',
|
||||
'Incorrect injection token! Expected service name as string, got {0}', key);
|
||||
}
|
||||
args.push(
|
||||
locals && locals.hasOwnProperty(key)
|
||||
? locals[key]
|
||||
: getService(key, serviceName)
|
||||
);
|
||||
}
|
||||
var args = injectionArgs(fn, locals, serviceName);
|
||||
if (isArray(fn)) {
|
||||
fn = fn[length];
|
||||
fn = fn[fn.length - 1];
|
||||
}
|
||||
|
||||
// http://jsperf.com/angularjs-invoke-apply-vs-switch
|
||||
// #5388
|
||||
return fn.apply(self, args);
|
||||
if (!isClass(fn)) {
|
||||
// http://jsperf.com/angularjs-invoke-apply-vs-switch
|
||||
// #5388
|
||||
return fn.apply(self, args);
|
||||
} else {
|
||||
args.unshift(null);
|
||||
return new (Function.prototype.bind.apply(fn, args))();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function instantiate(Type, locals, serviceName) {
|
||||
// Check if Type is annotated and use just the given function at n-1 as parameter
|
||||
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
|
||||
// Object creation: http://jsperf.com/create-constructor/2
|
||||
var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
|
||||
var returnedValue = invoke(Type, instance, locals, serviceName);
|
||||
|
||||
return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
|
||||
var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
|
||||
var args = injectionArgs(Type, locals, serviceName);
|
||||
// Empty object at position 0 is ignored for invocation with `new`, but required.
|
||||
args.unshift(null);
|
||||
return new (Function.prototype.bind.apply(ctor, args))();
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
invoke: invoke,
|
||||
instantiate: instantiate,
|
||||
|
||||
+95
-50
@@ -33,16 +33,22 @@
|
||||
*
|
||||
* If jQuery is available, `angular.element` is an alias for the
|
||||
* [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
|
||||
* delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
|
||||
* delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
|
||||
*
|
||||
* <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
|
||||
* Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
|
||||
* commonly needed functionality with the goal of having a very small footprint.</div>
|
||||
* jqLite is a tiny, API-compatible subset of jQuery that allows
|
||||
* Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
|
||||
* commonly needed functionality with the goal of having a very small footprint.
|
||||
*
|
||||
* To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
|
||||
* To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
|
||||
* {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
|
||||
* specific version of jQuery if multiple versions exist on the page.
|
||||
*
|
||||
* <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
|
||||
* jqLite; they are never raw DOM references.</div>
|
||||
* <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
|
||||
* jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
|
||||
*
|
||||
* <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
|
||||
* by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
|
||||
* or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
|
||||
*
|
||||
* ## Angular's jqLite
|
||||
* jqLite provides only the following jQuery methods:
|
||||
@@ -55,7 +61,8 @@
|
||||
* - [`children()`](http://api.jquery.com/children/) - Does not support selectors
|
||||
* - [`clone()`](http://api.jquery.com/clone/)
|
||||
* - [`contents()`](http://api.jquery.com/contents/)
|
||||
* - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
|
||||
* - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
|
||||
* As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
|
||||
* - [`data()`](http://api.jquery.com/data/)
|
||||
* - [`detach()`](http://api.jquery.com/detach/)
|
||||
* - [`empty()`](http://api.jquery.com/empty/)
|
||||
@@ -189,6 +196,12 @@ function jqLiteHasData(node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function jqLiteCleanData(nodes) {
|
||||
for (var i = 0, ii = nodes.length; i < ii; i++) {
|
||||
jqLiteRemoveData(nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function jqLiteBuildFragment(html, context) {
|
||||
var tmp, tag, wrap,
|
||||
fragment = context.createDocumentFragment(),
|
||||
@@ -241,6 +254,24 @@ function jqLiteParseHTML(html, context) {
|
||||
return [];
|
||||
}
|
||||
|
||||
function jqLiteWrapNode(node, wrapper) {
|
||||
var parent = node.parentNode;
|
||||
|
||||
if (parent) {
|
||||
parent.replaceChild(wrapper, node);
|
||||
}
|
||||
|
||||
wrapper.appendChild(node);
|
||||
}
|
||||
|
||||
|
||||
// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
|
||||
var jqLiteContains = Node.prototype.contains || function(arg) {
|
||||
// jshint bitwise: false
|
||||
return !!(this.compareDocumentPosition(arg) & 16);
|
||||
// jshint bitwise: true
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////
|
||||
function JQLite(element) {
|
||||
if (element instanceof JQLite) {
|
||||
@@ -299,17 +330,23 @@ function jqLiteOff(element, type, fn, unsupported) {
|
||||
delete events[type];
|
||||
}
|
||||
} else {
|
||||
forEach(type.split(' '), function(type) {
|
||||
if (isDefined(fn)) {
|
||||
var listenerFns = events[type];
|
||||
arrayRemove(listenerFns || [], fn);
|
||||
if (listenerFns && listenerFns.length > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
removeEventListenerFn(element, type, handle);
|
||||
delete events[type];
|
||||
var removeHandler = function(type) {
|
||||
var listenerFns = events[type];
|
||||
if (isDefined(fn)) {
|
||||
arrayRemove(listenerFns || [], fn);
|
||||
}
|
||||
if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
|
||||
removeEventListenerFn(element, type, handle);
|
||||
delete events[type];
|
||||
}
|
||||
};
|
||||
|
||||
forEach(type.split(' '), function(type) {
|
||||
removeHandler(type);
|
||||
if (MOUSE_EVENT_MAP[type]) {
|
||||
removeHandler(MOUSE_EVENT_MAP[type]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -477,7 +514,7 @@ function jqLiteRemove(element, keepData) {
|
||||
function jqLiteDocumentLoaded(action, win) {
|
||||
win = win || window;
|
||||
if (win.document.readyState === 'complete') {
|
||||
// Force the action to be run async for consistent behaviour
|
||||
// Force the action to be run async for consistent behavior
|
||||
// from the action's point of view
|
||||
// i.e. it will definitely not be in a $apply
|
||||
win.setTimeout(action);
|
||||
@@ -563,7 +600,8 @@ function getAliasedAttrName(name) {
|
||||
forEach({
|
||||
data: jqLiteData,
|
||||
removeData: jqLiteRemoveData,
|
||||
hasData: jqLiteHasData
|
||||
hasData: jqLiteHasData,
|
||||
cleanData: jqLiteCleanData
|
||||
}, function(fn, name) {
|
||||
JQLite[name] = fn;
|
||||
});
|
||||
@@ -764,6 +802,9 @@ function createEventHandler(element, events) {
|
||||
return event.immediatePropagationStopped === true;
|
||||
};
|
||||
|
||||
// Some events have special handlers that wrap the real handler
|
||||
var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
|
||||
|
||||
// Copy event handlers in case event handlers array is modified during execution.
|
||||
if ((eventFnsLength > 1)) {
|
||||
eventFns = shallowCopy(eventFns);
|
||||
@@ -771,7 +812,7 @@ function createEventHandler(element, events) {
|
||||
|
||||
for (var i = 0; i < eventFnsLength; i++) {
|
||||
if (!event.isImmediatePropagationStopped()) {
|
||||
eventFns[i].call(element, event);
|
||||
handlerWrapper(element, event, eventFns[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -782,6 +823,22 @@ function createEventHandler(element, events) {
|
||||
return eventHandler;
|
||||
}
|
||||
|
||||
function defaultHandlerWrapper(element, event, handler) {
|
||||
handler.call(element, event);
|
||||
}
|
||||
|
||||
function specialMouseHandlerWrapper(target, event, handler) {
|
||||
// Refer to jQuery's implementation of mouseenter & mouseleave
|
||||
// Read about mouseenter and mouseleave:
|
||||
// http://www.quirksmode.org/js/events_mouse.html#link8
|
||||
var related = event.relatedTarget;
|
||||
// For mousenter/leave call the handler if related is outside the target.
|
||||
// NB: No relatedTarget if the mouse left/entered the browser window
|
||||
if (!related || (related !== target && !jqLiteContains.call(target, related))) {
|
||||
handler.call(target, event);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Functions iterating traversal.
|
||||
// These functions chain results into a single
|
||||
@@ -810,35 +867,28 @@ forEach({
|
||||
var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
|
||||
var i = types.length;
|
||||
|
||||
while (i--) {
|
||||
type = types[i];
|
||||
var addHandler = function(type, specialHandlerWrapper, noEventListener) {
|
||||
var eventFns = events[type];
|
||||
|
||||
if (!eventFns) {
|
||||
events[type] = [];
|
||||
|
||||
if (type === 'mouseenter' || type === 'mouseleave') {
|
||||
// Refer to jQuery's implementation of mouseenter & mouseleave
|
||||
// Read about mouseenter and mouseleave:
|
||||
// http://www.quirksmode.org/js/events_mouse.html#link8
|
||||
|
||||
jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
|
||||
var target = this, related = event.relatedTarget;
|
||||
// For mousenter/leave call the handler if related is outside the target.
|
||||
// NB: No relatedTarget if the mouse left/entered the browser window
|
||||
if (!related || (related !== target && !target.contains(related))) {
|
||||
handle(event, type);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
if (type !== '$destroy') {
|
||||
addEventListenerFn(element, type, handle);
|
||||
}
|
||||
eventFns = events[type] = [];
|
||||
eventFns.specialHandlerWrapper = specialHandlerWrapper;
|
||||
if (type !== '$destroy' && !noEventListener) {
|
||||
addEventListenerFn(element, type, handle);
|
||||
}
|
||||
eventFns = events[type];
|
||||
}
|
||||
|
||||
eventFns.push(fn);
|
||||
};
|
||||
|
||||
while (i--) {
|
||||
type = types[i];
|
||||
if (MOUSE_EVENT_MAP[type]) {
|
||||
addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
|
||||
addHandler(type, undefined, true);
|
||||
} else {
|
||||
addHandler(type);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -906,12 +956,7 @@ forEach({
|
||||
},
|
||||
|
||||
wrap: function(element, wrapNode) {
|
||||
wrapNode = jqLite(wrapNode).eq(0).clone()[0];
|
||||
var parent = element.parentNode;
|
||||
if (parent) {
|
||||
parent.replaceChild(wrapNode, element);
|
||||
}
|
||||
wrapNode.appendChild(element);
|
||||
jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
|
||||
},
|
||||
|
||||
remove: jqLiteRemove,
|
||||
|
||||
+15
-2
@@ -76,7 +76,7 @@ function setupModuleLoader(window) {
|
||||
* unspecified then the module is being retrieved for further configuration.
|
||||
* @param {Function=} configFn Optional configuration function for the module. Same as
|
||||
* {@link angular.Module#config Module#config()}.
|
||||
* @returns {module} new module with the {@link angular.Module} api.
|
||||
* @returns {angular.Module} new module with the {@link angular.Module} api.
|
||||
*/
|
||||
return function module(name, requires, configFn) {
|
||||
var assertNotHasOwnProperty = function(name, context) {
|
||||
@@ -188,7 +188,7 @@ function setupModuleLoader(window) {
|
||||
* @param {string} name constant name
|
||||
* @param {*} object Constant value.
|
||||
* @description
|
||||
* Because the constant are fixed, they get applied before other provide methods.
|
||||
* Because the constants are fixed, they get applied before other provide methods.
|
||||
* See {@link auto.$provide#constant $provide.constant()}.
|
||||
*/
|
||||
constant: invokeLater('$provide', 'constant', 'unshift'),
|
||||
@@ -282,6 +282,19 @@ function setupModuleLoader(window) {
|
||||
*/
|
||||
directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#component
|
||||
* @module ng
|
||||
* @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
|
||||
* @param {Object} options Component definition object (a simplified
|
||||
* {@link ng.$compile#directive-definition-object directive definition object})
|
||||
*
|
||||
* @description
|
||||
* See {@link ng.$compileProvider#component $compileProvider.component()}.
|
||||
*/
|
||||
component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#config
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
'use strict';
|
||||
(function() {
|
||||
function isFunction(value) {return typeof value === 'function';};
|
||||
function isFunction(value) {return typeof value === 'function';};
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
|
||||
@@ -41,7 +41,7 @@ function $AnchorScrollProvider() {
|
||||
* When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
|
||||
* current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
|
||||
* in the
|
||||
* [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
|
||||
* [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
|
||||
*
|
||||
* It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
|
||||
* match any anchor whenever it changes. This can be disabled by calling
|
||||
|
||||
+29
-30
@@ -53,27 +53,8 @@ function prepareAnimateOptions(options) {
|
||||
: {};
|
||||
}
|
||||
|
||||
var $$CoreAnimateRunnerProvider = function() {
|
||||
this.$get = ['$q', '$$rAF', function($q, $$rAF) {
|
||||
function AnimateRunner() {}
|
||||
AnimateRunner.all = noop;
|
||||
AnimateRunner.chain = noop;
|
||||
AnimateRunner.prototype = {
|
||||
end: noop,
|
||||
cancel: noop,
|
||||
resume: noop,
|
||||
pause: noop,
|
||||
complete: noop,
|
||||
then: function(pass, fail) {
|
||||
return $q(function(resolve) {
|
||||
$$rAF(function() {
|
||||
resolve();
|
||||
});
|
||||
}).then(pass, fail);
|
||||
}
|
||||
};
|
||||
return AnimateRunner;
|
||||
}];
|
||||
var $$CoreAnimateJsProvider = function() {
|
||||
this.$get = function() {};
|
||||
};
|
||||
|
||||
// this is prefixed with Core since it conflicts with
|
||||
@@ -101,7 +82,12 @@ var $$CoreAnimateQueueProvider = function() {
|
||||
addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
|
||||
}
|
||||
|
||||
return new $$AnimateRunner(); // jshint ignore:line
|
||||
var runner = new $$AnimateRunner(); // jshint ignore:line
|
||||
|
||||
// since there are no animations to run the runner needs to be
|
||||
// notified that the animation call is complete.
|
||||
runner.complete();
|
||||
return runner;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -285,7 +271,7 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* when an animation is detected (and animations are enabled), $animate will do the heavy lifting
|
||||
* to ensure that animation runs with the triggered DOM operation.
|
||||
*
|
||||
* By default $animate doesn't trigger an animations. This is because the `ngAnimate` module isn't
|
||||
* By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
|
||||
* included and only when it is active then the animation hooks that `$animate` triggers will be
|
||||
* functional. Once active then all structural `ng-` directives will trigger animations as they perform
|
||||
* their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
|
||||
@@ -343,8 +329,8 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* // remove all the animation event listeners listening for `enter` on the given element and its children
|
||||
* $animate.off('enter', container);
|
||||
*
|
||||
* // remove the event listener function provided by `listenerFn` that is set
|
||||
* // to listen for `enter` on the given `element` as well as its children
|
||||
* // remove the event listener function provided by `callback` that is set
|
||||
* // to listen for `enter` on the given `container` as well as its children
|
||||
* $animate.off('enter', container, callback);
|
||||
* ```
|
||||
*
|
||||
@@ -566,17 +552,30 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* @kind function
|
||||
*
|
||||
* @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
|
||||
* If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
|
||||
* on the provided styles. For example, if a transition animation is set for the given className then the provided from and
|
||||
* to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
|
||||
* will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
|
||||
* If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
|
||||
* on the provided styles. For example, if a transition animation is set for the given classNamem, then the provided `from` and
|
||||
* `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
|
||||
* style in `to`, the style in `from` is applied immediately, and no animation is run.
|
||||
* If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
|
||||
* method (or as part of the `options` parameter):
|
||||
*
|
||||
* ```js
|
||||
* ngModule.animation('.my-inline-animation', function() {
|
||||
* return {
|
||||
* animate : function(element, from, to, done, options) {
|
||||
* //animation
|
||||
* done();
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param {DOMElement} element the element which the CSS styles will be applied to
|
||||
* @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
|
||||
* @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
|
||||
* @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
|
||||
* this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
|
||||
* (Note that if no animation is detected then this value will not be appplied to the element.)
|
||||
* (Note that if no animation is detected then this value will not be applied to the element.)
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
|
||||
+21
-32
@@ -12,43 +12,32 @@
|
||||
* Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
|
||||
*/
|
||||
var $CoreAnimateCssProvider = function() {
|
||||
this.$get = ['$$rAF', '$q', function($$rAF, $q) {
|
||||
this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
|
||||
|
||||
var RAFPromise = function() {};
|
||||
RAFPromise.prototype = {
|
||||
done: function(cancel) {
|
||||
this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
|
||||
},
|
||||
end: function() {
|
||||
this.done();
|
||||
},
|
||||
cancel: function() {
|
||||
this.done(true);
|
||||
},
|
||||
getPromise: function() {
|
||||
if (!this.defer) {
|
||||
this.defer = $q.defer();
|
||||
}
|
||||
return this.defer.promise;
|
||||
},
|
||||
then: function(f1,f2) {
|
||||
return this.getPromise().then(f1,f2);
|
||||
},
|
||||
'catch': function(f1) {
|
||||
return this.getPromise()['catch'](f1);
|
||||
},
|
||||
'finally': function(f1) {
|
||||
return this.getPromise()['finally'](f1);
|
||||
return function(element, initialOptions) {
|
||||
// all of the animation functions should create
|
||||
// a copy of the options data, however, if a
|
||||
// parent service has already created a copy then
|
||||
// we should stick to using that
|
||||
var options = initialOptions || {};
|
||||
if (!options.$$prepared) {
|
||||
options = copy(options);
|
||||
}
|
||||
|
||||
// there is no point in applying the styles since
|
||||
// there is no animation that goes on at all in
|
||||
// this version of $animateCss.
|
||||
if (options.cleanupStyles) {
|
||||
options.from = options.to = null;
|
||||
}
|
||||
};
|
||||
|
||||
return function(element, options) {
|
||||
if (options.from) {
|
||||
element.css(options.from);
|
||||
options.from = null;
|
||||
}
|
||||
|
||||
var closed, runner = new RAFPromise();
|
||||
/* jshint newcap: false */
|
||||
var closed, runner = new $$AnimateRunner();
|
||||
return {
|
||||
start: run,
|
||||
end: run
|
||||
@@ -56,16 +45,16 @@ var $CoreAnimateCssProvider = function() {
|
||||
|
||||
function run() {
|
||||
$$rAF(function() {
|
||||
close();
|
||||
applyAnimationContents();
|
||||
if (!closed) {
|
||||
runner.done();
|
||||
runner.complete();
|
||||
}
|
||||
closed = true;
|
||||
});
|
||||
return runner;
|
||||
}
|
||||
|
||||
function close() {
|
||||
function applyAnimationContents() {
|
||||
if (options.addClass) {
|
||||
element.addClass(options.addClass);
|
||||
options.addClass = null;
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
'use strict';
|
||||
|
||||
var $$AnimateAsyncRunFactoryProvider = function() {
|
||||
this.$get = ['$$rAF', function($$rAF) {
|
||||
var waitQueue = [];
|
||||
|
||||
function waitForTick(fn) {
|
||||
waitQueue.push(fn);
|
||||
if (waitQueue.length > 1) return;
|
||||
$$rAF(function() {
|
||||
for (var i = 0; i < waitQueue.length; i++) {
|
||||
waitQueue[i]();
|
||||
}
|
||||
waitQueue = [];
|
||||
});
|
||||
}
|
||||
|
||||
return function() {
|
||||
var passed = false;
|
||||
waitForTick(function() {
|
||||
passed = true;
|
||||
});
|
||||
return function(callback) {
|
||||
passed ? callback() : waitForTick(callback);
|
||||
};
|
||||
};
|
||||
}];
|
||||
};
|
||||
|
||||
var $$AnimateRunnerFactoryProvider = function() {
|
||||
this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
|
||||
function($q, $sniffer, $$animateAsyncRun, $document, $timeout) {
|
||||
|
||||
var INITIAL_STATE = 0;
|
||||
var DONE_PENDING_STATE = 1;
|
||||
var DONE_COMPLETE_STATE = 2;
|
||||
|
||||
AnimateRunner.chain = function(chain, callback) {
|
||||
var index = 0;
|
||||
|
||||
next();
|
||||
function next() {
|
||||
if (index === chain.length) {
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
|
||||
chain[index](function(response) {
|
||||
if (response === false) {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
next();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
AnimateRunner.all = function(runners, callback) {
|
||||
var count = 0;
|
||||
var status = true;
|
||||
forEach(runners, function(runner) {
|
||||
runner.done(onProgress);
|
||||
});
|
||||
|
||||
function onProgress(response) {
|
||||
status = status && response;
|
||||
if (++count === runners.length) {
|
||||
callback(status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function AnimateRunner(host) {
|
||||
this.setHost(host);
|
||||
|
||||
var rafTick = $$animateAsyncRun();
|
||||
var timeoutTick = function(fn) {
|
||||
$timeout(fn, 0, false);
|
||||
};
|
||||
|
||||
this._doneCallbacks = [];
|
||||
this._tick = function(fn) {
|
||||
var doc = $document[0];
|
||||
|
||||
// the document may not be ready or attached
|
||||
// to the module for some internal tests
|
||||
if (doc && doc.hidden) {
|
||||
timeoutTick(fn);
|
||||
} else {
|
||||
rafTick(fn);
|
||||
}
|
||||
};
|
||||
this._state = 0;
|
||||
}
|
||||
|
||||
AnimateRunner.prototype = {
|
||||
setHost: function(host) {
|
||||
this.host = host || {};
|
||||
},
|
||||
|
||||
done: function(fn) {
|
||||
if (this._state === DONE_COMPLETE_STATE) {
|
||||
fn();
|
||||
} else {
|
||||
this._doneCallbacks.push(fn);
|
||||
}
|
||||
},
|
||||
|
||||
progress: noop,
|
||||
|
||||
getPromise: function() {
|
||||
if (!this.promise) {
|
||||
var self = this;
|
||||
this.promise = $q(function(resolve, reject) {
|
||||
self.done(function(status) {
|
||||
status === false ? reject() : resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
return this.promise;
|
||||
},
|
||||
|
||||
then: function(resolveHandler, rejectHandler) {
|
||||
return this.getPromise().then(resolveHandler, rejectHandler);
|
||||
},
|
||||
|
||||
'catch': function(handler) {
|
||||
return this.getPromise()['catch'](handler);
|
||||
},
|
||||
|
||||
'finally': function(handler) {
|
||||
return this.getPromise()['finally'](handler);
|
||||
},
|
||||
|
||||
pause: function() {
|
||||
if (this.host.pause) {
|
||||
this.host.pause();
|
||||
}
|
||||
},
|
||||
|
||||
resume: function() {
|
||||
if (this.host.resume) {
|
||||
this.host.resume();
|
||||
}
|
||||
},
|
||||
|
||||
end: function() {
|
||||
if (this.host.end) {
|
||||
this.host.end();
|
||||
}
|
||||
this._resolve(true);
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
if (this.host.cancel) {
|
||||
this.host.cancel();
|
||||
}
|
||||
this._resolve(false);
|
||||
},
|
||||
|
||||
complete: function(response) {
|
||||
var self = this;
|
||||
if (self._state === INITIAL_STATE) {
|
||||
self._state = DONE_PENDING_STATE;
|
||||
self._tick(function() {
|
||||
self._resolve(response);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_resolve: function(response) {
|
||||
if (this._state !== DONE_COMPLETE_STATE) {
|
||||
forEach(this._doneCallbacks, function(fn) {
|
||||
fn(response);
|
||||
});
|
||||
this._doneCallbacks.length = 0;
|
||||
this._state = DONE_COMPLETE_STATE;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return AnimateRunner;
|
||||
}];
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user