mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 15:40:22 +00:00
Compare commits
229 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 |
151 changed files with 3961 additions and 4225 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
|
||||
}
|
||||
}
|
||||
|
|
@ -9,8 +9,6 @@ env:
|
|||
global:
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- BROWSER_STACK_USERNAME=VojtaJina
|
||||
- BROWSER_STACK_ACCESS_KEY=HAfHZaypxAc3PEUrUU9a
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
- BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready
|
||||
|
||||
|
|
|
|||
214
CHANGELOG.md
214
CHANGELOG.md
|
|
@ -1,3 +1,214 @@
|
|||
<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)
|
||||
|
||||
|
|
@ -4384,3 +4595,6 @@ with the `$route` service
|
|||
[module]: http://docs-next.angularjs.org/api/angular.mock.module
|
||||
[guide2.di]: http://docs-next.angularjs.org/guide/dev_guide.di
|
||||
[jqLite2]: http://docs.angularjs.org/#!/api/angular.element
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ Before you submit your pull request consider the following guidelines:
|
|||
```
|
||||
|
||||
* Create your patch, including appropriate test cases.
|
||||
* Follow our Coding Rules
|
||||
* Follow our [Coding Rules](#coding-rules)
|
||||
* Commit your changes and create a descriptive commit message (the
|
||||
commit message is used to generate release notes, please check out our
|
||||
[commit message conventions](#commit-message-format) and our commit message presubmit hook
|
||||
|
|
@ -258,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
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
|
|
|||
36
Gruntfile.js
36
Gruntfile.js
|
|
@ -4,17 +4,8 @@ var path = require('path');
|
|||
|
||||
module.exports = function(grunt) {
|
||||
//grunt plugins
|
||||
grunt.loadNpmTasks('grunt-bump');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-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');
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.loadTasks('lib/grunt');
|
||||
|
||||
var NG_VERSION = util.getVersion();
|
||||
|
|
@ -87,9 +78,7 @@ module.exports = function(grunt) {
|
|||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js',
|
||||
docs: 'karma-docs.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'
|
||||
modules: 'karma-modules.conf.js'
|
||||
},
|
||||
|
||||
|
||||
|
|
@ -101,6 +90,11 @@ module.exports = function(grunt) {
|
|||
},
|
||||
|
||||
|
||||
runprotractor: {
|
||||
normal: 'protractor-conf.js'
|
||||
},
|
||||
|
||||
|
||||
clean: {
|
||||
build: ['build'],
|
||||
tmp: ['tmp']
|
||||
|
|
@ -142,6 +136,13 @@ module.exports = function(grunt) {
|
|||
}
|
||||
},
|
||||
|
||||
jscs: {
|
||||
src: ['src/**/*.js', 'test/**/*.js'],
|
||||
options: {
|
||||
config: ".jscs.json"
|
||||
}
|
||||
},
|
||||
|
||||
build: {
|
||||
scenario: {
|
||||
dest: 'build/angular-scenario.js',
|
||||
|
|
@ -283,13 +284,14 @@ module.exports = function(grunt) {
|
|||
|
||||
|
||||
//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: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: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: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:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
|
||||
|
||||
|
|
@ -297,6 +299,6 @@ module.exports = function(grunt) {
|
|||
grunt.registerTask('webserver', ['connect:devserver']);
|
||||
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('package-without-bower', ['clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
|
|
|
|||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
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
|
||||
binding. To help you structure your application better and make it easy to test, AngularJS teaches
|
||||
the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with
|
||||
server-side communication, taming async callbacks with promises and deferreds; and make client-side
|
||||
server-side communication, taming async callbacks with promises and deferreds; and makes client-side
|
||||
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
|
||||
it makes development fun!
|
||||
|
||||
|
|
@ -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
|
||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ The following is done automatically and should not be done manually:
|
|||
* 1.2.x - everything else
|
||||
1. Label "GH: *" (to be automated via Mary Poppins)
|
||||
* PR - issue is a PR
|
||||
* issue - otherwise
|
||||
* issue - otherwise
|
||||
1. Bugs:
|
||||
* Label "Type: Bug"
|
||||
* Label "Type: Regression" - if the bug is a regression
|
||||
|
|
@ -43,7 +43,7 @@ The following is done automatically and should not be done manually:
|
|||
* 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.
|
||||
* 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
|
||||
|
|
@ -52,10 +52,12 @@ The following is done automatically and should not be done manually:
|
|||
* 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 welcome" for "GH: issue"
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
1975
closure/angular.js
vendored
1975
closure/angular.js
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -13,8 +13,7 @@ describe("docsSearch", function() {
|
|||
results[0] = { section: 'tutorial', shortName: 'item one', keywords: 'item, one, 1' };
|
||||
results[1] = { section: 'tutorial', shortName: 'item man', keywords: 'item, man' };
|
||||
results[2] = { section: 'api', shortName: 'item other', keywords: 'item, other' };
|
||||
results[3] = { section: 'cookbook', shortName: 'item cookbook', keywords: 'item, other' };
|
||||
results[4] = { section: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
|
||||
results[3] = { section: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
|
||||
|
||||
$provide.value('NG_PAGES', results);
|
||||
$provide.factory('lunrSearch', function() {
|
||||
|
|
@ -41,19 +40,14 @@ describe("docsSearch", function() {
|
|||
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) {
|
||||
var items = docsSearch();
|
||||
expect(items['tutorial'].length).toBe(3);
|
||||
expect(items['tutorial'].length).toBe(2);
|
||||
expect(items['api'].length).toBe(2);
|
||||
}));
|
||||
|
||||
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');
|
||||
}));
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
# AngularJS API Docs
|
||||
Welcome to the AngularJS API docs page. These pages contain the AngularJS reference materials for version <strong ng-bind="version"></strong>.
|
||||
|
||||
The documentation is organized into **modules** which contain various components of an AngularJS application.
|
||||
These components are directives, services, filters, providers, types, global APIs and testing mocks.
|
||||
The documentation is organized into **{@link guide/module modules}** which contain various components of an AngularJS application.
|
||||
These components are {@link guide/directive directives}, {@link guide/dev_guide.services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates types}, global APIs and testing mocks.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Angular Namespaces `$` and `$$`**
|
||||
|
|
@ -63,7 +63,7 @@ This module is provided by default and contains the core components of AngularJS
|
|||
</td>
|
||||
<td>
|
||||
<p>
|
||||
The core filters available in the ng module are used to transform template data before it is renders within directives and expressions.
|
||||
The core filters available in the ng module are used to transform template data before it is rendered within directives and expressions.
|
||||
</p>
|
||||
<p>
|
||||
Some examples include:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
```
|
||||
<input ng-mode="username">
|
||||
<input ng-model="username">
|
||||
<div onclick="{{username}}">click me</div>
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@
|
|||
This error occurs in browsers that do not support XmlHttpRequest. AngularJS
|
||||
supports Safari, Chrome, Firefox, Opera, IE8 and higher, and mobile browsers
|
||||
(Android, Chrome Mobile, iOS Safari). To avoid this error, use an officially
|
||||
supported browser.
|
||||
supported browser.
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ For example the issue can be triggered by this *invalid* code:
|
|||
<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:
|
||||
|
||||
|
|
|
|||
|
|
@ -21,19 +21,19 @@ Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
|
|||
<label>
|
||||
<input type="checkbox" ng-model="checked" style="float:left; margin-right:10px;"> Is Visible...
|
||||
</label>
|
||||
<div class="check-element animate-show-hide" ng-show="checked" style="clear:both;">
|
||||
<div class="check-element sample-show-hide" ng-show="checked" style="clear:both;">
|
||||
Visible...
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.animate-show-hide {
|
||||
.sample-show-hide {
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
background:white;
|
||||
}
|
||||
|
||||
.animate-show-hide.ng-hide-add, .animate-show-hide.ng-hide-remove {
|
||||
.sample-show-hide.ng-hide-add, .sample-show-hide.ng-hide-remove {
|
||||
-webkit-transition:all linear 0.5s;
|
||||
-moz-transition:all linear 0.5s;
|
||||
-o-transition:all linear 0.5s;
|
||||
|
|
@ -41,13 +41,13 @@ Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
|
|||
display:block!important;
|
||||
}
|
||||
|
||||
.animate-show-hide.ng-hide-add.ng-hide-add-active,
|
||||
.animate-show-hide.ng-hide-remove {
|
||||
.sample-show-hide.ng-hide-add.ng-hide-add-active,
|
||||
.sample-show-hide.ng-hide-remove {
|
||||
opacity:0;
|
||||
}
|
||||
|
||||
.animate-show-hide.ng-hide-add,
|
||||
.animate-show-hide.ng-hide-remove.ng-hide-remove-active {
|
||||
.sample-show-hide.ng-hide-add,
|
||||
.sample-show-hide.ng-hide-remove.ng-hide-remove-active {
|
||||
opacity:1;
|
||||
}
|
||||
</file>
|
||||
|
|
@ -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:ngSwitch#usage_animations ngSwitch} | 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) |
|
||||
|
||||
For a full breakdown of the steps involved during each animation event, refer to the {@link api/ngAnimate.$animate API docs}.
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ The following graphic shows how everything works together after we introduced th
|
|||
# View independent business logic: Services
|
||||
|
||||
Right now, the `InvoiceController` contains all logic of our example. When the application grows it
|
||||
is a good practise to move view independent logic from the controller into a so called
|
||||
is a good practice to move view independent logic from the controller into a so called
|
||||
<a name="service">"{@link dev_guide.services service}"</a>, so it can be reused by other parts
|
||||
of the application as well. Later on, we could also change that service to load the exchange rates
|
||||
from the web, e.g. by calling the Yahoo Finance API, without changing the controller.
|
||||
|
|
@ -272,8 +272,8 @@ including the configuration of all modules that this module depends on.
|
|||
In the example above:
|
||||
The template contains the directive `ng-app="invoice2"`. This tells Angular
|
||||
to use the `invoice` module as the main module for the application.
|
||||
The code snippet `angular.module('invoice', ['finance'])` specifies that the `invoice` module depends on the
|
||||
`finance` module. By this, Angular uses the `InvoiceController` as well as the `currencyConverter` service.
|
||||
The code snippet `angular.module('invoice2', ['finance2'])` specifies that the `invoice2` module depends on the
|
||||
`finance2` module. By this, Angular uses the `InvoiceController` as well as the `currencyConverter` service.
|
||||
|
||||
Now that Angular knows of all the parts of the application, it needs to create them.
|
||||
In the previous section we saw that controllers are created using a factory function.
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ starts with capital letter and ends with "Ctrl" or "Controller".
|
|||
- Assigning a property to `$scope` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method)
|
||||
- The Controller methods and properties are available in the template (for the `<div>` element and
|
||||
and its children).
|
||||
its children).
|
||||
|
||||
## Spicy Arguments Example
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ testable.
|
|||
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
|
||||
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:
|
||||
<pre>
|
||||
|
|
|
|||
|
|
@ -53,18 +53,19 @@ function myController(scope, notifyService) {
|
|||
myController.$inject = ['$scope','notify'];
|
||||
</script>
|
||||
|
||||
<div ng-controller="myController">
|
||||
<div id="simple" ng-controller="myController">
|
||||
<p>Let's try this simple notify service, injected into the controller...</p>
|
||||
<input ng-init="message='test'" ng-model="message" >
|
||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||
<p>(you have to click 3 times to see an alert)</p>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
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>
|
||||
|
||||
## Implicit Dependency Injection
|
||||
|
|
@ -95,7 +96,7 @@ function myController($scope, notify) {
|
|||
};
|
||||
}
|
||||
</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>
|
||||
<input ng-init="message='test'" ng-model="message">
|
||||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ of which depend on other services that are provided by the Angular framework:
|
|||
|
||||
function log() {
|
||||
if (messageQueue.length) {
|
||||
$log('batchLog messages: ', messageQueue);
|
||||
$log.log('batchLog messages: ', messageQueue);
|
||||
messageQueue = [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Angular sets these CSS classes. It is up to your application to provide useful s
|
|||
|
||||
* `ng-binding`
|
||||
- **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`
|
||||
- **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
|
||||
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
|
||||
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:
|
||||
<pre>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ The following also **matches** `ngModel`:
|
|||
|
||||
Angular **normalizes** an element's tag and attribute name to determine which elements match which
|
||||
directives. We typically refer to directives by their case-sensitive
|
||||
{@link http://en.wikipedia.org/wiki/CamelCase camelCase} **normalized** name (e.g. `ngModel`).
|
||||
{@link http://en.wikipedia.org/wiki/CamelCase camelCase} **normalized** name (e.g. `ngModel`).
|
||||
However, since HTML is case-insensitive, we refer to directives in the DOM by lower-case
|
||||
forms, typically using {@link http://en.wikipedia.org/wiki/Letter_case#Computers dash-delimited}
|
||||
attributes on DOM elements (e.g. `ng-model`).
|
||||
|
|
@ -84,10 +84,10 @@ Here are some equivalent examples of elements that match `ngBind`:
|
|||
<span x-ng-bind="name"></span> <br/>
|
||||
</div>
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
<file name="protractorTest.js">
|
||||
it('should show off bindings', function() {
|
||||
expect(element('div[ng-controller="Ctrl1"] span[ng-bind]').text())
|
||||
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)');
|
||||
expect(element(by.css('div[ng-controller="Ctrl1"] span[ng-bind]')).getText())
|
||||
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
|
@ -174,9 +174,9 @@ For example, we could fix the example above by instead writing:
|
|||
|
||||
## Creating Directives
|
||||
|
||||
First let's talk about the API for registering directives. Much like controllers, directives are
|
||||
registered on modules. To register a directive, you use the `module.directive` API.
|
||||
`module.directive` takes the
|
||||
First let's talk about the {@link api/ng.$compileProvider#methods_directive API for registering directives}. Much like
|
||||
controllers, directives are registered on modules. To register a directive, you use the
|
||||
`module.directive` API. `module.directive` takes the
|
||||
{@link guide/directive#creating-custom-directives_matching-directives normalized} directive name
|
||||
followed by a **factory function.** This factory function should return an object with the different
|
||||
options to tell `$compile` how the directive should behave when matched.
|
||||
|
|
@ -280,7 +280,7 @@ using `templateUrl` instead:
|
|||
</example>
|
||||
|
||||
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">
|
||||
**Note:** When you create a directive, it is restricted to attribute only by default. In order to
|
||||
|
|
@ -527,7 +527,8 @@ where:
|
|||
|
||||
* `scope` is an Angular scope object.
|
||||
* `element` is the jqLite-wrapped element that this directive matches.
|
||||
* `attrs` is an object with the normalized attribute names and their corresponding values.
|
||||
* `attrs` is a hash object with key-value pairs of normalized attribute names and their
|
||||
corresponding attribute values.
|
||||
|
||||
In our `link` function, we want to update the displayed time once a second, or whenever a user
|
||||
changes the time formatting string that our directive binds to. We will use the `$interval` service
|
||||
|
|
@ -734,13 +735,13 @@ own behavior to it.
|
|||
We want to run the function we pass by invoking it from the directive's scope, but have it run
|
||||
in the context of the scope where its registered.
|
||||
|
||||
We saw earlier how to use `=prop` in the `scope` option, but in the above example, we're using
|
||||
`&prop` instead. `&` bindings expose a function to an isolated scope allowing the isolated scope
|
||||
We saw earlier how to use `=attr` in the `scope` option, but in the above example, we're using
|
||||
`&attr` instead. `&` bindings expose a function to an isolated scope allowing the isolated scope
|
||||
to invoke it, but maintaining the original scope of the function. So when a user clicks the
|
||||
`x` in the dialog, it runs `Ctrl`'s `close` function.
|
||||
`x` in the dialog, it runs `Ctrl`'s `hideDialog` function.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** use `&prop` in the `scope` option when you want your directive
|
||||
**Best Practice:** use `&attr` in the `scope` option when you want your directive
|
||||
to expose an API for binding to behaviors.
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ JavaScript, use the {@link api/ng.$rootScope.Scope#methods_$eval `$eval()`} meth
|
|||
<doc:source>
|
||||
1+2={{1+2}}
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
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>
|
||||
|
||||
You can try evaluating different expressions here:
|
||||
|
|
@ -73,14 +73,14 @@ You can try evaluating different expressions here:
|
|||
</ul>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should allow user expression testing', function() {
|
||||
element('.expressions :button').click();
|
||||
var li = using('.expressions ul').repeater('li');
|
||||
expect(li.count()).toBe(1);
|
||||
expect(li.row(0)).toEqual(["3*10|currency", "$30.00"]);
|
||||
element(by.css('.expressions button')).click();
|
||||
var lis = element(by.css('.expressions ul')).element.all(by.repeater('expr in exprs'));
|
||||
expect(lis.count()).toBe(1);
|
||||
expect(lis.get(0).getText()).toEqual('[ X ] 3*10|currency => $30.00');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ prevent accidental access to the global state (a common source of subtle bugs).
|
|||
$scope.name = 'World';
|
||||
|
||||
$scope.greet = function() {
|
||||
($window.mockWindow || $window).alert('Hello ' + $scope.name);
|
||||
$window.alert('Hello ' + $scope.name);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -108,21 +108,17 @@ prevent accidental access to the global state (a common source of subtle bugs).
|
|||
<button ng-click="greet()">Greet</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should calculate expression in binding', function() {
|
||||
var alertText;
|
||||
this.addFutureAction('set mock', function($window, $document, done) {
|
||||
$window.mockWindow = {
|
||||
alert: function(text){ alertText = text; }
|
||||
};
|
||||
done();
|
||||
});
|
||||
element(':button:contains(Greet)').click();
|
||||
expect(this.addFuture('alert text', function(done) {
|
||||
done(null, alertText);
|
||||
})).toBe('Hello World');
|
||||
});
|
||||
</doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should calculate expression in binding', function() {
|
||||
element(by.css('[ng-click="greet()"]')).click();
|
||||
|
||||
var alertDialog = browser.switchTo().alert();
|
||||
|
||||
expect(alertDialog.getText()).toEqual('Hello World');
|
||||
|
||||
alertDialog.accept();
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
|
||||
## Forgiving
|
||||
|
|
|
|||
|
|
@ -121,3 +121,6 @@ text upper-case.
|
|||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
## Testing custom filters
|
||||
|
||||
See the {@link http://docs.angularjs.org/tutorial/step_09#test phonecat tutorial} for an example.
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ In addition it provides an {@link api/ng.directive:ngModel.NgModelController API
|
|||
|
||||
<script>
|
||||
function Controller($scope) {
|
||||
$scope.master= {};
|
||||
$scope.master = {};
|
||||
|
||||
$scope.update = function(user) {
|
||||
$scope.master= angular.copy(user);
|
||||
$scope.master = angular.copy(user);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
|
|
@ -116,7 +116,7 @@ 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}.
|
||||
The form instance can optionally be published into the scope using the `name` attribute.
|
||||
|
||||
Similarly, an input control that has the {@link api/ng.directive:ngModel} directive holds an
|
||||
Similarly, an input control that has the {@link api/ng.directive:ngModel ngModel} directive holds an
|
||||
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.
|
||||
|
|
@ -235,7 +235,7 @@ In the following example we create two directives.
|
|||
<script>
|
||||
var app = angular.module('form-example1', []);
|
||||
|
||||
var INTEGER_REGEXP = /^\-?\d*$/;
|
||||
var INTEGER_REGEXP = /^\-?\d+$/;
|
||||
app.directive('integer', function() {
|
||||
return {
|
||||
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
|
||||
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.
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, her
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, their
|
||||
browser will specify the locale as ja, and the balance of '¥1000.00' will be shown instead. This
|
||||
will really upset your client.
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
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
|
||||
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
|
||||
<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
|
||||
`<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>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
|
||||
|
|
|
|||
|
|
@ -87,6 +87,16 @@ grunt package
|
|||
Administrator). This is because `grunt package` creates some symbolic links.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** If you're using Linux, and npm install fails with the message
|
||||
'Please try running this command again as root/Administrator.', you may need to globally install grunt and bower:
|
||||
<ul>
|
||||
<li>sudo npm install -g grunt-cli</li>
|
||||
<li>sudo npm install -g bower</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
The build output can be located under the `build` directory. It consists of the following files and
|
||||
directories:
|
||||
|
||||
|
|
|
|||
|
|
@ -73,12 +73,11 @@ directory.</p></li>
|
|||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> and its plugins if you
|
||||
don't have it already:</p>
|
||||
<pre>
|
||||
npm install -g karma
|
||||
npm install
|
||||
</pre></li>
|
||||
<li><p>You will need an http server running on your system. Mac and Linux machines typically
|
||||
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
|
||||
to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
|
||||
to run a simple bundled http server: <code>node scripts/web-server.js</code>.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
|
@ -107,8 +106,8 @@ directory.</p>
|
|||
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
|
||||
executed from the Windows command line.</li>
|
||||
<li><p>You need an http server running on your system, but if you don't already have one
|
||||
already installed, you can use <code>node</code> to run <code>scripts\web-server.js</code>, a simple
|
||||
bundled http server.</p></li>
|
||||
already installed, you can use <code>node</code> to run a simple
|
||||
bundled http server: <code>node scripts\web-server.js</code>.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ angular-seed, and run the application in the browser.
|
|||
You can now see the page in your browser. It's not very exciting, but that's OK.
|
||||
|
||||
The HTML page that displays "Nothing here yet!" was constructed with the HTML code shown below.
|
||||
The code contains some key Angular elements that we will need going forward.
|
||||
The code contains some key Angular elements that we will need as we progress.
|
||||
|
||||
__`app/index.html`:__
|
||||
<pre>
|
||||
|
|
@ -104,7 +104,7 @@ __`app/index.html`:__
|
|||
|
||||
The `ng-app` attribute represents an Angular directive named `ngApp` (Angular uses
|
||||
`name-with-dashes` for its custom attributes and `camelCase` for the corresponding directives
|
||||
that implements them).
|
||||
which implement them).
|
||||
This directive is used to flag the html element that Angular should consider to be the root element
|
||||
of our application.
|
||||
This gives application developers the freedom to tell Angular if the entire html page or only a
|
||||
|
|
|
|||
|
|
@ -191,8 +191,8 @@ You can do this by issuing `npm install` into your terminal.
|
|||
To run the test, do the following:
|
||||
|
||||
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
|
||||
`./scripts/test.sh` to start the Karma server (the config file necessary to start the server
|
||||
is located at `./config/karma.conf.js`).
|
||||
`./scripts/test.sh` (if you are on Windows, run scripts\test.bat) to start the Karma server (the
|
||||
config file necessary to start the server is located at `./config/karma.conf.js`).
|
||||
|
||||
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
||||
the background. Karma will use this browser for test execution.
|
||||
|
|
@ -206,7 +206,7 @@ is located at `./config/karma.conf.js`).
|
|||
|
||||
Yay! The test passed! Or not...
|
||||
|
||||
4. To rerun the tests, just change any of the source or test files. Karma will notice the change
|
||||
4. To rerun the tests, just change any of the source or test .js files. Karma will notice the change
|
||||
and will rerun the tests for you. Now isn't that sweet?
|
||||
|
||||
# Experiments
|
||||
|
|
@ -217,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:
|
||||
|
||||
$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:
|
||||
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ phonecatApp.controller('PhoneListCtrl', function ($scope) {
|
|||
record. This property is used to order phones by age.
|
||||
|
||||
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
|
||||
not set the default value here, the model would stay uninitialized until our user picks an
|
||||
option from the drop down menu.
|
||||
not set a default value here, the `orderBy` filter would remain uninitialized until our
|
||||
user picked an option from the drop down menu.
|
||||
|
||||
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
|
||||
browser, "Newest" is selected in the drop down menu. This is because we set `orderProp` to `'age'`
|
||||
|
|
|
|||
|
|
@ -170,6 +170,9 @@ describe('PhoneCat controllers', function() {
|
|||
describe('PhoneListCtrl', function(){
|
||||
var scope, ctrl, $httpBackend;
|
||||
|
||||
// Load our app module definition before each test.
|
||||
beforeEach(module('phonecatApp'));
|
||||
|
||||
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
|
||||
// This allows us to inject a service but then attach it to a variable
|
||||
// with the same name as the service.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
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.
|
||||
|
||||
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">
|
||||
**Note:** Starting with AngularJS version 1.2, `ngRoute` is in its own module and must be loaded by loading
|
||||
the `angular-route.js` file distributed with Angular. The easist way to load the file is to add a `<script>`
|
||||
the `angular-route.js` file distributed with Angular. The easiest way to load the file is to add a `<script>`
|
||||
tag to your `index.html` file as shown below.
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -293,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,
|
||||
the profile phone image changes. But how can we change this around to add animations?
|
||||
|
||||
Lets 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
|
||||
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
|
||||
selected thumbnail image.
|
||||
The best way to specify state changes within HTML is to use classes.
|
||||
Much like before, how we used a CSS class to specify
|
||||
|
|
@ -341,45 +340,58 @@ Although we could do that, let's take the opportunity to learn how to create Jav
|
|||
|
||||
__`app/js/animations.js`.__
|
||||
<pre>
|
||||
angular.module('phonecatAnimations', ['ngAnimate'])
|
||||
var phonecatAnimations = angular.module('phonecatAnimations', ['ngAnimate']);
|
||||
|
||||
.animation('.phone', function() {
|
||||
return {
|
||||
addClass : function(element, className, done) {
|
||||
if(className != 'active') {
|
||||
return;
|
||||
}
|
||||
element.css({
|
||||
position: 'absolute',
|
||||
top: 500,
|
||||
left: 0,
|
||||
display: 'block'
|
||||
});
|
||||
jQuery(element).animate({
|
||||
top: 0
|
||||
}, done);
|
||||
phonecatAnimations.animation('.phone', function() {
|
||||
|
||||
return function(cancel) {
|
||||
if(cancel) element.stop();
|
||||
};
|
||||
},
|
||||
removeClass : function(element, className, done) {
|
||||
if(className != 'active') return;
|
||||
element.css({
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0
|
||||
});
|
||||
jQuery(element).animate({
|
||||
top: -500
|
||||
}, done);
|
||||
var animateUp = function(element, className, done) {
|
||||
if(className != 'active') {
|
||||
return;
|
||||
}
|
||||
element.css({
|
||||
position: 'absolute',
|
||||
top: 500,
|
||||
left: 0,
|
||||
display: 'block'
|
||||
});
|
||||
|
||||
return function(cancel) {
|
||||
if(cancel) element.stop();
|
||||
};
|
||||
jQuery(element).animate({
|
||||
top: 0
|
||||
}, done);
|
||||
|
||||
return function(cancel) {
|
||||
if(cancel) {
|
||||
element.stop();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
var animateDown = function(element, className, done) {
|
||||
if(className != 'active') {
|
||||
return;
|
||||
}
|
||||
element.css({
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0
|
||||
});
|
||||
|
||||
jQuery(element).animate({
|
||||
top: -500
|
||||
}, done);
|
||||
|
||||
return function(cancel) {
|
||||
if(cancel) {
|
||||
element.stop();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
addClass: animateUp,
|
||||
removeClass: animateDown
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
Note that we're using {@link http://jquery.com/ jQuery} to implement the animation. jQuery
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{@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
|
||||
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 DOM = require('../src/dom.js').DOM;
|
||||
var gruntUtil = require('../../lib/grunt/utils.js');
|
||||
|
||||
|
||||
describe('ngdoc', function() {
|
||||
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() {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ var makeUnique = {
|
|||
'script.js': true,
|
||||
'unit.js': true,
|
||||
'spec.js': true,
|
||||
'scenario.js': true
|
||||
'scenario.js': true,
|
||||
'protractorTest.js': true
|
||||
}
|
||||
|
||||
function ids(list) {
|
||||
|
|
@ -14,7 +15,7 @@ function ids(list) {
|
|||
};
|
||||
|
||||
|
||||
exports.Example = function(scenarios) {
|
||||
exports.Example = function(scenarios, protractorTests) {
|
||||
this.module = '';
|
||||
this.deps = ['angular.js'];
|
||||
this.html = [];
|
||||
|
|
@ -24,6 +25,8 @@ exports.Example = function(scenarios) {
|
|||
this.unit = [];
|
||||
this.scenario = [];
|
||||
this.scenarios = scenarios;
|
||||
this.protractorTest = [];
|
||||
this.protractorTests = protractorTests;
|
||||
}
|
||||
|
||||
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],
|
||||
id = name;
|
||||
|
||||
if (name == 'protractorTest.js') {
|
||||
ext = 'protractorTest';
|
||||
}
|
||||
|
||||
if (makeUnique[name] && usedIds[id]) {
|
||||
id = name + '-' + (seqCount++);
|
||||
}
|
||||
|
|
@ -56,6 +63,9 @@ exports.Example.prototype.addSource = function(name, content) {
|
|||
if (ext == 'scenario') {
|
||||
this.scenarios.push(content);
|
||||
}
|
||||
if (ext == 'protractorTest') {
|
||||
this.protractorTests.push(content);
|
||||
}
|
||||
};
|
||||
|
||||
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-unit="' + ids(this.unit) + '"');
|
||||
out.push(' source-edit-scenario="' + ids(this.scenario) + '"');
|
||||
out.push(' source-edit-protractor="' + ids(this.protractorTest) + '"');
|
||||
out.push('></div>\n');
|
||||
return out.join('');
|
||||
};
|
||||
|
|
@ -107,6 +118,7 @@ exports.Example.prototype.toHtmlTabs = function() {
|
|||
htmlTabs(this.json);
|
||||
htmlTabs(this.unit);
|
||||
htmlTabs(this.scenario);
|
||||
htmlTabs(this.protractorTest);
|
||||
out.push('</div>');
|
||||
return out.join('');
|
||||
|
||||
|
|
@ -119,7 +131,8 @@ exports.Example.prototype.toHtmlTabs = function() {
|
|||
if (name === 'index.html') {
|
||||
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(
|
||||
'<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');
|
||||
}).then(function() {
|
||||
return writer.makeDir('build/docs/components/font-awesome');
|
||||
}).then(function() {
|
||||
return writer.makeDir('build/docs/e2etests');
|
||||
}).then(function() {
|
||||
console.log('Generating AngularJS Reference Documentation...');
|
||||
return reader.collect();
|
||||
|
|
@ -53,6 +55,10 @@ writer.makeDir('build/docs/', true).then(function() {
|
|||
var id = doc.id.replace('angular.Module', 'angular.IModule');
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ var lookupMinerrMsg = function (doc) {
|
|||
exports.trim = trim;
|
||||
exports.metadata = metadata;
|
||||
exports.scenarios = scenarios;
|
||||
exports.writeProtractorTest = writeProtractorTest;
|
||||
exports.merge = merge;
|
||||
exports.checkBrokenLinks = checkBrokenLinks;
|
||||
exports.Doc = Doc;
|
||||
|
|
@ -50,7 +51,7 @@ exports.ngVersions = function() {
|
|||
});
|
||||
|
||||
//match the future version of AngularJS that is set in the package.json file
|
||||
return expandVersions(sortVersionsNatrually(versions), exports.ngCurrentVersion().full);
|
||||
return expandVersions(sortVersionsNaturally(versions), exports.ngCurrentVersion().full);
|
||||
|
||||
function expandVersions(versions, latestVersion) {
|
||||
var RC_VERSION = /rc\d/;
|
||||
|
|
@ -86,7 +87,7 @@ exports.ngVersions = function() {
|
|||
return expanded;
|
||||
};
|
||||
|
||||
function sortVersionsNatrually(versions) {
|
||||
function sortVersionsNaturally(versions) {
|
||||
var versionMap = {},
|
||||
NON_RC_RELEASE_NUMBER = 999;
|
||||
for(var i = versions.length - 1; i >= 0; i--) {
|
||||
|
|
@ -155,6 +156,7 @@ function Doc(text, file, line) {
|
|||
this.line = line;
|
||||
}
|
||||
this.scenarios = this.scenarios || [];
|
||||
this.protractorTests = this.protractorTests || [];
|
||||
this.requires = this.requires || [];
|
||||
this.param = this.param || [];
|
||||
this.properties = this.properties || [];
|
||||
|
|
@ -292,7 +294,7 @@ Doc.prototype = {
|
|||
replace(/<example(?:\s+module="([^"]*)")?(?:\s+deps="([^"]*)")?(\s+animations="true")?>([\s\S]*?)<\/example>/gmi,
|
||||
function(_, module, deps, animations, content) {
|
||||
|
||||
var example = new Example(self.scenarios);
|
||||
var example = new Example(self.scenarios, self.protractorTests);
|
||||
if(animations) {
|
||||
example.enableAnimations();
|
||||
example.addDeps('angular-animate.js');
|
||||
|
|
@ -329,7 +331,7 @@ Doc.prototype = {
|
|||
}).
|
||||
replace(/^<doc:example(\s+[^>]*)?>([\s\S]*)<\/doc:example>/mi, function(_, attrs, content) {
|
||||
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]);
|
||||
content.
|
||||
|
|
@ -347,6 +349,8 @@ Doc.prototype = {
|
|||
}).
|
||||
replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi, function(_, before, 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());
|
||||
|
|
@ -548,7 +552,7 @@ Doc.prototype = {
|
|||
minerrMsg;
|
||||
|
||||
var gitTagFromFullVersion = function(version) {
|
||||
var match = version.match(/-(\w{7})/);
|
||||
var match = version.match(/sha\.(\w{7})/);
|
||||
|
||||
if (match) {
|
||||
// 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){
|
||||
|
|
@ -1381,6 +1401,14 @@ function explainModuleInstallation(moduleName){
|
|||
modulePackage = 'angular-' + moduleName,
|
||||
modulePackageFile = modulePackage + '.js';
|
||||
|
||||
// Deal with inconsistent ngMock naming - doing it verbosely and explicitly here
|
||||
// rather than cleverly interweaving it in the previous lines to make it obvious
|
||||
// what is going on
|
||||
if ( moduleName == 'mock' ) {
|
||||
modulePackage = 'angular-mocks';
|
||||
modulePackageFile = modulePackage + '.js';
|
||||
}
|
||||
|
||||
return '<h1>Installation</h1>' +
|
||||
'<p>First include <code>' + modulePackageFile +'</code> in your HTML:</p><pre><code>' +
|
||||
' <script src="angular.js">\n' +
|
||||
|
|
|
|||
|
|
@ -16,4 +16,4 @@ RewriteCond %{HTTP_HOST} ^docs-next\.angularjs\.org$
|
|||
RewriteRule appcache.manifest http://code.angularjs.org/next/docs/appcache.manifest [R=301]
|
||||
|
||||
## HTML5 URL Support ##
|
||||
RewriteRule ^(guide|api|cookbook|misc|tutorial)(/.*)?$ index.html
|
||||
RewriteRule ^(guide|api|misc|tutorial)(/.*)?$ index.html
|
||||
|
|
|
|||
|
|
@ -167,6 +167,11 @@
|
|||
margin-top: .5em;
|
||||
}
|
||||
|
||||
.content h6 {
|
||||
text-transform:none;
|
||||
color:black;
|
||||
}
|
||||
|
||||
ul.parameters > li > p,
|
||||
.returns > p {
|
||||
display: inline;
|
||||
|
|
@ -241,6 +246,10 @@ ul.events > li > h3 {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tutorial-nav li {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
}
|
||||
|
||||
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),
|
||||
jQuery = /index-jq[^\.]*\.html$/.test(baseUrl),
|
||||
debug = /index[^\.]*-debug\.html$/.test(baseUrl),
|
||||
|
|
@ -334,10 +334,6 @@
|
|||
<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 class="alert alert-info">
|
||||
<a href="http://blog.angularjs.org/2013/11/farewell-disqus.html">Where did Disqus go?</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,11 +5,19 @@ var docsApp = {
|
|||
filter: {}
|
||||
};
|
||||
|
||||
docsApp.controller.DocsVersionsCtrl = ['$scope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $window, NG_VERSIONS, NG_VERSION) {
|
||||
docsApp.controller.DocsVersionsCtrl = ['$scope', '$rootScope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $rootScope, $window, NG_VERSIONS, NG_VERSION) {
|
||||
$scope.docs_versions = NG_VERSIONS;
|
||||
$scope.docs_version = NG_VERSIONS[0];
|
||||
|
||||
$scope.jumpToDocsVersion = function(version) {
|
||||
$window.location = version.url;
|
||||
var currentPagePath = '';
|
||||
|
||||
// preserve URL path when switching between doc versions
|
||||
if (angular.isObject($rootScope.currentPage) && $rootScope.currentPage.section && $rootScope.currentPage.id) {
|
||||
currentPagePath = '/' + $rootScope.currentPage.section + '/' + $rootScope.currentPage.id;
|
||||
}
|
||||
|
||||
$window.location = version.url + currentPagePath;
|
||||
};
|
||||
}];
|
||||
|
||||
|
|
@ -103,9 +111,6 @@ docsApp.serviceFactory.docsSearch = ['$rootScope','lunrSearch', 'NG_PAGES',
|
|||
angular.forEach(index.search(q), function(result) {
|
||||
var item = NG_PAGES[result.ref];
|
||||
var section = item.section;
|
||||
if(section == 'cookbook') {
|
||||
section = 'tutorial';
|
||||
}
|
||||
results[section] = results[section] || [];
|
||||
if(results[section].length < 15) {
|
||||
results[section].push(item);
|
||||
|
|
@ -137,11 +142,19 @@ docsApp.directive.docsSearchInput = ['$document',function($document) {
|
|||
var ESCAPE_KEY_KEYCODE = 27,
|
||||
FORWARD_SLASH_KEYCODE = 191;
|
||||
angular.element($document[0].body).bind('keydown', function(event) {
|
||||
var input = element[0];
|
||||
if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement != input) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
input.focus();
|
||||
if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement) {
|
||||
var activeElement = document.activeElement;
|
||||
var activeTagName = activeElement.nodeName.toLowerCase();
|
||||
var hasInputFocus = activeTagName == 'input' || activeTagName == 'select' ||
|
||||
activeTagName == 'option' || activeTagName == 'textarea' ||
|
||||
activeElement.hasAttribute('contenteditable');
|
||||
if(!hasInputFocus) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
var input = element[0];
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -614,7 +627,6 @@ docsApp.serviceFactory.sections = ['NG_PAGES', function sections(NG_PAGES) {
|
|||
api: [],
|
||||
tutorial: [],
|
||||
misc: [],
|
||||
cookbook: [],
|
||||
error: [],
|
||||
getPage: function(sectionId, partialId) {
|
||||
var pages = sections[sectionId];
|
||||
|
|
@ -645,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) {
|
||||
if(url) {
|
||||
$scope.docs_fold = '/notes/' + url;
|
||||
|
|
@ -659,7 +671,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
|||
}
|
||||
};
|
||||
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)$/,
|
||||
GLOBALS = /^angular\.([^\.]+)$/,
|
||||
ERROR = /^([a-zA-Z0-9_$]+:)?([a-zA-Z0-9_$]+)$/,
|
||||
|
|
@ -721,7 +733,6 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
|||
guide: 'Developer Guide',
|
||||
misc: 'Miscellaneous',
|
||||
tutorial: 'Tutorial',
|
||||
cookbook: 'Examples',
|
||||
error: 'Error Reference'
|
||||
};
|
||||
|
||||
|
|
@ -736,9 +747,9 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
|||
sectionName = SECTION_NAME[sectionId] || sectionId,
|
||||
page = sections.getPage(sectionId, partialId);
|
||||
|
||||
$scope.currentPage = sections.getPage(sectionId, partialId);
|
||||
$rootScope.currentPage = sections.getPage(sectionId, partialId);
|
||||
|
||||
if (!$scope.currentPage) {
|
||||
if (!$rootScope.currentPage) {
|
||||
$scope.partialTitle = 'Error: Page Not Found!';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ grunt test:unit --browsers $BROWSERS --reporters=dots,junit --no-colors --no-col
|
|||
|
||||
# END TO END TESTS #
|
||||
grunt test:e2e --browsers $BROWSERS_E2E --reporters=dots,junit --no-colors --no-color
|
||||
grunt test:protractor
|
||||
|
||||
# Promises/A+ TESTS #
|
||||
grunt test:promises-aplus --no-color
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@ module.exports = function(config) {
|
|||
|
||||
'build/angular.js',
|
||||
'build/angular-cookies.js',
|
||||
'build/angular-mocks.js',
|
||||
'build/angular-resource.js',
|
||||
'build/angular-touch.js',
|
||||
'build/angular-sanitize.js',
|
||||
'build/angular-route.js',
|
||||
'build/angular-animate.js',
|
||||
'build/angular-mocks.js',
|
||||
|
||||
'build/docs/components/lunr.js',
|
||||
'build/docs/components/google-code-prettify.js',
|
||||
|
|
|
|||
|
|
@ -1,27 +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'
|
||||
},
|
||||
|
||||
browserNoActivityTimeout: 90000
|
||||
});
|
||||
};
|
||||
|
|
@ -36,13 +36,14 @@ module.exports = function(config, specificOptions) {
|
|||
},
|
||||
'SL_Firefox': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox'
|
||||
browserName: 'firefox',
|
||||
version: '26'
|
||||
},
|
||||
'SL_Safari': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'Mac 10.8',
|
||||
version: '6'
|
||||
platform: 'OS X 10.9',
|
||||
version: '7'
|
||||
},
|
||||
'SL_IE_8': {
|
||||
base: 'SauceLabs',
|
||||
|
|
|
|||
|
|
@ -61,6 +61,14 @@ module.exports = function(grunt) {
|
|||
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 () {
|
||||
util.collectErrors();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ var fs = require('fs');
|
|||
var shell = require('shelljs');
|
||||
var grunt = require('grunt');
|
||||
var spawn = require('child_process').spawn;
|
||||
var semver = require('semver');
|
||||
var version;
|
||||
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
|
||||
|
||||
|
|
@ -32,31 +33,82 @@ module.exports = {
|
|||
|
||||
getVersion: function(){
|
||||
if (version) return version;
|
||||
|
||||
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
|
||||
var match = package.version.match(/^([^\-]*)(?:\-(.+))?$/);
|
||||
var semver = match[1].split('.');
|
||||
try {
|
||||
|
||||
var fullVersion = match[1];
|
||||
var gitTag = getTagOfCurrentCommit();
|
||||
var semVerVersion, codeName, fullVersion;
|
||||
if (gitTag) {
|
||||
// tagged release
|
||||
fullVersion = semVerVersion = semver.valid(gitTag);
|
||||
codeName = getTaggedReleaseCodeName(gitTag);
|
||||
} else {
|
||||
// snapshot release
|
||||
semVerVersion = getSnapshotVersion();
|
||||
fullVersion = semVerVersion + '-' + getSnapshotSuffix();
|
||||
codeName = 'snapshot'
|
||||
}
|
||||
|
||||
if (match[2]) {
|
||||
fullVersion += '-';
|
||||
fullVersion += (match[2] == 'snapshot') ? getSnapshotSuffix() : match[2];
|
||||
var versionParts = semVerVersion.match(/(\d+)\.(\d+)\.(\d+)/);
|
||||
|
||||
version = {
|
||||
full: fullVersion,
|
||||
major: versionParts[1],
|
||||
minor: versionParts[2],
|
||||
dot: versionParts[3],
|
||||
codename: codeName,
|
||||
cdn: package.cdnVersion
|
||||
};
|
||||
|
||||
return version;
|
||||
|
||||
} catch (e) {
|
||||
grunt.fail.warn(e);
|
||||
}
|
||||
|
||||
version = {
|
||||
full: fullVersion,
|
||||
major: semver[0],
|
||||
minor: semver[1],
|
||||
dot: semver[2].replace(/rc\d+/, ''),
|
||||
codename: package.codename,
|
||||
cdn: package.cdnVersion
|
||||
};
|
||||
function getTagOfCurrentCommit() {
|
||||
var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
|
||||
var gitTagOutput = gitTagResult.output.trim();
|
||||
var branchVersionPattern = new RegExp(package.branchVersion.replace('.', '\\.').replace('*', '\\d+'));
|
||||
if (gitTagResult.code === 0 && gitTagOutput.match(branchVersionPattern)) {
|
||||
return gitTagOutput;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return version;
|
||||
function getTaggedReleaseCodeName(tagName) {
|
||||
var tagMessage = shell.exec('git cat-file -p '+ tagName +' | grep "codename"', {silent:true}).output;
|
||||
var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
|
||||
if (!codeName) {
|
||||
throw new Error("Could not extract release code name. The message of tag "+tagName+
|
||||
" must match '*codename(some release name)*'");
|
||||
}
|
||||
return codeName;
|
||||
}
|
||||
|
||||
function getSnapshotVersion() {
|
||||
var oldTags = shell.exec('git tag -l v'+package.branchVersion, {silent:true}).output.trim().split('\n');
|
||||
// ignore non semver versions.
|
||||
oldTags = oldTags.filter(function(version) {
|
||||
return version && semver.valid(version);
|
||||
});
|
||||
if (oldTags.length) {
|
||||
oldTags.sort(semver.compare);
|
||||
semVerVersion = oldTags[oldTags.length-1];
|
||||
if (semVerVersion.indexOf('-') !== -1) {
|
||||
semVerVersion = semver.inc(semVerVersion, 'prerelease');
|
||||
} else {
|
||||
semVerVersion = semver.inc(semVerVersion, 'patch');
|
||||
}
|
||||
} else {
|
||||
semVerVersion = semver.valid(package.branchVersion.replace(/\*/g, '0'));
|
||||
}
|
||||
return semVerVersion;
|
||||
}
|
||||
|
||||
function getSnapshotSuffix() {
|
||||
var jenkinsBuild = process.env.BUILD_NUMBER || 'local';
|
||||
var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER || 'local';
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
return 'build.'+jenkinsBuild+'+sha.'+hash;
|
||||
}
|
||||
|
|
@ -84,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){
|
||||
src.unshift('src/' + name + '.prefix');
|
||||
src.push('src/' + name + '.suffix');
|
||||
|
|
|
|||
47
package.json
47
package.json
|
|
@ -1,45 +1,48 @@
|
|||
{
|
||||
"name": "angularjs",
|
||||
"version": "1.2.6",
|
||||
"cdnVersion": "1.2.5",
|
||||
"codename": "taco-salsafication",
|
||||
"branchVersion": "1.2.*",
|
||||
"cdnVersion": "1.2.11",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.1",
|
||||
"bower": "~1.2.2",
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-bump": "~0.0.13",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-contrib-compress": "~0.5.2",
|
||||
"grunt-contrib-connect": "~0.5.0",
|
||||
"grunt-contrib-compress": "~0.5.2",
|
||||
"grunt-contrib-copy": "~0.4.1",
|
||||
"grunt-contrib-jshint": "~0.7.2",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||
"grunt-jscs-checker": "~0.3.2",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"grunt-parallel": "~0.3.1",
|
||||
"grunt-shell": "~0.4.0",
|
||||
"load-grunt-tasks": "~0.3.0",
|
||||
"bower": "~1.2.2",
|
||||
"jasmine-node": "~1.11.0",
|
||||
"q": "~0.9.2",
|
||||
"q-io": "~1.10.6",
|
||||
"qq": "~0.3.5",
|
||||
"shelljs": "~0.2.6",
|
||||
"karma": "~0.11",
|
||||
"karma-jasmine": "~0.1.0",
|
||||
"karma-chrome-launcher": "~0.1.0",
|
||||
"karma-firefox-launcher": "~0.1.0",
|
||||
"karma-ng-scenario": "~0.1.0",
|
||||
"karma-junit-reporter": "~0.2.1",
|
||||
"karma-sauce-launcher": "~0.1.1",
|
||||
"karma-script-launcher": "~0.1.0",
|
||||
"karma": "0.11.12",
|
||||
"karma-jasmine": "0.1.5",
|
||||
"karma-chrome-launcher": "0.1.2",
|
||||
"karma-firefox-launcher": "0.1.3",
|
||||
"karma-ng-scenario": "0.1.0",
|
||||
"karma-junit-reporter": "0.2.1",
|
||||
"karma-sauce-launcher": "0.2.0",
|
||||
"karma-script-launcher": "0.1.0",
|
||||
"karma-browserstack-launcher": "0.0.7",
|
||||
"protractor": "~0.17.0",
|
||||
"yaml-js": "~0.0.8",
|
||||
"marked": "0.2.9",
|
||||
"rewire": "1.1.3",
|
||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||
"grunt-parallel": "~0.3.1",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"promises-aplus-tests": "~1.3.2",
|
||||
"grunt-shell": "~0.4.0",
|
||||
"semver": "~2.1.0",
|
||||
"lodash": "~2.1.0",
|
||||
"karma-browserstack-launcher": "git://github.com/karma-runner/karma-browserstack-launcher.git#master",
|
||||
"browserstacktunnel-wrapper": "~1.1.1"
|
||||
},
|
||||
"licenses": [
|
||||
|
|
@ -48,7 +51,5 @@
|
|||
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"grunt-contrib-jshint": "~0.7.2"
|
||||
}
|
||||
"dependencies": {}
|
||||
}
|
||||
|
|
|
|||
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
|
||||
}
|
||||
};
|
||||
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,15 +0,0 @@
|
|||
# Angular Bower Script
|
||||
|
||||
Script for updating the Angular bower repos from current local build.
|
||||
|
||||
## Instructions
|
||||
|
||||
`grunt package`: Build angular locally
|
||||
|
||||
```shell
|
||||
./publish.sh
|
||||
```
|
||||
|
||||
## License
|
||||
MIT
|
||||
|
||||
|
|
@ -1,88 +1,101 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Script for updating the Angular bower repos from current local build.
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Update bower ###############"
|
||||
echo "#################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`
|
||||
|
||||
SCRIPT_DIR=`pwd`
|
||||
TMP_DIR=../../tmp
|
||||
BUILD_DIR=../../build
|
||||
NEW_VERSION=`cat $BUILD_DIR/version.txt`
|
||||
|
||||
REPOS=(
|
||||
angular \
|
||||
angular-animate \
|
||||
angular-cookies \
|
||||
angular-i18n \
|
||||
angular-loader \
|
||||
angular-mocks \
|
||||
angular-route \
|
||||
angular-resource \
|
||||
angular-sanitize \
|
||||
angular-scenario \
|
||||
angular-touch \
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
)
|
||||
|
||||
#
|
||||
# clone repos
|
||||
#
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
echo "-- Cloning bower-$repo"
|
||||
git clone git@github.com:angular/bower-$repo.git $TMP_DIR/bower-$repo
|
||||
done
|
||||
function init {
|
||||
TMP_DIR=$(resolveDir ../../tmp)
|
||||
BUILD_DIR=$(resolveDir ../../build)
|
||||
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||
REPOS=(
|
||||
angular
|
||||
angular-animate
|
||||
angular-cookies
|
||||
angular-i18n
|
||||
angular-loader
|
||||
angular-mocks
|
||||
angular-route
|
||||
angular-resource
|
||||
angular-sanitize
|
||||
angular-scenario
|
||||
angular-touch
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# move the files from the build
|
||||
#
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
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
|
||||
cp $BUILD_DIR/i18n/*.js $TMP_DIR/bower-angular-i18n/
|
||||
|
||||
# move csp.css
|
||||
cp $BUILD_DIR/angular-csp.css $TMP_DIR/bower-angular
|
||||
function prepare {
|
||||
#
|
||||
# clone repos
|
||||
#
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
echo "-- Cloning bower-$repo"
|
||||
git clone git@github.com:angular/bower-$repo.git $TMP_DIR/bower-$repo
|
||||
done
|
||||
|
||||
|
||||
#
|
||||
# update bower.json
|
||||
# tag each repo
|
||||
#
|
||||
#
|
||||
# move the files from the build
|
||||
#
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
echo "-- Updating version in bower-$repo to $NEW_VERSION"
|
||||
cd $TMP_DIR/bower-$repo
|
||||
sed -i .tmp -E 's/"(version)":[ ]*".*"/"\1": "'$NEW_VERSION'"/g' bower.json
|
||||
sed -i .tmp -E 's/"(angular.*)":[ ]*".*"/"\1": "'$NEW_VERSION'"/g' bower.json
|
||||
# delete tmp files
|
||||
rm *.tmp
|
||||
git add -A
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
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
|
||||
|
||||
echo "-- Committing, tagging and pushing bower-$repo"
|
||||
git commit -m "v$NEW_VERSION"
|
||||
git tag v$NEW_VERSION
|
||||
git push origin master
|
||||
git push origin v$NEW_VERSION
|
||||
cd $SCRIPT_DIR
|
||||
done
|
||||
# move i18n files
|
||||
cp $BUILD_DIR/i18n/*.js $TMP_DIR/bower-angular-i18n/
|
||||
|
||||
# move csp.css
|
||||
cp $BUILD_DIR/angular-csp.css $TMP_DIR/bower-angular
|
||||
|
||||
|
||||
#
|
||||
# update bower.json
|
||||
# tag each repo
|
||||
#
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
echo "-- Updating version in bower-$repo to $NEW_VERSION"
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
# code.angular.js.org Script
|
||||
|
||||
Script for updating code.angularjs.org repo from current local build.
|
||||
|
||||
Note: For a snapshot build, this will fetch the data from the ci server
|
||||
and NOT take the local build!
|
||||
|
||||
## Instructions
|
||||
|
||||
`grunt package`: Build angular locally
|
||||
|
||||
```shell
|
||||
./publish.sh
|
||||
```
|
||||
|
||||
## License
|
||||
MIT
|
||||
|
||||
|
|
@ -1,62 +1,73 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Script for updating code.angularjs.org repo from current local build.
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update code.angular.js.org ###"
|
||||
echo "#################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
)
|
||||
|
||||
TMP_DIR=../../tmp
|
||||
REPO_DIR=$TMP_DIR/code.angularjs.org
|
||||
BUILD_DIR=../../build
|
||||
SCRIPT_DIR=`pwd`
|
||||
NEW_VERSION=`cat $BUILD_DIR/version.txt`
|
||||
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
|
||||
}
|
||||
|
||||
#
|
||||
# Snapshot builds are kept in a temp directory in code.angularjs.org
|
||||
# that is filled by calling a php script there.
|
||||
#
|
||||
if [[ "$NEW_VERSION" =~ sha ]] ;then
|
||||
echo "-- updating snapshot version"
|
||||
curl -G --data-urlencode "ver=$NEW_VERSION" http://code.angularjs.org/fetchLatestSnapshot.php
|
||||
exit 0;
|
||||
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
|
||||
|
||||
#
|
||||
# clone
|
||||
#
|
||||
echo "-- Cloning code.angularjs.org"
|
||||
git clone git@github.com:angular/code.angularjs.org.git $REPO_DIR
|
||||
|
||||
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/
|
||||
|
||||
#
|
||||
# copy the files from the build
|
||||
#
|
||||
#
|
||||
# commit
|
||||
#
|
||||
echo "-- Committing code.angularjs.org"
|
||||
cd $REPO_DIR
|
||||
git add -A
|
||||
git commit -m "v$NEW_VERSION"
|
||||
}
|
||||
|
||||
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/
|
||||
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
|
||||
|
||||
#
|
||||
# commit and push
|
||||
#
|
||||
echo "-- Committing and pushing code.angularjs.org"
|
||||
cd $REPO_DIR
|
||||
git add -A
|
||||
git commit -m "v$NEW_VERSION"
|
||||
git push origin master
|
||||
cd $SCRIPT_DIR
|
||||
cd $REPO_DIR
|
||||
echo "-- Pushing code.angularjs.org"
|
||||
git push origin master
|
||||
|
||||
#
|
||||
# refresh code.angularjs.org from github
|
||||
#
|
||||
curl http://code.angularjs.org/gitFetchSite.php
|
||||
echo "-- Refreshing code.angularjs.org"
|
||||
curl http://code.angularjs.org/gitFetchSite.php
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "############################################"
|
||||
echo "## Increment version and add "-snapshot" ##"
|
||||
echo "############################################"
|
||||
|
||||
if [ "$1" != "patch" -a "$1" != "minor" -a "$1" != "major" ]; then
|
||||
echo "Please specify the next version type: patch|minor|major"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_TYPE=$1
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`/../..
|
||||
|
||||
echo "-- increment version "
|
||||
grunt bump:$BUMP_TYPE
|
||||
NEXT_VERSION=`sed -En 's/.*"version"[ ]*:[ ]*"(.*)".*/\1/p' package.json`
|
||||
sed -i .tmp -E 's/"version": "(.*)"/"version": "\1-snapshot"/' package.json
|
||||
echo "-- new version: `grep '"version"' package.json`"
|
||||
echo "-- commit"
|
||||
git add package.json
|
||||
git commit -m "chore(release): start v$NEXT_VERSION"
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "############################################"
|
||||
echo "## Remove "-snapshot" from version ########"
|
||||
echo "############################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`/../..
|
||||
|
||||
echo "-- old version: `grep '"version"' package.json`"
|
||||
sed -i .tmp -E 's/"version": "(.*)-snapshot"/"version": "\1"/' package.json
|
||||
VERSION=`sed -En 's/.*"version"[ ]*:[ ]*"(.*)".*/\1/p' package.json`
|
||||
echo "-- local version: $VERSION"
|
||||
|
||||
echo "-- commit and tag with v$VERSION"
|
||||
git add package.json
|
||||
git commit -m "chore(release): cut v$VERSION release"
|
||||
git tag -m "v$VERSION" v$VERSION
|
||||
|
|
@ -4,22 +4,44 @@ echo "#################################"
|
|||
echo "#### Update master ##############"
|
||||
echo "#################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
ARG_DEFS=(
|
||||
"[--no-test=(true|false)]"
|
||||
)
|
||||
|
||||
cd `dirname $0`/../..
|
||||
function init {
|
||||
if [[ ! $VERBOSE ]]; then
|
||||
VERBOSE=false
|
||||
fi
|
||||
VERBOSE_ARG="--verbose=$VERBOSE"
|
||||
}
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Jenkins Build ############"
|
||||
echo "#################################"
|
||||
./jenkins_build.sh
|
||||
function build {
|
||||
cd ../..
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update code.angular.js.org ###"
|
||||
echo "#################################"
|
||||
./scripts/code.angularjs.org/publish.sh
|
||||
if [[ $NO_TEST == "true" ]]; then
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
else
|
||||
./jenkins_build.sh
|
||||
fi
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Update bower ###############"
|
||||
echo "#################################"
|
||||
./scripts/bower/publish.sh
|
||||
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
|
||||
|
|
@ -1,42 +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 "#### cut release ############"
|
||||
echo "#################################"
|
||||
|
||||
if [ "$1" != "patch" -a "$1" != "minor" -a "$1" != "major" ]; then
|
||||
echo "Please specify the next version type: patch|minor|major"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_TYPE=$1
|
||||
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=(.+)"
|
||||
)
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
function init {
|
||||
if [[ $(git rev-parse --short HEAD) != $COMMIT_SHA ]]; then
|
||||
echo "HEAD is not at $COMMIT_SHA"
|
||||
usage
|
||||
fi
|
||||
|
||||
# Jump onto the master branch and make sure we are using the latest
|
||||
git checkout -f master
|
||||
git merge --ff-only origin/master
|
||||
if [[ ! $VERBOSE ]]; then
|
||||
VERBOSE=false
|
||||
fi
|
||||
VERBOSE_ARG="--verbose=$VERBOSE"
|
||||
}
|
||||
|
||||
function build {
|
||||
cd ../..
|
||||
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`/../..
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
|
||||
cd $SCRIPT_DIR
|
||||
}
|
||||
|
||||
# Bump versions: remove "-snapshot" suffix
|
||||
./scripts/jenkins/bump-remove-snapshot.sh
|
||||
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
|
||||
|
||||
# Build
|
||||
./jenkins_build.sh
|
||||
if [[ $1 == "prepare" ]]; then
|
||||
# The build requires the tag to be set already!
|
||||
build
|
||||
fi
|
||||
|
||||
# Bump versions: Increment version and add "-snapshot"
|
||||
./scripts/jenkins/bump-increment.sh $BUMP_TYPE
|
||||
../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
|
||||
}
|
||||
|
||||
echo "-- push to Github"
|
||||
# push to github
|
||||
git push --all
|
||||
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
|
||||
}
|
||||
|
||||
# Update code.angularjs.org
|
||||
./scripts/code.angularjs.org/publish.sh
|
||||
|
||||
# Update bower
|
||||
./scripts/bower/publish.sh
|
||||
source $(dirname $0)/../utils.inc
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ if [ $JOB = "unit" ]; then
|
|||
grunt test:promises-aplus
|
||||
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_8,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
|
||||
elif [ $JOB = "e2e" ]; then
|
||||
grunt test:e2e --browsers SL_Chrome --reporters dots
|
||||
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
|
||||
|
|
|
|||
279
scripts/utils.inc
Normal file
279
scripts/utils.inc
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
# This file provides:
|
||||
# - a default control flow
|
||||
# * initializes the environment
|
||||
# * able to mock "git push" in your script and in all sub scripts
|
||||
# * call a function in your script based on the arguments
|
||||
# - named argument parsing and automatic generation of the "usage" for your script
|
||||
# - intercepting "git push" in your script and all sub scripts
|
||||
# - utility functions
|
||||
#
|
||||
# Usage:
|
||||
# - define the variable ARGS_DEF (see below) with the arguments for your script
|
||||
# - include this file using `source utils.inc` at the end of your script.
|
||||
#
|
||||
# Default control flow:
|
||||
# 0. Set the current directory to the directory of the script. By this
|
||||
# the script can be called from anywhere.
|
||||
# 1. Parse the named arguments
|
||||
# 2. If the parameter "git_push_dryrun" is set, all calls the `git push` in this script
|
||||
# or in child scripts will be intercepted so that the `--dry-run` and `--porcelain` is added
|
||||
# to show what the push would do but not actually do it.
|
||||
# 3. If the parameter "verbose" is set, the `-x` flag will be set in bash.
|
||||
# 4. The function "init" will be called if it exists
|
||||
# 5. If the parameter "action" is set, it will call the function with the name of that parameter.
|
||||
# Otherwise the function "run" will be called.
|
||||
#
|
||||
# Named Argument Parsing:
|
||||
# - The variable ARGS_DEF defines the valid command arguments
|
||||
# * Required args syntax: --paramName=paramRegex
|
||||
# * Optional args syntax: [--paramName=paramRegex]
|
||||
# * e.g. ARG_DEFS=("--required_param=(.+)" "[--optional_param=(.+)]")
|
||||
# - Checks that:
|
||||
# * all arguments match to an entry in ARGS_DEF
|
||||
# * all required arguments are present
|
||||
# * all arguments match their regex
|
||||
# - Afterwards, every paramter value will be stored in a variable
|
||||
# with the name of the parameter in upper case (with dash converted to underscore).
|
||||
#
|
||||
# Special arguments that are always available:
|
||||
# - "--action=.*": This parameter will be used to dispatch to a function with that name when the
|
||||
# script is started
|
||||
# - "--git_push_dryrun=true": This will intercept all calls to `git push` in this script
|
||||
# or in child scripts so that the `--dry-run` and `--porcelain` is added
|
||||
# to show what the push would do but not actually do it.
|
||||
# - "--verbose=true": This will set the `-x` flag in bash so that all calls will be logged
|
||||
#
|
||||
# Utility functions:
|
||||
# - readJsonProp
|
||||
# - replaceJsonProp
|
||||
# - resolveDir
|
||||
# - getVar
|
||||
# - serVar
|
||||
# - isFunction
|
||||
|
||||
# always stop on errors
|
||||
set -e
|
||||
|
||||
function usage {
|
||||
echo "Usage: ${0} ${ARG_DEFS[@]}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
function parseArgs {
|
||||
local REQUIRED_ARG_NAMES=()
|
||||
|
||||
# -- helper functions
|
||||
function varName {
|
||||
# everything to upper case and dash to underscore
|
||||
echo ${1//-/_} | tr '[:lower:]' '[:upper:]'
|
||||
}
|
||||
|
||||
function readArgDefs {
|
||||
local ARG_DEF
|
||||
local AD_OPTIONAL
|
||||
local AD_NAME
|
||||
local AD_RE
|
||||
|
||||
# -- helper functions
|
||||
function parseArgDef {
|
||||
local ARG_DEF_REGEX="(\[?)--([^=]+)=(.*)"
|
||||
if [[ ! $1 =~ $ARG_DEF_REGEX ]]; then
|
||||
echo "Internal error: arg def has wrong format: $ARG_DEF"
|
||||
exit 1
|
||||
fi
|
||||
AD_OPTIONAL="${BASH_REMATCH[1]}"
|
||||
AD_NAME="${BASH_REMATCH[2]}"
|
||||
AD_RE="${BASH_REMATCH[3]}"
|
||||
if [[ $AD_OPTIONAL ]]; then
|
||||
# Remove last bracket for optional args.
|
||||
# Can't put this into the ARG_DEF_REGEX somehow...
|
||||
AD_RE=${AD_RE%?}
|
||||
fi
|
||||
}
|
||||
|
||||
# -- run
|
||||
for ARG_DEF in "${ARG_DEFS[@]}"
|
||||
do
|
||||
parseArgDef $ARG_DEF
|
||||
|
||||
local AD_NAME_UPPER=$(varName $AD_NAME)
|
||||
setVar "${AD_NAME_UPPER}_OPTIONAL" "$AD_OPTIONAL"
|
||||
setVar "${AD_NAME_UPPER}_RE" "$AD_RE"
|
||||
if [[ ! $AD_OPTIONAL ]]; then
|
||||
REQUIRED_ARG_NAMES+=($AD_NAME)
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
function readAndValidateArgs {
|
||||
local ARG_NAME
|
||||
local ARG_VALUE
|
||||
local ARG_NAME_UPPER
|
||||
|
||||
# -- helper functions
|
||||
function parseArg {
|
||||
local ARG_REGEX="--([^=]+)=?(.*)"
|
||||
|
||||
if [[ ! $1 =~ $ARG_REGEX ]]; then
|
||||
echo "Can't parse argument $i"
|
||||
usage
|
||||
fi
|
||||
|
||||
ARG_NAME="${BASH_REMATCH[1]}"
|
||||
ARG_VALUE="${BASH_REMATCH[2]}"
|
||||
ARG_NAME_UPPER=$(varName $ARG_NAME)
|
||||
}
|
||||
|
||||
function validateArg {
|
||||
local AD_RE=$(getVar ${ARG_NAME_UPPER}_RE)
|
||||
|
||||
if [[ ! $AD_RE ]]; then
|
||||
echo "Unknown option: $ARG_NAME"
|
||||
usage
|
||||
fi
|
||||
|
||||
if [[ ! $ARG_VALUE =~ ^${AD_RE}$ ]]; then
|
||||
echo "Wrong format: $ARG_NAME"
|
||||
usage;
|
||||
fi
|
||||
|
||||
# validate that the "action" option points to a valid function
|
||||
if [[ $ARG_NAME == "action" ]] && ! isFunction $ARG_VALUE; then
|
||||
echo "No action $ARG_VALUE defined in this script"
|
||||
usage;
|
||||
fi
|
||||
}
|
||||
|
||||
# -- run
|
||||
for i in "$@"
|
||||
do
|
||||
parseArg $i
|
||||
validateArg
|
||||
setVar "${ARG_NAME_UPPER}" "$ARG_VALUE"
|
||||
done
|
||||
}
|
||||
|
||||
function checkMissingArgs {
|
||||
local ARG_NAME
|
||||
for ARG_NAME in "${REQUIRED_ARG_NAMES[@]}"
|
||||
do
|
||||
ARG_VALUE=$(getVar $(varName $ARG_NAME))
|
||||
|
||||
if [[ ! $ARG_VALUE ]]; then
|
||||
echo "Missing: $ARG_NAME"
|
||||
usage;
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# -- run
|
||||
readArgDefs
|
||||
readAndValidateArgs "$@"
|
||||
checkMissingArgs
|
||||
|
||||
}
|
||||
|
||||
# getVar(varName)
|
||||
function getVar {
|
||||
echo ${!1}
|
||||
}
|
||||
|
||||
# setVar(varName, varValue)
|
||||
function setVar {
|
||||
eval "$1=\"$2\""
|
||||
}
|
||||
|
||||
# isFunction(name)
|
||||
# - to be used in an if, so return 0 if successful and 1 if not!
|
||||
function isFunction {
|
||||
if [[ $(type -t $1) == "function" ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# readJsonProp(jsonFile, property)
|
||||
# - restriction: property needs to be on an own line!
|
||||
function readJsonProp {
|
||||
echo $(sed -En 's/.*"'$2'"[ ]*:[ ]*"(.*)".*/\1/p' $1)
|
||||
}
|
||||
|
||||
# replaceJsonProp(jsonFile, propertyRegex, valueRegex, replacePattern)
|
||||
# - note: propertyRegex will be automatically placed into a
|
||||
# capturing group! -> all other groups start at index 2!
|
||||
function replaceJsonProp {
|
||||
replaceInFile $1 '"('$2')"[ ]*:[ ]*"'$3'"' '"\1": "'$4'"'
|
||||
}
|
||||
|
||||
# replaceInFile(file, findPattern, replacePattern)
|
||||
function replaceInFile {
|
||||
sed -i .tmp -E "s/$2/$3/" $1
|
||||
rm $1.tmp
|
||||
}
|
||||
|
||||
# resolveDir(relativeDir)
|
||||
# - resolves a directory relative to the current script
|
||||
function resolveDir {
|
||||
echo $(cd $SCRIPT_DIR; cd $1; pwd)
|
||||
}
|
||||
|
||||
function git_push_dryrun_proxy {
|
||||
echo "## git push dryrun proxy enabled!"
|
||||
export ORIGIN_GIT=$(which git)
|
||||
|
||||
function git {
|
||||
local ARGS=("$@")
|
||||
local RC
|
||||
if [[ $1 == "push" ]]; then
|
||||
ARGS+=("--dry-run" "--porcelain")
|
||||
echo "####### START GIT PUSH DRYRUN #######"
|
||||
echo "${ARGS[@]}"
|
||||
fi
|
||||
if [[ $1 == "commit" ]]; then
|
||||
echo "${ARGS[@]}"
|
||||
fi
|
||||
$ORIGIN_GIT "${ARGS[@]}"
|
||||
RC=$?
|
||||
if [[ $1 == "push" ]]; then
|
||||
echo "####### END GIT PUSH DRYRUN #######"
|
||||
fi
|
||||
return $RC
|
||||
}
|
||||
|
||||
export -f git
|
||||
}
|
||||
|
||||
function main {
|
||||
# normalize the working dir to the directory of the script
|
||||
cd $(dirname $0);SCRIPT_DIR=$(pwd)
|
||||
|
||||
ARG_DEFS+=("[--git-push-dryrun=(true|false)]" "[--verbose=(true|false)]")
|
||||
parseArgs "$@"
|
||||
|
||||
# --git_push_dryrun argument
|
||||
if [[ $GIT_PUSH_DRYRUN == "true" ]]; then
|
||||
git_push_dryrun_proxy
|
||||
fi
|
||||
|
||||
# --verbose argument
|
||||
if [[ $VERBOSE == "true" ]]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
if isFunction init; then
|
||||
init "$@"
|
||||
fi
|
||||
|
||||
# jump to the function denoted by the --action argument,
|
||||
# otherwise call the "run" function
|
||||
if [[ $ACTION ]]; then
|
||||
$ACTION "$@"
|
||||
else
|
||||
run "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
main "$@"
|
||||
|
|
@ -192,7 +192,8 @@ function isArrayLike(obj) {
|
|||
* is the value of an object property or an array element and `key` is the object property key or
|
||||
* array element index. Specifying a `context` for the function is optional.
|
||||
*
|
||||
* Note: this function was previously known as `angular.foreach`.
|
||||
* It is worth nothing that `.forEach` does not iterate over inherited properties because it filters
|
||||
* using the `hasOwnProperty` method.
|
||||
*
|
||||
<pre>
|
||||
var values = {name: 'misko', gender: 'male'};
|
||||
|
|
@ -771,7 +772,7 @@ function shallowCopy(src, dst) {
|
|||
for(var key in src) {
|
||||
// shallowCopy is only ever called by $compile nodeLinkFn, which has control over src
|
||||
// so we don't need to worry about using our custom hasOwnProperty here
|
||||
if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') {
|
||||
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
|
|
@ -959,7 +960,9 @@ function fromJson(json) {
|
|||
|
||||
|
||||
function toBoolean(value) {
|
||||
if (value && value.length !== 0) {
|
||||
if (typeof value === 'function') {
|
||||
value = true;
|
||||
} else if (value && value.length !== 0) {
|
||||
var v = lowercase("" + value);
|
||||
value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -354,11 +354,9 @@ function annotate(fn) {
|
|||
* @param {(Object|function())} provider If the provider is:
|
||||
*
|
||||
* - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
|
||||
* {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be
|
||||
* created.
|
||||
* - `Constructor`: a new instance of the provider will be created using
|
||||
* {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as
|
||||
* `object`.
|
||||
* {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created.
|
||||
* - `Constructor`: a new instance of the provider will be created using
|
||||
* {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`.
|
||||
*
|
||||
* @returns {Object} registered provider instance
|
||||
|
||||
|
|
@ -474,7 +472,7 @@ function annotate(fn) {
|
|||
* constructor function that will be used to instantiate the service instance.
|
||||
*
|
||||
* You should use {@link AUTO.$provide#methods_service $provide.service(class)} if you define your service
|
||||
* as a type/class. This is common when using {@link http://coffeescript.org CoffeeScript}.
|
||||
* as a type/class.
|
||||
*
|
||||
* @param {string} name The name of the instance.
|
||||
* @param {Function} constructor A class (constructor function) that will be instantiated.
|
||||
|
|
@ -482,20 +480,24 @@ function annotate(fn) {
|
|||
*
|
||||
* @example
|
||||
* Here is an example of registering a service using
|
||||
* {@link AUTO.$provide#methods_service $provide.service(class)} that is defined as a CoffeeScript class.
|
||||
* {@link AUTO.$provide#methods_service $provide.service(class)}.
|
||||
* <pre>
|
||||
* class Ping
|
||||
* constructor: (@$http) ->
|
||||
* send: () =>
|
||||
* @$http.get('/ping')
|
||||
*
|
||||
* $provide.service('ping', ['$http', Ping])
|
||||
* var Ping = function($http) {
|
||||
* this.$http = $http;
|
||||
* };
|
||||
*
|
||||
* Ping.$inject = ['$http'];
|
||||
*
|
||||
* Ping.prototype.send = function() {
|
||||
* return this.$http.get('/ping');
|
||||
* };
|
||||
* $provide.service('ping', Ping);
|
||||
* </pre>
|
||||
* You would then inject and use this service like this:
|
||||
* <pre>
|
||||
* someModule.controller 'Ctrl', ['ping', (ping) ->
|
||||
* ping.send()
|
||||
* ]
|
||||
* someModule.controller('Ctrl', ['ping', function(ping) {
|
||||
* ping.send();
|
||||
* }]);
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
|
|
@ -740,6 +742,11 @@ function createInjector(modulesToLoad) {
|
|||
path.unshift(serviceName);
|
||||
cache[serviceName] = INSTANTIATING;
|
||||
return cache[serviceName] = factory(serviceName);
|
||||
} catch (err) {
|
||||
if (cache[serviceName] === INSTANTIATING) {
|
||||
delete cache[serviceName];
|
||||
}
|
||||
throw err;
|
||||
} finally {
|
||||
path.shift();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,6 +175,9 @@ function JQLite(element) {
|
|||
if (element instanceof JQLite) {
|
||||
return element;
|
||||
}
|
||||
if (isString(element)) {
|
||||
element = trim(element);
|
||||
}
|
||||
if (!(this instanceof JQLite)) {
|
||||
if (isString(element) && element.charAt(0) != '<') {
|
||||
throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
|
||||
|
|
|
|||
|
|
@ -148,8 +148,9 @@ function Browser(window, document, $log, $sniffer) {
|
|||
* @param {boolean=} replace Should new url replace current history record ?
|
||||
*/
|
||||
self.url = function(url, replace) {
|
||||
// Android Browser BFCache causes location reference to become stale.
|
||||
// Android Browser BFCache causes location, history reference to become stale.
|
||||
if (location !== window.location) location = window.location;
|
||||
if (history !== window.history) history = window.history;
|
||||
|
||||
// setter
|
||||
if (url) {
|
||||
|
|
@ -201,7 +202,7 @@ function Browser(window, document, $log, $sniffer) {
|
|||
* @description
|
||||
* Register callback function that will be called, when url changes.
|
||||
*
|
||||
* It's only called when the url is changed by outside of angular:
|
||||
* It's only called when the url is changed from outside of angular:
|
||||
* - user types different url into address bar
|
||||
* - user clicks on history (forward/back) button
|
||||
* - user clicks on a link
|
||||
|
|
@ -243,7 +244,7 @@ function Browser(window, document, $log, $sniffer) {
|
|||
/**
|
||||
* @name ng.$browser#baseHref
|
||||
* @methodOf ng.$browser
|
||||
*
|
||||
*
|
||||
* @description
|
||||
* Returns current <base href>
|
||||
* (always relative - without domain)
|
||||
|
|
@ -252,7 +253,7 @@ function Browser(window, document, $log, $sniffer) {
|
|||
*/
|
||||
self.baseHref = function() {
|
||||
var href = baseElement.attr('href');
|
||||
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
|
||||
return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
|
@ -274,13 +275,13 @@ function Browser(window, document, $log, $sniffer) {
|
|||
* It is not meant to be used directly, use the $cookie service instead.
|
||||
*
|
||||
* The return values vary depending on the arguments that the method was called with as follows:
|
||||
*
|
||||
*
|
||||
* - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
|
||||
* it
|
||||
* - cookies(name, value) -> set name to value, if value is undefined delete the cookie
|
||||
* - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
|
||||
* way)
|
||||
*
|
||||
*
|
||||
* @returns {Object} Hash of all cookies (if called without any parameter)
|
||||
*/
|
||||
self.cookies = function(name, value) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
* @function
|
||||
*
|
||||
* @description
|
||||
* Compiles a piece of HTML string or DOM into a template and produces a template function, which
|
||||
* Compiles an HTML string or DOM into a template and produces a template function, which
|
||||
* can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
|
||||
*
|
||||
* The compilation is a process of walking the DOM tree and matching DOM elements to
|
||||
|
|
@ -424,13 +424,17 @@
|
|||
<div compile="html"></div>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should auto compile', function() {
|
||||
expect(element('div[compile]').text()).toBe('Hello Angular');
|
||||
input('html').enter('{{name}}!');
|
||||
expect(element('div[compile]').text()).toBe('Angular!');
|
||||
var textarea = $('textarea');
|
||||
var output = $('div[compile]');
|
||||
// The initial state reads 'Hello Angular'.
|
||||
expect(output.getText()).toBe('Hello Angular');
|
||||
textarea.clear();
|
||||
textarea.sendKeys('{{name}}!');
|
||||
expect(output.getText()).toBe('Angular!');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
|
||||
*
|
||||
|
|
@ -1192,7 +1196,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|||
hasTranscludeDirective = true;
|
||||
|
||||
// Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
|
||||
// This option should only be used by directives that know how to how to safely handle element transclusion,
|
||||
// This option should only be used by directives that know how to safely handle element transclusion,
|
||||
// where the transcluded nodes are added or replaced after linking.
|
||||
if (!directive.$$tlb) {
|
||||
assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
|
||||
|
|
@ -1707,9 +1711,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|||
linkNode = $compileNode[0];
|
||||
|
||||
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
|
||||
var oldClasses = beforeTemplateLinkNode.className;
|
||||
// it was cloned therefore we have to clone as well.
|
||||
linkNode = jqLiteClone(compileNode);
|
||||
replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
|
||||
|
||||
// Copy in CSS classes from original node
|
||||
safeAddClass(jqLite(linkNode), oldClasses);
|
||||
}
|
||||
if (afterTemplateNodeLinkFn.transclude) {
|
||||
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude);
|
||||
|
|
@ -1957,7 +1965,7 @@ function directiveNormalize(name) {
|
|||
*
|
||||
*
|
||||
* @param {string} name Normalized element attribute name of the property to modify. The name is
|
||||
* revers translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
|
||||
* reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
|
||||
* property to the original name.
|
||||
* @param {string} value Value to set the attribute to. The value can be an interpolated string.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -32,11 +32,14 @@ var htmlAnchorDirective = valueFn({
|
|||
element.append(document.createComment('IE fix'));
|
||||
}
|
||||
|
||||
if (!attr.href && !attr.name) {
|
||||
if (!attr.href && !attr.xlinkHref && !attr.name) {
|
||||
return function(scope, element) {
|
||||
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
|
||||
var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
|
||||
'xlink:href' : 'href';
|
||||
element.on('click', function(event){
|
||||
// if we have no href url, then don't navigate anywhere.
|
||||
if (!element.attr('href')) {
|
||||
if (!element.attr(href)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -41,46 +41,48 @@
|
|||
<a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
|
||||
<a id="link-6" ng-href="{{value}}">link</a> (link, change location)
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should execute ng-click but not reload when href without value', function() {
|
||||
element('#link-1').click();
|
||||
expect(input('value').val()).toEqual('1');
|
||||
expect(element('#link-1').attr('href')).toBe("");
|
||||
element(by.id('link-1')).click();
|
||||
expect(element(by.model('value')).getAttribute('value')).toEqual('1');
|
||||
expect(element(by.id('link-1')).getAttribute('href')).toBe('');
|
||||
});
|
||||
|
||||
it('should execute ng-click but not reload when href empty string', function() {
|
||||
element('#link-2').click();
|
||||
expect(input('value').val()).toEqual('2');
|
||||
expect(element('#link-2').attr('href')).toBe("");
|
||||
element(by.id('link-2')).click();
|
||||
expect(element(by.model('value')).getAttribute('value')).toEqual('2');
|
||||
expect(element(by.id('link-2')).getAttribute('href')).toBe('');
|
||||
});
|
||||
|
||||
it('should execute ng-click and change url when ng-href specified', function() {
|
||||
expect(element('#link-3').attr('href')).toBe("/123");
|
||||
expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
|
||||
|
||||
element('#link-3').click();
|
||||
expect(browser().window().path()).toEqual('/123');
|
||||
element(by.id('link-3')).click();
|
||||
|
||||
expect(browser.driver.getCurrentUrl()).toMatch(/\/123$/);
|
||||
});
|
||||
|
||||
it('should execute ng-click but not reload when href empty string and name specified', function() {
|
||||
element('#link-4').click();
|
||||
expect(input('value').val()).toEqual('4');
|
||||
expect(element('#link-4').attr('href')).toBe('');
|
||||
element(by.id('link-4')).click();
|
||||
expect(element(by.model('value')).getAttribute('value')).toEqual('4');
|
||||
expect(element(by.id('link-4')).getAttribute('href')).toBe('');
|
||||
});
|
||||
|
||||
it('should execute ng-click but not reload when no href but name specified', function() {
|
||||
element('#link-5').click();
|
||||
expect(input('value').val()).toEqual('5');
|
||||
expect(element('#link-5').attr('href')).toBe(undefined);
|
||||
element(by.id('link-5')).click();
|
||||
expect(element(by.model('value')).getAttribute('value')).toEqual('5');
|
||||
expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
|
||||
});
|
||||
|
||||
it('should only change url when only ng-href', function() {
|
||||
input('value').enter('6');
|
||||
expect(element('#link-6').attr('href')).toBe('6');
|
||||
element(by.model('value')).clear();
|
||||
element(by.model('value')).sendKeys('6');
|
||||
expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
|
||||
|
||||
element('#link-6').click();
|
||||
expect(browser().location().url()).toEqual('/6');
|
||||
element(by.id('link-6')).click();
|
||||
expect(browser.getCurrentUrl()).toMatch(/\/6$/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
|
|
@ -165,13 +167,13 @@
|
|||
Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
|
||||
<button ng-model="button" ng-disabled="checked">Button</button>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should toggle button', function() {
|
||||
expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy();
|
||||
input('checked').check();
|
||||
expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy();
|
||||
expect(element(by.css('.doc-example-live button')).getAttribute('disabled')).toBeFalsy();
|
||||
element(by.model('checked')).click();
|
||||
expect(element(by.css('.doc-example-live button')).getAttribute('disabled')).toBeTruthy();
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*
|
||||
* @element INPUT
|
||||
|
|
@ -200,13 +202,13 @@
|
|||
Check me to check both: <input type="checkbox" ng-model="master"><br/>
|
||||
<input id="checkSlave" type="checkbox" ng-checked="master">
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should check both checkBoxes', function() {
|
||||
expect(element('.doc-example-live #checkSlave').prop('checked')).toBeFalsy();
|
||||
input('master').check();
|
||||
expect(element('.doc-example-live #checkSlave').prop('checked')).toBeTruthy();
|
||||
expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
|
||||
element(by.model('master')).click();
|
||||
expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*
|
||||
* @element INPUT
|
||||
|
|
@ -235,13 +237,13 @@
|
|||
Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
|
||||
<input type="text" ng-readonly="checked" value="I'm Angular"/>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should toggle readonly attr', function() {
|
||||
expect(element('.doc-example-live :text').prop('readonly')).toBeFalsy();
|
||||
input('checked').check();
|
||||
expect(element('.doc-example-live :text').prop('readonly')).toBeTruthy();
|
||||
expect(element(by.css('.doc-example-live [type="text"]')).getAttribute('readonly')).toBeFalsy();
|
||||
element(by.model('checked')).click();
|
||||
expect(element(by.css('.doc-example-live [type="text"]')).getAttribute('readonly')).toBeTruthy();
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*
|
||||
* @element INPUT
|
||||
|
|
@ -274,13 +276,13 @@
|
|||
<option id="greet" ng-selected="selected">Greetings!</option>
|
||||
</select>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should select Greetings!', function() {
|
||||
expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy();
|
||||
input('selected').check();
|
||||
expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy();
|
||||
expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
|
||||
element(by.model('selected')).click();
|
||||
expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*
|
||||
* @element OPTION
|
||||
|
|
@ -310,13 +312,13 @@
|
|||
<summary>Show/Hide me</summary>
|
||||
</details>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should toggle open', function() {
|
||||
expect(element('#details').prop('open')).toBeFalsy();
|
||||
input('open').check();
|
||||
expect(element('#details').prop('open')).toBeTruthy();
|
||||
expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
|
||||
element(by.model('open')).click();
|
||||
expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*
|
||||
* @element DETAILS
|
||||
|
|
@ -336,12 +338,10 @@ forEach(BOOLEAN_ATTR, function(propName, attrName) {
|
|||
ngAttributeAliasDirectives[normalized] = function() {
|
||||
return {
|
||||
priority: 100,
|
||||
compile: function() {
|
||||
return function(scope, element, attr) {
|
||||
scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
|
||||
attr.$set(attrName, !!value);
|
||||
});
|
||||
};
|
||||
link: function(scope, element, attr) {
|
||||
scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
|
||||
attr.$set(attrName, !!value);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -249,10 +249,10 @@ function FormController(element, attrs) {
|
|||
*
|
||||
*
|
||||
* # CSS classes
|
||||
* - `ng-valid` Is set if the form is valid.
|
||||
* - `ng-invalid` Is set if the form is invalid.
|
||||
* - `ng-pristine` Is set if the form is pristine.
|
||||
* - `ng-dirty` Is set if the form is dirty.
|
||||
* - `ng-valid` is set if the form is valid.
|
||||
* - `ng-invalid` is set if the form is invalid.
|
||||
* - `ng-pristine` is set if the form is pristine.
|
||||
* - `ng-dirty` is set if the form is dirty.
|
||||
*
|
||||
*
|
||||
* # Submitting a form and preventing the default action
|
||||
|
|
@ -305,18 +305,27 @@ function FormController(element, attrs) {
|
|||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should initialize to model', function() {
|
||||
expect(binding('userType')).toEqual('guest');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
var userType = element(by.binding('userType'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
|
||||
expect(userType.getText()).toContain('guest');
|
||||
expect(valid.getText()).toContain('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
input('userType').enter('');
|
||||
expect(binding('userType')).toEqual('');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
var userType = element(by.binding('userType'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var userInput = element(by.model('userType'));
|
||||
|
||||
userInput.clear();
|
||||
userInput.sendKeys('');
|
||||
|
||||
expect(userType.getText()).toEqual('userType =');
|
||||
expect(valid.getText()).toContain('false');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
var formDirectiveFactory = function(isNgForm) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
*/
|
||||
|
||||
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
|
||||
var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$/;
|
||||
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i;
|
||||
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
|
||||
|
||||
var inputType = {
|
||||
|
|
@ -62,29 +62,31 @@ var inputType = {
|
|||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
var text = element(by.binding('text'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('text'));
|
||||
|
||||
it('should initialize to model', function() {
|
||||
expect(binding('text')).toEqual('guest');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
expect(text.getText()).toContain('guest');
|
||||
expect(valid.getText()).toContain('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
input('text').enter('');
|
||||
expect(binding('text')).toEqual('');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
input.clear();
|
||||
input.sendKeys('');
|
||||
|
||||
expect(text.getText()).toEqual('text =');
|
||||
expect(valid.getText()).toContain('false');
|
||||
});
|
||||
|
||||
it('should be invalid if multi word', function() {
|
||||
input('text').enter('hello world');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
});
|
||||
input.clear();
|
||||
input.sendKeys('hello world');
|
||||
|
||||
it('should not be trimmed', function() {
|
||||
input('text').enter('untrimmed ');
|
||||
expect(binding('text')).toEqual('untrimmed ');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
expect(valid.getText()).toContain('false');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
'text': textInputType,
|
||||
|
|
@ -138,24 +140,30 @@ var inputType = {
|
|||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
var value = element(by.binding('value'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('value'));
|
||||
|
||||
it('should initialize to model', function() {
|
||||
expect(binding('value')).toEqual('12');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
expect(value.getText()).toContain('12');
|
||||
expect(valid.getText()).toContain('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
input('value').enter('');
|
||||
expect(binding('value')).toEqual('');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
input.clear();
|
||||
input.sendKeys('');
|
||||
expect(value.getText()).toEqual('value =');
|
||||
expect(valid.getText()).toContain('false');
|
||||
});
|
||||
|
||||
it('should be invalid if over max', function() {
|
||||
input('value').enter('123');
|
||||
expect(binding('value')).toEqual('');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
input.clear();
|
||||
input.sendKeys('123');
|
||||
expect(value.getText()).toEqual('value =');
|
||||
expect(valid.getText()).toContain('false');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
'number': numberInputType,
|
||||
|
|
@ -207,23 +215,31 @@ var inputType = {
|
|||
<tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
var text = element(by.binding('text'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('text'));
|
||||
|
||||
it('should initialize to model', function() {
|
||||
expect(binding('text')).toEqual('http://google.com');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
expect(text.getText()).toContain('http://google.com');
|
||||
expect(valid.getText()).toContain('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
input('text').enter('');
|
||||
expect(binding('text')).toEqual('');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
input.clear();
|
||||
input.sendKeys('');
|
||||
|
||||
expect(text.getText()).toEqual('text =');
|
||||
expect(valid.getText()).toContain('false');
|
||||
});
|
||||
|
||||
it('should be invalid if not url', function() {
|
||||
input('text').enter('xxx');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
input.clear();
|
||||
input.sendKeys('box');
|
||||
|
||||
expect(valid.getText()).toContain('false');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
'url': urlInputType,
|
||||
|
|
@ -275,23 +291,30 @@ var inputType = {
|
|||
<tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
var text = element(by.binding('text'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('text'));
|
||||
|
||||
it('should initialize to model', function() {
|
||||
expect(binding('text')).toEqual('me@example.com');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
expect(text.getText()).toContain('me@example.com');
|
||||
expect(valid.getText()).toContain('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
input('text').enter('');
|
||||
expect(binding('text')).toEqual('');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
input.clear();
|
||||
input.sendKeys('');
|
||||
expect(text.getText()).toEqual('text =');
|
||||
expect(valid.getText()).toContain('false');
|
||||
});
|
||||
|
||||
it('should be invalid if not email', function() {
|
||||
input('text').enter('xxx');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
input.clear();
|
||||
input.sendKeys('xxx');
|
||||
|
||||
expect(valid.getText()).toContain('false');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
'email': emailInputType,
|
||||
|
|
@ -309,6 +332,8 @@ var inputType = {
|
|||
* @param {string=} name Property name of the form under which the control is published.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
* @param {string} ngValue Angular expression which sets the value to which the expression should
|
||||
* be set when selected.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
|
|
@ -316,23 +341,31 @@ var inputType = {
|
|||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.color = 'blue';
|
||||
$scope.specialValue = {
|
||||
"id": "12345",
|
||||
"value": "green"
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<form name="myForm" ng-controller="Ctrl">
|
||||
<input type="radio" ng-model="color" value="red"> Red <br/>
|
||||
<input type="radio" ng-model="color" value="green"> Green <br/>
|
||||
<input type="radio" ng-model="color" ng-value="specialValue"> Green <br/>
|
||||
<input type="radio" ng-model="color" value="blue"> Blue <br/>
|
||||
<tt>color = {{color}}</tt><br/>
|
||||
<tt>color = {{color | json}}</tt><br/>
|
||||
</form>
|
||||
Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should change state', function() {
|
||||
expect(binding('color')).toEqual('blue');
|
||||
var color = element(by.binding('color'));
|
||||
|
||||
input('color').select('red');
|
||||
expect(binding('color')).toEqual('red');
|
||||
expect(color.getText()).toContain('blue');
|
||||
|
||||
element.all(by.model('color')).get(0).click();
|
||||
|
||||
expect(color.getText()).toContain('red');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
'radio': radioInputType,
|
||||
|
|
@ -369,17 +402,21 @@ var inputType = {
|
|||
<tt>value2 = {{value2}}</tt><br/>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should change state', function() {
|
||||
expect(binding('value1')).toEqual('true');
|
||||
expect(binding('value2')).toEqual('YES');
|
||||
var value1 = element(by.binding('value1'));
|
||||
var value2 = element(by.binding('value2'));
|
||||
|
||||
input('value1').check();
|
||||
input('value2').check();
|
||||
expect(binding('value1')).toEqual('false');
|
||||
expect(binding('value2')).toEqual('NO');
|
||||
expect(value1.getText()).toContain('true');
|
||||
expect(value2.getText()).toContain('YES');
|
||||
|
||||
element(by.model('value1')).click();
|
||||
element(by.model('value2')).click();
|
||||
|
||||
expect(value1.getText()).toContain('false');
|
||||
expect(value2.getText()).toContain('NO');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
'checkbox': checkboxInputType,
|
||||
|
|
@ -390,6 +427,12 @@ var inputType = {
|
|||
'reset': noop
|
||||
};
|
||||
|
||||
// A helper function to call $setValidity and return the value / undefined,
|
||||
// a pattern that is repeated a lot in the input validation logic.
|
||||
function validate(ctrl, validatorName, validity, value){
|
||||
ctrl.$setValidity(validatorName, validity);
|
||||
return validity ? value : undefined;
|
||||
}
|
||||
|
||||
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
// In composition mode, users are still inputing intermediate text buffer,
|
||||
|
|
@ -419,9 +462,13 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
}
|
||||
|
||||
if (ctrl.$viewValue !== value) {
|
||||
scope.$apply(function() {
|
||||
if (scope.$$phase) {
|
||||
ctrl.$setViewValue(value);
|
||||
});
|
||||
} else {
|
||||
scope.$apply(function() {
|
||||
ctrl.$setViewValue(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -470,22 +517,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
patternValidator,
|
||||
match;
|
||||
|
||||
var validate = function(regexp, value) {
|
||||
if (ctrl.$isEmpty(value) || regexp.test(value)) {
|
||||
ctrl.$setValidity('pattern', true);
|
||||
return value;
|
||||
} else {
|
||||
ctrl.$setValidity('pattern', false);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
if (pattern) {
|
||||
var validateRegex = function(regexp, value) {
|
||||
return validate(ctrl, 'pattern', ctrl.$isEmpty(value) || regexp.test(value), value);
|
||||
};
|
||||
match = pattern.match(/^\/(.*)\/([gim]*)$/);
|
||||
if (match) {
|
||||
pattern = new RegExp(match[1], match[2]);
|
||||
patternValidator = function(value) {
|
||||
return validate(pattern, value);
|
||||
return validateRegex(pattern, value);
|
||||
};
|
||||
} else {
|
||||
patternValidator = function(value) {
|
||||
|
|
@ -496,7 +536,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
'Expected {0} to be a RegExp but was {1}. Element: {2}', pattern,
|
||||
patternObj, startingTag(element));
|
||||
}
|
||||
return validate(patternObj, value);
|
||||
return validateRegex(patternObj, value);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -508,13 +548,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
if (attr.ngMinlength) {
|
||||
var minlength = int(attr.ngMinlength);
|
||||
var minLengthValidator = function(value) {
|
||||
if (!ctrl.$isEmpty(value) && value.length < minlength) {
|
||||
ctrl.$setValidity('minlength', false);
|
||||
return undefined;
|
||||
} else {
|
||||
ctrl.$setValidity('minlength', true);
|
||||
return value;
|
||||
}
|
||||
return validate(ctrl, 'minlength', ctrl.$isEmpty(value) || value.length >= minlength, value);
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(minLengthValidator);
|
||||
|
|
@ -525,13 +559,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
if (attr.ngMaxlength) {
|
||||
var maxlength = int(attr.ngMaxlength);
|
||||
var maxLengthValidator = function(value) {
|
||||
if (!ctrl.$isEmpty(value) && value.length > maxlength) {
|
||||
ctrl.$setValidity('maxlength', false);
|
||||
return undefined;
|
||||
} else {
|
||||
ctrl.$setValidity('maxlength', true);
|
||||
return value;
|
||||
}
|
||||
return validate(ctrl, 'maxlength', ctrl.$isEmpty(value) || value.length <= maxlength, value);
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(maxLengthValidator);
|
||||
|
|
@ -560,13 +588,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
if (attr.min) {
|
||||
var minValidator = function(value) {
|
||||
var min = parseFloat(attr.min);
|
||||
if (!ctrl.$isEmpty(value) && value < min) {
|
||||
ctrl.$setValidity('min', false);
|
||||
return undefined;
|
||||
} else {
|
||||
ctrl.$setValidity('min', true);
|
||||
return value;
|
||||
}
|
||||
return validate(ctrl, 'min', ctrl.$isEmpty(value) || value >= min, value);
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(minValidator);
|
||||
|
|
@ -576,13 +598,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
if (attr.max) {
|
||||
var maxValidator = function(value) {
|
||||
var max = parseFloat(attr.max);
|
||||
if (!ctrl.$isEmpty(value) && value > max) {
|
||||
ctrl.$setValidity('max', false);
|
||||
return undefined;
|
||||
} else {
|
||||
ctrl.$setValidity('max', true);
|
||||
return value;
|
||||
}
|
||||
return validate(ctrl, 'max', ctrl.$isEmpty(value) || value <= max, value);
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(maxValidator);
|
||||
|
|
@ -590,14 +606,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
}
|
||||
|
||||
ctrl.$formatters.push(function(value) {
|
||||
|
||||
if (ctrl.$isEmpty(value) || isNumber(value)) {
|
||||
ctrl.$setValidity('number', true);
|
||||
return value;
|
||||
} else {
|
||||
ctrl.$setValidity('number', false);
|
||||
return undefined;
|
||||
}
|
||||
return validate(ctrl, 'number', ctrl.$isEmpty(value) || isNumber(value), value);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -605,13 +614,7 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
||||
|
||||
var urlValidator = function(value) {
|
||||
if (ctrl.$isEmpty(value) || URL_REGEXP.test(value)) {
|
||||
ctrl.$setValidity('url', true);
|
||||
return value;
|
||||
} else {
|
||||
ctrl.$setValidity('url', false);
|
||||
return undefined;
|
||||
}
|
||||
return validate(ctrl, 'url', ctrl.$isEmpty(value) || URL_REGEXP.test(value), value);
|
||||
};
|
||||
|
||||
ctrl.$formatters.push(urlValidator);
|
||||
|
|
@ -622,13 +625,7 @@ function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
||||
|
||||
var emailValidator = function(value) {
|
||||
if (ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value)) {
|
||||
ctrl.$setValidity('email', true);
|
||||
return value;
|
||||
} else {
|
||||
ctrl.$setValidity('email', false);
|
||||
return undefined;
|
||||
}
|
||||
return validate(ctrl, 'email', ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value), value);
|
||||
};
|
||||
|
||||
ctrl.$formatters.push(emailValidator);
|
||||
|
|
@ -772,44 +769,59 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
|||
<tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
var user = element(by.binding('{{user}}'));
|
||||
var userNameValid = element(by.binding('myForm.userName.$valid'));
|
||||
var lastNameValid = element(by.binding('myForm.lastName.$valid'));
|
||||
var lastNameError = element(by.binding('myForm.lastName.$error'));
|
||||
var formValid = element(by.binding('myForm.$valid'));
|
||||
var userNameInput = element(by.model('user.name'));
|
||||
var userLastInput = element(by.model('user.last'));
|
||||
|
||||
it('should initialize to model', function() {
|
||||
expect(binding('user')).toEqual('{"name":"guest","last":"visitor"}');
|
||||
expect(binding('myForm.userName.$valid')).toEqual('true');
|
||||
expect(binding('myForm.$valid')).toEqual('true');
|
||||
expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
|
||||
expect(userNameValid.getText()).toContain('true');
|
||||
expect(formValid.getText()).toContain('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty when required', function() {
|
||||
input('user.name').enter('');
|
||||
expect(binding('user')).toEqual('{"last":"visitor"}');
|
||||
expect(binding('myForm.userName.$valid')).toEqual('false');
|
||||
expect(binding('myForm.$valid')).toEqual('false');
|
||||
userNameInput.clear();
|
||||
userNameInput.sendKeys('');
|
||||
|
||||
expect(user.getText()).toContain('{"last":"visitor"}');
|
||||
expect(userNameValid.getText()).toContain('false');
|
||||
expect(formValid.getText()).toContain('false');
|
||||
});
|
||||
|
||||
it('should be valid if empty when min length is set', function() {
|
||||
input('user.last').enter('');
|
||||
expect(binding('user')).toEqual('{"name":"guest","last":""}');
|
||||
expect(binding('myForm.lastName.$valid')).toEqual('true');
|
||||
expect(binding('myForm.$valid')).toEqual('true');
|
||||
userLastInput.clear();
|
||||
userLastInput.sendKeys('');
|
||||
|
||||
expect(user.getText()).toContain('{"name":"guest","last":""}');
|
||||
expect(lastNameValid.getText()).toContain('true');
|
||||
expect(formValid.getText()).toContain('true');
|
||||
});
|
||||
|
||||
it('should be invalid if less than required min length', function() {
|
||||
input('user.last').enter('xx');
|
||||
expect(binding('user')).toEqual('{"name":"guest"}');
|
||||
expect(binding('myForm.lastName.$valid')).toEqual('false');
|
||||
expect(binding('myForm.lastName.$error')).toMatch(/minlength/);
|
||||
expect(binding('myForm.$valid')).toEqual('false');
|
||||
userLastInput.clear();
|
||||
userLastInput.sendKeys('xx');
|
||||
|
||||
expect(user.getText()).toContain('{"name":"guest"}');
|
||||
expect(lastNameValid.getText()).toContain('false');
|
||||
expect(lastNameError.getText()).toContain('minlength');
|
||||
expect(formValid.getText()).toContain('false');
|
||||
});
|
||||
|
||||
it('should be invalid if longer than max length', function() {
|
||||
input('user.last').enter('some ridiculously long name');
|
||||
expect(binding('user'))
|
||||
.toEqual('{"name":"guest"}');
|
||||
expect(binding('myForm.lastName.$valid')).toEqual('false');
|
||||
expect(binding('myForm.lastName.$error')).toMatch(/maxlength/);
|
||||
expect(binding('myForm.$valid')).toEqual('false');
|
||||
userLastInput.clear();
|
||||
userLastInput.sendKeys('some ridiculously long name');
|
||||
|
||||
expect(user.getText()).toContain('{"name":"guest"}');
|
||||
expect(lastNameValid.getText()).toContain('false');
|
||||
expect(lastNameError.getText()).toContain('maxlength');
|
||||
expect(formValid.getText()).toContain('false');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) {
|
||||
|
|
@ -941,14 +953,17 @@ var VALID_CLASS = 'ng-valid',
|
|||
<textarea ng-model="userContent"></textarea>
|
||||
</form>
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
<file name="protractorTest.js">
|
||||
it('should data-bind and become invalid', function() {
|
||||
var contentEditable = element('[contenteditable]');
|
||||
var contentEditable = element(by.css('.doc-example-live [contenteditable]'));
|
||||
|
||||
expect(contentEditable.text()).toEqual('Change me!');
|
||||
input('userContent').enter('');
|
||||
expect(contentEditable.text()).toEqual('');
|
||||
expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/);
|
||||
expect(contentEditable.getText()).toEqual('Change me!');
|
||||
|
||||
contentEditable.clear();
|
||||
contentEditable.sendKeys(protractor.Key.BACK_SPACE);
|
||||
|
||||
expect(contentEditable.getText()).toEqual('');
|
||||
expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
|
||||
});
|
||||
</file>
|
||||
* </example>
|
||||
|
|
@ -1228,7 +1243,10 @@ var ngModelDirective = function() {
|
|||
* @name ng.directive:ngChange
|
||||
*
|
||||
* @description
|
||||
* Evaluate given expression when user changes the input.
|
||||
* Evaluate the given expression when the user changes the input.
|
||||
* The expression is evaluated immediately, unlike the JavaScript onchange event
|
||||
* which only triggers at the end of a change (usually, when the user leaves the
|
||||
* form element or presses the return key).
|
||||
* The expression is not evaluated when the value change is coming from the model.
|
||||
*
|
||||
* Note, this directive requires `ngModel` to be present.
|
||||
|
|
@ -1252,24 +1270,30 @@ var ngModelDirective = function() {
|
|||
* <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
|
||||
* <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
|
||||
* <label for="ng-change-example2">Confirmed</label><br />
|
||||
* debug = {{confirmed}}<br />
|
||||
* counter = {{counter}}
|
||||
* <tt>debug = {{confirmed}}</tt><br/>
|
||||
* <tt>counter = {{counter}}</tt><br/>
|
||||
* </div>
|
||||
* </doc:source>
|
||||
* <doc:scenario>
|
||||
* <doc:protractor>
|
||||
* var counter = element(by.binding('counter'));
|
||||
* var debug = element(by.binding('confirmed'));
|
||||
*
|
||||
* it('should evaluate the expression if changing from view', function() {
|
||||
* expect(binding('counter')).toEqual('0');
|
||||
* element('#ng-change-example1').click();
|
||||
* expect(binding('counter')).toEqual('1');
|
||||
* expect(binding('confirmed')).toEqual('true');
|
||||
* expect(counter.getText()).toContain('0');
|
||||
*
|
||||
* element(by.id('ng-change-example1')).click();
|
||||
*
|
||||
* expect(counter.getText()).toContain('1');
|
||||
* expect(debug.getText()).toContain('true');
|
||||
* });
|
||||
*
|
||||
* it('should not evaluate the expression if changing from model', function() {
|
||||
* element('#ng-change-example2').click();
|
||||
* expect(binding('counter')).toEqual('0');
|
||||
* expect(binding('confirmed')).toEqual('true');
|
||||
* element(by.id('ng-change-example2')).click();
|
||||
|
||||
* expect(counter.getText()).toContain('0');
|
||||
* expect(debug.getText()).toContain('true');
|
||||
* });
|
||||
* </doc:scenario>
|
||||
* </doc:protractor>
|
||||
* </doc:example>
|
||||
*/
|
||||
var ngChangeDirective = valueFn({
|
||||
|
|
@ -1342,20 +1366,26 @@ var requiredDirective = function() {
|
|||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
var listInput = element(by.model('names'));
|
||||
var names = element(by.binding('{{names}}'));
|
||||
var valid = element(by.binding('myForm.namesInput.$valid'));
|
||||
var error = element(by.css('span.error'));
|
||||
|
||||
it('should initialize to model', function() {
|
||||
expect(binding('names')).toEqual('["igor","misko","vojta"]');
|
||||
expect(binding('myForm.namesInput.$valid')).toEqual('true');
|
||||
expect(element('span.error').css('display')).toBe('none');
|
||||
expect(names.getText()).toContain('["igor","misko","vojta"]');
|
||||
expect(valid.getText()).toContain('true');
|
||||
expect(error.getCssValue('display')).toBe('none');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
input('names').enter('');
|
||||
expect(binding('names')).toEqual('');
|
||||
expect(binding('myForm.namesInput.$valid')).toEqual('false');
|
||||
expect(element('span.error').css('display')).not().toBe('none');
|
||||
});
|
||||
</doc:scenario>
|
||||
listInput.clear();
|
||||
listInput.sendKeys('');
|
||||
|
||||
expect(names.getText()).toContain('');
|
||||
expect(valid.getText()).toContain('false');
|
||||
expect(error.getCssValue('display')).not.toBe('none'); });
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngListDirective = function() {
|
||||
|
|
@ -1437,15 +1467,17 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
|
|||
<div>You chose {{my.favorite}}</div>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
var favorite = element(by.binding('my.favorite'));
|
||||
|
||||
it('should initialize to model', function() {
|
||||
expect(binding('my.favorite')).toEqual('unicorns');
|
||||
expect(favorite.getText()).toContain('unicorns');
|
||||
});
|
||||
it('should bind the values to the inputs', function() {
|
||||
input('my.favorite').select('pizza');
|
||||
expect(binding('my.favorite')).toEqual('pizza');
|
||||
element.all(by.model('my.favorite')).get(0).click();
|
||||
expect(favorite.getText()).toContain('pizza');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngValueDirective = function() {
|
||||
|
|
|
|||
|
|
@ -38,13 +38,17 @@
|
|||
Hello <span ng-bind="name"></span>!
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should check ng-bind', function() {
|
||||
expect(using('.doc-example-live').binding('name')).toBe('Whirled');
|
||||
using('.doc-example-live').input('name').enter('world');
|
||||
expect(using('.doc-example-live').binding('name')).toBe('world');
|
||||
var exampleContainer = $('.doc-example-live');
|
||||
var nameInput = element(by.model('name'));
|
||||
|
||||
expect(exampleContainer.findElement(by.binding('name')).getText()).toBe('Whirled');
|
||||
nameInput.clear();
|
||||
nameInput.sendKeys('world');
|
||||
expect(exampleContainer.findElement(by.binding('name')).getText()).toBe('world');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngBindDirective = ngDirective(function(scope, element, attr) {
|
||||
|
|
@ -90,20 +94,22 @@ var ngBindDirective = ngDirective(function(scope, element, attr) {
|
|||
<pre ng-bind-template="{{salutation}} {{name}}!"></pre>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should check ng-bind', function() {
|
||||
expect(using('.doc-example-live').binding('salutation')).
|
||||
toBe('Hello');
|
||||
expect(using('.doc-example-live').binding('name')).
|
||||
toBe('World');
|
||||
using('.doc-example-live').input('salutation').enter('Greetings');
|
||||
using('.doc-example-live').input('name').enter('user');
|
||||
expect(using('.doc-example-live').binding('salutation')).
|
||||
toBe('Greetings');
|
||||
expect(using('.doc-example-live').binding('name')).
|
||||
toBe('user');
|
||||
var salutationElem = element(by.binding('salutation'));
|
||||
var salutationInput = element(by.model('salutation'));
|
||||
var nameInput = element(by.model('name'));
|
||||
|
||||
expect(salutationElem.getText()).toBe('Hello World!');
|
||||
|
||||
salutationInput.clear();
|
||||
salutationInput.sendKeys('Greetings');
|
||||
nameInput.clear();
|
||||
nameInput.sendKeys('user');
|
||||
|
||||
expect(salutationElem.getText()).toBe('Greetings user!');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
|
||||
|
|
@ -156,12 +162,10 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
|
|||
}]);
|
||||
</file>
|
||||
|
||||
<file name="scenario.js">
|
||||
<file name="protractorTest.js">
|
||||
it('should check ng-bind-html', function() {
|
||||
expect(using('.doc-example-live').binding('myHTML')).
|
||||
toBe(
|
||||
'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>'
|
||||
);
|
||||
expect(element(by.binding('myHTML')).getText()).toBe(
|
||||
'I am an HTMLstring with links! and other stuff');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
|
|
|||
|
|
@ -114,31 +114,34 @@ function classDirective(name, selector) {
|
|||
color: red;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
<file name="protractorTest.js">
|
||||
var ps = element.all(by.css('.doc-example-live p'));
|
||||
|
||||
it('should let you toggle the class', function() {
|
||||
|
||||
expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/bold/);
|
||||
expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/red/);
|
||||
expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
|
||||
expect(ps.first().getAttribute('class')).not.toMatch(/red/);
|
||||
|
||||
input('important').check();
|
||||
expect(element('.doc-example-live p:first').prop('className')).toMatch(/bold/);
|
||||
element(by.model('important')).click();
|
||||
expect(ps.first().getAttribute('class')).toMatch(/bold/);
|
||||
|
||||
input('error').check();
|
||||
expect(element('.doc-example-live p:first').prop('className')).toMatch(/red/);
|
||||
element(by.model('error')).click();
|
||||
expect(ps.first().getAttribute('class')).toMatch(/red/);
|
||||
});
|
||||
|
||||
it('should let you toggle string example', function() {
|
||||
expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe('');
|
||||
input('style').enter('red');
|
||||
expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe('red');
|
||||
expect(ps.get(1).getAttribute('class')).toBe('');
|
||||
element(by.model('style')).clear();
|
||||
element(by.model('style')).sendKeys('red');
|
||||
expect(ps.get(1).getAttribute('class')).toBe('red');
|
||||
});
|
||||
|
||||
it('array example should have 3 classes', function() {
|
||||
expect(element('.doc-example-live p:last').prop('className')).toBe('');
|
||||
input('style1').enter('bold');
|
||||
input('style2').enter('strike');
|
||||
input('style3').enter('red');
|
||||
expect(element('.doc-example-live p:last').prop('className')).toBe('bold strike red');
|
||||
expect(ps.last().getAttribute('class')).toBe('');
|
||||
element(by.model('style1')).sendKeys('bold');
|
||||
element(by.model('style2')).sendKeys('strike');
|
||||
element(by.model('style3')).sendKeys('red');
|
||||
expect(ps.last().getAttribute('class')).toBe('bold strike red');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
|
@ -149,8 +152,8 @@ function classDirective(name, selector) {
|
|||
|
||||
<example animations="true">
|
||||
<file name="index.html">
|
||||
<input type="button" value="set" ng-click="myVar='my-class'">
|
||||
<input type="button" value="clear" ng-click="myVar=''">
|
||||
<input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
|
||||
<input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
|
||||
<br>
|
||||
<span class="base-class" ng-class="myVar">Sample Text</span>
|
||||
</file>
|
||||
|
|
@ -165,19 +168,19 @@ function classDirective(name, selector) {
|
|||
font-size:3em;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
<file name="protractorTest.js">
|
||||
it('should check ng-class', function() {
|
||||
expect(element('.doc-example-live span').prop('className')).not().
|
||||
expect(element(by.css('.base-class')).getAttribute('class')).not.
|
||||
toMatch(/my-class/);
|
||||
|
||||
using('.doc-example-live').element(':button:first').click();
|
||||
element(by.id('setbtn')).click();
|
||||
|
||||
expect(element('.doc-example-live span').prop('className')).
|
||||
expect(element(by.css('.base-class')).getAttribute('class')).
|
||||
toMatch(/my-class/);
|
||||
|
||||
using('.doc-example-live').element(':button:last').click();
|
||||
element(by.id('clearbtn')).click();
|
||||
|
||||
expect(element('.doc-example-live span').prop('className')).not().
|
||||
expect(element(by.css('.base-class')).getAttribute('class')).not.
|
||||
toMatch(/my-class/);
|
||||
});
|
||||
</file>
|
||||
|
|
@ -229,11 +232,11 @@ var ngClassDirective = classDirective('', true);
|
|||
color: blue;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
<file name="protractorTest.js">
|
||||
it('should check ng-class-odd and ng-class-even', function() {
|
||||
expect(element('.doc-example-live li:first span').prop('className')).
|
||||
expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
|
||||
toMatch(/odd/);
|
||||
expect(element('.doc-example-live li:last span').prop('className')).
|
||||
expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
|
||||
toMatch(/even/);
|
||||
});
|
||||
</file>
|
||||
|
|
@ -277,11 +280,11 @@ var ngClassOddDirective = classDirective('Odd', 0);
|
|||
color: blue;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
<file name="protractorTest.js">
|
||||
it('should check ng-class-odd and ng-class-even', function() {
|
||||
expect(element('.doc-example-live li:first span').prop('className')).
|
||||
expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
|
||||
toMatch(/odd/);
|
||||
expect(element('.doc-example-live li:last span').prop('className')).
|
||||
expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
|
||||
toMatch(/even/);
|
||||
});
|
||||
</file>
|
||||
|
|
|
|||
|
|
@ -45,14 +45,14 @@
|
|||
<div id="template1" ng-cloak>{{ 'hello' }}</div>
|
||||
<div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should remove the template directive and css class', function() {
|
||||
expect(element('.doc-example-live #template1').attr('ng-cloak')).
|
||||
not().toBeDefined();
|
||||
expect(element('.doc-example-live #template2').attr('ng-cloak')).
|
||||
not().toBeDefined();
|
||||
expect($('.doc-example-live #template1').getAttribute('ng-cloak')).
|
||||
toBeNull();
|
||||
expect($('.doc-example-live #template2').getAttribute('ng-cloak')).
|
||||
toBeNull();
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -82,22 +82,36 @@
|
|||
</ul>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should check controller as', function() {
|
||||
expect(element('#ctrl-as-exmpl>:input').val()).toBe('John Smith');
|
||||
expect(element('#ctrl-as-exmpl li:nth-child(1) input').val())
|
||||
.toBe('408 555 1212');
|
||||
expect(element('#ctrl-as-exmpl li:nth-child(2) input').val())
|
||||
.toBe('john.smith@example.org');
|
||||
var container = element(by.id('ctrl-as-exmpl'));
|
||||
|
||||
element('#ctrl-as-exmpl li:first a:contains("clear")').click();
|
||||
expect(element('#ctrl-as-exmpl li:first input').val()).toBe('');
|
||||
expect(container.findElement(by.model('settings.name'))
|
||||
.getAttribute('value')).toBe('John Smith');
|
||||
|
||||
element('#ctrl-as-exmpl li:last a:contains("add")').click();
|
||||
expect(element('#ctrl-as-exmpl li:nth-child(3) input').val())
|
||||
.toBe('yourname@example.org');
|
||||
var firstRepeat =
|
||||
container.findElement(by.repeater('contact in settings.contacts').row(0));
|
||||
var secondRepeat =
|
||||
container.findElement(by.repeater('contact in settings.contacts').row(1));
|
||||
|
||||
expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
|
||||
.toBe('408 555 1212');
|
||||
expect(secondRepeat.findElement(by.model('contact.value')).getAttribute('value'))
|
||||
.toBe('john.smith@example.org');
|
||||
|
||||
firstRepeat.findElement(by.linkText('clear')).click()
|
||||
|
||||
expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
|
||||
.toBe('');
|
||||
|
||||
container.findElement(by.linkText('add')).click();
|
||||
|
||||
expect(container.findElement(by.repeater('contact in settings.contacts').row(2))
|
||||
.findElement(by.model('contact.value'))
|
||||
.getAttribute('value'))
|
||||
.toBe('yourname@example.org');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
|
|
@ -145,22 +159,36 @@
|
|||
</ul>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should check controller', function() {
|
||||
expect(element('#ctrl-exmpl>:input').val()).toBe('John Smith');
|
||||
expect(element('#ctrl-exmpl li:nth-child(1) input').val())
|
||||
.toBe('408 555 1212');
|
||||
expect(element('#ctrl-exmpl li:nth-child(2) input').val())
|
||||
.toBe('john.smith@example.org');
|
||||
var container = element(by.id('ctrl-exmpl'));
|
||||
|
||||
element('#ctrl-exmpl li:first a:contains("clear")').click();
|
||||
expect(element('#ctrl-exmpl li:first input').val()).toBe('');
|
||||
expect(container.findElement(by.model('name'))
|
||||
.getAttribute('value')).toBe('John Smith');
|
||||
|
||||
element('#ctrl-exmpl li:last a:contains("add")').click();
|
||||
expect(element('#ctrl-exmpl li:nth-child(3) input').val())
|
||||
.toBe('yourname@example.org');
|
||||
var firstRepeat =
|
||||
container.findElement(by.repeater('contact in contacts').row(0));
|
||||
var secondRepeat =
|
||||
container.findElement(by.repeater('contact in contacts').row(1));
|
||||
|
||||
expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
|
||||
.toBe('408 555 1212');
|
||||
expect(secondRepeat.findElement(by.model('contact.value')).getAttribute('value'))
|
||||
.toBe('john.smith@example.org');
|
||||
|
||||
firstRepeat.findElement(by.linkText('clear')).click()
|
||||
|
||||
expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
|
||||
.toBe('');
|
||||
|
||||
container.findElement(by.linkText('add')).click();
|
||||
|
||||
expect(container.findElement(by.repeater('contact in contacts').row(2))
|
||||
.findElement(by.model('contact.value'))
|
||||
.getAttribute('value'))
|
||||
.toBe('yourname@example.org');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
* an element is clicked.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
|
||||
* click. (Event object is available as `$event`)
|
||||
*
|
||||
|
|
@ -20,13 +21,13 @@
|
|||
</button>
|
||||
count: {{count}}
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should check ng-click', function() {
|
||||
expect(binding('count')).toBe('0');
|
||||
element('.doc-example-live :button').click();
|
||||
expect(binding('count')).toBe('1');
|
||||
expect(element(by.binding('count')).getText()).toMatch('0');
|
||||
element(by.css('.doc-example-live button')).click();
|
||||
expect(element(by.binding('count')).getText()).toMatch('1');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
/*
|
||||
|
|
@ -65,6 +66,7 @@ forEach(
|
|||
* The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
|
||||
* a dblclick. (The Event object is available as `$event`)
|
||||
*
|
||||
|
|
@ -88,6 +90,7 @@ forEach(
|
|||
* The ngMousedown directive allows you to specify custom behavior on mousedown event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
|
||||
* mousedown. (Event object is available as `$event`)
|
||||
*
|
||||
|
|
@ -111,6 +114,7 @@ forEach(
|
|||
* Specify custom behavior on mouseup event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
|
||||
* mouseup. (Event object is available as `$event`)
|
||||
*
|
||||
|
|
@ -133,6 +137,7 @@ forEach(
|
|||
* Specify custom behavior on mouseover event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
|
||||
* mouseover. (Event object is available as `$event`)
|
||||
*
|
||||
|
|
@ -156,6 +161,7 @@ forEach(
|
|||
* Specify custom behavior on mouseenter event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
|
||||
* mouseenter. (Event object is available as `$event`)
|
||||
*
|
||||
|
|
@ -179,6 +185,7 @@ forEach(
|
|||
* Specify custom behavior on mouseleave event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
|
||||
* mouseleave. (Event object is available as `$event`)
|
||||
*
|
||||
|
|
@ -202,6 +209,7 @@ forEach(
|
|||
* Specify custom behavior on mousemove event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
|
||||
* mousemove. (Event object is available as `$event`)
|
||||
*
|
||||
|
|
@ -225,6 +233,7 @@ forEach(
|
|||
* Specify custom behavior on keydown event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
|
||||
* keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
|
|
@ -246,6 +255,7 @@ forEach(
|
|||
* Specify custom behavior on keyup event.
|
||||
*
|
||||
* @element ANY
|
||||
* @priority 0
|
||||
* @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
|
||||
* keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
|
||||
*
|
||||
|
|
@ -292,6 +302,7 @@ forEach(
|
|||
* attribute**.
|
||||
*
|
||||
* @element form
|
||||
* @priority 0
|
||||
* @param {expression} ngSubmit {@link guide/expression Expression} to eval. (Event object is available as `$event`)
|
||||
*
|
||||
* @example
|
||||
|
|
@ -316,20 +327,20 @@ forEach(
|
|||
<pre>list={{list}}</pre>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should check ng-submit', function() {
|
||||
expect(binding('list')).toBe('[]');
|
||||
element('.doc-example-live #submit').click();
|
||||
expect(binding('list')).toBe('["hello"]');
|
||||
expect(input('text').val()).toBe('');
|
||||
expect(element(by.binding('list')).getText()).toBe('list=[]');
|
||||
element(by.css('.doc-example-live #submit')).click();
|
||||
expect(element(by.binding('list')).getText()).toContain('hello');
|
||||
expect(element(by.input('text')).getAttribute('value')).toBe('');
|
||||
});
|
||||
it('should ignore empty strings', function() {
|
||||
expect(binding('list')).toBe('[]');
|
||||
element('.doc-example-live #submit').click();
|
||||
element('.doc-example-live #submit').click();
|
||||
expect(binding('list')).toBe('["hello"]');
|
||||
});
|
||||
</doc:scenario>
|
||||
expect(element(by.binding('list')).getText()).toBe('list=[]');
|
||||
element(by.css('.doc-example-live #submit')).click();
|
||||
element(by.css('.doc-example-live #submit')).click();
|
||||
expect(element(by.binding('list')).getText()).toContain('hello');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
|
|
@ -341,6 +352,7 @@ forEach(
|
|||
* Specify custom behavior on focus event.
|
||||
*
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
|
||||
* focus. (Event object is available as `$event`)
|
||||
*
|
||||
|
|
@ -356,6 +368,7 @@ forEach(
|
|||
* Specify custom behavior on blur event.
|
||||
*
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
|
||||
* blur. (Event object is available as `$event`)
|
||||
*
|
||||
|
|
@ -371,6 +384,7 @@ forEach(
|
|||
* Specify custom behavior on copy event.
|
||||
*
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
|
||||
* copy. (Event object is available as `$event`)
|
||||
*
|
||||
|
|
@ -391,6 +405,7 @@ forEach(
|
|||
* Specify custom behavior on cut event.
|
||||
*
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
|
||||
* cut. (Event object is available as `$event`)
|
||||
*
|
||||
|
|
@ -411,6 +426,7 @@ forEach(
|
|||
* Specify custom behavior on paste event.
|
||||
*
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
|
||||
* paste. (Event object is available as `$event`)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -110,19 +110,24 @@
|
|||
top:50px;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
<file name="protractorTest.js">
|
||||
var templateSelect = element(by.model('template'));
|
||||
var includeElem = element(by.css('.doc-example-live [ng-include]'));
|
||||
|
||||
it('should load template1.html', function() {
|
||||
expect(element('.doc-example-live [ng-include]').text()).
|
||||
toMatch(/Content of template1.html/);
|
||||
expect(includeElem.getText()).toMatch(/Content of template1.html/);
|
||||
});
|
||||
|
||||
it('should load template2.html', function() {
|
||||
select('template').option('1');
|
||||
expect(element('.doc-example-live [ng-include]').text()).
|
||||
toMatch(/Content of template2.html/);
|
||||
templateSelect.click();
|
||||
templateSelect.element.all(by.css('option')).get(2).click();
|
||||
expect(includeElem.getText()).toMatch(/Content of template2.html/);
|
||||
});
|
||||
|
||||
it('should change to blank', function() {
|
||||
select('template').option('');
|
||||
expect(element('.doc-example-live [ng-include]')).toBe(undefined);
|
||||
templateSelect.click();
|
||||
templateSelect.element.all(by.css('option')).get(0).click();
|
||||
expect(includeElem.isPresent()).toBe(false);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,13 @@
|
|||
* should use {@link guide/controller controllers} rather than `ngInit`
|
||||
* to initialize values on a scope.
|
||||
* </div>
|
||||
* <div class="alert alert-warning">
|
||||
* **Note**: If you have assignment in `ngInit` along with {@link api/ng.$filter `$filter`}, make
|
||||
* sure you have parenthesis for correct precedence:
|
||||
* <pre class="prettyprint">
|
||||
* <div ng-init="test1 = (data | orderBy:'name')"></div>
|
||||
* </pre>
|
||||
* </div>
|
||||
*
|
||||
* @priority 450
|
||||
*
|
||||
|
|
@ -37,15 +44,15 @@
|
|||
</div>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should alias index positions', function() {
|
||||
expect(element('.example-init').text())
|
||||
.toBe('list[ 0 ][ 0 ] = a;' +
|
||||
'list[ 0 ][ 1 ] = b;' +
|
||||
'list[ 1 ][ 0 ] = c;' +
|
||||
'list[ 1 ][ 1 ] = d;');
|
||||
var elements = element.all(by.css('.example-init'));
|
||||
expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
|
||||
expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
|
||||
expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
|
||||
expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngInitDirective = ngDirective({
|
||||
|
|
|
|||
|
|
@ -24,13 +24,12 @@
|
|||
<div>Normal: {{1 + 2}}</div>
|
||||
<div ng-non-bindable>Ignored: {{1 + 2}}</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should check ng-non-bindable', function() {
|
||||
expect(using('.doc-example-live').binding('1 + 2')).toBe('3');
|
||||
expect(using('.doc-example-live').element('div:last').text()).
|
||||
toMatch(/1 \+ 2/);
|
||||
expect(element(by.binding('1 + 2')).getText()).toContain('3');
|
||||
expect(element.all(by.css('.doc-example-live div')).last().getText()).toMatch(/1 \+ 2/);
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
|
||||
|
|
|
|||
|
|
@ -123,49 +123,53 @@
|
|||
</ng-pluralize>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should show correct pluralized string', function() {
|
||||
expect(element('.doc-example-live ng-pluralize:first').text()).
|
||||
toBe('1 person is viewing.');
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Igor is viewing.');
|
||||
var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
|
||||
var withOffset = element.all(by.css('ng-pluralize')).get(1);
|
||||
var countInput = element(by.model('personCount'));
|
||||
|
||||
using('.doc-example-live').input('personCount').enter('0');
|
||||
expect(element('.doc-example-live ng-pluralize:first').text()).
|
||||
toBe('Nobody is viewing.');
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Nobody is viewing.');
|
||||
expect(withoutOffset.getText()).toEqual('1 person is viewing.');
|
||||
expect(withOffset.getText()).toEqual('Igor is viewing.');
|
||||
|
||||
using('.doc-example-live').input('personCount').enter('2');
|
||||
expect(element('.doc-example-live ng-pluralize:first').text()).
|
||||
toBe('2 people are viewing.');
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Igor and Misko are viewing.');
|
||||
countInput.clear();
|
||||
countInput.sendKeys('0');
|
||||
|
||||
using('.doc-example-live').input('personCount').enter('3');
|
||||
expect(element('.doc-example-live ng-pluralize:first').text()).
|
||||
toBe('3 people are viewing.');
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Igor, Misko and one other person are viewing.');
|
||||
expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
|
||||
expect(withOffset.getText()).toEqual('Nobody is viewing.');
|
||||
|
||||
using('.doc-example-live').input('personCount').enter('4');
|
||||
expect(element('.doc-example-live ng-pluralize:first').text()).
|
||||
toBe('4 people are viewing.');
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Igor, Misko and 2 other people are viewing.');
|
||||
countInput.clear();
|
||||
countInput.sendKeys('2');
|
||||
|
||||
expect(withoutOffset.getText()).toEqual('2 people are viewing.');
|
||||
expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
|
||||
|
||||
countInput.clear();
|
||||
countInput.sendKeys('3');
|
||||
|
||||
expect(withoutOffset.getText()).toEqual('3 people are viewing.');
|
||||
expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
|
||||
|
||||
countInput.clear();
|
||||
countInput.sendKeys('4');
|
||||
|
||||
expect(withoutOffset.getText()).toEqual('4 people are viewing.');
|
||||
expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
|
||||
});
|
||||
|
||||
it('should show data-binded names', function() {
|
||||
using('.doc-example-live').input('personCount').enter('4');
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Igor, Misko and 2 other people are viewing.');
|
||||
|
||||
using('.doc-example-live').input('person1').enter('Di');
|
||||
using('.doc-example-live').input('person2').enter('Vojta');
|
||||
expect(element('.doc-example-live ng-pluralize:last').text()).
|
||||
toBe('Di, Vojta and 2 other people are viewing.');
|
||||
it('should show data-bound names', function() {
|
||||
var withOffset = element.all(by.css('ng-pluralize')).get(1);
|
||||
var personCount = element(by.model('personCount'));
|
||||
var person1 = element(by.model('person1'));
|
||||
var person2 = element(by.model('person2'));
|
||||
personCount.clear();
|
||||
personCount.sendKeys('4');
|
||||
person1.clear();
|
||||
person1.sendKeys('Di');
|
||||
person2.clear();
|
||||
person2.sendKeys('Vojta');
|
||||
expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
* | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
|
||||
* | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
|
||||
*
|
||||
* Creating aliases for these properties is possible with {@link api/ng.directive:ngInit `ngInit`}.
|
||||
* This may be useful when, for instance, nesting ngRepeats.
|
||||
*
|
||||
* # Special repeat start and end points
|
||||
* To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
|
||||
|
|
@ -170,25 +172,27 @@
|
|||
max-height:40px;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should render initial data set', function() {
|
||||
var r = using('.doc-example-live').repeater('ul li');
|
||||
expect(r.count()).toBe(10);
|
||||
expect(r.row(0)).toEqual(["1","John","25"]);
|
||||
expect(r.row(1)).toEqual(["2","Jessie","30"]);
|
||||
expect(r.row(9)).toEqual(["10","Samantha","60"]);
|
||||
expect(binding('friends.length')).toBe("10");
|
||||
});
|
||||
<file name="protractorTest.js">
|
||||
var friends = element(by.css('.doc-example-live'))
|
||||
.element.all(by.repeater('friend in friends'));
|
||||
|
||||
it('should render initial data set', function() {
|
||||
expect(friends.count()).toBe(10);
|
||||
expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
|
||||
expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
|
||||
expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
|
||||
expect(element(by.binding('friends.length')).getText())
|
||||
.toMatch("I have 10 friends. They are:");
|
||||
});
|
||||
|
||||
it('should update repeater when filter predicate changes', function() {
|
||||
var r = using('.doc-example-live').repeater('ul li');
|
||||
expect(r.count()).toBe(10);
|
||||
expect(friends.count()).toBe(10);
|
||||
|
||||
input('q').enter('ma');
|
||||
element(by.css('.doc-example-live')).element(by.model('q')).sendKeys('ma');
|
||||
|
||||
expect(r.count()).toBe(2);
|
||||
expect(r.row(0)).toEqual(["1","Mary","28"]);
|
||||
expect(r.row(1)).toEqual(["2","Samantha","60"]);
|
||||
expect(friends.count()).toBe(2);
|
||||
expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
|
||||
expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
|
@ -203,7 +207,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
|||
$$tlb: true,
|
||||
link: function($scope, $element, $attr, ctrl, $transclude){
|
||||
var expression = $attr.ngRepeat;
|
||||
var match = expression.match(/^\s*(.+)\s+in\s+([\r\n\s\S]*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
|
||||
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),
|
||||
trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
|
||||
lhs, rhs, valueIdentifier, keyIdentifier,
|
||||
hashFnLocals = {$id: hashKey};
|
||||
|
|
@ -215,7 +219,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
|||
|
||||
lhs = match[1];
|
||||
rhs = match[2];
|
||||
trackByExp = match[4];
|
||||
trackByExp = match[3];
|
||||
|
||||
if (trackByExp) {
|
||||
trackByExpGetter = $parse(trackByExp);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,11 @@
|
|||
*
|
||||
* Just remember to include the important flag so the CSS override will function.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** Here is a list of values that ngShow will consider as a falsy value (case insensitive):<br />
|
||||
* "f" / "0" / "false" / "no" / "n" / "[]"
|
||||
* </div>
|
||||
*
|
||||
* ## A note about animations with ngShow
|
||||
*
|
||||
* Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
|
||||
|
|
@ -127,16 +132,19 @@
|
|||
background:white;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should check ng-show / ng-hide', function() {
|
||||
expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
|
||||
expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
|
||||
<file name="protractorTest.js">
|
||||
var thumbsUp = element(by.css('.doc-example-live span.icon-thumbs-up'));
|
||||
var thumbsDown = element(by.css('.doc-example-live span.icon-thumbs-down'));
|
||||
|
||||
input('checked').check();
|
||||
it('should check ng-show / ng-hide', function() {
|
||||
expect(thumbsUp.isDisplayed()).toBeFalsy();
|
||||
expect(thumbsDown.isDisplayed()).toBeTruthy();
|
||||
|
||||
expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
|
||||
expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
|
||||
});
|
||||
element(by.model('checked')).click();
|
||||
|
||||
expect(thumbsUp.isDisplayed()).toBeTruthy();
|
||||
expect(thumbsDown.isDisplayed()).toBeFalsy();
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
|
|
@ -200,6 +208,11 @@ var ngShowDirective = ['$animate', function($animate) {
|
|||
* </pre>
|
||||
*
|
||||
* Just remember to include the important flag so the CSS override will function.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** Here is a list of values that ngHide will consider as a falsy value (case insensitive):<br />
|
||||
* "f" / "0" / "false" / "no" / "n" / "[]"
|
||||
* </div>
|
||||
*
|
||||
* ## A note about animations with ngHide
|
||||
*
|
||||
|
|
@ -276,16 +289,19 @@ var ngShowDirective = ['$animate', function($animate) {
|
|||
background:white;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
it('should check ng-show / ng-hide', function() {
|
||||
expect(element('.doc-example-live .check-element:first:hidden').count()).toEqual(1);
|
||||
expect(element('.doc-example-live .check-element:last:visible').count()).toEqual(1);
|
||||
<file name="protractorTest.js">
|
||||
var thumbsUp = element(by.css('.doc-example-live span.icon-thumbs-up'));
|
||||
var thumbsDown = element(by.css('.doc-example-live span.icon-thumbs-down'));
|
||||
|
||||
input('checked').check();
|
||||
it('should check ng-show / ng-hide', function() {
|
||||
expect(thumbsUp.isDisplayed()).toBeFalsy();
|
||||
expect(thumbsDown.isDisplayed()).toBeTruthy();
|
||||
|
||||
expect(element('.doc-example-live .check-element:first:visible').count()).toEqual(1);
|
||||
expect(element('.doc-example-live .check-element:last:hidden').count()).toEqual(1);
|
||||
});
|
||||
element(by.model('checked')).click();
|
||||
|
||||
expect(thumbsUp.isDisplayed()).toBeTruthy();
|
||||
expect(thumbsDown.isDisplayed()).toBeFalsy();
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -27,13 +27,15 @@
|
|||
color: black;
|
||||
}
|
||||
</file>
|
||||
<file name="scenario.js">
|
||||
<file name="protractorTest.js">
|
||||
var colorSpan = element(by.css('.doc-example-live span'));
|
||||
|
||||
it('should check ng-style', function() {
|
||||
expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
|
||||
element('.doc-example-live :button[value=set]').click();
|
||||
expect(element('.doc-example-live span').css('color')).toBe('rgb(255, 0, 0)');
|
||||
element('.doc-example-live :button[value=clear]').click();
|
||||
expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
|
||||
expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
|
||||
element(by.css('.doc-example-live input[value=set]')).click();
|
||||
expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
|
||||
element(by.css('.doc-example-live input[value=clear]')).click();
|
||||
expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue