mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 15:40:22 +00:00
Compare commits
No commits in common. "master" and "v1.2.5" have entirely different histories.
164 changed files with 4512 additions and 5012 deletions
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"disallowKeywords": ["with"]
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
// 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,6 +9,8 @@ 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
|
||||
|
||||
|
|
|
|||
272
CHANGELOG.md
272
CHANGELOG.md
|
|
@ -1,272 +1,3 @@
|
|||
<a name="1.2.11"></a>
|
||||
# 1.2.11 cryptocurrency-hyperdeflation (2014-02-03)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:** retain CSS classes added in cloneAttachFn on asynchronous directives
|
||||
([5ed721b9](https://github.com/angular/angular.js/commit/5ed721b9b5e95ae08450e1ae9d5202e7f3f79295),
|
||||
[#5439](https://github.com/angular/angular.js/issues/5439), [#5617](https://github.com/angular/angular.js/issues/5617))
|
||||
- **$http:** update httpBackend to use ActiveXObject on IE8 if necessary
|
||||
([ef210e5e](https://github.com/angular/angular.js/commit/ef210e5e119db4f5bfc9d2428b19f9b335c4f976),
|
||||
[#5677](https://github.com/angular/angular.js/issues/5677), [#5679](https://github.com/angular/angular.js/issues/5679))
|
||||
- **$q:** make $q.reject support `finally` and `catch`
|
||||
([074b0675](https://github.com/angular/angular.js/commit/074b0675a1f97dce07f520f1ae6198ed3c604000),
|
||||
[#6048](https://github.com/angular/angular.js/issues/6048), [#6076](https://github.com/angular/angular.js/issues/6076))
|
||||
- **filterFilter:** don't interpret dots in predicate object fields as paths
|
||||
([339a1658](https://github.com/angular/angular.js/commit/339a1658cd9bfa5e322a01c45aa0a1df67e3a842),
|
||||
[#6005](https://github.com/angular/angular.js/issues/6005), [#6009](https://github.com/angular/angular.js/issues/6009))
|
||||
- **mocks:** refactor currentSpec to work w/ Jasmine 2
|
||||
([95f0bf9b](https://github.com/angular/angular.js/commit/95f0bf9b526fda8964527c6d4aef1ad50a47f1f3),
|
||||
[#5662](https://github.com/angular/angular.js/issues/5662))
|
||||
- **ngResource:** don't append number to '$' in url param value when encoding URI
|
||||
([ce1f1f97](https://github.com/angular/angular.js/commit/ce1f1f97f0ebf77941b2bdaf5e8352d33786524d),
|
||||
[#6003](https://github.com/angular/angular.js/issues/6003), [#6004](https://github.com/angular/angular.js/issues/6004))
|
||||
|
||||
<a name="1.2.10"></a>
|
||||
# 1.2.10 augmented-serendipity (2014-01-24)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$parse:** do not use locals to resolve object properties
|
||||
([f09b6aa5](https://github.com/angular/angular.js/commit/f09b6aa5b58c090e3b8f8811fb7735e38d4b7623),
|
||||
[#5838](https://github.com/angular/angular.js/issues/5838), [#5862](https://github.com/angular/angular.js/issues/5862))
|
||||
- **a:** don't call preventDefault on click when a SVGAElement has an xlink:href attribute
|
||||
([e0209169](https://github.com/angular/angular.js/commit/e0209169bf1463465ad07484421620748a4d3908),
|
||||
[#5896](https://github.com/angular/angular.js/issues/5896), [#5897](https://github.com/angular/angular.js/issues/5897))
|
||||
- **input:** use Chromium's email validation regexp
|
||||
([79e519fe](https://github.com/angular/angular.js/commit/79e519fedaec54390a8bdacfb1926bfce57a1eb6),
|
||||
[#5899](https://github.com/angular/angular.js/issues/5899), [#5924](https://github.com/angular/angular.js/issues/5924))
|
||||
- **ngRoute:** pipe preceding route param no longer masks ? or * operator
|
||||
([fd6bac7d](https://github.com/angular/angular.js/commit/fd6bac7de56f728a89782dc80c78f7d5c21bbc65),
|
||||
[#5920](https://github.com/angular/angular.js/issues/5920))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$animate:** provide support for a close callback
|
||||
([ca6b7d0f](https://github.com/angular/angular.js/commit/ca6b7d0fa2e355ebd764230260758cee9a4ebe1e),
|
||||
[#5685](https://github.com/angular/angular.js/issues/5685), [#5053](https://github.com/angular/angular.js/issues/5053), [#4993](https://github.com/angular/angular.js/issues/4993))
|
||||
|
||||
|
||||
<a name="1.2.9"></a>
|
||||
# 1.2.9 enchanted-articulacy (2014-01-15)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:**
|
||||
- ensure the final closing timeout respects staggering animations
|
||||
([ed53100a](https://github.com/angular/angular.js/commit/ed53100a0dbc9119d5dfc8b7248845d4f6989df2))
|
||||
- prevent race conditions for class-based animations when animating on the same CSS class
|
||||
([4aa9df7a](https://github.com/angular/angular.js/commit/4aa9df7a7ae533531dfae1e3eb9646245d6b5ff4),
|
||||
[#5588](https://github.com/angular/angular.js/issues/5588))
|
||||
- correctly detect and handle CSS transition changes during class addition and removal
|
||||
([7d5d62da](https://github.com/angular/angular.js/commit/7d5d62dafe11620082c79da35958f8014eeb008c))
|
||||
- avoid accidentally matching substrings when resolving the presence of className tokens
|
||||
([524650a4](https://github.com/angular/angular.js/commit/524650a40ed20f01571e5466475749874ee67288))
|
||||
- **$http:** ensure default headers PUT and POST are different objects
|
||||
([e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
|
||||
[#5742](https://github.com/angular/angular.js/issues/5742), [#5747](https://github.com/angular/angular.js/issues/5747), [#5764](https://github.com/angular/angular.js/issues/5764))
|
||||
- **$rootScope:** prevent infinite $digest by checking if asyncQueue is empty when decrementing ttl
|
||||
([2cd09c9f](https://github.com/angular/angular.js/commit/2cd09c9f0e7766bcd191662841b7b1ffc3b6dc3f),
|
||||
[#2622](https://github.com/angular/angular.js/issues/2622))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$animate:**
|
||||
- provide support for DOM callbacks
|
||||
([dde1b294](https://github.com/angular/angular.js/commit/dde1b2949727c297e214c99960141bfad438d7a4))
|
||||
- use requestAnimationFrame instead of a timeout to issue a reflow
|
||||
([4ae3184c](https://github.com/angular/angular.js/commit/4ae3184c5915aac9aa00889aa2153c8e84c14966),
|
||||
[#4278](https://github.com/angular/angular.js/issues/4278), [#4225](https://github.com/angular/angular.js/issues/4225))
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$http:** due to [e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
|
||||
it is now necessary to seperately specify default HTTP headers for PUT, POST and PATCH requests, as these no longer share a single object.
|
||||
|
||||
To migrate your code, follow the example below:
|
||||
|
||||
Before:
|
||||
|
||||
// Will apply to POST, PUT and PATCH methods
|
||||
$httpProvider.defaults.headers.post = {
|
||||
"X-MY-CSRF-HEADER": "..."
|
||||
};
|
||||
|
||||
After:
|
||||
|
||||
// POST, PUT and PATCH default headers must be specified seperately,
|
||||
// as they do not share data.
|
||||
$httpProvider.defaults.headers.post =
|
||||
$httpProvider.defaults.headers.put =
|
||||
$httpProviders.defaults.headers.patch = {
|
||||
"X-MY-CSRF-HEADER": "..."
|
||||
};
|
||||
|
||||
<a name="1.2.8"></a>
|
||||
# 1.2.8 interdimensional-cartography (2014-01-10)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$http:**
|
||||
- return responseText on IE8 for requests with responseType set
|
||||
([a9cccbe1](https://github.com/angular/angular.js/commit/a9cccbe14f1bd9048f5dab4443f58c804d4259a1),
|
||||
[#4464](https://github.com/angular/angular.js/issues/4464), [#4738](https://github.com/angular/angular.js/issues/4738), [#5636](https://github.com/angular/angular.js/issues/5636))
|
||||
- Allow status code 0 from any protocol
|
||||
([28fc80bb](https://github.com/angular/angular.js/commit/28fc80bba0107075ab371fd0a7634a38891626b2),
|
||||
[#1356](https://github.com/angular/angular.js/issues/1356), [#5547](https://github.com/angular/angular.js/issues/5547))
|
||||
- cancelled JSONP requests will not print error in the console
|
||||
([95e1b2d6](https://github.com/angular/angular.js/commit/95e1b2d6121b4e26cf87dcf6746a7b8cb4c25e7f),
|
||||
[#5615](https://github.com/angular/angular.js/issues/5615), [#5616](https://github.com/angular/angular.js/issues/5616))
|
||||
- **$location:** return '/' for root path in hashbang mode
|
||||
([63cd873f](https://github.com/angular/angular.js/commit/63cd873fef3207deef30c7a7ed66f4b8f647dc12),
|
||||
[#5650](https://github.com/angular/angular.js/issues/5650), [#5712](https://github.com/angular/angular.js/issues/5712))
|
||||
- **$parse:** fix CSP nested property evaluation, and issue that prevented its tests from failing
|
||||
([3b1a4fe0](https://github.com/angular/angular.js/commit/3b1a4fe0c83c7898ecd7261ab4213998ee7be0ec),
|
||||
[#5591](https://github.com/angular/angular.js/issues/5591), [#5592](https://github.com/angular/angular.js/issues/5592))
|
||||
- **closure:** add Closure externs for angular.$q.Promise.finally
|
||||
([caeb7402](https://github.com/angular/angular.js/commit/caeb7402651702cd13df2f1594e9827439a8b760),
|
||||
[#4757](https://github.com/angular/angular.js/issues/4757))
|
||||
- **ngMock window.inject:** Remove Error 'stack' property changes
|
||||
([7e916455](https://github.com/angular/angular.js/commit/7e916455b36dc9ca4d4afc1e44cade90006d00e3))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **select:** allow multiline ng-options
|
||||
([43a2f3d0](https://github.com/angular/angular.js/commit/43a2f3d0bf435e3626cd679caff4281cfb3415bd),
|
||||
[#5602](https://github.com/angular/angular.js/issues/5602))
|
||||
|
||||
<a name="1.2.7"></a>
|
||||
# 1.2.7 emoji-clairvoyance (2014-01-03)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:**
|
||||
- ensue class-based animations are always skipped before structural post-digest tasks are run
|
||||
([bc492c0f](https://github.com/angular/angular.js/commit/bc492c0fc17257ddf2bc5964e205379aa766b3d8),
|
||||
[#5582](https://github.com/angular/angular.js/issues/5582))
|
||||
- remove trailing `s` from computed transition duration styles
|
||||
([50bf0296](https://github.com/angular/angular.js/commit/50bf029625d603fc652f0f413e709f43803743db))
|
||||
- **$http:**
|
||||
([3d38fff8](https://github.com/angular/angular.js/commit/3d38fff8b4ea2fd60fadef2028ea4dcddfccb1a4))
|
||||
- use ActiveX XHR when making PATCH requests on IE8
|
||||
([6c17d02b](https://github.com/angular/angular.js/commit/6c17d02bc4cc02f478775d62e1f9f77da9da82ad),
|
||||
[#2518](https://github.com/angular/angular.js/issues/2518), [#5043](https://github.com/angular/angular.js/issues/5043))
|
||||
- fix 'type mismatch' error on IE8 after each request
|
||||
([fd9a03e1](https://github.com/angular/angular.js/commit/fd9a03e147aac7e952c6dda1f381fd4662276ba2))
|
||||
- Ignore multiple calls to onreadystatechange with readyState=4
|
||||
([4f572366](https://github.com/angular/angular.js/commit/4f57236614415eea919221ea5f99c4d8689b3267),
|
||||
[#5426](https://github.com/angular/angular.js/issues/5426))
|
||||
- **$injector:** remove the `INSTANTIATING` flag properly when done
|
||||
([186a5912](https://github.com/angular/angular.js/commit/186a5912288acfff0ee59dae29af83c37c987921),
|
||||
[#4361](https://github.com/angular/angular.js/issues/4361), [#5577](https://github.com/angular/angular.js/issues/5577))
|
||||
- **$location:**
|
||||
- remove base href domain if the URL begins with '//'
|
||||
([760f2fb7](https://github.com/angular/angular.js/commit/760f2fb73178e56c37397b3c5876f7dac96f0455),
|
||||
[#5606](https://github.com/angular/angular.js/issues/5606))
|
||||
- fix $location.path() behaviour when $locationChangeStart is triggered by the browser
|
||||
([cf686285](https://github.com/angular/angular.js/commit/cf686285c22d528440e173fdb65ad1052d96df3c),
|
||||
[#4989](https://github.com/angular/angular.js/issues/4989), [#5089](https://github.com/angular/angular.js/issues/5089), [#5118](https://github.com/angular/angular.js/issues/5118), [#5580](https://github.com/angular/angular.js/issues/5580))
|
||||
- re-assign history after BFCache back on Android browser
|
||||
([bddd46c8](https://github.com/angular/angular.js/commit/bddd46c8ecf49cfe6c999cd6b4a69b7d7e1f9a33),
|
||||
[#5425](https://github.com/angular/angular.js/issues/5425))
|
||||
- **$resource:** prevent URL template from collapsing into an empty string
|
||||
([131e4014](https://github.com/angular/angular.js/commit/131e4014b831ac81b7979c4523da81ebc5861c70),
|
||||
[#5455](https://github.com/angular/angular.js/issues/5455), [#5493](https://github.com/angular/angular.js/issues/5493))
|
||||
- **$sanitize:** consider the `size` attribute as a valid/allowed attribute
|
||||
([056c8493](https://github.com/angular/angular.js/commit/056c8493521988dbb330c6636135b505737da918),
|
||||
[#5522](https://github.com/angular/angular.js/issues/5522))
|
||||
- **Scope:** don't let watch deregistration mess up the dirty-checking digest loop
|
||||
([884ef0db](https://github.com/angular/angular.js/commit/884ef0dbcdfe614cedc824d079361b53e675d033),
|
||||
[#5525](https://github.com/angular/angular.js/issues/5525))
|
||||
- **input:**
|
||||
- use apply on the change event only when one isn't already in progress
|
||||
([a80049fd](https://github.com/angular/angular.js/commit/a80049fd0ac858eeeb645a4209cb2a661d0b4c33),
|
||||
[#5293](https://github.com/angular/angular.js/issues/5293))
|
||||
- prevent double $digest when using jQuery trigger.
|
||||
([1147f219](https://github.com/angular/angular.js/commit/1147f21999edf9a434cd8d24865a6455e744d858),
|
||||
[#5293](https://github.com/angular/angular.js/issues/5293))
|
||||
- **ngRepeat:** allow for more flexible coding style in ngRepeat expression
|
||||
([c9705b75](https://github.com/angular/angular.js/commit/c9705b755645a4bfe066243f2ba15a733c3787e1),
|
||||
[#5537](https://github.com/angular/angular.js/issues/5537), [#5598](https://github.com/angular/angular.js/issues/5598))
|
||||
- **ngRoute:** instantiate controller when template is empty
|
||||
([498365f2](https://github.com/angular/angular.js/commit/498365f219f65d6c29bdf2f03610a4d3646009bb),
|
||||
[#5550](https://github.com/angular/angular.js/issues/5550))
|
||||
- **ngShow/ngHide, ngIf:** functions with zero args should be truthy
|
||||
([01c5be46](https://github.com/angular/angular.js/commit/01c5be4681e34cdc5f5c461b7a618fefe8038919),
|
||||
[#5414](https://github.com/angular/angular.js/issues/5414))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **Scope:** limit propagation of $broadcast to scopes that have listeners for the event
|
||||
([80e7a455](https://github.com/angular/angular.js/commit/80e7a4558490f7ffd33d142844b9153a5ed00e86),
|
||||
[#5341](https://github.com/angular/angular.js/issues/5341), [#5371](https://github.com/angular/angular.js/issues/5371))
|
||||
|
||||
<a name="1.2.6"></a>
|
||||
# 1.2.6 taco-salsafication (2013-12-19)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:** use a scheduled timeout in favor of a fallback property to close transitions
|
||||
([54637a33](https://github.com/angular/angular.js/commit/54637a335f885110efaa702a3bab29c77644b36c),
|
||||
[#5255](https://github.com/angular/angular.js/issues/5255), [#5241](https://github.com/angular/angular.js/issues/5241), [#5405](https://github.com/angular/angular.js/issues/5405))
|
||||
- **$compile:** remove invalid IE exceptional case for `href`
|
||||
([c7a1d1ab](https://github.com/angular/angular.js/commit/c7a1d1ab0b663edffc1ac7b54deea847e372468d),
|
||||
[#5479](https://github.com/angular/angular.js/issues/5479))
|
||||
- **$location:** parse xlink:href for SVGAElements
|
||||
([bc3ff2ce](https://github.com/angular/angular.js/commit/bc3ff2cecd0861766a9e8606f3cc2c582d9875df),
|
||||
[#5472](https://github.com/angular/angular.js/issues/5472), [#5198](https://github.com/angular/angular.js/issues/5198), [#5199](https://github.com/angular/angular.js/issues/5199), [#4098](https://github.com/angular/angular.js/issues/4098), [#1420](https://github.com/angular/angular.js/issues/1420))
|
||||
- **$log:** should work in IE8
|
||||
([4f5758e6](https://github.com/angular/angular.js/commit/4f5758e6669222369889c9e789601d25ff885530),
|
||||
[#5400](https://github.com/angular/angular.js/issues/5400))
|
||||
- **$parse:** return `undefined` if an intermetiate property's value is `null`
|
||||
([26d43cac](https://github.com/angular/angular.js/commit/26d43cacdc106765bd928d41600352198f887aef),
|
||||
[#5480](https://github.com/angular/angular.js/issues/5480))
|
||||
- **closure:** add type definition for `Scope#$watchCollection`
|
||||
([8f329ffb](https://github.com/angular/angular.js/commit/8f329ffb829410e1fd8f86a766929134e736e3e5),
|
||||
[#5475](https://github.com/angular/angular.js/issues/5475))
|
||||
- **forEach:** allow looping over result of `querySelectorAll` in IE8
|
||||
([274a6734](https://github.com/angular/angular.js/commit/274a6734ef1fff543cc50388a0958d1988baeb57))
|
||||
- **input:** do not hold input for composition on Android
|
||||
([3dc18037](https://github.com/angular/angular.js/commit/3dc18037e8db8766641a4d39f0fee96077db1fcb),
|
||||
[#5308](https://github.com/angular/angular.js/issues/5308))
|
||||
- **jqLite:** support unbind self within handler
|
||||
([2f91cfd0](https://github.com/angular/angular.js/commit/2f91cfd0d2986899c38641100c1851b2f9d3888a))
|
||||
- **ngRepeat:** allow multiline expressions
|
||||
([cbb3ce2c](https://github.com/angular/angular.js/commit/cbb3ce2c309052b951d0cc87e4c6daa9c48a3dd8),
|
||||
[#5000](https://github.com/angular/angular.js/issues/5000))
|
||||
- **select:** invalidate when `multiple`, `required`, and model is `[]`
|
||||
([5c97731a](https://github.com/angular/angular.js/commit/5c97731a22ed87d64712e673efea0e8a05eae65f),
|
||||
[#5337](https://github.com/angular/angular.js/issues/5337))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **jqLite:** provide support for `element.one()`
|
||||
([937caab6](https://github.com/angular/angular.js/commit/937caab6475e53a7ea0206e992f8a52449232e78))
|
||||
- **ngAnimate:** provide configuration support to match specific className values to trigger animations
|
||||
([cef084ad](https://github.com/angular/angular.js/commit/cef084ade9072090259d8c679751cac3ffeaed51),
|
||||
[#5357](https://github.com/angular/angular.js/issues/5357), [#5283](https://github.com/angular/angular.js/issues/5283))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **compile:** add class 'ng-scope' before cloning and other micro-optimizations
|
||||
([f3a796e5](https://github.com/angular/angular.js/commit/f3a796e522afdbd3b640d14426edb2fbfab463c5),
|
||||
[#5471](https://github.com/angular/angular.js/issues/5471))
|
||||
- **$parse:** use a faster path when the number of path parts is low
|
||||
([f4462319](https://github.com/angular/angular.js/commit/864b2596b246470cca9d4e223eaed720f4462319))
|
||||
- use faster check for `$$` prefix
|
||||
([06c5cfc7](https://github.com/angular/angular.js/commit/cb29632a5802e930262919b3db64ca4806c5cfc7))
|
||||
|
||||
<a name="1.2.5"></a>
|
||||
# 1.2.5 singularity-expansion (2013-12-13)
|
||||
|
||||
|
|
@ -4595,6 +4326,3 @@ 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](#coding-rules)
|
||||
* Follow our 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,5 +258,3 @@ 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)
|
||||
|
|
|
|||
44
Gruntfile.js
44
Gruntfile.js
|
|
@ -4,8 +4,16 @@ var path = require('path');
|
|||
|
||||
module.exports = function(grunt) {
|
||||
//grunt plugins
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
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');
|
||||
grunt.loadTasks('lib/grunt');
|
||||
|
||||
var NG_VERSION = util.getVersion();
|
||||
|
|
@ -78,7 +86,9 @@ module.exports = function(grunt) {
|
|||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js',
|
||||
docs: 'karma-docs.conf.js',
|
||||
modules: 'karma-modules.conf.js'
|
||||
modules: 'karma-modules.conf.js',
|
||||
//NOTE run grunt test:e2e instead and it will start a webserver for you
|
||||
end2end: 'karma-e2e.conf.js'
|
||||
},
|
||||
|
||||
|
||||
|
|
@ -90,11 +100,6 @@ module.exports = function(grunt) {
|
|||
},
|
||||
|
||||
|
||||
runprotractor: {
|
||||
normal: 'protractor-conf.js'
|
||||
},
|
||||
|
||||
|
||||
clean: {
|
||||
build: ['build'],
|
||||
tmp: ['tmp']
|
||||
|
|
@ -136,13 +141,6 @@ module.exports = function(grunt) {
|
|||
}
|
||||
},
|
||||
|
||||
jscs: {
|
||||
src: ['src/**/*.js', 'test/**/*.js'],
|
||||
options: {
|
||||
config: ".jscs.json"
|
||||
}
|
||||
},
|
||||
|
||||
build: {
|
||||
scenario: {
|
||||
dest: 'build/angular-scenario.js',
|
||||
|
|
@ -270,28 +268,18 @@ module.exports = function(grunt) {
|
|||
write: {
|
||||
versionTXT: {file: 'build/version.txt', val: NG_VERSION.full},
|
||||
versionJSON: {file: 'build/version.json', val: JSON.stringify(NG_VERSION)}
|
||||
},
|
||||
|
||||
bump: {
|
||||
options: {
|
||||
files: ['package.json'],
|
||||
commit: false,
|
||||
createTag: false,
|
||||
push: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//alias tasks
|
||||
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', 'Run unit, docs and e2e tests with Karma', ['jshint', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:e2e']);
|
||||
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: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: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:docgen', ['jasmine_node']);
|
||||
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
|
||||
|
||||
|
|
@ -299,6 +287,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', 'jscs']);
|
||||
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'test:docgen']);
|
||||
grunt.registerTask('default', ['package']);
|
||||
};
|
||||
|
|
|
|||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
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 makes client-side
|
||||
server-side communication, taming async callbacks with promises and deferreds; and make client-side
|
||||
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
|
||||
it makes development fun!
|
||||
|
||||
|
|
@ -38,7 +38,3 @@ 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,12 +52,10 @@ 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 plz!" for "GH: issue"
|
||||
1. Label "PRs welcome" 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)
|
||||
|
|
|
|||
17
closure/README.md
Normal file
17
closure/README.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
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/
|
||||
1966
closure/angular.js
vendored
Normal file
1966
closure/angular.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -9,3 +9,14 @@
|
|||
ng\:form {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* The styles below ensure that the CSS transition will ALWAYS
|
||||
* animate and close. A nasty bug occurs with CSS transitions where
|
||||
* when the active class isn't set, or if the active class doesn't
|
||||
* contain any styles to transition to, then, if ngAnimate is used,
|
||||
* it will appear as if the webpage is broken due to the forever hanging
|
||||
* animations. The border-spacing (!ie) and zoom (ie) CSS properties are
|
||||
* used below since they trigger a transition without making the browser
|
||||
* animate anything and they're both highly underused CSS properties */
|
||||
.ng-animate-start { border-spacing:1px 1px; -ms-zoom:1.0001; }
|
||||
.ng-animate-active { border-spacing:0px 0px; -ms-zoom:1; }
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ 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: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
|
||||
results[3] = { section: 'cookbook', shortName: 'item cookbook', keywords: 'item, other' };
|
||||
results[4] = { section: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
|
||||
|
||||
$provide.value('NG_PAGES', results);
|
||||
$provide.factory('lunrSearch', function() {
|
||||
|
|
@ -40,14 +41,19 @@ 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(2);
|
||||
expect(items['tutorial'].length).toBe(3);
|
||||
expect(items['api'].length).toBe(2);
|
||||
}));
|
||||
|
||||
it("should store values with and without a ng prefix", inject(function(docsSearch) {
|
||||
expect(interceptedLunrResults[3].title).toBe('ngRepeat repeat');
|
||||
expect(interceptedLunrResults[4].title).toBe('ngRepeat repeat');
|
||||
}));
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -215,7 +215,17 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
|
|||
}];
|
||||
this.html5Mode = angular.noop;
|
||||
});
|
||||
|
||||
$provide.decorator('$timeout', ['$rootScope', '$delegate', function($rootScope, $delegate) {
|
||||
return angular.extend(function(fn, delay) {
|
||||
if (delay && delay > 50) {
|
||||
return setTimeout(function() {
|
||||
$rootScope.$apply(fn);
|
||||
}, delay);
|
||||
} else {
|
||||
return $delegate.apply(this, arguments);
|
||||
}
|
||||
}, $delegate);
|
||||
}]);
|
||||
$provide.decorator('$rootScope', ['$delegate', function($delegate) {
|
||||
embedRootScope = $delegate;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 **{@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.
|
||||
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.
|
||||
|
||||
<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 rendered within directives and expressions.
|
||||
The core filters available in the ng module are used to transform template data before it is renders within directives and expressions.
|
||||
</p>
|
||||
<p>
|
||||
Some examples include:
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@
|
|||
@description
|
||||
|
||||
# ng (core module)
|
||||
The ng module is loaded by default when an AngularJS application is started. The module itself
|
||||
contains the essential components for an AngularJS application to function. The table below
|
||||
lists a high level breakdown of each of the services/factories, filters, directives and testing
|
||||
components available within this core module.
|
||||
The ng module is loaded by default when an AngularJS application is started. The module itself contains the essential components to for an AngularJS application to function. The table below lists a high level breakdown of each of the services/factories, filters, directives and testing components available within this core module.
|
||||
|
||||
<div doc-module-components="ng"></div>
|
||||
|
|
|
|||
122
docs/content/cookbook/advancedform.ngdoc
Normal file
122
docs/content/cookbook/advancedform.ngdoc
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
@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.
|
||||
63
docs/content/cookbook/buzz.ngdoc
Normal file
63
docs/content/cookbook/buzz.ngdoc
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
@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>
|
||||
151
docs/content/cookbook/deeplinking.ngdoc
Normal file
151
docs/content/cookbook/deeplinking.ngdoc
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
@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.
|
||||
114
docs/content/cookbook/form.ngdoc
Normal file
114
docs/content/cookbook/form.ngdoc
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
@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.
|
||||
39
docs/content/cookbook/helloworld.ngdoc
Normal file
39
docs/content/cookbook/helloworld.ngdoc
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
@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.
|
||||
58
docs/content/cookbook/index.ngdoc
Normal file
58
docs/content/cookbook/index.ngdoc
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
@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.
|
||||
128
docs/content/cookbook/mvc.ngdoc
Normal file
128
docs/content/cookbook/mvc.ngdoc
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
@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-model="username">
|
||||
<input ng-mode="username">
|
||||
<div onclick="{{username}}">click me</div>
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -6,5 +6,4 @@
|
|||
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.
|
||||
|
|
@ -4,4 +4,4 @@
|
|||
@description
|
||||
This error occurs when 'ngPattern' is passed an expression that isn't a regular expression or doesn't have the expected format.
|
||||
|
||||
For more information on valid expression syntax, see 'ngPattern' in {@link api/ng.directive:input input} directive docs.
|
||||
For more information on valid expression syntax, see 'ngPattern' in {@link api/ng.directive:select input} directive docs.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ For example the issue can be triggered by this *invalid* code:
|
|||
<div ng-repeat="value in [4, 4]"></div>
|
||||
```
|
||||
|
||||
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 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 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 sample-show-hide" ng-show="checked" style="clear:both;">
|
||||
<div class="check-element animate-show-hide" ng-show="checked" style="clear:both;">
|
||||
Visible...
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.sample-show-hide {
|
||||
.animate-show-hide {
|
||||
padding:10px;
|
||||
border:1px solid black;
|
||||
background:white;
|
||||
}
|
||||
|
||||
.sample-show-hide.ng-hide-add, .sample-show-hide.ng-hide-remove {
|
||||
.animate-show-hide.ng-hide-add, .animate-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;
|
||||
}
|
||||
|
||||
.sample-show-hide.ng-hide-add.ng-hide-add-active,
|
||||
.sample-show-hide.ng-hide-remove {
|
||||
.animate-show-hide.ng-hide-add.ng-hide-add-active,
|
||||
.animate-show-hide.ng-hide-remove {
|
||||
opacity:0;
|
||||
}
|
||||
|
||||
.sample-show-hide.ng-hide-add,
|
||||
.sample-show-hide.ng-hide-remove.ng-hide-remove-active {
|
||||
.animate-show-hide.ng-hide-add,
|
||||
.animate-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:ngClass#usage_animations ngClass or {{class}}} | add and remove |
|
||||
| {@link api/ng.directive:ngShow#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 practice to move view independent logic from the controller into a so called
|
||||
is a good practise 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('invoice2', ['finance2'])` specifies that the `invoice2` module depends on the
|
||||
`finance2` module. By this, Angular uses the `InvoiceController` as well as the `currencyConverter` service.
|
||||
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.
|
||||
|
||||
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
|
||||
its children).
|
||||
and 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,19 +53,18 @@ function myController(scope, notifyService) {
|
|||
myController.$inject = ['$scope','notify'];
|
||||
</script>
|
||||
|
||||
<div id="simple" ng-controller="myController">
|
||||
<div 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:protractor>
|
||||
<doc:scenario>
|
||||
it('should test service', function() {
|
||||
expect(element(by.id('simple')).element(by.model('message')).getAttribute('value'))
|
||||
.toEqual('test');
|
||||
expect(element(':input[ng\\:model="message"]').val()).toEqual('test');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
## Implicit Dependency Injection
|
||||
|
|
@ -96,7 +95,7 @@ function myController($scope, notify) {
|
|||
};
|
||||
}
|
||||
</script>
|
||||
<div id="implicit" ng-controller="myController">
|
||||
<div 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>
|
||||
|
|
|
|||
|
|
@ -50,18 +50,19 @@ of which depend on other services that are provided by the Angular framework:
|
|||
* @param {*} message Message to be logged.
|
||||
*/
|
||||
function batchLogModule($provide){
|
||||
$provide.factory('batchLog', ['$interval', '$log', function($interval, $log) {
|
||||
$provide.factory('batchLog', ['$timeout', '$log', function($timeout, $log) {
|
||||
var messageQueue = [];
|
||||
|
||||
function log() {
|
||||
if (messageQueue.length) {
|
||||
$log.log('batchLog messages: ', messageQueue);
|
||||
$log('batchLog messages: ', messageQueue);
|
||||
messageQueue = [];
|
||||
}
|
||||
$timeout(log, 50000);
|
||||
}
|
||||
|
||||
// start periodic checking
|
||||
$interval(log, 50000);
|
||||
log();
|
||||
|
||||
return function(message) {
|
||||
messageQueue.push(message);
|
||||
|
|
@ -81,13 +82,13 @@ of which depend on other services that are provided by the Angular framework:
|
|||
}]);
|
||||
}
|
||||
|
||||
// get the main service to kick off the application
|
||||
// get the main service to kick of the application
|
||||
angular.injector([batchLogModule]).get('routeTemplateMonitor');
|
||||
</pre>
|
||||
|
||||
Things to notice in this example:
|
||||
|
||||
* The `batchLog` service depends on the built-in {@link api/ng.$interval $interval} and
|
||||
* The `batchLog` service depends on the built-in {@link api/ng.$timeout $timeout} and
|
||||
{@link api/ng.$log $log} services, and allows messages to be logged into the
|
||||
`console.log` in batches.
|
||||
* The `routeTemplateMonitor` service depends on the built-in {@link api/ngRoute.$route
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Angular sets these CSS classes. It is up to your application to provide useful s
|
|||
|
||||
* `ng-binding`
|
||||
- **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 in a nutshell.
|
||||
creation from the logic. This is dependency-injection is 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="protractorTest.js">
|
||||
<file name="scenario.js">
|
||||
it('should show off bindings', function() {
|
||||
expect(element(by.css('div[ng-controller="Ctrl1"] span[ng-bind]')).getText())
|
||||
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)');
|
||||
expect(element('div[ng-controller="Ctrl1"] span[ng-bind]').text())
|
||||
.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 {@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
|
||||
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
|
||||
{@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 HTML, it doesn't work.
|
||||
If we simply put a `<my-customer>` element into the HMTL, 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
|
||||
|
|
@ -506,8 +506,6 @@ that you explicitly pass in.
|
|||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** Normally, a scope prototypically inherits from its parent. An isolated scope does not.
|
||||
See the {@link guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive
|
||||
"Isolating the Scope of a Directive"} section for more information about isolate scopes.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-success">
|
||||
|
|
@ -527,14 +525,11 @@ where:
|
|||
|
||||
* `scope` is an Angular scope object.
|
||||
* `element` is the jqLite-wrapped element that this directive matches.
|
||||
* `attrs` is a hash object with key-value pairs of normalized attribute names and their
|
||||
corresponding attribute values.
|
||||
* `attrs` is an object with the normalized attribute names and their corresponding 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
|
||||
to call a handler on a regular basis. This is easier than using `$timeout` but also works better with
|
||||
end 2 end testing, where we want to ensure that all $timeouts have completed before completing the test.
|
||||
We also want to remove the `$interval` if the directive is deleted so we don't introduce a memory leak.
|
||||
changes the time formatting string that our directive binds to. We also want to remove the timeout
|
||||
if the directive is deleted so we don't introduce a memory leak.
|
||||
|
||||
<example module="docsTimeDirective">
|
||||
<file name="script.js">
|
||||
|
|
@ -542,7 +537,7 @@ We also want to remove the `$interval` if the directive is deleted so we don't i
|
|||
.controller('Ctrl2', function($scope) {
|
||||
$scope.format = 'M/d/yy h:mm:ss a';
|
||||
})
|
||||
.directive('myCurrentTime', function($interval, dateFilter) {
|
||||
.directive('myCurrentTime', function($timeout, dateFilter) {
|
||||
|
||||
function link(scope, element, attrs) {
|
||||
var format,
|
||||
|
|
@ -557,14 +552,20 @@ We also want to remove the `$interval` if the directive is deleted so we don't i
|
|||
updateTime();
|
||||
});
|
||||
|
||||
function scheduleUpdate() {
|
||||
// save the timeoutId for canceling
|
||||
timeoutId = $timeout(function() {
|
||||
updateTime(); // update DOM
|
||||
scheduleUpdate(); // schedule the next update
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
element.on('$destroy', function() {
|
||||
$interval.cancel(timeoutId);
|
||||
$timeout.cancel(timeoutId);
|
||||
});
|
||||
|
||||
// start the UI update process; save the timeoutId for canceling
|
||||
timeoutId = $interval(function() {
|
||||
updateTime(); // update DOM
|
||||
}, 1000);
|
||||
// start the UI update process.
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
@ -582,7 +583,7 @@ We also want to remove the `$interval` if the directive is deleted so we don't i
|
|||
|
||||
There are a couple of things to note here.
|
||||
Just like the `module.controller` API, the function argument in `module.directive` is dependency
|
||||
injected. Because of this, we can use `$interval` and `dateFilter` inside our directive's `link`
|
||||
injected. Because of this, we can use `$timeout` and `dateFilter` inside our directive's `link`
|
||||
function.
|
||||
|
||||
We register an event `element.on('$destroy', ...)`. What fires this `$destroy` event?
|
||||
|
|
@ -735,13 +736,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 `=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
|
||||
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
|
||||
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 `hideDialog` function.
|
||||
`x` in the dialog, it runs `Ctrl`'s `close` function.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** use `&attr` in the `scope` option when you want your directive
|
||||
**Best Practice:** use `&prop` 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:protractor>
|
||||
<doc:scenario>
|
||||
it('should calculate expression in binding', function() {
|
||||
expect(element(by.binding('1+2')).getText()).toEqual('1+2=3');
|
||||
expect(binding('1+2')).toEqual('3');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</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:protractor>
|
||||
<doc:scenario>
|
||||
it('should allow user expression testing', function() {
|
||||
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');
|
||||
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"]);
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</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.alert('Hello ' + $scope.name);
|
||||
($window.mockWindow || $window).alert('Hello ' + $scope.name);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -108,17 +108,21 @@ prevent accidental access to the global state (a common source of subtle bugs).
|
|||
<button ng-click="greet()">Greet</button>
|
||||
</div>
|
||||
</doc:source>
|
||||
<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: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:example>
|
||||
|
||||
## Forgiving
|
||||
|
|
|
|||
|
|
@ -121,6 +121,3 @@ 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 ngModel} directive holds an
|
||||
Similarly, an input control that has the {@link api.ng.directive:ng-model} 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 an account balance of 1000 dollars with the following binding
|
||||
For example, if you want to display 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, their
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, her
|
||||
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 for IE7 and below. You can use
|
||||
1. You polyfill JSON.stringify if necessary (IE7 will need this). 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 8 and below happy:
|
||||
4. if you **do use** custom element tags, then you must take these steps to make IE happy:
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
|
||||
|
|
|
|||
|
|
@ -18,12 +18,8 @@ watch {@link guide/expression expressions} and propagate events.
|
|||
propagate any model changes through the system into the view from outside of the "Angular
|
||||
realm" (controllers, services, Angular event handlers).
|
||||
|
||||
- Scopes can be nested to limit access to the properties of application components while providing
|
||||
access to shared model properties. Nested scopes are either "child scopes" or "isolate scopes".
|
||||
A "child scope" (prototypically) inherits properties from its parent scope. An "isolate scope"
|
||||
does not. See {@link
|
||||
guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive isolated
|
||||
scopes} for more information.
|
||||
- Scopes can be nested to isolate application components while providing access to shared model
|
||||
properties. A scope (prototypically) inherits properties from its parent scope.
|
||||
|
||||
- Scopes provide context against which {@link guide/expression expressions} are evaluated. For
|
||||
example `{{username}}` expression is meaningless, unless it is evaluated against a specific
|
||||
|
|
@ -263,8 +259,8 @@ the `$digest` phase. This delay is desirable, since it coalesces multiple model
|
|||
For mutations to be properly observed, you should make them only within the {@link
|
||||
api/ng.$rootScope.Scope#methods_$apply scope.$apply()}. (Angular APIs do this
|
||||
implicitly, so no extra `$apply` call is needed when doing synchronous work in controllers,
|
||||
or asynchronous work with {@link api/ng.$http $http}, {@link api/ng.$timeout $timeout}
|
||||
or {@link api/ng.$interval $interval} services.
|
||||
or asynchronous work with {@link api/ng.$http $http} or {@link
|
||||
api/ng.$timeout $timeout} services.
|
||||
|
||||
4. **Mutation observation**
|
||||
|
||||
|
|
@ -310,8 +306,6 @@ api/ng.directive:ngController ng-controller} and {@link
|
|||
api/ng.directive:ngRepeat ng-repeat}, create new child scopes
|
||||
and attach the child scope to the corresponding DOM element. You can retrieve a scope for any DOM
|
||||
element by using an `angular.element(aDomElement).scope()` method call.
|
||||
See the {@link guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive
|
||||
directives guide} for more information about isolate scopes.
|
||||
|
||||
### Controllers and Scopes
|
||||
|
||||
|
|
|
|||
|
|
@ -46,8 +46,6 @@ and included in your {@link http://docs.oracle.com/javase/tutorial/essential/env
|
|||
npm install -g bower
|
||||
```
|
||||
|
||||
**Note:** You may need to use sudo (for OSX, *nix, BSD etc) or run your command shell as Administrator (for Windows) to install Grunt &
|
||||
Bower globally.
|
||||
|
||||
## Forking Angular on Github
|
||||
|
||||
|
|
@ -87,16 +85,6 @@ 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,11 +73,12 @@ 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 a simple bundled http server: <code>node scripts/web-server.js</code>.</p></li>
|
||||
to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
|
@ -106,8 +107,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 a simple
|
||||
bundled http server: <code>node scripts\web-server.js</code>.</p></li>
|
||||
already installed, you can use <code>node</code> to run <code>scripts\web-server.js</code>, a simple
|
||||
bundled http server.</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 as we progress.
|
||||
The code contains some key Angular elements that we will need going forward.
|
||||
|
||||
__`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
|
||||
which implement them).
|
||||
that implements 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
|
||||
|
|
@ -179,7 +179,7 @@ For the purposes of this tutorial, we modified the angular-seed with the followi
|
|||
* Removed the example app
|
||||
* Added phone images to `app/img/phones/`
|
||||
* Added phone data files (JSON) to `app/phones/`
|
||||
* Added [Bootstrap](http://getbootstrap.com) files to `app/css/` and `app/img/`
|
||||
* Added [Bootstrap](http://twitter.github.com/bootstrap/) files to `app/css/` and `app/img/`
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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` (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`).
|
||||
`./scripts/test.sh` 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 @@ To run the test, do the following:
|
|||
|
||||
Yay! The test passed! Or not...
|
||||
|
||||
4. To rerun the tests, just change any of the source or test .js files. Karma will notice the change
|
||||
4. To rerun the tests, just change any of the source or test files. Karma will notice the change
|
||||
and will rerun the tests for you. Now isn't that sweet?
|
||||
|
||||
# Experiments
|
||||
|
|
@ -217,13 +217,9 @@ To run the test, do the following:
|
|||
|
||||
* Create a new model property in the controller and bind to it from the template. For example:
|
||||
|
||||
$scope.name = "World";
|
||||
$scope.hello = "Hello, World!"
|
||||
|
||||
Then add a new binding to `index.html`:
|
||||
|
||||
<p>Hello, {{name}}!</p>
|
||||
|
||||
Refresh your browser and verify that it says "Hello, World!".
|
||||
Refresh your browser to make sure 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 a default value here, the `orderBy` filter would remain uninitialized until our
|
||||
user picked an option from the drop down menu.
|
||||
not set the default value here, the model would stay uninitialized until our user would pick 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,9 +170,6 @@ 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.
|
||||
|
|
@ -202,7 +199,7 @@ isolated from the work done in other tests.
|
|||
|
||||
* We created a new scope for our controller by calling `$rootScope.$new()`
|
||||
|
||||
* We called the injected `$controller` function passing the name of the `PhoneListCtrl` controller
|
||||
* We called the injected `$controller` function passing the name of the`PhoneListCtrl` controller
|
||||
and the created scope as parameters.
|
||||
|
||||
Because our code now uses the `$http` service to fetch the phone list data in our controller, before
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
`$routeProvider.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when the browser
|
||||
`$route.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 easiest way to load the file is to add a `<script>`
|
||||
the `angular-route.js` file distributed with Angular. The easist way to load the file is to add a `<script>`
|
||||
tag to your `index.html` file as shown below.
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ In this step, you will improve the way our app fetches data.
|
|||
<div doc-tutorial-reset="11"></div>
|
||||
|
||||
|
||||
The next improvement we will make to our app is to define a custom service that represents a {@link
|
||||
The last improvement we will make to our app is to define a custom service that represents a {@link
|
||||
http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client. Using this client we
|
||||
can make XHR requests for data in an easier way, without having to deal with the lower-level {@link
|
||||
api/ng.$http $http} API, HTTP methods and URLs.
|
||||
|
|
@ -185,7 +185,7 @@ describe('PhoneCat controllers', function() {
|
|||
xyzPhoneData = function() {
|
||||
return {
|
||||
name: 'phone xyz',
|
||||
images: ['image/url1.png', 'image/url2.png']
|
||||
images: ['image/url1.png', 'image/url2.png']
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -293,7 +293,8 @@ 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?
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
@ -340,58 +341,45 @@ Although we could do that, let's take the opportunity to learn how to create Jav
|
|||
|
||||
__`app/js/animations.js`.__
|
||||
<pre>
|
||||
var phonecatAnimations = angular.module('phonecatAnimations', ['ngAnimate']);
|
||||
angular.module('phonecatAnimations', ['ngAnimate'])
|
||||
|
||||
phonecatAnimations.animation('.phone', function() {
|
||||
.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);
|
||||
|
||||
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();
|
||||
};
|
||||
},
|
||||
removeClass : function(element, className, done) {
|
||||
if(className != 'active') return;
|
||||
element.css({
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0
|
||||
});
|
||||
jQuery(element).animate({
|
||||
top: -500
|
||||
}, done);
|
||||
|
||||
jQuery(element).animate({
|
||||
top: 0
|
||||
}, done);
|
||||
|
||||
return function(cancel) {
|
||||
if(cancel) {
|
||||
element.stop();
|
||||
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,6 +8,8 @@ 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,7 +1,5 @@
|
|||
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;
|
||||
|
|
@ -289,33 +287,6 @@ 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() {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
|
||||
exports.appCache = appCache;
|
||||
var fs = require('q-io/fs');
|
||||
var fs = require('q-fs');
|
||||
var Q = require('qq');
|
||||
function identity($) {return $;}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ var makeUnique = {
|
|||
'script.js': true,
|
||||
'unit.js': true,
|
||||
'spec.js': true,
|
||||
'scenario.js': true,
|
||||
'protractorTest.js': true
|
||||
'scenario.js': true
|
||||
}
|
||||
|
||||
function ids(list) {
|
||||
|
|
@ -15,7 +14,7 @@ function ids(list) {
|
|||
};
|
||||
|
||||
|
||||
exports.Example = function(scenarios, protractorTests) {
|
||||
exports.Example = function(scenarios) {
|
||||
this.module = '';
|
||||
this.deps = ['angular.js'];
|
||||
this.html = [];
|
||||
|
|
@ -25,8 +24,6 @@ exports.Example = function(scenarios, protractorTests) {
|
|||
this.unit = [];
|
||||
this.scenario = [];
|
||||
this.scenarios = scenarios;
|
||||
this.protractorTest = [];
|
||||
this.protractorTests = protractorTests;
|
||||
}
|
||||
|
||||
exports.Example.prototype.setModule = function(module) {
|
||||
|
|
@ -47,10 +44,6 @@ 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++);
|
||||
}
|
||||
|
|
@ -63,9 +56,6 @@ 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() {
|
||||
|
|
@ -102,7 +92,6 @@ 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('');
|
||||
};
|
||||
|
|
@ -118,7 +107,6 @@ exports.Example.prototype.toHtmlTabs = function() {
|
|||
htmlTabs(this.json);
|
||||
htmlTabs(this.unit);
|
||||
htmlTabs(this.scenario);
|
||||
htmlTabs(this.protractorTest);
|
||||
out.push('</div>');
|
||||
return out.join('');
|
||||
|
||||
|
|
@ -131,8 +119,7 @@ exports.Example.prototype.toHtmlTabs = function() {
|
|||
if (name === 'index.html') {
|
||||
wrap = ' ng-html-wrap="' + self.module + ' ' + self.deps.join(' ') + '"';
|
||||
}
|
||||
if (name == 'scenario.js') name = 'ngScenario e2e test';
|
||||
if (name == 'protractorTest.js') name = 'Protractor e2e test';
|
||||
if (name == 'scenario.js') name = 'End to end test';
|
||||
|
||||
out.push(
|
||||
'<div class="tab-pane" title="' + name + '">\n' +
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ 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();
|
||||
|
|
@ -55,10 +53,6 @@ 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);
|
||||
|
|
@ -80,10 +74,10 @@ function writeTheRest(writesFuture) {
|
|||
var versions = ngdoc.ngVersions();
|
||||
var currentVersion = ngdoc.ngCurrentVersion();
|
||||
|
||||
writesFuture.push(writer.symlink('../../docs/content/notes', 'build/docs/notes', 'directory'));
|
||||
writesFuture.push(writer.symlinkTemplate('css', 'directory'));
|
||||
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'directory'));
|
||||
writesFuture.push(writer.symlinkTemplate('js', 'directory'));
|
||||
writesFuture.push(writer.symlink('../../docs/content/notes', 'build/docs/notes', 'dir'));
|
||||
writesFuture.push(writer.symlinkTemplate('css', 'dir'));
|
||||
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'dir'));
|
||||
writesFuture.push(writer.symlinkTemplate('js', 'dir'));
|
||||
|
||||
var manifest = 'manifest="/build/docs/appcache.manifest"';
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ 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;
|
||||
|
|
@ -51,7 +50,7 @@ exports.ngVersions = function() {
|
|||
});
|
||||
|
||||
//match the future version of AngularJS that is set in the package.json file
|
||||
return expandVersions(sortVersionsNaturally(versions), exports.ngCurrentVersion().full);
|
||||
return expandVersions(sortVersionsNatrually(versions), exports.ngCurrentVersion().full);
|
||||
|
||||
function expandVersions(versions, latestVersion) {
|
||||
var RC_VERSION = /rc\d/;
|
||||
|
|
@ -87,7 +86,7 @@ exports.ngVersions = function() {
|
|||
return expanded;
|
||||
};
|
||||
|
||||
function sortVersionsNaturally(versions) {
|
||||
function sortVersionsNatrually(versions) {
|
||||
var versionMap = {},
|
||||
NON_RC_RELEASE_NUMBER = 999;
|
||||
for(var i = versions.length - 1; i >= 0; i--) {
|
||||
|
|
@ -156,7 +155,6 @@ 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 || [];
|
||||
|
|
@ -294,7 +292,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, self.protractorTests);
|
||||
var example = new Example(self.scenarios);
|
||||
if(animations) {
|
||||
example.enableAnimations();
|
||||
example.addDeps('angular-animate.js');
|
||||
|
|
@ -331,7 +329,7 @@ Doc.prototype = {
|
|||
}).
|
||||
replace(/^<doc:example(\s+[^>]*)?>([\s\S]*)<\/doc:example>/mi, function(_, attrs, content) {
|
||||
var html, script, scenario,
|
||||
example = new Example(self.scenarios, self.protractorTests);
|
||||
example = new Example(self.scenarios);
|
||||
|
||||
example.setModule((attrs||'module=""').match(/^\s*module=["'](.*)["']\s*$/)[1]);
|
||||
content.
|
||||
|
|
@ -349,8 +347,6 @@ 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());
|
||||
|
|
@ -552,7 +548,7 @@ Doc.prototype = {
|
|||
minerrMsg;
|
||||
|
||||
var gitTagFromFullVersion = function(version) {
|
||||
var match = version.match(/sha\.(\w{7})/);
|
||||
var match = version.match(/-(\w{7})/);
|
||||
|
||||
if (match) {
|
||||
// git sha
|
||||
|
|
@ -1110,22 +1106,6 @@ 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){
|
||||
|
|
@ -1401,14 +1381,6 @@ 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' +
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ exports.collect = collect;
|
|||
|
||||
var ngdoc = require('./ngdoc.js'),
|
||||
Q = require('qq'),
|
||||
qfs = require('q-io/fs'),
|
||||
qfs = require('q-fs'),
|
||||
PATH = require('path');
|
||||
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
|
|
|||
|
|
@ -16,4 +16,4 @@ RewriteCond %{HTTP_HOST} ^docs-next\.angularjs\.org$
|
|||
RewriteRule appcache.manifest http://code.angularjs.org/next/docs/appcache.manifest [R=301]
|
||||
|
||||
## HTML5 URL Support ##
|
||||
RewriteRule ^(guide|api|misc|tutorial)(/.*)?$ index.html
|
||||
RewriteRule ^(guide|api|cookbook|misc|tutorial)(/.*)?$ index.html
|
||||
|
|
|
|||
|
|
@ -167,11 +167,6 @@
|
|||
margin-top: .5em;
|
||||
}
|
||||
|
||||
.content h6 {
|
||||
text-transform:none;
|
||||
color:black;
|
||||
}
|
||||
|
||||
ul.parameters > li > p,
|
||||
.returns > p {
|
||||
display: inline;
|
||||
|
|
@ -246,10 +241,6 @@ ul.events > li > h3 {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tutorial-nav li {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
|
@ -552,10 +543,6 @@ pre ol li {
|
|||
margin-bottom:30px;
|
||||
}
|
||||
|
||||
.definition-table td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.component-heading {
|
||||
text-transform:capitalize;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
}
|
||||
|
||||
var indexFile = (location.pathname.match(/\/(index[^\.]*\.html)/) || ['', ''])[1],
|
||||
rUrl = /(#!\/|api|guide|misc|tutorial|error|index[^\.]*\.html).*$/,
|
||||
rUrl = /(#!\/|api|guide|misc|tutorial|cookbook|error|index[^\.]*\.html).*$/,
|
||||
baseUrl = location.href.replace(rUrl, indexFile),
|
||||
jQuery = /index-jq[^\.]*\.html$/.test(baseUrl),
|
||||
debug = /index[^\.]*-debug\.html$/.test(baseUrl),
|
||||
|
|
@ -334,6 +334,10 @@
|
|||
<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,19 +5,11 @@ var docsApp = {
|
|||
filter: {}
|
||||
};
|
||||
|
||||
docsApp.controller.DocsVersionsCtrl = ['$scope', '$rootScope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $rootScope, $window, NG_VERSIONS, NG_VERSION) {
|
||||
docsApp.controller.DocsVersionsCtrl = ['$scope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $window, NG_VERSIONS, NG_VERSION) {
|
||||
$scope.docs_versions = NG_VERSIONS;
|
||||
$scope.docs_version = NG_VERSIONS[0];
|
||||
|
||||
$scope.jumpToDocsVersion = function(version) {
|
||||
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;
|
||||
$window.location = version.url;
|
||||
};
|
||||
}];
|
||||
|
||||
|
|
@ -111,6 +103,9 @@ 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);
|
||||
|
|
@ -142,19 +137,11 @@ docsApp.directive.docsSearchInput = ['$document',function($document) {
|
|||
var ESCAPE_KEY_KEYCODE = 27,
|
||||
FORWARD_SLASH_KEYCODE = 191;
|
||||
angular.element($document[0].body).bind('keydown', function(event) {
|
||||
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();
|
||||
}
|
||||
var input = element[0];
|
||||
if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement != input) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
input.focus();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -627,6 +614,7 @@ docsApp.serviceFactory.sections = ['NG_PAGES', function sections(NG_PAGES) {
|
|||
api: [],
|
||||
tutorial: [],
|
||||
misc: [],
|
||||
cookbook: [],
|
||||
error: [],
|
||||
getPage: function(sectionId, partialId) {
|
||||
var pages = sections[sectionId];
|
||||
|
|
@ -657,7 +645,7 @@ docsApp.serviceFactory.sections = ['NG_PAGES', function sections(NG_PAGES) {
|
|||
}];
|
||||
|
||||
|
||||
docsApp.controller.DocsController = function($scope, $rootScope, $location, $window, $cookies, sections) {
|
||||
docsApp.controller.DocsController = function($scope, $location, $window, $cookies, sections) {
|
||||
$scope.fold = function(url) {
|
||||
if(url) {
|
||||
$scope.docs_fold = '/notes/' + url;
|
||||
|
|
@ -671,7 +659,7 @@ docsApp.controller.DocsController = function($scope, $rootScope, $location, $win
|
|||
}
|
||||
};
|
||||
var OFFLINE_COOKIE_NAME = 'ng-offline',
|
||||
DOCS_PATH = /^\/(api)|(guide)|(misc)|(tutorial)|(error)/,
|
||||
DOCS_PATH = /^\/(api)|(guide)|(cookbook)|(misc)|(tutorial)|(error)/,
|
||||
INDEX_PATH = /^(\/|\/index[^\.]*.html)$/,
|
||||
GLOBALS = /^angular\.([^\.]+)$/,
|
||||
ERROR = /^([a-zA-Z0-9_$]+:)?([a-zA-Z0-9_$]+)$/,
|
||||
|
|
@ -733,6 +721,7 @@ docsApp.controller.DocsController = function($scope, $rootScope, $location, $win
|
|||
guide: 'Developer Guide',
|
||||
misc: 'Miscellaneous',
|
||||
tutorial: 'Tutorial',
|
||||
cookbook: 'Examples',
|
||||
error: 'Error Reference'
|
||||
};
|
||||
|
||||
|
|
@ -747,9 +736,9 @@ docsApp.controller.DocsController = function($scope, $rootScope, $location, $win
|
|||
sectionName = SECTION_NAME[sectionId] || sectionId,
|
||||
page = sections.getPage(sectionId, partialId);
|
||||
|
||||
$rootScope.currentPage = sections.getPage(sectionId, partialId);
|
||||
$scope.currentPage = sections.getPage(sectionId, partialId);
|
||||
|
||||
if (!$rootScope.currentPage) {
|
||||
if (!$scope.currentPage) {
|
||||
$scope.partialTitle = 'Error: Page Not Found!';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* for testability
|
||||
*/
|
||||
var pathUtils = require('path');
|
||||
var qfs = require('q-io/fs');
|
||||
var qfs = require('q-fs');
|
||||
var Q = require('qq');
|
||||
var OUTPUT_DIR = pathUtils.join('build','docs');
|
||||
var TEMPLATES_DIR = pathUtils.join('docs','src','templates');
|
||||
|
|
@ -76,7 +76,7 @@ function symlink(from, to, type) {
|
|||
// qfs will normalize the path arguments for us here
|
||||
return qfs.exists(to).then(function(exists) {
|
||||
if (!exists) {
|
||||
return qfs.symbolicLink(to, from, type || 'file');
|
||||
return qfs.symbolicLink(to, from, type);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Jenkins Build ############"
|
||||
echo "#################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
|
||||
|
||||
# Define reasonable set of browsers in case we are running manually from commandline
|
||||
if [[ -z "$BROWSERS" ]]
|
||||
then
|
||||
|
|
@ -28,15 +25,13 @@ rm -f angular.js.size
|
|||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
|
||||
# DOCS generator unit tests #
|
||||
grunt test:docgen --no-color
|
||||
|
||||
# UNIT TESTS #
|
||||
grunt test:unit --browsers $BROWSERS --reporters=dots,junit --no-colors --no-color
|
||||
|
||||
|
||||
# END TO END TESTS #
|
||||
grunt test:e2e --browsers $BROWSERS_E2E --reporters=dots,junit --no-colors --no-color
|
||||
grunt test:protractor
|
||||
|
||||
# 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',
|
||||
|
|
|
|||
27
karma-e2e.conf.js
Normal file
27
karma-e2e.conf.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
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
|
||||
});
|
||||
};
|
||||
|
|
@ -13,10 +13,7 @@ module.exports = function(config, specificOptions) {
|
|||
// SauceLabs config for local development.
|
||||
sauceLabs: {
|
||||
testName: specificOptions.testName || 'AngularJS',
|
||||
startConnect: true,
|
||||
options: {
|
||||
'selenium-version': '2.37.0'
|
||||
}
|
||||
startConnect: true
|
||||
},
|
||||
|
||||
// BrowserStack config for local development.
|
||||
|
|
@ -36,14 +33,13 @@ module.exports = function(config, specificOptions) {
|
|||
},
|
||||
'SL_Firefox': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '26'
|
||||
browserName: 'firefox'
|
||||
},
|
||||
'SL_Safari': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.9',
|
||||
version: '7'
|
||||
platform: 'Mac 10.8',
|
||||
version: '6'
|
||||
},
|
||||
'SL_IE_8': {
|
||||
base: 'SauceLabs',
|
||||
|
|
|
|||
|
|
@ -61,14 +61,6 @@ 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,7 +2,6 @@ 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';
|
||||
|
||||
|
|
@ -33,85 +32,29 @@ module.exports = {
|
|||
|
||||
getVersion: function(){
|
||||
if (version) return version;
|
||||
|
||||
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
|
||||
try {
|
||||
var match = package.version.match(/^([^\-]*)(?:\-(.+))?$/);
|
||||
var semver = match[1].split('.');
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
|
||||
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'
|
||||
}
|
||||
var fullVersion = match[1];
|
||||
|
||||
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);
|
||||
if (match[2]) {
|
||||
fullVersion += '-';
|
||||
fullVersion += (match[2] == 'snapshot') ? hash : match[2];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
version = {
|
||||
full: fullVersion,
|
||||
major: semver[0],
|
||||
minor: semver[1],
|
||||
dot: semver[2].replace(/rc\d+/, ''),
|
||||
codename: package.codename,
|
||||
cdn: package.cdnVersion
|
||||
};
|
||||
|
||||
function getTaggedReleaseCodeName(tagName) {
|
||||
var tagMessage = shell.exec('git cat-file -p '+ tagName +' | grep "codename"', {silent:true}).output;
|
||||
var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
|
||||
if (!codeName) {
|
||||
throw new Error("Could not extract release code name. The message of tag "+tagName+
|
||||
" must match '*codename(some release name)*'");
|
||||
}
|
||||
return codeName;
|
||||
}
|
||||
|
||||
function getSnapshotVersion() {
|
||||
var oldTags = shell.exec('git tag -l v'+package.branchVersion, {silent:true}).output.trim().split('\n');
|
||||
// ignore non semver versions.
|
||||
oldTags = oldTags.filter(function(version) {
|
||||
return version && semver.valid(version);
|
||||
});
|
||||
if (oldTags.length) {
|
||||
oldTags.sort(semver.compare);
|
||||
semVerVersion = oldTags[oldTags.length-1];
|
||||
if (semVerVersion.indexOf('-') !== -1) {
|
||||
semVerVersion = semver.inc(semVerVersion, 'prerelease');
|
||||
} else {
|
||||
semVerVersion = semver.inc(semVerVersion, 'patch');
|
||||
}
|
||||
} else {
|
||||
semVerVersion = semver.valid(package.branchVersion.replace(/\*/g, '0'));
|
||||
}
|
||||
return semVerVersion;
|
||||
}
|
||||
|
||||
function getSnapshotSuffix() {
|
||||
var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER || 'local';
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
return 'build.'+jenkinsBuild+'+sha.'+hash;
|
||||
}
|
||||
return version;
|
||||
},
|
||||
|
||||
|
||||
|
|
@ -136,38 +79,6 @@ 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');
|
||||
|
|
|
|||
54
package.json
54
package.json
|
|
@ -1,48 +1,44 @@
|
|||
{
|
||||
"name": "angularjs",
|
||||
"branchVersion": "1.2.*",
|
||||
"cdnVersion": "1.2.11",
|
||||
"version": "1.2.5",
|
||||
"cdnVersion": "1.2.4",
|
||||
"codename": "singularity-expansion",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-bump": "~0.0.13",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"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",
|
||||
"grunt": "~0.4.1",
|
||||
"bower": "~1.2.2",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-contrib-compress": "~0.5.2",
|
||||
"grunt-contrib-connect": "~0.5.0",
|
||||
"grunt-contrib-copy": "~0.4.1",
|
||||
"jasmine-node": "~1.11.0",
|
||||
"q": "~0.9.2",
|
||||
"q-io": "~1.10.6",
|
||||
"q-fs": "~0.1.36",
|
||||
"qq": "~0.3.5",
|
||||
"shelljs": "~0.2.6",
|
||||
"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",
|
||||
"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",
|
||||
"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": [
|
||||
|
|
@ -51,5 +47,7 @@
|
|||
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
"grunt-contrib-jshint": "~0.7.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
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
|
||||
}
|
||||
};
|
||||
34
release-commit.sh
Executable file
34
release-commit.sh
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
function catch_errors() {
|
||||
echo "ERROR. That's life."
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap catch_errors ERR
|
||||
|
||||
TMP_FILE='changelog.tmp'
|
||||
CHANGELOG_FILE='CHANGELOG.md'
|
||||
|
||||
echo "Getting current version..."
|
||||
VERSION=`./version.js --current`
|
||||
|
||||
echo "Generating changelog..."
|
||||
./changelog.js $VERSION $TMP_FILE
|
||||
|
||||
cat $CHANGELOG_FILE >> $TMP_FILE
|
||||
mv -f $TMP_FILE $CHANGELOG_FILE
|
||||
|
||||
|
||||
echo "Updating version..."
|
||||
./version.js --remove-snapshot
|
||||
|
||||
echo "CONFIRM TO COMMIT"
|
||||
read WHATEVER
|
||||
|
||||
|
||||
echo "Creating commit..."
|
||||
git commit version.yaml CHANGELOG.md -m "chore(relase): cutting the v$VERSION release"
|
||||
|
||||
echo "Creating tag..."
|
||||
git tag "v$VERSION"
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
#!/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
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
#!/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
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
#!/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
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
#!/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
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#!/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
|
||||
23
scripts/bower/README.md
Normal file
23
scripts/bower/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Angular Bower Script
|
||||
|
||||
Script for updating the Angular bower repos from a code.angularjs.org package
|
||||
|
||||
Requires `node` (for parsing `bower.json`) and `wget` (for fetching the `angular.zip` from `code.angularjs.org`)
|
||||
|
||||
|
||||
## Instructions
|
||||
|
||||
You need to run `./init.sh` the first time you use this script to clone all the repos.
|
||||
|
||||
For subsequent updates:
|
||||
|
||||
```shell
|
||||
./publish.sh NEW_VERSION
|
||||
```
|
||||
|
||||
Where `NEW_VERSION` is a version number like `1.2.3`.
|
||||
|
||||
|
||||
## License
|
||||
MIT
|
||||
|
||||
28
scripts/bower/init.sh
Executable file
28
scripts/bower/init.sh
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/bin/bash
|
||||
|
||||
#
|
||||
# init all of the bower repos
|
||||
#
|
||||
|
||||
set -e # fail if any command fails
|
||||
|
||||
REPOS=(
|
||||
angular \
|
||||
angular-animate \
|
||||
angular-cookies \
|
||||
angular-i18n \
|
||||
angular-loader \
|
||||
angular-mocks \
|
||||
angular-route \
|
||||
angular-resource \
|
||||
angular-sanitize \
|
||||
angular-scenario \
|
||||
angular-touch \
|
||||
)
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
git clone git@github.com:angular/bower-$repo.git
|
||||
done
|
||||
|
|
@ -1,101 +1,90 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Script for updating the Angular bower repos from current local build.
|
||||
#
|
||||
# update all the things
|
||||
#
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Update bower ###############"
|
||||
echo "#################################"
|
||||
set -e # fail if any command fails
|
||||
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
cd `dirname $0`
|
||||
|
||||
NEW_VERSION=$1
|
||||
|
||||
ZIP_FILE=angular-$NEW_VERSION.zip
|
||||
ZIP_FILE_URL=http://code.angularjs.org/$NEW_VERSION/angular-$NEW_VERSION.zip
|
||||
ZIP_DIR=angular-$NEW_VERSION
|
||||
|
||||
REPOS=(
|
||||
angular \
|
||||
angular-animate \
|
||||
angular-cookies \
|
||||
angular-i18n \
|
||||
angular-loader \
|
||||
angular-mocks \
|
||||
angular-route \
|
||||
angular-resource \
|
||||
angular-sanitize \
|
||||
angular-scenario \
|
||||
angular-touch \
|
||||
)
|
||||
|
||||
function init {
|
||||
TMP_DIR=$(resolveDir ../../tmp)
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
#
|
||||
# download and unzip the file
|
||||
#
|
||||
|
||||
if [ ! -f $ZIP_FILE ]; then
|
||||
wget $ZIP_FILE_URL
|
||||
unzip $ZIP_FILE
|
||||
fi
|
||||
|
||||
|
||||
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
|
||||
#
|
||||
# move the files from the zip
|
||||
#
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
if [ -f $ZIP_DIR/$repo.js ] # ignore i18l
|
||||
then
|
||||
cd bower-$repo
|
||||
git reset --hard HEAD
|
||||
git checkout master
|
||||
git fetch --all
|
||||
git reset --hard origin/master
|
||||
cd ..
|
||||
mv $ZIP_DIR/$repo.* bower-$repo/
|
||||
fi
|
||||
done
|
||||
|
||||
# move i18n files
|
||||
mv $ZIP_DIR/i18n/*.js bower-angular-i18n/
|
||||
|
||||
# move csp.css
|
||||
mv $ZIP_DIR/angular-csp.css bower-angular
|
||||
|
||||
|
||||
#
|
||||
# move the files from the build
|
||||
#
|
||||
#
|
||||
# get the old version number
|
||||
#
|
||||
|
||||
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
|
||||
OLD_VERSION=$(node -e "console.log(require('./bower-angular/bower').version)" | sed -e 's/\r//g')
|
||||
echo $OLD_VERSION
|
||||
echo $NEW_VERSION
|
||||
|
||||
# move i18n files
|
||||
cp $BUILD_DIR/i18n/*.js $TMP_DIR/bower-angular-i18n/
|
||||
#
|
||||
# update bower.json
|
||||
# tag each repo
|
||||
#
|
||||
|
||||
# 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
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
cd bower-$repo
|
||||
sed -i '' -e "s/$OLD_VERSION/$NEW_VERSION/g" bower.json
|
||||
git add -A
|
||||
git commit -m "v$NEW_VERSION"
|
||||
git tag v$NEW_VERSION
|
||||
git push origin master
|
||||
git push origin v$NEW_VERSION
|
||||
cd ..
|
||||
done
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Script for updating code.angularjs.org repo from current local build.
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update code.angular.js.org ###"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
)
|
||||
|
||||
function init {
|
||||
TMP_DIR=$(resolveDir ../../tmp)
|
||||
BUILD_DIR=$(resolveDir ../../build)
|
||||
REPO_DIR=$TMP_DIR/code.angularjs.org
|
||||
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||
if [[ "$NEW_VERSION" =~ sha ]]; then
|
||||
IS_SNAPSHOT_BUILD=true
|
||||
else
|
||||
IS_SNAPSHOT_BUILD=
|
||||
fi
|
||||
}
|
||||
|
||||
function prepare {
|
||||
if [[ $IS_SNAPSHOT_BUILD ]]; then
|
||||
# nothing to prepare for snapshot builds as
|
||||
# code.angularjs.org will fetch the current snapshot from
|
||||
# the build server during publish
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "-- Cloning code.angularjs.org"
|
||||
git clone git@github.com:angular/code.angularjs.org.git $REPO_DIR
|
||||
|
||||
#
|
||||
# copy the files from the build
|
||||
#
|
||||
echo "-- Updating code.angularjs.org"
|
||||
mkdir $REPO_DIR/$NEW_VERSION
|
||||
cd $REPO_DIR
|
||||
git reset --hard HEAD
|
||||
git checkout master
|
||||
git fetch --all
|
||||
git reset --hard origin/master
|
||||
cd $SCRIPT_DIR
|
||||
cp -r $BUILD_DIR/* $REPO_DIR/$NEW_VERSION/
|
||||
|
||||
#
|
||||
# commit
|
||||
#
|
||||
echo "-- Committing code.angularjs.org"
|
||||
cd $REPO_DIR
|
||||
git add -A
|
||||
git commit -m "v$NEW_VERSION"
|
||||
}
|
||||
|
||||
function publish {
|
||||
if [[ $IS_SNAPSHOT_BUILD ]]; then
|
||||
echo "-- Updating snapshot version"
|
||||
curl -G --data-urlencode "ver=$NEW_VERSION" http://code.angularjs.org/fetchLatestSnapshot.php
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
cd $REPO_DIR
|
||||
echo "-- Pushing code.angularjs.org"
|
||||
git push origin master
|
||||
|
||||
echo "-- Refreshing code.angularjs.org"
|
||||
curl http://code.angularjs.org/gitFetchSite.php
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Update master ##############"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"[--no-test=(true|false)]"
|
||||
)
|
||||
|
||||
function init {
|
||||
if [[ ! $VERBOSE ]]; then
|
||||
VERBOSE=false
|
||||
fi
|
||||
VERBOSE_ARG="--verbose=$VERBOSE"
|
||||
}
|
||||
|
||||
function build {
|
||||
cd ../..
|
||||
|
||||
if [[ $NO_TEST == "true" ]]; then
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
else
|
||||
./jenkins_build.sh
|
||||
fi
|
||||
|
||||
cd $SCRIPT_DIR
|
||||
}
|
||||
|
||||
function phase {
|
||||
ACTION_ARG="--action=$1"
|
||||
../code.angularjs.org/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||
../bower/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||
}
|
||||
|
||||
function run {
|
||||
build
|
||||
|
||||
# First prepare all scripts (build, test, commit, tag, ...),
|
||||
# so we are sure everything is all right
|
||||
phase prepare
|
||||
# only then publish to github
|
||||
phase publish
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
#!/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,71 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# tags the current commit as a release and publishes all artifacts to
|
||||
# the different repositories.
|
||||
# Note: This will also works if the commit is in the past!
|
||||
|
||||
echo "#################################"
|
||||
echo "#### cut release ############"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
# require the git dryrun flag so the script can't be run without
|
||||
# thinking about this!
|
||||
"--git-push-dryrun=(true|false)"
|
||||
# The sha to release. Needs to be the same as HEAD.
|
||||
# given as parameter to double check.
|
||||
"--commit-sha=(.*)"
|
||||
# the version number of the release.
|
||||
# e.g. 1.2.12 or 1.2.12-rc.1
|
||||
"--version-number=([0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?)"
|
||||
# the codename of the release
|
||||
"--version-name=(.+)"
|
||||
)
|
||||
|
||||
function init {
|
||||
if [[ $(git rev-parse --short HEAD) != $COMMIT_SHA ]]; then
|
||||
echo "HEAD is not at $COMMIT_SHA"
|
||||
usage
|
||||
fi
|
||||
|
||||
if [[ ! $VERBOSE ]]; then
|
||||
VERBOSE=false
|
||||
fi
|
||||
VERBOSE_ARG="--verbose=$VERBOSE"
|
||||
}
|
||||
|
||||
function build {
|
||||
cd ../..
|
||||
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
|
||||
cd $SCRIPT_DIR
|
||||
}
|
||||
|
||||
function phase {
|
||||
ACTION_ARG="--action=$1"
|
||||
../angular.js/tag-release.sh $ACTION_ARG $VERBOSE_ARG\
|
||||
--version-number=$VERSION_NUMBER --version-name=$VERSION_NAME\
|
||||
--commit-sha=$COMMIT_SHA
|
||||
|
||||
if [[ $1 == "prepare" ]]; then
|
||||
# The build requires the tag to be set already!
|
||||
build
|
||||
fi
|
||||
|
||||
../code.angularjs.org/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||
../bower/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||
../angular-seed/publish.sh $ACTION_ARG $VERBOSE_ARG --no-test=true
|
||||
../angular-phonecat/publish.sh $ACTION_ARG $VERBOSE_ARG --no-test=true
|
||||
}
|
||||
|
||||
function run {
|
||||
# First prepare all scripts (build, test, commit, tag, ...),
|
||||
# so we are sure everything is all right
|
||||
phase prepare
|
||||
# only then publish to github
|
||||
phase publish
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
|
|
@ -10,10 +10,7 @@ if [ $JOB = "unit" ]; then
|
|||
grunt test:promises-aplus
|
||||
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_8,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
|
||||
elif [ $JOB = "e2e" ]; then
|
||||
grunt test:protractor --sauceUser $SAUCE_USERNAME \
|
||||
--sauceKey $SAUCE_ACCESS_KEY \
|
||||
--capabilities.tunnel-identifier=$TRAVIS_JOB_NUMBER \
|
||||
--capabilities.build=$TRAVIS_BUILD_NUMBER
|
||||
grunt test:e2e --browsers SL_Chrome --reporters dots
|
||||
else
|
||||
echo "Unknown job type. Please set JOB=unit or JOB=e2e."
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,279 +0,0 @@
|
|||
# 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,8 +192,7 @@ function isArrayLike(obj) {
|
|||
* is the value of an object property or an array element and `key` is the object property key or
|
||||
* array element index. Specifying a `context` for the function is optional.
|
||||
*
|
||||
* It is worth nothing that `.forEach` does not iterate over inherited properties because it filters
|
||||
* using the `hasOwnProperty` method.
|
||||
* Note: this function was previously known as `angular.foreach`.
|
||||
*
|
||||
<pre>
|
||||
var values = {name: 'misko', gender: 'male'};
|
||||
|
|
@ -214,9 +213,7 @@ function forEach(obj, iterator, context) {
|
|||
if (obj) {
|
||||
if (isFunction(obj)){
|
||||
for (key in obj) {
|
||||
// Need to check if hasOwnProperty exists,
|
||||
// as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
|
||||
if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
|
||||
if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) {
|
||||
iterator.call(context, obj[key], key);
|
||||
}
|
||||
}
|
||||
|
|
@ -772,7 +769,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.substr(0, 2) !== '$$') {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
|
|
@ -960,9 +957,7 @@ function fromJson(json) {
|
|||
|
||||
|
||||
function toBoolean(value) {
|
||||
if (typeof value === 'function') {
|
||||
value = true;
|
||||
} else if (value && value.length !== 0) {
|
||||
if (value && value.length !== 0) {
|
||||
var v = lowercase("" + value);
|
||||
value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -354,9 +354,11 @@ 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
|
||||
|
||||
|
|
@ -472,7 +474,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.
|
||||
* as a type/class. This is common when using {@link http://coffeescript.org CoffeeScript}.
|
||||
*
|
||||
* @param {string} name The name of the instance.
|
||||
* @param {Function} constructor A class (constructor function) that will be instantiated.
|
||||
|
|
@ -480,24 +482,20 @@ function annotate(fn) {
|
|||
*
|
||||
* @example
|
||||
* Here is an example of registering a service using
|
||||
* {@link AUTO.$provide#methods_service $provide.service(class)}.
|
||||
* {@link AUTO.$provide#methods_service $provide.service(class)} that is defined as a CoffeeScript class.
|
||||
* <pre>
|
||||
* var Ping = function($http) {
|
||||
* this.$http = $http;
|
||||
* };
|
||||
*
|
||||
* Ping.$inject = ['$http'];
|
||||
*
|
||||
* Ping.prototype.send = function() {
|
||||
* return this.$http.get('/ping');
|
||||
* };
|
||||
* $provide.service('ping', Ping);
|
||||
* class Ping
|
||||
* constructor: (@$http)->
|
||||
* send: ()=>
|
||||
* @$http.get('/ping')
|
||||
*
|
||||
* $provide.service('ping', ['$http', Ping])
|
||||
* </pre>
|
||||
* You would then inject and use this service like this:
|
||||
* <pre>
|
||||
* someModule.controller('Ctrl', ['ping', function(ping) {
|
||||
* ping.send();
|
||||
* }]);
|
||||
* someModule.controller 'Ctrl', ['ping', (ping)->
|
||||
* ping.send()
|
||||
* ]
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
|
|
@ -742,11 +740,6 @@ 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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@
|
|||
* - [`next()`](http://api.jquery.com/next/) - Does not support selectors
|
||||
* - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
|
||||
* - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
|
||||
* - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
|
||||
* - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
|
||||
* - [`prepend()`](http://api.jquery.com/prepend/)
|
||||
* - [`prop()`](http://api.jquery.com/prop/)
|
||||
|
|
@ -175,9 +174,6 @@ 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');
|
||||
|
|
@ -649,10 +645,7 @@ function createEventHandler(element, events) {
|
|||
return event.defaultPrevented || event.returnValue === false;
|
||||
};
|
||||
|
||||
// Copy event handlers in case event handlers array is modified during execution.
|
||||
var eventHandlersCopy = shallowCopy(events[type || event.type] || []);
|
||||
|
||||
forEach(eventHandlersCopy, function(fn) {
|
||||
forEach(events[type || event.type], function(fn) {
|
||||
fn.call(element, event);
|
||||
});
|
||||
|
||||
|
|
@ -748,19 +741,6 @@ forEach({
|
|||
|
||||
off: jqLiteOff,
|
||||
|
||||
one: function(element, type, fn) {
|
||||
element = jqLite(element);
|
||||
|
||||
//add the listener twice so that when it is called
|
||||
//you can remove the original function and still be
|
||||
//able to call element.off(ev, fn) normally
|
||||
element.on(type, function onFn() {
|
||||
element.off(type, fn);
|
||||
element.off(type, onFn);
|
||||
});
|
||||
element.on(type, fn);
|
||||
},
|
||||
|
||||
replaceWith: function(element, replaceNode) {
|
||||
var index, parent = element.parentNode;
|
||||
jqLiteDealoc(element);
|
||||
|
|
|
|||
|
|
@ -61,28 +61,6 @@ var $AnimateProvider = ['$provide', function($provide) {
|
|||
$provide.factory(key, factory);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ng.$animateProvider#classNameFilter
|
||||
* @methodOf ng.$animateProvider
|
||||
*
|
||||
* @description
|
||||
* Sets and/or returns the CSS class regular expression that is checked when performing
|
||||
* an animation. Upon bootstrap the classNameFilter value is not set at all and will
|
||||
* therefore enable $animate to attempt to perform an animation on any element.
|
||||
* When setting the classNameFilter value, animations will only be performed on elements
|
||||
* that successfully match the filter expression. This in turn can boost performance
|
||||
* for low-powered devices as well as applications containing a lot of structural operations.
|
||||
* @param {RegExp=} expression The className expression which will be checked against all animations
|
||||
* @return {RegExp} The current CSS className expression value. If null then there is no expression value
|
||||
*/
|
||||
this.classNameFilter = function(expression) {
|
||||
if(arguments.length === 1) {
|
||||
this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
|
||||
}
|
||||
return this.$$classNameFilter;
|
||||
};
|
||||
|
||||
this.$get = ['$timeout', function($timeout) {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -148,9 +148,8 @@ 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, history reference to become stale.
|
||||
// Android Browser BFCache causes location reference to become stale.
|
||||
if (location !== window.location) location = window.location;
|
||||
if (history !== window.history) history = window.history;
|
||||
|
||||
// setter
|
||||
if (url) {
|
||||
|
|
@ -202,7 +201,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 from outside of angular:
|
||||
* It's only called when the url is changed by outside of angular:
|
||||
* - user types different url into address bar
|
||||
* - user clicks on history (forward/back) button
|
||||
* - user clicks on a link
|
||||
|
|
@ -244,7 +243,7 @@ function Browser(window, document, $log, $sniffer) {
|
|||
/**
|
||||
* @name ng.$browser#baseHref
|
||||
* @methodOf ng.$browser
|
||||
*
|
||||
*
|
||||
* @description
|
||||
* Returns current <base href>
|
||||
* (always relative - without domain)
|
||||
|
|
@ -253,7 +252,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?\:\/\/[^\/]*/, '') : '';
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
|
@ -275,13 +274,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 an HTML string or DOM into a template and produces a template function, which
|
||||
* Compiles a piece of 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,17 +424,13 @@
|
|||
<div compile="html"></div>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
<doc:scenario>
|
||||
it('should auto compile', function() {
|
||||
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!');
|
||||
expect(element('div[compile]').text()).toBe('Hello Angular');
|
||||
input('html').enter('{{name}}!');
|
||||
expect(element('div[compile]').text()).toBe('Angular!');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
*
|
||||
|
|
@ -473,14 +469,14 @@
|
|||
* example would not point to the clone, but rather to the original template that was cloned. In
|
||||
* this case, you can access the clone via the cloneAttachFn:
|
||||
* <pre>
|
||||
* var templateElement = angular.element('<p>{{total}}</p>'),
|
||||
* var templateHTML = angular.element('<p>{{total}}</p>'),
|
||||
* scope = ....;
|
||||
*
|
||||
* var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
|
||||
* var clonedElement = $compile(templateHTML)(scope, function(clonedElement, scope) {
|
||||
* //attach the clone to DOM document at the right place
|
||||
* });
|
||||
*
|
||||
* //now we have reference to the cloned DOM via `clonedElement`
|
||||
* //now we have reference to the cloned DOM via `clone`
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
|
|
@ -822,7 +818,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|||
var compositeLinkFn =
|
||||
compileNodes($compileNodes, transcludeFn, $compileNodes,
|
||||
maxPriority, ignoreDirective, previousCompileContext);
|
||||
safeAddClass($compileNodes, 'ng-scope');
|
||||
return function publicLinkFn(scope, cloneConnectFn, transcludeControllers){
|
||||
assertArg(scope, 'scope');
|
||||
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
|
||||
|
|
@ -837,13 +832,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|||
|
||||
// Attach scope only to non-text nodes.
|
||||
for(var i = 0, ii = $linkNode.length; i<ii; i++) {
|
||||
var node = $linkNode[i],
|
||||
nodeType = node.nodeType;
|
||||
if (nodeType === 1 /* element */ || nodeType === 9 /* document */) {
|
||||
var node = $linkNode[i];
|
||||
if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
|
||||
$linkNode.eq(i).data('$scope', scope);
|
||||
}
|
||||
}
|
||||
|
||||
safeAddClass($linkNode, 'ng-scope');
|
||||
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
|
||||
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
|
||||
return $linkNode;
|
||||
|
|
@ -871,15 +865,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|||
* @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
|
||||
* the rootElement must be set the jqLite collection of the compile root. This is
|
||||
* needed so that the jqLite collection items can be replaced with widgets.
|
||||
* @param {number=} maxPriority Max directive priority.
|
||||
* @param {number=} max directive priority
|
||||
* @returns {?function} A composite linking function of all of the matched directives or null.
|
||||
*/
|
||||
function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
|
||||
previousCompileContext) {
|
||||
var linkFns = [],
|
||||
attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound;
|
||||
nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
|
||||
|
||||
for (var i = 0; i < nodeList.length; i++) {
|
||||
for(var i = 0; i < nodeList.length; i++) {
|
||||
attrs = new Attributes();
|
||||
|
||||
// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
|
||||
|
|
@ -891,19 +885,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|||
null, [], [], previousCompileContext)
|
||||
: null;
|
||||
|
||||
if (nodeLinkFn && nodeLinkFn.scope) {
|
||||
safeAddClass(jqLite(nodeList[i]), 'ng-scope');
|
||||
}
|
||||
|
||||
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
|
||||
!(childNodes = nodeList[i].childNodes) ||
|
||||
!childNodes.length)
|
||||
!nodeList[i].childNodes ||
|
||||
!nodeList[i].childNodes.length)
|
||||
? null
|
||||
: compileNodes(childNodes,
|
||||
: compileNodes(nodeList[i].childNodes,
|
||||
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
|
||||
|
||||
linkFns.push(nodeLinkFn, childLinkFn);
|
||||
linkFnFound = linkFnFound || nodeLinkFn || childLinkFn;
|
||||
linkFns.push(nodeLinkFn);
|
||||
linkFns.push(childLinkFn);
|
||||
linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
|
||||
//use the previous context only for the first element in the virtual group
|
||||
previousCompileContext = null;
|
||||
}
|
||||
|
|
@ -915,10 +906,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|||
var nodeLinkFn, childLinkFn, node, $node, childScope, childTranscludeFn, i, ii, n;
|
||||
|
||||
// copy nodeList so that linking doesn't break due to live list updates.
|
||||
var nodeListLength = nodeList.length,
|
||||
stableNodeList = new Array(nodeListLength);
|
||||
for (i = 0; i < nodeListLength; i++) {
|
||||
stableNodeList[i] = nodeList[i];
|
||||
var stableNodeList = [];
|
||||
for (i = 0, ii = nodeList.length; i < ii; i++) {
|
||||
stableNodeList.push(nodeList[i]);
|
||||
}
|
||||
|
||||
for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
|
||||
|
|
@ -931,6 +921,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|||
if (nodeLinkFn.scope) {
|
||||
childScope = scope.$new();
|
||||
$node.data('$scope', childScope);
|
||||
safeAddClass($node, 'ng-scope');
|
||||
} else {
|
||||
childScope = scope;
|
||||
}
|
||||
|
|
@ -1013,7 +1004,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
|||
|
||||
nName = directiveNormalize(name.toLowerCase());
|
||||
attrsMap[nName] = name;
|
||||
attrs[nName] = value = trim(attr.value);
|
||||
attrs[nName] = value = trim((msie && name == 'href')
|
||||
? decodeURIComponent(node.getAttribute(name, 2))
|
||||
: attr.value);
|
||||
if (getBooleanAttrName(node, nName)) {
|
||||
attrs[nName] = true; // presence means true
|
||||
}
|
||||
|
|
@ -1196,7 +1189,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 safely handle element transclusion,
|
||||
// This option should only be used by directives that know how to how to safely handle element transclusion,
|
||||
// where the transcluded nodes are added or replaced after linking.
|
||||
if (!directive.$$tlb) {
|
||||
assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
|
||||
|
|
@ -1711,13 +1704,9 @@ 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);
|
||||
|
|
@ -1965,7 +1954,7 @@ function directiveNormalize(name) {
|
|||
*
|
||||
*
|
||||
* @param {string} name Normalized element attribute name of the property to modify. The name is
|
||||
* reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
|
||||
* revers 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,14 +32,11 @@ var htmlAnchorDirective = valueFn({
|
|||
element.append(document.createComment('IE fix'));
|
||||
}
|
||||
|
||||
if (!attr.href && !attr.xlinkHref && !attr.name) {
|
||||
if (!attr.href && !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();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
* @ngdoc directive
|
||||
* @name ng.directive:ngHref
|
||||
* @restrict A
|
||||
* @priority 99
|
||||
*
|
||||
* @description
|
||||
* Using Angular markup like `{{hash}}` in an href attribute will
|
||||
|
|
@ -41,48 +40,46 @@
|
|||
<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:protractor>
|
||||
<doc:scenario>
|
||||
it('should execute ng-click but not reload when href without value', function() {
|
||||
element(by.id('link-1')).click();
|
||||
expect(element(by.model('value')).getAttribute('value')).toEqual('1');
|
||||
expect(element(by.id('link-1')).getAttribute('href')).toBe('');
|
||||
element('#link-1').click();
|
||||
expect(input('value').val()).toEqual('1');
|
||||
expect(element('#link-1').attr('href')).toBe("");
|
||||
});
|
||||
|
||||
it('should execute ng-click but not reload when href empty string', function() {
|
||||
element(by.id('link-2')).click();
|
||||
expect(element(by.model('value')).getAttribute('value')).toEqual('2');
|
||||
expect(element(by.id('link-2')).getAttribute('href')).toBe('');
|
||||
element('#link-2').click();
|
||||
expect(input('value').val()).toEqual('2');
|
||||
expect(element('#link-2').attr('href')).toBe("");
|
||||
});
|
||||
|
||||
it('should execute ng-click and change url when ng-href specified', function() {
|
||||
expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
|
||||
expect(element('#link-3').attr('href')).toBe("/123");
|
||||
|
||||
element(by.id('link-3')).click();
|
||||
|
||||
expect(browser.driver.getCurrentUrl()).toMatch(/\/123$/);
|
||||
element('#link-3').click();
|
||||
expect(browser().window().path()).toEqual('/123');
|
||||
});
|
||||
|
||||
it('should execute ng-click but not reload when href empty string and name specified', function() {
|
||||
element(by.id('link-4')).click();
|
||||
expect(element(by.model('value')).getAttribute('value')).toEqual('4');
|
||||
expect(element(by.id('link-4')).getAttribute('href')).toBe('');
|
||||
element('#link-4').click();
|
||||
expect(input('value').val()).toEqual('4');
|
||||
expect(element('#link-4').attr('href')).toBe('');
|
||||
});
|
||||
|
||||
it('should execute ng-click but not reload when no href but name specified', function() {
|
||||
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);
|
||||
element('#link-5').click();
|
||||
expect(input('value').val()).toEqual('5');
|
||||
expect(element('#link-5').attr('href')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should only change url when only ng-href', function() {
|
||||
element(by.model('value')).clear();
|
||||
element(by.model('value')).sendKeys('6');
|
||||
expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
|
||||
input('value').enter('6');
|
||||
expect(element('#link-6').attr('href')).toBe('6');
|
||||
|
||||
element(by.id('link-6')).click();
|
||||
expect(browser.getCurrentUrl()).toMatch(/\/6$/);
|
||||
element('#link-6').click();
|
||||
expect(browser().location().url()).toEqual('/6');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
|
||||
|
|
@ -90,7 +87,6 @@
|
|||
* @ngdoc directive
|
||||
* @name ng.directive:ngSrc
|
||||
* @restrict A
|
||||
* @priority 99
|
||||
*
|
||||
* @description
|
||||
* Using Angular markup like `{{hash}}` in a `src` attribute doesn't
|
||||
|
|
@ -116,7 +112,6 @@
|
|||
* @ngdoc directive
|
||||
* @name ng.directive:ngSrcset
|
||||
* @restrict A
|
||||
* @priority 99
|
||||
*
|
||||
* @description
|
||||
* Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
|
||||
|
|
@ -142,7 +137,6 @@
|
|||
* @ngdoc directive
|
||||
* @name ng.directive:ngDisabled
|
||||
* @restrict A
|
||||
* @priority 100
|
||||
*
|
||||
* @description
|
||||
*
|
||||
|
|
@ -167,13 +161,13 @@
|
|||
Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
|
||||
<button ng-model="button" ng-disabled="checked">Button</button>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
<doc:scenario>
|
||||
it('should toggle button', function() {
|
||||
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();
|
||||
expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy();
|
||||
input('checked').check();
|
||||
expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy();
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*
|
||||
* @element INPUT
|
||||
|
|
@ -186,7 +180,6 @@
|
|||
* @ngdoc directive
|
||||
* @name ng.directive:ngChecked
|
||||
* @restrict A
|
||||
* @priority 100
|
||||
*
|
||||
* @description
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
|
|
@ -202,13 +195,13 @@
|
|||
Check me to check both: <input type="checkbox" ng-model="master"><br/>
|
||||
<input id="checkSlave" type="checkbox" ng-checked="master">
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
<doc:scenario>
|
||||
it('should check both checkBoxes', function() {
|
||||
expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
|
||||
element(by.model('master')).click();
|
||||
expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
|
||||
expect(element('.doc-example-live #checkSlave').prop('checked')).toBeFalsy();
|
||||
input('master').check();
|
||||
expect(element('.doc-example-live #checkSlave').prop('checked')).toBeTruthy();
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*
|
||||
* @element INPUT
|
||||
|
|
@ -221,7 +214,6 @@
|
|||
* @ngdoc directive
|
||||
* @name ng.directive:ngReadonly
|
||||
* @restrict A
|
||||
* @priority 100
|
||||
*
|
||||
* @description
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
|
|
@ -231,19 +223,20 @@
|
|||
* The `ngReadonly` directive solves this problem for the `readonly` attribute.
|
||||
* This complementary directive is not removed by the browser and so provides
|
||||
* a permanent reliable place to store the binding information.
|
||||
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
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:protractor>
|
||||
<doc:scenario>
|
||||
it('should toggle readonly attr', function() {
|
||||
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();
|
||||
expect(element('.doc-example-live :text').prop('readonly')).toBeFalsy();
|
||||
input('checked').check();
|
||||
expect(element('.doc-example-live :text').prop('readonly')).toBeTruthy();
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*
|
||||
* @element INPUT
|
||||
|
|
@ -256,7 +249,6 @@
|
|||
* @ngdoc directive
|
||||
* @name ng.directive:ngSelected
|
||||
* @restrict A
|
||||
* @priority 100
|
||||
*
|
||||
* @description
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
|
|
@ -266,7 +258,6 @@
|
|||
* The `ngSelected` directive solves this problem for the `selected` atttribute.
|
||||
* This complementary directive is not removed by the browser and so provides
|
||||
* a permanent reliable place to store the binding information.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
|
|
@ -276,13 +267,13 @@
|
|||
<option id="greet" ng-selected="selected">Greetings!</option>
|
||||
</select>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
<doc:scenario>
|
||||
it('should select Greetings!', function() {
|
||||
expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
|
||||
element(by.model('selected')).click();
|
||||
expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
|
||||
expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy();
|
||||
input('selected').check();
|
||||
expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy();
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*
|
||||
* @element OPTION
|
||||
|
|
@ -294,7 +285,6 @@
|
|||
* @ngdoc directive
|
||||
* @name ng.directive:ngOpen
|
||||
* @restrict A
|
||||
* @priority 100
|
||||
*
|
||||
* @description
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
|
|
@ -304,6 +294,8 @@
|
|||
* The `ngOpen` directive solves this problem for the `open` attribute.
|
||||
* This complementary directive is not removed by the browser and so provides
|
||||
* a permanent reliable place to store the binding information.
|
||||
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
|
|
@ -312,13 +304,13 @@
|
|||
<summary>Show/Hide me</summary>
|
||||
</details>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
<doc:scenario>
|
||||
it('should toggle open', function() {
|
||||
expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
|
||||
element(by.model('open')).click();
|
||||
expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
|
||||
expect(element('#details').prop('open')).toBeFalsy();
|
||||
input('open').check();
|
||||
expect(element('#details').prop('open')).toBeTruthy();
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*
|
||||
* @element DETAILS
|
||||
|
|
@ -338,10 +330,12 @@ forEach(BOOLEAN_ATTR, function(propName, attrName) {
|
|||
ngAttributeAliasDirectives[normalized] = function() {
|
||||
return {
|
||||
priority: 100,
|
||||
link: function(scope, element, attr) {
|
||||
scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
|
||||
attr.$set(attrName, !!value);
|
||||
});
|
||||
compile: function() {
|
||||
return 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,27 +305,18 @@ function FormController(element, attrs) {
|
|||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
<doc:scenario>
|
||||
it('should initialize to model', function() {
|
||||
var userType = element(by.binding('userType'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
|
||||
expect(userType.getText()).toContain('guest');
|
||||
expect(valid.getText()).toContain('true');
|
||||
expect(binding('userType')).toEqual('guest');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
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');
|
||||
input('userType').enter('');
|
||||
expect(binding('userType')).toEqual('');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</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-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i;
|
||||
var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$/;
|
||||
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
|
||||
|
||||
var inputType = {
|
||||
|
|
@ -62,31 +62,29 @@ var inputType = {
|
|||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
var text = element(by.binding('text'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('text'));
|
||||
|
||||
<doc:scenario>
|
||||
it('should initialize to model', function() {
|
||||
expect(text.getText()).toContain('guest');
|
||||
expect(valid.getText()).toContain('true');
|
||||
expect(binding('text')).toEqual('guest');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
input.clear();
|
||||
input.sendKeys('');
|
||||
|
||||
expect(text.getText()).toEqual('text =');
|
||||
expect(valid.getText()).toContain('false');
|
||||
input('text').enter('');
|
||||
expect(binding('text')).toEqual('');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
});
|
||||
|
||||
it('should be invalid if multi word', function() {
|
||||
input.clear();
|
||||
input.sendKeys('hello world');
|
||||
|
||||
expect(valid.getText()).toContain('false');
|
||||
input('text').enter('hello world');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
});
|
||||
</doc:protractor>
|
||||
|
||||
it('should not be trimmed', function() {
|
||||
input('text').enter('untrimmed ');
|
||||
expect(binding('text')).toEqual('untrimmed ');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
'text': textInputType,
|
||||
|
|
@ -140,30 +138,24 @@ var inputType = {
|
|||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
var value = element(by.binding('value'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('value'));
|
||||
|
||||
<doc:scenario>
|
||||
it('should initialize to model', function() {
|
||||
expect(value.getText()).toContain('12');
|
||||
expect(valid.getText()).toContain('true');
|
||||
expect(binding('value')).toEqual('12');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
input.clear();
|
||||
input.sendKeys('');
|
||||
expect(value.getText()).toEqual('value =');
|
||||
expect(valid.getText()).toContain('false');
|
||||
input('value').enter('');
|
||||
expect(binding('value')).toEqual('');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
});
|
||||
|
||||
it('should be invalid if over max', function() {
|
||||
input.clear();
|
||||
input.sendKeys('123');
|
||||
expect(value.getText()).toEqual('value =');
|
||||
expect(valid.getText()).toContain('false');
|
||||
input('value').enter('123');
|
||||
expect(binding('value')).toEqual('');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
'number': numberInputType,
|
||||
|
|
@ -215,31 +207,23 @@ var inputType = {
|
|||
<tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
var text = element(by.binding('text'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('text'));
|
||||
|
||||
<doc:scenario>
|
||||
it('should initialize to model', function() {
|
||||
expect(text.getText()).toContain('http://google.com');
|
||||
expect(valid.getText()).toContain('true');
|
||||
expect(binding('text')).toEqual('http://google.com');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
input.clear();
|
||||
input.sendKeys('');
|
||||
|
||||
expect(text.getText()).toEqual('text =');
|
||||
expect(valid.getText()).toContain('false');
|
||||
input('text').enter('');
|
||||
expect(binding('text')).toEqual('');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
});
|
||||
|
||||
it('should be invalid if not url', function() {
|
||||
input.clear();
|
||||
input.sendKeys('box');
|
||||
|
||||
expect(valid.getText()).toContain('false');
|
||||
input('text').enter('xxx');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
'url': urlInputType,
|
||||
|
|
@ -291,30 +275,23 @@ var inputType = {
|
|||
<tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
var text = element(by.binding('text'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('text'));
|
||||
|
||||
<doc:scenario>
|
||||
it('should initialize to model', function() {
|
||||
expect(text.getText()).toContain('me@example.com');
|
||||
expect(valid.getText()).toContain('true');
|
||||
expect(binding('text')).toEqual('me@example.com');
|
||||
expect(binding('myForm.input.$valid')).toEqual('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
input.clear();
|
||||
input.sendKeys('');
|
||||
expect(text.getText()).toEqual('text =');
|
||||
expect(valid.getText()).toContain('false');
|
||||
input('text').enter('');
|
||||
expect(binding('text')).toEqual('');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
});
|
||||
|
||||
it('should be invalid if not email', function() {
|
||||
input.clear();
|
||||
input.sendKeys('xxx');
|
||||
|
||||
expect(valid.getText()).toContain('false');
|
||||
input('text').enter('xxx');
|
||||
expect(binding('myForm.input.$valid')).toEqual('false');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
'email': emailInputType,
|
||||
|
|
@ -332,8 +309,6 @@ 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>
|
||||
|
|
@ -341,31 +316,23 @@ 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" ng-value="specialValue"> Green <br/>
|
||||
<input type="radio" ng-model="color" value="green"> Green <br/>
|
||||
<input type="radio" ng-model="color" value="blue"> Blue <br/>
|
||||
<tt>color = {{color | json}}</tt><br/>
|
||||
<tt>color = {{color}}</tt><br/>
|
||||
</form>
|
||||
Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
<doc:scenario>
|
||||
it('should change state', function() {
|
||||
var color = element(by.binding('color'));
|
||||
expect(binding('color')).toEqual('blue');
|
||||
|
||||
expect(color.getText()).toContain('blue');
|
||||
|
||||
element.all(by.model('color')).get(0).click();
|
||||
|
||||
expect(color.getText()).toContain('red');
|
||||
input('color').select('red');
|
||||
expect(binding('color')).toEqual('red');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
'radio': radioInputType,
|
||||
|
|
@ -402,21 +369,17 @@ var inputType = {
|
|||
<tt>value2 = {{value2}}</tt><br/>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
<doc:scenario>
|
||||
it('should change state', function() {
|
||||
var value1 = element(by.binding('value1'));
|
||||
var value2 = element(by.binding('value2'));
|
||||
expect(binding('value1')).toEqual('true');
|
||||
expect(binding('value2')).toEqual('YES');
|
||||
|
||||
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');
|
||||
input('value1').check();
|
||||
input('value2').check();
|
||||
expect(binding('value1')).toEqual('false');
|
||||
expect(binding('value2')).toEqual('NO');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
'checkbox': checkboxInputType,
|
||||
|
|
@ -427,28 +390,20 @@ 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,
|
||||
// hold the listener until composition is done.
|
||||
// More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
|
||||
if (!$sniffer.android) {
|
||||
var composing = false;
|
||||
var composing = false;
|
||||
|
||||
element.on('compositionstart', function(data) {
|
||||
composing = true;
|
||||
});
|
||||
element.on('compositionstart', function() {
|
||||
composing = true;
|
||||
});
|
||||
|
||||
element.on('compositionend', function() {
|
||||
composing = false;
|
||||
});
|
||||
}
|
||||
element.on('compositionend', function() {
|
||||
composing = false;
|
||||
});
|
||||
|
||||
var listener = function() {
|
||||
if (composing) return;
|
||||
|
|
@ -462,13 +417,9 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
}
|
||||
|
||||
if (ctrl.$viewValue !== value) {
|
||||
if (scope.$$phase) {
|
||||
scope.$apply(function() {
|
||||
ctrl.$setViewValue(value);
|
||||
} else {
|
||||
scope.$apply(function() {
|
||||
ctrl.$setViewValue(value);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -517,15 +468,22 @@ 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 validateRegex(pattern, value);
|
||||
return validate(pattern, value);
|
||||
};
|
||||
} else {
|
||||
patternValidator = function(value) {
|
||||
|
|
@ -536,7 +494,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 validateRegex(patternObj, value);
|
||||
return validate(patternObj, value);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -548,7 +506,13 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
if (attr.ngMinlength) {
|
||||
var minlength = int(attr.ngMinlength);
|
||||
var minLengthValidator = function(value) {
|
||||
return validate(ctrl, 'minlength', ctrl.$isEmpty(value) || value.length >= minlength, value);
|
||||
if (!ctrl.$isEmpty(value) && value.length < minlength) {
|
||||
ctrl.$setValidity('minlength', false);
|
||||
return undefined;
|
||||
} else {
|
||||
ctrl.$setValidity('minlength', true);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(minLengthValidator);
|
||||
|
|
@ -559,7 +523,13 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
if (attr.ngMaxlength) {
|
||||
var maxlength = int(attr.ngMaxlength);
|
||||
var maxLengthValidator = function(value) {
|
||||
return validate(ctrl, 'maxlength', ctrl.$isEmpty(value) || value.length <= maxlength, value);
|
||||
if (!ctrl.$isEmpty(value) && value.length > maxlength) {
|
||||
ctrl.$setValidity('maxlength', false);
|
||||
return undefined;
|
||||
} else {
|
||||
ctrl.$setValidity('maxlength', true);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(maxLengthValidator);
|
||||
|
|
@ -588,7 +558,13 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
if (attr.min) {
|
||||
var minValidator = function(value) {
|
||||
var min = parseFloat(attr.min);
|
||||
return validate(ctrl, 'min', ctrl.$isEmpty(value) || value >= min, value);
|
||||
if (!ctrl.$isEmpty(value) && value < min) {
|
||||
ctrl.$setValidity('min', false);
|
||||
return undefined;
|
||||
} else {
|
||||
ctrl.$setValidity('min', true);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(minValidator);
|
||||
|
|
@ -598,7 +574,13 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
if (attr.max) {
|
||||
var maxValidator = function(value) {
|
||||
var max = parseFloat(attr.max);
|
||||
return validate(ctrl, 'max', ctrl.$isEmpty(value) || value <= max, value);
|
||||
if (!ctrl.$isEmpty(value) && value > max) {
|
||||
ctrl.$setValidity('max', false);
|
||||
return undefined;
|
||||
} else {
|
||||
ctrl.$setValidity('max', true);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(maxValidator);
|
||||
|
|
@ -606,7 +588,14 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
}
|
||||
|
||||
ctrl.$formatters.push(function(value) {
|
||||
return validate(ctrl, 'number', ctrl.$isEmpty(value) || isNumber(value), value);
|
||||
|
||||
if (ctrl.$isEmpty(value) || isNumber(value)) {
|
||||
ctrl.$setValidity('number', true);
|
||||
return value;
|
||||
} else {
|
||||
ctrl.$setValidity('number', false);
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -614,7 +603,13 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
||||
|
||||
var urlValidator = function(value) {
|
||||
return validate(ctrl, 'url', ctrl.$isEmpty(value) || URL_REGEXP.test(value), value);
|
||||
if (ctrl.$isEmpty(value) || URL_REGEXP.test(value)) {
|
||||
ctrl.$setValidity('url', true);
|
||||
return value;
|
||||
} else {
|
||||
ctrl.$setValidity('url', false);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$formatters.push(urlValidator);
|
||||
|
|
@ -625,7 +620,13 @@ function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
|||
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
||||
|
||||
var emailValidator = function(value) {
|
||||
return validate(ctrl, 'email', ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value), value);
|
||||
if (ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value)) {
|
||||
ctrl.$setValidity('email', true);
|
||||
return value;
|
||||
} else {
|
||||
ctrl.$setValidity('email', false);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.$formatters.push(emailValidator);
|
||||
|
|
@ -769,59 +770,44 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
|||
<tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
|
||||
</div>
|
||||
</doc:source>
|
||||
<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'));
|
||||
|
||||
<doc:scenario>
|
||||
it('should initialize to model', function() {
|
||||
expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
|
||||
expect(userNameValid.getText()).toContain('true');
|
||||
expect(formValid.getText()).toContain('true');
|
||||
expect(binding('user')).toEqual('{"name":"guest","last":"visitor"}');
|
||||
expect(binding('myForm.userName.$valid')).toEqual('true');
|
||||
expect(binding('myForm.$valid')).toEqual('true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty when required', function() {
|
||||
userNameInput.clear();
|
||||
userNameInput.sendKeys('');
|
||||
|
||||
expect(user.getText()).toContain('{"last":"visitor"}');
|
||||
expect(userNameValid.getText()).toContain('false');
|
||||
expect(formValid.getText()).toContain('false');
|
||||
input('user.name').enter('');
|
||||
expect(binding('user')).toEqual('{"last":"visitor"}');
|
||||
expect(binding('myForm.userName.$valid')).toEqual('false');
|
||||
expect(binding('myForm.$valid')).toEqual('false');
|
||||
});
|
||||
|
||||
it('should be valid if empty when min length is set', function() {
|
||||
userLastInput.clear();
|
||||
userLastInput.sendKeys('');
|
||||
|
||||
expect(user.getText()).toContain('{"name":"guest","last":""}');
|
||||
expect(lastNameValid.getText()).toContain('true');
|
||||
expect(formValid.getText()).toContain('true');
|
||||
input('user.last').enter('');
|
||||
expect(binding('user')).toEqual('{"name":"guest","last":""}');
|
||||
expect(binding('myForm.lastName.$valid')).toEqual('true');
|
||||
expect(binding('myForm.$valid')).toEqual('true');
|
||||
});
|
||||
|
||||
it('should be invalid if less than required min length', function() {
|
||||
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');
|
||||
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');
|
||||
});
|
||||
|
||||
it('should be invalid if longer than max length', function() {
|
||||
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');
|
||||
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');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) {
|
||||
|
|
@ -953,17 +939,14 @@ var VALID_CLASS = 'ng-valid',
|
|||
<textarea ng-model="userContent"></textarea>
|
||||
</form>
|
||||
</file>
|
||||
<file name="protractorTest.js">
|
||||
<file name="scenario.js">
|
||||
it('should data-bind and become invalid', function() {
|
||||
var contentEditable = element(by.css('.doc-example-live [contenteditable]'));
|
||||
var contentEditable = element('[contenteditable]');
|
||||
|
||||
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/);
|
||||
expect(contentEditable.text()).toEqual('Change me!');
|
||||
input('userContent').enter('');
|
||||
expect(contentEditable.text()).toEqual('');
|
||||
expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/);
|
||||
});
|
||||
</file>
|
||||
* </example>
|
||||
|
|
@ -1243,10 +1226,7 @@ var ngModelDirective = function() {
|
|||
* @name ng.directive:ngChange
|
||||
*
|
||||
* @description
|
||||
* 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).
|
||||
* Evaluate given expression when user changes the input.
|
||||
* The expression is not evaluated when the value change is coming from the model.
|
||||
*
|
||||
* Note, this directive requires `ngModel` to be present.
|
||||
|
|
@ -1270,30 +1250,24 @@ 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 />
|
||||
* <tt>debug = {{confirmed}}</tt><br/>
|
||||
* <tt>counter = {{counter}}</tt><br/>
|
||||
* debug = {{confirmed}}<br />
|
||||
* counter = {{counter}}
|
||||
* </div>
|
||||
* </doc:source>
|
||||
* <doc:protractor>
|
||||
* var counter = element(by.binding('counter'));
|
||||
* var debug = element(by.binding('confirmed'));
|
||||
*
|
||||
* <doc:scenario>
|
||||
* it('should evaluate the expression if changing from view', function() {
|
||||
* expect(counter.getText()).toContain('0');
|
||||
*
|
||||
* element(by.id('ng-change-example1')).click();
|
||||
*
|
||||
* expect(counter.getText()).toContain('1');
|
||||
* expect(debug.getText()).toContain('true');
|
||||
* expect(binding('counter')).toEqual('0');
|
||||
* element('#ng-change-example1').click();
|
||||
* expect(binding('counter')).toEqual('1');
|
||||
* expect(binding('confirmed')).toEqual('true');
|
||||
* });
|
||||
*
|
||||
* it('should not evaluate the expression if changing from model', function() {
|
||||
* element(by.id('ng-change-example2')).click();
|
||||
|
||||
* expect(counter.getText()).toContain('0');
|
||||
* expect(debug.getText()).toContain('true');
|
||||
* element('#ng-change-example2').click();
|
||||
* expect(binding('counter')).toEqual('0');
|
||||
* expect(binding('confirmed')).toEqual('true');
|
||||
* });
|
||||
* </doc:protractor>
|
||||
* </doc:scenario>
|
||||
* </doc:example>
|
||||
*/
|
||||
var ngChangeDirective = valueFn({
|
||||
|
|
@ -1366,26 +1340,20 @@ var requiredDirective = function() {
|
|||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
||||
</form>
|
||||
</doc:source>
|
||||
<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'));
|
||||
|
||||
<doc:scenario>
|
||||
it('should initialize to model', function() {
|
||||
expect(names.getText()).toContain('["igor","misko","vojta"]');
|
||||
expect(valid.getText()).toContain('true');
|
||||
expect(error.getCssValue('display')).toBe('none');
|
||||
expect(binding('names')).toEqual('["igor","misko","vojta"]');
|
||||
expect(binding('myForm.namesInput.$valid')).toEqual('true');
|
||||
expect(element('span.error').css('display')).toBe('none');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
listInput.clear();
|
||||
listInput.sendKeys('');
|
||||
|
||||
expect(names.getText()).toContain('');
|
||||
expect(valid.getText()).toContain('false');
|
||||
expect(error.getCssValue('display')).not.toBe('none'); });
|
||||
</doc:protractor>
|
||||
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>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngListDirective = function() {
|
||||
|
|
@ -1467,17 +1435,15 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
|
|||
<div>You chose {{my.favorite}}</div>
|
||||
</form>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
var favorite = element(by.binding('my.favorite'));
|
||||
|
||||
<doc:scenario>
|
||||
it('should initialize to model', function() {
|
||||
expect(favorite.getText()).toContain('unicorns');
|
||||
expect(binding('my.favorite')).toEqual('unicorns');
|
||||
});
|
||||
it('should bind the values to the inputs', function() {
|
||||
element.all(by.model('my.favorite')).get(0).click();
|
||||
expect(favorite.getText()).toContain('pizza');
|
||||
input('my.favorite').select('pizza');
|
||||
expect(binding('my.favorite')).toEqual('pizza');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngValueDirective = function() {
|
||||
|
|
|
|||
|
|
@ -38,17 +38,13 @@
|
|||
Hello <span ng-bind="name"></span>!
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
<doc:scenario>
|
||||
it('should check ng-bind', function() {
|
||||
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');
|
||||
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');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngBindDirective = ngDirective(function(scope, element, attr) {
|
||||
|
|
@ -94,22 +90,20 @@ var ngBindDirective = ngDirective(function(scope, element, attr) {
|
|||
<pre ng-bind-template="{{salutation}} {{name}}!"></pre>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:protractor>
|
||||
<doc:scenario>
|
||||
it('should check ng-bind', function() {
|
||||
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!');
|
||||
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');
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
|
||||
|
|
@ -162,10 +156,12 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
|
|||
}]);
|
||||
</file>
|
||||
|
||||
<file name="protractorTest.js">
|
||||
<file name="scenario.js">
|
||||
it('should check ng-bind-html', function() {
|
||||
expect(element(by.binding('myHTML')).getText()).toBe(
|
||||
'I am an HTMLstring with links! and other stuff');
|
||||
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>'
|
||||
);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
|
|
|||
|
|
@ -114,34 +114,31 @@ function classDirective(name, selector) {
|
|||
color: red;
|
||||
}
|
||||
</file>
|
||||
<file name="protractorTest.js">
|
||||
var ps = element.all(by.css('.doc-example-live p'));
|
||||
|
||||
<file name="scenario.js">
|
||||
it('should let you toggle the class', function() {
|
||||
|
||||
expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
|
||||
expect(ps.first().getAttribute('class')).not.toMatch(/red/);
|
||||
expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/bold/);
|
||||
expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/red/);
|
||||
|
||||
element(by.model('important')).click();
|
||||
expect(ps.first().getAttribute('class')).toMatch(/bold/);
|
||||
input('important').check();
|
||||
expect(element('.doc-example-live p:first').prop('className')).toMatch(/bold/);
|
||||
|
||||
element(by.model('error')).click();
|
||||
expect(ps.first().getAttribute('class')).toMatch(/red/);
|
||||
input('error').check();
|
||||
expect(element('.doc-example-live p:first').prop('className')).toMatch(/red/);
|
||||
});
|
||||
|
||||
it('should let you toggle string example', function() {
|
||||
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');
|
||||
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');
|
||||
});
|
||||
|
||||
it('array example should have 3 classes', function() {
|
||||
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');
|
||||
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');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
|
@ -152,8 +149,8 @@ function classDirective(name, selector) {
|
|||
|
||||
<example animations="true">
|
||||
<file name="index.html">
|
||||
<input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
|
||||
<input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
|
||||
<input type="button" value="set" ng-click="myVar='my-class'">
|
||||
<input type="button" value="clear" ng-click="myVar=''">
|
||||
<br>
|
||||
<span class="base-class" ng-class="myVar">Sample Text</span>
|
||||
</file>
|
||||
|
|
@ -168,19 +165,19 @@ function classDirective(name, selector) {
|
|||
font-size:3em;
|
||||
}
|
||||
</file>
|
||||
<file name="protractorTest.js">
|
||||
<file name="scenario.js">
|
||||
it('should check ng-class', function() {
|
||||
expect(element(by.css('.base-class')).getAttribute('class')).not.
|
||||
expect(element('.doc-example-live span').prop('className')).not().
|
||||
toMatch(/my-class/);
|
||||
|
||||
element(by.id('setbtn')).click();
|
||||
using('.doc-example-live').element(':button:first').click();
|
||||
|
||||
expect(element(by.css('.base-class')).getAttribute('class')).
|
||||
expect(element('.doc-example-live span').prop('className')).
|
||||
toMatch(/my-class/);
|
||||
|
||||
element(by.id('clearbtn')).click();
|
||||
using('.doc-example-live').element(':button:last').click();
|
||||
|
||||
expect(element(by.css('.base-class')).getAttribute('class')).not.
|
||||
expect(element('.doc-example-live span').prop('className')).not().
|
||||
toMatch(/my-class/);
|
||||
});
|
||||
</file>
|
||||
|
|
@ -232,11 +229,11 @@ var ngClassDirective = classDirective('', true);
|
|||
color: blue;
|
||||
}
|
||||
</file>
|
||||
<file name="protractorTest.js">
|
||||
<file name="scenario.js">
|
||||
it('should check ng-class-odd and ng-class-even', function() {
|
||||
expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
|
||||
expect(element('.doc-example-live li:first span').prop('className')).
|
||||
toMatch(/odd/);
|
||||
expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
|
||||
expect(element('.doc-example-live li:last span').prop('className')).
|
||||
toMatch(/even/);
|
||||
});
|
||||
</file>
|
||||
|
|
@ -280,11 +277,11 @@ var ngClassOddDirective = classDirective('Odd', 0);
|
|||
color: blue;
|
||||
}
|
||||
</file>
|
||||
<file name="protractorTest.js">
|
||||
<file name="scenario.js">
|
||||
it('should check ng-class-odd and ng-class-even', function() {
|
||||
expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
|
||||
expect(element('.doc-example-live li:first span').prop('className')).
|
||||
toMatch(/odd/);
|
||||
expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
|
||||
expect(element('.doc-example-live li:last span').prop('className')).
|
||||
toMatch(/even/);
|
||||
});
|
||||
</file>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
*
|
||||
* Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
|
||||
* cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
|
||||
* class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below.
|
||||
* class `ngCloak` in addition to the `ngCloak` directive as shown in the example below.
|
||||
*
|
||||
* @element ANY
|
||||
*
|
||||
|
|
@ -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:protractor>
|
||||
<doc:scenario>
|
||||
it('should remove the template directive and css class', function() {
|
||||
expect($('.doc-example-live #template1').getAttribute('ng-cloak')).
|
||||
toBeNull();
|
||||
expect($('.doc-example-live #template2').getAttribute('ng-cloak')).
|
||||
toBeNull();
|
||||
expect(element('.doc-example-live #template1').attr('ng-cloak')).
|
||||
not().toBeDefined();
|
||||
expect(element('.doc-example-live #template2').attr('ng-cloak')).
|
||||
not().toBeDefined();
|
||||
});
|
||||
</doc:protractor>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue