mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 15:40:22 +00:00
Compare commits
394 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5f69e3f64 | ||
|
|
dd24c78373 | ||
|
|
36d37c0e38 | ||
|
|
24699ee8f0 | ||
|
|
aa6a0e3fc6 | ||
|
|
8761ddc0e3 | ||
|
|
058842ad04 | ||
|
|
431bad0183 | ||
|
|
5850e61c82 | ||
|
|
d2e4e49986 | ||
|
|
0da6cc9118 | ||
|
|
cc60ba1f35 | ||
|
|
64d58a5b52 | ||
|
|
3bf4390339 | ||
|
|
e7ac7aa43b | ||
|
|
37781ed145 | ||
|
|
19ba6510d0 | ||
|
|
bc5ceee275 | ||
|
|
106af49258 | ||
|
|
d5eedf38db | ||
|
|
c0c0b2b674 | ||
|
|
ce1f1f97f0 | ||
|
|
8205158e47 | ||
|
|
6609e3da76 | ||
|
|
ef210e5e11 | ||
|
|
fd61e222c3 | ||
|
|
074b0675a1 | ||
|
|
5ed721b9b5 | ||
|
|
c22ab5d2e2 | ||
|
|
339a1658cd | ||
|
|
29432ffe37 | ||
|
|
caed2dfe4f | ||
|
|
7f4edaff6e | ||
|
|
6dcfccb32c | ||
|
|
e2173f9101 | ||
|
|
7a78aed160 | ||
|
|
7aef2d54e0 | ||
|
|
ce37ae2868 | ||
|
|
95f0bf9b52 | ||
|
|
8a0be355a9 | ||
|
|
849a1489c7 | ||
|
|
f54f0f98a0 | ||
|
|
766b3d5c87 | ||
|
|
4f735b0605 | ||
|
|
319dd1a449 | ||
|
|
88a14b4e25 | ||
|
|
451bde1244 | ||
|
|
4f827f587b | ||
|
|
131410b61b | ||
|
|
ca6b7d0fa2 | ||
|
|
40dc806e03 | ||
|
|
5e9835b4f2 | ||
|
|
e0209169bf | ||
|
|
b07afa0465 | ||
|
|
c3b5e16d84 | ||
|
|
7f444a205e | ||
|
|
82213efff2 | ||
|
|
ec59be67bc | ||
|
|
79e519feda | ||
|
|
7cf5544a9f | ||
|
|
030a9b8d33 | ||
|
|
310f129c1d | ||
|
|
0fc64ad8a1 | ||
|
|
12d1f5700d | ||
|
|
60e80509a8 | ||
|
|
3c12d36e73 | ||
|
|
fd6bac7de5 | ||
|
|
6d525f06c0 | ||
|
|
d7e6f1b192 | ||
|
|
5b9ff6cf48 | ||
|
|
f09b6aa5b5 | ||
|
|
8b395ff325 | ||
|
|
6c9131ef10 | ||
|
|
99c5027bf2 | ||
|
|
90e60d2d54 | ||
|
|
928d000db7 | ||
|
|
42ec95ebae | ||
|
|
1028cfaa30 | ||
|
|
756c52d6c1 | ||
|
|
c3e1a41d6f | ||
|
|
8dd4f14a04 | ||
|
|
7ba30fd2e7 | ||
|
|
5adea0ba64 | ||
|
|
2262ca6697 | ||
|
|
cd74f74468 | ||
|
|
fced1c0c16 | ||
|
|
1cdcddb5cc | ||
|
|
0e4d7cacad | ||
|
|
6a9ccacd62 | ||
|
|
e591ddcb30 | ||
|
|
cd0af8a771 | ||
|
|
51a7f9dc4a | ||
|
|
61eb426ab4 | ||
|
|
8ea8da4f11 | ||
|
|
07ee29c563 | ||
|
|
9f5d0cf79f | ||
|
|
1413328e6a | ||
|
|
7d09bd30f9 | ||
|
|
dde1b29497 | ||
|
|
4ae3184c59 | ||
|
|
ed53100a0d | ||
|
|
6df598d9f5 | ||
|
|
4aa9df7a7a | ||
|
|
7d5d62dafe | ||
|
|
524650a40e | ||
|
|
02a45826f1 | ||
|
|
e324c14907 | ||
|
|
e1cfb1957f | ||
|
|
2a3586381f | ||
|
|
834d316829 | ||
|
|
c61be8d0e6 | ||
|
|
465212835f | ||
|
|
b3acddea37 | ||
|
|
308598795a | ||
|
|
2cd09c9f0e | ||
|
|
34fee06ca7 | ||
|
|
c7a46d4b8a | ||
|
|
de065f1961 | ||
|
|
c3ab915d2e | ||
|
|
f4a4f42abb | ||
|
|
b2c84ccde3 | ||
|
|
2b344dbd20 | ||
|
|
cde840fdf8 | ||
|
|
f9656dab2d | ||
|
|
a0d759c613 | ||
|
|
0d421f093d | ||
|
|
5f937e54df | ||
|
|
0f9a1c21e6 | ||
|
|
ece7854972 | ||
|
|
8ace8073fd | ||
|
|
43a2f3d0bf | ||
|
|
a9cccbe14f | ||
|
|
36c9e42de2 | ||
|
|
9f566db33c | ||
|
|
c77b2bcca3 | ||
|
|
5a4145fe16 | ||
|
|
039b990d8d | ||
|
|
5a9cb8be3f | ||
|
|
63cd873fef | ||
|
|
69452fa94f | ||
|
|
2ed4ad5502 | ||
|
|
1d2a388830 | ||
|
|
ac05276a51 | ||
|
|
cb9c0f200a | ||
|
|
b1d676b7f7 | ||
|
|
9ddef840b6 | ||
|
|
28fc80bba0 | ||
|
|
b6c42d5e81 | ||
|
|
1c045f1b46 | ||
|
|
95e1b2d612 | ||
|
|
75345e3487 | ||
|
|
f4fe28bd92 | ||
|
|
ace13b94e6 | ||
|
|
5df7e73adf | ||
|
|
e115342fce | ||
|
|
e89150ca0f | ||
|
|
9693a426e3 | ||
|
|
162485d303 | ||
|
|
affcbad501 | ||
|
|
7e916455b3 | ||
|
|
cdc4d485a6 | ||
|
|
c894470d41 | ||
|
|
1b0718bf89 | ||
|
|
53fd24ffcb | ||
|
|
eb90672aae | ||
|
|
28cfd96fdc | ||
|
|
99d5defb1a | ||
|
|
efbc242875 | ||
|
|
d4d58f287f | ||
|
|
dc89db33df | ||
|
|
5dc27959d5 | ||
|
|
4c21355940 | ||
|
|
6f6cb5c8d8 | ||
|
|
821ed310a7 | ||
|
|
a7aa4cc0a9 | ||
|
|
e0ce9ed36d | ||
|
|
caeb740265 | ||
|
|
1bb33cccbe | ||
|
|
d9ed9c5ac1 | ||
|
|
00cac6ed10 | ||
|
|
2e9d7cc6cb | ||
|
|
32cc6cbb6f | ||
|
|
3b1a4fe0c8 | ||
|
|
9569778f2f | ||
|
|
0f61316b24 | ||
|
|
86151b0cea | ||
|
|
1b7a6c66f8 | ||
|
|
0ef76dde41 | ||
|
|
c6d04b3a3d | ||
|
|
e31560cf6b | ||
|
|
3d38fff8b4 | ||
|
|
bc492c0fc1 | ||
|
|
162144202c | ||
|
|
05596527ed | ||
|
|
5fea3471e8 | ||
|
|
131e4014b8 | ||
|
|
01c5be4681 | ||
|
|
fd9a03e147 | ||
|
|
6c17d02bc4 | ||
|
|
6a6f71f238 | ||
|
|
cf686285c2 | ||
|
|
7dfedb732d | ||
|
|
760f2fb731 | ||
|
|
c9705b7556 | ||
|
|
53ec33f07f | ||
|
|
884ef0dbcd | ||
|
|
010413f90a | ||
|
|
4f57236614 | ||
|
|
50bf029625 | ||
|
|
eff52ad877 | ||
|
|
e9ee492d35 | ||
|
|
e415e916e8 | ||
|
|
07084e1c8b | ||
|
|
186a591228 | ||
|
|
a80049fd0a | ||
|
|
d158dd131e | ||
|
|
1147f21999 | ||
|
|
bddd46c8ec | ||
|
|
80e7a45584 | ||
|
|
498365f219 | ||
|
|
056c849352 | ||
|
|
7d6e5a2d01 | ||
|
|
d1c4766d14 | ||
|
|
98473835a2 | ||
|
|
870232bd05 | ||
|
|
c31df32ca0 | ||
|
|
df2b88e230 | ||
|
|
83451d552e | ||
|
|
af7203e0b8 | ||
|
|
98ee3719f9 | ||
|
|
94b5f2dadb | ||
|
|
fe7decd1b0 | ||
|
|
cef084ade9 | ||
|
|
937caab647 | ||
|
|
3fc8017119 | ||
|
|
54637a335f | ||
|
|
277a5ea05d | ||
|
|
9865a7c0ad | ||
|
|
efba4731e4 | ||
|
|
a965984733 | ||
|
|
14d3e559d4 | ||
|
|
3d156a76e3 | ||
|
|
c7a1d1ab0b | ||
|
|
26d43cacdc | ||
|
|
4f5758e666 | ||
|
|
274a6734ef | ||
|
|
f0e3dfd008 | ||
|
|
bc3ff2cecd | ||
|
|
8f329ffb82 | ||
|
|
864b2596b2 | ||
|
|
f3a796e522 | ||
|
|
09f8962df2 | ||
|
|
a13c4ba770 | ||
|
|
040e743b39 | ||
|
|
bf816d3ade | ||
|
|
74b4ab8867 | ||
|
|
6e2359caa0 | ||
|
|
41534816a4 | ||
|
|
30252a0504 | ||
|
|
3dc18037e8 | ||
|
|
57d50582aa | ||
|
|
73c66715c9 | ||
|
|
cb29632a58 | ||
|
|
5c97731a22 | ||
|
|
b2e472e7a2 | ||
|
|
6ac773f350 | ||
|
|
3174f73336 | ||
|
|
3c62e4244e | ||
|
|
c432999572 | ||
|
|
f8d319c11a | ||
|
|
4f72433392 | ||
|
|
2f91cfd0d2 | ||
|
|
d5c5e2b584 | ||
|
|
cbb3ce2c30 | ||
|
|
45af02de04 | ||
|
|
6144df52af | ||
|
|
b0474cb984 | ||
|
|
9a4c9e6487 | ||
|
|
11fff8fa0d | ||
|
|
da8ab2f928 | ||
|
|
6d01384a55 | ||
|
|
109ffac975 | ||
|
|
8c10db3847 | ||
|
|
03088d6010 | ||
|
|
18e0768a2b | ||
|
|
ed4a1fddce | ||
|
|
cfde6f507c | ||
|
|
3468ad1b61 | ||
|
|
e9c79cad43 | ||
|
|
e455e7d878 | ||
|
|
3410f65e79 | ||
|
|
f3de5b6eac | ||
|
|
fcd2a8131a | ||
|
|
62dbe85798 | ||
|
|
1d5e18b062 | ||
|
|
a0ed371389 | ||
|
|
05e4fd3488 | ||
|
|
30a8b7d0b5 | ||
|
|
f8944efe70 | ||
|
|
43072e3812 | ||
|
|
9396d55414 | ||
|
|
82e97cf53e | ||
|
|
cf2a7614a4 | ||
|
|
9e538e7c31 | ||
|
|
4ac21ac039 | ||
|
|
f69dc16241 | ||
|
|
f1a8d419d5 | ||
|
|
8864e54f1f | ||
|
|
dc4df93177 | ||
|
|
043190f397 | ||
|
|
f4d850e168 | ||
|
|
8ec2743ca1 | ||
|
|
ecbee8147b | ||
|
|
f8c6ee3df5 | ||
|
|
fe84f7bef8 | ||
|
|
d653607162 | ||
|
|
082fe180ec | ||
|
|
d3491083a5 | ||
|
|
c3d6ca97e1 | ||
|
|
a14266e464 | ||
|
|
b4d44e1298 | ||
|
|
ca116c35a6 | ||
|
|
78ba429e6a | ||
|
|
dbf8c3c745 | ||
|
|
3602c9785b | ||
|
|
acaac21fd1 | ||
|
|
c98ef94706 | ||
|
|
b0972a2e75 | ||
|
|
2dbb6f9a54 | ||
|
|
785a5fd7c1 | ||
|
|
a55c1e79cf | ||
|
|
d070450cd2 | ||
|
|
09648e4888 | ||
|
|
2adbcf189b | ||
|
|
39c5ffb2a6 | ||
|
|
04a570d31c | ||
|
|
958d3d56b1 | ||
|
|
0e50810c53 | ||
|
|
21e48abbc1 | ||
|
|
b6d5439343 | ||
|
|
93901bdde4 | ||
|
|
d802ed1b36 | ||
|
|
e8f4305e9d | ||
|
|
b38a2287f2 | ||
|
|
1e7675ad4c | ||
|
|
280b5ce3c0 | ||
|
|
fbc5cf514b | ||
|
|
f01087f802 | ||
|
|
4ac6424e87 | ||
|
|
d3c486dd6d | ||
|
|
2d0f6ccba8 | ||
|
|
9a81b8668a | ||
|
|
9481d69d1c | ||
|
|
7615723547 | ||
|
|
338f949259 | ||
|
|
d0192b31a3 | ||
|
|
6127528b50 | ||
|
|
0410572322 | ||
|
|
fd2371cfc2 | ||
|
|
267fcc999c | ||
|
|
84187b6d94 | ||
|
|
5d6482bb3b | ||
|
|
023765c593 | ||
|
|
4a401bbcf3 | ||
|
|
7401c70718 | ||
|
|
bb36bc7edf | ||
|
|
bf1972dc1e | ||
|
|
689dfb1679 | ||
|
|
1169b54456 | ||
|
|
81b81856ee | ||
|
|
fd4b99936e | ||
|
|
09271a8ab9 | ||
|
|
5a8d9acacb | ||
|
|
04d5a5072f | ||
|
|
55c30e1be6 | ||
|
|
97fc84c151 | ||
|
|
4ee0687f3f | ||
|
|
ddff347b91 | ||
|
|
05ef1bd853 | ||
|
|
d0f8bd30a6 | ||
|
|
1a8d3c8b3a | ||
|
|
753687e5c2 | ||
|
|
1a15c01b64 | ||
|
|
7f33e1ca89 | ||
|
|
28d00945ba | ||
|
|
6f40c88f47 | ||
|
|
68dd621082 | ||
|
|
3abfb4ef51 | ||
|
|
1014e52349 | ||
|
|
cda061f723 | ||
|
|
1497c6c1fb | ||
|
|
e41e445b51 | ||
|
|
7ab73190b7 | ||
|
|
450b3a5460 |
190 changed files with 6461 additions and 4867 deletions
3
.jscs.json
Normal file
3
.jscs.json
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"disallowKeywords": ["with"]
|
||||||
|
}
|
||||||
22
.jscs.json.todo
Normal file
22
.jscs.json.todo
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
// This is an incomplete TODO list of checks we want to start enforcing
|
||||||
|
//
|
||||||
|
// The goal is to enable these checks one by one by moving them to .jscs.json along with commits
|
||||||
|
// that correct the existing code base issues and make the new check pass.
|
||||||
|
|
||||||
|
{
|
||||||
|
"requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"],
|
||||||
|
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
|
||||||
|
"disallowLeftStickedOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||||
|
"disallowRightStickedOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||||
|
"requireRightStickedOperators": ["!"],
|
||||||
|
"requireLeftStickedOperators": [","],
|
||||||
|
"disallowImplicitTypeConversion": ["string"],
|
||||||
|
"disallowMultipleLineBreaks": true,
|
||||||
|
"disallowKeywordsOnNewLine": ["else"],
|
||||||
|
"disallowTrailingWhitespace": true,
|
||||||
|
"requireLineFeedAtFileEnd": true,
|
||||||
|
"validateJSDoc": {
|
||||||
|
"checkParamNames": true,
|
||||||
|
"requireParamTypes": true
|
||||||
|
}
|
||||||
|
}
|
||||||
16
.travis.yml
16
.travis.yml
|
|
@ -3,26 +3,26 @@ node_js:
|
||||||
- 0.10
|
- 0.10
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
matrix:
|
||||||
|
- JOB=unit
|
||||||
|
- JOB=e2e
|
||||||
global:
|
global:
|
||||||
- SAUCE_USERNAME=angular-ci
|
- SAUCE_USERNAME=angular-ci
|
||||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||||
- SAUCE_CONNECT_READY_FILE=/tmp/sauce-connect-ready
|
|
||||||
- BROWSER_STACK_USERNAME=VojtaJina
|
|
||||||
- BROWSER_STACK_ACCESS_KEY=HAfHZaypxAc3PEUrUU9a
|
|
||||||
- LOGS_DIR=/tmp/angular-build/logs
|
- LOGS_DIR=/tmp/angular-build/logs
|
||||||
|
- BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- mkdir -p $LOGS_DIR
|
- mkdir -p $LOGS_DIR
|
||||||
- ./lib/browser-stack/start-tunnel.sh
|
- ./lib/sauce/sauce_connect_setup.sh
|
||||||
- npm install -g grunt-cli
|
- npm install -g grunt-cli
|
||||||
- grunt bower
|
- grunt bower
|
||||||
- grunt bower
|
- grunt bower
|
||||||
- grunt package-without-bower
|
- grunt package-without-bower
|
||||||
- grunt ci-checks
|
- ./scripts/travis/wait_for_browser_provider.sh
|
||||||
- ./lib/sauce/sauce_connect_block.sh
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./travis_build.sh
|
- ./scripts/travis/build.sh
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
- ./travis_print_logs.sh
|
- ./scripts/travis/print_logs.sh
|
||||||
|
|
|
||||||
372
CHANGELOG.md
372
CHANGELOG.md
|
|
@ -1,3 +1,372 @@
|
||||||
|
<a name="1.2.11"></a>
|
||||||
|
# 1.2.11 cryptocurrency-hyperdeflation (2014-02-03)
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
- **$compile:** retain CSS classes added in cloneAttachFn on asynchronous directives
|
||||||
|
([5ed721b9](https://github.com/angular/angular.js/commit/5ed721b9b5e95ae08450e1ae9d5202e7f3f79295),
|
||||||
|
[#5439](https://github.com/angular/angular.js/issues/5439), [#5617](https://github.com/angular/angular.js/issues/5617))
|
||||||
|
- **$http:** update httpBackend to use ActiveXObject on IE8 if necessary
|
||||||
|
([ef210e5e](https://github.com/angular/angular.js/commit/ef210e5e119db4f5bfc9d2428b19f9b335c4f976),
|
||||||
|
[#5677](https://github.com/angular/angular.js/issues/5677), [#5679](https://github.com/angular/angular.js/issues/5679))
|
||||||
|
- **$q:** make $q.reject support `finally` and `catch`
|
||||||
|
([074b0675](https://github.com/angular/angular.js/commit/074b0675a1f97dce07f520f1ae6198ed3c604000),
|
||||||
|
[#6048](https://github.com/angular/angular.js/issues/6048), [#6076](https://github.com/angular/angular.js/issues/6076))
|
||||||
|
- **filterFilter:** don't interpret dots in predicate object fields as paths
|
||||||
|
([339a1658](https://github.com/angular/angular.js/commit/339a1658cd9bfa5e322a01c45aa0a1df67e3a842),
|
||||||
|
[#6005](https://github.com/angular/angular.js/issues/6005), [#6009](https://github.com/angular/angular.js/issues/6009))
|
||||||
|
- **mocks:** refactor currentSpec to work w/ Jasmine 2
|
||||||
|
([95f0bf9b](https://github.com/angular/angular.js/commit/95f0bf9b526fda8964527c6d4aef1ad50a47f1f3),
|
||||||
|
[#5662](https://github.com/angular/angular.js/issues/5662))
|
||||||
|
- **ngResource:** don't append number to '$' in url param value when encoding URI
|
||||||
|
([ce1f1f97](https://github.com/angular/angular.js/commit/ce1f1f97f0ebf77941b2bdaf5e8352d33786524d),
|
||||||
|
[#6003](https://github.com/angular/angular.js/issues/6003), [#6004](https://github.com/angular/angular.js/issues/6004))
|
||||||
|
|
||||||
|
<a name="1.2.10"></a>
|
||||||
|
# 1.2.10 augmented-serendipity (2014-01-24)
|
||||||
|
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
- **$parse:** do not use locals to resolve object properties
|
||||||
|
([f09b6aa5](https://github.com/angular/angular.js/commit/f09b6aa5b58c090e3b8f8811fb7735e38d4b7623),
|
||||||
|
[#5838](https://github.com/angular/angular.js/issues/5838), [#5862](https://github.com/angular/angular.js/issues/5862))
|
||||||
|
- **a:** don't call preventDefault on click when a SVGAElement has an xlink:href attribute
|
||||||
|
([e0209169](https://github.com/angular/angular.js/commit/e0209169bf1463465ad07484421620748a4d3908),
|
||||||
|
[#5896](https://github.com/angular/angular.js/issues/5896), [#5897](https://github.com/angular/angular.js/issues/5897))
|
||||||
|
- **input:** use Chromium's email validation regexp
|
||||||
|
([79e519fe](https://github.com/angular/angular.js/commit/79e519fedaec54390a8bdacfb1926bfce57a1eb6),
|
||||||
|
[#5899](https://github.com/angular/angular.js/issues/5899), [#5924](https://github.com/angular/angular.js/issues/5924))
|
||||||
|
- **ngRoute:** pipe preceding route param no longer masks ? or * operator
|
||||||
|
([fd6bac7d](https://github.com/angular/angular.js/commit/fd6bac7de56f728a89782dc80c78f7d5c21bbc65),
|
||||||
|
[#5920](https://github.com/angular/angular.js/issues/5920))
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **$animate:** provide support for a close callback
|
||||||
|
([ca6b7d0f](https://github.com/angular/angular.js/commit/ca6b7d0fa2e355ebd764230260758cee9a4ebe1e),
|
||||||
|
[#5685](https://github.com/angular/angular.js/issues/5685), [#5053](https://github.com/angular/angular.js/issues/5053), [#4993](https://github.com/angular/angular.js/issues/4993))
|
||||||
|
|
||||||
|
|
||||||
|
<a name="1.2.9"></a>
|
||||||
|
# 1.2.9 enchanted-articulacy (2014-01-15)
|
||||||
|
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
- **$animate:**
|
||||||
|
- ensure the final closing timeout respects staggering animations
|
||||||
|
([ed53100a](https://github.com/angular/angular.js/commit/ed53100a0dbc9119d5dfc8b7248845d4f6989df2))
|
||||||
|
- prevent race conditions for class-based animations when animating on the same CSS class
|
||||||
|
([4aa9df7a](https://github.com/angular/angular.js/commit/4aa9df7a7ae533531dfae1e3eb9646245d6b5ff4),
|
||||||
|
[#5588](https://github.com/angular/angular.js/issues/5588))
|
||||||
|
- correctly detect and handle CSS transition changes during class addition and removal
|
||||||
|
([7d5d62da](https://github.com/angular/angular.js/commit/7d5d62dafe11620082c79da35958f8014eeb008c))
|
||||||
|
- avoid accidentally matching substrings when resolving the presence of className tokens
|
||||||
|
([524650a4](https://github.com/angular/angular.js/commit/524650a40ed20f01571e5466475749874ee67288))
|
||||||
|
- **$http:** ensure default headers PUT and POST are different objects
|
||||||
|
([e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
|
||||||
|
[#5742](https://github.com/angular/angular.js/issues/5742), [#5747](https://github.com/angular/angular.js/issues/5747), [#5764](https://github.com/angular/angular.js/issues/5764))
|
||||||
|
- **$rootScope:** prevent infinite $digest by checking if asyncQueue is empty when decrementing ttl
|
||||||
|
([2cd09c9f](https://github.com/angular/angular.js/commit/2cd09c9f0e7766bcd191662841b7b1ffc3b6dc3f),
|
||||||
|
[#2622](https://github.com/angular/angular.js/issues/2622))
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **$animate:**
|
||||||
|
- provide support for DOM callbacks
|
||||||
|
([dde1b294](https://github.com/angular/angular.js/commit/dde1b2949727c297e214c99960141bfad438d7a4))
|
||||||
|
- use requestAnimationFrame instead of a timeout to issue a reflow
|
||||||
|
([4ae3184c](https://github.com/angular/angular.js/commit/4ae3184c5915aac9aa00889aa2153c8e84c14966),
|
||||||
|
[#4278](https://github.com/angular/angular.js/issues/4278), [#4225](https://github.com/angular/angular.js/issues/4225))
|
||||||
|
|
||||||
|
## Breaking Changes
|
||||||
|
|
||||||
|
- **$http:** due to [e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
|
||||||
|
it is now necessary to seperately specify default HTTP headers for PUT, POST and PATCH requests, as these no longer share a single object.
|
||||||
|
|
||||||
|
To migrate your code, follow the example below:
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
// Will apply to POST, PUT and PATCH methods
|
||||||
|
$httpProvider.defaults.headers.post = {
|
||||||
|
"X-MY-CSRF-HEADER": "..."
|
||||||
|
};
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
// POST, PUT and PATCH default headers must be specified seperately,
|
||||||
|
// as they do not share data.
|
||||||
|
$httpProvider.defaults.headers.post =
|
||||||
|
$httpProvider.defaults.headers.put =
|
||||||
|
$httpProviders.defaults.headers.patch = {
|
||||||
|
"X-MY-CSRF-HEADER": "..."
|
||||||
|
};
|
||||||
|
|
||||||
|
<a name="1.2.8"></a>
|
||||||
|
# 1.2.8 interdimensional-cartography (2014-01-10)
|
||||||
|
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
- **$http:**
|
||||||
|
- return responseText on IE8 for requests with responseType set
|
||||||
|
([a9cccbe1](https://github.com/angular/angular.js/commit/a9cccbe14f1bd9048f5dab4443f58c804d4259a1),
|
||||||
|
[#4464](https://github.com/angular/angular.js/issues/4464), [#4738](https://github.com/angular/angular.js/issues/4738), [#5636](https://github.com/angular/angular.js/issues/5636))
|
||||||
|
- Allow status code 0 from any protocol
|
||||||
|
([28fc80bb](https://github.com/angular/angular.js/commit/28fc80bba0107075ab371fd0a7634a38891626b2),
|
||||||
|
[#1356](https://github.com/angular/angular.js/issues/1356), [#5547](https://github.com/angular/angular.js/issues/5547))
|
||||||
|
- cancelled JSONP requests will not print error in the console
|
||||||
|
([95e1b2d6](https://github.com/angular/angular.js/commit/95e1b2d6121b4e26cf87dcf6746a7b8cb4c25e7f),
|
||||||
|
[#5615](https://github.com/angular/angular.js/issues/5615), [#5616](https://github.com/angular/angular.js/issues/5616))
|
||||||
|
- **$location:** return '/' for root path in hashbang mode
|
||||||
|
([63cd873f](https://github.com/angular/angular.js/commit/63cd873fef3207deef30c7a7ed66f4b8f647dc12),
|
||||||
|
[#5650](https://github.com/angular/angular.js/issues/5650), [#5712](https://github.com/angular/angular.js/issues/5712))
|
||||||
|
- **$parse:** fix CSP nested property evaluation, and issue that prevented its tests from failing
|
||||||
|
([3b1a4fe0](https://github.com/angular/angular.js/commit/3b1a4fe0c83c7898ecd7261ab4213998ee7be0ec),
|
||||||
|
[#5591](https://github.com/angular/angular.js/issues/5591), [#5592](https://github.com/angular/angular.js/issues/5592))
|
||||||
|
- **closure:** add Closure externs for angular.$q.Promise.finally
|
||||||
|
([caeb7402](https://github.com/angular/angular.js/commit/caeb7402651702cd13df2f1594e9827439a8b760),
|
||||||
|
[#4757](https://github.com/angular/angular.js/issues/4757))
|
||||||
|
- **ngMock window.inject:** Remove Error 'stack' property changes
|
||||||
|
([7e916455](https://github.com/angular/angular.js/commit/7e916455b36dc9ca4d4afc1e44cade90006d00e3))
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **select:** allow multiline ng-options
|
||||||
|
([43a2f3d0](https://github.com/angular/angular.js/commit/43a2f3d0bf435e3626cd679caff4281cfb3415bd),
|
||||||
|
[#5602](https://github.com/angular/angular.js/issues/5602))
|
||||||
|
|
||||||
|
<a name="1.2.7"></a>
|
||||||
|
# 1.2.7 emoji-clairvoyance (2014-01-03)
|
||||||
|
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
- **$animate:**
|
||||||
|
- ensue class-based animations are always skipped before structural post-digest tasks are run
|
||||||
|
([bc492c0f](https://github.com/angular/angular.js/commit/bc492c0fc17257ddf2bc5964e205379aa766b3d8),
|
||||||
|
[#5582](https://github.com/angular/angular.js/issues/5582))
|
||||||
|
- remove trailing `s` from computed transition duration styles
|
||||||
|
([50bf0296](https://github.com/angular/angular.js/commit/50bf029625d603fc652f0f413e709f43803743db))
|
||||||
|
- **$http:**
|
||||||
|
([3d38fff8](https://github.com/angular/angular.js/commit/3d38fff8b4ea2fd60fadef2028ea4dcddfccb1a4))
|
||||||
|
- use ActiveX XHR when making PATCH requests on IE8
|
||||||
|
([6c17d02b](https://github.com/angular/angular.js/commit/6c17d02bc4cc02f478775d62e1f9f77da9da82ad),
|
||||||
|
[#2518](https://github.com/angular/angular.js/issues/2518), [#5043](https://github.com/angular/angular.js/issues/5043))
|
||||||
|
- fix 'type mismatch' error on IE8 after each request
|
||||||
|
([fd9a03e1](https://github.com/angular/angular.js/commit/fd9a03e147aac7e952c6dda1f381fd4662276ba2))
|
||||||
|
- Ignore multiple calls to onreadystatechange with readyState=4
|
||||||
|
([4f572366](https://github.com/angular/angular.js/commit/4f57236614415eea919221ea5f99c4d8689b3267),
|
||||||
|
[#5426](https://github.com/angular/angular.js/issues/5426))
|
||||||
|
- **$injector:** remove the `INSTANTIATING` flag properly when done
|
||||||
|
([186a5912](https://github.com/angular/angular.js/commit/186a5912288acfff0ee59dae29af83c37c987921),
|
||||||
|
[#4361](https://github.com/angular/angular.js/issues/4361), [#5577](https://github.com/angular/angular.js/issues/5577))
|
||||||
|
- **$location:**
|
||||||
|
- remove base href domain if the URL begins with '//'
|
||||||
|
([760f2fb7](https://github.com/angular/angular.js/commit/760f2fb73178e56c37397b3c5876f7dac96f0455),
|
||||||
|
[#5606](https://github.com/angular/angular.js/issues/5606))
|
||||||
|
- fix $location.path() behaviour when $locationChangeStart is triggered by the browser
|
||||||
|
([cf686285](https://github.com/angular/angular.js/commit/cf686285c22d528440e173fdb65ad1052d96df3c),
|
||||||
|
[#4989](https://github.com/angular/angular.js/issues/4989), [#5089](https://github.com/angular/angular.js/issues/5089), [#5118](https://github.com/angular/angular.js/issues/5118), [#5580](https://github.com/angular/angular.js/issues/5580))
|
||||||
|
- re-assign history after BFCache back on Android browser
|
||||||
|
([bddd46c8](https://github.com/angular/angular.js/commit/bddd46c8ecf49cfe6c999cd6b4a69b7d7e1f9a33),
|
||||||
|
[#5425](https://github.com/angular/angular.js/issues/5425))
|
||||||
|
- **$resource:** prevent URL template from collapsing into an empty string
|
||||||
|
([131e4014](https://github.com/angular/angular.js/commit/131e4014b831ac81b7979c4523da81ebc5861c70),
|
||||||
|
[#5455](https://github.com/angular/angular.js/issues/5455), [#5493](https://github.com/angular/angular.js/issues/5493))
|
||||||
|
- **$sanitize:** consider the `size` attribute as a valid/allowed attribute
|
||||||
|
([056c8493](https://github.com/angular/angular.js/commit/056c8493521988dbb330c6636135b505737da918),
|
||||||
|
[#5522](https://github.com/angular/angular.js/issues/5522))
|
||||||
|
- **Scope:** don't let watch deregistration mess up the dirty-checking digest loop
|
||||||
|
([884ef0db](https://github.com/angular/angular.js/commit/884ef0dbcdfe614cedc824d079361b53e675d033),
|
||||||
|
[#5525](https://github.com/angular/angular.js/issues/5525))
|
||||||
|
- **input:**
|
||||||
|
- use apply on the change event only when one isn't already in progress
|
||||||
|
([a80049fd](https://github.com/angular/angular.js/commit/a80049fd0ac858eeeb645a4209cb2a661d0b4c33),
|
||||||
|
[#5293](https://github.com/angular/angular.js/issues/5293))
|
||||||
|
- prevent double $digest when using jQuery trigger.
|
||||||
|
([1147f219](https://github.com/angular/angular.js/commit/1147f21999edf9a434cd8d24865a6455e744d858),
|
||||||
|
[#5293](https://github.com/angular/angular.js/issues/5293))
|
||||||
|
- **ngRepeat:** allow for more flexible coding style in ngRepeat expression
|
||||||
|
([c9705b75](https://github.com/angular/angular.js/commit/c9705b755645a4bfe066243f2ba15a733c3787e1),
|
||||||
|
[#5537](https://github.com/angular/angular.js/issues/5537), [#5598](https://github.com/angular/angular.js/issues/5598))
|
||||||
|
- **ngRoute:** instantiate controller when template is empty
|
||||||
|
([498365f2](https://github.com/angular/angular.js/commit/498365f219f65d6c29bdf2f03610a4d3646009bb),
|
||||||
|
[#5550](https://github.com/angular/angular.js/issues/5550))
|
||||||
|
- **ngShow/ngHide, ngIf:** functions with zero args should be truthy
|
||||||
|
([01c5be46](https://github.com/angular/angular.js/commit/01c5be4681e34cdc5f5c461b7a618fefe8038919),
|
||||||
|
[#5414](https://github.com/angular/angular.js/issues/5414))
|
||||||
|
|
||||||
|
|
||||||
|
## Performance Improvements
|
||||||
|
|
||||||
|
- **Scope:** limit propagation of $broadcast to scopes that have listeners for the event
|
||||||
|
([80e7a455](https://github.com/angular/angular.js/commit/80e7a4558490f7ffd33d142844b9153a5ed00e86),
|
||||||
|
[#5341](https://github.com/angular/angular.js/issues/5341), [#5371](https://github.com/angular/angular.js/issues/5371))
|
||||||
|
|
||||||
|
<a name="1.2.6"></a>
|
||||||
|
# 1.2.6 taco-salsafication (2013-12-19)
|
||||||
|
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
- **$animate:** use a scheduled timeout in favor of a fallback property to close transitions
|
||||||
|
([54637a33](https://github.com/angular/angular.js/commit/54637a335f885110efaa702a3bab29c77644b36c),
|
||||||
|
[#5255](https://github.com/angular/angular.js/issues/5255), [#5241](https://github.com/angular/angular.js/issues/5241), [#5405](https://github.com/angular/angular.js/issues/5405))
|
||||||
|
- **$compile:** remove invalid IE exceptional case for `href`
|
||||||
|
([c7a1d1ab](https://github.com/angular/angular.js/commit/c7a1d1ab0b663edffc1ac7b54deea847e372468d),
|
||||||
|
[#5479](https://github.com/angular/angular.js/issues/5479))
|
||||||
|
- **$location:** parse xlink:href for SVGAElements
|
||||||
|
([bc3ff2ce](https://github.com/angular/angular.js/commit/bc3ff2cecd0861766a9e8606f3cc2c582d9875df),
|
||||||
|
[#5472](https://github.com/angular/angular.js/issues/5472), [#5198](https://github.com/angular/angular.js/issues/5198), [#5199](https://github.com/angular/angular.js/issues/5199), [#4098](https://github.com/angular/angular.js/issues/4098), [#1420](https://github.com/angular/angular.js/issues/1420))
|
||||||
|
- **$log:** should work in IE8
|
||||||
|
([4f5758e6](https://github.com/angular/angular.js/commit/4f5758e6669222369889c9e789601d25ff885530),
|
||||||
|
[#5400](https://github.com/angular/angular.js/issues/5400))
|
||||||
|
- **$parse:** return `undefined` if an intermetiate property's value is `null`
|
||||||
|
([26d43cac](https://github.com/angular/angular.js/commit/26d43cacdc106765bd928d41600352198f887aef),
|
||||||
|
[#5480](https://github.com/angular/angular.js/issues/5480))
|
||||||
|
- **closure:** add type definition for `Scope#$watchCollection`
|
||||||
|
([8f329ffb](https://github.com/angular/angular.js/commit/8f329ffb829410e1fd8f86a766929134e736e3e5),
|
||||||
|
[#5475](https://github.com/angular/angular.js/issues/5475))
|
||||||
|
- **forEach:** allow looping over result of `querySelectorAll` in IE8
|
||||||
|
([274a6734](https://github.com/angular/angular.js/commit/274a6734ef1fff543cc50388a0958d1988baeb57))
|
||||||
|
- **input:** do not hold input for composition on Android
|
||||||
|
([3dc18037](https://github.com/angular/angular.js/commit/3dc18037e8db8766641a4d39f0fee96077db1fcb),
|
||||||
|
[#5308](https://github.com/angular/angular.js/issues/5308))
|
||||||
|
- **jqLite:** support unbind self within handler
|
||||||
|
([2f91cfd0](https://github.com/angular/angular.js/commit/2f91cfd0d2986899c38641100c1851b2f9d3888a))
|
||||||
|
- **ngRepeat:** allow multiline expressions
|
||||||
|
([cbb3ce2c](https://github.com/angular/angular.js/commit/cbb3ce2c309052b951d0cc87e4c6daa9c48a3dd8),
|
||||||
|
[#5000](https://github.com/angular/angular.js/issues/5000))
|
||||||
|
- **select:** invalidate when `multiple`, `required`, and model is `[]`
|
||||||
|
([5c97731a](https://github.com/angular/angular.js/commit/5c97731a22ed87d64712e673efea0e8a05eae65f),
|
||||||
|
[#5337](https://github.com/angular/angular.js/issues/5337))
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **jqLite:** provide support for `element.one()`
|
||||||
|
([937caab6](https://github.com/angular/angular.js/commit/937caab6475e53a7ea0206e992f8a52449232e78))
|
||||||
|
- **ngAnimate:** provide configuration support to match specific className values to trigger animations
|
||||||
|
([cef084ad](https://github.com/angular/angular.js/commit/cef084ade9072090259d8c679751cac3ffeaed51),
|
||||||
|
[#5357](https://github.com/angular/angular.js/issues/5357), [#5283](https://github.com/angular/angular.js/issues/5283))
|
||||||
|
|
||||||
|
|
||||||
|
## Performance Improvements
|
||||||
|
|
||||||
|
- **compile:** add class 'ng-scope' before cloning and other micro-optimizations
|
||||||
|
([f3a796e5](https://github.com/angular/angular.js/commit/f3a796e522afdbd3b640d14426edb2fbfab463c5),
|
||||||
|
[#5471](https://github.com/angular/angular.js/issues/5471))
|
||||||
|
- **$parse:** use a faster path when the number of path parts is low
|
||||||
|
([f4462319](https://github.com/angular/angular.js/commit/864b2596b246470cca9d4e223eaed720f4462319))
|
||||||
|
- use faster check for `$$` prefix
|
||||||
|
([06c5cfc7](https://github.com/angular/angular.js/commit/cb29632a5802e930262919b3db64ca4806c5cfc7))
|
||||||
|
|
||||||
|
<a name="1.2.5"></a>
|
||||||
|
# 1.2.5 singularity-expansion (2013-12-13)
|
||||||
|
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
- **$compile:** allow literals in isolate scope references
|
||||||
|
([43072e38](https://github.com/angular/angular.js/commit/43072e3812e32b89b97ad03144577cba50d4b776),
|
||||||
|
[#5296](https://github.com/angular/angular.js/issues/5296))
|
||||||
|
- **angular-mocks:** use copy of mock data in $httpBackend
|
||||||
|
([f69dc162](https://github.com/angular/angular.js/commit/f69dc16241c8b631123ad0b09674f0a5e0ff32fe))
|
||||||
|
- **closure:** add missing FormController extern definitions
|
||||||
|
([1d5e18b0](https://github.com/angular/angular.js/commit/1d5e18b062c3e33b2a8d96aa58d905ed2cd48649),
|
||||||
|
[#5303](https://github.com/angular/angular.js/issues/5303))
|
||||||
|
- **ngInclude:** add template to DOM before linking other directives
|
||||||
|
([30a8b7d0](https://github.com/angular/angular.js/commit/30a8b7d0b5d4882c2bf3b20eb696a02f5b667726),
|
||||||
|
[#5247](https://github.com/angular/angular.js/issues/5247))
|
||||||
|
- **ngView:** add template to DOM before linking other directives
|
||||||
|
([f8944efe](https://github.com/angular/angular.js/commit/f8944efe70b81e02704df9b53ea2546c80c73d3b))
|
||||||
|
|
||||||
|
|
||||||
|
## Performance Improvements
|
||||||
|
|
||||||
|
- **$injector:** remove invoke optimization that doesn't work
|
||||||
|
([05e4fd34](https://github.com/angular/angular.js/commit/05e4fd3488b89e670c36869f18defe26deac2efa),
|
||||||
|
[#5388](https://github.com/angular/angular.js/issues/5388))
|
||||||
|
- **$resource:** use shallow copy instead of angular.copy
|
||||||
|
([fcd2a813](https://github.com/angular/angular.js/commit/fcd2a8131a3cb3e59a616bf31e61510b5c3a97d3),
|
||||||
|
[#5300](https://github.com/angular/angular.js/issues/5300))
|
||||||
|
- **a:** do not link when href or name exists in template
|
||||||
|
([f3de5b6e](https://github.com/angular/angular.js/commit/f3de5b6eac90baf649506072162f36dbc6d2f028),
|
||||||
|
[#5362](https://github.com/angular/angular.js/issues/5362))
|
||||||
|
- **jqLite:** implement and use the `empty` method in place of `html(‘’)`
|
||||||
|
([3410f65e](https://github.com/angular/angular.js/commit/3410f65e790a81d457b4f4601a1e760a6f8ede5e),
|
||||||
|
[#4457](https://github.com/angular/angular.js/issues/4457))
|
||||||
|
|
||||||
|
## Breaking Changes
|
||||||
|
|
||||||
|
- **angular-mocks:** due to [f69dc162](https://github.com/angular/angular.js/commit/f69dc16241c8b631123ad0b09674f0a5e0ff32fe),
|
||||||
|
some tests that rely on identity comparison rather than equality comparison in checking mock http responses will be broken,
|
||||||
|
since now each mock response is a copy of the original response. This is usually fixable by changing a `.toBe()` comparison
|
||||||
|
to `toEqual()` inside of tests.
|
||||||
|
|
||||||
|
<a name="1.2.4"></a>
|
||||||
|
# 1.2.4 wormhole-blaster (2013-12-06)
|
||||||
|
|
||||||
|
|
||||||
|
## Bug Fixes
|
||||||
|
|
||||||
|
- **$animate:**
|
||||||
|
- ensure animations work with directives that share a transclusion
|
||||||
|
([958d3d56](https://github.com/angular/angular.js/commit/958d3d56b1899a2cfc7b18c0292e5a1d8c64d0a5),
|
||||||
|
[#4716](https://github.com/angular/angular.js/issues/4716), [#4871](https://github.com/angular/angular.js/issues/4871), [#5021](https://github.com/angular/angular.js/issues/5021), [#5278](https://github.com/angular/angular.js/issues/5278))
|
||||||
|
- ensure ms durations are properly rounded
|
||||||
|
([93901bdd](https://github.com/angular/angular.js/commit/93901bdde4bb9f0ba114ebb33b8885808e1823e1),
|
||||||
|
[#5113](https://github.com/angular/angular.js/issues/5113), [#5162](https://github.com/angular/angular.js/issues/5162))
|
||||||
|
- **$compile:**
|
||||||
|
- update cloned elements if the template arrives after the cloning
|
||||||
|
([b0972a2e](https://github.com/angular/angular.js/commit/b0972a2e75909e41dbac6e4413ada7df2d51df3a))
|
||||||
|
- ensure the isolated local watch `lastValue` is always in sync
|
||||||
|
([2d0f6ccb](https://github.com/angular/angular.js/commit/2d0f6ccba896fe34141d6d4f59eef6fba580c5c2),
|
||||||
|
[#5182](https://github.com/angular/angular.js/issues/5182))
|
||||||
|
- **$rootScope:**
|
||||||
|
- ensure that when the $destroy event is broadcast on $rootScope that it does something
|
||||||
|
([d802ed1b](https://github.com/angular/angular.js/commit/d802ed1b3680cfc1751777fac465b92ee29944dc),
|
||||||
|
[#5169](https://github.com/angular/angular.js/issues/5169))
|
||||||
|
- ensure the phase is cleared within a digest if an exception is raised by a watcher
|
||||||
|
([d3c486dd](https://github.com/angular/angular.js/commit/d3c486dd6dfa8d5dca32a3e28aa685fb7260c878))
|
||||||
|
- **$sanitize:** don't rely on YARR regex engine executing immediately in order to prevent object mutation
|
||||||
|
([81b81856](https://github.com/angular/angular.js/commit/81b81856ee43d2876927c4e1f774affa87e99707),
|
||||||
|
[#5193](https://github.com/angular/angular.js/issues/5193), [#5192](https://github.com/angular/angular.js/issues/5192))
|
||||||
|
- **closure:** closure compiler shouldn't rename .defaults.transformRequest
|
||||||
|
([f01087f8](https://github.com/angular/angular.js/commit/f01087f802839637843115cbcf99702e09d866f6))
|
||||||
|
- **input:** ensure ngModelWatch() triggers second digest pass when appropriate
|
||||||
|
([b6d54393](https://github.com/angular/angular.js/commit/b6d5439343b9801f7f2a009d0de09cba9aa21a1d),
|
||||||
|
[#5258](https://github.com/angular/angular.js/issues/5258), [#5282](https://github.com/angular/angular.js/issues/5282))
|
||||||
|
- **isElement:** return boolean value rather than `truthy` value.
|
||||||
|
([2dbb6f9a](https://github.com/angular/angular.js/commit/2dbb6f9a54eb5ff5847eed11c85ac4cf119eb41c),
|
||||||
|
[#4519](https://github.com/angular/angular.js/issues/4519), [#4534](https://github.com/angular/angular.js/issues/4534))
|
||||||
|
- **jqLite:** ignore incompatible nodes on find()
|
||||||
|
([1169b544](https://github.com/angular/angular.js/commit/1169b5445691e1495354d235a3badf05240e3904),
|
||||||
|
[#4120](https://github.com/angular/angular.js/issues/4120))
|
||||||
|
- **ngInit:** evaluate ngInit before ngInclude
|
||||||
|
([0e50810c](https://github.com/angular/angular.js/commit/0e50810c53428f4c1f5bfdba9599df54cb7a6c6e),
|
||||||
|
[#5167](https://github.com/angular/angular.js/issues/5167), [#5208](https://github.com/angular/angular.js/issues/5208))
|
||||||
|
- **ngSanitize:** prefer textContent to innerText to avoid layout trashing
|
||||||
|
([bf1972dc](https://github.com/angular/angular.js/commit/bf1972dc1e8ffbeaddfa53df1d49bc5a2177f09c))
|
||||||
|
|
||||||
|
|
||||||
|
## Performance Improvements
|
||||||
|
|
||||||
|
- **$parse:** micro-optimization for ensureSafeObject function
|
||||||
|
([689dfb16](https://github.com/angular/angular.js/commit/689dfb167924a61aef444ce7587fb987d8080990),
|
||||||
|
[#5246](https://github.com/angular/angular.js/issues/5246))
|
||||||
|
- **Scope:** short-circuit after dirty-checking last dirty watcher
|
||||||
|
([d070450c](https://github.com/angular/angular.js/commit/d070450cd2b3b3a3aa34b69d3fa1f4cc3be025dd),
|
||||||
|
[#5272](https://github.com/angular/angular.js/issues/5272), [#5287](https://github.com/angular/angular.js/issues/5287))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="1.2.3"></a>
|
<a name="1.2.3"></a>
|
||||||
# 1.2.3 unicorn-zapper (2013-11-27)
|
# 1.2.3 unicorn-zapper (2013-11-27)
|
||||||
|
|
||||||
|
|
@ -4226,3 +4595,6 @@ with the `$route` service
|
||||||
[module]: http://docs-next.angularjs.org/api/angular.mock.module
|
[module]: http://docs-next.angularjs.org/api/angular.mock.module
|
||||||
[guide2.di]: http://docs-next.angularjs.org/guide/dev_guide.di
|
[guide2.di]: http://docs-next.angularjs.org/guide/dev_guide.di
|
||||||
[jqLite2]: http://docs.angularjs.org/#!/api/angular.element
|
[jqLite2]: http://docs.angularjs.org/#!/api/angular.element
|
||||||
|
|
||||||
|
|
||||||
|
[](https://github.com/igrigorik/ga-beacon)
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,19 @@ duplication of work, and help you to craft the change so that it is successfully
|
||||||
project.
|
project.
|
||||||
* **Small Changes** can be crafted and submitted to [GitHub Repository][github] as a Pull Request.
|
* **Small Changes** can be crafted and submitted to [GitHub Repository][github] as a Pull Request.
|
||||||
|
|
||||||
|
|
||||||
|
## Want a Doc Fix?
|
||||||
|
If you want to help improve the docs, it's a good idea to let others know what you're working on to
|
||||||
|
minimize duplication of effort. Before starting, check out the issue queue for [Milestone:Docs Only](https://github.com/angular/angular.js/issues?milestone=24&state=open).
|
||||||
|
Comment on an issue to let others know what you're working on, or create a new issue if your work
|
||||||
|
doesn't fit within the scope of any of the existing doc fix projects.
|
||||||
|
|
||||||
|
For large fixes, please build and test the documentation before submitting the PR to be sure you haven't
|
||||||
|
accidentally introduced any layout or formatting issues.You should also make sure that your commit message
|
||||||
|
is labeled "docs:" and follows the **Git Commit Guidelines** outlined below.
|
||||||
|
|
||||||
|
If you're just making a small change, don't worry about filing an issue first. Use the friendly blue "Improve this doc" button at the top right of the doc page to fork the repository in-place and make a quick change on the fly.
|
||||||
|
|
||||||
## Submission Guidelines
|
## Submission Guidelines
|
||||||
|
|
||||||
### Submitting an Issue
|
### Submitting an Issue
|
||||||
|
|
@ -67,7 +80,7 @@ Before you submit your pull request consider the following guidelines:
|
||||||
```
|
```
|
||||||
|
|
||||||
* Create your patch, including appropriate test cases.
|
* Create your patch, including appropriate test cases.
|
||||||
* Follow our Coding Rules
|
* Follow our [Coding Rules](#coding-rules)
|
||||||
* Commit your changes and create a descriptive commit message (the
|
* Commit your changes and create a descriptive commit message (the
|
||||||
commit message is used to generate release notes, please check out our
|
commit message is used to generate release notes, please check out our
|
||||||
[commit message conventions](#commit-message-format) and our commit message presubmit hook
|
[commit message conventions](#commit-message-format) and our commit message presubmit hook
|
||||||
|
|
@ -185,6 +198,7 @@ Must be one of the following:
|
||||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
|
||||||
semi-colons, etc)
|
semi-colons, etc)
|
||||||
* **refactor**: A code change that neither fixes a bug or adds a feature
|
* **refactor**: A code change that neither fixes a bug or adds a feature
|
||||||
|
* **perf**: A code change that improves performance
|
||||||
* **test**: Adding missing tests
|
* **test**: Adding missing tests
|
||||||
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
|
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
|
||||||
generation
|
generation
|
||||||
|
|
@ -244,3 +258,5 @@ You can find out more detailed information about contributing in the
|
||||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||||
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
||||||
|
|
||||||
|
[](https://github.com/igrigorik/ga-beacon)
|
||||||
|
|
|
||||||
57
Gruntfile.js
57
Gruntfile.js
|
|
@ -4,16 +4,8 @@ var path = require('path');
|
||||||
|
|
||||||
module.exports = function(grunt) {
|
module.exports = function(grunt) {
|
||||||
//grunt plugins
|
//grunt plugins
|
||||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
require('load-grunt-tasks')(grunt);
|
||||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-connect');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
|
||||||
grunt.loadNpmTasks('grunt-jasmine-node');
|
|
||||||
grunt.loadNpmTasks('grunt-ddescribe-iit');
|
|
||||||
grunt.loadNpmTasks('grunt-merge-conflict');
|
|
||||||
grunt.loadNpmTasks('grunt-parallel');
|
|
||||||
grunt.loadNpmTasks('grunt-shell');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
|
||||||
grunt.loadTasks('lib/grunt');
|
grunt.loadTasks('lib/grunt');
|
||||||
|
|
||||||
var NG_VERSION = util.getVersion();
|
var NG_VERSION = util.getVersion();
|
||||||
|
|
@ -86,9 +78,7 @@ module.exports = function(grunt) {
|
||||||
jqlite: 'karma-jqlite.conf.js',
|
jqlite: 'karma-jqlite.conf.js',
|
||||||
jquery: 'karma-jquery.conf.js',
|
jquery: 'karma-jquery.conf.js',
|
||||||
docs: 'karma-docs.conf.js',
|
docs: 'karma-docs.conf.js',
|
||||||
modules: 'karma-modules.conf.js',
|
modules: 'karma-modules.conf.js'
|
||||||
//NOTE run grunt test:e2e instead and it will start a webserver for you
|
|
||||||
end2end: 'karma-e2e.conf.js'
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -100,51 +90,56 @@ module.exports = function(grunt) {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
runprotractor: {
|
||||||
|
normal: 'protractor-conf.js'
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
clean: {
|
clean: {
|
||||||
build: ['build'],
|
build: ['build'],
|
||||||
tmp: ['tmp']
|
tmp: ['tmp']
|
||||||
},
|
},
|
||||||
|
|
||||||
jshint: {
|
jshint: {
|
||||||
|
options: {
|
||||||
|
jshintrc: true,
|
||||||
|
},
|
||||||
ng: {
|
ng: {
|
||||||
files: { src: files['angularSrc'] },
|
files: { src: files['angularSrc'] },
|
||||||
options: { jshintrc: 'src/.jshintrc' }
|
|
||||||
},
|
},
|
||||||
ngAnimate: {
|
ngAnimate: {
|
||||||
files: { src: 'src/ngAnimate/**/*.js' },
|
files: { src: 'src/ngAnimate/**/*.js' },
|
||||||
options: { jshintrc: 'src/ngAnimate/.jshintrc' }
|
|
||||||
},
|
},
|
||||||
ngCookies: {
|
ngCookies: {
|
||||||
files: { src: 'src/ngCookies/**/*.js' },
|
files: { src: 'src/ngCookies/**/*.js' },
|
||||||
options: { jshintrc: 'src/ngCookies/.jshintrc' }
|
|
||||||
},
|
},
|
||||||
ngLocale: {
|
ngLocale: {
|
||||||
files: { src: 'src/ngLocale/**/*.js' },
|
files: { src: 'src/ngLocale/**/*.js' },
|
||||||
options: { jshintrc: 'src/ngLocale/.jshintrc' }
|
|
||||||
},
|
},
|
||||||
ngMock: {
|
ngMock: {
|
||||||
files: { src: 'src/ngMock/**/*.js' },
|
files: { src: 'src/ngMock/**/*.js' },
|
||||||
options: { jshintrc: 'src/ngMock/.jshintrc' }
|
|
||||||
},
|
},
|
||||||
ngResource: {
|
ngResource: {
|
||||||
files: { src: 'src/ngResource/**/*.js' },
|
files: { src: 'src/ngResource/**/*.js' },
|
||||||
options: { jshintrc: 'src/ngResource/.jshintrc' }
|
|
||||||
},
|
},
|
||||||
ngRoute: {
|
ngRoute: {
|
||||||
files: { src: 'src/ngRoute/**/*.js' },
|
files: { src: 'src/ngRoute/**/*.js' },
|
||||||
options: { jshintrc: 'src/ngRoute/.jshintrc' }
|
|
||||||
},
|
},
|
||||||
ngSanitize: {
|
ngSanitize: {
|
||||||
files: { src: 'src/ngSanitize/**/*.js' },
|
files: { src: 'src/ngSanitize/**/*.js' },
|
||||||
options: { jshintrc: 'src/ngSanitize/.jshintrc' }
|
|
||||||
},
|
},
|
||||||
ngScenario: {
|
ngScenario: {
|
||||||
files: { src: 'src/ngScenario/**/*.js' },
|
files: { src: 'src/ngScenario/**/*.js' },
|
||||||
options: { jshintrc: 'src/ngScenario/.jshintrc' }
|
|
||||||
},
|
},
|
||||||
ngTouch: {
|
ngTouch: {
|
||||||
files: { src: 'src/ngTouch/**/*.js' },
|
files: { src: 'src/ngTouch/**/*.js' },
|
||||||
options: { jshintrc: 'src/ngTouch/.jshintrc' }
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
jscs: {
|
||||||
|
src: ['src/**/*.js', 'test/**/*.js'],
|
||||||
|
options: {
|
||||||
|
config: ".jscs.json"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -275,18 +270,28 @@ module.exports = function(grunt) {
|
||||||
write: {
|
write: {
|
||||||
versionTXT: {file: 'build/version.txt', val: NG_VERSION.full},
|
versionTXT: {file: 'build/version.txt', val: NG_VERSION.full},
|
||||||
versionJSON: {file: 'build/version.json', val: JSON.stringify(NG_VERSION)}
|
versionJSON: {file: 'build/version.json', val: JSON.stringify(NG_VERSION)}
|
||||||
|
},
|
||||||
|
|
||||||
|
bump: {
|
||||||
|
options: {
|
||||||
|
files: ['package.json'],
|
||||||
|
commit: false,
|
||||||
|
createTag: false,
|
||||||
|
push: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//alias tasks
|
//alias tasks
|
||||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:e2e']);
|
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', '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: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:jquery', 'Run the jQuery unit tests with Karma', ['tests:jquery']);
|
||||||
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['tests:modules']);
|
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['tests:modules']);
|
||||||
grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
|
grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
|
||||||
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['tests:jqlite', 'tests:jquery', 'tests:modules']);
|
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['tests:jqlite', 'tests:jquery', 'tests:modules']);
|
||||||
grunt.registerTask('test:e2e', 'Run the end to end tests with Karma and keep a test server running in the background', ['connect:testserver', 'tests:end2end']);
|
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'runprotractor:normal']);
|
||||||
|
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
|
||||||
grunt.registerTask('test:docgen', ['jasmine_node']);
|
grunt.registerTask('test:docgen', ['jasmine_node']);
|
||||||
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']);
|
||||||
|
|
||||||
|
|
@ -294,6 +299,6 @@ module.exports = function(grunt) {
|
||||||
grunt.registerTask('webserver', ['connect:devserver']);
|
grunt.registerTask('webserver', ['connect:devserver']);
|
||||||
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||||
grunt.registerTask('package-without-bower', ['clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
grunt.registerTask('package-without-bower', ['clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'test:docgen']);
|
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
|
||||||
grunt.registerTask('default', ['package']);
|
grunt.registerTask('default', ['package']);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
The MIT License
|
The MIT License
|
||||||
|
|
||||||
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
|
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
23
README.closure.md
Normal file
23
README.closure.md
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
Using AngularJS with the Closure Compiler
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
The Closure Compiler project contains externs definitions for AngularJS
|
||||||
|
JavaScript in its `contrib/externs` directory.
|
||||||
|
|
||||||
|
The definitions contain externs for use with the Closure compiler (aka
|
||||||
|
JSCompiler). Passing these files to the --externs parameter of a compiler
|
||||||
|
pass allows using type annotations for AngularJS objects. For example,
|
||||||
|
Angular's $scope objects can be annotated as:
|
||||||
|
```js
|
||||||
|
/** @type {angular.Scope} */
|
||||||
|
var scope = $scope;
|
||||||
|
```
|
||||||
|
|
||||||
|
This allows JSCompiler to type check accesses to scope, give warnings about
|
||||||
|
missing methods or incorrect arguments, and also prevents renaming of property
|
||||||
|
accesses with advanced compilation.
|
||||||
|
|
||||||
|
The externs are incomplete and maintained on an as-needed basis, but strive to
|
||||||
|
be correct. Externs for individual modules should be added in separate files.
|
||||||
|
|
||||||
|
See https://developers.google.com/closure/compiler/
|
||||||
|
|
@ -7,7 +7,7 @@ syntax to express your application’s components clearly and succinctly. It au
|
||||||
synchronizes data from your UI (view) with your JavaScript objects (model) through 2-way data
|
synchronizes data from your UI (view) with your JavaScript objects (model) through 2-way data
|
||||||
binding. To help you structure your application better and make it easy to test, AngularJS teaches
|
binding. To help you structure your application better and make it easy to test, AngularJS teaches
|
||||||
the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with
|
the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with
|
||||||
server-side communication, taming async callbacks with promises and deferreds; and make client-side
|
server-side communication, taming async callbacks with promises and deferreds; and makes client-side
|
||||||
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
|
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
|
||||||
it makes development fun!
|
it makes development fun!
|
||||||
|
|
||||||
|
|
@ -38,3 +38,7 @@ To execute end-to-end (e2e) tests, use:
|
||||||
|
|
||||||
To learn more about the grunt tasks, run `grunt --help` and also read our
|
To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
|
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
|
||||||
|
|
||||||
|
|
||||||
|
[](https://github.com/igrigorik/ga-beacon)
|
||||||
|
|
||||||
|
|
|
||||||
63
TRIAGING.md
Normal file
63
TRIAGING.md
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Triage new issues/PRs on github
|
||||||
|
|
||||||
|
This document shows the steps the Angular team is using to triage issues.
|
||||||
|
The labels are used later on for planning releases.
|
||||||
|
|
||||||
|
## Tips ##
|
||||||
|
|
||||||
|
* install [github pr helper extension](https://github.com/petebacondarwin/github-pr-helper) and become 356% more productive
|
||||||
|
* Label "resolution:*"
|
||||||
|
* these tags can be used for labeling a closed issue/PR with a reason why it was closed. (we can add reasons as we need them, right there are only a few rejection reasons. it doesn't make sense to label issues that were fixed or prs that were merged)
|
||||||
|
|
||||||
|
|
||||||
|
## Automatic processing ##
|
||||||
|
|
||||||
|
We have automatic tools (e.g. Mary Poppins) that automatically add comments / labels to issues and PRs.
|
||||||
|
The following is done automatically and should not be done manually:
|
||||||
|
|
||||||
|
* Label "cla: yes" or "cla: no" for pull requests
|
||||||
|
|
||||||
|
## Process ##
|
||||||
|
|
||||||
|
1. Open list of [non triaged issues](https://github.com/angular/angular.js/issues?direction=desc&milestone=none&page=1&sort=created&state=open)
|
||||||
|
1. Assign yourself: Pick an issue that is not assigned to anyone and assign it to you
|
||||||
|
1. Assign milestone:
|
||||||
|
* "Docs only" milestone - for documentation PR -> **Done**.
|
||||||
|
* Current/next milestone - regressions
|
||||||
|
* 1.2.x - everything else
|
||||||
|
1. Label "GH: *" (to be automated via Mary Poppins)
|
||||||
|
* PR - issue is a PR
|
||||||
|
* issue - otherwise
|
||||||
|
1. Bugs:
|
||||||
|
* Label "Type: Bug"
|
||||||
|
* Label "Type: Regression" - if the bug is a regression
|
||||||
|
* Duplicate? - Check if there are comments pointing out that this is a dupe, if they do exist verify that this is indeed a dupe and close it and go to the last step
|
||||||
|
* Reproducible? - Steps to reproduce the bug are clear, if not ask for clarification (ideally plunker or fiddle)
|
||||||
|
* Reproducible on master? - http://code.angularjs.org/snapshot/
|
||||||
|
|
||||||
|
1. Non bugs:
|
||||||
|
* Label "Type: Feature" or "Type: Chore" or "Type: Perf"
|
||||||
|
* Label "needs: breaking change" - if needed
|
||||||
|
* Label "needs: public api" - if a new public api is needed
|
||||||
|
* Understandable? - verify if the description of the request is clear. if not ask for clarification
|
||||||
|
* Goals of angular core? - Often new features should be implemented as a third-party module rather than an addition to the core.
|
||||||
|
|
||||||
|
1. Label "component: *"
|
||||||
|
* In rare cases, it's ok to have multiple components.
|
||||||
|
1. Label "impact: *"
|
||||||
|
* small - obscure issue affecting one or handful of developers
|
||||||
|
* medium - impacts some usage patterns
|
||||||
|
* large - impacts most or all of angular apps
|
||||||
|
1. Label "complexity: *"
|
||||||
|
* small - trivial change
|
||||||
|
* medium - non-trivial but straightforward change
|
||||||
|
* large - changes to many components in angular or any changes to $compile, ngRepeat or other "fun" components
|
||||||
|
1. Label "PRs plz!" for "GH: issue"
|
||||||
|
* if complexity is small or medium and the problem as well as solution are well captured in the issue
|
||||||
|
1. Label "origin: google" for issues from Google
|
||||||
|
1. Label "high priority" for security issues, major performance regressions or memory leaks
|
||||||
|
|
||||||
|
1. Unassign yourself from the issue
|
||||||
|
|
||||||
|
|
||||||
|
[](https://github.com/igrigorik/ga-beacon)
|
||||||
|
|
@ -142,6 +142,7 @@ var writeChangelog = function(stream, commits, version) {
|
||||||
var sections = {
|
var sections = {
|
||||||
fix: {},
|
fix: {},
|
||||||
feat: {},
|
feat: {},
|
||||||
|
perf: {},
|
||||||
breaks: {}
|
breaks: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -169,6 +170,7 @@ var writeChangelog = function(stream, commits, version) {
|
||||||
stream.write(util.format(HEADER_TPL, version, version, currentDate()));
|
stream.write(util.format(HEADER_TPL, version, version, currentDate()));
|
||||||
printSection(stream, 'Bug Fixes', sections.fix);
|
printSection(stream, 'Bug Fixes', sections.fix);
|
||||||
printSection(stream, 'Features', sections.feat);
|
printSection(stream, 'Features', sections.feat);
|
||||||
|
printSection(stream, 'Performance Improvements', sections.perf);
|
||||||
printSection(stream, 'Breaking Changes', sections.breaks, false);
|
printSection(stream, 'Breaking Changes', sections.breaks, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,7 +188,7 @@ var getPreviousTag = function() {
|
||||||
var generate = function(version, file) {
|
var generate = function(version, file) {
|
||||||
getPreviousTag().then(function(tag) {
|
getPreviousTag().then(function(tag) {
|
||||||
console.log('Reading git log since', tag);
|
console.log('Reading git log since', tag);
|
||||||
readGitLog('^fix|^feat|BREAKING', tag).then(function(commits) {
|
readGitLog('^fix|^feat|^perf|BREAKING', tag).then(function(commits) {
|
||||||
console.log('Parsed', commits.length, 'commits');
|
console.log('Parsed', commits.length, 'commits');
|
||||||
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
|
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
|
||||||
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
|
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
This file contains externs for use with the Closure compiler (aka JSCompiler).
|
|
||||||
Passing these files to the --externs parameter of a compiler pass allows using
|
|
||||||
type annotations for AngularJS objects. For example, Angular's $scope objects
|
|
||||||
can be annotated as:
|
|
||||||
```js
|
|
||||||
/** @type {angular.Scope} */
|
|
||||||
var scope = $scope;
|
|
||||||
```
|
|
||||||
|
|
||||||
This allows JSCompiler to type check accesses to scope, give warnings about
|
|
||||||
missing methods or incorrect arguments, and also prevents renaming of property
|
|
||||||
accesses with advanced compilation.
|
|
||||||
|
|
||||||
The externs are incomplete and maintained on an as-needed basis, but strive to
|
|
||||||
be correct. Externs for individual modules should be added in separate files.
|
|
||||||
|
|
||||||
See https://developers.google.com/closure/compiler/
|
|
||||||
1921
closure/angular.js
vendored
1921
closure/angular.js
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -9,14 +9,3 @@
|
||||||
ng\:form {
|
ng\:form {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The styles below ensure that the CSS transition will ALWAYS
|
|
||||||
* animate and close. A nasty bug occurs with CSS transitions where
|
|
||||||
* when the active class isn't set, or if the active class doesn't
|
|
||||||
* contain any styles to transition to, then, if ngAnimate is used,
|
|
||||||
* it will appear as if the webpage is broken due to the forever hanging
|
|
||||||
* animations. The border-spacing (!ie) and zoom (ie) CSS properties are
|
|
||||||
* used below since they trigger a transition without making the browser
|
|
||||||
* animate anything and they're both highly underused CSS properties */
|
|
||||||
.ng-animate-start { border-spacing:1px 1px; -ms-zoom:1.0001; }
|
|
||||||
.ng-animate-active { border-spacing:0px 0px; -ms-zoom:1; }
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ describe('Docs Annotations', function() {
|
||||||
var body;
|
var body;
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
body = angular.element(document.body);
|
body = angular.element(document.body);
|
||||||
body.html('');
|
body.empty();
|
||||||
});
|
});
|
||||||
|
|
||||||
var normalizeHtml = function(html) {
|
var normalizeHtml = function(html) {
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,7 @@ describe("docsSearch", function() {
|
||||||
results[0] = { section: 'tutorial', shortName: 'item one', keywords: 'item, one, 1' };
|
results[0] = { section: 'tutorial', shortName: 'item one', keywords: 'item, one, 1' };
|
||||||
results[1] = { section: 'tutorial', shortName: 'item man', keywords: 'item, man' };
|
results[1] = { section: 'tutorial', shortName: 'item man', keywords: 'item, man' };
|
||||||
results[2] = { section: 'api', shortName: 'item other', keywords: 'item, other' };
|
results[2] = { section: 'api', shortName: 'item other', keywords: 'item, other' };
|
||||||
results[3] = { section: 'cookbook', shortName: 'item cookbook', keywords: 'item, other' };
|
results[3] = { section: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
|
||||||
results[4] = { section: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
|
|
||||||
|
|
||||||
$provide.value('NG_PAGES', results);
|
$provide.value('NG_PAGES', results);
|
||||||
$provide.factory('lunrSearch', function() {
|
$provide.factory('lunrSearch', function() {
|
||||||
|
|
@ -41,19 +40,14 @@ describe("docsSearch", function() {
|
||||||
expect(items['api'].length).toBe(2);
|
expect(items['api'].length).toBe(2);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it("should place cookbook items in the tutorial", inject(function(docsSearch) {
|
|
||||||
var items = docsSearch('item');
|
|
||||||
expect(items['tutorial'].length).toBe(3);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should return all results without a search", inject(function(docsSearch) {
|
it("should return all results without a search", inject(function(docsSearch) {
|
||||||
var items = docsSearch();
|
var items = docsSearch();
|
||||||
expect(items['tutorial'].length).toBe(3);
|
expect(items['tutorial'].length).toBe(2);
|
||||||
expect(items['api'].length).toBe(2);
|
expect(items['api'].length).toBe(2);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it("should store values with and without a ng prefix", inject(function(docsSearch) {
|
it("should store values with and without a ng prefix", inject(function(docsSearch) {
|
||||||
expect(interceptedLunrResults[4].title).toBe('ngRepeat repeat');
|
expect(interceptedLunrResults[3].title).toBe('ngRepeat repeat');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ function escape(text) {
|
||||||
function setHtmlIe8SafeWay(element, html) {
|
function setHtmlIe8SafeWay(element, html) {
|
||||||
var newElement = angular.element('<pre>' + html + '</pre>');
|
var newElement = angular.element('<pre>' + html + '</pre>');
|
||||||
|
|
||||||
element.html('');
|
element.empty();
|
||||||
element.append(newElement.contents());
|
element.append(newElement.contents());
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
@ -215,17 +215,7 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
||||||
}];
|
}];
|
||||||
this.html5Mode = angular.noop;
|
this.html5Mode = angular.noop;
|
||||||
});
|
});
|
||||||
$provide.decorator('$timeout', ['$rootScope', '$delegate', function($rootScope, $delegate) {
|
|
||||||
return angular.extend(function(fn, delay) {
|
|
||||||
if (delay && delay > 50) {
|
|
||||||
return setTimeout(function() {
|
|
||||||
$rootScope.$apply(fn);
|
|
||||||
}, delay);
|
|
||||||
} else {
|
|
||||||
return $delegate.apply(this, arguments);
|
|
||||||
}
|
|
||||||
}, $delegate);
|
|
||||||
}]);
|
|
||||||
$provide.decorator('$rootScope', ['$delegate', function($delegate) {
|
$provide.decorator('$rootScope', ['$delegate', function($delegate) {
|
||||||
embedRootScope = $delegate;
|
embedRootScope = $delegate;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
# AngularJS API Docs
|
# AngularJS API Docs
|
||||||
Welcome to the AngularJS API docs page. These pages contain the AngularJS reference materials for version <strong ng-bind="version"></strong>.
|
Welcome to the AngularJS API docs page. These pages contain the AngularJS reference materials for version <strong ng-bind="version"></strong>.
|
||||||
|
|
||||||
The documentation is organized into **modules** which contain various components of an AngularJS application.
|
The documentation is organized into **{@link guide/module modules}** which contain various components of an AngularJS application.
|
||||||
These components are directives, services, filters, providers, types, global APIs and testing mocks.
|
These components are {@link guide/directive directives}, {@link guide/dev_guide.services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates types}, global APIs and testing mocks.
|
||||||
|
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
**Angular Namespaces `$` and `$$`**
|
**Angular Namespaces `$` and `$$`**
|
||||||
|
|
@ -63,7 +63,7 @@ This module is provided by default and contains the core components of AngularJS
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<p>
|
<p>
|
||||||
The core filters available in the ng module are used to transform template data before it is renders within directives and expressions.
|
The core filters available in the ng module are used to transform template data before it is rendered within directives and expressions.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Some examples include:
|
Some examples include:
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@
|
||||||
@description
|
@description
|
||||||
|
|
||||||
# ng (core module)
|
# ng (core module)
|
||||||
The ng module is loaded by default when an AngularJS application is started. The module itself contains the essential components to for an AngularJS application to function. The table below lists a high level breakdown of each of the services/factories, filters, directives and testing components available within this core module.
|
The ng module is loaded by default when an AngularJS application is started. The module itself
|
||||||
|
contains the essential components for an AngularJS application to function. The table below
|
||||||
|
lists a high level breakdown of each of the services/factories, filters, directives and testing
|
||||||
|
components available within this core module.
|
||||||
|
|
||||||
<div doc-module-components="ng"></div>
|
<div doc-module-components="ng"></div>
|
||||||
|
|
|
||||||
|
|
@ -1,122 +0,0 @@
|
||||||
@ngdoc overview
|
|
||||||
@name Cookbook: Advanced Form
|
|
||||||
@description
|
|
||||||
|
|
||||||
Here we extend the basic form example to include common features such as reverting, dirty state
|
|
||||||
detection, and preventing invalid form submission.
|
|
||||||
|
|
||||||
<doc:example>
|
|
||||||
<doc:source>
|
|
||||||
<script>
|
|
||||||
function UserForm($scope) {
|
|
||||||
var master = {
|
|
||||||
name: 'John Smith',
|
|
||||||
address:{
|
|
||||||
line1: '123 Main St.',
|
|
||||||
city:'Anytown',
|
|
||||||
state:'AA',
|
|
||||||
zip:'12345'
|
|
||||||
},
|
|
||||||
contacts:[
|
|
||||||
{type:'phone', value:'1(234) 555-1212'}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.state = /^\w\w$/;
|
|
||||||
$scope.zip = /^\d\d\d\d\d$/;
|
|
||||||
|
|
||||||
$scope.cancel = function() {
|
|
||||||
$scope.form = angular.copy(master);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.save = function() {
|
|
||||||
master = $scope.form;
|
|
||||||
$scope.cancel();
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.addContact = function() {
|
|
||||||
$scope.form.contacts.push({type:'', value:''});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.removeContact = function(index) {
|
|
||||||
$scope.form.contacts.splice(index, 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.isCancelDisabled = function() {
|
|
||||||
return angular.equals(master, $scope.form);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.isSaveDisabled = function() {
|
|
||||||
return $scope.myForm.$invalid || angular.equals(master, $scope.form);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.cancel();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<div ng-controller="UserForm">
|
|
||||||
|
|
||||||
<form name="myForm">
|
|
||||||
|
|
||||||
<label>Name:</label><br/>
|
|
||||||
<input type="text" ng-model="form.name" required/> <br/><br/>
|
|
||||||
|
|
||||||
<label>Address:</label> <br/>
|
|
||||||
<input type="text" ng-model="form.address.line1" size="33" required/> <br/>
|
|
||||||
<input type="text" ng-model="form.address.city" size="12" required/>,
|
|
||||||
<input type="text" ng-model="form.address.state" size="2"
|
|
||||||
ng-pattern="state" required/>
|
|
||||||
<input type="text" ng-model="form.address.zip" size="5"
|
|
||||||
ng-pattern="zip" required/><br/><br/>
|
|
||||||
|
|
||||||
<label>Contacts:</label>
|
|
||||||
[ <a href="" ng-click="addContact()">add</a> ]
|
|
||||||
<div ng-repeat="contact in form.contacts">
|
|
||||||
<select ng-model="contact.type">
|
|
||||||
<option>email</option>
|
|
||||||
<option>phone</option>
|
|
||||||
<option>pager</option>
|
|
||||||
<option>IM</option>
|
|
||||||
</select>
|
|
||||||
<input type="text" ng-model="contact.value" required/>
|
|
||||||
[ <a href="" ng-click="removeContact($index)">X</a> ]
|
|
||||||
</div>
|
|
||||||
<button ng-click="cancel()" ng-disabled="isCancelDisabled()">Cancel</button>
|
|
||||||
<button ng-click="save()" ng-disabled="isSaveDisabled()">Save</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<hr/>
|
|
||||||
Debug View:
|
|
||||||
<pre>form={{form}}</pre>
|
|
||||||
</div>
|
|
||||||
</doc:source>
|
|
||||||
<doc:scenario>
|
|
||||||
it('should enable save button', function() {
|
|
||||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
|
||||||
input('form.name').enter('');
|
|
||||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
|
||||||
input('form.name').enter('change');
|
|
||||||
expect(element(':button:contains(Save)').attr('disabled')).toBeFalsy();
|
|
||||||
element(':button:contains(Save)').click();
|
|
||||||
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
|
|
||||||
});
|
|
||||||
it('should enable cancel button', function() {
|
|
||||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
|
||||||
input('form.name').enter('change');
|
|
||||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeFalsy();
|
|
||||||
element(':button:contains(Cancel)').click();
|
|
||||||
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
|
|
||||||
expect(element(':input[ng\\:model="form.name"]').val()).toEqual('John Smith');
|
|
||||||
});
|
|
||||||
</doc:scenario>
|
|
||||||
</doc:example>
|
|
||||||
|
|
||||||
|
|
||||||
#Things to notice
|
|
||||||
|
|
||||||
* Cancel & save buttons are only enabled if the form is dirty — there is something to cancel or
|
|
||||||
save.
|
|
||||||
* Save button is only enabled if there are no validation errors on the form.
|
|
||||||
* Cancel reverts the form changes back to original state.
|
|
||||||
* Save updates the internal model of the form.
|
|
||||||
* Debug view shows the two models. One presented to the user form and the other being the pristine
|
|
||||||
copy master.
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
@ngdoc overview
|
|
||||||
@name Cookbook: Resources - Buzz
|
|
||||||
@description
|
|
||||||
|
|
||||||
External resources are URLs that provide JSON data, which are then rendered with the help of
|
|
||||||
templates. Angular has a resource factory that can be used to give names to the URLs and then
|
|
||||||
attach behavior to them. For example you can use the
|
|
||||||
{@link http://code.google.com/apis/buzz/v1/getting_started.html#background-operations| Google Buzz
|
|
||||||
API}
|
|
||||||
to retrieve Buzz activity and comments.
|
|
||||||
|
|
||||||
<doc:example>
|
|
||||||
<doc:source>
|
|
||||||
<script>
|
|
||||||
BuzzController.$inject = ['$scope', '$resource'];
|
|
||||||
function BuzzController($scope, $resource) {
|
|
||||||
$scope.userId = 'googlebuzz';
|
|
||||||
$scope.Activity = $resource(
|
|
||||||
'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
|
|
||||||
{alt: 'json', callback: 'JSON_CALLBACK'},
|
|
||||||
{ get: {method: 'JSONP', params: {visibility: '@self'}},
|
|
||||||
replies: {method: 'JSONP', params: {visibility: '@self', comments: '@comments'}}
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.fetch = function() {
|
|
||||||
$scope.activities = $scope.Activity.get({userId:this.userId});
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.expandReplies = function(activity) {
|
|
||||||
activity.replies = $scope.Activity.replies({userId: this.userId, activityId: activity.id});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<div ng-controller="BuzzController">
|
|
||||||
<input ng-model="userId"/>
|
|
||||||
<button ng-click="fetch()">fetch</button>
|
|
||||||
<hr/>
|
|
||||||
<div class="buzz" ng-repeat="item in activities.data.items">
|
|
||||||
<h1 style="font-size: 15px;">
|
|
||||||
<img ng-src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
|
||||||
<a ng-href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
|
|
||||||
<a href ng-click="expandReplies(item)" style="float: right;">
|
|
||||||
Expand replies: {{item.links.replies[0].count}}
|
|
||||||
</a>
|
|
||||||
</h1>
|
|
||||||
{{item.object.content | html}}
|
|
||||||
<div class="reply" ng-repeat="reply in item.replies.data.items" style="margin-left: 20px;">
|
|
||||||
<img ng-src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
|
||||||
<a ng-href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>:
|
|
||||||
{{reply.content | html}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</doc:source>
|
|
||||||
<doc:scenario>
|
|
||||||
xit('fetch buzz and expand', function() {
|
|
||||||
element(':button:contains(fetch)').click();
|
|
||||||
expect(repeater('div.buzz').count()).toBeGreaterThan(0);
|
|
||||||
element('.buzz a:contains(Expand replies):first').click();
|
|
||||||
expect(repeater('div.reply').count()).toBeGreaterThan(0);
|
|
||||||
});
|
|
||||||
</doc:scenario>
|
|
||||||
</doc:example>
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
@ngdoc overview
|
|
||||||
@name Cookbook: Deep Linking
|
|
||||||
@description
|
|
||||||
|
|
||||||
Deep linking allows you to encode the state of the application in the URL so that it can be
|
|
||||||
bookmarked and the application can be restored from the URL to the same state.
|
|
||||||
|
|
||||||
While Angular does not force you to deal with bookmarks in any particular way, it has services
|
|
||||||
which make the common case described here very easy to implement.
|
|
||||||
|
|
||||||
# Assumptions
|
|
||||||
|
|
||||||
Your application consists of a single HTML page which bootstraps the application. We will refer
|
|
||||||
to this page as the chrome.
|
|
||||||
Your application is divided into several screens (or views) which the user can visit. For example,
|
|
||||||
the home screen, settings screen, details screen, etc. For each of these screens, we would like to
|
|
||||||
assign a URL so that it can be bookmarked and later restored. Each of these screens will be
|
|
||||||
associated with a controller which define the screen's behavior. The most common case is that the
|
|
||||||
screen will be constructed from an HTML snippet, which we will refer to as the partial. Screens can
|
|
||||||
have multiple partials, but a single partial is the most common construct. This example makes the
|
|
||||||
partial boundary visible using a blue line.
|
|
||||||
|
|
||||||
You can make a routing table which shows which URL maps to which partial view template and which
|
|
||||||
controller.
|
|
||||||
|
|
||||||
# Example
|
|
||||||
|
|
||||||
In this example we have a simple app which consist of two screens:
|
|
||||||
|
|
||||||
* Welcome: url `welcome` Show the user contact information.
|
|
||||||
* Settings: url `settings` Show an edit screen for user contact information.
|
|
||||||
|
|
||||||
<example module="deepLinking" deps="angular-route.js, angular-sanitize.js">
|
|
||||||
<file name="script.js">
|
|
||||||
angular.module('deepLinking', ['ngRoute', 'ngSanitize'])
|
|
||||||
.config(function($routeProvider) {
|
|
||||||
$routeProvider.
|
|
||||||
when("/welcome", {templateUrl:'welcome.html', controller:WelcomeCntl}).
|
|
||||||
when("/settings", {templateUrl:'settings.html', controller:SettingsCntl});
|
|
||||||
});
|
|
||||||
|
|
||||||
AppCntl.$inject = ['$scope', '$route']
|
|
||||||
function AppCntl($scope, $route) {
|
|
||||||
$scope.$route = $route;
|
|
||||||
|
|
||||||
// initialize the model to something useful
|
|
||||||
$scope.person = {
|
|
||||||
name:'anonymous',
|
|
||||||
contacts:[{type:'email', url:'anonymous@example.com'}]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function WelcomeCntl($scope) {
|
|
||||||
$scope.greet = function() {
|
|
||||||
alert("Hello " + $scope.person.name);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function SettingsCntl($scope, $location) {
|
|
||||||
$scope.cancel = function() {
|
|
||||||
$scope.form = angular.copy($scope.person);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.save = function() {
|
|
||||||
angular.copy($scope.form, $scope.person);
|
|
||||||
$location.path('/welcome');
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.cancel();
|
|
||||||
}
|
|
||||||
</file>
|
|
||||||
<file name="style.css">
|
|
||||||
[ng-view] {
|
|
||||||
border: 1px solid blue;
|
|
||||||
margin: 0;
|
|
||||||
padding:1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.partial-info {
|
|
||||||
background-color: blue;
|
|
||||||
color: white;
|
|
||||||
padding: 3px;
|
|
||||||
}
|
|
||||||
</file>
|
|
||||||
<file name="index.html">
|
|
||||||
<div ng-controller="AppCntl">
|
|
||||||
<h1>Your App Chrome</h1>
|
|
||||||
[ <a href="welcome">Welcome</a> | <a href="settings">Settings</a> ]
|
|
||||||
<hr/>
|
|
||||||
<span class="partial-info">
|
|
||||||
Partial: {{$route.current.template}}
|
|
||||||
</span>
|
|
||||||
<div ng-view></div>
|
|
||||||
<small>Your app footer </small>
|
|
||||||
</div>
|
|
||||||
</file>
|
|
||||||
<file name="settings.html">
|
|
||||||
<label>Name:</label>
|
|
||||||
<input type="text" ng:model="form.name" required>
|
|
||||||
|
|
||||||
<div ng:repeat="contact in form.contacts">
|
|
||||||
<select ng:model="contact.type">
|
|
||||||
<option>url</option>
|
|
||||||
<option>email</option>
|
|
||||||
<option>phone</option>
|
|
||||||
</select>
|
|
||||||
<input type="text" ng:model="contact.url">
|
|
||||||
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button ng:click="cancel()">Cancel</button>
|
|
||||||
<button ng:click="save()">Save</button>
|
|
||||||
</file>
|
|
||||||
<file name="welcome.html">
|
|
||||||
Hello {{person.name}},
|
|
||||||
<div>
|
|
||||||
Your contact information:
|
|
||||||
<div ng:repeat="contact in person.contacts">{{contact.type}}:
|
|
||||||
<span ng-bind-html="contact.url|linky"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</file>
|
|
||||||
<file name="scenario.js">
|
|
||||||
it('should navigate to URL', function() {
|
|
||||||
element('a:contains(Welcome)').click();
|
|
||||||
expect(element('[ng-view]').text()).toMatch(/Hello anonymous/);
|
|
||||||
element('a:contains(Settings)').click();
|
|
||||||
input('form.name').enter('yourname');
|
|
||||||
element(':button:contains(Save)').click();
|
|
||||||
element('a:contains(Welcome)').click();
|
|
||||||
expect(element('[ng-view]').text()).toMatch(/Hello yourname/);
|
|
||||||
});
|
|
||||||
</file>
|
|
||||||
</example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Things to notice
|
|
||||||
|
|
||||||
* Routes are defined in the `AppCntl` class. The initialization of the controller causes the
|
|
||||||
initialization of the {@link api/ngRoute.$route $route} service with the proper URL
|
|
||||||
routes.
|
|
||||||
* The {@link api/ngRoute.$route $route} service then watches the URL and instantiates the
|
|
||||||
appropriate controller when the URL changes.
|
|
||||||
* The {@link api/ngRoute.directive:ngView ngView} widget loads the
|
|
||||||
view when the URL changes. It also sets the view scope to the newly instantiated controller.
|
|
||||||
* Changing the URL is sufficient to change the controller and view. It makes no difference whether
|
|
||||||
the URL is changed programmatically or by the user.
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
@ngdoc overview
|
|
||||||
@name Cookbook: Form
|
|
||||||
@description
|
|
||||||
|
|
||||||
A web application's main purpose is to present and gather data. For this reason Angular strives
|
|
||||||
to make both of these operations trivial. This example shows off how you can build a simple form to
|
|
||||||
allow a user to enter data.
|
|
||||||
|
|
||||||
|
|
||||||
<doc:example>
|
|
||||||
<doc:source>
|
|
||||||
<script>
|
|
||||||
function FormController($scope) {
|
|
||||||
var user = $scope.user = {
|
|
||||||
name: 'John Smith',
|
|
||||||
address:{line1: '123 Main St.', city:'Anytown', state:'AA', zip:'12345'},
|
|
||||||
contacts:[{type:'phone', value:'1(234) 555-1212'}]
|
|
||||||
};
|
|
||||||
$scope.state = /^\w\w$/;
|
|
||||||
$scope.zip = /^\d\d\d\d\d$/;
|
|
||||||
|
|
||||||
$scope.addContact = function() {
|
|
||||||
user.contacts.push({type:'email', value:''});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.removeContact = function(contact) {
|
|
||||||
for (var i = 0, ii = user.contacts.length; i < ii; i++) {
|
|
||||||
if (contact === user.contacts[i]) {
|
|
||||||
$scope.user.contacts.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<div ng-controller="FormController" class="example">
|
|
||||||
|
|
||||||
<label>Name:</label><br>
|
|
||||||
<input type="text" ng-model="user.name" required/> <br><br>
|
|
||||||
|
|
||||||
<label>Address:</label><br>
|
|
||||||
<input type="text" ng-model="user.address.line1" size="33" required> <br>
|
|
||||||
<input type="text" ng-model="user.address.city" size="12" required>,
|
|
||||||
<input type="text" ng-model="user.address.state"
|
|
||||||
ng-pattern="state" size="2" required>
|
|
||||||
<input type="text" ng-model="user.address.zip" size="5"
|
|
||||||
ng-pattern="zip" required><br><br>
|
|
||||||
|
|
||||||
<label>Phone:</label>
|
|
||||||
[ <a href="" ng-click="addContact()">add</a> ]
|
|
||||||
<div ng-repeat="contact in user.contacts">
|
|
||||||
<select ng-model="contact.type">
|
|
||||||
<option>email</option>
|
|
||||||
<option>phone</option>
|
|
||||||
<option>pager</option>
|
|
||||||
<option>IM</option>
|
|
||||||
</select>
|
|
||||||
<input type="text" ng-model="contact.value" required>
|
|
||||||
[ <a href="" ng-click="removeContact(contact)">X</a> ]
|
|
||||||
</div>
|
|
||||||
<hr/>
|
|
||||||
Debug View:
|
|
||||||
<pre>user={{user | json}}</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</doc:source>
|
|
||||||
<doc:scenario>
|
|
||||||
it('should show debug', function() {
|
|
||||||
expect(binding('user')).toMatch(/John Smith/);
|
|
||||||
});
|
|
||||||
it('should add contact', function() {
|
|
||||||
using('.example').element('a:contains(add)').click();
|
|
||||||
using('.example div:last').input('contact.value').enter('you@example.org');
|
|
||||||
expect(binding('user')).toMatch(/\(234\) 555\-1212/);
|
|
||||||
expect(binding('user')).toMatch(/you@example.org/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove contact', function() {
|
|
||||||
using('.example').element('a:contains(X)').click();
|
|
||||||
expect(binding('user')).not().toMatch(/\(234\) 555\-1212/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should validate zip', function() {
|
|
||||||
expect(using('.example').
|
|
||||||
element(':input[ng\\:model="user.address.zip"]').
|
|
||||||
prop('className')).not().toMatch(/ng-invalid/);
|
|
||||||
using('.example').input('user.address.zip').enter('abc');
|
|
||||||
expect(using('.example').
|
|
||||||
element(':input[ng\\:model="user.address.zip"]').
|
|
||||||
prop('className')).toMatch(/ng-invalid/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should validate state', function() {
|
|
||||||
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
|
|
||||||
.not().toMatch(/ng-invalid/);
|
|
||||||
using('.example').input('user.address.state').enter('XXX');
|
|
||||||
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
|
|
||||||
.toMatch(/ng-invalid/);
|
|
||||||
});
|
|
||||||
</doc:scenario>
|
|
||||||
</doc:example>
|
|
||||||
|
|
||||||
|
|
||||||
# Things to notice
|
|
||||||
|
|
||||||
* The user data model is initialized {@link api/ng.directive:ngController controller} and is
|
|
||||||
available in the {@link api/ng.$rootScope.Scope scope} with the initial data.
|
|
||||||
* For debugging purposes we have included a debug view of the model to better understand what
|
|
||||||
is going on.
|
|
||||||
* The {@link api/ng.directive:input input directives} simply refer
|
|
||||||
to the model and are data-bound.
|
|
||||||
* The inputs validate. (Try leaving them blank or entering non digits in the zip field)
|
|
||||||
* In your application you can simply read from or write to the model and the form will be updated.
|
|
||||||
* By clicking the 'add' link you are adding new items into the `user.contacts` array which are then
|
|
||||||
reflected in the view.
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
@ngdoc overview
|
|
||||||
@name Cookbook: Hello World
|
|
||||||
@description
|
|
||||||
|
|
||||||
<doc:example>
|
|
||||||
<doc:source>
|
|
||||||
<script>
|
|
||||||
function HelloCntl($scope) {
|
|
||||||
$scope.name = 'World';
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<div ng-controller="HelloCntl">
|
|
||||||
Your name: <input type="text" ng-model="name"/>
|
|
||||||
<hr/>
|
|
||||||
Hello {{name || "World"}}!
|
|
||||||
</div>
|
|
||||||
</doc:source>
|
|
||||||
<doc:scenario>
|
|
||||||
it('should change the binding when user enters text', function() {
|
|
||||||
expect(binding('name')).toEqual('World');
|
|
||||||
input('name').enter('angular');
|
|
||||||
expect(binding('name')).toEqual('angular');
|
|
||||||
});
|
|
||||||
</doc:scenario>
|
|
||||||
</doc:example>
|
|
||||||
|
|
||||||
# Things to notice
|
|
||||||
|
|
||||||
Take a look through the source and note:
|
|
||||||
|
|
||||||
* The script tag that {@link guide/bootstrap bootstraps} the Angular environment.
|
|
||||||
* The text {@link api/ng.directive:input input form control} which is
|
|
||||||
bound to the greeting name text.
|
|
||||||
* There is no need for listener registration and event firing on change events.
|
|
||||||
* The implicit presence of the `name` variable which is in the root {@link api/ng.$rootScope.Scope scope}.
|
|
||||||
* The double curly brace `{{markup}}`, which binds the name variable to the greeting text.
|
|
||||||
* The concept of {@link guide/databinding data binding}, which reflects any
|
|
||||||
changes to the
|
|
||||||
input field in the greeting text.
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
@ngdoc overview
|
|
||||||
@name Cookbook
|
|
||||||
@description
|
|
||||||
|
|
||||||
Welcome to the Angular cookbook. Here we will show you typical uses of Angular by example.
|
|
||||||
|
|
||||||
|
|
||||||
# Hello World
|
|
||||||
|
|
||||||
{@link helloworld Hello World}: The simplest possible application that demonstrates the
|
|
||||||
classic Hello World!
|
|
||||||
|
|
||||||
|
|
||||||
# Basic Form
|
|
||||||
|
|
||||||
{@link form Basic Form}: Displaying forms to the user for editing is the bread and butter
|
|
||||||
of web applications. Angular makes forms easy through bidirectional data binding.
|
|
||||||
|
|
||||||
|
|
||||||
# Advanced Form
|
|
||||||
|
|
||||||
{@link advancedform Advanced Form}: Taking the form example to the next level and
|
|
||||||
providing advanced features such as dirty detection, form reverting and submit disabling if
|
|
||||||
validation errors exist.
|
|
||||||
|
|
||||||
|
|
||||||
# Model View Controller
|
|
||||||
|
|
||||||
{@link mvc MVC}: Tic-Tac-Toe: Model View Controller (MVC) is a time-tested design pattern
|
|
||||||
to separate the behavior (JavaScript controller) from the presentation (HTML view). This
|
|
||||||
separation aids in maintainability and testability of your project.
|
|
||||||
|
|
||||||
|
|
||||||
# Multi-page App and Deep Linking
|
|
||||||
|
|
||||||
{@link deeplinking Deep Linking}: An AJAX application never navigates away from the
|
|
||||||
first page it loads. Instead, it changes the DOM of its single page. Eliminating full-page reloads
|
|
||||||
is what makes AJAX apps responsive, but it creates a problem in that apps with a single URL
|
|
||||||
prevent you from emailing links to a particular screen within your application.
|
|
||||||
|
|
||||||
Deep linking tries to solve this by changing the URL anchor without reloading a page, thus
|
|
||||||
allowing you to send links to specific screens in your app.
|
|
||||||
|
|
||||||
|
|
||||||
# Services
|
|
||||||
|
|
||||||
{@link api/ng Services}: Services are long lived objects in your applications that are
|
|
||||||
available across controllers. A collection of useful services are pre-bundled with Angular but you
|
|
||||||
will likely add your own. Services are initialized using dependency injection, which resolves the
|
|
||||||
order of initialization. This safeguards you from the perils of global state (a common way to
|
|
||||||
implement long lived objects).
|
|
||||||
|
|
||||||
|
|
||||||
# External Resources
|
|
||||||
|
|
||||||
{@link buzz Resources}: Web applications must be able to communicate with the external
|
|
||||||
services to get and update data. Resources are the abstractions of external URLs which are
|
|
||||||
specially tailored to Angular data binding.
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
@ngdoc overview
|
|
||||||
@name Cookbook: MVC
|
|
||||||
@description
|
|
||||||
|
|
||||||
MVC allows for a clean and testable separation between the behavior (controller) and the view
|
|
||||||
(HTML template). A Controller is just a JavaScript class which is grafted onto the scope of the
|
|
||||||
view. This makes it very easy for the controller and the view to share the model.
|
|
||||||
|
|
||||||
The model is a set of objects and primitives that are referenced from the Scope ($scope) object.
|
|
||||||
This makes it very easy to test the controller in isolation since one can simply instantiate the
|
|
||||||
controller and test without a view, because there is no connection between the controller and the
|
|
||||||
view.
|
|
||||||
|
|
||||||
|
|
||||||
<doc:example>
|
|
||||||
<doc:source>
|
|
||||||
<script>
|
|
||||||
function TicTacToeCntl($scope, $location) {
|
|
||||||
$scope.cellStyle= {
|
|
||||||
'height': '20px',
|
|
||||||
'width': '20px',
|
|
||||||
'border': '1px solid black',
|
|
||||||
'text-align': 'center',
|
|
||||||
'vertical-align': 'middle',
|
|
||||||
'cursor': 'pointer'
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.reset = function() {
|
|
||||||
$scope.board = [
|
|
||||||
['', '', ''],
|
|
||||||
['', '', ''],
|
|
||||||
['', '', '']
|
|
||||||
];
|
|
||||||
$scope.nextMove = 'X';
|
|
||||||
$scope.winner = '';
|
|
||||||
setUrl();
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.dropPiece = function(row, col) {
|
|
||||||
if (!$scope.winner && !$scope.board[row][col]) {
|
|
||||||
$scope.board[row][col] = $scope.nextMove;
|
|
||||||
$scope.nextMove = $scope.nextMove == 'X' ? 'O' : 'X';
|
|
||||||
setUrl();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.reset();
|
|
||||||
$scope.$watch(function() { return $location.search().board;}, readUrl);
|
|
||||||
|
|
||||||
function setUrl() {
|
|
||||||
var rows = [];
|
|
||||||
angular.forEach($scope.board, function(row) {
|
|
||||||
rows.push(row.join(','));
|
|
||||||
});
|
|
||||||
$location.search({board: rows.join(';') + '/' + $scope.nextMove});
|
|
||||||
}
|
|
||||||
|
|
||||||
function grade() {
|
|
||||||
var b = $scope.board;
|
|
||||||
$scope.winner =
|
|
||||||
row(0) || row(1) || row(2) ||
|
|
||||||
col(0) || col(1) || col(2) ||
|
|
||||||
diagonal(-1) || diagonal(1);
|
|
||||||
function row(row) { return same(b[row][0], b[row][1], b[row][2]);}
|
|
||||||
function col(col) { return same(b[0][col], b[1][col], b[2][col]);}
|
|
||||||
function diagonal(i) { return same(b[0][1-i], b[1][1], b[2][1+i]);}
|
|
||||||
function same(a, b, c) { return (a==b && b==c) ? a : '';};
|
|
||||||
}
|
|
||||||
|
|
||||||
function readUrl(value) {
|
|
||||||
if (value) {
|
|
||||||
value = value.split('/');
|
|
||||||
$scope.nextMove = value[1];
|
|
||||||
angular.forEach(value[0].split(';'), function(row, col){
|
|
||||||
$scope.board[col] = row.split(',');
|
|
||||||
});
|
|
||||||
grade();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h3>Tic-Tac-Toe</h3>
|
|
||||||
<div ng-controller="TicTacToeCntl">
|
|
||||||
Next Player: {{nextMove}}
|
|
||||||
<div class="winner" ng-show="winner">Player {{winner}} has won!</div>
|
|
||||||
<table class="board">
|
|
||||||
<tr ng-repeat="row in board track by $index" style="height:15px;">
|
|
||||||
<td ng-repeat="cell in row track by $index" ng-style="cellStyle"
|
|
||||||
ng-click="dropPiece($parent.$index, $index)">{{cell}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<button ng-click="reset()">reset board</button>
|
|
||||||
</div>
|
|
||||||
</doc:source>
|
|
||||||
<doc:scenario>
|
|
||||||
it('should play a game', function() {
|
|
||||||
piece(1, 1);
|
|
||||||
expect(binding('nextMove')).toEqual('O');
|
|
||||||
piece(3, 1);
|
|
||||||
expect(binding('nextMove')).toEqual('X');
|
|
||||||
piece(1, 2);
|
|
||||||
piece(3, 2);
|
|
||||||
piece(1, 3);
|
|
||||||
expect(element('.winner').text()).toEqual('Player X has won!');
|
|
||||||
});
|
|
||||||
|
|
||||||
function piece(row, col) {
|
|
||||||
element('.board tr:nth-child('+row+') td:nth-child('+col+')').click();
|
|
||||||
}
|
|
||||||
</doc:scenario>
|
|
||||||
</doc:example>
|
|
||||||
|
|
||||||
|
|
||||||
# Things to notice
|
|
||||||
|
|
||||||
* The controller is defined in JavaScript and has no reference to the rendering logic.
|
|
||||||
* The controller is instantiated by Angular and injected into the view.
|
|
||||||
* The controller can be instantiated in isolation (without a view) and the code will still execute.
|
|
||||||
This makes it very testable.
|
|
||||||
* The HTML view is a projection of the model. In the above example, the model is stored in the
|
|
||||||
board variable.
|
|
||||||
* All of the controller's properties (such as board and nextMove) are available to the view.
|
|
||||||
* Changing the model changes the view.
|
|
||||||
* The view can call any controller function.
|
|
||||||
* In this example, the `setUrl()` and `readUrl()` functions copy the game state to/from the URL's
|
|
||||||
hash so the browser's back button will undo game steps. See deep-linking. This example calls {@link
|
|
||||||
api/ng.$rootScope.Scope#methods_$watch $watch()} to set up a listener that invokes `readUrl()` when needed.
|
|
||||||
|
|
@ -11,7 +11,7 @@ For these reasons binding to event handler attributes (all attributes that start
|
||||||
|
|
||||||
An example code that would allow XSS vulnerability by evaluating user input in the window context could look like this:
|
An example code that would allow XSS vulnerability by evaluating user input in the window context could look like this:
|
||||||
```
|
```
|
||||||
<input ng-mode="username">
|
<input ng-model="username">
|
||||||
<div onclick="{{username}}">click me</div>
|
<div onclick="{{username}}">click me</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,5 @@
|
||||||
This error occurs in browsers that do not support XmlHttpRequest. AngularJS
|
This error occurs in browsers that do not support XmlHttpRequest. AngularJS
|
||||||
supports Safari, Chrome, Firefox, Opera, IE8 and higher, and mobile browsers
|
supports Safari, Chrome, Firefox, Opera, IE8 and higher, and mobile browsers
|
||||||
(Android, Chrome Mobile, iOS Safari). To avoid this error, use an officially
|
(Android, Chrome Mobile, iOS Safari). To avoid this error, use an officially
|
||||||
supported browser.
|
supported browser.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@
|
||||||
@description
|
@description
|
||||||
This error occurs when 'ngPattern' is passed an expression that isn't a regular expression or doesn't have the expected format.
|
This error occurs when 'ngPattern' is passed an expression that isn't a regular expression or doesn't have the expected format.
|
||||||
|
|
||||||
For more information on valid expression syntax, see 'ngPattern' in {@link api/ng.directive:select input} directive docs.
|
For more information on valid expression syntax, see 'ngPattern' in {@link api/ng.directive:input input} directive docs.
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ For example the issue can be triggered by this *invalid* code:
|
||||||
<div ng-repeat="value in [4, 4]"></div>
|
<div ng-repeat="value in [4, 4]"></div>
|
||||||
```
|
```
|
||||||
|
|
||||||
To resolve this error either ensure that the items in the collection have unique identity of use the `track by` syntax to specify how to track the association between models and DOM.
|
To resolve this error either ensure that the items in the collection have unique identity or use the `track by` syntax to specify how to track the association between models and DOM.
|
||||||
|
|
||||||
To resolve the example above can be resolved by using `track by $index`, which will cause the items to be keyed by their position in the array instead of their value:
|
To resolve the example above can be resolved by using `track by $index`, which will cause the items to be keyed by their position in the array instead of their value:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@
|
||||||
@fullName Orphan ngTransclude Directive
|
@fullName Orphan ngTransclude Directive
|
||||||
@description
|
@description
|
||||||
|
|
||||||
Occurs when an `ngTransclude` occurs without a transcluded ancesstor element.
|
Occurs when an `ngTransclude` occurs without a transcluded ancestor element.
|
||||||
|
|
||||||
This error often occurs when you have forgotten to set `transclude: true` in some directive definition, and then used `ngTranslude` in the driective's template.
|
This error often occurs when you have forgotten to set `transclude: true` in some directive definition, and then used `ngTransclude` in the directive's template.
|
||||||
|
|
||||||
To resolve, either remove the offending `ngTransclude` or check that `transclude: true` is included in the intended directive definition.
|
To resolve, either remove the offending `ngTransclude` or check that `transclude: true` is included in the intended directive definition.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,19 +21,19 @@ Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" ng-model="checked" style="float:left; margin-right:10px;"> Is Visible...
|
<input type="checkbox" ng-model="checked" style="float:left; margin-right:10px;"> Is Visible...
|
||||||
</label>
|
</label>
|
||||||
<div class="check-element animate-show-hide" ng-show="checked" style="clear:both;">
|
<div class="check-element sample-show-hide" ng-show="checked" style="clear:both;">
|
||||||
Visible...
|
Visible...
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</file>
|
</file>
|
||||||
<file name="animations.css">
|
<file name="animations.css">
|
||||||
.animate-show-hide {
|
.sample-show-hide {
|
||||||
padding:10px;
|
padding:10px;
|
||||||
border:1px solid black;
|
border:1px solid black;
|
||||||
background:white;
|
background:white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-show-hide.ng-hide-add, .animate-show-hide.ng-hide-remove {
|
.sample-show-hide.ng-hide-add, .sample-show-hide.ng-hide-remove {
|
||||||
-webkit-transition:all linear 0.5s;
|
-webkit-transition:all linear 0.5s;
|
||||||
-moz-transition:all linear 0.5s;
|
-moz-transition:all linear 0.5s;
|
||||||
-o-transition:all linear 0.5s;
|
-o-transition:all linear 0.5s;
|
||||||
|
|
@ -41,13 +41,13 @@ Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
|
||||||
display:block!important;
|
display:block!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-show-hide.ng-hide-add.ng-hide-add-active,
|
.sample-show-hide.ng-hide-add.ng-hide-add-active,
|
||||||
.animate-show-hide.ng-hide-remove {
|
.sample-show-hide.ng-hide-remove {
|
||||||
opacity:0;
|
opacity:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-show-hide.ng-hide-add,
|
.sample-show-hide.ng-hide-add,
|
||||||
.animate-show-hide.ng-hide-remove.ng-hide-remove-active {
|
.sample-show-hide.ng-hide-remove.ng-hide-remove-active {
|
||||||
opacity:1;
|
opacity:1;
|
||||||
}
|
}
|
||||||
</file>
|
</file>
|
||||||
|
|
@ -258,7 +258,7 @@ The table below explains in detail which animation events are triggered
|
||||||
| {@link api/ng.directive:ngInclude#usage_animations ngInclude} | enter and leave |
|
| {@link api/ng.directive:ngInclude#usage_animations ngInclude} | enter and leave |
|
||||||
| {@link api/ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave |
|
| {@link api/ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave |
|
||||||
| {@link api/ng.directive:ngIf#usage_animations ngIf} | enter and leave |
|
| {@link api/ng.directive:ngIf#usage_animations ngIf} | enter and leave |
|
||||||
| {@link api/ng.directive:ngShow#usage_animations ngClass or {{class}}} | add and remove |
|
| {@link api/ng.directive:ngClass#usage_animations ngClass or {{class}}} | add and remove |
|
||||||
| {@link api/ng.directive:ngShow#usage_animations ngShow & ngHide} | add and remove (the ng-hide class value) |
|
| {@link api/ng.directive:ngShow#usage_animations ngShow & ngHide} | add and remove (the ng-hide class value) |
|
||||||
|
|
||||||
For a full breakdown of the steps involved during each animation event, refer to the {@link api/ngAnimate.$animate API docs}.
|
For a full breakdown of the steps involved during each animation event, refer to the {@link api/ngAnimate.$animate API docs}.
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,7 @@ This should help give you an idea of what Angular does internally.
|
||||||
<pre>
|
<pre>
|
||||||
var $compile = ...; // injected into your code
|
var $compile = ...; // injected into your code
|
||||||
var scope = ...;
|
var scope = ...;
|
||||||
|
var parent = ...; // DOM element where the compiled template can be appended
|
||||||
|
|
||||||
var html = '<div ng-bind="exp"></div>';
|
var html = '<div ng-bind="exp"></div>';
|
||||||
|
|
||||||
|
|
@ -200,7 +201,10 @@ This should help give you an idea of what Angular does internally.
|
||||||
var linkFn = $compile(template);
|
var linkFn = $compile(template);
|
||||||
|
|
||||||
// Step 3: link the compiled template with the scope.
|
// Step 3: link the compiled template with the scope.
|
||||||
linkFn(scope);
|
var element = linkFn(scope);
|
||||||
|
|
||||||
|
// Step 4: Append to DOM (optional)
|
||||||
|
parent.appendChild(element);
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### The difference between Compile and Link
|
### The difference between Compile and Link
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ The following graphic shows how everything works together after we introduced th
|
||||||
# View independent business logic: Services
|
# View independent business logic: Services
|
||||||
|
|
||||||
Right now, the `InvoiceController` contains all logic of our example. When the application grows it
|
Right now, the `InvoiceController` contains all logic of our example. When the application grows it
|
||||||
is a good practise to move view independent logic from the controller into a so called
|
is a good practice to move view independent logic from the controller into a so called
|
||||||
<a name="service">"{@link dev_guide.services service}"</a>, so it can be reused by other parts
|
<a name="service">"{@link dev_guide.services service}"</a>, so it can be reused by other parts
|
||||||
of the application as well. Later on, we could also change that service to load the exchange rates
|
of the application as well. Later on, we could also change that service to load the exchange rates
|
||||||
from the web, e.g. by calling the Yahoo Finance API, without changing the controller.
|
from the web, e.g. by calling the Yahoo Finance API, without changing the controller.
|
||||||
|
|
@ -270,10 +270,10 @@ When Angular starts, it will use the configuration of the module with the name d
|
||||||
including the configuration of all modules that this module depends on.
|
including the configuration of all modules that this module depends on.
|
||||||
|
|
||||||
In the example above:
|
In the example above:
|
||||||
The template contains the directive `ng-app="invoice"`. This tells Angular
|
The template contains the directive `ng-app="invoice2"`. This tells Angular
|
||||||
to use the `invoice` module as the main module for the application.
|
to use the `invoice` module as the main module for the application.
|
||||||
The code snippet `angular.module('invoice', ['finance'])` specifies that the `invoice` module depends on the
|
The code snippet `angular.module('invoice2', ['finance2'])` specifies that the `invoice2` module depends on the
|
||||||
`finance` module. By this, Angular uses the `InvoiceController` as well as the `currencyConverter` service.
|
`finance2` module. By this, Angular uses the `InvoiceController` as well as the `currencyConverter` service.
|
||||||
|
|
||||||
Now that Angular knows of all the parts of the application, it needs to create them.
|
Now that Angular knows of all the parts of the application, it needs to create them.
|
||||||
In the previous section we saw that controllers are created using a factory function.
|
In the previous section we saw that controllers are created using a factory function.
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ starts with capital letter and ends with "Ctrl" or "Controller".
|
||||||
- Assigning a property to `$scope` creates or updates the model.
|
- Assigning a property to `$scope` creates or updates the model.
|
||||||
- Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method)
|
- Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method)
|
||||||
- The Controller methods and properties are available in the template (for the `<div>` element and
|
- The Controller methods and properties are available in the template (for the `<div>` element and
|
||||||
and its children).
|
its children).
|
||||||
|
|
||||||
## Spicy Arguments Example
|
## Spicy Arguments Example
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ testable.
|
||||||
To register a service, you must have a module that this service will be part of. Afterwards, you
|
To register a service, you must have a module that this service will be part of. Afterwards, you
|
||||||
can register the service with the module either via the {@link api/angular.Module Module api} or
|
can register the service with the module either via the {@link api/angular.Module Module api} or
|
||||||
by using the {@link api/AUTO.$provide $provide} service in the module configuration
|
by using the {@link api/AUTO.$provide $provide} service in the module configuration
|
||||||
function.The following pseudo-code shows both approaches:
|
function. The following pseudo-code shows both approaches:
|
||||||
|
|
||||||
Using the angular.Module api:
|
Using the angular.Module api:
|
||||||
<pre>
|
<pre>
|
||||||
|
|
|
||||||
|
|
@ -53,18 +53,19 @@ function myController(scope, notifyService) {
|
||||||
myController.$inject = ['$scope','notify'];
|
myController.$inject = ['$scope','notify'];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div ng-controller="myController">
|
<div id="simple" ng-controller="myController">
|
||||||
<p>Let's try this simple notify service, injected into the controller...</p>
|
<p>Let's try this simple notify service, injected into the controller...</p>
|
||||||
<input ng-init="message='test'" ng-model="message" >
|
<input ng-init="message='test'" ng-model="message" >
|
||||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||||
<p>(you have to click 3 times to see an alert)</p>
|
<p>(you have to click 3 times to see an alert)</p>
|
||||||
</div>
|
</div>
|
||||||
</doc:source>
|
</doc:source>
|
||||||
<doc:scenario>
|
<doc:protractor>
|
||||||
it('should test service', function() {
|
it('should test service', function() {
|
||||||
expect(element(':input[ng\\:model="message"]').val()).toEqual('test');
|
expect(element(by.id('simple')).element(by.model('message')).getAttribute('value'))
|
||||||
|
.toEqual('test');
|
||||||
});
|
});
|
||||||
</doc:scenario>
|
</doc:protractor>
|
||||||
</doc:example>
|
</doc:example>
|
||||||
|
|
||||||
## Implicit Dependency Injection
|
## Implicit Dependency Injection
|
||||||
|
|
@ -95,7 +96,7 @@ function myController($scope, notify) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<div ng-controller="myController">
|
<div id="implicit" ng-controller="myController">
|
||||||
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
|
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
|
||||||
<input ng-init="message='test'" ng-model="message">
|
<input ng-init="message='test'" ng-model="message">
|
||||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||||
|
|
|
||||||
|
|
@ -50,19 +50,18 @@ of which depend on other services that are provided by the Angular framework:
|
||||||
* @param {*} message Message to be logged.
|
* @param {*} message Message to be logged.
|
||||||
*/
|
*/
|
||||||
function batchLogModule($provide){
|
function batchLogModule($provide){
|
||||||
$provide.factory('batchLog', ['$timeout', '$log', function($timeout, $log) {
|
$provide.factory('batchLog', ['$interval', '$log', function($interval, $log) {
|
||||||
var messageQueue = [];
|
var messageQueue = [];
|
||||||
|
|
||||||
function log() {
|
function log() {
|
||||||
if (messageQueue.length) {
|
if (messageQueue.length) {
|
||||||
$log('batchLog messages: ', messageQueue);
|
$log.log('batchLog messages: ', messageQueue);
|
||||||
messageQueue = [];
|
messageQueue = [];
|
||||||
}
|
}
|
||||||
$timeout(log, 50000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// start periodic checking
|
// start periodic checking
|
||||||
log();
|
$interval(log, 50000);
|
||||||
|
|
||||||
return function(message) {
|
return function(message) {
|
||||||
messageQueue.push(message);
|
messageQueue.push(message);
|
||||||
|
|
@ -82,13 +81,13 @@ of which depend on other services that are provided by the Angular framework:
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the main service to kick of the application
|
// get the main service to kick off the application
|
||||||
angular.injector([batchLogModule]).get('routeTemplateMonitor');
|
angular.injector([batchLogModule]).get('routeTemplateMonitor');
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
Things to notice in this example:
|
Things to notice in this example:
|
||||||
|
|
||||||
* The `batchLog` service depends on the built-in {@link api/ng.$timeout $timeout} and
|
* The `batchLog` service depends on the built-in {@link api/ng.$interval $interval} and
|
||||||
{@link api/ng.$log $log} services, and allows messages to be logged into the
|
{@link api/ng.$log $log} services, and allows messages to be logged into the
|
||||||
`console.log` in batches.
|
`console.log` in batches.
|
||||||
* The `routeTemplateMonitor` service depends on the built-in {@link api/ngRoute.$route
|
* The `routeTemplateMonitor` service depends on the built-in {@link api/ngRoute.$route
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ Angular sets these CSS classes. It is up to your application to provide useful s
|
||||||
|
|
||||||
* `ng-binding`
|
* `ng-binding`
|
||||||
- **Usage:** angular applies this class to any element that is attached to a data binding, via `ng-bind` or
|
- **Usage:** angular applies this class to any element that is attached to a data binding, via `ng-bind` or
|
||||||
{{}} curly braces, for example. (see {@link guide/databinding databinding} guide)
|
`{{}}` curly braces, for example. (see {@link guide/databinding databinding} guide)
|
||||||
|
|
||||||
* `ng-invalid`, `ng-valid`
|
* `ng-invalid`, `ng-valid`
|
||||||
- **Usage:** angular applies this class to an input widget element if that element's input does
|
- **Usage:** angular applies this class to an input widget element if that element's input does
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ function MyClass(xhr) {
|
||||||
This is the preferred method since the code makes no assumptions about the origin of `xhr` and cares
|
This is the preferred method since the code makes no assumptions about the origin of `xhr` and cares
|
||||||
instead about whoever created the class responsible for passing it in. Since the creator of the
|
instead about whoever created the class responsible for passing it in. Since the creator of the
|
||||||
class should be different code than the user of the class, it separates the responsibility of
|
class should be different code than the user of the class, it separates the responsibility of
|
||||||
creation from the logic. This is dependency-injection is in a nutshell.
|
creation from the logic. This is dependency-injection in a nutshell.
|
||||||
|
|
||||||
The class above is testable, since in the test we can write:
|
The class above is testable, since in the test we can write:
|
||||||
<pre>
|
<pre>
|
||||||
|
|
@ -222,7 +222,7 @@ var pc = new PasswordCtrl();
|
||||||
input.val('abc');
|
input.val('abc');
|
||||||
pc.grade();
|
pc.grade();
|
||||||
expect(span.text()).toEqual('weak');
|
expect(span.text()).toEqual('weak');
|
||||||
$('body').html('');
|
$('body').empty();
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
In angular the controllers are strictly separated from the DOM manipulation logic and this results in
|
In angular the controllers are strictly separated from the DOM manipulation logic and this results in
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ of service names to inject.
|
||||||
var MyController = function(renamed$scope, renamedGreeter) {
|
var MyController = function(renamed$scope, renamedGreeter) {
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
MyController.$inject = ['$scope', 'greeter'];
|
MyController['$inject'] = ['$scope', 'greeter'];
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
In this scenario the ordering of the values in the '$inject' array must match the ordering of the arguments to inject.
|
In this scenario the ordering of the values in the '$inject' array must match the ordering of the arguments to inject.
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ The following also **matches** `ngModel`:
|
||||||
|
|
||||||
Angular **normalizes** an element's tag and attribute name to determine which elements match which
|
Angular **normalizes** an element's tag and attribute name to determine which elements match which
|
||||||
directives. We typically refer to directives by their case-sensitive
|
directives. We typically refer to directives by their case-sensitive
|
||||||
{@link http://en.wikipedia.org/wiki/CamelCase camelCase} **normalized** name (e.g. `ngModel`).
|
{@link http://en.wikipedia.org/wiki/CamelCase camelCase} **normalized** name (e.g. `ngModel`).
|
||||||
However, since HTML is case-insensitive, we refer to directives in the DOM by lower-case
|
However, since HTML is case-insensitive, we refer to directives in the DOM by lower-case
|
||||||
forms, typically using {@link http://en.wikipedia.org/wiki/Letter_case#Computers dash-delimited}
|
forms, typically using {@link http://en.wikipedia.org/wiki/Letter_case#Computers dash-delimited}
|
||||||
attributes on DOM elements (e.g. `ng-model`).
|
attributes on DOM elements (e.g. `ng-model`).
|
||||||
|
|
@ -84,10 +84,10 @@ Here are some equivalent examples of elements that match `ngBind`:
|
||||||
<span x-ng-bind="name"></span> <br/>
|
<span x-ng-bind="name"></span> <br/>
|
||||||
</div>
|
</div>
|
||||||
</file>
|
</file>
|
||||||
<file name="scenario.js">
|
<file name="protractorTest.js">
|
||||||
it('should show off bindings', function() {
|
it('should show off bindings', function() {
|
||||||
expect(element('div[ng-controller="Ctrl1"] span[ng-bind]').text())
|
expect(element(by.css('div[ng-controller="Ctrl1"] span[ng-bind]')).getText())
|
||||||
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)');
|
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)');
|
||||||
});
|
});
|
||||||
</file>
|
</file>
|
||||||
</example>
|
</example>
|
||||||
|
|
@ -174,9 +174,9 @@ For example, we could fix the example above by instead writing:
|
||||||
|
|
||||||
## Creating Directives
|
## Creating Directives
|
||||||
|
|
||||||
First let's talk about the API for registering directives. Much like controllers, directives are
|
First let's talk about the {@link api/ng.$compileProvider#methods_directive API for registering directives}. Much like
|
||||||
registered on modules. To register a directive, you use the `module.directive` API.
|
controllers, directives are registered on modules. To register a directive, you use the
|
||||||
`module.directive` takes the
|
`module.directive` API. `module.directive` takes the
|
||||||
{@link guide/directive#creating-custom-directives_matching-directives normalized} directive name
|
{@link guide/directive#creating-custom-directives_matching-directives normalized} directive name
|
||||||
followed by a **factory function.** This factory function should return an object with the different
|
followed by a **factory function.** This factory function should return an object with the different
|
||||||
options to tell `$compile` how the directive should behave when matched.
|
options to tell `$compile` how the directive should behave when matched.
|
||||||
|
|
@ -280,7 +280,7 @@ using `templateUrl` instead:
|
||||||
</example>
|
</example>
|
||||||
|
|
||||||
Great! But what if we wanted to have our directive match the tag name `<my-customer>` instead?
|
Great! But what if we wanted to have our directive match the tag name `<my-customer>` instead?
|
||||||
If we simply put a `<my-customer>` element into the HMTL, it doesn't work.
|
If we simply put a `<my-customer>` element into the HTML, it doesn't work.
|
||||||
|
|
||||||
<div class="alert alert-waring">
|
<div class="alert alert-waring">
|
||||||
**Note:** When you create a directive, it is restricted to attribute only by default. In order to
|
**Note:** When you create a directive, it is restricted to attribute only by default. In order to
|
||||||
|
|
@ -506,6 +506,8 @@ that you explicitly pass in.
|
||||||
|
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
**Note:** Normally, a scope prototypically inherits from its parent. An isolated scope does not.
|
**Note:** Normally, a scope prototypically inherits from its parent. An isolated scope does not.
|
||||||
|
See the {@link guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive
|
||||||
|
"Isolating the Scope of a Directive"} section for more information about isolate scopes.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="alert alert-success">
|
<div class="alert alert-success">
|
||||||
|
|
@ -525,11 +527,14 @@ where:
|
||||||
|
|
||||||
* `scope` is an Angular scope object.
|
* `scope` is an Angular scope object.
|
||||||
* `element` is the jqLite-wrapped element that this directive matches.
|
* `element` is the jqLite-wrapped element that this directive matches.
|
||||||
* `attrs` is an object with the normalized attribute names and their corresponding values.
|
* `attrs` is a hash object with key-value pairs of normalized attribute names and their
|
||||||
|
corresponding attribute values.
|
||||||
|
|
||||||
In our `link` function, we want to update the displayed time once a second, or whenever a user
|
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 also want to remove the timeout
|
changes the time formatting string that our directive binds to. We will use the `$interval` service
|
||||||
if the directive is deleted so we don't introduce a memory leak.
|
to call a handler on a regular basis. This is easier than using `$timeout` but also works better with
|
||||||
|
end 2 end testing, where we want to ensure that all $timeouts have completed before completing the test.
|
||||||
|
We also want to remove the `$interval` if the directive is deleted so we don't introduce a memory leak.
|
||||||
|
|
||||||
<example module="docsTimeDirective">
|
<example module="docsTimeDirective">
|
||||||
<file name="script.js">
|
<file name="script.js">
|
||||||
|
|
@ -537,7 +542,7 @@ if the directive is deleted so we don't introduce a memory leak.
|
||||||
.controller('Ctrl2', function($scope) {
|
.controller('Ctrl2', function($scope) {
|
||||||
$scope.format = 'M/d/yy h:mm:ss a';
|
$scope.format = 'M/d/yy h:mm:ss a';
|
||||||
})
|
})
|
||||||
.directive('myCurrentTime', function($timeout, dateFilter) {
|
.directive('myCurrentTime', function($interval, dateFilter) {
|
||||||
|
|
||||||
function link(scope, element, attrs) {
|
function link(scope, element, attrs) {
|
||||||
var format,
|
var format,
|
||||||
|
|
@ -552,20 +557,14 @@ if the directive is deleted so we don't introduce a memory leak.
|
||||||
updateTime();
|
updateTime();
|
||||||
});
|
});
|
||||||
|
|
||||||
function scheduleUpdate() {
|
|
||||||
// save the timeoutId for canceling
|
|
||||||
timeoutId = $timeout(function() {
|
|
||||||
updateTime(); // update DOM
|
|
||||||
scheduleUpdate(); // schedule the next update
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
element.on('$destroy', function() {
|
element.on('$destroy', function() {
|
||||||
$timeout.cancel(timeoutId);
|
$interval.cancel(timeoutId);
|
||||||
});
|
});
|
||||||
|
|
||||||
// start the UI update process.
|
// start the UI update process; save the timeoutId for canceling
|
||||||
scheduleUpdate();
|
timeoutId = $interval(function() {
|
||||||
|
updateTime(); // update DOM
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -583,7 +582,7 @@ if the directive is deleted so we don't introduce a memory leak.
|
||||||
|
|
||||||
There are a couple of things to note here.
|
There are a couple of things to note here.
|
||||||
Just like the `module.controller` API, the function argument in `module.directive` is dependency
|
Just like the `module.controller` API, the function argument in `module.directive` is dependency
|
||||||
injected. Because of this, we can use `$timeout` and `dateFilter` inside our directive's `link`
|
injected. Because of this, we can use `$interval` and `dateFilter` inside our directive's `link`
|
||||||
function.
|
function.
|
||||||
|
|
||||||
We register an event `element.on('$destroy', ...)`. What fires this `$destroy` event?
|
We register an event `element.on('$destroy', ...)`. What fires this `$destroy` event?
|
||||||
|
|
@ -736,13 +735,13 @@ own behavior to it.
|
||||||
We want to run the function we pass by invoking it from the directive's scope, but have it run
|
We want to run the function we pass by invoking it from the directive's scope, but have it run
|
||||||
in the context of the scope where its registered.
|
in the context of the scope where its registered.
|
||||||
|
|
||||||
We saw earlier how to use `=prop` in the `scope` option, but in the above example, we're using
|
We saw earlier how to use `=attr` in the `scope` option, but in the above example, we're using
|
||||||
`&prop` instead. `&` bindings expose a function to an isolated scope allowing the isolated scope
|
`&attr` instead. `&` bindings expose a function to an isolated scope allowing the isolated scope
|
||||||
to invoke it, but maintaining the original scope of the function. So when a user clicks the
|
to invoke it, but maintaining the original scope of the function. So when a user clicks the
|
||||||
`x` in the dialog, it runs `Ctrl`'s `close` function.
|
`x` in the dialog, it runs `Ctrl`'s `hideDialog` function.
|
||||||
|
|
||||||
<div class="alert alert-success">
|
<div class="alert alert-success">
|
||||||
**Best Practice:** use `&prop` in the `scope` option when you want your directive
|
**Best Practice:** use `&attr` in the `scope` option when you want your directive
|
||||||
to expose an API for binding to behaviors.
|
to expose an API for binding to behaviors.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ It might be tempting to think of Angular view expressions as JavaScript expressi
|
||||||
not entirely correct, since Angular does not use a JavaScript `eval()` to evaluate expressions.
|
not entirely correct, since Angular does not use a JavaScript `eval()` to evaluate expressions.
|
||||||
You can think of Angular expressions as JavaScript expressions with following differences:
|
You can think of Angular expressions as JavaScript expressions with following differences:
|
||||||
|
|
||||||
* **Attribute Evaluation:** evaluation of all properties are against the scope, doing the
|
* **Attribute Evaluation:** evaluation of all properties are against the scope doing the
|
||||||
evaluation, unlike in JavaScript where the expressions are evaluated against the global
|
evaluation, unlike in JavaScript where the expressions are evaluated against the global
|
||||||
`window`.
|
`window`.
|
||||||
|
|
||||||
|
|
@ -37,11 +37,11 @@ JavaScript, use the {@link api/ng.$rootScope.Scope#methods_$eval `$eval()`} meth
|
||||||
<doc:source>
|
<doc:source>
|
||||||
1+2={{1+2}}
|
1+2={{1+2}}
|
||||||
</doc:source>
|
</doc:source>
|
||||||
<doc:scenario>
|
<doc:protractor>
|
||||||
it('should calculate expression in binding', function() {
|
it('should calculate expression in binding', function() {
|
||||||
expect(binding('1+2')).toEqual('3');
|
expect(element(by.binding('1+2')).getText()).toEqual('1+2=3');
|
||||||
});
|
});
|
||||||
</doc:scenario>
|
</doc:protractor>
|
||||||
</doc:example>
|
</doc:example>
|
||||||
|
|
||||||
You can try evaluating different expressions here:
|
You can try evaluating different expressions here:
|
||||||
|
|
@ -73,14 +73,14 @@ You can try evaluating different expressions here:
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</doc:source>
|
</doc:source>
|
||||||
<doc:scenario>
|
<doc:protractor>
|
||||||
it('should allow user expression testing', function() {
|
it('should allow user expression testing', function() {
|
||||||
element('.expressions :button').click();
|
element(by.css('.expressions button')).click();
|
||||||
var li = using('.expressions ul').repeater('li');
|
var lis = element(by.css('.expressions ul')).element.all(by.repeater('expr in exprs'));
|
||||||
expect(li.count()).toBe(1);
|
expect(lis.count()).toBe(1);
|
||||||
expect(li.row(0)).toEqual(["3*10|currency", "$30.00"]);
|
expect(lis.get(0).getText()).toEqual('[ X ] 3*10|currency => $30.00');
|
||||||
});
|
});
|
||||||
</doc:scenario>
|
</doc:protractor>
|
||||||
</doc:example>
|
</doc:example>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -99,7 +99,7 @@ prevent accidental access to the global state (a common source of subtle bugs).
|
||||||
$scope.name = 'World';
|
$scope.name = 'World';
|
||||||
|
|
||||||
$scope.greet = function() {
|
$scope.greet = function() {
|
||||||
($window.mockWindow || $window).alert('Hello ' + $scope.name);
|
$window.alert('Hello ' + $scope.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -108,21 +108,17 @@ prevent accidental access to the global state (a common source of subtle bugs).
|
||||||
<button ng-click="greet()">Greet</button>
|
<button ng-click="greet()">Greet</button>
|
||||||
</div>
|
</div>
|
||||||
</doc:source>
|
</doc:source>
|
||||||
<doc:scenario>
|
<doc:protractor>
|
||||||
it('should calculate expression in binding', function() {
|
it('should calculate expression in binding', function() {
|
||||||
var alertText;
|
element(by.css('[ng-click="greet()"]')).click();
|
||||||
this.addFutureAction('set mock', function($window, $document, done) {
|
|
||||||
$window.mockWindow = {
|
var alertDialog = browser.switchTo().alert();
|
||||||
alert: function(text){ alertText = text; }
|
|
||||||
};
|
expect(alertDialog.getText()).toEqual('Hello World');
|
||||||
done();
|
|
||||||
});
|
alertDialog.accept();
|
||||||
element(':button:contains(Greet)').click();
|
});
|
||||||
expect(this.addFuture('alert text', function(done) {
|
</doc:protractor>
|
||||||
done(null, alertText);
|
|
||||||
})).toBe('Hello World');
|
|
||||||
});
|
|
||||||
</doc:scenario>
|
|
||||||
</doc:example>
|
</doc:example>
|
||||||
|
|
||||||
## Forgiving
|
## Forgiving
|
||||||
|
|
|
||||||
|
|
@ -121,3 +121,6 @@ text upper-case.
|
||||||
</doc:source>
|
</doc:source>
|
||||||
</doc:example>
|
</doc:example>
|
||||||
|
|
||||||
|
## Testing custom filters
|
||||||
|
|
||||||
|
See the {@link http://docs.angularjs.org/tutorial/step_09#test phonecat tutorial} for an example.
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,10 @@ In addition it provides an {@link api/ng.directive:ngModel.NgModelController API
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function Controller($scope) {
|
function Controller($scope) {
|
||||||
$scope.master= {};
|
$scope.master = {};
|
||||||
|
|
||||||
$scope.update = function(user) {
|
$scope.update = function(user) {
|
||||||
$scope.master= angular.copy(user);
|
$scope.master = angular.copy(user);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.reset = function() {
|
$scope.reset = function() {
|
||||||
|
|
@ -115,9 +115,14 @@ This ensures that the user is not distracted with an error until after interacti
|
||||||
|
|
||||||
A form is an instance of {@link api/ng.directive:form.FormController FormController}.
|
A form is an instance of {@link api/ng.directive:form.FormController FormController}.
|
||||||
The form instance can optionally be published into the scope using the `name` attribute.
|
The form instance can optionally be published into the scope using the `name` attribute.
|
||||||
Similarly, control is an instance of {@link api/ng.directive:ngModel.NgModelController NgModelController}.
|
|
||||||
The control instance can similarly be published into the form instance using the `name` attribute.
|
Similarly, an input control that has the {@link api/ng.directive:ngModel ngModel} directive holds an
|
||||||
This implies that the internal state of both the form and the control is available for binding in the view using the standard binding primitives.
|
instance of {@link api/ng.directive:ngModel.NgModelController NgModelController}.
|
||||||
|
Such a control instance can be published as a property of the form instance using the `name` attribute
|
||||||
|
on the input control. The name attribute specifies the name of the property on the form instance.
|
||||||
|
|
||||||
|
This implies that the internal state of both the form and the control is available for binding in
|
||||||
|
the view using the standard binding primitives.
|
||||||
|
|
||||||
This allows us to extend the above example with these features:
|
This allows us to extend the above example with these features:
|
||||||
|
|
||||||
|
|
@ -230,7 +235,7 @@ In the following example we create two directives.
|
||||||
<script>
|
<script>
|
||||||
var app = angular.module('form-example1', []);
|
var app = angular.module('form-example1', []);
|
||||||
|
|
||||||
var INTEGER_REGEXP = /^\-?\d*$/;
|
var INTEGER_REGEXP = /^\-?\d+$/;
|
||||||
app.directive('integer', function() {
|
app.directive('integer', function() {
|
||||||
return {
|
return {
|
||||||
require: 'ngModel',
|
require: 'ngModel',
|
||||||
|
|
|
||||||
|
|
@ -97,9 +97,9 @@ locale, it is fine to rely on the default currency symbol. However, if you antic
|
||||||
in other locales might use your app, you should provide your own currency symbol to make sure the
|
in other locales might use your app, you should provide your own currency symbol to make sure the
|
||||||
actual value is understood.
|
actual value is understood.
|
||||||
|
|
||||||
For example, if you want to display account balance of 1000 dollars with the following binding
|
For example, if you want to display an account balance of 1000 dollars with the following binding
|
||||||
containing currency filter: `{{ 1000 | currency }}`, and your app is currently in en-US locale.
|
containing currency filter: `{{ 1000 | currency }}`, and your app is currently in en-US locale.
|
||||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, her
|
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, their
|
||||||
browser will specify the locale as ja, and the balance of '¥1000.00' will be shown instead. This
|
browser will specify the locale as ja, and the balance of '¥1000.00' will be shown instead. This
|
||||||
will really upset your client.
|
will really upset your client.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ It is very unlikely that issues specific to IE7 or earlier will be given any tim
|
||||||
|
|
||||||
To make your Angular application work on IE please make sure that:
|
To make your Angular application work on IE please make sure that:
|
||||||
|
|
||||||
1. You polyfill JSON.stringify if necessary (IE7 will need this). You can use
|
1. You polyfill JSON.stringify for IE7 and below. You can use
|
||||||
[JSON2](https://github.com/douglascrockford/JSON-js) or
|
[JSON2](https://github.com/douglascrockford/JSON-js) or
|
||||||
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
|
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
|
||||||
<pre>
|
<pre>
|
||||||
|
|
@ -51,7 +51,7 @@ To make your Angular application work on IE please make sure that:
|
||||||
3. you **do not** use custom element tags such as `<ng:view>` (use the attribute version
|
3. you **do not** use custom element tags such as `<ng:view>` (use the attribute version
|
||||||
`<div ng-view>` instead), or
|
`<div ng-view>` instead), or
|
||||||
|
|
||||||
4. if you **do use** custom element tags, then you must take these steps to make IE happy:
|
4. if you **do use** custom element tags, then you must take these steps to make IE 8 and below happy:
|
||||||
<pre>
|
<pre>
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
|
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,14 @@
|
||||||
Everything you need to know about AngularJS
|
Everything you need to know about AngularJS
|
||||||
|
|
||||||
* {@link guide/introduction What is AngularJS?}
|
* {@link guide/introduction What is AngularJS?}
|
||||||
|
|
||||||
* {@link guide/concepts Conceptual Overview}
|
* {@link guide/concepts Conceptual Overview}
|
||||||
|
|
||||||
## Tutorials
|
## Tutorials
|
||||||
|
|
||||||
* {@link tutorial/index Official AngularJS Tutorial}
|
* {@link tutorial/index Official AngularJS Tutorial}
|
||||||
|
|
||||||
* [10 Reasons Why You Should Use AngularJS](http://www.sitepoint.com/10-reasons-use-angularjs/)
|
* [10 Reasons Why You Should Use AngularJS](http://www.sitepoint.com/10-reasons-use-angularjs/)
|
||||||
|
|
||||||
* [Design Principles of AngularJS (video)](https://www.youtube.com/watch?v=HCR7i5F5L8c)
|
* [Design Principles of AngularJS (video)](https://www.youtube.com/watch?v=HCR7i5F5L8c)
|
||||||
|
|
||||||
* [Fundamentals in 60 Minutes (video)](http://www.youtube.com/watch?v=i9MHigUZKEM)
|
* [Fundamentals in 60 Minutes (video)](http://www.youtube.com/watch?v=i9MHigUZKEM)
|
||||||
|
|
||||||
* [For folks with jQuery background](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background)
|
* [For folks with jQuery background](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background)
|
||||||
|
|
||||||
## Core Concepts
|
## Core Concepts
|
||||||
|
|
@ -29,63 +24,44 @@ Everything you need to know about AngularJS
|
||||||
In Angular applications, you move the job of filling page templates with data from the server to the client. The result is a system better structured for dynamic page updates. Below are the core features you'll use.
|
In Angular applications, you move the job of filling page templates with data from the server to the client. The result is a system better structured for dynamic page updates. Below are the core features you'll use.
|
||||||
|
|
||||||
* {@link guide/databinding Data binding}
|
* {@link guide/databinding Data binding}
|
||||||
|
|
||||||
* {@link guide/expression Expressions}
|
* {@link guide/expression Expressions}
|
||||||
|
|
||||||
* {@link guide/directive Directives}
|
* {@link guide/directive Directives}
|
||||||
|
|
||||||
* {@link api/ngRoute.$route Views and routes (see the example)}
|
* {@link api/ngRoute.$route Views and routes (see the example)}
|
||||||
|
|
||||||
* {@link guide/filter Filters}
|
* {@link guide/filter Filters}
|
||||||
|
|
||||||
* {@link guide/forms Forms} and [Concepts of AngularJS Forms](http://mrbool.com/the-concepts-of-angularjs-forms/29117)
|
* {@link guide/forms Forms} and [Concepts of AngularJS Forms](http://mrbool.com/the-concepts-of-angularjs-forms/29117)
|
||||||
|
|
||||||
### Application Structure
|
### Application Structure
|
||||||
|
|
||||||
* **Blog post: **[When to use directives, controllers or services](http://kirkbushell.me/when-to-use-directives-controllers-or-services-in-angular/)
|
* **Blog post: **[When to use directives, controllers or services](http://kirkbushell.me/when-to-use-directives-controllers-or-services-in-angular/)
|
||||||
|
|
||||||
* **App wiring:** {@link guide/di Dependency injection}
|
* **App wiring:** {@link guide/di Dependency injection}
|
||||||
|
|
||||||
* **Exposing model to templates:** {@link guide/scope Scopes}
|
* **Exposing model to templates:** {@link guide/scope Scopes}
|
||||||
|
|
||||||
* **Communicating with servers:** {@link api/ng.$http $http}, {@link api/ngResource.$resource $resource}
|
* **Communicating with servers:** {@link api/ng.$http $http}, {@link api/ngResource.$resource $resource}
|
||||||
|
|
||||||
### Other AngularJS Features
|
### Other AngularJS Features
|
||||||
|
|
||||||
* **Animation:** {@link guide/animations Core concepts}, {@link api/ngAnimate ngAnimate API}, and [Animation in AngularJS 1.2](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html)
|
* **Animation:** {@link guide/animations Core concepts}, {@link api/ngAnimate ngAnimate API}, and [Animation in AngularJS 1.2](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html)
|
||||||
|
|
||||||
* **Security:** {@link api/ng.$sce Strict Contextual Escaping}, {@link api/ng.directive:ngCsp Content Security Policy}, {@link api/ngSanitize.$sanitize $sanitize}, [video](https://www.youtube.com/watch?v=18ifoT-Id54)
|
* **Security:** {@link api/ng.$sce Strict Contextual Escaping}, {@link api/ng.directive:ngCsp Content Security Policy}, {@link api/ngSanitize.$sanitize $sanitize}, [video](https://www.youtube.com/watch?v=18ifoT-Id54)
|
||||||
|
|
||||||
* **Internationalization and Localization:** {@link guide/i18n Angular Guide to i18n and l10n}, {@link api/ng.filter:date date filter}, {@link api/ng.filter:currency currency filter}, [Creating multilingual support](http://www.novanet.no/blog/hallstein-brotan/dates/2013/10/creating-multilingual-support-using-angularjs/)
|
* **Internationalization and Localization:** {@link guide/i18n Angular Guide to i18n and l10n}, {@link api/ng.filter:date date filter}, {@link api/ng.filter:currency currency filter}, [Creating multilingual support](http://www.novanet.no/blog/hallstein-brotan/dates/2013/10/creating-multilingual-support-using-angularjs/)
|
||||||
|
|
||||||
* **Mobile:** {@link api/ngTouch Touch events}
|
* **Mobile:** {@link api/ngTouch Touch events}
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/dev_guide.unit-testing Unit testing}, {@link guide/dev_guide.services.testing_services Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
|
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/dev_guide.unit-testing Unit testing}, {@link guide/dev_guide.services.testing_services Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
|
||||||
|
|
||||||
* **Scenario testing:** [Protractor](https://github.com/angular/protractor)
|
* **Scenario testing:** [Protractor](https://github.com/angular/protractor)
|
||||||
|
|
||||||
## Specific Topics
|
## Specific Topics
|
||||||
|
|
||||||
* **Login: **[Google example](https://developers.google.com/+/photohunt/python), [Facebook example](http://blog.brunoscopelliti.com/facebook-authentication-in-your-angularjs-web-app), [authentication strategy](http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app), [unix-style authorization](http://frederiknakstad.com/authentication-in-single-page-applications-with-angular-js/)
|
* **Login: **[Google example](https://developers.google.com/+/photohunt/python), [Facebook example](http://blog.brunoscopelliti.com/facebook-authentication-in-your-angularjs-web-app), [authentication strategy](http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app), [unix-style authorization](http://frederiknakstad.com/authentication-in-single-page-applications-with-angular-js/)
|
||||||
|
|
||||||
* **Mobile:** [Angular on Mobile Guide](http://www.ng-newsletter.com/posts/angular-on-mobile.html), [PhoneGap](http://devgirl.org/2013/06/10/quick-start-guide-phonegap-and-angularjs/)
|
* **Mobile:** [Angular on Mobile Guide](http://www.ng-newsletter.com/posts/angular-on-mobile.html), [PhoneGap](http://devgirl.org/2013/06/10/quick-start-guide-phonegap-and-angularjs/)
|
||||||
|
|
||||||
* **Other Languages:** [CoffeeScript](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html), [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
|
* **Other Languages:** [CoffeeScript](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html), [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
|
||||||
|
|
||||||
* **Realtime: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
|
* **Realtime: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
|
||||||
|
|
||||||
* **Visualization:** [SVG](http://gaslight.co/blog/angular-backed-svgs), [D3.js](http://www.ng-newsletter.com/posts/d3-on-angular.html)
|
* **Visualization:** [SVG](http://gaslight.co/blog/angular-backed-svgs), [D3.js](http://www.ng-newsletter.com/posts/d3-on-angular.html)
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
* **Debugging:** [Batarang](https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=en)
|
* **Debugging:** [Batarang](https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=en)
|
||||||
|
|
||||||
* **Testing:** [Karma](http://karma-runner.github.io), [Protractor](https://github.com/angular/protractor)
|
* **Testing:** [Karma](http://karma-runner.github.io), [Protractor](https://github.com/angular/protractor)
|
||||||
|
|
||||||
* **Editor support:** [Webstorm](http://plugins.jetbrains.com/plugin/6971) (and [video](http://www.youtube.com/watch?v=LJOyrSh1kDU)), [Sublime Text](https://github.com/angular-ui/AngularJS-sublime-package), [Visual Studio](http://madskristensen.net/post/angularjs-intellisense-in-visual-studio-2012)
|
* **Editor support:** [Webstorm](http://plugins.jetbrains.com/plugin/6971) (and [video](http://www.youtube.com/watch?v=LJOyrSh1kDU)), [Sublime Text](https://github.com/angular-ui/AngularJS-sublime-package), [Visual Studio](http://madskristensen.net/post/angularjs-intellisense-in-visual-studio-2012)
|
||||||
|
|
||||||
* **Workflow:** [Yeoman.io](https://github.com/yeoman/generator-angular) and [Angular Yeoman Tutorial](http://www.sitepoint.com/kickstart-your-angularjs-development-with-yeoman-grunt-and-bower/)
|
* **Workflow:** [Yeoman.io](https://github.com/yeoman/generator-angular) and [Angular Yeoman Tutorial](http://www.sitepoint.com/kickstart-your-angularjs-development-with-yeoman-grunt-and-bower/)
|
||||||
|
|
||||||
## Complementary Libraries
|
## Complementary Libraries
|
||||||
|
|
@ -93,37 +69,26 @@ In Angular applications, you move the job of filling page templates with data fr
|
||||||
This is a short list of libraries with specific support and documentation for working with Angular. You can find a full list of all known Angular external libraries at [ngmodules.org](http://ngmodules.org/).
|
This is a short list of libraries with specific support and documentation for working with Angular. You can find a full list of all known Angular external libraries at [ngmodules.org](http://ngmodules.org/).
|
||||||
|
|
||||||
* **Internationalization:** [angular-translate](http://pascalprecht.github.io/angular-translate/), [angular-gettext](http://angular-gettext.rocketeer.be/)
|
* **Internationalization:** [angular-translate](http://pascalprecht.github.io/angular-translate/), [angular-gettext](http://angular-gettext.rocketeer.be/)
|
||||||
|
|
||||||
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
|
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
|
||||||
|
|
||||||
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
|
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
|
||||||
|
|
||||||
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/)
|
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/)
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
||||||
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ngmin automation tool](http://www.thinkster.io/pick/XlWneEZCqY/angularjs-ngmin)
|
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ngmin automation tool](http://www.thinkster.io/pick/XlWneEZCqY/angularjs-ngmin)
|
||||||
|
|
||||||
* **Tracking:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
|
* **Tracking:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
|
||||||
|
|
||||||
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
|
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
|
||||||
|
|
||||||
### Server-Specific
|
### Server-Specific
|
||||||
|
|
||||||
* **Django:** [Tutorial](http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework), [Integrating AngularJS with Django](http://django-angular.readthedocs.org/en/latest/integration.html)
|
* **Django:** [Tutorial](http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework), [Integrating AngularJS with Django](http://django-angular.readthedocs.org/en/latest/integration.html)
|
||||||
|
|
||||||
* **FireBase:** [AngularFire](http://angularfire.com/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
|
* **FireBase:** [AngularFire](http://angularfire.com/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
|
||||||
|
|
||||||
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/resources/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
|
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/resources/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
|
||||||
|
|
||||||
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
|
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
|
||||||
|
|
||||||
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
|
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
|
||||||
|
|
||||||
* **Rails: **[Tutorial](http://coderberry.me/blog/2013/04/22/angularjs-on-rails-4-part-1/), [AngularJS with Rails4](https://shellycloud.com/blog/2013/10/how-to-integrate-angularjs-with-rails-4), [angularjs-rails](https://github.com/hiravgandhi/angularjs-rails)
|
* **Rails: **[Tutorial](http://coderberry.me/blog/2013/04/22/angularjs-on-rails-4-part-1/), [AngularJS with Rails4](https://shellycloud.com/blog/2013/10/how-to-integrate-angularjs-with-rails-4), [angularjs-rails](https://github.com/hiravgandhi/angularjs-rails)
|
||||||
|
|
||||||
* **PHP: **[Building a RESTful web service](http://blog.brunoscopelliti.com/building-a-restful-web-service-with-angularjs-and-php-more-power-with-resource), [End to End with Laravel 4 (video)](http://www.youtube.com/watch?v=hqAyiqUs93c)
|
* **PHP: **[Building a RESTful web service](http://blog.brunoscopelliti.com/building-a-restful-web-service-with-angularjs-and-php-more-power-with-resource), [End to End with Laravel 4 (video)](http://www.youtube.com/watch?v=hqAyiqUs93c)
|
||||||
|
|
||||||
## Learning Resources
|
## Learning Resources
|
||||||
|
|
@ -137,18 +102,18 @@ This is a short list of libraries with specific support and documentation for wo
|
||||||
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
|
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
|
||||||
|
|
||||||
###Videos:
|
###Videos:
|
||||||
* [egghead.io](http://egghead.io/),
|
* [egghead.io](http://egghead.io/)
|
||||||
* [Angular on YouTube](http://youtube.com/angularjs)
|
* [Angular on YouTube](http://youtube.com/angularjs)
|
||||||
|
|
||||||
###Courses
|
### Courses
|
||||||
* **Free on-line:**
|
* **Free online:**
|
||||||
[thinkster.io](http://thinkster.io),
|
[thinkster.io](http://thinkster.io),
|
||||||
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1)
|
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1)
|
||||||
* **Paid on-line:**
|
* **Paid online:**
|
||||||
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
|
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
|
||||||
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
|
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
|
||||||
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html)
|
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html)
|
||||||
* **Paid on-site:**
|
* **Paid onsite:**
|
||||||
[angularbootcamp.com](http://angularbootcamp.com/)
|
[angularbootcamp.com](http://angularbootcamp.com/)
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|
@ -156,19 +121,14 @@ This is a short list of libraries with specific support and documentation for wo
|
||||||
The recipe for getting help on your unique issue is to create an example that could work (even if it doesn't) in a shareable example on [Plunker](http://plnkr.co/), [JSFiddle](http://jsfiddle.net/), or similar site and then post to one of the following:
|
The recipe for getting help on your unique issue is to create an example that could work (even if it doesn't) in a shareable example on [Plunker](http://plnkr.co/), [JSFiddle](http://jsfiddle.net/), or similar site and then post to one of the following:
|
||||||
|
|
||||||
* [Stackoverflow.com](http://stackoverflow.com/search?q=angularjs)
|
* [Stackoverflow.com](http://stackoverflow.com/search?q=angularjs)
|
||||||
|
|
||||||
* [AngularJS mailing list](https://groups.google.com/forum/#!forum/angular)
|
* [AngularJS mailing list](https://groups.google.com/forum/#!forum/angular)
|
||||||
|
|
||||||
* [AngularJS IRC channel](http://webchat.freenode.net/?channels=angularjs&uio=d4)
|
* [AngularJS IRC channel](http://webchat.freenode.net/?channels=angularjs&uio=d4)
|
||||||
|
|
||||||
## Social Channels
|
## Social Channels
|
||||||
|
|
||||||
* **Daily updates:** [Google+](https://plus.google.com/u/0/+AngularJS) or [Twitter](https://twitter.com/angularjs)
|
* **Daily updates:** [Google+](https://plus.google.com/u/0/+AngularJS) or [Twitter](https://twitter.com/angularjs)
|
||||||
|
|
||||||
* **Weekly newsletter:** [ng-newsletter](http://www.ng-newsletter.com/)
|
* **Weekly newsletter:** [ng-newsletter](http://www.ng-newsletter.com/)
|
||||||
|
|
||||||
* **Meetups: **[meetup.com](http://www.meetup.com/find/?keywords=angularJS&radius=Infinity&userFreeform=San+Francisco%2C+CA&mcId=z94108&mcName=San+Francisco%2C+CA&sort=member_count&eventFilter=mysugg)
|
* **Meetups: **[meetup.com](http://www.meetup.com/find/?keywords=angularJS&radius=Infinity&userFreeform=San+Francisco%2C+CA&mcId=z94108&mcName=San+Francisco%2C+CA&sort=member_count&eventFilter=mysugg)
|
||||||
|
|
||||||
* **Official news and releases: **[AngularJS Blog](http://blog.angularjs.org/)
|
* **Official news and releases: **[AngularJS Blog](http://blog.angularjs.org/)
|
||||||
|
|
||||||
## Contributing to AngularJS
|
## Contributing to AngularJS
|
||||||
|
|
|
||||||
|
|
@ -590,6 +590,10 @@ See [79223eae](https://github.com/angular/angular.js/commit/79223eae502283889334
|
||||||
|
|
||||||
## 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>
|
||||||
|
</div>
|
||||||
|
|
||||||
This change introduces the notion of "private" properties (properties
|
This change introduces the notion of "private" properties (properties
|
||||||
whose names begin and/or end with an underscore) on the scope chain.
|
whose names begin and/or end with an underscore) on the scope chain.
|
||||||
These properties will not be available to Angular expressions (i.e. {{
|
These properties will not be available to Angular expressions (i.e. {{
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ myApp.factory('clientId', function clientIdFactory() {
|
||||||
But given that the token is just a string literal, sticking with the Value recipe is still more
|
But given that the token is just a string literal, sticking with the Value recipe is still more
|
||||||
appropriate as it makes the code easier to follow.
|
appropriate as it makes the code easier to follow.
|
||||||
|
|
||||||
Let's say, however, that we would also like create a service that computes a token used for
|
Let's say, however, that we would also like to create a service that computes a token used for
|
||||||
authentication against a remote API. This token will be called 'apiToken' and will be computed
|
authentication against a remote API. This token will be called 'apiToken' and will be computed
|
||||||
based on the `clientId` value and a secret stored in browser's local storage:
|
based on the `clientId` value and a secret stored in browser's local storage:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,12 @@ watch {@link guide/expression expressions} and propagate events.
|
||||||
propagate any model changes through the system into the view from outside of the "Angular
|
propagate any model changes through the system into the view from outside of the "Angular
|
||||||
realm" (controllers, services, Angular event handlers).
|
realm" (controllers, services, Angular event handlers).
|
||||||
|
|
||||||
- Scopes can be nested to isolate application components while providing access to shared model
|
- Scopes can be nested to limit access to the properties of application components while providing
|
||||||
properties. A scope (prototypically) inherits properties from its parent scope.
|
access to shared model properties. Nested scopes are either "child scopes" or "isolate scopes".
|
||||||
|
A "child scope" (prototypically) inherits properties from its parent scope. An "isolate scope"
|
||||||
|
does not. See {@link
|
||||||
|
guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive isolated
|
||||||
|
scopes} for more information.
|
||||||
|
|
||||||
- Scopes provide context against which {@link guide/expression expressions} are evaluated. For
|
- Scopes provide context against which {@link guide/expression expressions} are evaluated. For
|
||||||
example `{{username}}` expression is meaningless, unless it is evaluated against a specific
|
example `{{username}}` expression is meaningless, unless it is evaluated against a specific
|
||||||
|
|
@ -259,8 +263,8 @@ the `$digest` phase. This delay is desirable, since it coalesces multiple model
|
||||||
For mutations to be properly observed, you should make them only within the {@link
|
For mutations to be properly observed, you should make them only within the {@link
|
||||||
api/ng.$rootScope.Scope#methods_$apply scope.$apply()}. (Angular APIs do this
|
api/ng.$rootScope.Scope#methods_$apply scope.$apply()}. (Angular APIs do this
|
||||||
implicitly, so no extra `$apply` call is needed when doing synchronous work in controllers,
|
implicitly, so no extra `$apply` call is needed when doing synchronous work in controllers,
|
||||||
or asynchronous work with {@link api/ng.$http $http} or {@link
|
or asynchronous work with {@link api/ng.$http $http}, {@link api/ng.$timeout $timeout}
|
||||||
api/ng.$timeout $timeout} services.
|
or {@link api/ng.$interval $interval} services.
|
||||||
|
|
||||||
4. **Mutation observation**
|
4. **Mutation observation**
|
||||||
|
|
||||||
|
|
@ -306,6 +310,8 @@ api/ng.directive:ngController ng-controller} and {@link
|
||||||
api/ng.directive:ngRepeat ng-repeat}, create new child scopes
|
api/ng.directive:ngRepeat ng-repeat}, create new child scopes
|
||||||
and attach the child scope to the corresponding DOM element. You can retrieve a scope for any DOM
|
and attach the child scope to the corresponding DOM element. You can retrieve a scope for any DOM
|
||||||
element by using an `angular.element(aDomElement).scope()` method call.
|
element by using an `angular.element(aDomElement).scope()` method call.
|
||||||
|
See the {@link guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive
|
||||||
|
directives guide} for more information about isolate scopes.
|
||||||
|
|
||||||
### Controllers and Scopes
|
### Controllers and Scopes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ for how to contribute your own code to AngularJS.
|
||||||
Before you can build AngularJS, you must install and configure the following dependencies on your
|
Before you can build AngularJS, you must install and configure the following dependencies on your
|
||||||
machine:
|
machine:
|
||||||
|
|
||||||
* {@link http://git-scm.com/ Git}: The {@link http://help.github.com/mac-git-installation Github Guide to
|
* {@link http://git-scm.com/ Git}: The {@link https://help.github.com/articles/set-up-git Github Guide to
|
||||||
Installing Git} is a good source of information.
|
Installing Git} is a good source of information.
|
||||||
|
|
||||||
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation, run a
|
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation, run a
|
||||||
|
|
@ -46,6 +46,8 @@ and included in your {@link http://docs.oracle.com/javase/tutorial/essential/env
|
||||||
npm install -g bower
|
npm install -g bower
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Note:** You may need to use sudo (for OSX, *nix, BSD etc) or run your command shell as Administrator (for Windows) to install Grunt &
|
||||||
|
Bower globally.
|
||||||
|
|
||||||
## Forking Angular on Github
|
## Forking Angular on Github
|
||||||
|
|
||||||
|
|
@ -85,6 +87,16 @@ grunt package
|
||||||
Administrator). This is because `grunt package` creates some symbolic links.
|
Administrator). This is because `grunt package` creates some symbolic links.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
**Note:** If you're using Linux, and npm install fails with the message
|
||||||
|
'Please try running this command again as root/Administrator.', you may need to globally install grunt and bower:
|
||||||
|
<ul>
|
||||||
|
<li>sudo npm install -g grunt-cli</li>
|
||||||
|
<li>sudo npm install -g bower</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
The build output can be located under the `build` directory. It consists of the following files and
|
The build output can be located under the `build` directory. It consists of the following files and
|
||||||
directories:
|
directories:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,12 +73,11 @@ directory.</p></li>
|
||||||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> and its plugins if you
|
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> and its plugins if you
|
||||||
don't have it already:</p>
|
don't have it already:</p>
|
||||||
<pre>
|
<pre>
|
||||||
npm install -g karma
|
|
||||||
npm install
|
npm install
|
||||||
</pre></li>
|
</pre></li>
|
||||||
<li><p>You will need an http server running on your system. Mac and Linux machines typically
|
<li><p>You will need an http server running on your system. Mac and Linux machines typically
|
||||||
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
|
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
|
||||||
to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
|
to run a simple bundled http server: <code>node scripts/web-server.js</code>.</p></li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -107,8 +106,8 @@ directory.</p>
|
||||||
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
|
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
|
||||||
executed from the Windows command line.</li>
|
executed from the Windows command line.</li>
|
||||||
<li><p>You need an http server running on your system, but if you don't already have one
|
<li><p>You need an http server running on your system, but if you don't already have one
|
||||||
already installed, you can use <code>node</code> to run <code>scripts\web-server.js</code>, a simple
|
already installed, you can use <code>node</code> to run a simple
|
||||||
bundled http server.</p></li>
|
bundled http server: <code>node scripts\web-server.js</code>.</p></li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ angular-seed, and run the application in the browser.
|
||||||
<ol>
|
<ol>
|
||||||
<li>In a <i>separate</i> terminal tab or window, run <code>node ./scripts/web-server.js</code> to start the web server.</li>
|
<li>In a <i>separate</i> terminal tab or window, run <code>node ./scripts/web-server.js</code> to start the web server.</li>
|
||||||
<li>Open a browser window for the app and navigate to <a
|
<li>Open a browser window for the app and navigate to <a
|
||||||
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
</li>
|
</li>
|
||||||
<li><b>For other http servers:</b>
|
<li><b>For other http servers:</b>
|
||||||
|
|
@ -54,7 +54,7 @@ angular-seed, and run the application in the browser.
|
||||||
<li><b>For node.js users:</b>
|
<li><b>For node.js users:</b>
|
||||||
<ol>
|
<ol>
|
||||||
<li>In a <i>separate</i> terminal tab or window, run <code>node scripts\web-server.js</code> to start the web server.</li>
|
<li>In a <i>separate</i> terminal tab or window, run <code>node scripts\web-server.js</code> to start the web server.</li>
|
||||||
<li>Open a browser window for the app and navigate to <a href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
|
<li>Open a browser window for the app and navigate to <a href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
</li>
|
</li>
|
||||||
<li><b>For other http servers:</b>
|
<li><b>For other http servers:</b>
|
||||||
|
|
@ -73,7 +73,7 @@ angular-seed, and run the application in the browser.
|
||||||
You can now see the page in your browser. It's not very exciting, but that's OK.
|
You can now see the page in your browser. It's not very exciting, but that's OK.
|
||||||
|
|
||||||
The HTML page that displays "Nothing here yet!" was constructed with the HTML code shown below.
|
The HTML page that displays "Nothing here yet!" was constructed with the HTML code shown below.
|
||||||
The code contains some key Angular elements that we will need going forward.
|
The code contains some key Angular elements that we will need as we progress.
|
||||||
|
|
||||||
__`app/index.html`:__
|
__`app/index.html`:__
|
||||||
<pre>
|
<pre>
|
||||||
|
|
@ -104,7 +104,7 @@ __`app/index.html`:__
|
||||||
|
|
||||||
The `ng-app` attribute represents an Angular directive named `ngApp` (Angular uses
|
The `ng-app` attribute represents an Angular directive named `ngApp` (Angular uses
|
||||||
`name-with-dashes` for its custom attributes and `camelCase` for the corresponding directives
|
`name-with-dashes` for its custom attributes and `camelCase` for the corresponding directives
|
||||||
that implements them).
|
which implement them).
|
||||||
This directive is used to flag the html element that Angular should consider to be the root element
|
This directive is used to flag the html element that Angular should consider to be the root element
|
||||||
of our application.
|
of our application.
|
||||||
This gives application developers the freedom to tell Angular if the entire html page or only a
|
This gives application developers the freedom to tell Angular if the entire html page or only a
|
||||||
|
|
@ -179,7 +179,7 @@ For the purposes of this tutorial, we modified the angular-seed with the followi
|
||||||
* Removed the example app
|
* Removed the example app
|
||||||
* Added phone images to `app/img/phones/`
|
* Added phone images to `app/img/phones/`
|
||||||
* Added phone data files (JSON) to `app/phones/`
|
* Added phone data files (JSON) to `app/phones/`
|
||||||
* Added [Bootstrap](http://twitter.github.com/bootstrap/) files to `app/css/` and `app/img/`
|
* Added [Bootstrap](http://getbootstrap.com) files to `app/css/` and `app/img/`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -184,11 +184,15 @@ http://pivotal.github.com/jasmine/ Jasmine home page} and at the {@link
|
||||||
http://pivotal.github.io/jasmine/ Jasmine docs}.
|
http://pivotal.github.io/jasmine/ Jasmine docs}.
|
||||||
|
|
||||||
The angular-seed project is pre-configured to run all unit tests using {@link
|
The angular-seed project is pre-configured to run all unit tests using {@link
|
||||||
http://karma-runner.github.io/ Karma}. To run the test, do the following:
|
http://karma-runner.github.io/ Karma}. Ensure that the necessary karma plugins are installed.
|
||||||
|
You can do this by issuing `npm install` into your terminal.
|
||||||
|
|
||||||
|
|
||||||
|
To run the test, do the following:
|
||||||
|
|
||||||
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
|
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
|
||||||
`./scripts/test.sh` to start the Karma server (the config file necessary to start the server
|
`./scripts/test.sh` (if you are on Windows, run scripts\test.bat) to start the Karma server (the
|
||||||
is located at `./config/karma.conf.js`).
|
config file necessary to start the server is located at `./config/karma.conf.js`).
|
||||||
|
|
||||||
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
||||||
the background. Karma will use this browser for test execution.
|
the background. Karma will use this browser for test execution.
|
||||||
|
|
@ -202,7 +206,7 @@ is located at `./config/karma.conf.js`).
|
||||||
|
|
||||||
Yay! The test passed! Or not...
|
Yay! The test passed! Or not...
|
||||||
|
|
||||||
4. To rerun the tests, just change any of the source or test files. Karma will notice the change
|
4. To rerun the tests, just change any of the source or test .js files. Karma will notice the change
|
||||||
and will rerun the tests for you. Now isn't that sweet?
|
and will rerun the tests for you. Now isn't that sweet?
|
||||||
|
|
||||||
# Experiments
|
# Experiments
|
||||||
|
|
@ -213,9 +217,13 @@ is located at `./config/karma.conf.js`).
|
||||||
|
|
||||||
* Create a new model property in the controller and bind to it from the template. For example:
|
* Create a new model property in the controller and bind to it from the template. For example:
|
||||||
|
|
||||||
$scope.hello = "Hello, World!"
|
$scope.name = "World";
|
||||||
|
|
||||||
Refresh your browser to make sure it says, "Hello, World!"
|
Then add a new binding to `index.html`:
|
||||||
|
|
||||||
|
<p>Hello, {{name}}!</p>
|
||||||
|
|
||||||
|
Refresh your browser and verify that it says "Hello, World!".
|
||||||
|
|
||||||
* Create a repeater that constructs a simple table:
|
* Create a repeater that constructs a simple table:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -127,8 +127,8 @@ end-to-end tests! Use `./scripts/e2e-test.sh` script for that. End-to-end tests
|
||||||
with unit tests, Karma will exit after the test run and will not automatically rerun the test
|
with unit tests, Karma will exit after the test run and will not automatically rerun the test
|
||||||
suite on every file change. To rerun the test suite, execute the `e2e-test.sh` script again.
|
suite on every file change. To rerun the test suite, execute the `e2e-test.sh` script again.
|
||||||
|
|
||||||
Note: You must ensure you've installed karma-ng-scenario prior to running the `e2e-test.sh` script.
|
Note: You must ensure you've installed the karma-ng-scenario framework plugin prior to running the
|
||||||
You can do this by issuing `npm install karma-ng-scenario` into your terminal.
|
`e2e-test.sh` script. You can do this by issuing `npm install` into your terminal.
|
||||||
|
|
||||||
This test verifies that the search box and the repeater are correctly wired together. Notice how
|
This test verifies that the search box and the repeater are correctly wired together. Notice how
|
||||||
easy it is to write end-to-end tests in Angular. Although this example is for a simple test, it
|
easy it is to write end-to-end tests in Angular. Although this example is for a simple test, it
|
||||||
|
|
@ -154,7 +154,7 @@ really is that easy to set up any functional, readable, end-to-end test.
|
||||||
`ngController` declaration to the HTML element because it is the common parent of both the body
|
`ngController` declaration to the HTML element because it is the common parent of both the body
|
||||||
and title elements:
|
and title elements:
|
||||||
|
|
||||||
<html ng-app ng-controller="PhoneListCtrl">
|
<html ng-app="phonecatApp" ng-controller="PhoneListCtrl">
|
||||||
|
|
||||||
Be sure to __remove__ the `ng-controller` declaration from the body element.
|
Be sure to __remove__ the `ng-controller` declaration from the body element.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,8 +88,8 @@ phonecatApp.controller('PhoneListCtrl', function ($scope) {
|
||||||
record. This property is used to order phones by age.
|
record. This property is used to order phones by age.
|
||||||
|
|
||||||
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
|
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
|
||||||
not set the default value here, the model would stay uninitialized until our user would pick an
|
not set a default value here, the `orderBy` filter would remain uninitialized until our
|
||||||
option from the drop down menu.
|
user picked an option from the drop down menu.
|
||||||
|
|
||||||
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
|
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
|
||||||
browser, "Newest" is selected in the drop down menu. This is because we set `orderProp` to `'age'`
|
browser, "Newest" is selected in the drop down menu. This is because we set `orderProp` to `'age'`
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,9 @@ describe('PhoneCat controllers', function() {
|
||||||
describe('PhoneListCtrl', function(){
|
describe('PhoneListCtrl', function(){
|
||||||
var scope, ctrl, $httpBackend;
|
var scope, ctrl, $httpBackend;
|
||||||
|
|
||||||
|
// Load our app module definition before each test.
|
||||||
|
beforeEach(module('phonecatApp'));
|
||||||
|
|
||||||
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
|
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
|
||||||
// This allows us to inject a service but then attach it to a variable
|
// This allows us to inject a service but then attach it to a variable
|
||||||
// with the same name as the service.
|
// with the same name as the service.
|
||||||
|
|
@ -199,7 +202,7 @@ isolated from the work done in other tests.
|
||||||
|
|
||||||
* We created a new scope for our controller by calling `$rootScope.$new()`
|
* We created a new scope for our controller by calling `$rootScope.$new()`
|
||||||
|
|
||||||
* We called the injected `$controller` function passing the name of the`PhoneListCtrl` controller
|
* We called the injected `$controller` function passing the name of the `PhoneListCtrl` controller
|
||||||
and the created scope as parameters.
|
and the created scope as parameters.
|
||||||
|
|
||||||
Because our code now uses the `$http` service to fetch the phone list data in our controller, before
|
Because our code now uses the `$http` service to fetch the phone list data in our controller, before
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ We also added phone images next to each record using an image tag with the {@lin
|
||||||
api/ng.directive:ngSrc ngSrc} directive. That directive prevents the
|
api/ng.directive:ngSrc ngSrc} directive. That directive prevents the
|
||||||
browser from treating the angular `{{ expression }}` markup literally, and initiating a request to
|
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
|
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 class="diagram" src="{{phone.imageUrl}}">`).
|
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.
|
Using the `ngSrc` directive prevents the browser from making an http request to an invalid location.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ view, Angular will use the `phone-list.html` template and the `PhoneListCtrl` co
|
||||||
We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
|
We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
|
||||||
empty `PhoneDetailCtrl` controller to the `app/js/controllers.js` file for the phone details view.
|
empty `PhoneDetailCtrl` controller to the `app/js/controllers.js` file for the phone details view.
|
||||||
|
|
||||||
`$route.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when the browser
|
`$routeProvider.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when the browser
|
||||||
address doesn't match either of our routes.
|
address doesn't match either of our routes.
|
||||||
|
|
||||||
Note the use of the `:phoneId` parameter in the second route declaration. The `$route` service uses
|
Note the use of the `:phoneId` parameter in the second route declaration. The `$route` service uses
|
||||||
|
|
@ -177,7 +177,7 @@ route into the layout template. This makes it a perfect fit for our `index.html`
|
||||||
|
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
**Note:** Starting with AngularJS version 1.2, `ngRoute` is in its own module and must be loaded by loading
|
**Note:** Starting with AngularJS version 1.2, `ngRoute` is in its own module and must be loaded by loading
|
||||||
the `angular-route.js` file distributed with Angular. The easist way to load the file is to add a `<script>`
|
the `angular-route.js` file distributed with Angular. The easiest way to load the file is to add a `<script>`
|
||||||
tag to your `index.html` file as shown below.
|
tag to your `index.html` file as shown below.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -258,7 +258,7 @@ to various URLs and verify that the correct view was rendered.
|
||||||
<pre>
|
<pre>
|
||||||
...
|
...
|
||||||
it('should redirect index.html to index.html#/phones', function() {
|
it('should redirect index.html to index.html#/phones', function() {
|
||||||
browser().navigateTo('../../app/index.html');
|
browser().navigateTo('app/index.html');
|
||||||
expect(browser().location().url()).toBe('/phones');
|
expect(browser().location().url()).toBe('/phones');
|
||||||
});
|
});
|
||||||
...
|
...
|
||||||
|
|
@ -266,7 +266,7 @@ to various URLs and verify that the correct view was rendered.
|
||||||
describe('Phone detail view', function() {
|
describe('Phone detail view', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
browser().navigateTo('../../app/index.html#/phones/nexus-s');
|
browser().navigateTo('app/index.html#/phones/nexus-s');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ Angular's server}.
|
||||||
|
|
||||||
<button ng-click="hello('Elmo')">Hello</button>
|
<button ng-click="hello('Elmo')">Hello</button>
|
||||||
|
|
||||||
to the `phone-details.html` template.
|
to the `phone-detail.html` template.
|
||||||
|
|
||||||
<div style="display: none">
|
<div style="display: none">
|
||||||
TODO!
|
TODO!
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ In this step, you will improve the way our app fetches data.
|
||||||
<div doc-tutorial-reset="11"></div>
|
<div doc-tutorial-reset="11"></div>
|
||||||
|
|
||||||
|
|
||||||
The last improvement we will make to our app is to define a custom service that represents a {@link
|
The next improvement we will make to our app is to define a custom service that represents a {@link
|
||||||
http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client. Using this client we
|
http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client. Using this client we
|
||||||
can make XHR requests for data in an easier way, without having to deal with the lower-level {@link
|
can make XHR requests for data in an easier way, without having to deal with the lower-level {@link
|
||||||
api/ng.$http $http} API, HTTP methods and URLs.
|
api/ng.$http $http} API, HTTP methods and URLs.
|
||||||
|
|
@ -185,7 +185,7 @@ describe('PhoneCat controllers', function() {
|
||||||
xyzPhoneData = function() {
|
xyzPhoneData = function() {
|
||||||
return {
|
return {
|
||||||
name: 'phone xyz',
|
name: 'phone xyz',
|
||||||
images: ['image/url1.png', 'image/url2.png']
|
images: ['image/url1.png', 'image/url2.png']
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ __`app/index.html`.__
|
||||||
<pre>
|
<pre>
|
||||||
...
|
...
|
||||||
<!-- jQuery is used for JavaScript animations (include this before angular.js) -->
|
<!-- jQuery is used for JavaScript animations (include this before angular.js) -->
|
||||||
<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
|
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
|
||||||
|
|
||||||
<!-- required module to enable animation support in AngularJS -->
|
<!-- required module to enable animation support in AngularJS -->
|
||||||
<script src="lib/angular/angular-animate.js"></script>
|
<script src="lib/angular/angular-animate.js"></script>
|
||||||
|
|
@ -56,6 +56,10 @@ __`app/index.html`.__
|
||||||
...
|
...
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<div class="alert alert-error">
|
||||||
|
**Important:** Be sure to use jQuery version `1.10.x`. AngularJS does not yet support jQuery `2.x`.
|
||||||
|
</div>
|
||||||
|
|
||||||
Animations can now be created within the CSS code (`animations.css`) as well as the JavaScript code (`animations.js`).
|
Animations can now be created within the CSS code (`animations.css`) as well as the JavaScript code (`animations.js`).
|
||||||
But before we start, let's create a new module which uses the ngAnimate module as a dependency just like we did before
|
But before we start, let's create a new module which uses the ngAnimate module as a dependency just like we did before
|
||||||
with `ngResource`.
|
with `ngResource`.
|
||||||
|
|
@ -153,7 +157,7 @@ __`app/css/animations.css`__
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
As you can see our `phone-listing` CSS class is combined together with the animation hooks that occur when items are
|
As you can see our `phone-listing` CSS class is combined together with the animation hooks that occur when items are
|
||||||
inserted info and removed from the list:
|
inserted into and removed from the list:
|
||||||
|
|
||||||
* The `ng-enter` class is applied to the element when a new phone is added to the list and rendered on the page.
|
* The `ng-enter` class is applied to the element when a new phone is added to the list and rendered on the page.
|
||||||
* The `ng-move` class is applied when items are moved around in the list.
|
* The `ng-move` class is applied when items are moved around in the list.
|
||||||
|
|
@ -289,8 +293,7 @@ Let's add another animation to our application. Switching to our `phone-detail.h
|
||||||
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 clicking on the thumbnails listed on the page,
|
||||||
the profile phone image changes. But how can we change this around to add animations?
|
the profile phone image changes. But how can we change this around to add animations?
|
||||||
|
|
||||||
Lets think about it first,
|
Let's think about it first. Basically, when you click on a thumbnail image, you're changing the state of the profile image to reflect the newly
|
||||||
basically when you click on a thumbnail image, you're changing the state of the profile image to reflect the newly
|
|
||||||
selected thumbnail image.
|
selected thumbnail image.
|
||||||
The best way to specify state changes within HTML is to use classes.
|
The best way to specify state changes within HTML is to use classes.
|
||||||
Much like before, how we used a CSS class to specify
|
Much like before, how we used a CSS class to specify
|
||||||
|
|
@ -337,45 +340,58 @@ Although we could do that, let's take the opportunity to learn how to create Jav
|
||||||
|
|
||||||
__`app/js/animations.js`.__
|
__`app/js/animations.js`.__
|
||||||
<pre>
|
<pre>
|
||||||
angular.module('phonecatAnimations', ['ngAnimate'])
|
var phonecatAnimations = angular.module('phonecatAnimations', ['ngAnimate']);
|
||||||
|
|
||||||
.animation('.phone', function() {
|
phonecatAnimations.animation('.phone', function() {
|
||||||
return {
|
|
||||||
addClass : function(element, className, done) {
|
|
||||||
if(className != 'active') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
element.css({
|
|
||||||
position: 'absolute',
|
|
||||||
top: 500,
|
|
||||||
left: 0,
|
|
||||||
display: 'block'
|
|
||||||
});
|
|
||||||
jQuery(element).animate({
|
|
||||||
top: 0
|
|
||||||
}, done);
|
|
||||||
|
|
||||||
return function(cancel) {
|
var animateUp = function(element, className, done) {
|
||||||
if(cancel) element.stop();
|
if(className != 'active') {
|
||||||
};
|
return;
|
||||||
},
|
}
|
||||||
removeClass : function(element, className, done) {
|
element.css({
|
||||||
if(className != 'active') return;
|
position: 'absolute',
|
||||||
element.css({
|
top: 500,
|
||||||
position: 'absolute',
|
left: 0,
|
||||||
left: 0,
|
display: 'block'
|
||||||
top: 0
|
});
|
||||||
});
|
|
||||||
jQuery(element).animate({
|
|
||||||
top: -500
|
|
||||||
}, done);
|
|
||||||
|
|
||||||
return function(cancel) {
|
jQuery(element).animate({
|
||||||
if(cancel) element.stop();
|
top: 0
|
||||||
};
|
}, done);
|
||||||
|
|
||||||
|
return function(cancel) {
|
||||||
|
if(cancel) {
|
||||||
|
element.stop();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
|
var animateDown = function(element, className, done) {
|
||||||
|
if(className != 'active') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.css({
|
||||||
|
position: 'absolute',
|
||||||
|
left: 0,
|
||||||
|
top: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
jQuery(element).animate({
|
||||||
|
top: -500
|
||||||
|
}, done);
|
||||||
|
|
||||||
|
return function(cancel) {
|
||||||
|
if(cancel) {
|
||||||
|
element.stop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
addClass: animateUp,
|
||||||
|
removeClass: animateDown
|
||||||
|
};
|
||||||
|
});
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
Note that we're using {@link http://jquery.com/ jQuery} to implement the animation. jQuery
|
Note that we're using {@link http://jquery.com/ jQuery} to implement the animation. jQuery
|
||||||
|
|
@ -383,10 +399,6 @@ isn't required to do JavaScript animations with AngularJS, but we're going to us
|
||||||
your own JavaScript animation library is beyond the scope of this tutorial. For more on
|
your own JavaScript animation library is beyond the scope of this tutorial. For more on
|
||||||
`jQuery.animate`, see the {@link http://api.jquery.com/animate/ jQuery documentation}.
|
`jQuery.animate`, see the {@link http://api.jquery.com/animate/ jQuery documentation}.
|
||||||
|
|
||||||
<div class="alert alert-error">
|
|
||||||
**Important:** Be sure to use jQuery version `1.10.x`. AngularJS does not yet support jQuery `2.x`.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
The `addClass` and `removeClass` callback functions are called whenever an a class is added or removed
|
The `addClass` and `removeClass` callback functions are called whenever an a class is added or removed
|
||||||
on the element that contains the class we registered, which is in this case `.phone`. When the `.active`
|
on the element that contains the class we registered, which is in this case `.phone`. When the `.active`
|
||||||
class is added to the element (via the `ng-class` directive) the `addClass` JavaScript callback will
|
class is added to the element (via the `ng-class` directive) the `addClass` JavaScript callback will
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ previous steps using the `git checkout` command.
|
||||||
For more details and examples of the Angular concepts we touched on in this tutorial, see the
|
For more details and examples of the Angular concepts we touched on in this tutorial, see the
|
||||||
{@link guide/ Developer Guide}.
|
{@link guide/ Developer Guide}.
|
||||||
|
|
||||||
For several more examples of code, see the {@link cookbook/ Cookbook}.
|
|
||||||
|
|
||||||
When you are ready to start developing a project using Angular, we recommend that you bootstrap
|
When you are ready to start developing a project using Angular, we recommend that you bootstrap
|
||||||
your development with the {@link https://github.com/angular/angular-seed angular-seed} project.
|
your development with the {@link https://github.com/angular/angular-seed angular-seed} project.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
var ngdoc = require('../src/ngdoc.js');
|
var ngdoc = require('../src/ngdoc.js');
|
||||||
var DOM = require('../src/dom.js').DOM;
|
var DOM = require('../src/dom.js').DOM;
|
||||||
|
var gruntUtil = require('../../lib/grunt/utils.js');
|
||||||
|
|
||||||
|
|
||||||
describe('ngdoc', function() {
|
describe('ngdoc', function() {
|
||||||
var Doc = ngdoc.Doc;
|
var Doc = ngdoc.Doc;
|
||||||
|
|
@ -287,6 +289,33 @@ describe('ngdoc', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('api section', function() {
|
||||||
|
|
||||||
|
it('should render a "view source" button with link to the source in master', function() {
|
||||||
|
var doc = new Doc({
|
||||||
|
id: 'ng.abc',
|
||||||
|
name: 'ng.abc',
|
||||||
|
section: 'api',
|
||||||
|
ngdoc: 'service',
|
||||||
|
file: 'fooService.js',
|
||||||
|
line: '333'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (gruntUtil.getVersion().full.indexOf('-') === -1) {
|
||||||
|
expect(doc.html().match(/^(<a .*?<\/a>)/)[1]).toMatch(
|
||||||
|
/<a href="http:\/\/github\.com\/angular\/angular\.js\/tree\/v\d+\.\d+\.\d+\/fooService\.js#L333" class="view-source/
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
expect(doc.html().match(/^(<a .*?<\/a>)/)[1]).toMatch(
|
||||||
|
/<a href="http:\/\/github\.com\/angular\/angular\.js\/tree\/[a-z0-9]{7}\/fooService\.js#L333" class="view-source/
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
|
|
||||||
describe('TAG', function() {
|
describe('TAG', function() {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports.appCache = appCache;
|
exports.appCache = appCache;
|
||||||
var fs = require('q-fs');
|
var fs = require('q-io/fs');
|
||||||
var Q = require('qq');
|
var Q = require('qq');
|
||||||
function identity($) {return $;}
|
function identity($) {return $;}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ var makeUnique = {
|
||||||
'script.js': true,
|
'script.js': true,
|
||||||
'unit.js': true,
|
'unit.js': true,
|
||||||
'spec.js': true,
|
'spec.js': true,
|
||||||
'scenario.js': true
|
'scenario.js': true,
|
||||||
|
'protractorTest.js': true
|
||||||
}
|
}
|
||||||
|
|
||||||
function ids(list) {
|
function ids(list) {
|
||||||
|
|
@ -14,7 +15,7 @@ function ids(list) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
exports.Example = function(scenarios) {
|
exports.Example = function(scenarios, protractorTests) {
|
||||||
this.module = '';
|
this.module = '';
|
||||||
this.deps = ['angular.js'];
|
this.deps = ['angular.js'];
|
||||||
this.html = [];
|
this.html = [];
|
||||||
|
|
@ -24,6 +25,8 @@ exports.Example = function(scenarios) {
|
||||||
this.unit = [];
|
this.unit = [];
|
||||||
this.scenario = [];
|
this.scenario = [];
|
||||||
this.scenarios = scenarios;
|
this.scenarios = scenarios;
|
||||||
|
this.protractorTest = [];
|
||||||
|
this.protractorTests = protractorTests;
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.Example.prototype.setModule = function(module) {
|
exports.Example.prototype.setModule = function(module) {
|
||||||
|
|
@ -44,6 +47,10 @@ exports.Example.prototype.addSource = function(name, content) {
|
||||||
var ext = name == 'scenario.js' ? 'scenario' : name.split('.')[1],
|
var ext = name == 'scenario.js' ? 'scenario' : name.split('.')[1],
|
||||||
id = name;
|
id = name;
|
||||||
|
|
||||||
|
if (name == 'protractorTest.js') {
|
||||||
|
ext = 'protractorTest';
|
||||||
|
}
|
||||||
|
|
||||||
if (makeUnique[name] && usedIds[id]) {
|
if (makeUnique[name] && usedIds[id]) {
|
||||||
id = name + '-' + (seqCount++);
|
id = name + '-' + (seqCount++);
|
||||||
}
|
}
|
||||||
|
|
@ -56,6 +63,9 @@ exports.Example.prototype.addSource = function(name, content) {
|
||||||
if (ext == 'scenario') {
|
if (ext == 'scenario') {
|
||||||
this.scenarios.push(content);
|
this.scenarios.push(content);
|
||||||
}
|
}
|
||||||
|
if (ext == 'protractorTest') {
|
||||||
|
this.protractorTests.push(content);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.Example.prototype.enableAnimations = function() {
|
exports.Example.prototype.enableAnimations = function() {
|
||||||
|
|
@ -92,6 +102,7 @@ exports.Example.prototype.toHtmlEdit = function() {
|
||||||
out.push(' source-edit-json="' + ids(this.json) + '"');
|
out.push(' source-edit-json="' + ids(this.json) + '"');
|
||||||
out.push(' source-edit-unit="' + ids(this.unit) + '"');
|
out.push(' source-edit-unit="' + ids(this.unit) + '"');
|
||||||
out.push(' source-edit-scenario="' + ids(this.scenario) + '"');
|
out.push(' source-edit-scenario="' + ids(this.scenario) + '"');
|
||||||
|
out.push(' source-edit-protractor="' + ids(this.protractorTest) + '"');
|
||||||
out.push('></div>\n');
|
out.push('></div>\n');
|
||||||
return out.join('');
|
return out.join('');
|
||||||
};
|
};
|
||||||
|
|
@ -107,6 +118,7 @@ exports.Example.prototype.toHtmlTabs = function() {
|
||||||
htmlTabs(this.json);
|
htmlTabs(this.json);
|
||||||
htmlTabs(this.unit);
|
htmlTabs(this.unit);
|
||||||
htmlTabs(this.scenario);
|
htmlTabs(this.scenario);
|
||||||
|
htmlTabs(this.protractorTest);
|
||||||
out.push('</div>');
|
out.push('</div>');
|
||||||
return out.join('');
|
return out.join('');
|
||||||
|
|
||||||
|
|
@ -119,7 +131,8 @@ exports.Example.prototype.toHtmlTabs = function() {
|
||||||
if (name === 'index.html') {
|
if (name === 'index.html') {
|
||||||
wrap = ' ng-html-wrap="' + self.module + ' ' + self.deps.join(' ') + '"';
|
wrap = ' ng-html-wrap="' + self.module + ' ' + self.deps.join(' ') + '"';
|
||||||
}
|
}
|
||||||
if (name == 'scenario.js') name = 'End to end test';
|
if (name == 'scenario.js') name = 'ngScenario e2e test';
|
||||||
|
if (name == 'protractorTest.js') name = 'Protractor e2e test';
|
||||||
|
|
||||||
out.push(
|
out.push(
|
||||||
'<div class="tab-pane" title="' + name + '">\n' +
|
'<div class="tab-pane" title="' + name + '">\n' +
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ writer.makeDir('build/docs/', true).then(function() {
|
||||||
return writer.makeDir('build/docs/components/bootstrap');
|
return writer.makeDir('build/docs/components/bootstrap');
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
return writer.makeDir('build/docs/components/font-awesome');
|
return writer.makeDir('build/docs/components/font-awesome');
|
||||||
|
}).then(function() {
|
||||||
|
return writer.makeDir('build/docs/e2etests');
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
console.log('Generating AngularJS Reference Documentation...');
|
console.log('Generating AngularJS Reference Documentation...');
|
||||||
return reader.collect();
|
return reader.collect();
|
||||||
|
|
@ -53,6 +55,10 @@ writer.makeDir('build/docs/', true).then(function() {
|
||||||
var id = doc.id.replace('angular.Module', 'angular.IModule');
|
var id = doc.id.replace('angular.Module', 'angular.IModule');
|
||||||
|
|
||||||
fileFutures.push(writer.output('partials/' + doc.section + '/' + id + '.html', doc.html()));
|
fileFutures.push(writer.output('partials/' + doc.section + '/' + id + '.html', doc.html()));
|
||||||
|
// If it has a sample Protractor test, output that as well.
|
||||||
|
if (doc.protractorTests.length) {
|
||||||
|
fileFutures.push(writer.output('ptore2e/' + doc.section + '/' + id + '_test.js', ngdoc.writeProtractorTest(doc)));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ngdoc.checkBrokenLinks(docs);
|
ngdoc.checkBrokenLinks(docs);
|
||||||
|
|
@ -74,10 +80,10 @@ function writeTheRest(writesFuture) {
|
||||||
var versions = ngdoc.ngVersions();
|
var versions = ngdoc.ngVersions();
|
||||||
var currentVersion = ngdoc.ngCurrentVersion();
|
var currentVersion = ngdoc.ngCurrentVersion();
|
||||||
|
|
||||||
writesFuture.push(writer.symlink('../../docs/content/notes', 'build/docs/notes', 'dir'));
|
writesFuture.push(writer.symlink('../../docs/content/notes', 'build/docs/notes', 'directory'));
|
||||||
writesFuture.push(writer.symlinkTemplate('css', 'dir'));
|
writesFuture.push(writer.symlinkTemplate('css', 'directory'));
|
||||||
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'dir'));
|
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'directory'));
|
||||||
writesFuture.push(writer.symlinkTemplate('js', 'dir'));
|
writesFuture.push(writer.symlinkTemplate('js', 'directory'));
|
||||||
|
|
||||||
var manifest = 'manifest="/build/docs/appcache.manifest"';
|
var manifest = 'manifest="/build/docs/appcache.manifest"';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ var lookupMinerrMsg = function (doc) {
|
||||||
exports.trim = trim;
|
exports.trim = trim;
|
||||||
exports.metadata = metadata;
|
exports.metadata = metadata;
|
||||||
exports.scenarios = scenarios;
|
exports.scenarios = scenarios;
|
||||||
|
exports.writeProtractorTest = writeProtractorTest;
|
||||||
exports.merge = merge;
|
exports.merge = merge;
|
||||||
exports.checkBrokenLinks = checkBrokenLinks;
|
exports.checkBrokenLinks = checkBrokenLinks;
|
||||||
exports.Doc = Doc;
|
exports.Doc = Doc;
|
||||||
|
|
@ -50,7 +51,7 @@ exports.ngVersions = function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
//match the future version of AngularJS that is set in the package.json file
|
//match the future version of AngularJS that is set in the package.json file
|
||||||
return expandVersions(sortVersionsNatrually(versions), exports.ngCurrentVersion().full);
|
return expandVersions(sortVersionsNaturally(versions), exports.ngCurrentVersion().full);
|
||||||
|
|
||||||
function expandVersions(versions, latestVersion) {
|
function expandVersions(versions, latestVersion) {
|
||||||
var RC_VERSION = /rc\d/;
|
var RC_VERSION = /rc\d/;
|
||||||
|
|
@ -86,7 +87,7 @@ exports.ngVersions = function() {
|
||||||
return expanded;
|
return expanded;
|
||||||
};
|
};
|
||||||
|
|
||||||
function sortVersionsNatrually(versions) {
|
function sortVersionsNaturally(versions) {
|
||||||
var versionMap = {},
|
var versionMap = {},
|
||||||
NON_RC_RELEASE_NUMBER = 999;
|
NON_RC_RELEASE_NUMBER = 999;
|
||||||
for(var i = versions.length - 1; i >= 0; i--) {
|
for(var i = versions.length - 1; i >= 0; i--) {
|
||||||
|
|
@ -155,6 +156,7 @@ function Doc(text, file, line) {
|
||||||
this.line = line;
|
this.line = line;
|
||||||
}
|
}
|
||||||
this.scenarios = this.scenarios || [];
|
this.scenarios = this.scenarios || [];
|
||||||
|
this.protractorTests = this.protractorTests || [];
|
||||||
this.requires = this.requires || [];
|
this.requires = this.requires || [];
|
||||||
this.param = this.param || [];
|
this.param = this.param || [];
|
||||||
this.properties = this.properties || [];
|
this.properties = this.properties || [];
|
||||||
|
|
@ -292,7 +294,7 @@ Doc.prototype = {
|
||||||
replace(/<example(?:\s+module="([^"]*)")?(?:\s+deps="([^"]*)")?(\s+animations="true")?>([\s\S]*?)<\/example>/gmi,
|
replace(/<example(?:\s+module="([^"]*)")?(?:\s+deps="([^"]*)")?(\s+animations="true")?>([\s\S]*?)<\/example>/gmi,
|
||||||
function(_, module, deps, animations, content) {
|
function(_, module, deps, animations, content) {
|
||||||
|
|
||||||
var example = new Example(self.scenarios);
|
var example = new Example(self.scenarios, self.protractorTests);
|
||||||
if(animations) {
|
if(animations) {
|
||||||
example.enableAnimations();
|
example.enableAnimations();
|
||||||
example.addDeps('angular-animate.js');
|
example.addDeps('angular-animate.js');
|
||||||
|
|
@ -329,7 +331,7 @@ Doc.prototype = {
|
||||||
}).
|
}).
|
||||||
replace(/^<doc:example(\s+[^>]*)?>([\s\S]*)<\/doc:example>/mi, function(_, attrs, content) {
|
replace(/^<doc:example(\s+[^>]*)?>([\s\S]*)<\/doc:example>/mi, function(_, attrs, content) {
|
||||||
var html, script, scenario,
|
var html, script, scenario,
|
||||||
example = new Example(self.scenarios);
|
example = new Example(self.scenarios, self.protractorTests);
|
||||||
|
|
||||||
example.setModule((attrs||'module=""').match(/^\s*module=["'](.*)["']\s*$/)[1]);
|
example.setModule((attrs||'module=""').match(/^\s*module=["'](.*)["']\s*$/)[1]);
|
||||||
content.
|
content.
|
||||||
|
|
@ -347,6 +349,8 @@ Doc.prototype = {
|
||||||
}).
|
}).
|
||||||
replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi, function(_, before, content){
|
replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi, function(_, before, content){
|
||||||
example.addSource('scenario.js', content);
|
example.addSource('scenario.js', content);
|
||||||
|
}).replace(/(<doc:protractor>)([\s\S]*)(<\/doc:protractor>)/mi, function(_, before, content){
|
||||||
|
example.addSource('protractorTest.js', content);
|
||||||
});
|
});
|
||||||
|
|
||||||
return placeholder(example.toHtml());
|
return placeholder(example.toHtml());
|
||||||
|
|
@ -548,7 +552,7 @@ Doc.prototype = {
|
||||||
minerrMsg;
|
minerrMsg;
|
||||||
|
|
||||||
var gitTagFromFullVersion = function(version) {
|
var gitTagFromFullVersion = function(version) {
|
||||||
var match = version.match(/-(\w{7})/);
|
var match = version.match(/sha\.(\w{7})/);
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
// git sha
|
// git sha
|
||||||
|
|
@ -1106,6 +1110,22 @@ function scenarios(docs){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function writeProtractorTest(doc){
|
||||||
|
var lines = [];
|
||||||
|
lines.push('describe("' + doc.section + '/' + doc.id + '", function() {');
|
||||||
|
lines.push(' beforeEach(function() {');
|
||||||
|
lines.push(' browser.get("index-nocache.html#!/' + doc.section + '/' + doc.id + '");');
|
||||||
|
lines.push(' });');
|
||||||
|
lines.push('');
|
||||||
|
doc.protractorTests.forEach(function(test){
|
||||||
|
lines.push(indentCode(trim(test), 2));
|
||||||
|
lines.push('');
|
||||||
|
});
|
||||||
|
lines.push('});');
|
||||||
|
lines.push('');
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
function metadata(docs){
|
function metadata(docs){
|
||||||
|
|
@ -1381,6 +1401,14 @@ function explainModuleInstallation(moduleName){
|
||||||
modulePackage = 'angular-' + moduleName,
|
modulePackage = 'angular-' + moduleName,
|
||||||
modulePackageFile = modulePackage + '.js';
|
modulePackageFile = modulePackage + '.js';
|
||||||
|
|
||||||
|
// Deal with inconsistent ngMock naming - doing it verbosely and explicitly here
|
||||||
|
// rather than cleverly interweaving it in the previous lines to make it obvious
|
||||||
|
// what is going on
|
||||||
|
if ( moduleName == 'mock' ) {
|
||||||
|
modulePackage = 'angular-mocks';
|
||||||
|
modulePackageFile = modulePackage + '.js';
|
||||||
|
}
|
||||||
|
|
||||||
return '<h1>Installation</h1>' +
|
return '<h1>Installation</h1>' +
|
||||||
'<p>First include <code>' + modulePackageFile +'</code> in your HTML:</p><pre><code>' +
|
'<p>First include <code>' + modulePackageFile +'</code> in your HTML:</p><pre><code>' +
|
||||||
' <script src="angular.js">\n' +
|
' <script src="angular.js">\n' +
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ exports.collect = collect;
|
||||||
|
|
||||||
var ngdoc = require('./ngdoc.js'),
|
var ngdoc = require('./ngdoc.js'),
|
||||||
Q = require('qq'),
|
Q = require('qq'),
|
||||||
qfs = require('q-fs'),
|
qfs = require('q-io/fs'),
|
||||||
PATH = require('path');
|
PATH = require('path');
|
||||||
|
|
||||||
var NEW_LINE = /\n\r?/;
|
var NEW_LINE = /\n\r?/;
|
||||||
|
|
|
||||||
|
|
@ -16,4 +16,4 @@ RewriteCond %{HTTP_HOST} ^docs-next\.angularjs\.org$
|
||||||
RewriteRule appcache.manifest http://code.angularjs.org/next/docs/appcache.manifest [R=301]
|
RewriteRule appcache.manifest http://code.angularjs.org/next/docs/appcache.manifest [R=301]
|
||||||
|
|
||||||
## HTML5 URL Support ##
|
## HTML5 URL Support ##
|
||||||
RewriteRule ^(guide|api|cookbook|misc|tutorial)(/.*)?$ index.html
|
RewriteRule ^(guide|api|misc|tutorial)(/.*)?$ index.html
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,32 @@
|
||||||
.content h4,
|
.content h4,
|
||||||
.content h5 {
|
.content h5 {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
|
letter-spacing: -0.06em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h2 {
|
||||||
|
font-size: 36px;
|
||||||
|
margin-bottom: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h3 {
|
||||||
|
font-size: 24px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
padding-top: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h4 {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content ul {
|
||||||
|
margin-top: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content h6 {
|
||||||
|
text-transform:none;
|
||||||
|
color:black;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.parameters > li > p,
|
ul.parameters > li > p,
|
||||||
|
|
@ -220,6 +246,10 @@ ul.events > li > h3 {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tutorial-nav li {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.clear {
|
.clear {
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
@ -522,6 +552,10 @@ pre ol li {
|
||||||
margin-bottom:30px;
|
margin-bottom:30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.definition-table td {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
.component-heading {
|
.component-heading {
|
||||||
text-transform:capitalize;
|
text-transform:capitalize;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var indexFile = (location.pathname.match(/\/(index[^\.]*\.html)/) || ['', ''])[1],
|
var indexFile = (location.pathname.match(/\/(index[^\.]*\.html)/) || ['', ''])[1],
|
||||||
rUrl = /(#!\/|api|guide|misc|tutorial|cookbook|error|index[^\.]*\.html).*$/,
|
rUrl = /(#!\/|api|guide|misc|tutorial|error|index[^\.]*\.html).*$/,
|
||||||
baseUrl = location.href.replace(rUrl, indexFile),
|
baseUrl = location.href.replace(rUrl, indexFile),
|
||||||
jQuery = /index-jq[^\.]*\.html$/.test(baseUrl),
|
jQuery = /index-jq[^\.]*\.html$/.test(baseUrl),
|
||||||
debug = /index[^\.]*-debug\.html$/.test(baseUrl),
|
debug = /index[^\.]*-debug\.html$/.test(baseUrl),
|
||||||
|
|
@ -334,10 +334,6 @@
|
||||||
<div id="loading" ng-show="loading">Loading...</div>
|
<div id="loading" ng-show="loading">Loading...</div>
|
||||||
|
|
||||||
<div ng-hide="loading" ng-include src="currentPage.partialUrl" onload="afterPartialLoaded()" autoscroll class="content slide-reveal"></div>
|
<div ng-hide="loading" ng-include src="currentPage.partialUrl" onload="afterPartialLoaded()" autoscroll class="content slide-reveal"></div>
|
||||||
|
|
||||||
<div class="alert alert-info">
|
|
||||||
<a href="http://blog.angularjs.org/2013/11/farewell-disqus.html">Where did Disqus go?</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -364,7 +360,7 @@
|
||||||
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p class="pull-right"><a href="#">Back to top</a></p>
|
<p class="pull-right"><a back-to-top href="#">Back to top</a></p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Super-powered by Google ©2010-2012
|
Super-powered by Google ©2010-2012
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,19 @@ var docsApp = {
|
||||||
filter: {}
|
filter: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
docsApp.controller.DocsVersionsCtrl = ['$scope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $window, NG_VERSIONS, NG_VERSION) {
|
docsApp.controller.DocsVersionsCtrl = ['$scope', '$rootScope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $rootScope, $window, NG_VERSIONS, NG_VERSION) {
|
||||||
$scope.docs_versions = NG_VERSIONS;
|
$scope.docs_versions = NG_VERSIONS;
|
||||||
$scope.docs_version = NG_VERSIONS[0];
|
$scope.docs_version = NG_VERSIONS[0];
|
||||||
|
|
||||||
$scope.jumpToDocsVersion = function(version) {
|
$scope.jumpToDocsVersion = function(version) {
|
||||||
$window.location = version.url;
|
var currentPagePath = '';
|
||||||
|
|
||||||
|
// preserve URL path when switching between doc versions
|
||||||
|
if (angular.isObject($rootScope.currentPage) && $rootScope.currentPage.section && $rootScope.currentPage.id) {
|
||||||
|
currentPagePath = '/' + $rootScope.currentPage.section + '/' + $rootScope.currentPage.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$window.location = version.url + currentPagePath;
|
||||||
};
|
};
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|
@ -103,9 +111,6 @@ docsApp.serviceFactory.docsSearch = ['$rootScope','lunrSearch', 'NG_PAGES',
|
||||||
angular.forEach(index.search(q), function(result) {
|
angular.forEach(index.search(q), function(result) {
|
||||||
var item = NG_PAGES[result.ref];
|
var item = NG_PAGES[result.ref];
|
||||||
var section = item.section;
|
var section = item.section;
|
||||||
if(section == 'cookbook') {
|
|
||||||
section = 'tutorial';
|
|
||||||
}
|
|
||||||
results[section] = results[section] || [];
|
results[section] = results[section] || [];
|
||||||
if(results[section].length < 15) {
|
if(results[section].length < 15) {
|
||||||
results[section].push(item);
|
results[section].push(item);
|
||||||
|
|
@ -137,11 +142,19 @@ docsApp.directive.docsSearchInput = ['$document',function($document) {
|
||||||
var ESCAPE_KEY_KEYCODE = 27,
|
var ESCAPE_KEY_KEYCODE = 27,
|
||||||
FORWARD_SLASH_KEYCODE = 191;
|
FORWARD_SLASH_KEYCODE = 191;
|
||||||
angular.element($document[0].body).bind('keydown', function(event) {
|
angular.element($document[0].body).bind('keydown', function(event) {
|
||||||
var input = element[0];
|
if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement) {
|
||||||
if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement != input) {
|
var activeElement = document.activeElement;
|
||||||
event.stopPropagation();
|
var activeTagName = activeElement.nodeName.toLowerCase();
|
||||||
event.preventDefault();
|
var hasInputFocus = activeTagName == 'input' || activeTagName == 'select' ||
|
||||||
input.focus();
|
activeTagName == 'option' || activeTagName == 'textarea' ||
|
||||||
|
activeElement.hasAttribute('contenteditable');
|
||||||
|
if(!hasInputFocus) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
var input = element[0];
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -273,10 +286,10 @@ docsApp.directive.docTutorialNav = function(templateMerge) {
|
||||||
element.addClass('btn-group');
|
element.addClass('btn-group');
|
||||||
element.addClass('tutorial-nav');
|
element.addClass('tutorial-nav');
|
||||||
element.append(templateMerge(
|
element.append(templateMerge(
|
||||||
'<li class="btn btn-primary"><a href="tutorial/{{prev}}"><i class="icon-step-backward"></i> Previous</a></li>\n' +
|
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="icon-step-backward"></i> Previous</li></a>\n' +
|
||||||
'<li class="btn btn-primary"><a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><i class="icon-play"></i> Live Demo</a></li>\n' +
|
'<a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="icon-play"></i> Live Demo</li></a>\n' +
|
||||||
'<li class="btn btn-primary"><a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><i class="icon-search"></i> Code Diff</a></li>\n' +
|
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="icon-search"></i> Code Diff</li></a>\n' +
|
||||||
'<li class="btn btn-primary"><a href="tutorial/{{next}}">Next <i class="icon-step-forward"></i></a></li>', props));
|
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="icon-step-forward"></i></li></a>', props));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -372,6 +385,21 @@ docsApp.directive.errorDisplay = ['$location', 'errorLinkFilter', function ($loc
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* backToTop Directive
|
||||||
|
* @param {Function} $anchorScroll
|
||||||
|
*
|
||||||
|
* @description Ensure that the browser scrolls when the anchor is clicked
|
||||||
|
*/
|
||||||
|
docsApp.directive.backToTop = ['$anchorScroll', function($anchorScroll) {
|
||||||
|
return function link(scope, element) {
|
||||||
|
element.on('click', function(event) {
|
||||||
|
scope.$apply($anchorScroll);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}];
|
||||||
|
|
||||||
|
|
||||||
docsApp.serviceFactory.angularUrls = function($document) {
|
docsApp.serviceFactory.angularUrls = function($document) {
|
||||||
var urls = {};
|
var urls = {};
|
||||||
|
|
||||||
|
|
@ -599,7 +627,6 @@ docsApp.serviceFactory.sections = ['NG_PAGES', function sections(NG_PAGES) {
|
||||||
api: [],
|
api: [],
|
||||||
tutorial: [],
|
tutorial: [],
|
||||||
misc: [],
|
misc: [],
|
||||||
cookbook: [],
|
|
||||||
error: [],
|
error: [],
|
||||||
getPage: function(sectionId, partialId) {
|
getPage: function(sectionId, partialId) {
|
||||||
var pages = sections[sectionId];
|
var pages = sections[sectionId];
|
||||||
|
|
@ -630,7 +657,7 @@ docsApp.serviceFactory.sections = ['NG_PAGES', function sections(NG_PAGES) {
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|
||||||
docsApp.controller.DocsController = function($scope, $location, $window, $cookies, sections) {
|
docsApp.controller.DocsController = function($scope, $rootScope, $location, $window, $cookies, sections) {
|
||||||
$scope.fold = function(url) {
|
$scope.fold = function(url) {
|
||||||
if(url) {
|
if(url) {
|
||||||
$scope.docs_fold = '/notes/' + url;
|
$scope.docs_fold = '/notes/' + url;
|
||||||
|
|
@ -644,7 +671,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var OFFLINE_COOKIE_NAME = 'ng-offline',
|
var OFFLINE_COOKIE_NAME = 'ng-offline',
|
||||||
DOCS_PATH = /^\/(api)|(guide)|(cookbook)|(misc)|(tutorial)|(error)/,
|
DOCS_PATH = /^\/(api)|(guide)|(misc)|(tutorial)|(error)/,
|
||||||
INDEX_PATH = /^(\/|\/index[^\.]*.html)$/,
|
INDEX_PATH = /^(\/|\/index[^\.]*.html)$/,
|
||||||
GLOBALS = /^angular\.([^\.]+)$/,
|
GLOBALS = /^angular\.([^\.]+)$/,
|
||||||
ERROR = /^([a-zA-Z0-9_$]+:)?([a-zA-Z0-9_$]+)$/,
|
ERROR = /^([a-zA-Z0-9_$]+:)?([a-zA-Z0-9_$]+)$/,
|
||||||
|
|
@ -706,7 +733,6 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||||
guide: 'Developer Guide',
|
guide: 'Developer Guide',
|
||||||
misc: 'Miscellaneous',
|
misc: 'Miscellaneous',
|
||||||
tutorial: 'Tutorial',
|
tutorial: 'Tutorial',
|
||||||
cookbook: 'Examples',
|
|
||||||
error: 'Error Reference'
|
error: 'Error Reference'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -721,9 +747,9 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||||
sectionName = SECTION_NAME[sectionId] || sectionId,
|
sectionName = SECTION_NAME[sectionId] || sectionId,
|
||||||
page = sections.getPage(sectionId, partialId);
|
page = sections.getPage(sectionId, partialId);
|
||||||
|
|
||||||
$scope.currentPage = sections.getPage(sectionId, partialId);
|
$rootScope.currentPage = sections.getPage(sectionId, partialId);
|
||||||
|
|
||||||
if (!$scope.currentPage) {
|
if (!$rootScope.currentPage) {
|
||||||
$scope.partialTitle = 'Error: Page Not Found!';
|
$scope.partialTitle = 'Error: Page Not Found!';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* for testability
|
* for testability
|
||||||
*/
|
*/
|
||||||
var pathUtils = require('path');
|
var pathUtils = require('path');
|
||||||
var qfs = require('q-fs');
|
var qfs = require('q-io/fs');
|
||||||
var Q = require('qq');
|
var Q = require('qq');
|
||||||
var OUTPUT_DIR = pathUtils.join('build','docs');
|
var OUTPUT_DIR = pathUtils.join('build','docs');
|
||||||
var TEMPLATES_DIR = pathUtils.join('docs','src','templates');
|
var TEMPLATES_DIR = pathUtils.join('docs','src','templates');
|
||||||
|
|
@ -76,7 +76,7 @@ function symlink(from, to, type) {
|
||||||
// qfs will normalize the path arguments for us here
|
// qfs will normalize the path arguments for us here
|
||||||
return qfs.exists(to).then(function(exists) {
|
return qfs.exists(to).then(function(exists) {
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
return qfs.symbolicLink(to, from, type);
|
return qfs.symbolicLink(to, from, type || 'file');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "#################################"
|
||||||
|
echo "#### Jenkins Build ############"
|
||||||
|
echo "#################################"
|
||||||
|
|
||||||
# Enable tracing and exit on first failure
|
# Enable tracing and exit on first failure
|
||||||
set -xe
|
set -xe
|
||||||
|
|
||||||
|
|
||||||
# Define reasonable set of browsers in case we are running manually from commandline
|
# Define reasonable set of browsers in case we are running manually from commandline
|
||||||
if [[ -z "$BROWSERS" ]]
|
if [[ -z "$BROWSERS" ]]
|
||||||
then
|
then
|
||||||
|
|
@ -25,13 +28,15 @@ rm -f angular.js.size
|
||||||
npm install --color false
|
npm install --color false
|
||||||
grunt ci-checks package --no-color
|
grunt ci-checks package --no-color
|
||||||
|
|
||||||
|
# DOCS generator unit tests #
|
||||||
|
grunt test:docgen --no-color
|
||||||
|
|
||||||
# UNIT TESTS #
|
# UNIT TESTS #
|
||||||
grunt test:unit --browsers $BROWSERS --reporters=dots,junit --no-colors --no-color
|
grunt test:unit --browsers $BROWSERS --reporters=dots,junit --no-colors --no-color
|
||||||
|
|
||||||
|
|
||||||
# END TO END TESTS #
|
# END TO END TESTS #
|
||||||
grunt test:e2e --browsers $BROWSERS_E2E --reporters=dots,junit --no-colors --no-color
|
grunt test:e2e --browsers $BROWSERS_E2E --reporters=dots,junit --no-colors --no-color
|
||||||
|
grunt test:protractor
|
||||||
|
|
||||||
# Promises/A+ TESTS #
|
# Promises/A+ TESTS #
|
||||||
grunt test:promises-aplus --no-color
|
grunt test:promises-aplus --no-color
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@ module.exports = function(config) {
|
||||||
|
|
||||||
'build/angular.js',
|
'build/angular.js',
|
||||||
'build/angular-cookies.js',
|
'build/angular-cookies.js',
|
||||||
'build/angular-mocks.js',
|
|
||||||
'build/angular-resource.js',
|
'build/angular-resource.js',
|
||||||
'build/angular-touch.js',
|
'build/angular-touch.js',
|
||||||
'build/angular-sanitize.js',
|
'build/angular-sanitize.js',
|
||||||
'build/angular-route.js',
|
'build/angular-route.js',
|
||||||
'build/angular-animate.js',
|
'build/angular-animate.js',
|
||||||
|
'build/angular-mocks.js',
|
||||||
|
|
||||||
'build/docs/components/lunr.js',
|
'build/docs/components/lunr.js',
|
||||||
'build/docs/components/google-code-prettify.js',
|
'build/docs/components/google-code-prettify.js',
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
var sharedConfig = require('./karma-shared.conf');
|
|
||||||
|
|
||||||
module.exports = function(config) {
|
|
||||||
sharedConfig(config, {testName: 'AngularJS: e2e', logFile: 'karma-e2e.log'});
|
|
||||||
|
|
||||||
config.set({
|
|
||||||
frameworks: [],
|
|
||||||
files: [
|
|
||||||
'build/angular-scenario.js',
|
|
||||||
'node_modules/karma-ng-scenario/lib/adapter.js',
|
|
||||||
'build/docs/docs-scenario.js'
|
|
||||||
],
|
|
||||||
|
|
||||||
proxies: {
|
|
||||||
// angular.js, angular-resource.js, etc
|
|
||||||
'/angular': 'http://localhost:8000/build/angular',
|
|
||||||
'/': 'http://localhost:8000/build/docs/'
|
|
||||||
},
|
|
||||||
|
|
||||||
junitReporter: {
|
|
||||||
outputFile: 'test_out/e2e.xml',
|
|
||||||
suite: 'E2E'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
@ -6,21 +6,25 @@ module.exports = function(config, specificOptions) {
|
||||||
logColors: true,
|
logColors: true,
|
||||||
browsers: ['Chrome'],
|
browsers: ['Chrome'],
|
||||||
browserDisconnectTimeout: 10000,
|
browserDisconnectTimeout: 10000,
|
||||||
|
browserDisconnectTolerance: 2,
|
||||||
|
browserNoActivityTimeout: 20000,
|
||||||
|
|
||||||
|
|
||||||
// config for Travis CI
|
// SauceLabs config for local development.
|
||||||
sauceLabs: {
|
sauceLabs: {
|
||||||
testName: specificOptions.testName || 'AngularJS',
|
testName: specificOptions.testName || 'AngularJS',
|
||||||
startConnect: false,
|
startConnect: true,
|
||||||
tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
|
options: {
|
||||||
|
'selenium-version': '2.37.0'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// BrowserStack config for Travis CI
|
// BrowserStack config for local development.
|
||||||
browserStack: {
|
browserStack: {
|
||||||
startTunnel: false,
|
|
||||||
project: 'AngularJS',
|
project: 'AngularJS',
|
||||||
name: specificOptions.testName,
|
name: specificOptions.testName,
|
||||||
build: process.env.TRAVIS_BUILD_NUMBER
|
startTunnel: true,
|
||||||
|
timeout: 600 // 10min
|
||||||
},
|
},
|
||||||
|
|
||||||
// For more browsers on Sauce Labs see:
|
// For more browsers on Sauce Labs see:
|
||||||
|
|
@ -32,13 +36,14 @@ module.exports = function(config, specificOptions) {
|
||||||
},
|
},
|
||||||
'SL_Firefox': {
|
'SL_Firefox': {
|
||||||
base: 'SauceLabs',
|
base: 'SauceLabs',
|
||||||
browserName: 'firefox'
|
browserName: 'firefox',
|
||||||
|
version: '26'
|
||||||
},
|
},
|
||||||
'SL_Safari': {
|
'SL_Safari': {
|
||||||
base: 'SauceLabs',
|
base: 'SauceLabs',
|
||||||
browserName: 'safari',
|
browserName: 'safari',
|
||||||
platform: 'Mac 10.8',
|
platform: 'OS X 10.9',
|
||||||
version: '6'
|
version: '7'
|
||||||
},
|
},
|
||||||
'SL_IE_8': {
|
'SL_IE_8': {
|
||||||
base: 'SauceLabs',
|
base: 'SauceLabs',
|
||||||
|
|
@ -58,6 +63,12 @@ module.exports = function(config, specificOptions) {
|
||||||
platform: 'Windows 2012',
|
platform: 'Windows 2012',
|
||||||
version: '10'
|
version: '10'
|
||||||
},
|
},
|
||||||
|
'SL_IE_11': {
|
||||||
|
base: 'SauceLabs',
|
||||||
|
browserName: 'internet explorer',
|
||||||
|
platform: 'Windows 8.1',
|
||||||
|
version: '11'
|
||||||
|
},
|
||||||
|
|
||||||
'BS_Chrome': {
|
'BS_Chrome': {
|
||||||
base: 'BrowserStack',
|
base: 'BrowserStack',
|
||||||
|
|
@ -110,11 +121,66 @@ module.exports = function(config, specificOptions) {
|
||||||
|
|
||||||
|
|
||||||
if (process.env.TRAVIS) {
|
if (process.env.TRAVIS) {
|
||||||
|
var buildLabel = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||||
|
|
||||||
|
config.logLevel = config.LOG_DEBUG;
|
||||||
|
config.transports = ['websocket', 'xhr-polling'];
|
||||||
|
|
||||||
|
config.browserStack.build = buildLabel;
|
||||||
|
config.browserStack.startTunnel = false;
|
||||||
|
|
||||||
|
config.sauceLabs.build = buildLabel;
|
||||||
|
config.sauceLabs.startConnect = false;
|
||||||
|
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||||
|
|
||||||
|
// TODO(vojta): remove once SauceLabs supports websockets.
|
||||||
|
// This speeds up the capturing a bit, as browsers don't even try to use websocket.
|
||||||
|
config.transports = ['xhr-polling'];
|
||||||
|
|
||||||
// Debug logging into a file, that we print out at the end of the build.
|
// Debug logging into a file, that we print out at the end of the build.
|
||||||
config.loggers.push({
|
config.loggers.push({
|
||||||
type: 'file',
|
type: 'file',
|
||||||
filename: process.env.LOGS_DIR + '/' + (specificOptions.logFile || 'karma.log'),
|
filename: process.env.LOGS_DIR + '/' + (specificOptions.logFile || 'karma.log')
|
||||||
level: config.LOG_DEBUG
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Terrible hack to workaround inflexibility of log4js:
|
||||||
|
// - ignore web-server's 404 warnings,
|
||||||
|
// - ignore DEBUG logs (on Travis), we log them into a file instead.
|
||||||
|
var IGNORED_404 = [
|
||||||
|
'/favicon.ico',
|
||||||
|
'/%7B%7BtestUrl%7D%7D',
|
||||||
|
'/someSanitizedUrl',
|
||||||
|
'/{{testUrl}}'
|
||||||
|
];
|
||||||
|
var log4js = require('./node_modules/karma/node_modules/log4js');
|
||||||
|
var layouts = require('./node_modules/karma/node_modules/log4js/lib/layouts');
|
||||||
|
var originalConfigure = log4js.configure;
|
||||||
|
log4js.configure = function(log4jsConfig) {
|
||||||
|
var consoleAppender = log4jsConfig.appenders.shift();
|
||||||
|
var originalResult = originalConfigure.call(log4js, log4jsConfig);
|
||||||
|
var layout = layouts.layout(consoleAppender.layout.type, consoleAppender.layout);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
log4js.addAppender(function(log) {
|
||||||
|
var msg = log.data[0];
|
||||||
|
|
||||||
|
// ignore web-server's 404s
|
||||||
|
if (log.categoryName === 'web-server' && log.level.levelStr === config.LOG_WARN &&
|
||||||
|
IGNORED_404.some(function(ignoredLog) {return msg.indexOf(ignoredLog) !== -1})) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// on Travis, ignore DEBUG statements
|
||||||
|
if (process.env.TRAVIS && log.level.levelStr === config.LOG_DEBUG) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(layout(log));
|
||||||
|
});
|
||||||
|
|
||||||
|
return originalResult;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ var http = require('http');
|
||||||
var BrowserStackTunnel = require('browserstacktunnel-wrapper');
|
var BrowserStackTunnel = require('browserstacktunnel-wrapper');
|
||||||
|
|
||||||
var HOSTNAME = 'localhost';
|
var HOSTNAME = 'localhost';
|
||||||
var PORTS = [9090, 9876];
|
var PORTS = require('../grunt/utils').availablePorts;
|
||||||
var ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY;
|
var ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY;
|
||||||
var READY_FILE = process.env.SAUCE_CONNECT_READY_FILE;
|
var READY_FILE = process.env.SAUCE_CONNECT_READY_FILE;
|
||||||
|
|
||||||
|
|
@ -25,9 +25,8 @@ var tunnel = new BrowserStackTunnel({
|
||||||
hosts: hosts
|
hosts: hosts
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('Starting tunnel on ports', PORTS.join(', '));
|
||||||
tunnel.start(function(error) {
|
tunnel.start(function(error) {
|
||||||
console.log('** callback **')
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Can not establish the tunnel', error);
|
console.error('Can not establish the tunnel', error);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,14 @@ module.exports = function(grunt) {
|
||||||
util.startKarma.call(util, this.data, false, this.async());
|
util.startKarma.call(util, this.data, false, this.async());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
grunt.registerTask('webdriver', 'Update webdriver', function() {
|
||||||
|
util.updateWebdriver.call(util, this.async());
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.registerMultiTask('runprotractor', 'Run Protractor integration tests', function() {
|
||||||
|
util.startProtractor.call(util, this.data, this.async());
|
||||||
|
});
|
||||||
|
|
||||||
grunt.registerTask('collect-errors', 'Combine stripped error files', function () {
|
grunt.registerTask('collect-errors', 'Combine stripped error files', function () {
|
||||||
util.collectErrors();
|
util.collectErrors();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,26 @@ var fs = require('fs');
|
||||||
var shell = require('shelljs');
|
var shell = require('shelljs');
|
||||||
var grunt = require('grunt');
|
var grunt = require('grunt');
|
||||||
var spawn = require('child_process').spawn;
|
var spawn = require('child_process').spawn;
|
||||||
|
var semver = require('semver');
|
||||||
var version;
|
var version;
|
||||||
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
|
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
|
||||||
|
|
||||||
|
var PORT_MIN = 8000;
|
||||||
|
var PORT_MAX = 9999;
|
||||||
|
var TRAVIS_BUILD_NUMBER = parseInt(process.env.TRAVIS_BUILD_NUMBER, 10);
|
||||||
|
var getRandomPorts = function() {
|
||||||
|
if (!process.env.TRAVIS) {
|
||||||
|
return [9876, 9877];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate two numbers between PORT_MIN and PORT_MAX, based on TRAVIS_BUILD_NUMBER.
|
||||||
|
return [
|
||||||
|
PORT_MIN + (TRAVIS_BUILD_NUMBER % (PORT_MAX - PORT_MIN)),
|
||||||
|
PORT_MIN + ((TRAVIS_BUILD_NUMBER + 100) % (PORT_MAX - PORT_MIN))
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
init: function() {
|
init: function() {
|
||||||
|
|
@ -16,29 +33,85 @@ module.exports = {
|
||||||
|
|
||||||
getVersion: function(){
|
getVersion: function(){
|
||||||
if (version) return version;
|
if (version) return version;
|
||||||
|
|
||||||
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
|
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
|
||||||
var match = package.version.match(/^([^\-]*)(?:\-(.+))?$/);
|
try {
|
||||||
var semver = match[1].split('.');
|
|
||||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
|
||||||
|
|
||||||
var fullVersion = match[1];
|
var gitTag = getTagOfCurrentCommit();
|
||||||
|
var semVerVersion, codeName, fullVersion;
|
||||||
|
if (gitTag) {
|
||||||
|
// tagged release
|
||||||
|
fullVersion = semVerVersion = semver.valid(gitTag);
|
||||||
|
codeName = getTaggedReleaseCodeName(gitTag);
|
||||||
|
} else {
|
||||||
|
// snapshot release
|
||||||
|
semVerVersion = getSnapshotVersion();
|
||||||
|
fullVersion = semVerVersion + '-' + getSnapshotSuffix();
|
||||||
|
codeName = 'snapshot'
|
||||||
|
}
|
||||||
|
|
||||||
if (match[2]) {
|
var versionParts = semVerVersion.match(/(\d+)\.(\d+)\.(\d+)/);
|
||||||
fullVersion += '-';
|
|
||||||
fullVersion += (match[2] == 'snapshot') ? hash : match[2];
|
version = {
|
||||||
|
full: fullVersion,
|
||||||
|
major: versionParts[1],
|
||||||
|
minor: versionParts[2],
|
||||||
|
dot: versionParts[3],
|
||||||
|
codename: codeName,
|
||||||
|
cdn: package.cdnVersion
|
||||||
|
};
|
||||||
|
|
||||||
|
return version;
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
grunt.fail.warn(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
version = {
|
function getTagOfCurrentCommit() {
|
||||||
full: fullVersion,
|
var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
|
||||||
major: semver[0],
|
var gitTagOutput = gitTagResult.output.trim();
|
||||||
minor: semver[1],
|
var branchVersionPattern = new RegExp(package.branchVersion.replace('.', '\\.').replace('*', '\\d+'));
|
||||||
dot: semver[2].replace(/rc\d+/, ''),
|
if (gitTagResult.code === 0 && gitTagOutput.match(branchVersionPattern)) {
|
||||||
codename: package.codename,
|
return gitTagOutput;
|
||||||
cdn: package.cdnVersion
|
} else {
|
||||||
};
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return version;
|
function getTaggedReleaseCodeName(tagName) {
|
||||||
|
var tagMessage = shell.exec('git cat-file -p '+ tagName +' | grep "codename"', {silent:true}).output;
|
||||||
|
var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
|
||||||
|
if (!codeName) {
|
||||||
|
throw new Error("Could not extract release code name. The message of tag "+tagName+
|
||||||
|
" must match '*codename(some release name)*'");
|
||||||
|
}
|
||||||
|
return codeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSnapshotVersion() {
|
||||||
|
var oldTags = shell.exec('git tag -l v'+package.branchVersion, {silent:true}).output.trim().split('\n');
|
||||||
|
// ignore non semver versions.
|
||||||
|
oldTags = oldTags.filter(function(version) {
|
||||||
|
return version && semver.valid(version);
|
||||||
|
});
|
||||||
|
if (oldTags.length) {
|
||||||
|
oldTags.sort(semver.compare);
|
||||||
|
semVerVersion = oldTags[oldTags.length-1];
|
||||||
|
if (semVerVersion.indexOf('-') !== -1) {
|
||||||
|
semVerVersion = semver.inc(semVerVersion, 'prerelease');
|
||||||
|
} else {
|
||||||
|
semVerVersion = semver.inc(semVerVersion, 'patch');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
semVerVersion = semver.valid(package.branchVersion.replace(/\*/g, '0'));
|
||||||
|
}
|
||||||
|
return semVerVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSnapshotSuffix() {
|
||||||
|
var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER || 'local';
|
||||||
|
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||||
|
return 'build.'+jenkinsBuild+'+sha.'+hash;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -63,6 +136,38 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
updateWebdriver: function(done){
|
||||||
|
var p = spawn('node', ['node_modules/protractor/bin/webdriver-manager', 'update']);
|
||||||
|
p.stdout.pipe(process.stdout);
|
||||||
|
p.stderr.pipe(process.stderr);
|
||||||
|
p.on('exit', function(code){
|
||||||
|
if(code !== 0) grunt.fail.warn('Webdriver failed to update');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
startProtractor: function(config, done){
|
||||||
|
var sauceUser = grunt.option('sauceUser');
|
||||||
|
var sauceKey = grunt.option('sauceKey');
|
||||||
|
var tunnelIdentifier = grunt.option('capabilities.tunnel-identifier');
|
||||||
|
var sauceBuild = grunt.option('capabilities.build');
|
||||||
|
var args = ['node_modules/protractor/bin/protractor', config];
|
||||||
|
if (sauceUser) args.push('--sauceUser=' + sauceUser);
|
||||||
|
if (sauceKey) args.push('--sauceKey=' + sauceKey);
|
||||||
|
if (tunnelIdentifier) args.push('--capabilities.tunnel-identifier=' + tunnelIdentifier);
|
||||||
|
if (sauceBuild) args.push('--capabilities.build=' + sauceBuild);
|
||||||
|
|
||||||
|
|
||||||
|
var p = spawn('node', args);
|
||||||
|
p.stdout.pipe(process.stdout);
|
||||||
|
p.stderr.pipe(process.stderr);
|
||||||
|
p.on('exit', function(code){
|
||||||
|
if(code !== 0) grunt.fail.warn('Protractor test(s) failed. Exit code: ' + code);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
wrap: function(src, name){
|
wrap: function(src, name){
|
||||||
src.unshift('src/' + name + '.prefix');
|
src.unshift('src/' + name + '.prefix');
|
||||||
src.push('src/' + name + '.suffix');
|
src.push('src/' + name + '.suffix');
|
||||||
|
|
@ -295,5 +400,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// see http://saucelabs.com/docs/connect#localhost
|
// see http://saucelabs.com/docs/connect#localhost
|
||||||
sauceLabsAvailablePorts: [9000, 9001, 9080, 9090, 9876]
|
sauceLabsAvailablePorts: [9000, 9001, 9080, 9090, 9876],
|
||||||
|
// pseudo-random port numbers for BrowserStack
|
||||||
|
availablePorts: getRandomPorts()
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,8 @@ ARGS=""
|
||||||
if [ ! -z "$TRAVIS_JOB_NUMBER" ]; then
|
if [ ! -z "$TRAVIS_JOB_NUMBER" ]; then
|
||||||
ARGS="$ARGS --tunnel-identifier $TRAVIS_JOB_NUMBER"
|
ARGS="$ARGS --tunnel-identifier $TRAVIS_JOB_NUMBER"
|
||||||
fi
|
fi
|
||||||
if [ ! -z "$SAUCE_CONNECT_READY_FILE" ]; then
|
if [ ! -z "$BROWSER_PROVIDER_READY_FILE" ]; then
|
||||||
ARGS="$ARGS --readyfile $SAUCE_CONNECT_READY_FILE"
|
ARGS="$ARGS --readyfile $BROWSER_PROVIDER_READY_FILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
50
package.json
50
package.json
|
|
@ -1,44 +1,48 @@
|
||||||
{
|
{
|
||||||
"name": "angularjs",
|
"name": "angularjs",
|
||||||
"version": "1.2.3",
|
"branchVersion": "1.2.*",
|
||||||
"cdnVersion": "1.2.2",
|
"cdnVersion": "1.2.11",
|
||||||
"codename": "unicorn-zapper",
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/angular/angular.js.git"
|
"url": "https://github.com/angular/angular.js.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"grunt": "~0.4.1",
|
"grunt": "~0.4.2",
|
||||||
"bower": "~1.2.2",
|
"grunt-bump": "~0.0.13",
|
||||||
"grunt-contrib-clean": "~0.5.0",
|
"grunt-contrib-clean": "~0.5.0",
|
||||||
"grunt-contrib-compress": "~0.5.2",
|
|
||||||
"grunt-contrib-connect": "~0.5.0",
|
"grunt-contrib-connect": "~0.5.0",
|
||||||
|
"grunt-contrib-compress": "~0.5.2",
|
||||||
"grunt-contrib-copy": "~0.4.1",
|
"grunt-contrib-copy": "~0.4.1",
|
||||||
|
"grunt-contrib-jshint": "~0.7.2",
|
||||||
|
"grunt-ddescribe-iit": "~0.0.1",
|
||||||
|
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||||
|
"grunt-jscs-checker": "~0.3.2",
|
||||||
|
"grunt-merge-conflict": "~0.0.1",
|
||||||
|
"grunt-parallel": "~0.3.1",
|
||||||
|
"grunt-shell": "~0.4.0",
|
||||||
|
"load-grunt-tasks": "~0.3.0",
|
||||||
|
"bower": "~1.2.2",
|
||||||
"jasmine-node": "~1.11.0",
|
"jasmine-node": "~1.11.0",
|
||||||
"q": "~0.9.2",
|
"q": "~0.9.2",
|
||||||
"q-fs": "~0.1.36",
|
"q-io": "~1.10.6",
|
||||||
"qq": "~0.3.5",
|
"qq": "~0.3.5",
|
||||||
"shelljs": "~0.2.6",
|
"shelljs": "~0.2.6",
|
||||||
"karma": "~0.11",
|
"karma": "0.11.12",
|
||||||
"karma-jasmine": "~0.1.0",
|
"karma-jasmine": "0.1.5",
|
||||||
"karma-chrome-launcher": "~0.1.0",
|
"karma-chrome-launcher": "0.1.2",
|
||||||
"karma-firefox-launcher": "~0.1.0",
|
"karma-firefox-launcher": "0.1.3",
|
||||||
"karma-ng-scenario": "~0.1.0",
|
"karma-ng-scenario": "0.1.0",
|
||||||
"karma-junit-reporter": "git://github.com/karma-runner/karma-junit-reporter#karma-0.11",
|
"karma-junit-reporter": "0.2.1",
|
||||||
"karma-sauce-launcher": "~0.1.1",
|
"karma-sauce-launcher": "0.2.0",
|
||||||
"karma-script-launcher": "~0.1.0",
|
"karma-script-launcher": "0.1.0",
|
||||||
|
"karma-browserstack-launcher": "0.0.7",
|
||||||
|
"protractor": "~0.17.0",
|
||||||
"yaml-js": "~0.0.8",
|
"yaml-js": "~0.0.8",
|
||||||
"marked": "0.2.9",
|
"marked": "0.2.9",
|
||||||
"rewire": "1.1.3",
|
"rewire": "1.1.3",
|
||||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
|
||||||
"grunt-parallel": "~0.3.1",
|
|
||||||
"grunt-ddescribe-iit": "~0.0.1",
|
|
||||||
"grunt-merge-conflict": "~0.0.1",
|
|
||||||
"promises-aplus-tests": "~1.3.2",
|
"promises-aplus-tests": "~1.3.2",
|
||||||
"grunt-shell": "~0.4.0",
|
|
||||||
"semver": "~2.1.0",
|
"semver": "~2.1.0",
|
||||||
"lodash": "~2.1.0",
|
"lodash": "~2.1.0",
|
||||||
"karma-browserstack-launcher": "~0.0.4",
|
|
||||||
"browserstacktunnel-wrapper": "~1.1.1"
|
"browserstacktunnel-wrapper": "~1.1.1"
|
||||||
},
|
},
|
||||||
"licenses": [
|
"licenses": [
|
||||||
|
|
@ -47,7 +51,5 @@
|
||||||
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
|
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {}
|
||||||
"grunt-contrib-jshint": "~0.6.4"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
31
protractor-conf.js
Normal file
31
protractor-conf.js
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
exports.config = {
|
||||||
|
allScriptsTimeout: 11000,
|
||||||
|
|
||||||
|
specs: [
|
||||||
|
'build/docs/ptore2e/**/*.js',
|
||||||
|
'test/e2e/docsAppE2E.js'
|
||||||
|
],
|
||||||
|
|
||||||
|
capabilities: {
|
||||||
|
'browserName': 'chrome'
|
||||||
|
},
|
||||||
|
|
||||||
|
baseUrl: 'http://localhost:8000/build/docs/',
|
||||||
|
|
||||||
|
framework: 'jasmine',
|
||||||
|
|
||||||
|
onPrepare: function() {
|
||||||
|
// Disable animations so e2e tests run more quickly
|
||||||
|
var disableNgAnimate = function() {
|
||||||
|
angular.module('disableNgAnimate', []).run(function($animate) {
|
||||||
|
$animate.enabled(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
browser.addMockModule('disableNgAnimate', disableNgAnimate);
|
||||||
|
},
|
||||||
|
|
||||||
|
jasmineNodeOpts: {
|
||||||
|
defaultTimeoutInterval: 30000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
function catch_errors() {
|
|
||||||
echo "ERROR. That's life."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
trap catch_errors ERR
|
|
||||||
|
|
||||||
TMP_FILE='changelog.tmp'
|
|
||||||
CHANGELOG_FILE='CHANGELOG.md'
|
|
||||||
|
|
||||||
echo "Getting current version..."
|
|
||||||
VERSION=`./version.js --current`
|
|
||||||
|
|
||||||
echo "Generating changelog..."
|
|
||||||
./changelog.js $VERSION $TMP_FILE
|
|
||||||
|
|
||||||
cat $CHANGELOG_FILE >> $TMP_FILE
|
|
||||||
mv -f $TMP_FILE $CHANGELOG_FILE
|
|
||||||
|
|
||||||
|
|
||||||
echo "Updating version..."
|
|
||||||
./version.js --remove-snapshot
|
|
||||||
|
|
||||||
echo "CONFIRM TO COMMIT"
|
|
||||||
read WHATEVER
|
|
||||||
|
|
||||||
|
|
||||||
echo "Creating commit..."
|
|
||||||
git commit version.yaml CHANGELOG.md -m "chore(relase): cutting the v$VERSION release"
|
|
||||||
|
|
||||||
echo "Creating tag..."
|
|
||||||
git tag "v$VERSION"
|
|
||||||
54
scripts/angular-phonecat/publish.sh
Executable file
54
scripts/angular-phonecat/publish.sh
Executable file
|
|
@ -0,0 +1,54 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script for updating angular-phonecat repo from current local build.
|
||||||
|
|
||||||
|
echo "#################################"
|
||||||
|
echo "## Update angular-phonecat ###"
|
||||||
|
echo "#################################"
|
||||||
|
|
||||||
|
ARG_DEFS=(
|
||||||
|
"--action=(prepare|publish)"
|
||||||
|
"[--no-test=(true|false)]"
|
||||||
|
)
|
||||||
|
|
||||||
|
function init {
|
||||||
|
TMP_DIR=$(resolveDir ../../tmp)
|
||||||
|
BUILD_DIR=$(resolveDir ../../build)
|
||||||
|
REPO_DIR=$TMP_DIR/angular-phonecat
|
||||||
|
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepare {
|
||||||
|
echo "-- Cloning angular-phonecat"
|
||||||
|
git clone git@github.com:angular/angular-phonecat.git $REPO_DIR
|
||||||
|
|
||||||
|
#
|
||||||
|
# copy the files from the build
|
||||||
|
#
|
||||||
|
echo "-- Updating angular-phonecat"
|
||||||
|
cd $REPO_DIR
|
||||||
|
./scripts/private/update-angular.sh $BUILD_DIR
|
||||||
|
|
||||||
|
# Test
|
||||||
|
if [[ $NO_TEST != "true" ]]; then
|
||||||
|
./scripts/private/test-all.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate demo
|
||||||
|
./scripts/private/snapshot-web.sh
|
||||||
|
git checkout gh-pages
|
||||||
|
git pull
|
||||||
|
rm -r step*
|
||||||
|
mv angular-phonecat-snapshots-web/step* .
|
||||||
|
git add step*
|
||||||
|
git commit -am "Angular $NEW_VERSION release"
|
||||||
|
}
|
||||||
|
|
||||||
|
function publish {
|
||||||
|
cd $REPO_DIR
|
||||||
|
echo "-- Pushing angular-phonecat"
|
||||||
|
git push origin master -f --tags
|
||||||
|
git push origin gh-pages -f
|
||||||
|
}
|
||||||
|
|
||||||
|
source $(dirname $0)/../utils.inc
|
||||||
44
scripts/angular-seed/publish.sh
Executable file
44
scripts/angular-seed/publish.sh
Executable file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script for updating angular-seed repo from current local build.
|
||||||
|
|
||||||
|
echo "#################################"
|
||||||
|
echo "## Update angular-seed ###"
|
||||||
|
echo "#################################"
|
||||||
|
|
||||||
|
ARG_DEFS=(
|
||||||
|
"--action=(prepare|publish)"
|
||||||
|
"[--no-test=(true|false)]"
|
||||||
|
)
|
||||||
|
|
||||||
|
function init {
|
||||||
|
TMP_DIR=$(resolveDir ../../tmp)
|
||||||
|
BUILD_DIR=$(resolveDir ../../build)
|
||||||
|
REPO_DIR=$TMP_DIR/angular-seed
|
||||||
|
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepare {
|
||||||
|
echo "-- Cloning angular-seed"
|
||||||
|
git clone git@github.com:angular/angular-seed.git $REPO_DIR
|
||||||
|
|
||||||
|
#
|
||||||
|
# copy the files from the build
|
||||||
|
#
|
||||||
|
echo "-- Updating angular-seed"
|
||||||
|
cd $REPO_DIR
|
||||||
|
./scripts/update-angular.sh $BUILD_DIR
|
||||||
|
|
||||||
|
# Test
|
||||||
|
if [[ $NO_TEST != "true" ]]; then
|
||||||
|
./scripts/test-all.sh
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function publish {
|
||||||
|
cd $REPO_DIR
|
||||||
|
echo "-- Pushing angular-seed"
|
||||||
|
git push origin master
|
||||||
|
}
|
||||||
|
|
||||||
|
source $(dirname $0)/../utils.inc
|
||||||
30
scripts/angular.js/publish-cdn-version.sh
Executable file
30
scripts/angular.js/publish-cdn-version.sh
Executable file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Script for updating cdnVersion of angular.js
|
||||||
|
|
||||||
|
echo "###################################"
|
||||||
|
echo "## Update angular.js cdnVersion ###"
|
||||||
|
echo "###################################"
|
||||||
|
|
||||||
|
ARG_DEFS=(
|
||||||
|
"--cdn-version=(.*)"
|
||||||
|
"--action=(prepare|publish)"
|
||||||
|
)
|
||||||
|
|
||||||
|
function init {
|
||||||
|
cd ../..
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepare {
|
||||||
|
replaceJsonProp "package.json" "cdnVersion" "(.*)" "$CDN_VERSION"
|
||||||
|
git add package.json
|
||||||
|
git commit -m "chore(release): update cdn version"
|
||||||
|
}
|
||||||
|
|
||||||
|
function publish {
|
||||||
|
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
# push the commits to github
|
||||||
|
git push origin $BRANCH
|
||||||
|
}
|
||||||
|
|
||||||
|
source $(dirname $0)/../utils.inc
|
||||||
42
scripts/angular.js/tag-release.sh
Executable file
42
scripts/angular.js/tag-release.sh
Executable file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Tags a release
|
||||||
|
# so that travis can do the actual release.
|
||||||
|
|
||||||
|
echo "#################################"
|
||||||
|
echo "## Tag angular.js for a release #"
|
||||||
|
echo "#################################"
|
||||||
|
|
||||||
|
ARG_DEFS=(
|
||||||
|
"--action=(prepare|publish)"
|
||||||
|
"--commit-sha=(.*)"
|
||||||
|
# the version number of the release.
|
||||||
|
# e.g. 1.2.12 or 1.2.12-rc.1
|
||||||
|
"--version-number=([0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?)"
|
||||||
|
"--version-name=(.+)"
|
||||||
|
)
|
||||||
|
|
||||||
|
function checkVersionNumber() {
|
||||||
|
BRANCH_PATTERN=$(readJsonProp "package.json" "branchVersion")
|
||||||
|
if [[ $VERSION_NUMBER != $BRANCH_PATTERN ]]; then
|
||||||
|
echo "version-number needs to match $BRANCH_PATTERN on this branch"
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function init {
|
||||||
|
cd ../..
|
||||||
|
checkVersionNumber
|
||||||
|
TAG_NAME="v$VERSION_NUMBER"
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepare() {
|
||||||
|
git tag "$TAG_NAME" -m "chore(release): $TAG_NAME codename($VERSION_NAME)" "$COMMIT_SHA"
|
||||||
|
}
|
||||||
|
|
||||||
|
function publish() {
|
||||||
|
# push the tag to github
|
||||||
|
git push origin $TAG_NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
source $(dirname $0)/../utils.inc
|
||||||
47
scripts/angularjs.org/publish.sh
Executable file
47
scripts/angularjs.org/publish.sh
Executable file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# Script for updating angularjs.org repo
|
||||||
|
|
||||||
|
echo "#################################"
|
||||||
|
echo "##### Update angularjs.org ######"
|
||||||
|
echo "#################################"
|
||||||
|
|
||||||
|
ARG_DEFS=(
|
||||||
|
"--action=(prepare|publish)"
|
||||||
|
"--cdn-version=(.*)"
|
||||||
|
)
|
||||||
|
|
||||||
|
function init {
|
||||||
|
TMP_DIR=$(resolveDir ../../tmp)
|
||||||
|
REPO_DIR=$TMP_DIR/angularjs.org
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepare {
|
||||||
|
echo "-- Cloning angularjs.org"
|
||||||
|
git clone git@github.com:angular/angularjs.org.git $REPO_DIR
|
||||||
|
|
||||||
|
#
|
||||||
|
# update files
|
||||||
|
#
|
||||||
|
echo "-- Updating angularjs.org"
|
||||||
|
cd $REPO_DIR
|
||||||
|
VERSION_REGEX="[a-z0-9\-\.\+]+"
|
||||||
|
|
||||||
|
replaceInFile "index.html" "(ajax\/libs\/angularjs\/)$VERSION_REGEX" "\1$CDN_VERSION"
|
||||||
|
replaceInFile "index.html" "(<span class=\"version\">[^<]*<span>)$VERSION_REGEX" "\1$CDN_VERSION"
|
||||||
|
replaceInFile "index.html" "(code.angularjs.org\/)$VERSION_REGEX" "\1$CDN_VERSION"
|
||||||
|
|
||||||
|
replaceInFile "js/homepage.js" "($scope.CURRENT_STABLE_VERSION[ ]*=[ ]*')$VERSION_REGEX" "\1$CDN_VERSION"
|
||||||
|
replaceInFile "js/homepage.js" "($scope.CURRENT_UNSTABLE_VERSION[ ]*=[ ]*')$VERSION_REGEX" "\1$CDN_VERSION"
|
||||||
|
|
||||||
|
git add index.html
|
||||||
|
git add js/homepage.js
|
||||||
|
git commit -m "update(version): update angular version to $CDN_VERSION"
|
||||||
|
}
|
||||||
|
|
||||||
|
function publish {
|
||||||
|
cd $REPO_DIR
|
||||||
|
echo "-- Pushing angularjs.org"
|
||||||
|
git push origin master
|
||||||
|
}
|
||||||
|
|
||||||
|
source $(dirname $0)/../utils.inc
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
# Angular Bower Script
|
|
||||||
|
|
||||||
Script for updating the Angular bower repos from a code.angularjs.org package
|
|
||||||
|
|
||||||
Requires `node` (for parsing `bower.json`) and `wget` (for fetching the `angular.zip` from `code.angularjs.org`)
|
|
||||||
|
|
||||||
|
|
||||||
## Instructions
|
|
||||||
|
|
||||||
You need to run `./init.sh` the first time you use this script to clone all the repos.
|
|
||||||
|
|
||||||
For subsequent updates:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
./publish.sh NEW_VERSION
|
|
||||||
```
|
|
||||||
|
|
||||||
Where `NEW_VERSION` is a version number like `1.2.3`.
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
|
||||||
MIT
|
|
||||||
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
#
|
|
||||||
# init all of the bower repos
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e # fail if any command fails
|
|
||||||
|
|
||||||
REPOS=(
|
|
||||||
angular \
|
|
||||||
angular-animate \
|
|
||||||
angular-cookies \
|
|
||||||
angular-i18n \
|
|
||||||
angular-loader \
|
|
||||||
angular-mocks \
|
|
||||||
angular-route \
|
|
||||||
angular-resource \
|
|
||||||
angular-sanitize \
|
|
||||||
angular-scenario \
|
|
||||||
angular-touch \
|
|
||||||
)
|
|
||||||
|
|
||||||
cd `dirname $0`
|
|
||||||
|
|
||||||
for repo in "${REPOS[@]}"
|
|
||||||
do
|
|
||||||
git clone git@github.com:angular/bower-$repo.git
|
|
||||||
done
|
|
||||||
|
|
@ -1,87 +1,101 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
#
|
# Script for updating the Angular bower repos from current local build.
|
||||||
# update all the things
|
|
||||||
#
|
|
||||||
|
|
||||||
set -e # fail if any command fails
|
echo "#################################"
|
||||||
|
echo "#### Update bower ###############"
|
||||||
|
echo "#################################"
|
||||||
|
|
||||||
cd `dirname $0`
|
ARG_DEFS=(
|
||||||
|
"--action=(prepare|publish)"
|
||||||
NEW_VERSION=$1
|
|
||||||
|
|
||||||
ZIP_FILE=angular-$NEW_VERSION.zip
|
|
||||||
ZIP_FILE_URL=http://code.angularjs.org/$NEW_VERSION/angular-$NEW_VERSION.zip
|
|
||||||
ZIP_DIR=angular-$NEW_VERSION
|
|
||||||
|
|
||||||
REPOS=(
|
|
||||||
angular \
|
|
||||||
angular-animate \
|
|
||||||
angular-cookies \
|
|
||||||
angular-i18n \
|
|
||||||
angular-loader \
|
|
||||||
angular-mocks \
|
|
||||||
angular-route \
|
|
||||||
angular-resource \
|
|
||||||
angular-sanitize \
|
|
||||||
angular-scenario \
|
|
||||||
angular-touch \
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
function init {
|
||||||
#
|
TMP_DIR=$(resolveDir ../../tmp)
|
||||||
# download and unzip the file
|
BUILD_DIR=$(resolveDir ../../build)
|
||||||
#
|
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||||
|
REPOS=(
|
||||||
#wget $ZIP_FILE_URL
|
angular
|
||||||
unzip $ZIP_FILE
|
angular-animate
|
||||||
|
angular-cookies
|
||||||
|
angular-i18n
|
||||||
|
angular-loader
|
||||||
|
angular-mocks
|
||||||
|
angular-route
|
||||||
|
angular-resource
|
||||||
|
angular-sanitize
|
||||||
|
angular-scenario
|
||||||
|
angular-touch
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#
|
function prepare {
|
||||||
# move the files from the zip
|
#
|
||||||
#
|
# clone repos
|
||||||
|
#
|
||||||
for repo in "${REPOS[@]}"
|
for repo in "${REPOS[@]}"
|
||||||
do
|
do
|
||||||
if [ -f $ZIP_DIR/$repo.js ] # ignore i18l
|
echo "-- Cloning bower-$repo"
|
||||||
then
|
git clone git@github.com:angular/bower-$repo.git $TMP_DIR/bower-$repo
|
||||||
cd bower-$repo
|
done
|
||||||
git checkout master
|
|
||||||
git reset --hard HEAD
|
|
||||||
cd ..
|
|
||||||
mv $ZIP_DIR/$repo.* bower-$repo/
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# move i18n files
|
|
||||||
mv $ZIP_DIR/i18n/*.js bower-angular-i18n/
|
|
||||||
|
|
||||||
# move csp.css
|
|
||||||
mv $ZIP_DIR/angular-csp.css bower-angular
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# get the old version number
|
# move the files from the build
|
||||||
#
|
#
|
||||||
|
|
||||||
OLD_VERSION=$(node -e "console.log(require('./bower-angular/bower').version)" | sed -e 's/\r//g')
|
for repo in "${REPOS[@]}"
|
||||||
echo $OLD_VERSION
|
do
|
||||||
echo $NEW_VERSION
|
if [ -f $BUILD_DIR/$repo.js ] # ignore i18l
|
||||||
|
then
|
||||||
|
echo "-- Updating files in bower-$repo"
|
||||||
|
cd $TMP_DIR/bower-$repo
|
||||||
|
git reset --hard HEAD
|
||||||
|
git checkout master
|
||||||
|
git fetch --all
|
||||||
|
git reset --hard origin/master
|
||||||
|
cd $SCRIPT_DIR
|
||||||
|
cp $BUILD_DIR/$repo.* $TMP_DIR/bower-$repo/
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
#
|
# move i18n files
|
||||||
# update bower.json
|
cp $BUILD_DIR/i18n/*.js $TMP_DIR/bower-angular-i18n/
|
||||||
# tag each repo
|
|
||||||
#
|
|
||||||
|
|
||||||
for repo in "${REPOS[@]}"
|
# move csp.css
|
||||||
do
|
cp $BUILD_DIR/angular-csp.css $TMP_DIR/bower-angular
|
||||||
cd bower-$repo
|
|
||||||
pwd
|
|
||||||
sed -i '' -e "s/$OLD_VERSION/$NEW_VERSION/g" bower.json
|
#
|
||||||
git add -A
|
# update bower.json
|
||||||
git commit -m "v$NEW_VERSION"
|
# tag each repo
|
||||||
git tag v$NEW_VERSION
|
#
|
||||||
git push origin master
|
for repo in "${REPOS[@]}"
|
||||||
git push origin v$NEW_VERSION
|
do
|
||||||
cd ..
|
echo "-- Updating version in bower-$repo to $NEW_VERSION"
|
||||||
done
|
cd $TMP_DIR/bower-$repo
|
||||||
|
replaceJsonProp "bower.json" "version" ".*" "$NEW_VERSION"
|
||||||
|
replaceJsonProp "bower.json" "angular.*" ".*" "$NEW_VERSION"
|
||||||
|
|
||||||
|
git add -A
|
||||||
|
|
||||||
|
echo "-- Committing and tagging bower-$repo"
|
||||||
|
git commit -m "v$NEW_VERSION"
|
||||||
|
git tag v$NEW_VERSION
|
||||||
|
cd $SCRIPT_DIR
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function publish {
|
||||||
|
for repo in "${REPOS[@]}"
|
||||||
|
do
|
||||||
|
echo "-- Pushing bower-$repo"
|
||||||
|
cd $TMP_DIR/bower-$repo
|
||||||
|
git push origin master
|
||||||
|
git push origin v$NEW_VERSION
|
||||||
|
cd $SCRIPT_DIR
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
source $(dirname $0)/../utils.inc
|
||||||
|
|
|
||||||
73
scripts/code.angularjs.org/publish.sh
Executable file
73
scripts/code.angularjs.org/publish.sh
Executable file
|
|
@ -0,0 +1,73 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script for updating code.angularjs.org repo from current local build.
|
||||||
|
|
||||||
|
echo "#################################"
|
||||||
|
echo "## Update code.angular.js.org ###"
|
||||||
|
echo "#################################"
|
||||||
|
|
||||||
|
ARG_DEFS=(
|
||||||
|
"--action=(prepare|publish)"
|
||||||
|
)
|
||||||
|
|
||||||
|
function init {
|
||||||
|
TMP_DIR=$(resolveDir ../../tmp)
|
||||||
|
BUILD_DIR=$(resolveDir ../../build)
|
||||||
|
REPO_DIR=$TMP_DIR/code.angularjs.org
|
||||||
|
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||||
|
if [[ "$NEW_VERSION" =~ sha ]]; then
|
||||||
|
IS_SNAPSHOT_BUILD=true
|
||||||
|
else
|
||||||
|
IS_SNAPSHOT_BUILD=
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepare {
|
||||||
|
if [[ $IS_SNAPSHOT_BUILD ]]; then
|
||||||
|
# nothing to prepare for snapshot builds as
|
||||||
|
# code.angularjs.org will fetch the current snapshot from
|
||||||
|
# the build server during publish
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "-- Cloning code.angularjs.org"
|
||||||
|
git clone git@github.com:angular/code.angularjs.org.git $REPO_DIR
|
||||||
|
|
||||||
|
#
|
||||||
|
# copy the files from the build
|
||||||
|
#
|
||||||
|
echo "-- Updating code.angularjs.org"
|
||||||
|
mkdir $REPO_DIR/$NEW_VERSION
|
||||||
|
cd $REPO_DIR
|
||||||
|
git reset --hard HEAD
|
||||||
|
git checkout master
|
||||||
|
git fetch --all
|
||||||
|
git reset --hard origin/master
|
||||||
|
cd $SCRIPT_DIR
|
||||||
|
cp -r $BUILD_DIR/* $REPO_DIR/$NEW_VERSION/
|
||||||
|
|
||||||
|
#
|
||||||
|
# commit
|
||||||
|
#
|
||||||
|
echo "-- Committing code.angularjs.org"
|
||||||
|
cd $REPO_DIR
|
||||||
|
git add -A
|
||||||
|
git commit -m "v$NEW_VERSION"
|
||||||
|
}
|
||||||
|
|
||||||
|
function publish {
|
||||||
|
if [[ $IS_SNAPSHOT_BUILD ]]; then
|
||||||
|
echo "-- Updating snapshot version"
|
||||||
|
curl -G --data-urlencode "ver=$NEW_VERSION" http://code.angularjs.org/fetchLatestSnapshot.php
|
||||||
|
exit 0;
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $REPO_DIR
|
||||||
|
echo "-- Pushing code.angularjs.org"
|
||||||
|
git push origin master
|
||||||
|
|
||||||
|
echo "-- Refreshing code.angularjs.org"
|
||||||
|
curl http://code.angularjs.org/gitFetchSite.php
|
||||||
|
}
|
||||||
|
|
||||||
|
source $(dirname $0)/../utils.inc
|
||||||
47
scripts/jenkins/master.sh
Executable file
47
scripts/jenkins/master.sh
Executable file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "#################################"
|
||||||
|
echo "#### Update master ##############"
|
||||||
|
echo "#################################"
|
||||||
|
|
||||||
|
ARG_DEFS=(
|
||||||
|
"[--no-test=(true|false)]"
|
||||||
|
)
|
||||||
|
|
||||||
|
function init {
|
||||||
|
if [[ ! $VERBOSE ]]; then
|
||||||
|
VERBOSE=false
|
||||||
|
fi
|
||||||
|
VERBOSE_ARG="--verbose=$VERBOSE"
|
||||||
|
}
|
||||||
|
|
||||||
|
function build {
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
if [[ $NO_TEST == "true" ]]; then
|
||||||
|
npm install --color false
|
||||||
|
grunt ci-checks package --no-color
|
||||||
|
else
|
||||||
|
./jenkins_build.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $SCRIPT_DIR
|
||||||
|
}
|
||||||
|
|
||||||
|
function phase {
|
||||||
|
ACTION_ARG="--action=$1"
|
||||||
|
../code.angularjs.org/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||||
|
../bower/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||||
|
}
|
||||||
|
|
||||||
|
function run {
|
||||||
|
build
|
||||||
|
|
||||||
|
# First prepare all scripts (build, test, commit, tag, ...),
|
||||||
|
# so we are sure everything is all right
|
||||||
|
phase prepare
|
||||||
|
# only then publish to github
|
||||||
|
phase publish
|
||||||
|
}
|
||||||
|
|
||||||
|
source $(dirname $0)/../utils.inc
|
||||||
41
scripts/jenkins/release-after-cdn.sh
Executable file
41
scripts/jenkins/release-after-cdn.sh
Executable file
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
ARG_DEFS=(
|
||||||
|
# require the git dryrun flag so the script can't be run without
|
||||||
|
# thinking about this!
|
||||||
|
"--git-push-dryrun=(true|false)"
|
||||||
|
"--cdn-version=(.*)"
|
||||||
|
)
|
||||||
|
|
||||||
|
function init {
|
||||||
|
NG_ARGS=("$@")
|
||||||
|
if [[ ! $VERBOSE ]]; then
|
||||||
|
VERBOSE=false
|
||||||
|
fi
|
||||||
|
VERBOSE_ARG="--verbose=$VERBOSE"
|
||||||
|
}
|
||||||
|
|
||||||
|
function phase {
|
||||||
|
ACTION_ARG="--action=$1"
|
||||||
|
CDN_VERSION_ARG="--cdn-version=$CDN_VERSION"
|
||||||
|
./scripts/angular.js/publish-cdn-version.sh $ACTION_ARG $CDN_VERSION_ARG $VERBOSE_ARG
|
||||||
|
./scripts/angularjs.org/publish.sh $ACTION_ARG $CDN_VERSION_ARG $VERBOSE_ARG
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCdn {
|
||||||
|
STATUS=$(curl http://ajax.googleapis.com/ajax/libs/angularjs/$CDN_VERSION/angular.min.js --write-out '%{http_code}' -o /dev/null -silent)
|
||||||
|
if [[ $STATUS != 200 ]]; then
|
||||||
|
echo "Could not find release $CDN_VERSION on CDN"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function run {
|
||||||
|
cd ../..
|
||||||
|
checkCdn
|
||||||
|
|
||||||
|
phase prepare
|
||||||
|
phase publish
|
||||||
|
}
|
||||||
|
|
||||||
|
source $(dirname $0)/../utils.inc
|
||||||
71
scripts/jenkins/release.sh
Executable file
71
scripts/jenkins/release.sh
Executable file
|
|
@ -0,0 +1,71 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# tags the current commit as a release and publishes all artifacts to
|
||||||
|
# the different repositories.
|
||||||
|
# Note: This will also works if the commit is in the past!
|
||||||
|
|
||||||
|
echo "#################################"
|
||||||
|
echo "#### cut release ############"
|
||||||
|
echo "#################################"
|
||||||
|
|
||||||
|
ARG_DEFS=(
|
||||||
|
# require the git dryrun flag so the script can't be run without
|
||||||
|
# thinking about this!
|
||||||
|
"--git-push-dryrun=(true|false)"
|
||||||
|
# The sha to release. Needs to be the same as HEAD.
|
||||||
|
# given as parameter to double check.
|
||||||
|
"--commit-sha=(.*)"
|
||||||
|
# the version number of the release.
|
||||||
|
# e.g. 1.2.12 or 1.2.12-rc.1
|
||||||
|
"--version-number=([0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?)"
|
||||||
|
# the codename of the release
|
||||||
|
"--version-name=(.+)"
|
||||||
|
)
|
||||||
|
|
||||||
|
function init {
|
||||||
|
if [[ $(git rev-parse --short HEAD) != $COMMIT_SHA ]]; then
|
||||||
|
echo "HEAD is not at $COMMIT_SHA"
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! $VERBOSE ]]; then
|
||||||
|
VERBOSE=false
|
||||||
|
fi
|
||||||
|
VERBOSE_ARG="--verbose=$VERBOSE"
|
||||||
|
}
|
||||||
|
|
||||||
|
function build {
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
npm install --color false
|
||||||
|
grunt ci-checks package --no-color
|
||||||
|
|
||||||
|
cd $SCRIPT_DIR
|
||||||
|
}
|
||||||
|
|
||||||
|
function phase {
|
||||||
|
ACTION_ARG="--action=$1"
|
||||||
|
../angular.js/tag-release.sh $ACTION_ARG $VERBOSE_ARG\
|
||||||
|
--version-number=$VERSION_NUMBER --version-name=$VERSION_NAME\
|
||||||
|
--commit-sha=$COMMIT_SHA
|
||||||
|
|
||||||
|
if [[ $1 == "prepare" ]]; then
|
||||||
|
# The build requires the tag to be set already!
|
||||||
|
build
|
||||||
|
fi
|
||||||
|
|
||||||
|
../code.angularjs.org/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||||
|
../bower/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||||
|
../angular-seed/publish.sh $ACTION_ARG $VERBOSE_ARG --no-test=true
|
||||||
|
../angular-phonecat/publish.sh $ACTION_ARG $VERBOSE_ARG --no-test=true
|
||||||
|
}
|
||||||
|
|
||||||
|
function run {
|
||||||
|
# First prepare all scripts (build, test, commit, tag, ...),
|
||||||
|
# so we are sure everything is all right
|
||||||
|
phase prepare
|
||||||
|
# only then publish to github
|
||||||
|
phase publish
|
||||||
|
}
|
||||||
|
|
||||||
|
source $(dirname $0)/../utils.inc
|
||||||
19
scripts/travis/build.sh
Executable file
19
scripts/travis/build.sh
Executable file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||||
|
|
||||||
|
if [ $JOB = "unit" ]; then
|
||||||
|
grunt ci-checks
|
||||||
|
grunt test:docgen
|
||||||
|
grunt test:promises-aplus
|
||||||
|
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_8,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
|
||||||
|
elif [ $JOB = "e2e" ]; then
|
||||||
|
grunt test:protractor --sauceUser $SAUCE_USERNAME \
|
||||||
|
--sauceKey $SAUCE_ACCESS_KEY \
|
||||||
|
--capabilities.tunnel-identifier=$TRAVIS_JOB_NUMBER \
|
||||||
|
--capabilities.build=$TRAVIS_BUILD_NUMBER
|
||||||
|
else
|
||||||
|
echo "Unknown job type. Please set JOB=unit or JOB=e2e."
|
||||||
|
fi
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue